This module does not work as is from the new kernel.

In attachment you have a piece of the source code and the README file with 
the email address of the programmers plus the details...

It works now with my Quickcam Express USB!

Regards,

Marc
#ifndef __LINUX_QUICKCAM_H
#define __LINUX_QUICKCAM_H

#include <linux/list.h>
#include <linux/usb.h>
#include <linux/videodev.h>

#define _QUICKCAM_ISOPIPE 	0x81
#define SHUTTER_VAL 		0x80
#define EXPO_VAL 		0xa0
#define RGAIN_DEF		0x80
#define BGAIN_DEF		0x80
#define GGAIN_DEF		0x80

// define debug levels
#define DEBUGBASE      		1
#define DEBUGLOGIC      	2
#define DEBUGDATA       	4
#define DEBUGREAD       	8
#define DEBUGIOCTL     		16
#define DEBUGINT		32

#define VID_HARDWARE_QCE 	50

// Control register of the STV0600 ASIC
#define STV_ISO_ENABLE 		0x1440
#define STV_SCAN_RATE  		0x1443

#define STV_ISO_SIZE 		0x15c1
#define STV_Y_CTRL   		0x15c3
#define STV_X_CTRL   		0x1680

#define STV_REG00 		0x1500
#define STV_REG01 		0x1501
#define STV_REG02 		0x1502
#define STV_REG03 		0x1503
#define STV_REG04 		0x1504

#define STV_REG23 		0x0423

#define STREAM_BUF_SIZE		(PAGE_SIZE * 4)

#define SCRATCH_BUF_SIZE	(STREAM_BUF_SIZE * 2)

#define FRAMES_PER_DESC		10
#define FRAME_SIZE_PER_DESC	1023	// Shouldn't be hardcoded JFC was 960
// 356*292 from VV6410 352*288 otherwise.
// max size of frame received from the camera
#define BAYER_FRAME_SIZE	103952

#define QUICKCAM_NUMFRAMES	2
#define QUICKCAM_NUMSBUF	2

/* scanstate */
enum {
	STATE_OK,		// Ok.
	STATE_ERROR,		// An error has been detected.
};

/* store state, that to know what to do with the frame */
enum {
	FRAME_STORE,		// Ok.
	FRAME_SKIP,		// we have skipped data the frame is partial.
};

/* grabstate (of the frame). */
enum {
	FRAME_UNUSED,		/* Unused (no MCAPTURE) */
	FRAME_READY,		/* Ready to start grabbing */
	FRAME_GRABBING,		/* In the process of being grabbed into */
	FRAME_DONE,		/* Finished grabbing, but not been synced yet */
	FRAME_ERROR,		/* Something bad happened while processing */
};


struct usb_device;

struct quickcam_sbuf {
	char *data;
	urb_t *urb;
};

struct palette_list {
     int num;
     char *name;
     int supported;
};

/*
 * Structure filled in for each of the types of sensor (HDCS, PB0100)
 */
struct sensorctrl {
  int (*init) (struct usb_device * dev, int mode,
               int *rgain, int *bgain, int *ggain,
               struct sensorctrl *sensor_ctrl);
  int (*set_shutter) (struct usb_device * dev, int sval, int xval);
  int (*set_gains) (struct usb_device * dev, int rgain, int bgain, int ggain);
  int (*set_window) (struct usb_device * dev, int x, int y, int w, int h,
                     struct sensorctrl *sensor_ctrl);
  int (*set_size) (struct usb_device * dev, int mode);
  int (*start) (struct usb_device * dev, struct sensorctrl *sensor_ctrl);
  int (*stop) (struct usb_device * dev, struct sensorctrl *sensor_ctrl);
  // size delivered by the sensor.
  int width;
  int height;
  // mode: 0 full; 1 half; 2 quater.
  int mode;
};

/**
 * Structure storing the I2C messages.
 * Do use it:
 * usb_quickcam_i2c_new reset the structure.
 * usb_quickcam_i2c_add add register and value.
 * usb_quickcam_i2c_send send it to the sensor. It call a usb_quickcam_i2c_new.
 */
