/* OmniVision/Cypress FX2 Camera-to-USB Bridge Driver
 *
 * Copyright (c) 1999-2005 Mark W. McClelland, David Brownell
 * Many improvements by Bret Wallach <bwallac1@san.rr.com>
 * Color fixes by by Orion Sky Lawlor <olawlor@acm.org> (2/26/2000)
 * OV7620 fixes by Charl P. Botha <cpbotha@ieee.org>
 * Changes by Claudio Matsuoka <claudio@conectiva.com>
 * Kernel I2C interface improvements by Kyösti Mälkki
 * URB error messages from pwc driver by Nemosoft
 * Memory management (rvmalloc) code from bttv driver, by Gerd Knorr and others
 *
 * Based on the Linux OV511 driver by Mark W. McClelland, which is
 * based on the Linux CPiA driver written by Peter Pregler,
 * Scott J. Bertin and Johannes Erdfelt.
 * 
 * Please see the website at:  http://ovcam.org/ov511 for more info.
 *
 * 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. NO WARRANTY OF ANY KIND is expressed or implied.
 */

#include <linux/config.h>
#include <linux/version.h>

/* 2.6 Doesn't support /proc/video, but this is still defined somewhere */
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 0)
#  undef CONFIG_VIDEO_PROC_FS
#endif

#include <linux/module.h>
#include <linux/init.h>
#include <linux/vmalloc.h>
#include <linux/slab.h>
#if defined(CONFIG_VIDEO_PROC_FS)
#  include <linux/fs.h>
#  include <linux/proc_fs.h>
#  include <asm/io.h>
#endif
#include <linux/ctype.h>
#include <linux/pagemap.h>
#include <asm/semaphore.h>
#include <asm/processor.h>
#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 5, 68)
#  include <linux/wrapper.h>
#endif
#include <linux/mm.h>
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 0)
#  include <linux/device.h>
#endif

#include "ovfx2.h"
#include "id.h"

/* Driver will compile with older kernels, but will oops on open() */
#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 4, 19)
#  error "Kernel 2.4.19 is the minimum for this driver"
#endif

/*
 * Version Information
 */
#define DRIVER_VERSION "v2.29"
#define EMAIL "mark@ovcam.org"
#define DRIVER_AUTHOR "Mark McClelland <mark@ovcam.org> & Bret Wallach \
	& Orion Sky Lawlor <olawlor@acm.org> & Kevin Moore & Charl P. Botha \
	<cpbotha@ieee.org> & Claudio Matsuoka <claudio@conectiva.com>"
#define DRIVER_DESC "ovfx2 USB Camera Driver"

#define OVFX2_MAX_UNIT_VIDEO 16

/* Pixel count * 3 bytes per pixel (for BGR24) */
#define MAX_FRAME_SIZE(w, h) ((w) * (h) * 3)

#define MAX_DATA_SIZE(w, h) (MAX_FRAME_SIZE(w, h) + sizeof(struct timeval))

/* Max size * 2 bytes per pixel average + safety margin */
#define MAX_RAW_DATA_SIZE(w, h) ((w) * (h) * 2)

#define FATAL_ERROR(rc) ((rc) < 0 && (rc) != -EPERM)

/**********************************************************************
 * Module Parameters
 * (See ov511.txt for detailed descriptions of these)
 **********************************************************************/

/* These variables (and all static globals) default to zero */
static int autobright		= 1;
static int autoexp		= 1;
#ifdef OVFX2_DEBUG
static int debug;
#endif
static int dumppix;
static int dump_bridge;
static int lightfreq;
static int bandingfilter;
static int clockdiv		= -1;
static int framedrop		= -1;
static int fastset;
static int force_palette;
static int backlight;
static int unit_video[OVFX2_MAX_UNIT_VIDEO];
static int mirror;

MODULE_PARM(autobright, "i");
MODULE_PARM_DESC(autobright, "Sensor automatically changes brightness");
MODULE_PARM(autoexp, "i");
MODULE_PARM_DESC(autoexp, "Sensor automatically changes exposure");
#ifdef OVFX2_DEBUG
MODULE_PARM(debug, "i");
MODULE_PARM_DESC(debug,
  "Debug level: 0=none, 1=inits, 2=warning, 3=config, 4=functions, 5=max");
#endif
MODULE_PARM(dumppix, "i");
MODULE_PARM_DESC(dumppix, "Dump raw pixel data");
MODULE_PARM(dump_bridge, "i");
MODULE_PARM_DESC(dump_bridge, "Dump the bridge registers");
MODULE_PARM(lightfreq, "i");
MODULE_PARM_DESC(lightfreq,
  "Light frequency. Set to 50 or 60 Hz, or zero for default settings");
MODULE_PARM(bandingfilter, "i");
MODULE_PARM_DESC(bandingfilter,
  "Enable banding filter (to reduce effects of fluorescent lighting)");
MODULE_PARM(clockdiv, "i");
MODULE_PARM_DESC(clockdiv, "Force pixel clock divisor to a specific value");
MODULE_PARM(framedrop, "i");
MODULE_PARM_DESC(framedrop, "Force a specific frame drop register setting");
MODULE_PARM(fastset, "i");
MODULE_PARM_DESC(fastset, "Allows picture settings to take effect immediately");
MODULE_PARM(force_palette, "i");
MODULE_PARM_DESC(force_palette, "Force the palette to a specific value");
MODULE_PARM(backlight, "i");
MODULE_PARM_DESC(backlight, "For objects that are lit from behind");
MODULE_PARM(unit_video, "1-" __MODULE_STRING(OVFX2_MAX_UNIT_VIDEO) "i");
MODULE_PARM_DESC(unit_video,
  "Force use of specific minor number(s). 0 is not allowed.");
MODULE_PARM(mirror, "i");
MODULE_PARM_DESC(mirror, "Reverse image horizontally");

MODULE_AUTHOR(DRIVER_AUTHOR);
MODULE_DESCRIPTION(DRIVER_DESC);
#if defined(MODULE_LICENSE)	/* Introduced in ~2.4.10 */
MODULE_LICENSE("GPL");
#endif

/**********************************************************************
 * Miscellaneous Globals
 **********************************************************************/

static struct usb_driver ovfx2_driver;

/* Prevents double-free of ov struct */
static struct semaphore ov_free_lock;

/*
 * Omnivision provided a reference design and firmware connecting an FX2
 * to many of their I2C-aware sensor arrays.  Different products using
 * this design could have different sensors.
 */
static struct usb_device_id device_table [] = {
	/*
	 * Orange Micro "iBOT2" uses an ov7620. It does not have a snapshot
	 * button or L.E.D.
	 *
	 * HIGH SPEED:
	 *
	 * T:  Bus=04 Lev=01 Prnt=01 Port=00 Cnt=01 Dev#=  2 Spd=480 MxCh= 0
	 * D:  Ver= 2.00 Cls=00(>ifc ) Sub=00 Prot=00 MxPS=64 #Cfgs=  1
	 * P:  Vendor=0b62 ProdID=0059 Rev= 1.00
	 * S:  Manufacturer=Orange Micro
	 * S:  Product=iBOT2 Camera
	 * C:* #Ifs= 1 Cfg#= 1 Atr=80 MxPwr=400mA
	 * I:  If#= 0 Alt= 0 #EPs= 2 Cls=ff(vend.) Sub=00 Prot=00 Driver=(none)
	 * E:  Ad=81(I) Atr=03(Int.) MxPS=   4 Ivl=  4ms
	 * E:  Ad=82(I) Atr=02(Bulk) MxPS= 512 Ivl=  1ms
	 *
	 * FULL SPEED:
	 *
	 * T:  Bus=02 Lev=01 Prnt=01 Port=00 Cnt=01 Dev#=  3 Spd=12  MxCh= 0
	 * D:  Ver= 2.00 Cls=00(>ifc ) Sub=00 Prot=00 MxPS=64 #Cfgs=  1
	 * P:  Vendor=0b62 ProdID=0059 Rev= 1.00
	 * S:  Manufacturer=Orange Micro
	 * S:  Product=iBOT2 Camera
	 * C:* #Ifs= 1 Cfg#= 1 Atr=80 MxPwr=400mA
	 * I:  If#= 0 Alt= 0 #EPs= 2 Cls=ff(vend.) Sub=00 Prot=00 Driver=(none)
	 * E:  Ad=81(I) Atr=03(Int.) MxPS=   4 Ivl=  1ms
	 * E:  Ad=82(I) Atr=02(Bulk) MxPS=  64 Ivl=  1ms
	 *
	 */
	{ USB_DEVICE (VEND_ORANGE_MICRO, PROD_IBOT2),
		.driver_info = (unsigned long) "Orange Micro iBOT2",
		},

	/* OmniVision "2800" reference board, which supports almost all
	 * of their camera chips. The following boards are known to work:
	 *   - OV7620-ECX
	 */
	{ USB_DEVICE (VEND_OMNIVISION, PROD_2800),
		.driver_info = (unsigned long) "OmniVision \"2800\" ref. board",
		},

	/* Trust Sp@cec@m 380 (OV7620),
	 * Aplux MU2-35 (OV7620),
	 * Aplux MU2-48 (OV86108), or
	 * Aplux MU2-130 (OV9620).
	 *
	 * These are all based on the "2800" reference design. They have a
	 * snapshot button and an L.E.D.
	 */ 
	{ USB_DEVICE (VEND_APLUX, PROD_MU2CAM),
		.driver_info = (unsigned long)
			"Aplux MU2-35/48/130 or Trust 380",
		},
	{ }
};

MODULE_DEVICE_TABLE (usb, device_table);

/**********************************************************************
 * Symbolic Names
 **********************************************************************/

/* Video4Linux1 Palettes */
static struct symbolic_list v4l1_plist[] = {
	{ VIDEO_PALETTE_GREY,	"GREY" },
	{ VIDEO_PALETTE_HI240,	"HI240" },
	{ VIDEO_PALETTE_RGB565,	"RGB565" },
	{ VIDEO_PALETTE_RGB24,	"RGB24" },
	{ VIDEO_PALETTE_RGB32,	"RGB32" },
	{ VIDEO_PALETTE_RGB555,	"RGB555" },
	{ VIDEO_PALETTE_YUV422,	"YUV422" },
	{ VIDEO_PALETTE_YUYV,	"YUYV" },
	{ VIDEO_PALETTE_UYVY,	"UYVY" },
	{ VIDEO_PALETTE_YUV420,	"YUV420" },
	{ VIDEO_PALETTE_YUV411,	"YUV411" },
	{ VIDEO_PALETTE_RAW,	"RAW" },
	{ VIDEO_PALETTE_YUV422P,"YUV422P" },
	{ VIDEO_PALETTE_YUV411P,"YUV411P" },
	{ VIDEO_PALETTE_YUV420P,"YUV420P" },
	{ VIDEO_PALETTE_YUV410P,"YUV410P" },
	{ -1, NULL }
};

#if defined(CONFIG_VIDEO_PROC_FS) \
 || LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 0)
static struct symbolic_list senlist[] = {
	{ CC_OV76BE,	"OV76BE" },
	{ CC_OV7610,	"OV7610" },
	{ CC_OV7620,	"OV7620" },
	{ CC_OV7620AE,	"OV7620AE" },
	{ CC_OV6620,	"OV6620" },
	{ CC_OV6630,	"OV6630" },
	{ CC_OV6630AE,	"OV6630AE" },
	{ CC_OV6630AF,	"OV6630AF" },
	{ -1, NULL }
};
#endif

