/*
 * qce-ga, linux V4L driver for the Quickcam Express and Dexxa Quickcam
 *
 * quickcam.c - main driver part
 *
 * Copyright (C) 2001  Jean-Fredric Clere,Nikolas Zimmermann, Georg Acher
 * Mark Cave-Ayland, Carlo E Prelz, Dick Streefland
 *
 * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 *
 */

/* Cam variations of Logitech Quickcam Express:
   P/N 861037:      Sensor HDCS1000        ASIC STV0600
   P/N 861050-0010: Sensor HDCS1000        ASIC STV0600
   P/N 861050-0020: Sensor Photobit PB100  ASIC STV0600
   P/N 861055:      Sensor ST VV6410       ASIC STV0610 ("LEGO cam" not _yet_ supported)
   P/N 861075-0040: Sensor HDCS1000        ASIC
   P/N 961179-0700: Sensor ST VV6410       ASIC STV0602 (Dexxa WebCam USB not _yet_ supported)

   For any questions ask qce-ga-devel@lists.sourceforge.net!
*/

#include <linux/config.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/sched.h>
#include <linux/list.h>
#include <linux/version.h>
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0)
#include <linux/slab.h>
#else
#include <linux/malloc.h>
#endif
#include <linux/mm.h>
#include <linux/proc_fs.h>
#include <linux/smp_lock.h>
#include <linux/videodev.h>
#include <linux/vmalloc.h>
#include <linux/wrapper.h>
#include <linux/module.h>
#include <linux/spinlock.h>
#include <linux/usb.h>
#include <asm/io.h>

#include "quickcam.h"
#include "pb0100.h"
#include "hdcs.h"
#include "vv6410.h"
#include "memory.h"
#include "helper.h"

#ifndef MODULE
#define DEBUGLEVEL 0 // put in your wanted value here
#endif

#ifdef MODULE
#define DEBUGLEVEL 0
MODULE_PARM(debug, "i");
MODULE_PARM(interpolation, "i");
MODULE_PARM(mode, "i");
MODULE_PARM(keepexposure, "i");
MODULE_PARM(tobgr, "i");
MODULE_PARM(rgain, "i");
MODULE_PARM(bgain, "i");
MODULE_PARM(ggain, "i");
MODULE_PARM(bright,"i");
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,5)
MODULE_PARM(video_nr,"i");
#endif
MODULE_PARM_DESC(debug, "Sets the debug output (1,2,4,8,16,32)");
MODULE_PARM_DESC(interpolation, "Sets the interpolation mode (0-1)");
MODULE_PARM_DESC(mode, "Sets the speed (0-1)");
MODULE_PARM_DESC(keepexposure, "Keep gain settings across one open to another (0-1)");
MODULE_PARM_DESC(tobgr, "Automatic RGB -> BGR conversion");
MODULE_PARM_DESC(rgain, "Initial value of red gain (0-255)");
MODULE_PARM_DESC(bgain, "Initial value of blue gain (0-255)");
MODULE_PARM_DESC(ggain, "Initial value of green gains (0-255)");
MODULE_PARM_DESC(bright,"Initial value for brightness (0-65535)");
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,5)
MODULE_PARM_DESC(video_nr, "Set videodevice number (/dev/videoX)");
#endif
MODULE_SUPPORTED_DEVICE("video");
MODULE_DESCRIPTION("Logitech Quickcam Express Webcam driver");
MODULE_AUTHOR("see README");
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,9)
#ifdef MODULE_LICENSE
MODULE_LICENSE("GPL");
#endif
#endif
#endif

static int debug = DEBUGLEVEL;

static int keepexposure = 0;
static int tobgr = 0;

static int interpolation = 0; /* 1 = bilinear interpolation */

static int mode  = 0; /* normal or sub-sample (sub-sample to increase the speed) */

/* gains allow the user decide the initial value of the gains */
static int rgain = 0;
static int bgain = 0;
static int ggain = 0;
static int bright= 32768; /* Zero gives black images with gnomemeeting */

#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,5)
/* video_nr option allows to specify a certain /dev/videoX device */
/* (like /dev/video0 or /dev/video1 ...)                          */
/* for autodetect first available use video_nr=-1 (defaultvalue)  */
/* (code reused from bttv driver http://bytesex.org/bttv/)        */
static int video_nr = -1;
#endif

/* Video Size 352 x 288 x 4 bytes for 0RGB 32 bpp mode */
#define MAX_FRAME_SIZE (352 * 288 * 4)

#define VERSION "$Id: quickcam.c,v 1.102 2002/03/25 22:22:34 jfclere Exp $"

static struct usb_driver quickcam_driver;

#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0)
static __devinitdata struct usb_device_id device_table [] = {
	{ USB_DEVICE(0x046d, 0x0840) },	/* Quickcam Express */
	{ USB_DEVICE(0x046d, 0x0850) },	/* LEGO cam - not yet supported */
	{ USB_DEVICE(0x046d, 0x0870) },	/* Dexxa webcam USB - not yet supported */
	{ }
};

MODULE_DEVICE_TABLE (usb, device_table);
#endif
                                          
static struct sensor_data sensors [] = {
  /* name    reg23 i2c_addr     id_reg          id    l  load           */
  /* ----    ----- -----------  --------------	----  -	 --------------	*/
  { "HDCS1000",	0, HDCS_ADDR,   HDCS_IDENT + 1, 0x08, 1, load_hdcs_mod   },
  { "BP100",	1, PB_ADDR,     PB_IDENT,       0x64, 2, load_pb0100_mod },
  { "VV6410",	5, VV6410_ADDR, VV6410_IDENT,   0x19, 1, load_vv6410_mod },
  { "HDCS1020",	0, HDCS_ADDR,   HDCS_IDENT + 1, 0x10, 1, load_hdcs20_mod },
  { NULL }
};

/*
 * HexDump a string...
 */
static void usbvideo_HexDump(const unsigned char *data, int len)
{
        const int bytes_per_line = 32;
        char tmp[128]; /* 32*3 + 5 */
        int i, k;
 
        for (i=k=0; len > 0; i++, len--) {
                if (i > 0 && ((i % bytes_per_line) == 0)) {
                        printk("%s\n", tmp);
                        k=0;
                }
                if ((i % bytes_per_line) == 0)
                        k += sprintf(&tmp[k], "[%04x]: ", i);
                k += sprintf(&tmp[k], "%02x ", data[i]);
        }
        if (k > 0)
                printk("%s\n", tmp);
}                                                                               

/*
 * /proc interface for our driver
 */

#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0)
#if defined(CONFIG_PROC_FS) && defined(CONFIG_VIDEO_PROC_FS)

static struct proc_dir_entry *quickcam_proc_entry = NULL;
extern struct proc_dir_entry *video_proc_entry;

#define CHECK(x) ((x) ? "Yes" : "No")
#define CHOOSE(x,y,a,b) ((x == y) ? a : b) 

static int quickcam_read_proc(char *page, char **start, off_t off,
	                      int count, int *eof, void *data)
{
    char *out = page;
    int len;
    struct usb_quickcam *dev = data;
    
    out += sprintf(out, "*** Driver Status ***\n");
    out += sprintf(out, "Driver Version        : %s\n", VERSION);
    out += sprintf(out, "Sensor                : %s\n", dev->sensor_name);
    out += sprintf(out, "Streaming             : %s\n", CHECK(dev->streaming));
    out += sprintf(out, "Grabbing              : %s\n", CHECK(dev->grabbing));
    out += sprintf(out, "Reading frame         : %d\n", dev->readframe);
    out += sprintf(out, "\n*** Sensor Control ***\n");
    out += sprintf(out, "Shutter value         : %d\n", dev->shutter_val);
    out += sprintf(out, "Gain                  : %d\n", dev->gain);
	out += sprintf(out, "Exposure value        : %d\n", dev->val);
	out += sprintf(out, "Red Gain              : %d\n", dev->red);
	out += sprintf(out, "Green Gain            : %d\n", dev->green);
	out += sprintf(out, "Blue Gain             : %d\n", dev->blue);
    out += sprintf(out, "\n*** Output Window ***\n");
    out += sprintf(out, "Width                 : %d\n", dev->vwin.width);
    out += sprintf(out, "Height                : %d\n", dev->vwin.height);
    out += sprintf(out, "\n*** Output Picture ***\n");
    out += sprintf(out, "Brightness            : %d\n", dev->vpic.brightness);
    out += sprintf(out, "Whiteness             : %d\n", dev->vpic.whiteness);
    out += sprintf(out, "Contrast              : %d\n", dev->vpic.contrast);
    out += sprintf(out, "Hue                   : %d\n", dev->vpic.hue);
    out += sprintf(out, "Color                 : %d\n", dev->vpic.colour);
    out += sprintf(out, "Palette               : %s\n", FormatName(dev->vpic.palette));
    
    len = out - page;
    len -= off;
    if(len < count)
    {
	*eof = 1;
	if(len <= 0)
	    return 0;
    }
    else
	len = count;

    *start = page + off;
    
    return len;
}

static int quickcam_write_proc(struct file *file, const char *buffer,
	                       unsigned long count, void *data)
{
    // we don't support this....yet?
    return -EINVAL;
}

static void create_proc_quickcam(struct usb_quickcam *dev)
{
    char name[9];
    struct proc_dir_entry *entry;
    
    if(!quickcam_proc_entry || !dev)
	return;
	
    sprintf(name, "video%d", dev->vdev.minor);
    
    entry = create_proc_entry(name, S_IFREG | S_IRUGO | S_IWUSR, quickcam_proc_entry);
    
    if(!entry)
	return;

    entry->data = dev;
    entry->read_proc = quickcam_read_proc;
    entry->write_proc = quickcam_write_proc;
    dev->proc_entry = entry;
}

static void destroy_proc_quickcam(struct usb_quickcam *dev)
{
    char name[9];
    
    if(!dev->proc_entry || !dev)
	return;

    sprintf(name, "video%d", dev->vdev.minor);
    
    remove_proc_entry(name, quickcam_proc_entry);
    dev->proc_entry = NULL;
}

static void proc_quickcam_create(void)
{
    if(!video_proc_entry)
	return;

    quickcam_proc_entry = create_proc_entry("quickcam", S_IFDIR, video_proc_entry);

    if(quickcam_proc_entry)
	quickcam_proc_entry->owner = THIS_MODULE;
}