struct quickcam_i2c {
	int length;
	unsigned char buff[35];
};

struct quickcam_frame {

	char *data;		/* Frame buffer */
	long scanlength;	/* uncompressed, raw data length of frame */

	char *storedata;	/* Bayer data */
	long storelength;	/* len of received data */

 	int width;		/* Width application is expecting */
	int height;		/* Height */

//	int hdrwidth;		/* Width the frame actually is */
//	int hdrheight;		/* Height */

	volatile int grabstate;	/* State of grabbing */

	long bytes_read;	/* amount of scanlength that has been read from *data */

	wait_queue_head_t wq;	/* Processes waiting */
};

struct usb_quickcam {
	struct video_device vdev;

        struct video_picture vpic;
	struct video_window vwin;

	/* Device structure */
	struct usb_device *dev;

        /* For /proc interface */
	struct proc_dir_entry *proc_entry;

	unsigned char iface;

	struct semaphore lock;
	int user;		/* user count for exclusive use */

	int streaming;		/* Are we streaming Isochronous? */
	int grabbing;		/* Are we grabbing? */
	int readframe;		/* the frame we are reading. */

	int scanstate;		/* state of the automaton */

	int storestate;		/* indicated that we have to wait for the end */

	int sizechanged;	/* indicated size changes */

	int yy;
	int x;

	int shutter_val;
	int gain; /* global gain */
	int val; /* global exposure */

	int blue; /* blue gain */
	int red;  /* red gain  */
	int green;/* green gain  */
	int brightness;/* Control brightness of image through V4L */

	char *sensor_name;	/* for /proc */
	unsigned char sensor_addr; /* hdcs and photobit have different addr */

	char *fbuf;		/* Videodev buffer area */

	int curframe;
	struct quickcam_frame frame[QUICKCAM_NUMFRAMES];	/* Double buffering */

	struct quickcam_sbuf sbuf[QUICKCAM_NUMSBUF];		/* Double buffering */

	/* Scratch space from the Isochronous pipe */
	unsigned char scratch[SCRATCH_BUF_SIZE];
	int scratchlen;

	struct semaphore busy_lock;     /* guard against SMP multithreading */

	struct sensorctrl sensor_ctrl;  /* specific routines for a sensor */

};

/* sensor informations (input about all supported sensors) */

struct sensor_data {
	char	*name;
	int	reg23;
	unsigned char	i2c_addr;
	int	id_reg;
	unsigned char	id;
	int	length_id;
	void	(*load)(struct sensorctrl *);
}; 

/* Add prototyping to prevent warnings */

/* Sensor initialisation */
void load_hdcs_mod(struct sensorctrl *sensor_ctrl);
void load_hdcs20_mod(struct sensorctrl *sensor_ctrl);
void load_pb0100_mod(struct sensorctrl *sensor_ctrl);
void load_vv6410_mod(struct sensorctrl *sensor_ctrl);

/* Sensor commmon routines */
void usb_quickcam_i2c_new(struct quickcam_i2c *i2cbuff);
void usb_quickcam_i2c_add(struct quickcam_i2c *i2cbuff,unsigned char reg,
        unsigned char value);
void usb_quickcam_i2c_add2(struct quickcam_i2c *i2cbuff,unsigned char reg,
        unsigned short value);
int usb_quickcam_i2c_send(struct usb_device *dev, struct quickcam_i2c *i2cbuff,
        unsigned char sensor_add);

/* USB interface chip control */
int usb_quickcam_set1(struct usb_device *dev, short reg, char val);
int usb_quickcam_set2(struct usb_device *dev, short reg, short val);

/* conversion routines */
int IsSupported(int format);
void quickcam_convert_image(struct quickcam_frame *frame, int format);
char *FormatName(int format);