/* URB error codes: */
static struct symbolic_list urb_errlist[] = {
	{ -ENOSR,	"Buffer error (overrun)" },
	{ -EPIPE,	"Stalled (device not responding)" },
	{ -EOVERFLOW,	"Babble (bad cable?)" },
	{ -EPROTO,	"Bit-stuff error (bad cable?)" },
	{ -EILSEQ,	"CRC/Timeout" },
	{ -ETIMEDOUT,	"NAK (device does not respond)" },
	{ -1, NULL }
};

static const char *v4l1_ioctls[] = {
	"?", "CGAP", "GCHAN", "SCHAN", "GTUNER", "STUNER", "GPICT", "SPICT",
	"CCAPTURE", "GWIN", "SWIN", "GFBUF", "SFBUF", "KEY", "GFREQ",
	"SFREQ", "GAUDIO", "SAUDIO", "SYNC", "MCAPTURE", "GMBUF", "GUNIT",
	"GCAPTURE", "SCAPTURE", "SPLAYMODE", "SWRITEMODE", "GPLAYINFO",
	"SMICROCODE", "GVBIFMT", "SVBIFMT" };
#define NUM_V4L1_IOCTLS (sizeof(v4l1_ioctls)/sizeof(char*))

/**********************************************************************
 * Prototypes
 **********************************************************************/

static int ov7xx0_configure(struct usb_ovfx2 *);

/**********************************************************************
 * Memory management
 **********************************************************************/

#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 10)
/* Here we want the physical address of the memory.
 * This is used when initializing the contents of the area.
 */
static inline unsigned long
kvirt_to_pa(unsigned long adr)
{
	unsigned long kva, ret;

	kva = (unsigned long) page_address(vmalloc_to_page((void *)adr));
	kva |= adr & (PAGE_SIZE-1); /* restore the offset */
	ret = __pa(kva);
	return ret;
}
#endif

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

	size = PAGE_ALIGN(size);
	mem = vmalloc_32(size);
	if (!mem)
		return NULL;

	memset(mem, 0, size); /* Clear the ram out, no junk to the user */
	adr = (unsigned long) mem;
	while (size > 0) {
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 5, 68)
		SetPageReserved(vmalloc_to_page((void *)adr));
#else
		mem_map_reserve(vmalloc_to_page((void *)adr));
#endif
		adr += PAGE_SIZE;
		size -= PAGE_SIZE;
	}

	return mem;
}

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

	if (!mem)
		return;

	adr = (unsigned long) mem;
	while ((long) size > 0) {
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 5, 68)
		ClearPageReserved(vmalloc_to_page((void *)adr));
#else
		mem_map_unreserve(vmalloc_to_page((void *)adr));
#endif
		adr += PAGE_SIZE;
		size -= PAGE_SIZE;
	}
	vfree(mem);
}

/**********************************************************************
 * /proc interface
 * Based on the CPiA driver version 0.7.4 -claudio
 **********************************************************************/

#if defined(CONFIG_VIDEO_PROC_FS)

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

/* Prototypes */
static int sensor_get_picture(struct usb_ovfx2 *, struct video_picture *);
static int sensor_get_control(struct usb_ovfx2 *, int, int *);

#define YES_NO(x) ((x) ? "yes" : "no")

/* /proc/video/ovfx2/<minor#>/info */
static int
ovfx2_read_proc_info(char *page, char **start, off_t off, int count, int *eof,
		     void *data)
{
	char *out = page;
	int i, len;
	struct usb_ovfx2 *ov = data;
	struct video_picture p;
	int exp = 0;

	if (!ov || !ov->dev)
		return -ENODEV;

	sensor_get_picture(ov, &p);
	sensor_get_control(ov, OVCAMCHIP_CID_EXP, &exp);

	/* IMPORTANT: This output MUST be kept under PAGE_SIZE
	 *            or we need to get more sophisticated. */

	out += sprintf(out, "driver_version  : %s\n", DRIVER_VERSION);
	out += sprintf(out, "model           : %s\n", ov->desc);
	out += sprintf(out, "streaming       : %s\n", YES_NO(ov->streaming));
	out += sprintf(out, "grabbing        : %s\n", YES_NO(ov->grabbing));
	out += sprintf(out, "subcapture      : %s\n", YES_NO(ov->sub_flag));
	out += sprintf(out, "sub_size        : %d %d %d %d\n",
		       ov->subx, ov->suby, ov->subw, ov->subh);
	out += sprintf(out, "brightness      : %d\n", p.brightness >> 8);
	out += sprintf(out, "colour          : %d\n", p.colour >> 8);
	out += sprintf(out, "contrast        : %d\n", p.contrast >> 8);
	out += sprintf(out, "hue             : %d\n", p.hue >> 8);
	out += sprintf(out, "exposure        : %d\n", exp);
	out += sprintf(out, "num_frames      : %d\n", OVFX2_NUMFRAMES);
	for (i = 0; i < OVFX2_NUMFRAMES; i++) {
		out += sprintf(out, "frame           : %d\n", i);
		out += sprintf(out, "  size          : %d %d\n",
			       ov->frame[i].width, ov->frame[i].height);
		out += sprintf(out, "  format        : %s\n",
			       symbolic(v4l1_plist, ov->frame[i].format));
	}
	out += sprintf(out, "sensor          : %s\n",
		       symbolic(senlist, ov->sensor));
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 4, 20)
	out += sprintf(out, "topology        : %s\n", ov->usb_path);
#else
	out += sprintf(out, "usb_bus         : %d\n", ov->dev->bus->busnum);
	out += sprintf(out, "usb_device      : %d\n", ov->dev->devnum);
#endif
	out += sprintf(out, "i2c_adap_id     : %d\n",
	               i2c_adapter_id(&ov->i2c_adap));

	len = out - page;
	len -= off;
	if (len < count) {
		*eof = 1;
		if (len <= 0)
			return 0;
	} else
		len = count;

	*start = page + off;

	return len;
}

static void
create_proc_ovfx2_cam(struct usb_ovfx2 *ov)
{
	char dirname[10];

	if (!ovfx2_proc_entry || !ov)
		return;

	/* Create per-device directory */
	snprintf(dirname, 10, "%d", ov->vdev->minor);
	ov->proc_devdir = create_proc_entry(dirname, S_IFDIR, ovfx2_proc_entry);
	if (!ov->proc_devdir)
		return;
	ov->proc_devdir->owner = THIS_MODULE;

	/* Create "info" entry (human readable device information) */
	ov->proc_info = create_proc_read_entry("info", S_IFREG|S_IRUGO|S_IWUSR,
		ov->proc_devdir, ovfx2_read_proc_info, ov);
	if (!ov->proc_info)
		return;
	ov->proc_info->owner = THIS_MODULE;
}

static void
destroy_proc_ovfx2_cam(struct usb_ovfx2 *ov)
{
	char dirname[10];

	if (!ov || !ov->proc_devdir)
		return;

	snprintf(dirname, 10, "%d", ov->vdev->minor);

	/* Destroy "info" entry */
	if (ov->proc_info) {
		remove_proc_entry("info", ov->proc_devdir);
		ov->proc_info = NULL;
	}

	/* Destroy per-device directory */
	remove_proc_entry(dirname, ovfx2_proc_entry);
	ov->proc_devdir = NULL;
}

static void
proc_ovfx2_create(void)
{
	if (video_proc_entry == NULL) {
		err("Error: /proc/video/ does not exist");
		return;
	}

	ovfx2_proc_entry = create_proc_entry("ovfx2", S_IFDIR,
					     video_proc_entry);

	if (ovfx2_proc_entry)
		ovfx2_proc_entry->owner = THIS_MODULE;
	else
		err("Unable to create /proc/video/ovfx2");
}

static void
proc_ovfx2_destroy(void)
{
	if (ovfx2_proc_entry == NULL)
		return;

	remove_proc_entry("ovfx2", video_proc_entry);
}
#else
static inline void create_proc_ovfx2_cam(struct usb_ovfx2 *ov) { }
static inline void destroy_proc_ovfx2_cam(struct usb_ovfx2 *ov) { }
static inline void proc_ovfx2_create(void) { }
static inline void proc_ovfx2_destroy(void) { }
#endif /* #ifdef CONFIG_VIDEO_PROC_FS */

/**********************************************************************
 *
 * Register I/O
 *
 **********************************************************************/

/* Write an OVFX2 register */
static int
reg_w(struct usb_ovfx2 *ov, unsigned char reg, unsigned char value)
{
	int rc;

	PDEBUG(5, "0x%02X:0x%02X", reg, value);

	/* We don't use cbuf here, but the lock ensures we're done before
	 * disconnect() completes */
	down(&ov->cbuf_lock);

	if (!ov->cbuf) {
		up(&ov->cbuf_lock);
		return -ENODEV;
	}

	rc = usb_control_msg(ov->dev,
			     usb_sndctrlpipe(ov->dev, 0),
			     OVFX2_REQ_REG_WRITE,
			     USB_TYPE_VENDOR | USB_RECIP_DEVICE,
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 12)
			     (__u16)value, (__u16)reg, NULL, 0, 1000);
#else
			     (__u16)value, (__u16)reg, NULL, 0, HZ);
#endif
	up(&ov->cbuf_lock);

	if (rc < 0)
		err("reg write: error %d: %s", rc, symbolic(urb_errlist, rc));

	return rc;
}

/* Read from an OVFX2 register */
/* returns: negative is error, pos or zero is data */
static int
reg_r(struct usb_ovfx2 *ov, unsigned char reg)
{
	int rc;

	down(&ov->cbuf_lock);

	if (!ov->cbuf) {
		up(&ov->cbuf_lock);
		return -ENODEV;
	}

	rc = usb_control_msg(ov->dev,
			     usb_rcvctrlpipe(ov->dev, 0),
			     OVFX2_REQ_REG_READ,
			     USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 12)
			     0, (__u16)reg, &ov->cbuf[0], 1, 1000);
#else
			     0, (__u16)reg, &ov->cbuf[0], 1, HZ);
#endif
	if (rc < 0) {
		err("reg read: error %d: %s", rc, symbolic(urb_errlist, rc));
	} else {
		rc = ov->cbuf[0];
		PDEBUG(5, "0x%02X:0x%02X", reg, ov->cbuf[0]);
	}

	up(&ov->cbuf_lock);

	return rc;
}

/*
 * Writes bits at positions specified by mask to an OVFX2 reg. Bits that are in
 * the same position as 1's in "mask" are cleared and set to "value". Bits
 * that are in the same position as 0's in "mask" are preserved, regardless
 * of their respective state in "value".
 */
static int
reg_w_mask(struct usb_ovfx2 *ov,
	   unsigned char reg,
	   unsigned char value,
	   unsigned char mask)
{
	int ret;
	unsigned char oldval, newval;

	if (mask == 0xff) {
		newval = value;
	} else {
		ret = reg_r(ov, reg);
		if (ret < 0)
			return ret;

		oldval = (unsigned char) ret;
		oldval &= (~mask);		/* Clear the masked bits */
		value &= mask;			/* Enforce mask on value */
		newval = oldval | value;	/* Set the desired bits */
	}

	return (reg_w(ov, reg, newval));
}

static int
i2c_w(struct usb_ovfx2 *ov, unsigned char reg, unsigned char value)
{
	return i2c_smbus_write_byte_data(&ov->internal_client, reg, value);
}

static int
i2c_w_mask(struct usb_ovfx2 *ov,
	   unsigned char reg,
	   unsigned char value,
	   unsigned char mask)
{
	int rc;
	unsigned char oldval, newval;

	if (mask == 0xff) {
		newval = value;
	} else {
		rc = i2c_smbus_read_byte_data(&ov->internal_client, reg);
		if (rc < 0)
			return rc;

		oldval = (unsigned char) rc;
		oldval &= (~mask);		/* Clear the masked bits */
		value &= mask;			/* Enforce mask on value */
		newval = oldval | value;	/* Set the desired bits */
	}

	return i2c_smbus_write_byte_data(&ov->internal_client, reg, newval);
}