static void proc_quickcam_destroy(void)
{
    if(!quickcam_proc_entry)
	return;

    remove_proc_entry("quickcam", video_proc_entry);
}

#endif
#endif

/*
 * I2C read registers of HDCS1000, the result will be in the STV0600 register 0x1410.
 */
static int usb_quickcam_i2c_in(struct usb_device *dev, int reg,
	 unsigned char sensor_addr)
{
char buff[35]; /* why 35 = 23 hex? */

        buff[0]=reg;

        buff[0x20]=sensor_addr;
        buff[0x21]=0; // 1 value
        buff[0x22]=3; // Read cmd.

	return usb_control_msg(dev, usb_sndctrlpipe(dev, 0),
		0x04,
		0x40,
		0x1400, 0,
		buff, 0x23 , HZ);
}

/* read one byte identification register for HDCS.
 * write command to sensor.
 * read the STV0600.
 */
static int usb_quickcam_get_i2c(struct usb_device *dev, unsigned char sensor_addr, int reg, void *buf, int len)
{
	if (usb_quickcam_i2c_in(dev,reg,sensor_addr)<0) {
		printk("usb_quickcam_i2c_in failed\n");
		return(-1);
		}
	return usb_control_msg(dev, usb_rcvctrlpipe(dev, 0),
		0x04,
		0xC0,
		0x1410, 0, buf, len, HZ);
}

/*
 * Set register one byte
 */
int usb_quickcam_set1(struct usb_device *dev, short reg, char val)
{
char buff[1];
	buff[0] = val;
	return usb_control_msg(dev, usb_sndctrlpipe(dev, 0),
		0x04,
		0x40,
		reg, 0,
		buff, 1, HZ);
}

/*
 * Set register two byte
 */
int usb_quickcam_set2(struct usb_device *dev, short reg, short val)
{
char buff[2];
	buff[0] = val&0xFF;
	buff[1] = (val>>8)&255;
	return usb_control_msg(dev, usb_sndctrlpipe(dev, 0),
		0x04,
		0x40,
		reg, 0,
		buff, 2, HZ);
}

/* Send a command to the sensor */

static int quickcam_usb_control_msg(struct usb_device *dev, char *buff)
{
	return usb_control_msg(dev, usb_sndctrlpipe(dev, 0),
		0x04,
		0x40,
		0x400, 0,
		buff, 0x23 , HZ);
}

/**
 * Clean buffer for I2C messages.
 */
void usb_quickcam_i2c_new(struct quickcam_i2c *i2cbuff)
{
        i2cbuff->length=0;
	memset(i2cbuff->buff,'\0',0x23);
}

/**
 * Add register and byte value.
 */
void usb_quickcam_i2c_add(struct quickcam_i2c *i2cbuff,
unsigned char reg, unsigned char value)
{
        i2cbuff->buff[i2cbuff->length] = reg;
        i2cbuff->buff[i2cbuff->length+0x10] = value;
	i2cbuff->length++;
}

/**
 * Add register and 2 bytes value.
 */
void usb_quickcam_i2c_add2(struct quickcam_i2c *i2cbuff,
unsigned char reg, unsigned short value)
{
        i2cbuff->buff[i2cbuff->length] = reg;
        i2cbuff->buff[(i2cbuff->length*2)+0x10] = value&255;
        i2cbuff->buff[(i2cbuff->length*2)+0x11] = (value>>8)&255;
	i2cbuff->length++;
}

/**
 * Send the I2C message.
 */
int usb_quickcam_i2c_send(struct usb_device *dev, struct quickcam_i2c *i2cbuff,
unsigned char sensor_add)
{
int ret;
        i2cbuff->buff[0x20]=sensor_add;
        i2cbuff->buff[0x21]=i2cbuff->length-1;
        i2cbuff->buff[0x22]=1; // Write cmd, 03 would be read.
	ret = quickcam_usb_control_msg(dev,i2cbuff->buff);
        usb_quickcam_i2c_new(i2cbuff);
	return(ret);
}

/* set the gains according to V4L default settings */

static int  usb_quickcam_set_gains(struct usb_quickcam *quickcam)
{
struct usb_device *dev = quickcam->dev;
int gr, gb, gg;

	gr=qcmin(255,qcmax(1,(quickcam->gain*quickcam->red)>>7));
	gb=qcmin(255,qcmax(1,(quickcam->gain*quickcam->blue)>>7));
	gg=qcmin(255,qcmax(1,(quickcam->gain*quickcam->green)>>7));
	if (debug&DEBUGDATA)
        {
		printk("usb_quickcam_set_gains: gain %d, red %d, blue %d, green %d\n",
			quickcam->gain,quickcam->red,quickcam->blue,quickcam->green);
		printk("usb_quickcam_set_gains: gr %d, gb %d, gg %d\n",gr,gb,gg);
	}

	return quickcam->sensor_ctrl.set_gains(dev,gr,gb,gg); 
}

/*
 * Set the exposure: note the val is a long, kernel does not know __divdi3,
 * it is in libc but it a big piece of code... 
 * Of this is a copy ;=) of  Georg Acher (acher@in.tum.de) set_exposure().
 * And he says: "Auto-Exposure needs a bit more intelligence"!
 */

static int usb_quickcam_set_exposure(struct usb_quickcam *quickcam,
int val)
{
	int og=quickcam->gain;
	int os=quickcam->shutter_val;
	int brightness=quickcam->brightness>>8;
	struct usb_device *dev = quickcam->dev;

	quickcam->val = val;
	
	if (qcabs(val-brightness)>5) quickcam->gain-=(val-brightness)/5;
	quickcam->gain=qcmin(255,qcmax(1,quickcam->gain));

/* It seems that the less gain you have the better ... You have less noise */
	if (quickcam->gain<=2 && quickcam->shutter_val>2)
	{
		quickcam->gain+=2;
		quickcam->shutter_val-=2;
	}
/* Instead of amplifying the signal, open the shutter to get more light ! */
	if (quickcam->gain>=200 && quickcam->shutter_val<254)
	{
		quickcam->gain-=2;
		quickcam->shutter_val+=2;
	}
	if (debug&DEBUGDATA)
	{
		printk("usb_quickcam_set_exposure: required %d, mesured %d\n",brightness,val);
		printk("usb_quickcam_set_exposure: gain %d shutter %d\n",quickcam->gain,quickcam->shutter_val);
	}
	if (os!=quickcam->shutter_val)
		quickcam->sensor_ctrl.set_shutter(dev,quickcam->shutter_val,0x100);
	if (og!=quickcam->gain)
		return usb_quickcam_set_gains(quickcam);
	return 0;
}

/* Stop camera and disable ISO-streaming */
static int usb_quickcam_stop(struct usb_quickcam *quickcam)
{
int ret=0;

	if (!quickcam->dev)
		return 0; // nothing to do.

	// stop ISO-streaming.
        if (usb_quickcam_set1(quickcam->dev, STV_ISO_ENABLE, 0)<0)
		ret = -1;

 	// stop current frame.
	ret = quickcam->sensor_ctrl.stop(quickcam->dev,
                                         &(quickcam->sensor_ctrl));
	if (debug&DEBUGLOGIC)
		printk("usb_quickcam_stop:%d\n",ret);
	return ret;
}

/*
 * Change the control register to start an image capture.
 */
static int quickcam_init_isoc(struct usb_quickcam *quickcam);
static int quickcam_stop_isoc(struct usb_quickcam *quickcam);
static int usb_quickcam_upload_frame(struct usb_quickcam *quickcam)
{
	if (debug&DEBUGLOGIC)
		printk("usb_quickcam_upload_frame %d\n",quickcam->scanstate);

	if (quickcam->scanstate==STATE_ERROR) {
		quickcam->grabbing = 0;
		// stop the hardware and reinitialise the USB.
		if (quickcam_stop_isoc(quickcam)<0)
			return -1;
		if (quickcam_init_isoc(quickcam)<0)
			return -1;
		quickcam->scanstate = STATE_OK; // otherwise error forever!
	}

	/* if  we are not grabbing start grabbing */
	if (!quickcam->grabbing) {
		if (debug&DEBUGLOGIC)
			printk("usb_quickcam_upload_frame start grabbing\n");
		if (quickcam->sensor_ctrl.start(quickcam->dev,
                    &(quickcam->sensor_ctrl))<0)
		{
			if (debug&DEBUGLOGIC)
			    printk("usb_quickcam_upload_frame ...grabbing NOT started\n");
			return -1;
		}
		if (usb_quickcam_set1(quickcam->dev, STV_ISO_ENABLE, 1)<0)
		{
			if (debug&DEBUGLOGIC)
			    printk("usb_quickcam_upload_frame ...grabbing NOT started (iso enabling failed :()\n");
			return -1;
		}
		quickcam->grabbing = -1;
	}
	return 0;
}

/*
 *  Streamformat, as delivered by the camera:
 *
 * Raw image data for Bayer-RGB-matrix:
 * G R for even rows
 * B G for odd rows
 * Well not when frame by frame. It starts with 80 01 00 00 for the first time,
 * that 02 00 HH LL (and there is some grabages after the frame,
 * from time to time a 80 02 00 00 80 01 00 00 is received, it seems it is a
 * start of frame (at least it is handled as if it were one.
 *
 * Convert the Bayer-RGB to RBG24.
 *
 * Note:
 * 
 * A trick is used to calculate medium value:
 * a long long cannot be used in 32 bits processors (like 386 family)
 * so I calculate a medium value per line, and sum the line values.
 *
 */
