This is against 2.3.39-pre6 (or later) CHANGES: - The camera detection is now more intelligent and efficient. This is an attempt to eliminate the "Failed to read sensor ID" problem that many people have. - Added "i2c_detect_tries" module parameter. - OV7610 reg COM_F default changed from 0x90 to 0xA2. Bit 2 must be 1 according to the 7620 spec, and I assume the same here. - Added aperture parameter. Values are from 0 - 15 and are sensor specific. Value is written to the upper nybble of 7610/20 reg COM_F. - ov6710_configure() renamed to ov76xx_configure() - moved OV511 reg defaults from aRegvalsNorm7610[] to aRegvalsNorm511[] - moved OV76xx specific code from ov511_configure() to ov76xx_configure() - added version constant - updated memory management functions - proc filesystem support added - minor changes in ov511_read() - ov511_configure() now sets frame->depth and frame->segment; this fixed read - ov511_probe() modified to use custom ID table - ov511.h: PDEBUG updated to print function name and line number - ov511.h: Added proc fs vars to usb_ov511 -- Mark McClelland [EMAIL PROTECTED]
diff -Naur linux-2.3.99-pre6-orig/Documentation/usb/ov511.txt linux/Documentation/usb/ov511.txt --- linux-2.3.99-pre6-orig/Documentation/usb/ov511.txt Thu Apr 13 23:23:28 2000 +++ linux/Documentation/usb/ov511.txt Fri May 5 14:34:20 2000 @@ -6,8 +6,11 @@ Homepage: http://alpha.dyndns.org/ov511 NEW IN THIS VERSION: - o Support for OV511+ - o Support for OV7620 + o Improvements to sensor detection code + o Added "i2c_detect_tries" and "aperture" parameters + o proc filesystem status support + o read() fixed partially + o code cleanups and minor fixes INTRODUCTION: @@ -151,11 +154,13 @@ o Monochrome o Setting/getting of saturation, contrast and brightness (no hue yet; only works with OV7610, not the OV7620 or OV7620AE) + o proc status reporting EXPERIMENTAL FEATURES: o fix_rgb_offset: Sometimes works, but other times causes errors with xawtv and corrupted frames. o Snapshot mode (only works with some read() based apps; see below for more) + o read() support TODO: o Fix the noise / grainy image problem. @@ -180,6 +185,8 @@ o Get rid of the memory management functions (put them in videodev.c??) o Setting of contrast and brightness not working with 7620 o Driver/camera state save/restore for when USB supports suspend/resume + o Multiple cameras reportedly do not work simultaneously + o Problems with OHCI HOW TO CONTACT ME: diff -Naur linux-2.3.99-pre6-orig/drivers/usb/ov511.c linux/drivers/usb/ov511.c --- linux-2.3.99-pre6-orig/drivers/usb/ov511.c Thu Apr 13 23:23:44 2000 +++ linux/drivers/usb/ov511.c Fri May 5 14:34:33 2000 @@ -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, [EMAIL PROTECTED], 2/26/2000 * Snapshot code by Kevin Moore * OV7620 fixes by Charl P. Botha <[EMAIL PROTECTED]> + * Changes by Claudio Matsuoka, [EMAIL PROTECTED], 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.12"; + #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,89 @@ /* 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 + **********************************************************************/ + +#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,3,0)) + +static inline unsigned long uvirt_to_phys(unsigned long adr) +{ + pgd_t *pgd; + pmd_t *pmd; + pte_t *ptep, pte; + + pgd = pgd_offset(current->mm, adr); + if (pgd_none(*pgd)) + return 0; + pmd = pmd_offset(pgd, adr); + if (pmd_none(*pmd)) + return 0; + ptep = pte_offset(pmd, adr/*&(~PGDIR_MASK)*/); + pte = *ptep; + if (pte_present(pte)) + return virt_to_phys((void *)(pte_page(pte)|(adr&(PAGE_SIZE-1)))); + return 0; +} + +static inline unsigned long kvirt_to_pa(unsigned long adr) +{ + return uvirt_to_phys(VMALLOC_VMADDR(adr)); +} + +#else /* (LINUX_VERSION_CODE >= KERNEL_VERSION(2,3,0)) */ /* Given PGD from the address space's page table, return the kernel * virtual mapping of the physical memory mapped at ADR. @@ -119,31 +185,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,9 +204,10 @@ 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; } +#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(2,3,0)) */ + static void *rvmalloc(unsigned long size) { @@ -179,7 +226,11 @@ adr = (unsigned long) mem; while (size > 0) { page = kvirt_to_pa(adr); +#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,3,0)) + mem_map_reserve(MAP_NR(phys_to_virt(page))); +#else mem_map_reserve(MAP_NR(__va(page))); +#endif adr += PAGE_SIZE; if (size > PAGE_SIZE) size -= PAGE_SIZE; @@ -203,7 +254,11 @@ adr=(unsigned long) mem; while (size > 0) { page = kvirt_to_pa(adr); +#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,3,0)) + mem_map_unreserve(MAP_NR(phys_to_virt(page))); +#else mem_map_unreserve(MAP_NR(__va(page))); +#endif adr += PAGE_SIZE; if (size > PAGE_SIZE) size -= PAGE_SIZE; @@ -213,9 +268,290 @@ 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 +} + +#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,3,0)) +/* + * This is called as the fill_inode function when an inode + * is going into (fill = 1) or out of service (fill = 0). + * We use it here to manage the module use counts. + */ +static void proc_ov511_modcount(struct inode *inode, int fill) +{ +#ifdef MODULE + if (fill) + MOD_INC_USE_COUNT; + else + MOD_DEC_USE_COUNT; +#endif +} +#endif + +static void proc_ov511_create(void) +{ + ov511_proc_root = create_proc_entry("ov511", S_IFDIR, 0); + + if (ov511_proc_root) { +#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,3,0)) + ov511_proc_root->fill_inode = &proc_ov511_modcount; +#else + ov511_proc_root->owner = THIS_MODULE; +#endif + } 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 +569,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 +584,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 +616,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 +639,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 +656,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 +673,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 +702,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 +712,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 +741,7 @@ return rc; } + #if 0 static void ov511_dump_i2c_range( struct usb_device *dev, int reg1, int regn) { @@ -411,12 +753,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 +771,7 @@ } } + static void ov511_dump_regs( struct usb_device *dev) { PDEBUG(1, "CAMERA INTERFACE REGS"); @@ -451,6 +796,7 @@ } #endif + static int ov511_reset(struct usb_device *dev, unsigned char reset_type) { int rc; @@ -465,6 +811,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 +820,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 +828,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 +836,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 +851,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 +864,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 +891,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 +903,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 +919,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 +939,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 +962,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 +975,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 +999,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 +1040,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 +1122,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; +/* + * 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++; + 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; } - 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 +1547,7 @@ return totlen; } + static void ov511_isoc_irq(struct urb *urb) { int len; @@ -1187,28 +1560,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 +1612,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 +1631,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 +1657,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 +1681,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 +1704,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 +1717,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 +1736,12 @@ } -/* Video 4 Linux API */ +/**************************************************************************** + * + * V4L API + * + ***************************************************************************/ + static int ov511_open(struct video_device *dev, int flags) { int err = -EBUSY; @@ -1409,9 +1802,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 +1831,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; @@ -1756,13 +2156,15 @@ 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 +2178,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 +2197,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 +2224,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 +2246,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 +2272,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 +2313,7 @@ return 0; } + static struct video_device ov511_template = { name: "OV511 USB Camera", type: VID_TYPE_CAPTURE, @@ -1901,12 +2327,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 +2413,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("[EMAIL PROTECTED]"); 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 +2509,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 +2524,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 +2538,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 +2582,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 +2622,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 [EMAIL PROTECTED] 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 +2682,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 +2728,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 +2747,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"); } diff -Naur linux-2.3.99-pre6-orig/drivers/usb/ov511.h linux/drivers/usb/ov511.h --- linux-2.3.99-pre6-orig/drivers/usb/ov511.h Thu Apr 13 23:23:45 2000 +++ linux/drivers/usb/ov511.h Fri May 5 14:34:29 2000 @@ -1,34 +1,38 @@ #ifndef __LINUX_OV511_H #define __LINUX_OV511_H +#include <asm/uaccess.h> +#include <linux/videodev.h> +#include <linux/smp_lock.h> + #define OV511_DEBUG /* Turn on debug messages */ #ifdef OV511_DEBUG # define PDEBUG(level, fmt, args...) \ -if (debug >= level) printk("ov511: " fmt "\n" , ## args) +if (debug >= level) printk("ov511: [" __PRETTY_FUNCTION__ ":%d] " fmt "\n", __LINE__ +, ## args) #else # define PDEBUG(level, fmt, args...) do {} while(0) #endif /* Camera interface register numbers */ -#define OV511_REG_CAMERA_DELAY_MODE 0x10 -#define OV511_REG_CAMERA_EDGE_MODE 0x11 +#define OV511_REG_CAMERA_DELAY_MODE 0x10 +#define OV511_REG_CAMERA_EDGE_MODE 0x11 #define OV511_REG_CAMERA_CLAMPED_PIXEL_NUM 0x12 #define OV511_REG_CAMERA_CLAMPED_LINE_NUM 0x13 #define OV511_REG_CAMERA_PIXEL_DIVISOR 0x14 #define OV511_REG_CAMERA_LINE_DIVISOR 0x15 #define OV511_REG_CAMERA_DATA_INPUT_SELECT 0x16 #define OV511_REG_CAMERA_RESERVED_LINE_MODE 0x17 -#define OV511_REG_CAMERA_BITMASK 0x18 +#define OV511_REG_CAMERA_BITMASK 0x18 /* Snapshot mode camera interface register numbers */ #define OV511_REG_SNAP_CAPTURED_FRAME 0x19 #define OV511_REG_SNAP_CLAMPED_PIXEL_NUM 0x1A #define OV511_REG_SNAP_CLAMPED_LINE_NUM 0x1B #define OV511_REG_SNAP_PIXEL_DIVISOR 0x1C -#define OV511_REG_SNAP_LINE_DIVISOR 0x1D +#define OV511_REG_SNAP_LINE_DIVISOR 0x1D #define OV511_REG_SNAP_DATA_INPUT_SELECT 0x1E -#define OV511_REG_SNAP_BITMASK 0x1F +#define OV511_REG_SNAP_BITMASK 0x1F /* DRAM register numbers */ #define OV511_REG_DRAM_ENABLE_FLOW_CONTROL 0x20 @@ -37,21 +41,21 @@ #define OV511_REG_DRAM_REFRESH_COUNTER 0x23 /* ISO FIFO register numbers */ -#define OV511_REG_FIFO_PACKET_SIZE 0x30 -#define OV511_REG_FIFO_BITMASK 0x31 +#define OV511_REG_FIFO_PACKET_SIZE 0x30 +#define OV511_REG_FIFO_BITMASK 0x31 /* PIO register numbers */ -#define OV511_REG_PIO_BITMASK 0x38 -#define OV511_REG_PIO_DATA_PORT 0x39 -#define OV511_REG_PIO_BIST 0x3E +#define OV511_REG_PIO_BITMASK 0x38 +#define OV511_REG_PIO_DATA_PORT 0x39 +#define OV511_REG_PIO_BIST 0x3E /* I2C register numbers */ -#define OV511_REG_I2C_CONTROL 0x40 +#define OV511_REG_I2C_CONTROL 0x40 #define OV511_REG_I2C_SLAVE_ID_WRITE 0x41 #define OV511_REG_I2C_SUB_ADDRESS_3_BYTE 0x42 #define OV511_REG_I2C_SUB_ADDRESS_2_BYTE 0x43 -#define OV511_REG_I2C_SLAVE_ID_READ 0x44 -#define OV511_REG_I2C_DATA_PORT 0x45 +#define OV511_REG_I2C_SLAVE_ID_READ 0x44 +#define OV511_REG_I2C_DATA_PORT 0x45 #define OV511_REG_I2C_CLOCK_PRESCALER 0x46 #define OV511_REG_I2C_TIME_OUT_COUNTER 0x47 @@ -60,39 +64,39 @@ #define OV511_REG_I2C_SNAP_DATA_PORT 0x49 /* System control register numbers */ -#define OV511_REG_SYSTEM_RESET 0x50 -#define OV511_RESET_UDC 0x01 -#define OV511_RESET_I2C 0x02 -#define OV511_RESET_FIFO 0x04 -#define OV511_RESET_OMNICE 0x08 +#define OV511_REG_SYSTEM_RESET 0x50 +#define OV511_RESET_UDC 0x01 +#define OV511_RESET_I2C 0x02 +#define OV511_RESET_FIFO 0x04 +#define OV511_RESET_OMNICE 0x08 #define OV511_RESET_DRAM_INTF 0x10 #define OV511_RESET_CAMERA_INTF 0x20 -#define OV511_RESET_OV511 0x40 -#define OV511_RESET_NOREGS 0x3F /* All but OV511 & regs */ -#define OV511_RESET_ALL 0x7F +#define OV511_RESET_OV511 0x40 +#define OV511_RESET_NOREGS 0x3F /* All but OV511 & regs */ +#define OV511_RESET_ALL 0x7F #define OV511_REG_SYSTEM_CLOCK_DIVISOR 0x51 -#define OV511_REG_SYSTEM_SNAPSHOT 0x52 +#define OV511_REG_SYSTEM_SNAPSHOT 0x52 #define OV511_REG_SYSTEM_INIT 0x53 -#define OV511_REG_SYSTEM_PWR_CLK 0x54 /* OV511+ only */ -#define OV511_REG_SYSTEM_LED_CTL 0x55 /* OV511+ only */ +#define OV511_REG_SYSTEM_PWR_CLK 0x54 /* OV511+ only */ +#define OV511_REG_SYSTEM_LED_CTL 0x55 /* OV511+ only */ #define OV511_REG_SYSTEM_USER_DEFINED 0x5E -#define OV511_REG_SYSTEM_CUSTOM_ID 0x5F +#define OV511_REG_SYSTEM_CUSTOM_ID 0x5F /* OmniCE register numbers */ -#define OV511_OMNICE_PREDICTION_HORIZ_Y 0x70 +#define OV511_OMNICE_PREDICTION_HORIZ_Y 0x70 #define OV511_OMNICE_PREDICTION_HORIZ_UV 0x71 #define OV511_OMNICE_PREDICTION_VERT_Y 0x72 -#define OV511_OMNICE_PREDICTION_VERT_UV 0x73 +#define OV511_OMNICE_PREDICTION_VERT_UV 0x73 #define OV511_OMNICE_QUANTIZATION_HORIZ_Y 0x74 #define OV511_OMNICE_QUANTIZATION_HORIZ_UV 0x75 #define OV511_OMNICE_QUANTIZATION_VERT_Y 0x76 #define OV511_OMNICE_QUANTIZATION_VERT_UV 0x77 -#define OV511_OMNICE_ENABLE 0x78 -#define OV511_OMNICE_LUT_ENABLE 0x79 -#define OV511_OMNICE_Y_LUT_BEGIN 0x80 -#define OV511_OMNICE_Y_LUT_END 0x9F -#define OV511_OMNICE_UV_LUT_BEGIN 0xA0 -#define OV511_OMNICE_UV_LUT_END 0xBF +#define OV511_OMNICE_ENABLE 0x78 +#define OV511_OMNICE_LUT_ENABLE 0x79 +#define OV511_OMNICE_Y_LUT_BEGIN 0x80 +#define OV511_OMNICE_Y_LUT_END 0x9F +#define OV511_OMNICE_UV_LUT_BEGIN 0xA0 +#define OV511_OMNICE_UV_LUT_END 0xBF /* Alternate numbers for various max packet sizes (OV511 only) */ #define OV511_ALT_SIZE_992 0 @@ -114,7 +118,7 @@ #define OV511PLUS_ALT_SIZE_769 6 #define OV511PLUS_ALT_SIZE_961 7 -/* ov7610 registers */ +/* OV7610 registers */ #define OV7610_REG_GAIN 0x00 #define OV7610_REG_BLUE 0x01 #define OV7610_REG_RED 0x02 @@ -163,12 +167,12 @@ #define SCRATCH_BUF_SIZE 384 -#define FRAMES_PER_DESC 10 /* FIXME - What should this be? */ +#define FRAMES_PER_DESC 10 /* FIXME - What should this be? */ #define FRAME_SIZE_PER_DESC 993 /* FIXME - Deprecated */ -#define MAX_FRAME_SIZE_PER_DESC 993 /* For statically allocated stuff */ +#define MAX_FRAME_SIZE_PER_DESC 993 /* For statically allocated stuff */ // FIXME - should this be 0x81 (endpoint address) or 0x01 (endpoint number)? -#define OV511_ENDPOINT_ADDRESS 0x81 /* Address of isoc endpoint */ +#define OV511_ENDPOINT_ADDRESS 0x81 /* Address of isoc endpoint */ // CAMERA SPECIFIC // FIXME - these can vary between specific models @@ -213,7 +217,7 @@ FRAME_UNUSED, /* Unused (no MCAPTURE) */ FRAME_READY, /* Ready to start grabbing */ FRAME_GRABBING, /* In the process of being grabbed into */ - FRAME_DONE, /* Finished grabbing, but not been synced yet */ + FRAME_DONE, /* Finished grabbing, but not been synced yet */ FRAME_ERROR, /* Something bad happened while processing */ }; @@ -238,7 +242,7 @@ int hdrheight; /* Height */ int sub_flag; /* Sub-capture mode for this frame? */ - int format; /* Format for this frame */ + int format; /* Format for this frame */ int segsize; /* How big is each segment from the camera? */ volatile int grabstate; /* State of grabbing */ @@ -253,7 +257,7 @@ wait_queue_head_t wq; /* Processes waiting */ - int snapshot; /* True if frame was a snapshot */ + int snapshot; /* True if frame was a snapshot */ }; #define OV511_NUMFRAMES 2 @@ -261,23 +265,28 @@ struct usb_ov511 { struct video_device vdev; - + /* Device structure */ struct usb_device *dev; +#if 0 unsigned char customid; /* Type of camera */ +#else + int customid; + int desc; +#endif unsigned char iface; - + struct semaphore lock; - int user; /* user count for exclusive use */ + int user; /* user count for exclusive use */ int streaming; /* Are we streaming Isochronous? */ int grabbing; /* Are we grabbing? */ int compress; /* Should the next frame be compressed? */ - char *fbuf; /* Videodev buffer area */ + char *fbuf; /* Videodev buffer area */ int sub_flag; /* Pix Array subcapture on flag */ int subx; /* Pix Array subcapture x offset */ @@ -297,12 +306,22 @@ wait_queue_head_t wq; /* Processes waiting */ - int snap_enabled; /* Snapshot mode enabled */ + int snap_enabled; /* Snapshot mode enabled */ - int bridge; /* Type of bridge (OV511 or OV511+) */ - int sensor; /* Type of image sensor chip */ + int bridge; /* Type of bridge (OV511 or OV511+) */ + int sensor; /* Type of image sensor chip */ + + int packet_size; /* Frame size per isoc desc */ + + /* proc interface */ + struct semaphore param_lock; /* params lock for this camera */ + struct proc_dir_entry *proc_entry; /* /proc/ov511/videoX */ +}; + - int packet_size; /* Frame size per isoc desc */ +struct cam_list { + int id; + char *description; }; #endif
--------------------------------------------------------------------- To unsubscribe, e-mail: [EMAIL PROTECTED] For additional commands, e-mail: [EMAIL PROTECTED]