/**********************************************************************
 *
 * Low-level I2C I/O functions
 *
 **********************************************************************/

/* NOTE: Do not call this function directly!
 * Sets I2C read and write slave IDs, if they have changed.
 * Returns <0 for error
 */
static int
ovfx2_i2c_adap_set_slave(struct usb_ovfx2 *ov, unsigned char slave)
{
	int rc;

	if (ov->last_slave == slave)
		return 0;

	/* invalidate slave */
	ov->last_slave = 0;

	rc = reg_w(ov, REG_I2C_ADDR, (slave<<1));
	if (rc < 0)
		return rc;

	/* validate slave */
	ov->last_slave = slave;
	return 0;
}

static int
ovfx2_i2c_adap_read_byte_data(struct usb_ovfx2 *ov,
			      unsigned char addr,
			      unsigned char subaddr,
			      unsigned char *val)
{
	int rc;

	/* Set slave addresses */	
	rc = ovfx2_i2c_adap_set_slave(ov, addr);
	if (rc < 0)
		return rc;

	down(&ov->cbuf_lock);

	if (!ov->cbuf) {
		up(&ov->cbuf_lock);
		return -ENODEV;
	}

	rc = usb_control_msg(ov->dev,
			     usb_rcvctrlpipe(ov->dev, 0),
			     OVFX2_REQ_I2C_READ,
			     USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 12)
			     0, (__u16)subaddr, &ov->cbuf[0], 1, 1000);
#else
			     0, (__u16)subaddr, &ov->cbuf[0], 1, HZ);
#endif
	if (rc < 0) {
		PDEBUG(5, "error %d: %s", rc, symbolic(urb_errlist, rc));
	} else {
		*val = ov->cbuf[0];
		PDEBUG(5, "0x%02X:0x%02X", subaddr, ov->cbuf[0]);
	}

	up(&ov->cbuf_lock);

	return rc;
}

static int
ovfx2_i2c_adap_write_byte_data(struct usb_ovfx2 *ov,
			       unsigned char addr,
			       unsigned char subaddr,
			       unsigned char val)
{
	int rc;

	PDEBUG(5, "(0x%02X) 0x%02X:0x%02X", addr<<1, subaddr, val);

	/* Set slave addresses */	
	rc = ovfx2_i2c_adap_set_slave(ov, addr);
	if (rc < 0)
		return rc;

	/* We don't use cbuf here, but the lock ensures we're done before
	 * disconnect() completes */
	down(&ov->cbuf_lock);

	if (!ov->cbuf) {
		up(&ov->cbuf_lock);
		return -ENODEV;
	}

	rc = usb_control_msg(ov->dev,
			     usb_sndctrlpipe(ov->dev, 0),
			     OVFX2_REQ_I2C_WRITE,
			     USB_TYPE_VENDOR | USB_RECIP_DEVICE,
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 12)
			     (__u16)val, (__u16)subaddr, NULL, 0, 1000);
#else
			     (__u16)val, (__u16)subaddr, NULL, 0, HZ);
#endif
	up(&ov->cbuf_lock);

	if (rc < 0)
		PDEBUG(5, "error %d: %s", rc, symbolic(urb_errlist, rc));

	return rc;
}

static int
write_regvals(struct usb_ovfx2 *ov, struct regval *regvals)
{
	int rc;

	while (regvals->mask != 0) {
		rc = reg_w_mask(ov, regvals->reg, regvals->val, regvals->mask);
		if (rc < 0)
			return rc;

		regvals++;
	}

	return 0;
}

#ifdef OVFX2_DEBUG
static void
dump_reg_range(struct usb_ovfx2 *ov, int reg1, int regn)
{
	int i, rc;

	for (i = reg1; i <= regn; i++) {
		rc = reg_r(ov, i);
		info("OVFX2[0x%02X] = 0x%02X", i, rc);
	}
}

static void
ovfx2_dump_regs(struct usb_ovfx2 *ov)
{
	dump_reg_range(ov, 0x00, 0xff);
}
#endif

/**********************************************************************
 *
 * Kernel I2C Interface
 *
 **********************************************************************/

static int
ovfx2_i2c_validate_addr(struct usb_ovfx2 *ov, u16 addr)
{
	switch (addr) {
	case OV7xx0_SID:
		return 0;
	default:
		PDEBUG(4, "Rejected slave ID 0x%04X", addr);
		return -EINVAL;
	}
}

static inline int
sensor_cmd(struct usb_ovfx2 *ov, unsigned int cmd, void *arg)
{
	struct i2c_client *c = ov->sensor_client;

	if (c && c->driver->command)
		return c->driver->command(ov->sensor_client, cmd, arg);
	else
		return -ENODEV;
}

#if 0
static void
call_i2c_clients(struct usb_ovfx2 *ov, unsigned int cmd, void *arg)
{
#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 0)
	struct i2c_client *client;
	int i;

	for (i = 0; i < I2C_CLIENT_MAX; i++) {
		client = ov->i2c_adap.clients[i];

		/* Sensor_client is not called from here for now, since it
		   has different enumeration for cmd  */
		if (client == ov->sensor_client)
			continue;

		if (client && client->driver->command)
			client->driver->command(client, cmd, arg);
	}
#else
	i2c_clients_command(&ov->i2c_adap, cmd, arg);
#endif
}
#endif

static int
ovfx2_smbus_xfer(struct i2c_adapter *adapter, u16 addr, unsigned short flags,
		 char read_write, u8 command, int size,
		 union i2c_smbus_data *data)
{
	struct usb_ovfx2 *ov = i2c_get_adapdata(adapter);
	int rc = -ENOSYS;

	if (size == I2C_SMBUS_QUICK) {
		PDEBUG(4, "Got probed at addr 0x%04X", addr);
		rc = ovfx2_i2c_validate_addr(ov, addr);
	} else if (size == I2C_SMBUS_BYTE_DATA) {
		if (read_write == I2C_SMBUS_WRITE) {
 			rc = ovfx2_i2c_adap_write_byte_data(ov, addr, command,
							    data->byte);
		} else if (read_write == I2C_SMBUS_READ) {
			rc = ovfx2_i2c_adap_read_byte_data(ov, addr, command,
							   &data->byte);
		}
	} else {
		warn("Unsupported I2C transfer mode (%d)", size);
		return -EINVAL;
	}

	/* This works around a bug in the I2C core */
	if (rc > 0)
		rc = 0;

	return rc;
}

static u32
ovfx2_i2c_func(struct i2c_adapter *adap)
{
	return I2C_FUNC_SMBUS_QUICK
	     | I2C_FUNC_SMBUS_BYTE_DATA;
}

static int
i2c_attach_inform(struct i2c_client *client)
{
	struct usb_ovfx2 *ov = i2c_get_adapdata(client->adapter);
	int id = client->driver->id;

	if (id == I2C_DRIVERID_OVCAMCHIP) {
		int rc, mono = 0;

		ov->sensor_client = client;

		rc = sensor_cmd(ov, OVCAMCHIP_CMD_INITIALIZE, &mono);
		if (rc < 0) {
			err("ERROR: Sensor init failed (rc=%d)", rc);
			ov->sensor_client = NULL;
			return rc;
		}

		down(&ov->lock);
		if (sensor_cmd(ov, OVCAMCHIP_CMD_Q_SUBTYPE, &ov->sensor) < 0)
			rc = -EIO;
		else if (client->addr == OV7xx0_SID)
			rc = ov7xx0_configure(ov);
		else
			rc = -EINVAL;
		up(&ov->lock);

		if (rc) {
			ov->sensor_client = NULL;
			return rc;
		}
	} else	{
		PDEBUG(1, "Rejected client [%s] with [%s]",
		       i2c_clientname(client), client->driver->name);
		return -1;
	}

	PDEBUG(1, "i2c attach client [%s] with [%s]",
	       i2c_clientname(client), client->driver->name);

	return 0;
}

static int
i2c_detach_inform(struct i2c_client *client)
{
	struct usb_ovfx2 *ov = i2c_get_adapdata(client->adapter);

	if (ov->sensor_client == client) {
		ov->sensor_client = NULL;
	}

	PDEBUG(1, "i2c detach [%s]", i2c_clientname(client));

	return 0;
}

static int 
ovfx2_i2c_control(struct i2c_adapter *adapter, unsigned int cmd,
		  unsigned long arg)
{
	return 0;
}

#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 5, 0)
static void
ovfx2_i2c_inc_use(struct i2c_adapter *adap)
{
	MOD_INC_USE_COUNT;
}

static void
ovfx2_i2c_dec_use(struct i2c_adapter *adap)
{
	MOD_DEC_USE_COUNT;
}
#endif

static struct i2c_algorithm ovfx2_i2c_algo = {
	.name =			"OVFX2 algorithm",
	.id =			I2C_ALGO_SMBUS,
	.smbus_xfer =		ovfx2_smbus_xfer,
	.algo_control =		ovfx2_i2c_control,
	.functionality =	ovfx2_i2c_func,
};

static struct i2c_adapter i2c_adap_template = {
	I2C_DEVNAME("(unset)"),
	.id =			I2C_ALGO_SMBUS,
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 6)
	.class =		I2C_CLASS_CAM_DIGITAL,
#elif LINUX_VERSION_CODE >= KERNEL_VERSION(2, 5, 70)
	.class =		I2C_ADAP_CLASS_CAM_DIGITAL,
#endif
#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 5, 0)
	.inc_use =		ovfx2_i2c_inc_use,
	.dec_use =		ovfx2_i2c_dec_use,
#else
	.owner =		THIS_MODULE,
#endif
	.client_register =	i2c_attach_inform,
	.client_unregister =	i2c_detach_inform,
};

static int
ovfx2_init_i2c(struct usb_ovfx2 *ov)
{
	memcpy(&ov->i2c_adap, &i2c_adap_template, sizeof(struct i2c_adapter));

	/* Temporary name. We'll set the final one when we know the minor # */
	sprintf(i2c_adapname(&ov->i2c_adap), "OVFX2");
	i2c_set_adapdata(&ov->i2c_adap, ov);

	ov->i2c_adap.algo = &ovfx2_i2c_algo;
	ov->i2c_adap.id |= I2C_HW_SMBUS_OVFX2;

	ov->internal_client.adapter = &ov->i2c_adap;

	PDEBUG(4, "Registering I2C bus with kernel");

	return i2c_add_adapter(&ov->i2c_adap);
}

/*****************************************************************************/

/* Pause video streaming */
static inline int
ovfx2_pause(struct usb_ovfx2 *ov)
{
	PDEBUG(4, "pausing stream");
	ov->stopped = 1;
	return reg_w_mask(ov, 0x0f, 0x00, 0x02);
}

/* Resume video streaming if it was stopped. */
static inline int
ovfx2_resume(struct usb_ovfx2 *ov)
{
	if (ov->stopped) {
		PDEBUG(4, "resuming stream");
		ov->stopped = 0;

		return reg_w_mask(ov, 0x0f, 0x02, 0x02);
	}

	return 0;
}

/* Returns 1 if image streaming needs to be stopped while setting the specified
 * control, and returns 0 if not */
static int
sensor_needs_stop(struct usb_ovfx2 *ov, int cid)
{
	if (!ov->stop_during_set)
		return 0;

	/* FIXME: I don't know whether the FX2 needs to be stopped yet. Don't
	 * do it yet, since it might not be safe to pause the stream while bulk
	 * requests are active */
	return 0;

	switch (cid) {
	case OVCAMCHIP_CID_CONT:
	case OVCAMCHIP_CID_BRIGHT:
	case OVCAMCHIP_CID_SAT:
	case OVCAMCHIP_CID_HUE:
	case OVCAMCHIP_CID_EXP:
		 return 1;
	}

	return 0;
}