static void quickcam_parse_data(struct usb_quickcam *quickcam, int curframe)
{
#define addblue	0
#define addgreen 1
#define addred	2
	/* need cleaning ... */
#define XH quickcam->vwin.height
#define XW quickcam->vwin.width
#define SW quickcam->sensor_ctrl.width

	struct quickcam_frame *frame;
	long i;
	unsigned long mid_value=0;
	unsigned long mid_value_line=0;
	int xx=0;
	int yy=0;
	int xd=0;
	int copylen=0;
	unsigned char *o;
	unsigned char *data;
        int skiph;
        int startw, endw;

        /* calculate data to skip for the VV6410 sensor */
        if (mode) {
            skiph = (quickcam->sensor_ctrl.height-(XH/2))/2;
            startw = (quickcam->sensor_ctrl.width-(XW/2))/2;
        } else {
            skiph = (quickcam->sensor_ctrl.height-XH)/2;
            startw = (quickcam->sensor_ctrl.width-XW)/2;
        }
        if (skiph<0) skiph=0;
        if (startw<0) startw=0;
        endw = quickcam->sensor_ctrl.width-startw;

	frame = &quickcam->frame[curframe];
	if (debug&DEBUGDATA)
		printk("quickcam_parse_data %ld for frame %d\n",
			frame->storelength,curframe);

	/* Convert the current frame */

        /* skip fisrt and last lines if needed */
	data = frame->storedata+(skiph*quickcam->sensor_ctrl.width);
	for (i=skiph*quickcam->sensor_ctrl.width;
             i<frame->storelength-(skiph*quickcam->sensor_ctrl.width);
             i++) {

                /*
                 * Skip line data when needed.
                 */
                if ((i%quickcam->sensor_ctrl.width<startw) ||
                    (i%quickcam->sensor_ctrl.width>=endw && startw)) {
		    data++;
                    continue;
                }

		/*
		 * For HDCS Sensors:
		 * Camera is G1R1 G2R2 G3R3...
		 *           B4G4 B5G5 B6G6...
		 * Video  is B4G1R1 B4G1R1
		 *           B4G4R1 B4G4R1
		 * For Photobit...
		 */

		if (yy>XH)
			yy=XH;

		if (mode) {
			o=((char*)frame->data)+3*xd+(XW*3*yy);
			xd++;
			xd++;
		} else
			o=((char*)frame->data)+3*xx+(XW*3*yy);

		mid_value_line+=*data;
		
		if (interpolation == 1) { /* bilinear interpolation */
		    if (!(yy&1)) { // even row
		    	    if (xx&1) { // odd column, red
				    *(o+addred)=*data;
				    if (yy == 0) {
					if (xx == XW-1) {
    					    *(o+addblue)=*(data+SW-1);
					    *(o+addgreen)=(*(data-1) + *(data+SW))/2;
					} else {
    					    *(o+addblue)=(*(data+SW-1) + *(data+SW+1))/2;
					    *(o+addgreen)=(*(data-1) + *(data+1) + *(data+SW))/3;
					}
				    } else {
					if (xx == XW-1) {
    					    *(o+addblue)=(*(data-SW-1) + *(data+SW-1))/2;
					    *(o+addgreen)=(*(data-SW) + *(data-1) + *(data+SW))/3;
					} else {
    					    *(o+addblue)=(*(data-SW-1) + *(data-SW+1) + *(data+SW-1) + *(data+SW+1))/4;
					    *(o+addgreen)=(*(data-SW) + *(data-1) + *(data+1) + *(data+SW))/4;
					}
				    }
				    if (mode) {
				    }
			    } else  { // green
				    if (yy == 0) {
					if (xx == 0) {
					    *(o+addred)=*(data+1);
					    *(o+addblue)=*(data+SW);
					} else {
					    *(o+addred)=(*(data-1) + *(data+1))/2;
					    *(o+addblue)=*(data+SW);
					}
				    } else {
					if (xx == 0) {
					    *(o+addred)=*(data+1);
					    *(o+addblue)=(*(data-SW) + *(data+SW))/2;
					} else {
					    *(o+addred)=(*(data-1) + *(data+1))/2;
					    *(o+addblue)=(*(data-SW) + *(data+SW))/2;
					}
				    }
				    *(o+addgreen)=*data;
				    if (mode) {
				    }
			    }
		    } else {
			if (xx&1) { // odd column green
				if (yy == XH-1) {
				    if (xx == XW-1) {
					*(o+addred)=*(data-SW);
					*(o+addblue)=*(data-1);
				    } else {
					*(o+addred)=*(data-SW);
					*(o+addblue)=(*(data-1) + *(data+1))/2;
				    }
				} else {
				    if (xx == XW-1) {
					*(o+addred)=(*(data-SW) + *(data+SW))/2;
					*(o+addblue)=*(data-1);
				    } else {
					*(o+addred)=(*(data-SW) + *(data+SW))/2;
					*(o+addblue)=(*(data-1) + *(data+1))/2;
				    }
				}
				*(o+addgreen)=*data;
				if (mode) {
				}
			} else { //blue
				*(o+addblue)=*data;
				if (yy == XH-1) {
				    if (xx == 0) {
					*(o+addred)=*(data-SW+1);
					*(o+addgreen)=(*(data-SW) + *(data+1))/2;
				    } else {
					*(o+addred)=(*(data-SW-1) + *(data-SW+1))/2;
					*(o+addgreen)=(*(data-SW) + *(data-1) + *(data+1))/3;
				    }
				} else {
				    if (xx == 0) {
					*(o+addred)=(*(data-SW+1) + *(data+SW+1))/2;
					*(o+addgreen)=(*(data-SW) + *(data+1) + *(data+SW))/3;
				    } else {
					*(o+addred)=(*(data-SW-1) + *(data-SW+1) + *(data+SW-1) + *(data+SW+1))/4;
					*(o+addgreen)=(*(data-SW) + *(data-1) + *(data+1) + *(data+SW))/4;
				    }
				}
				if (mode) {
				}
			}
		    }
		} else { /* not bilinear interpolation */
		    if (!(yy&1)) { // even row
			if (xx&1) { // odd column, red
				*(o+addred)=*data;
				*(o+addred-3)=*data;
				*(o+addred+3*(XW))=*data;
				*(o+addred+3*(XW-1))=*data;
				if (mode) {
					*(o+addred+3)=*data;
					*(o+addred-6)=*data;
					*(o+addred+3*(XW+1))=*data;
					*(o+addred+3*(XW-2))=*data;
				}
			} else  { // green
				*(o+1)=*data;
				*(o+1+3)=*data;
				if (mode) {
					*(o+1+6)=*data;
					if (xd>2)
						*(o+1-3)=*data;
				}
			}
		} else {
			if (xx&1) { // odd column green
				*(o+1)=*data;
				*(o+1-3)=*data;
				if (mode) {
					*(o+1+3)=*data;
					*(o+1-6)=*data;
					*(o+1+6)=*data; // last would be missing.
				}
			} else { //blue
				*(o+addblue)=*data;
				*(o+addblue+3)=*data;
			 	*(o+addblue+3*(XW))=*data;
				*(o+addblue+3*(XW-1))=*data;
				if (mode) {
					*(o+addblue+6)=*data;
			 		*(o+addblue+3*(XW+1))=*data;
					*(o+addblue-6)=*data;
			 		*(o+addblue+3*(XW-2))=*data;
				}
			}
		    }
		}

		xx++;
		copylen ++;

		if (xx==XW || (xx==XW/2 && mode)) {
			if (mode)
				mid_value += (mid_value_line/(XW/2));
			else
				mid_value += (mid_value_line/XW);
			mid_value_line = 0;
			xx=0;
			xd = 0;
			yy++;
			if (yy>=XH) {
				if (debug&DEBUGDATA)
					printk("skipped %ld bytes\n",frame->storelength-i);
				break;
				}
			}
		data++;
	}

	/*
	 * the frame is done, the exposure should
	 * be set here (We are the waked-up process)
 	 */
	if (yy>=XH || (yy>=XH/2 && mode)) {
                if (quickcam->sensor_ctrl.mode==2)
		    mid_value = mid_value/(yy/2);
                else 
		    mid_value = mid_value/yy;
		usb_quickcam_set_exposure(quickcam,mid_value);
	}

        /* if mode=2 we have only 1/2 of the enlargement */
        if (quickcam->sensor_ctrl.mode==2) {
            data = frame->data;
            for (i=XH/2;i>1;i--) {
                memcpy(&data[i*6*XW],&data[i*3*XW],XW*3);
                memcpy(&data[i*6*XW-3*XW],&data[i*3*XW],XW*3);
            }
            // memcpy(&data[XW*6],&data,XW*3);
            copylen = copylen *2;
        }

        if (mode)
            copylen = copylen *2;
	frame->scanlength = (copylen*6)/2; // yes but how to compute partial pixels!.

	/* RGB -> BGR */
	if(tobgr)
	{
		char c;
		char *p = frame->data;
		int i = quickcam->vwin.width * quickcam->vwin.height;
		while(--i)
		{
			c = p[0];
			p[0] = p[2];
			p[2] = c;
			p += 3;
		}
	}
}

/*
 * Analyse the data and store it
 * This routine has been written by Carlo E. Prelz, fluido@fluido.as
 * Image starts with 80 01 00 00
 * and ends with     80 02 00 00
 * The actual unknown messages are:
 *                   80 05 00 00 (Assume it is start)
 *                   80 06 00 00 (Assume it is end)
 *                   C0 01 00 00 (Assume it is start).
 *                   C0 02 00 00 (Assume it is end).
 *                   C0 06 00 00 (? like 80 06 00 00).
 *                   C0 05 00 00 (Not found in my tries, but I have the feeling it exists).
 * Chunk-Header  02 00 HH LL, followed by chunk with HHLL bytes
 *           or  42 00 HH LL.
 */