#endif
 USB Quickcam Video Camera driver
 
 Supports Logitech Quickcam Express
 
 Quickcam Team:
         Lead developer
           Jean-Freceric Clere <[EMAIL PROTECTED]>
           Nikolas Zimmermann <[EMAIL PROTECTED]>
  Quickcam Revers-Enginnering:
           Georg Acher <[EMAIL PROTECTED]>
  Photobit Support:
            Mark Cave-Ayland <[EMAIL PROTECTED]>
 
  Would also like to thank the following contributors:
           Carlo E Prelz, Rogier Wolff, Samuel Linclau, Matthew Denner,
           and any body else whom we have failed to mention.
  Special thanks for the help for RB24 to YUV422 conversion:
            Aron Rosenberg <[EMAIL PROTECTED]>
 
 
   Thank the guys, who wrote the cpia driver (P.Prengler,S.Bertin,J.Erdfelt)

Lastly sourceforge for hosting the site
Check http://qce-ga.sourceforge.net for updates/news/mailing lists

Copyrights Major details  at the beginning of quickcam.c, additional
copyrighted material yuv.c(JF.Clere) and testquickcam.c(N.Zimmermann) see
the beginnings of those files for detail.

Logitech Quickcam Express USB Driver
------------------------------------

1. How to compile?
Just use a plain "make" to compile the driver.

2a. The easiest way to load modules is to use the quickcam.sh shell script
To run it type ./quickcam.sh (If you have compiled USB and/or V4L support into
Kernel rather than as modules you should use method 2b)

2b. How to use with insmod?
Typing "insmod mod_quickcam.o" should do the trick.

3.  What are qce-ga's features?
 * 2.2 + 2.4 Support
 * Video4Linux compatible driver
 * ProcFS Support (+ VideoProcFS)
 * RGB24 and YUV* support
 * full mmap() and read() support
 * ....

4. What are the parameters wich are accepted by mod_quickcam.o?
Easily find it out with "modinfo -p mod_quickcam.o"
   insmod mod_quickcam.o debug=n (n=1,2,4,8,16,32 or any ored values).
   insmod mod_quickcam.o rgain=red bgain=blue ggain=green.
   red, blue and green are initial gain values this allows to correct the 
   colour of the images. The default values are rgain=192 bgain=192 ggain=176

Cheers,
 Nikolas Zimmermann <[EMAIL PROTECTED]>
/*
 * qce-ga, linux V4L driver for the Quickcam Express and Dexxa Quickcam
 *
 * Copyright (C) 2001  Jean-Fredric Clere,Nikolas Zimmermann, Georg Acher
 * Mark Cave-Ayland, Carlo E Prelz
 *
 * 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

   For any questions ask [EMAIL PROTECTED]!
*/

#include <linux/config.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/sched.h>
#include <linux/list.h>
#include <linux/slab.h>
#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"

#define max(a,b) ((a)>(b)?(a):(b))
#define min(a,b) ((a)<(b)?(a):(b))

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

#ifdef MODULE
#define DEBUGLEVEL 0
MODULE_PARM(debug, "i");
MODULE_PARM(mode, "i");
MODULE_PARM(rgain, "i");
MODULE_PARM(bgain, "i");
MODULE_PARM(ggain, "i");
MODULE_PARM(video_nr,"i");
MODULE_PARM_DESC(debug, "Sets the debug output (1,2,4,8,16,32)");
MODULE_PARM_DESC(mode, "Sets the speed (0-1)");
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(video_nr, "Set videodevice number (/dev/videoX)");
MODULE_SUPPORTED_DEVICE("video");
MODULE_DESCRIPTION("Logitech Quickcam Express Webcam driver");
MODULE_AUTHOR("see README");
MODULE_LICENSE("GPL");
#endif

static int debug = DEBUGLEVEL;

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;

/* 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;

/* Video Size 352 x 288 x 3 bytes for RGB */
#define MAX_FRAME_SIZE (352 * 288 * 3)

#define VERSION "$Id: quickcam.c,v 1.80 2001/06/16 15:25:09 wildfox Exp $"

static struct usb_driver quickcam_driver;

#if LINUX_VERSION_CODE  >= 0x020400
static __devinitdata struct usb_device_id device_table [] = {
        { USB_DEVICE(0x046d, 0x0870) },
        { }
};