static int
sensor_set_control(struct usb_ovfx2 *ov, int cid, int val)
{
	struct ovcamchip_control ctl;
	int rc;

	if (sensor_needs_stop(ov, cid))
		if (ovfx2_pause(ov) < 0)
			return -EIO;

	ctl.id = cid;
	ctl.value = val;

	rc = sensor_cmd(ov, OVCAMCHIP_CMD_S_CTRL, &ctl);

	if (ovfx2_resume(ov) < 0)
		return -EIO;

	return rc;
}

static int
sensor_get_control(struct usb_ovfx2 *ov, int cid, int *val)
{
	struct ovcamchip_control ctl;
	int rc;

	ctl.id = cid;

	rc = sensor_cmd(ov, OVCAMCHIP_CMD_G_CTRL, &ctl);
	if (rc >= 0)
		*val = ctl.value;

	return rc;
}

static int
sensor_set_picture(struct usb_ovfx2 *ov, struct video_picture *p)
{
	int rc;

	PDEBUG(4, "sensor_set_picture");

	rc = reg_w(ov, REG_CNTR, p->contrast >> 8);
	if (rc < 0)
		return rc;

	rc = reg_w(ov, REG_BRIGHT, (p->brightness >> 8) - 127);
	if (rc < 0)
		return rc;

	rc = reg_w(ov, REG_SAT, p->colour >> 8);
	if (rc < 0)
		return rc;

	rc = reg_w(ov, REG_HUE, (p->hue >> 8) - 127);
	if (rc < 0)
		return rc;

	return 0;
}

static int
sensor_get_picture(struct usb_ovfx2 *ov, struct video_picture *p)
{
	int rc;

	PDEBUG(4, "sensor_get_picture");

	/* Don't return error if a setting is unsupported, or rest of settings
         * will not be performed */

	rc = reg_r(ov, REG_CNTR);
	if (rc < 0)
		return rc;
	p->contrast = rc << 8;

	rc = reg_r(ov, REG_BRIGHT);
	if (rc < 0)
		return rc;
	p->brightness = (rc + 127) << 8;

	rc = reg_r(ov, REG_SAT);
	if (rc < 0)
		return rc;
	p->colour = rc << 8;

	rc = reg_r(ov, REG_HUE);
	if (rc < 0)
		return rc;
	p->hue = (rc + 127) << 8;

	p->whiteness = 105 << 8;

	return 0;
}

/* Matches the sensor's internal frame rate to the lighting frequency.
 * Valid frequencies are:
 *	50 - 50Hz, for European and Asian lighting
 *	60 - 60Hz, for American lighting
 *
 * Returns: 0 for success
 */
static int
sensor_set_light_freq(struct usb_ovfx2 *ov, int freq)
{
	if (freq != 50 && freq != 60) {
		err("Invalid light freq (%d Hz)", freq);
		return -EINVAL;
	}

	return sensor_set_control(ov, OVCAMCHIP_CID_FREQ, freq);
}

/* Returns number of bits per pixel (regardless of where they are located;
 * planar or not), or zero for unsupported format.
 */
static inline int
get_depth(int palette)
{
	switch (palette) {
	case VIDEO_PALETTE_RGB24:	return 24; /* Planar */
	default:			return 0;  /* Invalid format */
	}
}

/* Bytes per frame. Used by read(). Return of 0 indicates error */
static inline long int
get_frame_length(struct ovfx2_frame *frame)
{
	if (!frame)
		return 0;
	else
		return ((frame->width * frame->height
			 * get_depth(frame->format)) >> 3);
}

static int
set_ov_sensor_window(struct usb_ovfx2 *ov, int width, int height, int mode,
		     int sub_flag)
{
	struct ovcamchip_window win;
	int rc;

	win.format = mode;

	/* Unless subcapture is enabled, center the image window and downsample
	 * if possible to increase the field of view */
	if (sub_flag) {
		win.x = ov->subx;
		win.y = ov->suby;
		win.width = ov->subw;
		win.height = ov->subh;
		win.quarter = 0;
	} else {
		win.x = 0;
		win.y = 0;
		win.width = ov->maxwidth;
		win.height = ov->maxheight;
		win.quarter = 0;
	}

	if (clockdiv >= 0)
		win.clockdiv = clockdiv;
	else
		win.clockdiv = 0x01;	/* Always use the max frame rate */

	rc = sensor_cmd(ov, OVCAMCHIP_CMD_S_MODE, &win);
	if (rc < 0)
		return rc;

	if (framedrop >= 0)
		i2c_w(ov, 0x16, framedrop);

	return 0;
}

/* Sets up the OVFX2 with the given image parameters */
static int
ovfx2_mode_init_regs(struct usb_ovfx2 *ov,
		     int width, int height, int mode, int sub_flag)
{
	if (sub_flag) {
		width = ov->subw;
		height = ov->subh;
	}

	PDEBUG(3, "width:%d, height:%d, mode:%d, sub:%d",
	       width, height, mode, sub_flag);

	if (width < ov->minwidth || height < ov->minheight) {
		err("Requested dimensions are too small");
		return -EINVAL;
	}

	/* Set palette */

	/* Set bus width */

	/* Set resolution */

	/* Set framerate/clock */
	reg_w_mask(ov, 0x0f, 0x08, 0x08);

	/* Reinitialize? */

	return 0;
}

/* This is a wrapper around the FX2 and sensor specific functions */
static int
mode_init_regs(struct usb_ovfx2 *ov,
	       int width, int height, int mode, int sub_flag)
{
	int rc = 0;

	rc = ovfx2_mode_init_regs(ov, width, height, mode, sub_flag);
	if (FATAL_ERROR(rc))
		return rc;

	rc = set_ov_sensor_window(ov, width, height, mode, sub_flag);
	if (FATAL_ERROR(rc))
		return rc;

	/********* The following settings are specific to this camera ********/
	i2c_w(ov, 0x03, 0x80);		  /* Hardcoded saturation value */
	i2c_w_mask(ov, 0x12, 0x48, 0x48); /* Enable h-mirror and raw output */
	i2c_w_mask(ov, 0x27, 0x10, 0x10); /* Bypass RGB matrix */
	i2c_w_mask(ov, 0x28, 0x04, 0x04); /* G/BR (YG) mode */
	i2c_w_mask(ov, 0x2a, 0x00, 0x10); /* No UV 2-pixel delay */
	i2c_w_mask(ov, 0x2d, 0x40, 0x40); /* QVGA 60 FPS (redundant?) */
	i2c_w(ov, 0x67, 0x00); 		  /* Disable YUV postprocess */
	i2c_w_mask(ov, 0x2d, 0x00, 0x0f); /* Disable anti-alias */
	i2c_w(ov, 0x24, 0x20); 		  /* Use QVGA white pixel ratio */
	i2c_w(ov, 0x25, 0x30); 		  /* Use QVGA black pixel ratio */
	i2c_w(ov, 0x60, 0x26);		  /* Unknown */
	i2c_w(ov, 0x74, 0x20);		  /* AGC max gain = 4x */
	i2c_w(ov, 0x06, 0x68);		  /* Hardcoded brightness value */
	i2c_w(ov, 0x14, 0x04);		  /*  */

	return 0;
}

/* Set up the camera chip with the options provided by the module params */
static int
camchip_init_settings(struct usb_ovfx2 *ov)
{
	int rc;

	rc = sensor_set_control(ov, OVCAMCHIP_CID_AUTOBRIGHT, autobright);
	if (FATAL_ERROR(rc))
		return rc;

	rc = sensor_set_control(ov, OVCAMCHIP_CID_AUTOEXP, autoexp);
	if (FATAL_ERROR(rc))
		return rc;

	rc = sensor_set_control(ov, OVCAMCHIP_CID_BANDFILT, bandingfilter);
	if (FATAL_ERROR(rc))
		return rc;

	if (lightfreq) {
		rc = sensor_set_light_freq(ov, lightfreq);
		if (FATAL_ERROR(rc))
			return rc;
	}

	rc = sensor_set_control(ov, OVCAMCHIP_CID_BACKLIGHT, backlight);
	if (FATAL_ERROR(rc))
		return rc;

	rc = sensor_set_control(ov, OVCAMCHIP_CID_MIRROR, mirror);
	if (FATAL_ERROR(rc))
		return rc;

	return 0;
}

/* This sets the default image parameters. This is useful for apps that use
 * read() and do not set these.
 */
static int
ovfx2_set_default_params(struct usb_ovfx2 *ov)
{
	int i;

	/* Set default sizes in case IOCTL (VIDIOCMCAPTURE) is not used
	 * (using read() instead). */
	for (i = 0; i < OVFX2_NUMFRAMES; i++) {
		ov->frame[i].width = ov->maxwidth;
		ov->frame[i].height = ov->maxheight;
		ov->frame[i].bytes_read = 0;
		if (force_palette)
			ov->frame[i].format = force_palette;
		else
			ov->frame[i].format = VIDEO_PALETTE_RGB24;
		ov->frame[i].depth = get_depth(ov->frame[i].format);
	}

	PDEBUG(3, "%dx%d, %s", ov->maxwidth, ov->maxheight,
	       symbolic(v4l1_plist, ov->frame[0].format));

	/* Initialize to max width/height, default palette */
	if (mode_init_regs(ov, ov->maxwidth, ov->maxheight,
			   ov->frame[0].format, 0) < 0)
		return -EINVAL;

	return 0;
}

/**********************************************************************
 *
 * Frame buffering
 *
 **********************************************************************/

/* Sleeps until no frames are active. Returns !0 if got signal or unplugged */
static int
ov51x_wait_frames_inactive(struct usb_ovfx2 *ov)
{
	int rc;

	rc = wait_event_interruptible(ov->wq, ov->curframe < 0 || !ov->present);
	if (rc)
		return rc;

	if (ov->present)
		return 0;
	else
		return -ENODEV;
}

/**********************************************************************
 *
 * Raw data parsing
 *
 **********************************************************************/

static void
gbgr_to_bgr24(struct ovfx2_frame *frame,
	      unsigned char *pIn,
	      unsigned char *pOut)
{
	const unsigned int a = frame->rawwidth * frame->rawheight / 2;
	int i;

	for (i = 0; i < a; i++) {
		pOut[0] = pIn[1];
		pOut[1] = pIn[0];
		pOut[2] = pIn[3];
		pOut[3] = pIn[1];
		pOut[4] = pIn[2];
		pOut[5] = pIn[3];

		pIn += 4;
		pOut += 6;
	}
}

/* Flip image vertically */
static void
vert_mirror(unsigned char *pIn,
	    unsigned char *pOut,
	    unsigned int width,
	    unsigned int height,
	    unsigned int depth)
{
	const unsigned int linesize = width * depth;
	int i;

	pOut += height * linesize;

	for (i = 0; i < height; i++) {
		pOut -= linesize;
		memcpy(pOut, pIn, linesize);
		pIn += linesize;
	}
}

/**********************************************************************
 *
 * Image processing
 *
 **********************************************************************/

