patch-2.3.99-pre7 linux/drivers/usb/ov511.c
Next file: linux/drivers/usb/ov511.h
Previous file: linux/drivers/usb/mousedev.c
Back to the patch index
Back to the overall index
- Lines: 2781
- Date:
Thu May 11 13:45:35 2000
- Orig file:
v2.3.99-pre6/linux/drivers/usb/ov511.c
- Orig date:
Tue Apr 11 15:09:20 2000
diff -u --recursive --new-file v2.3.99-pre6/linux/drivers/usb/ov511.c linux/drivers/usb/ov511.c
@@ -1,23 +1,20 @@
/*
* OmniVision OV511 Camera-to-USB Bridge Driver
- * Copyright (c) 1999/2000 Mark W. McClelland
+ *
+ * Copyright (c) 1999-2000 Mark W. McClelland
* Many improvements by Bret Wallach
* Color fixes by by Orion Sky Lawlor, olawlor@acm.org, 2/26/2000
* Snapshot code by Kevin Moore
* OV7620 fixes by Charl P. Botha <cpbotha@ieee.org>
+ * Changes by Claudio Matsuoka, claudio@conectiva.com, 3/26/2000
*
- * Based on the Linux CPiA driver.
+ * Based on the Linux CPiA driver written by Peter Pregler,
+ * Scott J. Bertin and Johannes Erdfelt.
*
- * Released under GPL v.2 license.
- *
- * Version: 1.11
- *
* Please see the file: linux/Documentation/usb/ov511.txt
- * and the website at: http://alpha.dyndns.org/ov511
+ * and the website at: http://alpha.dyndns.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
@@ -33,23 +30,28 @@
* Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
+static const char version[] = "1.13";
+
#define __NO_VERSION__
-#include <linux/kernel.h>
-#include <linux/sched.h>
-#include <linux/list.h>
-#include <linux/malloc.h>
-#include <linux/mm.h>
-#include <linux/smp_lock.h>
-#include <linux/videodev.h>
-#include <linux/vmalloc.h>
-#include <linux/wrapper.h>
+#include <linux/config.h>
#include <linux/module.h>
+#include <linux/version.h>
#include <linux/init.h>
-#include <linux/spinlock.h>
-#include <linux/time.h>
+#include <linux/fs.h>
+#include <linux/vmalloc.h>
+#include <linux/slab.h>
+#include <linux/proc_fs.h>
+#include <linux/ctype.h>
+#include <linux/pagemap.h>
#include <linux/usb.h>
#include <asm/io.h>
+#include <asm/semaphore.h>
+#include <linux/wrapper.h>
+
+#ifdef CONFIG_KMOD
+#include <linux/kmod.h>
+#endif
#include "ov511.h"
@@ -59,11 +61,10 @@
#define MAX_FRAME_SIZE (640 * 480 * 3)
#define MAX_DATA_SIZE (MAX_FRAME_SIZE + sizeof(struct timeval))
-// FIXME - Should find a better way to do this.
#define DEFAULT_WIDTH 640
#define DEFAULT_HEIGHT 480
-// PARAMETER VARIABLES:
+/* PARAMETER VARIABLES: */
static int autoadjust = 1; /* CCD dynamically changes exposure, etc... */
/* 0=no debug messages
@@ -85,24 +86,61 @@
/* Sensor detection override (global for all attached cameras) */
static int sensor = 0;
+/* Increase this if you are getting "Failed to read sensor ID..." */
+static int i2c_detect_tries = 5;
+
+/* For legal values, see the OV7610/7620 specs under register Common F,
+ upper nybble (set to 0-F) */
+static int aperture = -1;
+
MODULE_PARM(autoadjust, "i");
MODULE_PARM(debug, "i");
MODULE_PARM(fix_rgb_offset, "i");
MODULE_PARM(snapshot, "i");
MODULE_PARM(sensor, "i");
+MODULE_PARM(i2c_detect_tries, "i");
+MODULE_PARM(aperture, "i");
MODULE_AUTHOR("Mark McClelland (and others)");
MODULE_DESCRIPTION("OV511 USB Camera Driver");
char kernel_version[] = UTS_RELEASE;
-/*******************************/
-/* Memory management functions */
-/*******************************/
+static struct usb_driver ov511_driver;
-#define MDEBUG(x) do { } while(0) /* Debug memory management */
+/**********************************************************************
+ * List of known OV511-based cameras
+ **********************************************************************/
+
+static struct cam_list clist[] = {
+ { 0, "generic model (no ID)" },
+ { 3, "D-Link DSB-C300" },
+ { 4, "generic OV511/OV7610" },
+ { 5, "Puretek PT-6007" },
+ { 21, "Creative Labs WebCam 3" },
+ { 36, "Koala-Cam" },
+ { 38, "Lifeview USB Life TV" }, /* No support yet! */
+ { 100, "Lifeview RoboCam" },
+ { 102, "AverMedia InterCam Elite" },
+ { 112, "MediaForte MV300" }, /* or OV7110 evaluation kit */
+ { -1, NULL }
+};
-static struct usb_driver ov511_driver;
+/**********************************************************************
+ *
+ * Memory management
+ *
+ * This is a shameless copy from the USB-cpia driver (linux kernel
+ * version 2.3.29 or so, I have no idea what this code actually does ;).
+ * Actually it seems to be a copy of a shameless copy of the bttv-driver.
+ * Or that is a copy of a shameless copy of ... (To the powers: is there
+ * no generic kernel-function to do this sort of stuff?)
+ *
+ * Yes, it was a shameless copy from the bttv-driver. IIRC, Alan says
+ * there will be one, but apparentely not yet -jerdfelt
+ *
+ * So I copied it again for the OV511 driver -claudio
+ **********************************************************************/
/* Given PGD from the address space's page table, return the kernel
* virtual mapping of the physical memory mapped at ADR.
@@ -119,31 +157,11 @@
ptep = pte_offset(pmd, adr);
pte = *ptep;
if (pte_present(pte))
- ret = page_address(pte_page(pte)) | (adr & (PAGE_SIZE-1));
+ ret = page_address(pte_page(pte)) |
+ (adr & (PAGE_SIZE-1));
}
}
- MDEBUG(printk("uv2kva(%lx-->%lx)", adr, ret));
- 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);
- MDEBUG(printk("uv2b(%lx-->%lx)", adr, ret));
- 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);
- MDEBUG(printk("kv2b(%lx-->%lx)", adr, ret));
return ret;
}
@@ -158,7 +176,6 @@
va = VMALLOC_VMADDR(adr);
kva = uvirt_to_kva(pgd_offset_k(va), va);
ret = __pa(kva);
- MDEBUG(printk("kv2pa(%lx-->%lx)", adr, ret));
return ret;
}
@@ -213,9 +230,269 @@
vfree(mem);
}
+/**********************************************************************
+ * /proc interface
+ * Based on the CPiA driver version 0.7.4 -claudio
+ **********************************************************************/
+
+#ifdef CONFIG_PROC_FS
+static struct proc_dir_entry *ov511_proc_root = NULL;
+
+#define YES_NO(x) ((x) ? "yes" : "no")
+
+static int ov511_read_proc(char *page, char **start, off_t off,
+ int count, int *eof, void *data)
+{
+ char *out = page;
+ int i, len;
+ struct usb_ov511 *ov511 = data;
+
+ /* IMPORTANT: This output MUST be kept under PAGE_SIZE
+ * or we need to get more sophisticated. */
+
+ out += sprintf (out, "custom_id : %d\n", ov511->customid);
+ out += sprintf (out, "model : %s\n", ov511->desc ?
+ clist[ov511->desc].description : "unknown");
+ out += sprintf (out, "streaming : %s\n", YES_NO (ov511->streaming));
+ out += sprintf (out, "grabbing : %s\n", YES_NO (ov511->grabbing));
+ out += sprintf (out, "compress : %s\n", YES_NO (ov511->compress));
+ out += sprintf (out, "subcapture : %s\n", YES_NO (ov511->sub_flag));
+ out += sprintf (out, "sub_size : %d %d %d %d\n",
+ ov511->subx, ov511->suby, ov511->subw, ov511->subh);
+ out += sprintf (out, "num_frames : %d\n", OV511_NUMFRAMES);
+ for (i = 0; i < OV511_NUMFRAMES; i++) {
+ out += sprintf (out, "frame : %d\n", i);
+ out += sprintf (out, " depth : %d\n",
+ ov511->frame[i].depth);
+ out += sprintf (out, " size : %d %d\n",
+ ov511->frame[i].width, ov511->frame[i].height);
+ out += sprintf (out, " hdr_size : %d %d\n",
+ ov511->frame[i].hdrwidth, ov511->frame[i].hdrheight);
+ out += sprintf (out, " format : %d\n",
+ ov511->frame[i].format);
+ out += sprintf (out, " segsize : %d\n",
+ ov511->frame[i].segsize);
+#if 0
+ out += sprintf (out, " curline : %d\n",
+ ov511->frame[i].curline);
+ out += sprintf (out, " segment : %d\n",
+ ov511->frame[i].segment);
+ out += sprintf (out, " scanlength : %ld\n",
+ ov511->frame[i].scanlength);
+ out += sprintf (out, " bytesread : %ld\n",
+ ov511->frame[i].bytes_read);
+#endif
+ }
+ out += sprintf (out, "snap_enabled : %s\n", YES_NO (ov511->snap_enabled));
+ out += sprintf (out, "bridge : %d\n", ov511->bridge);
+ out += sprintf (out, "sensor : %d\n", ov511->sensor);
+ out += sprintf (out, "packet_size : %d\n", ov511->packet_size);
+
+ 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 ov511_write_proc(struct file *file, const char *buffer,
+ unsigned long count, void *data)
+{
+ int retval = -EINVAL;
+
+#if 0
+ /* struct cam_data *cam = data; */
+ struct usb_ov511 new_params;
+ int size = count;
+ int find_colon;
+ unsigned long val;
+ u32 command_flags = 0;
+ u8 new_mains;
+
+ if (down_interruptible(&cam->param_lock))
+ return -ERESTARTSYS;
+
+ /*
+ * Skip over leading whitespace
+ */
+ while (count && isspace(*buffer)) {
+ --count;
+ ++buffer;
+ }
+
+
+#define MATCH(x) \
+ ({ \
+ int _len = strlen(x), _ret, _colon_found; \
+ _ret = (_len <= count && strncmp(buffer, x, _len) == 0); \
+ if (_ret) { \
+ buffer += _len; \
+ count -= _len; \
+ if (find_colon) { \
+ _colon_found = 0; \
+ while (count && (*buffer == ' ' || *buffer == '\t' || \
+ (!_colon_found && *buffer == ':'))) { \
+ if (*buffer == ':') \
+ _colon_found = 1; \
+ --count; \
+ ++buffer; \
+ } \
+ if (!count || !_colon_found) \
+ retval = -EINVAL; \
+ find_colon = 0; \
+ } \
+ } \
+ _ret; \
+ })
+
+#define VALUE \
+ ({ \
+ char *_p; \
+ unsigned long int _ret; \
+ _ret = simple_strtoul(buffer, &_p, 0); \
+ if (_p == buffer) \
+ retval = -EINVAL; \
+ else { \
+ count -= _p - buffer; \
+ buffer = _p; \
+ } \
+ _ret; \
+ })
+
+
+ retval = 0;
+ while (count && !retval) {
+ find_colon = 1;
+
+ if (MATCH("")) {
+ if (!retval)
+ val = VALUE;
+
+ if (!retval) {
+ if (val <= 0xff)
+ /* ... = val */ ;
+ else
+ retval = -EINVAL;
+ }
+ } else {
+ DBG("No match found\n");
+ retval = -EINVAL;
+ }
+
+ if (!retval) {
+ while (count && isspace(*buffer) && *buffer != '\n') {
+ --count;
+ ++buffer;
+ }
+ if (count) {
+ if (*buffer != '\n' && *buffer != ';')
+ retval = -EINVAL;
+ else {
+ --count;
+ ++buffer;
+ }
+ }
+ }
+ }
+
+#undef MATCH
+#undef FIRMWARE_VERSION
+#undef VALUE
+#undef FIND_VALUE
+#undef FIND_END
+ if (!retval) {
+ if (command_flags & COMMAND_SETCOLOURPARAMS) {
+ /* Adjust cam->vp to reflect these changes */
+ cam->vp.brightness =
+ new_params.colourParams.brightness*65535/100;
+ cam->vp.contrast =
+ new_params.colourParams.contrast*65535/100;
+ cam->vp.colour =
+ new_params.colourParams.saturation*65535/100;
+ }
+
+ memcpy(&cam->params, &new_params, sizeof(struct cam_params));
+ cam->mainsFreq = new_mains;
+ cam->cmd_queue |= command_flags;
+ retval = size;
+ } else
+ PDEBUG(3, "error: %d\n", retval);
+
+ up(&cam->param_lock);
+#endif
+
+ return retval;
+}
+
+static void create_proc_ov511_cam (struct usb_ov511 *ov511)
+{
+ char name[7];
+ struct proc_dir_entry *ent;
+
+ PDEBUG (4, "***************");
+ if (!ov511_proc_root || !ov511)
+ return;
+
+ sprintf(name, "video%d", ov511->vdev.minor);
+ PDEBUG (4, "==== name: %s", name);
+
+ ent = create_proc_entry(name, S_IFREG|S_IRUGO|S_IWUSR, ov511_proc_root);
+ if (!ent)
+ return;
+
+ ent->data = ov511;
+ ent->read_proc = ov511_read_proc;
+ ent->write_proc = ov511_write_proc;
+ ent->size = 3626; /* FIXME */
+ ov511->proc_entry = ent;
+}
+
+static void destroy_proc_ov511_cam (struct usb_ov511 *ov511)
+{
+ char name[7];
+
+ if (!ov511 || !ov511->proc_entry)
+ return;
+
+ sprintf(name, "video%d", ov511->vdev.minor);
+ PDEBUG (4, "==== name: %s", name);
+#if 0
+ remove_proc_entry(name, ov511_proc_root);
+ ov511->proc_entry = NULL;
+#endif
+}
+
+static void proc_ov511_create(void)
+{
+ ov511_proc_root = create_proc_entry("ov511", S_IFDIR, 0);
+
+ if (ov511_proc_root)
+ ov511_proc_root->owner = THIS_MODULE;
+ else
+ printk("Unable to initialise /proc/ov511\n"); /***********/
+}
+
+static void proc_ov511_destroy(void)
+{
+ remove_proc_entry("ov511", 0);
+}
+#endif /* CONFIG_PROC_FS */
+
+
+/**********************************************************************
+ *
+ * Camera interface
+ *
+ **********************************************************************/
+
static int ov511_reg_write(struct usb_device *dev,
- unsigned char reg,
- unsigned char value)
+ unsigned char reg,
+ unsigned char value)
{
int rc;
@@ -233,6 +510,7 @@
return rc;
}
+
/* returns: negative is error, pos or zero is data */
static int ov511_reg_read(struct usb_device *dev, unsigned char reg)
{
@@ -247,24 +525,25 @@
PDEBUG(5, "reg read: 0x%02X:0x%02X", reg, buffer[0]);
- if(rc < 0) {
+ if (rc < 0) {
err("ov511_reg_read: error %d", rc);
return rc;
- }
- else
+ } else {
return buffer[0];
+ }
}
+
static int ov511_i2c_write(struct usb_device *dev,
- unsigned char reg,
- unsigned char value)
+ unsigned char reg,
+ unsigned char value)
{
int rc, retries;
PDEBUG(5, "i2c write: 0x%02X:0x%02X", reg, value);
/* Three byte write cycle */
- for(retries = OV511_I2C_RETRIES;;) {
+ for (retries = OV511_I2C_RETRIES; ; ) {
/* Select camera register */
rc = ov511_reg_write(dev, OV511_REG_I2C_SUB_ADDRESS_3_BYTE, reg);
if (rc < 0) goto error;
@@ -278,10 +557,10 @@
if (rc < 0) goto error;
do rc = ov511_reg_read(dev, OV511_REG_I2C_CONTROL);
- while(rc > 0 && ((rc&1) == 0)); /* Retry until idle */
+ while (rc > 0 && ((rc&1) == 0)); /* Retry until idle */
if (rc < 0) goto error;
- if((rc&2) == 0) /* Ack? */
+ if ((rc&2) == 0) /* Ack? */
break;
#if 0
/* I2C abort */
@@ -301,13 +580,14 @@
return rc;
}
+
/* returns: negative is error, pos or zero is data */
static int ov511_i2c_read(struct usb_device *dev, unsigned char reg)
{
int rc, value, retries;
/* Two byte write cycle */
- for(retries = OV511_I2C_RETRIES;;) {
+ for (retries = OV511_I2C_RETRIES; ; ) {
/* Select camera register */
rc = ov511_reg_write(dev, OV511_REG_I2C_SUB_ADDRESS_2_BYTE, reg);
if (rc < 0) goto error;
@@ -317,10 +597,10 @@
if (rc < 0) goto error;
do rc = ov511_reg_read(dev, OV511_REG_I2C_CONTROL);
- while(rc > 0 && ((rc&1) == 0)); /* Retry until idle */
+ while (rc > 0 && ((rc&1) == 0)); /* Retry until idle */
if (rc < 0) goto error;
- if((rc&2) == 0) /* Ack? */
+ if ((rc&2) == 0) /* Ack? */
break;
/* I2C abort */
@@ -334,16 +614,16 @@
}
/* Two byte read cycle */
- for(retries = OV511_I2C_RETRIES;;) {
+ for (retries = OV511_I2C_RETRIES; ; ) {
/* Initiate 2-byte read cycle */
rc = ov511_reg_write(dev, OV511_REG_I2C_CONTROL, 0x05);
if (rc < 0) goto error;
do rc = ov511_reg_read(dev, OV511_REG_I2C_CONTROL);
- while(rc > 0 && ((rc&1) == 0)); /* Retry until idle */
+ while (rc > 0 && ((rc&1) == 0)); /* Retry until idle */
if (rc < 0) goto error;
- if((rc&2) == 0) /* Ack? */
+ if ((rc&2) == 0) /* Ack? */
break;
/* I2C abort */
@@ -363,7 +643,8 @@
/* This is needed to make ov511_i2c_write() work */
rc = ov511_reg_write(dev, OV511_REG_I2C_CONTROL, 0x05);
- if (rc < 0) goto error;
+ if (rc < 0)
+ goto error;
return value;
@@ -372,12 +653,13 @@
return rc;
}
+
static int ov511_write_regvals(struct usb_device *dev,
struct ov511_regvals * pRegvals)
{
int rc;
- while(pRegvals->bus != OV511_DONE_BUS) {
+ while (pRegvals->bus != OV511_DONE_BUS) {
if (pRegvals->bus == OV511_REG_BUS) {
if ((rc = ov511_reg_write(dev, pRegvals->reg,
pRegvals->val)) < 0)
@@ -400,6 +682,7 @@
return rc;
}
+
#if 0
static void ov511_dump_i2c_range( struct usb_device *dev, int reg1, int regn)
{
@@ -411,12 +694,14 @@
}
}
+
static void ov511_dump_i2c_regs( struct usb_device *dev)
{
PDEBUG(3, "I2C REGS");
ov511_dump_i2c_range(dev, 0x00, 0x38);
}
+
static void ov511_dump_reg_range( struct usb_device *dev, int reg1, int regn)
{
int i;
@@ -427,6 +712,7 @@
}
}
+
static void ov511_dump_regs( struct usb_device *dev)
{
PDEBUG(1, "CAMERA INTERFACE REGS");
@@ -451,6 +737,7 @@
}
#endif
+
static int ov511_reset(struct usb_device *dev, unsigned char reset_type)
{
int rc;
@@ -465,6 +752,7 @@
return rc;
}
+
/* Temporarily stops OV511 from functioning. Must do this before changing
* registers while the camera is streaming */
static inline int ov511_stop(struct usb_device *dev)
@@ -473,6 +761,7 @@
return (ov511_reg_write(dev, OV511_REG_SYSTEM_RESET, 0x3d));
}
+
/* Restarts OV511 after ov511_stop() is called */
static inline int ov511_restart(struct usb_device *dev)
{
@@ -480,6 +769,7 @@
return (ov511_reg_write(dev, OV511_REG_SYSTEM_RESET, 0x00));
}
+
static int ov511_set_packet_size(struct usb_ov511 *ov511, int size)
{
int alt, mult;
@@ -487,7 +777,7 @@
if (ov511_stop(ov511->dev) < 0)
return -EIO;
- mult = size / 32;
+ mult = size >> 5;
if (ov511->bridge == BRG_OV511) {
if (size == 0) alt = OV511_ALT_SIZE_0;
@@ -502,8 +792,7 @@
err("Set packet size: invalid size (%d)", size);
return -EINVAL;
}
- }
- else if (ov511->bridge == BRG_OV511PLUS) {
+ } else if (ov511->bridge == BRG_OV511PLUS) {
if (size == 0) alt = OV511PLUS_ALT_SIZE_0;
else if (size == 33) alt = OV511PLUS_ALT_SIZE_33;
else if (size == 129) alt = OV511PLUS_ALT_SIZE_129;
@@ -516,16 +805,14 @@
err("Set packet size: invalid size (%d)", size);
return -EINVAL;
}
- }
- else {
+ } else {
err("Set packet size: Invalid bridge type");
return -EINVAL;
}
PDEBUG(3, "set packet size: %d, mult=%d, alt=%d", size, mult, alt);
- if (ov511_reg_write(ov511->dev, OV511_REG_FIFO_PACKET_SIZE,
- mult) < 0)
+ if (ov511_reg_write(ov511->dev, OV511_REG_FIFO_PACKET_SIZE, mult) < 0)
return -ENOMEM;
if (usb_set_interface(ov511->dev, ov511->iface, alt) < 0) {
@@ -545,8 +832,9 @@
return 0;
}
-static inline int ov7610_set_picture(struct usb_ov511 *ov511,
- struct video_picture *p)
+
+static inline int
+ov7610_set_picture(struct usb_ov511 *ov511, struct video_picture *p)
{
int ret;
struct usb_device *dev = ov511->dev;
@@ -556,10 +844,10 @@
if (ov511_stop(dev) < 0)
return -EIO;
- if((ret = ov511_i2c_read(dev, OV7610_REG_COM_B)) < 0)
+ if ((ret = ov511_i2c_read(dev, OV7610_REG_COM_B)) < 0)
return -EIO;
#if 0
- if(ov511_i2c_write(dev, OV7610_REG_COM_B, ret & 0xfe) < 0)
+ if (ov511_i2c_write(dev, OV7610_REG_COM_B, ret & 0xfe) < 0)
return -EIO;
#endif
if (ov511->sensor == SEN_OV7610 || ov511->sensor == SEN_OV7620AE)
@@ -572,17 +860,18 @@
if(ov511_i2c_write(dev, OV7610_REG_BRT, p->brightness >> 8) < 0)
return -EIO;
- }
- else if ((ov511->sensor == SEN_OV7620)
+ } else if ((ov511->sensor == SEN_OV7620)
|| (ov511->sensor == SEN_OV7620AE)) {
-// cur_con = ov511_i2c_read(dev, OV7610_REG_CNT);
-// cur_brt = ov511_i2c_read(dev, OV7610_REG_BRT);
-// // DEBUG_CODE
-// PDEBUG(1, "con=%d brt=%d", ov511_i2c_read(dev, OV7610_REG_CNT),
-// ov511_i2c_read(dev, OV7610_REG_BRT));
-//
-// if(ov511_i2c_write(dev, OV7610_REG_CNT, p->contrast >> 8) < 0)
-// return -EIO;
+#if 0
+ cur_con = ov511_i2c_read(dev, OV7610_REG_CNT);
+ cur_brt = ov511_i2c_read(dev, OV7610_REG_BRT);
+ // DEBUG_CODE
+ PDEBUG(1, "con=%d brt=%d", ov511_i2c_read(dev, OV7610_REG_CNT),
+ ov511_i2c_read(dev, OV7610_REG_BRT));
+
+ if(ov511_i2c_write(dev, OV7610_REG_CNT, p->contrast >> 8) < 0)
+ return -EIO;
+#endif
}
if (ov511_restart(dev) < 0)
@@ -591,8 +880,9 @@
return 0;
}
-static inline int ov7610_get_picture(struct usb_ov511 *ov511,
- struct video_picture *p)
+
+static inline int
+ov7610_get_picture(struct usb_ov511 *ov511, struct video_picture *p)
{
int ret;
struct usb_device *dev = ov511->dev;
@@ -613,7 +903,11 @@
p->hue = 0x8000;
p->whiteness = 105 << 8;
+#if 0
p->depth = 3; /* Don't know if this is right */
+#else
+ p->depth = 24;
+#endif
p->palette = VIDEO_PALETTE_RGB24;
if (ov511_restart(dev) < 0)
@@ -622,15 +916,17 @@
return 0;
}
-static int ov511_mode_init_regs(struct usb_ov511 *ov511,
- int width, int height, int mode, int sub_flag)
+
+static int
+ov511_mode_init_regs(struct usb_ov511 *ov511,
+ int width, int height, int mode, int sub_flag)
{
int rc = 0;
struct usb_device *dev = ov511->dev;
- int hwsbase = 0;
- int hwebase = 0;
+ int hwsbase = 0;
+ int hwebase = 0;
- PDEBUG(3, "ov511_mode_init_regs(ov511, w:%d, h:%d, mode:%d, sub:%d)",
+ PDEBUG(3, "width:%d, height:%d, mode:%d, sub:%d",
width, height, mode, sub_flag);
if (ov511_stop(ov511->dev) < 0)
@@ -644,34 +940,39 @@
ov511_i2c_write(dev, 0x0e, 0x44);
}
ov511_i2c_write(dev, 0x13, autoadjust ? 0x21 : 0x20);
+
/* For snapshot */
ov511_reg_write(dev, 0x1e, 0x00);
ov511_reg_write(dev, 0x1f, 0x01);
} else {
ov511_reg_write(dev, 0x16, 0x01);
- if (ov511->sensor == SEN_OV7610
+ if (ov511->sensor == SEN_OV7610
|| ov511->sensor == SEN_OV7620AE) {
- /* not valid on the OV7620 */
- ov511_i2c_write(dev, 0x0e, 0x04);
+ /* not valid on the OV7620 */
+ ov511_i2c_write(dev, 0x0e, 0x04);
}
ov511_i2c_write(dev, 0x13, autoadjust ? 0x01 : 0x00);
+
/* For snapshot */
ov511_reg_write(dev, 0x1e, 0x01);
ov511_reg_write(dev, 0x1f, 0x03);
}
/* The different sensor ICs handle setting up of window differently */
- switch (ov511->sensor) {
- case SEN_OV7610:
- case SEN_OV7620AE:
- hwsbase = 0x38;
- hwebase = 0x3a; break;
- case SEN_OV7620:
- hwsbase = 0x2c;
- hwebase = 0x2d; break;
- default:
- hwsbase = 0;
- hwebase = 0; break;
+ switch (ov511->sensor) {
+ case SEN_OV7610:
+ case SEN_OV7620AE:
+ hwsbase = 0x38;
+ hwebase = 0x3a;
+ break;
+ case SEN_OV7620:
+ hwsbase = 0x2c;
+ hwebase = 0x2d;
+ break;
+ default:
+ hwsbase = 0;
+ hwebase = 0;
+ break;
}
if (width == 640 && height == 480) {
@@ -680,12 +981,12 @@
ov511_i2c_write(dev, 0x17, hwsbase+(ov511->subx>>2));
/* horizontal window end */
ov511_i2c_write(dev, 0x18,
- hwebase+((ov511->subx+ov511->subw)>>2));
+ hwebase+((ov511->subx+ov511->subw)>>2));
/* vertical window start */
- ov511_i2c_write(dev, 0x19, 0x5+(ov511->suby>>1));
+ ov511_i2c_write(dev, 0x19, 0x5+(ov511->suby>>1));
/* vertical window end */
- ov511_i2c_write(dev, 0x1a,
- 0x5+((ov511->suby+ov511->subh)>>1));
+ ov511_i2c_write(dev, 0x1a,
+ 0x5+((ov511->suby+ov511->subh)>>1));
ov511_reg_write(dev, 0x12, (ov511->subw>>3)-1);
ov511_reg_write(dev, 0x13, (ov511->subh>>3)-1);
/* clock rate control */
@@ -762,255 +1063,267 @@
return rc;
}
-
-/*************************************************************
-
-Turn a YUV4:2:0 block into an RGB block
-Video4Linux seems to use the blue, green, red channel
-order convention-- rgb[0] is blue, rgb[1] is green, rgb[2] is red.
-
-Color space conversion coefficients taken from the excellent
-http://www.inforamp.net/~poynton/ColorFAQ.html
-In his terminology, this is a CCIR 601.1 YCbCr -> RGB.
-Y values are given for all 4 pixels, but the U (Pb)
-and V (Pr) are assumed constant over the 2x2 block.
+/**********************************************************************
+ *
+ * Color correction functions
+ *
+ **********************************************************************/
-To avoid floating point arithmetic, the color conversion
-coefficients are scaled into 16.16 fixed-point integers.
+/*
+ * Turn a YUV4:2:0 block into an RGB block
+ *
+ * Video4Linux seems to use the blue, green, red channel
+ * order convention-- rgb[0] is blue, rgb[1] is green, rgb[2] is red.
+ *
+ * Color space conversion coefficients taken from the excellent
+ * http://www.inforamp.net/~poynton/ColorFAQ.html
+ * In his terminology, this is a CCIR 601.1 YCbCr -> RGB.
+ * Y values are given for all 4 pixels, but the U (Pb)
+ * and V (Pr) are assumed constant over the 2x2 block.
+ *
+ * To avoid floating point arithmetic, the color conversion
+ * coefficients are scaled into 16.16 fixed-point integers.
+ */
-*************************************************************/
-// LIMIT: convert a 16.16 fixed-point value to a byte, with clipping.
+/* LIMIT: convert a 16.16 fixed-point value to a byte, with clipping. */
#define LIMIT(x) ((x)>0xffffff?0xff: ((x)<=0xffff?0:((x)>>16)))
-static inline void ov511_move_420_block(
- int yTL, int yTR, int yBL, int yBR,
- int u, int v,
- int rowPixels, unsigned char * rgb)
-{
- const double brightness=1.0;//0->black; 1->full scale
- const double saturation=1.0;//0->greyscale; 1->full color
- const double fixScale=brightness*256*256;
- const int rvScale=(int)(1.402*saturation*fixScale);
- const int guScale=(int)(-0.344136*saturation*fixScale);
- const int gvScale=(int)(-0.714136*saturation*fixScale);
- const int buScale=(int)(1.772*saturation*fixScale);
- const int yScale=(int)(fixScale);
-
- int r = rvScale * v;
- int g = guScale * u + gvScale * v;
- int b = buScale * u;
+
+static inline void
+ov511_move_420_block(int yTL, int yTR, int yBL, int yBR, int u, int v,
+ int rowPixels, unsigned char * rgb)
+{
+ const double brightness = 1.0; // 0->black; 1->full scale
+ const double saturation = 1.0; // 0->greyscale; 1->full color
+ const double fixScale = brightness * 256 * 256;
+ const int rvScale = (int)(1.402 * saturation * fixScale);
+ const int guScale = (int)(-0.344136 * saturation * fixScale);
+ const int gvScale = (int)(-0.714136 * saturation * fixScale);
+ const int buScale = (int)(1.772 * saturation * fixScale);
+ const int yScale = (int)(fixScale);
+
+ int r = rvScale * v;
+ int g = guScale * u + gvScale * v;
+ int b = buScale * u;
yTL *= yScale; yTR *= yScale;
yBL *= yScale; yBR *= yScale;
//Write out top two pixels
- rgb[0]=LIMIT(b+yTL); rgb[1]=LIMIT(g+yTL); rgb[2]=LIMIT(r+yTL);
- rgb[3]=LIMIT(b+yTR); rgb[4]=LIMIT(g+yTR); rgb[5]=LIMIT(r+yTR);
- rgb+=3*rowPixels;//Skip down to next line to write out bottom two pixels
- rgb[0]=LIMIT(b+yBL); rgb[1]=LIMIT(g+yBL); rgb[2]=LIMIT(r+yBL);
- rgb[3]=LIMIT(b+yBR); rgb[4]=LIMIT(g+yBR); rgb[5]=LIMIT(r+yBR);
-}
-
-
-/***************************************************************
+ rgb[0] = LIMIT(b+yTL); rgb[1] = LIMIT(g+yTL); rgb[2] = LIMIT(r+yTL);
+ rgb[3] = LIMIT(b+yTR); rgb[4] = LIMIT(g+yTR); rgb[5] = LIMIT(r+yTR);
-For a 640x480 YUV4:2:0 images, data shows up in 1200 384 byte segments. The
-first 64 bytes of each segment are U, the next 64 are V. The U and
-V are arranged as follows:
-
- 0 1 ... 7
- 8 9 ... 15
- ...
- 56 57 ... 63
-
-U and V are shipped at half resolution (1 U,V sample -> one 2x2 block).
-
-The next 256 bytes are full resolution Y data and represent 4
-squares of 8x8 pixels as follows:
-
- 0 1 ... 7 64 65 ... 71 ... 192 193 ... 199
- 8 9 ... 15 72 73 ... 79 200 201 ... 207
- ... ... ...
- 56 57 ... 63 120 121 127 248 249 ... 255
+ //Skip down to next line to write out bottom two pixels
+ rgb += 3 * rowPixels;
+ rgb[0] = LIMIT(b+yBL); rgb[1] = LIMIT(g+yBL); rgb[2] = LIMIT(r+yBL);
+ rgb[3] = LIMIT(b+yBR); rgb[4] = LIMIT(g+yBR); rgb[5] = LIMIT(r+yBR);
+}
-Note that the U and V data in one segment represents a 16 x 16 pixel
-area, but the Y data represents a 32 x 8 pixel area.
-If OV511_DUMPPIX is defined, _parse_data just dumps the
-incoming segments, verbatim, in order, into the frame.
-When used with vidcat -f ppm -s 640x480 this puts the data
-on the standard output and can be analyzed with the parseppm.c
-utility I wrote. That's a much faster way for figuring out how
-this data is scrambled.
+/*
+ * For a 640x480 YUV4:2:0 images, data shows up in 1200 384 byte segments.
+ * The first 64 bytes of each segment are U, the next 64 are V. The U and
+ * V are arranged as follows:
+ *
+ * 0 1 ... 7
+ * 8 9 ... 15
+ * ...
+ * 56 57 ... 63
+ *
+ * U and V are shipped at half resolution (1 U,V sample -> one 2x2 block).
+ *
+ * The next 256 bytes are full resolution Y data and represent 4 squares
+ * of 8x8 pixels as follows:
+ *
+ * 0 1 ... 7 64 65 ... 71 ... 192 193 ... 199
+ * 8 9 ... 15 72 73 ... 79 200 201 ... 207
+ * ... ... ...
+ * 56 57 ... 63 120 121 127 248 249 ... 255
+ *
+ * Note that the U and V data in one segment represents a 16 x 16 pixel
+ * area, but the Y data represents a 32 x 8 pixel area.
+ *
+ * If OV511_DUMPPIX is defined, _parse_data just dumps the incoming segments,
+ * verbatim, in order, into the frame. When used with vidcat -f ppm -s 640x480
+ * this puts the data on the standard output and can be analyzed with the
+ * parseppm.c utility I wrote. That's a much faster way for figuring out how
+ * this data is scrambled.
+ */
-****************************************************************/
#define HDIV 8
#define WDIV (256/HDIV)
+#undef OV511_DUMPPIX
-static void ov511_parse_data_rgb24(unsigned char * pIn0,
- unsigned char * pOut0,
- int iOutY,
- int iOutUV,
- int iHalf,
- int iWidth)
-
+static void
+ov511_parse_data_rgb24(unsigned char * pIn0, unsigned char * pOut0,
+ int iOutY, int iOutUV, int iHalf, int iWidth)
{
#ifndef OV511_DUMPPIX
- int k, l, m;
- unsigned char * pIn;
- unsigned char * pOut, * pOut1;
-
- /* Just copy the Y's if in the first stripe */
- if (!iHalf) {
- pIn = pIn0 + 128;
- pOut = pOut0 + iOutY;
- for(k=0; k<4; k++) {
- pOut1 = pOut;
- for(l=0; l<8; l++) {
- for(m=0; m<8; m++) {
- *pOut1 = *pIn++;
- pOut1 += 3;
- }
- pOut1 += (iWidth - 8) * 3;
- }
- pOut += 8 * 3;
- }
- }
-
- /* Use the first half of VUs to calculate value */
- pIn = pIn0;
- pOut = pOut0 + iOutUV;
- for(l=0; l<4; l++) {
- for(m=0; m<8; m++) {
- int y00 = *(pOut);
- int y01 = *(pOut+3);
- int y10 = *(pOut+iWidth*3);
- int y11 = *(pOut+iWidth*3+3);
- int v = *(pIn+64) - 128;
- int u = *pIn++ - 128;
- ov511_move_420_block(y00, y01, y10, y11, u, v, iWidth, pOut);
- pOut += 6;
- }
- pOut += (iWidth*2 - 16) * 3;
- }
-
- /* Just copy the other UV rows */
- for(l=0; l<4; l++) {
- for(m=0; m<8; m++) {
- *pOut++ = *(pIn + 64);
- *pOut = *pIn++;
- pOut += 5;
- }
- pOut += (iWidth*2 - 16) * 3;
- }
-
- /* Calculate values if it's the second half */
- if (iHalf) {
- pIn = pIn0 + 128;
- pOut = pOut0 + iOutY;
- for(k=0; k<4; k++) {
- pOut1 = pOut;
- for(l=0; l<4; l++) {
- for(m=0; m<4; m++) {
- int y10 = *(pIn+8);
- int y00 = *pIn++;
- int y11 = *(pIn+8);
- int y01 = *pIn++;
- int v = *pOut1 - 128;
- int u = *(pOut1+1) - 128;
- ov511_move_420_block(y00, y01, y10, y11, u, v, iWidth, pOut1);
- pOut1 += 6;
- }
- pOut1 += (iWidth*2 - 8) * 3;
- pIn += 8;
- }
- pOut += 8 * 3;
+ int k, l, m;
+ unsigned char * pIn;
+ unsigned char * pOut, * pOut1;
+
+ /* Just copy the Y's if in the first stripe */
+ if (!iHalf) {
+ pIn = pIn0 + 128;
+ pOut = pOut0 + iOutY;
+ for (k = 0; k < 4; k++) {
+ pOut1 = pOut;
+ for (l = 0; l < 8; l++) {
+ for (m = 0; m < 8; m++) {
+ *pOut1 = *pIn++;
+ pOut1 += 3;
+ }
+ pOut1 += (iWidth - 8) * 3;
+ }
+ pOut += 8 * 3;
+ }
+ }
+
+ /* Use the first half of VUs to calculate value */
+ pIn = pIn0;
+ pOut = pOut0 + iOutUV;
+ for (l = 0; l < 4; l++) {
+ for (m=0; m<8; m++) {
+ int y00 = *(pOut);
+ int y01 = *(pOut+3);
+ int y10 = *(pOut+iWidth*3);
+ int y11 = *(pOut+iWidth*3+3);
+ int v = *(pIn+64) - 128;
+ int u = *pIn++ - 128;
+ ov511_move_420_block(y00, y01, y10, y11, u, v, iWidth,
+ pOut);
+ pOut += 6;
+ }
+ pOut += (iWidth*2 - 16) * 3;
+ }
+
+ /* Just copy the other UV rows */
+ for (l = 0; l < 4; l++) {
+ for (m = 0; m < 8; m++) {
+ *pOut++ = *(pIn + 64);
+ *pOut = *pIn++;
+ pOut += 5;
+ }
+ pOut += (iWidth*2 - 16) * 3;
+ }
+
+ /* Calculate values if it's the second half */
+ if (iHalf) {
+ pIn = pIn0 + 128;
+ pOut = pOut0 + iOutY;
+ for (k = 0; k < 4; k++) {
+ pOut1 = pOut;
+ for (l=0; l<4; l++) {
+ for (m=0; m<4; m++) {
+ int y10 = *(pIn+8);
+ int y00 = *pIn++;
+ int y11 = *(pIn+8);
+ int y01 = *pIn++;
+ int v = *pOut1 - 128;
+ int u = *(pOut1+1) - 128;
+ ov511_move_420_block(y00, y01, y10,
+ y11, u, v, iWidth, pOut1);
+ pOut1 += 6;
+ }
+ pOut1 += (iWidth*2 - 8) * 3;
+ pIn += 8;
+ }
+ pOut += 8 * 3;
+ }
}
- }
#else
/* Just dump pix data straight out for debug */
- int i;
- pOut0 += iSegmentY * 384;
- for(i=0; i<384; i++) {
- *pOut0++ = *pIn0++;
+ int i, j;
+
+ pOut0 += iOutY;
+ for (i = 0; i < HDIV; i++) {
+ for (j = 0; j < WDIV; j++) {
+ *pOut0++ = *pIn0++;
+ *pOut0++ = *pIn0++;
+ *pOut0++ = *pIn0++;
+ }
+ pOut0 += (iWidth - WDIV) * 3;
}
#endif
}
-/***************************************************************
-For 640x480 RAW BW images, data shows up in 1200 256 byte segments.
-The segments represent 4 squares of 8x8 pixels as
-follows:
-
- 0 1 ... 7 64 65 ... 71 ... 192 193 ... 199
- 8 9 ... 15 72 73 ... 79 200 201 ... 207
- ... ... ...
- 56 57 ... 63 120 121 127 248 249 ... 255
-
-****************************************************************/
-static void ov511_parse_data_grey(unsigned char * pIn0,
- unsigned char * pOut0,
- int iOutY,
- int iWidth)
-
-{
- int k, l, m;
- unsigned char * pIn;
- unsigned char * pOut, * pOut1;
-
- pIn = pIn0;
- pOut = pOut0 + iOutY;
- for(k=0; k<4; k++) {
- pOut1 = pOut;
- for(l=0; l<8; l++) {
- for(m=0; m<8; m++) {
- *pOut1++ = *pIn++;
- }
- pOut1 += iWidth - 8;
- }
- pOut += 8;
- }
+/*
+ * For 640x480 RAW BW images, data shows up in 1200 256 byte segments.
+ * The segments represent 4 squares of 8x8 pixels as follows:
+ *
+ * 0 1 ... 7 64 65 ... 71 ... 192 193 ... 199
+ * 8 9 ... 15 72 73 ... 79 200 201 ... 207
+ * ... ... ...
+ * 56 57 ... 63 120 121 127 248 249 ... 255
+ *
+ */
+static void
+ov511_parse_data_grey(unsigned char * pIn0, unsigned char * pOut0,
+ int iOutY, int iWidth)
+{
+ int k, l, m;
+ unsigned char *pIn;
+ unsigned char *pOut, *pOut1;
+
+ pIn = pIn0;
+ pOut = pOut0 + iOutY;
+ for (k = 0; k < 4; k++) {
+ pOut1 = pOut;
+ for (l = 0; l < 8; l++) {
+ for (m = 0; m < 8; m++) {
+ *pOut1++ = *pIn++;
+ }
+ pOut1 += iWidth - 8;
+ }
+ pOut += 8;
+ }
}
-/**************************************************************
+/*
* fixFrameRGBoffset--
* My camera seems to return the red channel about 1 pixel
* low, and the blue channel about 1 pixel high. After YUV->RGB
* conversion, we can correct this easily. OSL 2/24/2000.
- *************************************************************/
+ */
static void fixFrameRGBoffset(struct ov511_frame *frame)
{
- int x, y;
- int rowBytes = frame->width*3, w = frame->width;
- unsigned char *rgb = frame->data;
- const int shift = 1; //Distance to shift pixels by, vertically
-
- if (frame->width < 400)
- return; //Don't bother with little images
-
- //Shift red channel up
- for (y = shift; y < frame->height; y++)
- {
- int lp = (y-shift)*rowBytes; //Previous line offset
- int lc = y*rowBytes; //Current line offset
- for (x = 0; x < w; x++)
- rgb[lp+x*3+2] = rgb[lc+x*3+2]; //Shift red up
- }
-
- //Shift blue channel down
- for (y=frame->height-shift-1; y >= 0; y--)
- {
- int ln = (y+shift)*rowBytes; //Next line offset
- int lc = y*rowBytes; //Current line offset
- for (x = 0; x < w; x++)
- rgb[ln+x*3+0] = rgb[lc+x*3+0]; //Shift blue down
- }
+ int x, y;
+ int rowBytes = frame->width*3, w = frame->width;
+ unsigned char *rgb = frame->data;
+ const int shift = 1; /* Distance to shift pixels by, vertically */
+
+ /* Don't bother with little images */
+ if (frame->width < 400)
+ return;
+
+ /* Shift red channel up */
+ for (y = shift; y < frame->height; y++) {
+ int lp = (y-shift)*rowBytes; /* Previous line offset */
+ int lc = y*rowBytes; /* Current line offset */
+ for (x = 0; x < w; x++)
+ rgb[lp+x*3+2] = rgb[lc+x*3+2]; /* Shift red up */
+ }
+
+ /* Shift blue channel down */
+ for (y = frame->height-shift-1; y >= 0; y--) {
+ int ln = (y + shift) * rowBytes; /* Next line offset */
+ int lc = y * rowBytes; /* Current line offset */
+ for (x = 0; x < w; x++)
+ rgb[ln+x*3+0] = rgb[lc+x*3+0]; /* Shift blue down */
+ }
}
+/**********************************************************************
+ *
+ * OV511 data transfer, IRQ handler
+ *
+ **********************************************************************/
+
static int ov511_move_data(struct usb_ov511 *ov511, urb_t *urb)
{
unsigned char *cdata;
@@ -1175,6 +1488,7 @@
return totlen;
}
+
static void ov511_isoc_irq(struct urb *urb)
{
int len;
@@ -1187,28 +1501,32 @@
if (!ov511->streaming) {
PDEBUG(2, "hmmm... not streaming, but got interrupt");
return;
+ } else {
+ PDEBUG(5, "streaming. got interrupt");
}
sbuf = &ov511->sbuf[ov511->cursbuf];
/* Copy the data received into our scratch buffer */
- if (ov511->curframe >= 0)
- len = ov511_move_data(ov511, urb);
- else if (waitqueue_active(&ov511->wq))
- wake_up_interruptible(&ov511->wq);
-
+ if (ov511->curframe >= 0) {
+ len = ov511_move_data(ov511, urb);
+ } else if (waitqueue_active(&ov511->wq)) {
+ wake_up_interruptible(&ov511->wq);
+ }
+
/* Move to the next sbuf */
ov511->cursbuf = (ov511->cursbuf + 1) % OV511_NUMSBUF;
return;
}
+
static int ov511_init_isoc(struct usb_ov511 *ov511)
{
urb_t *urb;
int fx, err;
- PDEBUG(4, "ov511_init_isoc");
+ PDEBUG(3, "*** Initializing capture ***");
ov511->compress = 0;
ov511->curframe = -1;
@@ -1235,11 +1553,11 @@
urb->pipe = usb_rcvisocpipe(ov511->dev, OV511_ENDPOINT_ADDRESS);
urb->transfer_flags = USB_ISO_ASAP;
urb->transfer_buffer = ov511->sbuf[0].data;
- urb->complete = ov511_isoc_irq;
- urb->number_of_packets = FRAMES_PER_DESC;
- urb->transfer_buffer_length = ov511->packet_size * FRAMES_PER_DESC;
- for (fx = 0; fx < FRAMES_PER_DESC; fx++) {
- urb->iso_frame_desc[fx].offset = ov511->packet_size * fx;
+ urb->complete = ov511_isoc_irq;
+ urb->number_of_packets = FRAMES_PER_DESC;
+ urb->transfer_buffer_length = ov511->packet_size * FRAMES_PER_DESC;
+ for (fx = 0; fx < FRAMES_PER_DESC; fx++) {
+ urb->iso_frame_desc[fx].offset = ov511->packet_size * fx;
urb->iso_frame_desc[fx].length = ov511->packet_size;
}
@@ -1254,11 +1572,11 @@
urb->pipe = usb_rcvisocpipe(ov511->dev, OV511_ENDPOINT_ADDRESS);
urb->transfer_flags = USB_ISO_ASAP;
urb->transfer_buffer = ov511->sbuf[1].data;
- urb->complete = ov511_isoc_irq;
- urb->number_of_packets = FRAMES_PER_DESC;
- urb->transfer_buffer_length = ov511->packet_size * FRAMES_PER_DESC;
- for (fx = 0; fx < FRAMES_PER_DESC; fx++) {
- urb->iso_frame_desc[fx].offset = ov511->packet_size * fx;
+ urb->complete = ov511_isoc_irq;
+ urb->number_of_packets = FRAMES_PER_DESC;
+ urb->transfer_buffer_length = ov511->packet_size * FRAMES_PER_DESC;
+ for (fx = 0; fx < FRAMES_PER_DESC; fx++) {
+ urb->iso_frame_desc[fx].offset = ov511->packet_size * fx;
urb->iso_frame_desc[fx].length = ov511->packet_size;
}
@@ -1280,10 +1598,11 @@
static void ov511_stop_isoc(struct usb_ov511 *ov511)
{
- PDEBUG(4, "ov511_stop_isoc");
if (!ov511->streaming || !ov511->dev)
return;
+ PDEBUG (3, "*** Stopping capture ***");
+
ov511_set_packet_size(ov511, 0);
ov511->streaming = 0;
@@ -1303,18 +1622,19 @@
}
}
+
static int ov511_new_frame(struct usb_ov511 *ov511, int framenum)
{
struct ov511_frame *frame;
int width, height;
- PDEBUG(4, "ov511_new_frame");
-
+ PDEBUG(4, "ov511->curframe = %d, framenum = %d", ov511->curframe,
+ framenum);
if (!ov511->dev)
return -1;
/* If we're not grabbing a frame right now and the other frame is */
- /* ready to be grabbed into, then use it instead */
+ /* ready to be grabbed into, then use it instead */
if (ov511->curframe == -1) {
if (ov511->frame[(framenum - 1 + OV511_NUMFRAMES) % OV511_NUMFRAMES].grabstate == FRAME_READY)
framenum = (framenum - 1 + OV511_NUMFRAMES) % OV511_NUMFRAMES;
@@ -1325,6 +1645,9 @@
width = frame->width;
height = frame->height;
+ PDEBUG (4, "framenum = %d, width = %d, height = %d", framenum, width,
+ height);
+
frame->grabstate = FRAME_GRABBING;
frame->scanstate = STATE_SCANNING;
frame->scanlength = 0; /* accumulated in ov511_parse_data() */
@@ -1335,11 +1658,17 @@
/* Make sure it's not too big */
if (width > DEFAULT_WIDTH)
width = DEFAULT_WIDTH;
+#if 0
width = (width / 8) * 8; /* Multiple of 8 */
+#endif
+ width &= ~7L;
if (height > DEFAULT_HEIGHT)
height = DEFAULT_HEIGHT;
+#if 0
height = (height / 4) * 4; /* Multiple of 4 */
+#endif
+ width &= ~3L;
// /* We want a fresh frame every 30 we get */
// ov511->compress = (ov511->compress + 1) % 30;
@@ -1348,7 +1677,12 @@
}
-/* Video 4 Linux API */
+/****************************************************************************
+ *
+ * V4L API
+ *
+ ***************************************************************************/
+
static int ov511_open(struct video_device *dev, int flags)
{
int err = -EBUSY;
@@ -1409,9 +1743,9 @@
out_unlock:
up(&ov511->lock);
return err;
-
}
+
static void ov511_close(struct video_device *dev)
{
struct usb_ov511 *ov511 = (struct usb_ov511 *)dev;
@@ -1438,16 +1772,23 @@
}
}
+
static int ov511_init_done(struct video_device *dev)
{
+#ifdef CONFIG_PROC_FS
+ create_proc_ov511_cam((struct usb_ov511 *)dev);
+#endif
+
return 0;
}
+
static long ov511_write(struct video_device *dev, const char *buf, unsigned long count, int noblock)
{
return -EINVAL;
}
+
static int ov511_ioctl(struct video_device *vdev, unsigned int cmd, void *arg)
{
struct usb_ov511 *ov511 = (struct usb_ov511 *)vdev;
@@ -1458,311 +1799,313 @@
return -EIO;
switch (cmd) {
- case VIDIOCGCAP:
- {
- struct video_capability b;
-
- strcpy(b.name, "OV511 USB Camera");
- b.type = VID_TYPE_CAPTURE | VID_TYPE_SUBCAPTURE;
- b.channels = 1;
- b.audios = 0;
- b.maxwidth = DEFAULT_WIDTH;
- b.maxheight = DEFAULT_HEIGHT;
- b.minwidth = 32;
- b.minheight = 16;
+ case VIDIOCGCAP:
+ {
+ struct video_capability b;
+
+ strcpy(b.name, "OV511 USB Camera");
+ b.type = VID_TYPE_CAPTURE | VID_TYPE_SUBCAPTURE;
+ b.channels = 1;
+ b.audios = 0;
+ b.maxwidth = DEFAULT_WIDTH;
+ b.maxheight = DEFAULT_HEIGHT;
+ b.minwidth = 32;
+ b.minheight = 16;
- if (copy_to_user(arg, &b, sizeof(b)))
- return -EFAULT;
+ if (copy_to_user(arg, &b, sizeof(b)))
+ return -EFAULT;
- return 0;
- }
- case VIDIOCGCHAN:
- {
- struct video_channel v;
-
- if (copy_from_user(&v, arg, sizeof(v)))
- return -EFAULT;
- if (v.channel != 0)
- return -EINVAL;
+ return 0;
+ }
+ case VIDIOCGCHAN:
+ {
+ struct video_channel v;
- v.flags = 0;
- v.tuners = 0;
- v.type = VIDEO_TYPE_CAMERA;
- strcpy(v.name, "Camera");
+ if (copy_from_user(&v, arg, sizeof(v)))
+ return -EFAULT;
+ if (v.channel != 0)
+ return -EINVAL;
- if (copy_to_user(arg, &v, sizeof(v)))
- return -EFAULT;
-
- return 0;
- }
- case VIDIOCSCHAN:
- {
- int v;
+ v.flags = 0;
+ v.tuners = 0;
+ v.type = VIDEO_TYPE_CAMERA;
+ strcpy(v.name, "Camera");
- if (copy_from_user(&v, arg, sizeof(v)))
- return -EFAULT;
+ if (copy_to_user(arg, &v, sizeof(v)))
+ return -EFAULT;
+
+ return 0;
+ }
+ case VIDIOCSCHAN:
+ {
+ int v;
- if (v != 0)
- return -EINVAL;
+ if (copy_from_user(&v, arg, sizeof(v)))
+ return -EFAULT;
- return 0;
- }
+ if (v != 0)
+ return -EINVAL;
- case VIDIOCGPICT:
- {
- struct video_picture p;
+ return 0;
+ }
+ case VIDIOCGPICT:
+ {
+ struct video_picture p;
- if (ov7610_get_picture(ov511, &p))
- return -EIO;
+ if (ov7610_get_picture(ov511, &p))
+ return -EIO;
- if (copy_to_user(arg, &p, sizeof(p)))
- return -EFAULT;
+ if (copy_to_user(arg, &p, sizeof(p)))
+ return -EFAULT;
- return 0;
- }
- case VIDIOCSPICT:
- {
- struct video_picture p;
+ return 0;
+ }
+ case VIDIOCSPICT:
+ {
+ struct video_picture p;
- if (copy_from_user(&p, arg, sizeof(p)))
- return -EFAULT;
+ if (copy_from_user(&p, arg, sizeof(p)))
+ return -EFAULT;
- if (ov7610_set_picture(ov511, &p))
- return -EIO;
+ if (ov7610_set_picture(ov511, &p))
+ return -EIO;
- return 0;
- }
- case VIDIOCGCAPTURE:
- {
- int vf;
- if (copy_from_user(&vf, arg, sizeof(vf)))
- return -EFAULT;
- ov511->sub_flag = vf;
- return 0;
- }
- case VIDIOCSCAPTURE:
- {
- struct video_capture vc;
-
- if (copy_from_user(&vc, arg, sizeof(vc)))
- return -EFAULT;
- if (vc.flags)
- return -EINVAL;
- if (vc.decimation)
- return -EINVAL;
- vc.x /= 4;
- vc.x *= 4;
- vc.y /= 2;
- vc.y *= 2;
- vc.width /= 32;
- vc.width *= 32;
- if (vc.width == 0) vc.width = 32;
- vc.height /= 16;
- vc.height *= 16;
- if (vc.height == 0) vc.height = 16;
-
- ov511->subx = vc.x;
- ov511->suby = vc.y;
- ov511->subw = vc.width;
- ov511->subh = vc.height;
-
- return 0;
- }
- case VIDIOCSWIN:
- {
- struct video_window vw;
-
- if (copy_from_user(&vw, arg, sizeof(vw)))
- return -EFAULT;
- if (vw.flags)
- return -EINVAL;
- if (vw.clipcount)
- return -EINVAL;
- if (vw.height != DEFAULT_HEIGHT)
- return -EINVAL;
- if (vw.width != DEFAULT_WIDTH)
- return -EINVAL;
+ return 0;
+ }
+ case VIDIOCGCAPTURE:
+ {
+ int vf;
+ if (copy_from_user(&vf, arg, sizeof(vf)))
+ return -EFAULT;
+ ov511->sub_flag = vf;
+ return 0;
+ }
+ case VIDIOCSCAPTURE:
+ {
+ struct video_capture vc;
- ov511->compress = 0;
+ if (copy_from_user(&vc, arg, sizeof(vc)))
+ return -EFAULT;
+ if (vc.flags)
+ return -EINVAL;
+ if (vc.decimation)
+ return -EINVAL;
+ vc.x /= 4;
+ vc.x *= 4;
+ vc.y /= 2;
+ vc.y *= 2;
+ vc.width /= 32;
+ vc.width *= 32;
+ if (vc.width == 0) vc.width = 32;
+ vc.height /= 16;
+ vc.height *= 16;
+ if (vc.height == 0) vc.height = 16;
+
+ ov511->subx = vc.x;
+ ov511->suby = vc.y;
+ ov511->subw = vc.width;
+ ov511->subh = vc.height;
- return 0;
- }
- case VIDIOCGWIN:
- {
- struct video_window vw;
+ return 0;
+ }
+ case VIDIOCSWIN:
+ {
+ struct video_window vw;
- vw.x = 0;
- vw.y = 0;
- vw.width = DEFAULT_WIDTH;
- vw.height = DEFAULT_HEIGHT;
- vw.chromakey = 0;
- vw.flags = 30;
+ if (copy_from_user(&vw, arg, sizeof(vw)))
+ return -EFAULT;
+ if (vw.flags)
+ return -EINVAL;
+ if (vw.clipcount)
+ return -EINVAL;
+ if (vw.height != DEFAULT_HEIGHT)
+ return -EINVAL;
+ if (vw.width != DEFAULT_WIDTH)
+ return -EINVAL;
- if (copy_to_user(arg, &vw, sizeof(vw)))
- return -EFAULT;
+ ov511->compress = 0;
- return 0;
- }
- case VIDIOCGMBUF:
- {
- struct video_mbuf vm;
+ return 0;
+ }
+ case VIDIOCGWIN:
+ {
+ struct video_window vw;
- memset(&vm, 0, sizeof(vm));
- vm.size = 2 * MAX_DATA_SIZE;
- vm.frames = 2;
- vm.offsets[0] = 0;
- vm.offsets[1] = MAX_FRAME_SIZE + sizeof (struct timeval);
+ vw.x = 0;
+ vw.y = 0;
+ vw.width = DEFAULT_WIDTH;
+ vw.height = DEFAULT_HEIGHT;
+ vw.chromakey = 0;
+ vw.flags = 30;
- if (copy_to_user((void *)arg, (void *)&vm, sizeof(vm)))
- return -EFAULT;
+ if (copy_to_user(arg, &vw, sizeof(vw)))
+ return -EFAULT;
- return 0;
- }
- case VIDIOCMCAPTURE:
- {
- struct video_mmap vm;
+ return 0;
+ }
+ case VIDIOCGMBUF:
+ {
+ struct video_mbuf vm;
- if (copy_from_user((void *)&vm, (void *)arg, sizeof(vm)))
- return -EFAULT;
+ memset(&vm, 0, sizeof(vm));
+ vm.size = 2 * MAX_DATA_SIZE;
+ vm.frames = 2;
+ vm.offsets[0] = 0;
+ vm.offsets[1] = MAX_FRAME_SIZE + sizeof (struct timeval);
- PDEBUG(4, "MCAPTURE");
- PDEBUG(4, "frame: %d, size: %dx%d, format: %d",
- vm.frame, vm.width, vm.height, vm.format);
+ if (copy_to_user((void *)arg, (void *)&vm, sizeof(vm)))
+ return -EFAULT;
- if (vm.format != VIDEO_PALETTE_RGB24 &&
- vm.format != VIDEO_PALETTE_GREY)
- return -EINVAL;
+ return 0;
+ }
+ case VIDIOCMCAPTURE:
+ {
+ struct video_mmap vm;
- if ((vm.frame != 0) && (vm.frame != 1))
- return -EINVAL;
+ if (copy_from_user((void *)&vm, (void *)arg, sizeof(vm)))
+ return -EFAULT;
+
+ PDEBUG(4, "MCAPTURE");
+ PDEBUG(4, "frame: %d, size: %dx%d, format: %d",
+ vm.frame, vm.width, vm.height, vm.format);
+
+ if (vm.format != VIDEO_PALETTE_RGB24 &&
+ vm.format != VIDEO_PALETTE_GREY)
+ return -EINVAL;
+
+ if ((vm.frame != 0) && (vm.frame != 1))
+ return -EINVAL;
- if (ov511->frame[vm.frame].grabstate == FRAME_GRABBING)
- return -EBUSY;
+ if (ov511->frame[vm.frame].grabstate == FRAME_GRABBING)
+ return -EBUSY;
- /* Don't compress if the size changed */
- if ((ov511->frame[vm.frame].width != vm.width) ||
- (ov511->frame[vm.frame].height != vm.height) ||
- (ov511->frame[vm.frame].format != vm.format) ||
- (ov511->frame[vm.frame].sub_flag !=
- ov511->sub_flag)) {
- /* If we're collecting previous frame wait
- before changing modes */
- interruptible_sleep_on(&ov511->wq);
- if (signal_pending(current)) return -EINTR;
- ov511_mode_init_regs(ov511,
- vm.width, vm.height,
- vm.format, ov511->sub_flag);
- }
+ /* Don't compress if the size changed */
+ if ((ov511->frame[vm.frame].width != vm.width) ||
+ (ov511->frame[vm.frame].height != vm.height) ||
+ (ov511->frame[vm.frame].format != vm.format) ||
+ (ov511->frame[vm.frame].sub_flag !=
+ ov511->sub_flag)) {
+ /* If we're collecting previous frame wait
+ before changing modes */
+ interruptible_sleep_on(&ov511->wq);
+ if (signal_pending(current)) return -EINTR;
+ ov511_mode_init_regs(ov511,
+ vm.width, vm.height,
+ vm.format, ov511->sub_flag);
+ }
+
+ ov511->frame[vm.frame].width = vm.width;
+ ov511->frame[vm.frame].height = vm.height;
+ ov511->frame[vm.frame].format = vm.format;
+ ov511->frame[vm.frame].sub_flag = ov511->sub_flag;
+ ov511->frame[vm.frame].segsize =
+ vm.format == VIDEO_PALETTE_RGB24 ? 384 : 256;
+ ov511->frame[vm.frame].depth =
+ vm.format == VIDEO_PALETTE_RGB24 ? 3 : 1;
+
+ /* Mark it as ready */
+ ov511->frame[vm.frame].grabstate = FRAME_READY;
+
+ return ov511_new_frame(ov511, vm.frame);
+ }
+ case VIDIOCSYNC:
+ {
+ int frame;
+
+ if (copy_from_user((void *)&frame, arg, sizeof(int)))
+ return -EFAULT;
+
+ PDEBUG(4, "syncing to frame %d, grabstate = %d", frame,
+ ov511->frame[frame].grabstate);
- ov511->frame[vm.frame].width = vm.width;
- ov511->frame[vm.frame].height = vm.height;
- ov511->frame[vm.frame].format = vm.format;
- ov511->frame[vm.frame].sub_flag = ov511->sub_flag;
- ov511->frame[vm.frame].segsize =
- vm.format == VIDEO_PALETTE_RGB24 ? 384 : 256;
- ov511->frame[vm.frame].depth =
- vm.format == VIDEO_PALETTE_RGB24 ? 3 : 1;
-
- /* Mark it as ready */
- ov511->frame[vm.frame].grabstate = FRAME_READY;
-
- return ov511_new_frame(ov511, vm.frame);
- }
- case VIDIOCSYNC:
- {
- int frame;
-
- if (copy_from_user((void *)&frame, arg, sizeof(int)))
- return -EFAULT;
-
- PDEBUG(4, "syncing to frame %d, grabstate = %d", frame,
- ov511->frame[frame].grabstate);
-
- switch (ov511->frame[frame].grabstate) {
- case FRAME_UNUSED:
- return -EINVAL;
- case FRAME_READY:
- case FRAME_GRABBING:
- case FRAME_ERROR:
+ switch (ov511->frame[frame].grabstate) {
+ case FRAME_UNUSED:
+ return -EINVAL;
+ case FRAME_READY:
+ case FRAME_GRABBING:
+ case FRAME_ERROR:
redo:
- if (!ov511->dev)
- return -EIO;
+ if (!ov511->dev)
+ return -EIO;
- do {
+ do {
#if 0
- init_waitqueue_head(&ov511->frame[frame].wq);
+ init_waitqueue_head(&ov511->frame[frame].wq);
#endif
- interruptible_sleep_on(&ov511->frame[frame].wq);
- if (signal_pending(current))
- return -EINTR;
- } while (ov511->frame[frame].grabstate == FRAME_GRABBING);
-
- if (ov511->frame[frame].grabstate == FRAME_ERROR) {
- int ret;
-
- if ((ret = ov511_new_frame(ov511, frame)) < 0)
- return ret;
- goto redo;
- }
- case FRAME_DONE:
- ov511->frame[frame].grabstate = FRAME_UNUSED;
- break;
- }
+ interruptible_sleep_on(&ov511->frame[frame].wq);
+ if (signal_pending(current))
+ return -EINTR;
+ } while (ov511->frame[frame].grabstate == FRAME_GRABBING);
+
+ if (ov511->frame[frame].grabstate == FRAME_ERROR) {
+ int ret;
+
+ if ((ret = ov511_new_frame(ov511, frame)) < 0)
+ return ret;
+ goto redo;
+ }
+ case FRAME_DONE:
+ ov511->frame[frame].grabstate = FRAME_UNUSED;
+ break;
+ }
+
+ ov511->frame[frame].grabstate = FRAME_UNUSED;
+
+ /* Reset the hardware snapshot button */
+ /* FIXME - Is this the best place for this? */
+ if ((ov511->snap_enabled) &&
+ (ov511->frame[frame].snapshot)) {
+ ov511->frame[frame].snapshot = 0;
+ ov511_reg_write(ov511->dev, 0x52, 0x01);
+ ov511_reg_write(ov511->dev, 0x52, 0x03);
+ ov511_reg_write(ov511->dev, 0x52, 0x01);
+ }
+
+ return 0;
+ }
+ case VIDIOCGFBUF:
+ {
+ struct video_buffer vb;
- ov511->frame[frame].grabstate = FRAME_UNUSED;
+ memset(&vb, 0, sizeof(vb));
+ vb.base = NULL; /* frame buffer not supported, not used */
- /* Reset the hardware snapshot button */
- /* FIXME - Is this the best place for this? */
- if ((ov511->snap_enabled) &&
- (ov511->frame[frame].snapshot)) {
- ov511->frame[frame].snapshot = 0;
- ov511_reg_write(ov511->dev, 0x52, 0x01);
- ov511_reg_write(ov511->dev, 0x52, 0x03);
- ov511_reg_write(ov511->dev, 0x52, 0x01);
- }
+ if (copy_to_user((void *)arg, (void *)&vb, sizeof(vb)))
+ return -EFAULT;
- return 0;
- }
- case VIDIOCGFBUF:
- {
- struct video_buffer vb;
-
- memset(&vb, 0, sizeof(vb));
- vb.base = NULL; /* frame buffer not supported, not used */
-
- if (copy_to_user((void *)arg, (void *)&vb, sizeof(vb)))
- return -EFAULT;
-
- return 0;
- }
- case VIDIOCKEY:
- return 0;
- case VIDIOCCAPTURE:
- return -EINVAL;
- case VIDIOCSFBUF:
- return -EINVAL;
- case VIDIOCGTUNER:
- case VIDIOCSTUNER:
- return -EINVAL;
- case VIDIOCGFREQ:
- case VIDIOCSFREQ:
- return -EINVAL;
- case VIDIOCGAUDIO:
- case VIDIOCSAUDIO:
- return -EINVAL;
- default:
- return -ENOIOCTLCMD;
+ return 0;
}
+ case VIDIOCKEY:
+ return 0;
+ case VIDIOCCAPTURE:
+ return -EINVAL;
+ case VIDIOCSFBUF:
+ return -EINVAL;
+ case VIDIOCGTUNER:
+ case VIDIOCSTUNER:
+ return -EINVAL;
+ case VIDIOCGFREQ:
+ case VIDIOCSFREQ:
+ return -EINVAL;
+ case VIDIOCGAUDIO:
+ case VIDIOCSAUDIO:
+ return -EINVAL;
+ default:
+ return -ENOIOCTLCMD;
+ } /* End switch(cmd) */
+
return 0;
}
+
static long ov511_read(struct video_device *dev, char *buf, unsigned long count, int noblock)
{
struct usb_ov511 *ov511 = (struct usb_ov511 *)dev;
+ int i;
int frmx = -1;
volatile struct ov511_frame *frame;
- PDEBUG(4, "ov511_read: %ld bytes, noblock=%d", count, noblock);
+ PDEBUG(4, "%ld bytes, noblock=%d", count, noblock);
if (!dev || !buf)
return -EFAULT;
@@ -1776,6 +2119,7 @@
else if (ov511->frame[1].grabstate >= FRAME_DONE)/* _DONE or _ERROR */
frmx = 1;
+ /* If nonblocking we return immediately */
if (noblock && (frmx == -1))
return -EAGAIN;
@@ -1794,19 +2138,26 @@
frame = &ov511->frame[frmx];
+ /* FIXME */
+ frame->segsize = frame->format == VIDEO_PALETTE_RGB24 ? 384 : 256;
+ frame->depth = frame->format == VIDEO_PALETTE_RGB24 ? 24 : 8;
+
restart:
if (!ov511->dev)
return -EIO;
+ /* Wait while we're grabbing the image */
+ PDEBUG(4, "Waiting image grabbing");
while (frame->grabstate == FRAME_GRABBING) {
interruptible_sleep_on(&ov511->frame[frmx].wq);
if (signal_pending(current))
return -EINTR;
}
+ PDEBUG(4, "Got image, frame->grabstate = %d", frame->grabstate);
if (frame->grabstate == FRAME_ERROR) {
frame->bytes_read = 0;
- err("ov511_read: errored frame %d", ov511->curframe);
+ err("** ick! ** Errored frame %d", ov511->curframe);
if (ov511_new_frame(ov511, frmx))
err("ov511_read: ov511_new_frame error");
goto restart;
@@ -1814,14 +2165,21 @@
/* Repeat until we get a snapshot frame */
+ if (!ov511->snap_enabled) {
+ PDEBUG (4, "snap disabled");
+ } else {
+ PDEBUG (4, "Waiting snapshot frame");
+ }
if (ov511->snap_enabled && !frame->snapshot) {
frame->bytes_read = 0;
if (ov511_new_frame(ov511, frmx))
- err("ov511_read: ov511_new_frame error");
+ err("ov511_new_frame error");
goto restart;
}
/* Clear the snapshot */
+ if (ov511->snap_enabled)
+ PDEBUG (4, "Clear snapshot");
if (ov511->snap_enabled && frame->snapshot) {
frame->snapshot = 0;
ov511_reg_write(ov511->dev, 0x52, 0x01);
@@ -1829,20 +2187,24 @@
ov511_reg_write(ov511->dev, 0x52, 0x01);
}
- PDEBUG(4, "ov511_read: frmx=%d, bytes_read=%ld, scanlength=%ld", frmx,
+ PDEBUG(4, "frmx=%d, bytes_read=%ld, scanlength=%ld", frmx,
frame->bytes_read, frame->scanlength);
/* copy bytes to user space; we allow for partials reads */
// if ((count + frame->bytes_read) > frame->scanlength)
// count = frame->scanlength - frame->bytes_read;
+
/* FIXME - count hardwired to be one frame... */
- count = frame->width * frame->height * frame->depth;
+ count = frame->width * frame->height * (frame->depth >> 3);
- if (copy_to_user(buf, frame->data + frame->bytes_read, count))
+ 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);
return -EFAULT;
+ }
frame->bytes_read += count;
- PDEBUG(4, "ov511_read: {copy} count used=%ld, new bytes_read=%ld",
+ PDEBUG(4, "{copy} count used=%ld, new bytes_read=%ld",
count, frame->bytes_read);
if (frame->bytes_read >= frame->scanlength) { /* All data has been read */
@@ -1851,13 +2213,17 @@
/* Mark it as available to be used again. */
ov511->frame[frmx].grabstate = FRAME_UNUSED;
if (ov511_new_frame(ov511, frmx ? 0 : 1))
- err("ov511_read: ov511_new_frame returned error");
+ err("ov511_new_frame returned error");
}
+ PDEBUG(4, "read finished, returning %ld (sweet)", count);
+
return count;
}
-static int ov511_mmap(struct video_device *dev, const char *adr, unsigned long size)
+
+static int ov511_mmap(struct video_device *dev, const char *adr,
+ unsigned long size)
{
struct usb_ov511 *ov511 = (struct usb_ov511 *)dev;
unsigned long start = (unsigned long)adr;
@@ -1888,6 +2254,7 @@
return 0;
}
+
static struct video_device ov511_template = {
name: "OV511 USB Camera",
type: VID_TYPE_CAPTURE,
@@ -1901,12 +2268,81 @@
initialize: ov511_init_done,
};
-static int ov7610_configure(struct usb_ov511 *ov511)
+
+/****************************************************************************
+ *
+ * OV511/OV7610 configuration
+ *
+ ***************************************************************************/
+
+static int ov76xx_configure(struct usb_ov511 *ov511)
{
struct usb_device *dev = ov511->dev;
- int tries;
+ int i, success;
int rc;
+ static struct ov511_regvals aRegvalsNorm7610[] =
+ {{OV511_I2C_BUS, 0x10, 0xff},
+ {OV511_I2C_BUS, 0x16, 0x06},
+ {OV511_I2C_BUS, 0x28, 0x24},
+ {OV511_I2C_BUS, 0x2b, 0xac},
+ {OV511_I2C_BUS, 0x05, 0x00},
+ {OV511_I2C_BUS, 0x06, 0x00},
+ {OV511_I2C_BUS, 0x12, 0x00},
+ {OV511_I2C_BUS, 0x38, 0x81},
+ {OV511_I2C_BUS, 0x28, 0x24}, /* 0c */
+ {OV511_I2C_BUS, 0x05, 0x00},
+ {OV511_I2C_BUS, 0x0f, 0x05},
+ {OV511_I2C_BUS, 0x15, 0x01},
+ {OV511_I2C_BUS, 0x20, 0x1c},
+ {OV511_I2C_BUS, 0x23, 0x2a},
+ {OV511_I2C_BUS, 0x24, 0x10},
+ {OV511_I2C_BUS, 0x25, 0x8a},
+ {OV511_I2C_BUS, 0x27, 0xc2},
+ {OV511_I2C_BUS, 0x29, 0x03}, /* 91 */
+ {OV511_I2C_BUS, 0x2a, 0x04},
+ {OV511_I2C_BUS, 0x2c, 0xfe},
+ {OV511_I2C_BUS, 0x30, 0x71},
+ {OV511_I2C_BUS, 0x31, 0x60},
+ {OV511_I2C_BUS, 0x32, 0x26},
+ {OV511_I2C_BUS, 0x33, 0x20},
+ {OV511_I2C_BUS, 0x34, 0x48},
+ {OV511_I2C_BUS, 0x12, 0x24},
+ {OV511_I2C_BUS, 0x11, 0x01},
+ {OV511_I2C_BUS, 0x0c, 0x24},
+ {OV511_I2C_BUS, 0x0d, 0x24},
+ {OV511_DONE_BUS, 0x0, 0x00},
+ };
+
+ static struct ov511_regvals aRegvalsNorm7620[] =
+ {{OV511_I2C_BUS, 0x10, 0xff},
+ {OV511_I2C_BUS, 0x16, 0x06},
+ {OV511_I2C_BUS, 0x28, 0x24},
+ {OV511_I2C_BUS, 0x2b, 0xac},
+ {OV511_I2C_BUS, 0x12, 0x00},
+ {OV511_I2C_BUS, 0x28, 0x24},
+ {OV511_I2C_BUS, 0x05, 0x00},
+ {OV511_I2C_BUS, 0x0f, 0x05},
+ {OV511_I2C_BUS, 0x15, 0x01},
+ {OV511_I2C_BUS, 0x23, 0x00},
+ {OV511_I2C_BUS, 0x24, 0x10},
+ {OV511_I2C_BUS, 0x25, 0x8a},
+ {OV511_I2C_BUS, 0x27, 0xe2},
+ {OV511_I2C_BUS, 0x29, 0x03},
+ {OV511_I2C_BUS, 0x2a, 0x00},
+ {OV511_I2C_BUS, 0x2c, 0xfe},
+ {OV511_I2C_BUS, 0x30, 0x71},
+ {OV511_I2C_BUS, 0x31, 0x60},
+ {OV511_I2C_BUS, 0x32, 0x26},
+ {OV511_I2C_BUS, 0x33, 0x20},
+ {OV511_I2C_BUS, 0x34, 0x48},
+ {OV511_I2C_BUS, 0x12, 0x24},
+ {OV511_I2C_BUS, 0x11, 0x01},
+ {OV511_I2C_BUS, 0x0c, 0x24},
+ {OV511_I2C_BUS, 0x0d, 0x24},
+ {OV511_DONE_BUS, 0x0, 0x00},
+ };
+
if(ov511_reg_write(dev, OV511_REG_I2C_SLAVE_ID_WRITE,
OV7610_I2C_WRITE_ID) < 0)
return -1;
@@ -1918,64 +2354,90 @@
if (ov511_reset(dev, OV511_RESET_NOREGS) < 0)
return -1;
- /* Reset the 7610 and wait a bit for it to initialize */
+ /* Reset the 76xx */
if (ov511_i2c_write(dev, 0x12, 0x80) < 0) return -1;
- schedule_timeout (1 + 150 * HZ / 1000);
- /* Dummy read to sync I2C */
- if(ov511_i2c_read(dev, 0x00) < 0)
- return -1;
-
- tries = 5;
- while((tries > 0) &&
- ((ov511_i2c_read(dev, OV7610_REG_ID_HIGH) != 0x7F) ||
- (ov511_i2c_read(dev, OV7610_REG_ID_LOW) != 0xA2))) {
- --tries;
+ for (i = 0, success = 0; i < i2c_detect_tries && !success; i++) {
+ if ((ov511_i2c_read(dev, OV7610_REG_ID_HIGH) == 0x7F) &&
+ (ov511_i2c_read(dev, OV7610_REG_ID_LOW) == 0xA2))
+ success = 1;
+
+ /* Dummy read to sync I2C */
+ if (ov511_i2c_read(dev, 0x00) < 0) return -1;
+ /* Wait for it to initialize */
+ schedule_timeout (1 + 150 * HZ / 1000);
}
-
- if (tries == 1) {
- err("Failed to read sensor ID. You might not have an OV7610/20,");
+
+ if (success) {
+ PDEBUG(1, "I2C synced in %d attempt(s)", i);
+ } else {
+ err("Failed to read sensor ID. You might not have an OV76xx,");
err("or it may be not responding. Report this to");
err("mmcclelland@delphi.com");
return -1;
}
+ /* Detect sensor if user didn't use override param */
if (sensor == 0) {
rc = ov511_i2c_read(dev, OV7610_REG_COM_I);
if (rc < 0) {
err("Error detecting sensor type");
return -1;
- }
- else if((rc & 3) == 3) {
+ } else if((rc & 3) == 3) {
printk("ov511: Sensor is an OV7610\n");
ov511->sensor = SEN_OV7610;
- }
- else if((rc & 3) == 1) {
+ } else if((rc & 3) == 1) {
printk("ov511: Sensor is an OV7620AE\n");
ov511->sensor = SEN_OV7620AE;
- }
- else if((rc & 3) == 0) {
+ } else if((rc & 3) == 0) {
printk("ov511: Sensor is an OV7620\n");
ov511->sensor = SEN_OV7620;
- }
- else {
+ } else {
err("Unknown image sensor version: %d", rc & 3);
return -1;
}
- }
- else { /* sensor != 0; user overrode detection */
+ } else { /* sensor != 0; user overrode detection */
ov511->sensor = sensor;
printk("ov511: Sensor set to type %d\n", ov511->sensor);
}
+ if (ov511->sensor == SEN_OV7620) {
+ if (ov511_write_regvals(dev, aRegvalsNorm7620))
+ return -1;
+ } else {
+ if (ov511_write_regvals(dev, aRegvalsNorm7610))
+ return -1;
+ }
+
+ if (aperture < 0) { /* go with the default */
+ if (ov511_i2c_write(dev, 0x26, 0xa2) < 0) return -1;
+ } else if (aperture <= 0xf) { /* user overrode default */
+ if (ov511_i2c_write(dev, 0x26, (aperture << 4) + 2) < 0)
+ return -1;
+ } else {
+ err("Invalid setting for aperture; legal value: 0 - 15");
+ return -1;
+ }
+
+ if (autoadjust) {
+ if (ov511_i2c_write(dev, 0x13, 0x01) < 0) return -1;
+ if (ov511_i2c_write(dev, 0x2d,
+ ov511->sensor==SEN_OV7620?0x91:0x93) < 0) return -1;
+ } else {
+ if (ov511_i2c_write(dev, 0x13, 0x00) < 0) return -1;
+ if (ov511_i2c_write(dev, 0x2d,
+ ov511->sensor==SEN_OV7620?0x81:0x83) < 0) return -1;
+ ov511_i2c_write(dev, 0x28, ov511_i2c_read(dev, 0x28) | 8);
+ }
+
return 0;
}
+
static int ov511_configure(struct usb_ov511 *ov511)
{
struct usb_device *dev = ov511->dev;
- int rc;
static struct ov511_regvals aRegvalsInit[] =
{{OV511_REG_BUS, OV511_REG_SYSTEM_RESET, 0x7f},
@@ -1988,7 +2450,7 @@
{OV511_DONE_BUS, 0x0, 0x00},
};
- static struct ov511_regvals aRegvalsNorm7610[] =
+ static struct ov511_regvals aRegvalsNorm511[] =
{{OV511_REG_BUS, 0x20, 0x01},
{OV511_REG_BUS, 0x52, 0x02},
{OV511_REG_BUS, 0x52, 0x00},
@@ -2003,85 +2465,6 @@
{OV511_REG_BUS, 0x77, 0x01},
{OV511_REG_BUS, 0x78, 0x06},
{OV511_REG_BUS, 0x79, 0x03},
-
- {OV511_I2C_BUS, 0x10, 0xff},
- {OV511_I2C_BUS, 0x16, 0x06},
- {OV511_I2C_BUS, 0x28, 0x24},
- {OV511_I2C_BUS, 0x2b, 0xac},
- {OV511_I2C_BUS, 0x05, 0x00},
- {OV511_I2C_BUS, 0x06, 0x00},
-
- {OV511_I2C_BUS, 0x12, 0x00},
- {OV511_I2C_BUS, 0x38, 0x81},
- {OV511_I2C_BUS, 0x28, 0x24}, /* 0c */
- {OV511_I2C_BUS, 0x05, 0x00},
- {OV511_I2C_BUS, 0x0f, 0x05},
- {OV511_I2C_BUS, 0x15, 0x01},
- {OV511_I2C_BUS, 0x20, 0x1c},
- {OV511_I2C_BUS, 0x23, 0x2a},
- {OV511_I2C_BUS, 0x24, 0x10},
- {OV511_I2C_BUS, 0x25, 0x8a},
- {OV511_I2C_BUS, 0x26, 0x90},
- {OV511_I2C_BUS, 0x27, 0xc2},
- {OV511_I2C_BUS, 0x29, 0x03}, /* 91 */
- {OV511_I2C_BUS, 0x2a, 0x04},
- {OV511_I2C_BUS, 0x2c, 0xfe},
- {OV511_I2C_BUS, 0x30, 0x71},
- {OV511_I2C_BUS, 0x31, 0x60},
- {OV511_I2C_BUS, 0x32, 0x26},
- {OV511_I2C_BUS, 0x33, 0x20},
- {OV511_I2C_BUS, 0x34, 0x48},
- {OV511_I2C_BUS, 0x12, 0x24},
- {OV511_I2C_BUS, 0x11, 0x01},
- {OV511_I2C_BUS, 0x0c, 0x24},
- {OV511_I2C_BUS, 0x0d, 0x24},
- {OV511_DONE_BUS, 0x0, 0x00},
- };
-
- static struct ov511_regvals aRegvalsNorm7620[] =
- {{OV511_REG_BUS, 0x20, 0x01},
- {OV511_REG_BUS, 0x52, 0x02},
- {OV511_REG_BUS, 0x52, 0x00},
- {OV511_REG_BUS, 0x31, 0x1f},
- {OV511_REG_BUS, 0x70, 0x3f},
- {OV511_REG_BUS, 0x71, 0x3f},
- {OV511_REG_BUS, 0x72, 0x01},
- {OV511_REG_BUS, 0x73, 0x01},
- {OV511_REG_BUS, 0x74, 0x01},
- {OV511_REG_BUS, 0x75, 0x01},
- {OV511_REG_BUS, 0x76, 0x01},
- {OV511_REG_BUS, 0x77, 0x01},
- {OV511_REG_BUS, 0x78, 0x06},
- {OV511_REG_BUS, 0x79, 0x03},
-
- {OV511_I2C_BUS, 0x10, 0xff},
- {OV511_I2C_BUS, 0x16, 0x06},
- {OV511_I2C_BUS, 0x28, 0x24},
- {OV511_I2C_BUS, 0x2b, 0xac},
-
- {OV511_I2C_BUS, 0x12, 0x00},
-
- {OV511_I2C_BUS, 0x28, 0x24},
- {OV511_I2C_BUS, 0x05, 0x00},
- {OV511_I2C_BUS, 0x0f, 0x05},
- {OV511_I2C_BUS, 0x15, 0x01},
- {OV511_I2C_BUS, 0x23, 0x00},
- {OV511_I2C_BUS, 0x24, 0x10},
- {OV511_I2C_BUS, 0x25, 0x8a},
- {OV511_I2C_BUS, 0x26, 0xa2},
- {OV511_I2C_BUS, 0x27, 0xe2},
- {OV511_I2C_BUS, 0x29, 0x03},
- {OV511_I2C_BUS, 0x2a, 0x00},
- {OV511_I2C_BUS, 0x2c, 0xfe},
- {OV511_I2C_BUS, 0x30, 0x71},
- {OV511_I2C_BUS, 0x31, 0x60},
- {OV511_I2C_BUS, 0x32, 0x26},
- {OV511_I2C_BUS, 0x33, 0x20},
- {OV511_I2C_BUS, 0x34, 0x48},
- {OV511_I2C_BUS, 0x12, 0x24},
- {OV511_I2C_BUS, 0x11, 0x01},
- {OV511_I2C_BUS, 0x0c, 0x24},
- {OV511_I2C_BUS, 0x0d, 0x24},
{OV511_DONE_BUS, 0x0, 0x00},
};
@@ -2096,54 +2479,36 @@
return -EBUSY;
}
- if ((rc = ov511_write_regvals(dev, aRegvalsInit)))
- return rc;
-
- if(ov7610_configure(ov511) < 0) {
- err("failed to configure OV7610");
- goto error;
- }
+ if (ov511_write_regvals(dev, aRegvalsInit)) goto error;
+ if (ov511_write_regvals(dev, aRegvalsNorm511)) goto error;
ov511_set_packet_size(ov511, 0);
- /* Disable compression */
- if (ov511_reg_write(dev, OV511_OMNICE_ENABLE, 0x00) < 0)
- goto error;
-
ov511->snap_enabled = snapshot;
/* Set default sizes in case IOCTL (VIDIOCMCAPTURE) is not used
* (using read() instead). */
ov511->frame[0].width = DEFAULT_WIDTH;
ov511->frame[0].height = DEFAULT_HEIGHT;
+ ov511->frame[0].depth = 24; /**************/
ov511->frame[0].bytes_read = 0;
+ ov511->frame[0].segment = 0;
ov511->frame[1].width = DEFAULT_WIDTH;
ov511->frame[1].height = DEFAULT_HEIGHT;
+ ov511->frame[1].depth = 24;
ov511->frame[1].bytes_read = 0;
+ ov511->frame[1].segment = 0;
/* Initialize to DEFAULT_WIDTH, DEFAULT_HEIGHT, YUV4:2:0 */
- if (ov511->sensor == SEN_OV7620) {
- if (ov511_write_regvals(dev, aRegvalsNorm7620)) goto error;
- }
- else {
- if (ov511_write_regvals(dev, aRegvalsNorm7610)) goto error;
+ if(ov76xx_configure(ov511) < 0) {
+ err("failed to configure OV76xx");
+ goto error;
}
if (ov511_mode_init_regs(ov511, DEFAULT_WIDTH, DEFAULT_HEIGHT,
- VIDEO_PALETTE_RGB24, 0) < 0) goto error;
-
- if (autoadjust) {
- if (ov511_i2c_write(dev, 0x13, 0x01) < 0) goto error;
- if (ov511_i2c_write(dev, 0x2d,
- ov511->sensor==SEN_OV7620?0x91:0x93) < 0) goto error;
- }
- else {
- if (ov511_i2c_write(dev, 0x13, 0x00) < 0) goto error;
- if (ov511_i2c_write(dev, 0x2d,
- ov511->sensor==SEN_OV7620?0x81:0x83) < 0) goto error;
- ov511_i2c_write(dev, 0x28, ov511_i2c_read(dev, 0x28) | 8);
- }
+ VIDEO_PALETTE_RGB24, 0) < 0)
+ goto error;
return 0;
@@ -2158,11 +2523,18 @@
return -EBUSY;
}
+
+/****************************************************************************
+ *
+ * USB routines
+ *
+ ***************************************************************************/
+
static void* ov511_probe(struct usb_device *dev, unsigned int ifnum)
{
struct usb_interface_descriptor *interface;
struct usb_ov511 *ov511;
- int rc;
+ int i;
PDEBUG(1, "probing for device...");
@@ -2191,79 +2563,58 @@
}
memset(ov511, 0, sizeof(*ov511));
-
+
ov511->dev = dev;
ov511->iface = interface->bInterfaceNumber;
- if (dev->descriptor.idProduct == 0x0511) {
+ switch (dev->descriptor.idProduct) {
+ case 0x0511:
info("USB OV511 camera found");
ov511->bridge = BRG_OV511;
- }
- else if (dev->descriptor.idProduct == 0xA511) {
+ break;
+ case 0xA511:
info("USB OV511+ camera found");
ov511->bridge = BRG_OV511PLUS;
+ break;
}
- rc = ov511_reg_read(dev, OV511_REG_SYSTEM_CUSTOM_ID);
- if (rc < 0) {
+ ov511->customid = ov511_reg_read(dev, OV511_REG_SYSTEM_CUSTOM_ID);
+ if (ov511->customid < 0) {
err("Unable to read camera bridge registers");
goto error;
}
-
- switch(ov511->customid = rc) {
- case 0: /* This also means that no custom ID was set */
- printk("ov511: Camera is a generic model (no ID)\n");
- break;
- case 3:
- printk("ov511: Camera is a D-Link DSB-C300\n");
- break;
- case 4:
- printk("ov511: Camera is a generic OV511/OV7610\n");
- break;
- case 5:
- printk("ov511: Camera is a Puretek PT-6007\n");
- break;
- case 21:
- printk("ov511: Camera is a Creative Labs WebCam 3\n");
- break;
- case 36:
- printk("ov511: Camera is a Koala-Cam\n");
- break;
- case 38:
- printk("ov511: Camera is a Lifeview USB Life TV\n");
- printk("ov511: This device is not supported, exiting...\n");
- goto error;
- break;
- case 100:
- printk("ov511: Camera is a Lifeview RoboCam\n");
- break;
- case 102:
- printk("ov511: Camera is a AverMedia InterCam Elite\n");
- break;
- case 112: /* The OmniVision OV7110 evaluation kit uses this too */
- printk("ov511: Camera is a MediaForte MV300\n");
- break;
- default:
- err("Specific camera type (%d) not recognized", rc);
+
+ ov511->desc = -1;
+ PDEBUG (4, "CustomID = %d", ov511->customid);
+ for (i = 0; clist[i].id >= 0; i++) {
+ if (ov511->customid == clist[i].id) {
+ printk ("Camera: %s\n", clist[i].description);
+ ov511->desc = i;
+ break;
+ }
+ }
+
+ /* Lifeview USB Life TV not supported */
+ if (clist[i].id == 38) {
+ err("This device is not supported yet.");
+ return NULL;
+ }
+
+ if (clist[i].id == -1) {
+ err("Camera type (%d) not recognized", ov511->customid);
err("Please contact mmcclelland@delphi.com to request");
err("support for your camera.");
}
-// if (ov511->bridge == BRG_OV511PLUS) {
-// err("Sorry, the OV511+ chip is not supported yet");
-// goto error;
-// }
-
if (!ov511_configure(ov511)) {
- ov511->user=0;
+ ov511->user = 0;
init_MUTEX(&ov511->lock); /* to 1 == available */
return ov511;
- }
- else {
+ } else {
err("Failed to configure camera");
goto error;
}
-
+
return ov511;
error:
@@ -2272,12 +2623,12 @@
ov511 = NULL;
}
- return NULL;
+ return NULL;
}
+
static void ov511_disconnect(struct usb_device *dev, void *ptr)
{
-
struct usb_ov511 *ov511 = (struct usb_ov511 *) ptr;
// video_unregister_device(&ov511->vdev);
@@ -2318,6 +2669,11 @@
ov511->sbuf[0].urb = NULL;
}
+#ifdef CONFIG_PROC_FS
+ PDEBUG(3, "destroying /proc/ov511/video%d", ov511->vdev.minor);
+ destroy_proc_ov511_cam(ov511);
+#endif
+
/* Free the memory */
if (!ov511->user) {
kfree(ov511);
@@ -2332,18 +2688,35 @@
{ NULL, NULL }
};
+
+/****************************************************************************
+ *
+ * Module routines
+ *
+ ***************************************************************************/
+
static int __init usb_ov511_init(void)
{
+#ifdef CONFIG_PROC_FS
+ PDEBUG(3, "creating /proc/ov511");
+ proc_ov511_create();
+#endif
+
if (usb_register(&ov511_driver) < 0)
return -1;
- info("ov511 driver registered");
+ info("ov511 driver version %s registered", version);
return 0;
}
static void __exit usb_ov511_exit(void)
{
+#ifdef CONFIG_PROC_FS
+ PDEBUG(3, "destroying /proc/ov511");
+ proc_ov511_destroy();
+#endif
+
usb_deregister(&ov511_driver);
info("ov511 driver deregistered");
}
FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen (who was at: slshen@lbl.gov)