static void quickcam_parse_store(struct urb *urb)
{
	unsigned char type1,type2,*ptr;
	int i,cur_len,tot_len,left;
	unsigned int frag_len=0;
	struct quickcam_frame *pframe=NULL;
	int framesize;

	struct usb_quickcam *quickcam;

	if (!urb)
		return;
	quickcam = urb->context;

	if (!quickcam)
		return;

	if (!quickcam->dev)
		return;

	if (!quickcam->streaming) {
		if (debug&DEBUGINT)
			printk("quickcam: oops, not streaming, but interrupt\n");
		return;
	}

        /* Use sensor information to get the framesize */
        if (quickcam->sensor_ctrl.mode==1)
            framesize = qcmin(BAYER_FRAME_SIZE,
                              (quickcam->sensor_ctrl.width*
                              quickcam->sensor_ctrl.height)*2);
        else
            framesize = qcmin(BAYER_FRAME_SIZE,
                              quickcam->sensor_ctrl.width*
                              quickcam->sensor_ctrl.height);

	/* Check the length */
	for(i=0,tot_len=quickcam->scratchlen;i<urb->number_of_packets;i++) {
		if(!urb->iso_frame_desc[i].status)
			tot_len+=urb->iso_frame_desc[i].actual_length;
		else
			printk("quickcam data error: [%d] len=%d, status=%X\n",
				i,
				urb->iso_frame_desc[i].actual_length,
				urb->iso_frame_desc[i].status);
	}

	if(tot_len<=quickcam->scratchlen)
		return;

	if (tot_len>=SCRATCH_BUF_SIZE) {
		printk("quickcam_parse_store: scratch overflow\n");
		return;
	}

	/* store the data (we skip the iso_frame in error) */
	if (debug&DEBUGINT)
		printk("quickcam_parse_store: %d\n",tot_len);
	ptr=quickcam->scratch+quickcam->scratchlen;
	for(i=0;i<urb->number_of_packets;i++) {
		if(!urb->iso_frame_desc[i].status &&
		    urb->iso_frame_desc[i].actual_length) {
			memcpy(ptr,urb->transfer_buffer+urb->iso_frame_desc[i].offset,
			urb->iso_frame_desc[i].actual_length);
			ptr+=urb->iso_frame_desc[i].actual_length;
			// usbvideo_HexDump(urb->transfer_buffer+urb->iso_frame_desc[i].offset,8);
		}
	}

	/* process the data */
	pframe = NULL;
	if (quickcam->curframe!=-1) {
		if (quickcam->storestate==FRAME_STORE) {
			pframe = &quickcam->frame[quickcam->curframe];
		}
	}
	if (debug&DEBUGINT)
		printk("quickcam_parse_store process data: %d curframe: %d\n",tot_len,quickcam->curframe);
	for(cur_len=0;cur_len+4<=tot_len;) {
		type1=quickcam->scratch[cur_len];
		type2=quickcam->scratch[cur_len+1];
		frag_len=quickcam->scratch[cur_len+2]<<8 | quickcam->scratch[cur_len+3];
		if (debug&DEBUGINT)
			printk("quickcam_parse_store %d %02x %02x\n",frag_len,0xFF&type1, 0xFF&type2);

		if(type1==0x80 || type1==0xC0) {
			if(type2==1 || type2==5) { // a frame begins - see if we can capture it.
				if (pframe) {
					if (pframe->grabstate != FRAME_READY) {
						printk("quickcam: frame %d not free. Skipping...!\n",
							quickcam->curframe);
						 pframe=NULL;
					}
				} else {
					// Check if we were waiting for a start of frame.
					if (quickcam->curframe!=-1) {
						pframe = &quickcam->frame[quickcam->curframe];
						quickcam->storestate=FRAME_STORE;
					}
				}
				cur_len+=4;
				continue;
			} else if(type2==2 || type2==6) { // a frame end
        			if(pframe) {
          				if(pframe->storelength!=framesize && quickcam->sizechanged) {
						/*
						 * That is possible that the size has been changed
                                                 * during the frame capture in this case the next frames will have
						 * the correct size.
						 * The PB100 reacts a stange way it first changes the width:
						 * oldwith * oldheight.
						 * newwith * oldheight.
						 * newwith * newheight. (Strange isn't it?).
						 */
            					printk("quickcam: frame size is the old one? (%ld)\n",
							pframe->storelength);
						cur_len+=4;
						pframe->storelength = 0;
						pframe->grabstate = FRAME_READY;
						continue;
					}
					pframe->grabstate=FRAME_DONE;
          				if(pframe->storelength!=framesize)
            					printk("quickcam: frame size is incorrect! (%ld)\n",
							pframe->storelength);
					else
						quickcam->sizechanged = 0;
          				if(waitqueue_active(&pframe->wq))
						wake_up_interruptible(&pframe->wq);
					/* check if next one could be processed. */
					if (quickcam->frame[(quickcam->curframe + 1) % 2].grabstate == FRAME_READY) {
						quickcam->curframe = (quickcam->curframe + 1) % 2;
						if (debug&DEBUGINT)
							printk("quickcam: marking as success next: %d\n",
								quickcam->curframe);
						pframe = &quickcam->frame[quickcam->curframe];
					} else {
						pframe = NULL;
						if (debug&DEBUGINT)
							printk("quickcam: marking as success stop\n");
						quickcam->curframe = -1;
					}
				}
				cur_len+=4;
				continue;
			}
		} else if(type1==0x02 || type1==0x42) { /* data */
			if (4+frag_len+cur_len>tot_len) break; // not enough data.

			if (debug&DEBUGINT)
				printk("quickcam: storing %d already in %ld\n",frag_len,pframe->storelength);
	
			if(pframe) {
				if (pframe->grabstate==FRAME_READY) {
					pframe->grabstate = FRAME_GRABBING;
					pframe->storelength = 0;
				}
				if(pframe->storelength+frag_len>framesize) {
					if (pframe->storelength!=framesize)
						printk("quickcam: warning -> frame overflow (%ld)\n",
							pframe->storelength+frag_len-framesize);
					if (framesize>pframe->storelength) {
						// The framesize may be smaller as what is already store!.
						memcpy(pframe->storedata+pframe->storelength,
						quickcam->scratch+cur_len+4,
						framesize - pframe->storelength);
					}
					pframe->storelength=framesize;
				} else {
					memcpy(pframe->storedata+pframe->storelength,
					quickcam->scratch+cur_len+4,frag_len);
					pframe->storelength+=frag_len;
				}
			} else {
				quickcam->storestate=FRAME_SKIP; // skip until next frame.
			}
    			cur_len+=4+frag_len;
			continue;
		}
		/*
		 * must be garbage, and probably a big disaster...
		 * Trying to find a start (VSYNC) as done in the old parse_data() could crash the box,
		 * because we are in a interruption routine and we are blocking the box.
		 * so we just dump the first 20 bytes (hopefully we find what to do with it one day,
		 * as it had happened with the other sequences.).
		 * 
		 */
		printk("quickcam got garbage at %ld\n",pframe->storelength);
		left = tot_len - cur_len;
		left = qcmin(20,left);
		usbvideo_HexDump(quickcam->scratch+cur_len,left);
       		if(pframe) {
			pframe->grabstate=FRAME_ERROR;
       			if(waitqueue_active(&pframe->wq))
				wake_up_interruptible(&pframe->wq);
		}
		quickcam->scratchlen = 0;
		return;
	}
	/* save remeaning data, probably there nothing, as Carlo was just ignoring this case. */
	left = tot_len - cur_len;
	if (left>0) {
		if (debug&DEBUGINT)
			printk("quickcam: save remeaning data: %d\n",left);
		memmove(quickcam->scratch, quickcam->scratch+cur_len, left);
		quickcam->scratchlen = left;
	} else
		quickcam->scratchlen = 0;
}

/*
 * Start isoc
 */
static int quickcam_init_isoc(struct usb_quickcam *quickcam)
{
	struct urb *urb;
	int fx, err;

	if (debug&DEBUGLOGIC)
		printk("quickcam_init_isoc\n");

	/* Alternate interface 3 is is the biggest frame size */
	/* JFC use 1: but do not know why */
	if (usb_set_interface(quickcam->dev, quickcam->iface, 1) < 0) {
		printk("usb_set_interface error\n");
		return -EBUSY;
	}

	/* We double buffer the Iso lists */
	urb = usb_alloc_urb(FRAMES_PER_DESC);
	
	if (!urb) {
		printk("quickcam_init_isoc: usb_init_isoc ret %d\n",
			0);
		return -ENOMEM;
	}
        /* first buffer */
	quickcam->sbuf[0].urb = urb;
	urb->dev = quickcam->dev;
	urb->context = quickcam;
	urb->pipe = usb_rcvisocpipe(quickcam->dev, _QUICKCAM_ISOPIPE);
	//printk("quickcam_init_isoc pipesize %d\n", usb_maxpacket (quickcam->dev, urb->pipe, usb_pipeout (urb->pipe)));
	urb->transfer_flags = USB_ISO_ASAP;
	urb->transfer_buffer = quickcam->sbuf[0].data;
 	urb->complete = quickcam_parse_store;
 	urb->number_of_packets = FRAMES_PER_DESC;
 	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);
	if (!urb) {
		printk("quickcam_init_isoc: usb_init_isoc ret %d\n",
			0);
		return -ENOMEM;
	}
        /* second buffer */
	quickcam->sbuf[1].urb = urb;
	urb->dev = quickcam->dev;
	urb->context = quickcam;
	urb->pipe = usb_rcvisocpipe(quickcam->dev, _QUICKCAM_ISOPIPE);
	urb->transfer_flags = USB_ISO_ASAP;
	urb->transfer_buffer = quickcam->sbuf[1].data;
 	urb->complete = quickcam_parse_store;
 	urb->number_of_packets = FRAMES_PER_DESC;
 	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;
	}

	quickcam->sbuf[1].urb->next = quickcam->sbuf[0].urb;
	quickcam->sbuf[0].urb->next = quickcam->sbuf[1].urb;
	
	err = usb_submit_urb(quickcam->sbuf[0].urb);
	if (err)
		printk("quickcam_init_isoc: usb_submit_urb(0) ret %d\n",
			err);
	err = usb_submit_urb(quickcam->sbuf[1].urb);
	if (err)
		printk("quickcam_init_isoc: usb_submit_urb(1) ret %d\n",
			err);

	quickcam->streaming = 1;
	if (debug&DEBUGLOGIC)
		printk("quickcam_init_isoc finished\n");

	return 0;
}
/*
 * Set size of window sensor, note that it is not yet center!
 * The mode is used to allow higher scan rate with smaller images.
 */
static int quickcam_set_size(struct usb_quickcam *quickcam, int width, int height)
{
	if (height > 288 || height<0)
		return -1;
	if (width > 352 || width<0)
		return -1;

	if (quickcam->sensor_ctrl.set_size(quickcam->dev, mode)<0) {
		printk("set_size sensor failed\n");
		return -1;
	}

	/* set the requested size (we should try to center it) */
	if (quickcam->sensor_ctrl.set_window(quickcam->dev, 0, 0,width,height,
            &(quickcam->sensor_ctrl))<0) {
		printk("set_window sensor failed\n");
		return(-1);
	}

	quickcam->vwin.width = width;
	quickcam->vwin.height = height;
	quickcam->sizechanged = -1; // changed!
	return(0);
}