static void
ovfx2_postprocess(struct usb_ovfx2 *ov, struct ovfx2_frame *frame)
{
	if (dumppix == 1) {		/* Dump with color decoding */
		memset(frame->data, 0,
			MAX_DATA_SIZE(ov->maxwidth, ov->maxheight));
		PDEBUG(4, "Dumping %d bytes", frame->bytes_recvd);
		gbgr_to_bgr24(frame, frame->rawdata, frame->data);
	} else if (dumppix == 2) {	/* Dump raw data */
		memset(frame->data, 0,
			MAX_DATA_SIZE(ov->maxwidth, ov->maxheight));
		PDEBUG(4, "Dumping %d bytes", frame->bytes_recvd);
		memcpy(frame->data, frame->rawdata, frame->bytes_recvd);
	} else {
		/* FIXME: Remove this once decoding is stable */
		memset(frame->data, 0,
			MAX_DATA_SIZE(ov->maxwidth, ov->maxheight));
		memset(frame->tempdata, 0,
			MAX_RAW_DATA_SIZE(ov->maxwidth, ov->maxheight));
		vert_mirror(frame->rawdata, frame->tempdata, frame->rawwidth,
			    frame->rawheight, 2);
		gbgr_to_bgr24(frame, frame->tempdata, frame->data);
	}
}

/**********************************************************************
 *
 * OV51x data transfer, IRQ handler
 *
 **********************************************************************/

static inline void
ovfx2_move_data(struct usb_ovfx2 *ov, unsigned char *in, int n)
{
	int max_raw = MAX_RAW_DATA_SIZE(ov->maxwidth, ov->maxheight);
	struct ovfx2_frame *frame = &ov->frame[ov->curframe];
	struct timeval *ts;

//	info("bytes_recvd = %d;  n = %d", frame->bytes_recvd, n);

	if (frame->scanstate == STATE_LOCKED) {
		PDEBUG(4, "Starting capture on frame %d", frame->framenum);
		frame->scanstate = STATE_RECEIVING;
		frame->bytes_recvd = 0;
	}

	if (n) {
		int b = n;	/* Number of image data bytes */
#if 1
		/* The first 2560 bytes of data (first 2 lines?) are invalid */
		if (frame->bytes_recvd == 0) {
			in += 2560;
			b -= 2560;
		}
#endif
		frame->bytes_recvd += b;
		if (frame->bytes_recvd <= max_raw)
			memcpy(frame->rawdata + frame->bytes_recvd - b, in, b);
		else
			PDEBUG(5, "Raw data buffer overrun!! (%d)",
				frame->bytes_recvd - max_raw);
	} else {
		PDEBUG(5, "Empty bulk packet");
	}

	/* Short packets indicate EOF */
	if (n < OVFX2_BULK_SIZE) {
    		int nextf;

		PDEBUG(4, "Frame end, curframe = %d, recvd=%d",
			ov->curframe, frame->bytes_recvd);

		if (frame->bytes_recvd < max_raw) {
			PDEBUG(3, "Frame was short; discarding");
			frame->grabstate = FRAME_ERROR;
			wake_up_interruptible(&frame->wq);
			return;
		}

		/* Don't allow byte count to exceed buffer size */
		RESTRICT_TO_RANGE(frame->bytes_recvd, 8, max_raw);

		ts = (struct timeval *)(frame->data
		      + MAX_FRAME_SIZE(ov->maxwidth, ov->maxheight));
		do_gettimeofday(ts);

		// FIXME: Since we don't know the header formats yet,
		// there is no way to know what the actual image size is
		frame->rawwidth = frame->width;
		frame->rawheight = frame->height;

		/* Don't wake up user for same frame more than once */
		if (frame->grabstate != FRAME_DONE) {
			frame->grabstate = FRAME_DONE;
			wake_up_interruptible(&frame->wq);
		}

		/* If next frame is ready or grabbing, point to it */
		nextf = (ov->curframe + 1) % OVFX2_NUMFRAMES;
		if (ov->frame[nextf].grabstate == FRAME_READY
		    || ov->frame[nextf].grabstate == FRAME_GRABBING) {
			ov->curframe = nextf;
			ov->frame[nextf].scanstate = STATE_LOCKED;
		} else {
			if (ov->frame[nextf].grabstate == FRAME_DONE) {
				PDEBUG(4, "No empty frames left");
			} else {
				PDEBUG(4, "Frame not ready? state = %d",
				       ov->frame[nextf].grabstate);
			}

			ov->curframe = -1;
		}
	}
}

static void
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 5, 51)
ovfx2_bulk_irq(struct urb *urb, struct pt_regs *regs)
#else
ovfx2_bulk_irq(struct urb *urb)
#endif
{
	int i;
	int actlen = urb->actual_length;
	struct usb_ovfx2 *ov;
	struct ovfx2_sbuf *sbuf;

	if (!urb->context) {
		PDEBUG(4, "no context");
		return;
	}

	sbuf = urb->context;
	ov = sbuf->ov;

	if (!ov || !ov->dev || !ov->user) {
		PDEBUG(4, "no device, or not open");
		return;
	}

	if (!ov->streaming) {
		PDEBUG(4, "hmmm... not streaming, but got interrupt");
		return;
	}

        if (urb->status == -ENOENT || urb->status == -ECONNRESET) {
                PDEBUG(4, "URB unlinked");
                return;
        }

	if (urb->status != -EINPROGRESS && urb->status != 0) {
		err("ERROR: urb->status=%d: %s", urb->status,
		    symbolic(urb_errlist, urb->status));
	}

	PDEBUG(5, "sbuf[%d]: Got bulk completion: actlen=%d", sbuf->n, actlen);

	/* Warning: Don't copy data if no frame active! */
	if (ov->curframe >= 0) {
		ovfx2_move_data(ov, urb->transfer_buffer, actlen);
	} else if (waitqueue_active(&ov->wq)) {
		wake_up_interruptible(&ov->wq);
	}

	/* Resubmit this URB */
	urb->status = 0; // FIXME: Is this necessary? What about w/ OV511/OV518?
	urb->dev = ov->dev;
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 5, 4)
	if ((i = usb_submit_urb(urb, GFP_ATOMIC)) != 0)
#else
	if ((i = usb_submit_urb(urb)) != 0)
#endif
		err("usb_submit_urb() ret %d", i);

	return;
}

/****************************************************************************
 *
 * Stream initialization and termination
 *
 ***************************************************************************/

static int
ovfx2_start_stream(struct usb_ovfx2 *ov)
{
	struct urb *urb;
	int err, n, size;

	PDEBUG(3, "*** Starting video stream ***");

	/* Start stream */
	err = reg_w_mask(ov, 0x0f, 0x02, 0x02);
	if (err)
		return err;

	ov->curframe = -1;

	size = OVFX2_BULK_SIZE;

	for (n = 0; n < OVFX2_NUMSBUF; n++) {
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 5, 5)
		urb = usb_alloc_urb(0, GFP_KERNEL);
#else
		urb = usb_alloc_urb(0);
#endif
		if (!urb) {
			err("start stream: usb_alloc_urb ret. NULL");
			return -ENOMEM;
		}

		usb_fill_bulk_urb(urb,
				  ov->dev,
				  usb_rcvbulkpipe(ov->dev,
						  OVFX2_BULK_ENDPOINT_ADDR),
				  ov->sbuf[n].data,
				  size,
				  ovfx2_bulk_irq,
				  &ov->sbuf[n]);

		ov->sbuf[n].urb = urb;
	}

	ov->streaming = 1;

	for (n = 0; n < OVFX2_NUMSBUF; n++) {
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 5, 4)
		err = usb_submit_urb(ov->sbuf[n].urb, GFP_KERNEL);
#else
		err = usb_submit_urb(ov->sbuf[n].urb);
#endif
		if (err) {
			err("start stream: usb_submit_urb(%d) ret %d", n, err);
			return err;
		}
	}

	return 0;
}

static void
ovfx2_unlink_bulk(struct usb_ovfx2 *ov)
{
	int n;

	/* Unschedule all of the bulk td's */
	for (n = OVFX2_NUMSBUF - 1; n >= 0; n--) {
		if (ov->sbuf[n].urb) {
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 8)
			usb_kill_urb(ov->sbuf[n].urb);
#else
			usb_unlink_urb(ov->sbuf[n].urb);
#endif
			usb_free_urb(ov->sbuf[n].urb);
			ov->sbuf[n].urb = NULL;
		}
	}
}

static int
ovfx2_stop_stream(struct usb_ovfx2 *ov)
{
	if (!ov->streaming)
		return -ENODEV;

	PDEBUG(3, "*** Stopping capture ***");

	ov->streaming = 0;

	ovfx2_unlink_bulk(ov);

	/* Stop stream */
	return reg_w_mask(ov, 0x0f, 0x00, 0x02);
}

static int
ovfx2_new_frame(struct usb_ovfx2 *ov, int framenum)
{
	struct ovfx2_frame *frame;
	int newnum;

	PDEBUG(4, "ov->curframe = %d, framenum = %d", ov->curframe, framenum);

	/* If we're not grabbing a frame right now and the other frame is */
	/* ready to be grabbed into, then use it instead */
	if (ov->curframe == -1) {
		newnum = (framenum - 1 + OVFX2_NUMFRAMES) % OVFX2_NUMFRAMES;
		if (ov->frame[newnum].grabstate == FRAME_READY)
			framenum = newnum;
	} else if (ov->frame[framenum].grabstate == FRAME_ERROR) {
		PDEBUG(4, "restoring broken frame #%d", framenum);
	} else
		return 0;

	frame = &ov->frame[framenum];

	PDEBUG(4, "framenum = %d, width = %d, height = %d", framenum,
	       frame->width, frame->height);

	frame->grabstate = FRAME_GRABBING;
	frame->scanstate = STATE_SCANNING;

	ov->curframe = framenum;

	/* Make sure it's not too big */
	if (frame->width > ov->maxwidth)
		frame->width = ov->maxwidth;

	frame->width &= ~7L;		/* Multiple of 8 */

	if (frame->height > ov->maxheight)
		frame->height = ov->maxheight;

	frame->height &= ~3L;		/* Multiple of 4 */

	return 0;
}

/****************************************************************************
 *
 * Buffer management
 *
 ***************************************************************************/

/*
 * - You must acquire buf_lock before entering this function.
 * - Because this code will free any non-null pointer, you must be sure to null
 *   them if you explicitly free them somewhere else!
 */
static void
ovfx2_do_dealloc(struct usb_ovfx2 *ov)
{
	int i;
	PDEBUG(4, "entered");

	if (ov->fbuf) {
		rvfree(ov->fbuf, OVFX2_NUMFRAMES
		       * MAX_DATA_SIZE(ov->maxwidth, ov->maxheight));
		ov->fbuf = NULL;
	}

	if (ov->rawfbuf) {
		vfree(ov->rawfbuf);
		ov->rawfbuf = NULL;
	}

	if (ov->tempfbuf) {
		vfree(ov->tempfbuf);
		ov->tempfbuf = NULL;
	}

	for (i = 0; i < OVFX2_NUMSBUF; i++) {
		if (ov->sbuf[i].data) {
			kfree(ov->sbuf[i].data);
			ov->sbuf[i].data = NULL;
		}
	}

	for (i = 0; i < OVFX2_NUMFRAMES; i++) {
		ov->frame[i].data = NULL;
		ov->frame[i].rawdata = NULL;
		ov->frame[i].tempdata = NULL;
	}

	PDEBUG(4, "buffer memory deallocated");
	ov->buf_state = BUF_NOT_ALLOCATED;
	PDEBUG(4, "leaving");
}