MODULE_DEVICE_TABLE (usb, device_table);

#define MAP_NR virt_to_page
#endif
                                          
/*******************************/
/* Memory management functions */
/*******************************/

/* Given PGD from the address space's page table, return the kernel
 * virtual mapping of the physical memory mapped at ADR.
 */
static inline unsigned long uvirt_to_kva(pgd_t *pgd, unsigned long adr)
{
	unsigned long ret = 0UL;
	pmd_t *pmd;
	pte_t *ptep, pte;

	if (!pgd_none(*pgd)) {
		pmd = pmd_offset(pgd, adr);
		if (!pmd_none(*pmd)) {
			ptep = pte_offset(pmd, adr);
			pte = *ptep;
			if (pte_present(pte)) {
				ret = (unsigned long) page_address(pte_page(pte));
				ret |= (adr & (PAGE_SIZE-1));
			}
		}
	}
	return ret;
}

static inline unsigned long uvirt_to_bus(unsigned long adr)
{
	unsigned long kva, ret;

	kva = uvirt_to_kva(pgd_offset(current->mm, adr), adr);
	ret = virt_to_bus((void *)kva);
	return ret;
}

static inline unsigned long kvirt_to_bus(unsigned long adr)
{
	unsigned long va, kva, ret;

	va = VMALLOC_VMADDR(adr);
	kva = uvirt_to_kva(pgd_offset_k(va), va);
	ret = virt_to_bus((void *)kva);
	return ret;
}

/* Here we want the physical address of the memory.
 * This is used when initializing the contents of the
 * area and marking the pages as reserved.
 */
static inline unsigned long kvirt_to_pa(unsigned long adr)
{
	unsigned long va, kva, ret;

	va = VMALLOC_VMADDR(adr);
	kva = uvirt_to_kva(pgd_offset_k(va), va);
	ret = __pa(kva);
	return ret;
}

static void *rvmalloc(unsigned long size)
{
	void *mem;
	unsigned long adr, page;

	/* Round it off to PAGE_SIZE */
	size += (PAGE_SIZE - 1);
	size &= ~(PAGE_SIZE - 1);

	mem = vmalloc(size);
	if (!mem)
		return NULL;

	memset(mem, 0, size); /* Clear the ram out, no junk to the user */
	adr = (unsigned long) mem;
	while (size > 0) {
		page = kvirt_to_pa(adr);
		mem_map_reserve(MAP_NR(__va(page)));
		adr += PAGE_SIZE;
		if (size > PAGE_SIZE)
			size -= PAGE_SIZE;
		else
			size = 0;
	}

	return mem;
}

static void rvfree(void *mem, unsigned long size)
{
	unsigned long adr, page;

	if (!mem)
		return;

	size += (PAGE_SIZE - 1);
	size &= ~(PAGE_SIZE - 1);

	adr=(unsigned long) mem;
	while (size > 0) {
		page = kvirt_to_pa(adr);
		mem_map_unreserve(MAP_NR(__va(page)));
		adr += PAGE_SIZE;
		if (size > PAGE_SIZE)
			size -= PAGE_SIZE;
		else
			size = 0;
	}
	vfree(mem);
}
/*
 * 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 >= 0x020400
#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", CHOOSE(HDCS_ADDR, dev->sensor_addr, "HDCS", "Photobit"));
    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, "*** Sensor Control ***\n");
    out += sprintf(out, "Shutter value         : %d\n", dev->shutter_val);
    out += sprintf(out, "Gain                  : %d\n", dev->gain);
    out += sprintf(out, "*** Output Window ***\n");
    out += sprintf(out, "Width                 : %d\n", dev->vwin.width);
    out += sprintf(out, "Height                : %d\n", dev->vwin.height);
    out += sprintf(out, "*** 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[7];
    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[7];
    
    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,
		0x400, 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, void *buf)
{
	if (usb_quickcam_i2c_in(dev,HDCS_IDENT + 1,HDCS_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, 1, HZ);
}
/* read two  byte identification register for PB */
static int usb_quickcam_get_i2c2(struct usb_device *dev, void *buf)
{
	if (usb_quickcam_i2c_in(dev,PB_IDENT,PB_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, 2, 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);
}
/*
 * I2C set registers of HDCS100 (8 bits registers)
 * Why not grouped in big blocs?
 */
int usb_quickcam_i2c_out(struct usb_device *dev, int reg, int val)
{
char buff[35]; /* why 35 = 23 hex? */

        buff[0]=reg;
        buff[0x10]=val;

        buff[0x20]=HDCS_ADDR;
        buff[0x21]=0; // 1 value
        buff[0x22]=1; // Write cmd, 03 would be read.

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

/* Send a command to the sensor */

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);
}
/*
 * I2C set registers of PB100 (16 bits registers).
 */