/*
 * Initialise sensor and set shutter
 * The Photobit starts the pixel integration inmendiatly after
 * the reset... That why I moved this out usb_quickcam_configure().
 */

int quickcam_init_sensor(struct usb_quickcam *quickcam)
{
	if (debug&DEBUGBASE)
		printk("quickcam_init_sensor: init sensor\n");
	if (quickcam->sensor_ctrl.init(quickcam->dev,mode,
                                       &rgain, &bgain, &ggain,
                                       &(quickcam->sensor_ctrl))<0) {
		printk("Init sensor failed\n");
		return(-1);
	}
/* gain is the average of blue, green and red gain */
	if(!keepexposure) {
		quickcam->gain =10;
	}
	quickcam->blue =qcmin(255,qcmax(2,bgain));
	quickcam->red  =qcmin(255,qcmax(2,rgain));
	quickcam->green=qcmin(255,qcmax(2,ggain));
	if (usb_quickcam_set_gains(quickcam)<0) {
		printk("set_gains sensor failed\n");
		return(-1);
	}
	if (quickcam->sensor_ctrl.set_shutter(quickcam->dev,quickcam->shutter_val,0x100)<0) {
		printk("set_shutter sensor failed\n");
		return(-1);
	}
	/* Set the size otherwise the read() will fail */
	/*
	 * JFC have to arrange this .... and use the value from the quickcam structure!
	 * NO: It is called in open() ... For the moment...
	 */

	if (quickcam_set_size(quickcam,352,288)<0)
		return(-1);

	return 0;
}

static int quickcam_stop_isoc(struct usb_quickcam *quickcam)
{
	if (!quickcam->dev)
		return -1;
	if (debug&DEBUGLOGIC)
		printk("quickcam_stop_isoc\n");

	/* Turn off continuous grab */
	if (usb_quickcam_stop(quickcam) < 0) {
		printk("usb_quickcam_stop error\n");
		return -EBUSY;
	}

	/* Set packet size to 0 */
	if (usb_set_interface(quickcam->dev, quickcam->iface, 0) < 0) {
		printk("usb_set_interface error\n");
		return -EINVAL;
	}

	quickcam->streaming = 0;

	/* make sure that the next is called */
	if (quickcam->sbuf[1].urb)
		quickcam->sbuf[1].urb->next = NULL;
	if (quickcam->sbuf[0].urb)
		quickcam->sbuf[0].urb->next = NULL;

	/* Unschedule all of the iso td's */
	if (quickcam->sbuf[1].urb) {
		usb_unlink_urb(quickcam->sbuf[1].urb);
		usb_free_urb(quickcam->sbuf[1].urb);
		quickcam->sbuf[1].urb = NULL;
	}
	if (quickcam->sbuf[0].urb) {
		usb_unlink_urb(quickcam->sbuf[0].urb);
		usb_free_urb(quickcam->sbuf[0].urb);
		quickcam->sbuf[0].urb = NULL;
	}
	return 0;
}

/*
 * Start grabing a new frame it must be the one required otherwise the calling
 * process will be lost (it waits for the wrong frame!).
 */
static int quickcam_new_frame(struct usb_quickcam *quickcam, int framenum)
{
	struct quickcam_frame *frame;
	int width, height;
	if (debug&DEBUGLOGIC)
		printk("quickcam_new_frame %d\n",framenum);

	if (!quickcam->dev)
		return -1;
	if (quickcam->grabbing) {
		quickcam->frame[framenum].grabstate = FRAME_READY;
		quickcam->frame[framenum].scanlength = 0;
		if (quickcam->curframe == -1)
			quickcam->curframe = framenum;
		return 0;
	}

        /* If we're not grabbing a frame right now and the other frame is */
        /*  ready to be grabbed into, then use it instead */
        if (quickcam->curframe == -1) {
                if (quickcam->frame[(framenum + 1) % 2].grabstate == FRAME_READY)
                        framenum = (framenum + 1) % 2;
	} else {
		/* previous frame errored, make sure we restart using the other one */
		if (quickcam->scanstate==STATE_ERROR) {
			//printk("grabing but STATE_ERROR (frame:%d)\n",quickcam->curframe);
			/* restart the grabing in the other frame */
			framenum = (quickcam->curframe -1 + QUICKCAM_NUMFRAMES) % QUICKCAM_NUMFRAMES;
		} else {
			if (debug&DEBUGLOGIC)
				printk("quickcam_new_frame %d ending\n",quickcam->curframe);
			return 0;
		}
	}

	frame = &quickcam->frame[framenum];
	width = frame->width;
	height = frame->height;

	/* Make sure it's not too big */
	if (width > 352)
		width = 352;
	width = (width / 8) * 8;	/* Multiple of 8 */

	if (height > 288)
		height = 288;
	height = (height / 4) * 4;	/* Multiple of 4 */
	if (width<8) width=8;
	if (height<4) height=4;

	/* Set the ROI they want, xawtv tries a small piece first time. */ 
	if (quickcam->vwin.height!=height && quickcam->vwin.width!=width) {
		if (quickcam_set_size(quickcam, width, height)<0) {
			if(debug&DEBUGLOGIC)
			    printk("quickcam_new_frame roi failed... EBUSY\n");
			return -EBUSY;
		}
	}

	/* set all the parameters for the irq modules */
	frame->grabstate = FRAME_READY;
	frame->scanlength = 0;		/* accumulated in quickcam_parse_data() */

	quickcam->curframe = framenum;

	/* Grab the frame (will cause the irq's) */
	if (usb_quickcam_upload_frame(quickcam) < 0) {
		if(debug&DEBUGLOGIC)
		    printk("quickcam_upload_frame error\n");
		quickcam->scanstate = STATE_ERROR;
		frame->grabstate = FRAME_UNUSED;
		quickcam->curframe = -1;
		quickcam->grabbing = 0; // not grabbing.
		return -EBUSY;
	}

	if (debug&DEBUGLOGIC)
		printk("quickcam_new_frame %d end success\n",framenum);
	return 0;
}

static int quickcam_init_done(struct video_device *dev)
{
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0)
#if defined(CONFIG_PROC_FS) && defined(CONFIG_VIDEO_PROC_FS)
    create_proc_quickcam((struct usb_quickcam *) dev);
#endif
#endif
    return 0;
}

static int quickcam_get_depth(struct usb_quickcam *quickcam)
{
    switch(quickcam->vpic.palette)
    {
	case VIDEO_PALETTE_RGB32:
	    return 32;

	case VIDEO_PALETTE_RGB24:
	    return 24;    

	case VIDEO_PALETTE_YUV420:
	    return 24;
	
	case VIDEO_PALETTE_YUV422:
	    return 16;    

	case VIDEO_PALETTE_YUYV:
	    return 16;

	case VIDEO_PALETTE_RGB565:
	    return 16;

	case VIDEO_PALETTE_RGB555:
	    return 15;

	default:
	    return -EINVAL;
    }
}

/* Video 4 Linux API */
static int quickcam_open(struct video_device *dev, int flags)
{
	int err = -EBUSY;
	struct usb_quickcam *quickcam = (struct usb_quickcam *)dev;

	if (debug&DEBUGLOGIC)
		printk("quickcam_open\n");
	down(&quickcam->lock);
	if (quickcam->user)
		goto out_unlock;
	
	// Re-Setup internal video_picture
	quickcam->vpic.hue        = (rgain - bgain + 0xFF) << 7;
	quickcam->vpic.colour     = ggain;
	quickcam->vpic.contrast   = 32768;
	quickcam->vpic.brightness = bright;
	quickcam->vpic.whiteness  = 0;
	quickcam->vpic.palette    = VIDEO_PALETTE_RGB24;
	quickcam->vpic.depth      = quickcam_get_depth(quickcam);

	// Re-Setup internal video_window
	quickcam->vwin.x = 0;
	quickcam->vwin.y = 0;
	quickcam->vwin.chromakey = 0;
	quickcam->vwin.flags = 30;		/* 30 fps */

	quickcam->frame[0].grabstate = FRAME_UNUSED;
	quickcam->frame[1].grabstate = FRAME_UNUSED;
	quickcam->streaming = 0;
	quickcam->curframe = -1;
	quickcam->scratchlen = 0;
	quickcam->storestate=FRAME_STORE;

	err = -ENOMEM;

	/* Allocate memory for the frame buffers */
	quickcam->fbuf = rvmalloc(2 * MAX_FRAME_SIZE);
	if (!quickcam->fbuf)
		goto out_unlock;

	quickcam->frame[0].data = quickcam->fbuf;
	quickcam->frame[1].data = quickcam->fbuf + MAX_FRAME_SIZE;

	quickcam->sbuf[0].data = kmalloc (FRAMES_PER_DESC * FRAME_SIZE_PER_DESC, GFP_KERNEL);
	if (!quickcam->sbuf[0].data)
		goto open_err_on0;

	quickcam->sbuf[1].data = kmalloc (FRAMES_PER_DESC * FRAME_SIZE_PER_DESC, GFP_KERNEL);
	if (!quickcam->sbuf[1].data)
		goto open_err_on1;

	quickcam->frame[0].storedata = vmalloc (BAYER_FRAME_SIZE);
	if (!quickcam->frame[0].storedata)
		goto open_err_on2;

	quickcam->frame[1].storedata = vmalloc (BAYER_FRAME_SIZE);
	if (!quickcam->frame[1].storedata)
		goto open_err_on3;

	/*
	 * Set default sizes in case IOCTL (VIDIOCMCAPTURE) is not used
	 * (when using read()).
	 */
	quickcam->frame[0].width = 352;
	quickcam->frame[0].height = 288;
	quickcam->frame[0].bytes_read = 0;
	quickcam->frame[1].width = 352;
	quickcam->frame[1].height = 288;
	quickcam->frame[1].bytes_read = 0;

	/* initialise the USB things */
	err = quickcam_init_isoc(quickcam);
	if (err)
		goto open_err_init;

	/* Start the sensor */
	err = quickcam_init_sensor(quickcam);
	if (err)
		goto open_err_init_sensor;

	quickcam->user++;
	quickcam->grabbing=0; // we are not grabbing.
	quickcam->curframe=-1; // no frame in progress.
	quickcam->readframe = -1; // no frame beeing read

	up(&quickcam->lock);

	MOD_INC_USE_COUNT;

	if (debug&DEBUGLOGIC)
		printk("quickcam_open successfull\n");
	return 0;

open_err_init_sensor:
	quickcam_stop_isoc(quickcam);
open_err_init:
	vfree (quickcam->frame[1].storedata);
open_err_on3:
	vfree (quickcam->frame[0].storedata);
open_err_on2:
	kfree (quickcam->sbuf[1].data);
open_err_on1:
	kfree (quickcam->sbuf[0].data);
open_err_on0:
	rvfree(quickcam->fbuf, 2 * MAX_FRAME_SIZE);

out_unlock:
	up(&quickcam->lock);
	if (debug&DEBUGLOGIC)
		printk("quickcam_open failed\n");
	return err;
}