static int
ovfx2_alloc(struct usb_ovfx2 *ov)
{
	int i;
	const int w = ov->maxwidth;
	const int h = ov->maxheight;
	const int data_bufsize = OVFX2_NUMFRAMES * MAX_DATA_SIZE(w, h);
	const int raw_bufsize = OVFX2_NUMFRAMES * MAX_RAW_DATA_SIZE(w, h);

	PDEBUG(4, "entered");
	down(&ov->buf_lock);

	if (ov->buf_state == BUF_ALLOCATED)
		goto out;

	ov->fbuf = rvmalloc(data_bufsize);
	if (!ov->fbuf)
		goto error;

	ov->rawfbuf = vmalloc(raw_bufsize);
	if (!ov->rawfbuf)
		goto error;

	memset(ov->rawfbuf, 0, raw_bufsize);

	ov->tempfbuf = vmalloc(raw_bufsize);
	if (!ov->tempfbuf)
		goto error;

	memset(ov->tempfbuf, 0, raw_bufsize);

	for (i = 0; i < OVFX2_NUMSBUF; i++) {
		ov->sbuf[i].data = kmalloc(OVFX2_BULK_SIZE, GFP_KERNEL);
		if (!ov->sbuf[i].data)
			goto error;

		PDEBUG(4, "sbuf[%d] @ %p", i, ov->sbuf[i].data);
	}

	for (i = 0; i < OVFX2_NUMFRAMES; i++) {
		ov->frame[i].data = ov->fbuf + i * MAX_DATA_SIZE(w, h);
		ov->frame[i].rawdata = ov->rawfbuf
		 + i * MAX_RAW_DATA_SIZE(w, h);
		ov->frame[i].tempdata = ov->tempfbuf
		 + i * MAX_RAW_DATA_SIZE(w, h);

		PDEBUG(4, "frame[%d] @ %p", i, ov->frame[i].data);
	}

	ov->buf_state = BUF_ALLOCATED;
out:
	up(&ov->buf_lock);
	PDEBUG(4, "leaving");
	return 0;
error:
	ovfx2_do_dealloc(ov);
	up(&ov->buf_lock);
	PDEBUG(4, "errored");
	return -ENOMEM;
}

static void
ovfx2_dealloc(struct usb_ovfx2 *ov)
{
	PDEBUG(4, "entered");
	down(&ov->buf_lock);
	ovfx2_do_dealloc(ov);
	up(&ov->buf_lock);
	PDEBUG(4, "leaving");
}

/****************************************************************************
 *
 * V4L API
 *
 ***************************************************************************/

static int
ovfx2_open(struct inode *inode, struct file *file)
{
	struct video_device *vdev = video_devdata(file);
	struct usb_ovfx2 *ov = video_get_drvdata(vdev);
	int err, i;

	PDEBUG(4, "opening");

	down(&ov->lock);

	err = -ENODEV;
	if (!ov->dev)
		goto out;

	if (ov->sensor == CC_UNKNOWN) {
		err("No sensor is detected yet");
		goto out;
	}

	err = -EBUSY;
	if (ov->user)
		goto out;

	ov->sub_flag = 0;

	/* In case app doesn't set them... */
	err = ovfx2_set_default_params(ov);
	if (err < 0)
		goto out;

	/* Make sure frames are reset */
	for (i = 0; i < OVFX2_NUMFRAMES; i++) {
		ov->frame[i].grabstate = FRAME_UNUSED;
		ov->frame[i].bytes_read = 0;
	}

	err = ovfx2_alloc(ov);
	if (err < 0)
		goto out;

	err = ovfx2_start_stream(ov);
	if (err) {
		ovfx2_dealloc(ov);
		goto out;
	}

	ov->user++;
	file->private_data = vdev;

out:
	up(&ov->lock);

	return err;
}

static int
ovfx2_release(struct inode *inode, struct file *file)
{
	struct video_device *vdev = file->private_data;
	struct usb_ovfx2 *ov = video_get_drvdata(vdev);

	PDEBUG(4, "close");

	down(&ov->lock);
	ov->user--;
	ovfx2_stop_stream(ov);
	ovfx2_dealloc(ov);
	up(&ov->lock);

	/* Device unplugged while open. Only a minimum of unregistration is done
	 * here; the disconnect callback already did the rest. */
	down(&ov_free_lock);
	if (!ov->dev) {
#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 0)
		if (ov->vdev)
			video_device_release(ov->vdev);
#endif
		kfree(ov);
		ov = NULL;
	}
	up(&ov_free_lock);

	file->private_data = NULL;
	return 0;
}

/* Do not call this function directly! */
static int
ovfx2_do_ioctl(struct inode *inode, struct file *file,
			  unsigned int cmd, void *arg)
{
	struct video_device *vdev = file->private_data;
	struct usb_ovfx2 *ov = video_get_drvdata(vdev);

	switch (_IOC_TYPE(cmd)) {
	case 'v':
		PDEBUG(4, "ioctl 0x%x (v4l1, VIDIOC%s)", cmd,
		       (_IOC_NR(cmd) < NUM_V4L1_IOCTLS) ?
		       v4l1_ioctls[_IOC_NR(cmd)] : "???");
		break;
#if defined(HAVE_V4L2)
	case 'V':
		PDEBUG(4, "ioctl 0x%x (v4l2, %s)", cmd,
		       v4l2_ioctl_names[_IOC_NR(cmd)]);
		break;
#endif
	default:
		PDEBUG(4, "ioctl 0x%x (?)", cmd);
	}

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

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

		memset(b, 0, sizeof(struct video_capability));
		sprintf(b->name, "OVFX2 USB Camera");
		b->type = VID_TYPE_CAPTURE | VID_TYPE_SUBCAPTURE;
		b->channels = 1;
		b->maxwidth = ov->maxwidth;
		b->maxheight = ov->maxheight;
		b->minwidth = ov->minwidth;
		b->minheight = ov->minheight;

		return 0;
	}
	case VIDIOCGCHAN:
	{
		struct video_channel *v = arg;

		if ((unsigned)(v->channel) > 0)
			return -EINVAL;

		v->norm = 0;
		v->type = VIDEO_TYPE_CAMERA;
		v->flags = 0;
		v->tuners = 0;
		sprintf(v->name, "%s", "Camera");

		return 0;
	}
	case VIDIOCSCHAN:
	{
		struct video_channel *v = arg;

		return (v->channel == 0) ? 0 : -EINVAL;
	}
	case VIDIOCGPICT:
	{
		struct video_picture *p = arg;

		memset(p, 0, sizeof(struct video_picture));
		if (sensor_get_picture(ov, p))
			return -EIO;

		/* Can we get these from frame[0]? -claudio? */
		p->depth = ov->frame[0].depth;
		p->palette = ov->frame[0].format;

		return 0;
	}
	case VIDIOCSPICT:
	{
		struct video_picture *p = arg;
		int i, rc;

		if (!get_depth(p->palette))
			return -EINVAL;

		if (sensor_set_picture(ov, p))
			return -EIO;

		if (force_palette && p->palette != force_palette) {
			info("SPICT: Palette rejected (%s)",
			     symbolic(v4l1_plist, p->palette));
			return -EINVAL;
		}

		// FIXME: Format should be independent of frames
		if (p->palette != ov->frame[0].format) {
			PDEBUG(4, "SPICT: Detected format change");

			rc = ov51x_wait_frames_inactive(ov);
			if (rc)
				return rc;

			mode_init_regs(ov, ov->frame[0].width,
				ov->frame[0].height, p->palette, ov->sub_flag);
		}

		PDEBUG(4, "SPICT: Setting depth=%d, palette=%s",
		       p->depth, symbolic(v4l1_plist, p->palette));

		for (i = 0; i < OVFX2_NUMFRAMES; i++) {
			ov->frame[i].depth = p->depth;
			ov->frame[i].format = p->palette;
		}

		return 0;
	}
	case VIDIOCGCAPTURE:
	{
		int *vf = arg;

		ov->sub_flag = *vf;
		return 0;
	}
	case VIDIOCSCAPTURE:
	{
		struct video_capture *vc = arg;

		if (vc->flags || vc->decimation)
			return -EINVAL;

		vc->x &= ~3L;
		vc->y &= ~1L;
		vc->y &= ~31L;

		if (vc->width == 0)
			vc->width = 32;

		vc->height /= 16;
		vc->height *= 16;
		if (vc->height == 0)
			vc->height = 16;

		ov->subx = vc->x;
		ov->suby = vc->y;
		ov->subw = vc->width;
		ov->subh = vc->height;

		return 0;
	}
	case VIDIOCSWIN:
	{
		struct video_window *vw = arg;
		int i, rc;

		PDEBUG(4, "Set window: %dx%d", vw->width, vw->height);

/* This code breaks a few apps (Gnomemeeting, possibly
  others). Disable it until a solution can be found */
#if 0
		if (vw->flags || vw->clipcount)
			return -EINVAL;
#endif

		rc = ov51x_wait_frames_inactive(ov);
		if (rc)
			return rc;

		rc = mode_init_regs(ov, vw->width, vw->height,
			ov->frame[0].format, ov->sub_flag);
		if (rc < 0)
			return rc;

		for (i = 0; i < OVFX2_NUMFRAMES; i++) {
			ov->frame[i].width = vw->width;
			ov->frame[i].height = vw->height;
		}

		return 0;
	}
	case VIDIOCGWIN:
	{
		struct video_window *vw = arg;

		memset(vw, 0, sizeof(struct video_window));
		vw->x = 0;		/* FIXME */
		vw->y = 0;
		vw->width = ov->frame[0].width;
		vw->height = ov->frame[0].height;
		vw->flags = 30;

		PDEBUG(4, "Get window: %dx%d", vw->width, vw->height);

		return 0;
	}
	case VIDIOCGMBUF:
	{
		struct video_mbuf *vm = arg;
		int i;

		memset(vm, 0, sizeof(struct video_mbuf));
		vm->size = OVFX2_NUMFRAMES
			   * MAX_DATA_SIZE(ov->maxwidth, ov->maxheight);
		vm->frames = OVFX2_NUMFRAMES;

		vm->offsets[0] = 0;
		for (i = 1; i < OVFX2_NUMFRAMES; i++) {
			vm->offsets[i] = vm->offsets[i-1]
			   + MAX_DATA_SIZE(ov->maxwidth, ov->maxheight);
		}

		return 0;
	}
	case VIDIOCMCAPTURE:
	{
		struct video_mmap *vm = arg;
		int rc, depth;
		unsigned int f = vm->frame;

		PDEBUG(4, "MCAPTURE: frame: %d, %dx%d, %s", f, vm->width,
			vm->height, symbolic(v4l1_plist, vm->format));

		depth = get_depth(vm->format);
		if (!depth) {
			PDEBUG(2, "MCAPTURE: invalid format (%s)",
			       symbolic(v4l1_plist, vm->format));
			return -EINVAL;
		}

		if (f >= OVFX2_NUMFRAMES) {
			err("MCAPTURE: invalid frame (%d)", f);
			return -EINVAL;
		}

		if (vm->width > ov->maxwidth
		    || vm->height > ov->maxheight) {
			err("MCAPTURE: requested dimensions too big");
			return -EINVAL;
		}

		if (ov->frame[f].grabstate == FRAME_GRABBING) {
			PDEBUG(4, "MCAPTURE: already grabbing");
			return -EBUSY;
		}

		if (force_palette && (vm->format != force_palette)) {
			PDEBUG(2, "MCAPTURE: palette rejected (%s)",
			       symbolic(v4l1_plist, vm->format));
			return -EINVAL;
		}

		if ((ov->frame[f].width != vm->width) ||
		    (ov->frame[f].height != vm->height) ||
		    (ov->frame[f].format != vm->format) ||
		    (ov->frame[f].sub_flag != ov->sub_flag) ||
		    (ov->frame[f].depth != depth)) {
			PDEBUG(4, "MCAPTURE: change in image parameters");

			rc = ov51x_wait_frames_inactive(ov);
			if (rc)
				return rc;

			rc = mode_init_regs(ov, vm->width, vm->height,
				vm->format, ov->sub_flag);
#if 0
			if (rc < 0) {
				PDEBUG(1, "Got error while initializing regs ");
				return rc;
			}
#endif
			ov->frame[f].width = vm->width;
			ov->frame[f].height = vm->height;
			ov->frame[f].format = vm->format;
			ov->frame[f].sub_flag = ov->sub_flag;
			ov->frame[f].depth = depth;
		}

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

		return ovfx2_new_frame(ov, f);
	}
	case VIDIOCSYNC:
	{
		unsigned int fnum = *((unsigned int *) arg);
		struct ovfx2_frame *frame;
		int rc;

		if (fnum >= OVFX2_NUMFRAMES) {
			err("SYNC: invalid frame (%d)", fnum);
			return -EINVAL;
		}

		frame = &ov->frame[fnum];

		PDEBUG(4, "syncing to frame %d, grabstate = %d", fnum,
		       frame->grabstate);

		switch (frame->grabstate) {
		case FRAME_UNUSED:
			return -EINVAL;
		case FRAME_READY:
		case FRAME_GRABBING:
		case FRAME_ERROR:
redo:
			rc = wait_event_interruptible(frame->wq,
			    (frame->grabstate == FRAME_DONE)
			    || (frame->grabstate == FRAME_ERROR)
			    || !ov->present);

			if (rc)
				return rc;

			if (!ov->present)
				return -ENODEV;

			if (frame->grabstate == FRAME_ERROR) {
				if ((rc = ovfx2_new_frame(ov, fnum)) < 0)
					return rc;
				goto redo;
			}
			/* Fall through */
		case FRAME_DONE:
			PDEBUG(4, "SYNC: frame %d is DONE", fnum);
			frame->grabstate = FRAME_UNUSED;

			/* Decompression, format conversion, etc... */
			ovfx2_postprocess(ov, frame);

			break;
		} /* end switch */

		return 0;
	}
	case VIDIOCGFBUF:
	{
		struct video_buffer *vb = arg;

		memset(vb, 0, sizeof(struct video_buffer));

		return 0;
	}
	case VIDIOCGUNIT:
	{
		struct video_unit *vu = arg;

		memset(vu, 0, sizeof(struct video_unit));

		vu->video = ov->vdev->minor;
		vu->vbi = VIDEO_NO_UNIT;
		vu->radio = VIDEO_NO_UNIT;
		vu->audio = VIDEO_NO_UNIT;
		vu->teletext = VIDEO_NO_UNIT;

		return 0;
	}
	default:
		PDEBUG(3, "Unsupported IOCtl: 0x%X", cmd);
		return -ENOIOCTLCMD;	/* V4L layer handles this */
	} /* end switch */

	return 0;
}

