From: Mauro Carvalho Chehab Date: Thu, 30 Sep 2010 11:25:42 +0000 (-0300) Subject: V4L/DVB: Deprecate cpia driver (used for parallel port webcams) X-Git-Tag: firefly_0821_release~7613^2~3587^2~349 X-Git-Url: http://demsky.eecs.uci.edu/git/?a=commitdiff_plain;h=7af97effb3f5a374482179aca490b5038de56fa6;p=firefly-linux-kernel-4.4.55.git V4L/DVB: Deprecate cpia driver (used for parallel port webcams) cpia driver were re-written inside gspca driver, for USB devices. The only functionality that were not migrated is the support for parallel port, as: 1) the developer didn't find any hardware; 2) it doesn't seem important to keep support for a parallel port webcam, as this is an obsolete technology; 3) the changes at gspca for it to work with parallel port would be very large; 4) this driver still uses BKL. So, let's move it to drivers/staging and label it to die at 2.6.38, if nobody cares enough to port parallel port support to gspca or to create a new driver that uses the same gspca-cpia sub-driver. Signed-off-by: Mauro Carvalho Chehab --- diff --git a/Documentation/feature-removal-schedule.txt b/Documentation/feature-removal-schedule.txt index 2372fb221969..448722555648 100644 --- a/Documentation/feature-removal-schedule.txt +++ b/Documentation/feature-removal-schedule.txt @@ -98,7 +98,7 @@ Who: Pavel Machek --------------------------- What: Video4Linux API 1 ioctls and from Video devices. -When: July 2009 +When: kernel 2.6.38 Files: include/linux/videodev.h Check: include/linux/videodev.h Why: V4L1 AP1 was replaced by V4L2 API during migration from 2.4 to 2.6 @@ -116,6 +116,21 @@ Who: Mauro Carvalho Chehab --------------------------- +What: Video4Linux obsolete drivers using V4L1 API +When: kernel 2.6.38 +Files: drivers/staging/cpia/* +Check: drivers/staging/cpia/cpia.c +Why: There are some drivers still using V4L1 API, despite all efforts we've done + to migrate. Those drivers are for obsolete hardware that the old maintainer + didn't care (or not have the hardware anymore), and that no other developer + could find any hardware to buy. They probably have no practical usage today, + and people with such old hardware could probably keep using an older version + of the kernel. Those drivers will be moved to staging on 2.6.37 and, if nobody + care enough to port and test them with V4L2 API, they'll be removed on 2.6.38. +Who: Mauro Carvalho Chehab + +--------------------------- + What: sys_sysctl When: September 2010 Option: CONFIG_SYSCTL_SYSCALL diff --git a/drivers/media/video/Kconfig b/drivers/media/video/Kconfig index 2c0a8f776f8a..4f21ef877f37 100644 --- a/drivers/media/video/Kconfig +++ b/drivers/media/video/Kconfig @@ -599,46 +599,6 @@ config VIDEO_W9966 Check out for more information. -config VIDEO_CPIA - tristate "CPiA Video For Linux (DEPRECATED)" - depends on VIDEO_V4L1 - default n - ---help--- - This driver is DEPRECATED please use the gspca cpia1 module - instead. Note that you need atleast version 0.6.4 of libv4l for - the cpia1 gspca module. - - 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. - - Please read for more - information. - - This driver is also available as a module (cpia). - -config VIDEO_CPIA_PP - tristate "CPiA Parallel Port Lowlevel Support" - depends on PARPORT_1284 && VIDEO_CPIA && PARPORT - help - 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). - -config VIDEO_CPIA_USB - tristate "CPiA USB Lowlevel Support" - depends on VIDEO_CPIA && USB - help - 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). - source "drivers/media/video/cpia2/Kconfig" config VIDEO_VINO diff --git a/drivers/media/video/Makefile b/drivers/media/video/Makefile index a8f89899dd6b..b947160a8cb0 100644 --- a/drivers/media/video/Makefile +++ b/drivers/media/video/Makefile @@ -92,9 +92,6 @@ obj-$(CONFIG_VIDEO_W9966) += w9966.o obj-$(CONFIG_VIDEO_PMS) += pms.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_VIDEO_MEYE) += meye.o obj-$(CONFIG_VIDEO_SAA7134) += saa7134/ obj-$(CONFIG_VIDEO_CX88) += cx88/ diff --git a/drivers/media/video/cpia.c b/drivers/media/video/cpia.c deleted file mode 100644 index 933ae4c8cb9a..000000000000 --- a/drivers/media/video/cpia.c +++ /dev/null @@ -1,4032 +0,0 @@ -/* - * 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 - * (C) Copyright 2000 STMicroelectronics - * - * 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_ for verbose debug output (see cpia.h) */ -/* #define _CPIA_DEBUG_ 1 */ - - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "cpia.h" - -static int video_nr = -1; - -#ifdef MODULE -module_param(video_nr, int, 0); -MODULE_AUTHOR("Scott J. Bertin & Peter Pregler & Johannes Erdfelt "); -MODULE_DESCRIPTION("V4L-driver for Vision CPiA based cameras"); -MODULE_LICENSE("GPL"); -MODULE_SUPPORTED_DEVICE("video"); -#endif - -static unsigned short colorspace_conv; -module_param(colorspace_conv, ushort, 0444); -MODULE_PARM_DESC(colorspace_conv, - " Colorspace conversion:" - "\n 0 = disable, 1 = enable" - "\n Default value is 0" - ); - -#define ABOUT "V4L-Driver for Vision CPiA based cameras" - -#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_ResetFrameCounter (INPUT | CPIA_MODULE_VP_CTRL | 2) -#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_GrabReset (OUTPUT | CPIA_MODULE_CAPTURE | 15) - -#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) -#define CPIA_COMMAND_Null (OUTPUT | CPIA_MODULE_DEBUG | 11) - -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 -#define COMMAND_SETLIGHTS 0x8000 - -#define ROUND_UP_EXP_FOR_FLICKER 15 - -/* Constants for automatic frame rate adjustment */ -#define MAX_EXP 302 -#define MAX_EXP_102 255 -#define LOW_EXP 140 -#define VERY_LOW_EXP 70 -#define TC 94 -#define EXP_ACC_DARK 50 -#define EXP_ACC_LIGHT 90 -#define HIGH_COMP_102 160 -#define MAX_COMP 239 -#define DARK_TIME 3 -#define LIGHT_TIME 3 - -/* Maximum number of 10ms loops to wait for the stream to become ready */ -#define READY_TIMEOUT 100 - -/* 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); -static int find_over_exposure(int brightness); -static void set_flicker(struct cam_params *params, volatile u32 *command_flags, - int on); - - -/********************************************************************** - * - * Memory management - * - **********************************************************************/ -static void *rvmalloc(unsigned long size) -{ - void *mem; - unsigned long adr; - - size = PAGE_ALIGN(size); - mem = vmalloc_32(size); - if (!mem) - return NULL; - - memset(mem, 0, size); /* Clear the ram out, no junk to the user */ - adr = (unsigned long) mem; - while (size > 0) { - SetPageReserved(vmalloc_to_page((void *)adr)); - adr += PAGE_SIZE; - size -= PAGE_SIZE; - } - - return mem; -} - -static void rvfree(void *mem, unsigned long size) -{ - unsigned long adr; - - if (!mem) - return; - - adr = (unsigned long) mem; - while ((long) size > 0) { - ClearPageReserved(vmalloc_to_page((void *)adr)); - adr += PAGE_SIZE; - size -= PAGE_SIZE; - } - vfree(mem); -} - -/********************************************************************** - * - * /proc interface - * - **********************************************************************/ -#ifdef CONFIG_PROC_FS -static struct proc_dir_entry *cpia_proc_root=NULL; - -static int cpia_proc_show(struct seq_file *m, void *v) -{ - struct cam_data *cam = m->private; - int tmp; - char tmpstr[29]; - - seq_printf(m, "read-only\n-----------------------\n"); - seq_printf(m, "V4L Driver version: %d.%d.%d\n", - CPIA_MAJ_VER, CPIA_MIN_VER, CPIA_PATCH_VER); - seq_printf(m, "CPIA Version: %d.%02d (%d.%d)\n", - cam->params.version.firmwareVersion, - cam->params.version.firmwareRevision, - cam->params.version.vcVersion, - cam->params.version.vcRevision); - seq_printf(m, "CPIA PnP-ID: %04x:%04x:%04x\n", - cam->params.pnpID.vendor, cam->params.pnpID.product, - cam->params.pnpID.deviceRevision); - seq_printf(m, "VP-Version: %d.%d %04x\n", - cam->params.vpVersion.vpVersion, - cam->params.vpVersion.vpRevision, - cam->params.vpVersion.cameraHeadID); - - seq_printf(m, "system_state: %#04x\n", - cam->params.status.systemState); - seq_printf(m, "grab_state: %#04x\n", - cam->params.status.grabState); - seq_printf(m, "stream_state: %#04x\n", - cam->params.status.streamState); - seq_printf(m, "fatal_error: %#04x\n", - cam->params.status.fatalError); - seq_printf(m, "cmd_error: %#04x\n", - cam->params.status.cmdError); - seq_printf(m, "debug_flags: %#04x\n", - cam->params.status.debugFlags); - seq_printf(m, "vp_status: %#04x\n", - cam->params.status.vpStatus); - seq_printf(m, "error_code: %#04x\n", - cam->params.status.errorCode); - /* QX3 specific entries */ - if (cam->params.qx3.qx3_detected) { - seq_printf(m, "button: %4d\n", - cam->params.qx3.button); - seq_printf(m, "cradled: %4d\n", - cam->params.qx3.cradled); - } - seq_printf(m, "video_size: %s\n", - cam->params.format.videoSize == VIDEOSIZE_CIF ? - "CIF " : "QCIF"); - seq_printf(m, "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); - seq_printf(m, "actual_fps: %3d\n", cam->fps); - seq_printf(m, "transfer_rate: %4dkB/s\n", - cam->transfer_rate); - - seq_printf(m, "\nread-write\n"); - seq_printf(m, "----------------------- current min" - " max default comment\n"); - seq_printf(m, "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; - - seq_printf(m, "contrast: %8d %8d %8d %8d" - " steps of 8\n", - cam->params.colourParams.contrast, 0, tmp, 48); - seq_printf(m, "saturation: %8d %8d %8d %8d\n", - cam->params.colourParams.saturation, 0, 100, 50); - tmp = (25000+5000*cam->params.sensorFps.baserate)/ - (1<params.sensorFps.divisor); - seq_printf(m, "sensor_fps: %4d.%03d %8d %8d %8d\n", - tmp/1000, tmp%1000, 3, 30, 15); - seq_printf(m, "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); - seq_printf(m, "sub_sample: %8s %8s %8s %8s\n", - cam->params.format.subSample == SUBSAMPLE_420 ? - "420" : "422", "420", "422", "422"); - seq_printf(m, "yuv_order: %8s %8s %8s %8s\n", - cam->params.format.yuvOrder == YUVORDER_YUYV ? - "YUYV" : "UYVY", "YUYV" , "UYVY", "YUYV"); - seq_printf(m, "ecp_timing: %8s %8s %8s %8s\n", - cam->params.ecpTiming ? "slow" : "normal", "slow", - "normal", "normal"); - - if (cam->params.colourBalance.balanceMode == 2) { - sprintf(tmpstr, "auto"); - } else { - sprintf(tmpstr, "manual"); - } - seq_printf(m, "color_balance_mode: %8s %8s %8s" - " %8s\n", tmpstr, "manual", "auto", "auto"); - seq_printf(m, "red_gain: %8d %8d %8d %8d\n", - cam->params.colourBalance.redGain, 0, 212, 32); - seq_printf(m, "green_gain: %8d %8d %8d %8d\n", - cam->params.colourBalance.greenGain, 0, 212, 6); - seq_printf(m, "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 %8d", 1, 2, 2); - else - sprintf(tmpstr, "%8d %8d %8d", 1, 8, 2); - - if (cam->params.exposure.gainMode == 0) - seq_printf(m, "max_gain: unknown %28s" - " powers of 2\n", tmpstr); - else - seq_printf(m, "max_gain: %8d %28s" - " 1,2,4 or 8 \n", - 1<<(cam->params.exposure.gainMode-1), tmpstr); - - switch(cam->params.exposure.expMode) { - case 1: - case 3: - sprintf(tmpstr, "manual"); - break; - case 2: - sprintf(tmpstr, "auto"); - break; - default: - sprintf(tmpstr, "unknown"); - break; - } - seq_printf(m, "exposure_mode: %8s %8s %8s" - " %8s\n", tmpstr, "manual", "auto", "auto"); - seq_printf(m, "centre_weight: %8s %8s %8s %8s\n", - (2-cam->params.exposure.centreWeight) ? "on" : "off", - "off", "on", "on"); - seq_printf(m, "gain: %8d %8d max_gain %8d 1,2,4,8 possible\n", - 1<params.exposure.gain, 1, 1); - if (cam->params.version.firmwareVersion == 1 && - cam->params.version.firmwareRevision == 2) - /* 1-02 firmware limits fineExp/2 to 127 */ - tmp = 254; - else - tmp = 510; - - seq_printf(m, "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 = MAX_EXP_102; - else - tmp = MAX_EXP; - - seq_printf(m, "coarse_exp: %8d %8d %8d" - " %8d\n", cam->params.exposure.coarseExpLo+ - 256*cam->params.exposure.coarseExpHi, 0, tmp, 185); - seq_printf(m, "red_comp: %8d %8d %8d %8d\n", - cam->params.exposure.redComp, COMP_RED, 255, COMP_RED); - seq_printf(m, "green1_comp: %8d %8d %8d %8d\n", - cam->params.exposure.green1Comp, COMP_GREEN1, 255, - COMP_GREEN1); - seq_printf(m, "green2_comp: %8d %8d %8d %8d\n", - cam->params.exposure.green2Comp, COMP_GREEN2, 255, - COMP_GREEN2); - seq_printf(m, "blue_comp: %8d %8d %8d %8d\n", - cam->params.exposure.blueComp, COMP_BLUE, 255, COMP_BLUE); - - seq_printf(m, "apcor_gain1: %#8x %#8x %#8x %#8x\n", - cam->params.apcor.gain1, 0, 0xff, 0x1c); - seq_printf(m, "apcor_gain2: %#8x %#8x %#8x %#8x\n", - cam->params.apcor.gain2, 0, 0xff, 0x1a); - seq_printf(m, "apcor_gain4: %#8x %#8x %#8x %#8x\n", - cam->params.apcor.gain4, 0, 0xff, 0x2d); - seq_printf(m, "apcor_gain8: %#8x %#8x %#8x %#8x\n", - cam->params.apcor.gain8, 0, 0xff, 0x2a); - seq_printf(m, "vl_offset_gain1: %8d %8d %8d %8d\n", - cam->params.vlOffset.gain1, 0, 255, 24); - seq_printf(m, "vl_offset_gain2: %8d %8d %8d %8d\n", - cam->params.vlOffset.gain2, 0, 255, 28); - seq_printf(m, "vl_offset_gain4: %8d %8d %8d %8d\n", - cam->params.vlOffset.gain4, 0, 255, 30); - seq_printf(m, "vl_offset_gain8: %8d %8d %8d %8d\n", - cam->params.vlOffset.gain8, 0, 255, 30); - seq_printf(m, "flicker_control: %8s %8s %8s %8s\n", - cam->params.flickerControl.flickerMode ? "on" : "off", - "off", "on", "off"); - seq_printf(m, "mains_frequency: %8d %8d %8d %8d" - " only 50/60\n", - cam->mainsFreq ? 60 : 50, 50, 60, 50); - if(cam->params.flickerControl.allowableOverExposure < 0) - seq_printf(m, "allowable_overexposure: %4dauto auto %8d auto\n", - -cam->params.flickerControl.allowableOverExposure, - 255); - else - seq_printf(m, "allowable_overexposure: %8d auto %8d auto\n", - cam->params.flickerControl.allowableOverExposure, - 255); - seq_printf(m, "compression_mode: "); - switch(cam->params.compression.mode) { - case CPIA_COMPRESSION_NONE: - seq_printf(m, "%8s", "none"); - break; - case CPIA_COMPRESSION_AUTO: - seq_printf(m, "%8s", "auto"); - break; - case CPIA_COMPRESSION_MANUAL: - seq_printf(m, "%8s", "manual"); - break; - default: - seq_printf(m, "%8s", "unknown"); - break; - } - seq_printf(m, " none,auto,manual auto\n"); - seq_printf(m, "decimation_enable: %8s %8s %8s %8s\n", - cam->params.compression.decimation == - DECIMATION_ENAB ? "on":"off", "off", "on", - "off"); - seq_printf(m, "compression_target: %9s %9s %9s %9s\n", - cam->params.compressionTarget.frTargeting == - CPIA_COMPRESSION_TARGET_FRAMERATE ? - "framerate":"quality", - "framerate", "quality", "quality"); - seq_printf(m, "target_framerate: %8d %8d %8d %8d\n", - cam->params.compressionTarget.targetFR, 1, 30, 15); - seq_printf(m, "target_quality: %8d %8d %8d %8d\n", - cam->params.compressionTarget.targetQ, 1, 64, 5); - seq_printf(m, "y_threshold: %8d %8d %8d %8d\n", - cam->params.yuvThreshold.yThreshold, 0, 31, 6); - seq_printf(m, "uv_threshold: %8d %8d %8d %8d\n", - cam->params.yuvThreshold.uvThreshold, 0, 31, 6); - seq_printf(m, "hysteresis: %8d %8d %8d %8d\n", - cam->params.compressionParams.hysteresis, 0, 255, 3); - seq_printf(m, "threshold_max: %8d %8d %8d %8d\n", - cam->params.compressionParams.threshMax, 0, 255, 11); - seq_printf(m, "small_step: %8d %8d %8d %8d\n", - cam->params.compressionParams.smallStep, 0, 255, 1); - seq_printf(m, "large_step: %8d %8d %8d %8d\n", - cam->params.compressionParams.largeStep, 0, 255, 3); - seq_printf(m, "decimation_hysteresis: %8d %8d %8d %8d\n", - cam->params.compressionParams.decimationHysteresis, - 0, 255, 2); - seq_printf(m, "fr_diff_step_thresh: %8d %8d %8d %8d\n", - cam->params.compressionParams.frDiffStepThresh, - 0, 255, 5); - seq_printf(m, "q_diff_step_thresh: %8d %8d %8d %8d\n", - cam->params.compressionParams.qDiffStepThresh, - 0, 255, 3); - seq_printf(m, "decimation_thresh_mod: %8d %8d %8d %8d\n", - cam->params.compressionParams.decimationThreshMod, - 0, 255, 2); - /* QX3 specific entries */ - if (cam->params.qx3.qx3_detected) { - seq_printf(m, "toplight: %8s %8s %8s %8s\n", - cam->params.qx3.toplight ? "on" : "off", - "off", "on", "off"); - seq_printf(m, "bottomlight: %8s %8s %8s %8s\n", - cam->params.qx3.bottomlight ? "on" : "off", - "off", "on", "off"); - } - - return 0; -} - -static int cpia_proc_open(struct inode *inode, struct file *file) -{ - return single_open(file, cpia_proc_show, PDE(inode)->data); -} - -static int match(char *checkstr, char **buffer, size_t *count, - int *find_colon, int *err) -{ - int ret, colon_found = 1; - int len = strlen(checkstr); - ret = (len <= *count && strncmp(*buffer, checkstr, 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) - *err = -EINVAL; - *find_colon = 0; - } - } - return ret; -} - -static unsigned long int value(char **buffer, size_t *count, int *err) -{ - char *p; - unsigned long int ret; - ret = simple_strtoul(*buffer, &p, 0); - if (p == *buffer) - *err = -EINVAL; - else { - *count -= p - *buffer; - *buffer = p; - } - return ret; -} - -static ssize_t cpia_proc_write(struct file *file, const char __user *buf, - size_t count, loff_t *pos) -{ - struct cam_data *cam = PDE(file->f_path.dentry->d_inode)->data; - struct cam_params new_params; - char *page, *buffer; - int retval, find_colon; - int size = count; - unsigned long val = 0; - u32 command_flags = 0; - u8 new_mains; - - /* - * This code to copy from buf to page is shamelessly copied - * from the comx driver - */ - if (count > PAGE_SIZE) { - printk(KERN_ERR "count is %zu > %d!!!\n", count, (int)PAGE_SIZE); - return -ENOSPC; - } - - if (!(page = (char *)__get_free_page(GFP_KERNEL))) return -ENOMEM; - - if(copy_from_user(page, buf, count)) - { - retval = -EFAULT; - goto out; - } - - if (page[count-1] == '\n') - page[count-1] = '\0'; - else if (count < PAGE_SIZE) - page[count] = '\0'; - else if (page[count]) { - retval = -EINVAL; - goto out; - } - - buffer = page; - - if (mutex_lock_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) (match(x, &buffer, &count, &find_colon, &retval)) -#define VALUE (value(&buffer,&count, &retval)) -#define FIRMWARE_VERSION(x,y) (new_params.version.firmwareVersion == (x) && \ - new_params.version.firmwareRevision == (y)) - - 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; - if(new_params.flickerControl.allowableOverExposure < 0) - new_params.flickerControl.allowableOverExposure = - -find_over_exposure(new_params.colourParams.brightness); - if(new_params.flickerControl.flickerMode != 0) - command_flags |= COMMAND_SETFLICKERCTRL; - - } 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; - cam->exposure_status = EXPOSURE_NORMAL; - } 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("sub_sample")) { - if (!retval && MATCH("420")) - new_params.format.subSample = SUBSAMPLE_420; - else if (!retval && MATCH("422")) - new_params.format.subSample = SUBSAMPLE_422; - else - retval = -EINVAL; - - command_flags |= COMMAND_SETFORMAT; - } else if (MATCH("yuv_order")) { - if (!retval && MATCH("YUYV")) - new_params.format.yuvOrder = YUVORDER_YUYV; - else if (!retval && MATCH("UYVY")) - new_params.format.yuvOrder = YUVORDER_UYVY; - else - retval = -EINVAL; - - command_flags |= COMMAND_SETFORMAT; - } 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.balanceMode = 3; - else if (!retval && MATCH("auto")) - new_params.colourBalance.balanceMode = 2; - 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; - new_params.colourBalance.balanceMode = 1; - } 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; - new_params.colourBalance.balanceMode = 1; - } 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; - new_params.colourBalance.balanceMode = 1; - } 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; - if(new_params.flickerControl.flickerMode != 0) - command_flags |= COMMAND_SETFLICKERCTRL; - new_params.flickerControl.flickerMode = 0; - } 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; - break; - case 2: - new_params.exposure.gain = 1; - break; - case 4: - new_params.exposure.gain = 2; - break; - case 8: - new_params.exposure.gain = 3; - break; - default: - retval = -EINVAL; - break; - } - new_params.exposure.expMode = 1; - if(new_params.flickerControl.flickerMode != 0) - command_flags |= COMMAND_SETFLICKERCTRL; - new_params.flickerControl.flickerMode = 0; - 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/2; - - if (!retval) { - if (val < 256) { - /* 1-02 firmware limits fineExp/2 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; - if(new_params.flickerControl.flickerMode != 0) - command_flags |= COMMAND_SETFLICKERCTRL; - 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 <= MAX_EXP) { - if (FIRMWARE_VERSION(1,2) && - val > MAX_EXP_102) - val = MAX_EXP_102; - new_params.exposure.coarseExpLo = - val & 0xff; - new_params.exposure.coarseExpHi = - val >> 8; - new_params.exposure.expMode = 1; - command_flags |= COMMAND_SETEXPOSURE; - if(new_params.flickerControl.flickerMode != 0) - command_flags |= COMMAND_SETFLICKERCTRL; - 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 >= COMP_RED && val <= 255) { - new_params.exposure.redComp = val; - new_params.exposure.compMode = 1; - command_flags |= COMMAND_SETEXPOSURE; - } else - retval = -EINVAL; - } - } else if (MATCH("green1_comp")) { - if (!retval) - val = VALUE; - - if (!retval) { - if (val >= COMP_GREEN1 && val <= 255) { - new_params.exposure.green1Comp = val; - new_params.exposure.compMode = 1; - command_flags |= COMMAND_SETEXPOSURE; - } else - retval = -EINVAL; - } - } else if (MATCH("green2_comp")) { - if (!retval) - val = VALUE; - - if (!retval) { - if (val >= COMP_GREEN2 && val <= 255) { - new_params.exposure.green2Comp = val; - new_params.exposure.compMode = 1; - command_flags |= COMMAND_SETEXPOSURE; - } else - retval = -EINVAL; - } - } else if (MATCH("blue_comp")) { - if (!retval) - val = VALUE; - - if (!retval) { - if (val >= COMP_BLUE && val <= 255) { - new_params.exposure.blueComp = val; - new_params.exposure.compMode = 1; - 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")) { - set_flicker(&new_params, &command_flags, 1); - } else if (!retval && MATCH("off")) { - set_flicker(&new_params, &command_flags, 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 && MATCH("auto")) { - new_params.flickerControl.allowableOverExposure = - -find_over_exposure(new_params.colourParams.brightness); - if(new_params.flickerControl.flickerMode != 0) - command_flags |= COMMAND_SETFLICKERCTRL; - } else { - if (!retval) - val = VALUE; - - if (!retval) { - if (val <= 0xff) { - new_params.flickerControl. - allowableOverExposure = val; - if(new_params.flickerControl.flickerMode != 0) - 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 if (!retval && MATCH("on")) - new_params.compression.decimation = 1; - 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) { - if(val > 0 && val <= 30) - new_params.compressionTarget.targetFR = val; - else - retval = -EINVAL; - } - command_flags |= COMMAND_SETCOMPRESSIONTARGET; - } else if (MATCH("target_quality")) { - if (!retval) - val = VALUE; - - if (!retval) { - if(val > 0 && val <= 64) - new_params.compressionTarget.targetQ = val; - else - retval = -EINVAL; - } - 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 if (MATCH("toplight")) { - if (!retval && MATCH("on")) - new_params.qx3.toplight = 1; - else if (!retval && MATCH("off")) - new_params.qx3.toplight = 0; - else - retval = -EINVAL; - command_flags |= COMMAND_SETLIGHTS; - } else if (MATCH("bottomlight")) { - if (!retval && MATCH("on")) - new_params.qx3.bottomlight = 1; - else if (!retval && MATCH("off")) - new_params.qx3.bottomlight = 0; - else - retval = -EINVAL; - command_flags |= COMMAND_SETLIGHTS; - } else { - DBG("No match found\n"); - retval = -EINVAL; - } - - if (!retval) { - while (count && isspace(*buffer) && *buffer != '\n') { - --count; - ++buffer; - } - if (count) { - if (*buffer == '\0' && count != 1) - retval = -EINVAL; - else if (*buffer != '\n' && *buffer != ';' && - *buffer != '\0') - retval = -EINVAL; - else { - --count; - ++buffer; - } - } - } - } -#undef MATCH -#undef VALUE -#undef FIRMWARE_VERSION - 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; - } - if((command_flags & COMMAND_SETEXPOSURE) && - new_params.exposure.expMode == 2) - cam->exposure_status = EXPOSURE_NORMAL; - - 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); - - mutex_unlock(&cam->param_lock); - -out: - free_page((unsigned long)page); - return retval; -} - -static const struct file_operations cpia_proc_fops = { - .owner = THIS_MODULE, - .open = cpia_proc_open, - .read = seq_read, - .llseek = seq_lseek, - .release = single_release, - .write = cpia_proc_write, -}; - -static void create_proc_cpia_cam(struct cam_data *cam) -{ - struct proc_dir_entry *ent; - - if (!cpia_proc_root || !cam) - return; - - ent = proc_create_data(video_device_node_name(&cam->vdev), - S_IRUGO|S_IWUSR, cpia_proc_root, - &cpia_proc_fops, cam); - if (!ent) - return; - - /* - size of the proc entry is 3736 bytes for the standard webcam; - the extra features of the QX3 microscope add 189 bytes. - (we have not yet probed the camera to see which type it is). - */ - ent->size = 3736 + 189; - cam->proc_entry = ent; -} - -static void destroy_proc_cpia_cam(struct cam_data *cam) -{ - if (!cam || !cam->proc_entry) - return; - - remove_proc_entry(video_device_node_name(&cam->vdev), cpia_proc_root); - cam->proc_entry = NULL; -} - -static void proc_cpia_create(void) -{ - cpia_proc_root = proc_mkdir("cpia", NULL); - - if (!cpia_proc_root) - LOG("Unable to initialise /proc/cpia\n"); -} - -static void __exit proc_cpia_destroy(void) -{ - remove_proc_entry("cpia", NULL); -} -#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) -{ - if ((palette == VIDEO_PALETTE_YUV422 && depth == 16) || - (palette == VIDEO_PALETTE_YUYV && depth == 16)) - return 1; - - if (colorspace_conv) - 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_UYVY && depth == 16); - - return 0; -} - -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.rowStart=0; - 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.rowStart=6; - 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.rowStart=9; - 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.rowStart=12; - 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.rowStart=15; - 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.rowStart=18; - 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.rowStart=0; - 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.rowStart=3; - 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.rowStart=6; - 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.rowStart=9; - 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.rowStart=12; - 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.rowStart=6; - cam->params.streamStartLine = 60; - break; - default: - LOG("bad videosize value: %d\n", cam->video_size); - return; - } - - if(cam->vc.width == 0) - cam->vc.width = cam->vw.width; - if(cam->vc.height == 0) - cam->vc.height = cam->vw.height; - - cam->params.roi.colStart += cam->vc.x >> 3; - cam->params.roi.colEnd = cam->params.roi.colStart + - (cam->vc.width >> 3); - cam->params.roi.rowStart += cam->vc.y >> 2; - cam->params.roi.rowEnd = cam->params.roi.rowStart + - (cam->vc.height >> 2); - - 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 = NULL; - for (i=0; i < FRAME_NUM; i++) - cam->frame[i].data = NULL; - - return 0; -} - - -static inline void 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: - mutex_lock(&cam->param_lock); - datasize=8; - break; - case CPIA_COMMAND_ReadMCPorts: - case CPIA_COMMAND_ReadVCRegs: - datasize = 4; - 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); - if (command == CPIA_COMMAND_GetColourParams || - command == CPIA_COMMAND_GetColourBalance || - command == CPIA_COMMAND_GetExposure) - mutex_unlock(&cam->param_lock); - } 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]; - mutex_unlock(&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]; - mutex_unlock(&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]; - mutex_unlock(&cam->param_lock); - break; - - case CPIA_COMMAND_ReadMCPorts: - if (!cam->params.qx3.qx3_detected) - break; - /* test button press */ - cam->params.qx3.button = ((data[1] & 0x02) == 0); - if (cam->params.qx3.button) { - /* button pressed - unlock the latch */ - do_command(cam,CPIA_COMMAND_WriteMCPort,3,0xDF,0xDF,0); - do_command(cam,CPIA_COMMAND_WriteMCPort,3,0xFF,0xFF,0); - } - - /* test whether microscope is cradled */ - cam->params.qx3.cradled = ((data[2] & 0x40) == 0); - 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) - DBG("%x - failed\n", command); - - return retval; -} - -/********************************************************************** - * - * Colorspace conversion - * - **********************************************************************/ -#define LIMIT(x) ((((x)>0xffffff)?0xff0000:(((x)<=0xffff)?0:(x)&0xff0000))>>16) - -static int convert420(unsigned char *yuv, unsigned char *rgb, int out_fmt, - int linesize, int mmap_kludge) -{ - int y, u, v, r, g, b, y1; - - /* Odd lines use the same u and v as the previous line. - * Because of compression, it is necessary to get this - * information from the decoded image. */ - switch(out_fmt) { - case VIDEO_PALETTE_RGB555: - y = (*yuv++ - 16) * 76310; - y1 = (*yuv - 16) * 76310; - r = ((*(rgb+1-linesize)) & 0x7c) << 1; - g = ((*(rgb-linesize)) & 0xe0) >> 4 | - ((*(rgb+1-linesize)) & 0x03) << 6; - b = ((*(rgb-linesize)) & 0x1f) << 3; - u = (-53294 * r - 104635 * g + 157929 * b) / 5756495; - v = (157968 * r - 132278 * g - 25690 * b) / 5366159; - r = 104635 * v; - g = -25690 * u - 53294 * v; - b = 132278 * u; - *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: - y = (*yuv++ - 16) * 76310; - y1 = (*yuv - 16) * 76310; - r = (*(rgb+1-linesize)) & 0xf8; - g = ((*(rgb-linesize)) & 0xe0) >> 3 | - ((*(rgb+1-linesize)) & 0x07) << 5; - b = ((*(rgb-linesize)) & 0x1f) << 3; - u = (-53294 * r - 104635 * g + 157929 * b) / 5756495; - v = (157968 * r - 132278 * g - 25690 * b) / 5366159; - r = 104635 * v; - g = -25690 * u - 53294 * v; - b = 132278 * u; - *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; - break; - case VIDEO_PALETTE_RGB24: - case VIDEO_PALETTE_RGB32: - y = (*yuv++ - 16) * 76310; - y1 = (*yuv - 16) * 76310; - if (mmap_kludge) { - r = *(rgb+2-linesize); - g = *(rgb+1-linesize); - b = *(rgb-linesize); - } else { - r = *(rgb-linesize); - g = *(rgb+1-linesize); - b = *(rgb+2-linesize); - } - u = (-53294 * r - 104635 * g + 157929 * b) / 5756495; - v = (157968 * r - 132278 * g - 25690 * b) / 5366159; - r = 104635 * v; - g = -25690 * u + -53294 * v; - b = 132278 * u; - if (mmap_kludge) { - *rgb++ = LIMIT(b+y); - *rgb++ = LIMIT(g+y); - *rgb++ = LIMIT(r+y); - if(out_fmt == VIDEO_PALETTE_RGB32) - 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); - if(out_fmt == VIDEO_PALETTE_RGB32) - rgb++; - *rgb++ = LIMIT(r+y1); - *rgb++ = LIMIT(g+y1); - *rgb = LIMIT(b+y1); - } - if(out_fmt == VIDEO_PALETTE_RGB32) - return 8; - return 6; - case VIDEO_PALETTE_YUV422: - case VIDEO_PALETTE_YUYV: - y = *yuv++; - u = *(rgb+1-linesize); - y1 = *yuv; - v = *(rgb+3-linesize); - *rgb++ = y; - *rgb++ = u; - *rgb++ = y1; - *rgb = v; - return 4; - case VIDEO_PALETTE_UYVY: - u = *(rgb-linesize); - y = *yuv++; - v = *(rgb+2-linesize); - y1 = *yuv; - *rgb++ = u; - *rgb++ = y; - *rgb++ = v; - *rgb = y1; - return 4; - case VIDEO_PALETTE_GREY: - *rgb++ = *yuv++; - *rgb = *yuv; - return 2; - default: - DBG("Empty: %d\n", out_fmt); - return 0; - } -} - - -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: - return count; - 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, decimation, even_line, origsize, out_fmt; - int rows, cols, linesize, subsample_422; - - /* make sure params don't change while we are decoding */ - mutex_lock(&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"); - mutex_unlock(&cam->param_lock); - return -1; - } - - if ((ibuf[16] != VIDEOSIZE_QCIF) && (ibuf[16] != VIDEOSIZE_CIF)) { - LOG("wrong video size\n"); - mutex_unlock(&cam->param_lock); - return -1; - } - - if (ibuf[17] != SUBSAMPLE_420 && ibuf[17] != SUBSAMPLE_422) { - LOG("illegal subtype %d\n",ibuf[17]); - mutex_unlock(&cam->param_lock); - return -1; - } - subsample_422 = ibuf[17] == SUBSAMPLE_422; - - if (ibuf[18] != YUVORDER_YUYV && ibuf[18] != YUVORDER_UYVY) { - LOG("illegal yuvorder %d\n",ibuf[18]); - mutex_unlock(&cam->param_lock); - return -1; - } - in_uyvy = ibuf[18] == YUVORDER_UYVY; - - 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"); - mutex_unlock(&cam->param_lock); - return -1; - } - cols = 8*(ibuf[25] - ibuf[24]); - rows = 4*(ibuf[27] - ibuf[26]); - - - if ((ibuf[28] != NOT_COMPRESSED) && (ibuf[28] != COMPRESSED)) { - LOG("illegal compression %d\n",ibuf[28]); - mutex_unlock(&cam->param_lock); - return -1; - } - compressed = (ibuf[28] == COMPRESSED); - - if (ibuf[29] != NO_DECIMATION && ibuf[29] != DECIMATION_ENAB) { - LOG("illegal decimation %d\n",ibuf[29]); - mutex_unlock(&cam->param_lock); - return -1; - } - decimation = (ibuf[29] == DECIMATION_ENAB); - - 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]; - mutex_unlock(&cam->param_lock); - - linesize = skipcount(cols, out_fmt); - ibuf += FRAME_HEADER_SIZE; - size -= FRAME_HEADER_SIZE; - ll = ibuf[0] | (ibuf[1] << 8); - ibuf += 2; - even_line = 1; - - 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))) { - if(subsample_422 || even_line) { - obuf += yuvconvert(ibuf, obuf, out_fmt, - in_uyvy, cam->mmap_kludge); - ibuf += 4; - ll -= 4; - } else { - /* SUBSAMPLE_420 on an odd line */ - obuf += convert420(ibuf, obuf, - out_fmt, linesize, - cam->mmap_kludge); - ibuf += 2; - ll -= 2; - } - } else { - /*skip compressed interval from previous frame*/ - obuf += skipcount(*ibuf >> 1, out_fmt); - if (obuf > end_obuf) { - LOG("Insufficient buffer size\n"); - return -1; - } - ++ibuf; - ll--; - } - } - if (ll == 1) { - if (*ibuf != EOL) { - DBG("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(decimation) { - /* skip the odd lines for now */ - obuf += linesize; - } - - if (size > 1) { - ll = ibuf[0] | (ibuf[1] << 8); - ibuf += 2; /* skip over line length */ - } - if(!decimation) - even_line = !even_line; - } else { - LOG("line length was not 1 but %d after %d/%d bytes\n", - ll, origsize-size, origsize); - return -1; - } - } - - if(decimation) { - /* interpolate odd rows */ - int i, j; - u8 *prev, *next; - prev = cam->decompressed_frame.data; - obuf = prev+linesize; - next = obuf+linesize; - for(i=1; idecompressed_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); -} - - -/* find_over_exposure - * Finds a suitable value of OverExposure for use with SetFlickerCtrl - * Some calculation is required because this value changes with the brightness - * set with SetColourParameters - * - * Parameters: Brightness - last brightness value set with SetColourParameters - * - * Returns: OverExposure value to use with SetFlickerCtrl - */ -#define FLICKER_MAX_EXPOSURE 250 -#define FLICKER_ALLOWABLE_OVER_EXPOSURE 146 -#define FLICKER_BRIGHTNESS_CONSTANT 59 -static int find_over_exposure(int brightness) -{ - int MaxAllowableOverExposure, OverExposure; - - MaxAllowableOverExposure = FLICKER_MAX_EXPOSURE - brightness - - FLICKER_BRIGHTNESS_CONSTANT; - - if (MaxAllowableOverExposure < FLICKER_ALLOWABLE_OVER_EXPOSURE) { - OverExposure = MaxAllowableOverExposure; - } else { - OverExposure = FLICKER_ALLOWABLE_OVER_EXPOSURE; - } - - return OverExposure; -} -#undef FLICKER_MAX_EXPOSURE -#undef FLICKER_ALLOWABLE_OVER_EXPOSURE -#undef FLICKER_BRIGHTNESS_CONSTANT - -/* update various camera modes and settings */ -static void dispatch_commands(struct cam_data *cam) -{ - mutex_lock(&cam->param_lock); - if (cam->cmd_queue==COMMAND_NONE) { - mutex_unlock(&cam->param_lock); - return; - } - DEB_BYTE(cam->cmd_queue); - DEB_BYTE(cam->cmd_queue>>8); - 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_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_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_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_SETEXPOSURE) { - do_command_extended(cam, CPIA_COMMAND_SetExposure, - cam->params.exposure.gainMode, - 1, - 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->params.exposure.expMode != 1) { - do_command_extended(cam, CPIA_COMMAND_SetExposure, - 0, - cam->params.exposure.expMode, - 0, 0, - cam->params.exposure.gain, - cam->params.exposure.fineExp, - cam->params.exposure.coarseExpLo, - cam->params.exposure.coarseExpHi, - 0, 0, 0, 0); - } - } - - if (cam->cmd_queue & COMMAND_SETCOLOURBALANCE) { - if (cam->params.colourBalance.balanceMode == 1) { - 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->params.colourBalance.balanceMode == 2) { - do_command(cam, CPIA_COMMAND_SetColourBalance, - 2, 0, 0, 0); - } - if (cam->params.colourBalance.balanceMode == 3) { - do_command(cam, CPIA_COMMAND_SetColourBalance, - 3, 0, 0, 0); - } - } - - 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_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_SETCOMPRESSION) - do_command(cam, CPIA_COMMAND_SetCompression, - cam->params.compression.mode, - cam->params.compression.decimation, 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_SETFLICKERCTRL) - do_command(cam, CPIA_COMMAND_SetFlickerCtrl, - cam->params.flickerControl.flickerMode, - cam->params.flickerControl.coarseJump, - abs(cam->params.flickerControl.allowableOverExposure), - 0); - - if (cam->cmd_queue & COMMAND_SETECPTIMING) - do_command(cam, CPIA_COMMAND_SetECPTiming, - cam->params.ecpTiming, 0, 0, 0); - - 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); - - if (cam->cmd_queue & COMMAND_SETLIGHTS && cam->params.qx3.qx3_detected) - { - int p1 = (cam->params.qx3.bottomlight == 0) << 1; - int p2 = (cam->params.qx3.toplight == 0) << 3; - do_command(cam, CPIA_COMMAND_WriteVCReg, 0x90, 0x8F, 0x50, 0); - do_command(cam, CPIA_COMMAND_WriteMCPort, 2, 0, (p1|p2|0xE0), 0); - } - - cam->cmd_queue = COMMAND_NONE; - mutex_unlock(&cam->param_lock); - return; -} - - - -static void set_flicker(struct cam_params *params, volatile u32 *command_flags, - int on) -{ - /* Everything in here is from the Windows driver */ -#define FIRMWARE_VERSION(x,y) (params->version.firmwareVersion == (x) && \ - params->version.firmwareRevision == (y)) -/* define for compgain calculation */ -#if 0 -#define COMPGAIN(base, curexp, newexp) \ - (u8) ((((float) base - 128.0) * ((float) curexp / (float) newexp)) + 128.5) -#define EXP_FROM_COMP(basecomp, curcomp, curexp) \ - (u16)((float)curexp * (float)(u8)(curcomp + 128) / (float)(u8)(basecomp - 128)) -#else - /* equivalent functions without floating point math */ -#define COMPGAIN(base, curexp, newexp) \ - (u8)(128 + (((u32)(2*(base-128)*curexp + newexp)) / (2* newexp)) ) -#define EXP_FROM_COMP(basecomp, curcomp, curexp) \ - (u16)(((u32)(curexp * (u8)(curcomp + 128)) / (u8)(basecomp - 128))) -#endif - - - int currentexp = params->exposure.coarseExpLo + - params->exposure.coarseExpHi*256; - int startexp; - if (on) { - int cj = params->flickerControl.coarseJump; - params->flickerControl.flickerMode = 1; - params->flickerControl.disabled = 0; - if(params->exposure.expMode != 2) - *command_flags |= COMMAND_SETEXPOSURE; - params->exposure.expMode = 2; - currentexp = currentexp << params->exposure.gain; - params->exposure.gain = 0; - /* round down current exposure to nearest value */ - startexp = (currentexp + ROUND_UP_EXP_FOR_FLICKER) / cj; - if(startexp < 1) - startexp = 1; - startexp = (startexp * cj) - 1; - if(FIRMWARE_VERSION(1,2)) - while(startexp > MAX_EXP_102) - startexp -= cj; - else - while(startexp > MAX_EXP) - startexp -= cj; - params->exposure.coarseExpLo = startexp & 0xff; - params->exposure.coarseExpHi = startexp >> 8; - if (currentexp > startexp) { - if (currentexp > (2 * startexp)) - currentexp = 2 * startexp; - params->exposure.redComp = COMPGAIN (COMP_RED, currentexp, startexp); - params->exposure.green1Comp = COMPGAIN (COMP_GREEN1, currentexp, startexp); - params->exposure.green2Comp = COMPGAIN (COMP_GREEN2, currentexp, startexp); - params->exposure.blueComp = COMPGAIN (COMP_BLUE, currentexp, startexp); - } else { - params->exposure.redComp = COMP_RED; - params->exposure.green1Comp = COMP_GREEN1; - params->exposure.green2Comp = COMP_GREEN2; - params->exposure.blueComp = COMP_BLUE; - } - if(FIRMWARE_VERSION(1,2)) - params->exposure.compMode = 0; - else - params->exposure.compMode = 1; - - params->apcor.gain1 = 0x18; - params->apcor.gain2 = 0x18; - params->apcor.gain4 = 0x16; - params->apcor.gain8 = 0x14; - *command_flags |= COMMAND_SETAPCOR; - } else { - params->flickerControl.flickerMode = 0; - params->flickerControl.disabled = 1; - /* Coarse = average of equivalent coarse for each comp channel */ - startexp = EXP_FROM_COMP(COMP_RED, params->exposure.redComp, currentexp); - startexp += EXP_FROM_COMP(COMP_GREEN1, params->exposure.green1Comp, currentexp); - startexp += EXP_FROM_COMP(COMP_GREEN2, params->exposure.green2Comp, currentexp); - startexp += EXP_FROM_COMP(COMP_BLUE, params->exposure.blueComp, currentexp); - startexp = startexp >> 2; - while(startexp > MAX_EXP && - params->exposure.gain < params->exposure.gainMode-1) { - startexp = startexp >> 1; - ++params->exposure.gain; - } - if(FIRMWARE_VERSION(1,2) && startexp > MAX_EXP_102) - startexp = MAX_EXP_102; - if(startexp > MAX_EXP) - startexp = MAX_EXP; - params->exposure.coarseExpLo = startexp&0xff; - params->exposure.coarseExpHi = startexp >> 8; - params->exposure.redComp = COMP_RED; - params->exposure.green1Comp = COMP_GREEN1; - params->exposure.green2Comp = COMP_GREEN2; - params->exposure.blueComp = COMP_BLUE; - params->exposure.compMode = 1; - *command_flags |= COMMAND_SETEXPOSURE; - params->apcor.gain1 = 0x18; - params->apcor.gain2 = 0x16; - params->apcor.gain4 = 0x24; - params->apcor.gain8 = 0x34; - *command_flags |= COMMAND_SETAPCOR; - } - params->vlOffset.gain1 = 20; - params->vlOffset.gain2 = 24; - params->vlOffset.gain4 = 26; - params->vlOffset.gain8 = 26; - *command_flags |= COMMAND_SETVLOFFSET; -#undef FIRMWARE_VERSION -#undef EXP_FROM_COMP -#undef COMPGAIN -} - -#define FIRMWARE_VERSION(x,y) (cam->params.version.firmwareVersion == (x) && \ - cam->params.version.firmwareRevision == (y)) -/* monitor the exposure and adjust the sensor frame rate if needed */ -static void monitor_exposure(struct cam_data *cam) -{ - u8 exp_acc, bcomp, gain, coarseL, cmd[8], data[8]; - int retval, light_exp, dark_exp, very_dark_exp; - int old_exposure, new_exposure, framerate; - - /* get necessary stats and register settings from camera */ - /* do_command can't handle this, so do it ourselves */ - cmd[0] = CPIA_COMMAND_ReadVPRegs>>8; - cmd[1] = CPIA_COMMAND_ReadVPRegs&0xff; - cmd[2] = 30; - cmd[3] = 4; - cmd[4] = 9; - cmd[5] = 8; - cmd[6] = 8; - cmd[7] = 0; - retval = cam->ops->transferCmd(cam->lowlevel_data, cmd, data); - if (retval) { - LOG("ReadVPRegs(30,4,9,8) - failed, retval=%d\n", - retval); - return; - } - exp_acc = data[0]; - bcomp = data[1]; - gain = data[2]; - coarseL = data[3]; - - mutex_lock(&cam->param_lock); - light_exp = cam->params.colourParams.brightness + - TC - 50 + EXP_ACC_LIGHT; - if(light_exp > 255) - light_exp = 255; - dark_exp = cam->params.colourParams.brightness + - TC - 50 - EXP_ACC_DARK; - if(dark_exp < 0) - dark_exp = 0; - very_dark_exp = dark_exp/2; - - old_exposure = cam->params.exposure.coarseExpHi * 256 + - cam->params.exposure.coarseExpLo; - - if(!cam->params.flickerControl.disabled) { - /* Flicker control on */ - int max_comp = FIRMWARE_VERSION(1,2) ? MAX_COMP : HIGH_COMP_102; - bcomp += 128; /* decode */ - if(bcomp >= max_comp && exp_acc < dark_exp) { - /* dark */ - if(exp_acc < very_dark_exp) { - /* very dark */ - if(cam->exposure_status == EXPOSURE_VERY_DARK) - ++cam->exposure_count; - else { - cam->exposure_status = EXPOSURE_VERY_DARK; - cam->exposure_count = 1; - } - } else { - /* just dark */ - if(cam->exposure_status == EXPOSURE_DARK) - ++cam->exposure_count; - else { - cam->exposure_status = EXPOSURE_DARK; - cam->exposure_count = 1; - } - } - } else if(old_exposure <= LOW_EXP || exp_acc > light_exp) { - /* light */ - if(old_exposure <= VERY_LOW_EXP) { - /* very light */ - if(cam->exposure_status == EXPOSURE_VERY_LIGHT) - ++cam->exposure_count; - else { - cam->exposure_status = EXPOSURE_VERY_LIGHT; - cam->exposure_count = 1; - } - } else { - /* just light */ - if(cam->exposure_status == EXPOSURE_LIGHT) - ++cam->exposure_count; - else { - cam->exposure_status = EXPOSURE_LIGHT; - cam->exposure_count = 1; - } - } - } else { - /* not dark or light */ - cam->exposure_status = EXPOSURE_NORMAL; - } - } else { - /* Flicker control off */ - if(old_exposure >= MAX_EXP && exp_acc < dark_exp) { - /* dark */ - if(exp_acc < very_dark_exp) { - /* very dark */ - if(cam->exposure_status == EXPOSURE_VERY_DARK) - ++cam->exposure_count; - else { - cam->exposure_status = EXPOSURE_VERY_DARK; - cam->exposure_count = 1; - } - } else { - /* just dark */ - if(cam->exposure_status == EXPOSURE_DARK) - ++cam->exposure_count; - else { - cam->exposure_status = EXPOSURE_DARK; - cam->exposure_count = 1; - } - } - } else if(old_exposure <= LOW_EXP || exp_acc > light_exp) { - /* light */ - if(old_exposure <= VERY_LOW_EXP) { - /* very light */ - if(cam->exposure_status == EXPOSURE_VERY_LIGHT) - ++cam->exposure_count; - else { - cam->exposure_status = EXPOSURE_VERY_LIGHT; - cam->exposure_count = 1; - } - } else { - /* just light */ - if(cam->exposure_status == EXPOSURE_LIGHT) - ++cam->exposure_count; - else { - cam->exposure_status = EXPOSURE_LIGHT; - cam->exposure_count = 1; - } - } - } else { - /* not dark or light */ - cam->exposure_status = EXPOSURE_NORMAL; - } - } - - framerate = cam->fps; - if(framerate > 30 || framerate < 1) - framerate = 1; - - if(!cam->params.flickerControl.disabled) { - /* Flicker control on */ - if((cam->exposure_status == EXPOSURE_VERY_DARK || - cam->exposure_status == EXPOSURE_DARK) && - cam->exposure_count >= DARK_TIME*framerate && - cam->params.sensorFps.divisor < 3) { - - /* dark for too long */ - ++cam->params.sensorFps.divisor; - cam->cmd_queue |= COMMAND_SETSENSORFPS; - - cam->params.flickerControl.coarseJump = - flicker_jumps[cam->mainsFreq] - [cam->params.sensorFps.baserate] - [cam->params.sensorFps.divisor]; - cam->cmd_queue |= COMMAND_SETFLICKERCTRL; - - new_exposure = cam->params.flickerControl.coarseJump-1; - while(new_exposure < old_exposure/2) - new_exposure += cam->params.flickerControl.coarseJump; - cam->params.exposure.coarseExpLo = new_exposure & 0xff; - cam->params.exposure.coarseExpHi = new_exposure >> 8; - cam->cmd_queue |= COMMAND_SETEXPOSURE; - cam->exposure_status = EXPOSURE_NORMAL; - LOG("Automatically decreasing sensor_fps\n"); - - } else if((cam->exposure_status == EXPOSURE_VERY_LIGHT || - cam->exposure_status == EXPOSURE_LIGHT) && - cam->exposure_count >= LIGHT_TIME*framerate && - cam->params.sensorFps.divisor > 0) { - - /* light for too long */ - int max_exp = FIRMWARE_VERSION(1,2) ? MAX_EXP_102 : MAX_EXP ; - - --cam->params.sensorFps.divisor; - cam->cmd_queue |= COMMAND_SETSENSORFPS; - - cam->params.flickerControl.coarseJump = - flicker_jumps[cam->mainsFreq] - [cam->params.sensorFps.baserate] - [cam->params.sensorFps.divisor]; - cam->cmd_queue |= COMMAND_SETFLICKERCTRL; - - new_exposure = cam->params.flickerControl.coarseJump-1; - while(new_exposure < 2*old_exposure && - new_exposure+ - cam->params.flickerControl.coarseJump < max_exp) - new_exposure += cam->params.flickerControl.coarseJump; - cam->params.exposure.coarseExpLo = new_exposure & 0xff; - cam->params.exposure.coarseExpHi = new_exposure >> 8; - cam->cmd_queue |= COMMAND_SETEXPOSURE; - cam->exposure_status = EXPOSURE_NORMAL; - LOG("Automatically increasing sensor_fps\n"); - } - } else { - /* Flicker control off */ - if((cam->exposure_status == EXPOSURE_VERY_DARK || - cam->exposure_status == EXPOSURE_DARK) && - cam->exposure_count >= DARK_TIME*framerate && - cam->params.sensorFps.divisor < 3) { - - /* dark for too long */ - ++cam->params.sensorFps.divisor; - cam->cmd_queue |= COMMAND_SETSENSORFPS; - - if(cam->params.exposure.gain > 0) { - --cam->params.exposure.gain; - cam->cmd_queue |= COMMAND_SETEXPOSURE; - } - cam->exposure_status = EXPOSURE_NORMAL; - LOG("Automatically decreasing sensor_fps\n"); - - } else if((cam->exposure_status == EXPOSURE_VERY_LIGHT || - cam->exposure_status == EXPOSURE_LIGHT) && - cam->exposure_count >= LIGHT_TIME*framerate && - cam->params.sensorFps.divisor > 0) { - - /* light for too long */ - --cam->params.sensorFps.divisor; - cam->cmd_queue |= COMMAND_SETSENSORFPS; - - if(cam->params.exposure.gain < - cam->params.exposure.gainMode-1) { - ++cam->params.exposure.gain; - cam->cmd_queue |= COMMAND_SETEXPOSURE; - } - cam->exposure_status = EXPOSURE_NORMAL; - LOG("Automatically increasing sensor_fps\n"); - } - } - mutex_unlock(&cam->param_lock); -} - -/*-----------------------------------------------------------------*/ -/* if flicker is switched off, this function switches it back on.It checks, - however, that conditions are suitable before restarting it. - This should only be called for firmware version 1.2. - - It also adjust the colour balance when an exposure step is detected - as - long as flicker is running -*/ -static void restart_flicker(struct cam_data *cam) -{ - int cam_exposure, old_exp; - if(!FIRMWARE_VERSION(1,2)) - return; - mutex_lock(&cam->param_lock); - if(cam->params.flickerControl.flickerMode == 0 || - cam->raw_image[39] == 0) { - mutex_unlock(&cam->param_lock); - return; - } - cam_exposure = cam->raw_image[39]*2; - old_exp = cam->params.exposure.coarseExpLo + - cam->params.exposure.coarseExpHi*256; - /* - see how far away camera exposure is from a valid - flicker exposure value - */ - cam_exposure %= cam->params.flickerControl.coarseJump; - if(!cam->params.flickerControl.disabled && - cam_exposure <= cam->params.flickerControl.coarseJump - 3) { - /* Flicker control auto-disabled */ - cam->params.flickerControl.disabled = 1; - } - - if(cam->params.flickerControl.disabled && - cam->params.flickerControl.flickerMode && - old_exp > cam->params.flickerControl.coarseJump + - ROUND_UP_EXP_FOR_FLICKER) { - /* exposure is now high enough to switch - flicker control back on */ - set_flicker(&cam->params, &cam->cmd_queue, 1); - if((cam->cmd_queue & COMMAND_SETEXPOSURE) && - cam->params.exposure.expMode == 2) - cam->exposure_status = EXPOSURE_NORMAL; - - } - mutex_unlock(&cam->param_lock); -} -#undef FIRMWARE_VERSION - -static int clear_stall(struct cam_data *cam) -{ - /* FIXME: Does this actually work? */ - LOG("Clearing stall\n"); - - cam->ops->streamRead(cam->lowlevel_data, cam->raw_image, 0); - do_command(cam, CPIA_COMMAND_GetCameraStatus,0,0,0,0); - return cam->params.status.streamState != STREAM_PAUSED; -} - -/* kernel thread function to read image from camera */ -static int 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); - /* Trial & error - Discarding a frame prevents the - first frame from having an error in the data. */ - do_command(cam, CPIA_COMMAND_DiscardFrame, 0, 0, 0, 0); - } - - /* init camera upload */ - 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 */ - int count = 0; - do_command(cam, CPIA_COMMAND_GetCameraStatus,0,0,0,0); - while (cam->params.status.streamState != STREAM_READY) { - if(++count > READY_TIMEOUT) - break; - if(cam->params.status.streamState == - STREAM_PAUSED) { - /* Bad news */ - if(!clear_stall(cam)) - return -EIO; - } - - cond_resched(); - - /* sleep for 10 ms, hopefully ;) */ - msleep_interruptible(10); - if (signal_pending(current)) - return -EINTR; - - do_command(cam, CPIA_COMMAND_GetCameraStatus, - 0, 0, 0, 0); - } - if(cam->params.status.streamState != STREAM_READY) { - continue; - } - } - - cond_resched(); - - /* grab image from camera */ - 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 */ - - /* Switch flicker control back on if it got turned off */ - restart_flicker(cam); - - /* If AEC is enabled, monitor the exposure and - adjust the sensor frame rate if needed */ - if(cam->params.exposure.expMode == 2) - monitor_exposure(cam); - - /* camera idle now so dispatch queued commands */ - dispatch_commands(cam); - - /* Update our knowledge of the camera state */ - do_command(cam, CPIA_COMMAND_GetColourBalance, 0, 0, 0, 0); - do_command(cam, CPIA_COMMAND_GetExposure, 0, 0, 0, 0); - do_command(cam, CPIA_COMMAND_ReadMCPorts, 0, 0, 0, 0); - - /* decompress and convert image to by copying it from - * raw_image to decompressed_frame - */ - - cond_resched(); - - cam->image_size = parse_picture(cam, image_size); - if (cam->image_size <= 0) { - DBG("parse_picture failed %d\n", cam->image_size); - if(cam->params.compression.mode != - CPIA_COMPRESSION_NONE) { - /* Compression may not work right if we - had a bad frame, get the next one - uncompressed. */ - cam->first_frame = 1; - do_command(cam, CPIA_COMMAND_SetGrabMode, - CPIA_GRAB_SINGLE, 0, 0, 0); - /* FIXME: Trial & error - need up to 70ms for - the grab mode change to complete ? */ - msleep_interruptible(70); - if (signal_pending(current)) - return -EINTR; - } - } 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 (cam->first_frame) { - cam->first_frame = 0; - do_command(cam, CPIA_COMMAND_SetCompression, - cam->params.compression.mode, - cam->params.compression.decimation, 0, 0); - - /* Switch from single-grab to continuous grab */ - do_command(cam, CPIA_COMMAND_SetGrabMode, - CPIA_GRAB_CONTINUOUS, 0, 0, 0); - } - return 0; - } - return -EIO; -} - -static int capture_frame(struct cam_data *cam, struct video_mmap *vm) -{ - if (!cam->frame_buf) { - /* we do lazy allocation */ - int err; - if ((err = allocate_frame_buf(cam))) - return err; - } - - cam->curframe = vm->frame; - cam->frame[cam->curframe].state = FRAME_READY; - return fetch_frame(cam); -} - -static int goto_high_power(struct cam_data *cam) -{ - if (do_command(cam, CPIA_COMMAND_GotoHiPower, 0, 0, 0, 0)) - return -EIO; - msleep_interruptible(40); /* windows driver does it too */ - if(signal_pending(current)) - return -EINTR; - if (do_command(cam, CPIA_COMMAND_GetCameraStatus, 0, 0, 0, 0)) - return -EIO; - if (cam->params.status.systemState == HI_POWER_STATE) { - DBG("camera now in HIGH power state\n"); - return 0; - } - printstatus(cam); - return -EIO; -} - -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) -{ - if(!(cam->cmd_queue & COMMAND_SETCOLOURBALANCE)) - do_command(cam, CPIA_COMMAND_GetColourBalance, 0, 0, 0, 0); - if(!(cam->cmd_queue & COMMAND_SETEXPOSURE)) - 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 int set_camera_state(struct cam_data *cam) -{ - cam->cmd_queue = COMMAND_SETCOMPRESSION | - COMMAND_SETCOMPRESSIONTARGET | - COMMAND_SETCOLOURPARAMS | - COMMAND_SETFORMAT | - COMMAND_SETYUVTHRESH | - COMMAND_SETECPTIMING | - COMMAND_SETCOMPRESSIONPARAMS | - COMMAND_SETEXPOSURE | - COMMAND_SETCOLOURBALANCE | - COMMAND_SETSENSORFPS | - COMMAND_SETAPCOR | - COMMAND_SETFLICKERCTRL | - COMMAND_SETVLOFFSET; - - do_command(cam, CPIA_COMMAND_SetGrabMode, CPIA_GRAB_SINGLE,0,0,0); - dispatch_commands(cam); - - /* Wait 6 frames for the sensor to get all settings and - AEC/ACB to settle */ - msleep_interruptible(6*(cam->params.sensorFps.baserate ? 33 : 40) * - (1 << cam->params.sensorFps.divisor) + 10); - - if(signal_pending(current)) - return -EINTR; - - save_camera_state(cam); - - return 0; -} - -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) -{ - int err; - /* 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 */ - err = goto_high_power(cam); - if(err) - return err; - do_command(cam, CPIA_COMMAND_DiscardFrame, 0, 0, 0, 0); - if (goto_low_power(cam)) - return -ENODEV; - } - - /* procedure described in developer's guide p3-28 */ - - /* Check the firmware version. */ - cam->params.version.firmwareVersion = 0; - get_version_information(cam); - if (cam->params.version.firmwareVersion != 1) - return -ENODEV; - - /* A bug in firmware 1-02 limits gainMode to 2 */ - if(cam->params.version.firmwareRevision <= 2 && - cam->params.exposure.gainMode > 2) { - cam->params.exposure.gainMode = 2; - } - - /* set QX3 detected flag */ - cam->params.qx3.qx3_detected = (cam->params.pnpID.vendor == 0x0813 && - cam->params.pnpID.product == 0x0001); - - /* 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 */ - err = goto_high_power(cam); - if (err) - return err; - - /* 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 */ - return set_camera_state(cam); -} - -static void put_cam(struct cpia_camera_ops* ops) -{ - module_put(ops->owner); -} - -/* ------------------------- V4L interface --------------------- */ -static int cpia_open(struct file *file) -{ - struct video_device *dev = video_devdata(file); - struct cam_data *cam = video_get_drvdata(dev); - int err; - - if (!cam) { - DBG("Internal error, cam_data not found!\n"); - return -ENODEV; - } - - if (cam->open_count > 0) { - DBG("Camera already open\n"); - return -EBUSY; - } - - if (!try_module_get(cam->ops->owner)) - return -ENODEV; - - mutex_lock(&cam->busy_lock); - err = -ENOMEM; - if (!cam->raw_image) { - cam->raw_image = rvmalloc(CPIA_MAX_IMAGE_SIZE); - if (!cam->raw_image) - goto oops; - } - - if (!cam->decompressed_frame.data) { - cam->decompressed_frame.data = rvmalloc(CPIA_MAX_FRAME_SIZE); - if (!cam->decompressed_frame.data) - goto oops; - } - - /* open cpia */ - err = -ENODEV; - if (cam->ops->open(cam->lowlevel_data)) - goto oops; - - /* reset the camera */ - if ((err = reset_camera(cam)) != 0) { - cam->ops->close(cam->lowlevel_data); - goto oops; - } - - err = -EINTR; - if(signal_pending(current)) - goto oops; - - /* 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; - file->private_data = dev; - mutex_unlock(&cam->busy_lock); - return 0; - - oops: - if (cam->decompressed_frame.data) { - rvfree(cam->decompressed_frame.data, CPIA_MAX_FRAME_SIZE); - cam->decompressed_frame.data = NULL; - } - if (cam->raw_image) { - rvfree(cam->raw_image, CPIA_MAX_IMAGE_SIZE); - cam->raw_image = NULL; - } - mutex_unlock(&cam->busy_lock); - put_cam(cam->ops); - return err; -} - -static int cpia_close(struct file *file) -{ - struct video_device *dev = file->private_data; - struct cam_data *cam = video_get_drvdata(dev); - - 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 status */ - 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); - - put_cam(cam->ops); - } - - 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) - kfree(cam); - } - file->private_data = NULL; - - return 0; -} - -static ssize_t cpia_read(struct file *file, char __user *buf, - size_t count, loff_t *ppos) -{ - struct video_device *dev = file->private_data; - struct cam_data *cam = video_get_drvdata(dev); - int err; - - /* make this _really_ smp and multithread-safe */ - if (mutex_lock_interruptible(&cam->busy_lock)) - return -EINTR; - - if (!buf) { - DBG("buf NULL\n"); - mutex_unlock(&cam->busy_lock); - return -EINVAL; - } - - if (!count) { - DBG("count 0\n"); - mutex_unlock(&cam->busy_lock); - return 0; - } - - if (!cam->ops) { - DBG("ops NULL\n"); - mutex_unlock(&cam->busy_lock); - return -ENODEV; - } - - /* upload frame */ - cam->decompressed_frame.state = FRAME_READY; - cam->mmap_kludge=0; - if((err = fetch_frame(cam)) != 0) { - DBG("ERROR from fetch_frame: %d\n", err); - mutex_unlock(&cam->busy_lock); - return err; - } - 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, - (unsigned long) count); - mutex_unlock(&cam->busy_lock); - return -EFAULT; - } - if (copy_to_user(buf, cam->decompressed_frame.data, - cam->decompressed_frame.count)) { - DBG("copy_to_user failed\n"); - mutex_unlock(&cam->busy_lock); - return -EFAULT; - } - - mutex_unlock(&cam->busy_lock); - return cam->decompressed_frame.count; -} - -static long cpia_do_ioctl(struct file *file, unsigned int cmd, void *arg) -{ - struct video_device *dev = file->private_data; - struct cam_data *cam = video_get_drvdata(dev); - int retval = 0; - - if (!cam || !cam->ops) - return -ENODEV; - - /* make this _really_ smp-safe */ - if (mutex_lock_interruptible(&cam->busy_lock)) - return -EINTR; - - /* DBG("cpia_ioctl: %u\n", cmd); */ - - switch (cmd) { - /* query capabilities */ - case VIDIOCGCAP: - { - struct video_capability *b = arg; - - DBG("VIDIOCGCAP\n"); - strcpy(b->name, "CPiA Camera"); - b->type = VID_TYPE_CAPTURE | VID_TYPE_SUBCAPTURE; - b->channels = 1; - b->audios = 0; - b->maxwidth = 352; /* VIDEOSIZE_CIF */ - b->maxheight = 288; - b->minwidth = 48; /* VIDEOSIZE_48_48 */ - b->minheight = 48; - break; - } - - /* get/set video source - we are a camera and nothing else */ - case VIDIOCGCHAN: - { - struct video_channel *v = arg; - - DBG("VIDIOCGCHAN\n"); - 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; - break; - } - - case VIDIOCSCHAN: - { - struct video_channel *v = arg; - - DBG("VIDIOCSCHAN\n"); - if (v->channel != 0) - retval = -EINVAL; - break; - } - - /* image properties */ - case VIDIOCGPICT: - { - struct video_picture *pic = arg; - DBG("VIDIOCGPICT\n"); - *pic = cam->vp; - break; - } - - case VIDIOCSPICT: - { - struct video_picture *vp = arg; - - DBG("VIDIOCSPICT\n"); - - /* check validity */ - DBG("palette: %d\n", vp->palette); - DBG("depth: %d\n", vp->depth); - if (!valid_mode(vp->palette, vp->depth)) { - retval = -EINVAL; - break; - } - - mutex_lock(&cam->param_lock); - /* brightness, colour, contrast need no check 0-65535 */ - cam->vp = *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; - } - - /* Adjust flicker control if necessary */ - if(cam->params.flickerControl.allowableOverExposure < 0) - cam->params.flickerControl.allowableOverExposure = - -find_over_exposure(cam->params.colourParams.brightness); - if(cam->params.flickerControl.flickerMode != 0) - cam->cmd_queue |= COMMAND_SETFLICKERCTRL; - - - /* queue command to update camera */ - cam->cmd_queue |= COMMAND_SETCOLOURPARAMS; - mutex_unlock(&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: - { - struct video_window *vw = arg; - DBG("VIDIOCGWIN\n"); - - *vw = cam->vw; - break; - } - - case VIDIOCSWIN: - { - /* copy_from_user, check validity, copy to internal structure */ - struct video_window *vw = arg; - DBG("VIDIOCSWIN\n"); - - 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??? - */ - mutex_lock(&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; - mutex_unlock(&cam->param_lock); - break; - } - cam->video_size = video_size; - - /* video size is changing, reset the subcapture area */ - memset(&cam->vc, 0, sizeof(cam->vc)); - - set_vw_size(cam); - DBG("%d / %d\n", cam->vw.width, cam->vw.height); - cam->cmd_queue |= COMMAND_SETFORMAT; - } - - mutex_unlock(&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 = arg; - 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; - break; - } - - case VIDIOCMCAPTURE: - { - struct video_mmap *vm = arg; - int video_size; - - DBG("VIDIOCMCAPTURE: %d / %d / %dx%d\n", vm->format, vm->frame, - vm->width, vm->height); - 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: - cam->vp.depth=8; - break; - 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 (video_size < 0) { - retval = -EINVAL; - break; - } - if (video_size != cam->video_size) { - cam->video_size = video_size; - - /* video size is changing, reset the subcapture area */ - memset(&cam->vc, 0, sizeof(cam->vc)); - - set_vw_size(cam); - cam->cmd_queue |= COMMAND_SETFORMAT; - dispatch_commands(cam); - } - /* according to v4l-spec we must start streaming here */ - cam->mmap_kludge = 1; - retval = capture_frame(cam, vm); - - break; - } - - case VIDIOCSYNC: - { - int *frame = arg; - - //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; - } - - case VIDIOCGCAPTURE: - { - struct video_capture *vc = arg; - - DBG("VIDIOCGCAPTURE\n"); - - *vc = cam->vc; - - break; - } - - case VIDIOCSCAPTURE: - { - struct video_capture *vc = arg; - - DBG("VIDIOCSCAPTURE\n"); - - if (vc->decimation != 0) { /* How should this be used? */ - retval = -EINVAL; - break; - } - if (vc->flags != 0) { /* Even/odd grab not supported */ - retval = -EINVAL; - break; - } - - /* Clip to the resolution we can set for the ROI - (every 8 columns and 4 rows) */ - vc->x = vc->x & ~(__u32)7; - vc->y = vc->y & ~(__u32)3; - vc->width = vc->width & ~(__u32)7; - vc->height = vc->height & ~(__u32)3; - - if(vc->width == 0 || vc->height == 0 || - vc->x + vc->width > cam->vw.width || - vc->y + vc->height > cam->vw.height) { - retval = -EINVAL; - break; - } - - DBG("%d,%d/%dx%d\n", vc->x,vc->y,vc->width, vc->height); - - mutex_lock(&cam->param_lock); - - cam->vc.x = vc->x; - cam->vc.y = vc->y; - cam->vc.width = vc->width; - cam->vc.height = vc->height; - - set_vw_size(cam); - cam->cmd_queue |= COMMAND_SETFORMAT; - - mutex_unlock(&cam->param_lock); - - /* setformat ignored by camera during streaming, - * so stop/dispatch/start */ - dispatch_commands(cam); - break; - } - - case VIDIOCGUNIT: - { - struct video_unit *vu = arg; - - DBG("VIDIOCGUNIT\n"); - - vu->video = cam->vdev.minor; - vu->vbi = VIDEO_NO_UNIT; - vu->radio = VIDEO_NO_UNIT; - vu->audio = VIDEO_NO_UNIT; - vu->teletext = VIDEO_NO_UNIT; - - break; - } - - - /* pointless to implement overlay with this camera */ - case VIDIOCCAPTURE: - case VIDIOCGFBUF: - case VIDIOCSFBUF: - case VIDIOCKEY: - /* tuner interface - we have none */ - case VIDIOCGTUNER: - case VIDIOCSTUNER: - case VIDIOCGFREQ: - case VIDIOCSFREQ: - /* audio interface - we have none */ - case VIDIOCGAUDIO: - case VIDIOCSAUDIO: - retval = -EINVAL; - break; - default: - retval = -ENOIOCTLCMD; - break; - } - - mutex_unlock(&cam->busy_lock); - return retval; -} - -static long cpia_ioctl(struct file *file, - unsigned int cmd, unsigned long arg) -{ - return video_usercopy(file, cmd, arg, cpia_do_ioctl); -} - - -/* FIXME */ -static int cpia_mmap(struct file *file, struct vm_area_struct *vma) -{ - struct video_device *dev = file->private_data; - unsigned long start = vma->vm_start; - unsigned long size = vma->vm_end - vma->vm_start; - unsigned long page, pos; - struct cam_data *cam = video_get_drvdata(dev); - int retval; - - if (!cam || !cam->ops) - return -ENODEV; - - DBG("cpia_mmap: %ld\n", size); - - if (size > FRAME_NUM*CPIA_MAX_FRAME_SIZE) - return -EINVAL; - - /* make this _really_ smp-safe */ - if (mutex_lock_interruptible(&cam->busy_lock)) - return -EINTR; - - if (!cam->frame_buf) { /* we do lazy allocation */ - if ((retval = allocate_frame_buf(cam))) { - mutex_unlock(&cam->busy_lock); - return retval; - } - } - - pos = (unsigned long)(cam->frame_buf); - while (size > 0) { - page = vmalloc_to_pfn((void *)pos); - if (remap_pfn_range(vma, start, page, PAGE_SIZE, PAGE_SHARED)) { - mutex_unlock(&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); - mutex_unlock(&cam->busy_lock); - - return 0; -} - -static const struct v4l2_file_operations cpia_fops = { - .owner = THIS_MODULE, - .open = cpia_open, - .release = cpia_close, - .read = cpia_read, - .mmap = cpia_mmap, - .ioctl = cpia_ioctl, -}; - -static struct video_device cpia_template = { - .name = "CPiA Camera", - .fops = &cpia_fops, - .release = video_device_release_empty, -}; - -/* 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 = 4; - 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 = COMP_RED; - cam->params.exposure.green1Comp = COMP_GREEN1; - cam->params.exposure.green2Comp = COMP_GREEN2; - cam->params.exposure.blueComp = COMP_BLUE; - cam->params.colourBalance.balanceMode = 2; /* ACB */ - cam->params.colourBalance.redGain = 32; - cam->params.colourBalance.greenGain = 6; - cam->params.colourBalance.blueGain = 92; - cam->params.apcor.gain1 = 0x18; - cam->params.apcor.gain2 = 0x16; - cam->params.apcor.gain4 = 0x24; - cam->params.apcor.gain8 = 0x34; - cam->params.flickerControl.flickerMode = 0; - cam->params.flickerControl.disabled = 1; - - cam->params.flickerControl.coarseJump = - flicker_jumps[cam->mainsFreq] - [cam->params.sensorFps.baserate] - [cam->params.sensorFps.divisor]; - cam->params.flickerControl.allowableOverExposure = - -find_over_exposure(cam->params.colourParams.brightness); - cam->params.vlOffset.gain1 = 20; - cam->params.vlOffset.gain2 = 24; - cam->params.vlOffset.gain4 = 26; - cam->params.vlOffset.gain8 = 26; - 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; - cam->exposure_status = EXPOSURE_NORMAL; - - /* 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 = 6; /* From windows driver */ - cam->params.yuvThreshold.uvThreshold = 6; /* From windows driver */ - - 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 = 15; /* From windows driver */ - cam->params.compressionTarget.targetQ = 5; /* From windows driver */ - - cam->params.qx3.qx3_detected = 0; - cam->params.qx3.toplight = 0; - cam->params.qx3.bottomlight = 0; - cam->params.qx3.button = 0; - cam->params.qx3.cradled = 0; - - 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 = 24; /* to be set by user */ - cam->vp.palette = VIDEO_PALETTE_RGB24; /* to be set by user */ - - cam->vc.x = 0; - cam->vc.y = 0; - cam->vc.width = 0; - cam->vc.height = 0; - - cam->vw.x = 0; - cam->vw.y = 0; - set_vw_size(cam); - cam->vw.chromakey = 0; - cam->vw.flags = 0; - cam->vw.clipcount = 0; - cam->vw.clips = NULL; - - cam->cmd_queue = COMMAND_NONE; - cam->first_frame = 1; - - 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; - mutex_init(&cam->param_lock); - mutex_init(&cam->busy_lock); - - reset_camera_struct(cam); - - cam->proc_entry = NULL; - - memcpy(&cam->vdev, &cpia_template, sizeof(cpia_template)); - video_set_drvdata(&cam->vdev, 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; - - if ((camera = kmalloc(sizeof(struct cam_data), GFP_KERNEL)) == NULL) - return NULL; - - - init_camera_struct( camera, ops ); - camera->lowlevel_data = lowlevel; - - /* register v4l device */ - if (video_register_device(&camera->vdev, VFL_TYPE_GRABBER, video_nr) < 0) { - kfree(camera); - 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); - -#ifdef CONFIG_PROC_FS - create_proc_cpia_cam(camera); -#endif - - 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) -{ - DBG("unregistering video\n"); - video_unregister_device(&cam->vdev); - if (cam->open_count) { - put_cam(cam->ops); - DBG("camera open -- setting ops to NULL\n"); - cam->ops = NULL; - } - -#ifdef CONFIG_PROC_FS - DBG("destroying /proc/cpia/%s\n", video_device_node_name(&cam->vdev)); - destroy_proc_cpia_cam(cam); -#endif - if (!cam->open_count) { - DBG("freeing camera\n"); - kfree(cam); - } -} - -static int __init cpia_init(void) -{ - printk(KERN_INFO "%s v%d.%d.%d\n", ABOUT, - CPIA_MAJ_VER, CPIA_MIN_VER, CPIA_PATCH_VER); - - printk(KERN_WARNING "Since in-kernel colorspace conversion is not " - "allowed, it is disabled by default now. Users should fix the " - "applications in case they don't work without conversion " - "reenabled by setting the 'colorspace_conv' module " - "parameter to 1\n"); - -#ifdef CONFIG_PROC_FS - proc_cpia_create(); -#endif - - return 0; -} - -static void __exit cpia_exit(void) -{ -#ifdef CONFIG_PROC_FS - proc_cpia_destroy(); -#endif -} - -module_init(cpia_init); -module_exit(cpia_exit); - -/* Exported symbols for modules. */ - -EXPORT_SYMBOL(cpia_register_camera); -EXPORT_SYMBOL(cpia_unregister_camera); diff --git a/drivers/media/video/cpia.h b/drivers/media/video/cpia.h deleted file mode 100644 index 8f0cfee4b8a1..000000000000 --- a/drivers/media/video/cpia.h +++ /dev/null @@ -1,432 +0,0 @@ -#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 1 -#define CPIA_MIN_VER 2 -#define CPIA_PATCH_VER 3 - -#define CPIA_PP_MAJ_VER CPIA_MAJ_VER -#define CPIA_PP_MIN_VER CPIA_MIN_VER -#define CPIA_PP_PATCH_VER CPIA_PATCH_VER - -#define CPIA_USB_MAJ_VER CPIA_MAJ_VER -#define CPIA_USB_MIN_VER CPIA_MIN_VER -#define CPIA_USB_PATCH_VER CPIA_PATCH_VER - -#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 -#include -#include -#include -#include -#include - -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 command. 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; - - /* - * Used to maintain lowlevel module usage counts - */ - struct module *owner; -}; - -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 balanceMode; - 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 disabled; - u8 flickerMode; - u8 coarseJump; - int 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 { /* Intel QX3 specific data */ - u8 qx3_detected; /* a QX3 is present */ - u8 toplight; /* top light lit , R/W */ - u8 bottomlight; /* bottom light lit, R/W */ - u8 button; /* snapshot button pressed (R/O) */ - u8 cradled; /* microscope is in cradle (R/O) */ - } qx3; - 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 list_head cam_data_list; - - struct mutex 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 mutex 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 */ - struct video_capture vc; /* v4l subcapture 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 */ - int exposure_status; /* EXPOSURE_* */ - int exposure_count; /* number of frames at this status */ -}; - -/* 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 - -/* default (minimum) compensation values */ -#define COMP_RED 220 -#define COMP_GREEN1 214 -#define COMP_GREEN2 COMP_GREEN1 -#define COMP_BLUE 230 - -/* exposure status */ -#define EXPOSURE_VERY_LIGHT 0 -#define EXPOSURE_LIGHT 1 -#define EXPOSURE_NORMAL 2 -#define EXPOSURE_DARK 3 -#define EXPOSURE_VERY_DARK 4 - -/* ErrorCode */ -#define ERROR_FLICKER_BELOW_MIN_EXP 0x01 /*flicker exposure got below minimum exposure */ -#define ALOG(fmt,args...) printk(fmt, ##args) -#define LOG(fmt,args...) ALOG(KERN_INFO __FILE__ ":%s(%d):" fmt, __func__ , __LINE__ , ##args) - -#ifdef _CPIA_DEBUG_ -#define ADBG(fmt,args...) printk(fmt, jiffies, ##args) -#define DBG(fmt,args...) ADBG(KERN_DEBUG __FILE__" (%ld):%s(%d):" fmt, __func__, __LINE__ , ##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); - -#endif /* __KERNEL__ */ - -#endif /* cpia_h */ diff --git a/drivers/media/video/cpia_pp.c b/drivers/media/video/cpia_pp.c deleted file mode 100644 index f5604c16a092..000000000000 --- a/drivers/media/video/cpia_pp.c +++ /dev/null @@ -1,869 +0,0 @@ -/* - * cpia_pp CPiA Parallel Port driver - * - * Supports CPiA based parallel port Video Camera's. - * - * (C) Copyright 1999 Bas Huisman - * (C) Copyright 1999-2000 Scott J. Bertin , - * (C) Copyright 1999-2000 Peter Pregler - * - * 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_ for verbose debug output (see cpia.h) */ -/* #define _CPIA_DEBUG_ 1 */ - - -#include -#include - -#include -#include -#include -#include -#include -#include -#include - -#include - -/* #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" - -#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 - -static int parport_nr[PARPORT_MAX] = {[0 ... PARPORT_MAX - 1] = PPCPIA_PARPORT_UNSPEC}; -static char *parport[PARPORT_MAX] = {NULL,}; - -MODULE_AUTHOR("B. Huisman & Peter Pregler "); -MODULE_DESCRIPTION("Parallel port driver for Vision CPiA based cameras"); -MODULE_LICENSE("GPL"); - -module_param_array(parport, charp, NULL, 0); -MODULE_PARM_DESC(parport, "'auto' or a list of parallel port numbers. Just like lp."); - -struct pp_cam_entry { - struct pardevice *pdev; - struct parport *port; - struct work_struct cb_task; - void (*cb_func)(void *cbdata); - void *cb_data; - 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, - THIS_MODULE -}; - -static LIST_HEAD(cam_list); -static spinlock_t cam_list_lock_pp; - -/* 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; -} - -/* Special CPiA PPC modes: These are invoked by using the 1284 Extensibility - * Link Flag during negotiation */ -#define UPLOAD_FLAG 0x08 -#define NIBBLE_TRANSFER 0x01 -#define ECP_TRANSFER 0x03 - -#define PARPORT_CHUNK_SIZE PAGE_SIZE - - -static void cpia_pp_run_callback(struct work_struct *work) -{ - void (*cb_func)(void *cbdata); - void *cb_data; - struct pp_cam_entry *cam; - - cam = container_of(work, struct pp_cam_entry, cb_task); - cb_func = cam->cb_func; - cb_data = cam->cb_data; - - cb_func(cb_data); -} - -/**************************************************************************** - * - * CPiA-specific low-level parport functions for nibble uploads - * - ***************************************************************************/ -/* CPiA nonstandard "Nibble" mode (no nDataAvail signal after each byte). */ -/* The standard kernel parport_ieee1284_read_nibble() fails with the CPiA... */ - -static size_t cpia_read_nibble (struct parport *port, - void *buffer, size_t len, - int flags) -{ - /* adapted verbatim, with one change, from - parport_ieee1284_read_nibble() in drivers/parport/ieee1284-ops.c */ - - unsigned char *buf = buffer; - int i; - unsigned char byte = 0; - - len *= 2; /* in nibbles */ - for (i=0; i < len; i++) { - unsigned char nibble; - - /* The CPiA firmware suppresses the use of nDataAvail (nFault LO) - * after every second nibble to signal that more - * data is available. (the total number of Bytes that - * should be sent is known; if too few are received, an error - * will be recorded after a timeout). - * This is incompatible with parport_ieee1284_read_nibble(), - * which expects to find nFault LO after every second nibble. - */ - - /* Solution: modify cpia_read_nibble to only check for - * nDataAvail before the first nibble is sent. - */ - - /* Does the error line indicate end of data? */ - if (((i /*& 1*/) == 0) && - (parport_read_status(port) & PARPORT_STATUS_ERROR)) { - DBG("%s: No more nibble data (%d bytes)\n", - port->name, i/2); - goto end_of_data; - } - - /* Event 7: Set nAutoFd low. */ - parport_frob_control (port, - PARPORT_CONTROL_AUTOFD, - PARPORT_CONTROL_AUTOFD); - - /* Event 9: nAck goes low. */ - port->ieee1284.phase = IEEE1284_PH_REV_DATA; - if (parport_wait_peripheral (port, - PARPORT_STATUS_ACK, 0)) { - /* Timeout -- no more data? */ - DBG("%s: Nibble timeout at event 9 (%d bytes)\n", - port->name, i/2); - parport_frob_control (port, PARPORT_CONTROL_AUTOFD, 0); - break; - } - - - /* Read a nibble. */ - nibble = parport_read_status (port) >> 3; - nibble &= ~8; - if ((nibble & 0x10) == 0) - nibble |= 8; - nibble &= 0xf; - - /* Event 10: Set nAutoFd high. */ - parport_frob_control (port, PARPORT_CONTROL_AUTOFD, 0); - - /* Event 11: nAck goes high. */ - if (parport_wait_peripheral (port, - PARPORT_STATUS_ACK, - PARPORT_STATUS_ACK)) { - /* Timeout -- no more data? */ - DBG("%s: Nibble timeout at event 11\n", - port->name); - break; - } - - if (i & 1) { - /* Second nibble */ - byte |= nibble << 4; - *buf++ = byte; - } else - byte = nibble; - } - - if (i == len) { - /* Read the last nibble without checking data avail. */ - if (parport_read_status (port) & PARPORT_STATUS_ERROR) { - end_of_data: - /* Go to reverse idle phase. */ - parport_frob_control (port, - PARPORT_CONTROL_AUTOFD, - PARPORT_CONTROL_AUTOFD); - port->physport->ieee1284.phase = IEEE1284_PH_REV_IDLE; - } - else - port->physport->ieee1284.phase = IEEE1284_PH_HBUSY_DAVAIL; - } - - return i/2; -} - -/* CPiA nonstandard "Nibble Stream" mode (2 nibbles per cycle, instead of 1) - * (See CPiA Data sheet p. 31) - * - * "Nibble Stream" mode used by CPiA for uploads to non-ECP ports is a - * nonstandard variant of nibble mode which allows the same (mediocre) - * data flow of 8 bits per cycle as software-enabled ECP by TRISTATE-capable - * parallel ports, but works also for non-TRISTATE-capable ports. - * (Standard nibble mode only send 4 bits per cycle) - * - */ - -static size_t cpia_read_nibble_stream(struct parport *port, - void *buffer, size_t len, - int flags) -{ - int i; - unsigned char *buf = buffer; - int endseen = 0; - - for (i=0; i < len; i++) { - unsigned char nibble[2], byte = 0; - int j; - - /* Image Data is complete when 4 consecutive EOI bytes (0xff) are seen */ - if (endseen > 3 ) - break; - - /* Event 7: Set nAutoFd low. */ - parport_frob_control (port, - PARPORT_CONTROL_AUTOFD, - PARPORT_CONTROL_AUTOFD); - - /* Event 9: nAck goes low. */ - port->ieee1284.phase = IEEE1284_PH_REV_DATA; - if (parport_wait_peripheral (port, - PARPORT_STATUS_ACK, 0)) { - /* Timeout -- no more data? */ - DBG("%s: Nibble timeout at event 9 (%d bytes)\n", - port->name, i/2); - parport_frob_control (port, PARPORT_CONTROL_AUTOFD, 0); - break; - } - - /* Read lower nibble */ - nibble[0] = parport_read_status (port) >>3; - - /* Event 10: Set nAutoFd high. */ - parport_frob_control (port, PARPORT_CONTROL_AUTOFD, 0); - - /* Event 11: nAck goes high. */ - if (parport_wait_peripheral (port, - PARPORT_STATUS_ACK, - PARPORT_STATUS_ACK)) { - /* Timeout -- no more data? */ - DBG("%s: Nibble timeout at event 11\n", - port->name); - break; - } - - /* Read upper nibble */ - nibble[1] = parport_read_status (port) >>3; - - /* reassemble the byte */ - for (j = 0; j < 2 ; j++ ) { - nibble[j] &= ~8; - if ((nibble[j] & 0x10) == 0) - nibble[j] |= 8; - nibble[j] &= 0xf; - } - byte = (nibble[0] |(nibble[1] << 4)); - *buf++ = byte; - - if(byte == EOI) - endseen++; - else - endseen = 0; - } - return i; -} - -/**************************************************************************** - * - * 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; - - /* The CPiA uses ECP protocol for Downloads from the Host to the camera. - * This will be software-emulated if ECP hardware is not present - */ - - /* the usual camera maximum response time is 10ms, but after receiving - * some commands, it needs up to 40ms. (Data Sheet p. 32)*/ - - for(retry = 0; retry < 4; ++retry) { - if(!parport_negotiate(cam->port, IEEE1284_MODE_ECP)) { - break; - } - mdelay(10); - } - if(retry == 4) { - DBG("Unable to negotiate IEEE1284 ECP Download mode\n"); - return -1; - } - return 0; -} -/**************************************************************************** - * - * ReverseSetup - * - ***************************************************************************/ -static int ReverseSetup(struct pp_cam_entry *cam, int extensibility) -{ - int retry; - int upload_mode, mode = IEEE1284_MODE_ECP; - int transfer_mode = ECP_TRANSFER; - - if (!(cam->port->modes & PARPORT_MODE_ECP) && - !(cam->port->modes & PARPORT_MODE_TRISTATE)) { - mode = IEEE1284_MODE_NIBBLE; - transfer_mode = NIBBLE_TRANSFER; - } - - upload_mode = mode; - if(extensibility) mode = UPLOAD_FLAG|transfer_mode|IEEE1284_EXT_LINK; - - /* the usual camera maximum response time is 10ms, but after - * receiving some commands, it needs up to 40ms. */ - - for(retry = 0; retry < 4; ++retry) { - if(!parport_negotiate(cam->port, mode)) { - break; - } - mdelay(10); - } - if(retry == 4) { - if(extensibility) - DBG("Unable to negotiate upload extensibility mode\n"); - else - DBG("Unable to negotiate upload mode\n"); - return -1; - } - if(extensibility) cam->port->ieee1284.mode = upload_mode; - 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; - } - - /* support for CPiA variant nibble reads */ - if(cam->port->ieee1284.mode == IEEE1284_MODE_NIBBLE) { - if(cpia_read_nibble(cam->port, packet, size, 0) != size) - retval = -EIO; - } else { - 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; -} - -/**************************************************************************** - * - * cpia_pp_streamRead - * - ***************************************************************************/ -static int cpia_pp_read(struct parport *port, u8 *buffer, int len) -{ - int bytes_read; - - /* support for CPiA variant "nibble stream" reads */ - if(port->ieee1284.mode == IEEE1284_MODE_NIBBLE) - bytes_read = cpia_read_nibble_stream(port,buffer,len,0); - else { - int new_bytes; - for(bytes_read=0; bytes_readstreaming) 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 ) { - cond_resched(); - - new_bytes = cpia_pp_read(cam->port, buffer, block_size ); - if( new_bytes <= 0 ) { - break; - } - i=-1; - while(++iimage_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) { - DBG("Error reading command result\n"); - return err; - } - 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; - - 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_func = cb; - cam->cb_data = cbdata; - INIT_WORK(&cam->cb_task, cpia_pp_run_callback); - } else { - retval = -1; - } - return retval; -} - -/**************************************************************************** - * - * cpia_pp_close - * - ***************************************************************************/ -static int cpia_pp_close(void *privdata) -{ - struct pp_cam_entry *cam = privdata; - 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_PCSPP)) { - LOG("port is not supported by CPiA driver\n"); - return -ENXIO; - } - - cam = kzalloc(sizeof(struct pp_cam_entry), GFP_KERNEL); - if (cam == NULL) { - LOG("failed to allocate camera structure\n"); - return -ENOMEM; - } - - 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; - } - spin_lock( &cam_list_lock_pp ); - list_add( &cpia->cam_data_list, &cam_list ); - spin_unlock( &cam_list_lock_pp ); - - return 0; -} - -static void cpia_pp_detach (struct parport *port) -{ - struct list_head *tmp; - struct cam_data *cpia = NULL; - struct pp_cam_entry *cam; - - spin_lock( &cam_list_lock_pp ); - list_for_each (tmp, &cam_list) { - cpia = list_entry(tmp, struct cam_data, cam_data_list); - cam = (struct pp_cam_entry *) cpia->lowlevel_data; - if (cam && cam->port->number == port->number) { - list_del(&cpia->cam_data_list); - break; - } - cpia = NULL; - } - spin_unlock( &cam_list_lock_pp ); - - if (!cpia) { - DBG("cpia_pp_detach failed to find cam_data in cam_list\n"); - return; - } - - cam = (struct pp_cam_entry *) cpia->lowlevel_data; - cpia_unregister_camera(cpia); - if(cam->open_count > 0) - cpia_pp_close(cam); - parport_unregister_device(cam->pdev); - cpia->lowlevel_data = NULL; - kfree(cam); -} - -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 = { - .name = "cpia_pp", - .attach = cpia_pp_attach, - .detach = cpia_pp_detach, -}; - -static int __init 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; - } - - spin_lock_init( &cam_list_lock_pp ); - - if (parport_register_driver (&cpia_pp_driver)) { - LOG ("unable to register with parport\n"); - return -EIO; - } - return 0; -} - -static int __init cpia_init(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; - } - } - } - } - } - return cpia_pp_init(); -} - -static void __exit cpia_cleanup(void) -{ - parport_unregister_driver(&cpia_pp_driver); - return; -} - -module_init(cpia_init); -module_exit(cpia_cleanup); diff --git a/drivers/media/video/cpia_usb.c b/drivers/media/video/cpia_usb.c deleted file mode 100644 index 58d193ff591c..000000000000 --- a/drivers/media/video/cpia_usb.c +++ /dev/null @@ -1,640 +0,0 @@ -/* - * cpia_usb CPiA USB driver - * - * Supports CPiA based parallel port Video Camera's. - * - * Copyright (C) 1999 Jochen Scharrlach - * Copyright (C) 1999, 2000 Johannes Erdfelt - * - * 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_ for verbose debug output (see cpia.h) */ -/* #define _CPIA_DEBUG_ 1 */ - -#include -#include -#include -#include -#include -#include -#include -#include - -#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; - struct urb *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, - THIS_MODULE -}; - -static LIST_HEAD(cam_list); -static spinlock_t cam_list_lock_usb; - -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); - } - } - } - - /* resubmit */ - urb->dev = ucpia->dev; - if ((i = usb_submit_urb(urb, GFP_ATOMIC)) != 0) - printk(KERN_ERR "%s: usb_submit_urb ret %d\n", __func__, i); -} - -static int cpia_usb_open(void *privdata) -{ - struct usb_cpia *ucpia = (struct usb_cpia *) privdata; - struct urb *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_1; - } - - 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, and also know the polling - * interval is every frame (1 == (1 << (bInterval -1))). - */ - urb = usb_alloc_urb(FRAMES_PER_DESC, GFP_KERNEL); - if (!urb) { - printk(KERN_ERR "cpia_init_isoc: usb_alloc_urb 0\n"); - retval = -ENOMEM; - goto error_1; - } - - ucpia->sbuf[0].urb = urb; - urb->dev = ucpia->dev; - urb->context = ucpia; - urb->pipe = usb_rcvisocpipe(ucpia->dev, 1); - urb->transfer_flags = URB_ISO_ASAP; - urb->transfer_buffer = ucpia->sbuf[0].data; - urb->complete = cpia_usb_complete; - urb->number_of_packets = FRAMES_PER_DESC; - urb->interval = 1; - 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, GFP_KERNEL); - if (!urb) { - printk(KERN_ERR "cpia_init_isoc: usb_alloc_urb 1\n"); - retval = -ENOMEM; - goto error_urb0; - } - - ucpia->sbuf[1].urb = urb; - urb->dev = ucpia->dev; - urb->context = ucpia; - urb->pipe = usb_rcvisocpipe(ucpia->dev, 1); - urb->transfer_flags = URB_ISO_ASAP; - urb->transfer_buffer = ucpia->sbuf[1].data; - urb->complete = cpia_usb_complete; - urb->number_of_packets = FRAMES_PER_DESC; - urb->interval = 1; - 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; - } - - /* queue the ISO urbs, and resubmit in the completion handler */ - err = usb_submit_urb(ucpia->sbuf[0].urb, GFP_KERNEL); - if (err) { - printk(KERN_ERR "cpia_init_isoc: usb_submit_urb 0 ret %d\n", - err); - goto error_urb1; - } - err = usb_submit_urb(ucpia->sbuf[1].urb, GFP_KERNEL); - if (err) { - printk(KERN_ERR "cpia_init_isoc: usb_submit_urb 1 ret %d\n", - err); - goto error_urb1; - } - - ucpia->streaming = 1; - ucpia->open = 1; - - return 0; - -error_urb1: /* free urb 1 */ - usb_free_urb(ucpia->sbuf[1].urb); - ucpia->sbuf[1].urb = NULL; -error_urb0: /* free urb 0 */ - usb_free_urb(ucpia->sbuf[0].urb); - ucpia->sbuf[0].urb = NULL; -error_1: - kfree (ucpia->sbuf[1].data); - ucpia->sbuf[1].data = NULL; -error_0: - kfree (ucpia->sbuf[0].data); - ucpia->sbuf[0].data = NULL; - - 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, 1000); -} - -/**************************************************************************** - * - * 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, 1000); -} - -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_kill_urb(ucpia->sbuf[1].urb); - usb_free_urb(ucpia->sbuf[1].urb); - ucpia->sbuf[1].urb = NULL; - } - - kfree(ucpia->sbuf[1].data); - ucpia->sbuf[1].data = NULL; - - if (ucpia->sbuf[0].urb) { - usb_kill_urb(ucpia->sbuf[0].urb); - usb_free_urb(ucpia->sbuf[0].urb); - ucpia->sbuf[0].urb = NULL; - } - - kfree(ucpia->sbuf[0].data); - ucpia->sbuf[0].data = NULL; -} - -static int cpia_usb_close(void *privdata) -{ - struct usb_cpia *ucpia = (struct usb_cpia *) privdata; - - if(!ucpia) - return -ENODEV; - - ucpia->open = 0; - - /* ucpia->present = 0 protects against trying to reset the - * alt setting if camera is physically disconnected while open */ - cpia_usb_free_resources(ucpia, ucpia->present); - - return 0; -} - -/* Probing and initializing */ - -static int cpia_probe(struct usb_interface *intf, - const struct usb_device_id *id) -{ - struct usb_device *udev = interface_to_usbdev(intf); - struct usb_host_interface *interface; - struct usb_cpia *ucpia; - struct cam_data *cam; - int ret; - - /* A multi-config CPiA camera? */ - if (udev->descriptor.bNumConfigurations != 1) - return -ENODEV; - - interface = intf->cur_altsetting; - - printk(KERN_INFO "USB CPiA camera found\n"); - - ucpia = kzalloc(sizeof(*ucpia), GFP_KERNEL); - if (!ucpia) { - printk(KERN_ERR "couldn't kmalloc cpia struct\n"); - return -ENOMEM; - } - - ucpia->dev = udev; - ucpia->iface = interface->desc.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; - } - - spin_lock( &cam_list_lock_usb ); - list_add( &cam->cam_data_list, &cam_list ); - spin_unlock( &cam_list_lock_usb ); - - usb_set_intfdata(intf, cam); - return 0; - -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: - kfree(ucpia); - return -EIO; -} - -static void cpia_disconnect(struct usb_interface *intf); - -static struct usb_device_id cpia_id_table [] = { - { USB_DEVICE(0x0553, 0x0002) }, - { USB_DEVICE(0x0813, 0x0001) }, - { } /* Terminating entry */ -}; - -MODULE_DEVICE_TABLE (usb, cpia_id_table); -MODULE_LICENSE("GPL"); - - -static struct usb_driver cpia_driver = { - .name = "cpia", - .probe = cpia_probe, - .disconnect = cpia_disconnect, - .id_table = cpia_id_table, -}; - -static void cpia_disconnect(struct usb_interface *intf) -{ - struct cam_data *cam = usb_get_intfdata(intf); - struct usb_cpia *ucpia; - - usb_set_intfdata(intf, NULL); - if (!cam) - return; - - ucpia = (struct usb_cpia *) cam->lowlevel_data; - spin_lock( &cam_list_lock_usb ); - list_del(&cam->cam_data_list); - spin_unlock( &cam_list_lock_usb ); - - ucpia->present = 0; - - cpia_unregister_camera(cam); - if(ucpia->open) - cpia_usb_close(cam->lowlevel_data); - - ucpia->curbuff->status = FRAME_ERROR; - - if (waitqueue_active(&ucpia->wq_stream)) - wake_up_interruptible(&ucpia->wq_stream); - - ucpia->curbuff = ucpia->workbuff = NULL; - - vfree(ucpia->buffers[2]); - ucpia->buffers[2] = NULL; - - vfree(ucpia->buffers[1]); - ucpia->buffers[1] = NULL; - - vfree(ucpia->buffers[0]); - ucpia->buffers[0] = NULL; - - cam->lowlevel_data = NULL; - kfree(ucpia); -} - -static int __init usb_cpia_init(void) -{ - printk(KERN_INFO "%s v%d.%d.%d\n",ABOUT, - CPIA_USB_MAJ_VER,CPIA_USB_MIN_VER,CPIA_USB_PATCH_VER); - - spin_lock_init(&cam_list_lock_usb); - return usb_register(&cpia_driver); -} - -static void __exit usb_cpia_cleanup(void) -{ - usb_deregister(&cpia_driver); -} - - -module_init (usb_cpia_init); -module_exit (usb_cpia_cleanup); - diff --git a/drivers/staging/Kconfig b/drivers/staging/Kconfig index 335311a98fdc..303c52cfe915 100644 --- a/drivers/staging/Kconfig +++ b/drivers/staging/Kconfig @@ -51,6 +51,8 @@ source "drivers/staging/cx25821/Kconfig" source "drivers/staging/tm6000/Kconfig" +source "drivers/staging/cpia/Kconfig" + source "drivers/staging/usbip/Kconfig" source "drivers/staging/winbond/Kconfig" diff --git a/drivers/staging/Makefile b/drivers/staging/Makefile index e3f1e1b6095e..ddcac245c99a 100644 --- a/drivers/staging/Makefile +++ b/drivers/staging/Makefile @@ -8,6 +8,7 @@ obj-$(CONFIG_SLICOSS) += slicoss/ obj-$(CONFIG_VIDEO_GO7007) += go7007/ obj-$(CONFIG_VIDEO_CX25821) += cx25821/ obj-$(CONFIG_VIDEO_TM6000) += tm6000/ +obj-$(CONFIG_VIDEO_CPIA) += cpia/ obj-$(CONFIG_LIRC_STAGING) += lirc/ obj-$(CONFIG_USB_IP_COMMON) += usbip/ obj-$(CONFIG_W35UND) += winbond/ diff --git a/drivers/staging/cpia/Kconfig b/drivers/staging/cpia/Kconfig new file mode 100644 index 000000000000..205d247ad373 --- /dev/null +++ b/drivers/staging/cpia/Kconfig @@ -0,0 +1,39 @@ +config VIDEO_CPIA + tristate "CPiA Video For Linux (DEPRECATED)" + depends on VIDEO_V4L1 + default n + ---help--- + This driver is DEPRECATED please use the gspca cpia1 module + instead. Note that you need atleast version 0.6.4 of libv4l for + the cpia1 gspca module. + + 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. + + Please read for more + information. + + This driver is also available as a module (cpia). + +config VIDEO_CPIA_PP + tristate "CPiA Parallel Port Lowlevel Support" + depends on PARPORT_1284 && VIDEO_CPIA && PARPORT + help + 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). + +config VIDEO_CPIA_USB + tristate "CPiA USB Lowlevel Support" + depends on VIDEO_CPIA && USB + help + 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). diff --git a/drivers/staging/cpia/Makefile b/drivers/staging/cpia/Makefile new file mode 100644 index 000000000000..89e52f10d739 --- /dev/null +++ b/drivers/staging/cpia/Makefile @@ -0,0 +1,5 @@ +obj-$(CONFIG_VIDEO_CPIA) += cpia.o +obj-$(CONFIG_VIDEO_CPIA_PP) += cpia_pp.o +obj-$(CONFIG_VIDEO_CPIA_USB) += cpia_usb.o + +EXTRA_CFLAGS += -Idrivers/media/video diff --git a/drivers/staging/cpia/TODO b/drivers/staging/cpia/TODO new file mode 100644 index 000000000000..ccb1c0775eec --- /dev/null +++ b/drivers/staging/cpia/TODO @@ -0,0 +1,8 @@ +This is an obsolete driver for some cpia-based webcams that use the parallel port. +We couldn't find anyone with this hardware in order to port it to use V4L2. + +Also, parallel-port webcams are obsolete nowadays. + +If nobody take care on it, the driver will be removed for 2.6.38. + +Please send patches to linux-media@vger.kernel.org diff --git a/drivers/staging/cpia/cpia.c b/drivers/staging/cpia/cpia.c new file mode 100644 index 000000000000..933ae4c8cb9a --- /dev/null +++ b/drivers/staging/cpia/cpia.c @@ -0,0 +1,4032 @@ +/* + * 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 + * (C) Copyright 2000 STMicroelectronics + * + * 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_ for verbose debug output (see cpia.h) */ +/* #define _CPIA_DEBUG_ 1 */ + + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "cpia.h" + +static int video_nr = -1; + +#ifdef MODULE +module_param(video_nr, int, 0); +MODULE_AUTHOR("Scott J. Bertin & Peter Pregler & Johannes Erdfelt "); +MODULE_DESCRIPTION("V4L-driver for Vision CPiA based cameras"); +MODULE_LICENSE("GPL"); +MODULE_SUPPORTED_DEVICE("video"); +#endif + +static unsigned short colorspace_conv; +module_param(colorspace_conv, ushort, 0444); +MODULE_PARM_DESC(colorspace_conv, + " Colorspace conversion:" + "\n 0 = disable, 1 = enable" + "\n Default value is 0" + ); + +#define ABOUT "V4L-Driver for Vision CPiA based cameras" + +#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_ResetFrameCounter (INPUT | CPIA_MODULE_VP_CTRL | 2) +#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_GrabReset (OUTPUT | CPIA_MODULE_CAPTURE | 15) + +#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) +#define CPIA_COMMAND_Null (OUTPUT | CPIA_MODULE_DEBUG | 11) + +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 +#define COMMAND_SETLIGHTS 0x8000 + +#define ROUND_UP_EXP_FOR_FLICKER 15 + +/* Constants for automatic frame rate adjustment */ +#define MAX_EXP 302 +#define MAX_EXP_102 255 +#define LOW_EXP 140 +#define VERY_LOW_EXP 70 +#define TC 94 +#define EXP_ACC_DARK 50 +#define EXP_ACC_LIGHT 90 +#define HIGH_COMP_102 160 +#define MAX_COMP 239 +#define DARK_TIME 3 +#define LIGHT_TIME 3 + +/* Maximum number of 10ms loops to wait for the stream to become ready */ +#define READY_TIMEOUT 100 + +/* 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); +static int find_over_exposure(int brightness); +static void set_flicker(struct cam_params *params, volatile u32 *command_flags, + int on); + + +/********************************************************************** + * + * Memory management + * + **********************************************************************/ +static void *rvmalloc(unsigned long size) +{ + void *mem; + unsigned long adr; + + size = PAGE_ALIGN(size); + mem = vmalloc_32(size); + if (!mem) + return NULL; + + memset(mem, 0, size); /* Clear the ram out, no junk to the user */ + adr = (unsigned long) mem; + while (size > 0) { + SetPageReserved(vmalloc_to_page((void *)adr)); + adr += PAGE_SIZE; + size -= PAGE_SIZE; + } + + return mem; +} + +static void rvfree(void *mem, unsigned long size) +{ + unsigned long adr; + + if (!mem) + return; + + adr = (unsigned long) mem; + while ((long) size > 0) { + ClearPageReserved(vmalloc_to_page((void *)adr)); + adr += PAGE_SIZE; + size -= PAGE_SIZE; + } + vfree(mem); +} + +/********************************************************************** + * + * /proc interface + * + **********************************************************************/ +#ifdef CONFIG_PROC_FS +static struct proc_dir_entry *cpia_proc_root=NULL; + +static int cpia_proc_show(struct seq_file *m, void *v) +{ + struct cam_data *cam = m->private; + int tmp; + char tmpstr[29]; + + seq_printf(m, "read-only\n-----------------------\n"); + seq_printf(m, "V4L Driver version: %d.%d.%d\n", + CPIA_MAJ_VER, CPIA_MIN_VER, CPIA_PATCH_VER); + seq_printf(m, "CPIA Version: %d.%02d (%d.%d)\n", + cam->params.version.firmwareVersion, + cam->params.version.firmwareRevision, + cam->params.version.vcVersion, + cam->params.version.vcRevision); + seq_printf(m, "CPIA PnP-ID: %04x:%04x:%04x\n", + cam->params.pnpID.vendor, cam->params.pnpID.product, + cam->params.pnpID.deviceRevision); + seq_printf(m, "VP-Version: %d.%d %04x\n", + cam->params.vpVersion.vpVersion, + cam->params.vpVersion.vpRevision, + cam->params.vpVersion.cameraHeadID); + + seq_printf(m, "system_state: %#04x\n", + cam->params.status.systemState); + seq_printf(m, "grab_state: %#04x\n", + cam->params.status.grabState); + seq_printf(m, "stream_state: %#04x\n", + cam->params.status.streamState); + seq_printf(m, "fatal_error: %#04x\n", + cam->params.status.fatalError); + seq_printf(m, "cmd_error: %#04x\n", + cam->params.status.cmdError); + seq_printf(m, "debug_flags: %#04x\n", + cam->params.status.debugFlags); + seq_printf(m, "vp_status: %#04x\n", + cam->params.status.vpStatus); + seq_printf(m, "error_code: %#04x\n", + cam->params.status.errorCode); + /* QX3 specific entries */ + if (cam->params.qx3.qx3_detected) { + seq_printf(m, "button: %4d\n", + cam->params.qx3.button); + seq_printf(m, "cradled: %4d\n", + cam->params.qx3.cradled); + } + seq_printf(m, "video_size: %s\n", + cam->params.format.videoSize == VIDEOSIZE_CIF ? + "CIF " : "QCIF"); + seq_printf(m, "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); + seq_printf(m, "actual_fps: %3d\n", cam->fps); + seq_printf(m, "transfer_rate: %4dkB/s\n", + cam->transfer_rate); + + seq_printf(m, "\nread-write\n"); + seq_printf(m, "----------------------- current min" + " max default comment\n"); + seq_printf(m, "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; + + seq_printf(m, "contrast: %8d %8d %8d %8d" + " steps of 8\n", + cam->params.colourParams.contrast, 0, tmp, 48); + seq_printf(m, "saturation: %8d %8d %8d %8d\n", + cam->params.colourParams.saturation, 0, 100, 50); + tmp = (25000+5000*cam->params.sensorFps.baserate)/ + (1<params.sensorFps.divisor); + seq_printf(m, "sensor_fps: %4d.%03d %8d %8d %8d\n", + tmp/1000, tmp%1000, 3, 30, 15); + seq_printf(m, "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); + seq_printf(m, "sub_sample: %8s %8s %8s %8s\n", + cam->params.format.subSample == SUBSAMPLE_420 ? + "420" : "422", "420", "422", "422"); + seq_printf(m, "yuv_order: %8s %8s %8s %8s\n", + cam->params.format.yuvOrder == YUVORDER_YUYV ? + "YUYV" : "UYVY", "YUYV" , "UYVY", "YUYV"); + seq_printf(m, "ecp_timing: %8s %8s %8s %8s\n", + cam->params.ecpTiming ? "slow" : "normal", "slow", + "normal", "normal"); + + if (cam->params.colourBalance.balanceMode == 2) { + sprintf(tmpstr, "auto"); + } else { + sprintf(tmpstr, "manual"); + } + seq_printf(m, "color_balance_mode: %8s %8s %8s" + " %8s\n", tmpstr, "manual", "auto", "auto"); + seq_printf(m, "red_gain: %8d %8d %8d %8d\n", + cam->params.colourBalance.redGain, 0, 212, 32); + seq_printf(m, "green_gain: %8d %8d %8d %8d\n", + cam->params.colourBalance.greenGain, 0, 212, 6); + seq_printf(m, "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 %8d", 1, 2, 2); + else + sprintf(tmpstr, "%8d %8d %8d", 1, 8, 2); + + if (cam->params.exposure.gainMode == 0) + seq_printf(m, "max_gain: unknown %28s" + " powers of 2\n", tmpstr); + else + seq_printf(m, "max_gain: %8d %28s" + " 1,2,4 or 8 \n", + 1<<(cam->params.exposure.gainMode-1), tmpstr); + + switch(cam->params.exposure.expMode) { + case 1: + case 3: + sprintf(tmpstr, "manual"); + break; + case 2: + sprintf(tmpstr, "auto"); + break; + default: + sprintf(tmpstr, "unknown"); + break; + } + seq_printf(m, "exposure_mode: %8s %8s %8s" + " %8s\n", tmpstr, "manual", "auto", "auto"); + seq_printf(m, "centre_weight: %8s %8s %8s %8s\n", + (2-cam->params.exposure.centreWeight) ? "on" : "off", + "off", "on", "on"); + seq_printf(m, "gain: %8d %8d max_gain %8d 1,2,4,8 possible\n", + 1<params.exposure.gain, 1, 1); + if (cam->params.version.firmwareVersion == 1 && + cam->params.version.firmwareRevision == 2) + /* 1-02 firmware limits fineExp/2 to 127 */ + tmp = 254; + else + tmp = 510; + + seq_printf(m, "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 = MAX_EXP_102; + else + tmp = MAX_EXP; + + seq_printf(m, "coarse_exp: %8d %8d %8d" + " %8d\n", cam->params.exposure.coarseExpLo+ + 256*cam->params.exposure.coarseExpHi, 0, tmp, 185); + seq_printf(m, "red_comp: %8d %8d %8d %8d\n", + cam->params.exposure.redComp, COMP_RED, 255, COMP_RED); + seq_printf(m, "green1_comp: %8d %8d %8d %8d\n", + cam->params.exposure.green1Comp, COMP_GREEN1, 255, + COMP_GREEN1); + seq_printf(m, "green2_comp: %8d %8d %8d %8d\n", + cam->params.exposure.green2Comp, COMP_GREEN2, 255, + COMP_GREEN2); + seq_printf(m, "blue_comp: %8d %8d %8d %8d\n", + cam->params.exposure.blueComp, COMP_BLUE, 255, COMP_BLUE); + + seq_printf(m, "apcor_gain1: %#8x %#8x %#8x %#8x\n", + cam->params.apcor.gain1, 0, 0xff, 0x1c); + seq_printf(m, "apcor_gain2: %#8x %#8x %#8x %#8x\n", + cam->params.apcor.gain2, 0, 0xff, 0x1a); + seq_printf(m, "apcor_gain4: %#8x %#8x %#8x %#8x\n", + cam->params.apcor.gain4, 0, 0xff, 0x2d); + seq_printf(m, "apcor_gain8: %#8x %#8x %#8x %#8x\n", + cam->params.apcor.gain8, 0, 0xff, 0x2a); + seq_printf(m, "vl_offset_gain1: %8d %8d %8d %8d\n", + cam->params.vlOffset.gain1, 0, 255, 24); + seq_printf(m, "vl_offset_gain2: %8d %8d %8d %8d\n", + cam->params.vlOffset.gain2, 0, 255, 28); + seq_printf(m, "vl_offset_gain4: %8d %8d %8d %8d\n", + cam->params.vlOffset.gain4, 0, 255, 30); + seq_printf(m, "vl_offset_gain8: %8d %8d %8d %8d\n", + cam->params.vlOffset.gain8, 0, 255, 30); + seq_printf(m, "flicker_control: %8s %8s %8s %8s\n", + cam->params.flickerControl.flickerMode ? "on" : "off", + "off", "on", "off"); + seq_printf(m, "mains_frequency: %8d %8d %8d %8d" + " only 50/60\n", + cam->mainsFreq ? 60 : 50, 50, 60, 50); + if(cam->params.flickerControl.allowableOverExposure < 0) + seq_printf(m, "allowable_overexposure: %4dauto auto %8d auto\n", + -cam->params.flickerControl.allowableOverExposure, + 255); + else + seq_printf(m, "allowable_overexposure: %8d auto %8d auto\n", + cam->params.flickerControl.allowableOverExposure, + 255); + seq_printf(m, "compression_mode: "); + switch(cam->params.compression.mode) { + case CPIA_COMPRESSION_NONE: + seq_printf(m, "%8s", "none"); + break; + case CPIA_COMPRESSION_AUTO: + seq_printf(m, "%8s", "auto"); + break; + case CPIA_COMPRESSION_MANUAL: + seq_printf(m, "%8s", "manual"); + break; + default: + seq_printf(m, "%8s", "unknown"); + break; + } + seq_printf(m, " none,auto,manual auto\n"); + seq_printf(m, "decimation_enable: %8s %8s %8s %8s\n", + cam->params.compression.decimation == + DECIMATION_ENAB ? "on":"off", "off", "on", + "off"); + seq_printf(m, "compression_target: %9s %9s %9s %9s\n", + cam->params.compressionTarget.frTargeting == + CPIA_COMPRESSION_TARGET_FRAMERATE ? + "framerate":"quality", + "framerate", "quality", "quality"); + seq_printf(m, "target_framerate: %8d %8d %8d %8d\n", + cam->params.compressionTarget.targetFR, 1, 30, 15); + seq_printf(m, "target_quality: %8d %8d %8d %8d\n", + cam->params.compressionTarget.targetQ, 1, 64, 5); + seq_printf(m, "y_threshold: %8d %8d %8d %8d\n", + cam->params.yuvThreshold.yThreshold, 0, 31, 6); + seq_printf(m, "uv_threshold: %8d %8d %8d %8d\n", + cam->params.yuvThreshold.uvThreshold, 0, 31, 6); + seq_printf(m, "hysteresis: %8d %8d %8d %8d\n", + cam->params.compressionParams.hysteresis, 0, 255, 3); + seq_printf(m, "threshold_max: %8d %8d %8d %8d\n", + cam->params.compressionParams.threshMax, 0, 255, 11); + seq_printf(m, "small_step: %8d %8d %8d %8d\n", + cam->params.compressionParams.smallStep, 0, 255, 1); + seq_printf(m, "large_step: %8d %8d %8d %8d\n", + cam->params.compressionParams.largeStep, 0, 255, 3); + seq_printf(m, "decimation_hysteresis: %8d %8d %8d %8d\n", + cam->params.compressionParams.decimationHysteresis, + 0, 255, 2); + seq_printf(m, "fr_diff_step_thresh: %8d %8d %8d %8d\n", + cam->params.compressionParams.frDiffStepThresh, + 0, 255, 5); + seq_printf(m, "q_diff_step_thresh: %8d %8d %8d %8d\n", + cam->params.compressionParams.qDiffStepThresh, + 0, 255, 3); + seq_printf(m, "decimation_thresh_mod: %8d %8d %8d %8d\n", + cam->params.compressionParams.decimationThreshMod, + 0, 255, 2); + /* QX3 specific entries */ + if (cam->params.qx3.qx3_detected) { + seq_printf(m, "toplight: %8s %8s %8s %8s\n", + cam->params.qx3.toplight ? "on" : "off", + "off", "on", "off"); + seq_printf(m, "bottomlight: %8s %8s %8s %8s\n", + cam->params.qx3.bottomlight ? "on" : "off", + "off", "on", "off"); + } + + return 0; +} + +static int cpia_proc_open(struct inode *inode, struct file *file) +{ + return single_open(file, cpia_proc_show, PDE(inode)->data); +} + +static int match(char *checkstr, char **buffer, size_t *count, + int *find_colon, int *err) +{ + int ret, colon_found = 1; + int len = strlen(checkstr); + ret = (len <= *count && strncmp(*buffer, checkstr, 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) + *err = -EINVAL; + *find_colon = 0; + } + } + return ret; +} + +static unsigned long int value(char **buffer, size_t *count, int *err) +{ + char *p; + unsigned long int ret; + ret = simple_strtoul(*buffer, &p, 0); + if (p == *buffer) + *err = -EINVAL; + else { + *count -= p - *buffer; + *buffer = p; + } + return ret; +} + +static ssize_t cpia_proc_write(struct file *file, const char __user *buf, + size_t count, loff_t *pos) +{ + struct cam_data *cam = PDE(file->f_path.dentry->d_inode)->data; + struct cam_params new_params; + char *page, *buffer; + int retval, find_colon; + int size = count; + unsigned long val = 0; + u32 command_flags = 0; + u8 new_mains; + + /* + * This code to copy from buf to page is shamelessly copied + * from the comx driver + */ + if (count > PAGE_SIZE) { + printk(KERN_ERR "count is %zu > %d!!!\n", count, (int)PAGE_SIZE); + return -ENOSPC; + } + + if (!(page = (char *)__get_free_page(GFP_KERNEL))) return -ENOMEM; + + if(copy_from_user(page, buf, count)) + { + retval = -EFAULT; + goto out; + } + + if (page[count-1] == '\n') + page[count-1] = '\0'; + else if (count < PAGE_SIZE) + page[count] = '\0'; + else if (page[count]) { + retval = -EINVAL; + goto out; + } + + buffer = page; + + if (mutex_lock_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) (match(x, &buffer, &count, &find_colon, &retval)) +#define VALUE (value(&buffer,&count, &retval)) +#define FIRMWARE_VERSION(x,y) (new_params.version.firmwareVersion == (x) && \ + new_params.version.firmwareRevision == (y)) + + 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; + if(new_params.flickerControl.allowableOverExposure < 0) + new_params.flickerControl.allowableOverExposure = + -find_over_exposure(new_params.colourParams.brightness); + if(new_params.flickerControl.flickerMode != 0) + command_flags |= COMMAND_SETFLICKERCTRL; + + } 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; + cam->exposure_status = EXPOSURE_NORMAL; + } 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("sub_sample")) { + if (!retval && MATCH("420")) + new_params.format.subSample = SUBSAMPLE_420; + else if (!retval && MATCH("422")) + new_params.format.subSample = SUBSAMPLE_422; + else + retval = -EINVAL; + + command_flags |= COMMAND_SETFORMAT; + } else if (MATCH("yuv_order")) { + if (!retval && MATCH("YUYV")) + new_params.format.yuvOrder = YUVORDER_YUYV; + else if (!retval && MATCH("UYVY")) + new_params.format.yuvOrder = YUVORDER_UYVY; + else + retval = -EINVAL; + + command_flags |= COMMAND_SETFORMAT; + } 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.balanceMode = 3; + else if (!retval && MATCH("auto")) + new_params.colourBalance.balanceMode = 2; + 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; + new_params.colourBalance.balanceMode = 1; + } 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; + new_params.colourBalance.balanceMode = 1; + } 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; + new_params.colourBalance.balanceMode = 1; + } 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; + if(new_params.flickerControl.flickerMode != 0) + command_flags |= COMMAND_SETFLICKERCTRL; + new_params.flickerControl.flickerMode = 0; + } 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; + break; + case 2: + new_params.exposure.gain = 1; + break; + case 4: + new_params.exposure.gain = 2; + break; + case 8: + new_params.exposure.gain = 3; + break; + default: + retval = -EINVAL; + break; + } + new_params.exposure.expMode = 1; + if(new_params.flickerControl.flickerMode != 0) + command_flags |= COMMAND_SETFLICKERCTRL; + new_params.flickerControl.flickerMode = 0; + 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/2; + + if (!retval) { + if (val < 256) { + /* 1-02 firmware limits fineExp/2 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; + if(new_params.flickerControl.flickerMode != 0) + command_flags |= COMMAND_SETFLICKERCTRL; + 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 <= MAX_EXP) { + if (FIRMWARE_VERSION(1,2) && + val > MAX_EXP_102) + val = MAX_EXP_102; + new_params.exposure.coarseExpLo = + val & 0xff; + new_params.exposure.coarseExpHi = + val >> 8; + new_params.exposure.expMode = 1; + command_flags |= COMMAND_SETEXPOSURE; + if(new_params.flickerControl.flickerMode != 0) + command_flags |= COMMAND_SETFLICKERCTRL; + 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 >= COMP_RED && val <= 255) { + new_params.exposure.redComp = val; + new_params.exposure.compMode = 1; + command_flags |= COMMAND_SETEXPOSURE; + } else + retval = -EINVAL; + } + } else if (MATCH("green1_comp")) { + if (!retval) + val = VALUE; + + if (!retval) { + if (val >= COMP_GREEN1 && val <= 255) { + new_params.exposure.green1Comp = val; + new_params.exposure.compMode = 1; + command_flags |= COMMAND_SETEXPOSURE; + } else + retval = -EINVAL; + } + } else if (MATCH("green2_comp")) { + if (!retval) + val = VALUE; + + if (!retval) { + if (val >= COMP_GREEN2 && val <= 255) { + new_params.exposure.green2Comp = val; + new_params.exposure.compMode = 1; + command_flags |= COMMAND_SETEXPOSURE; + } else + retval = -EINVAL; + } + } else if (MATCH("blue_comp")) { + if (!retval) + val = VALUE; + + if (!retval) { + if (val >= COMP_BLUE && val <= 255) { + new_params.exposure.blueComp = val; + new_params.exposure.compMode = 1; + 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")) { + set_flicker(&new_params, &command_flags, 1); + } else if (!retval && MATCH("off")) { + set_flicker(&new_params, &command_flags, 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 && MATCH("auto")) { + new_params.flickerControl.allowableOverExposure = + -find_over_exposure(new_params.colourParams.brightness); + if(new_params.flickerControl.flickerMode != 0) + command_flags |= COMMAND_SETFLICKERCTRL; + } else { + if (!retval) + val = VALUE; + + if (!retval) { + if (val <= 0xff) { + new_params.flickerControl. + allowableOverExposure = val; + if(new_params.flickerControl.flickerMode != 0) + 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 if (!retval && MATCH("on")) + new_params.compression.decimation = 1; + 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) { + if(val > 0 && val <= 30) + new_params.compressionTarget.targetFR = val; + else + retval = -EINVAL; + } + command_flags |= COMMAND_SETCOMPRESSIONTARGET; + } else if (MATCH("target_quality")) { + if (!retval) + val = VALUE; + + if (!retval) { + if(val > 0 && val <= 64) + new_params.compressionTarget.targetQ = val; + else + retval = -EINVAL; + } + 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 if (MATCH("toplight")) { + if (!retval && MATCH("on")) + new_params.qx3.toplight = 1; + else if (!retval && MATCH("off")) + new_params.qx3.toplight = 0; + else + retval = -EINVAL; + command_flags |= COMMAND_SETLIGHTS; + } else if (MATCH("bottomlight")) { + if (!retval && MATCH("on")) + new_params.qx3.bottomlight = 1; + else if (!retval && MATCH("off")) + new_params.qx3.bottomlight = 0; + else + retval = -EINVAL; + command_flags |= COMMAND_SETLIGHTS; + } else { + DBG("No match found\n"); + retval = -EINVAL; + } + + if (!retval) { + while (count && isspace(*buffer) && *buffer != '\n') { + --count; + ++buffer; + } + if (count) { + if (*buffer == '\0' && count != 1) + retval = -EINVAL; + else if (*buffer != '\n' && *buffer != ';' && + *buffer != '\0') + retval = -EINVAL; + else { + --count; + ++buffer; + } + } + } + } +#undef MATCH +#undef VALUE +#undef FIRMWARE_VERSION + 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; + } + if((command_flags & COMMAND_SETEXPOSURE) && + new_params.exposure.expMode == 2) + cam->exposure_status = EXPOSURE_NORMAL; + + 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); + + mutex_unlock(&cam->param_lock); + +out: + free_page((unsigned long)page); + return retval; +} + +static const struct file_operations cpia_proc_fops = { + .owner = THIS_MODULE, + .open = cpia_proc_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, + .write = cpia_proc_write, +}; + +static void create_proc_cpia_cam(struct cam_data *cam) +{ + struct proc_dir_entry *ent; + + if (!cpia_proc_root || !cam) + return; + + ent = proc_create_data(video_device_node_name(&cam->vdev), + S_IRUGO|S_IWUSR, cpia_proc_root, + &cpia_proc_fops, cam); + if (!ent) + return; + + /* + size of the proc entry is 3736 bytes for the standard webcam; + the extra features of the QX3 microscope add 189 bytes. + (we have not yet probed the camera to see which type it is). + */ + ent->size = 3736 + 189; + cam->proc_entry = ent; +} + +static void destroy_proc_cpia_cam(struct cam_data *cam) +{ + if (!cam || !cam->proc_entry) + return; + + remove_proc_entry(video_device_node_name(&cam->vdev), cpia_proc_root); + cam->proc_entry = NULL; +} + +static void proc_cpia_create(void) +{ + cpia_proc_root = proc_mkdir("cpia", NULL); + + if (!cpia_proc_root) + LOG("Unable to initialise /proc/cpia\n"); +} + +static void __exit proc_cpia_destroy(void) +{ + remove_proc_entry("cpia", NULL); +} +#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) +{ + if ((palette == VIDEO_PALETTE_YUV422 && depth == 16) || + (palette == VIDEO_PALETTE_YUYV && depth == 16)) + return 1; + + if (colorspace_conv) + 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_UYVY && depth == 16); + + return 0; +} + +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.rowStart=0; + 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.rowStart=6; + 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.rowStart=9; + 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.rowStart=12; + 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.rowStart=15; + 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.rowStart=18; + 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.rowStart=0; + 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.rowStart=3; + 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.rowStart=6; + 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.rowStart=9; + 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.rowStart=12; + 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.rowStart=6; + cam->params.streamStartLine = 60; + break; + default: + LOG("bad videosize value: %d\n", cam->video_size); + return; + } + + if(cam->vc.width == 0) + cam->vc.width = cam->vw.width; + if(cam->vc.height == 0) + cam->vc.height = cam->vw.height; + + cam->params.roi.colStart += cam->vc.x >> 3; + cam->params.roi.colEnd = cam->params.roi.colStart + + (cam->vc.width >> 3); + cam->params.roi.rowStart += cam->vc.y >> 2; + cam->params.roi.rowEnd = cam->params.roi.rowStart + + (cam->vc.height >> 2); + + 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 = NULL; + for (i=0; i < FRAME_NUM; i++) + cam->frame[i].data = NULL; + + return 0; +} + + +static inline void 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: + mutex_lock(&cam->param_lock); + datasize=8; + break; + case CPIA_COMMAND_ReadMCPorts: + case CPIA_COMMAND_ReadVCRegs: + datasize = 4; + 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); + if (command == CPIA_COMMAND_GetColourParams || + command == CPIA_COMMAND_GetColourBalance || + command == CPIA_COMMAND_GetExposure) + mutex_unlock(&cam->param_lock); + } 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]; + mutex_unlock(&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]; + mutex_unlock(&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]; + mutex_unlock(&cam->param_lock); + break; + + case CPIA_COMMAND_ReadMCPorts: + if (!cam->params.qx3.qx3_detected) + break; + /* test button press */ + cam->params.qx3.button = ((data[1] & 0x02) == 0); + if (cam->params.qx3.button) { + /* button pressed - unlock the latch */ + do_command(cam,CPIA_COMMAND_WriteMCPort,3,0xDF,0xDF,0); + do_command(cam,CPIA_COMMAND_WriteMCPort,3,0xFF,0xFF,0); + } + + /* test whether microscope is cradled */ + cam->params.qx3.cradled = ((data[2] & 0x40) == 0); + 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) + DBG("%x - failed\n", command); + + return retval; +} + +/********************************************************************** + * + * Colorspace conversion + * + **********************************************************************/ +#define LIMIT(x) ((((x)>0xffffff)?0xff0000:(((x)<=0xffff)?0:(x)&0xff0000))>>16) + +static int convert420(unsigned char *yuv, unsigned char *rgb, int out_fmt, + int linesize, int mmap_kludge) +{ + int y, u, v, r, g, b, y1; + + /* Odd lines use the same u and v as the previous line. + * Because of compression, it is necessary to get this + * information from the decoded image. */ + switch(out_fmt) { + case VIDEO_PALETTE_RGB555: + y = (*yuv++ - 16) * 76310; + y1 = (*yuv - 16) * 76310; + r = ((*(rgb+1-linesize)) & 0x7c) << 1; + g = ((*(rgb-linesize)) & 0xe0) >> 4 | + ((*(rgb+1-linesize)) & 0x03) << 6; + b = ((*(rgb-linesize)) & 0x1f) << 3; + u = (-53294 * r - 104635 * g + 157929 * b) / 5756495; + v = (157968 * r - 132278 * g - 25690 * b) / 5366159; + r = 104635 * v; + g = -25690 * u - 53294 * v; + b = 132278 * u; + *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: + y = (*yuv++ - 16) * 76310; + y1 = (*yuv - 16) * 76310; + r = (*(rgb+1-linesize)) & 0xf8; + g = ((*(rgb-linesize)) & 0xe0) >> 3 | + ((*(rgb+1-linesize)) & 0x07) << 5; + b = ((*(rgb-linesize)) & 0x1f) << 3; + u = (-53294 * r - 104635 * g + 157929 * b) / 5756495; + v = (157968 * r - 132278 * g - 25690 * b) / 5366159; + r = 104635 * v; + g = -25690 * u - 53294 * v; + b = 132278 * u; + *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; + break; + case VIDEO_PALETTE_RGB24: + case VIDEO_PALETTE_RGB32: + y = (*yuv++ - 16) * 76310; + y1 = (*yuv - 16) * 76310; + if (mmap_kludge) { + r = *(rgb+2-linesize); + g = *(rgb+1-linesize); + b = *(rgb-linesize); + } else { + r = *(rgb-linesize); + g = *(rgb+1-linesize); + b = *(rgb+2-linesize); + } + u = (-53294 * r - 104635 * g + 157929 * b) / 5756495; + v = (157968 * r - 132278 * g - 25690 * b) / 5366159; + r = 104635 * v; + g = -25690 * u + -53294 * v; + b = 132278 * u; + if (mmap_kludge) { + *rgb++ = LIMIT(b+y); + *rgb++ = LIMIT(g+y); + *rgb++ = LIMIT(r+y); + if(out_fmt == VIDEO_PALETTE_RGB32) + 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); + if(out_fmt == VIDEO_PALETTE_RGB32) + rgb++; + *rgb++ = LIMIT(r+y1); + *rgb++ = LIMIT(g+y1); + *rgb = LIMIT(b+y1); + } + if(out_fmt == VIDEO_PALETTE_RGB32) + return 8; + return 6; + case VIDEO_PALETTE_YUV422: + case VIDEO_PALETTE_YUYV: + y = *yuv++; + u = *(rgb+1-linesize); + y1 = *yuv; + v = *(rgb+3-linesize); + *rgb++ = y; + *rgb++ = u; + *rgb++ = y1; + *rgb = v; + return 4; + case VIDEO_PALETTE_UYVY: + u = *(rgb-linesize); + y = *yuv++; + v = *(rgb+2-linesize); + y1 = *yuv; + *rgb++ = u; + *rgb++ = y; + *rgb++ = v; + *rgb = y1; + return 4; + case VIDEO_PALETTE_GREY: + *rgb++ = *yuv++; + *rgb = *yuv; + return 2; + default: + DBG("Empty: %d\n", out_fmt); + return 0; + } +} + + +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: + return count; + 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, decimation, even_line, origsize, out_fmt; + int rows, cols, linesize, subsample_422; + + /* make sure params don't change while we are decoding */ + mutex_lock(&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"); + mutex_unlock(&cam->param_lock); + return -1; + } + + if ((ibuf[16] != VIDEOSIZE_QCIF) && (ibuf[16] != VIDEOSIZE_CIF)) { + LOG("wrong video size\n"); + mutex_unlock(&cam->param_lock); + return -1; + } + + if (ibuf[17] != SUBSAMPLE_420 && ibuf[17] != SUBSAMPLE_422) { + LOG("illegal subtype %d\n",ibuf[17]); + mutex_unlock(&cam->param_lock); + return -1; + } + subsample_422 = ibuf[17] == SUBSAMPLE_422; + + if (ibuf[18] != YUVORDER_YUYV && ibuf[18] != YUVORDER_UYVY) { + LOG("illegal yuvorder %d\n",ibuf[18]); + mutex_unlock(&cam->param_lock); + return -1; + } + in_uyvy = ibuf[18] == YUVORDER_UYVY; + + 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"); + mutex_unlock(&cam->param_lock); + return -1; + } + cols = 8*(ibuf[25] - ibuf[24]); + rows = 4*(ibuf[27] - ibuf[26]); + + + if ((ibuf[28] != NOT_COMPRESSED) && (ibuf[28] != COMPRESSED)) { + LOG("illegal compression %d\n",ibuf[28]); + mutex_unlock(&cam->param_lock); + return -1; + } + compressed = (ibuf[28] == COMPRESSED); + + if (ibuf[29] != NO_DECIMATION && ibuf[29] != DECIMATION_ENAB) { + LOG("illegal decimation %d\n",ibuf[29]); + mutex_unlock(&cam->param_lock); + return -1; + } + decimation = (ibuf[29] == DECIMATION_ENAB); + + 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]; + mutex_unlock(&cam->param_lock); + + linesize = skipcount(cols, out_fmt); + ibuf += FRAME_HEADER_SIZE; + size -= FRAME_HEADER_SIZE; + ll = ibuf[0] | (ibuf[1] << 8); + ibuf += 2; + even_line = 1; + + 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))) { + if(subsample_422 || even_line) { + obuf += yuvconvert(ibuf, obuf, out_fmt, + in_uyvy, cam->mmap_kludge); + ibuf += 4; + ll -= 4; + } else { + /* SUBSAMPLE_420 on an odd line */ + obuf += convert420(ibuf, obuf, + out_fmt, linesize, + cam->mmap_kludge); + ibuf += 2; + ll -= 2; + } + } else { + /*skip compressed interval from previous frame*/ + obuf += skipcount(*ibuf >> 1, out_fmt); + if (obuf > end_obuf) { + LOG("Insufficient buffer size\n"); + return -1; + } + ++ibuf; + ll--; + } + } + if (ll == 1) { + if (*ibuf != EOL) { + DBG("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(decimation) { + /* skip the odd lines for now */ + obuf += linesize; + } + + if (size > 1) { + ll = ibuf[0] | (ibuf[1] << 8); + ibuf += 2; /* skip over line length */ + } + if(!decimation) + even_line = !even_line; + } else { + LOG("line length was not 1 but %d after %d/%d bytes\n", + ll, origsize-size, origsize); + return -1; + } + } + + if(decimation) { + /* interpolate odd rows */ + int i, j; + u8 *prev, *next; + prev = cam->decompressed_frame.data; + obuf = prev+linesize; + next = obuf+linesize; + for(i=1; idecompressed_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); +} + + +/* find_over_exposure + * Finds a suitable value of OverExposure for use with SetFlickerCtrl + * Some calculation is required because this value changes with the brightness + * set with SetColourParameters + * + * Parameters: Brightness - last brightness value set with SetColourParameters + * + * Returns: OverExposure value to use with SetFlickerCtrl + */ +#define FLICKER_MAX_EXPOSURE 250 +#define FLICKER_ALLOWABLE_OVER_EXPOSURE 146 +#define FLICKER_BRIGHTNESS_CONSTANT 59 +static int find_over_exposure(int brightness) +{ + int MaxAllowableOverExposure, OverExposure; + + MaxAllowableOverExposure = FLICKER_MAX_EXPOSURE - brightness - + FLICKER_BRIGHTNESS_CONSTANT; + + if (MaxAllowableOverExposure < FLICKER_ALLOWABLE_OVER_EXPOSURE) { + OverExposure = MaxAllowableOverExposure; + } else { + OverExposure = FLICKER_ALLOWABLE_OVER_EXPOSURE; + } + + return OverExposure; +} +#undef FLICKER_MAX_EXPOSURE +#undef FLICKER_ALLOWABLE_OVER_EXPOSURE +#undef FLICKER_BRIGHTNESS_CONSTANT + +/* update various camera modes and settings */ +static void dispatch_commands(struct cam_data *cam) +{ + mutex_lock(&cam->param_lock); + if (cam->cmd_queue==COMMAND_NONE) { + mutex_unlock(&cam->param_lock); + return; + } + DEB_BYTE(cam->cmd_queue); + DEB_BYTE(cam->cmd_queue>>8); + 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_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_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_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_SETEXPOSURE) { + do_command_extended(cam, CPIA_COMMAND_SetExposure, + cam->params.exposure.gainMode, + 1, + 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->params.exposure.expMode != 1) { + do_command_extended(cam, CPIA_COMMAND_SetExposure, + 0, + cam->params.exposure.expMode, + 0, 0, + cam->params.exposure.gain, + cam->params.exposure.fineExp, + cam->params.exposure.coarseExpLo, + cam->params.exposure.coarseExpHi, + 0, 0, 0, 0); + } + } + + if (cam->cmd_queue & COMMAND_SETCOLOURBALANCE) { + if (cam->params.colourBalance.balanceMode == 1) { + 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->params.colourBalance.balanceMode == 2) { + do_command(cam, CPIA_COMMAND_SetColourBalance, + 2, 0, 0, 0); + } + if (cam->params.colourBalance.balanceMode == 3) { + do_command(cam, CPIA_COMMAND_SetColourBalance, + 3, 0, 0, 0); + } + } + + 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_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_SETCOMPRESSION) + do_command(cam, CPIA_COMMAND_SetCompression, + cam->params.compression.mode, + cam->params.compression.decimation, 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_SETFLICKERCTRL) + do_command(cam, CPIA_COMMAND_SetFlickerCtrl, + cam->params.flickerControl.flickerMode, + cam->params.flickerControl.coarseJump, + abs(cam->params.flickerControl.allowableOverExposure), + 0); + + if (cam->cmd_queue & COMMAND_SETECPTIMING) + do_command(cam, CPIA_COMMAND_SetECPTiming, + cam->params.ecpTiming, 0, 0, 0); + + 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); + + if (cam->cmd_queue & COMMAND_SETLIGHTS && cam->params.qx3.qx3_detected) + { + int p1 = (cam->params.qx3.bottomlight == 0) << 1; + int p2 = (cam->params.qx3.toplight == 0) << 3; + do_command(cam, CPIA_COMMAND_WriteVCReg, 0x90, 0x8F, 0x50, 0); + do_command(cam, CPIA_COMMAND_WriteMCPort, 2, 0, (p1|p2|0xE0), 0); + } + + cam->cmd_queue = COMMAND_NONE; + mutex_unlock(&cam->param_lock); + return; +} + + + +static void set_flicker(struct cam_params *params, volatile u32 *command_flags, + int on) +{ + /* Everything in here is from the Windows driver */ +#define FIRMWARE_VERSION(x,y) (params->version.firmwareVersion == (x) && \ + params->version.firmwareRevision == (y)) +/* define for compgain calculation */ +#if 0 +#define COMPGAIN(base, curexp, newexp) \ + (u8) ((((float) base - 128.0) * ((float) curexp / (float) newexp)) + 128.5) +#define EXP_FROM_COMP(basecomp, curcomp, curexp) \ + (u16)((float)curexp * (float)(u8)(curcomp + 128) / (float)(u8)(basecomp - 128)) +#else + /* equivalent functions without floating point math */ +#define COMPGAIN(base, curexp, newexp) \ + (u8)(128 + (((u32)(2*(base-128)*curexp + newexp)) / (2* newexp)) ) +#define EXP_FROM_COMP(basecomp, curcomp, curexp) \ + (u16)(((u32)(curexp * (u8)(curcomp + 128)) / (u8)(basecomp - 128))) +#endif + + + int currentexp = params->exposure.coarseExpLo + + params->exposure.coarseExpHi*256; + int startexp; + if (on) { + int cj = params->flickerControl.coarseJump; + params->flickerControl.flickerMode = 1; + params->flickerControl.disabled = 0; + if(params->exposure.expMode != 2) + *command_flags |= COMMAND_SETEXPOSURE; + params->exposure.expMode = 2; + currentexp = currentexp << params->exposure.gain; + params->exposure.gain = 0; + /* round down current exposure to nearest value */ + startexp = (currentexp + ROUND_UP_EXP_FOR_FLICKER) / cj; + if(startexp < 1) + startexp = 1; + startexp = (startexp * cj) - 1; + if(FIRMWARE_VERSION(1,2)) + while(startexp > MAX_EXP_102) + startexp -= cj; + else + while(startexp > MAX_EXP) + startexp -= cj; + params->exposure.coarseExpLo = startexp & 0xff; + params->exposure.coarseExpHi = startexp >> 8; + if (currentexp > startexp) { + if (currentexp > (2 * startexp)) + currentexp = 2 * startexp; + params->exposure.redComp = COMPGAIN (COMP_RED, currentexp, startexp); + params->exposure.green1Comp = COMPGAIN (COMP_GREEN1, currentexp, startexp); + params->exposure.green2Comp = COMPGAIN (COMP_GREEN2, currentexp, startexp); + params->exposure.blueComp = COMPGAIN (COMP_BLUE, currentexp, startexp); + } else { + params->exposure.redComp = COMP_RED; + params->exposure.green1Comp = COMP_GREEN1; + params->exposure.green2Comp = COMP_GREEN2; + params->exposure.blueComp = COMP_BLUE; + } + if(FIRMWARE_VERSION(1,2)) + params->exposure.compMode = 0; + else + params->exposure.compMode = 1; + + params->apcor.gain1 = 0x18; + params->apcor.gain2 = 0x18; + params->apcor.gain4 = 0x16; + params->apcor.gain8 = 0x14; + *command_flags |= COMMAND_SETAPCOR; + } else { + params->flickerControl.flickerMode = 0; + params->flickerControl.disabled = 1; + /* Coarse = average of equivalent coarse for each comp channel */ + startexp = EXP_FROM_COMP(COMP_RED, params->exposure.redComp, currentexp); + startexp += EXP_FROM_COMP(COMP_GREEN1, params->exposure.green1Comp, currentexp); + startexp += EXP_FROM_COMP(COMP_GREEN2, params->exposure.green2Comp, currentexp); + startexp += EXP_FROM_COMP(COMP_BLUE, params->exposure.blueComp, currentexp); + startexp = startexp >> 2; + while(startexp > MAX_EXP && + params->exposure.gain < params->exposure.gainMode-1) { + startexp = startexp >> 1; + ++params->exposure.gain; + } + if(FIRMWARE_VERSION(1,2) && startexp > MAX_EXP_102) + startexp = MAX_EXP_102; + if(startexp > MAX_EXP) + startexp = MAX_EXP; + params->exposure.coarseExpLo = startexp&0xff; + params->exposure.coarseExpHi = startexp >> 8; + params->exposure.redComp = COMP_RED; + params->exposure.green1Comp = COMP_GREEN1; + params->exposure.green2Comp = COMP_GREEN2; + params->exposure.blueComp = COMP_BLUE; + params->exposure.compMode = 1; + *command_flags |= COMMAND_SETEXPOSURE; + params->apcor.gain1 = 0x18; + params->apcor.gain2 = 0x16; + params->apcor.gain4 = 0x24; + params->apcor.gain8 = 0x34; + *command_flags |= COMMAND_SETAPCOR; + } + params->vlOffset.gain1 = 20; + params->vlOffset.gain2 = 24; + params->vlOffset.gain4 = 26; + params->vlOffset.gain8 = 26; + *command_flags |= COMMAND_SETVLOFFSET; +#undef FIRMWARE_VERSION +#undef EXP_FROM_COMP +#undef COMPGAIN +} + +#define FIRMWARE_VERSION(x,y) (cam->params.version.firmwareVersion == (x) && \ + cam->params.version.firmwareRevision == (y)) +/* monitor the exposure and adjust the sensor frame rate if needed */ +static void monitor_exposure(struct cam_data *cam) +{ + u8 exp_acc, bcomp, gain, coarseL, cmd[8], data[8]; + int retval, light_exp, dark_exp, very_dark_exp; + int old_exposure, new_exposure, framerate; + + /* get necessary stats and register settings from camera */ + /* do_command can't handle this, so do it ourselves */ + cmd[0] = CPIA_COMMAND_ReadVPRegs>>8; + cmd[1] = CPIA_COMMAND_ReadVPRegs&0xff; + cmd[2] = 30; + cmd[3] = 4; + cmd[4] = 9; + cmd[5] = 8; + cmd[6] = 8; + cmd[7] = 0; + retval = cam->ops->transferCmd(cam->lowlevel_data, cmd, data); + if (retval) { + LOG("ReadVPRegs(30,4,9,8) - failed, retval=%d\n", + retval); + return; + } + exp_acc = data[0]; + bcomp = data[1]; + gain = data[2]; + coarseL = data[3]; + + mutex_lock(&cam->param_lock); + light_exp = cam->params.colourParams.brightness + + TC - 50 + EXP_ACC_LIGHT; + if(light_exp > 255) + light_exp = 255; + dark_exp = cam->params.colourParams.brightness + + TC - 50 - EXP_ACC_DARK; + if(dark_exp < 0) + dark_exp = 0; + very_dark_exp = dark_exp/2; + + old_exposure = cam->params.exposure.coarseExpHi * 256 + + cam->params.exposure.coarseExpLo; + + if(!cam->params.flickerControl.disabled) { + /* Flicker control on */ + int max_comp = FIRMWARE_VERSION(1,2) ? MAX_COMP : HIGH_COMP_102; + bcomp += 128; /* decode */ + if(bcomp >= max_comp && exp_acc < dark_exp) { + /* dark */ + if(exp_acc < very_dark_exp) { + /* very dark */ + if(cam->exposure_status == EXPOSURE_VERY_DARK) + ++cam->exposure_count; + else { + cam->exposure_status = EXPOSURE_VERY_DARK; + cam->exposure_count = 1; + } + } else { + /* just dark */ + if(cam->exposure_status == EXPOSURE_DARK) + ++cam->exposure_count; + else { + cam->exposure_status = EXPOSURE_DARK; + cam->exposure_count = 1; + } + } + } else if(old_exposure <= LOW_EXP || exp_acc > light_exp) { + /* light */ + if(old_exposure <= VERY_LOW_EXP) { + /* very light */ + if(cam->exposure_status == EXPOSURE_VERY_LIGHT) + ++cam->exposure_count; + else { + cam->exposure_status = EXPOSURE_VERY_LIGHT; + cam->exposure_count = 1; + } + } else { + /* just light */ + if(cam->exposure_status == EXPOSURE_LIGHT) + ++cam->exposure_count; + else { + cam->exposure_status = EXPOSURE_LIGHT; + cam->exposure_count = 1; + } + } + } else { + /* not dark or light */ + cam->exposure_status = EXPOSURE_NORMAL; + } + } else { + /* Flicker control off */ + if(old_exposure >= MAX_EXP && exp_acc < dark_exp) { + /* dark */ + if(exp_acc < very_dark_exp) { + /* very dark */ + if(cam->exposure_status == EXPOSURE_VERY_DARK) + ++cam->exposure_count; + else { + cam->exposure_status = EXPOSURE_VERY_DARK; + cam->exposure_count = 1; + } + } else { + /* just dark */ + if(cam->exposure_status == EXPOSURE_DARK) + ++cam->exposure_count; + else { + cam->exposure_status = EXPOSURE_DARK; + cam->exposure_count = 1; + } + } + } else if(old_exposure <= LOW_EXP || exp_acc > light_exp) { + /* light */ + if(old_exposure <= VERY_LOW_EXP) { + /* very light */ + if(cam->exposure_status == EXPOSURE_VERY_LIGHT) + ++cam->exposure_count; + else { + cam->exposure_status = EXPOSURE_VERY_LIGHT; + cam->exposure_count = 1; + } + } else { + /* just light */ + if(cam->exposure_status == EXPOSURE_LIGHT) + ++cam->exposure_count; + else { + cam->exposure_status = EXPOSURE_LIGHT; + cam->exposure_count = 1; + } + } + } else { + /* not dark or light */ + cam->exposure_status = EXPOSURE_NORMAL; + } + } + + framerate = cam->fps; + if(framerate > 30 || framerate < 1) + framerate = 1; + + if(!cam->params.flickerControl.disabled) { + /* Flicker control on */ + if((cam->exposure_status == EXPOSURE_VERY_DARK || + cam->exposure_status == EXPOSURE_DARK) && + cam->exposure_count >= DARK_TIME*framerate && + cam->params.sensorFps.divisor < 3) { + + /* dark for too long */ + ++cam->params.sensorFps.divisor; + cam->cmd_queue |= COMMAND_SETSENSORFPS; + + cam->params.flickerControl.coarseJump = + flicker_jumps[cam->mainsFreq] + [cam->params.sensorFps.baserate] + [cam->params.sensorFps.divisor]; + cam->cmd_queue |= COMMAND_SETFLICKERCTRL; + + new_exposure = cam->params.flickerControl.coarseJump-1; + while(new_exposure < old_exposure/2) + new_exposure += cam->params.flickerControl.coarseJump; + cam->params.exposure.coarseExpLo = new_exposure & 0xff; + cam->params.exposure.coarseExpHi = new_exposure >> 8; + cam->cmd_queue |= COMMAND_SETEXPOSURE; + cam->exposure_status = EXPOSURE_NORMAL; + LOG("Automatically decreasing sensor_fps\n"); + + } else if((cam->exposure_status == EXPOSURE_VERY_LIGHT || + cam->exposure_status == EXPOSURE_LIGHT) && + cam->exposure_count >= LIGHT_TIME*framerate && + cam->params.sensorFps.divisor > 0) { + + /* light for too long */ + int max_exp = FIRMWARE_VERSION(1,2) ? MAX_EXP_102 : MAX_EXP ; + + --cam->params.sensorFps.divisor; + cam->cmd_queue |= COMMAND_SETSENSORFPS; + + cam->params.flickerControl.coarseJump = + flicker_jumps[cam->mainsFreq] + [cam->params.sensorFps.baserate] + [cam->params.sensorFps.divisor]; + cam->cmd_queue |= COMMAND_SETFLICKERCTRL; + + new_exposure = cam->params.flickerControl.coarseJump-1; + while(new_exposure < 2*old_exposure && + new_exposure+ + cam->params.flickerControl.coarseJump < max_exp) + new_exposure += cam->params.flickerControl.coarseJump; + cam->params.exposure.coarseExpLo = new_exposure & 0xff; + cam->params.exposure.coarseExpHi = new_exposure >> 8; + cam->cmd_queue |= COMMAND_SETEXPOSURE; + cam->exposure_status = EXPOSURE_NORMAL; + LOG("Automatically increasing sensor_fps\n"); + } + } else { + /* Flicker control off */ + if((cam->exposure_status == EXPOSURE_VERY_DARK || + cam->exposure_status == EXPOSURE_DARK) && + cam->exposure_count >= DARK_TIME*framerate && + cam->params.sensorFps.divisor < 3) { + + /* dark for too long */ + ++cam->params.sensorFps.divisor; + cam->cmd_queue |= COMMAND_SETSENSORFPS; + + if(cam->params.exposure.gain > 0) { + --cam->params.exposure.gain; + cam->cmd_queue |= COMMAND_SETEXPOSURE; + } + cam->exposure_status = EXPOSURE_NORMAL; + LOG("Automatically decreasing sensor_fps\n"); + + } else if((cam->exposure_status == EXPOSURE_VERY_LIGHT || + cam->exposure_status == EXPOSURE_LIGHT) && + cam->exposure_count >= LIGHT_TIME*framerate && + cam->params.sensorFps.divisor > 0) { + + /* light for too long */ + --cam->params.sensorFps.divisor; + cam->cmd_queue |= COMMAND_SETSENSORFPS; + + if(cam->params.exposure.gain < + cam->params.exposure.gainMode-1) { + ++cam->params.exposure.gain; + cam->cmd_queue |= COMMAND_SETEXPOSURE; + } + cam->exposure_status = EXPOSURE_NORMAL; + LOG("Automatically increasing sensor_fps\n"); + } + } + mutex_unlock(&cam->param_lock); +} + +/*-----------------------------------------------------------------*/ +/* if flicker is switched off, this function switches it back on.It checks, + however, that conditions are suitable before restarting it. + This should only be called for firmware version 1.2. + + It also adjust the colour balance when an exposure step is detected - as + long as flicker is running +*/ +static void restart_flicker(struct cam_data *cam) +{ + int cam_exposure, old_exp; + if(!FIRMWARE_VERSION(1,2)) + return; + mutex_lock(&cam->param_lock); + if(cam->params.flickerControl.flickerMode == 0 || + cam->raw_image[39] == 0) { + mutex_unlock(&cam->param_lock); + return; + } + cam_exposure = cam->raw_image[39]*2; + old_exp = cam->params.exposure.coarseExpLo + + cam->params.exposure.coarseExpHi*256; + /* + see how far away camera exposure is from a valid + flicker exposure value + */ + cam_exposure %= cam->params.flickerControl.coarseJump; + if(!cam->params.flickerControl.disabled && + cam_exposure <= cam->params.flickerControl.coarseJump - 3) { + /* Flicker control auto-disabled */ + cam->params.flickerControl.disabled = 1; + } + + if(cam->params.flickerControl.disabled && + cam->params.flickerControl.flickerMode && + old_exp > cam->params.flickerControl.coarseJump + + ROUND_UP_EXP_FOR_FLICKER) { + /* exposure is now high enough to switch + flicker control back on */ + set_flicker(&cam->params, &cam->cmd_queue, 1); + if((cam->cmd_queue & COMMAND_SETEXPOSURE) && + cam->params.exposure.expMode == 2) + cam->exposure_status = EXPOSURE_NORMAL; + + } + mutex_unlock(&cam->param_lock); +} +#undef FIRMWARE_VERSION + +static int clear_stall(struct cam_data *cam) +{ + /* FIXME: Does this actually work? */ + LOG("Clearing stall\n"); + + cam->ops->streamRead(cam->lowlevel_data, cam->raw_image, 0); + do_command(cam, CPIA_COMMAND_GetCameraStatus,0,0,0,0); + return cam->params.status.streamState != STREAM_PAUSED; +} + +/* kernel thread function to read image from camera */ +static int 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); + /* Trial & error - Discarding a frame prevents the + first frame from having an error in the data. */ + do_command(cam, CPIA_COMMAND_DiscardFrame, 0, 0, 0, 0); + } + + /* init camera upload */ + 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 */ + int count = 0; + do_command(cam, CPIA_COMMAND_GetCameraStatus,0,0,0,0); + while (cam->params.status.streamState != STREAM_READY) { + if(++count > READY_TIMEOUT) + break; + if(cam->params.status.streamState == + STREAM_PAUSED) { + /* Bad news */ + if(!clear_stall(cam)) + return -EIO; + } + + cond_resched(); + + /* sleep for 10 ms, hopefully ;) */ + msleep_interruptible(10); + if (signal_pending(current)) + return -EINTR; + + do_command(cam, CPIA_COMMAND_GetCameraStatus, + 0, 0, 0, 0); + } + if(cam->params.status.streamState != STREAM_READY) { + continue; + } + } + + cond_resched(); + + /* grab image from camera */ + 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 */ + + /* Switch flicker control back on if it got turned off */ + restart_flicker(cam); + + /* If AEC is enabled, monitor the exposure and + adjust the sensor frame rate if needed */ + if(cam->params.exposure.expMode == 2) + monitor_exposure(cam); + + /* camera idle now so dispatch queued commands */ + dispatch_commands(cam); + + /* Update our knowledge of the camera state */ + do_command(cam, CPIA_COMMAND_GetColourBalance, 0, 0, 0, 0); + do_command(cam, CPIA_COMMAND_GetExposure, 0, 0, 0, 0); + do_command(cam, CPIA_COMMAND_ReadMCPorts, 0, 0, 0, 0); + + /* decompress and convert image to by copying it from + * raw_image to decompressed_frame + */ + + cond_resched(); + + cam->image_size = parse_picture(cam, image_size); + if (cam->image_size <= 0) { + DBG("parse_picture failed %d\n", cam->image_size); + if(cam->params.compression.mode != + CPIA_COMPRESSION_NONE) { + /* Compression may not work right if we + had a bad frame, get the next one + uncompressed. */ + cam->first_frame = 1; + do_command(cam, CPIA_COMMAND_SetGrabMode, + CPIA_GRAB_SINGLE, 0, 0, 0); + /* FIXME: Trial & error - need up to 70ms for + the grab mode change to complete ? */ + msleep_interruptible(70); + if (signal_pending(current)) + return -EINTR; + } + } 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 (cam->first_frame) { + cam->first_frame = 0; + do_command(cam, CPIA_COMMAND_SetCompression, + cam->params.compression.mode, + cam->params.compression.decimation, 0, 0); + + /* Switch from single-grab to continuous grab */ + do_command(cam, CPIA_COMMAND_SetGrabMode, + CPIA_GRAB_CONTINUOUS, 0, 0, 0); + } + return 0; + } + return -EIO; +} + +static int capture_frame(struct cam_data *cam, struct video_mmap *vm) +{ + if (!cam->frame_buf) { + /* we do lazy allocation */ + int err; + if ((err = allocate_frame_buf(cam))) + return err; + } + + cam->curframe = vm->frame; + cam->frame[cam->curframe].state = FRAME_READY; + return fetch_frame(cam); +} + +static int goto_high_power(struct cam_data *cam) +{ + if (do_command(cam, CPIA_COMMAND_GotoHiPower, 0, 0, 0, 0)) + return -EIO; + msleep_interruptible(40); /* windows driver does it too */ + if(signal_pending(current)) + return -EINTR; + if (do_command(cam, CPIA_COMMAND_GetCameraStatus, 0, 0, 0, 0)) + return -EIO; + if (cam->params.status.systemState == HI_POWER_STATE) { + DBG("camera now in HIGH power state\n"); + return 0; + } + printstatus(cam); + return -EIO; +} + +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) +{ + if(!(cam->cmd_queue & COMMAND_SETCOLOURBALANCE)) + do_command(cam, CPIA_COMMAND_GetColourBalance, 0, 0, 0, 0); + if(!(cam->cmd_queue & COMMAND_SETEXPOSURE)) + 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 int set_camera_state(struct cam_data *cam) +{ + cam->cmd_queue = COMMAND_SETCOMPRESSION | + COMMAND_SETCOMPRESSIONTARGET | + COMMAND_SETCOLOURPARAMS | + COMMAND_SETFORMAT | + COMMAND_SETYUVTHRESH | + COMMAND_SETECPTIMING | + COMMAND_SETCOMPRESSIONPARAMS | + COMMAND_SETEXPOSURE | + COMMAND_SETCOLOURBALANCE | + COMMAND_SETSENSORFPS | + COMMAND_SETAPCOR | + COMMAND_SETFLICKERCTRL | + COMMAND_SETVLOFFSET; + + do_command(cam, CPIA_COMMAND_SetGrabMode, CPIA_GRAB_SINGLE,0,0,0); + dispatch_commands(cam); + + /* Wait 6 frames for the sensor to get all settings and + AEC/ACB to settle */ + msleep_interruptible(6*(cam->params.sensorFps.baserate ? 33 : 40) * + (1 << cam->params.sensorFps.divisor) + 10); + + if(signal_pending(current)) + return -EINTR; + + save_camera_state(cam); + + return 0; +} + +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) +{ + int err; + /* 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 */ + err = goto_high_power(cam); + if(err) + return err; + do_command(cam, CPIA_COMMAND_DiscardFrame, 0, 0, 0, 0); + if (goto_low_power(cam)) + return -ENODEV; + } + + /* procedure described in developer's guide p3-28 */ + + /* Check the firmware version. */ + cam->params.version.firmwareVersion = 0; + get_version_information(cam); + if (cam->params.version.firmwareVersion != 1) + return -ENODEV; + + /* A bug in firmware 1-02 limits gainMode to 2 */ + if(cam->params.version.firmwareRevision <= 2 && + cam->params.exposure.gainMode > 2) { + cam->params.exposure.gainMode = 2; + } + + /* set QX3 detected flag */ + cam->params.qx3.qx3_detected = (cam->params.pnpID.vendor == 0x0813 && + cam->params.pnpID.product == 0x0001); + + /* 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 */ + err = goto_high_power(cam); + if (err) + return err; + + /* 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 */ + return set_camera_state(cam); +} + +static void put_cam(struct cpia_camera_ops* ops) +{ + module_put(ops->owner); +} + +/* ------------------------- V4L interface --------------------- */ +static int cpia_open(struct file *file) +{ + struct video_device *dev = video_devdata(file); + struct cam_data *cam = video_get_drvdata(dev); + int err; + + if (!cam) { + DBG("Internal error, cam_data not found!\n"); + return -ENODEV; + } + + if (cam->open_count > 0) { + DBG("Camera already open\n"); + return -EBUSY; + } + + if (!try_module_get(cam->ops->owner)) + return -ENODEV; + + mutex_lock(&cam->busy_lock); + err = -ENOMEM; + if (!cam->raw_image) { + cam->raw_image = rvmalloc(CPIA_MAX_IMAGE_SIZE); + if (!cam->raw_image) + goto oops; + } + + if (!cam->decompressed_frame.data) { + cam->decompressed_frame.data = rvmalloc(CPIA_MAX_FRAME_SIZE); + if (!cam->decompressed_frame.data) + goto oops; + } + + /* open cpia */ + err = -ENODEV; + if (cam->ops->open(cam->lowlevel_data)) + goto oops; + + /* reset the camera */ + if ((err = reset_camera(cam)) != 0) { + cam->ops->close(cam->lowlevel_data); + goto oops; + } + + err = -EINTR; + if(signal_pending(current)) + goto oops; + + /* 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; + file->private_data = dev; + mutex_unlock(&cam->busy_lock); + return 0; + + oops: + if (cam->decompressed_frame.data) { + rvfree(cam->decompressed_frame.data, CPIA_MAX_FRAME_SIZE); + cam->decompressed_frame.data = NULL; + } + if (cam->raw_image) { + rvfree(cam->raw_image, CPIA_MAX_IMAGE_SIZE); + cam->raw_image = NULL; + } + mutex_unlock(&cam->busy_lock); + put_cam(cam->ops); + return err; +} + +static int cpia_close(struct file *file) +{ + struct video_device *dev = file->private_data; + struct cam_data *cam = video_get_drvdata(dev); + + 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 status */ + 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); + + put_cam(cam->ops); + } + + 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) + kfree(cam); + } + file->private_data = NULL; + + return 0; +} + +static ssize_t cpia_read(struct file *file, char __user *buf, + size_t count, loff_t *ppos) +{ + struct video_device *dev = file->private_data; + struct cam_data *cam = video_get_drvdata(dev); + int err; + + /* make this _really_ smp and multithread-safe */ + if (mutex_lock_interruptible(&cam->busy_lock)) + return -EINTR; + + if (!buf) { + DBG("buf NULL\n"); + mutex_unlock(&cam->busy_lock); + return -EINVAL; + } + + if (!count) { + DBG("count 0\n"); + mutex_unlock(&cam->busy_lock); + return 0; + } + + if (!cam->ops) { + DBG("ops NULL\n"); + mutex_unlock(&cam->busy_lock); + return -ENODEV; + } + + /* upload frame */ + cam->decompressed_frame.state = FRAME_READY; + cam->mmap_kludge=0; + if((err = fetch_frame(cam)) != 0) { + DBG("ERROR from fetch_frame: %d\n", err); + mutex_unlock(&cam->busy_lock); + return err; + } + 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, + (unsigned long) count); + mutex_unlock(&cam->busy_lock); + return -EFAULT; + } + if (copy_to_user(buf, cam->decompressed_frame.data, + cam->decompressed_frame.count)) { + DBG("copy_to_user failed\n"); + mutex_unlock(&cam->busy_lock); + return -EFAULT; + } + + mutex_unlock(&cam->busy_lock); + return cam->decompressed_frame.count; +} + +static long cpia_do_ioctl(struct file *file, unsigned int cmd, void *arg) +{ + struct video_device *dev = file->private_data; + struct cam_data *cam = video_get_drvdata(dev); + int retval = 0; + + if (!cam || !cam->ops) + return -ENODEV; + + /* make this _really_ smp-safe */ + if (mutex_lock_interruptible(&cam->busy_lock)) + return -EINTR; + + /* DBG("cpia_ioctl: %u\n", cmd); */ + + switch (cmd) { + /* query capabilities */ + case VIDIOCGCAP: + { + struct video_capability *b = arg; + + DBG("VIDIOCGCAP\n"); + strcpy(b->name, "CPiA Camera"); + b->type = VID_TYPE_CAPTURE | VID_TYPE_SUBCAPTURE; + b->channels = 1; + b->audios = 0; + b->maxwidth = 352; /* VIDEOSIZE_CIF */ + b->maxheight = 288; + b->minwidth = 48; /* VIDEOSIZE_48_48 */ + b->minheight = 48; + break; + } + + /* get/set video source - we are a camera and nothing else */ + case VIDIOCGCHAN: + { + struct video_channel *v = arg; + + DBG("VIDIOCGCHAN\n"); + 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; + break; + } + + case VIDIOCSCHAN: + { + struct video_channel *v = arg; + + DBG("VIDIOCSCHAN\n"); + if (v->channel != 0) + retval = -EINVAL; + break; + } + + /* image properties */ + case VIDIOCGPICT: + { + struct video_picture *pic = arg; + DBG("VIDIOCGPICT\n"); + *pic = cam->vp; + break; + } + + case VIDIOCSPICT: + { + struct video_picture *vp = arg; + + DBG("VIDIOCSPICT\n"); + + /* check validity */ + DBG("palette: %d\n", vp->palette); + DBG("depth: %d\n", vp->depth); + if (!valid_mode(vp->palette, vp->depth)) { + retval = -EINVAL; + break; + } + + mutex_lock(&cam->param_lock); + /* brightness, colour, contrast need no check 0-65535 */ + cam->vp = *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; + } + + /* Adjust flicker control if necessary */ + if(cam->params.flickerControl.allowableOverExposure < 0) + cam->params.flickerControl.allowableOverExposure = + -find_over_exposure(cam->params.colourParams.brightness); + if(cam->params.flickerControl.flickerMode != 0) + cam->cmd_queue |= COMMAND_SETFLICKERCTRL; + + + /* queue command to update camera */ + cam->cmd_queue |= COMMAND_SETCOLOURPARAMS; + mutex_unlock(&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: + { + struct video_window *vw = arg; + DBG("VIDIOCGWIN\n"); + + *vw = cam->vw; + break; + } + + case VIDIOCSWIN: + { + /* copy_from_user, check validity, copy to internal structure */ + struct video_window *vw = arg; + DBG("VIDIOCSWIN\n"); + + 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??? + */ + mutex_lock(&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; + mutex_unlock(&cam->param_lock); + break; + } + cam->video_size = video_size; + + /* video size is changing, reset the subcapture area */ + memset(&cam->vc, 0, sizeof(cam->vc)); + + set_vw_size(cam); + DBG("%d / %d\n", cam->vw.width, cam->vw.height); + cam->cmd_queue |= COMMAND_SETFORMAT; + } + + mutex_unlock(&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 = arg; + 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; + break; + } + + case VIDIOCMCAPTURE: + { + struct video_mmap *vm = arg; + int video_size; + + DBG("VIDIOCMCAPTURE: %d / %d / %dx%d\n", vm->format, vm->frame, + vm->width, vm->height); + 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: + cam->vp.depth=8; + break; + 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 (video_size < 0) { + retval = -EINVAL; + break; + } + if (video_size != cam->video_size) { + cam->video_size = video_size; + + /* video size is changing, reset the subcapture area */ + memset(&cam->vc, 0, sizeof(cam->vc)); + + set_vw_size(cam); + cam->cmd_queue |= COMMAND_SETFORMAT; + dispatch_commands(cam); + } + /* according to v4l-spec we must start streaming here */ + cam->mmap_kludge = 1; + retval = capture_frame(cam, vm); + + break; + } + + case VIDIOCSYNC: + { + int *frame = arg; + + //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; + } + + case VIDIOCGCAPTURE: + { + struct video_capture *vc = arg; + + DBG("VIDIOCGCAPTURE\n"); + + *vc = cam->vc; + + break; + } + + case VIDIOCSCAPTURE: + { + struct video_capture *vc = arg; + + DBG("VIDIOCSCAPTURE\n"); + + if (vc->decimation != 0) { /* How should this be used? */ + retval = -EINVAL; + break; + } + if (vc->flags != 0) { /* Even/odd grab not supported */ + retval = -EINVAL; + break; + } + + /* Clip to the resolution we can set for the ROI + (every 8 columns and 4 rows) */ + vc->x = vc->x & ~(__u32)7; + vc->y = vc->y & ~(__u32)3; + vc->width = vc->width & ~(__u32)7; + vc->height = vc->height & ~(__u32)3; + + if(vc->width == 0 || vc->height == 0 || + vc->x + vc->width > cam->vw.width || + vc->y + vc->height > cam->vw.height) { + retval = -EINVAL; + break; + } + + DBG("%d,%d/%dx%d\n", vc->x,vc->y,vc->width, vc->height); + + mutex_lock(&cam->param_lock); + + cam->vc.x = vc->x; + cam->vc.y = vc->y; + cam->vc.width = vc->width; + cam->vc.height = vc->height; + + set_vw_size(cam); + cam->cmd_queue |= COMMAND_SETFORMAT; + + mutex_unlock(&cam->param_lock); + + /* setformat ignored by camera during streaming, + * so stop/dispatch/start */ + dispatch_commands(cam); + break; + } + + case VIDIOCGUNIT: + { + struct video_unit *vu = arg; + + DBG("VIDIOCGUNIT\n"); + + vu->video = cam->vdev.minor; + vu->vbi = VIDEO_NO_UNIT; + vu->radio = VIDEO_NO_UNIT; + vu->audio = VIDEO_NO_UNIT; + vu->teletext = VIDEO_NO_UNIT; + + break; + } + + + /* pointless to implement overlay with this camera */ + case VIDIOCCAPTURE: + case VIDIOCGFBUF: + case VIDIOCSFBUF: + case VIDIOCKEY: + /* tuner interface - we have none */ + case VIDIOCGTUNER: + case VIDIOCSTUNER: + case VIDIOCGFREQ: + case VIDIOCSFREQ: + /* audio interface - we have none */ + case VIDIOCGAUDIO: + case VIDIOCSAUDIO: + retval = -EINVAL; + break; + default: + retval = -ENOIOCTLCMD; + break; + } + + mutex_unlock(&cam->busy_lock); + return retval; +} + +static long cpia_ioctl(struct file *file, + unsigned int cmd, unsigned long arg) +{ + return video_usercopy(file, cmd, arg, cpia_do_ioctl); +} + + +/* FIXME */ +static int cpia_mmap(struct file *file, struct vm_area_struct *vma) +{ + struct video_device *dev = file->private_data; + unsigned long start = vma->vm_start; + unsigned long size = vma->vm_end - vma->vm_start; + unsigned long page, pos; + struct cam_data *cam = video_get_drvdata(dev); + int retval; + + if (!cam || !cam->ops) + return -ENODEV; + + DBG("cpia_mmap: %ld\n", size); + + if (size > FRAME_NUM*CPIA_MAX_FRAME_SIZE) + return -EINVAL; + + /* make this _really_ smp-safe */ + if (mutex_lock_interruptible(&cam->busy_lock)) + return -EINTR; + + if (!cam->frame_buf) { /* we do lazy allocation */ + if ((retval = allocate_frame_buf(cam))) { + mutex_unlock(&cam->busy_lock); + return retval; + } + } + + pos = (unsigned long)(cam->frame_buf); + while (size > 0) { + page = vmalloc_to_pfn((void *)pos); + if (remap_pfn_range(vma, start, page, PAGE_SIZE, PAGE_SHARED)) { + mutex_unlock(&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); + mutex_unlock(&cam->busy_lock); + + return 0; +} + +static const struct v4l2_file_operations cpia_fops = { + .owner = THIS_MODULE, + .open = cpia_open, + .release = cpia_close, + .read = cpia_read, + .mmap = cpia_mmap, + .ioctl = cpia_ioctl, +}; + +static struct video_device cpia_template = { + .name = "CPiA Camera", + .fops = &cpia_fops, + .release = video_device_release_empty, +}; + +/* 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 = 4; + 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 = COMP_RED; + cam->params.exposure.green1Comp = COMP_GREEN1; + cam->params.exposure.green2Comp = COMP_GREEN2; + cam->params.exposure.blueComp = COMP_BLUE; + cam->params.colourBalance.balanceMode = 2; /* ACB */ + cam->params.colourBalance.redGain = 32; + cam->params.colourBalance.greenGain = 6; + cam->params.colourBalance.blueGain = 92; + cam->params.apcor.gain1 = 0x18; + cam->params.apcor.gain2 = 0x16; + cam->params.apcor.gain4 = 0x24; + cam->params.apcor.gain8 = 0x34; + cam->params.flickerControl.flickerMode = 0; + cam->params.flickerControl.disabled = 1; + + cam->params.flickerControl.coarseJump = + flicker_jumps[cam->mainsFreq] + [cam->params.sensorFps.baserate] + [cam->params.sensorFps.divisor]; + cam->params.flickerControl.allowableOverExposure = + -find_over_exposure(cam->params.colourParams.brightness); + cam->params.vlOffset.gain1 = 20; + cam->params.vlOffset.gain2 = 24; + cam->params.vlOffset.gain4 = 26; + cam->params.vlOffset.gain8 = 26; + 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; + cam->exposure_status = EXPOSURE_NORMAL; + + /* 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 = 6; /* From windows driver */ + cam->params.yuvThreshold.uvThreshold = 6; /* From windows driver */ + + 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 = 15; /* From windows driver */ + cam->params.compressionTarget.targetQ = 5; /* From windows driver */ + + cam->params.qx3.qx3_detected = 0; + cam->params.qx3.toplight = 0; + cam->params.qx3.bottomlight = 0; + cam->params.qx3.button = 0; + cam->params.qx3.cradled = 0; + + 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 = 24; /* to be set by user */ + cam->vp.palette = VIDEO_PALETTE_RGB24; /* to be set by user */ + + cam->vc.x = 0; + cam->vc.y = 0; + cam->vc.width = 0; + cam->vc.height = 0; + + cam->vw.x = 0; + cam->vw.y = 0; + set_vw_size(cam); + cam->vw.chromakey = 0; + cam->vw.flags = 0; + cam->vw.clipcount = 0; + cam->vw.clips = NULL; + + cam->cmd_queue = COMMAND_NONE; + cam->first_frame = 1; + + 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; + mutex_init(&cam->param_lock); + mutex_init(&cam->busy_lock); + + reset_camera_struct(cam); + + cam->proc_entry = NULL; + + memcpy(&cam->vdev, &cpia_template, sizeof(cpia_template)); + video_set_drvdata(&cam->vdev, 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; + + if ((camera = kmalloc(sizeof(struct cam_data), GFP_KERNEL)) == NULL) + return NULL; + + + init_camera_struct( camera, ops ); + camera->lowlevel_data = lowlevel; + + /* register v4l device */ + if (video_register_device(&camera->vdev, VFL_TYPE_GRABBER, video_nr) < 0) { + kfree(camera); + 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); + +#ifdef CONFIG_PROC_FS + create_proc_cpia_cam(camera); +#endif + + 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) +{ + DBG("unregistering video\n"); + video_unregister_device(&cam->vdev); + if (cam->open_count) { + put_cam(cam->ops); + DBG("camera open -- setting ops to NULL\n"); + cam->ops = NULL; + } + +#ifdef CONFIG_PROC_FS + DBG("destroying /proc/cpia/%s\n", video_device_node_name(&cam->vdev)); + destroy_proc_cpia_cam(cam); +#endif + if (!cam->open_count) { + DBG("freeing camera\n"); + kfree(cam); + } +} + +static int __init cpia_init(void) +{ + printk(KERN_INFO "%s v%d.%d.%d\n", ABOUT, + CPIA_MAJ_VER, CPIA_MIN_VER, CPIA_PATCH_VER); + + printk(KERN_WARNING "Since in-kernel colorspace conversion is not " + "allowed, it is disabled by default now. Users should fix the " + "applications in case they don't work without conversion " + "reenabled by setting the 'colorspace_conv' module " + "parameter to 1\n"); + +#ifdef CONFIG_PROC_FS + proc_cpia_create(); +#endif + + return 0; +} + +static void __exit cpia_exit(void) +{ +#ifdef CONFIG_PROC_FS + proc_cpia_destroy(); +#endif +} + +module_init(cpia_init); +module_exit(cpia_exit); + +/* Exported symbols for modules. */ + +EXPORT_SYMBOL(cpia_register_camera); +EXPORT_SYMBOL(cpia_unregister_camera); diff --git a/drivers/staging/cpia/cpia.h b/drivers/staging/cpia/cpia.h new file mode 100644 index 000000000000..8f0cfee4b8a1 --- /dev/null +++ b/drivers/staging/cpia/cpia.h @@ -0,0 +1,432 @@ +#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 1 +#define CPIA_MIN_VER 2 +#define CPIA_PATCH_VER 3 + +#define CPIA_PP_MAJ_VER CPIA_MAJ_VER +#define CPIA_PP_MIN_VER CPIA_MIN_VER +#define CPIA_PP_PATCH_VER CPIA_PATCH_VER + +#define CPIA_USB_MAJ_VER CPIA_MAJ_VER +#define CPIA_USB_MIN_VER CPIA_MIN_VER +#define CPIA_USB_PATCH_VER CPIA_PATCH_VER + +#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 +#include +#include +#include +#include +#include + +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 command. 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; + + /* + * Used to maintain lowlevel module usage counts + */ + struct module *owner; +}; + +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 balanceMode; + 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 disabled; + u8 flickerMode; + u8 coarseJump; + int 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 { /* Intel QX3 specific data */ + u8 qx3_detected; /* a QX3 is present */ + u8 toplight; /* top light lit , R/W */ + u8 bottomlight; /* bottom light lit, R/W */ + u8 button; /* snapshot button pressed (R/O) */ + u8 cradled; /* microscope is in cradle (R/O) */ + } qx3; + 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 list_head cam_data_list; + + struct mutex 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 mutex 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 */ + struct video_capture vc; /* v4l subcapture 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 */ + int exposure_status; /* EXPOSURE_* */ + int exposure_count; /* number of frames at this status */ +}; + +/* 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 + +/* default (minimum) compensation values */ +#define COMP_RED 220 +#define COMP_GREEN1 214 +#define COMP_GREEN2 COMP_GREEN1 +#define COMP_BLUE 230 + +/* exposure status */ +#define EXPOSURE_VERY_LIGHT 0 +#define EXPOSURE_LIGHT 1 +#define EXPOSURE_NORMAL 2 +#define EXPOSURE_DARK 3 +#define EXPOSURE_VERY_DARK 4 + +/* ErrorCode */ +#define ERROR_FLICKER_BELOW_MIN_EXP 0x01 /*flicker exposure got below minimum exposure */ +#define ALOG(fmt,args...) printk(fmt, ##args) +#define LOG(fmt,args...) ALOG(KERN_INFO __FILE__ ":%s(%d):" fmt, __func__ , __LINE__ , ##args) + +#ifdef _CPIA_DEBUG_ +#define ADBG(fmt,args...) printk(fmt, jiffies, ##args) +#define DBG(fmt,args...) ADBG(KERN_DEBUG __FILE__" (%ld):%s(%d):" fmt, __func__, __LINE__ , ##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); + +#endif /* __KERNEL__ */ + +#endif /* cpia_h */ diff --git a/drivers/staging/cpia/cpia_pp.c b/drivers/staging/cpia/cpia_pp.c new file mode 100644 index 000000000000..f5604c16a092 --- /dev/null +++ b/drivers/staging/cpia/cpia_pp.c @@ -0,0 +1,869 @@ +/* + * cpia_pp CPiA Parallel Port driver + * + * Supports CPiA based parallel port Video Camera's. + * + * (C) Copyright 1999 Bas Huisman + * (C) Copyright 1999-2000 Scott J. Bertin , + * (C) Copyright 1999-2000 Peter Pregler + * + * 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_ for verbose debug output (see cpia.h) */ +/* #define _CPIA_DEBUG_ 1 */ + + +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include + +/* #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" + +#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 + +static int parport_nr[PARPORT_MAX] = {[0 ... PARPORT_MAX - 1] = PPCPIA_PARPORT_UNSPEC}; +static char *parport[PARPORT_MAX] = {NULL,}; + +MODULE_AUTHOR("B. Huisman & Peter Pregler "); +MODULE_DESCRIPTION("Parallel port driver for Vision CPiA based cameras"); +MODULE_LICENSE("GPL"); + +module_param_array(parport, charp, NULL, 0); +MODULE_PARM_DESC(parport, "'auto' or a list of parallel port numbers. Just like lp."); + +struct pp_cam_entry { + struct pardevice *pdev; + struct parport *port; + struct work_struct cb_task; + void (*cb_func)(void *cbdata); + void *cb_data; + 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, + THIS_MODULE +}; + +static LIST_HEAD(cam_list); +static spinlock_t cam_list_lock_pp; + +/* 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; +} + +/* Special CPiA PPC modes: These are invoked by using the 1284 Extensibility + * Link Flag during negotiation */ +#define UPLOAD_FLAG 0x08 +#define NIBBLE_TRANSFER 0x01 +#define ECP_TRANSFER 0x03 + +#define PARPORT_CHUNK_SIZE PAGE_SIZE + + +static void cpia_pp_run_callback(struct work_struct *work) +{ + void (*cb_func)(void *cbdata); + void *cb_data; + struct pp_cam_entry *cam; + + cam = container_of(work, struct pp_cam_entry, cb_task); + cb_func = cam->cb_func; + cb_data = cam->cb_data; + + cb_func(cb_data); +} + +/**************************************************************************** + * + * CPiA-specific low-level parport functions for nibble uploads + * + ***************************************************************************/ +/* CPiA nonstandard "Nibble" mode (no nDataAvail signal after each byte). */ +/* The standard kernel parport_ieee1284_read_nibble() fails with the CPiA... */ + +static size_t cpia_read_nibble (struct parport *port, + void *buffer, size_t len, + int flags) +{ + /* adapted verbatim, with one change, from + parport_ieee1284_read_nibble() in drivers/parport/ieee1284-ops.c */ + + unsigned char *buf = buffer; + int i; + unsigned char byte = 0; + + len *= 2; /* in nibbles */ + for (i=0; i < len; i++) { + unsigned char nibble; + + /* The CPiA firmware suppresses the use of nDataAvail (nFault LO) + * after every second nibble to signal that more + * data is available. (the total number of Bytes that + * should be sent is known; if too few are received, an error + * will be recorded after a timeout). + * This is incompatible with parport_ieee1284_read_nibble(), + * which expects to find nFault LO after every second nibble. + */ + + /* Solution: modify cpia_read_nibble to only check for + * nDataAvail before the first nibble is sent. + */ + + /* Does the error line indicate end of data? */ + if (((i /*& 1*/) == 0) && + (parport_read_status(port) & PARPORT_STATUS_ERROR)) { + DBG("%s: No more nibble data (%d bytes)\n", + port->name, i/2); + goto end_of_data; + } + + /* Event 7: Set nAutoFd low. */ + parport_frob_control (port, + PARPORT_CONTROL_AUTOFD, + PARPORT_CONTROL_AUTOFD); + + /* Event 9: nAck goes low. */ + port->ieee1284.phase = IEEE1284_PH_REV_DATA; + if (parport_wait_peripheral (port, + PARPORT_STATUS_ACK, 0)) { + /* Timeout -- no more data? */ + DBG("%s: Nibble timeout at event 9 (%d bytes)\n", + port->name, i/2); + parport_frob_control (port, PARPORT_CONTROL_AUTOFD, 0); + break; + } + + + /* Read a nibble. */ + nibble = parport_read_status (port) >> 3; + nibble &= ~8; + if ((nibble & 0x10) == 0) + nibble |= 8; + nibble &= 0xf; + + /* Event 10: Set nAutoFd high. */ + parport_frob_control (port, PARPORT_CONTROL_AUTOFD, 0); + + /* Event 11: nAck goes high. */ + if (parport_wait_peripheral (port, + PARPORT_STATUS_ACK, + PARPORT_STATUS_ACK)) { + /* Timeout -- no more data? */ + DBG("%s: Nibble timeout at event 11\n", + port->name); + break; + } + + if (i & 1) { + /* Second nibble */ + byte |= nibble << 4; + *buf++ = byte; + } else + byte = nibble; + } + + if (i == len) { + /* Read the last nibble without checking data avail. */ + if (parport_read_status (port) & PARPORT_STATUS_ERROR) { + end_of_data: + /* Go to reverse idle phase. */ + parport_frob_control (port, + PARPORT_CONTROL_AUTOFD, + PARPORT_CONTROL_AUTOFD); + port->physport->ieee1284.phase = IEEE1284_PH_REV_IDLE; + } + else + port->physport->ieee1284.phase = IEEE1284_PH_HBUSY_DAVAIL; + } + + return i/2; +} + +/* CPiA nonstandard "Nibble Stream" mode (2 nibbles per cycle, instead of 1) + * (See CPiA Data sheet p. 31) + * + * "Nibble Stream" mode used by CPiA for uploads to non-ECP ports is a + * nonstandard variant of nibble mode which allows the same (mediocre) + * data flow of 8 bits per cycle as software-enabled ECP by TRISTATE-capable + * parallel ports, but works also for non-TRISTATE-capable ports. + * (Standard nibble mode only send 4 bits per cycle) + * + */ + +static size_t cpia_read_nibble_stream(struct parport *port, + void *buffer, size_t len, + int flags) +{ + int i; + unsigned char *buf = buffer; + int endseen = 0; + + for (i=0; i < len; i++) { + unsigned char nibble[2], byte = 0; + int j; + + /* Image Data is complete when 4 consecutive EOI bytes (0xff) are seen */ + if (endseen > 3 ) + break; + + /* Event 7: Set nAutoFd low. */ + parport_frob_control (port, + PARPORT_CONTROL_AUTOFD, + PARPORT_CONTROL_AUTOFD); + + /* Event 9: nAck goes low. */ + port->ieee1284.phase = IEEE1284_PH_REV_DATA; + if (parport_wait_peripheral (port, + PARPORT_STATUS_ACK, 0)) { + /* Timeout -- no more data? */ + DBG("%s: Nibble timeout at event 9 (%d bytes)\n", + port->name, i/2); + parport_frob_control (port, PARPORT_CONTROL_AUTOFD, 0); + break; + } + + /* Read lower nibble */ + nibble[0] = parport_read_status (port) >>3; + + /* Event 10: Set nAutoFd high. */ + parport_frob_control (port, PARPORT_CONTROL_AUTOFD, 0); + + /* Event 11: nAck goes high. */ + if (parport_wait_peripheral (port, + PARPORT_STATUS_ACK, + PARPORT_STATUS_ACK)) { + /* Timeout -- no more data? */ + DBG("%s: Nibble timeout at event 11\n", + port->name); + break; + } + + /* Read upper nibble */ + nibble[1] = parport_read_status (port) >>3; + + /* reassemble the byte */ + for (j = 0; j < 2 ; j++ ) { + nibble[j] &= ~8; + if ((nibble[j] & 0x10) == 0) + nibble[j] |= 8; + nibble[j] &= 0xf; + } + byte = (nibble[0] |(nibble[1] << 4)); + *buf++ = byte; + + if(byte == EOI) + endseen++; + else + endseen = 0; + } + return i; +} + +/**************************************************************************** + * + * 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; + + /* The CPiA uses ECP protocol for Downloads from the Host to the camera. + * This will be software-emulated if ECP hardware is not present + */ + + /* the usual camera maximum response time is 10ms, but after receiving + * some commands, it needs up to 40ms. (Data Sheet p. 32)*/ + + for(retry = 0; retry < 4; ++retry) { + if(!parport_negotiate(cam->port, IEEE1284_MODE_ECP)) { + break; + } + mdelay(10); + } + if(retry == 4) { + DBG("Unable to negotiate IEEE1284 ECP Download mode\n"); + return -1; + } + return 0; +} +/**************************************************************************** + * + * ReverseSetup + * + ***************************************************************************/ +static int ReverseSetup(struct pp_cam_entry *cam, int extensibility) +{ + int retry; + int upload_mode, mode = IEEE1284_MODE_ECP; + int transfer_mode = ECP_TRANSFER; + + if (!(cam->port->modes & PARPORT_MODE_ECP) && + !(cam->port->modes & PARPORT_MODE_TRISTATE)) { + mode = IEEE1284_MODE_NIBBLE; + transfer_mode = NIBBLE_TRANSFER; + } + + upload_mode = mode; + if(extensibility) mode = UPLOAD_FLAG|transfer_mode|IEEE1284_EXT_LINK; + + /* the usual camera maximum response time is 10ms, but after + * receiving some commands, it needs up to 40ms. */ + + for(retry = 0; retry < 4; ++retry) { + if(!parport_negotiate(cam->port, mode)) { + break; + } + mdelay(10); + } + if(retry == 4) { + if(extensibility) + DBG("Unable to negotiate upload extensibility mode\n"); + else + DBG("Unable to negotiate upload mode\n"); + return -1; + } + if(extensibility) cam->port->ieee1284.mode = upload_mode; + 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; + } + + /* support for CPiA variant nibble reads */ + if(cam->port->ieee1284.mode == IEEE1284_MODE_NIBBLE) { + if(cpia_read_nibble(cam->port, packet, size, 0) != size) + retval = -EIO; + } else { + 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; +} + +/**************************************************************************** + * + * cpia_pp_streamRead + * + ***************************************************************************/ +static int cpia_pp_read(struct parport *port, u8 *buffer, int len) +{ + int bytes_read; + + /* support for CPiA variant "nibble stream" reads */ + if(port->ieee1284.mode == IEEE1284_MODE_NIBBLE) + bytes_read = cpia_read_nibble_stream(port,buffer,len,0); + else { + int new_bytes; + for(bytes_read=0; bytes_readstreaming) 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 ) { + cond_resched(); + + new_bytes = cpia_pp_read(cam->port, buffer, block_size ); + if( new_bytes <= 0 ) { + break; + } + i=-1; + while(++iimage_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) { + DBG("Error reading command result\n"); + return err; + } + 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; + + 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_func = cb; + cam->cb_data = cbdata; + INIT_WORK(&cam->cb_task, cpia_pp_run_callback); + } else { + retval = -1; + } + return retval; +} + +/**************************************************************************** + * + * cpia_pp_close + * + ***************************************************************************/ +static int cpia_pp_close(void *privdata) +{ + struct pp_cam_entry *cam = privdata; + 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_PCSPP)) { + LOG("port is not supported by CPiA driver\n"); + return -ENXIO; + } + + cam = kzalloc(sizeof(struct pp_cam_entry), GFP_KERNEL); + if (cam == NULL) { + LOG("failed to allocate camera structure\n"); + return -ENOMEM; + } + + 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; + } + spin_lock( &cam_list_lock_pp ); + list_add( &cpia->cam_data_list, &cam_list ); + spin_unlock( &cam_list_lock_pp ); + + return 0; +} + +static void cpia_pp_detach (struct parport *port) +{ + struct list_head *tmp; + struct cam_data *cpia = NULL; + struct pp_cam_entry *cam; + + spin_lock( &cam_list_lock_pp ); + list_for_each (tmp, &cam_list) { + cpia = list_entry(tmp, struct cam_data, cam_data_list); + cam = (struct pp_cam_entry *) cpia->lowlevel_data; + if (cam && cam->port->number == port->number) { + list_del(&cpia->cam_data_list); + break; + } + cpia = NULL; + } + spin_unlock( &cam_list_lock_pp ); + + if (!cpia) { + DBG("cpia_pp_detach failed to find cam_data in cam_list\n"); + return; + } + + cam = (struct pp_cam_entry *) cpia->lowlevel_data; + cpia_unregister_camera(cpia); + if(cam->open_count > 0) + cpia_pp_close(cam); + parport_unregister_device(cam->pdev); + cpia->lowlevel_data = NULL; + kfree(cam); +} + +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 = { + .name = "cpia_pp", + .attach = cpia_pp_attach, + .detach = cpia_pp_detach, +}; + +static int __init 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; + } + + spin_lock_init( &cam_list_lock_pp ); + + if (parport_register_driver (&cpia_pp_driver)) { + LOG ("unable to register with parport\n"); + return -EIO; + } + return 0; +} + +static int __init cpia_init(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; + } + } + } + } + } + return cpia_pp_init(); +} + +static void __exit cpia_cleanup(void) +{ + parport_unregister_driver(&cpia_pp_driver); + return; +} + +module_init(cpia_init); +module_exit(cpia_cleanup); diff --git a/drivers/staging/cpia/cpia_usb.c b/drivers/staging/cpia/cpia_usb.c new file mode 100644 index 000000000000..58d193ff591c --- /dev/null +++ b/drivers/staging/cpia/cpia_usb.c @@ -0,0 +1,640 @@ +/* + * cpia_usb CPiA USB driver + * + * Supports CPiA based parallel port Video Camera's. + * + * Copyright (C) 1999 Jochen Scharrlach + * Copyright (C) 1999, 2000 Johannes Erdfelt + * + * 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_ for verbose debug output (see cpia.h) */ +/* #define _CPIA_DEBUG_ 1 */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#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; + struct urb *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, + THIS_MODULE +}; + +static LIST_HEAD(cam_list); +static spinlock_t cam_list_lock_usb; + +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); + } + } + } + + /* resubmit */ + urb->dev = ucpia->dev; + if ((i = usb_submit_urb(urb, GFP_ATOMIC)) != 0) + printk(KERN_ERR "%s: usb_submit_urb ret %d\n", __func__, i); +} + +static int cpia_usb_open(void *privdata) +{ + struct usb_cpia *ucpia = (struct usb_cpia *) privdata; + struct urb *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_1; + } + + 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, and also know the polling + * interval is every frame (1 == (1 << (bInterval -1))). + */ + urb = usb_alloc_urb(FRAMES_PER_DESC, GFP_KERNEL); + if (!urb) { + printk(KERN_ERR "cpia_init_isoc: usb_alloc_urb 0\n"); + retval = -ENOMEM; + goto error_1; + } + + ucpia->sbuf[0].urb = urb; + urb->dev = ucpia->dev; + urb->context = ucpia; + urb->pipe = usb_rcvisocpipe(ucpia->dev, 1); + urb->transfer_flags = URB_ISO_ASAP; + urb->transfer_buffer = ucpia->sbuf[0].data; + urb->complete = cpia_usb_complete; + urb->number_of_packets = FRAMES_PER_DESC; + urb->interval = 1; + 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, GFP_KERNEL); + if (!urb) { + printk(KERN_ERR "cpia_init_isoc: usb_alloc_urb 1\n"); + retval = -ENOMEM; + goto error_urb0; + } + + ucpia->sbuf[1].urb = urb; + urb->dev = ucpia->dev; + urb->context = ucpia; + urb->pipe = usb_rcvisocpipe(ucpia->dev, 1); + urb->transfer_flags = URB_ISO_ASAP; + urb->transfer_buffer = ucpia->sbuf[1].data; + urb->complete = cpia_usb_complete; + urb->number_of_packets = FRAMES_PER_DESC; + urb->interval = 1; + 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; + } + + /* queue the ISO urbs, and resubmit in the completion handler */ + err = usb_submit_urb(ucpia->sbuf[0].urb, GFP_KERNEL); + if (err) { + printk(KERN_ERR "cpia_init_isoc: usb_submit_urb 0 ret %d\n", + err); + goto error_urb1; + } + err = usb_submit_urb(ucpia->sbuf[1].urb, GFP_KERNEL); + if (err) { + printk(KERN_ERR "cpia_init_isoc: usb_submit_urb 1 ret %d\n", + err); + goto error_urb1; + } + + ucpia->streaming = 1; + ucpia->open = 1; + + return 0; + +error_urb1: /* free urb 1 */ + usb_free_urb(ucpia->sbuf[1].urb); + ucpia->sbuf[1].urb = NULL; +error_urb0: /* free urb 0 */ + usb_free_urb(ucpia->sbuf[0].urb); + ucpia->sbuf[0].urb = NULL; +error_1: + kfree (ucpia->sbuf[1].data); + ucpia->sbuf[1].data = NULL; +error_0: + kfree (ucpia->sbuf[0].data); + ucpia->sbuf[0].data = NULL; + + 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, 1000); +} + +/**************************************************************************** + * + * 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, 1000); +} + +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_kill_urb(ucpia->sbuf[1].urb); + usb_free_urb(ucpia->sbuf[1].urb); + ucpia->sbuf[1].urb = NULL; + } + + kfree(ucpia->sbuf[1].data); + ucpia->sbuf[1].data = NULL; + + if (ucpia->sbuf[0].urb) { + usb_kill_urb(ucpia->sbuf[0].urb); + usb_free_urb(ucpia->sbuf[0].urb); + ucpia->sbuf[0].urb = NULL; + } + + kfree(ucpia->sbuf[0].data); + ucpia->sbuf[0].data = NULL; +} + +static int cpia_usb_close(void *privdata) +{ + struct usb_cpia *ucpia = (struct usb_cpia *) privdata; + + if(!ucpia) + return -ENODEV; + + ucpia->open = 0; + + /* ucpia->present = 0 protects against trying to reset the + * alt setting if camera is physically disconnected while open */ + cpia_usb_free_resources(ucpia, ucpia->present); + + return 0; +} + +/* Probing and initializing */ + +static int cpia_probe(struct usb_interface *intf, + const struct usb_device_id *id) +{ + struct usb_device *udev = interface_to_usbdev(intf); + struct usb_host_interface *interface; + struct usb_cpia *ucpia; + struct cam_data *cam; + int ret; + + /* A multi-config CPiA camera? */ + if (udev->descriptor.bNumConfigurations != 1) + return -ENODEV; + + interface = intf->cur_altsetting; + + printk(KERN_INFO "USB CPiA camera found\n"); + + ucpia = kzalloc(sizeof(*ucpia), GFP_KERNEL); + if (!ucpia) { + printk(KERN_ERR "couldn't kmalloc cpia struct\n"); + return -ENOMEM; + } + + ucpia->dev = udev; + ucpia->iface = interface->desc.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; + } + + spin_lock( &cam_list_lock_usb ); + list_add( &cam->cam_data_list, &cam_list ); + spin_unlock( &cam_list_lock_usb ); + + usb_set_intfdata(intf, cam); + return 0; + +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: + kfree(ucpia); + return -EIO; +} + +static void cpia_disconnect(struct usb_interface *intf); + +static struct usb_device_id cpia_id_table [] = { + { USB_DEVICE(0x0553, 0x0002) }, + { USB_DEVICE(0x0813, 0x0001) }, + { } /* Terminating entry */ +}; + +MODULE_DEVICE_TABLE (usb, cpia_id_table); +MODULE_LICENSE("GPL"); + + +static struct usb_driver cpia_driver = { + .name = "cpia", + .probe = cpia_probe, + .disconnect = cpia_disconnect, + .id_table = cpia_id_table, +}; + +static void cpia_disconnect(struct usb_interface *intf) +{ + struct cam_data *cam = usb_get_intfdata(intf); + struct usb_cpia *ucpia; + + usb_set_intfdata(intf, NULL); + if (!cam) + return; + + ucpia = (struct usb_cpia *) cam->lowlevel_data; + spin_lock( &cam_list_lock_usb ); + list_del(&cam->cam_data_list); + spin_unlock( &cam_list_lock_usb ); + + ucpia->present = 0; + + cpia_unregister_camera(cam); + if(ucpia->open) + cpia_usb_close(cam->lowlevel_data); + + ucpia->curbuff->status = FRAME_ERROR; + + if (waitqueue_active(&ucpia->wq_stream)) + wake_up_interruptible(&ucpia->wq_stream); + + ucpia->curbuff = ucpia->workbuff = NULL; + + vfree(ucpia->buffers[2]); + ucpia->buffers[2] = NULL; + + vfree(ucpia->buffers[1]); + ucpia->buffers[1] = NULL; + + vfree(ucpia->buffers[0]); + ucpia->buffers[0] = NULL; + + cam->lowlevel_data = NULL; + kfree(ucpia); +} + +static int __init usb_cpia_init(void) +{ + printk(KERN_INFO "%s v%d.%d.%d\n",ABOUT, + CPIA_USB_MAJ_VER,CPIA_USB_MIN_VER,CPIA_USB_PATCH_VER); + + spin_lock_init(&cam_list_lock_usb); + return usb_register(&cpia_driver); +} + +static void __exit usb_cpia_cleanup(void) +{ + usb_deregister(&cpia_driver); +} + + +module_init (usb_cpia_init); +module_exit (usb_cpia_cleanup); +