int usb_quickcam_i2c_out2(struct usb_device *dev, int reg, int val)
{
char buff[35]; /* why 35 = 23 hex? */

        buff[0]=reg;
        buff[0x10]=val&255;
        buff[0x11]=(val>>8)&255;

        buff[0x20]=PB_ADDR;
        buff[0x21]=0; // 1 value
        buff[0x22]=1; // Write cmd, 03 would be read.

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

/*
 * 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 ([EMAIL PROTECTED]) 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;
	struct usb_device *dev = quickcam->dev;

	if (debug&DEBUGDATA)
		printk("usb_quickcam_set_exposure: %d gain %d shutter %d\n",val,quickcam->gain,quickcam->shutter_val);
	if (val>140)
		quickcam->gain-=4+(val-140)/4;
	if (val<90)
		quickcam->gain+=4+(90-val)/4;

	if (quickcam->gain<=40)
	{
		if (quickcam->shutter_val>16)
		{
			quickcam->gain+=16;
			quickcam->gain = min(255,quickcam->gain);
			quickcam->shutter_val-=16;
			if (os!=quickcam->shutter_val)
				quickcam->sensor_ctrl.set_shutter(dev,quickcam->shutter_val,0x100);
		}
		else
			if (quickcam->gain<2)
				quickcam->gain=2;
	}
	if ((quickcam->gain+abs(quickcam->vpic.whiteness / 256))>255 && (quickcam->shutter_val<500))
	{
		quickcam->gain-=16;
		quickcam->gain = max(0,quickcam->gain);
		quickcam->shutter_val+=16;
		if (os!=quickcam->shutter_val)
			quickcam->sensor_ctrl.set_shutter(dev,quickcam->shutter_val,0x100);
	}
	if (og!=quickcam->gain)
	{
		return quickcam->sensor_ctrl.set_gains(dev, quickcam->gain,quickcam->vpic.whiteness / 256);
	}
	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);
	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)<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 addred	2
	/* need cleaning ... */
#define XH quickcam->vwin.height
#define XW quickcam->vwin.width

	struct quickcam_frame *frame;
	int 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;

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

	/* Convert the current frame */

	data = frame->storedata;
	for (i=0;i<frame->storelength;i++) {
		/*
		 * 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 (!(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) {
		mid_value = mid_value/yy;
		usb_quickcam_set_exposure(quickcam,mid_value);
	}

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

/*
 * Analyse the data and store it
 * This routine has been written by Carlo E. Prelz, [EMAIL PROTECTED]
 * 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;
	}

	if (mode)
		framesize = min(BAYER_FRAME_SIZE,(quickcam->vwin.height*quickcam->vwin.width)/2);
	else
		framesize = min(BAYER_FRAME_SIZE,quickcam->vwin.height*quickcam->vwin.width);

	/* 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 = min(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)
{
	urb_t *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)<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)<0) {
		printk("Init sensor failed\n");
		return(-1);
	}
	if (quickcam->sensor_ctrl.set_gains(quickcam->dev,quickcam->gain,quickcam->vpic.whiteness / 256)<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 >= 0x020400
#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_RGB24:
	    return 24;    

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

	case VIDEO_PALETTE_YUYV:
	    return 16;

	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.colour = 0x8000;
	quickcam->vpic.hue = 0x8000;
	quickcam->vpic.brightness = 180 * 256;
	quickcam->vpic.contrast = 128 * 256;
	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;

	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:
	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");

			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 {
				/* Black and white only?... */
				if(p.whiteness >= 256 && !(p.whiteness >= 150 * 256)) {
					if((p.whiteness / 256) != quickcam->vpic.whiteness / 256) {
						quickcam->vpic.whiteness = p.whiteness;
						quickcam->sensor_ctrl.set_gains(quickcam->dev, quickcam->gain, quickcam->vpic.whiteness / 256);
					}
				}

				/* 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\n");
			if (copy_from_user(&vw, arg, sizeof(vw))) {
				retval = -EFAULT;
				break;
			}
			printk("quickcam_ioctl: VIDIOCSWIN vw.flags %d vw.clipcount %d\n",vw.flags,vw.clipcount);

			/* 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 i2c[1];
	unsigned char i2c2[2];

	/* set default values for the camera */
	quickcam->shutter_val=SHUTTER_VAL;
	quickcam->gain=GAIN_VAL;
        quickcam->scanstate = STATE_OK;
	quickcam->x=quickcam->yy=0;
	quickcam->readframe = -1;

	/* 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;
	}
	/* Set quickcam i2c for 8-bit regs */
        if (usb_quickcam_set1(dev, STV_REG23, 0) < 0) {
		printk("usb_quickcam_set1 STV_REG23(0) failed\n");
		goto error;
	}

        /* Check I2C */
	if (usb_quickcam_get_i2c(dev, i2c) < 0) {
		printk("quickcam_get_i2c error\n");
		return -EBUSY;
	}

	printk("quickcam: HDCS1000-sensor ident:%02x\n", i2c[0]);
	if (i2c[0]==8) {
		printk("This Quickcam has a HDCS1000 sensor!\n");
		quickcam->sensor_addr = HDCS_ADDR;
		load_hdcs_mod(&quickcam->sensor_ctrl);
	}
	else {
		/* Check for Photobit */
        	if (usb_quickcam_set1(dev, STV_REG23, 1) < 0)
			goto error;
		if (usb_quickcam_get_i2c2(dev, i2c2) < 0) {
			printk("quickcam_get_i2c error\n");
			return -EBUSY;
		}
		printk("quickcam: Photobit-sensor ident:%02x %02x\n",
			i2c2[0],i2c2[1]);
		if (i2c2[1]==0x64) {
			printk("This Quickcam has a PB100 sensor!\n");
			quickcam->sensor_addr = PB_ADDR;
			load_pb0100_mod(&quickcam->sensor_ctrl);
		} else {
			printk("unsupported sensor!\n");
			return -EBUSY;
		}
		
	}

	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 >= 0x020405
	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;
	}
	
	// Setup internal video_picture
	quickcam->vpic.colour = 0x8000;
	quickcam->vpic.hue = 0x8000;
	quickcam->vpic.brightness = 180 * 256;
	quickcam->vpic.contrast = 128 * 256;
	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 >= 0x020400
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 testing Class %x SubClass %x\n",interface->bInterfaceClass, interface->bInterfaceSubClass);
	
#if LINUX_VERSION_CODE < 0x0202FF
	if (dev->descriptor.idVendor != 0x046d)
		return NULL;
	if (dev->descriptor.idProduct != 0x0870)
		return NULL;

	/* Checking vendor/product should be enough, but what the hell */
	if (interface->bInterfaceClass != 0xFF)
		return NULL;
	if (interface->bInterfaceSubClass != 0xFF)
		return NULL;
#endif

	/* 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 >= 0x02040
#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 >= 0x020400
id_table:	device_table,
#endif
probe:		quickcam_probe,
disconnect:	quickcam_disconnect,
};

static int __init usb_quickcam_init(void)
{
#if LINUX_VERSION_CODE >= 0x020400
#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 >= 0x020400
#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);

Reply via email to