V4L/DVB: Deprecate cpia driver (used for parallel port webcams)
authorMauro Carvalho Chehab <mchehab@redhat.com>
Thu, 30 Sep 2010 11:25:42 +0000 (08:25 -0300)
committerMauro Carvalho Chehab <mchehab@redhat.com>
Thu, 21 Oct 2010 03:06:13 +0000 (01:06 -0200)
cpia driver were re-written inside gspca driver, for USB devices. The only
functionality that were not migrated is the support for parallel port,
as:
1) the developer didn't find any hardware;
2) it doesn't  seem important to keep support for a parallel port webcam,
   as this is an obsolete technology;
3) the changes at gspca for it to work with parallel port would be very large;
4) this driver still uses BKL.

So, let's move it to drivers/staging and label it to die at 2.6.38, if nobody
cares enough to port parallel port support to gspca or to create a new driver
that uses the same gspca-cpia sub-driver.

Signed-off-by: Mauro Carvalho Chehab <mchehab@redhat.com>
16 files changed:
Documentation/feature-removal-schedule.txt
drivers/media/video/Kconfig
drivers/media/video/Makefile
drivers/media/video/cpia.c [deleted file]
drivers/media/video/cpia.h [deleted file]
drivers/media/video/cpia_pp.c [deleted file]
drivers/media/video/cpia_usb.c [deleted file]
drivers/staging/Kconfig
drivers/staging/Makefile
drivers/staging/cpia/Kconfig [new file with mode: 0644]
drivers/staging/cpia/Makefile [new file with mode: 0644]
drivers/staging/cpia/TODO [new file with mode: 0644]
drivers/staging/cpia/cpia.c [new file with mode: 0644]
drivers/staging/cpia/cpia.h [new file with mode: 0644]
drivers/staging/cpia/cpia_pp.c [new file with mode: 0644]
drivers/staging/cpia/cpia_usb.c [new file with mode: 0644]

index 2372fb221969ff522258ad898317b9ed2d7dad2d..448722555648310f917e635a92feef34bec6d265 100644 (file)
@@ -98,7 +98,7 @@ Who:  Pavel Machek <pavel@ucw.cz>
 ---------------------------
 
 What:  Video4Linux API 1 ioctls and from Video devices.
-When:  July 2009
+When:  kernel 2.6.38
 Files: include/linux/videodev.h
 Check: include/linux/videodev.h
 Why:   V4L1 AP1 was replaced by V4L2 API during migration from 2.4 to 2.6
@@ -116,6 +116,21 @@ Who:       Mauro Carvalho Chehab <mchehab@infradead.org>
 
 ---------------------------
 
+What:  Video4Linux obsolete drivers using V4L1 API
+When:  kernel 2.6.38
+Files: drivers/staging/cpia/*
+Check: drivers/staging/cpia/cpia.c
+Why:   There are some drivers still using V4L1 API, despite all efforts we've done
+       to migrate. Those drivers are for obsolete hardware that the old maintainer
+       didn't care (or not have the hardware anymore), and that no other developer
+       could find any hardware to buy. They probably have no practical usage today,
+       and people with such old hardware could probably keep using an older version
+       of the kernel. Those drivers will be moved to staging on 2.6.37 and, if nobody
+       care enough to port and test them with V4L2 API, they'll be removed on 2.6.38.
+Who:   Mauro Carvalho Chehab <mchehab@infradead.org>
+
+---------------------------
+
 What:  sys_sysctl
 When:  September 2010
 Option: CONFIG_SYSCTL_SYSCALL
index 2c0a8f776f8aafeb2431b216b22e00f362653db0..4f21ef877f375aa0747107442bfc82525215e911 100644 (file)
@@ -599,46 +599,6 @@ config VIDEO_W9966
          Check out <file:Documentation/video4linux/w9966.txt> for more
          information.
 
-config VIDEO_CPIA
-       tristate "CPiA Video For Linux (DEPRECATED)"
-       depends on VIDEO_V4L1
-       default n
-       ---help---
-         This driver is DEPRECATED please use the gspca cpia1 module
-         instead. Note that you need atleast version 0.6.4 of libv4l for
-         the cpia1 gspca module.
-
-         This is the video4linux driver for cameras based on Vision's CPiA
-         (Colour Processor Interface ASIC), such as the Creative Labs Video
-         Blaster Webcam II. If you have one of these cameras, say Y here
-         and select parallel port and/or USB lowlevel support below,
-         otherwise say N. This will not work with the Creative Webcam III.
-
-         Please read <file:Documentation/video4linux/README.cpia> for more
-         information.
-
-         This driver is also available as a module (cpia).
-
-config VIDEO_CPIA_PP
-       tristate "CPiA Parallel Port Lowlevel Support"
-       depends on PARPORT_1284 && VIDEO_CPIA && PARPORT
-       help
-         This is the lowlevel parallel port support for cameras based on
-         Vision's CPiA (Colour Processor Interface ASIC), such as the
-         Creative Webcam II. If you have the parallel port version of one
-         of these cameras, say Y here, otherwise say N. It is also available
-         as a module (cpia_pp).
-
-config VIDEO_CPIA_USB
-       tristate "CPiA USB Lowlevel Support"
-       depends on VIDEO_CPIA && USB
-       help
-         This is the lowlevel USB support for cameras based on Vision's CPiA
-         (Colour Processor Interface ASIC), such as the Creative Webcam II.
-         If you have the USB version of one of these cameras, say Y here,
-         otherwise say N. This will not work with the Creative Webcam III.
-         It is also available as a module (cpia_usb).
-
 source "drivers/media/video/cpia2/Kconfig"
 
 config VIDEO_VINO
index a8f89899dd6bc9bc4eeec64af1619891cc544b4f..b947160a8cb0313dc2d497078ab24c0a492974b8 100644 (file)
@@ -92,9 +92,6 @@ obj-$(CONFIG_VIDEO_W9966) += w9966.o
 obj-$(CONFIG_VIDEO_PMS) += pms.o
 obj-$(CONFIG_VIDEO_VINO) += vino.o
 obj-$(CONFIG_VIDEO_STRADIS) += stradis.o
-obj-$(CONFIG_VIDEO_CPIA) += cpia.o
-obj-$(CONFIG_VIDEO_CPIA_PP) += cpia_pp.o
-obj-$(CONFIG_VIDEO_CPIA_USB) += cpia_usb.o
 obj-$(CONFIG_VIDEO_MEYE) += meye.o
 obj-$(CONFIG_VIDEO_SAA7134) += saa7134/
 obj-$(CONFIG_VIDEO_CX88) += cx88/
diff --git a/drivers/media/video/cpia.c b/drivers/media/video/cpia.c
deleted file mode 100644 (file)
index 933ae4c..0000000
+++ /dev/null
@@ -1,4032 +0,0 @@
-/*
- * cpia CPiA driver
- *
- * Supports CPiA based Video Camera's.
- *
- * (C) Copyright 1999-2000 Peter Pregler
- * (C) Copyright 1999-2000 Scott J. Bertin
- * (C) Copyright 1999-2000 Johannes Erdfelt <johannes@erdfelt.com>
- * (C) Copyright 2000 STMicroelectronics
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- */
-
-/* define _CPIA_DEBUG_ for verbose debug output (see cpia.h) */
-/* #define _CPIA_DEBUG_  1 */
-
-
-#include <linux/module.h>
-#include <linux/init.h>
-#include <linux/fs.h>
-#include <linux/vmalloc.h>
-#include <linux/sched.h>
-#include <linux/seq_file.h>
-#include <linux/slab.h>
-#include <linux/proc_fs.h>
-#include <linux/ctype.h>
-#include <linux/pagemap.h>
-#include <linux/delay.h>
-#include <asm/io.h>
-#include <linux/mutex.h>
-
-#include "cpia.h"
-
-static int video_nr = -1;
-
-#ifdef MODULE
-module_param(video_nr, int, 0);
-MODULE_AUTHOR("Scott J. Bertin <sbertin@securenym.net> & Peter Pregler <Peter_Pregler@email.com> & Johannes Erdfelt <johannes@erdfelt.com>");
-MODULE_DESCRIPTION("V4L-driver for Vision CPiA based cameras");
-MODULE_LICENSE("GPL");
-MODULE_SUPPORTED_DEVICE("video");
-#endif
-
-static unsigned short colorspace_conv;
-module_param(colorspace_conv, ushort, 0444);
-MODULE_PARM_DESC(colorspace_conv,
-                " Colorspace conversion:"
-                "\n  0 = disable, 1 = enable"
-                "\n  Default value is 0"
-                );
-
-#define ABOUT "V4L-Driver for Vision CPiA based cameras"
-
-#define CPIA_MODULE_CPIA                       (0<<5)
-#define CPIA_MODULE_SYSTEM                     (1<<5)
-#define CPIA_MODULE_VP_CTRL                    (5<<5)
-#define CPIA_MODULE_CAPTURE                    (6<<5)
-#define CPIA_MODULE_DEBUG                      (7<<5)
-
-#define INPUT (DATA_IN << 8)
-#define OUTPUT (DATA_OUT << 8)
-
-#define CPIA_COMMAND_GetCPIAVersion    (INPUT | CPIA_MODULE_CPIA | 1)
-#define CPIA_COMMAND_GetPnPID          (INPUT | CPIA_MODULE_CPIA | 2)
-#define CPIA_COMMAND_GetCameraStatus   (INPUT | CPIA_MODULE_CPIA | 3)
-#define CPIA_COMMAND_GotoHiPower       (OUTPUT | CPIA_MODULE_CPIA | 4)
-#define CPIA_COMMAND_GotoLoPower       (OUTPUT | CPIA_MODULE_CPIA | 5)
-#define CPIA_COMMAND_GotoSuspend       (OUTPUT | CPIA_MODULE_CPIA | 7)
-#define CPIA_COMMAND_GotoPassThrough   (OUTPUT | CPIA_MODULE_CPIA | 8)
-#define CPIA_COMMAND_ModifyCameraStatus        (OUTPUT | CPIA_MODULE_CPIA | 10)
-
-#define CPIA_COMMAND_ReadVCRegs                (INPUT | CPIA_MODULE_SYSTEM | 1)
-#define CPIA_COMMAND_WriteVCReg                (OUTPUT | CPIA_MODULE_SYSTEM | 2)
-#define CPIA_COMMAND_ReadMCPorts       (INPUT | CPIA_MODULE_SYSTEM | 3)
-#define CPIA_COMMAND_WriteMCPort       (OUTPUT | CPIA_MODULE_SYSTEM | 4)
-#define CPIA_COMMAND_SetBaudRate       (OUTPUT | CPIA_MODULE_SYSTEM | 5)
-#define CPIA_COMMAND_SetECPTiming      (OUTPUT | CPIA_MODULE_SYSTEM | 6)
-#define CPIA_COMMAND_ReadIDATA         (INPUT | CPIA_MODULE_SYSTEM | 7)
-#define CPIA_COMMAND_WriteIDATA                (OUTPUT | CPIA_MODULE_SYSTEM | 8)
-#define CPIA_COMMAND_GenericCall       (OUTPUT | CPIA_MODULE_SYSTEM | 9)
-#define CPIA_COMMAND_I2CStart          (OUTPUT | CPIA_MODULE_SYSTEM | 10)
-#define CPIA_COMMAND_I2CStop           (OUTPUT | CPIA_MODULE_SYSTEM | 11)
-#define CPIA_COMMAND_I2CWrite          (OUTPUT | CPIA_MODULE_SYSTEM | 12)
-#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)
-#define CPIA_COMMAND_SetSensorFPS      (OUTPUT | CPIA_MODULE_VP_CTRL | 7)
-#define CPIA_COMMAND_SetVPDefaults     (OUTPUT | CPIA_MODULE_VP_CTRL | 8)
-#define CPIA_COMMAND_SetApcor          (OUTPUT | CPIA_MODULE_VP_CTRL | 9)
-#define CPIA_COMMAND_SetFlickerCtrl    (OUTPUT | CPIA_MODULE_VP_CTRL | 10)
-#define CPIA_COMMAND_SetVLOffset       (OUTPUT | CPIA_MODULE_VP_CTRL | 11)
-#define CPIA_COMMAND_GetColourParams   (INPUT | CPIA_MODULE_VP_CTRL | 16)
-#define CPIA_COMMAND_GetColourBalance  (INPUT | CPIA_MODULE_VP_CTRL | 17)
-#define CPIA_COMMAND_GetExposure       (INPUT | CPIA_MODULE_VP_CTRL | 18)
-#define CPIA_COMMAND_SetSensorMatrix   (OUTPUT | CPIA_MODULE_VP_CTRL | 19)
-#define CPIA_COMMAND_ColourBars                (OUTPUT | CPIA_MODULE_VP_CTRL | 25)
-#define CPIA_COMMAND_ReadVPRegs                (INPUT | CPIA_MODULE_VP_CTRL | 30)
-#define CPIA_COMMAND_WriteVPReg                (OUTPUT | CPIA_MODULE_VP_CTRL | 31)
-
-#define CPIA_COMMAND_GrabFrame         (OUTPUT | CPIA_MODULE_CAPTURE | 1)
-#define CPIA_COMMAND_UploadFrame       (OUTPUT | CPIA_MODULE_CAPTURE | 2)
-#define CPIA_COMMAND_SetGrabMode       (OUTPUT | CPIA_MODULE_CAPTURE | 3)
-#define CPIA_COMMAND_InitStreamCap     (OUTPUT | CPIA_MODULE_CAPTURE | 4)
-#define CPIA_COMMAND_FiniStreamCap     (OUTPUT | CPIA_MODULE_CAPTURE | 5)
-#define CPIA_COMMAND_StartStreamCap    (OUTPUT | CPIA_MODULE_CAPTURE | 6)
-#define CPIA_COMMAND_EndStreamCap      (OUTPUT | CPIA_MODULE_CAPTURE | 7)
-#define CPIA_COMMAND_SetFormat         (OUTPUT | CPIA_MODULE_CAPTURE | 8)
-#define CPIA_COMMAND_SetROI            (OUTPUT | CPIA_MODULE_CAPTURE | 9)
-#define CPIA_COMMAND_SetCompression    (OUTPUT | CPIA_MODULE_CAPTURE | 10)
-#define CPIA_COMMAND_SetCompressionTarget (OUTPUT | CPIA_MODULE_CAPTURE | 11)
-#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)
-#define CPIA_COMMAND_SetDramPage       (OUTPUT | CPIA_MODULE_DEBUG | 5)
-#define CPIA_COMMAND_StartDramUpload   (OUTPUT | CPIA_MODULE_DEBUG | 6)
-#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)
-#define CPIA_COMMAND_Null              (OUTPUT | CPIA_MODULE_DEBUG | 11)
-
-enum {
-       FRAME_READY,            /* Ready to grab into */
-       FRAME_GRABBING,         /* In the process of being grabbed into */
-       FRAME_DONE,             /* Finished grabbing, but not been synced yet */
-       FRAME_UNUSED,           /* Unused (no MCAPTURE) */
-};
-
-#define COMMAND_NONE                   0x0000
-#define COMMAND_SETCOMPRESSION         0x0001
-#define COMMAND_SETCOMPRESSIONTARGET   0x0002
-#define COMMAND_SETCOLOURPARAMS                0x0004
-#define COMMAND_SETFORMAT              0x0008
-#define COMMAND_PAUSE                  0x0010
-#define COMMAND_RESUME                 0x0020
-#define COMMAND_SETYUVTHRESH           0x0040
-#define COMMAND_SETECPTIMING           0x0080
-#define COMMAND_SETCOMPRESSIONPARAMS   0x0100
-#define COMMAND_SETEXPOSURE            0x0200
-#define COMMAND_SETCOLOURBALANCE       0x0400
-#define COMMAND_SETSENSORFPS           0x0800
-#define COMMAND_SETAPCOR               0x1000
-#define COMMAND_SETFLICKERCTRL         0x2000
-#define COMMAND_SETVLOFFSET            0x4000
-#define COMMAND_SETLIGHTS              0x8000
-
-#define ROUND_UP_EXP_FOR_FLICKER 15
-
-/* Constants for automatic frame rate adjustment */
-#define MAX_EXP       302
-#define MAX_EXP_102   255
-#define LOW_EXP       140
-#define VERY_LOW_EXP   70
-#define TC             94
-#define        EXP_ACC_DARK   50
-#define        EXP_ACC_LIGHT  90
-#define HIGH_COMP_102 160
-#define MAX_COMP      239
-#define DARK_TIME       3
-#define LIGHT_TIME      3
-
-/* Maximum number of 10ms loops to wait for the stream to become ready */
-#define READY_TIMEOUT 100
-
-/* Developer's Guide Table 5 p 3-34
- * indexed by [mains][sensorFps.baserate][sensorFps.divisor]*/
-static u8 flicker_jumps[2][2][4] =
-{ { { 76, 38, 19, 9 }, { 92, 46, 23, 11 } },
-  { { 64, 32, 16, 8 }, { 76, 38, 19, 9} }
-};
-
-/* forward declaration of local function */
-static void reset_camera_struct(struct cam_data *cam);
-static int find_over_exposure(int brightness);
-static void set_flicker(struct cam_params *params, volatile u32 *command_flags,
-                       int on);
-
-
-/**********************************************************************
- *
- * Memory management
- *
- **********************************************************************/
-static void *rvmalloc(unsigned long size)
-{
-       void *mem;
-       unsigned long adr;
-
-       size = PAGE_ALIGN(size);
-       mem = vmalloc_32(size);
-       if (!mem)
-               return NULL;
-
-       memset(mem, 0, size); /* Clear the ram out, no junk to the user */
-       adr = (unsigned long) mem;
-       while (size > 0) {
-               SetPageReserved(vmalloc_to_page((void *)adr));
-               adr += PAGE_SIZE;
-               size -= PAGE_SIZE;
-       }
-
-       return mem;
-}
-
-static void rvfree(void *mem, unsigned long size)
-{
-       unsigned long adr;
-
-       if (!mem)
-               return;
-
-       adr = (unsigned long) mem;
-       while ((long) size > 0) {
-               ClearPageReserved(vmalloc_to_page((void *)adr));
-               adr += PAGE_SIZE;
-               size -= PAGE_SIZE;
-       }
-       vfree(mem);
-}
-
-/**********************************************************************
- *
- * /proc interface
- *
- **********************************************************************/
-#ifdef CONFIG_PROC_FS
-static struct proc_dir_entry *cpia_proc_root=NULL;
-
-static int cpia_proc_show(struct seq_file *m, void *v)
-{
-       struct cam_data *cam = m->private;
-       int tmp;
-       char tmpstr[29];
-
-       seq_printf(m, "read-only\n-----------------------\n");
-       seq_printf(m, "V4L Driver version:       %d.%d.%d\n",
-                      CPIA_MAJ_VER, CPIA_MIN_VER, CPIA_PATCH_VER);
-       seq_printf(m, "CPIA Version:             %d.%02d (%d.%d)\n",
-                      cam->params.version.firmwareVersion,
-                      cam->params.version.firmwareRevision,
-                      cam->params.version.vcVersion,
-                      cam->params.version.vcRevision);
-       seq_printf(m, "CPIA PnP-ID:              %04x:%04x:%04x\n",
-                      cam->params.pnpID.vendor, cam->params.pnpID.product,
-                      cam->params.pnpID.deviceRevision);
-       seq_printf(m, "VP-Version:               %d.%d %04x\n",
-                      cam->params.vpVersion.vpVersion,
-                      cam->params.vpVersion.vpRevision,
-                      cam->params.vpVersion.cameraHeadID);
-
-       seq_printf(m, "system_state:             %#04x\n",
-                      cam->params.status.systemState);
-       seq_printf(m, "grab_state:               %#04x\n",
-                      cam->params.status.grabState);
-       seq_printf(m, "stream_state:             %#04x\n",
-                      cam->params.status.streamState);
-       seq_printf(m, "fatal_error:              %#04x\n",
-                      cam->params.status.fatalError);
-       seq_printf(m, "cmd_error:                %#04x\n",
-                      cam->params.status.cmdError);
-       seq_printf(m, "debug_flags:              %#04x\n",
-                      cam->params.status.debugFlags);
-       seq_printf(m, "vp_status:                %#04x\n",
-                      cam->params.status.vpStatus);
-       seq_printf(m, "error_code:               %#04x\n",
-                      cam->params.status.errorCode);
-       /* QX3 specific entries */
-       if (cam->params.qx3.qx3_detected) {
-               seq_printf(m, "button:                   %4d\n",
-                              cam->params.qx3.button);
-               seq_printf(m, "cradled:                  %4d\n",
-                              cam->params.qx3.cradled);
-       }
-       seq_printf(m, "video_size:               %s\n",
-                      cam->params.format.videoSize == VIDEOSIZE_CIF ?
-                      "CIF " : "QCIF");
-       seq_printf(m, "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);
-       seq_printf(m, "actual_fps:               %3d\n", cam->fps);
-       seq_printf(m, "transfer_rate:            %4dkB/s\n",
-                      cam->transfer_rate);
-
-       seq_printf(m, "\nread-write\n");
-       seq_printf(m, "-----------------------  current       min"
-                      "       max   default  comment\n");
-       seq_printf(m, "brightness:             %8d  %8d  %8d  %8d\n",
-                      cam->params.colourParams.brightness, 0, 100, 50);
-       if (cam->params.version.firmwareVersion == 1 &&
-          cam->params.version.firmwareRevision == 2)
-               /* 1-02 firmware limits contrast to 80 */
-               tmp = 80;
-       else
-               tmp = 96;
-
-       seq_printf(m, "contrast:               %8d  %8d  %8d  %8d"
-                      "  steps of 8\n",
-                      cam->params.colourParams.contrast, 0, tmp, 48);
-       seq_printf(m, "saturation:             %8d  %8d  %8d  %8d\n",
-                      cam->params.colourParams.saturation, 0, 100, 50);
-       tmp = (25000+5000*cam->params.sensorFps.baserate)/
-             (1<<cam->params.sensorFps.divisor);
-       seq_printf(m, "sensor_fps:             %4d.%03d  %8d  %8d  %8d\n",
-                      tmp/1000, tmp%1000, 3, 30, 15);
-       seq_printf(m, "stream_start_line:      %8d  %8d  %8d  %8d\n",
-                      2*cam->params.streamStartLine, 0,
-                      cam->params.format.videoSize == VIDEOSIZE_CIF ? 288:144,
-                      cam->params.format.videoSize == VIDEOSIZE_CIF ? 240:120);
-       seq_printf(m, "sub_sample:             %8s  %8s  %8s  %8s\n",
-                      cam->params.format.subSample == SUBSAMPLE_420 ?
-                      "420" : "422", "420", "422", "422");
-       seq_printf(m, "yuv_order:              %8s  %8s  %8s  %8s\n",
-                      cam->params.format.yuvOrder == YUVORDER_YUYV ?
-                      "YUYV" : "UYVY", "YUYV" , "UYVY", "YUYV");
-       seq_printf(m, "ecp_timing:             %8s  %8s  %8s  %8s\n",
-                      cam->params.ecpTiming ? "slow" : "normal", "slow",
-                      "normal", "normal");
-
-       if (cam->params.colourBalance.balanceMode == 2) {
-               sprintf(tmpstr, "auto");
-       } else {
-               sprintf(tmpstr, "manual");
-       }
-       seq_printf(m, "color_balance_mode:     %8s  %8s  %8s"
-                      "  %8s\n",  tmpstr, "manual", "auto", "auto");
-       seq_printf(m, "red_gain:               %8d  %8d  %8d  %8d\n",
-                      cam->params.colourBalance.redGain, 0, 212, 32);
-       seq_printf(m, "green_gain:             %8d  %8d  %8d  %8d\n",
-                      cam->params.colourBalance.greenGain, 0, 212, 6);
-       seq_printf(m, "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)
-               /* 1-02 firmware limits gain to 2 */
-               sprintf(tmpstr, "%8d  %8d  %8d", 1, 2, 2);
-       else
-               sprintf(tmpstr, "%8d  %8d  %8d", 1, 8, 2);
-
-       if (cam->params.exposure.gainMode == 0)
-               seq_printf(m, "max_gain:                unknown  %28s"
-                              "  powers of 2\n", tmpstr);
-       else
-               seq_printf(m, "max_gain:               %8d  %28s"
-                              "  1,2,4 or 8 \n",
-                              1<<(cam->params.exposure.gainMode-1), tmpstr);
-
-       switch(cam->params.exposure.expMode) {
-       case 1:
-       case 3:
-               sprintf(tmpstr, "manual");
-               break;
-       case 2:
-               sprintf(tmpstr, "auto");
-               break;
-       default:
-               sprintf(tmpstr, "unknown");
-               break;
-       }
-       seq_printf(m, "exposure_mode:          %8s  %8s  %8s"
-                      "  %8s\n",  tmpstr, "manual", "auto", "auto");
-       seq_printf(m, "centre_weight:          %8s  %8s  %8s  %8s\n",
-                      (2-cam->params.exposure.centreWeight) ? "on" : "off",
-                      "off", "on", "on");
-       seq_printf(m, "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)
-               /* 1-02 firmware limits fineExp/2 to 127 */
-               tmp = 254;
-       else
-               tmp = 510;
-
-       seq_printf(m, "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)
-               /* 1-02 firmware limits coarseExpHi to 0 */
-               tmp = MAX_EXP_102;
-       else
-               tmp = MAX_EXP;
-
-       seq_printf(m, "coarse_exp:             %8d  %8d  %8d"
-                      "  %8d\n", cam->params.exposure.coarseExpLo+
-                      256*cam->params.exposure.coarseExpHi, 0, tmp, 185);
-       seq_printf(m, "red_comp:               %8d  %8d  %8d  %8d\n",
-                      cam->params.exposure.redComp, COMP_RED, 255, COMP_RED);
-       seq_printf(m, "green1_comp:            %8d  %8d  %8d  %8d\n",
-                      cam->params.exposure.green1Comp, COMP_GREEN1, 255,
-                      COMP_GREEN1);
-       seq_printf(m, "green2_comp:            %8d  %8d  %8d  %8d\n",
-                      cam->params.exposure.green2Comp, COMP_GREEN2, 255,
-                      COMP_GREEN2);
-       seq_printf(m, "blue_comp:              %8d  %8d  %8d  %8d\n",
-                      cam->params.exposure.blueComp, COMP_BLUE, 255, COMP_BLUE);
-
-       seq_printf(m, "apcor_gain1:            %#8x  %#8x  %#8x  %#8x\n",
-                      cam->params.apcor.gain1, 0, 0xff, 0x1c);
-       seq_printf(m, "apcor_gain2:            %#8x  %#8x  %#8x  %#8x\n",
-                      cam->params.apcor.gain2, 0, 0xff, 0x1a);
-       seq_printf(m, "apcor_gain4:            %#8x  %#8x  %#8x  %#8x\n",
-                      cam->params.apcor.gain4, 0, 0xff, 0x2d);
-       seq_printf(m, "apcor_gain8:            %#8x  %#8x  %#8x  %#8x\n",
-                      cam->params.apcor.gain8, 0, 0xff, 0x2a);
-       seq_printf(m, "vl_offset_gain1:        %8d  %8d  %8d  %8d\n",
-                      cam->params.vlOffset.gain1, 0, 255, 24);
-       seq_printf(m, "vl_offset_gain2:        %8d  %8d  %8d  %8d\n",
-                      cam->params.vlOffset.gain2, 0, 255, 28);
-       seq_printf(m, "vl_offset_gain4:        %8d  %8d  %8d  %8d\n",
-                      cam->params.vlOffset.gain4, 0, 255, 30);
-       seq_printf(m, "vl_offset_gain8:        %8d  %8d  %8d  %8d\n",
-                      cam->params.vlOffset.gain8, 0, 255, 30);
-       seq_printf(m, "flicker_control:        %8s  %8s  %8s  %8s\n",
-                      cam->params.flickerControl.flickerMode ? "on" : "off",
-                      "off", "on", "off");
-       seq_printf(m, "mains_frequency:        %8d  %8d  %8d  %8d"
-                      " only 50/60\n",
-                      cam->mainsFreq ? 60 : 50, 50, 60, 50);
-       if(cam->params.flickerControl.allowableOverExposure < 0)
-               seq_printf(m, "allowable_overexposure: %4dauto      auto  %8d      auto\n",
-                              -cam->params.flickerControl.allowableOverExposure,
-                              255);
-       else
-               seq_printf(m, "allowable_overexposure: %8d      auto  %8d      auto\n",
-                              cam->params.flickerControl.allowableOverExposure,
-                              255);
-       seq_printf(m, "compression_mode:       ");
-       switch(cam->params.compression.mode) {
-       case CPIA_COMPRESSION_NONE:
-               seq_printf(m, "%8s", "none");
-               break;
-       case CPIA_COMPRESSION_AUTO:
-               seq_printf(m, "%8s", "auto");
-               break;
-       case CPIA_COMPRESSION_MANUAL:
-               seq_printf(m, "%8s", "manual");
-               break;
-       default:
-               seq_printf(m, "%8s", "unknown");
-               break;
-       }
-       seq_printf(m, "    none,auto,manual      auto\n");
-       seq_printf(m, "decimation_enable:      %8s  %8s  %8s  %8s\n",
-                      cam->params.compression.decimation ==
-                      DECIMATION_ENAB ? "on":"off", "off", "on",
-                      "off");
-       seq_printf(m, "compression_target:    %9s %9s %9s %9s\n",
-                      cam->params.compressionTarget.frTargeting  ==
-                      CPIA_COMPRESSION_TARGET_FRAMERATE ?
-                      "framerate":"quality",
-                      "framerate", "quality", "quality");
-       seq_printf(m, "target_framerate:       %8d  %8d  %8d  %8d\n",
-                      cam->params.compressionTarget.targetFR, 1, 30, 15);
-       seq_printf(m, "target_quality:         %8d  %8d  %8d  %8d\n",
-                      cam->params.compressionTarget.targetQ, 1, 64, 5);
-       seq_printf(m, "y_threshold:            %8d  %8d  %8d  %8d\n",
-                      cam->params.yuvThreshold.yThreshold, 0, 31, 6);
-       seq_printf(m, "uv_threshold:           %8d  %8d  %8d  %8d\n",
-                      cam->params.yuvThreshold.uvThreshold, 0, 31, 6);
-       seq_printf(m, "hysteresis:             %8d  %8d  %8d  %8d\n",
-                      cam->params.compressionParams.hysteresis, 0, 255, 3);
-       seq_printf(m, "threshold_max:          %8d  %8d  %8d  %8d\n",
-                      cam->params.compressionParams.threshMax, 0, 255, 11);
-       seq_printf(m, "small_step:             %8d  %8d  %8d  %8d\n",
-                      cam->params.compressionParams.smallStep, 0, 255, 1);
-       seq_printf(m, "large_step:             %8d  %8d  %8d  %8d\n",
-                      cam->params.compressionParams.largeStep, 0, 255, 3);
-       seq_printf(m, "decimation_hysteresis:  %8d  %8d  %8d  %8d\n",
-                      cam->params.compressionParams.decimationHysteresis,
-                      0, 255, 2);
-       seq_printf(m, "fr_diff_step_thresh:    %8d  %8d  %8d  %8d\n",
-                      cam->params.compressionParams.frDiffStepThresh,
-                      0, 255, 5);
-       seq_printf(m, "q_diff_step_thresh:     %8d  %8d  %8d  %8d\n",
-                      cam->params.compressionParams.qDiffStepThresh,
-                      0, 255, 3);
-       seq_printf(m, "decimation_thresh_mod:  %8d  %8d  %8d  %8d\n",
-                      cam->params.compressionParams.decimationThreshMod,
-                      0, 255, 2);
-       /* QX3 specific entries */
-       if (cam->params.qx3.qx3_detected) {
-               seq_printf(m, "toplight:               %8s  %8s  %8s  %8s\n",
-                              cam->params.qx3.toplight ? "on" : "off",
-                              "off", "on", "off");
-               seq_printf(m, "bottomlight:            %8s  %8s  %8s  %8s\n",
-                              cam->params.qx3.bottomlight ? "on" : "off",
-                              "off", "on", "off");
-       }
-
-       return 0;
-}
-
-static int cpia_proc_open(struct inode *inode, struct file *file)
-{
-       return single_open(file, cpia_proc_show, PDE(inode)->data);
-}
-
-static int match(char *checkstr, char **buffer, size_t *count,
-                int *find_colon, int *err)
-{
-       int ret, colon_found = 1;
-       int len = strlen(checkstr);
-       ret = (len <= *count && strncmp(*buffer, checkstr, 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)
-                               *err = -EINVAL;
-                       *find_colon = 0;
-               }
-       }
-       return ret;
-}
-
-static unsigned long int value(char **buffer, size_t *count, int *err)
-{
-       char *p;
-       unsigned long int ret;
-       ret = simple_strtoul(*buffer, &p, 0);
-       if (p == *buffer)
-               *err = -EINVAL;
-       else {
-               *count -= p - *buffer;
-               *buffer = p;
-       }
-       return ret;
-}
-
-static ssize_t cpia_proc_write(struct file *file, const char __user *buf,
-                              size_t count, loff_t *pos)
-{
-       struct cam_data *cam = PDE(file->f_path.dentry->d_inode)->data;
-       struct cam_params new_params;
-       char *page, *buffer;
-       int retval, find_colon;
-       int size = count;
-       unsigned long val = 0;
-       u32 command_flags = 0;
-       u8 new_mains;
-
-       /*
-        * This code to copy from buf to page is shamelessly copied
-        * from the comx driver
-        */
-       if (count > PAGE_SIZE) {
-               printk(KERN_ERR "count is %zu > %d!!!\n", count, (int)PAGE_SIZE);
-               return -ENOSPC;
-       }
-
-       if (!(page = (char *)__get_free_page(GFP_KERNEL))) return -ENOMEM;
-
-       if(copy_from_user(page, buf, count))
-       {
-               retval = -EFAULT;
-               goto out;
-       }
-
-       if (page[count-1] == '\n')
-               page[count-1] = '\0';
-       else if (count < PAGE_SIZE)
-               page[count] = '\0';
-       else if (page[count]) {
-               retval = -EINVAL;
-               goto out;
-       }
-
-       buffer = page;
-
-       if (mutex_lock_interruptible(&cam->param_lock))
-               return -ERESTARTSYS;
-
-       /*
-        * Skip over leading whitespace
-        */
-       while (count && isspace(*buffer)) {
-               --count;
-               ++buffer;
-       }
-
-       memcpy(&new_params, &cam->params, sizeof(struct cam_params));
-       new_mains = cam->mainsFreq;
-
-#define MATCH(x) (match(x, &buffer, &count, &find_colon, &retval))
-#define VALUE (value(&buffer,&count, &retval))
-#define FIRMWARE_VERSION(x,y) (new_params.version.firmwareVersion == (x) && \
-                              new_params.version.firmwareRevision == (y))
-
-       retval = 0;
-       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;
-                       if(new_params.flickerControl.allowableOverExposure < 0)
-                               new_params.flickerControl.allowableOverExposure =
-                                       -find_over_exposure(new_params.colourParams.brightness);
-                       if(new_params.flickerControl.flickerMode != 0)
-                               command_flags |= COMMAND_SETFLICKERCTRL;
-
-               } 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)
-                                               val = 80;
-
-                                       new_params.colourParams.contrast = val;
-                               } else
-                                       retval = -EINVAL;
-                       }
-                       command_flags |= COMMAND_SETCOLOURPARAMS;
-               } 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;
-               } else if (MATCH("sensor_fps")) {
-                       if (!retval)
-                               val = VALUE;
-
-                       if (!retval) {
-                               /* find values so that sensorFPS is minimized,
-                                * but >= val */
-                               if (val > 30)
-                                       retval = -EINVAL;
-                               else if (val > 25) {
-                                       new_params.sensorFps.divisor = 0;
-                                       new_params.sensorFps.baserate = 1;
-                               } else if (val > 15) {
-                                       new_params.sensorFps.divisor = 0;
-                                       new_params.sensorFps.baserate = 0;
-                               } else if (val > 12) {
-                                       new_params.sensorFps.divisor = 1;
-                                       new_params.sensorFps.baserate = 1;
-                               } else if (val > 7) {
-                                       new_params.sensorFps.divisor = 1;
-                                       new_params.sensorFps.baserate = 0;
-                               } else if (val > 6) {
-                                       new_params.sensorFps.divisor = 2;
-                                       new_params.sensorFps.baserate = 1;
-                               } else if (val > 3) {
-                                       new_params.sensorFps.divisor = 2;
-                                       new_params.sensorFps.baserate = 0;
-                               } else {
-                                       new_params.sensorFps.divisor = 3;
-                                       /* Either base rate would work here */
-                                       new_params.sensorFps.baserate = 1;
-                               }
-                               new_params.flickerControl.coarseJump =
-                                       flicker_jumps[new_mains]
-                                       [new_params.sensorFps.baserate]
-                                       [new_params.sensorFps.divisor];
-                               if (new_params.flickerControl.flickerMode)
-                                       command_flags |= COMMAND_SETFLICKERCTRL;
-                       }
-                       command_flags |= COMMAND_SETSENSORFPS;
-                       cam->exposure_status = EXPOSURE_NORMAL;
-               } else if (MATCH("stream_start_line")) {
-                       if (!retval)
-                               val = VALUE;
-
-                       if (!retval) {
-                               int max_line = 288;
-
-                               if (new_params.format.videoSize == VIDEOSIZE_QCIF)
-                                       max_line = 144;
-                               if (val <= max_line)
-                                       new_params.streamStartLine = val/2;
-                               else
-                                       retval = -EINVAL;
-                       }
-               } 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"))
-                               new_params.ecpTiming = 1;
-                       else
-                               retval = -EINVAL;
-
-                       command_flags |= COMMAND_SETECPTIMING;
-               } else if (MATCH("color_balance_mode")) {
-                       if (!retval && MATCH("manual"))
-                               new_params.colourBalance.balanceMode = 3;
-                       else if (!retval && MATCH("auto"))
-                               new_params.colourBalance.balanceMode = 2;
-                       else
-                               retval = -EINVAL;
-
-                       command_flags |= COMMAND_SETCOLOURBALANCE;
-               } else if (MATCH("red_gain")) {
-                       if (!retval)
-                               val = VALUE;
-
-                       if (!retval) {
-                               if (val <= 212) {
-                                       new_params.colourBalance.redGain = val;
-                                       new_params.colourBalance.balanceMode = 1;
-                               } else
-                                       retval = -EINVAL;
-                       }
-                       command_flags |= COMMAND_SETCOLOURBALANCE;
-               } else if (MATCH("green_gain")) {
-                       if (!retval)
-                               val = VALUE;
-
-                       if (!retval) {
-                               if (val <= 212) {
-                                       new_params.colourBalance.greenGain = val;
-                                       new_params.colourBalance.balanceMode = 1;
-                               } else
-                                       retval = -EINVAL;
-                       }
-                       command_flags |= COMMAND_SETCOLOURBALANCE;
-               } else if (MATCH("blue_gain")) {
-                       if (!retval)
-                               val = VALUE;
-
-                       if (!retval) {
-                               if (val <= 212) {
-                                       new_params.colourBalance.blueGain = val;
-                                       new_params.colourBalance.balanceMode = 1;
-                               } else
-                                       retval = -EINVAL;
-                       }
-                       command_flags |= COMMAND_SETCOLOURBALANCE;
-               } 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)
-                                       val = 2;
-                               switch(val) {
-                               case 1:
-                                       new_params.exposure.gainMode = 1;
-                                       break;
-                               case 2:
-                                       new_params.exposure.gainMode = 2;
-                                       break;
-                               case 4:
-                                       new_params.exposure.gainMode = 3;
-                                       break;
-                               case 8:
-                                       new_params.exposure.gainMode = 4;
-                                       break;
-                               default:
-                                       retval = -EINVAL;
-                                       break;
-                               }
-                       }
-                       command_flags |= COMMAND_SETEXPOSURE;
-               } else if (MATCH("exposure_mode")) {
-                       if (!retval && MATCH("auto"))
-                               new_params.exposure.expMode = 2;
-                       else if (!retval && MATCH("manual")) {
-                               if (new_params.exposure.expMode == 2)
-                                       new_params.exposure.expMode = 3;
-                               if(new_params.flickerControl.flickerMode != 0)
-                                       command_flags |= COMMAND_SETFLICKERCTRL;
-                               new_params.flickerControl.flickerMode = 0;
-                       } else
-                               retval = -EINVAL;
-
-                       command_flags |= COMMAND_SETEXPOSURE;
-               } else if (MATCH("centre_weight")) {
-                       if (!retval && MATCH("on"))
-                               new_params.exposure.centreWeight = 1;
-                       else if (!retval && MATCH("off"))
-                               new_params.exposure.centreWeight = 2;
-                       else
-                               retval = -EINVAL;
-
-                       command_flags |= COMMAND_SETEXPOSURE;
-               } else if (MATCH("gain")) {
-                       if (!retval)
-                               val = VALUE;
-
-                       if (!retval) {
-                               switch(val) {
-                               case 1:
-                                       new_params.exposure.gain = 0;
-                                       break;
-                               case 2:
-                                       new_params.exposure.gain = 1;
-                                       break;
-                               case 4:
-                                       new_params.exposure.gain = 2;
-                                       break;
-                               case 8:
-                                       new_params.exposure.gain = 3;
-                                       break;
-                               default:
-                                       retval = -EINVAL;
-                                       break;
-                               }
-                               new_params.exposure.expMode = 1;
-                               if(new_params.flickerControl.flickerMode != 0)
-                                       command_flags |= COMMAND_SETFLICKERCTRL;
-                               new_params.flickerControl.flickerMode = 0;
-                               command_flags |= COMMAND_SETEXPOSURE;
-                               if (new_params.exposure.gain >
-                                   new_params.exposure.gainMode-1)
-                                       retval = -EINVAL;
-                       }
-               } else if (MATCH("fine_exp")) {
-                       if (!retval)
-                               val = VALUE/2;
-
-                       if (!retval) {
-                               if (val < 256) {
-                                       /* 1-02 firmware limits fineExp/2 to 127*/
-                                       if (FIRMWARE_VERSION(1,2) && val > 127)
-                                               val = 127;
-                                       new_params.exposure.fineExp = val;
-                                       new_params.exposure.expMode = 1;
-                                       command_flags |= COMMAND_SETEXPOSURE;
-                                       if(new_params.flickerControl.flickerMode != 0)
-                                               command_flags |= COMMAND_SETFLICKERCTRL;
-                                       new_params.flickerControl.flickerMode = 0;
-                                       command_flags |= COMMAND_SETFLICKERCTRL;
-                               } else
-                                       retval = -EINVAL;
-                       }
-               } else if (MATCH("coarse_exp")) {
-                       if (!retval)
-                               val = VALUE;
-
-                       if (!retval) {
-                               if (val <= MAX_EXP) {
-                                       if (FIRMWARE_VERSION(1,2) &&
-                                           val > MAX_EXP_102)
-                                               val = MAX_EXP_102;
-                                       new_params.exposure.coarseExpLo =
-                                               val & 0xff;
-                                       new_params.exposure.coarseExpHi =
-                                               val >> 8;
-                                       new_params.exposure.expMode = 1;
-                                       command_flags |= COMMAND_SETEXPOSURE;
-                                       if(new_params.flickerControl.flickerMode != 0)
-                                               command_flags |= COMMAND_SETFLICKERCTRL;
-                                       new_params.flickerControl.flickerMode = 0;
-                                       command_flags |= COMMAND_SETFLICKERCTRL;
-                               } else
-                                       retval = -EINVAL;
-                       }
-               } else if (MATCH("red_comp")) {
-                       if (!retval)
-                               val = VALUE;
-
-                       if (!retval) {
-                               if (val >= COMP_RED && val <= 255) {
-                                       new_params.exposure.redComp = val;
-                                       new_params.exposure.compMode = 1;
-                                       command_flags |= COMMAND_SETEXPOSURE;
-                               } else
-                                       retval = -EINVAL;
-                       }
-               } else if (MATCH("green1_comp")) {
-                       if (!retval)
-                               val = VALUE;
-
-                       if (!retval) {
-                               if (val >= COMP_GREEN1 && val <= 255) {
-                                       new_params.exposure.green1Comp = val;
-                                       new_params.exposure.compMode = 1;
-                                       command_flags |= COMMAND_SETEXPOSURE;
-                               } else
-                                       retval = -EINVAL;
-                       }
-               } else if (MATCH("green2_comp")) {
-                       if (!retval)
-                               val = VALUE;
-
-                       if (!retval) {
-                               if (val >= COMP_GREEN2 && val <= 255) {
-                                       new_params.exposure.green2Comp = val;
-                                       new_params.exposure.compMode = 1;
-                                       command_flags |= COMMAND_SETEXPOSURE;
-                               } else
-                                       retval = -EINVAL;
-                       }
-               } else if (MATCH("blue_comp")) {
-                       if (!retval)
-                               val = VALUE;
-
-                       if (!retval) {
-                               if (val >= COMP_BLUE && val <= 255) {
-                                       new_params.exposure.blueComp = val;
-                                       new_params.exposure.compMode = 1;
-                                       command_flags |= COMMAND_SETEXPOSURE;
-                               } else
-                                       retval = -EINVAL;
-                       }
-               } 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;
-                       }
-               } 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;
-                       }
-               } 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;
-                       }
-               } 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;
-                       }
-               } else if (MATCH("vl_offset_gain1")) {
-                       if (!retval)
-                               val = VALUE;
-
-                       if (!retval) {
-                               if (val <= 0xff)
-                                       new_params.vlOffset.gain1 = val;
-                               else
-                                       retval = -EINVAL;
-                       }
-                       command_flags |= COMMAND_SETVLOFFSET;
-               } else if (MATCH("vl_offset_gain2")) {
-                       if (!retval)
-                               val = VALUE;
-
-                       if (!retval) {
-                               if (val <= 0xff)
-                                       new_params.vlOffset.gain2 = val;
-                               else
-                                       retval = -EINVAL;
-                       }
-                       command_flags |= COMMAND_SETVLOFFSET;
-               } else if (MATCH("vl_offset_gain4")) {
-                       if (!retval)
-                               val = VALUE;
-
-                       if (!retval) {
-                               if (val <= 0xff)
-                                       new_params.vlOffset.gain4 = val;
-                               else
-                                       retval = -EINVAL;
-                       }
-                       command_flags |= COMMAND_SETVLOFFSET;
-               } else if (MATCH("vl_offset_gain8")) {
-                       if (!retval)
-                               val = VALUE;
-
-                       if (!retval) {
-                               if (val <= 0xff)
-                                       new_params.vlOffset.gain8 = val;
-                               else
-                                       retval = -EINVAL;
-                       }
-                       command_flags |= COMMAND_SETVLOFFSET;
-               } else if (MATCH("flicker_control")) {
-                       if (!retval && MATCH("on")) {
-                               set_flicker(&new_params, &command_flags, 1);
-                       } else if (!retval && MATCH("off")) {
-                               set_flicker(&new_params, &command_flags, 0);
-                       } else
-                               retval = -EINVAL;
-
-                       command_flags |= COMMAND_SETFLICKERCTRL;
-               } 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)
-                                       command_flags |= COMMAND_SETFLICKERCTRL;
-                       } 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)
-                                       command_flags |= COMMAND_SETFLICKERCTRL;
-                       } else
-                               retval = -EINVAL;
-               } else if (MATCH("allowable_overexposure")) {
-                       if (!retval && MATCH("auto")) {
-                               new_params.flickerControl.allowableOverExposure =
-                                       -find_over_exposure(new_params.colourParams.brightness);
-                               if(new_params.flickerControl.flickerMode != 0)
-                                       command_flags |= COMMAND_SETFLICKERCTRL;
-                       } else {
-                               if (!retval)
-                                       val = VALUE;
-
-                               if (!retval) {
-                                       if (val <= 0xff) {
-                                               new_params.flickerControl.
-                                                       allowableOverExposure = val;
-                                               if(new_params.flickerControl.flickerMode != 0)
-                                                       command_flags |= COMMAND_SETFLICKERCTRL;
-                                       } else
-                                               retval = -EINVAL;
-                               }
-                       }
-               } else if (MATCH("compression_mode")) {
-                       if (!retval && MATCH("none"))
-                               new_params.compression.mode =
-                                       CPIA_COMPRESSION_NONE;
-                       else if (!retval && MATCH("auto"))
-                               new_params.compression.mode =
-                                       CPIA_COMPRESSION_AUTO;
-                       else if (!retval && MATCH("manual"))
-                               new_params.compression.mode =
-                                       CPIA_COMPRESSION_MANUAL;
-                       else
-                               retval = -EINVAL;
-
-                       command_flags |= COMMAND_SETCOMPRESSION;
-               } else if (MATCH("decimation_enable")) {
-                       if (!retval && MATCH("off"))
-                               new_params.compression.decimation = 0;
-                       else if (!retval && MATCH("on"))
-                               new_params.compression.decimation = 1;
-                       else
-                               retval = -EINVAL;
-
-                       command_flags |= COMMAND_SETCOMPRESSION;
-               } else if (MATCH("compression_target")) {
-                       if (!retval && MATCH("quality"))
-                               new_params.compressionTarget.frTargeting =
-                                       CPIA_COMPRESSION_TARGET_QUALITY;
-                       else if (!retval && MATCH("framerate"))
-                               new_params.compressionTarget.frTargeting =
-                                       CPIA_COMPRESSION_TARGET_FRAMERATE;
-                       else
-                               retval = -EINVAL;
-
-                       command_flags |= COMMAND_SETCOMPRESSIONTARGET;
-               } 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;
-               } 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;
-               } 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;
-               } 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;
-               } 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;
-               } 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;
-               } 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;
-               } 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;
-               } 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;
-               } 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;
-               } else if (MATCH("q_diff_step_thresh")) {
-                       if (!retval)
-                               val = VALUE;
-
-                       if (!retval) {
-                               if (val <= 0xff)
-                                       new_params.compressionParams.qDiffStepThresh = 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;
-               } else if (MATCH("toplight")) {
-                       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")) {
-                       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 == '\0' && count != 1)
-                                       retval = -EINVAL;
-                               else if (*buffer != '\n' && *buffer != ';' &&
-                                        *buffer != '\0')
-                                       retval = -EINVAL;
-                               else {
-                                       --count;
-                                       ++buffer;
-                               }
-                       }
-               }
-       }
-#undef MATCH
-#undef VALUE
-#undef FIRMWARE_VERSION
-       if (!retval) {
-               if (command_flags & COMMAND_SETCOLOURPARAMS) {
-                       /* Adjust cam->vp to reflect these changes */
-                       cam->vp.brightness =
-                               new_params.colourParams.brightness*65535/100;
-                       cam->vp.contrast =
-                               new_params.colourParams.contrast*65535/100;
-                       cam->vp.colour =
-                               new_params.colourParams.saturation*65535/100;
-               }
-               if((command_flags & COMMAND_SETEXPOSURE) &&
-                  new_params.exposure.expMode == 2)
-                       cam->exposure_status = EXPOSURE_NORMAL;
-
-               memcpy(&cam->params, &new_params, sizeof(struct cam_params));
-               cam->mainsFreq = new_mains;
-               cam->cmd_queue |= command_flags;
-               retval = size;
-       } else
-               DBG("error: %d\n", retval);
-
-       mutex_unlock(&cam->param_lock);
-
-out:
-       free_page((unsigned long)page);
-       return retval;
-}
-
-static const struct file_operations cpia_proc_fops = {
-       .owner          = THIS_MODULE,
-       .open           = cpia_proc_open,
-       .read           = seq_read,
-       .llseek         = seq_lseek,
-       .release        = single_release,
-       .write          = cpia_proc_write,
-};
-
-static void create_proc_cpia_cam(struct cam_data *cam)
-{
-       struct proc_dir_entry *ent;
-
-       if (!cpia_proc_root || !cam)
-               return;
-
-       ent = proc_create_data(video_device_node_name(&cam->vdev),
-                              S_IRUGO|S_IWUSR, cpia_proc_root,
-                              &cpia_proc_fops, cam);
-       if (!ent)
-               return;
-
-       /*
-          size of the proc entry is 3736 bytes for the standard webcam;
-          the extra features of the QX3 microscope add 189 bytes.
-          (we have not yet probed the camera to see which type it is).
-       */
-       ent->size = 3736 + 189;
-       cam->proc_entry = ent;
-}
-
-static void destroy_proc_cpia_cam(struct cam_data *cam)
-{
-       if (!cam || !cam->proc_entry)
-               return;
-
-       remove_proc_entry(video_device_node_name(&cam->vdev), cpia_proc_root);
-       cam->proc_entry = NULL;
-}
-
-static void proc_cpia_create(void)
-{
-       cpia_proc_root = proc_mkdir("cpia", NULL);
-
-       if (!cpia_proc_root)
-               LOG("Unable to initialise /proc/cpia\n");
-}
-
-static void __exit proc_cpia_destroy(void)
-{
-       remove_proc_entry("cpia", NULL);
-}
-#endif /* CONFIG_PROC_FS */
-
-/* ----------------------- debug functions ---------------------- */
-
-#define printstatus(cam) \
-  DBG("%02x %02x %02x %02x %02x %02x %02x %02x\n",\
-       cam->params.status.systemState, cam->params.status.grabState, \
-       cam->params.status.streamState, cam->params.status.fatalError, \
-       cam->params.status.cmdError, cam->params.status.debugFlags, \
-       cam->params.status.vpStatus, cam->params.status.errorCode);
-
-/* ----------------------- v4l helpers -------------------------- */
-
-/* supported frame palettes and depths */
-static inline int valid_mode(u16 palette, u16 depth)
-{
-       if ((palette == VIDEO_PALETTE_YUV422 && depth == 16) ||
-           (palette == VIDEO_PALETTE_YUYV && depth == 16))
-               return 1;
-
-       if (colorspace_conv)
-               return (palette == VIDEO_PALETTE_GREY && depth == 8) ||
-                      (palette == VIDEO_PALETTE_RGB555 && depth == 16) ||
-                      (palette == VIDEO_PALETTE_RGB565 && depth == 16) ||
-                      (palette == VIDEO_PALETTE_RGB24 && depth == 24) ||
-                      (palette == VIDEO_PALETTE_RGB32 && depth == 32) ||
-                      (palette == VIDEO_PALETTE_UYVY && depth == 16);
-
-       return 0;
-}
-
-static int match_videosize( int width, int height )
-{
-       /* return the best match, where 'best' is as always
-        * the largest that is not bigger than what is requested. */
-       if (width>=352 && height>=288)
-               return VIDEOSIZE_352_288; /* CIF */
-
-       if (width>=320 && height>=240)
-               return VIDEOSIZE_320_240; /* SIF */
-
-       if (width>=288 && height>=216)
-               return VIDEOSIZE_288_216;
-
-       if (width>=256 && height>=192)
-               return VIDEOSIZE_256_192;
-
-       if (width>=224 && height>=168)
-               return VIDEOSIZE_224_168;
-
-       if (width>=192 && height>=144)
-               return VIDEOSIZE_192_144;
-
-       if (width>=176 && height>=144)
-               return VIDEOSIZE_176_144; /* QCIF */
-
-       if (width>=160 && height>=120)
-               return VIDEOSIZE_160_120; /* QSIF */
-
-       if (width>=128 && height>=96)
-               return VIDEOSIZE_128_96;
-
-       if (width>=88 && height>=72)
-               return VIDEOSIZE_88_72;
-
-       if (width>=64 && height>=48)
-               return VIDEOSIZE_64_48;
-
-       if (width>=48 && height>=48)
-               return VIDEOSIZE_48_48;
-
-       return -1;
-}
-
-/* these are the capture sizes we support */
-static void set_vw_size(struct cam_data *cam)
-{
-       /* the col/row/start/end values are the result of simple math    */
-       /* study the SetROI-command in cpia developers guide p 2-22      */
-       /* streamStartLine is set to the recommended value in the cpia   */
-       /*  developers guide p 3-37                                      */
-       switch(cam->video_size) {
-       case VIDEOSIZE_CIF:
-               cam->vw.width = 352;
-               cam->vw.height = 288;
-               cam->params.format.videoSize=VIDEOSIZE_CIF;
-               cam->params.roi.colStart=0;
-               cam->params.roi.rowStart=0;
-               cam->params.streamStartLine = 120;
-               break;
-       case VIDEOSIZE_SIF:
-               cam->vw.width = 320;
-               cam->vw.height = 240;
-               cam->params.format.videoSize=VIDEOSIZE_CIF;
-               cam->params.roi.colStart=2;
-               cam->params.roi.rowStart=6;
-               cam->params.streamStartLine = 120;
-               break;
-       case VIDEOSIZE_288_216:
-               cam->vw.width = 288;
-               cam->vw.height = 216;
-               cam->params.format.videoSize=VIDEOSIZE_CIF;
-               cam->params.roi.colStart=4;
-               cam->params.roi.rowStart=9;
-               cam->params.streamStartLine = 120;
-               break;
-       case VIDEOSIZE_256_192:
-               cam->vw.width = 256;
-               cam->vw.height = 192;
-               cam->params.format.videoSize=VIDEOSIZE_CIF;
-               cam->params.roi.colStart=6;
-               cam->params.roi.rowStart=12;
-               cam->params.streamStartLine = 120;
-               break;
-       case VIDEOSIZE_224_168:
-               cam->vw.width = 224;
-               cam->vw.height = 168;
-               cam->params.format.videoSize=VIDEOSIZE_CIF;
-               cam->params.roi.colStart=8;
-               cam->params.roi.rowStart=15;
-               cam->params.streamStartLine = 120;
-               break;
-       case VIDEOSIZE_192_144:
-               cam->vw.width = 192;
-               cam->vw.height = 144;
-               cam->params.format.videoSize=VIDEOSIZE_CIF;
-               cam->params.roi.colStart=10;
-               cam->params.roi.rowStart=18;
-               cam->params.streamStartLine = 120;
-               break;
-       case VIDEOSIZE_QCIF:
-               cam->vw.width = 176;
-               cam->vw.height = 144;
-               cam->params.format.videoSize=VIDEOSIZE_QCIF;
-               cam->params.roi.colStart=0;
-               cam->params.roi.rowStart=0;
-               cam->params.streamStartLine = 60;
-               break;
-       case VIDEOSIZE_QSIF:
-               cam->vw.width = 160;
-               cam->vw.height = 120;
-               cam->params.format.videoSize=VIDEOSIZE_QCIF;
-               cam->params.roi.colStart=1;
-               cam->params.roi.rowStart=3;
-               cam->params.streamStartLine = 60;
-               break;
-       case VIDEOSIZE_128_96:
-               cam->vw.width = 128;
-               cam->vw.height = 96;
-               cam->params.format.videoSize=VIDEOSIZE_QCIF;
-               cam->params.roi.colStart=3;
-               cam->params.roi.rowStart=6;
-               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.rowStart=9;
-               cam->params.streamStartLine = 60;
-               break;
-       case VIDEOSIZE_64_48:
-               cam->vw.width = 64;
-               cam->vw.height = 48;
-               cam->params.format.videoSize=VIDEOSIZE_QCIF;
-               cam->params.roi.colStart=7;
-               cam->params.roi.rowStart=12;
-               cam->params.streamStartLine = 60;
-               break;
-       case VIDEOSIZE_48_48:
-               cam->vw.width = 48;
-               cam->vw.height = 48;
-               cam->params.format.videoSize=VIDEOSIZE_QCIF;
-               cam->params.roi.colStart=8;
-               cam->params.roi.rowStart=6;
-               cam->params.streamStartLine = 60;
-               break;
-       default:
-               LOG("bad videosize value: %d\n", cam->video_size);
-               return;
-       }
-
-       if(cam->vc.width == 0)
-               cam->vc.width = cam->vw.width;
-       if(cam->vc.height == 0)
-               cam->vc.height = cam->vw.height;
-
-       cam->params.roi.colStart += cam->vc.x >> 3;
-       cam->params.roi.colEnd = cam->params.roi.colStart +
-                                (cam->vc.width >> 3);
-       cam->params.roi.rowStart += cam->vc.y >> 2;
-       cam->params.roi.rowEnd = cam->params.roi.rowStart +
-                                (cam->vc.height >> 2);
-
-       return;
-}
-
-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)
-               return -ENOBUFS;
-
-       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)
-{
-       int i;
-
-       rvfree(cam->frame_buf, FRAME_NUM*CPIA_MAX_FRAME_SIZE);
-       cam->frame_buf = NULL;
-       for (i=0; i < FRAME_NUM; i++)
-               cam->frame[i].data = NULL;
-
-       return 0;
-}
-
-
-static inline void free_frames(struct cpia_frame frame[FRAME_NUM])
-{
-       int i;
-
-       for (i=0; i < FRAME_NUM; i++)
-               frame[i].state = FRAME_UNUSED;
-       return;
-}
-
-/**********************************************************************
- *
- * General functions
- *
- **********************************************************************/
-/* send an arbitrary command to the camera */
-static int do_command(struct cam_data *cam, u16 command, u8 a, u8 b, u8 c, u8 d)
-{
-       int retval, datasize;
-       u8 cmd[8], data[8];
-
-       switch(command) {
-       case CPIA_COMMAND_GetCPIAVersion:
-       case CPIA_COMMAND_GetPnPID:
-       case CPIA_COMMAND_GetCameraStatus:
-       case CPIA_COMMAND_GetVPVersion:
-               datasize=8;
-               break;
-       case CPIA_COMMAND_GetColourParams:
-       case CPIA_COMMAND_GetColourBalance:
-       case CPIA_COMMAND_GetExposure:
-               mutex_lock(&cam->param_lock);
-               datasize=8;
-               break;
-       case CPIA_COMMAND_ReadMCPorts:
-       case CPIA_COMMAND_ReadVCRegs:
-               datasize = 4;
-               break;
-       default:
-               datasize=0;
-               break;
-       }
-
-       cmd[0] = command>>8;
-       cmd[1] = command&0xff;
-       cmd[2] = a;
-       cmd[3] = b;
-       cmd[4] = c;
-       cmd[5] = d;
-       cmd[6] = datasize;
-       cmd[7] = 0;
-
-       retval = cam->ops->transferCmd(cam->lowlevel_data, cmd, data);
-       if (retval) {
-               DBG("%x - failed, retval=%d\n", command, retval);
-               if (command == CPIA_COMMAND_GetColourParams ||
-                   command == CPIA_COMMAND_GetColourBalance ||
-                   command == CPIA_COMMAND_GetExposure)
-                       mutex_unlock(&cam->param_lock);
-       } else {
-               switch(command) {
-               case CPIA_COMMAND_GetCPIAVersion:
-                       cam->params.version.firmwareVersion = data[0];
-                       cam->params.version.firmwareRevision = data[1];
-                       cam->params.version.vcVersion = data[2];
-                       cam->params.version.vcRevision = data[3];
-                       break;
-               case CPIA_COMMAND_GetPnPID:
-                       cam->params.pnpID.vendor = data[0]+(((u16)data[1])<<8);
-                       cam->params.pnpID.product = data[2]+(((u16)data[3])<<8);
-                       cam->params.pnpID.deviceRevision =
-                               data[4]+(((u16)data[5])<<8);
-                       break;
-               case CPIA_COMMAND_GetCameraStatus:
-                       cam->params.status.systemState = data[0];
-                       cam->params.status.grabState = data[1];
-                       cam->params.status.streamState = data[2];
-                       cam->params.status.fatalError = data[3];
-                       cam->params.status.cmdError = data[4];
-                       cam->params.status.debugFlags = data[5];
-                       cam->params.status.vpStatus = data[6];
-                       cam->params.status.errorCode = data[7];
-                       break;
-               case CPIA_COMMAND_GetVPVersion:
-                       cam->params.vpVersion.vpVersion = data[0];
-                       cam->params.vpVersion.vpRevision = data[1];
-                       cam->params.vpVersion.cameraHeadID =
-                               data[2]+(((u16)data[3])<<8);
-                       break;
-               case CPIA_COMMAND_GetColourParams:
-                       cam->params.colourParams.brightness = data[0];
-                       cam->params.colourParams.contrast = data[1];
-                       cam->params.colourParams.saturation = data[2];
-                       mutex_unlock(&cam->param_lock);
-                       break;
-               case CPIA_COMMAND_GetColourBalance:
-                       cam->params.colourBalance.redGain = data[0];
-                       cam->params.colourBalance.greenGain = data[1];
-                       cam->params.colourBalance.blueGain = data[2];
-                       mutex_unlock(&cam->param_lock);
-                       break;
-               case CPIA_COMMAND_GetExposure:
-                       cam->params.exposure.gain = data[0];
-                       cam->params.exposure.fineExp = data[1];
-                       cam->params.exposure.coarseExpLo = data[2];
-                       cam->params.exposure.coarseExpHi = data[3];
-                       cam->params.exposure.redComp = data[4];
-                       cam->params.exposure.green1Comp = data[5];
-                       cam->params.exposure.green2Comp = data[6];
-                       cam->params.exposure.blueComp = data[7];
-                       mutex_unlock(&cam->param_lock);
-                       break;
-
-               case CPIA_COMMAND_ReadMCPorts:
-                       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;
-               }
-       }
-       return retval;
-}
-
-/* send a command  to the camera with an additional data transaction */
-static int do_command_extended(struct cam_data *cam, u16 command,
-                              u8 a, u8 b, u8 c, u8 d,
-                              u8 e, u8 f, u8 g, u8 h,
-                              u8 i, u8 j, u8 k, u8 l)
-{
-       int retval;
-       u8 cmd[8], data[8];
-
-       cmd[0] = command>>8;
-       cmd[1] = command&0xff;
-       cmd[2] = a;
-       cmd[3] = b;
-       cmd[4] = c;
-       cmd[5] = d;
-       cmd[6] = 8;
-       cmd[7] = 0;
-       data[0] = e;
-       data[1] = f;
-       data[2] = g;
-       data[3] = h;
-       data[4] = i;
-       data[5] = j;
-       data[6] = k;
-       data[7] = l;
-
-       retval = cam->ops->transferCmd(cam->lowlevel_data, cmd, data);
-       if (retval)
-               DBG("%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;
-
-       /* Odd lines use the same u and v as the previous line.
-        * Because of compression, it is necessary to get this
-        * information from the decoded image. */
-       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;
-               *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:
-               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;
-               *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;
-               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;
-               if (mmap_kludge) {
-                       *rgb++ = LIMIT(b+y);
-                       *rgb++ = LIMIT(g+y);
-                       *rgb++ = LIMIT(r+y);
-                       if(out_fmt == VIDEO_PALETTE_RGB32)
-                               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);
-                       if(out_fmt == VIDEO_PALETTE_RGB32)
-                               rgb++;
-                       *rgb++ = LIMIT(r+y1);
-                       *rgb++ = LIMIT(g+y1);
-                       *rgb = LIMIT(b+y1);
-               }
-               if(out_fmt == VIDEO_PALETTE_RGB32)
-                       return 8;
-               return 6;
-       case VIDEO_PALETTE_YUV422:
-       case VIDEO_PALETTE_YUYV:
-               y = *yuv++;
-               u = *(rgb+1-linesize);
-               y1 = *yuv;
-               v = *(rgb+3-linesize);
-               *rgb++ = y;
-               *rgb++ = u;
-               *rgb++ = y1;
-               *rgb = v;
-               return 4;
-       case VIDEO_PALETTE_UYVY:
-               u = *(rgb-linesize);
-               y = *yuv++;
-               v = *(rgb+2-linesize);
-               y1 = *yuv;
-               *rgb++ = u;
-               *rgb++ = y;
-               *rgb++ = v;
-               *rgb = y1;
-               return 4;
-       case VIDEO_PALETTE_GREY:
-               *rgb++ = *yuv++;
-               *rgb = *yuv;
-               return 2;
-       default:
-               DBG("Empty: %d\n", out_fmt);
-               return 0;
-       }
-}
-
-
-static int yuvconvert(unsigned char *yuv, unsigned char *rgb, int out_fmt,
-                     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) {
-                       u = *yuv++ - 128;
-                       y = (*yuv++ - 16) * 76310;
-                       v = *yuv++ - 128;
-                       y1 = (*yuv - 16) * 76310;
-               } else {
-                       y = (*yuv++ - 16) * 76310;
-                       u = *yuv++ - 128;
-                       y1 = (*yuv++ - 16) * 76310;
-                       v = *yuv - 128;
-               }
-               r = 104635 * v;
-               g = -25690 * u + -53294 * v;
-               b = 132278 * u;
-               break;
-       default:
-               y = *yuv++;
-               u = *yuv++;
-               y1 = *yuv++;
-               v = *yuv;
-               /* Just to avoid compiler warnings */
-               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;
-       }
-}
-
-static int skipcount(int count, int fmt)
-{
-       switch(fmt) {
-       case VIDEO_PALETTE_GREY:
-               return count;
-       case VIDEO_PALETTE_RGB555:
-       case VIDEO_PALETTE_RGB565:
-       case VIDEO_PALETTE_YUV422:
-       case VIDEO_PALETTE_YUYV:
-       case VIDEO_PALETTE_UYVY:
-               return 2*count;
-       case VIDEO_PALETTE_RGB24:
-               return 3*count;
-       case VIDEO_PALETTE_RGB32:
-               return 4*count;
-       default:
-               return 0;
-       }
-}
-
-static int parse_picture(struct cam_data *cam, int size)
-{
-       u8 *obuf, *ibuf, *end_obuf;
-       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 */
-       mutex_lock(&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)) {
-               LOG("header not found\n");
-               mutex_unlock(&cam->param_lock);
-               return -1;
-       }
-
-       if ((ibuf[16] != VIDEOSIZE_QCIF) && (ibuf[16] != VIDEOSIZE_CIF)) {
-               LOG("wrong video size\n");
-               mutex_unlock(&cam->param_lock);
-               return -1;
-       }
-
-       if (ibuf[17] != SUBSAMPLE_420 && ibuf[17] != SUBSAMPLE_422) {
-               LOG("illegal subtype %d\n",ibuf[17]);
-               mutex_unlock(&cam->param_lock);
-               return -1;
-       }
-       subsample_422 = ibuf[17] == SUBSAMPLE_422;
-
-       if (ibuf[18] != YUVORDER_YUYV && ibuf[18] != YUVORDER_UYVY) {
-               LOG("illegal yuvorder %d\n",ibuf[18]);
-               mutex_unlock(&cam->param_lock);
-               return -1;
-       }
-       in_uyvy = ibuf[18] == YUVORDER_UYVY;
-
-       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");
-               mutex_unlock(&cam->param_lock);
-               return -1;
-       }
-       cols = 8*(ibuf[25] - ibuf[24]);
-       rows = 4*(ibuf[27] - ibuf[26]);
-
-
-       if ((ibuf[28] != NOT_COMPRESSED) && (ibuf[28] != COMPRESSED)) {
-               LOG("illegal compression %d\n",ibuf[28]);
-               mutex_unlock(&cam->param_lock);
-               return -1;
-       }
-       compressed = (ibuf[28] == COMPRESSED);
-
-       if (ibuf[29] != NO_DECIMATION && ibuf[29] != DECIMATION_ENAB) {
-               LOG("illegal decimation %d\n",ibuf[29]);
-               mutex_unlock(&cam->param_lock);
-               return -1;
-       }
-       decimation = (ibuf[29] == DECIMATION_ENAB);
-
-       cam->params.yuvThreshold.yThreshold = ibuf[30];
-       cam->params.yuvThreshold.uvThreshold = ibuf[31];
-       cam->params.status.systemState = ibuf[32];
-       cam->params.status.grabState = ibuf[33];
-       cam->params.status.streamState = ibuf[34];
-       cam->params.status.fatalError = ibuf[35];
-       cam->params.status.cmdError = ibuf[36];
-       cam->params.status.debugFlags = ibuf[37];
-       cam->params.status.vpStatus = ibuf[38];
-       cam->params.status.errorCode = ibuf[39];
-       cam->fps = ibuf[41];
-       mutex_unlock(&cam->param_lock);
-
-       linesize = skipcount(cols, out_fmt);
-       ibuf += FRAME_HEADER_SIZE;
-       size -= FRAME_HEADER_SIZE;
-       ll = ibuf[0] | (ibuf[1] << 8);
-       ibuf += 2;
-       even_line = 1;
-
-       while (size > 0) {
-               size -= (ll+2);
-               if (size < 0) {
-                       LOG("Insufficient data in buffer\n");
-                       return -1;
-               }
-
-               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*/
-                               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) {
-                               DBG("EOL not found giving up after %d/%d"
-                                   " bytes\n", origsize-size, origsize);
-                               return -1;
-                       }
-
-                       ++ibuf; /* skip over EOL */
-
-                       if ((size > 3) && (ibuf[0] == EOI) && (ibuf[1] == EOI) &&
-                          (ibuf[2] == EOI) && (ibuf[3] == EOI)) {
-                               size -= 4;
-                               break;
-                       }
-
-                       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);
-                       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;
-
-       return cam->decompressed_frame.count;
-}
-
-/* InitStreamCap wrapper to select correct start line */
-static inline int init_stream_cap(struct cam_data *cam)
-{
-       return do_command(cam, CPIA_COMMAND_InitStreamCap,
-                         0, cam->params.streamStartLine, 0, 0);
-}
-
-
-/*  find_over_exposure
- *    Finds a suitable value of OverExposure for use with SetFlickerCtrl
- *    Some calculation is required because this value changes with the brightness
- *    set with SetColourParameters
- *
- *  Parameters: Brightness  -  last brightness value set with SetColourParameters
- *
- *  Returns: OverExposure value to use with SetFlickerCtrl
- */
-#define FLICKER_MAX_EXPOSURE                    250
-#define FLICKER_ALLOWABLE_OVER_EXPOSURE         146
-#define FLICKER_BRIGHTNESS_CONSTANT             59
-static int find_over_exposure(int brightness)
-{
-       int MaxAllowableOverExposure, OverExposure;
-
-       MaxAllowableOverExposure = FLICKER_MAX_EXPOSURE - brightness -
-                                  FLICKER_BRIGHTNESS_CONSTANT;
-
-       if (MaxAllowableOverExposure < FLICKER_ALLOWABLE_OVER_EXPOSURE) {
-               OverExposure = MaxAllowableOverExposure;
-       } else {
-               OverExposure = FLICKER_ALLOWABLE_OVER_EXPOSURE;
-       }
-
-       return OverExposure;
-}
-#undef FLICKER_MAX_EXPOSURE
-#undef FLICKER_ALLOWABLE_OVER_EXPOSURE
-#undef FLICKER_BRIGHTNESS_CONSTANT
-
-/* update various camera modes and settings */
-static void dispatch_commands(struct cam_data *cam)
-{
-       mutex_lock(&cam->param_lock);
-       if (cam->cmd_queue==COMMAND_NONE) {
-               mutex_unlock(&cam->param_lock);
-               return;
-       }
-       DEB_BYTE(cam->cmd_queue);
-       DEB_BYTE(cam->cmd_queue>>8);
-       if (cam->cmd_queue & COMMAND_SETFORMAT) {
-               do_command(cam, CPIA_COMMAND_SetFormat,
-                          cam->params.format.videoSize,
-                          cam->params.format.subSample,
-                          cam->params.format.yuvOrder, 0);
-               do_command(cam, CPIA_COMMAND_SetROI,
-                          cam->params.roi.colStart, cam->params.roi.colEnd,
-                          cam->params.roi.rowStart, cam->params.roi.rowEnd);
-               cam->first_frame = 1;
-       }
-
-       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_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_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_SETEXPOSURE) {
-               do_command_extended(cam, CPIA_COMMAND_SetExposure,
-                                   cam->params.exposure.gainMode,
-                                   1,
-                                   cam->params.exposure.compMode,
-                                   cam->params.exposure.centreWeight,
-                                   cam->params.exposure.gain,
-                                   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);
-               if(cam->params.exposure.expMode != 1) {
-                       do_command_extended(cam, CPIA_COMMAND_SetExposure,
-                                           0,
-                                           cam->params.exposure.expMode,
-                                           0, 0,
-                                           cam->params.exposure.gain,
-                                           cam->params.exposure.fineExp,
-                                           cam->params.exposure.coarseExpLo,
-                                           cam->params.exposure.coarseExpHi,
-                                           0, 0, 0, 0);
-               }
-       }
-
-       if (cam->cmd_queue & COMMAND_SETCOLOURBALANCE) {
-               if (cam->params.colourBalance.balanceMode == 1) {
-                       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 == 2) {
-                       do_command(cam, CPIA_COMMAND_SetColourBalance,
-                                  2, 0, 0, 0);
-               }
-               if (cam->params.colourBalance.balanceMode == 3) {
-                       do_command(cam, CPIA_COMMAND_SetColourBalance,
-                                  3, 0, 0, 0);
-               }
-       }
-
-       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)
-               do_command(cam, CPIA_COMMAND_SetYUVThresh,
-                          cam->params.yuvThreshold.yThreshold,
-                          cam->params.yuvThreshold.uvThreshold, 0, 0);
-
-       if (cam->cmd_queue & COMMAND_SETCOMPRESSIONPARAMS)
-               do_command_extended(cam, CPIA_COMMAND_SetCompressionParams,
-                           0, 0, 0, 0,
-                           cam->params.compressionParams.hysteresis,
-                           cam->params.compressionParams.threshMax,
-                           cam->params.compressionParams.smallStep,
-                           cam->params.compressionParams.largeStep,
-                           cam->params.compressionParams.decimationHysteresis,
-                           cam->params.compressionParams.frDiffStepThresh,
-                           cam->params.compressionParams.qDiffStepThresh,
-                           cam->params.compressionParams.decimationThreshMod);
-
-       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_SETSENSORFPS)
-               do_command(cam, CPIA_COMMAND_SetSensorFPS,
-                          cam->params.sensorFps.divisor,
-                          cam->params.sensorFps.baserate, 0, 0);
-
-       if (cam->cmd_queue & COMMAND_SETFLICKERCTRL)
-               do_command(cam, CPIA_COMMAND_SetFlickerCtrl,
-                          cam->params.flickerControl.flickerMode,
-                          cam->params.flickerControl.coarseJump,
-                          abs(cam->params.flickerControl.allowableOverExposure),
-                          0);
-
-       if (cam->cmd_queue & COMMAND_SETECPTIMING)
-               do_command(cam, CPIA_COMMAND_SetECPTiming,
-                          cam->params.ecpTiming, 0, 0, 0);
-
-       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_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);
-         }
-
-       cam->cmd_queue = COMMAND_NONE;
-       mutex_unlock(&cam->param_lock);
-       return;
-}
-
-
-
-static void set_flicker(struct cam_params *params, volatile u32 *command_flags,
-                       int on)
-{
-       /* Everything in here is from the Windows driver */
-#define FIRMWARE_VERSION(x,y) (params->version.firmwareVersion == (x) && \
-                              params->version.firmwareRevision == (y))
-/* define for compgain calculation */
-#if 0
-#define COMPGAIN(base, curexp, newexp) \
-    (u8) ((((float) base - 128.0) * ((float) curexp / (float) newexp)) + 128.5)
-#define EXP_FROM_COMP(basecomp, curcomp, curexp) \
-    (u16)((float)curexp * (float)(u8)(curcomp + 128) / (float)(u8)(basecomp - 128))
-#else
-  /* equivalent functions without floating point math */
-#define COMPGAIN(base, curexp, newexp) \
-    (u8)(128 + (((u32)(2*(base-128)*curexp + newexp)) / (2* newexp)) )
-#define EXP_FROM_COMP(basecomp, curcomp, curexp) \
-     (u16)(((u32)(curexp * (u8)(curcomp + 128)) / (u8)(basecomp - 128)))
-#endif
-
-
-       int currentexp = params->exposure.coarseExpLo +
-                        params->exposure.coarseExpHi*256;
-       int startexp;
-       if (on) {
-               int cj = params->flickerControl.coarseJump;
-               params->flickerControl.flickerMode = 1;
-               params->flickerControl.disabled = 0;
-               if(params->exposure.expMode != 2)
-                       *command_flags |= COMMAND_SETEXPOSURE;
-               params->exposure.expMode = 2;
-               currentexp = currentexp << params->exposure.gain;
-               params->exposure.gain = 0;
-               /* round down current exposure to nearest value */
-               startexp = (currentexp + ROUND_UP_EXP_FOR_FLICKER) / cj;
-               if(startexp < 1)
-                       startexp = 1;
-               startexp = (startexp * cj) - 1;
-               if(FIRMWARE_VERSION(1,2))
-                       while(startexp > MAX_EXP_102)
-                               startexp -= cj;
-               else
-                       while(startexp > MAX_EXP)
-                               startexp -= cj;
-               params->exposure.coarseExpLo = startexp & 0xff;
-               params->exposure.coarseExpHi = startexp >> 8;
-               if (currentexp > startexp) {
-                       if (currentexp > (2 * startexp))
-                               currentexp = 2 * startexp;
-                       params->exposure.redComp = COMPGAIN (COMP_RED, currentexp, startexp);
-                       params->exposure.green1Comp = COMPGAIN (COMP_GREEN1, currentexp, startexp);
-                       params->exposure.green2Comp = COMPGAIN (COMP_GREEN2, currentexp, startexp);
-                       params->exposure.blueComp = COMPGAIN (COMP_BLUE, currentexp, startexp);
-               } else {
-                       params->exposure.redComp = COMP_RED;
-                       params->exposure.green1Comp = COMP_GREEN1;
-                       params->exposure.green2Comp = COMP_GREEN2;
-                       params->exposure.blueComp = COMP_BLUE;
-               }
-               if(FIRMWARE_VERSION(1,2))
-                       params->exposure.compMode = 0;
-               else
-                       params->exposure.compMode = 1;
-
-               params->apcor.gain1 = 0x18;
-               params->apcor.gain2 = 0x18;
-               params->apcor.gain4 = 0x16;
-               params->apcor.gain8 = 0x14;
-               *command_flags |= COMMAND_SETAPCOR;
-       } else {
-               params->flickerControl.flickerMode = 0;
-               params->flickerControl.disabled = 1;
-               /* Coarse = average of equivalent coarse for each comp channel */
-               startexp = EXP_FROM_COMP(COMP_RED, params->exposure.redComp, currentexp);
-               startexp += EXP_FROM_COMP(COMP_GREEN1, params->exposure.green1Comp, currentexp);
-               startexp += EXP_FROM_COMP(COMP_GREEN2, params->exposure.green2Comp, currentexp);
-               startexp += EXP_FROM_COMP(COMP_BLUE, params->exposure.blueComp, currentexp);
-               startexp = startexp >> 2;
-               while(startexp > MAX_EXP &&
-                     params->exposure.gain < params->exposure.gainMode-1) {
-                       startexp = startexp >> 1;
-                       ++params->exposure.gain;
-               }
-               if(FIRMWARE_VERSION(1,2) && startexp > MAX_EXP_102)
-                       startexp = MAX_EXP_102;
-               if(startexp > MAX_EXP)
-                       startexp = MAX_EXP;
-               params->exposure.coarseExpLo = startexp&0xff;
-               params->exposure.coarseExpHi = startexp >> 8;
-               params->exposure.redComp = COMP_RED;
-               params->exposure.green1Comp = COMP_GREEN1;
-               params->exposure.green2Comp = COMP_GREEN2;
-               params->exposure.blueComp = COMP_BLUE;
-               params->exposure.compMode = 1;
-               *command_flags |= COMMAND_SETEXPOSURE;
-               params->apcor.gain1 = 0x18;
-               params->apcor.gain2 = 0x16;
-               params->apcor.gain4 = 0x24;
-               params->apcor.gain8 = 0x34;
-               *command_flags |= COMMAND_SETAPCOR;
-       }
-       params->vlOffset.gain1 = 20;
-       params->vlOffset.gain2 = 24;
-       params->vlOffset.gain4 = 26;
-       params->vlOffset.gain8 = 26;
-       *command_flags |= COMMAND_SETVLOFFSET;
-#undef FIRMWARE_VERSION
-#undef EXP_FROM_COMP
-#undef COMPGAIN
-}
-
-#define FIRMWARE_VERSION(x,y) (cam->params.version.firmwareVersion == (x) && \
-                              cam->params.version.firmwareRevision == (y))
-/* monitor the exposure and adjust the sensor frame rate if needed */
-static void monitor_exposure(struct cam_data *cam)
-{
-       u8 exp_acc, bcomp, gain, coarseL, cmd[8], data[8];
-       int retval, light_exp, dark_exp, very_dark_exp;
-       int old_exposure, new_exposure, framerate;
-
-       /* get necessary stats and register settings from camera */
-       /* do_command can't handle this, so do it ourselves */
-       cmd[0] = CPIA_COMMAND_ReadVPRegs>>8;
-       cmd[1] = CPIA_COMMAND_ReadVPRegs&0xff;
-       cmd[2] = 30;
-       cmd[3] = 4;
-       cmd[4] = 9;
-       cmd[5] = 8;
-       cmd[6] = 8;
-       cmd[7] = 0;
-       retval = cam->ops->transferCmd(cam->lowlevel_data, cmd, data);
-       if (retval) {
-               LOG("ReadVPRegs(30,4,9,8) - failed, retval=%d\n",
-                   retval);
-               return;
-       }
-       exp_acc = data[0];
-       bcomp = data[1];
-       gain = data[2];
-       coarseL = data[3];
-
-       mutex_lock(&cam->param_lock);
-       light_exp = cam->params.colourParams.brightness +
-                   TC - 50 + EXP_ACC_LIGHT;
-       if(light_exp > 255)
-               light_exp = 255;
-       dark_exp = cam->params.colourParams.brightness +
-                  TC - 50 - EXP_ACC_DARK;
-       if(dark_exp < 0)
-               dark_exp = 0;
-       very_dark_exp = dark_exp/2;
-
-       old_exposure = cam->params.exposure.coarseExpHi * 256 +
-                      cam->params.exposure.coarseExpLo;
-
-       if(!cam->params.flickerControl.disabled) {
-               /* Flicker control on */
-               int max_comp = FIRMWARE_VERSION(1,2) ? MAX_COMP : HIGH_COMP_102;
-               bcomp += 128;   /* decode */
-               if(bcomp >= max_comp && exp_acc < dark_exp) {
-                       /* dark */
-                       if(exp_acc < very_dark_exp) {
-                               /* very dark */
-                               if(cam->exposure_status == EXPOSURE_VERY_DARK)
-                                       ++cam->exposure_count;
-                               else {
-                                       cam->exposure_status = EXPOSURE_VERY_DARK;
-                                       cam->exposure_count = 1;
-                               }
-                       } else {
-                               /* just dark */
-                               if(cam->exposure_status == EXPOSURE_DARK)
-                                       ++cam->exposure_count;
-                               else {
-                                       cam->exposure_status = EXPOSURE_DARK;
-                                       cam->exposure_count = 1;
-                               }
-                       }
-               } else if(old_exposure <= LOW_EXP || exp_acc > light_exp) {
-                       /* light */
-                       if(old_exposure <= VERY_LOW_EXP) {
-                               /* very light */
-                               if(cam->exposure_status == EXPOSURE_VERY_LIGHT)
-                                       ++cam->exposure_count;
-                               else {
-                                       cam->exposure_status = EXPOSURE_VERY_LIGHT;
-                                       cam->exposure_count = 1;
-                               }
-                       } else {
-                               /* just light */
-                               if(cam->exposure_status == EXPOSURE_LIGHT)
-                                       ++cam->exposure_count;
-                               else {
-                                       cam->exposure_status = EXPOSURE_LIGHT;
-                                       cam->exposure_count = 1;
-                               }
-                       }
-               } else {
-                       /* not dark or light */
-                       cam->exposure_status = EXPOSURE_NORMAL;
-               }
-       } else {
-               /* Flicker control off */
-               if(old_exposure >= MAX_EXP && exp_acc < dark_exp) {
-                       /* dark */
-                       if(exp_acc < very_dark_exp) {
-                               /* very dark */
-                               if(cam->exposure_status == EXPOSURE_VERY_DARK)
-                                       ++cam->exposure_count;
-                               else {
-                                       cam->exposure_status = EXPOSURE_VERY_DARK;
-                                       cam->exposure_count = 1;
-                               }
-                       } else {
-                               /* just dark */
-                               if(cam->exposure_status == EXPOSURE_DARK)
-                                       ++cam->exposure_count;
-                               else {
-                                       cam->exposure_status = EXPOSURE_DARK;
-                                       cam->exposure_count = 1;
-                               }
-                       }
-               } else if(old_exposure <= LOW_EXP || exp_acc > light_exp) {
-                       /* light */
-                       if(old_exposure <= VERY_LOW_EXP) {
-                               /* very light */
-                               if(cam->exposure_status == EXPOSURE_VERY_LIGHT)
-                                       ++cam->exposure_count;
-                               else {
-                                       cam->exposure_status = EXPOSURE_VERY_LIGHT;
-                                       cam->exposure_count = 1;
-                               }
-                       } else {
-                               /* just light */
-                               if(cam->exposure_status == EXPOSURE_LIGHT)
-                                       ++cam->exposure_count;
-                               else {
-                                       cam->exposure_status = EXPOSURE_LIGHT;
-                                       cam->exposure_count = 1;
-                               }
-                       }
-               } else {
-                       /* not dark or light */
-                       cam->exposure_status = EXPOSURE_NORMAL;
-               }
-       }
-
-       framerate = cam->fps;
-       if(framerate > 30 || framerate < 1)
-               framerate = 1;
-
-       if(!cam->params.flickerControl.disabled) {
-               /* Flicker control on */
-               if((cam->exposure_status == EXPOSURE_VERY_DARK ||
-                   cam->exposure_status == EXPOSURE_DARK) &&
-                  cam->exposure_count >= DARK_TIME*framerate &&
-                  cam->params.sensorFps.divisor < 3) {
-
-                       /* dark for too long */
-                       ++cam->params.sensorFps.divisor;
-                       cam->cmd_queue |= COMMAND_SETSENSORFPS;
-
-                       cam->params.flickerControl.coarseJump =
-                               flicker_jumps[cam->mainsFreq]
-                                            [cam->params.sensorFps.baserate]
-                                            [cam->params.sensorFps.divisor];
-                       cam->cmd_queue |= COMMAND_SETFLICKERCTRL;
-
-                       new_exposure = cam->params.flickerControl.coarseJump-1;
-                       while(new_exposure < old_exposure/2)
-                               new_exposure += cam->params.flickerControl.coarseJump;
-                       cam->params.exposure.coarseExpLo = new_exposure & 0xff;
-                       cam->params.exposure.coarseExpHi = new_exposure >> 8;
-                       cam->cmd_queue |= COMMAND_SETEXPOSURE;
-                       cam->exposure_status = EXPOSURE_NORMAL;
-                       LOG("Automatically decreasing sensor_fps\n");
-
-               } else if((cam->exposure_status == EXPOSURE_VERY_LIGHT ||
-                   cam->exposure_status == EXPOSURE_LIGHT) &&
-                  cam->exposure_count >= LIGHT_TIME*framerate &&
-                  cam->params.sensorFps.divisor > 0) {
-
-                       /* light for too long */
-                       int max_exp = FIRMWARE_VERSION(1,2) ? MAX_EXP_102 : MAX_EXP ;
-
-                       --cam->params.sensorFps.divisor;
-                       cam->cmd_queue |= COMMAND_SETSENSORFPS;
-
-                       cam->params.flickerControl.coarseJump =
-                               flicker_jumps[cam->mainsFreq]
-                                            [cam->params.sensorFps.baserate]
-                                            [cam->params.sensorFps.divisor];
-                       cam->cmd_queue |= COMMAND_SETFLICKERCTRL;
-
-                       new_exposure = cam->params.flickerControl.coarseJump-1;
-                       while(new_exposure < 2*old_exposure &&
-                             new_exposure+
-                             cam->params.flickerControl.coarseJump < max_exp)
-                               new_exposure += cam->params.flickerControl.coarseJump;
-                       cam->params.exposure.coarseExpLo = new_exposure & 0xff;
-                       cam->params.exposure.coarseExpHi = new_exposure >> 8;
-                       cam->cmd_queue |= COMMAND_SETEXPOSURE;
-                       cam->exposure_status = EXPOSURE_NORMAL;
-                       LOG("Automatically increasing sensor_fps\n");
-               }
-       } else {
-               /* Flicker control off */
-               if((cam->exposure_status == EXPOSURE_VERY_DARK ||
-                   cam->exposure_status == EXPOSURE_DARK) &&
-                  cam->exposure_count >= DARK_TIME*framerate &&
-                  cam->params.sensorFps.divisor < 3) {
-
-                       /* dark for too long */
-                       ++cam->params.sensorFps.divisor;
-                       cam->cmd_queue |= COMMAND_SETSENSORFPS;
-
-                       if(cam->params.exposure.gain > 0) {
-                               --cam->params.exposure.gain;
-                               cam->cmd_queue |= COMMAND_SETEXPOSURE;
-                       }
-                       cam->exposure_status = EXPOSURE_NORMAL;
-                       LOG("Automatically decreasing sensor_fps\n");
-
-               } else if((cam->exposure_status == EXPOSURE_VERY_LIGHT ||
-                   cam->exposure_status == EXPOSURE_LIGHT) &&
-                  cam->exposure_count >= LIGHT_TIME*framerate &&
-                  cam->params.sensorFps.divisor > 0) {
-
-                       /* light for too long */
-                       --cam->params.sensorFps.divisor;
-                       cam->cmd_queue |= COMMAND_SETSENSORFPS;
-
-                       if(cam->params.exposure.gain <
-                          cam->params.exposure.gainMode-1) {
-                               ++cam->params.exposure.gain;
-                               cam->cmd_queue |= COMMAND_SETEXPOSURE;
-                       }
-                       cam->exposure_status = EXPOSURE_NORMAL;
-                       LOG("Automatically increasing sensor_fps\n");
-               }
-       }
-       mutex_unlock(&cam->param_lock);
-}
-
-/*-----------------------------------------------------------------*/
-/* if flicker is switched off, this function switches it back on.It checks,
-   however, that conditions are suitable before restarting it.
-   This should only be called for firmware version 1.2.
-
-   It also adjust the colour balance when an exposure step is detected - as
-   long as flicker is running
-*/
-static void restart_flicker(struct cam_data *cam)
-{
-       int cam_exposure, old_exp;
-       if(!FIRMWARE_VERSION(1,2))
-               return;
-       mutex_lock(&cam->param_lock);
-       if(cam->params.flickerControl.flickerMode == 0 ||
-          cam->raw_image[39] == 0) {
-               mutex_unlock(&cam->param_lock);
-               return;
-       }
-       cam_exposure = cam->raw_image[39]*2;
-       old_exp = cam->params.exposure.coarseExpLo +
-                 cam->params.exposure.coarseExpHi*256;
-       /*
-         see how far away camera exposure is from a valid
-         flicker exposure value
-       */
-       cam_exposure %= cam->params.flickerControl.coarseJump;
-       if(!cam->params.flickerControl.disabled &&
-          cam_exposure <= cam->params.flickerControl.coarseJump - 3) {
-               /* Flicker control auto-disabled */
-               cam->params.flickerControl.disabled = 1;
-       }
-
-       if(cam->params.flickerControl.disabled &&
-          cam->params.flickerControl.flickerMode &&
-          old_exp > cam->params.flickerControl.coarseJump +
-                    ROUND_UP_EXP_FOR_FLICKER) {
-               /* exposure is now high enough to switch
-                  flicker control back on */
-               set_flicker(&cam->params, &cam->cmd_queue, 1);
-               if((cam->cmd_queue & COMMAND_SETEXPOSURE) &&
-                  cam->params.exposure.expMode == 2)
-                       cam->exposure_status = EXPOSURE_NORMAL;
-
-       }
-       mutex_unlock(&cam->param_lock);
-}
-#undef FIRMWARE_VERSION
-
-static int clear_stall(struct cam_data *cam)
-{
-       /* FIXME: Does this actually work? */
-       LOG("Clearing stall\n");
-
-       cam->ops->streamRead(cam->lowlevel_data, cam->raw_image, 0);
-       do_command(cam, CPIA_COMMAND_GetCameraStatus,0,0,0,0);
-       return cam->params.status.streamState != STREAM_PAUSED;
-}
-
-/* kernel thread function to read image from camera */
-static int fetch_frame(void *data)
-{
-       int image_size, retry;
-       struct cam_data *cam = (struct cam_data *)data;
-       unsigned long oldjif, rate, diff;
-
-       /* 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);
-                       /* Trial & error - Discarding a frame prevents the
-                          first frame from having an error in the data. */
-                       do_command(cam, CPIA_COMMAND_DiscardFrame, 0, 0, 0, 0);
-               }
-
-               /* init camera upload */
-               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 */
-                       int count = 0;
-                       do_command(cam, CPIA_COMMAND_GetCameraStatus,0,0,0,0);
-                       while (cam->params.status.streamState != STREAM_READY) {
-                               if(++count > READY_TIMEOUT)
-                                       break;
-                               if(cam->params.status.streamState ==
-                                  STREAM_PAUSED) {
-                                       /* Bad news */
-                                       if(!clear_stall(cam))
-                                               return -EIO;
-                               }
-
-                               cond_resched();
-
-                               /* sleep for 10 ms, hopefully ;) */
-                               msleep_interruptible(10);
-                               if (signal_pending(current))
-                                       return -EINTR;
-
-                               do_command(cam, CPIA_COMMAND_GetCameraStatus,
-                                          0, 0, 0, 0);
-                       }
-                       if(cam->params.status.streamState != STREAM_READY) {
-                               continue;
-                       }
-               }
-
-               cond_resched();
-
-               /* grab image from camera */
-               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 */
-
-               /* Switch flicker control back on if it got turned off */
-               restart_flicker(cam);
-
-               /* If AEC is enabled, monitor the exposure and
-                  adjust the sensor frame rate if needed */
-               if(cam->params.exposure.expMode == 2)
-                       monitor_exposure(cam);
-
-               /* camera idle now so dispatch queued commands */
-               dispatch_commands(cam);
-
-               /* Update our knowledge of the camera state */
-               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);
-
-               /* decompress and convert image to by copying it from
-                * raw_image to decompressed_frame
-                */
-
-               cond_resched();
-
-               cam->image_size = parse_picture(cam, image_size);
-               if (cam->image_size <= 0) {
-                       DBG("parse_picture failed %d\n", cam->image_size);
-                       if(cam->params.compression.mode !=
-                          CPIA_COMPRESSION_NONE) {
-                               /* Compression may not work right if we
-                                  had a bad frame, get the next one
-                                  uncompressed. */
-                               cam->first_frame = 1;
-                               do_command(cam, CPIA_COMMAND_SetGrabMode,
-                                          CPIA_GRAB_SINGLE, 0, 0, 0);
-                               /* FIXME: Trial & error - need up to 70ms for
-                                  the grab mode change to complete ? */
-                               msleep_interruptible(70);
-                               if (signal_pending(current))
-                                       return -EINTR;
-                       }
-               } 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 (cam->first_frame) {
-                       cam->first_frame = 0;
-                       do_command(cam, CPIA_COMMAND_SetCompression,
-                                  cam->params.compression.mode,
-                                  cam->params.compression.decimation, 0, 0);
-
-                       /* Switch from single-grab to continuous grab */
-                       do_command(cam, CPIA_COMMAND_SetGrabMode,
-                                  CPIA_GRAB_CONTINUOUS, 0, 0, 0);
-               }
-               return 0;
-       }
-       return -EIO;
-}
-
-static int capture_frame(struct cam_data *cam, struct video_mmap *vm)
-{
-       if (!cam->frame_buf) {
-               /* we do lazy allocation */
-               int err;
-               if ((err = allocate_frame_buf(cam)))
-                       return err;
-       }
-
-       cam->curframe = vm->frame;
-       cam->frame[cam->curframe].state = FRAME_READY;
-       return fetch_frame(cam);
-}
-
-static int goto_high_power(struct cam_data *cam)
-{
-       if (do_command(cam, CPIA_COMMAND_GotoHiPower, 0, 0, 0, 0))
-               return -EIO;
-       msleep_interruptible(40);       /* windows driver does it too */
-       if(signal_pending(current))
-               return -EINTR;
-       if (do_command(cam, CPIA_COMMAND_GetCameraStatus, 0, 0, 0, 0))
-               return -EIO;
-       if (cam->params.status.systemState == HI_POWER_STATE) {
-               DBG("camera now in HIGH power state\n");
-               return 0;
-       }
-       printstatus(cam);
-       return -EIO;
-}
-
-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) {
-               DBG("camera now in LOW power state\n");
-               return 0;
-       }
-       printstatus(cam);
-       return -1;
-}
-
-static void save_camera_state(struct cam_data *cam)
-{
-       if(!(cam->cmd_queue & COMMAND_SETCOLOURBALANCE))
-               do_command(cam, CPIA_COMMAND_GetColourBalance, 0, 0, 0, 0);
-       if(!(cam->cmd_queue & COMMAND_SETEXPOSURE))
-               do_command(cam, CPIA_COMMAND_GetExposure, 0, 0, 0, 0);
-
-       DBG("%d/%d/%d/%d/%d/%d/%d/%d\n",
-            cam->params.exposure.gain,
-            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);
-       DBG("%d/%d/%d\n",
-            cam->params.colourBalance.redGain,
-            cam->params.colourBalance.greenGain,
-            cam->params.colourBalance.blueGain);
-}
-
-static int set_camera_state(struct cam_data *cam)
-{
-       cam->cmd_queue = COMMAND_SETCOMPRESSION |
-                        COMMAND_SETCOMPRESSIONTARGET |
-                        COMMAND_SETCOLOURPARAMS |
-                        COMMAND_SETFORMAT |
-                        COMMAND_SETYUVTHRESH |
-                        COMMAND_SETECPTIMING |
-                        COMMAND_SETCOMPRESSIONPARAMS |
-                        COMMAND_SETEXPOSURE |
-                        COMMAND_SETCOLOURBALANCE |
-                        COMMAND_SETSENSORFPS |
-                        COMMAND_SETAPCOR |
-                        COMMAND_SETFLICKERCTRL |
-                        COMMAND_SETVLOFFSET;
-
-       do_command(cam, CPIA_COMMAND_SetGrabMode, CPIA_GRAB_SINGLE,0,0,0);
-       dispatch_commands(cam);
-
-       /* Wait 6 frames for the sensor to get all settings and
-          AEC/ACB to settle */
-       msleep_interruptible(6*(cam->params.sensorFps.baserate ? 33 : 40) *
-                              (1 << cam->params.sensorFps.divisor) + 10);
-
-       if(signal_pending(current))
-               return -EINTR;
-
-       save_camera_state(cam);
-
-       return 0;
-}
-
-static void get_version_information(struct cam_data *cam)
-{
-       /* GetCPIAVersion */
-       do_command(cam, CPIA_COMMAND_GetCPIAVersion, 0, 0, 0, 0);
-
-       /* GetPnPID */
-       do_command(cam, CPIA_COMMAND_GetPnPID, 0, 0, 0, 0);
-}
-
-/* initialize camera */
-static int reset_camera(struct cam_data *cam)
-{
-       int err;
-       /* Start the camera in low power mode */
-       if (goto_low_power(cam)) {
-               if (cam->params.status.systemState != WARM_BOOT_STATE)
-                       return -ENODEV;
-
-               /* FIXME: this is just dirty trial and error */
-               err = goto_high_power(cam);
-               if(err)
-                       return err;
-               do_command(cam, CPIA_COMMAND_DiscardFrame, 0, 0, 0, 0);
-               if (goto_low_power(cam))
-                       return -ENODEV;
-       }
-
-       /* procedure described in developer's guide p3-28 */
-
-       /* Check the firmware version. */
-       cam->params.version.firmwareVersion = 0;
-       get_version_information(cam);
-       if (cam->params.version.firmwareVersion != 1)
-               return -ENODEV;
-
-       /* A bug in firmware 1-02 limits gainMode to 2 */
-       if(cam->params.version.firmwareRevision <= 2 &&
-          cam->params.exposure.gainMode > 2) {
-               cam->params.exposure.gainMode = 2;
-       }
-
-       /* 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) */
-
-       /* Set streamState before transition to high power to avoid bug
-        * in firmware 1-02 */
-       do_command(cam, CPIA_COMMAND_ModifyCameraStatus, STREAMSTATE, 0,
-                  STREAM_NOT_READY, 0);
-
-       /* GotoHiPower */
-       err = goto_high_power(cam);
-       if (err)
-               return err;
-
-       /* Check the camera status */
-       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)) {
-                       /* Fatal error in camera */
-                       return -EIO;
-               } 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)
-                       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);
-
-       /* set camera to a known state */
-       return set_camera_state(cam);
-}
-
-static void put_cam(struct cpia_camera_ops* ops)
-{
-       module_put(ops->owner);
-}
-
-/* ------------------------- V4L interface --------------------- */
-static int cpia_open(struct file *file)
-{
-       struct video_device *dev = video_devdata(file);
-       struct cam_data *cam = video_get_drvdata(dev);
-       int err;
-
-       if (!cam) {
-               DBG("Internal error, cam_data not found!\n");
-               return -ENODEV;
-       }
-
-       if (cam->open_count > 0) {
-               DBG("Camera already open\n");
-               return -EBUSY;
-       }
-
-       if (!try_module_get(cam->ops->owner))
-               return -ENODEV;
-
-       mutex_lock(&cam->busy_lock);
-       err = -ENOMEM;
-       if (!cam->raw_image) {
-               cam->raw_image = rvmalloc(CPIA_MAX_IMAGE_SIZE);
-               if (!cam->raw_image)
-                       goto oops;
-       }
-
-       if (!cam->decompressed_frame.data) {
-               cam->decompressed_frame.data = rvmalloc(CPIA_MAX_FRAME_SIZE);
-               if (!cam->decompressed_frame.data)
-                       goto oops;
-       }
-
-       /* open cpia */
-       err = -ENODEV;
-       if (cam->ops->open(cam->lowlevel_data))
-               goto oops;
-
-       /* reset the camera */
-       if ((err = reset_camera(cam)) != 0) {
-               cam->ops->close(cam->lowlevel_data);
-               goto oops;
-       }
-
-       err = -EINTR;
-       if(signal_pending(current))
-               goto oops;
-
-       /* Set ownership of /proc/cpia/videoX to current user */
-       if(cam->proc_entry)
-               cam->proc_entry->uid = current_uid();
-
-       /* set mark for loading first frame uncompressed */
-       cam->first_frame = 1;
-
-       /* init it to something */
-       cam->mmap_kludge = 0;
-
-       ++cam->open_count;
-       file->private_data = dev;
-       mutex_unlock(&cam->busy_lock);
-       return 0;
-
- oops:
-       if (cam->decompressed_frame.data) {
-               rvfree(cam->decompressed_frame.data, CPIA_MAX_FRAME_SIZE);
-               cam->decompressed_frame.data = NULL;
-       }
-       if (cam->raw_image) {
-               rvfree(cam->raw_image, CPIA_MAX_IMAGE_SIZE);
-               cam->raw_image = NULL;
-       }
-       mutex_unlock(&cam->busy_lock);
-       put_cam(cam->ops);
-       return err;
-}
-
-static int cpia_close(struct file *file)
-{
-       struct  video_device *dev = file->private_data;
-       struct cam_data *cam = video_get_drvdata(dev);
-
-       if (cam->ops) {
-               /* Return ownership of /proc/cpia/videoX to root */
-               if(cam->proc_entry)
-                       cam->proc_entry->uid = 0;
-
-               /* save camera state for later open (developers guide ch 3.5.3) */
-               save_camera_state(cam);
-
-               /* GotoLoPower */
-               goto_low_power(cam);
-
-               /* Update the camera status */
-               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);
-
-               put_cam(cam->ops);
-       }
-
-       if (--cam->open_count == 0) {
-               /* clean up capture-buffers */
-               if (cam->raw_image) {
-                       rvfree(cam->raw_image, CPIA_MAX_IMAGE_SIZE);
-                       cam->raw_image = NULL;
-               }
-
-               if (cam->decompressed_frame.data) {
-                       rvfree(cam->decompressed_frame.data, CPIA_MAX_FRAME_SIZE);
-                       cam->decompressed_frame.data = NULL;
-               }
-
-               if (cam->frame_buf)
-                       free_frame_buf(cam);
-
-               if (!cam->ops)
-                       kfree(cam);
-       }
-       file->private_data = NULL;
-
-       return 0;
-}
-
-static ssize_t cpia_read(struct file *file, char __user *buf,
-                        size_t count, loff_t *ppos)
-{
-       struct video_device *dev = file->private_data;
-       struct cam_data *cam = video_get_drvdata(dev);
-       int err;
-
-       /* make this _really_ smp and multithread-safe */
-       if (mutex_lock_interruptible(&cam->busy_lock))
-               return -EINTR;
-
-       if (!buf) {
-               DBG("buf NULL\n");
-               mutex_unlock(&cam->busy_lock);
-               return -EINVAL;
-       }
-
-       if (!count) {
-               DBG("count 0\n");
-               mutex_unlock(&cam->busy_lock);
-               return 0;
-       }
-
-       if (!cam->ops) {
-               DBG("ops NULL\n");
-               mutex_unlock(&cam->busy_lock);
-               return -ENODEV;
-       }
-
-       /* upload frame */
-       cam->decompressed_frame.state = FRAME_READY;
-       cam->mmap_kludge=0;
-       if((err = fetch_frame(cam)) != 0) {
-               DBG("ERROR from fetch_frame: %d\n", err);
-               mutex_unlock(&cam->busy_lock);
-               return err;
-       }
-       cam->decompressed_frame.state = FRAME_UNUSED;
-
-       /* copy data to user space */
-       if (cam->decompressed_frame.count > count) {
-               DBG("count wrong: %d, %lu\n", cam->decompressed_frame.count,
-                   (unsigned long) count);
-               mutex_unlock(&cam->busy_lock);
-               return -EFAULT;
-       }
-       if (copy_to_user(buf, cam->decompressed_frame.data,
-                       cam->decompressed_frame.count)) {
-               DBG("copy_to_user failed\n");
-               mutex_unlock(&cam->busy_lock);
-               return -EFAULT;
-       }
-
-       mutex_unlock(&cam->busy_lock);
-       return cam->decompressed_frame.count;
-}
-
-static long cpia_do_ioctl(struct file *file, unsigned int cmd, void *arg)
-{
-       struct video_device *dev = file->private_data;
-       struct cam_data *cam = video_get_drvdata(dev);
-       int retval = 0;
-
-       if (!cam || !cam->ops)
-               return -ENODEV;
-
-       /* make this _really_ smp-safe */
-       if (mutex_lock_interruptible(&cam->busy_lock))
-               return -EINTR;
-
-       /* DBG("cpia_ioctl: %u\n", cmd); */
-
-       switch (cmd) {
-       /* query capabilities */
-       case VIDIOCGCAP:
-       {
-               struct video_capability *b = arg;
-
-               DBG("VIDIOCGCAP\n");
-               strcpy(b->name, "CPiA Camera");
-               b->type = VID_TYPE_CAPTURE | VID_TYPE_SUBCAPTURE;
-               b->channels = 1;
-               b->audios = 0;
-               b->maxwidth = 352;      /* VIDEOSIZE_CIF */
-               b->maxheight = 288;
-               b->minwidth = 48;       /* VIDEOSIZE_48_48 */
-               b->minheight = 48;
-               break;
-       }
-
-       /* get/set video source - we are a camera and nothing else */
-       case VIDIOCGCHAN:
-       {
-               struct video_channel *v = arg;
-
-               DBG("VIDIOCGCHAN\n");
-               if (v->channel != 0) {
-                       retval = -EINVAL;
-                       break;
-               }
-
-               v->channel = 0;
-               strcpy(v->name, "Camera");
-               v->tuners = 0;
-               v->flags = 0;
-               v->type = VIDEO_TYPE_CAMERA;
-               v->norm = 0;
-               break;
-       }
-
-       case VIDIOCSCHAN:
-       {
-               struct video_channel *v = arg;
-
-               DBG("VIDIOCSCHAN\n");
-               if (v->channel != 0)
-                       retval = -EINVAL;
-               break;
-       }
-
-       /* image properties */
-       case VIDIOCGPICT:
-       {
-               struct video_picture *pic = arg;
-               DBG("VIDIOCGPICT\n");
-               *pic = cam->vp;
-               break;
-       }
-
-       case VIDIOCSPICT:
-       {
-               struct video_picture *vp = arg;
-
-               DBG("VIDIOCSPICT\n");
-
-               /* check validity */
-               DBG("palette: %d\n", vp->palette);
-               DBG("depth: %d\n", vp->depth);
-               if (!valid_mode(vp->palette, vp->depth)) {
-                       retval = -EINVAL;
-                       break;
-               }
-
-               mutex_lock(&cam->param_lock);
-               /* brightness, colour, contrast need no check 0-65535 */
-               cam->vp = *vp;
-               /* update cam->params.colourParams */
-               cam->params.colourParams.brightness = vp->brightness*100/65535;
-               cam->params.colourParams.contrast = vp->contrast*100/65535;
-               cam->params.colourParams.saturation = vp->colour*100/65535;
-               /* 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) {
-                       /* 1-02 firmware limits contrast to 80 */
-                       cam->params.colourParams.contrast = 80;
-               }
-
-               /* Adjust flicker control if necessary */
-               if(cam->params.flickerControl.allowableOverExposure < 0)
-                       cam->params.flickerControl.allowableOverExposure =
-                               -find_over_exposure(cam->params.colourParams.brightness);
-               if(cam->params.flickerControl.flickerMode != 0)
-                       cam->cmd_queue |= COMMAND_SETFLICKERCTRL;
-
-
-               /* queue command to update camera */
-               cam->cmd_queue |= COMMAND_SETCOLOURPARAMS;
-               mutex_unlock(&cam->param_lock);
-               DBG("VIDIOCSPICT: %d / %d // %d / %d / %d / %d\n",
-                   vp->depth, vp->palette, vp->brightness, vp->hue, vp->colour,
-                   vp->contrast);
-               break;
-       }
-
-       /* get/set capture window */
-       case VIDIOCGWIN:
-       {
-               struct video_window *vw = arg;
-               DBG("VIDIOCGWIN\n");
-
-               *vw = cam->vw;
-               break;
-       }
-
-       case VIDIOCSWIN:
-       {
-               /* copy_from_user, check validity, copy to internal structure */
-               struct video_window *vw = arg;
-               DBG("VIDIOCSWIN\n");
-
-               if (vw->clipcount != 0) {    /* clipping not supported */
-                       retval = -EINVAL;
-                       break;
-               }
-               if (vw->clips != NULL) {     /* clipping not supported */
-                       retval = -EINVAL;
-                       break;
-               }
-
-               /* we set the video window to something smaller or equal to what
-               * is requested by the user???
-               */
-               mutex_lock(&cam->param_lock);
-               if (vw->width != cam->vw.width || vw->height != cam->vw.height) {
-                       int video_size = match_videosize(vw->width, vw->height);
-
-                       if (video_size < 0) {
-                               retval = -EINVAL;
-                               mutex_unlock(&cam->param_lock);
-                               break;
-                       }
-                       cam->video_size = video_size;
-
-                       /* video size is changing, reset the subcapture area */
-                       memset(&cam->vc, 0, sizeof(cam->vc));
-
-                       set_vw_size(cam);
-                       DBG("%d / %d\n", cam->vw.width, cam->vw.height);
-                       cam->cmd_queue |= COMMAND_SETFORMAT;
-               }
-
-               mutex_unlock(&cam->param_lock);
-
-               /* setformat ignored by camera during streaming,
-                * so stop/dispatch/start */
-               if (cam->cmd_queue & COMMAND_SETFORMAT) {
-                       DBG("\n");
-                       dispatch_commands(cam);
-               }
-               DBG("%d/%d:%d\n", cam->video_size,
-                   cam->vw.width, cam->vw.height);
-               break;
-       }
-
-       /* mmap interface */
-       case VIDIOCGMBUF:
-       {
-               struct video_mbuf *vm = arg;
-               int i;
-
-               DBG("VIDIOCGMBUF\n");
-               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;
-               break;
-       }
-
-       case VIDIOCMCAPTURE:
-       {
-               struct video_mmap *vm = arg;
-               int video_size;
-
-               DBG("VIDIOCMCAPTURE: %d / %d / %dx%d\n", vm->format, vm->frame,
-                   vm->width, vm->height);
-               if (vm->frame<0||vm->frame>=FRAME_NUM) {
-                       retval = -EINVAL;
-                       break;
-               }
-
-               /* set video format */
-               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:
-               case VIDEO_PALETTE_YUYV:
-               case VIDEO_PALETTE_UYVY:
-                       cam->vp.depth = 16;
-                       break;
-               case VIDEO_PALETTE_RGB24:
-                       cam->vp.depth = 24;
-                       break;
-               case VIDEO_PALETTE_RGB32:
-                       cam->vp.depth = 32;
-                       break;
-               default:
-                       retval = -EINVAL;
-                       break;
-               }
-               if (retval)
-                       break;
-
-               /* set video size */
-               video_size = match_videosize(vm->width, vm->height);
-               if (video_size < 0) {
-                       retval = -EINVAL;
-                       break;
-               }
-               if (video_size != cam->video_size) {
-                       cam->video_size = video_size;
-
-                       /* video size is changing, reset the subcapture area */
-                       memset(&cam->vc, 0, sizeof(cam->vc));
-
-                       set_vw_size(cam);
-                       cam->cmd_queue |= COMMAND_SETFORMAT;
-                       dispatch_commands(cam);
-               }
-               /* according to v4l-spec we must start streaming here */
-               cam->mmap_kludge = 1;
-               retval = capture_frame(cam, vm);
-
-               break;
-       }
-
-       case VIDIOCSYNC:
-       {
-               int *frame = arg;
-
-               //DBG("VIDIOCSYNC: %d\n", *frame);
-
-               if (*frame<0 || *frame >= FRAME_NUM) {
-                       retval = -EINVAL;
-                       break;
-               }
-
-               switch (cam->frame[*frame].state) {
-               case FRAME_UNUSED:
-               case FRAME_READY:
-               case FRAME_GRABBING:
-                       DBG("sync to unused frame %d\n", *frame);
-                       retval = -EINVAL;
-                       break;
-
-               case FRAME_DONE:
-                       cam->frame[*frame].state = FRAME_UNUSED;
-                       //DBG("VIDIOCSYNC: %d synced\n", *frame);
-                       break;
-               }
-               if (retval == -EINTR) {
-                       /* FIXME - xawtv does not handle this nice */
-                       retval = 0;
-               }
-               break;
-       }
-
-       case VIDIOCGCAPTURE:
-       {
-               struct video_capture *vc = arg;
-
-               DBG("VIDIOCGCAPTURE\n");
-
-               *vc = cam->vc;
-
-               break;
-       }
-
-       case VIDIOCSCAPTURE:
-       {
-               struct video_capture *vc = arg;
-
-               DBG("VIDIOCSCAPTURE\n");
-
-               if (vc->decimation != 0) {    /* How should this be used? */
-                       retval = -EINVAL;
-                       break;
-               }
-               if (vc->flags != 0) {     /* Even/odd grab not supported */
-                       retval = -EINVAL;
-                       break;
-               }
-
-               /* Clip to the resolution we can set for the ROI
-                  (every 8 columns and 4 rows) */
-               vc->x      = vc->x      & ~(__u32)7;
-               vc->y      = vc->y      & ~(__u32)3;
-               vc->width  = vc->width  & ~(__u32)7;
-               vc->height = vc->height & ~(__u32)3;
-
-               if(vc->width == 0 || vc->height == 0 ||
-                  vc->x + vc->width  > cam->vw.width ||
-                  vc->y + vc->height > cam->vw.height) {
-                       retval = -EINVAL;
-                       break;
-               }
-
-               DBG("%d,%d/%dx%d\n", vc->x,vc->y,vc->width, vc->height);
-
-               mutex_lock(&cam->param_lock);
-
-               cam->vc.x      = vc->x;
-               cam->vc.y      = vc->y;
-               cam->vc.width  = vc->width;
-               cam->vc.height = vc->height;
-
-               set_vw_size(cam);
-               cam->cmd_queue |= COMMAND_SETFORMAT;
-
-               mutex_unlock(&cam->param_lock);
-
-               /* setformat ignored by camera during streaming,
-                * so stop/dispatch/start */
-               dispatch_commands(cam);
-               break;
-       }
-
-       case VIDIOCGUNIT:
-       {
-               struct video_unit *vu = arg;
-
-               DBG("VIDIOCGUNIT\n");
-
-               vu->video    = cam->vdev.minor;
-               vu->vbi      = VIDEO_NO_UNIT;
-               vu->radio    = VIDEO_NO_UNIT;
-               vu->audio    = VIDEO_NO_UNIT;
-               vu->teletext = VIDEO_NO_UNIT;
-
-               break;
-       }
-
-
-       /* pointless to implement overlay with this camera */
-       case VIDIOCCAPTURE:
-       case VIDIOCGFBUF:
-       case VIDIOCSFBUF:
-       case VIDIOCKEY:
-       /* tuner interface - we have none */
-       case VIDIOCGTUNER:
-       case VIDIOCSTUNER:
-       case VIDIOCGFREQ:
-       case VIDIOCSFREQ:
-       /* audio interface - we have none */
-       case VIDIOCGAUDIO:
-       case VIDIOCSAUDIO:
-               retval = -EINVAL;
-               break;
-       default:
-               retval = -ENOIOCTLCMD;
-               break;
-       }
-
-       mutex_unlock(&cam->busy_lock);
-       return retval;
-}
-
-static long cpia_ioctl(struct file *file,
-                    unsigned int cmd, unsigned long arg)
-{
-       return video_usercopy(file, cmd, arg, cpia_do_ioctl);
-}
-
-
-/* FIXME */
-static int cpia_mmap(struct file *file, struct vm_area_struct *vma)
-{
-       struct video_device *dev = file->private_data;
-       unsigned long start = vma->vm_start;
-       unsigned long size  = vma->vm_end - vma->vm_start;
-       unsigned long page, pos;
-       struct cam_data *cam = video_get_drvdata(dev);
-       int retval;
-
-       if (!cam || !cam->ops)
-               return -ENODEV;
-
-       DBG("cpia_mmap: %ld\n", size);
-
-       if (size > FRAME_NUM*CPIA_MAX_FRAME_SIZE)
-               return -EINVAL;
-
-       /* make this _really_ smp-safe */
-       if (mutex_lock_interruptible(&cam->busy_lock))
-               return -EINTR;
-
-       if (!cam->frame_buf) {  /* we do lazy allocation */
-               if ((retval = allocate_frame_buf(cam))) {
-                       mutex_unlock(&cam->busy_lock);
-                       return retval;
-               }
-       }
-
-       pos = (unsigned long)(cam->frame_buf);
-       while (size > 0) {
-               page = vmalloc_to_pfn((void *)pos);
-               if (remap_pfn_range(vma, start, page, PAGE_SIZE, PAGE_SHARED)) {
-                       mutex_unlock(&cam->busy_lock);
-                       return -EAGAIN;
-               }
-               start += PAGE_SIZE;
-               pos += PAGE_SIZE;
-               if (size > PAGE_SIZE)
-                       size -= PAGE_SIZE;
-               else
-                       size = 0;
-       }
-
-       DBG("cpia_mmap: %ld\n", size);
-       mutex_unlock(&cam->busy_lock);
-
-       return 0;
-}
-
-static const struct v4l2_file_operations cpia_fops = {
-       .owner          = THIS_MODULE,
-       .open           = cpia_open,
-       .release        = cpia_close,
-       .read           = cpia_read,
-       .mmap           = cpia_mmap,
-       .ioctl          = cpia_ioctl,
-};
-
-static struct video_device cpia_template = {
-       .name           = "CPiA Camera",
-       .fops           = &cpia_fops,
-       .release        = video_device_release_empty,
-};
-
-/* initialise cam_data structure  */
-static void reset_camera_struct(struct cam_data *cam)
-{
-       /* The following parameter values are the defaults from
-        * "Software Developer's Guide for CPiA Cameras".  Any changes
-        * to the defaults are noted in comments. */
-       cam->params.colourParams.brightness = 50;
-       cam->params.colourParams.contrast = 48;
-       cam->params.colourParams.saturation = 50;
-       cam->params.exposure.gainMode = 4;
-       cam->params.exposure.expMode = 2;               /* AEC */
-       cam->params.exposure.compMode = 1;
-       cam->params.exposure.centreWeight = 1;
-       cam->params.exposure.gain = 0;
-       cam->params.exposure.fineExp = 0;
-       cam->params.exposure.coarseExpLo = 185;
-       cam->params.exposure.coarseExpHi = 0;
-       cam->params.exposure.redComp = COMP_RED;
-       cam->params.exposure.green1Comp = COMP_GREEN1;
-       cam->params.exposure.green2Comp = COMP_GREEN2;
-       cam->params.exposure.blueComp = COMP_BLUE;
-       cam->params.colourBalance.balanceMode = 2;      /* ACB */
-       cam->params.colourBalance.redGain = 32;
-       cam->params.colourBalance.greenGain = 6;
-       cam->params.colourBalance.blueGain = 92;
-       cam->params.apcor.gain1 = 0x18;
-       cam->params.apcor.gain2 = 0x16;
-       cam->params.apcor.gain4 = 0x24;
-       cam->params.apcor.gain8 = 0x34;
-       cam->params.flickerControl.flickerMode = 0;
-       cam->params.flickerControl.disabled = 1;
-
-       cam->params.flickerControl.coarseJump =
-               flicker_jumps[cam->mainsFreq]
-                            [cam->params.sensorFps.baserate]
-                            [cam->params.sensorFps.divisor];
-       cam->params.flickerControl.allowableOverExposure =
-               -find_over_exposure(cam->params.colourParams.brightness);
-       cam->params.vlOffset.gain1 = 20;
-       cam->params.vlOffset.gain2 = 24;
-       cam->params.vlOffset.gain4 = 26;
-       cam->params.vlOffset.gain8 = 26;
-       cam->params.compressionParams.hysteresis = 3;
-       cam->params.compressionParams.threshMax = 11;
-       cam->params.compressionParams.smallStep = 1;
-       cam->params.compressionParams.largeStep = 3;
-       cam->params.compressionParams.decimationHysteresis = 2;
-       cam->params.compressionParams.frDiffStepThresh = 5;
-       cam->params.compressionParams.qDiffStepThresh = 3;
-       cam->params.compressionParams.decimationThreshMod = 2;
-       /* End of default values from Software Developer's Guide */
-
-       cam->transfer_rate = 0;
-       cam->exposure_status = EXPOSURE_NORMAL;
-
-       /* 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 = 6; /* From windows driver */
-       cam->params.yuvThreshold.uvThreshold = 6; /* From windows driver */
-
-       cam->params.format.subSample = SUBSAMPLE_422;
-       cam->params.format.yuvOrder = YUVORDER_YUYV;
-
-       cam->params.compression.mode = CPIA_COMPRESSION_AUTO;
-       cam->params.compressionTarget.frTargeting =
-               CPIA_COMPRESSION_TARGET_QUALITY;
-       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;
-
-       cam->vp.colour = 32768;      /* 50% */
-       cam->vp.hue = 32768;         /* 50% */
-       cam->vp.brightness = 32768;  /* 50% */
-       cam->vp.contrast = 32768;    /* 50% */
-       cam->vp.whiteness = 0;       /* not used -> grayscale only */
-       cam->vp.depth = 24;          /* to be set by user */
-       cam->vp.palette = VIDEO_PALETTE_RGB24; /* to be set by user */
-
-       cam->vc.x = 0;
-       cam->vc.y = 0;
-       cam->vc.width = 0;
-       cam->vc.height = 0;
-
-       cam->vw.x = 0;
-       cam->vw.y = 0;
-       set_vw_size(cam);
-       cam->vw.chromakey = 0;
-       cam->vw.flags = 0;
-       cam->vw.clipcount = 0;
-       cam->vw.clips = NULL;
-
-       cam->cmd_queue = COMMAND_NONE;
-       cam->first_frame = 1;
-
-       return;
-}
-
-/* initialize cam_data structure  */
-static void init_camera_struct(struct cam_data *cam,
-                              struct cpia_camera_ops *ops )
-{
-       int i;
-
-       /* Default everything to 0 */
-       memset(cam, 0, sizeof(struct cam_data));
-
-       cam->ops = ops;
-       mutex_init(&cam->param_lock);
-       mutex_init(&cam->busy_lock);
-
-       reset_camera_struct(cam);
-
-       cam->proc_entry = NULL;
-
-       memcpy(&cam->vdev, &cpia_template, sizeof(cpia_template));
-       video_set_drvdata(&cam->vdev, cam);
-
-       cam->curframe = 0;
-       for (i = 0; i < FRAME_NUM; i++) {
-               cam->frame[i].width = 0;
-               cam->frame[i].height = 0;
-               cam->frame[i].state = FRAME_UNUSED;
-               cam->frame[i].data = NULL;
-       }
-       cam->decompressed_frame.width = 0;
-       cam->decompressed_frame.height = 0;
-       cam->decompressed_frame.state = FRAME_UNUSED;
-       cam->decompressed_frame.data = NULL;
-}
-
-struct cam_data *cpia_register_camera(struct cpia_camera_ops *ops, void *lowlevel)
-{
-       struct cam_data *camera;
-
-       if ((camera = kmalloc(sizeof(struct cam_data), GFP_KERNEL)) == NULL)
-               return NULL;
-
-
-       init_camera_struct( camera, ops );
-       camera->lowlevel_data = lowlevel;
-
-       /* register v4l device */
-       if (video_register_device(&camera->vdev, VFL_TYPE_GRABBER, video_nr) < 0) {
-               kfree(camera);
-               printk(KERN_DEBUG "video_register_device failed\n");
-               return NULL;
-       }
-
-       /* get version information from camera: open/reset/close */
-
-       /* open cpia */
-       if (camera->ops->open(camera->lowlevel_data))
-               return camera;
-
-       /* reset the camera */
-       if (reset_camera(camera) != 0) {
-               camera->ops->close(camera->lowlevel_data);
-               return camera;
-       }
-
-       /* close cpia */
-       camera->ops->close(camera->lowlevel_data);
-
-#ifdef CONFIG_PROC_FS
-       create_proc_cpia_cam(camera);
-#endif
-
-       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(struct cam_data *cam)
-{
-       DBG("unregistering video\n");
-       video_unregister_device(&cam->vdev);
-       if (cam->open_count) {
-               put_cam(cam->ops);
-               DBG("camera open -- setting ops to NULL\n");
-               cam->ops = NULL;
-       }
-
-#ifdef CONFIG_PROC_FS
-       DBG("destroying /proc/cpia/%s\n", video_device_node_name(&cam->vdev));
-       destroy_proc_cpia_cam(cam);
-#endif
-       if (!cam->open_count) {
-               DBG("freeing camera\n");
-               kfree(cam);
-       }
-}
-
-static int __init cpia_init(void)
-{
-       printk(KERN_INFO "%s v%d.%d.%d\n", ABOUT,
-              CPIA_MAJ_VER, CPIA_MIN_VER, CPIA_PATCH_VER);
-
-       printk(KERN_WARNING "Since in-kernel colorspace conversion is not "
-              "allowed, it is disabled by default now. Users should fix the "
-              "applications in case they don't work without conversion "
-              "reenabled by setting the 'colorspace_conv' module "
-              "parameter to 1\n");
-
-#ifdef CONFIG_PROC_FS
-       proc_cpia_create();
-#endif
-
-       return 0;
-}
-
-static void __exit cpia_exit(void)
-{
-#ifdef CONFIG_PROC_FS
-       proc_cpia_destroy();
-#endif
-}
-
-module_init(cpia_init);
-module_exit(cpia_exit);
-
-/* Exported symbols for modules. */
-
-EXPORT_SYMBOL(cpia_register_camera);
-EXPORT_SYMBOL(cpia_unregister_camera);
diff --git a/drivers/media/video/cpia.h b/drivers/media/video/cpia.h
deleted file mode 100644 (file)
index 8f0cfee..0000000
+++ /dev/null
@@ -1,432 +0,0 @@
-#ifndef cpia_h
-#define cpia_h
-
-/*
- * CPiA Parallel Port Video4Linux driver
- *
- * Supports CPiA based parallel port Video Camera's.
- *
- * (C) Copyright 1999 Bas Huisman,
- *                    Peter Pregler,
- *                    Scott J. Bertin,
- *                    VLSI Vision Ltd.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- */
-
-#define CPIA_MAJ_VER   1
-#define CPIA_MIN_VER   2
-#define CPIA_PATCH_VER 3
-
-#define CPIA_PP_MAJ_VER       CPIA_MAJ_VER
-#define CPIA_PP_MIN_VER       CPIA_MIN_VER
-#define CPIA_PP_PATCH_VER     CPIA_PATCH_VER
-
-#define CPIA_USB_MAJ_VER      CPIA_MAJ_VER
-#define CPIA_USB_MIN_VER      CPIA_MIN_VER
-#define CPIA_USB_PATCH_VER    CPIA_PATCH_VER
-
-#define CPIA_MAX_FRAME_SIZE_UNALIGNED  (352 * 288 * 4)   /* CIF at RGB32 */
-#define CPIA_MAX_FRAME_SIZE    ((CPIA_MAX_FRAME_SIZE_UNALIGNED + PAGE_SIZE - 1) & ~(PAGE_SIZE - 1)) /* align above to PAGE_SIZE */
-
-#ifdef __KERNEL__
-
-#include <asm/uaccess.h>
-#include <linux/videodev.h>
-#include <media/v4l2-common.h>
-#include <media/v4l2-ioctl.h>
-#include <linux/list.h>
-#include <linux/mutex.h>
-
-struct cpia_camera_ops
-{
-       /* open sets privdata to point to structure for this camera.
-        * Returns negative value on error, otherwise 0.
-        */
-       int (*open)(void *privdata);
-
-       /* Registers callback function cb to be called with cbdata
-        * when an image is ready.  If cb is NULL, only single image grabs
-        * should be used.  cb should immediately call streamRead to read
-        * the data or data may be lost. Returns negative value on error,
-        * otherwise 0.
-        */
-       int (*registerCallback)(void *privdata, void (*cb)(void *cbdata),
-                               void *cbdata);
-
-       /* transferCmd sends commands to the camera.  command MUST point to
-        * an  8 byte buffer in kernel space. data can be NULL if no extra
-        * data is needed.  The size of the data is given by the last 2
-        * bytes of command.  data must also point to memory in kernel space.
-        * Returns negative value on error, otherwise 0.
-        */
-       int (*transferCmd)(void *privdata, u8 *command, u8 *data);
-
-       /* streamStart initiates stream capture mode.
-        * Returns negative value on error, otherwise 0.
-        */
-       int (*streamStart)(void *privdata);
-
-       /* streamStop terminates stream capture mode.
-        * Returns negative value on error, otherwise 0.
-        */
-       int (*streamStop)(void *privdata);
-
-       /* streamRead reads a frame from the camera.  buffer points to a
-        * buffer large enough to hold a complete frame in kernel space.
-        * noblock indicates if this should be a non blocking read.
-        * Returns the number of bytes read, or negative value on error.
-        */
-       int (*streamRead)(void *privdata, u8 *buffer, int noblock);
-
-       /* close disables the device until open() is called again.
-        * Returns negative value on error, otherwise 0.
-        */
-       int (*close)(void *privdata);
-
-       /* If wait_for_stream_ready is non-zero, wait until the streamState
-        * is STREAM_READY before calling streamRead.
-        */
-       int wait_for_stream_ready;
-
-       /*
-        * Used to maintain lowlevel module usage counts
-        */
-       struct module *owner;
-};
-
-struct cpia_frame {
-       u8 *data;
-       int count;
-       int width;
-       int height;
-       volatile int state;
-};
-
-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 disabled;
-               u8 flickerMode;
-               u8 coarseJump;
-               int 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 {                        /* Intel QX3 specific data */
-               u8 qx3_detected;        /* a QX3 is present */
-               u8 toplight;            /* top light lit , R/W */
-               u8 bottomlight;         /* bottom light lit, R/W */
-               u8 button;              /* snapshot button pressed (R/O) */
-               u8 cradled;             /* microscope is in cradle (R/O) */
-       } qx3;
-       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;
-};
-
-enum v4l_camstates {
-       CPIA_V4L_IDLE = 0,
-       CPIA_V4L_ERROR,
-       CPIA_V4L_COMMAND,
-       CPIA_V4L_GRABBING,
-       CPIA_V4L_STREAMING,
-       CPIA_V4L_STREAMING_PAUSED,
-};
-
-#define FRAME_NUM      2       /* double buffering for now */
-
-struct cam_data {
-       struct list_head cam_data_list;
-
-       struct mutex 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 mutex 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 */
-       struct video_capture vc;        /* v4l subcapture 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;
-       int mmap_kludge;                /* 'wrong' byte order for mmap */
-       volatile u32 cmd_queue;         /* queued commands */
-       int exposure_status;            /* EXPOSURE_* */
-       int exposure_count;             /* number of frames at this status */
-};
-
-/* cpia_register_camera is called by low level driver for each camera.
- * A unique camera number is returned, or a negative value on error */
-struct cam_data *cpia_register_camera(struct cpia_camera_ops *ops, void *lowlevel);
-
-/* cpia_unregister_camera is called by low level driver when a camera
- * is removed.  This must not fail. */
-void cpia_unregister_camera(struct cam_data *cam);
-
-/* raw CIF + 64 byte header + (2 bytes line_length + EOL) per line + 4*EOI +
- * one byte 16bit DMA alignment
- */
-#define CPIA_MAX_IMAGE_SIZE ((352*288*2)+64+(288*3)+5)
-
-/* constant value's */
-#define MAGIC_0                0x19
-#define MAGIC_1                0x68
-#define DATA_IN                0xC0
-#define DATA_OUT       0x40
-#define VIDEOSIZE_QCIF 0       /* 176x144 */
-#define VIDEOSIZE_CIF  1       /* 352x288 */
-#define VIDEOSIZE_SIF  2       /* 320x240 */
-#define VIDEOSIZE_QSIF 3       /* 160x120 */
-#define VIDEOSIZE_48_48                4 /* where no one has gone before, iconsize! */
-#define VIDEOSIZE_64_48                5
-#define VIDEOSIZE_128_96       6
-#define VIDEOSIZE_160_120      VIDEOSIZE_QSIF
-#define VIDEOSIZE_176_144      VIDEOSIZE_QCIF
-#define VIDEOSIZE_192_144      7
-#define VIDEOSIZE_224_168      8
-#define VIDEOSIZE_256_192      9
-#define VIDEOSIZE_288_216      10
-#define VIDEOSIZE_320_240      VIDEOSIZE_SIF
-#define VIDEOSIZE_352_288      VIDEOSIZE_CIF
-#define VIDEOSIZE_88_72                11 /* quarter CIF */
-#define SUBSAMPLE_420  0
-#define SUBSAMPLE_422  1
-#define YUVORDER_YUYV  0
-#define YUVORDER_UYVY  1
-#define NOT_COMPRESSED 0
-#define COMPRESSED     1
-#define NO_DECIMATION  0
-#define DECIMATION_ENAB        1
-#define EOI            0xff    /* End Of Image */
-#define EOL            0xfd    /* End Of Line */
-#define FRAME_HEADER_SIZE      64
-
-/* Image grab modes */
-#define CPIA_GRAB_SINGLE       0
-#define CPIA_GRAB_CONTINUOUS   1
-
-/* Compression parameters */
-#define CPIA_COMPRESSION_NONE  0
-#define CPIA_COMPRESSION_AUTO  1
-#define CPIA_COMPRESSION_MANUAL        2
-#define CPIA_COMPRESSION_TARGET_QUALITY         0
-#define CPIA_COMPRESSION_TARGET_FRAMERATE       1
-
-/* Return offsets for GetCameraState */
-#define SYSTEMSTATE    0
-#define GRABSTATE      1
-#define STREAMSTATE    2
-#define FATALERROR     3
-#define CMDERROR       4
-#define DEBUGFLAGS     5
-#define VPSTATUS       6
-#define ERRORCODE      7
-
-/* SystemState */
-#define UNINITIALISED_STATE    0
-#define PASS_THROUGH_STATE     1
-#define LO_POWER_STATE         2
-#define HI_POWER_STATE         3
-#define WARM_BOOT_STATE                4
-
-/* GrabState */
-#define GRAB_IDLE              0
-#define GRAB_ACTIVE            1
-#define GRAB_DONE              2
-
-/* StreamState */
-#define STREAM_NOT_READY       0
-#define STREAM_READY           1
-#define STREAM_OPEN            2
-#define STREAM_PAUSED          3
-#define STREAM_FINISHED                4
-
-/* Fatal Error, CmdError, and DebugFlags */
-#define CPIA_FLAG        1
-#define SYSTEM_FLAG      2
-#define INT_CTRL_FLAG    4
-#define PROCESS_FLAG     8
-#define COM_FLAG        16
-#define VP_CTRL_FLAG    32
-#define CAPTURE_FLAG    64
-#define DEBUG_FLAG     128
-
-/* VPStatus */
-#define VP_STATE_OK                    0x00
-
-#define VP_STATE_FAILED_VIDEOINIT      0x01
-#define VP_STATE_FAILED_AECACBINIT     0x02
-#define VP_STATE_AEC_MAX               0x04
-#define VP_STATE_ACB_BMAX              0x08
-
-#define VP_STATE_ACB_RMIN              0x10
-#define VP_STATE_ACB_GMIN              0x20
-#define VP_STATE_ACB_RMAX              0x40
-#define VP_STATE_ACB_GMAX              0x80
-
-/* default (minimum) compensation values */
-#define COMP_RED        220
-#define COMP_GREEN1     214
-#define COMP_GREEN2     COMP_GREEN1
-#define COMP_BLUE       230
-
-/* exposure status */
-#define EXPOSURE_VERY_LIGHT 0
-#define EXPOSURE_LIGHT      1
-#define EXPOSURE_NORMAL     2
-#define EXPOSURE_DARK       3
-#define EXPOSURE_VERY_DARK  4
-
-/* ErrorCode */
-#define ERROR_FLICKER_BELOW_MIN_EXP     0x01 /*flicker exposure got below minimum exposure */
-#define ALOG(fmt,args...) printk(fmt, ##args)
-#define LOG(fmt,args...) ALOG(KERN_INFO __FILE__ ":%s(%d):" fmt, __func__ , __LINE__ , ##args)
-
-#ifdef _CPIA_DEBUG_
-#define ADBG(fmt,args...) printk(fmt, jiffies, ##args)
-#define DBG(fmt,args...) ADBG(KERN_DEBUG __FILE__" (%ld):%s(%d):" fmt, __func__, __LINE__ , ##args)
-#else
-#define DBG(fmn,args...) do {} while(0)
-#endif
-
-#define DEB_BYTE(p)\
-  DBG("%1d %1d %1d %1d %1d %1d %1d %1d \n",\
-      (p)&0x80?1:0, (p)&0x40?1:0, (p)&0x20?1:0, (p)&0x10?1:0,\
-       (p)&0x08?1:0, (p)&0x04?1:0, (p)&0x02?1:0, (p)&0x01?1:0);
-
-#endif /* __KERNEL__ */
-
-#endif /* cpia_h */
diff --git a/drivers/media/video/cpia_pp.c b/drivers/media/video/cpia_pp.c
deleted file mode 100644 (file)
index f5604c1..0000000
+++ /dev/null
@@ -1,869 +0,0 @@
-/*
- * cpia_pp CPiA Parallel Port driver
- *
- * Supports CPiA based parallel port Video Camera's.
- *
- * (C) Copyright 1999 Bas Huisman <bhuism@cs.utwente.nl>
- * (C) Copyright 1999-2000 Scott J. Bertin <sbertin@securenym.net>,
- * (C) Copyright 1999-2000 Peter Pregler <Peter_Pregler@email.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
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- */
-
-/* define _CPIA_DEBUG_ for verbose debug output (see cpia.h) */
-/* #define _CPIA_DEBUG_  1 */
-
-
-#include <linux/module.h>
-#include <linux/init.h>
-
-#include <linux/kernel.h>
-#include <linux/parport.h>
-#include <linux/interrupt.h>
-#include <linux/delay.h>
-#include <linux/workqueue.h>
-#include <linux/sched.h>
-#include <linux/slab.h>
-
-#include <linux/kmod.h>
-
-/* #define _CPIA_DEBUG_                define for verbose debug output */
-#include "cpia.h"
-
-static int cpia_pp_open(void *privdata);
-static int cpia_pp_registerCallback(void *privdata, void (*cb) (void *cbdata),
-                                   void *cbdata);
-static int cpia_pp_transferCmd(void *privdata, u8 *command, u8 *data);
-static int cpia_pp_streamStart(void *privdata);
-static int cpia_pp_streamStop(void *privdata);
-static int cpia_pp_streamRead(void *privdata, u8 *buffer, int noblock);
-static int cpia_pp_close(void *privdata);
-
-
-#define ABOUT "Parallel port driver for Vision CPiA based cameras"
-
-#define PACKET_LENGTH  8
-
-/* Magic numbers for defining port-device mappings */
-#define PPCPIA_PARPORT_UNSPEC -4
-#define PPCPIA_PARPORT_AUTO -3
-#define PPCPIA_PARPORT_OFF -2
-#define PPCPIA_PARPORT_NONE -1
-
-static int parport_nr[PARPORT_MAX] = {[0 ... PARPORT_MAX - 1] = PPCPIA_PARPORT_UNSPEC};
-static char *parport[PARPORT_MAX] = {NULL,};
-
-MODULE_AUTHOR("B. Huisman <bhuism@cs.utwente.nl> & Peter Pregler <Peter_Pregler@email.com>");
-MODULE_DESCRIPTION("Parallel port driver for Vision CPiA based cameras");
-MODULE_LICENSE("GPL");
-
-module_param_array(parport, charp, NULL, 0);
-MODULE_PARM_DESC(parport, "'auto' or a list of parallel port numbers. Just like lp.");
-
-struct pp_cam_entry {
-       struct pardevice *pdev;
-       struct parport *port;
-       struct work_struct cb_task;
-       void (*cb_func)(void *cbdata);
-       void *cb_data;
-       int open_count;
-       wait_queue_head_t wq_stream;
-       /* image state flags */
-       int image_ready;        /* we got an interrupt */
-       int image_complete;     /* we have seen 4 EOI */
-
-       int streaming; /* we are in streaming mode */
-       int stream_irq;
-};
-
-static struct cpia_camera_ops cpia_pp_ops =
-{
-       cpia_pp_open,
-       cpia_pp_registerCallback,
-       cpia_pp_transferCmd,
-       cpia_pp_streamStart,
-       cpia_pp_streamStop,
-       cpia_pp_streamRead,
-       cpia_pp_close,
-       1,
-       THIS_MODULE
-};
-
-static LIST_HEAD(cam_list);
-static spinlock_t cam_list_lock_pp;
-
-/* FIXME */
-static void cpia_parport_enable_irq( struct parport *port ) {
-       parport_enable_irq(port);
-       mdelay(10);
-       return;
-}
-
-static void cpia_parport_disable_irq( struct parport *port ) {
-       parport_disable_irq(port);
-       mdelay(10);
-       return;
-}
-
-/* Special CPiA PPC modes: These are invoked by using the 1284 Extensibility
- * Link Flag during negotiation */
-#define UPLOAD_FLAG  0x08
-#define NIBBLE_TRANSFER 0x01
-#define ECP_TRANSFER 0x03
-
-#define PARPORT_CHUNK_SIZE     PAGE_SIZE
-
-
-static void cpia_pp_run_callback(struct work_struct *work)
-{
-       void (*cb_func)(void *cbdata);
-       void *cb_data;
-       struct pp_cam_entry *cam;
-
-       cam = container_of(work, struct pp_cam_entry, cb_task);
-       cb_func = cam->cb_func;
-       cb_data = cam->cb_data;
-
-       cb_func(cb_data);
-}
-
-/****************************************************************************
- *
- *  CPiA-specific  low-level parport functions for nibble uploads
- *
- ***************************************************************************/
-/*  CPiA nonstandard "Nibble" mode (no nDataAvail signal after each byte). */
-/* The standard kernel parport_ieee1284_read_nibble() fails with the CPiA... */
-
-static size_t cpia_read_nibble (struct parport *port,
-                        void *buffer, size_t len,
-                        int flags)
-{
-       /* adapted verbatim, with one change, from
-          parport_ieee1284_read_nibble() in drivers/parport/ieee1284-ops.c */
-
-       unsigned char *buf = buffer;
-       int i;
-       unsigned char byte = 0;
-
-       len *= 2; /* in nibbles */
-       for (i=0; i < len; i++) {
-               unsigned char nibble;
-
-               /* The CPiA firmware suppresses the use of nDataAvail (nFault LO)
-                * after every second nibble to signal that more
-                * data is available.  (the total number of Bytes that
-                * should be sent is known; if too few are received, an error
-                * will be recorded after a timeout).
-                * This is incompatible with parport_ieee1284_read_nibble(),
-                * which expects to find nFault LO after every second nibble.
-                */
-
-               /* Solution: modify cpia_read_nibble to only check for
-                * nDataAvail before the first nibble is sent.
-                */
-
-               /* Does the error line indicate end of data? */
-               if (((i /*& 1*/) == 0) &&
-                   (parport_read_status(port) & PARPORT_STATUS_ERROR)) {
-                       DBG("%s: No more nibble data (%d bytes)\n",
-                           port->name, i/2);
-                       goto end_of_data;
-               }
-
-               /* Event 7: Set nAutoFd low. */
-               parport_frob_control (port,
-                                     PARPORT_CONTROL_AUTOFD,
-                                     PARPORT_CONTROL_AUTOFD);
-
-               /* Event 9: nAck goes low. */
-               port->ieee1284.phase = IEEE1284_PH_REV_DATA;
-               if (parport_wait_peripheral (port,
-                                            PARPORT_STATUS_ACK, 0)) {
-                       /* Timeout -- no more data? */
-                                DBG("%s: Nibble timeout at event 9 (%d bytes)\n",
-                                port->name, i/2);
-                       parport_frob_control (port, PARPORT_CONTROL_AUTOFD, 0);
-                       break;
-               }
-
-
-               /* Read a nibble. */
-               nibble = parport_read_status (port) >> 3;
-               nibble &= ~8;
-               if ((nibble & 0x10) == 0)
-                       nibble |= 8;
-               nibble &= 0xf;
-
-               /* Event 10: Set nAutoFd high. */
-               parport_frob_control (port, PARPORT_CONTROL_AUTOFD, 0);
-
-               /* Event 11: nAck goes high. */
-               if (parport_wait_peripheral (port,
-                                            PARPORT_STATUS_ACK,
-                                            PARPORT_STATUS_ACK)) {
-                       /* Timeout -- no more data? */
-                       DBG("%s: Nibble timeout at event 11\n",
-                                port->name);
-                       break;
-               }
-
-               if (i & 1) {
-                       /* Second nibble */
-                       byte |= nibble << 4;
-                       *buf++ = byte;
-               } else
-                       byte = nibble;
-       }
-
-       if (i == len) {
-               /* Read the last nibble without checking data avail. */
-               if (parport_read_status (port) & PARPORT_STATUS_ERROR) {
-               end_of_data:
-                       /* Go to reverse idle phase. */
-                       parport_frob_control (port,
-                                             PARPORT_CONTROL_AUTOFD,
-                                             PARPORT_CONTROL_AUTOFD);
-                       port->physport->ieee1284.phase = IEEE1284_PH_REV_IDLE;
-               }
-               else
-                       port->physport->ieee1284.phase = IEEE1284_PH_HBUSY_DAVAIL;
-       }
-
-       return i/2;
-}
-
-/* CPiA nonstandard "Nibble Stream" mode (2 nibbles per cycle, instead of 1)
- * (See CPiA Data sheet p. 31)
- *
- * "Nibble Stream" mode used by CPiA for uploads to non-ECP ports is a
- * nonstandard variant of nibble mode which allows the same (mediocre)
- * data flow of 8 bits per cycle as software-enabled ECP by TRISTATE-capable
- * parallel ports, but works also for  non-TRISTATE-capable ports.
- * (Standard nibble mode only send 4 bits per cycle)
- *
- */
-
-static size_t cpia_read_nibble_stream(struct parport *port,
-                              void *buffer, size_t len,
-                              int flags)
-{
-       int i;
-       unsigned char *buf = buffer;
-       int endseen = 0;
-
-       for (i=0; i < len; i++) {
-               unsigned char nibble[2], byte = 0;
-               int j;
-
-               /* Image Data is complete when 4 consecutive EOI bytes (0xff) are seen */
-               if (endseen > 3 )
-                       break;
-
-               /* Event 7: Set nAutoFd low. */
-               parport_frob_control (port,
-                                     PARPORT_CONTROL_AUTOFD,
-                                     PARPORT_CONTROL_AUTOFD);
-
-               /* Event 9: nAck goes low. */
-               port->ieee1284.phase = IEEE1284_PH_REV_DATA;
-               if (parport_wait_peripheral (port,
-                                            PARPORT_STATUS_ACK, 0)) {
-                       /* Timeout -- no more data? */
-                                DBG("%s: Nibble timeout at event 9 (%d bytes)\n",
-                                port->name, i/2);
-                       parport_frob_control (port, PARPORT_CONTROL_AUTOFD, 0);
-                       break;
-               }
-
-               /* Read lower nibble */
-               nibble[0] = parport_read_status (port) >>3;
-
-               /* Event 10: Set nAutoFd high. */
-               parport_frob_control (port, PARPORT_CONTROL_AUTOFD, 0);
-
-               /* Event 11: nAck goes high. */
-               if (parport_wait_peripheral (port,
-                                            PARPORT_STATUS_ACK,
-                                            PARPORT_STATUS_ACK)) {
-                       /* Timeout -- no more data? */
-                       DBG("%s: Nibble timeout at event 11\n",
-                                port->name);
-                       break;
-               }
-
-               /* Read upper nibble */
-               nibble[1] = parport_read_status (port) >>3;
-
-               /* reassemble the byte */
-               for (j = 0; j < 2 ; j++ ) {
-                       nibble[j] &= ~8;
-                       if ((nibble[j] & 0x10) == 0)
-                               nibble[j] |= 8;
-                       nibble[j] &= 0xf;
-               }
-               byte = (nibble[0] |(nibble[1] << 4));
-               *buf++ = byte;
-
-               if(byte == EOI)
-                 endseen++;
-               else
-                 endseen = 0;
-       }
-       return i;
-}
-
-/****************************************************************************
- *
- *  EndTransferMode
- *
- ***************************************************************************/
-static void EndTransferMode(struct pp_cam_entry *cam)
-{
-       parport_negotiate(cam->port, IEEE1284_MODE_COMPAT);
-}
-
-/****************************************************************************
- *
- *  ForwardSetup
- *
- ***************************************************************************/
-static int ForwardSetup(struct pp_cam_entry *cam)
-{
-       int retry;
-
-       /* The CPiA uses ECP protocol for Downloads from the Host to the camera.
-        * This will be software-emulated if ECP hardware is not present
-        */
-
-       /* the usual camera maximum response time is 10ms, but after receiving
-        * some commands, it needs up to 40ms. (Data Sheet p. 32)*/
-
-       for(retry = 0; retry < 4; ++retry) {
-               if(!parport_negotiate(cam->port, IEEE1284_MODE_ECP)) {
-                       break;
-               }
-               mdelay(10);
-       }
-       if(retry == 4) {
-               DBG("Unable to negotiate IEEE1284 ECP Download mode\n");
-               return -1;
-       }
-       return 0;
-}
-/****************************************************************************
- *
- *  ReverseSetup
- *
- ***************************************************************************/
-static int ReverseSetup(struct pp_cam_entry *cam, int extensibility)
-{
-       int retry;
-       int upload_mode, mode = IEEE1284_MODE_ECP;
-       int transfer_mode = ECP_TRANSFER;
-
-       if (!(cam->port->modes & PARPORT_MODE_ECP) &&
-            !(cam->port->modes & PARPORT_MODE_TRISTATE)) {
-               mode = IEEE1284_MODE_NIBBLE;
-               transfer_mode = NIBBLE_TRANSFER;
-       }
-
-       upload_mode = mode;
-       if(extensibility) mode = UPLOAD_FLAG|transfer_mode|IEEE1284_EXT_LINK;
-
-       /* the usual camera maximum response time is 10ms, but after
-        * receiving some commands, it needs up to 40ms. */
-
-       for(retry = 0; retry < 4; ++retry) {
-               if(!parport_negotiate(cam->port, mode)) {
-                       break;
-               }
-               mdelay(10);
-       }
-       if(retry == 4) {
-               if(extensibility)
-                       DBG("Unable to negotiate upload extensibility mode\n");
-               else
-                       DBG("Unable to negotiate upload mode\n");
-               return -1;
-       }
-       if(extensibility) cam->port->ieee1284.mode = upload_mode;
-       return 0;
-}
-
-/****************************************************************************
- *
- *  WritePacket
- *
- ***************************************************************************/
-static int WritePacket(struct pp_cam_entry *cam, const u8 *packet, size_t size)
-{
-       int retval=0;
-       int size_written;
-
-       if (packet == NULL) {
-               return -EINVAL;
-       }
-       if (ForwardSetup(cam)) {
-               DBG("Write failed in setup\n");
-               return -EIO;
-       }
-       size_written = parport_write(cam->port, packet, size);
-       if(size_written != size) {
-               DBG("Write failed, wrote %d/%d\n", size_written, size);
-               retval = -EIO;
-       }
-       EndTransferMode(cam);
-       return retval;
-}
-
-/****************************************************************************
- *
- *  ReadPacket
- *
- ***************************************************************************/
-static int ReadPacket(struct pp_cam_entry *cam, u8 *packet, size_t size)
-{
-       int retval=0;
-
-       if (packet == NULL) {
-               return -EINVAL;
-       }
-       if (ReverseSetup(cam, 0)) {
-               return -EIO;
-       }
-
-       /* support for CPiA variant nibble reads */
-       if(cam->port->ieee1284.mode == IEEE1284_MODE_NIBBLE) {
-               if(cpia_read_nibble(cam->port, packet, size, 0) != size)
-                       retval = -EIO;
-       } else {
-               if(parport_read(cam->port, packet, size) != size)
-                       retval = -EIO;
-       }
-       EndTransferMode(cam);
-       return retval;
-}
-
-/****************************************************************************
- *
- *  cpia_pp_streamStart
- *
- ***************************************************************************/
-static int cpia_pp_streamStart(void *privdata)
-{
-       struct pp_cam_entry *cam = privdata;
-       DBG("\n");
-       cam->streaming=1;
-       cam->image_ready=0;
-       //if (ReverseSetup(cam,1)) return -EIO;
-       if(cam->stream_irq) cpia_parport_enable_irq(cam->port);
-       return 0;
-}
-
-/****************************************************************************
- *
- *  cpia_pp_streamStop
- *
- ***************************************************************************/
-static int cpia_pp_streamStop(void *privdata)
-{
-       struct pp_cam_entry *cam = privdata;
-
-       DBG("\n");
-       cam->streaming=0;
-       cpia_parport_disable_irq(cam->port);
-       //EndTransferMode(cam);
-
-       return 0;
-}
-
-/****************************************************************************
- *
- *  cpia_pp_streamRead
- *
- ***************************************************************************/
-static int cpia_pp_read(struct parport *port, u8 *buffer, int len)
-{
-       int bytes_read;
-
-       /* support for CPiA variant "nibble stream" reads */
-       if(port->ieee1284.mode == IEEE1284_MODE_NIBBLE)
-               bytes_read = cpia_read_nibble_stream(port,buffer,len,0);
-       else {
-               int new_bytes;
-               for(bytes_read=0; bytes_read<len; bytes_read += new_bytes) {
-                       new_bytes = parport_read(port, buffer+bytes_read,
-                                                len-bytes_read);
-                       if(new_bytes < 0) break;
-               }
-       }
-       return bytes_read;
-}
-
-static int cpia_pp_streamRead(void *privdata, u8 *buffer, int noblock)
-{
-       struct pp_cam_entry *cam = privdata;
-       int read_bytes = 0;
-       int i, endseen, block_size, new_bytes;
-
-       if(cam == NULL) {
-               DBG("Internal driver error: cam is NULL\n");
-               return -EINVAL;
-       }
-       if(buffer == NULL) {
-               DBG("Internal driver error: buffer is NULL\n");
-               return -EINVAL;
-       }
-       //if(cam->streaming) DBG("%d / %d\n", cam->image_ready, noblock);
-       if( cam->stream_irq ) {
-               DBG("%d\n", cam->image_ready);
-               cam->image_ready--;
-       }
-       cam->image_complete=0;
-       if (0/*cam->streaming*/) {
-               if(!cam->image_ready) {
-                       if(noblock) return -EWOULDBLOCK;
-                       interruptible_sleep_on(&cam->wq_stream);
-                       if( signal_pending(current) ) return -EINTR;
-                       DBG("%d\n", cam->image_ready);
-               }
-       } else {
-               if (ReverseSetup(cam, 1)) {
-                       DBG("unable to ReverseSetup\n");
-                       return -EIO;
-               }
-       }
-       endseen = 0;
-       block_size = PARPORT_CHUNK_SIZE;
-       while( !cam->image_complete ) {
-               cond_resched();
-
-               new_bytes = cpia_pp_read(cam->port, buffer, block_size );
-               if( new_bytes <= 0 ) {
-                       break;
-               }
-               i=-1;
-               while(++i<new_bytes && endseen<4) {
-                       if(*buffer==EOI) {
-                               endseen++;
-                       } else {
-                               endseen=0;
-                       }
-                       buffer++;
-               }
-               read_bytes += i;
-               if( endseen==4 ) {
-                       cam->image_complete=1;
-                       break;
-               }
-               if( CPIA_MAX_IMAGE_SIZE-read_bytes <= PARPORT_CHUNK_SIZE ) {
-                       block_size=CPIA_MAX_IMAGE_SIZE-read_bytes;
-               }
-       }
-       EndTransferMode(cam);
-       return cam->image_complete ? read_bytes : -EIO;
-}
-/****************************************************************************
- *
- *  cpia_pp_transferCmd
- *
- ***************************************************************************/
-static int cpia_pp_transferCmd(void *privdata, u8 *command, u8 *data)
-{
-       int err;
-       int retval=0;
-       int databytes;
-       struct pp_cam_entry *cam = privdata;
-
-       if(cam == NULL) {
-               DBG("Internal driver error: cam is NULL\n");
-               return -EINVAL;
-       }
-       if(command == NULL) {
-               DBG("Internal driver error: command is NULL\n");
-               return -EINVAL;
-       }
-       databytes = (((int)command[7])<<8) | command[6];
-       if ((err = WritePacket(cam, command, PACKET_LENGTH)) < 0) {
-               DBG("Error writing command\n");
-               return err;
-       }
-       if(command[0] == DATA_IN) {
-               u8 buffer[8];
-               if(data == NULL) {
-                       DBG("Internal driver error: data is NULL\n");
-                       return -EINVAL;
-               }
-               if((err = ReadPacket(cam, buffer, 8)) < 0) {
-                       DBG("Error reading command result\n");
-                      return err;
-               }
-               memcpy(data, buffer, databytes);
-       } else if(command[0] == DATA_OUT) {
-               if(databytes > 0) {
-                       if(data == NULL) {
-                               DBG("Internal driver error: data is NULL\n");
-                               retval = -EINVAL;
-                       } else {
-                               if((err=WritePacket(cam, data, databytes)) < 0){
-                                       DBG("Error writing command data\n");
-                                       return err;
-                               }
-                       }
-               }
-       } else {
-               DBG("Unexpected first byte of command: %x\n", command[0]);
-               retval = -EINVAL;
-       }
-       return retval;
-}
-
-/****************************************************************************
- *
- *  cpia_pp_open
- *
- ***************************************************************************/
-static int cpia_pp_open(void *privdata)
-{
-       struct pp_cam_entry *cam = (struct pp_cam_entry *)privdata;
-
-       if (cam == NULL)
-               return -EINVAL;
-
-       if(cam->open_count == 0) {
-               if (parport_claim(cam->pdev)) {
-                       DBG("failed to claim the port\n");
-                       return -EBUSY;
-               }
-               parport_negotiate(cam->port, IEEE1284_MODE_COMPAT);
-               parport_data_forward(cam->port);
-               parport_write_control(cam->port, PARPORT_CONTROL_SELECT);
-               udelay(50);
-               parport_write_control(cam->port,
-                                     PARPORT_CONTROL_SELECT
-                                     | PARPORT_CONTROL_INIT);
-       }
-
-       ++cam->open_count;
-
-       return 0;
-}
-
-/****************************************************************************
- *
- *  cpia_pp_registerCallback
- *
- ***************************************************************************/
-static int cpia_pp_registerCallback(void *privdata, void (*cb)(void *cbdata), void *cbdata)
-{
-       struct pp_cam_entry *cam = privdata;
-       int retval = 0;
-
-       if(cam->port->irq != PARPORT_IRQ_NONE) {
-               cam->cb_func = cb;
-               cam->cb_data = cbdata;
-               INIT_WORK(&cam->cb_task, cpia_pp_run_callback);
-       } else {
-               retval = -1;
-       }
-       return retval;
-}
-
-/****************************************************************************
- *
- *  cpia_pp_close
- *
- ***************************************************************************/
-static int cpia_pp_close(void *privdata)
-{
-       struct pp_cam_entry *cam = privdata;
-       if (--cam->open_count == 0) {
-               parport_release(cam->pdev);
-       }
-       return 0;
-}
-
-/****************************************************************************
- *
- *  cpia_pp_register
- *
- ***************************************************************************/
-static int cpia_pp_register(struct parport *port)
-{
-       struct pardevice *pdev = NULL;
-       struct pp_cam_entry *cam;
-       struct cam_data *cpia;
-
-       if (!(port->modes & PARPORT_MODE_PCSPP)) {
-               LOG("port is not supported by CPiA driver\n");
-               return -ENXIO;
-       }
-
-       cam = kzalloc(sizeof(struct pp_cam_entry), GFP_KERNEL);
-       if (cam == NULL) {
-               LOG("failed to allocate camera structure\n");
-               return -ENOMEM;
-       }
-
-       pdev = parport_register_device(port, "cpia_pp", NULL, NULL,
-                                      NULL, 0, cam);
-
-       if (!pdev) {
-               LOG("failed to parport_register_device\n");
-               kfree(cam);
-               return -ENXIO;
-       }
-
-       cam->pdev = pdev;
-       cam->port = port;
-       init_waitqueue_head(&cam->wq_stream);
-
-       cam->streaming = 0;
-       cam->stream_irq = 0;
-
-       if((cpia = cpia_register_camera(&cpia_pp_ops, cam)) == NULL) {
-               LOG("failed to cpia_register_camera\n");
-               parport_unregister_device(pdev);
-               kfree(cam);
-               return -ENXIO;
-       }
-       spin_lock( &cam_list_lock_pp );
-       list_add( &cpia->cam_data_list, &cam_list );
-       spin_unlock( &cam_list_lock_pp );
-
-       return 0;
-}
-
-static void cpia_pp_detach (struct parport *port)
-{
-       struct list_head *tmp;
-       struct cam_data *cpia = NULL;
-       struct pp_cam_entry *cam;
-
-       spin_lock( &cam_list_lock_pp );
-       list_for_each (tmp, &cam_list) {
-               cpia = list_entry(tmp, struct cam_data, cam_data_list);
-               cam = (struct pp_cam_entry *) cpia->lowlevel_data;
-               if (cam && cam->port->number == port->number) {
-                       list_del(&cpia->cam_data_list);
-                       break;
-               }
-               cpia = NULL;
-       }
-       spin_unlock( &cam_list_lock_pp );
-
-       if (!cpia) {
-               DBG("cpia_pp_detach failed to find cam_data in cam_list\n");
-               return;
-       }
-
-       cam = (struct pp_cam_entry *) cpia->lowlevel_data;
-       cpia_unregister_camera(cpia);
-       if(cam->open_count > 0)
-               cpia_pp_close(cam);
-       parport_unregister_device(cam->pdev);
-       cpia->lowlevel_data = NULL;
-       kfree(cam);
-}
-
-static void cpia_pp_attach (struct parport *port)
-{
-       unsigned int i;
-
-       switch (parport_nr[0])
-       {
-       case PPCPIA_PARPORT_UNSPEC:
-       case PPCPIA_PARPORT_AUTO:
-               if (port->probe_info[0].class != PARPORT_CLASS_MEDIA ||
-                   port->probe_info[0].cmdset == NULL ||
-                   strncmp(port->probe_info[0].cmdset, "CPIA_1", 6) != 0)
-                       return;
-
-               cpia_pp_register(port);
-
-               break;
-
-       default:
-               for (i = 0; i < PARPORT_MAX; ++i) {
-                       if (port->number == parport_nr[i]) {
-                               cpia_pp_register(port);
-                               break;
-                       }
-               }
-               break;
-       }
-}
-
-static struct parport_driver cpia_pp_driver = {
-       .name = "cpia_pp",
-       .attach = cpia_pp_attach,
-       .detach = cpia_pp_detach,
-};
-
-static int __init cpia_pp_init(void)
-{
-       printk(KERN_INFO "%s v%d.%d.%d\n",ABOUT,
-              CPIA_PP_MAJ_VER,CPIA_PP_MIN_VER,CPIA_PP_PATCH_VER);
-
-       if(parport_nr[0] == PPCPIA_PARPORT_OFF) {
-               printk("  disabled\n");
-               return 0;
-       }
-
-       spin_lock_init( &cam_list_lock_pp );
-
-       if (parport_register_driver (&cpia_pp_driver)) {
-               LOG ("unable to register with parport\n");
-               return -EIO;
-       }
-       return 0;
-}
-
-static int __init cpia_init(void)
-{
-       if (parport[0]) {
-               /* The user gave some parameters.  Let's see what they were. */
-               if (!strncmp(parport[0], "auto", 4)) {
-                       parport_nr[0] = PPCPIA_PARPORT_AUTO;
-               } else {
-                       int n;
-                       for (n = 0; n < PARPORT_MAX && parport[n]; n++) {
-                               if (!strncmp(parport[n], "none", 4)) {
-                                       parport_nr[n] = PPCPIA_PARPORT_NONE;
-                               } else {
-                                       char *ep;
-                                       unsigned long r = simple_strtoul(parport[n], &ep, 0);
-                                       if (ep != parport[n]) {
-                                               parport_nr[n] = r;
-                                       } else {
-                                               LOG("bad port specifier `%s'\n", parport[n]);
-                                               return -ENODEV;
-                                       }
-                               }
-                       }
-               }
-       }
-       return cpia_pp_init();
-}
-
-static void __exit cpia_cleanup(void)
-{
-       parport_unregister_driver(&cpia_pp_driver);
-       return;
-}
-
-module_init(cpia_init);
-module_exit(cpia_cleanup);
diff --git a/drivers/media/video/cpia_usb.c b/drivers/media/video/cpia_usb.c
deleted file mode 100644 (file)
index 58d193f..0000000
+++ /dev/null
@@ -1,640 +0,0 @@
-/*
- * cpia_usb CPiA USB driver
- *
- * Supports CPiA based parallel port Video Camera's.
- *
- * Copyright (C) 1999        Jochen Scharrlach <Jochen.Scharrlach@schwaben.de>
- * Copyright (C) 1999, 2000  Johannes Erdfelt <johannes@erdfelt.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
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- */
-
-/* define _CPIA_DEBUG_ for verbose debug output (see cpia.h) */
-/* #define _CPIA_DEBUG_  1 */
-
-#include <linux/module.h>
-#include <linux/kernel.h>
-#include <linux/init.h>
-#include <linux/wait.h>
-#include <linux/list.h>
-#include <linux/slab.h>
-#include <linux/vmalloc.h>
-#include <linux/usb.h>
-
-#include "cpia.h"
-
-#define USB_REQ_CPIA_GRAB_FRAME                        0xC1
-#define USB_REQ_CPIA_UPLOAD_FRAME              0xC2
-#define  WAIT_FOR_NEXT_FRAME                   0
-#define  FORCE_FRAME_UPLOAD                    1
-
-#define FRAMES_PER_DESC                10
-#define FRAME_SIZE_PER_DESC    960     /* Shouldn't be hardcoded */
-#define CPIA_NUMSBUF           2
-#define STREAM_BUF_SIZE                (PAGE_SIZE * 4)
-#define SCRATCH_BUF_SIZE       (STREAM_BUF_SIZE * 2)
-
-struct cpia_sbuf {
-       char *data;
-       struct urb *urb;
-};
-
-#define FRAMEBUF_LEN (CPIA_MAX_FRAME_SIZE+100)
-enum framebuf_status {
-       FRAME_EMPTY,
-       FRAME_READING,
-       FRAME_READY,
-       FRAME_ERROR,
-};
-
-struct framebuf {
-       int length;
-       enum framebuf_status status;
-       u8 data[FRAMEBUF_LEN];
-       struct framebuf *next;
-};
-
-struct usb_cpia {
-       /* Device structure */
-       struct usb_device *dev;
-
-       unsigned char iface;
-       wait_queue_head_t wq_stream;
-
-       int cursbuf;            /* Current receiving sbuf */
-       struct cpia_sbuf sbuf[CPIA_NUMSBUF];            /* Double buffering */
-
-       int streaming;
-       int open;
-       int present;
-       struct framebuf *buffers[3];
-       struct framebuf *curbuff, *workbuff;
-};
-
-static int cpia_usb_open(void *privdata);
-static int cpia_usb_registerCallback(void *privdata, void (*cb) (void *cbdata),
-                                    void *cbdata);
-static int cpia_usb_transferCmd(void *privdata, u8 *command, u8 *data);
-static int cpia_usb_streamStart(void *privdata);
-static int cpia_usb_streamStop(void *privdata);
-static int cpia_usb_streamRead(void *privdata, u8 *frame, int noblock);
-static int cpia_usb_close(void *privdata);
-
-#define ABOUT "USB driver for Vision CPiA based cameras"
-
-static struct cpia_camera_ops cpia_usb_ops = {
-       cpia_usb_open,
-       cpia_usb_registerCallback,
-       cpia_usb_transferCmd,
-       cpia_usb_streamStart,
-       cpia_usb_streamStop,
-       cpia_usb_streamRead,
-       cpia_usb_close,
-       0,
-       THIS_MODULE
-};
-
-static LIST_HEAD(cam_list);
-static spinlock_t cam_list_lock_usb;
-
-static void cpia_usb_complete(struct urb *urb)
-{
-       int i;
-       char *cdata;
-       struct usb_cpia *ucpia;
-
-       if (!urb || !urb->context)
-               return;
-
-       ucpia = (struct usb_cpia *) urb->context;
-
-       if (!ucpia->dev || !ucpia->streaming || !ucpia->present || !ucpia->open)
-               return;
-
-       if (ucpia->workbuff->status == FRAME_EMPTY) {
-               ucpia->workbuff->status = FRAME_READING;
-               ucpia->workbuff->length = 0;
-       }
-
-       for (i = 0; i < urb->number_of_packets; i++) {
-               int n = urb->iso_frame_desc[i].actual_length;
-               int st = urb->iso_frame_desc[i].status;
-
-               cdata = urb->transfer_buffer + urb->iso_frame_desc[i].offset;
-
-               if (st)
-                       printk(KERN_DEBUG "cpia data error: [%d] len=%d, status=%X\n", i, n, st);
-
-               if (FRAMEBUF_LEN < ucpia->workbuff->length + n) {
-                       printk(KERN_DEBUG "cpia: scratch buf overflow!scr_len: %d, n: %d\n", ucpia->workbuff->length, n);
-                       return;
-               }
-
-               if (n) {
-                       if ((ucpia->workbuff->length > 0) ||
-                           (0x19 == cdata[0] && 0x68 == cdata[1])) {
-                               memcpy(ucpia->workbuff->data + ucpia->workbuff->length, cdata, n);
-                               ucpia->workbuff->length += n;
-                       } else
-                               DBG("Ignoring packet!\n");
-               } else {
-                       if (ucpia->workbuff->length > 4 &&
-                           0xff == ucpia->workbuff->data[ucpia->workbuff->length-1] &&
-                           0xff == ucpia->workbuff->data[ucpia->workbuff->length-2] &&
-                           0xff == ucpia->workbuff->data[ucpia->workbuff->length-3] &&
-                           0xff == ucpia->workbuff->data[ucpia->workbuff->length-4]) {
-                               ucpia->workbuff->status = FRAME_READY;
-                               ucpia->curbuff = ucpia->workbuff;
-                               ucpia->workbuff = ucpia->workbuff->next;
-                               ucpia->workbuff->status = FRAME_EMPTY;
-                               ucpia->workbuff->length = 0;
-
-                               if (waitqueue_active(&ucpia->wq_stream))
-                                       wake_up_interruptible(&ucpia->wq_stream);
-                       }
-               }
-       }
-
-       /* resubmit */
-       urb->dev = ucpia->dev;
-       if ((i = usb_submit_urb(urb, GFP_ATOMIC)) != 0)
-               printk(KERN_ERR "%s: usb_submit_urb ret %d\n", __func__,  i);
-}
-
-static int cpia_usb_open(void *privdata)
-{
-       struct usb_cpia *ucpia = (struct usb_cpia *) privdata;
-       struct urb *urb;
-       int ret, retval = 0, fx, err;
-
-       if (!ucpia)
-               return -EINVAL;
-
-       ucpia->sbuf[0].data = kmalloc(FRAMES_PER_DESC * FRAME_SIZE_PER_DESC, GFP_KERNEL);
-       if (!ucpia->sbuf[0].data)
-               return -EINVAL;
-
-       ucpia->sbuf[1].data = kmalloc(FRAMES_PER_DESC * FRAME_SIZE_PER_DESC, GFP_KERNEL);
-       if (!ucpia->sbuf[1].data) {
-               retval = -EINVAL;
-               goto error_0;
-       }
-
-       ret = usb_set_interface(ucpia->dev, ucpia->iface, 3);
-       if (ret < 0) {
-               printk(KERN_ERR "cpia_usb_open: usb_set_interface error (ret = %d)\n", ret);
-               retval = -EBUSY;
-               goto error_1;
-       }
-
-       ucpia->buffers[0]->status = FRAME_EMPTY;
-       ucpia->buffers[0]->length = 0;
-       ucpia->buffers[1]->status = FRAME_EMPTY;
-       ucpia->buffers[1]->length = 0;
-       ucpia->buffers[2]->status = FRAME_EMPTY;
-       ucpia->buffers[2]->length = 0;
-       ucpia->curbuff = ucpia->buffers[0];
-       ucpia->workbuff = ucpia->buffers[1];
-
-       /* We double buffer the Iso lists, and also know the polling
-        * interval is every frame (1 == (1 << (bInterval -1))).
-        */
-       urb = usb_alloc_urb(FRAMES_PER_DESC, GFP_KERNEL);
-       if (!urb) {
-               printk(KERN_ERR "cpia_init_isoc: usb_alloc_urb 0\n");
-               retval = -ENOMEM;
-               goto error_1;
-       }
-
-       ucpia->sbuf[0].urb = urb;
-       urb->dev = ucpia->dev;
-       urb->context = ucpia;
-       urb->pipe = usb_rcvisocpipe(ucpia->dev, 1);
-       urb->transfer_flags = URB_ISO_ASAP;
-       urb->transfer_buffer = ucpia->sbuf[0].data;
-       urb->complete = cpia_usb_complete;
-       urb->number_of_packets = FRAMES_PER_DESC;
-       urb->interval = 1;
-       urb->transfer_buffer_length = FRAME_SIZE_PER_DESC * FRAMES_PER_DESC;
-       for (fx = 0; fx < FRAMES_PER_DESC; fx++) {
-               urb->iso_frame_desc[fx].offset = FRAME_SIZE_PER_DESC * fx;
-               urb->iso_frame_desc[fx].length = FRAME_SIZE_PER_DESC;
-       }
-
-       urb = usb_alloc_urb(FRAMES_PER_DESC, GFP_KERNEL);
-       if (!urb) {
-               printk(KERN_ERR "cpia_init_isoc: usb_alloc_urb 1\n");
-               retval = -ENOMEM;
-               goto error_urb0;
-       }
-
-       ucpia->sbuf[1].urb = urb;
-       urb->dev = ucpia->dev;
-       urb->context = ucpia;
-       urb->pipe = usb_rcvisocpipe(ucpia->dev, 1);
-       urb->transfer_flags = URB_ISO_ASAP;
-       urb->transfer_buffer = ucpia->sbuf[1].data;
-       urb->complete = cpia_usb_complete;
-       urb->number_of_packets = FRAMES_PER_DESC;
-       urb->interval = 1;
-       urb->transfer_buffer_length = FRAME_SIZE_PER_DESC * FRAMES_PER_DESC;
-       for (fx = 0; fx < FRAMES_PER_DESC; fx++) {
-               urb->iso_frame_desc[fx].offset = FRAME_SIZE_PER_DESC * fx;
-               urb->iso_frame_desc[fx].length = FRAME_SIZE_PER_DESC;
-       }
-
-       /* queue the ISO urbs, and resubmit in the completion handler */
-       err = usb_submit_urb(ucpia->sbuf[0].urb, GFP_KERNEL);
-       if (err) {
-               printk(KERN_ERR "cpia_init_isoc: usb_submit_urb 0 ret %d\n",
-                       err);
-               goto error_urb1;
-       }
-       err = usb_submit_urb(ucpia->sbuf[1].urb, GFP_KERNEL);
-       if (err) {
-               printk(KERN_ERR "cpia_init_isoc: usb_submit_urb 1 ret %d\n",
-                       err);
-               goto error_urb1;
-       }
-
-       ucpia->streaming = 1;
-       ucpia->open = 1;
-
-       return 0;
-
-error_urb1:            /* free urb 1 */
-       usb_free_urb(ucpia->sbuf[1].urb);
-       ucpia->sbuf[1].urb = NULL;
-error_urb0:            /* free urb 0 */
-       usb_free_urb(ucpia->sbuf[0].urb);
-       ucpia->sbuf[0].urb = NULL;
-error_1:
-       kfree (ucpia->sbuf[1].data);
-       ucpia->sbuf[1].data = NULL;
-error_0:
-       kfree (ucpia->sbuf[0].data);
-       ucpia->sbuf[0].data = NULL;
-
-       return retval;
-}
-
-//
-// convenience functions
-//
-
-/****************************************************************************
- *
- *  WritePacket
- *
- ***************************************************************************/
-static int WritePacket(struct usb_device *udev, const u8 *packet, u8 *buf, size_t size)
-{
-       if (!packet)
-               return -EINVAL;
-
-       return usb_control_msg(udev, usb_sndctrlpipe(udev, 0),
-                        packet[1] + (packet[0] << 8),
-                        USB_TYPE_VENDOR | USB_RECIP_DEVICE,
-                        packet[2] + (packet[3] << 8),
-                        packet[4] + (packet[5] << 8), buf, size, 1000);
-}
-
-/****************************************************************************
- *
- *  ReadPacket
- *
- ***************************************************************************/
-static int ReadPacket(struct usb_device *udev, u8 *packet, u8 *buf, size_t size)
-{
-       if (!packet || size <= 0)
-               return -EINVAL;
-
-       return usb_control_msg(udev, usb_rcvctrlpipe(udev, 0),
-                        packet[1] + (packet[0] << 8),
-                        USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
-                        packet[2] + (packet[3] << 8),
-                        packet[4] + (packet[5] << 8), buf, size, 1000);
-}
-
-static int cpia_usb_transferCmd(void *privdata, u8 *command, u8 *data)
-{
-       int err = 0;
-       int databytes;
-       struct usb_cpia *ucpia = (struct usb_cpia *)privdata;
-       struct usb_device *udev = ucpia->dev;
-
-       if (!udev) {
-               DBG("Internal driver error: udev is NULL\n");
-               return -EINVAL;
-       }
-
-       if (!command) {
-               DBG("Internal driver error: command is NULL\n");
-               return -EINVAL;
-       }
-
-       databytes = (((int)command[7])<<8) | command[6];
-
-       if (command[0] == DATA_IN) {
-               u8 buffer[8];
-
-               if (!data) {
-                       DBG("Internal driver error: data is NULL\n");
-                       return -EINVAL;
-               }
-
-               err = ReadPacket(udev, command, buffer, 8);
-               if (err < 0)
-                       return err;
-
-               memcpy(data, buffer, databytes);
-       } else if(command[0] == DATA_OUT)
-               WritePacket(udev, command, data, databytes);
-       else {
-               DBG("Unexpected first byte of command: %x\n", command[0]);
-               err = -EINVAL;
-       }
-
-       return 0;
-}
-
-static int cpia_usb_registerCallback(void *privdata, void (*cb) (void *cbdata),
-       void *cbdata)
-{
-       return -ENODEV;
-}
-
-static int cpia_usb_streamStart(void *privdata)
-{
-       return -ENODEV;
-}
-
-static int cpia_usb_streamStop(void *privdata)
-{
-       return -ENODEV;
-}
-
-static int cpia_usb_streamRead(void *privdata, u8 *frame, int noblock)
-{
-       struct usb_cpia *ucpia = (struct usb_cpia *) privdata;
-       struct framebuf *mybuff;
-
-       if (!ucpia || !ucpia->present)
-               return -1;
-
-       if (ucpia->curbuff->status != FRAME_READY)
-               interruptible_sleep_on(&ucpia->wq_stream);
-       else
-               DBG("Frame already waiting!\n");
-
-       mybuff = ucpia->curbuff;
-
-       if (!mybuff)
-               return -1;
-
-       if (mybuff->status != FRAME_READY || mybuff->length < 4) {
-               DBG("Something went wrong!\n");
-               return -1;
-       }
-
-       memcpy(frame, mybuff->data, mybuff->length);
-       mybuff->status = FRAME_EMPTY;
-
-/*   DBG("read done, %d bytes, Header: %x/%x, Footer: %x%x%x%x\n",  */
-/*       mybuff->length, frame[0], frame[1], */
-/*       frame[mybuff->length-4], frame[mybuff->length-3],  */
-/*       frame[mybuff->length-2], frame[mybuff->length-1]); */
-
-       return mybuff->length;
-}
-
-static void cpia_usb_free_resources(struct usb_cpia *ucpia, int try)
-{
-       if (!ucpia->streaming)
-               return;
-
-       ucpia->streaming = 0;
-
-       /* Set packet size to 0 */
-       if (try) {
-               int ret;
-
-               ret = usb_set_interface(ucpia->dev, ucpia->iface, 0);
-               if (ret < 0) {
-                       printk(KERN_ERR "usb_set_interface error (ret = %d)\n", ret);
-                       return;
-               }
-       }
-
-       /* Unschedule all of the iso td's */
-       if (ucpia->sbuf[1].urb) {
-               usb_kill_urb(ucpia->sbuf[1].urb);
-               usb_free_urb(ucpia->sbuf[1].urb);
-               ucpia->sbuf[1].urb = NULL;
-       }
-
-       kfree(ucpia->sbuf[1].data);
-       ucpia->sbuf[1].data = NULL;
-
-       if (ucpia->sbuf[0].urb) {
-               usb_kill_urb(ucpia->sbuf[0].urb);
-               usb_free_urb(ucpia->sbuf[0].urb);
-               ucpia->sbuf[0].urb = NULL;
-       }
-
-       kfree(ucpia->sbuf[0].data);
-       ucpia->sbuf[0].data = NULL;
-}
-
-static int cpia_usb_close(void *privdata)
-{
-       struct usb_cpia *ucpia = (struct usb_cpia *) privdata;
-
-       if(!ucpia)
-               return -ENODEV;
-
-       ucpia->open = 0;
-
-       /* ucpia->present = 0 protects against trying to reset the
-        * alt setting if camera is physically disconnected while open */
-       cpia_usb_free_resources(ucpia, ucpia->present);
-
-       return 0;
-}
-
-/* Probing and initializing */
-
-static int cpia_probe(struct usb_interface *intf,
-                     const struct usb_device_id *id)
-{
-       struct usb_device *udev = interface_to_usbdev(intf);
-       struct usb_host_interface *interface;
-       struct usb_cpia *ucpia;
-       struct cam_data *cam;
-       int ret;
-
-       /* A multi-config CPiA camera? */
-       if (udev->descriptor.bNumConfigurations != 1)
-               return -ENODEV;
-
-       interface = intf->cur_altsetting;
-
-       printk(KERN_INFO "USB CPiA camera found\n");
-
-       ucpia = kzalloc(sizeof(*ucpia), GFP_KERNEL);
-       if (!ucpia) {
-               printk(KERN_ERR "couldn't kmalloc cpia struct\n");
-               return -ENOMEM;
-       }
-
-       ucpia->dev = udev;
-       ucpia->iface = interface->desc.bInterfaceNumber;
-       init_waitqueue_head(&ucpia->wq_stream);
-
-       ucpia->buffers[0] = vmalloc(sizeof(*ucpia->buffers[0]));
-       if (!ucpia->buffers[0]) {
-               printk(KERN_ERR "couldn't vmalloc frame buffer 0\n");
-               goto fail_alloc_0;
-       }
-
-       ucpia->buffers[1] = vmalloc(sizeof(*ucpia->buffers[1]));
-       if (!ucpia->buffers[1]) {
-               printk(KERN_ERR "couldn't vmalloc frame buffer 1\n");
-               goto fail_alloc_1;
-       }
-
-       ucpia->buffers[2] = vmalloc(sizeof(*ucpia->buffers[2]));
-       if (!ucpia->buffers[2]) {
-               printk(KERN_ERR "couldn't vmalloc frame buffer 2\n");
-               goto fail_alloc_2;
-       }
-
-       ucpia->buffers[0]->next = ucpia->buffers[1];
-       ucpia->buffers[1]->next = ucpia->buffers[2];
-       ucpia->buffers[2]->next = ucpia->buffers[0];
-
-       ret = usb_set_interface(udev, ucpia->iface, 0);
-       if (ret < 0) {
-               printk(KERN_ERR "cpia_probe: usb_set_interface error (ret = %d)\n", ret);
-               /* goto fail_all; */
-       }
-
-       /* Before register_camera, important */
-       ucpia->present = 1;
-
-       cam = cpia_register_camera(&cpia_usb_ops, ucpia);
-       if (!cam) {
-               LOG("failed to cpia_register_camera\n");
-               goto fail_all;
-       }
-
-       spin_lock( &cam_list_lock_usb );
-       list_add( &cam->cam_data_list, &cam_list );
-       spin_unlock( &cam_list_lock_usb );
-
-       usb_set_intfdata(intf, cam);
-       return 0;
-
-fail_all:
-       vfree(ucpia->buffers[2]);
-       ucpia->buffers[2] = NULL;
-fail_alloc_2:
-       vfree(ucpia->buffers[1]);
-       ucpia->buffers[1] = NULL;
-fail_alloc_1:
-       vfree(ucpia->buffers[0]);
-       ucpia->buffers[0] = NULL;
-fail_alloc_0:
-       kfree(ucpia);
-       return -EIO;
-}
-
-static void cpia_disconnect(struct usb_interface *intf);
-
-static struct usb_device_id cpia_id_table [] = {
-       { USB_DEVICE(0x0553, 0x0002) },
-       { USB_DEVICE(0x0813, 0x0001) },
-       { }                                     /* Terminating entry */
-};
-
-MODULE_DEVICE_TABLE (usb, cpia_id_table);
-MODULE_LICENSE("GPL");
-
-
-static struct usb_driver cpia_driver = {
-       .name           = "cpia",
-       .probe          = cpia_probe,
-       .disconnect     = cpia_disconnect,
-       .id_table       = cpia_id_table,
-};
-
-static void cpia_disconnect(struct usb_interface *intf)
-{
-       struct cam_data *cam = usb_get_intfdata(intf);
-       struct usb_cpia *ucpia;
-
-       usb_set_intfdata(intf, NULL);
-       if (!cam)
-               return;
-
-       ucpia = (struct usb_cpia *) cam->lowlevel_data;
-       spin_lock( &cam_list_lock_usb );
-       list_del(&cam->cam_data_list);
-       spin_unlock( &cam_list_lock_usb );
-
-       ucpia->present = 0;
-
-       cpia_unregister_camera(cam);
-       if(ucpia->open)
-               cpia_usb_close(cam->lowlevel_data);
-
-       ucpia->curbuff->status = FRAME_ERROR;
-
-       if (waitqueue_active(&ucpia->wq_stream))
-               wake_up_interruptible(&ucpia->wq_stream);
-
-       ucpia->curbuff = ucpia->workbuff = NULL;
-
-       vfree(ucpia->buffers[2]);
-       ucpia->buffers[2] = NULL;
-
-       vfree(ucpia->buffers[1]);
-       ucpia->buffers[1] = NULL;
-
-       vfree(ucpia->buffers[0]);
-       ucpia->buffers[0] = NULL;
-
-       cam->lowlevel_data = NULL;
-       kfree(ucpia);
-}
-
-static int __init usb_cpia_init(void)
-{
-       printk(KERN_INFO "%s v%d.%d.%d\n",ABOUT,
-              CPIA_USB_MAJ_VER,CPIA_USB_MIN_VER,CPIA_USB_PATCH_VER);
-
-       spin_lock_init(&cam_list_lock_usb);
-       return usb_register(&cpia_driver);
-}
-
-static void __exit usb_cpia_cleanup(void)
-{
-       usb_deregister(&cpia_driver);
-}
-
-
-module_init (usb_cpia_init);
-module_exit (usb_cpia_cleanup);
-
index 335311a98fdcb0d4451c8fa2bfd604f937bae637..303c52cfe915ad1960915a72375478f7cf312f6d 100644 (file)
@@ -51,6 +51,8 @@ source "drivers/staging/cx25821/Kconfig"
 
 source "drivers/staging/tm6000/Kconfig"
 
+source "drivers/staging/cpia/Kconfig"
+
 source "drivers/staging/usbip/Kconfig"
 
 source "drivers/staging/winbond/Kconfig"
index e3f1e1b6095e5e790db3b9af1de06e7e29d4f9b7..ddcac245c99a9d8c96f4ba502955e146e3c50b29 100644 (file)
@@ -8,6 +8,7 @@ obj-$(CONFIG_SLICOSS)           += slicoss/
 obj-$(CONFIG_VIDEO_GO7007)     += go7007/
 obj-$(CONFIG_VIDEO_CX25821)    += cx25821/
 obj-$(CONFIG_VIDEO_TM6000)     += tm6000/
+obj-$(CONFIG_VIDEO_CPIA)       += cpia/
 obj-$(CONFIG_LIRC_STAGING)     += lirc/
 obj-$(CONFIG_USB_IP_COMMON)    += usbip/
 obj-$(CONFIG_W35UND)           += winbond/
diff --git a/drivers/staging/cpia/Kconfig b/drivers/staging/cpia/Kconfig
new file mode 100644 (file)
index 0000000..205d247
--- /dev/null
@@ -0,0 +1,39 @@
+config VIDEO_CPIA
+       tristate "CPiA Video For Linux (DEPRECATED)"
+       depends on VIDEO_V4L1
+       default n
+       ---help---
+         This driver is DEPRECATED please use the gspca cpia1 module
+         instead. Note that you need atleast version 0.6.4 of libv4l for
+         the cpia1 gspca module.
+
+         This is the video4linux driver for cameras based on Vision's CPiA
+         (Colour Processor Interface ASIC), such as the Creative Labs Video
+         Blaster Webcam II. If you have one of these cameras, say Y here
+         and select parallel port and/or USB lowlevel support below,
+         otherwise say N. This will not work with the Creative Webcam III.
+
+         Please read <file:Documentation/video4linux/README.cpia> for more
+         information.
+
+         This driver is also available as a module (cpia).
+
+config VIDEO_CPIA_PP
+       tristate "CPiA Parallel Port Lowlevel Support"
+       depends on PARPORT_1284 && VIDEO_CPIA && PARPORT
+       help
+         This is the lowlevel parallel port support for cameras based on
+         Vision's CPiA (Colour Processor Interface ASIC), such as the
+         Creative Webcam II. If you have the parallel port version of one
+         of these cameras, say Y here, otherwise say N. It is also available
+         as a module (cpia_pp).
+
+config VIDEO_CPIA_USB
+       tristate "CPiA USB Lowlevel Support"
+       depends on VIDEO_CPIA && USB
+       help
+         This is the lowlevel USB support for cameras based on Vision's CPiA
+         (Colour Processor Interface ASIC), such as the Creative Webcam II.
+         If you have the USB version of one of these cameras, say Y here,
+         otherwise say N. This will not work with the Creative Webcam III.
+         It is also available as a module (cpia_usb).
diff --git a/drivers/staging/cpia/Makefile b/drivers/staging/cpia/Makefile
new file mode 100644 (file)
index 0000000..89e52f1
--- /dev/null
@@ -0,0 +1,5 @@
+obj-$(CONFIG_VIDEO_CPIA) += cpia.o
+obj-$(CONFIG_VIDEO_CPIA_PP) += cpia_pp.o
+obj-$(CONFIG_VIDEO_CPIA_USB) += cpia_usb.o
+
+EXTRA_CFLAGS += -Idrivers/media/video
diff --git a/drivers/staging/cpia/TODO b/drivers/staging/cpia/TODO
new file mode 100644 (file)
index 0000000..ccb1c07
--- /dev/null
@@ -0,0 +1,8 @@
+This is an obsolete driver for some cpia-based webcams that use the parallel port.
+We couldn't find anyone with this hardware in order to port it to use V4L2.
+
+Also, parallel-port webcams are obsolete nowadays.
+
+If nobody take care on it, the driver will be removed for 2.6.38.
+
+Please send patches to linux-media@vger.kernel.org
diff --git a/drivers/staging/cpia/cpia.c b/drivers/staging/cpia/cpia.c
new file mode 100644 (file)
index 0000000..933ae4c
--- /dev/null
@@ -0,0 +1,4032 @@
+/*
+ * cpia CPiA driver
+ *
+ * Supports CPiA based Video Camera's.
+ *
+ * (C) Copyright 1999-2000 Peter Pregler
+ * (C) Copyright 1999-2000 Scott J. Bertin
+ * (C) Copyright 1999-2000 Johannes Erdfelt <johannes@erdfelt.com>
+ * (C) Copyright 2000 STMicroelectronics
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+/* define _CPIA_DEBUG_ for verbose debug output (see cpia.h) */
+/* #define _CPIA_DEBUG_  1 */
+
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/fs.h>
+#include <linux/vmalloc.h>
+#include <linux/sched.h>
+#include <linux/seq_file.h>
+#include <linux/slab.h>
+#include <linux/proc_fs.h>
+#include <linux/ctype.h>
+#include <linux/pagemap.h>
+#include <linux/delay.h>
+#include <asm/io.h>
+#include <linux/mutex.h>
+
+#include "cpia.h"
+
+static int video_nr = -1;
+
+#ifdef MODULE
+module_param(video_nr, int, 0);
+MODULE_AUTHOR("Scott J. Bertin <sbertin@securenym.net> & Peter Pregler <Peter_Pregler@email.com> & Johannes Erdfelt <johannes@erdfelt.com>");
+MODULE_DESCRIPTION("V4L-driver for Vision CPiA based cameras");
+MODULE_LICENSE("GPL");
+MODULE_SUPPORTED_DEVICE("video");
+#endif
+
+static unsigned short colorspace_conv;
+module_param(colorspace_conv, ushort, 0444);
+MODULE_PARM_DESC(colorspace_conv,
+                " Colorspace conversion:"
+                "\n  0 = disable, 1 = enable"
+                "\n  Default value is 0"
+                );
+
+#define ABOUT "V4L-Driver for Vision CPiA based cameras"
+
+#define CPIA_MODULE_CPIA                       (0<<5)
+#define CPIA_MODULE_SYSTEM                     (1<<5)
+#define CPIA_MODULE_VP_CTRL                    (5<<5)
+#define CPIA_MODULE_CAPTURE                    (6<<5)
+#define CPIA_MODULE_DEBUG                      (7<<5)
+
+#define INPUT (DATA_IN << 8)
+#define OUTPUT (DATA_OUT << 8)
+
+#define CPIA_COMMAND_GetCPIAVersion    (INPUT | CPIA_MODULE_CPIA | 1)
+#define CPIA_COMMAND_GetPnPID          (INPUT | CPIA_MODULE_CPIA | 2)
+#define CPIA_COMMAND_GetCameraStatus   (INPUT | CPIA_MODULE_CPIA | 3)
+#define CPIA_COMMAND_GotoHiPower       (OUTPUT | CPIA_MODULE_CPIA | 4)
+#define CPIA_COMMAND_GotoLoPower       (OUTPUT | CPIA_MODULE_CPIA | 5)
+#define CPIA_COMMAND_GotoSuspend       (OUTPUT | CPIA_MODULE_CPIA | 7)
+#define CPIA_COMMAND_GotoPassThrough   (OUTPUT | CPIA_MODULE_CPIA | 8)
+#define CPIA_COMMAND_ModifyCameraStatus        (OUTPUT | CPIA_MODULE_CPIA | 10)
+
+#define CPIA_COMMAND_ReadVCRegs                (INPUT | CPIA_MODULE_SYSTEM | 1)
+#define CPIA_COMMAND_WriteVCReg                (OUTPUT | CPIA_MODULE_SYSTEM | 2)
+#define CPIA_COMMAND_ReadMCPorts       (INPUT | CPIA_MODULE_SYSTEM | 3)
+#define CPIA_COMMAND_WriteMCPort       (OUTPUT | CPIA_MODULE_SYSTEM | 4)
+#define CPIA_COMMAND_SetBaudRate       (OUTPUT | CPIA_MODULE_SYSTEM | 5)
+#define CPIA_COMMAND_SetECPTiming      (OUTPUT | CPIA_MODULE_SYSTEM | 6)
+#define CPIA_COMMAND_ReadIDATA         (INPUT | CPIA_MODULE_SYSTEM | 7)
+#define CPIA_COMMAND_WriteIDATA                (OUTPUT | CPIA_MODULE_SYSTEM | 8)
+#define CPIA_COMMAND_GenericCall       (OUTPUT | CPIA_MODULE_SYSTEM | 9)
+#define CPIA_COMMAND_I2CStart          (OUTPUT | CPIA_MODULE_SYSTEM | 10)
+#define CPIA_COMMAND_I2CStop           (OUTPUT | CPIA_MODULE_SYSTEM | 11)
+#define CPIA_COMMAND_I2CWrite          (OUTPUT | CPIA_MODULE_SYSTEM | 12)
+#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)
+#define CPIA_COMMAND_SetSensorFPS      (OUTPUT | CPIA_MODULE_VP_CTRL | 7)
+#define CPIA_COMMAND_SetVPDefaults     (OUTPUT | CPIA_MODULE_VP_CTRL | 8)
+#define CPIA_COMMAND_SetApcor          (OUTPUT | CPIA_MODULE_VP_CTRL | 9)
+#define CPIA_COMMAND_SetFlickerCtrl    (OUTPUT | CPIA_MODULE_VP_CTRL | 10)
+#define CPIA_COMMAND_SetVLOffset       (OUTPUT | CPIA_MODULE_VP_CTRL | 11)
+#define CPIA_COMMAND_GetColourParams   (INPUT | CPIA_MODULE_VP_CTRL | 16)
+#define CPIA_COMMAND_GetColourBalance  (INPUT | CPIA_MODULE_VP_CTRL | 17)
+#define CPIA_COMMAND_GetExposure       (INPUT | CPIA_MODULE_VP_CTRL | 18)
+#define CPIA_COMMAND_SetSensorMatrix   (OUTPUT | CPIA_MODULE_VP_CTRL | 19)
+#define CPIA_COMMAND_ColourBars                (OUTPUT | CPIA_MODULE_VP_CTRL | 25)
+#define CPIA_COMMAND_ReadVPRegs                (INPUT | CPIA_MODULE_VP_CTRL | 30)
+#define CPIA_COMMAND_WriteVPReg                (OUTPUT | CPIA_MODULE_VP_CTRL | 31)
+
+#define CPIA_COMMAND_GrabFrame         (OUTPUT | CPIA_MODULE_CAPTURE | 1)
+#define CPIA_COMMAND_UploadFrame       (OUTPUT | CPIA_MODULE_CAPTURE | 2)
+#define CPIA_COMMAND_SetGrabMode       (OUTPUT | CPIA_MODULE_CAPTURE | 3)
+#define CPIA_COMMAND_InitStreamCap     (OUTPUT | CPIA_MODULE_CAPTURE | 4)
+#define CPIA_COMMAND_FiniStreamCap     (OUTPUT | CPIA_MODULE_CAPTURE | 5)
+#define CPIA_COMMAND_StartStreamCap    (OUTPUT | CPIA_MODULE_CAPTURE | 6)
+#define CPIA_COMMAND_EndStreamCap      (OUTPUT | CPIA_MODULE_CAPTURE | 7)
+#define CPIA_COMMAND_SetFormat         (OUTPUT | CPIA_MODULE_CAPTURE | 8)
+#define CPIA_COMMAND_SetROI            (OUTPUT | CPIA_MODULE_CAPTURE | 9)
+#define CPIA_COMMAND_SetCompression    (OUTPUT | CPIA_MODULE_CAPTURE | 10)
+#define CPIA_COMMAND_SetCompressionTarget (OUTPUT | CPIA_MODULE_CAPTURE | 11)
+#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)
+#define CPIA_COMMAND_SetDramPage       (OUTPUT | CPIA_MODULE_DEBUG | 5)
+#define CPIA_COMMAND_StartDramUpload   (OUTPUT | CPIA_MODULE_DEBUG | 6)
+#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)
+#define CPIA_COMMAND_Null              (OUTPUT | CPIA_MODULE_DEBUG | 11)
+
+enum {
+       FRAME_READY,            /* Ready to grab into */
+       FRAME_GRABBING,         /* In the process of being grabbed into */
+       FRAME_DONE,             /* Finished grabbing, but not been synced yet */
+       FRAME_UNUSED,           /* Unused (no MCAPTURE) */
+};
+
+#define COMMAND_NONE                   0x0000
+#define COMMAND_SETCOMPRESSION         0x0001
+#define COMMAND_SETCOMPRESSIONTARGET   0x0002
+#define COMMAND_SETCOLOURPARAMS                0x0004
+#define COMMAND_SETFORMAT              0x0008
+#define COMMAND_PAUSE                  0x0010
+#define COMMAND_RESUME                 0x0020
+#define COMMAND_SETYUVTHRESH           0x0040
+#define COMMAND_SETECPTIMING           0x0080
+#define COMMAND_SETCOMPRESSIONPARAMS   0x0100
+#define COMMAND_SETEXPOSURE            0x0200
+#define COMMAND_SETCOLOURBALANCE       0x0400
+#define COMMAND_SETSENSORFPS           0x0800
+#define COMMAND_SETAPCOR               0x1000
+#define COMMAND_SETFLICKERCTRL         0x2000
+#define COMMAND_SETVLOFFSET            0x4000
+#define COMMAND_SETLIGHTS              0x8000
+
+#define ROUND_UP_EXP_FOR_FLICKER 15
+
+/* Constants for automatic frame rate adjustment */
+#define MAX_EXP       302
+#define MAX_EXP_102   255
+#define LOW_EXP       140
+#define VERY_LOW_EXP   70
+#define TC             94
+#define        EXP_ACC_DARK   50
+#define        EXP_ACC_LIGHT  90
+#define HIGH_COMP_102 160
+#define MAX_COMP      239
+#define DARK_TIME       3
+#define LIGHT_TIME      3
+
+/* Maximum number of 10ms loops to wait for the stream to become ready */
+#define READY_TIMEOUT 100
+
+/* Developer's Guide Table 5 p 3-34
+ * indexed by [mains][sensorFps.baserate][sensorFps.divisor]*/
+static u8 flicker_jumps[2][2][4] =
+{ { { 76, 38, 19, 9 }, { 92, 46, 23, 11 } },
+  { { 64, 32, 16, 8 }, { 76, 38, 19, 9} }
+};
+
+/* forward declaration of local function */
+static void reset_camera_struct(struct cam_data *cam);
+static int find_over_exposure(int brightness);
+static void set_flicker(struct cam_params *params, volatile u32 *command_flags,
+                       int on);
+
+
+/**********************************************************************
+ *
+ * Memory management
+ *
+ **********************************************************************/
+static void *rvmalloc(unsigned long size)
+{
+       void *mem;
+       unsigned long adr;
+
+       size = PAGE_ALIGN(size);
+       mem = vmalloc_32(size);
+       if (!mem)
+               return NULL;
+
+       memset(mem, 0, size); /* Clear the ram out, no junk to the user */
+       adr = (unsigned long) mem;
+       while (size > 0) {
+               SetPageReserved(vmalloc_to_page((void *)adr));
+               adr += PAGE_SIZE;
+               size -= PAGE_SIZE;
+       }
+
+       return mem;
+}
+
+static void rvfree(void *mem, unsigned long size)
+{
+       unsigned long adr;
+
+       if (!mem)
+               return;
+
+       adr = (unsigned long) mem;
+       while ((long) size > 0) {
+               ClearPageReserved(vmalloc_to_page((void *)adr));
+               adr += PAGE_SIZE;
+               size -= PAGE_SIZE;
+       }
+       vfree(mem);
+}
+
+/**********************************************************************
+ *
+ * /proc interface
+ *
+ **********************************************************************/
+#ifdef CONFIG_PROC_FS
+static struct proc_dir_entry *cpia_proc_root=NULL;
+
+static int cpia_proc_show(struct seq_file *m, void *v)
+{
+       struct cam_data *cam = m->private;
+       int tmp;
+       char tmpstr[29];
+
+       seq_printf(m, "read-only\n-----------------------\n");
+       seq_printf(m, "V4L Driver version:       %d.%d.%d\n",
+                      CPIA_MAJ_VER, CPIA_MIN_VER, CPIA_PATCH_VER);
+       seq_printf(m, "CPIA Version:             %d.%02d (%d.%d)\n",
+                      cam->params.version.firmwareVersion,
+                      cam->params.version.firmwareRevision,
+                      cam->params.version.vcVersion,
+                      cam->params.version.vcRevision);
+       seq_printf(m, "CPIA PnP-ID:              %04x:%04x:%04x\n",
+                      cam->params.pnpID.vendor, cam->params.pnpID.product,
+                      cam->params.pnpID.deviceRevision);
+       seq_printf(m, "VP-Version:               %d.%d %04x\n",
+                      cam->params.vpVersion.vpVersion,
+                      cam->params.vpVersion.vpRevision,
+                      cam->params.vpVersion.cameraHeadID);
+
+       seq_printf(m, "system_state:             %#04x\n",
+                      cam->params.status.systemState);
+       seq_printf(m, "grab_state:               %#04x\n",
+                      cam->params.status.grabState);
+       seq_printf(m, "stream_state:             %#04x\n",
+                      cam->params.status.streamState);
+       seq_printf(m, "fatal_error:              %#04x\n",
+                      cam->params.status.fatalError);
+       seq_printf(m, "cmd_error:                %#04x\n",
+                      cam->params.status.cmdError);
+       seq_printf(m, "debug_flags:              %#04x\n",
+                      cam->params.status.debugFlags);
+       seq_printf(m, "vp_status:                %#04x\n",
+                      cam->params.status.vpStatus);
+       seq_printf(m, "error_code:               %#04x\n",
+                      cam->params.status.errorCode);
+       /* QX3 specific entries */
+       if (cam->params.qx3.qx3_detected) {
+               seq_printf(m, "button:                   %4d\n",
+                              cam->params.qx3.button);
+               seq_printf(m, "cradled:                  %4d\n",
+                              cam->params.qx3.cradled);
+       }
+       seq_printf(m, "video_size:               %s\n",
+                      cam->params.format.videoSize == VIDEOSIZE_CIF ?
+                      "CIF " : "QCIF");
+       seq_printf(m, "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);
+       seq_printf(m, "actual_fps:               %3d\n", cam->fps);
+       seq_printf(m, "transfer_rate:            %4dkB/s\n",
+                      cam->transfer_rate);
+
+       seq_printf(m, "\nread-write\n");
+       seq_printf(m, "-----------------------  current       min"
+                      "       max   default  comment\n");
+       seq_printf(m, "brightness:             %8d  %8d  %8d  %8d\n",
+                      cam->params.colourParams.brightness, 0, 100, 50);
+       if (cam->params.version.firmwareVersion == 1 &&
+          cam->params.version.firmwareRevision == 2)
+               /* 1-02 firmware limits contrast to 80 */
+               tmp = 80;
+       else
+               tmp = 96;
+
+       seq_printf(m, "contrast:               %8d  %8d  %8d  %8d"
+                      "  steps of 8\n",
+                      cam->params.colourParams.contrast, 0, tmp, 48);
+       seq_printf(m, "saturation:             %8d  %8d  %8d  %8d\n",
+                      cam->params.colourParams.saturation, 0, 100, 50);
+       tmp = (25000+5000*cam->params.sensorFps.baserate)/
+             (1<<cam->params.sensorFps.divisor);
+       seq_printf(m, "sensor_fps:             %4d.%03d  %8d  %8d  %8d\n",
+                      tmp/1000, tmp%1000, 3, 30, 15);
+       seq_printf(m, "stream_start_line:      %8d  %8d  %8d  %8d\n",
+                      2*cam->params.streamStartLine, 0,
+                      cam->params.format.videoSize == VIDEOSIZE_CIF ? 288:144,
+                      cam->params.format.videoSize == VIDEOSIZE_CIF ? 240:120);
+       seq_printf(m, "sub_sample:             %8s  %8s  %8s  %8s\n",
+                      cam->params.format.subSample == SUBSAMPLE_420 ?
+                      "420" : "422", "420", "422", "422");
+       seq_printf(m, "yuv_order:              %8s  %8s  %8s  %8s\n",
+                      cam->params.format.yuvOrder == YUVORDER_YUYV ?
+                      "YUYV" : "UYVY", "YUYV" , "UYVY", "YUYV");
+       seq_printf(m, "ecp_timing:             %8s  %8s  %8s  %8s\n",
+                      cam->params.ecpTiming ? "slow" : "normal", "slow",
+                      "normal", "normal");
+
+       if (cam->params.colourBalance.balanceMode == 2) {
+               sprintf(tmpstr, "auto");
+       } else {
+               sprintf(tmpstr, "manual");
+       }
+       seq_printf(m, "color_balance_mode:     %8s  %8s  %8s"
+                      "  %8s\n",  tmpstr, "manual", "auto", "auto");
+       seq_printf(m, "red_gain:               %8d  %8d  %8d  %8d\n",
+                      cam->params.colourBalance.redGain, 0, 212, 32);
+       seq_printf(m, "green_gain:             %8d  %8d  %8d  %8d\n",
+                      cam->params.colourBalance.greenGain, 0, 212, 6);
+       seq_printf(m, "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)
+               /* 1-02 firmware limits gain to 2 */
+               sprintf(tmpstr, "%8d  %8d  %8d", 1, 2, 2);
+       else
+               sprintf(tmpstr, "%8d  %8d  %8d", 1, 8, 2);
+
+       if (cam->params.exposure.gainMode == 0)
+               seq_printf(m, "max_gain:                unknown  %28s"
+                              "  powers of 2\n", tmpstr);
+       else
+               seq_printf(m, "max_gain:               %8d  %28s"
+                              "  1,2,4 or 8 \n",
+                              1<<(cam->params.exposure.gainMode-1), tmpstr);
+
+       switch(cam->params.exposure.expMode) {
+       case 1:
+       case 3:
+               sprintf(tmpstr, "manual");
+               break;
+       case 2:
+               sprintf(tmpstr, "auto");
+               break;
+       default:
+               sprintf(tmpstr, "unknown");
+               break;
+       }
+       seq_printf(m, "exposure_mode:          %8s  %8s  %8s"
+                      "  %8s\n",  tmpstr, "manual", "auto", "auto");
+       seq_printf(m, "centre_weight:          %8s  %8s  %8s  %8s\n",
+                      (2-cam->params.exposure.centreWeight) ? "on" : "off",
+                      "off", "on", "on");
+       seq_printf(m, "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)
+               /* 1-02 firmware limits fineExp/2 to 127 */
+               tmp = 254;
+       else
+               tmp = 510;
+
+       seq_printf(m, "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)
+               /* 1-02 firmware limits coarseExpHi to 0 */
+               tmp = MAX_EXP_102;
+       else
+               tmp = MAX_EXP;
+
+       seq_printf(m, "coarse_exp:             %8d  %8d  %8d"
+                      "  %8d\n", cam->params.exposure.coarseExpLo+
+                      256*cam->params.exposure.coarseExpHi, 0, tmp, 185);
+       seq_printf(m, "red_comp:               %8d  %8d  %8d  %8d\n",
+                      cam->params.exposure.redComp, COMP_RED, 255, COMP_RED);
+       seq_printf(m, "green1_comp:            %8d  %8d  %8d  %8d\n",
+                      cam->params.exposure.green1Comp, COMP_GREEN1, 255,
+                      COMP_GREEN1);
+       seq_printf(m, "green2_comp:            %8d  %8d  %8d  %8d\n",
+                      cam->params.exposure.green2Comp, COMP_GREEN2, 255,
+                      COMP_GREEN2);
+       seq_printf(m, "blue_comp:              %8d  %8d  %8d  %8d\n",
+                      cam->params.exposure.blueComp, COMP_BLUE, 255, COMP_BLUE);
+
+       seq_printf(m, "apcor_gain1:            %#8x  %#8x  %#8x  %#8x\n",
+                      cam->params.apcor.gain1, 0, 0xff, 0x1c);
+       seq_printf(m, "apcor_gain2:            %#8x  %#8x  %#8x  %#8x\n",
+                      cam->params.apcor.gain2, 0, 0xff, 0x1a);
+       seq_printf(m, "apcor_gain4:            %#8x  %#8x  %#8x  %#8x\n",
+                      cam->params.apcor.gain4, 0, 0xff, 0x2d);
+       seq_printf(m, "apcor_gain8:            %#8x  %#8x  %#8x  %#8x\n",
+                      cam->params.apcor.gain8, 0, 0xff, 0x2a);
+       seq_printf(m, "vl_offset_gain1:        %8d  %8d  %8d  %8d\n",
+                      cam->params.vlOffset.gain1, 0, 255, 24);
+       seq_printf(m, "vl_offset_gain2:        %8d  %8d  %8d  %8d\n",
+                      cam->params.vlOffset.gain2, 0, 255, 28);
+       seq_printf(m, "vl_offset_gain4:        %8d  %8d  %8d  %8d\n",
+                      cam->params.vlOffset.gain4, 0, 255, 30);
+       seq_printf(m, "vl_offset_gain8:        %8d  %8d  %8d  %8d\n",
+                      cam->params.vlOffset.gain8, 0, 255, 30);
+       seq_printf(m, "flicker_control:        %8s  %8s  %8s  %8s\n",
+                      cam->params.flickerControl.flickerMode ? "on" : "off",
+                      "off", "on", "off");
+       seq_printf(m, "mains_frequency:        %8d  %8d  %8d  %8d"
+                      " only 50/60\n",
+                      cam->mainsFreq ? 60 : 50, 50, 60, 50);
+       if(cam->params.flickerControl.allowableOverExposure < 0)
+               seq_printf(m, "allowable_overexposure: %4dauto      auto  %8d      auto\n",
+                              -cam->params.flickerControl.allowableOverExposure,
+                              255);
+       else
+               seq_printf(m, "allowable_overexposure: %8d      auto  %8d      auto\n",
+                              cam->params.flickerControl.allowableOverExposure,
+                              255);
+       seq_printf(m, "compression_mode:       ");
+       switch(cam->params.compression.mode) {
+       case CPIA_COMPRESSION_NONE:
+               seq_printf(m, "%8s", "none");
+               break;
+       case CPIA_COMPRESSION_AUTO:
+               seq_printf(m, "%8s", "auto");
+               break;
+       case CPIA_COMPRESSION_MANUAL:
+               seq_printf(m, "%8s", "manual");
+               break;
+       default:
+               seq_printf(m, "%8s", "unknown");
+               break;
+       }
+       seq_printf(m, "    none,auto,manual      auto\n");
+       seq_printf(m, "decimation_enable:      %8s  %8s  %8s  %8s\n",
+                      cam->params.compression.decimation ==
+                      DECIMATION_ENAB ? "on":"off", "off", "on",
+                      "off");
+       seq_printf(m, "compression_target:    %9s %9s %9s %9s\n",
+                      cam->params.compressionTarget.frTargeting  ==
+                      CPIA_COMPRESSION_TARGET_FRAMERATE ?
+                      "framerate":"quality",
+                      "framerate", "quality", "quality");
+       seq_printf(m, "target_framerate:       %8d  %8d  %8d  %8d\n",
+                      cam->params.compressionTarget.targetFR, 1, 30, 15);
+       seq_printf(m, "target_quality:         %8d  %8d  %8d  %8d\n",
+                      cam->params.compressionTarget.targetQ, 1, 64, 5);
+       seq_printf(m, "y_threshold:            %8d  %8d  %8d  %8d\n",
+                      cam->params.yuvThreshold.yThreshold, 0, 31, 6);
+       seq_printf(m, "uv_threshold:           %8d  %8d  %8d  %8d\n",
+                      cam->params.yuvThreshold.uvThreshold, 0, 31, 6);
+       seq_printf(m, "hysteresis:             %8d  %8d  %8d  %8d\n",
+                      cam->params.compressionParams.hysteresis, 0, 255, 3);
+       seq_printf(m, "threshold_max:          %8d  %8d  %8d  %8d\n",
+                      cam->params.compressionParams.threshMax, 0, 255, 11);
+       seq_printf(m, "small_step:             %8d  %8d  %8d  %8d\n",
+                      cam->params.compressionParams.smallStep, 0, 255, 1);
+       seq_printf(m, "large_step:             %8d  %8d  %8d  %8d\n",
+                      cam->params.compressionParams.largeStep, 0, 255, 3);
+       seq_printf(m, "decimation_hysteresis:  %8d  %8d  %8d  %8d\n",
+                      cam->params.compressionParams.decimationHysteresis,
+                      0, 255, 2);
+       seq_printf(m, "fr_diff_step_thresh:    %8d  %8d  %8d  %8d\n",
+                      cam->params.compressionParams.frDiffStepThresh,
+                      0, 255, 5);
+       seq_printf(m, "q_diff_step_thresh:     %8d  %8d  %8d  %8d\n",
+                      cam->params.compressionParams.qDiffStepThresh,
+                      0, 255, 3);
+       seq_printf(m, "decimation_thresh_mod:  %8d  %8d  %8d  %8d\n",
+                      cam->params.compressionParams.decimationThreshMod,
+                      0, 255, 2);
+       /* QX3 specific entries */
+       if (cam->params.qx3.qx3_detected) {
+               seq_printf(m, "toplight:               %8s  %8s  %8s  %8s\n",
+                              cam->params.qx3.toplight ? "on" : "off",
+                              "off", "on", "off");
+               seq_printf(m, "bottomlight:            %8s  %8s  %8s  %8s\n",
+                              cam->params.qx3.bottomlight ? "on" : "off",
+                              "off", "on", "off");
+       }
+
+       return 0;
+}
+
+static int cpia_proc_open(struct inode *inode, struct file *file)
+{
+       return single_open(file, cpia_proc_show, PDE(inode)->data);
+}
+
+static int match(char *checkstr, char **buffer, size_t *count,
+                int *find_colon, int *err)
+{
+       int ret, colon_found = 1;
+       int len = strlen(checkstr);
+       ret = (len <= *count && strncmp(*buffer, checkstr, 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)
+                               *err = -EINVAL;
+                       *find_colon = 0;
+               }
+       }
+       return ret;
+}
+
+static unsigned long int value(char **buffer, size_t *count, int *err)
+{
+       char *p;
+       unsigned long int ret;
+       ret = simple_strtoul(*buffer, &p, 0);
+       if (p == *buffer)
+               *err = -EINVAL;
+       else {
+               *count -= p - *buffer;
+               *buffer = p;
+       }
+       return ret;
+}
+
+static ssize_t cpia_proc_write(struct file *file, const char __user *buf,
+                              size_t count, loff_t *pos)
+{
+       struct cam_data *cam = PDE(file->f_path.dentry->d_inode)->data;
+       struct cam_params new_params;
+       char *page, *buffer;
+       int retval, find_colon;
+       int size = count;
+       unsigned long val = 0;
+       u32 command_flags = 0;
+       u8 new_mains;
+
+       /*
+        * This code to copy from buf to page is shamelessly copied
+        * from the comx driver
+        */
+       if (count > PAGE_SIZE) {
+               printk(KERN_ERR "count is %zu > %d!!!\n", count, (int)PAGE_SIZE);
+               return -ENOSPC;
+       }
+
+       if (!(page = (char *)__get_free_page(GFP_KERNEL))) return -ENOMEM;
+
+       if(copy_from_user(page, buf, count))
+       {
+               retval = -EFAULT;
+               goto out;
+       }
+
+       if (page[count-1] == '\n')
+               page[count-1] = '\0';
+       else if (count < PAGE_SIZE)
+               page[count] = '\0';
+       else if (page[count]) {
+               retval = -EINVAL;
+               goto out;
+       }
+
+       buffer = page;
+
+       if (mutex_lock_interruptible(&cam->param_lock))
+               return -ERESTARTSYS;
+
+       /*
+        * Skip over leading whitespace
+        */
+       while (count && isspace(*buffer)) {
+               --count;
+               ++buffer;
+       }
+
+       memcpy(&new_params, &cam->params, sizeof(struct cam_params));
+       new_mains = cam->mainsFreq;
+
+#define MATCH(x) (match(x, &buffer, &count, &find_colon, &retval))
+#define VALUE (value(&buffer,&count, &retval))
+#define FIRMWARE_VERSION(x,y) (new_params.version.firmwareVersion == (x) && \
+                              new_params.version.firmwareRevision == (y))
+
+       retval = 0;
+       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;
+                       if(new_params.flickerControl.allowableOverExposure < 0)
+                               new_params.flickerControl.allowableOverExposure =
+                                       -find_over_exposure(new_params.colourParams.brightness);
+                       if(new_params.flickerControl.flickerMode != 0)
+                               command_flags |= COMMAND_SETFLICKERCTRL;
+
+               } 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)
+                                               val = 80;
+
+                                       new_params.colourParams.contrast = val;
+                               } else
+                                       retval = -EINVAL;
+                       }
+                       command_flags |= COMMAND_SETCOLOURPARAMS;
+               } 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;
+               } else if (MATCH("sensor_fps")) {
+                       if (!retval)
+                               val = VALUE;
+
+                       if (!retval) {
+                               /* find values so that sensorFPS is minimized,
+                                * but >= val */
+                               if (val > 30)
+                                       retval = -EINVAL;
+                               else if (val > 25) {
+                                       new_params.sensorFps.divisor = 0;
+                                       new_params.sensorFps.baserate = 1;
+                               } else if (val > 15) {
+                                       new_params.sensorFps.divisor = 0;
+                                       new_params.sensorFps.baserate = 0;
+                               } else if (val > 12) {
+                                       new_params.sensorFps.divisor = 1;
+                                       new_params.sensorFps.baserate = 1;
+                               } else if (val > 7) {
+                                       new_params.sensorFps.divisor = 1;
+                                       new_params.sensorFps.baserate = 0;
+                               } else if (val > 6) {
+                                       new_params.sensorFps.divisor = 2;
+                                       new_params.sensorFps.baserate = 1;
+                               } else if (val > 3) {
+                                       new_params.sensorFps.divisor = 2;
+                                       new_params.sensorFps.baserate = 0;
+                               } else {
+                                       new_params.sensorFps.divisor = 3;
+                                       /* Either base rate would work here */
+                                       new_params.sensorFps.baserate = 1;
+                               }
+                               new_params.flickerControl.coarseJump =
+                                       flicker_jumps[new_mains]
+                                       [new_params.sensorFps.baserate]
+                                       [new_params.sensorFps.divisor];
+                               if (new_params.flickerControl.flickerMode)
+                                       command_flags |= COMMAND_SETFLICKERCTRL;
+                       }
+                       command_flags |= COMMAND_SETSENSORFPS;
+                       cam->exposure_status = EXPOSURE_NORMAL;
+               } else if (MATCH("stream_start_line")) {
+                       if (!retval)
+                               val = VALUE;
+
+                       if (!retval) {
+                               int max_line = 288;
+
+                               if (new_params.format.videoSize == VIDEOSIZE_QCIF)
+                                       max_line = 144;
+                               if (val <= max_line)
+                                       new_params.streamStartLine = val/2;
+                               else
+                                       retval = -EINVAL;
+                       }
+               } 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"))
+                               new_params.ecpTiming = 1;
+                       else
+                               retval = -EINVAL;
+
+                       command_flags |= COMMAND_SETECPTIMING;
+               } else if (MATCH("color_balance_mode")) {
+                       if (!retval && MATCH("manual"))
+                               new_params.colourBalance.balanceMode = 3;
+                       else if (!retval && MATCH("auto"))
+                               new_params.colourBalance.balanceMode = 2;
+                       else
+                               retval = -EINVAL;
+
+                       command_flags |= COMMAND_SETCOLOURBALANCE;
+               } else if (MATCH("red_gain")) {
+                       if (!retval)
+                               val = VALUE;
+
+                       if (!retval) {
+                               if (val <= 212) {
+                                       new_params.colourBalance.redGain = val;
+                                       new_params.colourBalance.balanceMode = 1;
+                               } else
+                                       retval = -EINVAL;
+                       }
+                       command_flags |= COMMAND_SETCOLOURBALANCE;
+               } else if (MATCH("green_gain")) {
+                       if (!retval)
+                               val = VALUE;
+
+                       if (!retval) {
+                               if (val <= 212) {
+                                       new_params.colourBalance.greenGain = val;
+                                       new_params.colourBalance.balanceMode = 1;
+                               } else
+                                       retval = -EINVAL;
+                       }
+                       command_flags |= COMMAND_SETCOLOURBALANCE;
+               } else if (MATCH("blue_gain")) {
+                       if (!retval)
+                               val = VALUE;
+
+                       if (!retval) {
+                               if (val <= 212) {
+                                       new_params.colourBalance.blueGain = val;
+                                       new_params.colourBalance.balanceMode = 1;
+                               } else
+                                       retval = -EINVAL;
+                       }
+                       command_flags |= COMMAND_SETCOLOURBALANCE;
+               } 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)
+                                       val = 2;
+                               switch(val) {
+                               case 1:
+                                       new_params.exposure.gainMode = 1;
+                                       break;
+                               case 2:
+                                       new_params.exposure.gainMode = 2;
+                                       break;
+                               case 4:
+                                       new_params.exposure.gainMode = 3;
+                                       break;
+                               case 8:
+                                       new_params.exposure.gainMode = 4;
+                                       break;
+                               default:
+                                       retval = -EINVAL;
+                                       break;
+                               }
+                       }
+                       command_flags |= COMMAND_SETEXPOSURE;
+               } else if (MATCH("exposure_mode")) {
+                       if (!retval && MATCH("auto"))
+                               new_params.exposure.expMode = 2;
+                       else if (!retval && MATCH("manual")) {
+                               if (new_params.exposure.expMode == 2)
+                                       new_params.exposure.expMode = 3;
+                               if(new_params.flickerControl.flickerMode != 0)
+                                       command_flags |= COMMAND_SETFLICKERCTRL;
+                               new_params.flickerControl.flickerMode = 0;
+                       } else
+                               retval = -EINVAL;
+
+                       command_flags |= COMMAND_SETEXPOSURE;
+               } else if (MATCH("centre_weight")) {
+                       if (!retval && MATCH("on"))
+                               new_params.exposure.centreWeight = 1;
+                       else if (!retval && MATCH("off"))
+                               new_params.exposure.centreWeight = 2;
+                       else
+                               retval = -EINVAL;
+
+                       command_flags |= COMMAND_SETEXPOSURE;
+               } else if (MATCH("gain")) {
+                       if (!retval)
+                               val = VALUE;
+
+                       if (!retval) {
+                               switch(val) {
+                               case 1:
+                                       new_params.exposure.gain = 0;
+                                       break;
+                               case 2:
+                                       new_params.exposure.gain = 1;
+                                       break;
+                               case 4:
+                                       new_params.exposure.gain = 2;
+                                       break;
+                               case 8:
+                                       new_params.exposure.gain = 3;
+                                       break;
+                               default:
+                                       retval = -EINVAL;
+                                       break;
+                               }
+                               new_params.exposure.expMode = 1;
+                               if(new_params.flickerControl.flickerMode != 0)
+                                       command_flags |= COMMAND_SETFLICKERCTRL;
+                               new_params.flickerControl.flickerMode = 0;
+                               command_flags |= COMMAND_SETEXPOSURE;
+                               if (new_params.exposure.gain >
+                                   new_params.exposure.gainMode-1)
+                                       retval = -EINVAL;
+                       }
+               } else if (MATCH("fine_exp")) {
+                       if (!retval)
+                               val = VALUE/2;
+
+                       if (!retval) {
+                               if (val < 256) {
+                                       /* 1-02 firmware limits fineExp/2 to 127*/
+                                       if (FIRMWARE_VERSION(1,2) && val > 127)
+                                               val = 127;
+                                       new_params.exposure.fineExp = val;
+                                       new_params.exposure.expMode = 1;
+                                       command_flags |= COMMAND_SETEXPOSURE;
+                                       if(new_params.flickerControl.flickerMode != 0)
+                                               command_flags |= COMMAND_SETFLICKERCTRL;
+                                       new_params.flickerControl.flickerMode = 0;
+                                       command_flags |= COMMAND_SETFLICKERCTRL;
+                               } else
+                                       retval = -EINVAL;
+                       }
+               } else if (MATCH("coarse_exp")) {
+                       if (!retval)
+                               val = VALUE;
+
+                       if (!retval) {
+                               if (val <= MAX_EXP) {
+                                       if (FIRMWARE_VERSION(1,2) &&
+                                           val > MAX_EXP_102)
+                                               val = MAX_EXP_102;
+                                       new_params.exposure.coarseExpLo =
+                                               val & 0xff;
+                                       new_params.exposure.coarseExpHi =
+                                               val >> 8;
+                                       new_params.exposure.expMode = 1;
+                                       command_flags |= COMMAND_SETEXPOSURE;
+                                       if(new_params.flickerControl.flickerMode != 0)
+                                               command_flags |= COMMAND_SETFLICKERCTRL;
+                                       new_params.flickerControl.flickerMode = 0;
+                                       command_flags |= COMMAND_SETFLICKERCTRL;
+                               } else
+                                       retval = -EINVAL;
+                       }
+               } else if (MATCH("red_comp")) {
+                       if (!retval)
+                               val = VALUE;
+
+                       if (!retval) {
+                               if (val >= COMP_RED && val <= 255) {
+                                       new_params.exposure.redComp = val;
+                                       new_params.exposure.compMode = 1;
+                                       command_flags |= COMMAND_SETEXPOSURE;
+                               } else
+                                       retval = -EINVAL;
+                       }
+               } else if (MATCH("green1_comp")) {
+                       if (!retval)
+                               val = VALUE;
+
+                       if (!retval) {
+                               if (val >= COMP_GREEN1 && val <= 255) {
+                                       new_params.exposure.green1Comp = val;
+                                       new_params.exposure.compMode = 1;
+                                       command_flags |= COMMAND_SETEXPOSURE;
+                               } else
+                                       retval = -EINVAL;
+                       }
+               } else if (MATCH("green2_comp")) {
+                       if (!retval)
+                               val = VALUE;
+
+                       if (!retval) {
+                               if (val >= COMP_GREEN2 && val <= 255) {
+                                       new_params.exposure.green2Comp = val;
+                                       new_params.exposure.compMode = 1;
+                                       command_flags |= COMMAND_SETEXPOSURE;
+                               } else
+                                       retval = -EINVAL;
+                       }
+               } else if (MATCH("blue_comp")) {
+                       if (!retval)
+                               val = VALUE;
+
+                       if (!retval) {
+                               if (val >= COMP_BLUE && val <= 255) {
+                                       new_params.exposure.blueComp = val;
+                                       new_params.exposure.compMode = 1;
+                                       command_flags |= COMMAND_SETEXPOSURE;
+                               } else
+                                       retval = -EINVAL;
+                       }
+               } 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;
+                       }
+               } 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;
+                       }
+               } 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;
+                       }
+               } 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;
+                       }
+               } else if (MATCH("vl_offset_gain1")) {
+                       if (!retval)
+                               val = VALUE;
+
+                       if (!retval) {
+                               if (val <= 0xff)
+                                       new_params.vlOffset.gain1 = val;
+                               else
+                                       retval = -EINVAL;
+                       }
+                       command_flags |= COMMAND_SETVLOFFSET;
+               } else if (MATCH("vl_offset_gain2")) {
+                       if (!retval)
+                               val = VALUE;
+
+                       if (!retval) {
+                               if (val <= 0xff)
+                                       new_params.vlOffset.gain2 = val;
+                               else
+                                       retval = -EINVAL;
+                       }
+                       command_flags |= COMMAND_SETVLOFFSET;
+               } else if (MATCH("vl_offset_gain4")) {
+                       if (!retval)
+                               val = VALUE;
+
+                       if (!retval) {
+                               if (val <= 0xff)
+                                       new_params.vlOffset.gain4 = val;
+                               else
+                                       retval = -EINVAL;
+                       }
+                       command_flags |= COMMAND_SETVLOFFSET;
+               } else if (MATCH("vl_offset_gain8")) {
+                       if (!retval)
+                               val = VALUE;
+
+                       if (!retval) {
+                               if (val <= 0xff)
+                                       new_params.vlOffset.gain8 = val;
+                               else
+                                       retval = -EINVAL;
+                       }
+                       command_flags |= COMMAND_SETVLOFFSET;
+               } else if (MATCH("flicker_control")) {
+                       if (!retval && MATCH("on")) {
+                               set_flicker(&new_params, &command_flags, 1);
+                       } else if (!retval && MATCH("off")) {
+                               set_flicker(&new_params, &command_flags, 0);
+                       } else
+                               retval = -EINVAL;
+
+                       command_flags |= COMMAND_SETFLICKERCTRL;
+               } 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)
+                                       command_flags |= COMMAND_SETFLICKERCTRL;
+                       } 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)
+                                       command_flags |= COMMAND_SETFLICKERCTRL;
+                       } else
+                               retval = -EINVAL;
+               } else if (MATCH("allowable_overexposure")) {
+                       if (!retval && MATCH("auto")) {
+                               new_params.flickerControl.allowableOverExposure =
+                                       -find_over_exposure(new_params.colourParams.brightness);
+                               if(new_params.flickerControl.flickerMode != 0)
+                                       command_flags |= COMMAND_SETFLICKERCTRL;
+                       } else {
+                               if (!retval)
+                                       val = VALUE;
+
+                               if (!retval) {
+                                       if (val <= 0xff) {
+                                               new_params.flickerControl.
+                                                       allowableOverExposure = val;
+                                               if(new_params.flickerControl.flickerMode != 0)
+                                                       command_flags |= COMMAND_SETFLICKERCTRL;
+                                       } else
+                                               retval = -EINVAL;
+                               }
+                       }
+               } else if (MATCH("compression_mode")) {
+                       if (!retval && MATCH("none"))
+                               new_params.compression.mode =
+                                       CPIA_COMPRESSION_NONE;
+                       else if (!retval && MATCH("auto"))
+                               new_params.compression.mode =
+                                       CPIA_COMPRESSION_AUTO;
+                       else if (!retval && MATCH("manual"))
+                               new_params.compression.mode =
+                                       CPIA_COMPRESSION_MANUAL;
+                       else
+                               retval = -EINVAL;
+
+                       command_flags |= COMMAND_SETCOMPRESSION;
+               } else if (MATCH("decimation_enable")) {
+                       if (!retval && MATCH("off"))
+                               new_params.compression.decimation = 0;
+                       else if (!retval && MATCH("on"))
+                               new_params.compression.decimation = 1;
+                       else
+                               retval = -EINVAL;
+
+                       command_flags |= COMMAND_SETCOMPRESSION;
+               } else if (MATCH("compression_target")) {
+                       if (!retval && MATCH("quality"))
+                               new_params.compressionTarget.frTargeting =
+                                       CPIA_COMPRESSION_TARGET_QUALITY;
+                       else if (!retval && MATCH("framerate"))
+                               new_params.compressionTarget.frTargeting =
+                                       CPIA_COMPRESSION_TARGET_FRAMERATE;
+                       else
+                               retval = -EINVAL;
+
+                       command_flags |= COMMAND_SETCOMPRESSIONTARGET;
+               } 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;
+               } 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;
+               } 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;
+               } 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;
+               } 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;
+               } 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;
+               } 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;
+               } 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;
+               } 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;
+               } 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;
+               } else if (MATCH("q_diff_step_thresh")) {
+                       if (!retval)
+                               val = VALUE;
+
+                       if (!retval) {
+                               if (val <= 0xff)
+                                       new_params.compressionParams.qDiffStepThresh = 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;
+               } else if (MATCH("toplight")) {
+                       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")) {
+                       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 == '\0' && count != 1)
+                                       retval = -EINVAL;
+                               else if (*buffer != '\n' && *buffer != ';' &&
+                                        *buffer != '\0')
+                                       retval = -EINVAL;
+                               else {
+                                       --count;
+                                       ++buffer;
+                               }
+                       }
+               }
+       }
+#undef MATCH
+#undef VALUE
+#undef FIRMWARE_VERSION
+       if (!retval) {
+               if (command_flags & COMMAND_SETCOLOURPARAMS) {
+                       /* Adjust cam->vp to reflect these changes */
+                       cam->vp.brightness =
+                               new_params.colourParams.brightness*65535/100;
+                       cam->vp.contrast =
+                               new_params.colourParams.contrast*65535/100;
+                       cam->vp.colour =
+                               new_params.colourParams.saturation*65535/100;
+               }
+               if((command_flags & COMMAND_SETEXPOSURE) &&
+                  new_params.exposure.expMode == 2)
+                       cam->exposure_status = EXPOSURE_NORMAL;
+
+               memcpy(&cam->params, &new_params, sizeof(struct cam_params));
+               cam->mainsFreq = new_mains;
+               cam->cmd_queue |= command_flags;
+               retval = size;
+       } else
+               DBG("error: %d\n", retval);
+
+       mutex_unlock(&cam->param_lock);
+
+out:
+       free_page((unsigned long)page);
+       return retval;
+}
+
+static const struct file_operations cpia_proc_fops = {
+       .owner          = THIS_MODULE,
+       .open           = cpia_proc_open,
+       .read           = seq_read,
+       .llseek         = seq_lseek,
+       .release        = single_release,
+       .write          = cpia_proc_write,
+};
+
+static void create_proc_cpia_cam(struct cam_data *cam)
+{
+       struct proc_dir_entry *ent;
+
+       if (!cpia_proc_root || !cam)
+               return;
+
+       ent = proc_create_data(video_device_node_name(&cam->vdev),
+                              S_IRUGO|S_IWUSR, cpia_proc_root,
+                              &cpia_proc_fops, cam);
+       if (!ent)
+               return;
+
+       /*
+          size of the proc entry is 3736 bytes for the standard webcam;
+          the extra features of the QX3 microscope add 189 bytes.
+          (we have not yet probed the camera to see which type it is).
+       */
+       ent->size = 3736 + 189;
+       cam->proc_entry = ent;
+}
+
+static void destroy_proc_cpia_cam(struct cam_data *cam)
+{
+       if (!cam || !cam->proc_entry)
+               return;
+
+       remove_proc_entry(video_device_node_name(&cam->vdev), cpia_proc_root);
+       cam->proc_entry = NULL;
+}
+
+static void proc_cpia_create(void)
+{
+       cpia_proc_root = proc_mkdir("cpia", NULL);
+
+       if (!cpia_proc_root)
+               LOG("Unable to initialise /proc/cpia\n");
+}
+
+static void __exit proc_cpia_destroy(void)
+{
+       remove_proc_entry("cpia", NULL);
+}
+#endif /* CONFIG_PROC_FS */
+
+/* ----------------------- debug functions ---------------------- */
+
+#define printstatus(cam) \
+  DBG("%02x %02x %02x %02x %02x %02x %02x %02x\n",\
+       cam->params.status.systemState, cam->params.status.grabState, \
+       cam->params.status.streamState, cam->params.status.fatalError, \
+       cam->params.status.cmdError, cam->params.status.debugFlags, \
+       cam->params.status.vpStatus, cam->params.status.errorCode);
+
+/* ----------------------- v4l helpers -------------------------- */
+
+/* supported frame palettes and depths */
+static inline int valid_mode(u16 palette, u16 depth)
+{
+       if ((palette == VIDEO_PALETTE_YUV422 && depth == 16) ||
+           (palette == VIDEO_PALETTE_YUYV && depth == 16))
+               return 1;
+
+       if (colorspace_conv)
+               return (palette == VIDEO_PALETTE_GREY && depth == 8) ||
+                      (palette == VIDEO_PALETTE_RGB555 && depth == 16) ||
+                      (palette == VIDEO_PALETTE_RGB565 && depth == 16) ||
+                      (palette == VIDEO_PALETTE_RGB24 && depth == 24) ||
+                      (palette == VIDEO_PALETTE_RGB32 && depth == 32) ||
+                      (palette == VIDEO_PALETTE_UYVY && depth == 16);
+
+       return 0;
+}
+
+static int match_videosize( int width, int height )
+{
+       /* return the best match, where 'best' is as always
+        * the largest that is not bigger than what is requested. */
+       if (width>=352 && height>=288)
+               return VIDEOSIZE_352_288; /* CIF */
+
+       if (width>=320 && height>=240)
+               return VIDEOSIZE_320_240; /* SIF */
+
+       if (width>=288 && height>=216)
+               return VIDEOSIZE_288_216;
+
+       if (width>=256 && height>=192)
+               return VIDEOSIZE_256_192;
+
+       if (width>=224 && height>=168)
+               return VIDEOSIZE_224_168;
+
+       if (width>=192 && height>=144)
+               return VIDEOSIZE_192_144;
+
+       if (width>=176 && height>=144)
+               return VIDEOSIZE_176_144; /* QCIF */
+
+       if (width>=160 && height>=120)
+               return VIDEOSIZE_160_120; /* QSIF */
+
+       if (width>=128 && height>=96)
+               return VIDEOSIZE_128_96;
+
+       if (width>=88 && height>=72)
+               return VIDEOSIZE_88_72;
+
+       if (width>=64 && height>=48)
+               return VIDEOSIZE_64_48;
+
+       if (width>=48 && height>=48)
+               return VIDEOSIZE_48_48;
+
+       return -1;
+}
+
+/* these are the capture sizes we support */
+static void set_vw_size(struct cam_data *cam)
+{
+       /* the col/row/start/end values are the result of simple math    */
+       /* study the SetROI-command in cpia developers guide p 2-22      */
+       /* streamStartLine is set to the recommended value in the cpia   */
+       /*  developers guide p 3-37                                      */
+       switch(cam->video_size) {
+       case VIDEOSIZE_CIF:
+               cam->vw.width = 352;
+               cam->vw.height = 288;
+               cam->params.format.videoSize=VIDEOSIZE_CIF;
+               cam->params.roi.colStart=0;
+               cam->params.roi.rowStart=0;
+               cam->params.streamStartLine = 120;
+               break;
+       case VIDEOSIZE_SIF:
+               cam->vw.width = 320;
+               cam->vw.height = 240;
+               cam->params.format.videoSize=VIDEOSIZE_CIF;
+               cam->params.roi.colStart=2;
+               cam->params.roi.rowStart=6;
+               cam->params.streamStartLine = 120;
+               break;
+       case VIDEOSIZE_288_216:
+               cam->vw.width = 288;
+               cam->vw.height = 216;
+               cam->params.format.videoSize=VIDEOSIZE_CIF;
+               cam->params.roi.colStart=4;
+               cam->params.roi.rowStart=9;
+               cam->params.streamStartLine = 120;
+               break;
+       case VIDEOSIZE_256_192:
+               cam->vw.width = 256;
+               cam->vw.height = 192;
+               cam->params.format.videoSize=VIDEOSIZE_CIF;
+               cam->params.roi.colStart=6;
+               cam->params.roi.rowStart=12;
+               cam->params.streamStartLine = 120;
+               break;
+       case VIDEOSIZE_224_168:
+               cam->vw.width = 224;
+               cam->vw.height = 168;
+               cam->params.format.videoSize=VIDEOSIZE_CIF;
+               cam->params.roi.colStart=8;
+               cam->params.roi.rowStart=15;
+               cam->params.streamStartLine = 120;
+               break;
+       case VIDEOSIZE_192_144:
+               cam->vw.width = 192;
+               cam->vw.height = 144;
+               cam->params.format.videoSize=VIDEOSIZE_CIF;
+               cam->params.roi.colStart=10;
+               cam->params.roi.rowStart=18;
+               cam->params.streamStartLine = 120;
+               break;
+       case VIDEOSIZE_QCIF:
+               cam->vw.width = 176;
+               cam->vw.height = 144;
+               cam->params.format.videoSize=VIDEOSIZE_QCIF;
+               cam->params.roi.colStart=0;
+               cam->params.roi.rowStart=0;
+               cam->params.streamStartLine = 60;
+               break;
+       case VIDEOSIZE_QSIF:
+               cam->vw.width = 160;
+               cam->vw.height = 120;
+               cam->params.format.videoSize=VIDEOSIZE_QCIF;
+               cam->params.roi.colStart=1;
+               cam->params.roi.rowStart=3;
+               cam->params.streamStartLine = 60;
+               break;
+       case VIDEOSIZE_128_96:
+               cam->vw.width = 128;
+               cam->vw.height = 96;
+               cam->params.format.videoSize=VIDEOSIZE_QCIF;
+               cam->params.roi.colStart=3;
+               cam->params.roi.rowStart=6;
+               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.rowStart=9;
+               cam->params.streamStartLine = 60;
+               break;
+       case VIDEOSIZE_64_48:
+               cam->vw.width = 64;
+               cam->vw.height = 48;
+               cam->params.format.videoSize=VIDEOSIZE_QCIF;
+               cam->params.roi.colStart=7;
+               cam->params.roi.rowStart=12;
+               cam->params.streamStartLine = 60;
+               break;
+       case VIDEOSIZE_48_48:
+               cam->vw.width = 48;
+               cam->vw.height = 48;
+               cam->params.format.videoSize=VIDEOSIZE_QCIF;
+               cam->params.roi.colStart=8;
+               cam->params.roi.rowStart=6;
+               cam->params.streamStartLine = 60;
+               break;
+       default:
+               LOG("bad videosize value: %d\n", cam->video_size);
+               return;
+       }
+
+       if(cam->vc.width == 0)
+               cam->vc.width = cam->vw.width;
+       if(cam->vc.height == 0)
+               cam->vc.height = cam->vw.height;
+
+       cam->params.roi.colStart += cam->vc.x >> 3;
+       cam->params.roi.colEnd = cam->params.roi.colStart +
+                                (cam->vc.width >> 3);
+       cam->params.roi.rowStart += cam->vc.y >> 2;
+       cam->params.roi.rowEnd = cam->params.roi.rowStart +
+                                (cam->vc.height >> 2);
+
+       return;
+}
+
+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)
+               return -ENOBUFS;
+
+       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)
+{
+       int i;
+
+       rvfree(cam->frame_buf, FRAME_NUM*CPIA_MAX_FRAME_SIZE);
+       cam->frame_buf = NULL;
+       for (i=0; i < FRAME_NUM; i++)
+               cam->frame[i].data = NULL;
+
+       return 0;
+}
+
+
+static inline void free_frames(struct cpia_frame frame[FRAME_NUM])
+{
+       int i;
+
+       for (i=0; i < FRAME_NUM; i++)
+               frame[i].state = FRAME_UNUSED;
+       return;
+}
+
+/**********************************************************************
+ *
+ * General functions
+ *
+ **********************************************************************/
+/* send an arbitrary command to the camera */
+static int do_command(struct cam_data *cam, u16 command, u8 a, u8 b, u8 c, u8 d)
+{
+       int retval, datasize;
+       u8 cmd[8], data[8];
+
+       switch(command) {
+       case CPIA_COMMAND_GetCPIAVersion:
+       case CPIA_COMMAND_GetPnPID:
+       case CPIA_COMMAND_GetCameraStatus:
+       case CPIA_COMMAND_GetVPVersion:
+               datasize=8;
+               break;
+       case CPIA_COMMAND_GetColourParams:
+       case CPIA_COMMAND_GetColourBalance:
+       case CPIA_COMMAND_GetExposure:
+               mutex_lock(&cam->param_lock);
+               datasize=8;
+               break;
+       case CPIA_COMMAND_ReadMCPorts:
+       case CPIA_COMMAND_ReadVCRegs:
+               datasize = 4;
+               break;
+       default:
+               datasize=0;
+               break;
+       }
+
+       cmd[0] = command>>8;
+       cmd[1] = command&0xff;
+       cmd[2] = a;
+       cmd[3] = b;
+       cmd[4] = c;
+       cmd[5] = d;
+       cmd[6] = datasize;
+       cmd[7] = 0;
+
+       retval = cam->ops->transferCmd(cam->lowlevel_data, cmd, data);
+       if (retval) {
+               DBG("%x - failed, retval=%d\n", command, retval);
+               if (command == CPIA_COMMAND_GetColourParams ||
+                   command == CPIA_COMMAND_GetColourBalance ||
+                   command == CPIA_COMMAND_GetExposure)
+                       mutex_unlock(&cam->param_lock);
+       } else {
+               switch(command) {
+               case CPIA_COMMAND_GetCPIAVersion:
+                       cam->params.version.firmwareVersion = data[0];
+                       cam->params.version.firmwareRevision = data[1];
+                       cam->params.version.vcVersion = data[2];
+                       cam->params.version.vcRevision = data[3];
+                       break;
+               case CPIA_COMMAND_GetPnPID:
+                       cam->params.pnpID.vendor = data[0]+(((u16)data[1])<<8);
+                       cam->params.pnpID.product = data[2]+(((u16)data[3])<<8);
+                       cam->params.pnpID.deviceRevision =
+                               data[4]+(((u16)data[5])<<8);
+                       break;
+               case CPIA_COMMAND_GetCameraStatus:
+                       cam->params.status.systemState = data[0];
+                       cam->params.status.grabState = data[1];
+                       cam->params.status.streamState = data[2];
+                       cam->params.status.fatalError = data[3];
+                       cam->params.status.cmdError = data[4];
+                       cam->params.status.debugFlags = data[5];
+                       cam->params.status.vpStatus = data[6];
+                       cam->params.status.errorCode = data[7];
+                       break;
+               case CPIA_COMMAND_GetVPVersion:
+                       cam->params.vpVersion.vpVersion = data[0];
+                       cam->params.vpVersion.vpRevision = data[1];
+                       cam->params.vpVersion.cameraHeadID =
+                               data[2]+(((u16)data[3])<<8);
+                       break;
+               case CPIA_COMMAND_GetColourParams:
+                       cam->params.colourParams.brightness = data[0];
+                       cam->params.colourParams.contrast = data[1];
+                       cam->params.colourParams.saturation = data[2];
+                       mutex_unlock(&cam->param_lock);
+                       break;
+               case CPIA_COMMAND_GetColourBalance:
+                       cam->params.colourBalance.redGain = data[0];
+                       cam->params.colourBalance.greenGain = data[1];
+                       cam->params.colourBalance.blueGain = data[2];
+                       mutex_unlock(&cam->param_lock);
+                       break;
+               case CPIA_COMMAND_GetExposure:
+                       cam->params.exposure.gain = data[0];
+                       cam->params.exposure.fineExp = data[1];
+                       cam->params.exposure.coarseExpLo = data[2];
+                       cam->params.exposure.coarseExpHi = data[3];
+                       cam->params.exposure.redComp = data[4];
+                       cam->params.exposure.green1Comp = data[5];
+                       cam->params.exposure.green2Comp = data[6];
+                       cam->params.exposure.blueComp = data[7];
+                       mutex_unlock(&cam->param_lock);
+                       break;
+
+               case CPIA_COMMAND_ReadMCPorts:
+                       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;
+               }
+       }
+       return retval;
+}
+
+/* send a command  to the camera with an additional data transaction */
+static int do_command_extended(struct cam_data *cam, u16 command,
+                              u8 a, u8 b, u8 c, u8 d,
+                              u8 e, u8 f, u8 g, u8 h,
+                              u8 i, u8 j, u8 k, u8 l)
+{
+       int retval;
+       u8 cmd[8], data[8];
+
+       cmd[0] = command>>8;
+       cmd[1] = command&0xff;
+       cmd[2] = a;
+       cmd[3] = b;
+       cmd[4] = c;
+       cmd[5] = d;
+       cmd[6] = 8;
+       cmd[7] = 0;
+       data[0] = e;
+       data[1] = f;
+       data[2] = g;
+       data[3] = h;
+       data[4] = i;
+       data[5] = j;
+       data[6] = k;
+       data[7] = l;
+
+       retval = cam->ops->transferCmd(cam->lowlevel_data, cmd, data);
+       if (retval)
+               DBG("%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;
+
+       /* Odd lines use the same u and v as the previous line.
+        * Because of compression, it is necessary to get this
+        * information from the decoded image. */
+       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;
+               *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:
+               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;
+               *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;
+               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;
+               if (mmap_kludge) {
+                       *rgb++ = LIMIT(b+y);
+                       *rgb++ = LIMIT(g+y);
+                       *rgb++ = LIMIT(r+y);
+                       if(out_fmt == VIDEO_PALETTE_RGB32)
+                               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);
+                       if(out_fmt == VIDEO_PALETTE_RGB32)
+                               rgb++;
+                       *rgb++ = LIMIT(r+y1);
+                       *rgb++ = LIMIT(g+y1);
+                       *rgb = LIMIT(b+y1);
+               }
+               if(out_fmt == VIDEO_PALETTE_RGB32)
+                       return 8;
+               return 6;
+       case VIDEO_PALETTE_YUV422:
+       case VIDEO_PALETTE_YUYV:
+               y = *yuv++;
+               u = *(rgb+1-linesize);
+               y1 = *yuv;
+               v = *(rgb+3-linesize);
+               *rgb++ = y;
+               *rgb++ = u;
+               *rgb++ = y1;
+               *rgb = v;
+               return 4;
+       case VIDEO_PALETTE_UYVY:
+               u = *(rgb-linesize);
+               y = *yuv++;
+               v = *(rgb+2-linesize);
+               y1 = *yuv;
+               *rgb++ = u;
+               *rgb++ = y;
+               *rgb++ = v;
+               *rgb = y1;
+               return 4;
+       case VIDEO_PALETTE_GREY:
+               *rgb++ = *yuv++;
+               *rgb = *yuv;
+               return 2;
+       default:
+               DBG("Empty: %d\n", out_fmt);
+               return 0;
+       }
+}
+
+
+static int yuvconvert(unsigned char *yuv, unsigned char *rgb, int out_fmt,
+                     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) {
+                       u = *yuv++ - 128;
+                       y = (*yuv++ - 16) * 76310;
+                       v = *yuv++ - 128;
+                       y1 = (*yuv - 16) * 76310;
+               } else {
+                       y = (*yuv++ - 16) * 76310;
+                       u = *yuv++ - 128;
+                       y1 = (*yuv++ - 16) * 76310;
+                       v = *yuv - 128;
+               }
+               r = 104635 * v;
+               g = -25690 * u + -53294 * v;
+               b = 132278 * u;
+               break;
+       default:
+               y = *yuv++;
+               u = *yuv++;
+               y1 = *yuv++;
+               v = *yuv;
+               /* Just to avoid compiler warnings */
+               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;
+       }
+}
+
+static int skipcount(int count, int fmt)
+{
+       switch(fmt) {
+       case VIDEO_PALETTE_GREY:
+               return count;
+       case VIDEO_PALETTE_RGB555:
+       case VIDEO_PALETTE_RGB565:
+       case VIDEO_PALETTE_YUV422:
+       case VIDEO_PALETTE_YUYV:
+       case VIDEO_PALETTE_UYVY:
+               return 2*count;
+       case VIDEO_PALETTE_RGB24:
+               return 3*count;
+       case VIDEO_PALETTE_RGB32:
+               return 4*count;
+       default:
+               return 0;
+       }
+}
+
+static int parse_picture(struct cam_data *cam, int size)
+{
+       u8 *obuf, *ibuf, *end_obuf;
+       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 */
+       mutex_lock(&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)) {
+               LOG("header not found\n");
+               mutex_unlock(&cam->param_lock);
+               return -1;
+       }
+
+       if ((ibuf[16] != VIDEOSIZE_QCIF) && (ibuf[16] != VIDEOSIZE_CIF)) {
+               LOG("wrong video size\n");
+               mutex_unlock(&cam->param_lock);
+               return -1;
+       }
+
+       if (ibuf[17] != SUBSAMPLE_420 && ibuf[17] != SUBSAMPLE_422) {
+               LOG("illegal subtype %d\n",ibuf[17]);
+               mutex_unlock(&cam->param_lock);
+               return -1;
+       }
+       subsample_422 = ibuf[17] == SUBSAMPLE_422;
+
+       if (ibuf[18] != YUVORDER_YUYV && ibuf[18] != YUVORDER_UYVY) {
+               LOG("illegal yuvorder %d\n",ibuf[18]);
+               mutex_unlock(&cam->param_lock);
+               return -1;
+       }
+       in_uyvy = ibuf[18] == YUVORDER_UYVY;
+
+       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");
+               mutex_unlock(&cam->param_lock);
+               return -1;
+       }
+       cols = 8*(ibuf[25] - ibuf[24]);
+       rows = 4*(ibuf[27] - ibuf[26]);
+
+
+       if ((ibuf[28] != NOT_COMPRESSED) && (ibuf[28] != COMPRESSED)) {
+               LOG("illegal compression %d\n",ibuf[28]);
+               mutex_unlock(&cam->param_lock);
+               return -1;
+       }
+       compressed = (ibuf[28] == COMPRESSED);
+
+       if (ibuf[29] != NO_DECIMATION && ibuf[29] != DECIMATION_ENAB) {
+               LOG("illegal decimation %d\n",ibuf[29]);
+               mutex_unlock(&cam->param_lock);
+               return -1;
+       }
+       decimation = (ibuf[29] == DECIMATION_ENAB);
+
+       cam->params.yuvThreshold.yThreshold = ibuf[30];
+       cam->params.yuvThreshold.uvThreshold = ibuf[31];
+       cam->params.status.systemState = ibuf[32];
+       cam->params.status.grabState = ibuf[33];
+       cam->params.status.streamState = ibuf[34];
+       cam->params.status.fatalError = ibuf[35];
+       cam->params.status.cmdError = ibuf[36];
+       cam->params.status.debugFlags = ibuf[37];
+       cam->params.status.vpStatus = ibuf[38];
+       cam->params.status.errorCode = ibuf[39];
+       cam->fps = ibuf[41];
+       mutex_unlock(&cam->param_lock);
+
+       linesize = skipcount(cols, out_fmt);
+       ibuf += FRAME_HEADER_SIZE;
+       size -= FRAME_HEADER_SIZE;
+       ll = ibuf[0] | (ibuf[1] << 8);
+       ibuf += 2;
+       even_line = 1;
+
+       while (size > 0) {
+               size -= (ll+2);
+               if (size < 0) {
+                       LOG("Insufficient data in buffer\n");
+                       return -1;
+               }
+
+               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*/
+                               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) {
+                               DBG("EOL not found giving up after %d/%d"
+                                   " bytes\n", origsize-size, origsize);
+                               return -1;
+                       }
+
+                       ++ibuf; /* skip over EOL */
+
+                       if ((size > 3) && (ibuf[0] == EOI) && (ibuf[1] == EOI) &&
+                          (ibuf[2] == EOI) && (ibuf[3] == EOI)) {
+                               size -= 4;
+                               break;
+                       }
+
+                       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);
+                       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;
+
+       return cam->decompressed_frame.count;
+}
+
+/* InitStreamCap wrapper to select correct start line */
+static inline int init_stream_cap(struct cam_data *cam)
+{
+       return do_command(cam, CPIA_COMMAND_InitStreamCap,
+                         0, cam->params.streamStartLine, 0, 0);
+}
+
+
+/*  find_over_exposure
+ *    Finds a suitable value of OverExposure for use with SetFlickerCtrl
+ *    Some calculation is required because this value changes with the brightness
+ *    set with SetColourParameters
+ *
+ *  Parameters: Brightness  -  last brightness value set with SetColourParameters
+ *
+ *  Returns: OverExposure value to use with SetFlickerCtrl
+ */
+#define FLICKER_MAX_EXPOSURE                    250
+#define FLICKER_ALLOWABLE_OVER_EXPOSURE         146
+#define FLICKER_BRIGHTNESS_CONSTANT             59
+static int find_over_exposure(int brightness)
+{
+       int MaxAllowableOverExposure, OverExposure;
+
+       MaxAllowableOverExposure = FLICKER_MAX_EXPOSURE - brightness -
+                                  FLICKER_BRIGHTNESS_CONSTANT;
+
+       if (MaxAllowableOverExposure < FLICKER_ALLOWABLE_OVER_EXPOSURE) {
+               OverExposure = MaxAllowableOverExposure;
+       } else {
+               OverExposure = FLICKER_ALLOWABLE_OVER_EXPOSURE;
+       }
+
+       return OverExposure;
+}
+#undef FLICKER_MAX_EXPOSURE
+#undef FLICKER_ALLOWABLE_OVER_EXPOSURE
+#undef FLICKER_BRIGHTNESS_CONSTANT
+
+/* update various camera modes and settings */
+static void dispatch_commands(struct cam_data *cam)
+{
+       mutex_lock(&cam->param_lock);
+       if (cam->cmd_queue==COMMAND_NONE) {
+               mutex_unlock(&cam->param_lock);
+               return;
+       }
+       DEB_BYTE(cam->cmd_queue);
+       DEB_BYTE(cam->cmd_queue>>8);
+       if (cam->cmd_queue & COMMAND_SETFORMAT) {
+               do_command(cam, CPIA_COMMAND_SetFormat,
+                          cam->params.format.videoSize,
+                          cam->params.format.subSample,
+                          cam->params.format.yuvOrder, 0);
+               do_command(cam, CPIA_COMMAND_SetROI,
+                          cam->params.roi.colStart, cam->params.roi.colEnd,
+                          cam->params.roi.rowStart, cam->params.roi.rowEnd);
+               cam->first_frame = 1;
+       }
+
+       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_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_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_SETEXPOSURE) {
+               do_command_extended(cam, CPIA_COMMAND_SetExposure,
+                                   cam->params.exposure.gainMode,
+                                   1,
+                                   cam->params.exposure.compMode,
+                                   cam->params.exposure.centreWeight,
+                                   cam->params.exposure.gain,
+                                   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);
+               if(cam->params.exposure.expMode != 1) {
+                       do_command_extended(cam, CPIA_COMMAND_SetExposure,
+                                           0,
+                                           cam->params.exposure.expMode,
+                                           0, 0,
+                                           cam->params.exposure.gain,
+                                           cam->params.exposure.fineExp,
+                                           cam->params.exposure.coarseExpLo,
+                                           cam->params.exposure.coarseExpHi,
+                                           0, 0, 0, 0);
+               }
+       }
+
+       if (cam->cmd_queue & COMMAND_SETCOLOURBALANCE) {
+               if (cam->params.colourBalance.balanceMode == 1) {
+                       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 == 2) {
+                       do_command(cam, CPIA_COMMAND_SetColourBalance,
+                                  2, 0, 0, 0);
+               }
+               if (cam->params.colourBalance.balanceMode == 3) {
+                       do_command(cam, CPIA_COMMAND_SetColourBalance,
+                                  3, 0, 0, 0);
+               }
+       }
+
+       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)
+               do_command(cam, CPIA_COMMAND_SetYUVThresh,
+                          cam->params.yuvThreshold.yThreshold,
+                          cam->params.yuvThreshold.uvThreshold, 0, 0);
+
+       if (cam->cmd_queue & COMMAND_SETCOMPRESSIONPARAMS)
+               do_command_extended(cam, CPIA_COMMAND_SetCompressionParams,
+                           0, 0, 0, 0,
+                           cam->params.compressionParams.hysteresis,
+                           cam->params.compressionParams.threshMax,
+                           cam->params.compressionParams.smallStep,
+                           cam->params.compressionParams.largeStep,
+                           cam->params.compressionParams.decimationHysteresis,
+                           cam->params.compressionParams.frDiffStepThresh,
+                           cam->params.compressionParams.qDiffStepThresh,
+                           cam->params.compressionParams.decimationThreshMod);
+
+       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_SETSENSORFPS)
+               do_command(cam, CPIA_COMMAND_SetSensorFPS,
+                          cam->params.sensorFps.divisor,
+                          cam->params.sensorFps.baserate, 0, 0);
+
+       if (cam->cmd_queue & COMMAND_SETFLICKERCTRL)
+               do_command(cam, CPIA_COMMAND_SetFlickerCtrl,
+                          cam->params.flickerControl.flickerMode,
+                          cam->params.flickerControl.coarseJump,
+                          abs(cam->params.flickerControl.allowableOverExposure),
+                          0);
+
+       if (cam->cmd_queue & COMMAND_SETECPTIMING)
+               do_command(cam, CPIA_COMMAND_SetECPTiming,
+                          cam->params.ecpTiming, 0, 0, 0);
+
+       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_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);
+         }
+
+       cam->cmd_queue = COMMAND_NONE;
+       mutex_unlock(&cam->param_lock);
+       return;
+}
+
+
+
+static void set_flicker(struct cam_params *params, volatile u32 *command_flags,
+                       int on)
+{
+       /* Everything in here is from the Windows driver */
+#define FIRMWARE_VERSION(x,y) (params->version.firmwareVersion == (x) && \
+                              params->version.firmwareRevision == (y))
+/* define for compgain calculation */
+#if 0
+#define COMPGAIN(base, curexp, newexp) \
+    (u8) ((((float) base - 128.0) * ((float) curexp / (float) newexp)) + 128.5)
+#define EXP_FROM_COMP(basecomp, curcomp, curexp) \
+    (u16)((float)curexp * (float)(u8)(curcomp + 128) / (float)(u8)(basecomp - 128))
+#else
+  /* equivalent functions without floating point math */
+#define COMPGAIN(base, curexp, newexp) \
+    (u8)(128 + (((u32)(2*(base-128)*curexp + newexp)) / (2* newexp)) )
+#define EXP_FROM_COMP(basecomp, curcomp, curexp) \
+     (u16)(((u32)(curexp * (u8)(curcomp + 128)) / (u8)(basecomp - 128)))
+#endif
+
+
+       int currentexp = params->exposure.coarseExpLo +
+                        params->exposure.coarseExpHi*256;
+       int startexp;
+       if (on) {
+               int cj = params->flickerControl.coarseJump;
+               params->flickerControl.flickerMode = 1;
+               params->flickerControl.disabled = 0;
+               if(params->exposure.expMode != 2)
+                       *command_flags |= COMMAND_SETEXPOSURE;
+               params->exposure.expMode = 2;
+               currentexp = currentexp << params->exposure.gain;
+               params->exposure.gain = 0;
+               /* round down current exposure to nearest value */
+               startexp = (currentexp + ROUND_UP_EXP_FOR_FLICKER) / cj;
+               if(startexp < 1)
+                       startexp = 1;
+               startexp = (startexp * cj) - 1;
+               if(FIRMWARE_VERSION(1,2))
+                       while(startexp > MAX_EXP_102)
+                               startexp -= cj;
+               else
+                       while(startexp > MAX_EXP)
+                               startexp -= cj;
+               params->exposure.coarseExpLo = startexp & 0xff;
+               params->exposure.coarseExpHi = startexp >> 8;
+               if (currentexp > startexp) {
+                       if (currentexp > (2 * startexp))
+                               currentexp = 2 * startexp;
+                       params->exposure.redComp = COMPGAIN (COMP_RED, currentexp, startexp);
+                       params->exposure.green1Comp = COMPGAIN (COMP_GREEN1, currentexp, startexp);
+                       params->exposure.green2Comp = COMPGAIN (COMP_GREEN2, currentexp, startexp);
+                       params->exposure.blueComp = COMPGAIN (COMP_BLUE, currentexp, startexp);
+               } else {
+                       params->exposure.redComp = COMP_RED;
+                       params->exposure.green1Comp = COMP_GREEN1;
+                       params->exposure.green2Comp = COMP_GREEN2;
+                       params->exposure.blueComp = COMP_BLUE;
+               }
+               if(FIRMWARE_VERSION(1,2))
+                       params->exposure.compMode = 0;
+               else
+                       params->exposure.compMode = 1;
+
+               params->apcor.gain1 = 0x18;
+               params->apcor.gain2 = 0x18;
+               params->apcor.gain4 = 0x16;
+               params->apcor.gain8 = 0x14;
+               *command_flags |= COMMAND_SETAPCOR;
+       } else {
+               params->flickerControl.flickerMode = 0;
+               params->flickerControl.disabled = 1;
+               /* Coarse = average of equivalent coarse for each comp channel */
+               startexp = EXP_FROM_COMP(COMP_RED, params->exposure.redComp, currentexp);
+               startexp += EXP_FROM_COMP(COMP_GREEN1, params->exposure.green1Comp, currentexp);
+               startexp += EXP_FROM_COMP(COMP_GREEN2, params->exposure.green2Comp, currentexp);
+               startexp += EXP_FROM_COMP(COMP_BLUE, params->exposure.blueComp, currentexp);
+               startexp = startexp >> 2;
+               while(startexp > MAX_EXP &&
+                     params->exposure.gain < params->exposure.gainMode-1) {
+                       startexp = startexp >> 1;
+                       ++params->exposure.gain;
+               }
+               if(FIRMWARE_VERSION(1,2) && startexp > MAX_EXP_102)
+                       startexp = MAX_EXP_102;
+               if(startexp > MAX_EXP)
+                       startexp = MAX_EXP;
+               params->exposure.coarseExpLo = startexp&0xff;
+               params->exposure.coarseExpHi = startexp >> 8;
+               params->exposure.redComp = COMP_RED;
+               params->exposure.green1Comp = COMP_GREEN1;
+               params->exposure.green2Comp = COMP_GREEN2;
+               params->exposure.blueComp = COMP_BLUE;
+               params->exposure.compMode = 1;
+               *command_flags |= COMMAND_SETEXPOSURE;
+               params->apcor.gain1 = 0x18;
+               params->apcor.gain2 = 0x16;
+               params->apcor.gain4 = 0x24;
+               params->apcor.gain8 = 0x34;
+               *command_flags |= COMMAND_SETAPCOR;
+       }
+       params->vlOffset.gain1 = 20;
+       params->vlOffset.gain2 = 24;
+       params->vlOffset.gain4 = 26;
+       params->vlOffset.gain8 = 26;
+       *command_flags |= COMMAND_SETVLOFFSET;
+#undef FIRMWARE_VERSION
+#undef EXP_FROM_COMP
+#undef COMPGAIN
+}
+
+#define FIRMWARE_VERSION(x,y) (cam->params.version.firmwareVersion == (x) && \
+                              cam->params.version.firmwareRevision == (y))
+/* monitor the exposure and adjust the sensor frame rate if needed */
+static void monitor_exposure(struct cam_data *cam)
+{
+       u8 exp_acc, bcomp, gain, coarseL, cmd[8], data[8];
+       int retval, light_exp, dark_exp, very_dark_exp;
+       int old_exposure, new_exposure, framerate;
+
+       /* get necessary stats and register settings from camera */
+       /* do_command can't handle this, so do it ourselves */
+       cmd[0] = CPIA_COMMAND_ReadVPRegs>>8;
+       cmd[1] = CPIA_COMMAND_ReadVPRegs&0xff;
+       cmd[2] = 30;
+       cmd[3] = 4;
+       cmd[4] = 9;
+       cmd[5] = 8;
+       cmd[6] = 8;
+       cmd[7] = 0;
+       retval = cam->ops->transferCmd(cam->lowlevel_data, cmd, data);
+       if (retval) {
+               LOG("ReadVPRegs(30,4,9,8) - failed, retval=%d\n",
+                   retval);
+               return;
+       }
+       exp_acc = data[0];
+       bcomp = data[1];
+       gain = data[2];
+       coarseL = data[3];
+
+       mutex_lock(&cam->param_lock);
+       light_exp = cam->params.colourParams.brightness +
+                   TC - 50 + EXP_ACC_LIGHT;
+       if(light_exp > 255)
+               light_exp = 255;
+       dark_exp = cam->params.colourParams.brightness +
+                  TC - 50 - EXP_ACC_DARK;
+       if(dark_exp < 0)
+               dark_exp = 0;
+       very_dark_exp = dark_exp/2;
+
+       old_exposure = cam->params.exposure.coarseExpHi * 256 +
+                      cam->params.exposure.coarseExpLo;
+
+       if(!cam->params.flickerControl.disabled) {
+               /* Flicker control on */
+               int max_comp = FIRMWARE_VERSION(1,2) ? MAX_COMP : HIGH_COMP_102;
+               bcomp += 128;   /* decode */
+               if(bcomp >= max_comp && exp_acc < dark_exp) {
+                       /* dark */
+                       if(exp_acc < very_dark_exp) {
+                               /* very dark */
+                               if(cam->exposure_status == EXPOSURE_VERY_DARK)
+                                       ++cam->exposure_count;
+                               else {
+                                       cam->exposure_status = EXPOSURE_VERY_DARK;
+                                       cam->exposure_count = 1;
+                               }
+                       } else {
+                               /* just dark */
+                               if(cam->exposure_status == EXPOSURE_DARK)
+                                       ++cam->exposure_count;
+                               else {
+                                       cam->exposure_status = EXPOSURE_DARK;
+                                       cam->exposure_count = 1;
+                               }
+                       }
+               } else if(old_exposure <= LOW_EXP || exp_acc > light_exp) {
+                       /* light */
+                       if(old_exposure <= VERY_LOW_EXP) {
+                               /* very light */
+                               if(cam->exposure_status == EXPOSURE_VERY_LIGHT)
+                                       ++cam->exposure_count;
+                               else {
+                                       cam->exposure_status = EXPOSURE_VERY_LIGHT;
+                                       cam->exposure_count = 1;
+                               }
+                       } else {
+                               /* just light */
+                               if(cam->exposure_status == EXPOSURE_LIGHT)
+                                       ++cam->exposure_count;
+                               else {
+                                       cam->exposure_status = EXPOSURE_LIGHT;
+                                       cam->exposure_count = 1;
+                               }
+                       }
+               } else {
+                       /* not dark or light */
+                       cam->exposure_status = EXPOSURE_NORMAL;
+               }
+       } else {
+               /* Flicker control off */
+               if(old_exposure >= MAX_EXP && exp_acc < dark_exp) {
+                       /* dark */
+                       if(exp_acc < very_dark_exp) {
+                               /* very dark */
+                               if(cam->exposure_status == EXPOSURE_VERY_DARK)
+                                       ++cam->exposure_count;
+                               else {
+                                       cam->exposure_status = EXPOSURE_VERY_DARK;
+                                       cam->exposure_count = 1;
+                               }
+                       } else {
+                               /* just dark */
+                               if(cam->exposure_status == EXPOSURE_DARK)
+                                       ++cam->exposure_count;
+                               else {
+                                       cam->exposure_status = EXPOSURE_DARK;
+                                       cam->exposure_count = 1;
+                               }
+                       }
+               } else if(old_exposure <= LOW_EXP || exp_acc > light_exp) {
+                       /* light */
+                       if(old_exposure <= VERY_LOW_EXP) {
+                               /* very light */
+                               if(cam->exposure_status == EXPOSURE_VERY_LIGHT)
+                                       ++cam->exposure_count;
+                               else {
+                                       cam->exposure_status = EXPOSURE_VERY_LIGHT;
+                                       cam->exposure_count = 1;
+                               }
+                       } else {
+                               /* just light */
+                               if(cam->exposure_status == EXPOSURE_LIGHT)
+                                       ++cam->exposure_count;
+                               else {
+                                       cam->exposure_status = EXPOSURE_LIGHT;
+                                       cam->exposure_count = 1;
+                               }
+                       }
+               } else {
+                       /* not dark or light */
+                       cam->exposure_status = EXPOSURE_NORMAL;
+               }
+       }
+
+       framerate = cam->fps;
+       if(framerate > 30 || framerate < 1)
+               framerate = 1;
+
+       if(!cam->params.flickerControl.disabled) {
+               /* Flicker control on */
+               if((cam->exposure_status == EXPOSURE_VERY_DARK ||
+                   cam->exposure_status == EXPOSURE_DARK) &&
+                  cam->exposure_count >= DARK_TIME*framerate &&
+                  cam->params.sensorFps.divisor < 3) {
+
+                       /* dark for too long */
+                       ++cam->params.sensorFps.divisor;
+                       cam->cmd_queue |= COMMAND_SETSENSORFPS;
+
+                       cam->params.flickerControl.coarseJump =
+                               flicker_jumps[cam->mainsFreq]
+                                            [cam->params.sensorFps.baserate]
+                                            [cam->params.sensorFps.divisor];
+                       cam->cmd_queue |= COMMAND_SETFLICKERCTRL;
+
+                       new_exposure = cam->params.flickerControl.coarseJump-1;
+                       while(new_exposure < old_exposure/2)
+                               new_exposure += cam->params.flickerControl.coarseJump;
+                       cam->params.exposure.coarseExpLo = new_exposure & 0xff;
+                       cam->params.exposure.coarseExpHi = new_exposure >> 8;
+                       cam->cmd_queue |= COMMAND_SETEXPOSURE;
+                       cam->exposure_status = EXPOSURE_NORMAL;
+                       LOG("Automatically decreasing sensor_fps\n");
+
+               } else if((cam->exposure_status == EXPOSURE_VERY_LIGHT ||
+                   cam->exposure_status == EXPOSURE_LIGHT) &&
+                  cam->exposure_count >= LIGHT_TIME*framerate &&
+                  cam->params.sensorFps.divisor > 0) {
+
+                       /* light for too long */
+                       int max_exp = FIRMWARE_VERSION(1,2) ? MAX_EXP_102 : MAX_EXP ;
+
+                       --cam->params.sensorFps.divisor;
+                       cam->cmd_queue |= COMMAND_SETSENSORFPS;
+
+                       cam->params.flickerControl.coarseJump =
+                               flicker_jumps[cam->mainsFreq]
+                                            [cam->params.sensorFps.baserate]
+                                            [cam->params.sensorFps.divisor];
+                       cam->cmd_queue |= COMMAND_SETFLICKERCTRL;
+
+                       new_exposure = cam->params.flickerControl.coarseJump-1;
+                       while(new_exposure < 2*old_exposure &&
+                             new_exposure+
+                             cam->params.flickerControl.coarseJump < max_exp)
+                               new_exposure += cam->params.flickerControl.coarseJump;
+                       cam->params.exposure.coarseExpLo = new_exposure & 0xff;
+                       cam->params.exposure.coarseExpHi = new_exposure >> 8;
+                       cam->cmd_queue |= COMMAND_SETEXPOSURE;
+                       cam->exposure_status = EXPOSURE_NORMAL;
+                       LOG("Automatically increasing sensor_fps\n");
+               }
+       } else {
+               /* Flicker control off */
+               if((cam->exposure_status == EXPOSURE_VERY_DARK ||
+                   cam->exposure_status == EXPOSURE_DARK) &&
+                  cam->exposure_count >= DARK_TIME*framerate &&
+                  cam->params.sensorFps.divisor < 3) {
+
+                       /* dark for too long */
+                       ++cam->params.sensorFps.divisor;
+                       cam->cmd_queue |= COMMAND_SETSENSORFPS;
+
+                       if(cam->params.exposure.gain > 0) {
+                               --cam->params.exposure.gain;
+                               cam->cmd_queue |= COMMAND_SETEXPOSURE;
+                       }
+                       cam->exposure_status = EXPOSURE_NORMAL;
+                       LOG("Automatically decreasing sensor_fps\n");
+
+               } else if((cam->exposure_status == EXPOSURE_VERY_LIGHT ||
+                   cam->exposure_status == EXPOSURE_LIGHT) &&
+                  cam->exposure_count >= LIGHT_TIME*framerate &&
+                  cam->params.sensorFps.divisor > 0) {
+
+                       /* light for too long */
+                       --cam->params.sensorFps.divisor;
+                       cam->cmd_queue |= COMMAND_SETSENSORFPS;
+
+                       if(cam->params.exposure.gain <
+                          cam->params.exposure.gainMode-1) {
+                               ++cam->params.exposure.gain;
+                               cam->cmd_queue |= COMMAND_SETEXPOSURE;
+                       }
+                       cam->exposure_status = EXPOSURE_NORMAL;
+                       LOG("Automatically increasing sensor_fps\n");
+               }
+       }
+       mutex_unlock(&cam->param_lock);
+}
+
+/*-----------------------------------------------------------------*/
+/* if flicker is switched off, this function switches it back on.It checks,
+   however, that conditions are suitable before restarting it.
+   This should only be called for firmware version 1.2.
+
+   It also adjust the colour balance when an exposure step is detected - as
+   long as flicker is running
+*/
+static void restart_flicker(struct cam_data *cam)
+{
+       int cam_exposure, old_exp;
+       if(!FIRMWARE_VERSION(1,2))
+               return;
+       mutex_lock(&cam->param_lock);
+       if(cam->params.flickerControl.flickerMode == 0 ||
+          cam->raw_image[39] == 0) {
+               mutex_unlock(&cam->param_lock);
+               return;
+       }
+       cam_exposure = cam->raw_image[39]*2;
+       old_exp = cam->params.exposure.coarseExpLo +
+                 cam->params.exposure.coarseExpHi*256;
+       /*
+         see how far away camera exposure is from a valid
+         flicker exposure value
+       */
+       cam_exposure %= cam->params.flickerControl.coarseJump;
+       if(!cam->params.flickerControl.disabled &&
+          cam_exposure <= cam->params.flickerControl.coarseJump - 3) {
+               /* Flicker control auto-disabled */
+               cam->params.flickerControl.disabled = 1;
+       }
+
+       if(cam->params.flickerControl.disabled &&
+          cam->params.flickerControl.flickerMode &&
+          old_exp > cam->params.flickerControl.coarseJump +
+                    ROUND_UP_EXP_FOR_FLICKER) {
+               /* exposure is now high enough to switch
+                  flicker control back on */
+               set_flicker(&cam->params, &cam->cmd_queue, 1);
+               if((cam->cmd_queue & COMMAND_SETEXPOSURE) &&
+                  cam->params.exposure.expMode == 2)
+                       cam->exposure_status = EXPOSURE_NORMAL;
+
+       }
+       mutex_unlock(&cam->param_lock);
+}
+#undef FIRMWARE_VERSION
+
+static int clear_stall(struct cam_data *cam)
+{
+       /* FIXME: Does this actually work? */
+       LOG("Clearing stall\n");
+
+       cam->ops->streamRead(cam->lowlevel_data, cam->raw_image, 0);
+       do_command(cam, CPIA_COMMAND_GetCameraStatus,0,0,0,0);
+       return cam->params.status.streamState != STREAM_PAUSED;
+}
+
+/* kernel thread function to read image from camera */
+static int fetch_frame(void *data)
+{
+       int image_size, retry;
+       struct cam_data *cam = (struct cam_data *)data;
+       unsigned long oldjif, rate, diff;
+
+       /* 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);
+                       /* Trial & error - Discarding a frame prevents the
+                          first frame from having an error in the data. */
+                       do_command(cam, CPIA_COMMAND_DiscardFrame, 0, 0, 0, 0);
+               }
+
+               /* init camera upload */
+               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 */
+                       int count = 0;
+                       do_command(cam, CPIA_COMMAND_GetCameraStatus,0,0,0,0);
+                       while (cam->params.status.streamState != STREAM_READY) {
+                               if(++count > READY_TIMEOUT)
+                                       break;
+                               if(cam->params.status.streamState ==
+                                  STREAM_PAUSED) {
+                                       /* Bad news */
+                                       if(!clear_stall(cam))
+                                               return -EIO;
+                               }
+
+                               cond_resched();
+
+                               /* sleep for 10 ms, hopefully ;) */
+                               msleep_interruptible(10);
+                               if (signal_pending(current))
+                                       return -EINTR;
+
+                               do_command(cam, CPIA_COMMAND_GetCameraStatus,
+                                          0, 0, 0, 0);
+                       }
+                       if(cam->params.status.streamState != STREAM_READY) {
+                               continue;
+                       }
+               }
+
+               cond_resched();
+
+               /* grab image from camera */
+               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 */
+
+               /* Switch flicker control back on if it got turned off */
+               restart_flicker(cam);
+
+               /* If AEC is enabled, monitor the exposure and
+                  adjust the sensor frame rate if needed */
+               if(cam->params.exposure.expMode == 2)
+                       monitor_exposure(cam);
+
+               /* camera idle now so dispatch queued commands */
+               dispatch_commands(cam);
+
+               /* Update our knowledge of the camera state */
+               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);
+
+               /* decompress and convert image to by copying it from
+                * raw_image to decompressed_frame
+                */
+
+               cond_resched();
+
+               cam->image_size = parse_picture(cam, image_size);
+               if (cam->image_size <= 0) {
+                       DBG("parse_picture failed %d\n", cam->image_size);
+                       if(cam->params.compression.mode !=
+                          CPIA_COMPRESSION_NONE) {
+                               /* Compression may not work right if we
+                                  had a bad frame, get the next one
+                                  uncompressed. */
+                               cam->first_frame = 1;
+                               do_command(cam, CPIA_COMMAND_SetGrabMode,
+                                          CPIA_GRAB_SINGLE, 0, 0, 0);
+                               /* FIXME: Trial & error - need up to 70ms for
+                                  the grab mode change to complete ? */
+                               msleep_interruptible(70);
+                               if (signal_pending(current))
+                                       return -EINTR;
+                       }
+               } 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 (cam->first_frame) {
+                       cam->first_frame = 0;
+                       do_command(cam, CPIA_COMMAND_SetCompression,
+                                  cam->params.compression.mode,
+                                  cam->params.compression.decimation, 0, 0);
+
+                       /* Switch from single-grab to continuous grab */
+                       do_command(cam, CPIA_COMMAND_SetGrabMode,
+                                  CPIA_GRAB_CONTINUOUS, 0, 0, 0);
+               }
+               return 0;
+       }
+       return -EIO;
+}
+
+static int capture_frame(struct cam_data *cam, struct video_mmap *vm)
+{
+       if (!cam->frame_buf) {
+               /* we do lazy allocation */
+               int err;
+               if ((err = allocate_frame_buf(cam)))
+                       return err;
+       }
+
+       cam->curframe = vm->frame;
+       cam->frame[cam->curframe].state = FRAME_READY;
+       return fetch_frame(cam);
+}
+
+static int goto_high_power(struct cam_data *cam)
+{
+       if (do_command(cam, CPIA_COMMAND_GotoHiPower, 0, 0, 0, 0))
+               return -EIO;
+       msleep_interruptible(40);       /* windows driver does it too */
+       if(signal_pending(current))
+               return -EINTR;
+       if (do_command(cam, CPIA_COMMAND_GetCameraStatus, 0, 0, 0, 0))
+               return -EIO;
+       if (cam->params.status.systemState == HI_POWER_STATE) {
+               DBG("camera now in HIGH power state\n");
+               return 0;
+       }
+       printstatus(cam);
+       return -EIO;
+}
+
+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) {
+               DBG("camera now in LOW power state\n");
+               return 0;
+       }
+       printstatus(cam);
+       return -1;
+}
+
+static void save_camera_state(struct cam_data *cam)
+{
+       if(!(cam->cmd_queue & COMMAND_SETCOLOURBALANCE))
+               do_command(cam, CPIA_COMMAND_GetColourBalance, 0, 0, 0, 0);
+       if(!(cam->cmd_queue & COMMAND_SETEXPOSURE))
+               do_command(cam, CPIA_COMMAND_GetExposure, 0, 0, 0, 0);
+
+       DBG("%d/%d/%d/%d/%d/%d/%d/%d\n",
+            cam->params.exposure.gain,
+            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);
+       DBG("%d/%d/%d\n",
+            cam->params.colourBalance.redGain,
+            cam->params.colourBalance.greenGain,
+            cam->params.colourBalance.blueGain);
+}
+
+static int set_camera_state(struct cam_data *cam)
+{
+       cam->cmd_queue = COMMAND_SETCOMPRESSION |
+                        COMMAND_SETCOMPRESSIONTARGET |
+                        COMMAND_SETCOLOURPARAMS |
+                        COMMAND_SETFORMAT |
+                        COMMAND_SETYUVTHRESH |
+                        COMMAND_SETECPTIMING |
+                        COMMAND_SETCOMPRESSIONPARAMS |
+                        COMMAND_SETEXPOSURE |
+                        COMMAND_SETCOLOURBALANCE |
+                        COMMAND_SETSENSORFPS |
+                        COMMAND_SETAPCOR |
+                        COMMAND_SETFLICKERCTRL |
+                        COMMAND_SETVLOFFSET;
+
+       do_command(cam, CPIA_COMMAND_SetGrabMode, CPIA_GRAB_SINGLE,0,0,0);
+       dispatch_commands(cam);
+
+       /* Wait 6 frames for the sensor to get all settings and
+          AEC/ACB to settle */
+       msleep_interruptible(6*(cam->params.sensorFps.baserate ? 33 : 40) *
+                              (1 << cam->params.sensorFps.divisor) + 10);
+
+       if(signal_pending(current))
+               return -EINTR;
+
+       save_camera_state(cam);
+
+       return 0;
+}
+
+static void get_version_information(struct cam_data *cam)
+{
+       /* GetCPIAVersion */
+       do_command(cam, CPIA_COMMAND_GetCPIAVersion, 0, 0, 0, 0);
+
+       /* GetPnPID */
+       do_command(cam, CPIA_COMMAND_GetPnPID, 0, 0, 0, 0);
+}
+
+/* initialize camera */
+static int reset_camera(struct cam_data *cam)
+{
+       int err;
+       /* Start the camera in low power mode */
+       if (goto_low_power(cam)) {
+               if (cam->params.status.systemState != WARM_BOOT_STATE)
+                       return -ENODEV;
+
+               /* FIXME: this is just dirty trial and error */
+               err = goto_high_power(cam);
+               if(err)
+                       return err;
+               do_command(cam, CPIA_COMMAND_DiscardFrame, 0, 0, 0, 0);
+               if (goto_low_power(cam))
+                       return -ENODEV;
+       }
+
+       /* procedure described in developer's guide p3-28 */
+
+       /* Check the firmware version. */
+       cam->params.version.firmwareVersion = 0;
+       get_version_information(cam);
+       if (cam->params.version.firmwareVersion != 1)
+               return -ENODEV;
+
+       /* A bug in firmware 1-02 limits gainMode to 2 */
+       if(cam->params.version.firmwareRevision <= 2 &&
+          cam->params.exposure.gainMode > 2) {
+               cam->params.exposure.gainMode = 2;
+       }
+
+       /* 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) */
+
+       /* Set streamState before transition to high power to avoid bug
+        * in firmware 1-02 */
+       do_command(cam, CPIA_COMMAND_ModifyCameraStatus, STREAMSTATE, 0,
+                  STREAM_NOT_READY, 0);
+
+       /* GotoHiPower */
+       err = goto_high_power(cam);
+       if (err)
+               return err;
+
+       /* Check the camera status */
+       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)) {
+                       /* Fatal error in camera */
+                       return -EIO;
+               } 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)
+                       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);
+
+       /* set camera to a known state */
+       return set_camera_state(cam);
+}
+
+static void put_cam(struct cpia_camera_ops* ops)
+{
+       module_put(ops->owner);
+}
+
+/* ------------------------- V4L interface --------------------- */
+static int cpia_open(struct file *file)
+{
+       struct video_device *dev = video_devdata(file);
+       struct cam_data *cam = video_get_drvdata(dev);
+       int err;
+
+       if (!cam) {
+               DBG("Internal error, cam_data not found!\n");
+               return -ENODEV;
+       }
+
+       if (cam->open_count > 0) {
+               DBG("Camera already open\n");
+               return -EBUSY;
+       }
+
+       if (!try_module_get(cam->ops->owner))
+               return -ENODEV;
+
+       mutex_lock(&cam->busy_lock);
+       err = -ENOMEM;
+       if (!cam->raw_image) {
+               cam->raw_image = rvmalloc(CPIA_MAX_IMAGE_SIZE);
+               if (!cam->raw_image)
+                       goto oops;
+       }
+
+       if (!cam->decompressed_frame.data) {
+               cam->decompressed_frame.data = rvmalloc(CPIA_MAX_FRAME_SIZE);
+               if (!cam->decompressed_frame.data)
+                       goto oops;
+       }
+
+       /* open cpia */
+       err = -ENODEV;
+       if (cam->ops->open(cam->lowlevel_data))
+               goto oops;
+
+       /* reset the camera */
+       if ((err = reset_camera(cam)) != 0) {
+               cam->ops->close(cam->lowlevel_data);
+               goto oops;
+       }
+
+       err = -EINTR;
+       if(signal_pending(current))
+               goto oops;
+
+       /* Set ownership of /proc/cpia/videoX to current user */
+       if(cam->proc_entry)
+               cam->proc_entry->uid = current_uid();
+
+       /* set mark for loading first frame uncompressed */
+       cam->first_frame = 1;
+
+       /* init it to something */
+       cam->mmap_kludge = 0;
+
+       ++cam->open_count;
+       file->private_data = dev;
+       mutex_unlock(&cam->busy_lock);
+       return 0;
+
+ oops:
+       if (cam->decompressed_frame.data) {
+               rvfree(cam->decompressed_frame.data, CPIA_MAX_FRAME_SIZE);
+               cam->decompressed_frame.data = NULL;
+       }
+       if (cam->raw_image) {
+               rvfree(cam->raw_image, CPIA_MAX_IMAGE_SIZE);
+               cam->raw_image = NULL;
+       }
+       mutex_unlock(&cam->busy_lock);
+       put_cam(cam->ops);
+       return err;
+}
+
+static int cpia_close(struct file *file)
+{
+       struct  video_device *dev = file->private_data;
+       struct cam_data *cam = video_get_drvdata(dev);
+
+       if (cam->ops) {
+               /* Return ownership of /proc/cpia/videoX to root */
+               if(cam->proc_entry)
+                       cam->proc_entry->uid = 0;
+
+               /* save camera state for later open (developers guide ch 3.5.3) */
+               save_camera_state(cam);
+
+               /* GotoLoPower */
+               goto_low_power(cam);
+
+               /* Update the camera status */
+               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);
+
+               put_cam(cam->ops);
+       }
+
+       if (--cam->open_count == 0) {
+               /* clean up capture-buffers */
+               if (cam->raw_image) {
+                       rvfree(cam->raw_image, CPIA_MAX_IMAGE_SIZE);
+                       cam->raw_image = NULL;
+               }
+
+               if (cam->decompressed_frame.data) {
+                       rvfree(cam->decompressed_frame.data, CPIA_MAX_FRAME_SIZE);
+                       cam->decompressed_frame.data = NULL;
+               }
+
+               if (cam->frame_buf)
+                       free_frame_buf(cam);
+
+               if (!cam->ops)
+                       kfree(cam);
+       }
+       file->private_data = NULL;
+
+       return 0;
+}
+
+static ssize_t cpia_read(struct file *file, char __user *buf,
+                        size_t count, loff_t *ppos)
+{
+       struct video_device *dev = file->private_data;
+       struct cam_data *cam = video_get_drvdata(dev);
+       int err;
+
+       /* make this _really_ smp and multithread-safe */
+       if (mutex_lock_interruptible(&cam->busy_lock))
+               return -EINTR;
+
+       if (!buf) {
+               DBG("buf NULL\n");
+               mutex_unlock(&cam->busy_lock);
+               return -EINVAL;
+       }
+
+       if (!count) {
+               DBG("count 0\n");
+               mutex_unlock(&cam->busy_lock);
+               return 0;
+       }
+
+       if (!cam->ops) {
+               DBG("ops NULL\n");
+               mutex_unlock(&cam->busy_lock);
+               return -ENODEV;
+       }
+
+       /* upload frame */
+       cam->decompressed_frame.state = FRAME_READY;
+       cam->mmap_kludge=0;
+       if((err = fetch_frame(cam)) != 0) {
+               DBG("ERROR from fetch_frame: %d\n", err);
+               mutex_unlock(&cam->busy_lock);
+               return err;
+       }
+       cam->decompressed_frame.state = FRAME_UNUSED;
+
+       /* copy data to user space */
+       if (cam->decompressed_frame.count > count) {
+               DBG("count wrong: %d, %lu\n", cam->decompressed_frame.count,
+                   (unsigned long) count);
+               mutex_unlock(&cam->busy_lock);
+               return -EFAULT;
+       }
+       if (copy_to_user(buf, cam->decompressed_frame.data,
+                       cam->decompressed_frame.count)) {
+               DBG("copy_to_user failed\n");
+               mutex_unlock(&cam->busy_lock);
+               return -EFAULT;
+       }
+
+       mutex_unlock(&cam->busy_lock);
+       return cam->decompressed_frame.count;
+}
+
+static long cpia_do_ioctl(struct file *file, unsigned int cmd, void *arg)
+{
+       struct video_device *dev = file->private_data;
+       struct cam_data *cam = video_get_drvdata(dev);
+       int retval = 0;
+
+       if (!cam || !cam->ops)
+               return -ENODEV;
+
+       /* make this _really_ smp-safe */
+       if (mutex_lock_interruptible(&cam->busy_lock))
+               return -EINTR;
+
+       /* DBG("cpia_ioctl: %u\n", cmd); */
+
+       switch (cmd) {
+       /* query capabilities */
+       case VIDIOCGCAP:
+       {
+               struct video_capability *b = arg;
+
+               DBG("VIDIOCGCAP\n");
+               strcpy(b->name, "CPiA Camera");
+               b->type = VID_TYPE_CAPTURE | VID_TYPE_SUBCAPTURE;
+               b->channels = 1;
+               b->audios = 0;
+               b->maxwidth = 352;      /* VIDEOSIZE_CIF */
+               b->maxheight = 288;
+               b->minwidth = 48;       /* VIDEOSIZE_48_48 */
+               b->minheight = 48;
+               break;
+       }
+
+       /* get/set video source - we are a camera and nothing else */
+       case VIDIOCGCHAN:
+       {
+               struct video_channel *v = arg;
+
+               DBG("VIDIOCGCHAN\n");
+               if (v->channel != 0) {
+                       retval = -EINVAL;
+                       break;
+               }
+
+               v->channel = 0;
+               strcpy(v->name, "Camera");
+               v->tuners = 0;
+               v->flags = 0;
+               v->type = VIDEO_TYPE_CAMERA;
+               v->norm = 0;
+               break;
+       }
+
+       case VIDIOCSCHAN:
+       {
+               struct video_channel *v = arg;
+
+               DBG("VIDIOCSCHAN\n");
+               if (v->channel != 0)
+                       retval = -EINVAL;
+               break;
+       }
+
+       /* image properties */
+       case VIDIOCGPICT:
+       {
+               struct video_picture *pic = arg;
+               DBG("VIDIOCGPICT\n");
+               *pic = cam->vp;
+               break;
+       }
+
+       case VIDIOCSPICT:
+       {
+               struct video_picture *vp = arg;
+
+               DBG("VIDIOCSPICT\n");
+
+               /* check validity */
+               DBG("palette: %d\n", vp->palette);
+               DBG("depth: %d\n", vp->depth);
+               if (!valid_mode(vp->palette, vp->depth)) {
+                       retval = -EINVAL;
+                       break;
+               }
+
+               mutex_lock(&cam->param_lock);
+               /* brightness, colour, contrast need no check 0-65535 */
+               cam->vp = *vp;
+               /* update cam->params.colourParams */
+               cam->params.colourParams.brightness = vp->brightness*100/65535;
+               cam->params.colourParams.contrast = vp->contrast*100/65535;
+               cam->params.colourParams.saturation = vp->colour*100/65535;
+               /* 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) {
+                       /* 1-02 firmware limits contrast to 80 */
+                       cam->params.colourParams.contrast = 80;
+               }
+
+               /* Adjust flicker control if necessary */
+               if(cam->params.flickerControl.allowableOverExposure < 0)
+                       cam->params.flickerControl.allowableOverExposure =
+                               -find_over_exposure(cam->params.colourParams.brightness);
+               if(cam->params.flickerControl.flickerMode != 0)
+                       cam->cmd_queue |= COMMAND_SETFLICKERCTRL;
+
+
+               /* queue command to update camera */
+               cam->cmd_queue |= COMMAND_SETCOLOURPARAMS;
+               mutex_unlock(&cam->param_lock);
+               DBG("VIDIOCSPICT: %d / %d // %d / %d / %d / %d\n",
+                   vp->depth, vp->palette, vp->brightness, vp->hue, vp->colour,
+                   vp->contrast);
+               break;
+       }
+
+       /* get/set capture window */
+       case VIDIOCGWIN:
+       {
+               struct video_window *vw = arg;
+               DBG("VIDIOCGWIN\n");
+
+               *vw = cam->vw;
+               break;
+       }
+
+       case VIDIOCSWIN:
+       {
+               /* copy_from_user, check validity, copy to internal structure */
+               struct video_window *vw = arg;
+               DBG("VIDIOCSWIN\n");
+
+               if (vw->clipcount != 0) {    /* clipping not supported */
+                       retval = -EINVAL;
+                       break;
+               }
+               if (vw->clips != NULL) {     /* clipping not supported */
+                       retval = -EINVAL;
+                       break;
+               }
+
+               /* we set the video window to something smaller or equal to what
+               * is requested by the user???
+               */
+               mutex_lock(&cam->param_lock);
+               if (vw->width != cam->vw.width || vw->height != cam->vw.height) {
+                       int video_size = match_videosize(vw->width, vw->height);
+
+                       if (video_size < 0) {
+                               retval = -EINVAL;
+                               mutex_unlock(&cam->param_lock);
+                               break;
+                       }
+                       cam->video_size = video_size;
+
+                       /* video size is changing, reset the subcapture area */
+                       memset(&cam->vc, 0, sizeof(cam->vc));
+
+                       set_vw_size(cam);
+                       DBG("%d / %d\n", cam->vw.width, cam->vw.height);
+                       cam->cmd_queue |= COMMAND_SETFORMAT;
+               }
+
+               mutex_unlock(&cam->param_lock);
+
+               /* setformat ignored by camera during streaming,
+                * so stop/dispatch/start */
+               if (cam->cmd_queue & COMMAND_SETFORMAT) {
+                       DBG("\n");
+                       dispatch_commands(cam);
+               }
+               DBG("%d/%d:%d\n", cam->video_size,
+                   cam->vw.width, cam->vw.height);
+               break;
+       }
+
+       /* mmap interface */
+       case VIDIOCGMBUF:
+       {
+               struct video_mbuf *vm = arg;
+               int i;
+
+               DBG("VIDIOCGMBUF\n");
+               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;
+               break;
+       }
+
+       case VIDIOCMCAPTURE:
+       {
+               struct video_mmap *vm = arg;
+               int video_size;
+
+               DBG("VIDIOCMCAPTURE: %d / %d / %dx%d\n", vm->format, vm->frame,
+                   vm->width, vm->height);
+               if (vm->frame<0||vm->frame>=FRAME_NUM) {
+                       retval = -EINVAL;
+                       break;
+               }
+
+               /* set video format */
+               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:
+               case VIDEO_PALETTE_YUYV:
+               case VIDEO_PALETTE_UYVY:
+                       cam->vp.depth = 16;
+                       break;
+               case VIDEO_PALETTE_RGB24:
+                       cam->vp.depth = 24;
+                       break;
+               case VIDEO_PALETTE_RGB32:
+                       cam->vp.depth = 32;
+                       break;
+               default:
+                       retval = -EINVAL;
+                       break;
+               }
+               if (retval)
+                       break;
+
+               /* set video size */
+               video_size = match_videosize(vm->width, vm->height);
+               if (video_size < 0) {
+                       retval = -EINVAL;
+                       break;
+               }
+               if (video_size != cam->video_size) {
+                       cam->video_size = video_size;
+
+                       /* video size is changing, reset the subcapture area */
+                       memset(&cam->vc, 0, sizeof(cam->vc));
+
+                       set_vw_size(cam);
+                       cam->cmd_queue |= COMMAND_SETFORMAT;
+                       dispatch_commands(cam);
+               }
+               /* according to v4l-spec we must start streaming here */
+               cam->mmap_kludge = 1;
+               retval = capture_frame(cam, vm);
+
+               break;
+       }
+
+       case VIDIOCSYNC:
+       {
+               int *frame = arg;
+
+               //DBG("VIDIOCSYNC: %d\n", *frame);
+
+               if (*frame<0 || *frame >= FRAME_NUM) {
+                       retval = -EINVAL;
+                       break;
+               }
+
+               switch (cam->frame[*frame].state) {
+               case FRAME_UNUSED:
+               case FRAME_READY:
+               case FRAME_GRABBING:
+                       DBG("sync to unused frame %d\n", *frame);
+                       retval = -EINVAL;
+                       break;
+
+               case FRAME_DONE:
+                       cam->frame[*frame].state = FRAME_UNUSED;
+                       //DBG("VIDIOCSYNC: %d synced\n", *frame);
+                       break;
+               }
+               if (retval == -EINTR) {
+                       /* FIXME - xawtv does not handle this nice */
+                       retval = 0;
+               }
+               break;
+       }
+
+       case VIDIOCGCAPTURE:
+       {
+               struct video_capture *vc = arg;
+
+               DBG("VIDIOCGCAPTURE\n");
+
+               *vc = cam->vc;
+
+               break;
+       }
+
+       case VIDIOCSCAPTURE:
+       {
+               struct video_capture *vc = arg;
+
+               DBG("VIDIOCSCAPTURE\n");
+
+               if (vc->decimation != 0) {    /* How should this be used? */
+                       retval = -EINVAL;
+                       break;
+               }
+               if (vc->flags != 0) {     /* Even/odd grab not supported */
+                       retval = -EINVAL;
+                       break;
+               }
+
+               /* Clip to the resolution we can set for the ROI
+                  (every 8 columns and 4 rows) */
+               vc->x      = vc->x      & ~(__u32)7;
+               vc->y      = vc->y      & ~(__u32)3;
+               vc->width  = vc->width  & ~(__u32)7;
+               vc->height = vc->height & ~(__u32)3;
+
+               if(vc->width == 0 || vc->height == 0 ||
+                  vc->x + vc->width  > cam->vw.width ||
+                  vc->y + vc->height > cam->vw.height) {
+                       retval = -EINVAL;
+                       break;
+               }
+
+               DBG("%d,%d/%dx%d\n", vc->x,vc->y,vc->width, vc->height);
+
+               mutex_lock(&cam->param_lock);
+
+               cam->vc.x      = vc->x;
+               cam->vc.y      = vc->y;
+               cam->vc.width  = vc->width;
+               cam->vc.height = vc->height;
+
+               set_vw_size(cam);
+               cam->cmd_queue |= COMMAND_SETFORMAT;
+
+               mutex_unlock(&cam->param_lock);
+
+               /* setformat ignored by camera during streaming,
+                * so stop/dispatch/start */
+               dispatch_commands(cam);
+               break;
+       }
+
+       case VIDIOCGUNIT:
+       {
+               struct video_unit *vu = arg;
+
+               DBG("VIDIOCGUNIT\n");
+
+               vu->video    = cam->vdev.minor;
+               vu->vbi      = VIDEO_NO_UNIT;
+               vu->radio    = VIDEO_NO_UNIT;
+               vu->audio    = VIDEO_NO_UNIT;
+               vu->teletext = VIDEO_NO_UNIT;
+
+               break;
+       }
+
+
+       /* pointless to implement overlay with this camera */
+       case VIDIOCCAPTURE:
+       case VIDIOCGFBUF:
+       case VIDIOCSFBUF:
+       case VIDIOCKEY:
+       /* tuner interface - we have none */
+       case VIDIOCGTUNER:
+       case VIDIOCSTUNER:
+       case VIDIOCGFREQ:
+       case VIDIOCSFREQ:
+       /* audio interface - we have none */
+       case VIDIOCGAUDIO:
+       case VIDIOCSAUDIO:
+               retval = -EINVAL;
+               break;
+       default:
+               retval = -ENOIOCTLCMD;
+               break;
+       }
+
+       mutex_unlock(&cam->busy_lock);
+       return retval;
+}
+
+static long cpia_ioctl(struct file *file,
+                    unsigned int cmd, unsigned long arg)
+{
+       return video_usercopy(file, cmd, arg, cpia_do_ioctl);
+}
+
+
+/* FIXME */
+static int cpia_mmap(struct file *file, struct vm_area_struct *vma)
+{
+       struct video_device *dev = file->private_data;
+       unsigned long start = vma->vm_start;
+       unsigned long size  = vma->vm_end - vma->vm_start;
+       unsigned long page, pos;
+       struct cam_data *cam = video_get_drvdata(dev);
+       int retval;
+
+       if (!cam || !cam->ops)
+               return -ENODEV;
+
+       DBG("cpia_mmap: %ld\n", size);
+
+       if (size > FRAME_NUM*CPIA_MAX_FRAME_SIZE)
+               return -EINVAL;
+
+       /* make this _really_ smp-safe */
+       if (mutex_lock_interruptible(&cam->busy_lock))
+               return -EINTR;
+
+       if (!cam->frame_buf) {  /* we do lazy allocation */
+               if ((retval = allocate_frame_buf(cam))) {
+                       mutex_unlock(&cam->busy_lock);
+                       return retval;
+               }
+       }
+
+       pos = (unsigned long)(cam->frame_buf);
+       while (size > 0) {
+               page = vmalloc_to_pfn((void *)pos);
+               if (remap_pfn_range(vma, start, page, PAGE_SIZE, PAGE_SHARED)) {
+                       mutex_unlock(&cam->busy_lock);
+                       return -EAGAIN;
+               }
+               start += PAGE_SIZE;
+               pos += PAGE_SIZE;
+               if (size > PAGE_SIZE)
+                       size -= PAGE_SIZE;
+               else
+                       size = 0;
+       }
+
+       DBG("cpia_mmap: %ld\n", size);
+       mutex_unlock(&cam->busy_lock);
+
+       return 0;
+}
+
+static const struct v4l2_file_operations cpia_fops = {
+       .owner          = THIS_MODULE,
+       .open           = cpia_open,
+       .release        = cpia_close,
+       .read           = cpia_read,
+       .mmap           = cpia_mmap,
+       .ioctl          = cpia_ioctl,
+};
+
+static struct video_device cpia_template = {
+       .name           = "CPiA Camera",
+       .fops           = &cpia_fops,
+       .release        = video_device_release_empty,
+};
+
+/* initialise cam_data structure  */
+static void reset_camera_struct(struct cam_data *cam)
+{
+       /* The following parameter values are the defaults from
+        * "Software Developer's Guide for CPiA Cameras".  Any changes
+        * to the defaults are noted in comments. */
+       cam->params.colourParams.brightness = 50;
+       cam->params.colourParams.contrast = 48;
+       cam->params.colourParams.saturation = 50;
+       cam->params.exposure.gainMode = 4;
+       cam->params.exposure.expMode = 2;               /* AEC */
+       cam->params.exposure.compMode = 1;
+       cam->params.exposure.centreWeight = 1;
+       cam->params.exposure.gain = 0;
+       cam->params.exposure.fineExp = 0;
+       cam->params.exposure.coarseExpLo = 185;
+       cam->params.exposure.coarseExpHi = 0;
+       cam->params.exposure.redComp = COMP_RED;
+       cam->params.exposure.green1Comp = COMP_GREEN1;
+       cam->params.exposure.green2Comp = COMP_GREEN2;
+       cam->params.exposure.blueComp = COMP_BLUE;
+       cam->params.colourBalance.balanceMode = 2;      /* ACB */
+       cam->params.colourBalance.redGain = 32;
+       cam->params.colourBalance.greenGain = 6;
+       cam->params.colourBalance.blueGain = 92;
+       cam->params.apcor.gain1 = 0x18;
+       cam->params.apcor.gain2 = 0x16;
+       cam->params.apcor.gain4 = 0x24;
+       cam->params.apcor.gain8 = 0x34;
+       cam->params.flickerControl.flickerMode = 0;
+       cam->params.flickerControl.disabled = 1;
+
+       cam->params.flickerControl.coarseJump =
+               flicker_jumps[cam->mainsFreq]
+                            [cam->params.sensorFps.baserate]
+                            [cam->params.sensorFps.divisor];
+       cam->params.flickerControl.allowableOverExposure =
+               -find_over_exposure(cam->params.colourParams.brightness);
+       cam->params.vlOffset.gain1 = 20;
+       cam->params.vlOffset.gain2 = 24;
+       cam->params.vlOffset.gain4 = 26;
+       cam->params.vlOffset.gain8 = 26;
+       cam->params.compressionParams.hysteresis = 3;
+       cam->params.compressionParams.threshMax = 11;
+       cam->params.compressionParams.smallStep = 1;
+       cam->params.compressionParams.largeStep = 3;
+       cam->params.compressionParams.decimationHysteresis = 2;
+       cam->params.compressionParams.frDiffStepThresh = 5;
+       cam->params.compressionParams.qDiffStepThresh = 3;
+       cam->params.compressionParams.decimationThreshMod = 2;
+       /* End of default values from Software Developer's Guide */
+
+       cam->transfer_rate = 0;
+       cam->exposure_status = EXPOSURE_NORMAL;
+
+       /* 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 = 6; /* From windows driver */
+       cam->params.yuvThreshold.uvThreshold = 6; /* From windows driver */
+
+       cam->params.format.subSample = SUBSAMPLE_422;
+       cam->params.format.yuvOrder = YUVORDER_YUYV;
+
+       cam->params.compression.mode = CPIA_COMPRESSION_AUTO;
+       cam->params.compressionTarget.frTargeting =
+               CPIA_COMPRESSION_TARGET_QUALITY;
+       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;
+
+       cam->vp.colour = 32768;      /* 50% */
+       cam->vp.hue = 32768;         /* 50% */
+       cam->vp.brightness = 32768;  /* 50% */
+       cam->vp.contrast = 32768;    /* 50% */
+       cam->vp.whiteness = 0;       /* not used -> grayscale only */
+       cam->vp.depth = 24;          /* to be set by user */
+       cam->vp.palette = VIDEO_PALETTE_RGB24; /* to be set by user */
+
+       cam->vc.x = 0;
+       cam->vc.y = 0;
+       cam->vc.width = 0;
+       cam->vc.height = 0;
+
+       cam->vw.x = 0;
+       cam->vw.y = 0;
+       set_vw_size(cam);
+       cam->vw.chromakey = 0;
+       cam->vw.flags = 0;
+       cam->vw.clipcount = 0;
+       cam->vw.clips = NULL;
+
+       cam->cmd_queue = COMMAND_NONE;
+       cam->first_frame = 1;
+
+       return;
+}
+
+/* initialize cam_data structure  */
+static void init_camera_struct(struct cam_data *cam,
+                              struct cpia_camera_ops *ops )
+{
+       int i;
+
+       /* Default everything to 0 */
+       memset(cam, 0, sizeof(struct cam_data));
+
+       cam->ops = ops;
+       mutex_init(&cam->param_lock);
+       mutex_init(&cam->busy_lock);
+
+       reset_camera_struct(cam);
+
+       cam->proc_entry = NULL;
+
+       memcpy(&cam->vdev, &cpia_template, sizeof(cpia_template));
+       video_set_drvdata(&cam->vdev, cam);
+
+       cam->curframe = 0;
+       for (i = 0; i < FRAME_NUM; i++) {
+               cam->frame[i].width = 0;
+               cam->frame[i].height = 0;
+               cam->frame[i].state = FRAME_UNUSED;
+               cam->frame[i].data = NULL;
+       }
+       cam->decompressed_frame.width = 0;
+       cam->decompressed_frame.height = 0;
+       cam->decompressed_frame.state = FRAME_UNUSED;
+       cam->decompressed_frame.data = NULL;
+}
+
+struct cam_data *cpia_register_camera(struct cpia_camera_ops *ops, void *lowlevel)
+{
+       struct cam_data *camera;
+
+       if ((camera = kmalloc(sizeof(struct cam_data), GFP_KERNEL)) == NULL)
+               return NULL;
+
+
+       init_camera_struct( camera, ops );
+       camera->lowlevel_data = lowlevel;
+
+       /* register v4l device */
+       if (video_register_device(&camera->vdev, VFL_TYPE_GRABBER, video_nr) < 0) {
+               kfree(camera);
+               printk(KERN_DEBUG "video_register_device failed\n");
+               return NULL;
+       }
+
+       /* get version information from camera: open/reset/close */
+
+       /* open cpia */
+       if (camera->ops->open(camera->lowlevel_data))
+               return camera;
+
+       /* reset the camera */
+       if (reset_camera(camera) != 0) {
+               camera->ops->close(camera->lowlevel_data);
+               return camera;
+       }
+
+       /* close cpia */
+       camera->ops->close(camera->lowlevel_data);
+
+#ifdef CONFIG_PROC_FS
+       create_proc_cpia_cam(camera);
+#endif
+
+       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(struct cam_data *cam)
+{
+       DBG("unregistering video\n");
+       video_unregister_device(&cam->vdev);
+       if (cam->open_count) {
+               put_cam(cam->ops);
+               DBG("camera open -- setting ops to NULL\n");
+               cam->ops = NULL;
+       }
+
+#ifdef CONFIG_PROC_FS
+       DBG("destroying /proc/cpia/%s\n", video_device_node_name(&cam->vdev));
+       destroy_proc_cpia_cam(cam);
+#endif
+       if (!cam->open_count) {
+               DBG("freeing camera\n");
+               kfree(cam);
+       }
+}
+
+static int __init cpia_init(void)
+{
+       printk(KERN_INFO "%s v%d.%d.%d\n", ABOUT,
+              CPIA_MAJ_VER, CPIA_MIN_VER, CPIA_PATCH_VER);
+
+       printk(KERN_WARNING "Since in-kernel colorspace conversion is not "
+              "allowed, it is disabled by default now. Users should fix the "
+              "applications in case they don't work without conversion "
+              "reenabled by setting the 'colorspace_conv' module "
+              "parameter to 1\n");
+
+#ifdef CONFIG_PROC_FS
+       proc_cpia_create();
+#endif
+
+       return 0;
+}
+
+static void __exit cpia_exit(void)
+{
+#ifdef CONFIG_PROC_FS
+       proc_cpia_destroy();
+#endif
+}
+
+module_init(cpia_init);
+module_exit(cpia_exit);
+
+/* Exported symbols for modules. */
+
+EXPORT_SYMBOL(cpia_register_camera);
+EXPORT_SYMBOL(cpia_unregister_camera);
diff --git a/drivers/staging/cpia/cpia.h b/drivers/staging/cpia/cpia.h
new file mode 100644 (file)
index 0000000..8f0cfee
--- /dev/null
@@ -0,0 +1,432 @@
+#ifndef cpia_h
+#define cpia_h
+
+/*
+ * CPiA Parallel Port Video4Linux driver
+ *
+ * Supports CPiA based parallel port Video Camera's.
+ *
+ * (C) Copyright 1999 Bas Huisman,
+ *                    Peter Pregler,
+ *                    Scott J. Bertin,
+ *                    VLSI Vision Ltd.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#define CPIA_MAJ_VER   1
+#define CPIA_MIN_VER   2
+#define CPIA_PATCH_VER 3
+
+#define CPIA_PP_MAJ_VER       CPIA_MAJ_VER
+#define CPIA_PP_MIN_VER       CPIA_MIN_VER
+#define CPIA_PP_PATCH_VER     CPIA_PATCH_VER
+
+#define CPIA_USB_MAJ_VER      CPIA_MAJ_VER
+#define CPIA_USB_MIN_VER      CPIA_MIN_VER
+#define CPIA_USB_PATCH_VER    CPIA_PATCH_VER
+
+#define CPIA_MAX_FRAME_SIZE_UNALIGNED  (352 * 288 * 4)   /* CIF at RGB32 */
+#define CPIA_MAX_FRAME_SIZE    ((CPIA_MAX_FRAME_SIZE_UNALIGNED + PAGE_SIZE - 1) & ~(PAGE_SIZE - 1)) /* align above to PAGE_SIZE */
+
+#ifdef __KERNEL__
+
+#include <asm/uaccess.h>
+#include <linux/videodev.h>
+#include <media/v4l2-common.h>
+#include <media/v4l2-ioctl.h>
+#include <linux/list.h>
+#include <linux/mutex.h>
+
+struct cpia_camera_ops
+{
+       /* open sets privdata to point to structure for this camera.
+        * Returns negative value on error, otherwise 0.
+        */
+       int (*open)(void *privdata);
+
+       /* Registers callback function cb to be called with cbdata
+        * when an image is ready.  If cb is NULL, only single image grabs
+        * should be used.  cb should immediately call streamRead to read
+        * the data or data may be lost. Returns negative value on error,
+        * otherwise 0.
+        */
+       int (*registerCallback)(void *privdata, void (*cb)(void *cbdata),
+                               void *cbdata);
+
+       /* transferCmd sends commands to the camera.  command MUST point to
+        * an  8 byte buffer in kernel space. data can be NULL if no extra
+        * data is needed.  The size of the data is given by the last 2
+        * bytes of command.  data must also point to memory in kernel space.
+        * Returns negative value on error, otherwise 0.
+        */
+       int (*transferCmd)(void *privdata, u8 *command, u8 *data);
+
+       /* streamStart initiates stream capture mode.
+        * Returns negative value on error, otherwise 0.
+        */
+       int (*streamStart)(void *privdata);
+
+       /* streamStop terminates stream capture mode.
+        * Returns negative value on error, otherwise 0.
+        */
+       int (*streamStop)(void *privdata);
+
+       /* streamRead reads a frame from the camera.  buffer points to a
+        * buffer large enough to hold a complete frame in kernel space.
+        * noblock indicates if this should be a non blocking read.
+        * Returns the number of bytes read, or negative value on error.
+        */
+       int (*streamRead)(void *privdata, u8 *buffer, int noblock);
+
+       /* close disables the device until open() is called again.
+        * Returns negative value on error, otherwise 0.
+        */
+       int (*close)(void *privdata);
+
+       /* If wait_for_stream_ready is non-zero, wait until the streamState
+        * is STREAM_READY before calling streamRead.
+        */
+       int wait_for_stream_ready;
+
+       /*
+        * Used to maintain lowlevel module usage counts
+        */
+       struct module *owner;
+};
+
+struct cpia_frame {
+       u8 *data;
+       int count;
+       int width;
+       int height;
+       volatile int state;
+};
+
+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 disabled;
+               u8 flickerMode;
+               u8 coarseJump;
+               int 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 {                        /* Intel QX3 specific data */
+               u8 qx3_detected;        /* a QX3 is present */
+               u8 toplight;            /* top light lit , R/W */
+               u8 bottomlight;         /* bottom light lit, R/W */
+               u8 button;              /* snapshot button pressed (R/O) */
+               u8 cradled;             /* microscope is in cradle (R/O) */
+       } qx3;
+       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;
+};
+
+enum v4l_camstates {
+       CPIA_V4L_IDLE = 0,
+       CPIA_V4L_ERROR,
+       CPIA_V4L_COMMAND,
+       CPIA_V4L_GRABBING,
+       CPIA_V4L_STREAMING,
+       CPIA_V4L_STREAMING_PAUSED,
+};
+
+#define FRAME_NUM      2       /* double buffering for now */
+
+struct cam_data {
+       struct list_head cam_data_list;
+
+       struct mutex 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 mutex 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 */
+       struct video_capture vc;        /* v4l subcapture 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;
+       int mmap_kludge;                /* 'wrong' byte order for mmap */
+       volatile u32 cmd_queue;         /* queued commands */
+       int exposure_status;            /* EXPOSURE_* */
+       int exposure_count;             /* number of frames at this status */
+};
+
+/* cpia_register_camera is called by low level driver for each camera.
+ * A unique camera number is returned, or a negative value on error */
+struct cam_data *cpia_register_camera(struct cpia_camera_ops *ops, void *lowlevel);
+
+/* cpia_unregister_camera is called by low level driver when a camera
+ * is removed.  This must not fail. */
+void cpia_unregister_camera(struct cam_data *cam);
+
+/* raw CIF + 64 byte header + (2 bytes line_length + EOL) per line + 4*EOI +
+ * one byte 16bit DMA alignment
+ */
+#define CPIA_MAX_IMAGE_SIZE ((352*288*2)+64+(288*3)+5)
+
+/* constant value's */
+#define MAGIC_0                0x19
+#define MAGIC_1                0x68
+#define DATA_IN                0xC0
+#define DATA_OUT       0x40
+#define VIDEOSIZE_QCIF 0       /* 176x144 */
+#define VIDEOSIZE_CIF  1       /* 352x288 */
+#define VIDEOSIZE_SIF  2       /* 320x240 */
+#define VIDEOSIZE_QSIF 3       /* 160x120 */
+#define VIDEOSIZE_48_48                4 /* where no one has gone before, iconsize! */
+#define VIDEOSIZE_64_48                5
+#define VIDEOSIZE_128_96       6
+#define VIDEOSIZE_160_120      VIDEOSIZE_QSIF
+#define VIDEOSIZE_176_144      VIDEOSIZE_QCIF
+#define VIDEOSIZE_192_144      7
+#define VIDEOSIZE_224_168      8
+#define VIDEOSIZE_256_192      9
+#define VIDEOSIZE_288_216      10
+#define VIDEOSIZE_320_240      VIDEOSIZE_SIF
+#define VIDEOSIZE_352_288      VIDEOSIZE_CIF
+#define VIDEOSIZE_88_72                11 /* quarter CIF */
+#define SUBSAMPLE_420  0
+#define SUBSAMPLE_422  1
+#define YUVORDER_YUYV  0
+#define YUVORDER_UYVY  1
+#define NOT_COMPRESSED 0
+#define COMPRESSED     1
+#define NO_DECIMATION  0
+#define DECIMATION_ENAB        1
+#define EOI            0xff    /* End Of Image */
+#define EOL            0xfd    /* End Of Line */
+#define FRAME_HEADER_SIZE      64
+
+/* Image grab modes */
+#define CPIA_GRAB_SINGLE       0
+#define CPIA_GRAB_CONTINUOUS   1
+
+/* Compression parameters */
+#define CPIA_COMPRESSION_NONE  0
+#define CPIA_COMPRESSION_AUTO  1
+#define CPIA_COMPRESSION_MANUAL        2
+#define CPIA_COMPRESSION_TARGET_QUALITY         0
+#define CPIA_COMPRESSION_TARGET_FRAMERATE       1
+
+/* Return offsets for GetCameraState */
+#define SYSTEMSTATE    0
+#define GRABSTATE      1
+#define STREAMSTATE    2
+#define FATALERROR     3
+#define CMDERROR       4
+#define DEBUGFLAGS     5
+#define VPSTATUS       6
+#define ERRORCODE      7
+
+/* SystemState */
+#define UNINITIALISED_STATE    0
+#define PASS_THROUGH_STATE     1
+#define LO_POWER_STATE         2
+#define HI_POWER_STATE         3
+#define WARM_BOOT_STATE                4
+
+/* GrabState */
+#define GRAB_IDLE              0
+#define GRAB_ACTIVE            1
+#define GRAB_DONE              2
+
+/* StreamState */
+#define STREAM_NOT_READY       0
+#define STREAM_READY           1
+#define STREAM_OPEN            2
+#define STREAM_PAUSED          3
+#define STREAM_FINISHED                4
+
+/* Fatal Error, CmdError, and DebugFlags */
+#define CPIA_FLAG        1
+#define SYSTEM_FLAG      2
+#define INT_CTRL_FLAG    4
+#define PROCESS_FLAG     8
+#define COM_FLAG        16
+#define VP_CTRL_FLAG    32
+#define CAPTURE_FLAG    64
+#define DEBUG_FLAG     128
+
+/* VPStatus */
+#define VP_STATE_OK                    0x00
+
+#define VP_STATE_FAILED_VIDEOINIT      0x01
+#define VP_STATE_FAILED_AECACBINIT     0x02
+#define VP_STATE_AEC_MAX               0x04
+#define VP_STATE_ACB_BMAX              0x08
+
+#define VP_STATE_ACB_RMIN              0x10
+#define VP_STATE_ACB_GMIN              0x20
+#define VP_STATE_ACB_RMAX              0x40
+#define VP_STATE_ACB_GMAX              0x80
+
+/* default (minimum) compensation values */
+#define COMP_RED        220
+#define COMP_GREEN1     214
+#define COMP_GREEN2     COMP_GREEN1
+#define COMP_BLUE       230
+
+/* exposure status */
+#define EXPOSURE_VERY_LIGHT 0
+#define EXPOSURE_LIGHT      1
+#define EXPOSURE_NORMAL     2
+#define EXPOSURE_DARK       3
+#define EXPOSURE_VERY_DARK  4
+
+/* ErrorCode */
+#define ERROR_FLICKER_BELOW_MIN_EXP     0x01 /*flicker exposure got below minimum exposure */
+#define ALOG(fmt,args...) printk(fmt, ##args)
+#define LOG(fmt,args...) ALOG(KERN_INFO __FILE__ ":%s(%d):" fmt, __func__ , __LINE__ , ##args)
+
+#ifdef _CPIA_DEBUG_
+#define ADBG(fmt,args...) printk(fmt, jiffies, ##args)
+#define DBG(fmt,args...) ADBG(KERN_DEBUG __FILE__" (%ld):%s(%d):" fmt, __func__, __LINE__ , ##args)
+#else
+#define DBG(fmn,args...) do {} while(0)
+#endif
+
+#define DEB_BYTE(p)\
+  DBG("%1d %1d %1d %1d %1d %1d %1d %1d \n",\
+      (p)&0x80?1:0, (p)&0x40?1:0, (p)&0x20?1:0, (p)&0x10?1:0,\
+       (p)&0x08?1:0, (p)&0x04?1:0, (p)&0x02?1:0, (p)&0x01?1:0);
+
+#endif /* __KERNEL__ */
+
+#endif /* cpia_h */
diff --git a/drivers/staging/cpia/cpia_pp.c b/drivers/staging/cpia/cpia_pp.c
new file mode 100644 (file)
index 0000000..f5604c1
--- /dev/null
@@ -0,0 +1,869 @@
+/*
+ * cpia_pp CPiA Parallel Port driver
+ *
+ * Supports CPiA based parallel port Video Camera's.
+ *
+ * (C) Copyright 1999 Bas Huisman <bhuism@cs.utwente.nl>
+ * (C) Copyright 1999-2000 Scott J. Bertin <sbertin@securenym.net>,
+ * (C) Copyright 1999-2000 Peter Pregler <Peter_Pregler@email.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
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+/* define _CPIA_DEBUG_ for verbose debug output (see cpia.h) */
+/* #define _CPIA_DEBUG_  1 */
+
+
+#include <linux/module.h>
+#include <linux/init.h>
+
+#include <linux/kernel.h>
+#include <linux/parport.h>
+#include <linux/interrupt.h>
+#include <linux/delay.h>
+#include <linux/workqueue.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+
+#include <linux/kmod.h>
+
+/* #define _CPIA_DEBUG_                define for verbose debug output */
+#include "cpia.h"
+
+static int cpia_pp_open(void *privdata);
+static int cpia_pp_registerCallback(void *privdata, void (*cb) (void *cbdata),
+                                   void *cbdata);
+static int cpia_pp_transferCmd(void *privdata, u8 *command, u8 *data);
+static int cpia_pp_streamStart(void *privdata);
+static int cpia_pp_streamStop(void *privdata);
+static int cpia_pp_streamRead(void *privdata, u8 *buffer, int noblock);
+static int cpia_pp_close(void *privdata);
+
+
+#define ABOUT "Parallel port driver for Vision CPiA based cameras"
+
+#define PACKET_LENGTH  8
+
+/* Magic numbers for defining port-device mappings */
+#define PPCPIA_PARPORT_UNSPEC -4
+#define PPCPIA_PARPORT_AUTO -3
+#define PPCPIA_PARPORT_OFF -2
+#define PPCPIA_PARPORT_NONE -1
+
+static int parport_nr[PARPORT_MAX] = {[0 ... PARPORT_MAX - 1] = PPCPIA_PARPORT_UNSPEC};
+static char *parport[PARPORT_MAX] = {NULL,};
+
+MODULE_AUTHOR("B. Huisman <bhuism@cs.utwente.nl> & Peter Pregler <Peter_Pregler@email.com>");
+MODULE_DESCRIPTION("Parallel port driver for Vision CPiA based cameras");
+MODULE_LICENSE("GPL");
+
+module_param_array(parport, charp, NULL, 0);
+MODULE_PARM_DESC(parport, "'auto' or a list of parallel port numbers. Just like lp.");
+
+struct pp_cam_entry {
+       struct pardevice *pdev;
+       struct parport *port;
+       struct work_struct cb_task;
+       void (*cb_func)(void *cbdata);
+       void *cb_data;
+       int open_count;
+       wait_queue_head_t wq_stream;
+       /* image state flags */
+       int image_ready;        /* we got an interrupt */
+       int image_complete;     /* we have seen 4 EOI */
+
+       int streaming; /* we are in streaming mode */
+       int stream_irq;
+};
+
+static struct cpia_camera_ops cpia_pp_ops =
+{
+       cpia_pp_open,
+       cpia_pp_registerCallback,
+       cpia_pp_transferCmd,
+       cpia_pp_streamStart,
+       cpia_pp_streamStop,
+       cpia_pp_streamRead,
+       cpia_pp_close,
+       1,
+       THIS_MODULE
+};
+
+static LIST_HEAD(cam_list);
+static spinlock_t cam_list_lock_pp;
+
+/* FIXME */
+static void cpia_parport_enable_irq( struct parport *port ) {
+       parport_enable_irq(port);
+       mdelay(10);
+       return;
+}
+
+static void cpia_parport_disable_irq( struct parport *port ) {
+       parport_disable_irq(port);
+       mdelay(10);
+       return;
+}
+
+/* Special CPiA PPC modes: These are invoked by using the 1284 Extensibility
+ * Link Flag during negotiation */
+#define UPLOAD_FLAG  0x08
+#define NIBBLE_TRANSFER 0x01
+#define ECP_TRANSFER 0x03
+
+#define PARPORT_CHUNK_SIZE     PAGE_SIZE
+
+
+static void cpia_pp_run_callback(struct work_struct *work)
+{
+       void (*cb_func)(void *cbdata);
+       void *cb_data;
+       struct pp_cam_entry *cam;
+
+       cam = container_of(work, struct pp_cam_entry, cb_task);
+       cb_func = cam->cb_func;
+       cb_data = cam->cb_data;
+
+       cb_func(cb_data);
+}
+
+/****************************************************************************
+ *
+ *  CPiA-specific  low-level parport functions for nibble uploads
+ *
+ ***************************************************************************/
+/*  CPiA nonstandard "Nibble" mode (no nDataAvail signal after each byte). */
+/* The standard kernel parport_ieee1284_read_nibble() fails with the CPiA... */
+
+static size_t cpia_read_nibble (struct parport *port,
+                        void *buffer, size_t len,
+                        int flags)
+{
+       /* adapted verbatim, with one change, from
+          parport_ieee1284_read_nibble() in drivers/parport/ieee1284-ops.c */
+
+       unsigned char *buf = buffer;
+       int i;
+       unsigned char byte = 0;
+
+       len *= 2; /* in nibbles */
+       for (i=0; i < len; i++) {
+               unsigned char nibble;
+
+               /* The CPiA firmware suppresses the use of nDataAvail (nFault LO)
+                * after every second nibble to signal that more
+                * data is available.  (the total number of Bytes that
+                * should be sent is known; if too few are received, an error
+                * will be recorded after a timeout).
+                * This is incompatible with parport_ieee1284_read_nibble(),
+                * which expects to find nFault LO after every second nibble.
+                */
+
+               /* Solution: modify cpia_read_nibble to only check for
+                * nDataAvail before the first nibble is sent.
+                */
+
+               /* Does the error line indicate end of data? */
+               if (((i /*& 1*/) == 0) &&
+                   (parport_read_status(port) & PARPORT_STATUS_ERROR)) {
+                       DBG("%s: No more nibble data (%d bytes)\n",
+                           port->name, i/2);
+                       goto end_of_data;
+               }
+
+               /* Event 7: Set nAutoFd low. */
+               parport_frob_control (port,
+                                     PARPORT_CONTROL_AUTOFD,
+                                     PARPORT_CONTROL_AUTOFD);
+
+               /* Event 9: nAck goes low. */
+               port->ieee1284.phase = IEEE1284_PH_REV_DATA;
+               if (parport_wait_peripheral (port,
+                                            PARPORT_STATUS_ACK, 0)) {
+                       /* Timeout -- no more data? */
+                                DBG("%s: Nibble timeout at event 9 (%d bytes)\n",
+                                port->name, i/2);
+                       parport_frob_control (port, PARPORT_CONTROL_AUTOFD, 0);
+                       break;
+               }
+
+
+               /* Read a nibble. */
+               nibble = parport_read_status (port) >> 3;
+               nibble &= ~8;
+               if ((nibble & 0x10) == 0)
+                       nibble |= 8;
+               nibble &= 0xf;
+
+               /* Event 10: Set nAutoFd high. */
+               parport_frob_control (port, PARPORT_CONTROL_AUTOFD, 0);
+
+               /* Event 11: nAck goes high. */
+               if (parport_wait_peripheral (port,
+                                            PARPORT_STATUS_ACK,
+                                            PARPORT_STATUS_ACK)) {
+                       /* Timeout -- no more data? */
+                       DBG("%s: Nibble timeout at event 11\n",
+                                port->name);
+                       break;
+               }
+
+               if (i & 1) {
+                       /* Second nibble */
+                       byte |= nibble << 4;
+                       *buf++ = byte;
+               } else
+                       byte = nibble;
+       }
+
+       if (i == len) {
+               /* Read the last nibble without checking data avail. */
+               if (parport_read_status (port) & PARPORT_STATUS_ERROR) {
+               end_of_data:
+                       /* Go to reverse idle phase. */
+                       parport_frob_control (port,
+                                             PARPORT_CONTROL_AUTOFD,
+                                             PARPORT_CONTROL_AUTOFD);
+                       port->physport->ieee1284.phase = IEEE1284_PH_REV_IDLE;
+               }
+               else
+                       port->physport->ieee1284.phase = IEEE1284_PH_HBUSY_DAVAIL;
+       }
+
+       return i/2;
+}
+
+/* CPiA nonstandard "Nibble Stream" mode (2 nibbles per cycle, instead of 1)
+ * (See CPiA Data sheet p. 31)
+ *
+ * "Nibble Stream" mode used by CPiA for uploads to non-ECP ports is a
+ * nonstandard variant of nibble mode which allows the same (mediocre)
+ * data flow of 8 bits per cycle as software-enabled ECP by TRISTATE-capable
+ * parallel ports, but works also for  non-TRISTATE-capable ports.
+ * (Standard nibble mode only send 4 bits per cycle)
+ *
+ */
+
+static size_t cpia_read_nibble_stream(struct parport *port,
+                              void *buffer, size_t len,
+                              int flags)
+{
+       int i;
+       unsigned char *buf = buffer;
+       int endseen = 0;
+
+       for (i=0; i < len; i++) {
+               unsigned char nibble[2], byte = 0;
+               int j;
+
+               /* Image Data is complete when 4 consecutive EOI bytes (0xff) are seen */
+               if (endseen > 3 )
+                       break;
+
+               /* Event 7: Set nAutoFd low. */
+               parport_frob_control (port,
+                                     PARPORT_CONTROL_AUTOFD,
+                                     PARPORT_CONTROL_AUTOFD);
+
+               /* Event 9: nAck goes low. */
+               port->ieee1284.phase = IEEE1284_PH_REV_DATA;
+               if (parport_wait_peripheral (port,
+                                            PARPORT_STATUS_ACK, 0)) {
+                       /* Timeout -- no more data? */
+                                DBG("%s: Nibble timeout at event 9 (%d bytes)\n",
+                                port->name, i/2);
+                       parport_frob_control (port, PARPORT_CONTROL_AUTOFD, 0);
+                       break;
+               }
+
+               /* Read lower nibble */
+               nibble[0] = parport_read_status (port) >>3;
+
+               /* Event 10: Set nAutoFd high. */
+               parport_frob_control (port, PARPORT_CONTROL_AUTOFD, 0);
+
+               /* Event 11: nAck goes high. */
+               if (parport_wait_peripheral (port,
+                                            PARPORT_STATUS_ACK,
+                                            PARPORT_STATUS_ACK)) {
+                       /* Timeout -- no more data? */
+                       DBG("%s: Nibble timeout at event 11\n",
+                                port->name);
+                       break;
+               }
+
+               /* Read upper nibble */
+               nibble[1] = parport_read_status (port) >>3;
+
+               /* reassemble the byte */
+               for (j = 0; j < 2 ; j++ ) {
+                       nibble[j] &= ~8;
+                       if ((nibble[j] & 0x10) == 0)
+                               nibble[j] |= 8;
+                       nibble[j] &= 0xf;
+               }
+               byte = (nibble[0] |(nibble[1] << 4));
+               *buf++ = byte;
+
+               if(byte == EOI)
+                 endseen++;
+               else
+                 endseen = 0;
+       }
+       return i;
+}
+
+/****************************************************************************
+ *
+ *  EndTransferMode
+ *
+ ***************************************************************************/
+static void EndTransferMode(struct pp_cam_entry *cam)
+{
+       parport_negotiate(cam->port, IEEE1284_MODE_COMPAT);
+}
+
+/****************************************************************************
+ *
+ *  ForwardSetup
+ *
+ ***************************************************************************/
+static int ForwardSetup(struct pp_cam_entry *cam)
+{
+       int retry;
+
+       /* The CPiA uses ECP protocol for Downloads from the Host to the camera.
+        * This will be software-emulated if ECP hardware is not present
+        */
+
+       /* the usual camera maximum response time is 10ms, but after receiving
+        * some commands, it needs up to 40ms. (Data Sheet p. 32)*/
+
+       for(retry = 0; retry < 4; ++retry) {
+               if(!parport_negotiate(cam->port, IEEE1284_MODE_ECP)) {
+                       break;
+               }
+               mdelay(10);
+       }
+       if(retry == 4) {
+               DBG("Unable to negotiate IEEE1284 ECP Download mode\n");
+               return -1;
+       }
+       return 0;
+}
+/****************************************************************************
+ *
+ *  ReverseSetup
+ *
+ ***************************************************************************/
+static int ReverseSetup(struct pp_cam_entry *cam, int extensibility)
+{
+       int retry;
+       int upload_mode, mode = IEEE1284_MODE_ECP;
+       int transfer_mode = ECP_TRANSFER;
+
+       if (!(cam->port->modes & PARPORT_MODE_ECP) &&
+            !(cam->port->modes & PARPORT_MODE_TRISTATE)) {
+               mode = IEEE1284_MODE_NIBBLE;
+               transfer_mode = NIBBLE_TRANSFER;
+       }
+
+       upload_mode = mode;
+       if(extensibility) mode = UPLOAD_FLAG|transfer_mode|IEEE1284_EXT_LINK;
+
+       /* the usual camera maximum response time is 10ms, but after
+        * receiving some commands, it needs up to 40ms. */
+
+       for(retry = 0; retry < 4; ++retry) {
+               if(!parport_negotiate(cam->port, mode)) {
+                       break;
+               }
+               mdelay(10);
+       }
+       if(retry == 4) {
+               if(extensibility)
+                       DBG("Unable to negotiate upload extensibility mode\n");
+               else
+                       DBG("Unable to negotiate upload mode\n");
+               return -1;
+       }
+       if(extensibility) cam->port->ieee1284.mode = upload_mode;
+       return 0;
+}
+
+/****************************************************************************
+ *
+ *  WritePacket
+ *
+ ***************************************************************************/
+static int WritePacket(struct pp_cam_entry *cam, const u8 *packet, size_t size)
+{
+       int retval=0;
+       int size_written;
+
+       if (packet == NULL) {
+               return -EINVAL;
+       }
+       if (ForwardSetup(cam)) {
+               DBG("Write failed in setup\n");
+               return -EIO;
+       }
+       size_written = parport_write(cam->port, packet, size);
+       if(size_written != size) {
+               DBG("Write failed, wrote %d/%d\n", size_written, size);
+               retval = -EIO;
+       }
+       EndTransferMode(cam);
+       return retval;
+}
+
+/****************************************************************************
+ *
+ *  ReadPacket
+ *
+ ***************************************************************************/
+static int ReadPacket(struct pp_cam_entry *cam, u8 *packet, size_t size)
+{
+       int retval=0;
+
+       if (packet == NULL) {
+               return -EINVAL;
+       }
+       if (ReverseSetup(cam, 0)) {
+               return -EIO;
+       }
+
+       /* support for CPiA variant nibble reads */
+       if(cam->port->ieee1284.mode == IEEE1284_MODE_NIBBLE) {
+               if(cpia_read_nibble(cam->port, packet, size, 0) != size)
+                       retval = -EIO;
+       } else {
+               if(parport_read(cam->port, packet, size) != size)
+                       retval = -EIO;
+       }
+       EndTransferMode(cam);
+       return retval;
+}
+
+/****************************************************************************
+ *
+ *  cpia_pp_streamStart
+ *
+ ***************************************************************************/
+static int cpia_pp_streamStart(void *privdata)
+{
+       struct pp_cam_entry *cam = privdata;
+       DBG("\n");
+       cam->streaming=1;
+       cam->image_ready=0;
+       //if (ReverseSetup(cam,1)) return -EIO;
+       if(cam->stream_irq) cpia_parport_enable_irq(cam->port);
+       return 0;
+}
+
+/****************************************************************************
+ *
+ *  cpia_pp_streamStop
+ *
+ ***************************************************************************/
+static int cpia_pp_streamStop(void *privdata)
+{
+       struct pp_cam_entry *cam = privdata;
+
+       DBG("\n");
+       cam->streaming=0;
+       cpia_parport_disable_irq(cam->port);
+       //EndTransferMode(cam);
+
+       return 0;
+}
+
+/****************************************************************************
+ *
+ *  cpia_pp_streamRead
+ *
+ ***************************************************************************/
+static int cpia_pp_read(struct parport *port, u8 *buffer, int len)
+{
+       int bytes_read;
+
+       /* support for CPiA variant "nibble stream" reads */
+       if(port->ieee1284.mode == IEEE1284_MODE_NIBBLE)
+               bytes_read = cpia_read_nibble_stream(port,buffer,len,0);
+       else {
+               int new_bytes;
+               for(bytes_read=0; bytes_read<len; bytes_read += new_bytes) {
+                       new_bytes = parport_read(port, buffer+bytes_read,
+                                                len-bytes_read);
+                       if(new_bytes < 0) break;
+               }
+       }
+       return bytes_read;
+}
+
+static int cpia_pp_streamRead(void *privdata, u8 *buffer, int noblock)
+{
+       struct pp_cam_entry *cam = privdata;
+       int read_bytes = 0;
+       int i, endseen, block_size, new_bytes;
+
+       if(cam == NULL) {
+               DBG("Internal driver error: cam is NULL\n");
+               return -EINVAL;
+       }
+       if(buffer == NULL) {
+               DBG("Internal driver error: buffer is NULL\n");
+               return -EINVAL;
+       }
+       //if(cam->streaming) DBG("%d / %d\n", cam->image_ready, noblock);
+       if( cam->stream_irq ) {
+               DBG("%d\n", cam->image_ready);
+               cam->image_ready--;
+       }
+       cam->image_complete=0;
+       if (0/*cam->streaming*/) {
+               if(!cam->image_ready) {
+                       if(noblock) return -EWOULDBLOCK;
+                       interruptible_sleep_on(&cam->wq_stream);
+                       if( signal_pending(current) ) return -EINTR;
+                       DBG("%d\n", cam->image_ready);
+               }
+       } else {
+               if (ReverseSetup(cam, 1)) {
+                       DBG("unable to ReverseSetup\n");
+                       return -EIO;
+               }
+       }
+       endseen = 0;
+       block_size = PARPORT_CHUNK_SIZE;
+       while( !cam->image_complete ) {
+               cond_resched();
+
+               new_bytes = cpia_pp_read(cam->port, buffer, block_size );
+               if( new_bytes <= 0 ) {
+                       break;
+               }
+               i=-1;
+               while(++i<new_bytes && endseen<4) {
+                       if(*buffer==EOI) {
+                               endseen++;
+                       } else {
+                               endseen=0;
+                       }
+                       buffer++;
+               }
+               read_bytes += i;
+               if( endseen==4 ) {
+                       cam->image_complete=1;
+                       break;
+               }
+               if( CPIA_MAX_IMAGE_SIZE-read_bytes <= PARPORT_CHUNK_SIZE ) {
+                       block_size=CPIA_MAX_IMAGE_SIZE-read_bytes;
+               }
+       }
+       EndTransferMode(cam);
+       return cam->image_complete ? read_bytes : -EIO;
+}
+/****************************************************************************
+ *
+ *  cpia_pp_transferCmd
+ *
+ ***************************************************************************/
+static int cpia_pp_transferCmd(void *privdata, u8 *command, u8 *data)
+{
+       int err;
+       int retval=0;
+       int databytes;
+       struct pp_cam_entry *cam = privdata;
+
+       if(cam == NULL) {
+               DBG("Internal driver error: cam is NULL\n");
+               return -EINVAL;
+       }
+       if(command == NULL) {
+               DBG("Internal driver error: command is NULL\n");
+               return -EINVAL;
+       }
+       databytes = (((int)command[7])<<8) | command[6];
+       if ((err = WritePacket(cam, command, PACKET_LENGTH)) < 0) {
+               DBG("Error writing command\n");
+               return err;
+       }
+       if(command[0] == DATA_IN) {
+               u8 buffer[8];
+               if(data == NULL) {
+                       DBG("Internal driver error: data is NULL\n");
+                       return -EINVAL;
+               }
+               if((err = ReadPacket(cam, buffer, 8)) < 0) {
+                       DBG("Error reading command result\n");
+                      return err;
+               }
+               memcpy(data, buffer, databytes);
+       } else if(command[0] == DATA_OUT) {
+               if(databytes > 0) {
+                       if(data == NULL) {
+                               DBG("Internal driver error: data is NULL\n");
+                               retval = -EINVAL;
+                       } else {
+                               if((err=WritePacket(cam, data, databytes)) < 0){
+                                       DBG("Error writing command data\n");
+                                       return err;
+                               }
+                       }
+               }
+       } else {
+               DBG("Unexpected first byte of command: %x\n", command[0]);
+               retval = -EINVAL;
+       }
+       return retval;
+}
+
+/****************************************************************************
+ *
+ *  cpia_pp_open
+ *
+ ***************************************************************************/
+static int cpia_pp_open(void *privdata)
+{
+       struct pp_cam_entry *cam = (struct pp_cam_entry *)privdata;
+
+       if (cam == NULL)
+               return -EINVAL;
+
+       if(cam->open_count == 0) {
+               if (parport_claim(cam->pdev)) {
+                       DBG("failed to claim the port\n");
+                       return -EBUSY;
+               }
+               parport_negotiate(cam->port, IEEE1284_MODE_COMPAT);
+               parport_data_forward(cam->port);
+               parport_write_control(cam->port, PARPORT_CONTROL_SELECT);
+               udelay(50);
+               parport_write_control(cam->port,
+                                     PARPORT_CONTROL_SELECT
+                                     | PARPORT_CONTROL_INIT);
+       }
+
+       ++cam->open_count;
+
+       return 0;
+}
+
+/****************************************************************************
+ *
+ *  cpia_pp_registerCallback
+ *
+ ***************************************************************************/
+static int cpia_pp_registerCallback(void *privdata, void (*cb)(void *cbdata), void *cbdata)
+{
+       struct pp_cam_entry *cam = privdata;
+       int retval = 0;
+
+       if(cam->port->irq != PARPORT_IRQ_NONE) {
+               cam->cb_func = cb;
+               cam->cb_data = cbdata;
+               INIT_WORK(&cam->cb_task, cpia_pp_run_callback);
+       } else {
+               retval = -1;
+       }
+       return retval;
+}
+
+/****************************************************************************
+ *
+ *  cpia_pp_close
+ *
+ ***************************************************************************/
+static int cpia_pp_close(void *privdata)
+{
+       struct pp_cam_entry *cam = privdata;
+       if (--cam->open_count == 0) {
+               parport_release(cam->pdev);
+       }
+       return 0;
+}
+
+/****************************************************************************
+ *
+ *  cpia_pp_register
+ *
+ ***************************************************************************/
+static int cpia_pp_register(struct parport *port)
+{
+       struct pardevice *pdev = NULL;
+       struct pp_cam_entry *cam;
+       struct cam_data *cpia;
+
+       if (!(port->modes & PARPORT_MODE_PCSPP)) {
+               LOG("port is not supported by CPiA driver\n");
+               return -ENXIO;
+       }
+
+       cam = kzalloc(sizeof(struct pp_cam_entry), GFP_KERNEL);
+       if (cam == NULL) {
+               LOG("failed to allocate camera structure\n");
+               return -ENOMEM;
+       }
+
+       pdev = parport_register_device(port, "cpia_pp", NULL, NULL,
+                                      NULL, 0, cam);
+
+       if (!pdev) {
+               LOG("failed to parport_register_device\n");
+               kfree(cam);
+               return -ENXIO;
+       }
+
+       cam->pdev = pdev;
+       cam->port = port;
+       init_waitqueue_head(&cam->wq_stream);
+
+       cam->streaming = 0;
+       cam->stream_irq = 0;
+
+       if((cpia = cpia_register_camera(&cpia_pp_ops, cam)) == NULL) {
+               LOG("failed to cpia_register_camera\n");
+               parport_unregister_device(pdev);
+               kfree(cam);
+               return -ENXIO;
+       }
+       spin_lock( &cam_list_lock_pp );
+       list_add( &cpia->cam_data_list, &cam_list );
+       spin_unlock( &cam_list_lock_pp );
+
+       return 0;
+}
+
+static void cpia_pp_detach (struct parport *port)
+{
+       struct list_head *tmp;
+       struct cam_data *cpia = NULL;
+       struct pp_cam_entry *cam;
+
+       spin_lock( &cam_list_lock_pp );
+       list_for_each (tmp, &cam_list) {
+               cpia = list_entry(tmp, struct cam_data, cam_data_list);
+               cam = (struct pp_cam_entry *) cpia->lowlevel_data;
+               if (cam && cam->port->number == port->number) {
+                       list_del(&cpia->cam_data_list);
+                       break;
+               }
+               cpia = NULL;
+       }
+       spin_unlock( &cam_list_lock_pp );
+
+       if (!cpia) {
+               DBG("cpia_pp_detach failed to find cam_data in cam_list\n");
+               return;
+       }
+
+       cam = (struct pp_cam_entry *) cpia->lowlevel_data;
+       cpia_unregister_camera(cpia);
+       if(cam->open_count > 0)
+               cpia_pp_close(cam);
+       parport_unregister_device(cam->pdev);
+       cpia->lowlevel_data = NULL;
+       kfree(cam);
+}
+
+static void cpia_pp_attach (struct parport *port)
+{
+       unsigned int i;
+
+       switch (parport_nr[0])
+       {
+       case PPCPIA_PARPORT_UNSPEC:
+       case PPCPIA_PARPORT_AUTO:
+               if (port->probe_info[0].class != PARPORT_CLASS_MEDIA ||
+                   port->probe_info[0].cmdset == NULL ||
+                   strncmp(port->probe_info[0].cmdset, "CPIA_1", 6) != 0)
+                       return;
+
+               cpia_pp_register(port);
+
+               break;
+
+       default:
+               for (i = 0; i < PARPORT_MAX; ++i) {
+                       if (port->number == parport_nr[i]) {
+                               cpia_pp_register(port);
+                               break;
+                       }
+               }
+               break;
+       }
+}
+
+static struct parport_driver cpia_pp_driver = {
+       .name = "cpia_pp",
+       .attach = cpia_pp_attach,
+       .detach = cpia_pp_detach,
+};
+
+static int __init cpia_pp_init(void)
+{
+       printk(KERN_INFO "%s v%d.%d.%d\n",ABOUT,
+              CPIA_PP_MAJ_VER,CPIA_PP_MIN_VER,CPIA_PP_PATCH_VER);
+
+       if(parport_nr[0] == PPCPIA_PARPORT_OFF) {
+               printk("  disabled\n");
+               return 0;
+       }
+
+       spin_lock_init( &cam_list_lock_pp );
+
+       if (parport_register_driver (&cpia_pp_driver)) {
+               LOG ("unable to register with parport\n");
+               return -EIO;
+       }
+       return 0;
+}
+
+static int __init cpia_init(void)
+{
+       if (parport[0]) {
+               /* The user gave some parameters.  Let's see what they were. */
+               if (!strncmp(parport[0], "auto", 4)) {
+                       parport_nr[0] = PPCPIA_PARPORT_AUTO;
+               } else {
+                       int n;
+                       for (n = 0; n < PARPORT_MAX && parport[n]; n++) {
+                               if (!strncmp(parport[n], "none", 4)) {
+                                       parport_nr[n] = PPCPIA_PARPORT_NONE;
+                               } else {
+                                       char *ep;
+                                       unsigned long r = simple_strtoul(parport[n], &ep, 0);
+                                       if (ep != parport[n]) {
+                                               parport_nr[n] = r;
+                                       } else {
+                                               LOG("bad port specifier `%s'\n", parport[n]);
+                                               return -ENODEV;
+                                       }
+                               }
+                       }
+               }
+       }
+       return cpia_pp_init();
+}
+
+static void __exit cpia_cleanup(void)
+{
+       parport_unregister_driver(&cpia_pp_driver);
+       return;
+}
+
+module_init(cpia_init);
+module_exit(cpia_cleanup);
diff --git a/drivers/staging/cpia/cpia_usb.c b/drivers/staging/cpia/cpia_usb.c
new file mode 100644 (file)
index 0000000..58d193f
--- /dev/null
@@ -0,0 +1,640 @@
+/*
+ * cpia_usb CPiA USB driver
+ *
+ * Supports CPiA based parallel port Video Camera's.
+ *
+ * Copyright (C) 1999        Jochen Scharrlach <Jochen.Scharrlach@schwaben.de>
+ * Copyright (C) 1999, 2000  Johannes Erdfelt <johannes@erdfelt.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
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+/* define _CPIA_DEBUG_ for verbose debug output (see cpia.h) */
+/* #define _CPIA_DEBUG_  1 */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/wait.h>
+#include <linux/list.h>
+#include <linux/slab.h>
+#include <linux/vmalloc.h>
+#include <linux/usb.h>
+
+#include "cpia.h"
+
+#define USB_REQ_CPIA_GRAB_FRAME                        0xC1
+#define USB_REQ_CPIA_UPLOAD_FRAME              0xC2
+#define  WAIT_FOR_NEXT_FRAME                   0
+#define  FORCE_FRAME_UPLOAD                    1
+
+#define FRAMES_PER_DESC                10
+#define FRAME_SIZE_PER_DESC    960     /* Shouldn't be hardcoded */
+#define CPIA_NUMSBUF           2
+#define STREAM_BUF_SIZE                (PAGE_SIZE * 4)
+#define SCRATCH_BUF_SIZE       (STREAM_BUF_SIZE * 2)
+
+struct cpia_sbuf {
+       char *data;
+       struct urb *urb;
+};
+
+#define FRAMEBUF_LEN (CPIA_MAX_FRAME_SIZE+100)
+enum framebuf_status {
+       FRAME_EMPTY,
+       FRAME_READING,
+       FRAME_READY,
+       FRAME_ERROR,
+};
+
+struct framebuf {
+       int length;
+       enum framebuf_status status;
+       u8 data[FRAMEBUF_LEN];
+       struct framebuf *next;
+};
+
+struct usb_cpia {
+       /* Device structure */
+       struct usb_device *dev;
+
+       unsigned char iface;
+       wait_queue_head_t wq_stream;
+
+       int cursbuf;            /* Current receiving sbuf */
+       struct cpia_sbuf sbuf[CPIA_NUMSBUF];            /* Double buffering */
+
+       int streaming;
+       int open;
+       int present;
+       struct framebuf *buffers[3];
+       struct framebuf *curbuff, *workbuff;
+};
+
+static int cpia_usb_open(void *privdata);
+static int cpia_usb_registerCallback(void *privdata, void (*cb) (void *cbdata),
+                                    void *cbdata);
+static int cpia_usb_transferCmd(void *privdata, u8 *command, u8 *data);
+static int cpia_usb_streamStart(void *privdata);
+static int cpia_usb_streamStop(void *privdata);
+static int cpia_usb_streamRead(void *privdata, u8 *frame, int noblock);
+static int cpia_usb_close(void *privdata);
+
+#define ABOUT "USB driver for Vision CPiA based cameras"
+
+static struct cpia_camera_ops cpia_usb_ops = {
+       cpia_usb_open,
+       cpia_usb_registerCallback,
+       cpia_usb_transferCmd,
+       cpia_usb_streamStart,
+       cpia_usb_streamStop,
+       cpia_usb_streamRead,
+       cpia_usb_close,
+       0,
+       THIS_MODULE
+};
+
+static LIST_HEAD(cam_list);
+static spinlock_t cam_list_lock_usb;
+
+static void cpia_usb_complete(struct urb *urb)
+{
+       int i;
+       char *cdata;
+       struct usb_cpia *ucpia;
+
+       if (!urb || !urb->context)
+               return;
+
+       ucpia = (struct usb_cpia *) urb->context;
+
+       if (!ucpia->dev || !ucpia->streaming || !ucpia->present || !ucpia->open)
+               return;
+
+       if (ucpia->workbuff->status == FRAME_EMPTY) {
+               ucpia->workbuff->status = FRAME_READING;
+               ucpia->workbuff->length = 0;
+       }
+
+       for (i = 0; i < urb->number_of_packets; i++) {
+               int n = urb->iso_frame_desc[i].actual_length;
+               int st = urb->iso_frame_desc[i].status;
+
+               cdata = urb->transfer_buffer + urb->iso_frame_desc[i].offset;
+
+               if (st)
+                       printk(KERN_DEBUG "cpia data error: [%d] len=%d, status=%X\n", i, n, st);
+
+               if (FRAMEBUF_LEN < ucpia->workbuff->length + n) {
+                       printk(KERN_DEBUG "cpia: scratch buf overflow!scr_len: %d, n: %d\n", ucpia->workbuff->length, n);
+                       return;
+               }
+
+               if (n) {
+                       if ((ucpia->workbuff->length > 0) ||
+                           (0x19 == cdata[0] && 0x68 == cdata[1])) {
+                               memcpy(ucpia->workbuff->data + ucpia->workbuff->length, cdata, n);
+                               ucpia->workbuff->length += n;
+                       } else
+                               DBG("Ignoring packet!\n");
+               } else {
+                       if (ucpia->workbuff->length > 4 &&
+                           0xff == ucpia->workbuff->data[ucpia->workbuff->length-1] &&
+                           0xff == ucpia->workbuff->data[ucpia->workbuff->length-2] &&
+                           0xff == ucpia->workbuff->data[ucpia->workbuff->length-3] &&
+                           0xff == ucpia->workbuff->data[ucpia->workbuff->length-4]) {
+                               ucpia->workbuff->status = FRAME_READY;
+                               ucpia->curbuff = ucpia->workbuff;
+                               ucpia->workbuff = ucpia->workbuff->next;
+                               ucpia->workbuff->status = FRAME_EMPTY;
+                               ucpia->workbuff->length = 0;
+
+                               if (waitqueue_active(&ucpia->wq_stream))
+                                       wake_up_interruptible(&ucpia->wq_stream);
+                       }
+               }
+       }
+
+       /* resubmit */
+       urb->dev = ucpia->dev;
+       if ((i = usb_submit_urb(urb, GFP_ATOMIC)) != 0)
+               printk(KERN_ERR "%s: usb_submit_urb ret %d\n", __func__,  i);
+}
+
+static int cpia_usb_open(void *privdata)
+{
+       struct usb_cpia *ucpia = (struct usb_cpia *) privdata;
+       struct urb *urb;
+       int ret, retval = 0, fx, err;
+
+       if (!ucpia)
+               return -EINVAL;
+
+       ucpia->sbuf[0].data = kmalloc(FRAMES_PER_DESC * FRAME_SIZE_PER_DESC, GFP_KERNEL);
+       if (!ucpia->sbuf[0].data)
+               return -EINVAL;
+
+       ucpia->sbuf[1].data = kmalloc(FRAMES_PER_DESC * FRAME_SIZE_PER_DESC, GFP_KERNEL);
+       if (!ucpia->sbuf[1].data) {
+               retval = -EINVAL;
+               goto error_0;
+       }
+
+       ret = usb_set_interface(ucpia->dev, ucpia->iface, 3);
+       if (ret < 0) {
+               printk(KERN_ERR "cpia_usb_open: usb_set_interface error (ret = %d)\n", ret);
+               retval = -EBUSY;
+               goto error_1;
+       }
+
+       ucpia->buffers[0]->status = FRAME_EMPTY;
+       ucpia->buffers[0]->length = 0;
+       ucpia->buffers[1]->status = FRAME_EMPTY;
+       ucpia->buffers[1]->length = 0;
+       ucpia->buffers[2]->status = FRAME_EMPTY;
+       ucpia->buffers[2]->length = 0;
+       ucpia->curbuff = ucpia->buffers[0];
+       ucpia->workbuff = ucpia->buffers[1];
+
+       /* We double buffer the Iso lists, and also know the polling
+        * interval is every frame (1 == (1 << (bInterval -1))).
+        */
+       urb = usb_alloc_urb(FRAMES_PER_DESC, GFP_KERNEL);
+       if (!urb) {
+               printk(KERN_ERR "cpia_init_isoc: usb_alloc_urb 0\n");
+               retval = -ENOMEM;
+               goto error_1;
+       }
+
+       ucpia->sbuf[0].urb = urb;
+       urb->dev = ucpia->dev;
+       urb->context = ucpia;
+       urb->pipe = usb_rcvisocpipe(ucpia->dev, 1);
+       urb->transfer_flags = URB_ISO_ASAP;
+       urb->transfer_buffer = ucpia->sbuf[0].data;
+       urb->complete = cpia_usb_complete;
+       urb->number_of_packets = FRAMES_PER_DESC;
+       urb->interval = 1;
+       urb->transfer_buffer_length = FRAME_SIZE_PER_DESC * FRAMES_PER_DESC;
+       for (fx = 0; fx < FRAMES_PER_DESC; fx++) {
+               urb->iso_frame_desc[fx].offset = FRAME_SIZE_PER_DESC * fx;
+               urb->iso_frame_desc[fx].length = FRAME_SIZE_PER_DESC;
+       }
+
+       urb = usb_alloc_urb(FRAMES_PER_DESC, GFP_KERNEL);
+       if (!urb) {
+               printk(KERN_ERR "cpia_init_isoc: usb_alloc_urb 1\n");
+               retval = -ENOMEM;
+               goto error_urb0;
+       }
+
+       ucpia->sbuf[1].urb = urb;
+       urb->dev = ucpia->dev;
+       urb->context = ucpia;
+       urb->pipe = usb_rcvisocpipe(ucpia->dev, 1);
+       urb->transfer_flags = URB_ISO_ASAP;
+       urb->transfer_buffer = ucpia->sbuf[1].data;
+       urb->complete = cpia_usb_complete;
+       urb->number_of_packets = FRAMES_PER_DESC;
+       urb->interval = 1;
+       urb->transfer_buffer_length = FRAME_SIZE_PER_DESC * FRAMES_PER_DESC;
+       for (fx = 0; fx < FRAMES_PER_DESC; fx++) {
+               urb->iso_frame_desc[fx].offset = FRAME_SIZE_PER_DESC * fx;
+               urb->iso_frame_desc[fx].length = FRAME_SIZE_PER_DESC;
+       }
+
+       /* queue the ISO urbs, and resubmit in the completion handler */
+       err = usb_submit_urb(ucpia->sbuf[0].urb, GFP_KERNEL);
+       if (err) {
+               printk(KERN_ERR "cpia_init_isoc: usb_submit_urb 0 ret %d\n",
+                       err);
+               goto error_urb1;
+       }
+       err = usb_submit_urb(ucpia->sbuf[1].urb, GFP_KERNEL);
+       if (err) {
+               printk(KERN_ERR "cpia_init_isoc: usb_submit_urb 1 ret %d\n",
+                       err);
+               goto error_urb1;
+       }
+
+       ucpia->streaming = 1;
+       ucpia->open = 1;
+
+       return 0;
+
+error_urb1:            /* free urb 1 */
+       usb_free_urb(ucpia->sbuf[1].urb);
+       ucpia->sbuf[1].urb = NULL;
+error_urb0:            /* free urb 0 */
+       usb_free_urb(ucpia->sbuf[0].urb);
+       ucpia->sbuf[0].urb = NULL;
+error_1:
+       kfree (ucpia->sbuf[1].data);
+       ucpia->sbuf[1].data = NULL;
+error_0:
+       kfree (ucpia->sbuf[0].data);
+       ucpia->sbuf[0].data = NULL;
+
+       return retval;
+}
+
+//
+// convenience functions
+//
+
+/****************************************************************************
+ *
+ *  WritePacket
+ *
+ ***************************************************************************/
+static int WritePacket(struct usb_device *udev, const u8 *packet, u8 *buf, size_t size)
+{
+       if (!packet)
+               return -EINVAL;
+
+       return usb_control_msg(udev, usb_sndctrlpipe(udev, 0),
+                        packet[1] + (packet[0] << 8),
+                        USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+                        packet[2] + (packet[3] << 8),
+                        packet[4] + (packet[5] << 8), buf, size, 1000);
+}
+
+/****************************************************************************
+ *
+ *  ReadPacket
+ *
+ ***************************************************************************/
+static int ReadPacket(struct usb_device *udev, u8 *packet, u8 *buf, size_t size)
+{
+       if (!packet || size <= 0)
+               return -EINVAL;
+
+       return usb_control_msg(udev, usb_rcvctrlpipe(udev, 0),
+                        packet[1] + (packet[0] << 8),
+                        USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+                        packet[2] + (packet[3] << 8),
+                        packet[4] + (packet[5] << 8), buf, size, 1000);
+}
+
+static int cpia_usb_transferCmd(void *privdata, u8 *command, u8 *data)
+{
+       int err = 0;
+       int databytes;
+       struct usb_cpia *ucpia = (struct usb_cpia *)privdata;
+       struct usb_device *udev = ucpia->dev;
+
+       if (!udev) {
+               DBG("Internal driver error: udev is NULL\n");
+               return -EINVAL;
+       }
+
+       if (!command) {
+               DBG("Internal driver error: command is NULL\n");
+               return -EINVAL;
+       }
+
+       databytes = (((int)command[7])<<8) | command[6];
+
+       if (command[0] == DATA_IN) {
+               u8 buffer[8];
+
+               if (!data) {
+                       DBG("Internal driver error: data is NULL\n");
+                       return -EINVAL;
+               }
+
+               err = ReadPacket(udev, command, buffer, 8);
+               if (err < 0)
+                       return err;
+
+               memcpy(data, buffer, databytes);
+       } else if(command[0] == DATA_OUT)
+               WritePacket(udev, command, data, databytes);
+       else {
+               DBG("Unexpected first byte of command: %x\n", command[0]);
+               err = -EINVAL;
+       }
+
+       return 0;
+}
+
+static int cpia_usb_registerCallback(void *privdata, void (*cb) (void *cbdata),
+       void *cbdata)
+{
+       return -ENODEV;
+}
+
+static int cpia_usb_streamStart(void *privdata)
+{
+       return -ENODEV;
+}
+
+static int cpia_usb_streamStop(void *privdata)
+{
+       return -ENODEV;
+}
+
+static int cpia_usb_streamRead(void *privdata, u8 *frame, int noblock)
+{
+       struct usb_cpia *ucpia = (struct usb_cpia *) privdata;
+       struct framebuf *mybuff;
+
+       if (!ucpia || !ucpia->present)
+               return -1;
+
+       if (ucpia->curbuff->status != FRAME_READY)
+               interruptible_sleep_on(&ucpia->wq_stream);
+       else
+               DBG("Frame already waiting!\n");
+
+       mybuff = ucpia->curbuff;
+
+       if (!mybuff)
+               return -1;
+
+       if (mybuff->status != FRAME_READY || mybuff->length < 4) {
+               DBG("Something went wrong!\n");
+               return -1;
+       }
+
+       memcpy(frame, mybuff->data, mybuff->length);
+       mybuff->status = FRAME_EMPTY;
+
+/*   DBG("read done, %d bytes, Header: %x/%x, Footer: %x%x%x%x\n",  */
+/*       mybuff->length, frame[0], frame[1], */
+/*       frame[mybuff->length-4], frame[mybuff->length-3],  */
+/*       frame[mybuff->length-2], frame[mybuff->length-1]); */
+
+       return mybuff->length;
+}
+
+static void cpia_usb_free_resources(struct usb_cpia *ucpia, int try)
+{
+       if (!ucpia->streaming)
+               return;
+
+       ucpia->streaming = 0;
+
+       /* Set packet size to 0 */
+       if (try) {
+               int ret;
+
+               ret = usb_set_interface(ucpia->dev, ucpia->iface, 0);
+               if (ret < 0) {
+                       printk(KERN_ERR "usb_set_interface error (ret = %d)\n", ret);
+                       return;
+               }
+       }
+
+       /* Unschedule all of the iso td's */
+       if (ucpia->sbuf[1].urb) {
+               usb_kill_urb(ucpia->sbuf[1].urb);
+               usb_free_urb(ucpia->sbuf[1].urb);
+               ucpia->sbuf[1].urb = NULL;
+       }
+
+       kfree(ucpia->sbuf[1].data);
+       ucpia->sbuf[1].data = NULL;
+
+       if (ucpia->sbuf[0].urb) {
+               usb_kill_urb(ucpia->sbuf[0].urb);
+               usb_free_urb(ucpia->sbuf[0].urb);
+               ucpia->sbuf[0].urb = NULL;
+       }
+
+       kfree(ucpia->sbuf[0].data);
+       ucpia->sbuf[0].data = NULL;
+}
+
+static int cpia_usb_close(void *privdata)
+{
+       struct usb_cpia *ucpia = (struct usb_cpia *) privdata;
+
+       if(!ucpia)
+               return -ENODEV;
+
+       ucpia->open = 0;
+
+       /* ucpia->present = 0 protects against trying to reset the
+        * alt setting if camera is physically disconnected while open */
+       cpia_usb_free_resources(ucpia, ucpia->present);
+
+       return 0;
+}
+
+/* Probing and initializing */
+
+static int cpia_probe(struct usb_interface *intf,
+                     const struct usb_device_id *id)
+{
+       struct usb_device *udev = interface_to_usbdev(intf);
+       struct usb_host_interface *interface;
+       struct usb_cpia *ucpia;
+       struct cam_data *cam;
+       int ret;
+
+       /* A multi-config CPiA camera? */
+       if (udev->descriptor.bNumConfigurations != 1)
+               return -ENODEV;
+
+       interface = intf->cur_altsetting;
+
+       printk(KERN_INFO "USB CPiA camera found\n");
+
+       ucpia = kzalloc(sizeof(*ucpia), GFP_KERNEL);
+       if (!ucpia) {
+               printk(KERN_ERR "couldn't kmalloc cpia struct\n");
+               return -ENOMEM;
+       }
+
+       ucpia->dev = udev;
+       ucpia->iface = interface->desc.bInterfaceNumber;
+       init_waitqueue_head(&ucpia->wq_stream);
+
+       ucpia->buffers[0] = vmalloc(sizeof(*ucpia->buffers[0]));
+       if (!ucpia->buffers[0]) {
+               printk(KERN_ERR "couldn't vmalloc frame buffer 0\n");
+               goto fail_alloc_0;
+       }
+
+       ucpia->buffers[1] = vmalloc(sizeof(*ucpia->buffers[1]));
+       if (!ucpia->buffers[1]) {
+               printk(KERN_ERR "couldn't vmalloc frame buffer 1\n");
+               goto fail_alloc_1;
+       }
+
+       ucpia->buffers[2] = vmalloc(sizeof(*ucpia->buffers[2]));
+       if (!ucpia->buffers[2]) {
+               printk(KERN_ERR "couldn't vmalloc frame buffer 2\n");
+               goto fail_alloc_2;
+       }
+
+       ucpia->buffers[0]->next = ucpia->buffers[1];
+       ucpia->buffers[1]->next = ucpia->buffers[2];
+       ucpia->buffers[2]->next = ucpia->buffers[0];
+
+       ret = usb_set_interface(udev, ucpia->iface, 0);
+       if (ret < 0) {
+               printk(KERN_ERR "cpia_probe: usb_set_interface error (ret = %d)\n", ret);
+               /* goto fail_all; */
+       }
+
+       /* Before register_camera, important */
+       ucpia->present = 1;
+
+       cam = cpia_register_camera(&cpia_usb_ops, ucpia);
+       if (!cam) {
+               LOG("failed to cpia_register_camera\n");
+               goto fail_all;
+       }
+
+       spin_lock( &cam_list_lock_usb );
+       list_add( &cam->cam_data_list, &cam_list );
+       spin_unlock( &cam_list_lock_usb );
+
+       usb_set_intfdata(intf, cam);
+       return 0;
+
+fail_all:
+       vfree(ucpia->buffers[2]);
+       ucpia->buffers[2] = NULL;
+fail_alloc_2:
+       vfree(ucpia->buffers[1]);
+       ucpia->buffers[1] = NULL;
+fail_alloc_1:
+       vfree(ucpia->buffers[0]);
+       ucpia->buffers[0] = NULL;
+fail_alloc_0:
+       kfree(ucpia);
+       return -EIO;
+}
+
+static void cpia_disconnect(struct usb_interface *intf);
+
+static struct usb_device_id cpia_id_table [] = {
+       { USB_DEVICE(0x0553, 0x0002) },
+       { USB_DEVICE(0x0813, 0x0001) },
+       { }                                     /* Terminating entry */
+};
+
+MODULE_DEVICE_TABLE (usb, cpia_id_table);
+MODULE_LICENSE("GPL");
+
+
+static struct usb_driver cpia_driver = {
+       .name           = "cpia",
+       .probe          = cpia_probe,
+       .disconnect     = cpia_disconnect,
+       .id_table       = cpia_id_table,
+};
+
+static void cpia_disconnect(struct usb_interface *intf)
+{
+       struct cam_data *cam = usb_get_intfdata(intf);
+       struct usb_cpia *ucpia;
+
+       usb_set_intfdata(intf, NULL);
+       if (!cam)
+               return;
+
+       ucpia = (struct usb_cpia *) cam->lowlevel_data;
+       spin_lock( &cam_list_lock_usb );
+       list_del(&cam->cam_data_list);
+       spin_unlock( &cam_list_lock_usb );
+
+       ucpia->present = 0;
+
+       cpia_unregister_camera(cam);
+       if(ucpia->open)
+               cpia_usb_close(cam->lowlevel_data);
+
+       ucpia->curbuff->status = FRAME_ERROR;
+
+       if (waitqueue_active(&ucpia->wq_stream))
+               wake_up_interruptible(&ucpia->wq_stream);
+
+       ucpia->curbuff = ucpia->workbuff = NULL;
+
+       vfree(ucpia->buffers[2]);
+       ucpia->buffers[2] = NULL;
+
+       vfree(ucpia->buffers[1]);
+       ucpia->buffers[1] = NULL;
+
+       vfree(ucpia->buffers[0]);
+       ucpia->buffers[0] = NULL;
+
+       cam->lowlevel_data = NULL;
+       kfree(ucpia);
+}
+
+static int __init usb_cpia_init(void)
+{
+       printk(KERN_INFO "%s v%d.%d.%d\n",ABOUT,
+              CPIA_USB_MAJ_VER,CPIA_USB_MIN_VER,CPIA_USB_PATCH_VER);
+
+       spin_lock_init(&cam_list_lock_usb);
+       return usb_register(&cpia_driver);
+}
+
+static void __exit usb_cpia_cleanup(void)
+{
+       usb_deregister(&cpia_driver);
+}
+
+
+module_init (usb_cpia_init);
+module_exit (usb_cpia_cleanup);
+