static void quickcam_close(struct video_device *dev)
{
	struct usb_quickcam *quickcam = (struct usb_quickcam *)dev;

	if (debug&DEBUGLOGIC)
		printk("quickcam_close\n");
	down(&quickcam->lock);	
	quickcam->user--;

	MOD_DEC_USE_COUNT;

	quickcam_stop_isoc(quickcam);
	quickcam->grabbing = 0;    // not grabbing.
	quickcam->readframe = -1;  // no frame beeing read
	quickcam->sizechanged = 0; // frame unchanged!

	rvfree(quickcam->fbuf, 2 * MAX_FRAME_SIZE);

	kfree(quickcam->sbuf[1].data);
	kfree(quickcam->sbuf[0].data);

	vfree(quickcam->frame[1].storedata);
	vfree(quickcam->frame[0].storedata);

	up(&quickcam->lock);

	if (!quickcam->dev) {
		video_unregister_device(&quickcam->vdev);
		kfree(quickcam);
	}
}

static long quickcam_write(struct video_device *dev, const char *buf, unsigned long count, int noblock)
{
	return -EINVAL;
}

static int quickcam_ioctl(struct video_device *dev, unsigned int cmd, void *arg)
{
	struct usb_quickcam *quickcam = (struct usb_quickcam *)dev;
	int retval = 0;

	if (!quickcam->dev)
		return -EIO;
	if (down_interruptible(&quickcam->busy_lock))
		return -EINTR;

	switch (cmd) {
		case VIDIOCGCAP:
		{
			struct video_capability b;

			if (debug&DEBUGIOCTL)
				printk("quickcam_ioctl: VIDIOCGCAP\n");
			strcpy(b.name, "Logitech USB Camera");
			b.type = VID_TYPE_CAPTURE | VID_TYPE_SUBCAPTURE;
			b.channels = 1;
			b.audios = 0;
			b.maxwidth = 352;	/* CIF */
			b.maxheight = 288;	/*  "  */
			b.minwidth = 176;
			b.minheight = 144;

			if (copy_to_user(arg, &b, sizeof(b)))
				retval = -EFAULT;

			break;
		}
		case VIDIOCGCHAN:
		{
			struct video_channel v;

			if (debug&DEBUGIOCTL)
				printk("quickcam_ioctl: VIDIOCGCHAN\n");
			if (copy_from_user(&v, arg, sizeof(v))) {
				retval = -EFAULT;
				break;
			}
			if (v.channel != 0) {
				retval = -EINVAL;
				break;
			}

			v.flags = 0;
			v.tuners = 0;
			v.type = VIDEO_TYPE_CAMERA;
			strcpy(v.name, "Camera");

			if (copy_to_user(arg, &v, sizeof(v)))
				retval = -EFAULT;

			break;
		}
		case VIDIOCSCHAN:
		{
			int v;

			if (copy_from_user(&v, arg, sizeof(v))) {
				retval = -EFAULT;
				break;
			}

			if (debug&DEBUGIOCTL)
				printk("quickcam_ioctl: VIDIOCSCHAN %d\n",v);
			if (v != 0)
				retval = -EINVAL;

			break;
		}
		case VIDIOCGPICT:
		{
			if (debug&DEBUGIOCTL)
				printk("quickcam_ioctl: VIDIOCGPICT\n");

			quickcam->vpic.brightness = quickcam->brightness;
			quickcam->vpic.colour     = quickcam->green<<8;
			quickcam->vpic.contrast   = quickcam->shutter_val<<8;
			quickcam->vpic.hue        = (quickcam->red  - quickcam->blue + 0xff)<<7;
			if (copy_to_user(arg, &quickcam->vpic, sizeof(quickcam->vpic)))
				retval = -EFAULT;
				
			break;
		}
		case VIDIOCSPICT:
		{
			struct video_picture p;

			if (debug&DEBUGIOCTL)
				printk("quickcam_ioctl: VIDIOCSPICT\n");
			if (copy_from_user(&p, arg, sizeof(p)))
				retval = -EFAULT;
			else {
				if(p.brightness != quickcam->vpic.brightness) {
					quickcam->vpic.brightness = p.brightness;
					quickcam->brightness = qcmin(0xfd00,qcmax(0x100,quickcam->vpic.brightness));
				}
				if(p.contrast != quickcam->vpic.contrast) {
					quickcam->vpic.contrast = p.contrast;
					quickcam->shutter_val   = qcmin(0xfd,qcmax(1,quickcam->vpic.contrast>>8));
					quickcam->sensor_ctrl.set_shutter(quickcam->dev,quickcam->shutter_val,0x100);
				}
				if(p.colour != quickcam->vpic.colour) {
					quickcam->vpic.colour = p.colour;
					quickcam->green = qcmin(0xfd,qcmax(1,quickcam->vpic.colour>>8));
					usb_quickcam_set_gains(quickcam);
				}
				if(p.hue != quickcam->vpic.hue) {
					quickcam->vpic.hue = p.hue;
					quickcam->red = qcmin(0xfd,qcmax(1,quickcam->vpic.hue>>8));
					quickcam->blue = 0xff-quickcam->red;
					usb_quickcam_set_gains(quickcam);
				}

				/* xawtv use this to find the supported format */
				if (debug&DEBUGIOCTL)
					printk("quickcam_ioctl: VIDIOCSPICT depth: %d palette %d\n",p.depth, p.palette);
				if (p.depth!=24)
					retval = -EINVAL;
				else {
					retval = IsSupported(p.palette);
					if (!retval)
						quickcam->vpic.palette = p.palette;
				}
			}
			if (debug&DEBUGIOCTL)
				printk("quickcam_ioctl: VIDIOCSPICT return %d\n",retval);
		
			break;
		}
		case VIDIOCSWIN:
		{
			struct video_window vw;

			if (debug&DEBUGIOCTL)
			        printk("quickcam_ioctl: VIDIOCSWIN vw.flags %d vw.clipcount %d\n",vw.flags,vw.clipcount);

			if (copy_from_user(&vw, arg, sizeof(vw))) {
				retval = -EFAULT;
				break;
			}

			/* set the size only if changed */
			if (quickcam->vwin.width != vw.width || quickcam->vwin.height != vw.height) {
				// Set requested size.
				if (quickcam_set_size(quickcam,vw.width,vw.height)<0)
					retval =  -EINVAL;
			}
			if (debug&DEBUGIOCTL)
				printk("quickcam_ioctl: VIDIOCSWIN returns %d\n",retval);
			break;
		}
		case VIDIOCGWIN:
		{
			if (debug&DEBUGIOCTL)
				printk("quickcam_ioctl: VIDIOCGWIN\n");

			if (copy_to_user(arg, &quickcam->vwin, sizeof(quickcam->vwin)))
				retval = -EFAULT;

			break;
		}
		case VIDIOCGMBUF:
		{
			struct video_mbuf vm;

			if (debug&DEBUGIOCTL)
				printk("quickcam_ioctl: VIDIOCGMBUF\n");
			memset(&vm, 0, sizeof(vm));
			vm.size = MAX_FRAME_SIZE * 2;
			vm.frames = 2;
			vm.offsets[0] = 0;
			vm.offsets[1] = MAX_FRAME_SIZE;

			if (copy_to_user((void *)arg, (void *)&vm, sizeof(vm)))
				retval = -EFAULT;

			break;
		}
		case VIDIOCMCAPTURE:
		{
			struct video_mmap vm;

			if (debug&DEBUGIOCTL)
				printk("quickcam_ioctl: VIDIOCMCAPTURE\n");
			if (copy_from_user((void *)&vm, (void *)arg, sizeof(vm))) {
				retval = -EFAULT;
				break;
			}

			if (debug&DEBUGIOCTL)
				printk("quickcam_ioctl: frame: %d, size: %dx%d, format: %d\n",
					vm.frame, vm.width, vm.height, vm.format);

			/* check supported formats */
			retval = IsSupported(vm.format);
			if (retval)
				break; /* format not supported */
			quickcam->vpic.palette = vm.format;

			if ((vm.frame != 0) && (vm.frame != 1)) {
				retval = -EINVAL;
				break;
			}

			if (quickcam->frame[vm.frame].grabstate == FRAME_GRABBING) {
				//printk("frame: %d, FRAME_GRABBING\n", vm.frame);
				retval = -EBUSY;
				break;
			}

			quickcam->frame[vm.frame].width = vm.width;
			quickcam->frame[vm.frame].height = vm.height;

			/* Mark it as ready */
			quickcam->frame[vm.frame].grabstate = FRAME_READY;

			retval = quickcam_new_frame(quickcam, vm.frame);
			break;
		}
		case VIDIOCSYNC:
		{
			int frame,retry;

			if (copy_from_user((void *)&frame, arg, sizeof(int))) {
				retval = -EFAULT;
				break;
			}

			if (debug&DEBUGIOCTL)
				printk("quickcam_ioctl: VIDIOCSYNC for %d (%d)\n", frame,quickcam->frame[frame].grabstate);
			if (debug&DEBUGIOCTL)
				printk("quickcam_ioctl: VIDIOCSYNC to frame %d\n", frame);

			for (retry=0;retry<3;retry++) {
				if (!quickcam->dev) {
					retval = -EIO;
					break;
				}

				/* Check that CAPTURE was done for this frame */
				if (quickcam->frame[frame].grabstate==FRAME_UNUSED) {
					retval = -EINVAL;
					break;
				}
				/* if error we retry */
				else if (quickcam->frame[frame].grabstate==FRAME_ERROR) {
					if (debug&DEBUGIOCTL)
						printk("quickcam_ioctl: error retrying!\n");
/*
					if (usb_quickcam_set1(quickcam->dev, STV_ISO_ENABLE, 0)<0) {
						printk("usb_quickcam_set1 failed!\n");
						retval = -EBUSY;
						break;
					}
 */
					quickcam->scratchlen = 0; // discard buffer.

					if ((retval = usb_quickcam_upload_frame(quickcam)) < 0)
						break;
					quickcam->frame[frame].grabstate = FRAME_GRABBING;
					quickcam->frame[frame].scanlength = 0;
				}
				else if (quickcam->frame[frame].grabstate==FRAME_DONE) {
					break;
				}
				else {
					do {
						interruptible_sleep_on(&quickcam->frame[frame].wq);
						if (signal_pending(current)) {
							if (debug&DEBUGIOCTL)
							    printk("quickcam_ioctl: VIDIOCSYNC alarmed (%d:%d)\n",quickcam->frame[frame].grabstate,quickcam->scanstate);
							/* try JFC */
							quickcam->frame[frame].grabstate = FRAME_ERROR;
							quickcam->scanstate = STATE_ERROR;
							quickcam->scratchlen = 0;
							quickcam->grabbing = 0; // I am looking another to detect it.
							retval = -EINTR;
							break;
						}
					} while (quickcam->frame[frame].grabstate == FRAME_GRABBING ||
						 quickcam->frame[frame].grabstate == FRAME_READY);
				if (retval) break;
				}
			}

			/* too many errors, return error */
			if (retry==3 && quickcam->frame[frame].grabstate == FRAME_ERROR) {
				retval = -EBUSY;
				break;
			}
			/*
			 * stop isostream if we have all what we need.
 			 */
			if (quickcam->curframe==-1) {
				usb_quickcam_stop(quickcam);
				quickcam->grabbing = 0;
			}

			/* Convert the Bayer to RBG24 and set gains & exposure */
			quickcam_parse_data(quickcam,frame);

			if (quickcam->vpic.palette!=VIDEO_PALETTE_RGB24)
				quickcam_convert_image(&quickcam->frame[frame],quickcam->vpic.palette);

			if (debug&DEBUGIOCTL)
				printk("quickcam_ioctl: VIDIOCSYNC got %d (%d) RC:%d)\n", frame,quickcam->frame[frame].grabstate,retval);
			quickcam->frame[frame].grabstate = FRAME_UNUSED;

			break;
		}
		case VIDIOCGFBUF:
		{
			struct video_buffer vb;

			if (debug&DEBUGIOCTL)
				printk("quickcam_ioctl: VIDIOCGFBUF\n");
			memset(&vb, 0, sizeof(vb));
			vb.base = NULL;	/* frame buffer not supported, not used */

			if (copy_to_user((void *)arg, (void *)&vb, sizeof(vb)))
				retval = -EFAULT;

 			break;
 		}
		case VIDIOCKEY:
			break;
		case VIDIOCCAPTURE:
			retval = -EINVAL;
			break;
		case VIDIOCSFBUF:
			retval = -EINVAL;
			break;
		case VIDIOCGTUNER:
		case VIDIOCSTUNER:
			retval = -EINVAL;
			break;
		case VIDIOCGFREQ:
		case VIDIOCSFREQ:
			retval = -EINVAL;
			break;
		case VIDIOCGAUDIO:
		case VIDIOCSAUDIO:
			retval = -EINVAL;
			break;
		default:
			retval = -ENOIOCTLCMD;
			break;
	}
	up(&quickcam->busy_lock);
	return retval;
}