static int
ovfx2_ioctl(struct inode *inode, struct file *file,
		 unsigned int cmd, unsigned long arg)
{
	struct video_device *vdev = file->private_data;
	struct usb_ovfx2 *ov = video_get_drvdata(vdev);
	int rc;

	if (down_interruptible(&ov->lock))
		return -EINTR;

	rc = video_usercopy(inode, file, cmd, arg, ovfx2_do_ioctl);

	up(&ov->lock);
	return rc;
}

static int
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 8)
ovfx2_read(struct file *file, char __user *buf, size_t cnt, loff_t *ppos)
#else
ovfx2_read(struct file *file, char *buf, size_t cnt, loff_t *ppos)
#endif
{
	struct video_device *vdev = file->private_data;
	int noblock = file->f_flags&O_NONBLOCK;
	unsigned long count = cnt;
	struct usb_ovfx2 *ov = video_get_drvdata(vdev);
	int i, rc = 0, frmx = -1;
	struct ovfx2_frame *frame;

	if (down_interruptible(&ov->lock))
		return -EINTR;

	PDEBUG(4, "%ld bytes, noblock=%d", count, noblock);

	if (!vdev || !buf) {
		rc = -EFAULT;
		goto error;
	}

	if (!ov->dev) {
		rc = -ENODEV;
		goto error;
	}

// FIXME: Only supports two frames
	/* See if a frame is completed, then use it. */
	if (ov->frame[0].grabstate >= FRAME_DONE)	/* _DONE or _ERROR */
		frmx = 0;
	else if (ov->frame[1].grabstate >= FRAME_DONE)/* _DONE or _ERROR */
		frmx = 1;

	/* If nonblocking we return immediately */
	if (noblock && (frmx == -1)) {
		rc = -EAGAIN;
		goto error;
	}

	/* 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 (ov->frame[0].grabstate == FRAME_GRABBING)
			frmx = 0;
		else if (ov->frame[1].grabstate == FRAME_GRABBING)
			frmx = 1;
	}

	/* If no frame is active, start one. */
	if (frmx == -1) {
		if ((rc = ovfx2_new_frame(ov, frmx = 0))) {
			err("read: ovfx2_new_frame error");
			goto error;
		}
	}

	frame = &ov->frame[frmx];

restart:
	/* Wait while we're grabbing the image */
	PDEBUG(4, "Waiting image grabbing");
	rc = wait_event_interruptible(frame->wq,
		(frame->grabstate == FRAME_DONE)
		|| (frame->grabstate == FRAME_ERROR)
		|| !ov->present);

	if (rc)
		goto error;

	if (!ov->present) {
		rc = -ENODEV;
		goto error;
	}

	PDEBUG(4, "Got image, frame->grabstate = %d", frame->grabstate);
	PDEBUG(4, "bytes_recvd = %d", frame->bytes_recvd);

	if (frame->grabstate == FRAME_ERROR) {
		frame->bytes_read = 0;
		err("** ick! ** Errored frame %d", ov->curframe);
		if (ovfx2_new_frame(ov, frmx)) {
			err("read: ovfx2_new_frame error");
			goto error;
		}
		goto restart;
	}

	/* Decompression, format conversion, etc... */
	ovfx2_postprocess(ov, frame);

	PDEBUG(4, "frmx=%d, bytes_read=%ld, length=%ld", frmx,
		frame->bytes_read,
		get_frame_length(frame));

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

	/* FIXME - count hardwired to be one frame... */
	count = get_frame_length(frame);

	PDEBUG(4, "Copy to user space: %ld bytes", count);
	if ((i = copy_to_user(buf, frame->data + frame->bytes_read, count))) {
		PDEBUG(4, "Copy failed! %d bytes not copied", i);
		rc = -EFAULT;
		goto error;
	}

	frame->bytes_read += count;
	PDEBUG(4, "{copy} count used=%ld, new bytes_read=%ld",
		count, frame->bytes_read);

	/* If all data have been read... */
	if (frame->bytes_read
	    >= get_frame_length(frame)) {
		frame->bytes_read = 0;

// FIXME: Only supports two frames
		/* Mark it as available to be used again. */
		ov->frame[frmx].grabstate = FRAME_UNUSED;
		if ((rc = ovfx2_new_frame(ov, !frmx))) {
			err("ovfx2_new_frame returned error");
			goto error;
		}
	}

	PDEBUG(4, "read finished, returning %ld (sweet)", count);

	up(&ov->lock);
	return count;

error:
	up(&ov->lock);
	return rc;
}

static int
ovfx2_mmap(struct file *file, struct vm_area_struct *vma)
{
	struct video_device *vdev = file->private_data;
	unsigned long start = vma->vm_start;
	unsigned long size  = vma->vm_end - vma->vm_start;
	struct usb_ovfx2 *ov = video_get_drvdata(vdev);
	unsigned long page, pos;

	PDEBUG(4, "mmap: %ld (%lX) bytes", size, size);

	if (size > (((OVFX2_NUMFRAMES
	              * MAX_DATA_SIZE(ov->maxwidth, ov->maxheight)
	              + PAGE_SIZE - 1) & ~(PAGE_SIZE - 1))))
		return -EINVAL;

	if (down_interruptible(&ov->lock))
		return -EINTR;

	if (!ov->dev) {
		up(&ov->lock);
		return -ENODEV;
	}

	pos = (unsigned long)ov->fbuf;
	while (size > 0) {
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 10)
		page = vmalloc_to_pfn((void *)pos);
		if (remap_pfn_range(vma, start, page, PAGE_SIZE, PAGE_SHARED)) {
#elif LINUX_VERSION_CODE >= KERNEL_VERSION(2, 5, 3) || defined(RH9_REMAP)
		page = kvirt_to_pa(pos);
		if (remap_page_range(vma, start, page, PAGE_SIZE,
				     PAGE_SHARED)) {
#else
		page = kvirt_to_pa(pos);
		if (remap_page_range(start, page, PAGE_SIZE, PAGE_SHARED)) {
#endif
			up(&ov->lock);
			return -EAGAIN;
		}
		start += PAGE_SIZE;
		pos += PAGE_SIZE;
		if (size > PAGE_SIZE)
			size -= PAGE_SIZE;
		else
			size = 0;
	}

	up(&ov->lock);
	return 0;
}

static struct file_operations ovfx2_fops = {
	.owner =	THIS_MODULE,
	.open =		ovfx2_open,
	.release =	ovfx2_release,
	.read =		ovfx2_read,
	.mmap =		ovfx2_mmap,
	.ioctl =	ovfx2_ioctl,
	.llseek =	no_llseek,
};

static struct video_device vdev_template = {
	.owner =	THIS_MODULE,
	.name =		"OVFX2 USB Camera",
	.type =		VID_TYPE_CAPTURE,
	.hardware =	VID_HARDWARE_OV511,	/* FIXME */
	.fops =		&ovfx2_fops,
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 0)
	.release =	video_device_release,
#endif
	.minor =	-1,
};

/****************************************************************************
 *
 * FX2 and sensor configuration
 *
 ***************************************************************************/

/* This is called when an OV7610, OV7620, or OV76BE is detected. */
static int
ov7xx0_configure(struct usb_ovfx2 *ov)
{
	if (ov->sensor != CC_OV7620)
		return -ENODEV;

	ov->internal_client.addr = OV7xx0_SID;

	ov->maxwidth = 640;
	ov->maxheight = 480;

#if 0
	ov->minwidth = 64;
	ov->minheight = 48;
#else
	/* Can't do less than max res yet */
	ov->minwidth = ov->maxwidth;
	ov->minheight = ov->maxheight;
#endif

	return camchip_init_settings(ov);
}

/* This initializes the FX2 and the sensor */
static int
ovfx2_configure(struct usb_ovfx2 *ov)
{
	static struct regval regvals_init[] = {
		/* Reset */
		{ 0x0f, 0x01, 0xff },
		{ 0x0f, 0x03, 0xff },
		{ 0x0f, 0x01, 0xff },

		{ 0x0f, 0x08, 0x08 },

		/* Default camera interface settings */
		{ 0xee, 0x80, 0xff },
		{ 0x0f, 0x04, 0x04 },
		{ 0xe0, 0x46, 0xff },
		{ 0xe1, 0xf8, 0xff },
		{ 0xe2, 0x02, 0xff },
		{ 0xe3, 0x07, 0xff },
		{ 0xe4, 0x49, 0xff },
		{ 0xe5, 0xf0, 0xff },
		{ 0xe6, 0x0c, 0xff },
		{ 0xe7, 0xf4, 0xff },
		{ 0xe8, 0x3f, 0xff },

		{ 0x0f, 0x80, 0x80 },

		/* Default picture settings */
		{ REG_SAT,	 187, 0xff },
		{ REG_HUE,	0x00, 0xff },
		{ REG_CNTR,	0x80, 0xff },
		{ REG_BRIGHT,	0x14, 0xff },
		{ REG_SHARP,	  88, 0xff },

		{ 0x00, 0x00, 0x00 }, /* end */
	};

	PDEBUG(4, "");

	if (write_regvals(ov, regvals_init)) goto error;

	ovfx2_init_i2c(ov);

	return 0;

error:
	err("OVFX2 Config failed");
	return -EBUSY;
}

