On Fri, Mar 24, 2000, Dunlap, Randy <[EMAIL PROTECTED]> wrote: > Last error was as modules. Sorry about not saying that. Actually, you did say that, I was just curious since I don't get that error while compiling as modules, and now compiling into the kernel, I see I get that error. I also forgot to rip out the 2.2 code for the core and PP driver. I've done it in this patch. I've attached a patch to fix these problems. It compiles into modules and into the kernel now, without any of the 2.2 compatibility code. > Yep, I could need to upgrade some things. > Would you compare and let me know? > > patch 2.5 > gcc: egcs-2.91.66 > binutils: 2.9.1.0.25 Same here. JE
diff -urN linux-2.3.99-pre3.orig/Documentation/Configure.help linux-2.3.99-pre3/Documentation/Configure.help --- linux-2.3.99-pre3.orig/Documentation/Configure.help Fri Mar 24 13:58:02 2000 +++ linux-2.3.99-pre3/Documentation/Configure.help Fri Mar 24 13:57:52 2000 @@ -9203,22 +9203,6 @@ The module will be called printer.o. If you want to compile it as a module, say M here and read Documentation/modules.txt. -USB CPiA Camera support -CONFIG_USB_CPIA - Say Y here if you want to connect this type of camera to your - computer's USB port. - - This driver uses the Video For Linux API. You must enable - (Y or M in config) Video For Linux (under Character Devices) - to use this driver. Information on this API and pointers to - "v4l" programs may be found on the WWW at - http://roadrunner.swansea.uk.linux.org/v4l.shtml . - - This code is also available as a module ( = code which can be - inserted in and removed from the running kernel whenever you want). - The module will be called cpia.o. If you want to compile it as a - module, say M here and read Documentation/modules.txt. - USB IBM (Xirlink) C-It Camera support CONFIG_USB_IBMCAM Say Y here if you want to connect this type of camera to your @@ -14915,6 +14899,31 @@ Here you find the drivers which allow you to use the i2c-* device files, usually found in the /dev directory on your system. They make it possible to have user-space programs use the I2C bus. + +CPiA Video For Linux +CONFIG_VIDEO_CPIA + This is the video4linux driver for cameras based on Vision's CPiA + (Colour Processor Interface ASIC), such as the Creative Labs Video + Blaster Webcam II. If you have one of these cameras, say Y here + and select parallel port and/or USB lowlevel support below, + otherwise say N. This will not work with the Creative Webcam III. + It is also available as a module (cpia.o). + +CPiA Parallel Port Lowlevel Support +CONFIG_VIDEO_CPIA_PP + This is the lowlevel parallel port support for cameras based on + Vision's CPiA (Colour Processor Interface ASIC), such as the + Creative Webcam II. If you have the parallel port version of one + of these cameras, say Y here, otherwise say N. It is also available + as a module (cpia_pp.o). + +CPiA USB Lowlevel Support +CONFIG_VIDEO_CPIA_USB + This is the lowlevel USB support for cameras based on Vision's CPiA + (Colour Processor Interface ASIC), such as the Creative Webcam II. + If you have the USB version of one of these cameras, say Y here, + otherwise say N. This will not work with the Creative Webcam III. + It is also available as a module (cpia_usb.o). # # A couple of things I keep forgetting: diff -urN linux-2.3.99-pre3.orig/Documentation/video4linux/README.cpia linux-2.3.99-pre3/Documentation/video4linux/README.cpia --- linux-2.3.99-pre3.orig/Documentation/video4linux/README.cpia Wed Dec 31 16:00:00 1969 +++ linux-2.3.99-pre3/Documentation/video4linux/README.cpia Fri Mar 24 13:57:52 +2000 @@ -0,0 +1,191 @@ +This is a driver for the CPiA PPC2 driven parallel connected +Camera. For example the Creative WebcamII is CPiA driven. + + ) [1]Peter Pregler, Linz 2000, published under the [2]GNU GPL + +--------------------------------------------------------------------------- + +USAGE: + +General: +======== + +1) Make sure you have created the video devices (/dev/video*): + +- if you have a recent MAKEDEV do a 'cd /dev;./MAKEDEV video' +- otherwise do a: + +cd /dev +mknod video0 c 81 0 +ln -s video0 video + +2) Compile the kernel (see below for the list of options to use), + configure your parport and reboot. + +3) If all worked well you should get messages similar + to the following (your versions may be different) on the console: + +V4L-Driver for Vision CPiA based cameras v0.7.4 +parport0: read2 timeout. +parport0: Multimedia device, VLSI Vision Ltd PPC2 +Parallel port driver for Vision CPiA based camera + CPIA Version: 1.20 (2.0) + CPIA PnP-ID: 0553:0002:0100 + VP-Version: 1.0 0100 + 1 camera(s) found + + +As modules: +=========== + +Make sure you have selected the following kernel options (you can +select all stuff as modules): + +The cpia-stuff is in the section 'Character devices -> Video For Linux'. + +CONFIG_PARPORT=m +CONFIG_PARPORT_PC=m +CONFIG_PARPORT_PC_FIFO=y +CONFIG_PARPORT_1284=y +CONFIG_VIDEO_DEV=m +CONFIG_VIDEO_CPIA=m +CONFIG_VIDEO_CPIA_PP=m + +For autoloading of all those modules you need to tell kerneld some +stuff. Add the following line to your kerneld config-file +(e.g. /etc/modules.conf or wherever your distribution does store that +stuff): + +options parport_pc dma=3 irq=7 +alias char-major-81 cpia_pp + +The first line tells the dma/irq channels to use. Those _must_ match +the settings of your BIOS. Do NOT simply use the values above. See +Documentation/parport.txt for more information about this. The second +line associates the video-device file with the driver. Of cause you +can also load the modules once upon boot (usually done in /etc/modules). + +Linked into the kernel: +======================= + +Make sure you have selected the following kernel options. Note that +you cannot compile the parport-stuff as modules and the cpia-driver +statically (the other way round is okay though). + +The cpia-stuff is in the section 'Character devices -> Video For Linux'. + +CONFIG_PARPORT=y +CONFIG_PARPORT_PC=y +CONFIG_PARPORT_PC_FIFO=y +CONFIG_PARPORT_1284=y +CONFIG_VIDEO_DEV=y +CONFIG_VIDEO_CPIA=y +CONFIG_VIDEO_CPIA_PP=y + +To use DMA/irq you will need to tell the kernel upon boot time the +hardware configuration of the parport. You can give the boot-parameter +at the LILO-prompt or specify it in lilo.conf. I use the following +append-line in lilo.conf: + + append="parport=0x378,7,3" + +See Documentation/parport.txt for more information about the +configuration of the parport and the values given above. Do not simply +use the values given above. + +--------------------------------------------------------------------------- +FEATURES: + +- mmap/read v4l-interface (but no overlay) +- image formats: CIF/QCIF, SIF/QSIF, various others used by isabel; + note: all sizes except CIF/QCIF are implemented by clipping, i.e. + pixels are not uploaded from the camera +- palettes: VIDEO_PALETTE_GRAY, VIDEO_PALETTE_RGB565, VIDEO_PALETTE_RGB555, + VIDEO_PALETTE_RGB24, VIDEO_PALETTE_RGB32, VIDEO_PALETTE_YUYV, + VIDEO_PALETTE_UYVY, VIDEO_PALETTE_YUV422 +- state information (color balance, exposure, ...) is preserved between + device opens +- complete control over camera via proc-interface (_all_ camera settings are + supported), there is also a python-gtk application available for this [3] +- works under SMP (but the driver is completly serialized and synchronous) + so you get no benefit from SMP, but at least it does not crash your box +- might work for non-Intel architecture, let us know about this + +--------------------------------------------------------------------------- +TESTED APPLICATIONS: + +- a simple test application based on Xt is available at [3] +- another test-application based on gqcam-0.4 (uses GTK) +- gqcam-0.6 should work +- xawtv-3.x (also the webcam software) +- xawtv-2.46 +- w3cam (cgi-interface and vidcat, e.g. you may try out 'vidcat |xv + -maxpect -root -quit +noresetroot -rmode 5 -') +- vic, the MBONE video conferencing tool (version 2.8ucl4-1) +- isabel 3R4beta (barely working, but AFAICT all the problems are on + their side) +- camserv-0.40 + +See [3] for pointers to v4l-applications. + +--------------------------------------------------------------------------- +KNOWN PROBLEMS: + +- some applications do not handle the image format correctly, you will + see strange horizontal stripes instead of a nice picture -> make sure + your application does use a supported image size or queries the driver + for the actually used size (reason behind this: the camera cannot + provide any image format, so if size NxM is requested the driver will + use a format to the closest fitting N1xM1, the application should now + query for this granted size, most applications do not). +- all the todo ;) +- if there is not enough light and the picture is too dark try to + adjust the SetSensorFPS setting, automatic frame rate adjustment + has its price +- do not try out isabel 3R4beta (built 135), you will be disappointed + +--------------------------------------------------------------------------- +TODO: + +- multiple camera support (struct camera or something) - This should work, + but hasn't been tested yet. +- architecture independence? +- SMP-safe asynchronous mmap interface +- nibble mode for old parport interfaces +- streaming capture, this should give a performance gain + +--------------------------------------------------------------------------- +IMPLEMENTATION NOTES: + +The camera can act in two modes, streaming or grabbing. Right now a +polling grab-scheme is used. Maybe interrupt driven streaming will be +used for a ansychronous mmap interface in the next major release of the +driver. This might give a better frame rate. + +--------------------------------------------------------------------------- +THANKS (in no particular order): + +- Scott J. Bertin <[EMAIL PROTECTED]> for cleanups, the proc-filesystem + and much more +- Henry Bruce <[EMAIL PROTECTED]> for providing developers information about + the CPiA chip, I wish all companies would treat Linux as seriously +- Karoly Erdei <[EMAIL PROTECTED]> and RISC-Linz for being + my boss ;) resp. my employer and for providing me the hardware and + allow me to devote some working time to this project +- Manuel J. Petit de Gabriel <[EMAIL PROTECTED]> for providing help + with Isabel (http://isabel.dit.upm.es/) +- Bas Huisman <[EMAIL PROTECTED]> for writing the initial parport code +- Jarl Totland <[EMAIL PROTECTED]> for setting up the mailing list + and maintaining the web-server[3] +- Chris Whiteford <[EMAIL PROTECTED]> for fixes related to the + 1.02 firmware +- special kudos to all the tester whose machines crashed and/or + will crash. :) + +--------------------------------------------------------------------------- +REFERENCES + + 1. http://www.risc.uni-linz.ac.at/people/ppregler + mailto:[EMAIL PROTECTED] + 2. see the file COPYING in the top directory of the kernel tree + 3. http://webcam.sourceforge.net/ diff -urN linux-2.3.99-pre3.orig/drivers/char/Config.in linux-2.3.99-pre3/drivers/char/Config.in --- linux-2.3.99-pre3.orig/drivers/char/Config.in Fri Mar 24 13:58:02 2000 +++ linux-2.3.99-pre3/drivers/char/Config.in Fri Mar 24 13:59:23 2000 @@ -209,6 +209,15 @@ dep_tristate ' QuickCam Colour Video For Linux (EXPERIMENTAL)' CONFIG_VIDEO_CQCAM $CONFIG_VIDEO_DEV $CONFIG_PARPORT fi fi + dep_tristate 'CPiA Video For Linux' CONFIG_VIDEO_CPIA $CONFIG_VIDEO_DEV + if [ "$CONFIG_VIDEO_CPIA" != "n" ]; then + if [ "CONFIG_PARPORT_1284" != "n" ]; then + dep_tristate 'CPiA Parallel Port Lowlevel Support' CONFIG_VIDEO_CPIA_PP +$CONFIG_VIDEO_CPIA $CONFIG_PARPORT + fi + if [ "$CONFIG_USB" != "n" ]; then + dep_tristate 'CPiA USB Lowlevel Support' CONFIG_VIDEO_CPIA_USB +$CONFIG_VIDEO_CPIA $CONFIG_USB + fi + fi dep_tristate ' SAA5249 Teletext processor' CONFIG_VIDEO_SAA5249 $CONFIG_VIDEO_DEV $CONFIG_I2C dep_tristate ' SAB3036 tuner' CONFIG_TUNER_3036 $CONFIG_VIDEO_DEV $CONFIG_I2C if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then diff -urN linux-2.3.99-pre3.orig/drivers/char/Makefile linux-2.3.99-pre3/drivers/char/Makefile --- linux-2.3.99-pre3.orig/drivers/char/Makefile Fri Mar 24 13:58:02 2000 +++ linux-2.3.99-pre3/drivers/char/Makefile Fri Mar 24 13:57:52 2000 @@ -38,7 +38,7 @@ export-objs := busmouse.o console.o i2c-old.o keyboard.o sysrq.o \ misc.o pty.o random.o selection.o serial.o videodev.o \ - tty_io.o bttv.o + tty_io.o bttv.o cpia.o KEYMAP =defkeymap.o KEYBD =pc_keyb.o @@ -252,6 +252,9 @@ obj-$(CONFIG_VIDEO_PLANB) += planb.o obj-$(CONFIG_VIDEO_VINO) += vino.o obj-$(CONFIG_VIDEO_STRADIS) += stradis.o +obj-$(CONFIG_VIDEO_CPIA) += cpia.o +obj-$(CONFIG_VIDEO_CPIA_PP) += cpia_pp.o +obj-$(CONFIG_VIDEO_CPIA_USB) += cpia_usb.o obj-$(CONFIG_RADIO_AZTECH) += radio-aztech.o obj-$(CONFIG_RADIO_RTRACK2) += radio-rtrack2.o obj-$(CONFIG_RADIO_SF16FMI) += radio-sf16fmi.o diff -urN linux-2.3.99-pre3.orig/drivers/char/cpia.c linux-2.3.99-pre3/drivers/char/cpia.c --- linux-2.3.99-pre3.orig/drivers/char/cpia.c Wed Dec 31 16:00:00 1969 +++ linux-2.3.99-pre3/drivers/char/cpia.c Fri Mar 24 14:27:09 2000 @@ -0,0 +1,3319 @@ +/* + * cpia CPiA driver + * + * Supports CPiA based Video Camera's. + * + * (C) Copyright 1999-2000 Peter Pregler, + * (C) Copyright 1999-2000 Scott J. Bertin, + * (C) Copyright 1999-2000 Johannes Erdfelt, [EMAIL PROTECTED] + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +/* #define _CPIA_DEBUG_ define for verbose debug output */ +#include <linux/config.h> + +#include <linux/module.h> +#include <linux/version.h> +#include <linux/init.h> +#include <linux/fs.h> +#include <linux/vmalloc.h> +#include <linux/delay.h> +#include <linux/slab.h> +#include <linux/proc_fs.h> +#include <linux/ctype.h> +#include <linux/pagemap.h> +#include <asm/io.h> +#include <asm/semaphore.h> +#include <linux/wrapper.h> + +#ifdef CONFIG_KMOD +#include <linux/kmod.h> +#endif + +#include "cpia.h" + +#ifdef CONFIG_VIDEO_CPIA_PP +extern int cpia_pp_init(void); +#endif +#ifdef CONFIG_VIDEO_CPIA_USB +extern int cpia_usb_init(void); +#endif + +#ifdef MODULE +MODULE_AUTHOR("Scott J. Bertin <[EMAIL PROTECTED]> & Peter Pregler +<[EMAIL PROTECTED]> & Johannes Erdfelt <[EMAIL PROTECTED]>"); +MODULE_DESCRIPTION("V4L-driver for Vision CPiA based cameras"); +MODULE_SUPPORTED_DEVICE("video"); +#endif + +#define ABOUT "V4L-Driver for Vision CPiA based cameras" + +#ifndef VID_HARDWARE_CPIA +#define VID_HARDWARE_CPIA 24 /* FIXME -> from linux/videodev.h */ +#endif + +#define CPIA_MODULE_CPIA (0<<5) +#define CPIA_MODULE_SYSTEM (1<<5) +#define CPIA_MODULE_VP_CTRL (5<<5) +#define CPIA_MODULE_CAPTURE (6<<5) +#define CPIA_MODULE_DEBUG (7<<5) + +#define INPUT (DATA_IN << 8) +#define OUTPUT (DATA_OUT << 8) + +#define CPIA_COMMAND_GetCPIAVersion (INPUT | CPIA_MODULE_CPIA | 1) +#define CPIA_COMMAND_GetPnPID (INPUT | CPIA_MODULE_CPIA | 2) +#define CPIA_COMMAND_GetCameraStatus (INPUT | CPIA_MODULE_CPIA | 3) +#define CPIA_COMMAND_GotoHiPower (OUTPUT | CPIA_MODULE_CPIA | 4) +#define CPIA_COMMAND_GotoLoPower (OUTPUT | CPIA_MODULE_CPIA | 5) +#define CPIA_COMMAND_GotoSuspend (OUTPUT | CPIA_MODULE_CPIA | 7) +#define CPIA_COMMAND_GotoPassThrough (OUTPUT | CPIA_MODULE_CPIA | 8) +#define CPIA_COMMAND_ModifyCameraStatus (OUTPUT | CPIA_MODULE_CPIA | 10) + +#define CPIA_COMMAND_ReadVCRegs (INPUT | CPIA_MODULE_SYSTEM | 1) +#define CPIA_COMMAND_WriteVCReg (OUTPUT | CPIA_MODULE_SYSTEM | 2) +#define CPIA_COMMAND_ReadMCPorts (INPUT | CPIA_MODULE_SYSTEM | 3) +#define CPIA_COMMAND_WriteMCPort (OUTPUT | CPIA_MODULE_SYSTEM | 4) +#define CPIA_COMMAND_SetBaudRate (OUTPUT | CPIA_MODULE_SYSTEM | 5) +#define CPIA_COMMAND_SetECPTiming (OUTPUT | CPIA_MODULE_SYSTEM | 6) +#define CPIA_COMMAND_ReadIDATA (INPUT | CPIA_MODULE_SYSTEM | 7) +#define CPIA_COMMAND_WriteIDATA (OUTPUT | CPIA_MODULE_SYSTEM | 8) +#define CPIA_COMMAND_GenericCall (OUTPUT | CPIA_MODULE_SYSTEM | 9) +#define CPIA_COMMAND_I2CStart (OUTPUT | CPIA_MODULE_SYSTEM | 10) +#define CPIA_COMMAND_I2CStop (OUTPUT | CPIA_MODULE_SYSTEM | 11) +#define CPIA_COMMAND_I2CWrite (OUTPUT | CPIA_MODULE_SYSTEM | 12) +#define CPIA_COMMAND_I2CRead (INPUT | CPIA_MODULE_SYSTEM | 13) + +#define CPIA_COMMAND_GetVPVersion (INPUT | CPIA_MODULE_VP_CTRL | 1) +#define CPIA_COMMAND_SetColourParams (OUTPUT | CPIA_MODULE_VP_CTRL | 3) +#define CPIA_COMMAND_SetExposure (OUTPUT | CPIA_MODULE_VP_CTRL | 4) +#define CPIA_COMMAND_SetColourBalance (OUTPUT | CPIA_MODULE_VP_CTRL | 6) +#define CPIA_COMMAND_SetSensorFPS (OUTPUT | CPIA_MODULE_VP_CTRL | 7) +#define CPIA_COMMAND_SetVPDefaults (OUTPUT | CPIA_MODULE_VP_CTRL | 8) +#define CPIA_COMMAND_SetApcor (OUTPUT | CPIA_MODULE_VP_CTRL | 9) +#define CPIA_COMMAND_SetFlickerCtrl (OUTPUT | CPIA_MODULE_VP_CTRL | 10) +#define CPIA_COMMAND_SetVLOffset (OUTPUT | CPIA_MODULE_VP_CTRL | 11) +#define CPIA_COMMAND_GetColourParams (INPUT | CPIA_MODULE_VP_CTRL | 16) +#define CPIA_COMMAND_GetColourBalance (INPUT | CPIA_MODULE_VP_CTRL | 17) +#define CPIA_COMMAND_GetExposure (INPUT | CPIA_MODULE_VP_CTRL | 18) +#define CPIA_COMMAND_SetSensorMatrix (OUTPUT | CPIA_MODULE_VP_CTRL | 19) +#define CPIA_COMMAND_ColourBars (OUTPUT | CPIA_MODULE_VP_CTRL | 25) +#define CPIA_COMMAND_ReadVPRegs (INPUT | CPIA_MODULE_VP_CTRL | 30) +#define CPIA_COMMAND_WriteVPReg (OUTPUT | CPIA_MODULE_VP_CTRL | 31) + +#define CPIA_COMMAND_GrabFrame (OUTPUT | CPIA_MODULE_CAPTURE | 1) +#define CPIA_COMMAND_UploadFrame (OUTPUT | CPIA_MODULE_CAPTURE | 2) +#define CPIA_COMMAND_SetGrabMode (OUTPUT | CPIA_MODULE_CAPTURE | 3) +#define CPIA_COMMAND_InitStreamCap (OUTPUT | CPIA_MODULE_CAPTURE | 4) +#define CPIA_COMMAND_FiniStreamCap (OUTPUT | CPIA_MODULE_CAPTURE | 5) +#define CPIA_COMMAND_StartStreamCap (OUTPUT | CPIA_MODULE_CAPTURE | 6) +#define CPIA_COMMAND_EndStreamCap (OUTPUT | CPIA_MODULE_CAPTURE | 7) +#define CPIA_COMMAND_SetFormat (OUTPUT | CPIA_MODULE_CAPTURE | 8) +#define CPIA_COMMAND_SetROI (OUTPUT | CPIA_MODULE_CAPTURE | 9) +#define CPIA_COMMAND_SetCompression (OUTPUT | CPIA_MODULE_CAPTURE | 10) +#define CPIA_COMMAND_SetCompressionTarget (OUTPUT | CPIA_MODULE_CAPTURE | 11) +#define CPIA_COMMAND_SetYUVThresh (OUTPUT | CPIA_MODULE_CAPTURE | 12) +#define CPIA_COMMAND_SetCompressionParams (OUTPUT | CPIA_MODULE_CAPTURE | 13) +#define CPIA_COMMAND_DiscardFrame (OUTPUT | CPIA_MODULE_CAPTURE | 14) + +#define CPIA_COMMAND_OutputRS232 (OUTPUT | CPIA_MODULE_DEBUG | 1) +#define CPIA_COMMAND_AbortProcess (OUTPUT | CPIA_MODULE_DEBUG | 4) +#define CPIA_COMMAND_SetDramPage (OUTPUT | CPIA_MODULE_DEBUG | 5) +#define CPIA_COMMAND_StartDramUpload (OUTPUT | CPIA_MODULE_DEBUG | 6) +#define CPIA_COMMAND_StartDummyDtream (OUTPUT | CPIA_MODULE_DEBUG | 8) +#define CPIA_COMMAND_AbortStream (OUTPUT | CPIA_MODULE_DEBUG | 9) +#define CPIA_COMMAND_DownloadDRAM (OUTPUT | CPIA_MODULE_DEBUG | 10) + +enum { + FRAME_READY, /* Ready to grab into */ + FRAME_GRABBING, /* In the process of being grabbed into */ + FRAME_DONE, /* Finished grabbing, but not been synced yet */ + FRAME_UNUSED, /* Unused (no MCAPTURE) */ +}; + +#define COMMAND_NONE 0x0000 +#define COMMAND_SETCOMPRESSION 0x0001 +#define COMMAND_SETCOMPRESSIONTARGET 0x0002 +#define COMMAND_SETCOLOURPARAMS 0x0004 +#define COMMAND_SETFORMAT 0x0008 +#define COMMAND_PAUSE 0x0010 +#define COMMAND_RESUME 0x0020 +#define COMMAND_SETYUVTHRESH 0x0040 +#define COMMAND_SETECPTIMING 0x0080 +#define COMMAND_SETCOMPRESSIONPARAMS 0x0100 +#define COMMAND_SETEXPOSURE 0x0200 +#define COMMAND_SETCOLOURBALANCE 0x0400 +#define COMMAND_SETSENSORFPS 0x0800 +#define COMMAND_SETAPCOR 0x1000 +#define COMMAND_SETFLICKERCTRL 0x2000 +#define COMMAND_SETVLOFFSET 0x4000 + +/* Developer's Guide Table 5 p 3-34 + * indexed by [mains][sensorFps.baserate][sensorFps.divisor]*/ +static u8 flicker_jumps[2][2][4] = +{ { { 76, 38, 19, 9 }, { 92, 46, 23, 11 } }, + { { 64, 32, 16, 8 }, { 76, 38, 19, 9} } +}; + +/* forward declaration of local function */ +static void reset_camera_struct(struct cam_data *cam); + +/********************************************************************** + * + * 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 + * + **********************************************************************/ + +/* Given PGD from the address space's page table, return the kernel + * virtual mapping of the physical memory mapped at ADR. + */ +static inline unsigned long uvirt_to_kva(pgd_t *pgd, unsigned long adr) +{ + unsigned long ret = 0UL; + pmd_t *pmd; + pte_t *ptep, pte; + + if (!pgd_none(*pgd)) { + pmd = pmd_offset(pgd, adr); + if (!pmd_none(*pmd)) { + ptep = pte_offset(pmd, adr); + pte = *ptep; + if (pte_present(pte)) + ret = page_address(pte_page(pte)) | + (adr & (PAGE_SIZE-1)); + } + } + return ret; +} + +/* Here we want the physical address of the memory. + * This is used when initializing the contents of the + * area and marking the pages as reserved. + */ +static inline unsigned long kvirt_to_pa(unsigned long adr) +{ + unsigned long va, kva, ret; + + va = VMALLOC_VMADDR(adr); + kva = uvirt_to_kva(pgd_offset_k(va), va); + ret = __pa(kva); + return ret; +} + +static void *rvmalloc(unsigned long size) +{ + void *mem; + unsigned long adr, page; + + /* Round it off to PAGE_SIZE */ + size += (PAGE_SIZE - 1); + size &= ~(PAGE_SIZE - 1); + + mem = vmalloc(size); + if (!mem) + return NULL; + + memset(mem, 0, size); /* Clear the ram out, no junk to the user */ + adr = (unsigned long) mem; + while (size > 0) { + page = kvirt_to_pa(adr); + mem_map_reserve(MAP_NR(__va(page))); + adr += PAGE_SIZE; + if (size > PAGE_SIZE) + size -= PAGE_SIZE; + else + size = 0; + } + + return mem; +} + +static void rvfree(void *mem, unsigned long size) +{ + unsigned long adr, page; + + if (!mem) + return; + + size += (PAGE_SIZE - 1); + size &= ~(PAGE_SIZE - 1); + + adr = (unsigned long) mem; + while (size > 0) { + page = kvirt_to_pa(adr); + mem_map_unreserve(MAP_NR(__va(page))); + adr += PAGE_SIZE; + if (size > PAGE_SIZE) + size -= PAGE_SIZE; + else + size = 0; + } + vfree(mem); +} + +/********************************************************************** + * + * /proc interface + * + **********************************************************************/ +#ifdef CONFIG_PROC_FS +static struct proc_dir_entry *cpia_proc_root=NULL; + +static int cpia_read_proc(char *page, char **start, off_t off, + int count, int *eof, void *data) +{ + char *out = page; + int len, tmp; + struct cam_data *cam = data; + char tmpstr[20]; + + /* IMPORTANT: This output MUST be kept under PAGE_SIZE + * or we need to get more sophisticated. */ + + out += sprintf(out, "read-only\n-----------------------\n"); + out += sprintf(out, "V4L Driver version: %d.%d.%d\n", + CPIA_MAJ_VER, CPIA_MIN_VER, CPIA_PATCH_VER); + out += sprintf(out, "CPIA Version: %d.%02d (%d.%d)\n", + cam->params.version.firmwareVersion, + cam->params.version.firmwareRevision, + cam->params.version.vcVersion, + cam->params.version.vcRevision); + out += sprintf(out, "CPIA PnP-ID: %04x:%04x:%04x\n", + cam->params.pnpID.vendor, cam->params.pnpID.product, + cam->params.pnpID.deviceRevision); + out += sprintf(out, "VP-Version: %d.%d %04x\n", + cam->params.vpVersion.vpVersion, + cam->params.vpVersion.vpRevision, + cam->params.vpVersion.cameraHeadID); + + out += sprintf(out, "system_state: %#04x\n", + cam->params.status.systemState); + out += sprintf(out, "grab_state: %#04x\n", + cam->params.status.grabState); + out += sprintf(out, "stream_state: %#04x\n", + cam->params.status.streamState); + out += sprintf(out, "fatal_error: %#04x\n", + cam->params.status.fatalError); + out += sprintf(out, "cmd_error: %#04x\n", + cam->params.status.cmdError); + out += sprintf(out, "debug_flags: %#04x\n", + cam->params.status.debugFlags); + out += sprintf(out, "vp_status: %#04x\n", + cam->params.status.vpStatus); + out += sprintf(out, "error_code: %#04x\n", + cam->params.status.errorCode); + out += sprintf(out, "video_size: %s\n", + cam->params.format.videoSize == VIDEOSIZE_CIF ? + "CIF " : "QCIF"); + out += sprintf(out, "sub_sample: %s\n", + cam->params.format.subSample == SUBSAMPLE_420 ? + "420" : "422"); + out += sprintf(out, "yuv_order: %s\n", + cam->params.format.yuvOrder == YUVORDER_YUYV ? + "YUYV" : "UYVY"); + out += sprintf(out, "roi: (%3d, %3d) to (%3d, %3d)\n", + cam->params.roi.colStart*8, + cam->params.roi.rowStart*4, + cam->params.roi.colEnd*8, + cam->params.roi.rowEnd*4); + out += sprintf(out, "actual_fps: %3d\n", cam->fps); + out += sprintf(out, "transfer_rate: %4dkB/s\n", + cam->transfer_rate); + + out += sprintf(out, "\nread-write\n"); + out += sprintf(out, "----------------------- current min" + " max default comment\n"); + out += sprintf(out, "brightness: %8d %8d %8d %8d\n", + cam->params.colourParams.brightness, 0, 100, 50); + if (cam->params.version.firmwareVersion == 1 && + cam->params.version.firmwareRevision == 2) + /* 1-02 firmware limits contrast to 80 */ + tmp = 80; + else + tmp = 96; + + out += sprintf(out, "contrast: %8d %8d %8d %8d" + " steps of 8\n", + cam->params.colourParams.contrast, 0, tmp, 48); + out += sprintf(out, "saturation: %8d %8d %8d %8d\n", + cam->params.colourParams.saturation, 0, 100, 50); + tmp = (25000+5000*cam->params.sensorFps.baserate)/ + (1<<cam->params.sensorFps.divisor); + out += sprintf(out, "sensor_fps: %4d.%03d %8d %8d %8d\n", + tmp/1000, tmp%1000, 3, 30, 15); + out += sprintf(out, "stream_start_line: %8d %8d %8d %8d\n", + 2*cam->params.streamStartLine, 0, + cam->params.format.videoSize == VIDEOSIZE_CIF ? 288:144, + cam->params.format.videoSize == VIDEOSIZE_CIF ? 240:120); + out += sprintf(out, "ecp_timing: %8s %8s %8s %8s\n", + cam->params.ecpTiming ? "slow" : "normal", "slow", + "normal", "normal"); + + if (cam->params.colourBalance.balanceModeIsAuto) { + sprintf(tmpstr, "auto"); + } else { + sprintf(tmpstr, "manual"); + } + out += sprintf(out, "color_balance_mode: %8s %8s %8s" + " %8s\n", tmpstr, "manual", "auto", "auto"); + out += sprintf(out, "red_gain: %8d %8d %8d %8d\n", + cam->params.colourBalance.redGain, 0, 212, 32); + out += sprintf(out, "green_gain: %8d %8d %8d %8d\n", + cam->params.colourBalance.greenGain, 0, 212, 6); + out += sprintf(out, "blue_gain: %8d %8d %8d %8d\n", + cam->params.colourBalance.blueGain, 0, 212, 92); + + if (cam->params.version.firmwareVersion == 1 && + cam->params.version.firmwareRevision == 2) + /* 1-02 firmware limits gain to 2 */ + sprintf(tmpstr, "%8d %8d", 1, 2); + else + sprintf(tmpstr, "1,2,4,8"); + + if (cam->params.exposure.gainMode == 0) + out += sprintf(out, "max_gain: unknown %18s" + " %8d\n", tmpstr, 2); + else + out += sprintf(out, "max_gain: %8d %18s %8d\n", + 1<<(cam->params.exposure.gainMode-1), tmpstr, 2); + + switch(cam->params.exposure.expMode) { + case 1: + case 3: + sprintf(tmpstr, "manual"); + break; + case 2: + sprintf(tmpstr, "auto"); + break; + default: + sprintf(tmpstr, "unknown"); + break; + } + out += sprintf(out, "exposure_mode: %8s %8s %8s" + " %8s\n", tmpstr, "manual", "auto", "auto"); + out += sprintf(out, "centre_weight: %8s %8s %8s %8s\n", + (2-cam->params.exposure.centreWeight) ? "on" : "off", + "off", "on", "on"); + out += sprintf(out, "gain: %8d %8d max_gain %8d 1,2,4,8 +possible\n", + 1<<cam->params.exposure.gain, 1, 1); + if (cam->params.version.firmwareVersion == 1 && + cam->params.version.firmwareRevision == 2) + /* 1-02 firmware limits fineExp to 127 */ + tmp = 255; + else + tmp = 511; + + out += sprintf(out, "fine_exp: %8d %8d %8d %8d\n", + cam->params.exposure.fineExp*2, 0, tmp, 0); + if (cam->params.version.firmwareVersion == 1 && + cam->params.version.firmwareRevision == 2) + /* 1-02 firmware limits coarseExpHi to 0 */ + tmp = 255; + else + tmp = 65535; + + out += sprintf(out, "coarse_exp: %8d %8d %8d" + " %8d\n", cam->params.exposure.coarseExpLo+ + 256*cam->params.exposure.coarseExpHi, 0, tmp, 185); + out += sprintf(out, "red_comp: %8d %8d %8d %8d\n", + cam->params.exposure.redComp, 220, 255, 220); + out += sprintf(out, "green1_comp: %8d %8d %8d %8d\n", + cam->params.exposure.green1Comp, 214, 255, 214); + out += sprintf(out, "green2_comp: %8d %8d %8d %8d\n", + cam->params.exposure.green2Comp, 214, 255, 214); + out += sprintf(out, "blue_comp: %8d %8d %8d %8d\n", + cam->params.exposure.blueComp, 230, 255, 230); + + out += sprintf(out, "apcor_gain1: %#8x %#8x %#8x %#8x\n", + cam->params.apcor.gain1, 0, 0xff, 0x1c); + out += sprintf(out, "apcor_gain2: %#8x %#8x %#8x %#8x\n", + cam->params.apcor.gain2, 0, 0xff, 0x1a); + out += sprintf(out, "apcor_gain4: %#8x %#8x %#8x %#8x\n", + cam->params.apcor.gain4, 0, 0xff, 0x2d); + out += sprintf(out, "apcor_gain8: %#8x %#8x %#8x %#8x\n", + cam->params.apcor.gain8, 0, 0xff, 0x2a); + out += sprintf(out, "vl_offset_gain1: %8d %8d %8d %8d\n", + cam->params.vlOffset.gain1, 0, 255, 24); + out += sprintf(out, "vl_offset_gain2: %8d %8d %8d %8d\n", + cam->params.vlOffset.gain2, 0, 255, 28); + out += sprintf(out, "vl_offset_gain4: %8d %8d %8d %8d\n", + cam->params.vlOffset.gain4, 0, 255, 30); + out += sprintf(out, "vl_offset_gain8: %8d %8d %8d %8d\n", + cam->params.vlOffset.gain8, 0, 255, 30); + out += sprintf(out, "flicker_control: %8s %8s %8s %8s\n", + cam->params.flickerControl.flickerMode ? "on" : "off", + "off", "on", "off"); + out += sprintf(out, "mains_frequency: %8d %8d %8d %8d" + " only 50/60\n", + cam->mainsFreq ? 60 : 50, 50, 60, 50); + out += sprintf(out, "allowable_overexposure: %8d %8d %8d %8d\n", + cam->params.flickerControl.allowableOverExposure, 0, + 255, 0); + out += sprintf(out, "compression_mode: "); + switch(cam->params.compression.mode) { + case CPIA_COMPRESSION_NONE: + out += sprintf(out, "%8s", "none"); + break; + case CPIA_COMPRESSION_AUTO: + out += sprintf(out, "%8s", "auto"); + break; + case CPIA_COMPRESSION_MANUAL: + out += sprintf(out, "%8s", "manual"); + break; + default: + out += sprintf(out, "%8s", "unknown"); + break; + } + out += sprintf(out, " none,auto,manual auto\n"); + out += sprintf(out, "decimation_enable: %8s %8s %8s %8s\n", + cam->params.compression.decimation == + DECIMATION_ENAB ? "on":"off", "off", "off", + "off"); + out += sprintf(out, "compression_target: %9s %9s %9s %9s\n", + cam->params.compressionTarget.frTargeting == + CPIA_COMPRESSION_TARGET_FRAMERATE ? + "framerate":"quality", + "framerate", "quality", "quality"); + out += sprintf(out, "target_framerate: %8d %8d %8d %8d\n", + cam->params.compressionTarget.targetFR, 0, 30, 7); + out += sprintf(out, "target_quality: %8d %8d %8d %8d\n", + cam->params.compressionTarget.targetQ, 0, 255, 10); + out += sprintf(out, "y_threshold: %8d %8d %8d %8d\n", + cam->params.yuvThreshold.yThreshold, 0, 31, 15); + out += sprintf(out, "uv_threshold: %8d %8d %8d %8d\n", + cam->params.yuvThreshold.uvThreshold, 0, 31, 15); + out += sprintf(out, "hysteresis: %8d %8d %8d %8d\n", + cam->params.compressionParams.hysteresis, 0, 255, 3); + out += sprintf(out, "threshold_max: %8d %8d %8d %8d\n", + cam->params.compressionParams.threshMax, 0, 255, 11); + out += sprintf(out, "small_step: %8d %8d %8d %8d\n", + cam->params.compressionParams.smallStep, 0, 255, 1); + out += sprintf(out, "large_step: %8d %8d %8d %8d\n", + cam->params.compressionParams.largeStep, 0, 255, 3); + out += sprintf(out, "decimation_hysteresis: %8d %8d %8d %8d\n", + cam->params.compressionParams.decimationHysteresis, + 0, 255, 2); + out += sprintf(out, "fr_diff_step_thresh: %8d %8d %8d %8d\n", + cam->params.compressionParams.frDiffStepThresh, + 0, 255, 5); + out += sprintf(out, "q_diff_step_thresh: %8d %8d %8d %8d\n", + cam->params.compressionParams.qDiffStepThresh, + 0, 255, 3); + out += sprintf(out, "decimation_thresh_mod: %8d %8d %8d %8d\n", + cam->params.compressionParams.decimationThreshMod, + 0, 255, 2); + + 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 cpia_write_proc(struct file *file, const char *buffer, + unsigned long count, void *data) +{ + struct cam_data *cam = data; + struct cam_params new_params; + int retval, find_colon; + int size = count; + 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; + } + + memcpy(&new_params, &cam->params, sizeof(struct cam_params)); + new_mains = cam->mainsFreq; + +#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 FIRMWARE_VERSION(x,y) (new_params.version.firmwareVersion == (x) && \ + new_params.version.firmwareRevision == (y)) +#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("brightness")) { + if (!retval) + val = VALUE; + + if (!retval) { + if (val <= 100) + new_params.colourParams.brightness = val; + else + retval = -EINVAL; + } + command_flags |= COMMAND_SETCOLOURPARAMS; + } else if (MATCH("contrast")) { + if (!retval) + val = VALUE; + + if (!retval) { + if (val <= 100) { + /* contrast is in steps of 8, so round*/ + val = ((val + 3) / 8) * 8; + /* 1-02 firmware limits contrast to 80*/ + if (FIRMWARE_VERSION(1,2) && val > 80) + val = 80; + + new_params.colourParams.contrast = val; + } else + retval = -EINVAL; + } + command_flags |= COMMAND_SETCOLOURPARAMS; + } else if (MATCH("saturation")) { + if (!retval) + val = VALUE; + + if (!retval) { + if (val <= 100) + new_params.colourParams.saturation = val; + else + retval = -EINVAL; + } + command_flags |= COMMAND_SETCOLOURPARAMS; + } else if (MATCH("sensor_fps")) { + if (!retval) + val = VALUE; + + if (!retval) { + /* find values so that sensorFPS is minimized, + * but >= val */ + if (val > 30) + retval = -EINVAL; + else if (val > 25) { + new_params.sensorFps.divisor = 0; + new_params.sensorFps.baserate = 1; + } else if (val > 15) { + new_params.sensorFps.divisor = 0; + new_params.sensorFps.baserate = 0; + } else if (val > 12) { + new_params.sensorFps.divisor = 1; + new_params.sensorFps.baserate = 1; + } else if (val > 7) { + new_params.sensorFps.divisor = 1; + new_params.sensorFps.baserate = 0; + } else if (val > 6) { + new_params.sensorFps.divisor = 2; + new_params.sensorFps.baserate = 1; + } else if (val > 3) { + new_params.sensorFps.divisor = 2; + new_params.sensorFps.baserate = 0; + } else { + new_params.sensorFps.divisor = 3; + /* Either base rate would work here */ + new_params.sensorFps.baserate = 1; + } + new_params.flickerControl.coarseJump = + flicker_jumps[new_mains] + [new_params.sensorFps.baserate] + [new_params.sensorFps.divisor]; + if (new_params.flickerControl.flickerMode) + command_flags |= COMMAND_SETFLICKERCTRL; + } + command_flags |= COMMAND_SETSENSORFPS; + } else if (MATCH("stream_start_line")) { + if (!retval) + val = VALUE; + + if (!retval) { + int max_line = 288; + + if (new_params.format.videoSize == VIDEOSIZE_QCIF) + max_line = 144; + if (val <= max_line) + new_params.streamStartLine = val/2; + else + retval = -EINVAL; + } + } else if (MATCH("ecp_timing")) { + if (!retval && MATCH("normal")) + new_params.ecpTiming = 0; + else if (!retval && MATCH("slow")) + new_params.ecpTiming = 1; + else + retval = -EINVAL; + + command_flags |= COMMAND_SETECPTIMING; + } else if (MATCH("color_balance_mode")) { + if (!retval && MATCH("manual")) + new_params.colourBalance.balanceModeIsAuto = 0; + else if (!retval && MATCH("auto")) + new_params.colourBalance.balanceModeIsAuto = 1; + else + retval = -EINVAL; + + command_flags |= COMMAND_SETCOLOURBALANCE; + } else if (MATCH("red_gain")) { + if (!retval) + val = VALUE; + + if (!retval) { + if (val <= 212) + new_params.colourBalance.redGain = val; + else + retval = -EINVAL; + } + command_flags |= COMMAND_SETCOLOURBALANCE; + } else if (MATCH("green_gain")) { + if (!retval) + val = VALUE; + + if (!retval) { + if (val <= 212) + new_params.colourBalance.greenGain = val; + else + retval = -EINVAL; + } + command_flags |= COMMAND_SETCOLOURBALANCE; + } else if (MATCH("blue_gain")) { + if (!retval) + val = VALUE; + + if (!retval) { + if (val <= 212) + new_params.colourBalance.blueGain = val; + else + retval = -EINVAL; + } + command_flags |= COMMAND_SETCOLOURBALANCE; + } else if (MATCH("max_gain")) { + if (!retval) + val = VALUE; + + if (!retval) { + /* 1-02 firmware limits gain to 2 */ + if (FIRMWARE_VERSION(1,2) && val > 2) + val = 2; + switch(val) { + case 1: + new_params.exposure.gainMode = 1; + break; + case 2: + new_params.exposure.gainMode = 2; + break; + case 4: + new_params.exposure.gainMode = 3; + break; + case 8: + new_params.exposure.gainMode = 4; + break; + default: + retval = -EINVAL; + break; + } + } + command_flags |= COMMAND_SETEXPOSURE; + } else if (MATCH("exposure_mode")) { + if (!retval && MATCH("auto")) + new_params.exposure.expMode = 2; + else if (!retval && MATCH("manual")) { + if (new_params.exposure.expMode == 2) + new_params.exposure.expMode = 3; + new_params.flickerControl.flickerMode = 0; + command_flags |= COMMAND_SETFLICKERCTRL; + } else + retval = -EINVAL; + + command_flags |= COMMAND_SETEXPOSURE; + } else if (MATCH("centre_weight")) { + if (!retval && MATCH("on")) + new_params.exposure.centreWeight = 1; + else if (!retval && MATCH("off")) + new_params.exposure.centreWeight = 2; + else + retval = -EINVAL; + + command_flags |= COMMAND_SETEXPOSURE; + } else if (MATCH("gain")) { + if (!retval) + val = VALUE; + + if (!retval) { + switch(val) { + case 1: + new_params.exposure.gain = 0; + new_params.exposure.expMode = 1; + new_params.flickerControl.flickerMode = 0; + command_flags |= COMMAND_SETFLICKERCTRL; + break; + case 2: + new_params.exposure.gain = 1; + new_params.exposure.expMode = 1; + new_params.flickerControl.flickerMode = 0; + command_flags |= COMMAND_SETFLICKERCTRL; + break; + case 4: + new_params.exposure.gain = 2; + new_params.exposure.expMode = 1; + new_params.flickerControl.flickerMode = 0; + command_flags |= COMMAND_SETFLICKERCTRL; + break; + case 8: + new_params.exposure.gain = 3; + new_params.exposure.expMode = 1; + new_params.flickerControl.flickerMode = 0; + command_flags |= COMMAND_SETFLICKERCTRL; + break; + default: + retval = -EINVAL; + break; + } + command_flags |= COMMAND_SETEXPOSURE; + if (new_params.exposure.gain > + new_params.exposure.gainMode-1) + retval = -EINVAL; + } + } else if (MATCH("fine_exp")) { + if (!retval) + val = VALUE; + + if (!retval) { + if (val < 256) { + /* 1-02 firmware limits fineExp to 127*/ + if (FIRMWARE_VERSION(1,2) && val > 127) + val = 127; + new_params.exposure.fineExp = val; + new_params.exposure.expMode = 1; + command_flags |= COMMAND_SETEXPOSURE; + new_params.flickerControl.flickerMode = 0; + command_flags |= COMMAND_SETFLICKERCTRL; + } else + retval = -EINVAL; + } + } else if (MATCH("coarse_exp")) { + if (!retval) + val = VALUE; + + if (!retval) { + if (val < 65536) { + /* 1-02 firmware limits + * coarseExp to 255 */ + if (FIRMWARE_VERSION(1,2) && val > 255) + val = 255; + new_params.exposure.coarseExpLo = + val & 0xff; + new_params.exposure.coarseExpHi = + val >> 8; + new_params.exposure.expMode = 1; + command_flags |= COMMAND_SETEXPOSURE; + new_params.flickerControl.flickerMode = 0; + command_flags |= COMMAND_SETFLICKERCTRL; + } else + retval = -EINVAL; + } + } else if (MATCH("red_comp")) { + if (!retval) + val = VALUE; + + if (!retval) { + if (val >= 220 && val <= 255) { + new_params.exposure.redComp = val; + command_flags |= COMMAND_SETEXPOSURE; + } else + retval = -EINVAL; + } + } else if (MATCH("green1_comp")) { + if (!retval) + val = VALUE; + + if (!retval) { + if (val >= 214 && val <= 255) { + new_params.exposure.green1Comp = val; + command_flags |= COMMAND_SETEXPOSURE; + } else + retval = -EINVAL; + } + } else if (MATCH("green2_comp")) { + if (!retval) + val = VALUE; + + if (!retval) { + if (val >= 214 && val <= 255) { + new_params.exposure.green2Comp = val; + command_flags |= COMMAND_SETEXPOSURE; + } else + retval = -EINVAL; + } + } else if (MATCH("blue_comp")) { + if (!retval) + val = VALUE; + + if (!retval) { + if (val >= 230 && val <= 255) { + new_params.exposure.blueComp = val; + command_flags |= COMMAND_SETEXPOSURE; + } else + retval = -EINVAL; + } + } else if (MATCH("apcor_gain1")) { + if (!retval) + val = VALUE; + + if (!retval) { + command_flags |= COMMAND_SETAPCOR; + if (val <= 0xff) + new_params.apcor.gain1 = val; + else + retval = -EINVAL; + } + } else if (MATCH("apcor_gain2")) { + if (!retval) + val = VALUE; + + if (!retval) { + command_flags |= COMMAND_SETAPCOR; + if (val <= 0xff) + new_params.apcor.gain2 = val; + else + retval = -EINVAL; + } + } else if (MATCH("apcor_gain4")) { + if (!retval) + val = VALUE; + + if (!retval) { + command_flags |= COMMAND_SETAPCOR; + if (val <= 0xff) + new_params.apcor.gain4 = val; + else + retval = -EINVAL; + } + } else if (MATCH("apcor_gain8")) { + if (!retval) + val = VALUE; + + if (!retval) { + command_flags |= COMMAND_SETAPCOR; + if (val <= 0xff) + new_params.apcor.gain8 = val; + else + retval = -EINVAL; + } + } else if (MATCH("vl_offset_gain1")) { + if (!retval) + val = VALUE; + + if (!retval) { + if (val <= 0xff) + new_params.vlOffset.gain1 = val; + else + retval = -EINVAL; + } + command_flags |= COMMAND_SETVLOFFSET; + } else if (MATCH("vl_offset_gain2")) { + if (!retval) + val = VALUE; + + if (!retval) { + if (val <= 0xff) + new_params.vlOffset.gain2 = val; + else + retval = -EINVAL; + } + command_flags |= COMMAND_SETVLOFFSET; + } else if (MATCH("vl_offset_gain4")) { + if (!retval) + val = VALUE; + + if (!retval) { + if (val <= 0xff) + new_params.vlOffset.gain4 = val; + else + retval = -EINVAL; + } + command_flags |= COMMAND_SETVLOFFSET; + } else if (MATCH("vl_offset_gain8")) { + if (!retval) + val = VALUE; + + if (!retval) { + if (val <= 0xff) + new_params.vlOffset.gain8 = val; + else + retval = -EINVAL; + } + command_flags |= COMMAND_SETVLOFFSET; + } else if (MATCH("flicker_control")) { + if (!retval && MATCH("on")) { + new_params.flickerControl.flickerMode = 1; + new_params.exposure.expMode = 2; + command_flags |= COMMAND_SETEXPOSURE; + } else if (!retval && MATCH("off")) + new_params.flickerControl.flickerMode = 0; + else + retval = -EINVAL; + + command_flags |= COMMAND_SETFLICKERCTRL; + } else if (MATCH("mains_frequency")) { + if (!retval && MATCH("50")) { + new_mains = 0; + new_params.flickerControl.coarseJump = + flicker_jumps[new_mains] + [new_params.sensorFps.baserate] + [new_params.sensorFps.divisor]; + if (new_params.flickerControl.flickerMode) + command_flags |= COMMAND_SETFLICKERCTRL; + } else if (!retval && MATCH("60")) { + new_mains = 1; + new_params.flickerControl.coarseJump = + flicker_jumps[new_mains] + [new_params.sensorFps.baserate] + [new_params.sensorFps.divisor]; + if (new_params.flickerControl.flickerMode) + command_flags |= COMMAND_SETFLICKERCTRL; + } else + retval = -EINVAL; + } else if (MATCH("allowable_overexposure")) { + if (!retval) + val = VALUE; + + if (!retval) { + if (val <= 0xff) { + new_params.flickerControl. + allowableOverExposure = val; + command_flags |= COMMAND_SETFLICKERCTRL; + } else + retval = -EINVAL; + } + } else if (MATCH("compression_mode")) { + if (!retval && MATCH("none")) + new_params.compression.mode = + CPIA_COMPRESSION_NONE; + else if (!retval && MATCH("auto")) + new_params.compression.mode = + CPIA_COMPRESSION_AUTO; + else if (!retval && MATCH("manual")) + new_params.compression.mode = + CPIA_COMPRESSION_MANUAL; + else + retval = -EINVAL; + + command_flags |= COMMAND_SETCOMPRESSION; + } else if (MATCH("decimation_enable")) { + if (!retval && MATCH("off")) + new_params.compression.decimation = 0; + else + retval = -EINVAL; + + command_flags |= COMMAND_SETCOMPRESSION; + } else if (MATCH("compression_target")) { + if (!retval && MATCH("quality")) + new_params.compressionTarget.frTargeting = + CPIA_COMPRESSION_TARGET_QUALITY; + else if (!retval && MATCH("framerate")) + new_params.compressionTarget.frTargeting = + CPIA_COMPRESSION_TARGET_FRAMERATE; + else + retval = -EINVAL; + + command_flags |= COMMAND_SETCOMPRESSIONTARGET; + } else if (MATCH("target_framerate")) { + if (!retval) + val = VALUE; + + if (!retval) + new_params.compressionTarget.targetFR = val; + command_flags |= COMMAND_SETCOMPRESSIONTARGET; + } else if (MATCH("target_quality")) { + if (!retval) + val = VALUE; + + if (!retval) + new_params.compressionTarget.targetQ = val; + + command_flags |= COMMAND_SETCOMPRESSIONTARGET; + } else if (MATCH("y_threshold")) { + if (!retval) + val = VALUE; + + if (!retval) { + if (val < 32) + new_params.yuvThreshold.yThreshold = val; + else + retval = -EINVAL; + } + command_flags |= COMMAND_SETYUVTHRESH; + } else if (MATCH("uv_threshold")) { + if (!retval) + val = VALUE; + + if (!retval) { + if (val < 32) + new_params.yuvThreshold.uvThreshold = val; + else + retval = -EINVAL; + } + command_flags |= COMMAND_SETYUVTHRESH; + } else if (MATCH("hysteresis")) { + if (!retval) + val = VALUE; + + if (!retval) { + if (val <= 0xff) + new_params.compressionParams.hysteresis = val; + else + retval = -EINVAL; + } + command_flags |= COMMAND_SETCOMPRESSIONPARAMS; + } else if (MATCH("threshold_max")) { + if (!retval) + val = VALUE; + + if (!retval) { + if (val <= 0xff) + new_params.compressionParams.threshMax = val; + else + retval = -EINVAL; + } + command_flags |= COMMAND_SETCOMPRESSIONPARAMS; + } else if (MATCH("small_step")) { + if (!retval) + val = VALUE; + + if (!retval) { + if (val <= 0xff) + new_params.compressionParams.smallStep = val; + else + retval = -EINVAL; + } + command_flags |= COMMAND_SETCOMPRESSIONPARAMS; + } else if (MATCH("large_step")) { + if (!retval) + val = VALUE; + + if (!retval) { + if (val <= 0xff) + new_params.compressionParams.largeStep = val; + else + retval = -EINVAL; + } + command_flags |= COMMAND_SETCOMPRESSIONPARAMS; + } else if (MATCH("decimation_hysteresis")) { + if (!retval) + val = VALUE; + + if (!retval) { + if (val <= 0xff) + +new_params.compressionParams.decimationHysteresis = val; + else + retval = -EINVAL; + } + command_flags |= COMMAND_SETCOMPRESSIONPARAMS; + } else if (MATCH("fr_diff_step_thresh")) { + if (!retval) + val = VALUE; + + if (!retval) { + if (val <= 0xff) + new_params.compressionParams.frDiffStepThresh += val; + else + retval = -EINVAL; + } + command_flags |= COMMAND_SETCOMPRESSIONPARAMS; + } else if (MATCH("q_diff_step_thresh")) { + if (!retval) + val = VALUE; + + if (!retval) { + if (val <= 0xff) + new_params.compressionParams.qDiffStepThresh = +val; + else + retval = -EINVAL; + } + command_flags |= COMMAND_SETCOMPRESSIONPARAMS; + } else if (MATCH("decimation_thresh_mod")) { + if (!retval) + val = VALUE; + + if (!retval) { + if (val <= 0xff) + +new_params.compressionParams.decimationThreshMod = val; + else + retval = -EINVAL; + } + command_flags |= COMMAND_SETCOMPRESSIONPARAMS; + } 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 + DBG("error: %d\n", retval); + + up(&cam->param_lock); + + return retval; +} + +static void create_proc_cpia_cam(struct cam_data *cam) +{ + char name[7]; + struct proc_dir_entry *ent; + + if (!cpia_proc_root || !cam) + return; + + sprintf(name, "video%d", cam->vdev.minor); + + ent = create_proc_entry(name, S_IFREG|S_IRUGO|S_IWUSR, cpia_proc_root); + if (!ent) + return; + + ent->data = cam; + ent->read_proc = cpia_read_proc; + ent->write_proc = cpia_write_proc; + ent->size = 3626; + cam->proc_entry = ent; +} + +static void destroy_proc_cpia_cam(struct cam_data *cam) +{ + char name[7]; + + if (!cam || !cam->proc_entry) + return; + + sprintf(name, "video%d", cam->vdev.minor); + remove_proc_entry(name, cpia_proc_root); + cam->proc_entry = NULL; +} + +static void proc_cpia_create(void) +{ + cpia_proc_root = create_proc_entry("cpia", S_IFDIR, 0); + + if (cpia_proc_root) + cpia_proc_root->owner = THIS_MODULE; + else + LOG("Unable to initialise /proc/cpia\n"); +} + +static void proc_cpia_destroy(void) +{ + remove_proc_entry("cpia", 0); +} +#endif /* CONFIG_PROC_FS */ + +/* ----------------------- debug functions ---------------------- */ + +#define printstatus(cam) \ + DBG("%02x %02x %02x %02x %02x %02x %02x %02x\n",\ + cam->params.status.systemState, cam->params.status.grabState, \ + cam->params.status.streamState, cam->params.status.fatalError, \ + cam->params.status.cmdError, cam->params.status.debugFlags, \ + cam->params.status.vpStatus, cam->params.status.errorCode); + +/* ----------------------- v4l helpers -------------------------- */ + +/* supported frame palettes and depths */ +static inline int valid_mode(u16 palette, u16 depth) +{ + return (palette == VIDEO_PALETTE_GREY && depth == 8) || + (palette == VIDEO_PALETTE_RGB555 && depth == 16) || + (palette == VIDEO_PALETTE_RGB565 && depth == 16) || + (palette == VIDEO_PALETTE_RGB24 && depth == 24) || + (palette == VIDEO_PALETTE_RGB32 && depth == 32) || + (palette == VIDEO_PALETTE_YUV422 && depth == 16) || + (palette == VIDEO_PALETTE_YUYV && depth == 16) || + (palette == VIDEO_PALETTE_UYVY && depth == 16); +} + +static int match_videosize( int width, int height ) +{ + /* return the best match, where 'best' is as always + * the largest that is not bigger than what is requested. */ + if (width>=352 && height>=288) + return VIDEOSIZE_352_288; /* CIF */ + + if (width>=320 && height>=240) + return VIDEOSIZE_320_240; /* SIF */ + + if (width>=288 && height>=216) + return VIDEOSIZE_288_216; + + if (width>=256 && height>=192) + return VIDEOSIZE_256_192; + + if (width>=224 && height>=168) + return VIDEOSIZE_224_168; + + if (width>=192 && height>=144) + return VIDEOSIZE_192_144; + + if (width>=176 && height>=144) + return VIDEOSIZE_176_144; /* QCIF */ + + if (width>=160 && height>=120) + return VIDEOSIZE_160_120; /* QSIF */ + + if (width>=128 && height>=96) + return VIDEOSIZE_128_96; + + if (width>=88 && height>=72) + return VIDEOSIZE_88_72; + + if (width>=64 && height>=48) + return VIDEOSIZE_64_48; + + if (width>=48 && height>=48) + return VIDEOSIZE_48_48; + + return -1; +} + +/* these are the capture sizes we support */ +static void set_vw_size(struct cam_data *cam) +{ + /* the col/row/start/end values are the result of simple math */ + /* study the SetROI-command in cpia developers guide p 2-22 */ + /* streamStartLine is set to the recommended value in the cpia */ + /* developers guide p 3-37 */ + switch(cam->video_size) { + case VIDEOSIZE_CIF: + cam->vw.width = 352; + cam->vw.height = 288; + cam->params.format.videoSize=VIDEOSIZE_CIF; + cam->params.roi.colStart=0; + cam->params.roi.colEnd=44; + cam->params.roi.rowStart=0; + cam->params.roi.rowEnd=72; + cam->params.streamStartLine = 120; + break; + case VIDEOSIZE_SIF: + cam->vw.width = 320; + cam->vw.height = 240; + cam->params.format.videoSize=VIDEOSIZE_CIF; + cam->params.roi.colStart=2; + cam->params.roi.colEnd=42; + cam->params.roi.rowStart=6; + cam->params.roi.rowEnd=66; + cam->params.streamStartLine = 120; + break; + case VIDEOSIZE_288_216: + cam->vw.width = 288; + cam->vw.height = 216; + cam->params.format.videoSize=VIDEOSIZE_CIF; + cam->params.roi.colStart=4; + cam->params.roi.colEnd=40; + cam->params.roi.rowStart=9; + cam->params.roi.rowEnd=63; + cam->params.streamStartLine = 120; + break; + case VIDEOSIZE_256_192: + cam->vw.width = 256; + cam->vw.height = 192; + cam->params.format.videoSize=VIDEOSIZE_CIF; + cam->params.roi.colStart=6; + cam->params.roi.colEnd=38; + cam->params.roi.rowStart=12; + cam->params.roi.rowEnd=60; + cam->params.streamStartLine = 120; + break; + case VIDEOSIZE_224_168: + cam->vw.width = 224; + cam->vw.height = 168; + cam->params.format.videoSize=VIDEOSIZE_CIF; + cam->params.roi.colStart=8; + cam->params.roi.colEnd=36; + cam->params.roi.rowStart=15; + cam->params.roi.rowEnd=57; + cam->params.streamStartLine = 120; + break; + case VIDEOSIZE_192_144: + cam->vw.width = 192; + cam->vw.height = 144; + cam->params.format.videoSize=VIDEOSIZE_CIF; + cam->params.roi.colStart=10; + cam->params.roi.colEnd=34; + cam->params.roi.rowStart=18; + cam->params.roi.rowEnd=54; + cam->params.streamStartLine = 120; + break; + case VIDEOSIZE_QCIF: + cam->vw.width = 176; + cam->vw.height = 144; + cam->params.format.videoSize=VIDEOSIZE_QCIF; + cam->params.roi.colStart=0; + cam->params.roi.colEnd=22; + cam->params.roi.rowStart=0; + cam->params.roi.rowEnd=36; + cam->params.streamStartLine = 60; + break; + case VIDEOSIZE_QSIF: + cam->vw.width = 160; + cam->vw.height = 120; + cam->params.format.videoSize=VIDEOSIZE_QCIF; + cam->params.roi.colStart=1; + cam->params.roi.colEnd=21; + cam->params.roi.rowStart=3; + cam->params.roi.rowEnd=33; + cam->params.streamStartLine = 60; + break; + case VIDEOSIZE_128_96: + cam->vw.width = 128; + cam->vw.height = 96; + cam->params.format.videoSize=VIDEOSIZE_QCIF; + cam->params.roi.colStart=3; + cam->params.roi.colEnd=19; + cam->params.roi.rowStart=6; + cam->params.roi.rowEnd=30; + cam->params.streamStartLine = 60; + break; + case VIDEOSIZE_88_72: + cam->vw.width = 88; + cam->vw.height = 72; + cam->params.format.videoSize=VIDEOSIZE_QCIF; + cam->params.roi.colStart=5; + cam->params.roi.colEnd=16; + cam->params.roi.rowStart=9; + cam->params.roi.rowEnd=27; + cam->params.streamStartLine = 60; + break; + case VIDEOSIZE_64_48: + cam->vw.width = 64; + cam->vw.height = 48; + cam->params.format.videoSize=VIDEOSIZE_QCIF; + cam->params.roi.colStart=7; + cam->params.roi.colEnd=15; + cam->params.roi.rowStart=12; + cam->params.roi.rowEnd=24; + cam->params.streamStartLine = 60; + break; + case VIDEOSIZE_48_48: + cam->vw.width = 48; + cam->vw.height = 48; + cam->params.format.videoSize=VIDEOSIZE_QCIF; + cam->params.roi.colStart=8; + cam->params.roi.colEnd=14; + cam->params.roi.rowStart=6; + cam->params.roi.rowEnd=30; + cam->params.streamStartLine = 60; + break; + default: + LOG("bad videosize value: %d\n", cam->video_size); + } + + return; +} + +static int allocate_frame_buf(struct cam_data *cam) +{ + int i; + + cam->frame_buf = rvmalloc(FRAME_NUM * CPIA_MAX_FRAME_SIZE); + if (!cam->frame_buf) + return -ENOBUFS; + + for (i = 0; i < FRAME_NUM; i++) + cam->frame[i].data = cam->frame_buf + i * CPIA_MAX_FRAME_SIZE; + + return 0; +} + +static int free_frame_buf(struct cam_data *cam) +{ + int i; + + rvfree(cam->frame_buf, FRAME_NUM*CPIA_MAX_FRAME_SIZE); + cam->frame_buf = 0; + for (i=0; i < FRAME_NUM; i++) + cam->frame[i].data = NULL; + + return 0; +} + + +static void inline free_frames(struct cpia_frame frame[FRAME_NUM]) +{ + int i; + + for (i=0; i < FRAME_NUM; i++) + frame[i].state = FRAME_UNUSED; + return; +} + +/********************************************************************** + * + * General functions + * + **********************************************************************/ +/* send an arbitrary command to the camera */ +static int do_command(struct cam_data *cam, u16 command, u8 a, u8 b, u8 c, u8 d) +{ + int retval, datasize; + u8 cmd[8], data[8]; + + switch(command) { + case CPIA_COMMAND_GetCPIAVersion: + case CPIA_COMMAND_GetPnPID: + case CPIA_COMMAND_GetCameraStatus: + case CPIA_COMMAND_GetVPVersion: + datasize=8; + break; + case CPIA_COMMAND_GetColourParams: + case CPIA_COMMAND_GetColourBalance: + case CPIA_COMMAND_GetExposure: + down(&cam->param_lock); + datasize=8; + break; + default: + datasize=0; + break; + } + + cmd[0] = command>>8; + cmd[1] = command&0xff; + cmd[2] = a; + cmd[3] = b; + cmd[4] = c; + cmd[5] = d; + cmd[6] = datasize; + cmd[7] = 0; + + retval = cam->ops->transferCmd(cam->lowlevel_data, cmd, data); + if (retval) + DBG("%x - failed, retval=%d\n", command, retval); + else { + switch(command) { + case CPIA_COMMAND_GetCPIAVersion: + cam->params.version.firmwareVersion = data[0]; + cam->params.version.firmwareRevision = data[1]; + cam->params.version.vcVersion = data[2]; + cam->params.version.vcRevision = data[3]; + break; + case CPIA_COMMAND_GetPnPID: + cam->params.pnpID.vendor = data[0]+(((u16)data[1])<<8); + cam->params.pnpID.product = data[2]+(((u16)data[3])<<8); + cam->params.pnpID.deviceRevision = + data[4]+(((u16)data[5])<<8); + break; + case CPIA_COMMAND_GetCameraStatus: + cam->params.status.systemState = data[0]; + cam->params.status.grabState = data[1]; + cam->params.status.streamState = data[2]; + cam->params.status.fatalError = data[3]; + cam->params.status.cmdError = data[4]; + cam->params.status.debugFlags = data[5]; + cam->params.status.vpStatus = data[6]; + cam->params.status.errorCode = data[7]; + break; + case CPIA_COMMAND_GetVPVersion: + cam->params.vpVersion.vpVersion = data[0]; + cam->params.vpVersion.vpRevision = data[1]; + cam->params.vpVersion.cameraHeadID = + data[2]+(((u16)data[3])<<8); + break; + case CPIA_COMMAND_GetColourParams: + cam->params.colourParams.brightness = data[0]; + cam->params.colourParams.contrast = data[1]; + cam->params.colourParams.saturation = data[2]; + up(&cam->param_lock); + break; + case CPIA_COMMAND_GetColourBalance: + cam->params.colourBalance.redGain = data[0]; + cam->params.colourBalance.greenGain = data[1]; + cam->params.colourBalance.blueGain = data[2]; + up(&cam->param_lock); + break; + case CPIA_COMMAND_GetExposure: + cam->params.exposure.gain = data[0]; + cam->params.exposure.fineExp = data[1]; + cam->params.exposure.coarseExpLo = data[2]; + cam->params.exposure.coarseExpHi = data[3]; + cam->params.exposure.redComp = data[4]; + cam->params.exposure.green1Comp = data[5]; + cam->params.exposure.green2Comp = data[6]; + cam->params.exposure.blueComp = data[7]; + /* If the *Comp parameters are wacko, generate + * a warning, and reset them back to default + * values. - [EMAIL PROTECTED] + */ + if (cam->params.exposure.redComp < 220 || + cam->params.exposure.redComp > 255 || + cam->params.exposure.green1Comp < 214 || + cam->params.exposure.green1Comp > 255 || + cam->params.exposure.green2Comp < 214 || + cam->params.exposure.green2Comp > 255 || + cam->params.exposure.blueComp < 230 || + cam->params.exposure.blueComp > 255) + { + printk (KERN_WARNING "*_comp parameters have gone AWOL +(%d/%d/%d/%d) - reseting them\n", + cam->params.exposure.redComp, + cam->params.exposure.green1Comp, + cam->params.exposure.green2Comp, + cam->params.exposure.blueComp); + cam->params.exposure.redComp = 220; + cam->params.exposure.green1Comp = 214; + cam->params.exposure.green2Comp = 214; + cam->params.exposure.blueComp = 230; + } + up(&cam->param_lock); + break; + default: + break; + } + } + return retval; +} + +/* send a command to the camera with an additional data transaction */ +static int do_command_extended(struct cam_data *cam, u16 command, + u8 a, u8 b, u8 c, u8 d, + u8 e, u8 f, u8 g, u8 h, + u8 i, u8 j, u8 k, u8 l) +{ + int retval; + u8 cmd[8], data[8]; + + cmd[0] = command>>8; + cmd[1] = command&0xff; + cmd[2] = a; + cmd[3] = b; + cmd[4] = c; + cmd[5] = d; + cmd[6] = 8; + cmd[7] = 0; + data[0] = e; + data[1] = f; + data[2] = g; + data[3] = h; + data[4] = i; + data[5] = j; + data[6] = k; + data[7] = l; + + retval = cam->ops->transferCmd(cam->lowlevel_data, cmd, data); + if (retval) + LOG("%x - failed\n", command); + + return retval; +} + +/********************************************************************** + * + * Colorspace conversion + * + **********************************************************************/ +#define LIMIT(x) ((((x)>0xffffff)?0xff0000:(((x)<=0xffff)?0:(x)&0xff0000))>>16) + +static int yuvconvert(unsigned char *yuv, unsigned char *rgb, int out_fmt, + int in_uyvy, int mmap_kludge) +{ + int y, u, v, r, g, b, y1; + + switch(out_fmt) { + case VIDEO_PALETTE_RGB555: + case VIDEO_PALETTE_RGB565: + case VIDEO_PALETTE_RGB24: + case VIDEO_PALETTE_RGB32: + if (in_uyvy) { + u = *yuv++ - 128; + y = (*yuv++ - 16) * 76310; + v = *yuv++ - 128; + y1 = (*yuv - 16) * 76310; + } else { + y = (*yuv++ - 16) * 76310; + u = *yuv++ - 128; + y1 = (*yuv++ - 16) * 76310; + v = *yuv - 128; + } + r = 104635 * v; + g = -25690 * u + -53294 * v; + b = 132278 * u; + break; + default: + y = *yuv++; + u = *yuv++; + y1 = *yuv++; + v = *yuv; + /* Just to avoid compiler warnings */ + r = 0; + g = 0; + b = 0; + break; + } + switch(out_fmt) { + case VIDEO_PALETTE_RGB555: + *rgb++ = ((LIMIT(g+y) & 0xf8) << 2) | (LIMIT(b+y) >> 3); + *rgb++ = ((LIMIT(r+y) & 0xf8) >> 1) | (LIMIT(g+y) >> 6); + *rgb++ = ((LIMIT(g+y1) & 0xf8) << 2) | (LIMIT(b+y1) >> 3); + *rgb = ((LIMIT(r+y1) & 0xf8) >> 1) | (LIMIT(g+y1) >> 6); + return 4; + case VIDEO_PALETTE_RGB565: + *rgb++ = ((LIMIT(g+y) & 0xfc) << 3) | (LIMIT(b+y) >> 3); + *rgb++ = (LIMIT(r+y) & 0xf8) | (LIMIT(g+y) >> 5); + *rgb++ = ((LIMIT(g+y1) & 0xfc) << 3) | (LIMIT(b+y1) >> 3); + *rgb = (LIMIT(r+y1) & 0xf8) | (LIMIT(g+y1) >> 5); + return 4; + case VIDEO_PALETTE_RGB24: + if (mmap_kludge) { + *rgb++ = LIMIT(b+y); + *rgb++ = LIMIT(g+y); + *rgb++ = LIMIT(r+y); + *rgb++ = LIMIT(b+y1); + *rgb++ = LIMIT(g+y1); + *rgb = LIMIT(r+y1); + } else { + *rgb++ = LIMIT(r+y); + *rgb++ = LIMIT(g+y); + *rgb++ = LIMIT(b+y); + *rgb++ = LIMIT(r+y1); + *rgb++ = LIMIT(g+y1); + *rgb = LIMIT(b+y1); + } + return 6; + case VIDEO_PALETTE_RGB32: + if (mmap_kludge) { + *rgb++ = LIMIT(b+y); + *rgb++ = LIMIT(g+y); + *rgb++ = LIMIT(r+y); + rgb++; + *rgb++ = LIMIT(b+y1); + *rgb++ = LIMIT(g+y1); + *rgb = LIMIT(r+y1); + } else { + *rgb++ = LIMIT(r+y); + *rgb++ = LIMIT(g+y); + *rgb++ = LIMIT(b+y); + rgb++; + *rgb++ = LIMIT(r+y1); + *rgb++ = LIMIT(g+y1); + *rgb = LIMIT(b+y1); + } + return 8; + case VIDEO_PALETTE_GREY: + *rgb++ = y; + *rgb = y1; + return 2; + case VIDEO_PALETTE_YUV422: + case VIDEO_PALETTE_YUYV: + *rgb++ = y; + *rgb++ = u; + *rgb++ = y1; + *rgb = v; + return 4; + case VIDEO_PALETTE_UYVY: + *rgb++ = u; + *rgb++ = y; + *rgb++ = v; + *rgb = y1; + return 4; + default: + DBG("Empty: %d\n", out_fmt); + return 0; + } +} + +static int skipcount(int count, int fmt) +{ + switch(fmt) { + case VIDEO_PALETTE_GREY: + case VIDEO_PALETTE_RGB555: + case VIDEO_PALETTE_RGB565: + case VIDEO_PALETTE_YUV422: + case VIDEO_PALETTE_YUYV: + case VIDEO_PALETTE_UYVY: + return 2*count; + case VIDEO_PALETTE_RGB24: + return 3*count; + case VIDEO_PALETTE_RGB32: + return 4*count; + default: + return 0; + } +} + +static int parse_picture(struct cam_data *cam, int size) +{ + u8 *obuf, *ibuf, *end_obuf; + int ll, in_uyvy, compressed, origsize, out_fmt; + + /* make sure params don't change while we are decoding */ + down(&cam->param_lock); + + obuf = cam->decompressed_frame.data; + end_obuf = obuf+CPIA_MAX_FRAME_SIZE; + ibuf = cam->raw_image; + origsize = size; + out_fmt = cam->vp.palette; + + if ((ibuf[0] != MAGIC_0) || (ibuf[1] != MAGIC_1)) { + LOG("header not found\n"); + up(&cam->param_lock); + return -1; + } + + if ((ibuf[16] != VIDEOSIZE_QCIF) && (ibuf[16] != VIDEOSIZE_CIF)) { + LOG("wrong video size\n"); + up(&cam->param_lock); + return -1; + } + + if (ibuf[17] != SUBSAMPLE_422) { + LOG("illegal subtype %d\n",ibuf[17]); + up(&cam->param_lock); + return -1; + } + + if (ibuf[18] != YUVORDER_YUYV && ibuf[18] != YUVORDER_UYVY) { + LOG("illegal yuvorder %d\n",ibuf[18]); + up(&cam->param_lock); + return -1; + } + in_uyvy = ibuf[18] == YUVORDER_UYVY; + +#if 0 + /* FIXME: ROI mismatch occurs when switching capture sizes */ + if ((ibuf[24] != cam->params.roi.colStart) || + (ibuf[25] != cam->params.roi.colEnd) || + (ibuf[26] != cam->params.roi.rowStart) || + (ibuf[27] != cam->params.roi.rowEnd)) { + LOG("ROI mismatch\n"); + up(&cam->param_lock); + return -1; + } +#endif + + if ((ibuf[28] != NOT_COMPRESSED) && (ibuf[28] != COMPRESSED)) { + LOG("illegal compression %d\n",ibuf[28]); + up(&cam->param_lock); + return -1; + } + compressed = (ibuf[28] == COMPRESSED); + + if (ibuf[29] != NO_DECIMATION) { + LOG("decimation not supported\n"); + up(&cam->param_lock); + return -1; + } + + cam->params.yuvThreshold.yThreshold = ibuf[30]; + cam->params.yuvThreshold.uvThreshold = ibuf[31]; + cam->params.status.systemState = ibuf[32]; + cam->params.status.grabState = ibuf[33]; + cam->params.status.streamState = ibuf[34]; + cam->params.status.fatalError = ibuf[35]; + cam->params.status.cmdError = ibuf[36]; + cam->params.status.debugFlags = ibuf[37]; + cam->params.status.vpStatus = ibuf[38]; + cam->params.status.errorCode = ibuf[39]; + cam->fps = ibuf[41]; + up(&cam->param_lock); + + ibuf += FRAME_HEADER_SIZE; + size -= FRAME_HEADER_SIZE; + ll = ibuf[0] | (ibuf[1] << 8); + ibuf += 2; + + while (size > 0) { + size -= (ll+2); + if (size < 0) { + LOG("Insufficient data in buffer\n"); + return -1; + } + + while (ll > 1) { + if (!compressed || (compressed && !(*ibuf & 1))) { + obuf += yuvconvert(ibuf, obuf, out_fmt, + in_uyvy, cam->mmap_kludge); + ibuf += 4; + ll -= 4; + } else { + /*skip compressed interval from previous frame*/ + int skipsize = skipcount(*ibuf >> 1, out_fmt); + obuf += skipsize; + if (obuf > end_obuf) { + LOG("Insufficient data in buffer\n"); + return -1; + } + ++ibuf; + ll--; + } + } + if (ll == 1) { + if (*ibuf != EOL) { + LOG("EOL not found giving up after %d/%d" + " bytes\n", origsize-size, origsize); + return -1; + } + + ibuf++; /* skip over EOL */ + + if ((size > 3) && (ibuf[0] == EOI) && (ibuf[1] == EOI) && + (ibuf[2] == EOI) && (ibuf[3] == EOI)) { + size -= 4; + break; + } + + if (size > 1) { + ll = ibuf[0] | (ibuf[1] << 8); + ibuf += 2; /* skip over line length */ + } + } else { + LOG("line length was not 1 but %d after %d/%d bytes\n", + ll, origsize-size, origsize); + return -1; + } + } + + cam->decompressed_frame.count = obuf-cam->decompressed_frame.data; + + return cam->decompressed_frame.count; +} + +/* InitStreamCap wrapper to select correct start line */ +static inline int init_stream_cap(struct cam_data *cam) +{ + return do_command(cam, CPIA_COMMAND_InitStreamCap, + 0, cam->params.streamStartLine, 0, 0); +} + +/* update various camera modes and settings */ +static void dispatch_commands(struct cam_data *cam) +{ + down(&cam->param_lock); + if (cam->cmd_queue==COMMAND_NONE) { + up(&cam->param_lock); + return; + } + DEB_BYTE(cam->cmd_queue); + DEB_BYTE(cam->cmd_queue>>8); + if (cam->cmd_queue & COMMAND_SETCOLOURPARAMS) + do_command(cam, CPIA_COMMAND_SetColourParams, + cam->params.colourParams.brightness, + cam->params.colourParams.contrast, + cam->params.colourParams.saturation, 0); + + if (cam->cmd_queue & COMMAND_SETCOMPRESSION) + do_command(cam, CPIA_COMMAND_SetCompression, + cam->params.compression.mode, + cam->params.compression.decimation, 0, 0); + + if (cam->cmd_queue & COMMAND_SETFORMAT) { + do_command(cam, CPIA_COMMAND_SetFormat, + cam->params.format.videoSize, + cam->params.format.subSample, + cam->params.format.yuvOrder, 0); + do_command(cam, CPIA_COMMAND_SetROI, + cam->params.roi.colStart, cam->params.roi.colEnd, + cam->params.roi.rowStart, cam->params.roi.rowEnd); + cam->first_frame = 1; + } + + if (cam->cmd_queue & COMMAND_SETCOMPRESSIONTARGET) + do_command(cam, CPIA_COMMAND_SetCompressionTarget, + cam->params.compressionTarget.frTargeting, + cam->params.compressionTarget.targetFR, + cam->params.compressionTarget.targetQ, 0); + + if (cam->cmd_queue & COMMAND_SETYUVTHRESH) + do_command(cam, CPIA_COMMAND_SetYUVThresh, + cam->params.yuvThreshold.yThreshold, + cam->params.yuvThreshold.uvThreshold, 0, 0); + + if (cam->cmd_queue & COMMAND_SETECPTIMING) + do_command(cam, CPIA_COMMAND_SetECPTiming, + cam->params.ecpTiming, 0, 0, 0); + + if (cam->cmd_queue & COMMAND_SETCOMPRESSIONPARAMS) + do_command_extended(cam, CPIA_COMMAND_SetCompressionParams, + 0, 0, 0, 0, + cam->params.compressionParams.hysteresis, + cam->params.compressionParams.threshMax, + cam->params.compressionParams.smallStep, + cam->params.compressionParams.largeStep, + cam->params.compressionParams.decimationHysteresis, + cam->params.compressionParams.frDiffStepThresh, + cam->params.compressionParams.qDiffStepThresh, + cam->params.compressionParams.decimationThreshMod); + + if (cam->cmd_queue & COMMAND_SETEXPOSURE) + do_command_extended(cam, CPIA_COMMAND_SetExposure, + cam->params.exposure.gainMode, + cam->params.exposure.expMode, + cam->params.exposure.compMode, + cam->params.exposure.centreWeight, + cam->params.exposure.gain, + cam->params.exposure.fineExp, + cam->params.exposure.coarseExpLo, + cam->params.exposure.coarseExpHi, + cam->params.exposure.redComp, + cam->params.exposure.green1Comp, + cam->params.exposure.green2Comp, + cam->params.exposure.blueComp); + + if (cam->cmd_queue & COMMAND_SETCOLOURBALANCE) { + if (cam->params.colourBalance.balanceModeIsAuto) { + do_command(cam, CPIA_COMMAND_SetColourBalance, + 2, 0, 0, 0); + } else { + do_command(cam, CPIA_COMMAND_SetColourBalance, + 1, + cam->params.colourBalance.redGain, + cam->params.colourBalance.greenGain, + cam->params.colourBalance.blueGain); + do_command(cam, CPIA_COMMAND_SetColourBalance, + 3, 0, 0, 0); + } + } + + if (cam->cmd_queue & COMMAND_SETSENSORFPS) + do_command(cam, CPIA_COMMAND_SetSensorFPS, + cam->params.sensorFps.divisor, + cam->params.sensorFps.baserate, 0, 0); + + if (cam->cmd_queue & COMMAND_SETAPCOR) + do_command(cam, CPIA_COMMAND_SetApcor, + cam->params.apcor.gain1, + cam->params.apcor.gain2, + cam->params.apcor.gain4, + cam->params.apcor.gain8); + + if (cam->cmd_queue & COMMAND_SETFLICKERCTRL) + do_command(cam, CPIA_COMMAND_SetFlickerCtrl, + cam->params.flickerControl.flickerMode, + cam->params.flickerControl.coarseJump, + cam->params.flickerControl.allowableOverExposure, 0); + + if (cam->cmd_queue & COMMAND_SETVLOFFSET) + do_command(cam, CPIA_COMMAND_SetVLOffset, + cam->params.vlOffset.gain1, + cam->params.vlOffset.gain2, + cam->params.vlOffset.gain4, + cam->params.vlOffset.gain8); + + if (cam->cmd_queue & COMMAND_PAUSE) + do_command(cam, CPIA_COMMAND_EndStreamCap, 0, 0, 0, 0); + + if (cam->cmd_queue & COMMAND_RESUME) + init_stream_cap(cam); + + up(&cam->param_lock); + cam->cmd_queue = COMMAND_NONE; + return; +} + +/* kernel thread function to read image from camera */ +static void fetch_frame(void *data) +{ + int image_size, retry; + struct cam_data *cam = (struct cam_data *)data; + unsigned long oldjif, rate, diff; + + /* Allow up to two bad images in a row to be read and + * ignored before an error is reported */ + for (retry = 0; retry < 3; ++retry) { + if (retry) + DBG("retry=%d\n", retry); + + if (!cam->ops) + continue; + + /* load first frame always uncompressed */ + if (cam->first_frame && + cam->params.compression.mode != CPIA_COMPRESSION_NONE) + do_command(cam, CPIA_COMMAND_SetCompression, + CPIA_COMPRESSION_NONE, + NO_DECIMATION, 0, 0); + + /* init camera upload */ + if (do_command(cam, CPIA_COMMAND_SetGrabMode, + CPIA_GRAB_CONTINUOUS, 0, 0, 0)) + continue; + + if (do_command(cam, CPIA_COMMAND_GrabFrame, 0, + cam->params.streamStartLine, 0, 0)) + continue; + + if (cam->ops->wait_for_stream_ready) { + /* loop until image ready */ + do_command(cam, CPIA_COMMAND_GetCameraStatus,0,0,0,0); + while (cam->params.status.streamState != STREAM_READY) { + if (current->need_resched) + schedule(); + + current->state = TASK_INTERRUPTIBLE; + + /* sleep for 10 ms, hopefully ;) */ + schedule_timeout(10*HZ/1000); + if (signal_pending(current)) + return; + + do_command(cam, CPIA_COMMAND_GetCameraStatus, + 0, 0, 0, 0); + } + } + + /* grab image from camera */ + if (current->need_resched) + schedule(); + + oldjif = jiffies; + image_size = cam->ops->streamRead(cam->lowlevel_data, + cam->raw_image, 0); + if (image_size <= 0) { + DBG("streamRead failed: %d\n", image_size); + continue; + } + + rate = image_size * HZ / 1024; + diff = jiffies-oldjif; + cam->transfer_rate = diff==0 ? rate : rate/diff; + /* diff==0 ? unlikely but possible */ + + /* camera idle now so dispatch queued commands */ + dispatch_commands(cam); + + /* Update our knowledge of the camera state - FIXME: necessary? */ + do_command(cam, CPIA_COMMAND_GetColourBalance, 0, 0, 0, 0); + do_command(cam, CPIA_COMMAND_GetExposure, 0, 0, 0, 0); + + /* decompress and convert image to by copying it from + * raw_image to decompressed_frame + */ + if (current->need_resched) + schedule(); + + cam->image_size = parse_picture(cam, image_size); + if (cam->image_size <= 0) + DBG("parse_picture failed %d\n", cam->image_size); + else + break; + } + + if (retry < 3) { + /* FIXME: this only works for double buffering */ + if (cam->frame[cam->curframe].state == FRAME_READY) { + memcpy(cam->frame[cam->curframe].data, + cam->decompressed_frame.data, + cam->decompressed_frame.count); + cam->frame[cam->curframe].state = FRAME_DONE; + } else + cam->decompressed_frame.state = FRAME_DONE; + +#if 0 + if (cam->first_frame && + cam->params.compression.mode != CPIA_COMPRESSION_NONE) { + cam->first_frame = 0; + cam->cmd_queue |= COMMAND_SETCOMPRESSION; + } +#else + if (cam->first_frame) { + cam->first_frame = 0; + cam->cmd_queue |= COMMAND_SETCOMPRESSION; + cam->cmd_queue |= COMMAND_SETEXPOSURE; + } +#endif + } +} + +static int capture_frame(struct cam_data *cam, struct video_mmap *vm) +{ + int retval = 0; + + if (!cam->frame_buf) { + /* we do lazy allocation */ + if ((retval = allocate_frame_buf(cam))) + return retval; + } + + /* FIXME: the first frame seems to be captured by the camera + without regards to any initial settings, so we throw away + that one, the next one is generated with our settings + (exposure, color balance, ...) + */ + if (cam->first_frame) { + cam->curframe = vm->frame; + cam->frame[cam->curframe].state = FRAME_READY; + fetch_frame(cam); + if (cam->frame[cam->curframe].state != FRAME_DONE) + retval = -EIO; + } + cam->curframe = vm->frame; + cam->frame[cam->curframe].state = FRAME_READY; + fetch_frame(cam); + if (cam->frame[cam->curframe].state != FRAME_DONE) + retval=-EIO; + + return retval; +} + +static int goto_high_power(struct cam_data *cam) +{ + if (do_command(cam, CPIA_COMMAND_GotoHiPower, 0, 0, 0, 0)) + return -1; + mdelay(100); /* windows driver does it too */ + if (do_command(cam, CPIA_COMMAND_GetCameraStatus, 0, 0, 0, 0)) + return -1; + if (cam->params.status.systemState == HI_POWER_STATE) { + DBG("camera now in HIGH power state\n"); + return 0; + } + printstatus(cam); + return -1; +} + +static int goto_low_power(struct cam_data *cam) +{ + if (do_command(cam, CPIA_COMMAND_GotoLoPower, 0, 0, 0, 0)) + return -1; + if (do_command(cam, CPIA_COMMAND_GetCameraStatus, 0, 0, 0, 0)) + return -1; + if (cam->params.status.systemState == LO_POWER_STATE) { + DBG("camera now in LOW power state\n"); + return 0; + } + printstatus(cam); + return -1; +} + +static void save_camera_state(struct cam_data *cam) +{ + do_command(cam, CPIA_COMMAND_GetColourBalance, 0, 0, 0, 0); + do_command(cam, CPIA_COMMAND_GetExposure, 0, 0, 0, 0); + + DBG("%d/%d/%d/%d/%d/%d/%d/%d\n", + cam->params.exposure.gain, + cam->params.exposure.fineExp, + cam->params.exposure.coarseExpLo, + cam->params.exposure.coarseExpHi, + cam->params.exposure.redComp, + cam->params.exposure.green1Comp, + cam->params.exposure.green2Comp, + cam->params.exposure.blueComp); + DBG("%d/%d/%d\n", + cam->params.colourBalance.redGain, + cam->params.colourBalance.greenGain, + cam->params.colourBalance.blueGain); +} + +static void set_camera_state(struct cam_data *cam) +{ + if(cam->params.colourBalance.balanceModeIsAuto) { + do_command(cam, CPIA_COMMAND_SetColourBalance, + 2, 0, 0, 0); + } else { + do_command(cam, CPIA_COMMAND_SetColourBalance, + 1, + cam->params.colourBalance.redGain, + cam->params.colourBalance.greenGain, + cam->params.colourBalance.blueGain); + do_command(cam, CPIA_COMMAND_SetColourBalance, + 3, 0, 0, 0); + } + + + do_command_extended(cam, CPIA_COMMAND_SetExposure, + cam->params.exposure.gainMode, 1, 1, + cam->params.exposure.centreWeight, + cam->params.exposure.gain, + cam->params.exposure.fineExp, + cam->params.exposure.coarseExpLo, + cam->params.exposure.coarseExpHi, + cam->params.exposure.redComp, + cam->params.exposure.green1Comp, + cam->params.exposure.green2Comp, + cam->params.exposure.blueComp); + do_command_extended(cam, CPIA_COMMAND_SetExposure, + 0, 3, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0); + + if (!cam->params.exposure.gainMode) + cam->params.exposure.gainMode = 2; + if (!cam->params.exposure.expMode) + cam->params.exposure.expMode = 2; + if (!cam->params.exposure.centreWeight) + cam->params.exposure.centreWeight = 1; + + cam->cmd_queue = COMMAND_SETCOMPRESSION | + COMMAND_SETCOMPRESSIONTARGET | + COMMAND_SETCOLOURPARAMS | + COMMAND_SETFORMAT | + COMMAND_SETYUVTHRESH | + COMMAND_SETECPTIMING | + COMMAND_SETCOMPRESSIONPARAMS | +#if 0 + COMMAND_SETEXPOSURE | +#endif + COMMAND_SETCOLOURBALANCE | + COMMAND_SETSENSORFPS | + COMMAND_SETAPCOR | + COMMAND_SETFLICKERCTRL | + COMMAND_SETVLOFFSET; + dispatch_commands(cam); + save_camera_state(cam); + + return; +} + +static void get_version_information(struct cam_data *cam) +{ + /* GetCPIAVersion */ + do_command(cam, CPIA_COMMAND_GetCPIAVersion, 0, 0, 0, 0); + + /* GetPnPID */ + do_command(cam, CPIA_COMMAND_GetPnPID, 0, 0, 0, 0); +} + +/* initialize camera */ +static int reset_camera(struct cam_data *cam) +{ + /* Start the camera in low power mode */ + if (goto_low_power(cam)) { + if (cam->params.status.systemState != WARM_BOOT_STATE) + return -ENODEV; + + /* FIXME: this is just dirty trial and error */ + reset_camera_struct(cam); + goto_high_power(cam); + do_command(cam, CPIA_COMMAND_DiscardFrame, 0, 0, 0, 0); + if (goto_low_power(cam)) + return -NODEV; + } + + /* procedure described in developer's guide p3-28 */ + + /* Check the firmware version FIXME: should we check PNPID? */ + cam->params.version.firmwareVersion = 0; + get_version_information(cam); + if (cam->params.version.firmwareVersion != 1) + return -ENODEV; + + /* The fatal error checking should be done after + * the camera powers up (developer's guide p 3-38) */ + + /* Set streamState before transition to high power to avoid bug + * in firmware 1-02 */ + do_command(cam, CPIA_COMMAND_ModifyCameraStatus, STREAMSTATE, 0, + STREAM_NOT_READY, 0); + + /* GotoHiPower */ + if (goto_high_power(cam)) + return -ENODEV; + + /* Check the camera status */ + if (do_command(cam, CPIA_COMMAND_GetCameraStatus, 0, 0, 0, 0)) + return -EIO; + + if (cam->params.status.fatalError) { + DBG("fatal_error: %#04x\n", + cam->params.status.fatalError); + DBG("vp_status: %#04x\n", + cam->params.status.vpStatus); + if (cam->params.status.fatalError & ~(COM_FLAG|CPIA_FLAG)) { + /* Fatal error in camera */ + return -EIO; + } else if (cam->params.status.fatalError & (COM_FLAG|CPIA_FLAG)) { + /* Firmware 1-02 may do this for parallel port cameras, + * just clear the flags (developer's guide p 3-38) */ + do_command(cam, CPIA_COMMAND_ModifyCameraStatus, + FATALERROR, ~(COM_FLAG|CPIA_FLAG), 0, 0); + } + } + + /* Check the camera status again */ + if (cam->params.status.fatalError) { + if (cam->params.status.fatalError) + return -EIO; + } + + /* VPVersion can't be retrieved before the camera is in HiPower, + * so get it here instead of in get_version_information. */ + do_command(cam, CPIA_COMMAND_GetVPVersion, 0, 0, 0, 0); + + /* set camera to a known state */ + set_camera_state(cam); + + return 0; +} + +/* ------------------------- V4L interface --------------------- */ +static int cpia_open(struct video_device *dev, int flags) +{ + int i; + struct cam_data *cam = dev->priv; + + if (!cam) { + DBG("Internal error, cam_data not found!\n"); + return -EBUSY; + } + + if (cam->open_count > 0) { + DBG("Camera already open\n"); + return -EBUSY; + } + + if (!cam->raw_image) { + cam->raw_image = rvmalloc(CPIA_MAX_IMAGE_SIZE); + if (!cam->raw_image) + return -ENOMEM; + } + + if (!cam->decompressed_frame.data) { + cam->decompressed_frame.data = rvmalloc(CPIA_MAX_FRAME_SIZE); + if (!cam->decompressed_frame.data) { + rvfree(cam->raw_image, CPIA_MAX_IMAGE_SIZE); + cam->raw_image = NULL; + return -ENOMEM; + } + } + + /* open cpia */ + if (cam->ops->open(cam->lowlevel_data)) { + rvfree(cam->decompressed_frame.data, CPIA_MAX_FRAME_SIZE); + cam->decompressed_frame.data = NULL; + rvfree(cam->raw_image, CPIA_MAX_IMAGE_SIZE); + cam->raw_image = NULL; + return -ENODEV; + } + + /* reset the camera */ + if ((i = reset_camera(cam)) != 0) { + cam->ops->close(cam->lowlevel_data); + rvfree(cam->decompressed_frame.data, CPIA_MAX_FRAME_SIZE); + cam->decompressed_frame.data = NULL; + rvfree(cam->raw_image, CPIA_MAX_IMAGE_SIZE); + cam->raw_image = NULL; + return i; + } + + /* Set ownership of /proc/cpia/videoX to current user */ + if(cam->proc_entry) + cam->proc_entry->uid = current->uid; + + /* set mark for loading first frame uncompressed */ + cam->first_frame = 1; + + /* init it to something */ + cam->mmap_kludge = 0; + + ++cam->open_count; +#ifdef MODULE + MOD_INC_USE_COUNT; +#endif + return 0; +} + +static void cpia_close(struct video_device *dev) +{ + struct cam_data *cam; + + cam = dev->priv; + + if (cam->ops) { + /* Return ownership of /proc/cpia/videoX to root */ + if(cam->proc_entry) + cam->proc_entry->uid = 0; + + /* save camera state for later open (developers guide ch 3.5.3) */ + save_camera_state(cam); + + /* GotoLoPower */ + goto_low_power(cam); + + /* Update the camera ststus */ + do_command(cam, CPIA_COMMAND_GetCameraStatus, 0, 0, 0, 0); + + /* cleanup internal state stuff */ + free_frames(cam->frame); + + /* close cpia */ + cam->ops->close(cam->lowlevel_data); + } + + if (--cam->open_count == 0) { + /* clean up capture-buffers */ + if (cam->raw_image) { + rvfree(cam->raw_image, CPIA_MAX_IMAGE_SIZE); + cam->raw_image = NULL; + } + + if (cam->decompressed_frame.data) { + rvfree(cam->decompressed_frame.data, CPIA_MAX_FRAME_SIZE); + cam->decompressed_frame.data = NULL; + } + + if (cam->frame_buf) + free_frame_buf(cam); + + if (!cam->ops) { + video_unregister_device(dev); + kfree(cam); + } + } + + +#ifdef MODULE + MOD_DEC_USE_COUNT; +#endif + return; +} + +static long cpia_read(struct video_device *dev, char *buf, + unsigned long count, int noblock) +{ + struct cam_data *cam = dev->priv; + + /* make this _really_ smp and multithredi-safe */ + if (down_interruptible(&cam->busy_lock)) + return -EINTR; + + if (!buf) { + DBG("buf NULL\n"); + up(&cam->busy_lock); + return -EINVAL; + } + + if (!count) { + DBG("count 0\n"); + up(&cam->busy_lock); + return 0; + } + + if (!cam->ops) { + DBG("ops NULL\n"); + up(&cam->busy_lock); + return -ENODEV; + } + + /* upload frame */ + cam->decompressed_frame.state = FRAME_READY; + cam->mmap_kludge=0; + fetch_frame(cam); + if (cam->decompressed_frame.state != FRAME_DONE) { + DBG("upload failed %d/%d\n", cam->decompressed_frame.count, + cam->decompressed_frame.state); + up(&cam->busy_lock); + return -EIO; + } + cam->decompressed_frame.state = FRAME_UNUSED; + + /* copy data to user space */ + if (cam->decompressed_frame.count > count) { + DBG("count wrong: %d, %lu\n", cam->decompressed_frame.count, + count); + up(&cam->busy_lock); + return -EFAULT; + } + if (copy_to_user(buf, cam->decompressed_frame.data, + cam->decompressed_frame.count)) { + DBG("copy_to_user failed\n"); + up(&cam->busy_lock); + return -EFAULT; + } + + up(&cam->busy_lock); + return cam->decompressed_frame.count; +} + +static int cpia_ioctl(struct video_device *dev, unsigned int ioctlnr, void *arg) +{ + struct cam_data *cam = dev->priv; + int retval = 0; + + if (!cam || !cam->ops) + return -ENODEV; + + /* make this _really_ smp-safe */ + if (down_interruptible(&cam->busy_lock)) + return -EINTR; + + //DBG("cpia_ioctl: %u\n", ioctlnr); + + switch (ioctlnr) { + /* query capabilites */ + case VIDIOCGCAP: + { + struct video_capability b; + + DBG("VIDIOCGCAP\n"); + strcpy(b.name, "CPiA Camera"); + b.type = VID_TYPE_CAPTURE; + b.channels = 1; + b.audios = 0; + b.maxwidth = 352; /* VIDEOSIZE_CIF */ + b.maxheight = 288; + b.minwidth = 48; /* VIDEOSIZE_48_48 */ + b.minheight = 48; + + if (copy_to_user(arg, &b, sizeof(b))) + retval = -EFAULT; + + break; + } + + /* get/set video source - we are a camera and nothing else */ + case VIDIOCGCHAN: + { + struct video_channel v; + + DBG("VIDIOCGCHAN\n"); + if (copy_from_user(&v, arg, sizeof(v))) { + retval = -EFAULT; + break; + } + if (v.channel != 0) { + retval = -EINVAL; + break; + } + + v.channel = 0; + strcpy(v.name, "Camera"); + v.tuners = 0; + v.flags = 0; + v.type = VIDEO_TYPE_CAMERA; + v.norm = 0; + + if (copy_to_user(arg, &v, sizeof(v))) + retval = -EFAULT; + break; + } + + case VIDIOCSCHAN: + { + int v; + + DBG("VIDIOCSCHAN\n"); + if (copy_from_user(&v, arg, sizeof(v))) + retval = -EFAULT; + + if (retval == 0 && v != 0) + retval = -EINVAL; + + break; + } + + /* image properties */ + case VIDIOCGPICT: + DBG("VIDIOCGPICT\n"); + if (copy_to_user(arg, &cam->vp, sizeof(struct video_picture))) + retval = -EFAULT; + break; + + case VIDIOCSPICT: + { + struct video_picture vp; + + DBG("VIDIOCSPICT\n"); + + /* copy_from_user */ + if (copy_from_user(&vp, arg, sizeof(vp))) { + retval = -EFAULT; + break; + } + + /* check validity */ + DBG("palette: %d\n", vp.palette); + DBG("depth: %d\n", vp.depth); + if (!valid_mode(vp.palette, vp.depth)) { + retval = -EINVAL; + break; + } + + down(&cam->param_lock); + /* brightness, colour, contrast need no check 0-65535 */ + memcpy( &cam->vp, &vp, sizeof(vp) ); + /* update cam->params.colourParams */ + cam->params.colourParams.brightness = vp.brightness*100/65535; + cam->params.colourParams.contrast = vp.contrast*100/65535; + cam->params.colourParams.saturation = vp.colour*100/65535; + /* contrast is in steps of 8, so round */ + cam->params.colourParams.contrast = + ((cam->params.colourParams.contrast + 3) / 8) * 8; + if (cam->params.version.firmwareVersion == 1 && + cam->params.version.firmwareRevision == 2 && + cam->params.colourParams.contrast > 80) { + /* 1-02 firmware limits contrast to 80 */ + cam->params.colourParams.contrast = 80; + } + + /* queue command to update camera */ + cam->cmd_queue |= COMMAND_SETCOLOURPARAMS; + up(&cam->param_lock); + DBG("VIDIOCSPICT: %d / %d // %d / %d / %d / %d\n", + vp.depth, vp.palette, vp.brightness, vp.hue, vp.colour, + vp.contrast); + break; + } + + /* get/set capture window */ + case VIDIOCGWIN: + DBG("VIDIOCGWIN\n"); + + if (copy_to_user(arg, &cam->vw, sizeof(struct video_window))) + retval = -EFAULT; + break; + + case VIDIOCSWIN: + { + /* copy_from_user, check validity, copy to internal structure */ + struct video_window vw; + DBG("VIDIOCSWIN\n"); + if (copy_from_user(&vw, arg, sizeof(vw))) { + retval = -EFAULT; + break; + } + + if (vw.clipcount != 0) { /* clipping not supported */ + retval = -EINVAL; + break; + } + if (vw.clips != NULL) { /* clipping not supported */ + retval = -EINVAL; + break; + } + + /* we set the video window to something smaller or equal to what + * is requested by the user??? + */ + down(&cam->param_lock); + if (vw.width != cam->vw.width || vw.height != cam->vw.height) { + int video_size = match_videosize(vw.width, vw.height); + + if (video_size < 0) { + retval = -EINVAL; + up(&cam->param_lock); + break; + } + cam->video_size = video_size; + set_vw_size(cam); + DBG("%d / %d\n", cam->vw.width, cam->vw.height); + cam->cmd_queue |= COMMAND_SETFORMAT; + } + + // FIXME needed??? memcpy(&cam->vw, &vw, sizeof(vw)); + up(&cam->param_lock); + + /* setformat ignored by camera during streaming, + * so stop/dispatch/start */ + if (cam->cmd_queue & COMMAND_SETFORMAT) { + DBG("\n"); + dispatch_commands(cam); + } + DBG("%d/%d:%d\n", cam->video_size, + cam->vw.width, cam->vw.height); + break; + } + + /* mmap interface */ + case VIDIOCGMBUF: + { + struct video_mbuf vm; + int i; + + DBG("VIDIOCGMBUF\n"); + memset(&vm, 0, sizeof(vm)); + vm.size = CPIA_MAX_FRAME_SIZE*FRAME_NUM; + vm.frames = FRAME_NUM; + for (i = 0; i < FRAME_NUM; i++) + vm.offsets[i] = CPIA_MAX_FRAME_SIZE * i; + + if (copy_to_user((void *)arg, (void *)&vm, sizeof(vm))) + retval = -EFAULT; + + break; + } + + case VIDIOCMCAPTURE: + { + struct video_mmap vm; + int video_size; + + if (copy_from_user((void *)&vm, (void *)arg, sizeof(vm))) { + retval = -EFAULT; + break; + } +#if 1 + DBG("VIDIOCMCAPTURE: %d / %d / %dx%d\n", vm.format, vm.frame, + vm.width, vm.height); +#endif + if (vm.frame<0||vm.frame>FRAME_NUM) { + retval = -EINVAL; + break; + } + + /* set video format */ + cam->vp.palette = vm.format; + switch(vm.format) { + case VIDEO_PALETTE_GREY: + case VIDEO_PALETTE_RGB555: + case VIDEO_PALETTE_RGB565: + case VIDEO_PALETTE_YUV422: + case VIDEO_PALETTE_YUYV: + case VIDEO_PALETTE_UYVY: + cam->vp.depth = 16; + break; + case VIDEO_PALETTE_RGB24: + cam->vp.depth = 24; + break; + case VIDEO_PALETTE_RGB32: + cam->vp.depth = 32; + break; + default: + retval = -EINVAL; + break; + } + if (retval) + break; + + /* set video size */ + video_size = match_videosize(vm.width, vm.height); + if (cam->video_size < 0) { + retval = -EINVAL; + break; + } + if (video_size != cam->video_size) { + cam->video_size = video_size; + set_vw_size(cam); + cam->cmd_queue |= COMMAND_SETFORMAT; + dispatch_commands(cam); + } +#if 0 + DBG("VIDIOCMCAPTURE: %d / %d/%d\n", cam->video_size, + cam->vw.width, cam->vw.height); +#endif + /* according to v4l-spec we must start streaming here */ + cam->mmap_kludge = 1; + retval = capture_frame(cam, &vm); + + break; + } + + case VIDIOCSYNC: + { + int frame; + + if (copy_from_user((void *)&frame, arg, sizeof(int))) { + retval = -EFAULT; + break; + } + //DBG("VIDIOCSYNC: %d\n", frame); + + if (frame<0 || frame >= FRAME_NUM) { + retval = -EINVAL; + break; + } + + switch (cam->frame[frame].state) { + case FRAME_UNUSED: + case FRAME_READY: + case FRAME_GRABBING: + DBG("sync to unused frame %d\n", frame); + retval = -EINVAL; + break; + + case FRAME_DONE: + cam->frame[frame].state = FRAME_UNUSED; + //DBG("VIDIOCSYNC: %d synced\n", frame); + break; + } + if (retval == -EINTR) { + /* FIXME - xawtv does not handle this nice */ + retval = 0; + } + break; + } + + /* pointless to implement overlay with this camera */ + case VIDIOCCAPTURE: + retval = -EINVAL; + break; + case VIDIOCGFBUF: + retval = -EINVAL; + break; + case VIDIOCSFBUF: + retval = -EINVAL; + break; + case VIDIOCKEY: + retval = -EINVAL; + break; + + /* tuner interface - we have none */ + case VIDIOCGTUNER: + retval = -EINVAL; + break; + case VIDIOCSTUNER: + retval = -EINVAL; + break; + case VIDIOCGFREQ: + retval = -EINVAL; + break; + case VIDIOCSFREQ: + retval = -EINVAL; + break; + + /* audio interface - we have none */ + case VIDIOCGAUDIO: + retval = -EINVAL; + break; + case VIDIOCSAUDIO: + retval = -EINVAL; + break; + default: + retval = -ENOIOCTLCMD; + break; + } + + up(&cam->param_lock); + up(&cam->busy_lock); + return retval; +} + +/* FIXME */ +static int cpia_mmap(struct video_device *dev, const char *adr, + unsigned long size) +{ + unsigned long start = (unsigned long)adr; + unsigned long page, pos; + struct cam_data *cam = dev->priv; + int retval; + + if (!cam || !cam->ops) + return -ENODEV; + + DBG("cpia_mmap: %ld\n", size); + + if (size > FRAME_NUM*CPIA_MAX_FRAME_SIZE) + return -EINVAL; + + if (!cam || !cam->ops) + return -ENODEV; + + /* make this _really_ smp-safe */ + if (down_interruptible(&cam->busy_lock)) + return -EINTR; + + if (!cam->frame_buf) { /* we do lazy allocation */ + if ((retval = allocate_frame_buf(cam))) { + up(&cam->busy_lock); + return retval; + } + } + + pos = (unsigned long)(cam->frame_buf); + while (size > 0) { + page = kvirt_to_pa(pos); + if (remap_page_range(start, page, PAGE_SIZE, PAGE_SHARED)) { + up(&cam->busy_lock); + return -EAGAIN; + } + start += PAGE_SIZE; + pos += PAGE_SIZE; + if (size > PAGE_SIZE) + size -= PAGE_SIZE; + else + size = 0; + } + + DBG("cpia_mmap: %ld\n", size); + up(&cam->busy_lock); + + return 0; +} + +int cpia_video_init(struct video_device *vdev) +{ +#ifdef CONFIG_PROC_FS + create_proc_cpia_cam(vdev->priv); +#endif + return 0; +} + +static struct video_device cpia_template = { + "CPiA Camera", + VID_TYPE_CAPTURE, + VID_HARDWARE_CPIA, /* FIXME */ + cpia_open, /* open */ + cpia_close, /* close */ + cpia_read, /* read */ + NULL, /* no write */ + NULL, /* no poll */ + cpia_ioctl, /* ioctl */ + cpia_mmap, /* mmap */ + cpia_video_init, /* initialize */ + NULL, /* priv */ + 0, /* busy */ + -1 /* minor - unset */ +}; + +/* initialise cam_data structure */ +static void reset_camera_struct(struct cam_data *cam) +{ + /* The following parameter values are the defaults from + * "Software Developer's Guide for CPiA Cameras". Any changes + * to the defaults are noted in comments. */ + cam->params.colourParams.brightness = 50; + cam->params.colourParams.contrast = 48; + cam->params.colourParams.saturation = 50; + cam->params.exposure.gainMode = 2; + cam->params.exposure.expMode = 2; /* AEC */ + cam->params.exposure.compMode = 1; + cam->params.exposure.centreWeight = 1; + cam->params.exposure.gain = 0; + cam->params.exposure.fineExp = 0; + cam->params.exposure.coarseExpLo = 185; + cam->params.exposure.coarseExpHi = 0; + cam->params.exposure.redComp = 220; + cam->params.exposure.green1Comp = 214; + cam->params.exposure.green2Comp = 214; + cam->params.exposure.blueComp = 230; + cam->params.colourBalance.balanceModeIsAuto = 1; + cam->params.colourBalance.redGain = 32; + cam->params.colourBalance.greenGain = 6; + cam->params.colourBalance.blueGain = 92; + cam->params.apcor.gain1 = 0x1c; + cam->params.apcor.gain2 = 0x1a; + cam->params.apcor.gain4 = 0x2d; + cam->params.apcor.gain8 = 0x2a; + cam->params.flickerControl.flickerMode = 0; + cam->params.flickerControl.coarseJump = + flicker_jumps[cam->mainsFreq] + [cam->params.sensorFps.baserate] + [cam->params.sensorFps.divisor]; + cam->params.vlOffset.gain1 = 24; + cam->params.vlOffset.gain2 = 28; + cam->params.vlOffset.gain4 = 30; + cam->params.vlOffset.gain8 = 30; + cam->params.compressionParams.hysteresis = 3; + cam->params.compressionParams.threshMax = 11; + cam->params.compressionParams.smallStep = 1; + cam->params.compressionParams.largeStep = 3; + cam->params.compressionParams.decimationHysteresis = 2; + cam->params.compressionParams.frDiffStepThresh = 5; + cam->params.compressionParams.qDiffStepThresh = 3; + cam->params.compressionParams.decimationThreshMod = 2; + /* End of default values from Software Developer's Guide */ + + cam->transfer_rate = 0; + + /* Set Sensor FPS to 15fps. This seems better than 30fps + * for indoor lighting. */ + cam->params.sensorFps.divisor = 1; + cam->params.sensorFps.baserate = 1; + + cam->params.yuvThreshold.yThreshold = 15; /* FIXME? */ + cam->params.yuvThreshold.uvThreshold = 15; /* FIXME? */ + + cam->params.format.subSample = SUBSAMPLE_422; + cam->params.format.yuvOrder = YUVORDER_YUYV; + + cam->params.compression.mode = CPIA_COMPRESSION_AUTO; + cam->params.compressionTarget.frTargeting = + CPIA_COMPRESSION_TARGET_QUALITY; + cam->params.compressionTarget.targetFR = 7; /* FIXME? */ + cam->params.compressionTarget.targetQ = 10; /* FIXME? */ + + cam->video_size = VIDEOSIZE_CIF; + + cam->vp.colour = 32768; /* 50% */ + cam->vp.hue = 32768; /* 50% */ + cam->vp.brightness = 32768; /* 50% */ + cam->vp.contrast = 32768; /* 50% */ + cam->vp.whiteness = 0; /* not used -> grayscale only */ + cam->vp.depth = 0; /* FIXME: to be set by user? */ + cam->vp.palette = VIDEO_PALETTE_RGB24; /* FIXME: to be set by user? */ + + cam->vw.x = 0; + cam->vw.y = 0; + set_vw_size(cam); + cam->vw.chromakey = 0; + /* PP NOTE: my extension to use vw.flags for this, bear it! */ + cam->vw.flags = 0; + cam->vw.clipcount = 0; + cam->vw.clips = NULL; + + cam->cmd_queue = COMMAND_NONE; + cam->first_frame = 0; + + return; +} + +/* initialize cam_data structure */ +static void init_camera_struct(struct cam_data *cam, + struct cpia_camera_ops *ops ) +{ + int i; + + /* Default everything to 0 */ + memset(cam, 0, sizeof(struct cam_data)); + + cam->ops = ops; + init_MUTEX(&cam->param_lock); + init_MUTEX(&cam->busy_lock); + + reset_camera_struct(cam); + + cam->proc_entry = NULL; + + memcpy(&cam->vdev, &cpia_template, sizeof(cpia_template)); + cam->vdev.priv = cam; + + cam->curframe = 0; + for (i = 0; i < FRAME_NUM; i++) { + cam->frame[i].width = 0; + cam->frame[i].height = 0; + cam->frame[i].state = FRAME_UNUSED; + cam->frame[i].data = NULL; + } + cam->decompressed_frame.width = 0; + cam->decompressed_frame.height = 0; + cam->decompressed_frame.state = FRAME_UNUSED; + cam->decompressed_frame.data = NULL; +} + +struct cam_data *cpia_register_camera(struct cpia_camera_ops *ops, void *lowlevel) +{ + struct cam_data *camera; + + /* Need a lock when adding/removing cameras. This doesn't happen + * often and doesn't take very long, so grabbing the kernel lock + * should be OK. */ + + if ((camera = kmalloc(sizeof(struct cam_data), GFP_KERNEL)) == NULL) { + unlock_kernel(); + return NULL; + } + + init_camera_struct( camera, ops ); + camera->lowlevel_data = lowlevel; + + /* register v4l device */ + if (video_register_device(&camera->vdev, VFL_TYPE_GRABBER) == -1) { + kfree(camera); + unlock_kernel(); + printk(KERN_DEBUG "video_register_device failed\n"); + return NULL; + } + + /* get version information from camera: open/reset/close */ + + /* open cpia */ + if (camera->ops->open(camera->lowlevel_data)) + return camera; + + /* reset the camera */ + if (reset_camera(camera) != 0) { + camera->ops->close(camera->lowlevel_data); + return camera; + } + + /* close cpia */ + camera->ops->close(camera->lowlevel_data); + +/* Eh? Feeling happy? - jerdfelt */ +/* + camera->ops->open(camera->lowlevel_data); + camera->ops->close(camera->lowlevel_data); +*/ + + printk(KERN_INFO " CPiA Version: %d.%02d (%d.%d)\n", + camera->params.version.firmwareVersion, + camera->params.version.firmwareRevision, + camera->params.version.vcVersion, + camera->params.version.vcRevision); + printk(KERN_INFO " CPiA PnP-ID: %04x:%04x:%04x\n", + camera->params.pnpID.vendor, + camera->params.pnpID.product, + camera->params.pnpID.deviceRevision); + printk(KERN_INFO " VP-Version: %d.%d %04x\n", + camera->params.vpVersion.vpVersion, + camera->params.vpVersion.vpRevision, + camera->params.vpVersion.cameraHeadID); + + return camera; +} + +void cpia_unregister_camera(struct cam_data *cam) +{ + if (!cam->open_count) { + DBG("unregistering video\n"); + video_unregister_device(&cam->vdev); + } else { + LOG("/dev/video%d removed while open, " + "deferring video_unregister_device\n", cam->vdev.minor); + DBG("camera open -- setting ops to NULL\n"); + cam->ops = NULL; + } + +#ifdef CONFIG_PROC_FS + DBG("destroying /proc/cpia/video%d\n", cam->vdev.minor); + destroy_proc_cpia_cam(cam); +#endif + if (!cam->open_count) { + DBG("freeing camera\n"); + kfree(cam); + } +} + +/**************************************************************************** + * + * Module routines + * + ***************************************************************************/ + +#ifdef MODULE +int init_module(void) +{ + printk(KERN_INFO "%s v%d.%d.%d\n", ABOUT, + CPIA_MAJ_VER, CPIA_MIN_VER, CPIA_PATCH_VER); +#ifdef CONFIG_PROC_FS + proc_cpia_create(); +#endif +#ifdef CONFIG_KMOD +#ifdef CONFIG_VIDEO_CPIA_PP_MODULE + request_module("cpia_pp"); +#endif +#ifdef CONFIG_VIDEO_CPIA_USB_MODULE + request_module("cpia_usb"); +#endif +#endif +return 0; +} + +void cleanup_module(void) +{ +#ifdef CONFIG_PROC_FS + proc_cpia_destroy(); +#endif +} + +#else + +int cpia_init(struct video_init *unused) +{ + printk(KERN_INFO "%s v%d.%d.%d\n", ABOUT, + CPIA_MAJ_VER, CPIA_MIN_VER, CPIA_PATCH_VER); +#ifdef CONFIG_PROC_FS + proc_cpia_create(); +#endif + +#ifdef CONFIG_VIDEO_CPIA_PP + cpia_pp_init(); +#endif +#ifdef CONFIG_KMOD +#ifdef CONFIG_VIDEO_CPIA_PP_MODULE + request_module("cpia_pp"); +#endif + +#ifdef CONFIG_VIDEO_CPIA_USB_MODULE + request_module("cpia_usb"); +#endif +#endif /* CONFIG_KMOD */ +#ifdef CONFIG_VIDEO_CPIA_USB + cpia_usb_init(); +#endif + return 0; +} + +/* Exported symbols for modules. */ + +EXPORT_SYMBOL(cpia_register_camera); +EXPORT_SYMBOL(cpia_unregister_camera); + +#endif diff -urN linux-2.3.99-pre3.orig/drivers/char/cpia.h linux-2.3.99-pre3/drivers/char/cpia.h --- linux-2.3.99-pre3.orig/drivers/char/cpia.h Wed Dec 31 16:00:00 1969 +++ linux-2.3.99-pre3/drivers/char/cpia.h Fri Mar 24 13:57:52 2000 @@ -0,0 +1,421 @@ +#ifndef cpia_h +#define cpia_h + +/* + * CPiA Parallel Port Video4Linux driver + * + * Supports CPiA based parallel port Video Camera's. + * + * (C) Copyright 1999 Bas Huisman, + * Peter Pregler, + * Scott J. Bertin, + * VLSI Vision Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#define CPIA_MAJ_VER 0 +#define CPIA_MIN_VER 7 +#define CPIA_PATCH_VER 4 + +#define CPIA_PP_MAJ_VER 0 +#define CPIA_PP_MIN_VER 7 +#define CPIA_PP_PATCH_VER 4 + +#define CPIA_MAX_FRAME_SIZE_UNALIGNED (352 * 288 * 4) /* CIF at RGB32 */ +#define CPIA_MAX_FRAME_SIZE ((CPIA_MAX_FRAME_SIZE_UNALIGNED + PAGE_SIZE - 1) & +~(PAGE_SIZE - 1)) /* align above to PAGE_SIZE */ + +#ifdef __KERNEL__ + +#include <asm/uaccess.h> +#include <linux/videodev.h> +#include <linux/smp_lock.h> + +struct cpia_camera_ops +{ + /* open sets privdata to point to structure for this camera. + * Returns negative value on error, otherwise 0. + */ + int (*open)(void *privdata); + + /* Registers callback function cb to be called with cbdata + * when an image is ready. If cb is NULL, only single image grabs + * should be used. cb should immediately call streamRead to read + * the data or data may be lost. Returns negative value on error, + * otherwise 0. + */ + int (*registerCallback)(void *privdata, void (*cb)(void *cbdata), + void *cbdata); + + /* transferCmd sends commands to the camera. command MUST point to + * an 8 byte buffer in kernel space. data can be NULL if no extra + * data is needed. The size of the data is given by the last 2 + * bytes of comand. data must also point to memory in kernel space. + * Returns negative value on error, otherwise 0. + */ + int (*transferCmd)(void *privdata, u8 *command, u8 *data); + + /* streamStart initiates stream capture mode. + * Returns negative value on error, otherwise 0. + */ + int (*streamStart)(void *privdata); + + /* streamStop terminates stream capture mode. + * Returns negative value on error, otherwise 0. + */ + int (*streamStop)(void *privdata); + + /* streamRead reads a frame from the camera. buffer points to a + * buffer large enough to hold a complete frame in kernel space. + * noblock indicates if this should be a non blocking read. + * Returns the number of bytes read, or negative value on error. + */ + int (*streamRead)(void *privdata, u8 *buffer, int noblock); + + /* close disables the device until open() is called again. + * Returns negative value on error, otherwise 0. + */ + int (*close)(void *privdata); + + /* If wait_for_stream_ready is non-zero, wait until the streamState + * is STREAM_READY before calling streamRead. + */ + int wait_for_stream_ready; +}; + +struct cpia_frame { + u8 *data; + int count; + int width; + int height; + volatile int state; +}; + +struct cam_params { + struct { + u8 firmwareVersion; + u8 firmwareRevision; + u8 vcVersion; + u8 vcRevision; + } version; + struct { + u16 vendor; + u16 product; + u16 deviceRevision; + } pnpID; + struct { + u8 vpVersion; + u8 vpRevision; + u16 cameraHeadID; + } vpVersion; + struct { + u8 systemState; + u8 grabState; + u8 streamState; + u8 fatalError; + u8 cmdError; + u8 debugFlags; + u8 vpStatus; + u8 errorCode; + } status; + struct { + u8 brightness; + u8 contrast; + u8 saturation; + } colourParams; + struct { + u8 gainMode; + u8 expMode; + u8 compMode; + u8 centreWeight; + u8 gain; + u8 fineExp; + u8 coarseExpLo; + u8 coarseExpHi; + u8 redComp; + u8 green1Comp; + u8 green2Comp; + u8 blueComp; + } exposure; + struct { + u8 balanceModeIsAuto; + u8 redGain; + u8 greenGain; + u8 blueGain; + } colourBalance; + struct { + u8 divisor; + u8 baserate; + } sensorFps; + struct { + u8 gain1; + u8 gain2; + u8 gain4; + u8 gain8; + } apcor; + struct { + u8 flickerMode; + u8 coarseJump; + u8 allowableOverExposure; + } flickerControl; + struct { + u8 gain1; + u8 gain2; + u8 gain4; + u8 gain8; + } vlOffset; + struct { + u8 mode; + u8 decimation; + } compression; + struct { + u8 frTargeting; + u8 targetFR; + u8 targetQ; + } compressionTarget; + struct { + u8 yThreshold; + u8 uvThreshold; + } yuvThreshold; + struct { + u8 hysteresis; + u8 threshMax; + u8 smallStep; + u8 largeStep; + u8 decimationHysteresis; + u8 frDiffStepThresh; + u8 qDiffStepThresh; + u8 decimationThreshMod; + } compressionParams; + struct { + u8 videoSize; /* CIF/QCIF */ + u8 subSample; + u8 yuvOrder; + } format; + struct { + u8 colStart; /* skip first 8*colStart pixels */ + u8 colEnd; /* finish at 8*colEnd pixels */ + u8 rowStart; /* skip first 4*rowStart lines */ + u8 rowEnd; /* finish at 4*rowEnd lines */ + } roi; + u8 ecpTiming; + u8 streamStartLine; +}; + +enum v4l_camstates { + CPIA_V4L_IDLE = 0, + CPIA_V4L_ERROR, + CPIA_V4L_COMMAND, + CPIA_V4L_GRABBING, + CPIA_V4L_STREAMING, + CPIA_V4L_STREAMING_PAUSED, +}; + +#define FRAME_NUM 2 /* double buffering for now */ + +struct cam_data { + struct cam_data **previous; + struct cam_data *next; + + struct semaphore busy_lock; /* guard against SMP multithreading */ + struct cpia_camera_ops *ops; /* lowlevel driver operations */ + void *lowlevel_data; /* private data for lowlevel driver */ + u8 *raw_image; /* buffer for raw image data */ + struct cpia_frame decompressed_frame; + /* buffer to hold decompressed frame */ + int image_size; /* sizeof last decompressed image */ + int open_count; /* # of process that have camera open */ + + /* camera status */ + int fps; /* actual fps reported by the camera */ + int transfer_rate; /* transfer rate from camera in kB/s */ + u8 mainsFreq; /* for flicker control */ + + /* proc interface */ + struct semaphore param_lock; /* params lock for this camera */ + struct cam_params params; /* camera settings */ + struct proc_dir_entry *proc_entry; /* /proc/cpia/videoX */ + + /* v4l */ + int video_size; /* VIDEO_SIZE_ */ + volatile enum v4l_camstates camstate; /* v4l layer status */ + struct video_device vdev; /* v4l videodev */ + struct video_picture vp; /* v4l camera settings */ + struct video_window vw; /* v4l capture area */ + + /* mmap interface */ + int curframe; /* the current frame to grab into */ + u8 *frame_buf; /* frame buffer data */ + struct cpia_frame frame[FRAME_NUM]; + /* FRAME_NUM-buffering, so we need a array */ + + int first_frame; + int mmap_kludge; /* 'wrong' byte order for mmap */ + volatile u32 cmd_queue; /* queued commands */ +}; + +/* cpia_register_camera is called by low level driver for each camera. + * A unique camera number is returned, or a negative value on error */ +struct cam_data *cpia_register_camera(struct cpia_camera_ops *ops, void *lowlevel); + +/* cpia_unregister_camera is called by low level driver when a camera + * is removed. This must not fail. */ +void cpia_unregister_camera(struct cam_data *cam); + +/* raw CIF + 64 byte header + (2 bytes line_length + EOL) per line + 4*EOI + + * one byte 16bit DMA alignment + */ +#define CPIA_MAX_IMAGE_SIZE ((352*288*2)+64+(288*3)+5) + +/* constant value's */ +#define MAGIC_0 0x19 +#define MAGIC_1 0x68 +#define DATA_IN 0xC0 +#define DATA_OUT 0x40 +#define VIDEOSIZE_QCIF 0 /* 176x144 */ +#define VIDEOSIZE_CIF 1 /* 352x288 */ +#define VIDEOSIZE_SIF 2 /* 320x240 */ +#define VIDEOSIZE_QSIF 3 /* 160x120 */ +#define VIDEOSIZE_48_48 4 /* where no one has gone before, iconsize! */ +#define VIDEOSIZE_64_48 5 +#define VIDEOSIZE_128_96 6 +#define VIDEOSIZE_160_120 VIDEOSIZE_QSIF +#define VIDEOSIZE_176_144 VIDEOSIZE_QCIF +#define VIDEOSIZE_192_144 7 +#define VIDEOSIZE_224_168 8 +#define VIDEOSIZE_256_192 9 +#define VIDEOSIZE_288_216 10 +#define VIDEOSIZE_320_240 VIDEOSIZE_SIF +#define VIDEOSIZE_352_288 VIDEOSIZE_CIF +#define VIDEOSIZE_88_72 11 /* quarter CIF */ +#define SUBSAMPLE_420 0 +#define SUBSAMPLE_422 1 +#define YUVORDER_YUYV 0 +#define YUVORDER_UYVY 1 +#define NOT_COMPRESSED 0 +#define COMPRESSED 1 +#define NO_DECIMATION 0 +#define DECIMATION_ENAB 1 +#define EOI 0xff /* End Of Image */ +#define EOL 0xfd /* End Of Line */ +#define FRAME_HEADER_SIZE 64 + +/* Image grab modes */ +#define CPIA_GRAB_SINGLE 0 +#define CPIA_GRAB_CONTINUOUS 1 + +/* Compression parameters */ +#define CPIA_COMPRESSION_NONE 0 +#define CPIA_COMPRESSION_AUTO 1 +#define CPIA_COMPRESSION_MANUAL 2 +#define CPIA_COMPRESSION_TARGET_QUALITY 0 +#define CPIA_COMPRESSION_TARGET_FRAMERATE 1 + +/* Return offsets for GetCameraState */ +#define SYSTEMSTATE 0 +#define GRABSTATE 1 +#define STREAMSTATE 2 +#define FATALERROR 3 +#define CMDERROR 4 +#define DEBUGFLAGS 5 +#define VPSTATUS 6 +#define ERRORCODE 7 + +/* SystemState */ +#define UNINITIALISED_STATE 0 +#define PASS_THROUGH_STATE 1 +#define LO_POWER_STATE 2 +#define HI_POWER_STATE 3 +#define WARM_BOOT_STATE 4 + +/* GrabState */ +#define GRAB_IDLE 0 +#define GRAB_ACTIVE 1 +#define GRAB_DONE 2 + +/* StreamState */ +#define STREAM_NOT_READY 0 +#define STREAM_READY 1 +#define STREAM_OPEN 2 +#define STREAM_PAUSED 3 +#define STREAM_FINISHED 4 + +/* Fatal Error, CmdError, and DebugFlags */ +#define CPIA_FLAG 1 +#define SYSTEM_FLAG 2 +#define INT_CTRL_FLAG 4 +#define PROCESS_FLAG 8 +#define COM_FLAG 16 +#define VP_CTRL_FLAG 32 +#define CAPTURE_FLAG 64 +#define DEBUG_FLAG 128 + +/* VPStatus */ +#define VP_STATE_OK 0x00 + +#define VP_STATE_FAILED_VIDEOINIT 0x01 +#define VP_STATE_FAILED_AECACBINIT 0x02 +#define VP_STATE_AEC_MAX 0x04 +#define VP_STATE_ACB_BMAX 0x08 + +#define VP_STATE_ACB_RMIN 0x10 +#define VP_STATE_ACB_GMIN 0x20 +#define VP_STATE_ACB_RMAX 0x40 +#define VP_STATE_ACB_GMAX 0x80 + +/* ErrorCode */ +#define ERROR_FLICKER_BELOW_MIN_EXP 0x01 /*flicker exposure got below minimum +exposure */ + +#define ALOG(lineno,fmt,args...) printk(fmt,lineno,##args) +#define LOG(fmt,args...) ALOG((__LINE__),KERN_INFO +__FILE__":"__FUNCTION__"(%d):"fmt,##args) + +#ifdef _CPIA_DEBUG_ +#define ADBG(lineno,fmt,args...) printk(fmt, jiffies, lineno, ##args) +#define DBG(fmt,args...) ADBG((__LINE__),KERN_DEBUG +__FILE__"(%ld):"__FUNCTION__"(%d):"fmt,##args) +#else +#define DBG(fmn,args...) do {} while(0) +#endif + +#define DEB_BYTE(p)\ + DBG("%1d %1d %1d %1d %1d %1d %1d %1d \n",\ + (p)&0x80?1:0, (p)&0x40?1:0, (p)&0x20?1:0, (p)&0x10?1:0,\ + (p)&0x08?1:0, (p)&0x04?1:0, (p)&0x02?1:0, (p)&0x01?1:0); + +#define ADD_TO_LIST(l, drv) \ + {\ + lock_kernel();\ + (drv)->next = l;\ + (drv)->previous = &(l);\ + (l) = drv;\ + unlock_kernel();\ + } while(0) + +#define REMOVE_FROM_LIST(drv) \ + {\ + if ((drv)->previous != NULL) {\ + lock_kernel();\ + if ((drv)->next != NULL)\ + (drv)->next->previous = (drv)->previous;\ + *((drv)->previous) = (drv)->next;\ + (drv)->previous = NULL;\ + (drv)->next = NULL;\ + unlock_kernel();\ + }\ + } while (0) + + +#endif /* __KERNEL__ */ + +#endif /* cpia_h */ diff -urN linux-2.3.99-pre3.orig/drivers/char/cpia_pp.c linux-2.3.99-pre3/drivers/char/cpia_pp.c --- linux-2.3.99-pre3.orig/drivers/char/cpia_pp.c Wed Dec 31 16:00:00 1969 +++ linux-2.3.99-pre3/drivers/char/cpia_pp.c Fri Mar 24 14:25:26 2000 @@ -0,0 +1,745 @@ +/* + * cpia_pp CPiA Parallel Port driver + * + * Supports CPiA based parallel port Video Camera's. + * + * (C) Copyright 1999 Bas Huisman <[EMAIL PROTECTED]> + * (C) Copyright 1999-2000 Scott J. Bertin <[EMAIL PROTECTED]>, + * (C) Copyright 1999-2000 Peter Pregler <[EMAIL PROTECTED]> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include <linux/config.h> +#include <linux/version.h> + +#include <linux/module.h> +#include <linux/init.h> + +#include <linux/kernel.h> +#include <linux/parport.h> +#include <linux/interrupt.h> +#include <linux/delay.h> +#include <linux/smp_lock.h> + +#ifdef CONFIG_KMOD +#include <linux/kmod.h> +#endif + +/* #define _CPIA_DEBUG_ define for verbose debug output */ +#include "cpia.h" + +static int cpia_pp_open(void *privdata); +static int cpia_pp_registerCallback(void *privdata, void (*cb) (void *cbdata), + void *cbdata); +static int cpia_pp_transferCmd(void *privdata, u8 *command, u8 *data); +static int cpia_pp_streamStart(void *privdata); +static int cpia_pp_streamStop(void *privdata); +static int cpia_pp_streamRead(void *privdata, u8 *buffer, int noblock); +static int cpia_pp_close(void *privdata); + +#define ABOUT "Parallel port driver for Vision CPiA based cameras" + +/* IEEE 1284 Compatiblity Mode signal names */ +#define nStrobe PARPORT_CONTROL_STROBE /* inverted */ +#define nAutoFd PARPORT_CONTROL_AUTOFD /* inverted */ +#define nInit PARPORT_CONTROL_INIT +#define nSelectIn PARPORT_CONTROL_SELECT +#define IntrEnable PARPORT_CONTROL_INTEN /* normally zero for no IRQ */ +#define DirBit PARPORT_CONTROL_DIRECTION /* 0 = Forward, 1 = Reverse */ + +#define nFault PARPORT_STATUS_ERROR +#define Select PARPORT_STATUS_SELECT +#define PError PARPORT_STATUS_PAPEROUT +#define nAck PARPORT_STATUS_ACK +#define Busy PARPORT_STATUS_BUSY /* inverted */ + +/* some more */ +#define HostClk nStrobe +#define HostAck nAutoFd +#define nReverseRequest nInit +#define Active_1284 nSelectIn +#define nPeriphRequest nFault +#define XFlag Select +#define nAckReverse PError +#define PeriphClk nAck +#define PeriphAck Busy + +/* these can be used to correct for the inversion on some bits */ +#define STATUS_INVERSION_MASK (Busy) +#define CONTROL_INVERSION_MASK (nStrobe|nAutoFd|nSelectIn) + +#define ECR_empty 0x01 +#define ECR_full 0x02 +#define ECR_serviceIntr 0x04 +#define ECR_dmaEn 0x08 +#define ECR_nErrIntrEn 0x10 + +#define ECR_mode_mask 0xE0 +#define ECR_SPP_mode 0x00 +#define ECR_PS2_mode 0x20 +#define ECR_FIFO_mode 0x40 +#define ECR_ECP_mode 0x60 + +#define ECP_FIFO_SIZE 16 +#define DMA_BUFFER_SIZE PAGE_SIZE + /* for 16bit DMA make sure DMA_BUFFER_SIZE is 16 bit aligned */ +#define PARPORT_CHUNK_SIZE PAGE_SIZE/* >=2.3.x */ + /* we read this many bytes at once */ + +#define GetECRMasked(port,mask) (parport_read_econtrol(port) & (mask)) +#define GetStatus(port) +((parport_read_status(port)^STATUS_INVERSION_MASK)&(0xf8)) +#define SetStatus(port,val) parport_write_status(port,(val)^STATUS_INVERSION_MASK) +#define GetControl(port) +((parport_read_control(port)^CONTROL_INVERSION_MASK)&(0x3f)) +#define SetControl(port,val) +parport_write_control(port,(val)^CONTROL_INVERSION_MASK) + +#define GetStatusMasked(port,mask) (GetStatus(port) & (mask)) +#define GetControlMasked(port,mask) (GetControl(port) & (mask)) +#define SetControlMasked(port,mask) SetControl(port,GetControl(port) | (mask)); +#define ClearControlMasked(port,mask) SetControl(port,GetControl(port)&~(mask)); +#define FrobControlBit(port,mask,value) +SetControl(port,(GetControl(port)&~(mask))|((value)&(mask))); + +#define PACKET_LENGTH 8 + +/* Magic numbers for defining port-device mappings */ +#define PPCPIA_PARPORT_UNSPEC -4 +#define PPCPIA_PARPORT_AUTO -3 +#define PPCPIA_PARPORT_OFF -2 +#define PPCPIA_PARPORT_NONE -1 + +#ifdef MODULE +static int parport_nr[PARPORT_MAX] = {[0 ... PARPORT_MAX - 1] = +PPCPIA_PARPORT_UNSPEC}; +static char *parport[PARPORT_MAX] = {NULL,}; + +MODULE_AUTHOR("B. Huisman <[EMAIL PROTECTED]> & Peter Pregler +<[EMAIL PROTECTED]>"); +MODULE_DESCRIPTION("Parallel port driver for Vision CPiA based cameras"); +MODULE_PARM(parport, "1-" __MODULE_STRING(PARPORT_MAX) "s"); +MODULE_PARM_DESC(parport, "'auto' or a list of parallel port numbers. Just like lp."); +#else +static int parport_nr[PARPORT_MAX] __initdata = + {[0 ... PARPORT_MAX - 1] = PPCPIA_PARPORT_UNSPEC}; +static int parport_ptr = 0; +#endif + +struct pp_cam_entry { + struct pardevice *pdev; + struct parport *port; + struct tq_struct cb_task; + int open_count; + wait_queue_head_t wq_stream; + /* image state flags */ + int image_ready; /* we got an interrupt */ + int image_complete; /* we have seen 4 EOI */ + + int streaming; /* we are in streaming mode */ + int stream_irq; +}; + +static struct cpia_camera_ops cpia_pp_ops = +{ + cpia_pp_open, + cpia_pp_registerCallback, + cpia_pp_transferCmd, + cpia_pp_streamStart, + cpia_pp_streamStop, + cpia_pp_streamRead, + cpia_pp_close, + 1 +}; + +static struct cam_data *cam_list; + +#ifdef _CPIA_DEBUG_ +#define DEB_PORT(port) { \ +u8 controll = GetControl(port); \ +u8 statusss = GetStatus(port); \ +DBG("nsel %c per %c naut %c nstrob %c nak %c busy %c nfaul %c sel %c init %c dir +%c\n",\ +((controll & nSelectIn) ? 'U' : 'D'), \ +((statusss & PError) ? 'U' : 'D'), \ +((controll & nAutoFd) ? 'U' : 'D'), \ +((controll & nStrobe) ? 'U' : 'D'), \ +((statusss & nAck) ? 'U' : 'D'), \ +((statusss & Busy) ? 'U' : 'D'), \ +((statusss & nFault) ? 'U' : 'D'), \ +((statusss & Select) ? 'U' : 'D'), \ +((controll & nInit) ? 'U' : 'D'), \ +((controll & DirBit) ? 'R' : 'F') \ +); } +#else +#define DEB_PORT(port) {} +#endif + +#define WHILE_OUT_TIMEOUT (HZ/10) +#define DMA_TIMEOUT 10*HZ + +/* FIXME */ +static void cpia_parport_enable_irq( struct parport *port ) { + parport_enable_irq(port); + mdelay(10); + return; +} + +static void cpia_parport_disable_irq( struct parport *port ) { + parport_disable_irq(port); + mdelay(10); + return; +} + +/**************************************************************************** + * + * EndTransferMode + * + ***************************************************************************/ +static void EndTransferMode(struct pp_cam_entry *cam) +{ + parport_negotiate(cam->port, IEEE1284_MODE_COMPAT); +} + +/**************************************************************************** + * + * ForwardSetup + * + ***************************************************************************/ +static int ForwardSetup(struct pp_cam_entry *cam) +{ + int retry; + + /* After some commands the camera needs extra time before + * it will respond again, so we try up to 3 times */ + for(retry=0; retry<3; ++retry) { + if(!parport_negotiate(cam->port, IEEE1284_MODE_ECP)) { + break; + } + } + if(retry == 3) { + DBG("Unable to negotiate ECP mode\n"); + return -1; + } + return 0; +} + +/**************************************************************************** + * + * ReverseSetup + * + ***************************************************************************/ +static int ReverseSetup(struct pp_cam_entry *cam, int extensibility) +{ + int retry; + int mode = IEEE1284_MODE_ECP; + if(extensibility) mode = 8|3|IEEE1284_EXT_LINK; + + /* After some commands the camera needs extra time before + * it will respond again, so we try up to 3 times */ + for(retry=0; retry<3; ++retry) { + if(!parport_negotiate(cam->port, mode)) { + break; + } + } + if(retry == 3) { + if(extensibility) + DBG("Unable to negotiate extensibility mode\n"); + else + DBG("Unable to negotiate ECP mode\n"); + return -1; + } + if(extensibility) cam->port->ieee1284.mode = IEEE1284_MODE_ECP; + return 0; +} + +/**************************************************************************** + * + * WritePacket + * + ***************************************************************************/ +static int WritePacket(struct pp_cam_entry *cam, const u8 *packet, size_t size) +{ + int retval=0; + int size_written; + + if (packet == NULL) { + return -EINVAL; + } + if (ForwardSetup(cam)) { + DBG("Write failed in setup\n"); + return -EIO; + } + size_written = parport_write(cam->port, packet, size); + if(size_written != size) { + DBG("Write failed, wrote %d/%d\n", size_written, size); + retval = -EIO; + } + EndTransferMode(cam); + return retval; +} + +/**************************************************************************** + * + * ReadPacket + * + ***************************************************************************/ +static int ReadPacket(struct pp_cam_entry *cam, u8 *packet, size_t size) +{ + int retval=0; + if (packet == NULL) { + return -EINVAL; + } + if (ReverseSetup(cam, 0)) { + return -EIO; + } + if(parport_read(cam->port, packet, size) != size) { + retval = -EIO; + } + EndTransferMode(cam); + return retval; +} + +/**************************************************************************** + * + * cpia_pp_streamStart + * + ***************************************************************************/ +static int cpia_pp_streamStart(void *privdata) +{ + struct pp_cam_entry *cam = privdata; + DBG("\n"); + cam->streaming=1; + cam->image_ready=0; + //if (ReverseSetup(cam,1)) return -EIO; + if(cam->stream_irq) cpia_parport_enable_irq(cam->port); + return 0; +} + +/**************************************************************************** + * + * cpia_pp_streamStop + * + ***************************************************************************/ +static int cpia_pp_streamStop(void *privdata) +{ + struct pp_cam_entry *cam = privdata; + + DBG("\n"); + cam->streaming=0; + cpia_parport_disable_irq(cam->port); + //EndTransferMode(cam); + + return 0; +} + +static int cpia_pp_read(struct parport *port, u8 *buffer, int len) +{ + int bytes_read, new_bytes; + for(bytes_read=0; bytes_read<len; bytes_read += new_bytes) { + new_bytes = parport_read(port, buffer+bytes_read, + len-bytes_read); + if(new_bytes < 0) break; + } + return bytes_read; +} + +/**************************************************************************** + * + * cpia_pp_streamRead + * + ***************************************************************************/ +static int cpia_pp_streamRead(void *privdata, u8 *buffer, int noblock) +{ + struct pp_cam_entry *cam = privdata; + int read_bytes = 0; + int i, endseen, block_size, new_bytes; + + if(cam == NULL) { + DBG("Internal driver error: cam is NULL\n"); + return -EINVAL; + } + if(buffer == NULL) { + DBG("Internal driver error: buffer is NULL\n"); + return -EINVAL; + } + //if(cam->streaming) DBG("%d / %d\n", cam->image_ready, noblock); + if( cam->stream_irq ) { + DBG("%d\n", cam->image_ready); + cam->image_ready--; + } + cam->image_complete=0; + if (0/*cam->streaming*/) { + if(!cam->image_ready) { + if(noblock) return -EWOULDBLOCK; + interruptible_sleep_on(&cam->wq_stream); + if( signal_pending(current) ) return -EINTR; + DBG("%d\n", cam->image_ready); + } + } else { + if (ReverseSetup(cam, 1)) { + DBG("unable to ReverseSetup\n"); + return -EIO; + } + } + endseen = 0; + block_size = PARPORT_CHUNK_SIZE; + while( !cam->image_complete ) { + if(current->need_resched) schedule(); + + new_bytes = cpia_pp_read(cam->port, buffer, block_size ); + if( new_bytes <= 0 ) { + break; + } + i=-1; + while(++i<new_bytes && endseen<4) { + if(*buffer==EOI) { + endseen++; + } else { + endseen=0; + } + buffer++; + } + read_bytes += i; + if( endseen==4 ) { + cam->image_complete=1; + break; + } + if( CPIA_MAX_IMAGE_SIZE-read_bytes <= PARPORT_CHUNK_SIZE ) { + block_size=CPIA_MAX_IMAGE_SIZE-read_bytes; + } + } + EndTransferMode(cam); + return cam->image_complete ? read_bytes : -EIO; +} + +/**************************************************************************** + * + * cpia_pp_transferCmd + * + ***************************************************************************/ +static int cpia_pp_transferCmd(void *privdata, u8 *command, u8 *data) +{ + int err; + int retval=0; + int databytes; + struct pp_cam_entry *cam = privdata; + + if(cam == NULL) { + DBG("Internal driver error: cam is NULL\n"); + return -EINVAL; + } + if(command == NULL) { + DBG("Internal driver error: command is NULL\n"); + return -EINVAL; + } + databytes = (((int)command[7])<<8) | command[6]; + if ((err = WritePacket(cam, command, PACKET_LENGTH)) < 0) { + DBG("Error writing command\n"); + return err; + } + if(command[0] == DATA_IN) { + u8 buffer[8]; + if(data == NULL) { + DBG("Internal driver error: data is NULL\n"); + return -EINVAL; + } + if((err = ReadPacket(cam, buffer, 8)) < 0) { + return err; + DBG("Error reading command result\n"); + } + memcpy(data, buffer, databytes); + } else if(command[0] == DATA_OUT) { + if(databytes > 0) { + if(data == NULL) { + DBG("Internal driver error: data is NULL\n"); + retval = -EINVAL; + } else { + if((err=WritePacket(cam, data, databytes)) < 0){ + DBG("Error writing command data\n"); + return err; + } + } + } + } else { + DBG("Unexpected first byte of command: %x\n", command[0]); + retval = -EINVAL; + } + return retval; +} + +/**************************************************************************** + * + * cpia_pp_open + * + ***************************************************************************/ +static int cpia_pp_open(void *privdata) +{ + struct pp_cam_entry *cam = (struct pp_cam_entry *)privdata; + + if (cam == NULL) + return -EINVAL; + + if(cam->open_count == 0) { + if (parport_claim(cam->pdev)) { + DBG("failed to claim the port\n"); + return -EBUSY; + } + parport_negotiate(cam->port, IEEE1284_MODE_COMPAT); + parport_data_forward(cam->port); + parport_write_control(cam->port, PARPORT_CONTROL_SELECT); + udelay(50); + parport_write_control(cam->port, + PARPORT_CONTROL_SELECT + | PARPORT_CONTROL_INIT); + } + + ++cam->open_count; + +#ifdef MODULE + MOD_INC_USE_COUNT; +#endif + return 0; +} + +/**************************************************************************** + * + * cpia_pp_registerCallback + * + ***************************************************************************/ +static int cpia_pp_registerCallback(void *privdata, void (*cb)(void *cbdata), void +*cbdata) +{ + struct pp_cam_entry *cam = privdata; + int retval = 0; + + if(cam->port->irq != PARPORT_IRQ_NONE) { + cam->cb_task.routine = cb; + cam->cb_task.data = cbdata; + } else { + retval = -1; + } + return retval; +} + +/**************************************************************************** + * + * cpia_pp_close + * + ***************************************************************************/ +static int cpia_pp_close(void *privdata) +{ + struct pp_cam_entry *cam = privdata; +#ifdef MODULE + MOD_DEC_USE_COUNT; +#endif + if (--cam->open_count == 0) { + parport_release(cam->pdev); + } + return 0; +} + +/**************************************************************************** + * + * cpia_pp_register + * + ***************************************************************************/ +static int cpia_pp_register(struct parport *port) +{ + struct pardevice *pdev = NULL; + struct pp_cam_entry *cam; + struct cam_data *cpia; + + if (!(port->modes & PARPORT_MODE_ECP) && + !(port->modes & PARPORT_MODE_TRISTATE)) { + LOG("port is not ECP capable\n"); + return -ENXIO; + } + + cam = kmalloc(sizeof(struct pp_cam_entry), GFP_KERNEL); + if (cam == NULL) { + LOG("failed to allocate camera structure\n"); + return -ENOMEM; + } + memset(cam,0,sizeof(struct pp_cam_entry)); + + pdev = parport_register_device(port, "cpia_pp", NULL, NULL, + NULL, 0, cam); + + if (!pdev) { + LOG("failed to parport_register_device\n"); + kfree(cam); + return -ENXIO; + } + + cam->pdev = pdev; + cam->port = port; + init_waitqueue_head(&cam->wq_stream); + + cam->streaming = 0; + cam->stream_irq = 0; + + if((cpia = cpia_register_camera(&cpia_pp_ops, cam)) == NULL) { + LOG("failed to cpia_register_camera\n"); + parport_unregister_device(pdev); + kfree(cam); + return -ENXIO; + } + ADD_TO_LIST(cam_list, cpia); + + return 0; +} + +static void cpia_pp_detach (struct parport *port) +{ + struct cam_data *cpia; + + for(cpia = cam_list; cpia != NULL; cpia = cpia->next) { + struct pp_cam_entry *cam = cpia->lowlevel_data; + if (cam && cam->port->number == port->number) { + REMOVE_FROM_LIST(cpia); + + cpia_unregister_camera(cpia); + + if(cam->open_count > 0) { + cpia_pp_close(cam); + } + + parport_unregister_device(cam->pdev); + + kfree(cam); + cpia->lowlevel_data = NULL; + break; + } + } +} + +static void cpia_pp_attach (struct parport *port) +{ + unsigned int i; + + switch (parport_nr[0]) + { + case PPCPIA_PARPORT_UNSPEC: + case PPCPIA_PARPORT_AUTO: + if (port->probe_info[0].class != PARPORT_CLASS_MEDIA || + port->probe_info[0].cmdset == NULL || + strncmp(port->probe_info[0].cmdset, "CPIA_1", 6) != 0) + return; + + cpia_pp_register(port); + + break; + + default: + for (i = 0; i < PARPORT_MAX; ++i) { + if (port->number == parport_nr[i]) { + cpia_pp_register(port); + break; + } + } + break; + } +} + +static struct parport_driver cpia_pp_driver = { + "cpia_pp", + cpia_pp_attach, + cpia_pp_detach, + NULL +}; + +int cpia_pp_init(void) +{ + printk(KERN_INFO "%s v%d.%d.%d\n",ABOUT, + CPIA_PP_MAJ_VER,CPIA_PP_MIN_VER,CPIA_PP_PATCH_VER); + + if(parport_nr[0] == PPCPIA_PARPORT_OFF) { + printk(" disabled\n"); + return 0; + } + + if (parport_register_driver (&cpia_pp_driver)) { + LOG ("unable to register with parport\n"); + return -EIO; + } + + return 0; +} + +#ifdef MODULE +int init_module(void) +{ + if (parport[0]) { + /* The user gave some parameters. Let's see what they were. */ + if (!strncmp(parport[0], "auto", 4)) { + parport_nr[0] = PPCPIA_PARPORT_AUTO; + } else { + int n; + for (n = 0; n < PARPORT_MAX && parport[n]; n++) { + if (!strncmp(parport[n], "none", 4)) { + parport_nr[n] = PPCPIA_PARPORT_NONE; + } else { + char *ep; + unsigned long r = simple_strtoul(parport[n], +&ep, 0); + if (ep != parport[n]) { + parport_nr[n] = r; + } else { + LOG("bad port specifier `%s'\n", +parport[n]); + return -ENODEV; + } + } + } + } + } +#if defined(CONFIG_KMOD) && defined(CONFIG_PNP_PARPORT_MODULE) + if(parport_enumerate() && !parport_enumerate()->probe_info.model) { + request_module("parport_probe"); + } +#endif + return cpia_pp_init(); +} + +void cleanup_module(void) +{ + parport_unregister_driver (&cpia_pp_driver); + return; +} + +#else /* !MODULE */ + +static int __init cpia_pp_setup(char *str) +{ +#if 0 + /* Is this only a 2.2ism? -jerdfelt */ + if (!str) { + if (ints[0] == 0 || ints[1] == 0) { + /* disable driver on "cpia_pp=" or "cpia_pp=0" */ + parport_nr[0] = PPCPIA_PARPORT_OFF; + } + } else +#endif + if (!strncmp(str, "parport", 7)) { + int n = simple_strtoul(str + 7, NULL, 10); + if (parport_ptr < PARPORT_MAX) { + parport_nr[parport_ptr++] = n; + } else { + LOG("too many ports, %s ignored.\n", str); + } + } else if (!strcmp(str, "auto")) { + parport_nr[0] = PPCPIA_PARPORT_AUTO; + } else if (!strcmp(str, "none")) { + parport_nr[parport_ptr++] = PPCPIA_PARPORT_NONE; + } + + return 0; +} + +__setup("cpia_pp=", cpia_pp_setup); + +#endif /* !MODULE */ diff -urN linux-2.3.99-pre3.orig/drivers/char/cpia_usb.c linux-2.3.99-pre3/drivers/char/cpia_usb.c --- linux-2.3.99-pre3.orig/drivers/char/cpia_usb.c Wed Dec 31 16:00:00 1969 +++ linux-2.3.99-pre3/drivers/char/cpia_usb.c Fri Mar 24 14:25:47 2000 @@ -0,0 +1,627 @@ +/* + * cpia_usb CPiA USB driver + * + * Supports CPiA based parallel port Video Camera's. + * + * Copyright (C) 1999 Jochen Scharrlach <[EMAIL PROTECTED]> + * Copyright (C) 1999, 2000 Johannes Erdfelt <[EMAIL PROTECTED]> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include <linux/config.h> +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/wait.h> +#include <linux/sched.h> +#include <linux/list.h> +#include <linux/slab.h> +#include <linux/vmalloc.h> +#include <linux/usb.h> + +#include "cpia.h" + +#define USB_REQ_CPIA_GRAB_FRAME 0xC1 +#define USB_REQ_CPIA_UPLOAD_FRAME 0xC2 +#define WAIT_FOR_NEXT_FRAME 0 +#define FORCE_FRAME_UPLOAD 1 + +#define FRAMES_PER_DESC 10 +#define FRAME_SIZE_PER_DESC 960 /* Shouldn't be hardcoded */ +#define CPIA_NUMSBUF 2 +#define STREAM_BUF_SIZE (PAGE_SIZE * 4) +#define SCRATCH_BUF_SIZE (STREAM_BUF_SIZE * 2) + +struct cpia_sbuf { + char *data; + urb_t *urb; +}; + +#define FRAMEBUF_LEN (CPIA_MAX_FRAME_SIZE+100) +enum framebuf_status { + FRAME_EMPTY, + FRAME_READING, + FRAME_READY, + FRAME_ERROR, +}; + +struct framebuf { + int length; + enum framebuf_status status; + u8 data[FRAMEBUF_LEN]; + struct framebuf *next; +}; + +struct usb_cpia { + /* Device structure */ + struct usb_device *dev; + + unsigned char iface; + wait_queue_head_t wq_stream; + + int cursbuf; /* Current receiving sbuf */ + struct cpia_sbuf sbuf[CPIA_NUMSBUF]; /* Double buffering */ + + int streaming; + int open; + int present; + struct framebuf *buffers[3]; + struct framebuf *curbuff, *workbuff; +}; + +static int cpia_usb_open(void *privdata); +static int cpia_usb_registerCallback(void *privdata, void (*cb) (void *cbdata), + void *cbdata); +static int cpia_usb_transferCmd(void *privdata, u8 *command, u8 *data); +static int cpia_usb_streamStart(void *privdata); +static int cpia_usb_streamStop(void *privdata); +static int cpia_usb_streamRead(void *privdata, u8 *frame, int noblock); +static int cpia_usb_close(void *privdata); + +#define ABOUT "USB driver for Vision CPiA based cameras" + +static struct cpia_camera_ops cpia_usb_ops = { + cpia_usb_open, + cpia_usb_registerCallback, + cpia_usb_transferCmd, + cpia_usb_streamStart, + cpia_usb_streamStop, + cpia_usb_streamRead, + cpia_usb_close, + 0 +}; + +static struct cam_data *cam_list; + +static void cpia_usb_complete(struct urb *urb) +{ + int i; + char *cdata; + struct usb_cpia *ucpia; + + if (!urb || !urb->context) + return; + + ucpia = (struct usb_cpia *) urb->context; + + if (!ucpia->dev || !ucpia->streaming || !ucpia->present || !ucpia->open) + return; + + if (ucpia->workbuff->status == FRAME_EMPTY) { + ucpia->workbuff->status = FRAME_READING; + ucpia->workbuff->length = 0; + } + + for (i = 0; i < urb->number_of_packets; i++) { + int n = urb->iso_frame_desc[i].actual_length; + int st = urb->iso_frame_desc[i].status; + + cdata = urb->transfer_buffer + urb->iso_frame_desc[i].offset; + + if (st) + printk(KERN_DEBUG "cpia data error: [%d] len=%d, status=%X\n", +i, n, st); + + if (FRAMEBUF_LEN < ucpia->workbuff->length + n) { + printk(KERN_DEBUG "cpia: scratch buf overflow!scr_len: %d, n: +%d\n", ucpia->workbuff->length, n); + return; + } + + if (n) { + if ((ucpia->workbuff->length > 0) || + (0x19 == cdata[0] && 0x68 == cdata[1])) { + memcpy(ucpia->workbuff->data + +ucpia->workbuff->length, cdata, n); + ucpia->workbuff->length += n; + } else + DBG("Ignoring packet!\n"); + } else { + if (ucpia->workbuff->length > 4 && + 0xff == ucpia->workbuff->data[ucpia->workbuff->length-1] && + 0xff == ucpia->workbuff->data[ucpia->workbuff->length-2] && + 0xff == ucpia->workbuff->data[ucpia->workbuff->length-3] && + 0xff == ucpia->workbuff->data[ucpia->workbuff->length-4]) { + ucpia->workbuff->status = FRAME_READY; + ucpia->curbuff = ucpia->workbuff; + ucpia->workbuff = ucpia->workbuff->next; + ucpia->workbuff->status = FRAME_EMPTY; + ucpia->workbuff->length = 0; + + if (waitqueue_active(&ucpia->wq_stream)) + wake_up_interruptible(&ucpia->wq_stream); + } + } + } +} + +static int cpia_usb_open(void *privdata) +{ + struct usb_cpia *ucpia = (struct usb_cpia *) privdata; + urb_t *urb; + int ret, retval = 0, fx, err; + + if (!ucpia) + return -EINVAL; + + ucpia->sbuf[0].data = kmalloc(FRAMES_PER_DESC * FRAME_SIZE_PER_DESC, +GFP_KERNEL); + if (!ucpia->sbuf[0].data) + return -EINVAL; + + ucpia->sbuf[1].data = kmalloc(FRAMES_PER_DESC * FRAME_SIZE_PER_DESC, +GFP_KERNEL); + if (!ucpia->sbuf[1].data) { + retval = -EINVAL; + goto error_0; + } + + ret = usb_set_interface(ucpia->dev, ucpia->iface, 3); + if (ret < 0) { + printk(KERN_ERR "cpia_usb_open: usb_set_interface error (ret = %d)\n", +ret); + retval = -EBUSY; + goto error_all; + } + + ucpia->buffers[0]->status = FRAME_EMPTY; + ucpia->buffers[0]->length = 0; + ucpia->buffers[1]->status = FRAME_EMPTY; + ucpia->buffers[1]->length = 0; + ucpia->buffers[2]->status = FRAME_EMPTY; + ucpia->buffers[2]->length = 0; + ucpia->curbuff = ucpia->buffers[0]; + ucpia->workbuff = ucpia->buffers[1]; + + /* We double buffer the Iso lists */ + urb = usb_alloc_urb(FRAMES_PER_DESC); + if (!urb) { + printk(KERN_ERR "cpia_init_isoc: usb_alloc_urb 0\n"); + retval = -ENOMEM; + goto error_all; + } + + ucpia->sbuf[0].urb = urb; + urb->dev = ucpia->dev; + urb->context = ucpia; + urb->pipe = usb_rcvisocpipe(ucpia->dev, 1); + urb->transfer_flags = USB_ISO_ASAP; + urb->transfer_buffer = ucpia->sbuf[0].data; + urb->complete = cpia_usb_complete; + urb->number_of_packets = FRAMES_PER_DESC; + urb->transfer_buffer_length = FRAME_SIZE_PER_DESC * FRAMES_PER_DESC; + for (fx = 0; fx < FRAMES_PER_DESC; fx++) { + urb->iso_frame_desc[fx].offset = FRAME_SIZE_PER_DESC * fx; + urb->iso_frame_desc[fx].length = FRAME_SIZE_PER_DESC; + } + + urb = usb_alloc_urb(FRAMES_PER_DESC); + if (!urb) { + printk(KERN_ERR "cpia_init_isoc: usb_alloc_urb 0\n"); + retval = -ENOMEM; + goto error_all; + } + + ucpia->sbuf[1].urb = urb; + urb->dev = ucpia->dev; + urb->context = ucpia; + urb->pipe = usb_rcvisocpipe(ucpia->dev, 1); + urb->transfer_flags = USB_ISO_ASAP; + urb->transfer_buffer = ucpia->sbuf[1].data; + urb->complete = cpia_usb_complete; + urb->number_of_packets = FRAMES_PER_DESC; + urb->transfer_buffer_length = FRAME_SIZE_PER_DESC * FRAMES_PER_DESC; + for (fx = 0; fx < FRAMES_PER_DESC; fx++) { + urb->iso_frame_desc[fx].offset = FRAME_SIZE_PER_DESC * fx; + urb->iso_frame_desc[fx].length = FRAME_SIZE_PER_DESC; + } + + ucpia->sbuf[1].urb->next = ucpia->sbuf[0].urb; + ucpia->sbuf[0].urb->next = ucpia->sbuf[1].urb; + + err = usb_submit_urb(ucpia->sbuf[0].urb); + if (err) + printk(KERN_ERR "cpia_init_isoc: usb_submit_urb 0 ret %d\n", + err); + err = usb_submit_urb(ucpia->sbuf[1].urb); + if (err) + printk(KERN_ERR "cpia_init_isoc: usb_submit_urb 1 ret %d\n", + err); + + ucpia->streaming = 1; + ucpia->open = 1; + + return 0; + +error_all: + kfree (ucpia->sbuf[1].data); +error_0: + kfree (ucpia->sbuf[0].data); + + return retval; +} + +// +// convenience functions +// + +/**************************************************************************** + * + * WritePacket + * + ***************************************************************************/ +static int WritePacket(struct usb_device *udev, const u8 *packet, u8 *buf, size_t +size) +{ + if (!packet) + return -EINVAL; + + return usb_control_msg(udev, usb_sndctrlpipe(udev, 0), + packet[1] + (packet[0] << 8), + USB_TYPE_VENDOR | USB_RECIP_DEVICE, + packet[2] + (packet[3] << 8), + packet[4] + (packet[5] << 8), buf, size, HZ); +} + +/**************************************************************************** + * + * ReadPacket + * + ***************************************************************************/ +static int ReadPacket(struct usb_device *udev, u8 *packet, u8 *buf, size_t size) +{ + if (!packet || size <= 0) + return -EINVAL; + + return usb_control_msg(udev, usb_rcvctrlpipe(udev, 0), + packet[1] + (packet[0] << 8), + USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE, + packet[2] + (packet[3] << 8), + packet[4] + (packet[5] << 8), buf, size, HZ); +} + +static int cpia_usb_transferCmd(void *privdata, u8 *command, u8 *data) +{ + int err = 0; + int databytes; + struct usb_cpia *ucpia = (struct usb_cpia *)privdata; + struct usb_device *udev = ucpia->dev; + + if (!udev) { + DBG("Internal driver error: udev is NULL\n"); + return -EINVAL; + } + + if (!command) { + DBG("Internal driver error: command is NULL\n"); + return -EINVAL; + } + + databytes = (((int)command[7])<<8) | command[6]; + + if (command[0] == DATA_IN) { + u8 buffer[8]; + + if (!data) { + DBG("Internal driver error: data is NULL\n"); + return -EINVAL; + } + + err = ReadPacket(udev, command, buffer, 8); + if (err < 0) + return err; + + memcpy(data, buffer, databytes); + } else if(command[0] == DATA_OUT) + WritePacket(udev, command, data, databytes); + else { + DBG("Unexpected first byte of command: %x\n", command[0]); + err = -EINVAL; + } + + return 0; +} + +static int cpia_usb_registerCallback(void *privdata, void (*cb) (void *cbdata), + void *cbdata) +{ + return -ENODEV; +} + +static int cpia_usb_streamStart(void *privdata) +{ + return -ENODEV; +} + +static int cpia_usb_streamStop(void *privdata) +{ + return -ENODEV; +} + +static int cpia_usb_streamRead(void *privdata, u8 *frame, int noblock) +{ + struct usb_cpia *ucpia = (struct usb_cpia *) privdata; + struct framebuf *mybuff; + + if (!ucpia || !ucpia->present) + return -1; + + if (ucpia->curbuff->status != FRAME_READY) + interruptible_sleep_on(&ucpia->wq_stream); + else + DBG("Frame already waiting!\n"); + + mybuff = ucpia->curbuff; + + if (!mybuff) + return -1; + + if (mybuff->status != FRAME_READY || mybuff->length < 4) { + DBG("Something went wrong!\n"); + return -1; + } + + memcpy(frame, mybuff->data, mybuff->length); + mybuff->status = FRAME_EMPTY; + +/* DBG("read done, %d bytes, Header: %x/%x, Footer: %x%x%x%x\n", */ +/* mybuff->length, frame[0], frame[1], */ +/* frame[mybuff->length-4], frame[mybuff->length-3], */ +/* frame[mybuff->length-2], frame[mybuff->length-1]); */ + + return mybuff->length; +} + +static void cpia_usb_free_resources(struct usb_cpia *ucpia, int try) +{ + if (!ucpia->streaming) + return; + + ucpia->streaming = 0; + + /* Set packet size to 0 */ + if (try) { + int ret; + + ret = usb_set_interface(ucpia->dev, ucpia->iface, 0); + if (ret < 0) { + printk(KERN_ERR "usb_set_interface error (ret = %d)\n", ret); + return; + } + } + + /* Unschedule all of the iso td's */ + if (ucpia->sbuf[1].urb) { + usb_unlink_urb(ucpia->sbuf[1].urb); + usb_free_urb(ucpia->sbuf[1].urb); + ucpia->sbuf[1].urb = NULL; + } + + if (ucpia->sbuf[0].urb) { + usb_unlink_urb(ucpia->sbuf[0].urb); + usb_free_urb(ucpia->sbuf[0].urb); + ucpia->sbuf[0].urb = NULL; + } +} + +static int cpia_usb_close(void *privdata) +{ + struct usb_cpia *ucpia = (struct usb_cpia *) privdata; + + ucpia->open = 0; + + cpia_usb_free_resources(ucpia, 1); + + if (!ucpia->present) + kfree(ucpia); + + return 0; +} + +int cpia_usb_init(void) +{ + /* return -ENODEV; */ + return 0; +} + +/* Probing and initializing */ + +static void *cpia_probe(struct usb_device *udev, unsigned int ifnum) +{ + struct usb_interface_descriptor *interface; + struct usb_cpia *ucpia; + struct cam_data *cam; + int ret; + + /* A multi-config CPiA camera? */ + if (udev->descriptor.bNumConfigurations != 1) + return NULL; + + interface = &udev->actconfig->interface[ifnum].altsetting[0]; + + /* Is it a CPiA? */ + if (udev->descriptor.idVendor != 0x0553) + return NULL; + if (udev->descriptor.idProduct != 0x0002) + return NULL; + + /* We found a CPiA */ + printk(KERN_INFO "USB CPiA camera found\n"); + + ucpia = kmalloc(sizeof(*ucpia), GFP_KERNEL); + if (!ucpia) { + printk(KERN_ERR "couldn't kmalloc cpia struct\n"); + return NULL; + } + + memset(ucpia, 0, sizeof(*ucpia)); + + ucpia->dev = udev; + ucpia->iface = interface->bInterfaceNumber; + init_waitqueue_head(&ucpia->wq_stream); + + ucpia->buffers[0] = vmalloc(sizeof(*ucpia->buffers[0])); + if (!ucpia->buffers[0]) { + printk(KERN_ERR "couldn't vmalloc frame buffer 0\n"); + goto fail_alloc_0; + } + + ucpia->buffers[1] = vmalloc(sizeof(*ucpia->buffers[1])); + if (!ucpia->buffers[1]) { + printk(KERN_ERR "couldn't vmalloc frame buffer 1\n"); + goto fail_alloc_1; + } + + ucpia->buffers[2] = vmalloc(sizeof(*ucpia->buffers[2])); + if (!ucpia->buffers[2]) { + printk(KERN_ERR "couldn't vmalloc frame buffer 2\n"); + goto fail_alloc_2; + } + + ucpia->buffers[0]->next = ucpia->buffers[1]; + ucpia->buffers[1]->next = ucpia->buffers[2]; + ucpia->buffers[2]->next = ucpia->buffers[0]; + + ret = usb_set_interface(udev, ucpia->iface, 0); + if (ret < 0) { + printk(KERN_ERR "cpia_probe: usb_set_interface error (ret = %d)\n", +ret); + /* goto fail_all; */ + } + + /* Before register_camera, important */ + ucpia->present = 1; + + cam = cpia_register_camera(&cpia_usb_ops, ucpia); + if (!cam) { + LOG("failed to cpia_register_camera\n"); + goto fail_all; + } + + ADD_TO_LIST(cam_list, cam); + + return cam; + +fail_all: + vfree(ucpia->buffers[2]); + ucpia->buffers[2] = NULL; +fail_alloc_2: + vfree(ucpia->buffers[1]); + ucpia->buffers[1] = NULL; +fail_alloc_1: + vfree(ucpia->buffers[0]); + ucpia->buffers[0] = NULL; +fail_alloc_0: + + return NULL; +} + +static void cpia_disconnect(struct usb_device *dev, void *ptr); + +static struct usb_driver cpia_driver = { + "cpia", + cpia_probe, + cpia_disconnect, + { NULL, NULL } +}; + +/* don't use dev, it may be NULL! (see usb_cpia_cleanup) */ +/* _disconnect from usb_cpia_cleanup is not necessary since usb_deregister */ +/* will do it for us as well as passing a udev structure - jerdfelt */ +static void cpia_disconnect(struct usb_device *udev, void *ptr) +{ + struct cam_data *cam = (struct cam_data *) ptr; + struct usb_cpia *ucpia = (struct usb_cpia *) cam->lowlevel_data; + + REMOVE_FROM_LIST(cam); + + /* Don't even try to reset the altsetting if we're disconnected */ + cpia_usb_free_resources(ucpia, 0); + + ucpia->present = 0; + + cpia_unregister_camera(cam); + + ucpia->curbuff->status = FRAME_ERROR; + + if (waitqueue_active(&ucpia->wq_stream)) + wake_up_interruptible(&ucpia->wq_stream); + + usb_driver_release_interface(&cpia_driver, + &udev->actconfig->interface[0]); + + ucpia->curbuff = ucpia->workbuff = NULL; + + if (ucpia->buffers[2]) { + vfree(ucpia->buffers[2]); + ucpia->buffers[2] = NULL; + } + + if (ucpia->buffers[1]) { + vfree(ucpia->buffers[1]); + ucpia->buffers[1] = NULL; + } + + if (ucpia->buffers[0]) { + vfree(ucpia->buffers[0]); + ucpia->buffers[0] = NULL; + } + + if (!ucpia->open) + kfree(ucpia); +} + +int usb_cpia_init(void) +{ + cam_list = NULL; + + return usb_register(&cpia_driver); +} + +void usb_cpia_cleanup(void) +{ +/* + struct cam_data *cam; + + while ((cam = cam_list) != NULL) + cpia_disconnect(NULL, cam); +*/ + + usb_deregister(&cpia_driver); +} + +#ifdef MODULE +int init_module(void) +{ + return usb_cpia_init(); +} + +void cleanup_module(void) +{ + usb_cpia_cleanup(); +} +#endif /* !MODULE */ diff -urN linux-2.3.99-pre3.orig/drivers/char/videodev.c linux-2.3.99-pre3/drivers/char/videodev.c --- linux-2.3.99-pre3.orig/drivers/char/videodev.c Fri Mar 24 13:58:02 2000 +++ linux-2.3.99-pre3/drivers/char/videodev.c Fri Mar 24 13:57:52 2000 @@ -47,6 +47,9 @@ #ifdef CONFIG_VIDEO_BWQCAM extern int init_bw_qcams(struct video_init *); #endif +#ifdef CONFIG_VIDEO_CPIA +extern int cpia_init(struct video_init *); +#endif #ifdef CONFIG_VIDEO_PLANB extern int init_planbs(struct video_init *); #endif @@ -61,6 +64,9 @@ #endif #ifdef CONFIG_VIDEO_BWQCAM {"bw-qcam", init_bw_qcams}, +#endif +#ifdef CONFIG_VIDEO_CPIA + {"cpia", cpia_init}, #endif #ifdef CONFIG_VIDEO_PLANB {"planb", init_planbs}, diff -urN linux-2.3.99-pre3.orig/drivers/usb/Config.in linux-2.3.99-pre3/drivers/usb/Config.in --- linux-2.3.99-pre3.orig/drivers/usb/Config.in Fri Mar 24 13:58:02 2000 +++ linux-2.3.99-pre3/drivers/usb/Config.in Fri Mar 24 13:57:52 2000 @@ -38,7 +38,6 @@ fi bool ' USB Serial Converter verbose debug' CONFIG_USB_SERIAL_DEBUG fi - dep_tristate ' USB CPiA Camera support' CONFIG_USB_CPIA $CONFIG_USB dep_tristate ' USB IBM (Xirlink) C-it Camera support' CONFIG_USB_IBMCAM $CONFIG_USB dep_tristate ' USB OV511 Camera support' CONFIG_USB_OV511 $CONFIG_USB dep_tristate ' USB Kodak DC-2xx Camera support' CONFIG_USB_DC2XX $CONFIG_USB diff -urN linux-2.3.99-pre3.orig/drivers/usb/Makefile linux-2.3.99-pre3/drivers/usb/Makefile --- linux-2.3.99-pre3.orig/drivers/usb/Makefile Fri Mar 24 13:58:02 2000 +++ linux-2.3.99-pre3/drivers/usb/Makefile Fri Mar 24 13:57:52 2000 @@ -71,7 +71,6 @@ obj-$(CONFIG_USB_ACM) += acm.o obj-$(CONFIG_USB_PRINTER) += printer.o obj-$(CONFIG_USB_AUDIO) += audio.o -obj-$(CONFIG_USB_CPIA) += cpia.o obj-$(CONFIG_USB_IBMCAM) += ibmcam.o obj-$(CONFIG_USB_DC2XX) += dc2xx.o obj-$(CONFIG_USB_MDC800) += mdc800.o diff -urN linux-2.3.99-pre3.orig/drivers/usb/cpia.c linux-2.3.99-pre3/drivers/usb/cpia.c --- linux-2.3.99-pre3.orig/drivers/usb/cpia.c Fri Mar 24 13:58:02 2000 +++ linux-2.3.99-pre3/drivers/usb/cpia.c Wed Dec 31 16:00:00 1969 @@ -1,1425 +0,0 @@ -/* - * USB CPiA Video Camera driver - * - * Supports CPiA based Video Cameras. Many manufacturers use this chipset. - * - * (C) Copyright 1999-2000 Johannes Erdfelt, [EMAIL PROTECTED] - * (C) Copyright 1999 Randy Dunlap - */ - -#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/module.h> -#include <linux/spinlock.h> -#include <linux/usb.h> - -#include <asm/io.h> - -#include "cpia.h" - -static int debug = 0; -MODULE_PARM(debug, "i"); - -/* Video Size 384 x 288 x 3 bytes for RGB */ -/* 384 because xawtv tries to grab 384 even though we tell it 352 is our max */ -#define MAX_FRAME_SIZE (384 * 288 * 3) - -/*******************************/ -/* Memory management functions */ -/*******************************/ - -#define MDEBUG(x) do { } while(0) /* Debug memory management */ - -static struct usb_driver cpia_driver; - -/* Given PGD from the address space's page table, return the kernel - * virtual mapping of the physical memory mapped at ADR. - */ -static inline unsigned long uvirt_to_kva(pgd_t *pgd, unsigned long adr) -{ - unsigned long ret = 0UL; - pmd_t *pmd; - pte_t *ptep, pte; - - if (!pgd_none(*pgd)) { - pmd = pmd_offset(pgd, adr); - if (!pmd_none(*pmd)) { - ptep = pte_offset(pmd, adr); - pte = *ptep; - if (pte_present(pte)) - ret = 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; -} - -/* Here we want the physical address of the memory. - * This is used when initializing the contents of the - * area and marking the pages as reserved. - */ -static inline unsigned long kvirt_to_pa(unsigned long adr) -{ - unsigned long va, kva, ret; - - va = VMALLOC_VMADDR(adr); - kva = uvirt_to_kva(pgd_offset_k(va), va); - ret = __pa(kva); - MDEBUG(printk("kv2pa(%lx-->%lx)", adr, ret)); - return ret; -} - -static void *rvmalloc(unsigned long size) -{ - void *mem; - unsigned long adr, page; - - /* Round it off to PAGE_SIZE */ - size += (PAGE_SIZE - 1); - size &= ~(PAGE_SIZE - 1); - - mem = vmalloc(size); - if (!mem) - return NULL; - - memset(mem, 0, size); /* Clear the ram out, no junk to the user */ - adr = (unsigned long) mem; - while (size > 0) { - page = kvirt_to_pa(adr); - mem_map_reserve(MAP_NR(__va(page))); - adr += PAGE_SIZE; - if (size > PAGE_SIZE) - size -= PAGE_SIZE; - else - size = 0; - } - - return mem; -} - -static void rvfree(void *mem, unsigned long size) -{ - unsigned long adr, page; - - if (!mem) - return; - - size += (PAGE_SIZE - 1); - size &= ~(PAGE_SIZE - 1); - - adr=(unsigned long) mem; - while (size > 0) { - page = kvirt_to_pa(adr); - mem_map_unreserve(MAP_NR(__va(page))); - adr += PAGE_SIZE; - if (size > PAGE_SIZE) - size -= PAGE_SIZE; - else - size = 0; - } - vfree(mem); -} - -static int usb_cpia_get_version(struct usb_device *dev, void *buf) -{ - return usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), - USB_REQ_CPIA_GET_VERSION, - USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE, - 0, 0, buf, 4, HZ); -} - -#ifdef NOTUSED -static int usb_cpia_get_pnp_id(struct usb_device *dev, void *buf) -{ - return usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), - USB_REQ_CPIA_GET_PNP_ID, - USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE, - 0, 0, buf, 6, HZ); -} -#endif - -#ifdef NOTUSED -static int usb_cpia_get_camera_status(struct usb_device *dev, void *buf) -{ - return usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), - USB_REQ_CPIA_GET_CAMERA_STATUS, - USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE, - 0, 0, buf, 8, HZ); -} -#endif - -static int usb_cpia_goto_hi_power(struct usb_device *dev) -{ - return usb_control_msg(dev, usb_sndctrlpipe(dev, 0), - USB_REQ_CPIA_GOTO_HI_POWER, USB_TYPE_VENDOR | USB_RECIP_DEVICE, - 0, 0, NULL, 0, HZ); -} - -static int usb_cpia_get_vp_version(struct usb_device *dev, void *buf) -{ - return usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), - USB_REQ_CPIA_GET_VP_VERSION, - USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE, - 0, 0, buf, 4, HZ); -} - -static int usb_cpia_set_sensor_fps(struct usb_device *dev, int sensorbaserate, int sensorclkdivisor) -{ - return usb_control_msg(dev, usb_sndctrlpipe(dev, 0), - USB_REQ_CPIA_SET_SENSOR_FPS, - USB_TYPE_VENDOR | USB_RECIP_DEVICE, - (sensorbaserate << 8) + sensorclkdivisor, 0, NULL, 0, HZ); -} - -#ifdef NOTUSED -static int usb_cpia_grab_frame(struct usb_device *dev, int streamstartline) -{ - return usb_control_msg(dev, usb_sndctrlpipe(dev, 0), - USB_REQ_CPIA_GRAB_FRAME, USB_TYPE_VENDOR | USB_RECIP_DEVICE, - streamstartline << 8, 0, NULL, 0, HZ); -} -#endif - -static int usb_cpia_upload_frame(struct usb_device *dev, int forceupload) -{ - return usb_control_msg(dev, usb_sndctrlpipe(dev, 0), - USB_REQ_CPIA_UPLOAD_FRAME, USB_TYPE_VENDOR | USB_RECIP_DEVICE, - forceupload, 0, NULL, 0, HZ); -} - -static int usb_cpia_set_grab_mode(struct usb_device *dev, int continuousgrab) -{ - return usb_control_msg(dev, usb_sndctrlpipe(dev, 0), - USB_REQ_CPIA_SET_GRAB_MODE, - USB_TYPE_VENDOR | USB_RECIP_DEVICE, continuousgrab, - 0, NULL, 0, HZ); -} - -static int usb_cpia_set_format(struct usb_device *dev, int size, int subsample, int order) -{ - return usb_control_msg(dev, usb_sndctrlpipe(dev, 0), - USB_REQ_CPIA_SET_FORMAT, - USB_TYPE_VENDOR | USB_RECIP_DEVICE, - (subsample << 8) + size, order, NULL, 0, HZ); -} - -static int usb_cpia_set_roi(struct usb_device *dev, int colstart, int colend, int rowstart, int rowend) -{ - return usb_control_msg(dev, usb_sndctrlpipe(dev, 0), - USB_REQ_CPIA_SET_ROI, - USB_TYPE_VENDOR | USB_RECIP_DEVICE, - (colend << 8) + colstart, (rowend << 8) + rowstart, - NULL, 0, HZ); -} - -static int usb_cpia_set_compression(struct usb_device *dev, int compmode, int decimation) -{ - return usb_control_msg(dev, usb_sndctrlpipe(dev, 0), - USB_REQ_CPIA_SET_COMPRESSION, - USB_TYPE_VENDOR | USB_RECIP_DEVICE, - (decimation << 8) + compmode, 0, NULL, 0, HZ); -} - -#ifdef NOTUSED -static int usb_cpia_set_compression_target(struct usb_device *dev, int target, int targetfr, int targetq) -{ - return usb_control_msg(dev, usb_sndctrlpipe(dev, 0), - USB_REQ_CPIA_SET_COMPRESSION_TARGET, - USB_TYPE_VENDOR | USB_RECIP_DEVICE, - (targetfr << 8) + target, targetq, NULL, 0, HZ); -} -#endif - -#ifdef NOTUSED -static int usb_cpia_initstreamcap(struct usb_device *dev, int skipframes, int streamstartline) -{ - return usb_control_msg(dev, usb_sndctrlpipe(dev, 0), - USB_REQ_CPIA_INIT_STREAM_CAP, - USB_TYPE_VENDOR | USB_RECIP_DEVICE, - (streamstartline << 8) + skipframes, 0, NULL, 0, HZ); -} - -static int usb_cpia_finistreamcap(struct usb_device *dev) -{ - return usb_control_msg(dev, usb_sndctrlpipe(dev, 0), - USB_REQ_CPIA_FINI_STREAM_CAP, - USB_TYPE_VENDOR | USB_RECIP_DEVICE, 0, 0, NULL, 0, HZ); -} - -static int usb_cpia_startstreamcap(struct usb_device *dev) -{ - return usb_control_msg(dev, usb_sndctrlpipe(dev, 0), - USB_REQ_CPIA_START_STREAM_CAP, - USB_TYPE_VENDOR | USB_RECIP_DEVICE, 0, 0, NULL, 0, HZ); -} - -static int usb_cpia_endstreamcap(struct usb_device *dev) -{ - return usb_control_msg(dev, usb_sndctrlpipe(dev, 0), - USB_REQ_CPIA_END_STREAM_CAP, - USB_TYPE_VENDOR | USB_RECIP_DEVICE, 0, 0, NULL, 0, HZ); -} -#endif - -/* How much data is left in the scratch buf? */ -#define scratch_left(x) (cpia->scratchlen - (int)((char *)x - (char *)cpia->scratch)) - -static void cpia_parse_data(struct usb_cpia *cpia) -{ - struct cpia_frame *frame, *pframe; - unsigned char *data = cpia->scratch; - unsigned long left; - long copylen = 0; - - /* Grab the current frame and the previous frame */ - frame = &cpia->frame[cpia->curframe]; - pframe = &cpia->frame[(cpia->curframe - 1 + CPIA_NUMFRAMES) % CPIA_NUMFRAMES]; - - while (1) { - if (!scratch_left(data)) - goto out; - - switch (frame->scanstate) { - case STATE_SCANNING: - { - struct cpia_frame_header *header; - - /* We need at least 2 bytes for the magic value */ - if (scratch_left(data) < 2) - goto out; - - header = (struct cpia_frame_header *)data; - - if (be16_to_cpup(&header->magic) == CPIA_MAGIC) { - frame->scanstate = STATE_HEADER; - break; - } - - /* Woops, lost the header, find the end of the frame */ - if (scratch_left(data) < 4) - goto out; - - /* See if we found the end of the frame */ - while (scratch_left(data) >= 4) { - if (*((__u32 *)data) == 0xFFFFFFFF) { - data += 4; - if (debug >= 1) - printk(KERN_INFO "cpia: EOF while scanning for magic\n"); - goto error; - } - data++; - } - break; - } - case STATE_HEADER: - /* We need at least 64 bytes for the header */ - if (scratch_left(data) < - sizeof(struct cpia_frame_header)) - goto out; - - memcpy(&frame->header, data, - sizeof(struct cpia_frame_header)); - - /* Skip over the header */ - data += sizeof(struct cpia_frame_header); - - frame->hdrwidth = (frame->header.col_end - - frame->header.col_start) * 8; - frame->hdrheight = (frame->header.row_end - - frame->header.row_start) * 4; - if (debug >= 2) { - printk(KERN_DEBUG "cpia: frame size %dx%d\n", - frame->hdrwidth, frame->hdrheight); - printk(KERN_DEBUG "cpia: frame %scompressed\n", - frame->header.comp_enable ? "" : "not "); - } - - frame->scanstate = STATE_LINES; - frame->curline = 0; - break; - - case STATE_LINES: - { - unsigned char *f, *end; - unsigned int len; - int i; - int y, u, y1, v, r, g, b; - - /* We want at least 2 bytes for the length */ - if (scratch_left(data) < 2) - goto out; - - /* Grab the length */ - len = data[0] + (data[1] << 8); - - /* Check to make sure it's nothing outrageous */ - if (len > (frame->hdrwidth * 2) + 1) { - if (debug >= 1) - printk(KERN_DEBUG "cpia: bad length, resynching (expected %d, got %d)\n", (frame->hdrwidth * 2) + 1, len); - goto error; - } - - /* Make sure there's enough data for the entire line */ - if (scratch_left(data + 2) < len) - goto out; - - /* Skip over the length */ - data += 2; - - /* Is the end of the line there */ - if (data[len - 1] != 0xFD) { - if (debug >= 1) - printk(KERN_DEBUG "cpia: lost synch\n"); - goto error; - } - - /* Start at the beginning */ - end = data + len - 1; - - f = frame->data + (frame->width * 3 * frame->curline); - - if (frame->header.comp_enable) { - unsigned char *fp; - - /* We use the previous frame as a reference */ - fp = pframe->data + - (frame->width * 3 * frame->curline); - - while (data < end) { - if (*data & 1) { - /* Compress RLE data */ - i = *data >> 1; - memcpy(f, fp, i * 3); - copylen += (i * 3); - f += (i * 3); - fp += (i * 3); - data++; - } else { - /* Raw data */ - -#define LIMIT(x) ((((x)>0xffffff)?0xff0000:(((x)<=0xffff)?0:(x)&0xff0000))>>16) - -y = *data++ - 16; -u = *data++ - 128; -y1 = *data++ - 16; -v = *data++ - 128; -r = 104635 * v; -g = -25690 * u + -53294 * v; -b = 132278 * u; -y *= 76310; -y1 *= 76310; -*f++ = LIMIT(b + y); *f++ = LIMIT(g + y); *f++ = LIMIT(r + y); -*f++ = LIMIT(b + y1); *f++ = LIMIT(g + y1); *f++ = LIMIT(r + y1); - fp += 6; - copylen += 6; - } - } - } else { - /* Raw data */ - while (data < end) { -y = *data++ - 16; -u = *data++ - 128; -y1 = *data++ - 16; -v = *data++ - 128; -r = 104635 * v; -g = -25690 * u + -53294 * v; -b = 132278 * u; -y *= 76310; -y1 *= 76310; -*f++ = LIMIT(b + y); *f++ = LIMIT(g + y); *f++ = LIMIT(r + y); -*f++ = LIMIT(b + y1); *f++ = LIMIT(g + y1); *f++ = LIMIT(r + y1); -copylen += 6; - } - } - - /* Skip the last byte */ - data++; - - if (++frame->curline >= frame->hdrheight) - goto nextframe; - - break; - } /* end case STATE_LINES */ - } /* end switch (scanstate) */ - } /* end while (1) */ - -nextframe: - if (debug >= 1) - printk(KERN_DEBUG "cpia: marking as success\n"); - - if (scratch_left(data) >= 4 && *((__u32 *)data) == 0xFFFFFFFF) - data += 4; - - frame->grabstate = FRAME_DONE; - - goto wakeup; - -error: - if (debug >= 1) - printk(KERN_DEBUG "cpia: marking as error\n"); - - frame->grabstate = FRAME_ERROR; - - /* Get a fresh frame since this frame may have been important */ - cpia->compress = 0; - - copylen = 0; - -wakeup: - cpia->curframe = -1; - - /* This will cause the process to request another frame. */ - if (waitqueue_active(&frame->wq)) - wake_up_interruptible(&frame->wq); - -out: - /* Grab the remaining */ - left = scratch_left(data); - memmove(cpia->scratch, data, left); - cpia->scratchlen = left; - - /* Update the frame's uncompressed length. */ - frame->scanlength += copylen; -} - -/* - * Make all of the blocks of data contiguous - */ -static int cpia_compress_isochronous(struct usb_cpia *cpia, urb_t *urb) -{ - unsigned char *cdata, *data; - int i, totlen = 0; - - data = cpia->scratch + cpia->scratchlen; - for (i = 0; i < urb->number_of_packets; i++) { - int n = urb->iso_frame_desc[i].actual_length; - int st = urb->iso_frame_desc[i].status; - - cdata = urb->transfer_buffer + urb->iso_frame_desc[i].offset; - - if (st && debug >= 1) - printk(KERN_DEBUG "cpia data error: [%d] len=%d, status=%X\n", - i, n, st); - - if ((cpia->scratchlen + n) > SCRATCH_BUF_SIZE) { - printk(KERN_DEBUG "cpia: scratch buf overflow!scr_len: %d, n: %d\n",cpia->scratchlen, n ); - return totlen; - } - - if (n) { - memmove(data, cdata, n); - data += n; - totlen += n; - cpia->scratchlen += n; - } - } - - return totlen; -} - -static void cpia_isoc_irq(struct urb *urb) -{ - int len; - struct usb_cpia *cpia = urb->context; - struct cpia_sbuf *sbuf; - int i; - - if (!cpia->dev) - return; - - if (!cpia->streaming) { - if (debug >= 1) - printk(KERN_DEBUG "cpia: oops, not streaming, but interrupt\n"); - return; - } - - sbuf = &cpia->sbuf[cpia->cursbuf]; - - /* Copy the data received into our scratch buffer */ - len = cpia_compress_isochronous(cpia, urb); - - /* If we don't have a frame we're current working on, complain */ - if (cpia->scratchlen) { - if (cpia->curframe < 0) { - if (debug >= 1) - printk(KERN_DEBUG "cpia: received data, but no frame available\n"); - } else - cpia_parse_data(cpia); - } - - for (i = 0; i < FRAMES_PER_DESC; i++) { - sbuf->urb->iso_frame_desc[i].status = 0; - sbuf->urb->iso_frame_desc[i].actual_length = 0; - } - - /* Move to the next sbuf */ - cpia->cursbuf = (cpia->cursbuf + 1) % CPIA_NUMSBUF; - - return; -} - -static int cpia_init_isoc(struct usb_cpia *cpia) -{ - urb_t *urb; - int fx, err; - - cpia->compress = 0; - cpia->curframe = -1; - cpia->cursbuf = 0; - cpia->scratchlen = 0; - - /* Alternate interface 3 is is the biggest frame size */ - if (usb_set_interface(cpia->dev, cpia->iface, 3) < 0) { - printk(KERN_ERR "usb_set_interface error\n"); - return -EBUSY; - } - - /* We double buffer the Iso lists */ - urb = usb_alloc_urb(FRAMES_PER_DESC); - - if (!urb) { - printk(KERN_ERR "cpia_init_isoc: usb_init_isoc ret %d\n", - 0); - return -ENOMEM; - } - cpia->sbuf[0].urb = urb; - urb->dev = cpia->dev; - urb->context = cpia; - urb->pipe = usb_rcvisocpipe(cpia->dev, 1); - urb->transfer_flags = USB_ISO_ASAP; - urb->transfer_buffer = cpia->sbuf[0].data; - urb->complete = cpia_isoc_irq; - urb->number_of_packets = FRAMES_PER_DESC; - urb->transfer_buffer_length = FRAME_SIZE_PER_DESC * FRAMES_PER_DESC; - for (fx = 0; fx < FRAMES_PER_DESC; fx++) { - urb->iso_frame_desc[fx].offset = FRAME_SIZE_PER_DESC * fx; - urb->iso_frame_desc[fx].length = FRAME_SIZE_PER_DESC; - } - urb = usb_alloc_urb(FRAMES_PER_DESC); - if (!urb) { - printk(KERN_ERR "cpia_init_isoc: usb_init_isoc ret %d\n", - 0); - return -ENOMEM; - } - cpia->sbuf[1].urb = urb; - urb->dev = cpia->dev; - urb->context = cpia; - urb->pipe = usb_rcvisocpipe(cpia->dev, 1); - urb->transfer_flags = USB_ISO_ASAP; - urb->transfer_buffer = cpia->sbuf[1].data; - urb->complete = cpia_isoc_irq; - urb->number_of_packets = FRAMES_PER_DESC; - urb->transfer_buffer_length = FRAME_SIZE_PER_DESC * FRAMES_PER_DESC; - for (fx = 0; fx < FRAMES_PER_DESC; fx++) { - urb->iso_frame_desc[fx].offset = FRAME_SIZE_PER_DESC * fx; - urb->iso_frame_desc[fx].length = FRAME_SIZE_PER_DESC; - } - - cpia->sbuf[1].urb->next = cpia->sbuf[0].urb; - cpia->sbuf[0].urb->next = cpia->sbuf[1].urb; - - err = usb_submit_urb(cpia->sbuf[0].urb); - if (err) - printk(KERN_ERR "cpia_init_isoc: usb_submit_urb(0) ret %d\n", - err); - err = usb_submit_urb(cpia->sbuf[1].urb); - if (err) - printk(KERN_ERR "cpia_init_isoc: usb_submit_urb(1) ret %d\n", - err); - - cpia->streaming = 1; - - return 0; -} - -static void cpia_stop_isoc(struct usb_cpia *cpia) -{ - if (!cpia->streaming || !cpia->dev) - return; - - /* Turn off continuous grab */ - if (usb_cpia_set_grab_mode(cpia->dev, 0) < 0) { - printk(KERN_ERR "cpia_set_grab_mode error\n"); - return /* -EBUSY */; - } - - /* Set packet size to 0 */ - if (usb_set_interface(cpia->dev, cpia->iface, 0) < 0) { - printk(KERN_ERR "usb_set_interface error\n"); - return /* -EINVAL */; - } - - cpia->streaming = 0; - - /* Unschedule all of the iso td's */ - if (cpia->sbuf[1].urb) { - cpia->sbuf[1].urb->next = NULL; - usb_unlink_urb(cpia->sbuf[1].urb); - usb_free_urb(cpia->sbuf[1].urb); - cpia->sbuf[1].urb = NULL; - } - if (cpia->sbuf[0].urb) { - cpia->sbuf[0].urb->next = NULL; - usb_unlink_urb(cpia->sbuf[0].urb); - usb_free_urb(cpia->sbuf[0].urb); - cpia->sbuf[0].urb = NULL; - } -} - -static int cpia_new_frame(struct usb_cpia *cpia, int framenum) -{ - struct cpia_frame *frame; - int width, height; - - if (!cpia->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 */ - if (cpia->curframe == -1) { - if (cpia->frame[(framenum - 1 + CPIA_NUMFRAMES) % CPIA_NUMFRAMES].grabstate == FRAME_READY) - framenum = (framenum - 1 + CPIA_NUMFRAMES) % CPIA_NUMFRAMES; - } else - return 0; - - frame = &cpia->frame[framenum]; - width = frame->width; - height = frame->height; - - frame->grabstate = FRAME_GRABBING; - frame->scanstate = STATE_SCANNING; - frame->scanlength = 0; /* accumulated in cpia_parse_data() */ - - cpia->curframe = framenum; - - /* Make sure it's not too big */ - if (width > 352) - width = 352; - width = (width / 8) * 8; /* Multiple of 8 */ - - if (height > 288) - height = 288; - height = (height / 4) * 4; /* Multiple of 4 */ - - /* Set the ROI they want */ - if (usb_cpia_set_roi(cpia->dev, 0, width / 8, 0, height / 4) < 0) - return -EBUSY; - - if (usb_cpia_set_compression(cpia->dev, cpia->compress ? - COMP_AUTO : COMP_DISABLED, DONT_DECIMATE) < 0) { - printk(KERN_ERR "cpia_set_compression error\n"); - return -EBUSY; - } - - /* We want a fresh frame every 30 we get */ - cpia->compress = (cpia->compress + 1) % 30; - - /* Grab the frame */ - if (usb_cpia_upload_frame(cpia->dev, WAIT_FOR_NEXT_FRAME) < 0) { - printk(KERN_ERR "cpia_upload_frame error\n"); - return -EBUSY; - } - - return 0; -} - -/* Video 4 Linux API */ -static int cpia_open(struct video_device *dev, int flags) -{ - int err = -EBUSY; - struct usb_cpia *cpia = (struct usb_cpia *)dev; - - down(&cpia->lock); - if (cpia->user) - goto out_unlock; - - cpia->frame[0].grabstate = FRAME_UNUSED; - cpia->frame[1].grabstate = FRAME_UNUSED; - - err = -ENOMEM; - - /* Allocate memory for the frame buffers */ - cpia->fbuf = rvmalloc(2 * MAX_FRAME_SIZE); - if (!cpia->fbuf) - goto open_err_ret; - - cpia->frame[0].data = cpia->fbuf; - cpia->frame[1].data = cpia->fbuf + MAX_FRAME_SIZE; - - cpia->sbuf[0].data = kmalloc (FRAMES_PER_DESC * FRAME_SIZE_PER_DESC, GFP_KERNEL); - if (!cpia->sbuf[0].data) - goto open_err_on0; - - cpia->sbuf[1].data = kmalloc (FRAMES_PER_DESC * FRAME_SIZE_PER_DESC, GFP_KERNEL); - if (!cpia->sbuf[1].data) - goto open_err_on1; - - /* Set default sizes in case IOCTL (VIDIOCMCAPTURE) is not used - * (using read() instead). */ - cpia->frame[0].width = 352; - cpia->frame[0].height = 288; - cpia->frame[0].bytes_read = 0; - cpia->frame[1].width = 352; - cpia->frame[1].height = 288; - cpia->frame[1].bytes_read = 0; - - err = cpia_init_isoc(cpia); - if (err) - goto open_err_on2; - - cpia->user++; - up(&cpia->lock); - - MOD_INC_USE_COUNT; - - return 0; - -open_err_on2: - kfree (cpia->sbuf[1].data); -open_err_on1: - kfree (cpia->sbuf[0].data); -open_err_on0: - rvfree(cpia->fbuf, 2 * MAX_FRAME_SIZE); -open_err_ret: - return err; - -out_unlock: - up(&cpia->lock); - return err; -} - -static void cpia_close(struct video_device *dev) -{ - struct usb_cpia *cpia = (struct usb_cpia *)dev; - - down(&cpia->lock); - cpia->user--; - - MOD_DEC_USE_COUNT; - - cpia_stop_isoc(cpia); - - rvfree(cpia->fbuf, 2 * MAX_FRAME_SIZE); - - kfree(cpia->sbuf[1].data); - kfree(cpia->sbuf[0].data); - - up(&cpia->lock); - - if (!cpia->dev) { - video_unregister_device(&cpia->vdev); - kfree(cpia); - } -} - -static int cpia_init_done(struct video_device *dev) -{ - return 0; -} - -static long cpia_write(struct video_device *dev, const char *buf, unsigned long count, int noblock) -{ - return -EINVAL; -} - -static int cpia_ioctl(struct video_device *dev, unsigned int cmd, void *arg) -{ - struct usb_cpia *cpia = (struct usb_cpia *)dev; - - if (!cpia->dev) - return -EIO; - - switch (cmd) { - case VIDIOCGCAP: - { - struct video_capability b; - - strcpy(b.name, "CPiA USB Camera"); - b.type = VID_TYPE_CAPTURE | VID_TYPE_SUBCAPTURE; - b.channels = 1; - b.audios = 0; - b.maxwidth = 352; /* CIF */ - b.maxheight = 288; /* " */ - b.minwidth = 8; - b.minheight = 4; - - 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; - - v.flags = 0; - v.tuners = 0; - v.type = VIDEO_TYPE_CAMERA; - strcpy(v.name, "Camera"); - - if (copy_to_user(arg, &v, sizeof(v))) - return -EFAULT; - - return 0; - } - case VIDIOCSCHAN: - { - int v; - - if (copy_from_user(&v, arg, sizeof(v))) - return -EFAULT; - - if (v != 0) - return -EINVAL; - - return 0; - } - case VIDIOCGPICT: - { - struct video_picture p; - - p.colour = 0x8000; /* Damn British people :) */ - p.hue = 0x8000; - p.brightness = 180 << 8; /* XXX */ - p.contrast = 192 << 8; /* XXX */ - p.whiteness = 105 << 8; /* XXX */ - p.depth = 24; - p.palette = VIDEO_PALETTE_RGB24; - - if (copy_to_user(arg, &p, sizeof(p))) - return -EFAULT; - - return 0; - } - case VIDIOCSPICT: - { - struct video_picture p; - - if (copy_from_user(&p, arg, sizeof(p))) - return -EFAULT; - - 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 != 288) - return -EINVAL; - if (vw.width != 352) - return -EINVAL; - - cpia->compress = 0; - - return 0; - } - case VIDIOCGWIN: - { - struct video_window vw; - - vw.x = 0; - vw.y = 0; - vw.width = 352; - vw.height = 288; - vw.chromakey = 0; - vw.flags = 30; /* 30 fps */ - - if (copy_to_user(arg, &vw, sizeof(vw))) - return -EFAULT; - - return 0; - } - case VIDIOCGMBUF: - { - struct video_mbuf vm; - - memset(&vm, 0, sizeof(vm)); - vm.size = MAX_FRAME_SIZE * 2; - vm.frames = 2; - vm.offsets[0] = 0; - vm.offsets[1] = MAX_FRAME_SIZE; - - if (copy_to_user((void *)arg, (void *)&vm, sizeof(vm))) - return -EFAULT; - - return 0; - } - case VIDIOCMCAPTURE: - { - struct video_mmap vm; - - if (copy_from_user((void *)&vm, (void *)arg, sizeof(vm))) - return -EFAULT; - - if (debug >= 1) - printk(KERN_DEBUG "frame: %d, size: %dx%d, format: %d\n", - vm.frame, vm.width, vm.height, vm.format); - - if (vm.format != VIDEO_PALETTE_RGB24) - return -EINVAL; - - if ((vm.frame != 0) && (vm.frame != 1)) - return -EINVAL; - - if (cpia->frame[vm.frame].grabstate == FRAME_GRABBING) - return -EBUSY; - - /* Don't compress if the size changed */ - if ((cpia->frame[vm.frame].width != vm.width) || - (cpia->frame[vm.frame].height != vm.height)) - cpia->compress = 0; - - cpia->frame[vm.frame].width = vm.width; - cpia->frame[vm.frame].height = vm.height; - - /* Mark it as ready */ - cpia->frame[vm.frame].grabstate = FRAME_READY; - - return cpia_new_frame(cpia, vm.frame); - } - case VIDIOCSYNC: - { - int frame; - - if (copy_from_user((void *)&frame, arg, sizeof(int))) - return -EFAULT; - - if (debug >= 1) - printk(KERN_DEBUG "cpia: syncing to frame %d\n", frame); - - switch (cpia->frame[frame].grabstate) { - case FRAME_UNUSED: - return -EINVAL; - case FRAME_READY: - case FRAME_GRABBING: - case FRAME_ERROR: -redo: - if (!cpia->dev) - return -EIO; - - do { - interruptible_sleep_on(&cpia->frame[frame].wq); - if (signal_pending(current)) - return -EINTR; - } while (cpia->frame[frame].grabstate == FRAME_GRABBING); - - if (cpia->frame[frame].grabstate == FRAME_ERROR) { - int ret; - - if ((ret = cpia_new_frame(cpia, frame)) < 0) - return ret; - goto redo; - } - case FRAME_DONE: - cpia->frame[frame].grabstate = FRAME_UNUSED; - break; - } - - cpia->frame[frame].grabstate = FRAME_UNUSED; - - 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; -} - -static long cpia_read(struct video_device *dev, char *buf, unsigned long count, int noblock) -{ - struct usb_cpia *cpia = (struct usb_cpia *)dev; - int frmx = -1; - volatile struct cpia_frame *frame; - - if (debug >= 1) - printk(KERN_DEBUG "cpia_read: %ld bytes, noblock=%d\n", count, noblock); - - if (!dev || !buf) - return -EFAULT; - - if (!cpia->dev) - return -EIO; - - /* See if a frame is completed, then use it. */ - if (cpia->frame[0].grabstate >= FRAME_DONE) /* _DONE or _ERROR */ - frmx = 0; - else if (cpia->frame[1].grabstate >= FRAME_DONE)/* _DONE or _ERROR */ - frmx = 1; - - if (noblock && (frmx == -1)) - return -EAGAIN; - - /* If no FRAME_DONE, look for a FRAME_GRABBING state. */ - /* See if a frame is in process (grabbing), then use it. */ - if (frmx == -1) { - if (cpia->frame[0].grabstate == FRAME_GRABBING) - frmx = 0; - else if (cpia->frame[1].grabstate == FRAME_GRABBING) - frmx = 1; - } - - /* If no frame is active, start one. */ - if (frmx == -1) - cpia_new_frame(cpia, frmx = 0); - - frame = &cpia->frame[frmx]; - -restart: - if (!cpia->dev) - return -EIO; - - while (frame->grabstate == FRAME_GRABBING) { - interruptible_sleep_on(&frame->wq); - if (signal_pending(current)) - return -EINTR; - } - - if (frame->grabstate == FRAME_ERROR) { - frame->bytes_read = 0; -printk("cpia_read: errored frame %d\n", cpia->curframe); - if (cpia_new_frame(cpia, frmx)) - printk(KERN_ERR "cpia_read: cpia_new_frame error\n"); - goto restart; - } - - if (debug >= 1) - printk(KERN_DEBUG "cpia_read: frmx=%d, bytes_read=%ld, scanlength=%ld\n", - frmx, frame->bytes_read, frame->scanlength); - - /* copy bytes to user space; we allow for partials reads */ - if ((count + frame->bytes_read) > frame->scanlength) - count = frame->scanlength - frame->bytes_read; - - if (copy_to_user(buf, frame->data + frame->bytes_read, count)) - return -EFAULT; - - frame->bytes_read += count; - if (debug >= 1) - printk(KERN_DEBUG "cpia_read: {copy} count used=%ld, new bytes_read=%ld\n", - count, frame->bytes_read); - - if (frame->bytes_read >= frame->scanlength) { /* All data has been read */ - frame->bytes_read = 0; - - /* Mark it as available to be used again. */ - cpia->frame[frmx].grabstate = FRAME_UNUSED; - if (cpia_new_frame(cpia, frmx ? 0 : 1)) - printk(KERN_ERR "cpia_read: cpia_new_frame returned error\n"); - } - - return count; -} - -static int cpia_mmap(struct video_device *dev, const char *adr, unsigned long size) -{ - struct usb_cpia *cpia = (struct usb_cpia *)dev; - unsigned long start = (unsigned long)adr; - unsigned long page, pos; - - if (!cpia->dev) - return -EIO; - - if (size > (((2 * MAX_FRAME_SIZE) + PAGE_SIZE - 1) & ~(PAGE_SIZE - 1))) - return -EINVAL; - - pos = (unsigned long)cpia->fbuf; - while (size > 0) { - page = kvirt_to_pa(pos); - if (remap_page_range(start, page, PAGE_SIZE, PAGE_SHARED)) - return -EAGAIN; - - start += PAGE_SIZE; - pos += PAGE_SIZE; - if (size > PAGE_SIZE) - size -= PAGE_SIZE; - else - size = 0; - } - - return 0; -} - -static struct video_device cpia_template = { - "CPiA USB Camera", - VID_TYPE_CAPTURE, - VID_HARDWARE_CPIA, - cpia_open, - cpia_close, - cpia_read, - cpia_write, - NULL, - cpia_ioctl, - cpia_mmap, - cpia_init_done, - NULL, - 0, - 0 -}; - -static int usb_cpia_configure(struct usb_cpia *cpia) -{ - struct usb_device *dev = cpia->dev; - unsigned char version[4]; - - /* Set altsetting 0 */ - if (usb_set_interface(dev, cpia->iface, 0) < 0) { - printk(KERN_ERR "usb_set_interface error\n"); - return -EBUSY; - } - - if (usb_cpia_get_version(dev, version) < 0) { - printk(KERN_ERR "cpia_get_version error\n"); - return -EBUSY; - } - - if (debug >= 1) - printk(KERN_DEBUG "cpia: Firmware v%d.%d, VC Hardware v%d.%d\n", - version[0], version[1], version[2], version[3]); - - memcpy(&cpia->vdev, &cpia_template, sizeof(cpia_template)); - - init_waitqueue_head(&cpia->frame[0].wq); - init_waitqueue_head(&cpia->frame[1].wq); - - if (video_register_device(&cpia->vdev, VFL_TYPE_GRABBER) == -1) { - printk(KERN_ERR "video_register_device failed\n"); - return -EBUSY; - } - - if (usb_cpia_goto_hi_power(dev) < 0) { - printk(KERN_ERR "cpia_goto_hi_power error\n"); - goto error; - } - - if (usb_cpia_get_vp_version(dev, version) < 0) { - printk(KERN_ERR "cpia_get_vp_version error\n"); - goto error; - } - - if (debug >= 1) { - printk(KERN_DEBUG "cpia: VP v%d rev %d\n", version[0], version[1]); - printk(KERN_DEBUG "cpia: Camera Head ID %04X\n", (version[3] << 8) + version[2]); - } - - /* Turn on continuous grab */ - if (usb_cpia_set_grab_mode(dev, 1) < 0) { - printk(KERN_ERR "cpia_set_grab_mode error\n"); - goto error; - } - - /* Set up the sensor to be 30fps */ - if (usb_cpia_set_sensor_fps(dev, 1, 0) < 0) { - printk(KERN_ERR "cpia_set_sensor_fps error\n"); - goto error; - } - - /* Set video into CIF mode, and order into YUYV mode */ - if (usb_cpia_set_format(dev, FORMAT_CIF, FORMAT_422, - FORMAT_YUYV) < 0) { - printk(KERN_ERR "cpia_set_format error\n"); - goto error; - } - - /* Turn off compression */ - if (usb_cpia_set_compression(dev, COMP_DISABLED, DONT_DECIMATE) < 0) { - printk(KERN_ERR "cpia_set_compression error\n"); - goto error; - } - - cpia->compress = 0; - - return 0; - -error: - video_unregister_device(&cpia->vdev); - usb_driver_release_interface(&cpia_driver, - &dev->actconfig->interface[0]); - - kfree(cpia); - - return -EBUSY; -} - -static void * cpia_probe(struct usb_device *dev, unsigned int ifnum) -{ - struct usb_interface_descriptor *interface; - struct usb_cpia *cpia; - - /* We don't handle multi-config cameras */ - if (dev->descriptor.bNumConfigurations != 1) - return NULL; - - interface = &dev->actconfig->interface[ifnum].altsetting[0]; - - /* Is it a CPiA? */ - if (dev->descriptor.idVendor != 0x0553) - return NULL; - if (dev->descriptor.idProduct != 0x0002) - return NULL; - - /* Checking vendor/product should be enough, but what the hell */ - if (interface->bInterfaceClass != 0xFF) - return NULL; - if (interface->bInterfaceSubClass != 0x00) - return NULL; - - /* We found a CPiA */ - printk(KERN_INFO "USB CPiA camera found\n"); - - if ((cpia = kmalloc(sizeof(*cpia), GFP_KERNEL)) == NULL) { - printk(KERN_ERR "couldn't kmalloc cpia struct\n"); - return NULL; - } - - memset(cpia, 0, sizeof(*cpia)); - - cpia->dev = dev; - cpia->iface = interface->bInterfaceNumber; - - if (!usb_cpia_configure(cpia)) { - cpia->user=0; - init_MUTEX(&cpia->lock); /* to 1 == available */ - - return cpia; - } else - return NULL; -} - -static void cpia_disconnect(struct usb_device *dev, void *ptr) -{ - struct usb_cpia *cpia = (struct usb_cpia *) ptr; - - /* We don't want people trying to open up the device */ - if (!cpia->user) - video_unregister_device(&cpia->vdev); - - usb_driver_release_interface(&cpia_driver, - &cpia->dev->actconfig->interface[0]); - - cpia->dev = NULL; - cpia->frame[0].grabstate = FRAME_ERROR; - cpia->frame[1].grabstate = FRAME_ERROR; - cpia->curframe = -1; - - /* This will cause the process to request another frame. */ - if (waitqueue_active(&cpia->frame[0].wq)) - wake_up_interruptible(&cpia->frame[0].wq); - - if (waitqueue_active(&cpia->frame[1].wq)) - wake_up_interruptible(&cpia->frame[1].wq); - - cpia->streaming = 0; - - /* Unschedule all of the iso td's */ - if (cpia->sbuf[1].urb) { - cpia->sbuf[1].urb->next = NULL; - usb_unlink_urb(cpia->sbuf[1].urb); - usb_free_urb(cpia->sbuf[1].urb); - cpia->sbuf[1].urb = NULL; - } - if (cpia->sbuf[0].urb) { - cpia->sbuf[0].urb->next = NULL; - usb_unlink_urb(cpia->sbuf[0].urb); - usb_free_urb(cpia->sbuf[0].urb); - cpia->sbuf[0].urb = NULL; - } - - /* Free the memory */ - if (!cpia->user) - kfree(cpia); -} - -static struct usb_driver cpia_driver = { - "cpia", - cpia_probe, - cpia_disconnect, - { NULL, NULL } -}; - -int usb_cpia_init(void) -{ - return usb_register(&cpia_driver); -} - -void usb_cpia_cleanup(void) -{ - usb_deregister(&cpia_driver); -} - -#ifdef MODULE -int init_module(void) -{ - return usb_cpia_init(); -} - -void cleanup_module(void) -{ - usb_cpia_cleanup(); -} -#endif diff -urN linux-2.3.99-pre3.orig/drivers/usb/cpia.h linux-2.3.99-pre3/drivers/usb/cpia.h --- linux-2.3.99-pre3.orig/drivers/usb/cpia.h Fri Mar 24 13:58:02 2000 +++ linux-2.3.99-pre3/drivers/usb/cpia.h Wed Dec 31 16:00:00 1969 @@ -1,205 +0,0 @@ -#ifndef __LINUX_CPIA_H -#define __LINUX_CPIA_H - -#include <linux/list.h> - -#define USB_REQ_CPIA_GET_VERSION 0x01 -#define USB_REQ_CPIA_GET_PNP_ID 0x02 -#define USB_REQ_CPIA_GET_CAMERA_STATUS 0x03 -#define USB_REQ_CPIA_GOTO_HI_POWER 0x04 -#define USB_REQ_CPIA_GOTO_LO_POWER 0x05 -/* No 0x06 */ -#define USB_REQ_CPIA_GOTO_SUSPEND 0x07 -#define USB_REQ_CPIA_GOTO_PASS_THROUGH 0x08 -/* No 0x09 */ -#define USB_REQ_CPIA_MODIFY_CAMERA_STATUS 0x0A - -#define USB_REQ_CPIA_READ_VC_REGS 0x21 -#define USB_REQ_CPIA_WRITE_BC_REG 0x22 -#define USB_REQ_CPIA_READ_MC_PORTS 0x23 -#define USB_REQ_CPIA_WRITE_MC_PORT 0x24 -#define USB_REQ_CPIA_SET_BAUD_RATE 0x25 -#define USB_REQ_CPIA_SET_ECP_TIMING 0x26 -#define USB_REQ_CPIA_READ_IDATA 0x27 -#define USB_REQ_CPIA_WRITE_IDATA 0x28 -#define USB_REQ_CPIA_GENERIC_CALL 0x29 -#define USB_REQ_CPIA_I2CSTART 0x2A -#define USB_REQ_CPIA_I2CSTOP 0x2B -#define USB_REQ_CPIA_I2CWRITE 0x2C -#define USB_REQ_CPIA_I2CREAD 0x2D - -#define USB_REQ_CPIA_GET_VP_VERSION 0xA1 -#define USB_REQ_CPIA_SET_COLOUR_PARAMS 0xA3 -#define USB_REQ_CPIA_SET_EXPOSURE 0xA4 -/* No 0xA5 */ -#define USB_REQ_CPIA_SET_COLOUR_BALANCE 0xA6 -#define USB_REQ_CPIA_SET_SENSOR_FPS 0xA7 -#define USB_REQ_CPIA_SET_VP_DEFAULTS 0xA8 -#define USB_REQ_CPIA_SET_APCOR 0xA9 -#define USB_REQ_CPIA_SET_FLICKER_CTRL 0xAA -#define USB_REQ_CPIA_SET_VL_OFFSET 0xAB - -#define USB_REQ_CPIA_GET_COLOUR_PARAMETERS 0xB0 -#define USB_REQ_CPIA_GET_COLOUR_BALANCE 0xB1 -#define USB_REQ_CPIA_GET_EXPOSURE 0xB2 -#define USB_REQ_CPIA_SET_SENSOR_MATRIX 0xB3 - -#define USB_REQ_CPIA_COLOUR_BARS 0xBD -#define USB_REQ_CPIA_READ_VP_REGS 0xBE -#define USB_REQ_CPIA_WRITE_VP_REGS 0xBF - -#define USB_REQ_CPIA_GRAB_FRAME 0xC1 -#define USB_REQ_CPIA_UPLOAD_FRAME 0xC2 -#define WAIT_FOR_NEXT_FRAME 0 -#define FORCE_FRAME_UPLOAD 1 -#define USB_REQ_CPIA_SET_GRAB_MODE 0xC3 -#define USB_REQ_CPIA_INIT_STREAM_CAP 0xC4 -#define USB_REQ_CPIA_FINI_STREAM_CAP 0xC5 -#define USB_REQ_CPIA_START_STREAM_CAP 0xC6 -#define USB_REQ_CPIA_END_STREAM_CAP 0xC7 -#define USB_REQ_CPIA_SET_FORMAT 0xC8 -#define FORMAT_QCIF 0 -#define FORMAT_CIF 1 -#define FORMAT_YUYV 0 -#define FORMAT_UYVY 1 -#define FORMAT_420 0 -#define FORMAT_422 1 -#define USB_REQ_CPIA_SET_ROI 0xC9 -#define USB_REQ_CPIA_SET_COMPRESSION 0xCA -#define COMP_DISABLED 0 -#define COMP_AUTO 1 -#define COMP_MANUAL 2 -#define DONT_DECIMATE 0 -#define DECIMATE 1 -#define USB_REQ_CPIA_SET_COMPRESSION_TARGET 0xCB -#define TARGET_QUALITY 0 -#define TARGET_FRAMERATE 1 -#define USB_REQ_CPIA_SET_YUV_THRESH 0xCC -#define USB_REQ_CPIA_SET_COMPRESSION_PARAMS 0xCD -#define USB_REQ_CPIA_DISCARD_FRAME 0xCE - -#define USB_REQ_CPIA_OUTPUT_RS232 0xE1 -#define USB_REQ_CPIA_ABORT_PROCESS 0xE4 -#define USB_REQ_CPIA_SET_DRAM_PAGE 0xE5 -#define USB_REQ_CPIA_START_DRAM_UPLOAD 0xE6 -#define USB_REQ_CPIA_START_DUMMY_STREAM 0xE8 -#define USB_REQ_CPIA_ABORT_STREAM 0xE9 -#define USB_REQ_CPIA_DOWNLOAD_DRAM 0xEA -/* #define USB_REQ_CPIA_NULL_CMD 0x?? */ - -#define STREAM_BUF_SIZE (PAGE_SIZE * 4) -/* #define STREAM_BUF_SIZE (FRAMES_PER_DESC * FRAME_SIZE_PER_DESC) */ - -#define SCRATCH_BUF_SIZE (STREAM_BUF_SIZE * 2) - -#define FRAMES_PER_DESC 10 -#define FRAME_SIZE_PER_DESC 960 /* Shouldn't be hardcoded */ - -enum { - STATE_SCANNING, /* Scanning for start */ - STATE_HEADER, /* Parsing header */ - STATE_LINES, /* Parsing lines */ -}; - -#define CPIA_MAGIC 0x1968 -struct cpia_frame_header { - __u16 magic; /* 0 - 1 */ - __u16 timestamp; /* 2 - 3 */ - __u16 unused; /* 4 - 5 */ - __u16 timestamp1; /* 6 - 7 */ - __u8 unused1[8]; /* 8 - 15 */ - __u8 video_size; /* 16 0 = QCIF, 1 = CIF */ - __u8 sub_sample; /* 17 0 = 4:2:0, 1 = 4:2:2 */ - __u8 yuv_order; /* 18 0 = YUYV, 1 = UYVY */ - __u8 unused2[5]; /* 19 - 23 */ - __u8 col_start; /* 24 */ - __u8 col_end; /* 25 */ - __u8 row_start; /* 26 */ - __u8 row_end; /* 27 */ - __u8 comp_enable; /* 28 0 = non compressed, 1 = compressed */ - __u8 decimation; /* 29 0 = no decimation, 1 = decimation */ - __u8 y_thresh; /* 30 */ - __u8 uv_thresh; /* 31 */ - __u8 system_state; /* 32 */ - __u8 grab_state; /* 33 */ - __u8 stream_state; /* 34 */ - __u8 fatal_error; /* 35 */ - __u8 cmd_error; /* 36 */ - __u8 debug_flags; /* 37 */ - __u8 camera_state_7; /* 38 */ - __u8 camera_state_8; /* 39 */ - __u8 cr_achieved; /* 40 */ - __u8 fr_achieved; /* 41 */ - __u8 unused3[22]; /* 42 - 63 */ -}; - -struct usb_device; - -struct cpia_sbuf { - char *data; - urb_t *urb; -}; - -enum { - FRAME_UNUSED, /* Unused (no MCAPTURE) */ - FRAME_READY, /* Ready to start grabbing */ - FRAME_GRABBING, /* In the process of being grabbed into */ - FRAME_DONE, /* Finished grabbing, but not been synced yet */ - FRAME_ERROR, /* Something bad happened while processing */ -}; - -struct cpia_frame { - char *data; /* Frame buffer */ - - struct cpia_frame_header header; /* Header from stream */ - - int width; /* Width application is expecting */ - int height; /* Height */ - - int hdrwidth; /* Width the frame actually is */ - int hdrheight; /* Height */ - - volatile int grabstate; /* State of grabbing */ - int scanstate; /* State of scanning */ - - int curline; /* Line of frame we're working on */ - - long scanlength; /* uncompressed, raw data length of frame */ - long bytes_read; /* amount of scanlength that has been read from *data */ - - wait_queue_head_t wq; /* Processes waiting */ -}; - -#define CPIA_NUMFRAMES 2 -#define CPIA_NUMSBUF 2 - -struct usb_cpia { - struct video_device vdev; - - /* Device structure */ - struct usb_device *dev; - - unsigned char iface; - - struct semaphore lock; - int user; /* user count for exclusive use */ - - int streaming; /* Are we streaming Isochronous? */ - int grabbing; /* Are we grabbing? */ - - int compress; /* Should the next frame be compressed? */ - - char *fbuf; /* Videodev buffer area */ - - int curframe; - struct cpia_frame frame[CPIA_NUMFRAMES]; /* Double buffering */ - - int cursbuf; /* Current receiving sbuf */ - struct cpia_sbuf sbuf[CPIA_NUMSBUF]; /* Double buffering */ - - /* Scratch space from the Isochronous pipe */ - unsigned char scratch[SCRATCH_BUF_SIZE]; - int scratchlen; -}; - -#endif diff -urN linux-2.3.99-pre3.orig/drivers/usb/usb-core.c linux-2.3.99-pre3/drivers/usb/usb-core.c --- linux-2.3.99-pre3.orig/drivers/usb/usb-core.c Fri Mar 24 13:58:02 2000 +++ linux-2.3.99-pre3/drivers/usb/usb-core.c Fri Mar 24 13:57:52 2000 @@ -29,7 +29,6 @@ */ int usb_audio_init(void); -int usb_cpia_init(void); int usb_ibmcam_init(void); int dabusb_init(void); int plusb_init(void); @@ -70,9 +69,6 @@ #ifndef CONFIG_USB_MODULE #ifdef CONFIG_USB_AUDIO usb_audio_init(); -#endif -#ifdef CONFIG_USB_CPIA - usb_cpia_init(); #endif #ifdef CONFIG_USB_IBMCAM usb_ibmcam_init();
--------------------------------------------------------------------- To unsubscribe, e-mail: [EMAIL PROTECTED] For additional commands, e-mail: [EMAIL PROTECTED]