static long quickcam_read(struct video_device *dev, char *buf, unsigned long count, int noblock)
{
	struct usb_quickcam *quickcam = (struct usb_quickcam *)dev;
	int frmx = -1;
	struct quickcam_frame *frame;
	int retry=0;

	if (debug&DEBUGREAD)
		printk("quickcam_read: %ld bytes, noblock=%d\n", count, noblock);

	if (!dev || !buf)
	{
		if (debug&DEBUGREAD)
		    printk("quickcam_read: no video_device available or no buffer attached :( EFAULT\n");
		return -EFAULT;
	}

	if (!quickcam->dev)
	{
		if (debug&DEBUGREAD)
		    printk("quickcam_read: no quickcam_device available :( EIO\n");
		return -EIO;
	}

	if (down_interruptible(&quickcam->busy_lock))
	{
	    if (debug&DEBUGREAD)
		printk("quickcam_read: quickcam_device busy :( EINTR\n");
		return -EINTR;
	}

	/* check if we have already started to read */
	if (quickcam->readframe!=-1) {
		frmx = quickcam->readframe; // well I could have put a goto here...
		frame = &quickcam->frame[frmx];
		if (debug&DEBUGREAD)
		    printk("quickcam_read: we didn't start reading yet...start now\n");
		goto read; // damned I have done it...
	}

	/* See if a frame is completed, then use it. */
	if (frmx == - 1) {
		if (debug&DEBUGREAD)
		    printk("quickcam_read: check if a frame is already completed... ");
		if (quickcam->frame[0].grabstate >= FRAME_DONE)	/* _DONE or _ERROR */
			frmx = 0;
		else if (quickcam->frame[1].grabstate >= FRAME_DONE)/* _DONE or _ERROR */
			frmx = 1;
			
		if (debug&DEBUGREAD) { /* Not mine jfclere */
			if (frmx == -1)
				printk("NO\n");
			else
				printk("YES\n");
		}
	}

	if (noblock && (frmx == -1)) {
		if (debug&DEBUGREAD)
		    printk("quickcam_read: blocked device :( EAGAIN\n");
		up(&quickcam->busy_lock);
		return -EAGAIN;
	}

	/* If no FRAME_DONE, look for a FRAME_GRABBING state. */
	/* See if a frame is in process (grabbing), then use it. */
	if (frmx == -1) {
		if (debug&DEBUGREAD)
		    printk("quickcam_read: checking wheter something is grabbing at the moment... ");
		if (quickcam->frame[0].grabstate == FRAME_GRABBING)
			frmx = 0;
		else if (quickcam->frame[1].grabstate == FRAME_GRABBING)
			frmx = 1;
			
		if (debug&DEBUGREAD) { /* Not mine jfclere */
			if (frmx == -1)
				printk("NO\n");
			else
				printk("YES\n");
		}
	}

	/* If no frame is active, start one. */
	if (frmx == -1) {
		if (debug&DEBUGREAD)
		    printk("quickcam_read: no active frames....start new one\n");
		/* grabbing or not grabbing */
		if (!quickcam->grabbing || quickcam->curframe==-1) {
			if (debug&DEBUGREAD)
			    printk("quickcam_read: we are not grabbing....start it\n");
			quickcam_new_frame(quickcam, 0);
			quickcam_new_frame(quickcam, 1); /* allow double buffering */
		}
		if (debug&DEBUGREAD)
		    printk("quickcam_read: waiting for the frame to finish...\n");
		frmx = quickcam->curframe; /* we have to wait for the one in progress */
	}

	if (frmx == -1)
		frmx = 0;

	frame = &quickcam->frame[frmx];
	if (debug&DEBUGREAD)
		printk("quickcam_read: using %d state %d\n", frmx, frame->grabstate);

restart:
	if (debug&DEBUGREAD)
	    printk("quickcam_read: goto restart called\n");

	if (!quickcam->dev) {
		if (debug&DEBUGREAD)
		    printk("quickcam_read: no quickcam_device available :( EIO\n");
		up(&quickcam->busy_lock);
		return -EIO;
	}

	while (frame->grabstate == FRAME_READY ||
	       frame->grabstate == FRAME_GRABBING) {
	        //if (debug&DEBUGREAD)
	    	    //printk("quickcam_read: in FRAME_READY || FRAME_GRABBING loop ...waiting\n");
		interruptible_sleep_on(&frame->wq);
		if (signal_pending(current)) {
			if (debug&DEBUGREAD)
			    printk("quickcam_read: aborting... :( EINTR\n");

			up(&quickcam->busy_lock);
			return -EINTR;
		}
	}

	if (frame->grabstate != FRAME_DONE) {
		frame->bytes_read = 0;
		if (debug&DEBUGREAD)
		    printk("quickcam_read: errored frame %d\n", quickcam->curframe);
		if (quickcam_new_frame(quickcam, frmx))
	{			if (debug&DEBUGREAD)
			    printk("quickcam_read: quickcam_new_frame error\n");
		}
		retry++;
		if (retry<=3)
			goto restart;
		up(&quickcam->busy_lock);
		return -EINTR;
	}

	/* Convert the Bayer to RBG24 and set gains & exposure */
	quickcam_parse_data(quickcam,frmx); 
	if (quickcam->vpic.palette!=VIDEO_PALETTE_RGB24)
		quickcam_convert_image(&quickcam->frame[frmx],quickcam->vpic.palette);

read:
	if (debug&DEBUGREAD)
		printk("quickcam_read: frmx=%d, bytes_read=%ld, scanlength=%ld\n",
			frmx, frame->bytes_read, frame->scanlength);

	/* copy bytes to user space; we allow for partials reads */
	quickcam->readframe = frmx;
	if ((count + frame->bytes_read) > frame->scanlength)
		count = frame->scanlength - frame->bytes_read;


	if(copy_to_user(buf, frame->data + frame->bytes_read, count)) {
		up(&quickcam->busy_lock);
		return -EFAULT;
	}

	frame->bytes_read += count;
	if (debug&DEBUGREAD)
		printk("quickcam_read: {copy} count used=%ld, new bytes_read=%ld\n",
			count, frame->bytes_read);

	if (frame->bytes_read >= frame->scanlength) { /* All data has been read */
		frame->bytes_read = 0;
		 quickcam->readframe = -1; // we have finished.

		/* Mark it as available to be used again. */
		quickcam->frame[frmx].grabstate = FRAME_UNUSED;
		if (quickcam_new_frame(quickcam, frmx))
			printk(KERN_ERR "quickcam_read: quickcam_new_frame returned error\n");
	}

	up(&quickcam->busy_lock);
	return count;
}