#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 0)
/****************************************************************************
 *  sysfs
 ***************************************************************************/

static inline struct usb_ovfx2 *cd_to_ov(struct class_device *cd)
{
	struct video_device *vdev = to_video_device(cd);
	return video_get_drvdata(vdev);
}

static ssize_t show_model(struct class_device *cd, char *buf)
{
	struct usb_ovfx2 *ov = cd_to_ov(cd);
	return sprintf(buf, "%s\n", ov->desc);
} 
static CLASS_DEVICE_ATTR(model, S_IRUGO, show_model, NULL);


static ssize_t show_sensor(struct class_device *cd, char *buf)
{
	struct usb_ovfx2 *ov = cd_to_ov(cd);
	return sprintf(buf, "%s\n", symbolic(senlist, ov->sensor));
} 
static CLASS_DEVICE_ATTR(sensor, S_IRUGO, show_sensor, NULL);

static ssize_t show_brightness(struct class_device *cd, char *buf)
{
	struct usb_ovfx2 *ov = cd_to_ov(cd);
	int x;

	if (!ov->dev)
		return -ENODEV;
	sensor_get_control(ov, OVCAMCHIP_CID_BRIGHT, &x);
	return sprintf(buf, "%d\n", x);
} 
static CLASS_DEVICE_ATTR(brightness, S_IRUGO, show_brightness, NULL);

static ssize_t show_saturation(struct class_device *cd, char *buf)
{
	struct usb_ovfx2 *ov = cd_to_ov(cd);
	int x;

	if (!ov->dev)
		return -ENODEV;
	sensor_get_control(ov, OVCAMCHIP_CID_SAT, &x);
	return sprintf(buf, "%d\n", x);
} 
static CLASS_DEVICE_ATTR(saturation, S_IRUGO, show_saturation, NULL);

static ssize_t show_contrast(struct class_device *cd, char *buf)
{
	struct usb_ovfx2 *ov = cd_to_ov(cd);
	int x;

	if (!ov->dev)
		return -ENODEV;
	sensor_get_control(ov, OVCAMCHIP_CID_CONT, &x);
	return sprintf(buf, "%d\n", x);
} 
static CLASS_DEVICE_ATTR(contrast, S_IRUGO, show_contrast, NULL);

static ssize_t show_hue(struct class_device *cd, char *buf)
{
	struct usb_ovfx2 *ov = cd_to_ov(cd);
	int x;

	if (!ov->dev)
		return -ENODEV;
	sensor_get_control(ov, OVCAMCHIP_CID_HUE, &x);
	return sprintf(buf, "%d\n", x);
} 
static CLASS_DEVICE_ATTR(hue, S_IRUGO, show_hue, NULL);

static ssize_t show_exposure(struct class_device *cd, char *buf)
{
	struct usb_ovfx2 *ov = cd_to_ov(cd);
	int x;

	if (!ov->dev)
		return -ENODEV;
	sensor_get_control(ov, OVCAMCHIP_CID_EXP, &x);
	return sprintf(buf, "%d\n", x);
} 
static CLASS_DEVICE_ATTR(exposure, S_IRUGO, show_exposure, NULL);

static void ov_create_sysfs(struct video_device *vdev)
{
	video_device_create_file(vdev, &class_device_attr_model);
	video_device_create_file(vdev, &class_device_attr_sensor);
	video_device_create_file(vdev, &class_device_attr_brightness);
	video_device_create_file(vdev, &class_device_attr_saturation);
	video_device_create_file(vdev, &class_device_attr_contrast);
	video_device_create_file(vdev, &class_device_attr_hue);
	video_device_create_file(vdev, &class_device_attr_exposure);
}
#endif

/****************************************************************************
 *
 *  USB routines
 *
 ***************************************************************************/

#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 5, 36)
static int
ovfx2_probe(struct usb_interface *intf, const struct usb_device_id *id)
{
	struct usb_device *dev = interface_to_usbdev(intf);
#else
static void *
ovfx2_probe(struct usb_device *dev, unsigned int ifnum,
	    const struct usb_device_id *id)
{
#endif
	struct usb_ovfx2 *ov;
	int i;

	PDEBUG(1, "probing for device...");

#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 4, 20)
	/* Since code below may sleep, we use this as a lock */
	MOD_INC_USE_COUNT;
#endif

	if ((ov = kmalloc(sizeof(*ov), GFP_KERNEL)) == NULL) {
		err("couldn't kmalloc ov struct");
		goto error_out;
	}

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

	ov->dev = dev;
	ov->stop_during_set = !fastset;
	ov->sensor = CC_UNKNOWN;

	/* FIXME: Determine this from the VID/PID */
	ov->bridge = BRG_2800;		/* iBot2 */

	init_waitqueue_head(&ov->wq);

	init_MUTEX(&ov->lock);	/* to 1 == available */
	init_MUTEX(&ov->buf_lock);
	init_MUTEX(&ov->cbuf_lock);

	ov->buf_state = BUF_NOT_ALLOCATED;

#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 4, 20)
	if (usb_make_path(dev, ov->usb_path, OVFX2_USB_PATH_LEN) < 0) {
		err("usb_make_path error");
		goto error;
	}
#else
	snprintf(ov->usb_path, OVFX2_USB_PATH_LEN, "dev:%d/bus:%d",
		dev->bus->busnum, dev->devnum);
#endif

	/* Allocate control transfer buffer. */
	/* Must be kmalloc()'ed, for DMA compatibility */
	ov->cbuf = kmalloc(OVFX2_CBUF_SIZE, GFP_KERNEL);
	if (!ov->cbuf)
		goto error;

	if (ovfx2_configure(ov) < 0)
		goto error;

	for (i = 0; i < OVFX2_NUMFRAMES; i++) {
		ov->frame[i].framenum = i;
		init_waitqueue_head(&ov->frame[i].wq);
	}

	for (i = 0; i < OVFX2_NUMSBUF; i++) {
		ov->sbuf[i].ov = ov;
		spin_lock_init(&ov->sbuf[i].lock);
		ov->sbuf[i].n = i;
	}

#ifdef OVFX2_DEBUG
	if (dump_bridge)
		ovfx2_dump_regs(ov);
#endif

	ov->vdev = video_device_alloc();
	if (!ov->vdev)
		goto error;

	memcpy(ov->vdev, &vdev_template, sizeof(*ov->vdev));
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 0)
	ov->vdev->dev = &dev->dev;
#endif
	video_set_drvdata(ov->vdev, ov);

	for (i = 0; i < OVFX2_MAX_UNIT_VIDEO; i++) {
		/* Minor 0 cannot be specified; assume user wants autodetect */
		if (unit_video[i] == 0)
			break;

		if (video_register_device(ov->vdev, VFL_TYPE_GRABBER,
			unit_video[i]) >= 0) {
			break;
		}
	}

	/* Use the next available one */
	if ((ov->vdev->minor == -1) &&
	    video_register_device(ov->vdev, VFL_TYPE_GRABBER, -1) < 0) {
		err("video_register_device failed");
		goto error;
	}

	info("%s at %s registered to minor %d (%s speed)",
	     (char *) id->driver_info, ov->usb_path, ov->vdev->minor,
	     (dev->speed == USB_SPEED_HIGH) ? "high" : "full");

	/* Update I2C adapter name with minor # */
	sprintf(i2c_adapname(&ov->i2c_adap), "OVFX2 #%d", ov->vdev->minor);

	create_proc_ovfx2_cam(ov);

#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 0)
	usb_set_intfdata(intf, ov);
	ov_create_sysfs(ov->vdev);
#endif

	ov->present = 1;

#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 4, 20)
	MOD_DEC_USE_COUNT;
#endif
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 0)
	return 0;
#else
	return ov;
#endif

error:
	if (ov->vdev) {
		if (-1 == ov->vdev->minor)
			video_device_release(ov->vdev);
		else
			video_unregister_device(ov->vdev);
		ov->vdev = NULL;
	}

	if (ov->cbuf) {
		down(&ov->cbuf_lock);
		kfree(ov->cbuf);
		ov->cbuf = NULL;
		up(&ov->cbuf_lock);
	}

	if (ov) {
		kfree(ov);
		ov = NULL;
	}

error_out:
#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 4, 20)
	MOD_DEC_USE_COUNT;
#endif
	err("Camera initialization failed");
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 5, 36)
	return -EIO;
#else
	return NULL;
#endif
}

static void
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 0)
ovfx2_disconnect(struct usb_interface *intf)
{
	struct usb_ovfx2 *ov = usb_get_intfdata(intf);
#else
ovfx2_disconnect(struct usb_device *dev, void *ptr)
{
	struct usb_ovfx2 *ov = (struct usb_ovfx2 *) ptr;
#endif
	int n;

#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 4, 20)
	MOD_INC_USE_COUNT;
#endif

	PDEBUG(3, "");

	ov->present = 0;

	/* Ensure that no synchronous control requests
	 * are active before disconnect() returns */
	down(&ov->cbuf_lock);
	kfree(ov->cbuf);
	ov->cbuf = NULL;
	up(&ov->cbuf_lock);

#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 5, 36)
	dev_set_drvdata(&intf->dev, NULL);
#endif
	if (!ov)
		return;

#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 0)
	if (ov->vdev)
		video_unregister_device(ov->vdev);
#else
	video_unregister_device(ov->vdev);
	if (ov->user)
		PDEBUG(3, "Device open...deferring video_unregister_device");
#endif

	for (n = 0; n < OVFX2_NUMFRAMES; n++)
		ov->frame[n].grabstate = FRAME_ERROR;

	ov->curframe = -1;

	/* This will cause the process to request another frame */
	for (n = 0; n < OVFX2_NUMFRAMES; n++)
		wake_up_interruptible(&ov->frame[n].wq);

	wake_up_interruptible(&ov->wq);

	/* Can't take lock earlier since it would deadlock
	 * read() or ioctl() if open and sleeping */
	down(&ov->lock);

	ov->streaming = 0;
	ovfx2_unlink_bulk(ov);

        destroy_proc_ovfx2_cam(ov);

	i2c_del_adapter(&ov->i2c_adap);

	ov->dev = NULL;

	up(&ov->lock);

	down(&ov_free_lock);
	if (ov && !ov->user) {
#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 0)
		if (ov->vdev)
			video_device_release(ov->vdev);
#endif
		kfree(ov);
		ov = NULL;
	}
	up(&ov_free_lock);

#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 4, 20)
	MOD_DEC_USE_COUNT;
#endif
	PDEBUG(3, "Disconnect complete");
}

static struct usb_driver ovfx2_driver = {
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 4, 20)
	.owner =	THIS_MODULE,
#endif
	.name =		"ovfx2",
	.id_table =	device_table,
	.probe =	ovfx2_probe,
	.disconnect =	ovfx2_disconnect
};

/****************************************************************************
 *
 *  Module routines
 *
 ***************************************************************************/

static int __init
usb_ovfx2_init(void)
{
#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 5, 0)
	EXPORT_NO_SYMBOLS;
#endif
	init_MUTEX(&ov_free_lock);

        proc_ovfx2_create();

	if (usb_register(&ovfx2_driver) < 0)
		return -1;

	info(DRIVER_VERSION " : " DRIVER_DESC);

	return 0;
}

static void __exit
usb_ovfx2_exit(void)
{
	usb_deregister(&ovfx2_driver);
	info("driver deregistered");

        proc_ovfx2_destroy();
}

module_init(usb_ovfx2_init);
module_exit(usb_ovfx2_exit);