static int quickcam_mmap(struct video_device *dev, const char *adr, unsigned long size)
{
	struct usb_quickcam *quickcam = (struct usb_quickcam *)dev;
	unsigned long start = (unsigned long)adr;
	unsigned long page, pos;

	if (!quickcam->dev)
		return -EIO;

	if (size > (((2 * MAX_FRAME_SIZE) + PAGE_SIZE - 1) & ~(PAGE_SIZE - 1)))
		return -EINVAL;
	if (down_interruptible(&quickcam->busy_lock))
		return -EINTR;

	pos = (unsigned long)quickcam->fbuf;
	while (size > 0) {
		page = kvirt_to_pa(pos);
		if (remap_page_range(start, page, PAGE_SIZE, PAGE_SHARED)) {
			up(&quickcam->busy_lock);
			return -EAGAIN;
		}

		start += PAGE_SIZE;
		pos += PAGE_SIZE;
		if (size > PAGE_SIZE)
			size -= PAGE_SIZE;
		else
			size = 0;
	}
	
	up(&quickcam->busy_lock);
	return 0;
}

static struct video_device quickcam_template = {
name:		"Logitech Quickcam Express USB",
type:		VID_TYPE_CAPTURE,
hardware:	VID_HARDWARE_QCE,
initialize:     quickcam_init_done,
open:		quickcam_open,
close:		quickcam_close,
read:		quickcam_read,
write:		quickcam_write,
ioctl:		quickcam_ioctl,
mmap:		quickcam_mmap,
};

static int usb_quickcam_configure(struct usb_quickcam *quickcam)
{
	struct usb_device *dev = quickcam->dev;
	unsigned char id[2];
	struct sensor_data *sensor;

	/* Set altsetting 0 */
	if (usb_set_interface(dev, quickcam->iface, 0) < 0) {
		printk("usb_set_interface error\n");
		return -EBUSY;
	}
	if (usb_quickcam_set1(dev, STV_ISO_ENABLE, 0) < 0) {
		printk("usb_quickcam_set1 STV_ISO_ENABLE(0) failed\n");
		return -EBUSY;
	}

	/* Probe for the sensor type. */
	for (sensor = sensors; sensor->name; sensor++) {
		if (usb_quickcam_set1(dev, STV_REG23, sensor->reg23) < 0) {
			printk("usb_quickcam_set1 STV_REG23 failed\n");
			return -EBUSY;
		}
		if (usb_quickcam_get_i2c(dev, sensor->i2c_addr, sensor->id_reg, id, sensor->length_id) < 0) {
			printk("usb_quickcam_get_i2c error\n");
			return -EBUSY;
		}
		printk("quickcam: probe of %s sensor = %02x %02x id: %02x\n", sensor->name, id[0], id[1],sensor->id);
		if (id[sensor->length_id-1] == sensor->id)
			break;
	}
	if (!sensor->name) {
		printk("quickcam: unsupported sensor\n");
		return -EBUSY;
	}
	printk("quickcam: %s sensor detected\n", sensor->name);
	quickcam->sensor_name = sensor->name;
	quickcam->sensor_addr = sensor->i2c_addr;
	sensor->load(&quickcam->sensor_ctrl);

	memcpy(&quickcam->vdev, &quickcam_template, sizeof(quickcam_template));

	init_waitqueue_head(&quickcam->frame[0].wq);
	init_waitqueue_head(&quickcam->frame[1].wq);

#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,5)
	if (video_register_device(&quickcam->vdev, VFL_TYPE_GRABBER, video_nr) == -1)
#else
	if (video_register_device(&quickcam->vdev, VFL_TYPE_GRABBER) == -1)
#endif
	{
		printk("video_register_device failed\n");
		return -EBUSY;
	}

	// Disable data stream.
	if (usb_quickcam_set1(dev, STV_ISO_ENABLE, 0) < 0) {
		printk("usb_quickcam_set1 STV_ISO_ENABLE(1) failed\n");
		goto error;
	}
        if (usb_quickcam_set1(dev, STV_REG23, 1) < 0) {
		/* I see no reason that the others failed if this one is OK */
		printk("usb_quickcam_set1 STV_REG23(1) failed\n");
		goto error;
	}
	

	/* set default values for the camera */
	if(bright<=0 || bright>=65535) bright=EXPO_VAL;
	quickcam->brightness = bright;
	quickcam->shutter_val= SHUTTER_VAL;
	quickcam->scanstate  = STATE_OK;
	quickcam->x          = quickcam->yy=0;
	quickcam->readframe  = -1;

	// Setup internal video_picture
	quickcam->vpic.hue        = (rgain -bgain +0xFF) << 7;
	quickcam->vpic.colour     = ggain;
	quickcam->vpic.contrast   = 32768;
	quickcam->vpic.brightness = bright;
	quickcam->vpic.whiteness  = 0;
	quickcam->vpic.palette    = VIDEO_PALETTE_RGB24;
	quickcam->vpic.depth      = quickcam_get_depth(quickcam);

	// Setup internal video_window
	quickcam->vwin.x = 0;
	quickcam->vwin.y = 0;
	quickcam->vwin.chromakey = 0;
	quickcam->vwin.flags = 30;		/* 30 fps */

	return 0;

error:
	video_unregister_device(&quickcam->vdev);
	usb_driver_release_interface(&quickcam_driver,
		&dev->actconfig->interface[0]);
	return -EBUSY;
}

#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0)
static void *quickcam_probe(struct usb_device *dev,
unsigned int ifnum, const struct usb_device_id *id)
#else
static void *quickcam_probe(struct usb_device *dev,
unsigned int ifnum)
#endif
{
	struct usb_interface_descriptor *interface;
	struct usb_quickcam *quickcam;

	/* We don't handle multi-config cameras */
	if (dev->descriptor.bNumConfigurations != 1)
		return NULL;

	interface = &dev->actconfig->interface[ifnum].altsetting[0];

	/* Is it a Quickcam? */
        printk("USB Quickcam Class %x SubClass %x idVendor %x idProduct %x\n",
                interface->bInterfaceClass, interface->bInterfaceSubClass,
                dev->descriptor.idVendor, dev->descriptor.idProduct);
	
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,2,255)
	if (dev->descriptor.idVendor != 0x046d)
		return NULL;
	if (dev->descriptor.idProduct != 0x0840 && dev->descriptor.idProduct != 0x0870)
		return NULL;

#endif
	/*
         * Checking vendor/product is not enough
         * In case on Quickcam Web the audio is at class 1 and subclass 1/2.
         * ono /dev/dsp and one /dev/mixer
         */
	if (interface->bInterfaceClass != 0xFF)
		return NULL;
	if (interface->bInterfaceSubClass != 0xFF)
		return NULL;

	/* We found a Quickcam */
	printk("USB Quickcam camera found using: %s\n", VERSION);

	if ((quickcam = kmalloc(sizeof(*quickcam), GFP_KERNEL)) == NULL) {
		printk("couldn't kmalloc quickcam struct\n");
		return NULL;
	}

	memset(quickcam, 0, sizeof(*quickcam));

	quickcam->dev = dev;
	quickcam->iface = interface->bInterfaceNumber;

	if (!usb_quickcam_configure(quickcam)) {
		quickcam->user=0;
		init_MUTEX(&quickcam->lock);	/* to 1 == available */
		init_MUTEX(&quickcam->busy_lock);	/* to 1 == available */

		return quickcam;
	} else {
		kfree(quickcam);
		return NULL;
	}
}

static void quickcam_disconnect(struct usb_device *dev, void *ptr)
{
	struct usb_quickcam *quickcam = (struct usb_quickcam *) ptr;
	if (!quickcam)
		return;

	/* We don't want people trying to open up the device */
	if (!quickcam->user)
		video_unregister_device(&quickcam->vdev);

	usb_driver_release_interface(&quickcam_driver,
		&quickcam->dev->actconfig->interface[0]);

	quickcam->dev = NULL;
	
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0)
#if defined(CONFIG_PROC_FS) && defined(CONFIG_VIDEO_PROC_FS)
	destroy_proc_quickcam(quickcam);
#endif
#endif
	
	quickcam->frame[0].grabstate = FRAME_ERROR;
	quickcam->frame[1].grabstate = FRAME_ERROR;
	quickcam->curframe = -1;

	/* This will cause the process to request another frame. */
	if (waitqueue_active(&quickcam->frame[0].wq))
		wake_up_interruptible(&quickcam->frame[0].wq);

	if (waitqueue_active(&quickcam->frame[1].wq))
		wake_up_interruptible(&quickcam->frame[1].wq);

	quickcam->streaming = 0;

	/* Unschedule all of the iso td's */
	if (quickcam->sbuf[1].urb) {
		quickcam->sbuf[1].urb->next = NULL;
		usb_unlink_urb(quickcam->sbuf[1].urb);
		usb_free_urb(quickcam->sbuf[1].urb);
		quickcam->sbuf[1].urb = NULL;
	}
	if (quickcam->sbuf[0].urb) {
		quickcam->sbuf[0].urb->next = NULL;
		usb_unlink_urb(quickcam->sbuf[0].urb);
		usb_free_urb(quickcam->sbuf[0].urb);
		quickcam->sbuf[0].urb = NULL;
	}

	/* Free the memory */
	if (!quickcam->user)
		kfree(quickcam);
}

static struct usb_driver quickcam_driver = {
name:		"quickcam",
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0)
id_table:	device_table,
#endif
probe:		quickcam_probe,
disconnect:	quickcam_disconnect,
};

static int __init usb_quickcam_init(void)
{
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0)
#if defined(CONFIG_PROC_FS) && defined(CONFIG_VIDEO_PROC_FS)
	proc_quickcam_create();
#endif
#endif
	return usb_register(&quickcam_driver);
}

static void __exit usb_quickcam_cleanup(void)
{
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0)
#if defined(CONFIG_PROC_FS) && defined(CONFIG_VIDEO_PROC_FS)
	proc_quickcam_destroy();
#endif
#endif
	usb_deregister(&quickcam_driver);
}

module_init(usb_quickcam_init);
module_exit(usb_quickcam_cleanup);
