patch-2.2.15 linux/drivers/char/cpia.c
Next file: linux/drivers/char/cpia_pp.c
Previous file: linux/drivers/char/console.c
Back to the patch index
Back to the overall index
- Lines: 3376
- Date:
Fri Apr 21 12:45:51 2000
- Orig file:
v2.2.14/drivers/char/cpia.c
- Orig date:
Thu Jan 1 01:00:00 1970
diff -u --new-file --recursive --exclude-from ../../exclude v2.2.14/drivers/char/cpia.c linux/drivers/char/cpia.c
@@ -0,0 +1,3375 @@
+/*
+ * cpia CPiA driver
+ *
+ * Supports CPiA based Video Camera's.
+ *
+ * (C) 1999 Peter Pregler,
+ * Scott J. Bertin,
+ * 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.
+ */
+
+#include <linux/config.h>
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/wrapper.h>
+#include <linux/fs.h>
+#include <linux/vmalloc.h>
+#include <linux/slab.h>
+#include <linux/smp_lock.h>
+#include <linux/proc_fs.h>
+#include <linux/ctype.h>
+#include <linux/videodev.h>
+#include <asm/io.h>
+#include <asm/semaphore.h>
+
+#ifdef CONFIG_KMOD
+#include <linux/kmod.h>
+#endif
+
+#undef _CPIA_DEBUG_ /* define for verbose debug output */
+#include <linux/cpia.h>
+
+#ifdef CONFIG_VIDEO_CPIA_PP
+extern int cpia_pp_init(void);
+#endif
+#ifdef CONFIG_VIDEO_CPIA_USB
+extern int cpia_usb_init(void);
+#endif
+
+#ifdef MODULE
+MODULE_AUTHOR("Scott J. Bertin <sbertin@mindspring.com> & Peter Pregler <Peter_Pregler@email.com>");
+MODULE_DESCRIPTION("V4L-driver for Vision CPiA based cameras");
+MODULE_SUPPORTED_DEVICE("video");
+#endif
+
+#define ABOUT "V4L-Driver for Vision CPiA based cameras"
+
+#ifndef VID_HARDWARE_CPIA
+#define VID_HARDWARE_CPIA 24 /* FIXME -> from linux/videodev.h */
+#endif
+
+#define CPIA_MODULE_CPIA (0<<5)
+#define CPIA_MODULE_SYSTEM (1<<5)
+#define CPIA_MODULE_VP_CTRL (5<<5)
+#define CPIA_MODULE_CAPTURE (6<<5)
+#define CPIA_MODULE_DEBUG (7<<5)
+
+#define INPUT (DATA_IN << 8)
+#define OUTPUT (DATA_OUT << 8)
+
+#define CPIA_COMMAND_GetCPIAVersion (INPUT | CPIA_MODULE_CPIA | 1)
+#define CPIA_COMMAND_GetPnPID (INPUT | CPIA_MODULE_CPIA | 2)
+#define CPIA_COMMAND_GetCameraStatus (INPUT | CPIA_MODULE_CPIA | 3)
+#define CPIA_COMMAND_GotoHiPower (OUTPUT | CPIA_MODULE_CPIA | 4)
+#define CPIA_COMMAND_GotoLoPower (OUTPUT | CPIA_MODULE_CPIA | 5)
+#define CPIA_COMMAND_GotoSuspend (OUTPUT | CPIA_MODULE_CPIA | 7)
+#define CPIA_COMMAND_GotoPassThrough (OUTPUT | CPIA_MODULE_CPIA | 8)
+#define CPIA_COMMAND_ModifyCameraStatus (OUTPUT | CPIA_MODULE_CPIA | 10)
+
+#define CPIA_COMMAND_ReadVCRegs (INPUT | CPIA_MODULE_SYSTEM | 1)
+#define CPIA_COMMAND_WriteVCReg (OUTPUT | CPIA_MODULE_SYSTEM | 2)
+#define CPIA_COMMAND_ReadMCPorts (INPUT | CPIA_MODULE_SYSTEM | 3)
+#define CPIA_COMMAND_WriteMCPort (OUTPUT | CPIA_MODULE_SYSTEM | 4)
+#define CPIA_COMMAND_SetBaudRate (OUTPUT | CPIA_MODULE_SYSTEM | 5)
+#define CPIA_COMMAND_SetECPTiming (OUTPUT | CPIA_MODULE_SYSTEM | 6)
+#define CPIA_COMMAND_ReadIDATA (INPUT | CPIA_MODULE_SYSTEM | 7)
+#define CPIA_COMMAND_WriteIDATA (OUTPUT | CPIA_MODULE_SYSTEM | 8)
+#define CPIA_COMMAND_GenericCall (OUTPUT | CPIA_MODULE_SYSTEM | 9)
+#define CPIA_COMMAND_I2CStart (OUTPUT | CPIA_MODULE_SYSTEM | 10)
+#define CPIA_COMMAND_I2CStop (OUTPUT | CPIA_MODULE_SYSTEM | 11)
+#define CPIA_COMMAND_I2CWrite (OUTPUT | CPIA_MODULE_SYSTEM | 12)
+#define CPIA_COMMAND_I2CRead (INPUT | CPIA_MODULE_SYSTEM | 13)
+
+#define CPIA_COMMAND_GetVPVersion (INPUT | CPIA_MODULE_VP_CTRL | 1)
+#define CPIA_COMMAND_SetColourParams (OUTPUT | CPIA_MODULE_VP_CTRL | 3)
+#define CPIA_COMMAND_SetExposure (OUTPUT | CPIA_MODULE_VP_CTRL | 4)
+#define CPIA_COMMAND_SetColourBalance (OUTPUT | CPIA_MODULE_VP_CTRL | 6)
+#define CPIA_COMMAND_SetSensorFPS (OUTPUT | CPIA_MODULE_VP_CTRL | 7)
+#define CPIA_COMMAND_SetVPDefaults (OUTPUT | CPIA_MODULE_VP_CTRL | 8)
+#define CPIA_COMMAND_SetApcor (OUTPUT | CPIA_MODULE_VP_CTRL | 9)
+#define CPIA_COMMAND_SetFlickerCtrl (OUTPUT | CPIA_MODULE_VP_CTRL | 10)
+#define CPIA_COMMAND_SetVLOffset (OUTPUT | CPIA_MODULE_VP_CTRL | 11)
+#define CPIA_COMMAND_GetColourParams (INPUT | CPIA_MODULE_VP_CTRL | 16)
+#define CPIA_COMMAND_GetColourBalance (INPUT | CPIA_MODULE_VP_CTRL | 17)
+#define CPIA_COMMAND_GetExposure (INPUT | CPIA_MODULE_VP_CTRL | 18)
+#define CPIA_COMMAND_SetSensorMatrix (OUTPUT | CPIA_MODULE_VP_CTRL | 19)
+#define CPIA_COMMAND_ColourBars (OUTPUT | CPIA_MODULE_VP_CTRL | 25)
+#define CPIA_COMMAND_ReadVPRegs (INPUT | CPIA_MODULE_VP_CTRL | 30)
+#define CPIA_COMMAND_WriteVPReg (OUTPUT | CPIA_MODULE_VP_CTRL | 31)
+
+#define CPIA_COMMAND_GrabFrame (OUTPUT | CPIA_MODULE_CAPTURE | 1)
+#define CPIA_COMMAND_UploadFrame (OUTPUT | CPIA_MODULE_CAPTURE | 2)
+#define CPIA_COMMAND_SetGrabMode (OUTPUT | CPIA_MODULE_CAPTURE | 3)
+#define CPIA_COMMAND_InitStreamCap (OUTPUT | CPIA_MODULE_CAPTURE | 4)
+#define CPIA_COMMAND_FiniStreamCap (OUTPUT | CPIA_MODULE_CAPTURE | 5)
+#define CPIA_COMMAND_StartStreamCap (OUTPUT | CPIA_MODULE_CAPTURE | 6)
+#define CPIA_COMMAND_EndStreamCap (OUTPUT | CPIA_MODULE_CAPTURE | 7)
+#define CPIA_COMMAND_SetFormat (OUTPUT | CPIA_MODULE_CAPTURE | 8)
+#define CPIA_COMMAND_SetROI (OUTPUT | CPIA_MODULE_CAPTURE | 9)
+#define CPIA_COMMAND_SetCompression (OUTPUT | CPIA_MODULE_CAPTURE | 10)
+#define CPIA_COMMAND_SetCompressionTarget (OUTPUT | CPIA_MODULE_CAPTURE | 11)
+#define CPIA_COMMAND_SetYUVThresh (OUTPUT | CPIA_MODULE_CAPTURE | 12)
+#define CPIA_COMMAND_SetCompressionParams (OUTPUT | CPIA_MODULE_CAPTURE | 13)
+#define CPIA_COMMAND_DiscardFrame (OUTPUT | CPIA_MODULE_CAPTURE | 14)
+
+#define CPIA_COMMAND_OutputRS232 (OUTPUT | CPIA_MODULE_DEBUG | 1)
+#define CPIA_COMMAND_AbortProcess (OUTPUT | CPIA_MODULE_DEBUG | 4)
+#define CPIA_COMMAND_SetDramPage (OUTPUT | CPIA_MODULE_DEBUG | 5)
+#define CPIA_COMMAND_StartDramUpload (OUTPUT | CPIA_MODULE_DEBUG | 6)
+#define CPIA_COMMAND_StartDummyDtream (OUTPUT | CPIA_MODULE_DEBUG | 8)
+#define CPIA_COMMAND_AbortStream (OUTPUT | CPIA_MODULE_DEBUG | 9)
+#define CPIA_COMMAND_DownloadDRAM (OUTPUT | CPIA_MODULE_DEBUG | 10)
+
+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 flickerMode;
+ u8 coarseJump;
+ u8 allowableOverExposure;
+ } flickerControl;
+ struct {
+ u8 gain1;
+ u8 gain2;
+ u8 gain4;
+ u8 gain8;
+ } vlOffset;
+ struct {
+ u8 mode;
+ u8 decimation;
+ } compression;
+ struct {
+ u8 frTargeting;
+ u8 targetFR;
+ u8 targetQ;
+ } compressionTarget;
+ struct {
+ u8 yThreshold;
+ u8 uvThreshold;
+ } yuvThreshold;
+ struct {
+ u8 hysteresis;
+ u8 threshMax;
+ u8 smallStep;
+ u8 largeStep;
+ u8 decimationHysteresis;
+ u8 frDiffStepThresh;
+ u8 qDiffStepThresh;
+ u8 decimationThreshMod;
+ } compressionParams;
+ struct {
+ u8 videoSize; /* CIF/QCIF */
+ u8 subSample;
+ u8 yuvOrder;
+ } format;
+ struct {
+ u8 colStart; /* skip first 8*colStart pixels */
+ u8 colEnd; /* finish at 8*colEnd pixels */
+ u8 rowStart; /* skip first 4*rowStart lines */
+ u8 rowEnd; /* finish at 4*rowEnd lines */
+ } roi;
+ u8 ecpTiming;
+ u8 streamStartLine;
+};
+
+enum {
+ 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 FRAME_NUM 2 /* double buffering for now */
+struct cpia_frame {
+ u8 *data;
+ int count;
+ int width;
+ int height;
+ volatile int state;
+};
+
+enum v4l_camstates {
+ CPIA_V4L_IDLE = 0,
+ CPIA_V4L_ERROR,
+ CPIA_V4L_COMMAND,
+ CPIA_V4L_GRABBING,
+ CPIA_V4L_STREAMING,
+ CPIA_V4L_STREAMING_PAUSED,
+};
+
+#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
+
+struct cam_data {
+ int index; /* which camera is this */
+ struct semaphore busy_lock; /* guard against SMP multithreading */
+ struct cpia_camera_ops *ops; /* lowlevel driver operations */
+ void *lowlevel_data; /* private data for lowlevel driver */
+ u8 *raw_image; /* buffer for raw image data */
+ struct cpia_frame decompressed_frame;
+ /* buffer to hold decompressed frame */
+ int image_size; /* sizeof last decompressed image */
+ int open_count; /* # of process that have camera open */
+ /* camera status */
+ int fps; /* actual fps reported by the camera */
+ int transfer_rate; /* transfer rate from camera in kB/s */
+ u8 mainsFreq; /* for flicker control */
+
+ /* proc interface */
+ struct semaphore param_lock; /* params lock for this camera */
+ struct cam_params params; /* camera settings */
+ struct proc_dir_entry *proc_entry; /* /proc/cpia/videoX */
+
+ /* v4l */
+ int video_size; /* VIDEO_SIZE_ */
+ volatile enum v4l_camstates camstate; /* v4l layer status */
+ struct video_device vdev; /* v4l videodev */
+ struct video_picture vp; /* v4l camera settings */
+ struct video_window vw; /* v4l capture area */
+
+ /* mmap interface */
+ int curframe; /* the current frame to grab into */
+ u8 *frame_buf; /* frame buffer data */
+ struct cpia_frame frame[FRAME_NUM];
+ /* FRAME_NUM-buffering, so we need a array */
+
+ int first_frame;
+ volatile u32 cmd_queue; /* queued commands */
+};
+
+static struct cam_data *camera[CPIA_MAXCAMS] ={ [0 ... CPIA_MAXCAMS-1] = NULL };
+
+/* Developer's Guide Table 5 p 3-34
+ * indexed by [mains][sensorFps.baserate][sensorFps.divisor]*/
+static u8 flicker_jumps[2][2][4] =
+{ { { 76, 38, 19, 9 }, { 92, 46, 23, 11 } },
+ { { 64, 32, 16, 8 }, { 76, 38, 19, 9} }
+};
+
+/* forward declaration of local function */
+static void reset_camera_struct(struct cam_data *cam);
+
+/**********************************************************************
+ *
+ * Memory management
+ *
+ * This is a shameless copy from the USB-cpia driver (linux kernel
+ * version 2.3.13 or so, I have no idea what this code actually does ;).
+ * Actually it seems to be a copy of a shameless copy of the bttv-driver.
+ * Or that is a copy of a shameless copy of ... (To the powers: is there
+ * no generic kernel-function to do this sort of stuff?)
+ *
+ **********************************************************************/
+
+static inline unsigned long uvirt_to_phys(unsigned long adr)
+{
+ pgd_t *pgd;
+ pmd_t *pmd;
+ pte_t *ptep, pte;
+
+ pgd = pgd_offset(current->mm, adr);
+ if (pgd_none(*pgd)) return 0;
+ pmd = pmd_offset(pgd, adr);
+ if (pmd_none(*pmd)) return 0;
+ ptep = pte_offset(pmd, adr/*&(~PGDIR_MASK)*/);
+ pte = *ptep;
+ if(pte_present(pte))
+ return
+ virt_to_phys((void *)(pte_page(pte)|(adr&(PAGE_SIZE-1))));
+ return 0;
+}
+
+static inline unsigned long kvirt_to_phys(unsigned long adr)
+{
+ return uvirt_to_phys(VMALLOC_VMADDR(adr));
+}
+
+static void * rvmalloc(unsigned long size)
+{
+ void * mem;
+ unsigned long adr, page;
+
+ size += (PAGE_SIZE - 1);
+ size &= ~(PAGE_SIZE - 1);
+
+ mem=vmalloc(size);
+ if (mem) {
+ /* Clear the ram out, no junk to the user */
+ memset(mem, 0, size);
+ adr=(unsigned long) mem;
+ while (size > 0) {
+ page = kvirt_to_phys(adr);
+ mem_map_reserve(MAP_NR(phys_to_virt(page)));
+ adr+=PAGE_SIZE;
+ if (size > PAGE_SIZE) size-=PAGE_SIZE;
+ else size=0;
+ }
+ }
+ return mem;
+}
+
+static void rvfree(void * mem, unsigned long size)
+{
+ unsigned long adr, page;
+
+ size += (PAGE_SIZE - 1);
+ size &= ~(PAGE_SIZE - 1);
+
+ if (mem) {
+ adr=(unsigned long) mem;
+ while (size > 0) {
+ page = kvirt_to_phys(adr);
+ mem_map_unreserve(MAP_NR(phys_to_virt(page)));
+ adr+=PAGE_SIZE;
+ if (size > PAGE_SIZE) size-=PAGE_SIZE;
+ else size=0;
+ }
+ vfree(mem);
+ }
+}
+
+/**********************************************************************
+ *
+ * /proc interface
+ *
+ **********************************************************************/
+static struct proc_dir_entry *cpia_proc_root=NULL;
+
+static int cpia_read_proc(char *page, char **start, off_t off,
+ int count, int *eof, void *data)
+{
+ char *out = page;
+ int len, tmp;
+ struct cam_data *cam = data;
+ char tmpstr[20];
+
+ /* IMPORTANT: This output MUST be kept under 4k (FIXME: PAGE_SIZE?)
+ * or we need to get more sophisticated. */
+
+ out += sprintf(out, "read-only\n-----------------------\n");
+ out += sprintf(out, "CPIA Version: %d.%02d (%d.%d)\n",
+ cam->params.version.firmwareVersion,
+ cam->params.version.firmwareRevision,
+ cam->params.version.vcVersion,
+ cam->params.version.vcRevision);
+ out += sprintf(out, "CPIA PnP-ID: %04x:%04x:%04x\n",
+ cam->params.pnpID.vendor, cam->params.pnpID.product,
+ cam->params.pnpID.deviceRevision);
+ out += sprintf(out, "VP-Version: %d.%d %04x\n",
+ cam->params.vpVersion.vpVersion,
+ cam->params.vpVersion.vpRevision,
+ cam->params.vpVersion.cameraHeadID);
+
+ out += sprintf(out, "system_state: %#04x\n",
+ cam->params.status.systemState);
+ out += sprintf(out, "grab_state: %#04x\n",
+ cam->params.status.grabState);
+ out += sprintf(out, "stream_state: %#04x\n",
+ cam->params.status.streamState);
+ out += sprintf(out, "fatal_error: %#04x\n",
+ cam->params.status.fatalError);
+ out += sprintf(out, "cmd_error: %#04x\n",
+ cam->params.status.cmdError);
+ out += sprintf(out, "debug_flags: %#04x\n",
+ cam->params.status.debugFlags);
+ out += sprintf(out, "vp_status: %#04x\n",
+ cam->params.status.vpStatus);
+ out += sprintf(out, "error_code: %#04x\n",
+ cam->params.status.errorCode);
+ out += sprintf(out, "video_size: %s\n",
+ cam->params.format.videoSize == VIDEOSIZE_CIF ?
+ "CIF " : "QCIF");
+ out += sprintf(out, "sub_sample: %s\n",
+ cam->params.format.subSample == SUBSAMPLE_420 ?
+ "420" : "422");
+ out += sprintf(out, "yuv_order: %s\n",
+ cam->params.format.yuvOrder == YUVORDER_YUYV ?
+ "YUYV" : "UYVY");
+ out += sprintf(out, "roi: (%3d, %3d) to (%3d, %3d)\n",
+ cam->params.roi.colStart*8,
+ cam->params.roi.rowStart*4,
+ cam->params.roi.colEnd*8,
+ cam->params.roi.rowEnd*4);
+ out += sprintf(out, "actual_fps: %d\n", cam->fps);
+ out += sprintf(out, "transfer_rate: %dkB/s\n",
+ cam->transfer_rate);
+
+ out += sprintf(out, "\nread-write\n");
+ out += sprintf(out, "----------------------- current min"
+ " max default comment\n");
+ out += sprintf(out, "brightness: %8d %8d %8d %8d\n",
+ cam->params.colourParams.brightness, 0, 100, 50);
+ if(cam->params.version.firmwareVersion == 1 &&
+ cam->params.version.firmwareRevision == 2) {
+ /* 1-02 firmware limits contrast to 80 */
+ tmp = 80;
+ } else {
+ tmp = 96;
+ }
+ out += sprintf(out, "contrast: %8d %8d %8d %8d"
+ " steps of 8\n",
+ cam->params.colourParams.contrast, 0, tmp, 48);
+ out += sprintf(out, "saturation: %8d %8d %8d %8d\n",
+ cam->params.colourParams.saturation, 0, 100, 50);
+ tmp = (25000+5000*cam->params.sensorFps.baserate)/
+ (1<<cam->params.sensorFps.divisor);
+ out += sprintf(out, "sensor_fps: %4d.%03d %8d %8d %8d\n",
+ tmp/1000, tmp%1000, 3, 30, 15);
+ out += sprintf(out, "stream_start_line: %8d %8d %8d %8d\n",
+ 2*cam->params.streamStartLine, 0,
+ cam->params.format.videoSize == VIDEOSIZE_CIF ? 288:144,
+ cam->params.format.videoSize == VIDEOSIZE_CIF ? 240:120);
+ out += sprintf(out, "ecp_timing: %8s %8s %8s %8s\n",
+ cam->params.ecpTiming ? "slow" : "normal", "slow",
+ "normal", "normal");
+
+ switch(cam->params.colourBalance.balanceMode) {
+ case 1:
+ // FIXME case 3:
+ sprintf(tmpstr, "manual");
+ break;
+ case 2:
+ sprintf(tmpstr, "auto");
+ break;
+ default:
+ sprintf(tmpstr, "unknown");
+ break;
+ }
+ out += sprintf(out, "color_balance_mode: %8s %8s %8s"
+ " %8s\n", tmpstr, "manual", "auto", "auto");
+ out += sprintf(out, "red_gain: %8d %8d %8d %8d\n",
+ cam->params.colourBalance.redGain, 0, 212, 32);
+ out += sprintf(out, "green_gain: %8d %8d %8d %8d\n",
+ cam->params.colourBalance.greenGain, 0, 212, 6);
+ out += sprintf(out, "blue_gain: %8d %8d %8d %8d\n",
+ cam->params.colourBalance.blueGain, 0, 212, 92);
+
+ if(cam->params.version.firmwareVersion == 1 &&
+ cam->params.version.firmwareRevision == 2) {
+ /* 1-02 firmware limits gain to 2 */
+ sprintf(tmpstr, "%8d %8d", 1, 2);
+ } else {
+ sprintf(tmpstr, "1,2,4,8");
+ }
+ if(cam->params.exposure.gainMode == 0) {
+ out += sprintf(out, "max_gain: unknown %18s"
+ " %8d\n", tmpstr, 2);
+ } else {
+ out += sprintf(out, "max_gain: %8d %18s %8d\n",
+ 1<<(cam->params.exposure.gainMode-1), tmpstr, 2);
+ }
+ switch(cam->params.exposure.expMode) {
+ case 1:
+ case 3:
+ sprintf(tmpstr, "manual");
+ break;
+ case 2:
+ sprintf(tmpstr, "auto");
+ break;
+ default:
+ sprintf(tmpstr, "unknown");
+ break;
+ }
+ out += sprintf(out, "exposure_mode: %8s %8s %8s"
+ " %8s\n", tmpstr, "manual", "auto", "auto");
+ out += sprintf(out, "centre_weight: %8s %8s %8s %8s\n",
+ (2-cam->params.exposure.centreWeight) ? "on" : "off",
+ "off", "on", "on");
+ out += sprintf(out, "gain: %8d %8d max_gain %8d 1,2,4,8 possible\n",
+ 1<<cam->params.exposure.gain, 1, 1);
+ if(cam->params.version.firmwareVersion == 1 &&
+ cam->params.version.firmwareRevision == 2) {
+ /* 1-02 firmware limits fineExp to 127 */
+ tmp = 255;
+ } else {
+ tmp = 511;
+ }
+ out += sprintf(out, "fine_exp: %8d %8d %8d %8d\n",
+ cam->params.exposure.fineExp*2, 0, tmp, 0);
+ if(cam->params.version.firmwareVersion == 1 &&
+ cam->params.version.firmwareRevision == 2) {
+ /* 1-02 firmware limits coarseExpHi to 0 */
+ tmp = 255;
+ } else {
+ tmp = 65535;
+ }
+ out += sprintf(out, "coarse_exp: %8d %8d %8d"
+ " %8d\n", cam->params.exposure.coarseExpLo+
+ 256*cam->params.exposure.coarseExpHi, 0, tmp, 185);
+ out += sprintf(out, "red_comp: %8d %8d %8d %8d\n",
+ cam->params.exposure.redComp, 220, 255, 220);
+ out += sprintf(out, "green1_comp: %8d %8d %8d %8d\n",
+ cam->params.exposure.green1Comp, 214, 255, 214);
+ out += sprintf(out, "green2_comp: %8d %8d %8d %8d\n",
+ cam->params.exposure.green2Comp, 214, 255, 214);
+ out += sprintf(out, "blue_comp: %8d %8d %8d %8d\n",
+ cam->params.exposure.blueComp, 230, 255, 230);
+
+ out += sprintf(out, "apcor_gain1: %#8x %#8x %#8x %#8x\n",
+ cam->params.apcor.gain1, 0, 0xff, 0x1c);
+ out += sprintf(out, "apcor_gain2: %#8x %#8x %#8x %#8x\n",
+ cam->params.apcor.gain2, 0, 0xff, 0x1a);
+ out += sprintf(out, "apcor_gain4: %#8x %#8x %#8x %#8x\n",
+ cam->params.apcor.gain4, 0, 0xff, 0x2d);
+ out += sprintf(out, "apcor_gain8: %#8x %#8x %#8x %#8x\n",
+ cam->params.apcor.gain8, 0, 0xff, 0x2a);
+ out += sprintf(out, "vl_offset_gain1: %8d %8d %8d %8d\n",
+ cam->params.vlOffset.gain1, 0, 255, 24);
+ out += sprintf(out, "vl_offset_gain2: %8d %8d %8d %8d\n",
+ cam->params.vlOffset.gain2, 0, 255, 28);
+ out += sprintf(out, "vl_offset_gain4: %8d %8d %8d %8d\n",
+ cam->params.vlOffset.gain4, 0, 255, 30);
+ out += sprintf(out, "vl_offset_gain8: %8d %8d %8d %8d\n",
+ cam->params.vlOffset.gain8, 0, 255, 30);
+ out += sprintf(out, "flicker_control: %8s %8s %8s %8s\n",
+ cam->params.flickerControl.flickerMode ? "on" : "off",
+ "off", "on", "off");
+ out += sprintf(out, "mains_frequency: %8d %8d %8d %8d"
+ " only 50/60\n",
+ cam->mainsFreq ? 60 : 50, 50, 60, 50);
+ out += sprintf(out, "allowable_overexposure: %8d %8d %8d %8d\n",
+ cam->params.flickerControl.allowableOverExposure, 0,
+ 255, 0);
+ out += sprintf(out, "compression_mode: ");
+ switch(cam->params.compression.mode) {
+ case CPIA_COMPRESSION_NONE:
+ out += sprintf(out, "%8s", "none");
+ break;
+ case CPIA_COMPRESSION_AUTO:
+ out += sprintf(out, "%8s", "auto");
+ break;
+ case CPIA_COMPRESSION_MANUAL:
+ out += sprintf(out, "%8s", "manual");
+ break;
+ default:
+ out += sprintf(out, "%8s", "unknown");
+ break;
+ }
+ out += sprintf(out, " none,auto,manual auto\n");
+ out += sprintf(out, "decimation: %8s %8s %8s %8s\n",
+ cam->params.compression.decimation ==
+ DECIMATION_ENAB ? "on":"off", "off", "off",
+ "off");
+ out += sprintf(out, "compression_target: %9s %9s %9s %9s\n",
+ cam->params.compressionTarget.frTargeting ==
+ CPIA_COMPRESSION_TARGET_FRAMERATE ?
+ "framerate":"quality",
+ "framerate", "quality", "quality");
+ out += sprintf(out, "target_framerate: %8d %8d %8d %8d\n",
+ cam->params.compressionTarget.targetFR, 0, 30, 7);
+ out += sprintf(out, "target_quality: %8d %8d %8d %8d\n",
+ cam->params.compressionTarget.targetQ, 0, 255, 10);
+ out += sprintf(out, "y_threshold: %8d %8d %8d %8d\n",
+ cam->params.yuvThreshold.yThreshold, 0, 31, 15);
+ out += sprintf(out, "uv_threshold: %8d %8d %8d %8d\n",
+ cam->params.yuvThreshold.uvThreshold, 0, 31, 15);
+ out += sprintf(out, "hysteresis: %8d %8d %8d %8d\n",
+ cam->params.compressionParams.hysteresis, 0, 255, 3);
+ out += sprintf(out, "threshold_max: %8d %8d %8d %8d\n",
+ cam->params.compressionParams.threshMax, 0, 255, 11);
+ out += sprintf(out, "small_step: %8d %8d %8d %8d\n",
+ cam->params.compressionParams.smallStep, 0, 255, 1);
+ out += sprintf(out, "large_step: %8d %8d %8d %8d\n",
+ cam->params.compressionParams.largeStep, 0, 255, 3);
+ out += sprintf(out, "decimation_hysteresis: %8d %8d %8d %8d\n",
+ cam->params.compressionParams.decimationHysteresis,
+ 0, 255, 2);
+ out += sprintf(out, "fr_diff_step_thresh: %8d %8d %8d %8d\n",
+ cam->params.compressionParams.frDiffStepThresh,
+ 0, 255, 5);
+ out += sprintf(out, "q_diff_step_thresh: %8d %8d %8d %8d\n",
+ cam->params.compressionParams.qDiffStepThresh,
+ 0, 255, 3);
+ out += sprintf(out, "decimation_thresh_mod: %8d %8d %8d %8d\n",
+ cam->params.compressionParams.decimationThreshMod,
+ 0, 255, 2);
+
+ len = out - page;
+ len -= off;
+ if (len < count) {
+ *eof = 1;
+ if (len <= 0) return 0;
+ } else {
+ len = count;
+ }
+ *start = page + off;
+ return len;
+}
+
+static int cpia_write_proc(struct file *file, const char *buffer,
+ unsigned long count, void *data)
+{
+ struct cam_data *cam = data;
+ struct cam_params new_params;
+ int retval, len, colon_found;
+ int size = count;
+ char *p;
+ unsigned long val;
+ u32 command_flags = 0;
+ u8 new_mains;
+
+ if(down_interruptible(&cam->param_lock)) {
+ return -ERESTARTSYS;
+ }
+
+ /*
+ * Skip over leading whitespace
+ */
+ while (count && isspace(*buffer)) {
+ --count;
+ ++buffer;
+ }
+
+ memcpy(&new_params, &cam->params, sizeof(struct cam_params));
+ new_mains = cam->mainsFreq;
+
+#define MATCH(x) (len=strlen(x), len <= count && strncmp(buffer, x, len) == 0)
+#define FIRMWARE_VERSION(x,y) (new_params.version.firmwareVersion == (x) && \
+ new_params.version.firmwareRevision == (y))
+#define VALUE \
+ simple_strtoul(buffer, &p, 0); \
+ if(p == buffer) { \
+ retval = -EINVAL; \
+ } else { \
+ count -= p-buffer; \
+ buffer = p; \
+ }
+#define FIND_VALUE \
+ buffer += len;\
+ count -= len; \
+ colon_found=0; \
+ while(count && (*buffer == ' ' || *buffer == '\t' || \
+ (!colon_found && *buffer == ':'))) { \
+ if(*buffer == ':') colon_found = 1; \
+ --count; \
+ ++buffer; \
+ } \
+ if(!count || !colon_found) { \
+ retval = -EINVAL; \
+ }
+#define FIND_END \
+ if(retval == 0) { \
+ while(count && isspace(*buffer) && *buffer != '\n'){ \
+ --count; \
+ ++buffer; \
+ } \
+ if(count) { \
+ if(*buffer != '\n' && *buffer != ';') { \
+ retval = -EINVAL; \
+ } else { \
+ --count; \
+ ++buffer; \
+ } \
+ } \
+ }
+ retval = 0;
+ while(count && !retval) {
+ if(MATCH("brightness")) {
+ FIND_VALUE
+ if(!retval) { val = VALUE }
+ if(!retval) {
+ if(val <= 100)
+ new_params.colourParams.brightness=val;
+ else retval = -EINVAL;
+ }
+ command_flags |= COMMAND_SETCOLOURPARAMS;
+ FIND_END
+ } else if(MATCH("contrast")) {
+ FIND_VALUE
+ 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;
+ FIND_END
+ } else if(MATCH("saturation")) {
+ FIND_VALUE
+ if(!retval) { val = VALUE }
+ if(!retval) {
+ if(val <= 100)
+ new_params.colourParams.saturation=val;
+ else retval = -EINVAL;
+ }
+ command_flags |= COMMAND_SETCOLOURPARAMS;
+ FIND_END
+ } else if(MATCH("sensor_fps")) {
+ FIND_VALUE
+ 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;
+ FIND_END
+ } else if(MATCH("stream_start_line")) {
+ FIND_VALUE
+ 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;
+ }
+ FIND_END
+ } else if(MATCH("ecp_timing")) {
+ FIND_VALUE
+ if(!retval && MATCH("normal")) {
+ buffer += len;
+ count -= len;
+ new_params.ecpTiming = 0;
+ } else if(!retval && MATCH("slow")) {
+ buffer += len;
+ count -= len;
+ new_params.ecpTiming = 1;
+ } else {
+ retval = -EINVAL;
+ }
+ command_flags |= COMMAND_SETECPTIMING;
+ FIND_END
+ } else if(MATCH("color_balance_mode")) {
+ FIND_VALUE
+ if(!retval && MATCH("manual")) {
+ buffer += len;
+ count -= len;
+ new_params.colourBalance.balanceMode=1;
+ } else if(!retval && MATCH("auto")) {
+ buffer += len;
+ count -= len;
+ new_params.colourBalance.balanceMode=2;
+ } else {
+ retval = -EINVAL;
+ }
+ command_flags |= COMMAND_SETCOLOURBALANCE;
+ FIND_END
+ } else if(MATCH("red_gain")) {
+ FIND_VALUE
+ if(!retval) { val = VALUE }
+ if(!retval) {
+ if(val <= 212)
+ new_params.colourBalance.redGain = val;
+ else retval = -EINVAL;
+ }
+ command_flags |= COMMAND_SETCOLOURBALANCE;
+ FIND_END
+ } else if(MATCH("green_gain")) {
+ FIND_VALUE
+ if(!retval) { val = VALUE }
+ if(!retval) {
+ if(val <= 212)
+ new_params.colourBalance.greenGain=val;
+ else retval = -EINVAL;
+ }
+ command_flags |= COMMAND_SETCOLOURBALANCE;
+ FIND_END
+ } else if(MATCH("blue_gain")) {
+ FIND_VALUE
+ if(!retval) { val = VALUE }
+ if(!retval) {
+ if(val <= 212)
+ new_params.colourBalance.blueGain = val;
+ else retval = -EINVAL;
+ }
+ command_flags |= COMMAND_SETCOLOURBALANCE;
+ FIND_END
+ } else if(MATCH("max_gain")) {
+ FIND_VALUE
+ 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;
+ FIND_END
+ } else if(MATCH("exposure_mode")) {
+ FIND_VALUE
+ if(!retval && MATCH("auto")) {
+ buffer += len;
+ count -= len;
+ new_params.exposure.expMode = 2;
+ } else if(!retval && MATCH("manual")) {
+ buffer += len;
+ count -= len;
+ if(new_params.exposure.expMode == 2)
+ new_params.exposure.expMode = 3;
+ new_params.flickerControl.flickerMode = 0;
+ command_flags |= COMMAND_SETFLICKERCTRL;
+ } else {
+ retval = -EINVAL;
+ }
+ command_flags |= COMMAND_SETEXPOSURE;
+ FIND_END
+ } else if(MATCH("centre_weight")) {
+ FIND_VALUE
+ if(!retval && MATCH("on")) {
+ buffer += len;
+ count -= len;
+ new_params.exposure.centreWeight = 1;
+ } else if(!retval && MATCH("off")) {
+ buffer += len;
+ count -= len;
+ new_params.exposure.centreWeight = 2;
+ } else {
+ retval = -EINVAL;
+ }
+ command_flags |= COMMAND_SETEXPOSURE;
+ FIND_END
+ } else if(MATCH("gain")) {
+ FIND_VALUE
+ if(!retval) { val = VALUE }
+ if(!retval) {
+ switch(val) {
+ case 1:
+ new_params.exposure.gain = 0;
+ new_params.exposure.expMode = 1;
+ new_params.flickerControl.flickerMode = 0;
+ command_flags |= COMMAND_SETFLICKERCTRL;
+ break;
+ case 2:
+ new_params.exposure.gain = 1;
+ new_params.exposure.expMode = 1;
+ new_params.flickerControl.flickerMode = 0;
+ command_flags |= COMMAND_SETFLICKERCTRL;
+ break;
+ case 4:
+ new_params.exposure.gain = 2;
+ new_params.exposure.expMode = 1;
+ new_params.flickerControl.flickerMode = 0;
+ command_flags |= COMMAND_SETFLICKERCTRL;
+ break;
+ case 8:
+ new_params.exposure.gain = 3;
+ new_params.exposure.expMode = 1;
+ new_params.flickerControl.flickerMode = 0;
+ command_flags |= COMMAND_SETFLICKERCTRL;
+ break;
+ default:
+ retval = -EINVAL;
+ break;
+ }
+ command_flags |= COMMAND_SETEXPOSURE;
+ if(new_params.exposure.gain >
+ new_params.exposure.gainMode-1)
+ retval = -EINVAL;
+ }
+ FIND_END
+ } else if(MATCH("fine_exp")) {
+ FIND_VALUE
+ if(!retval) { val = VALUE }
+ if(!retval) {
+ if(val < 256) {
+ /* 1-02 firmware limits fineExp to 127*/
+ if(FIRMWARE_VERSION(1,2) && val > 127)
+ val = 127;
+ new_params.exposure.fineExp = val;
+ new_params.exposure.expMode = 1;
+ command_flags |= COMMAND_SETEXPOSURE;
+ new_params.flickerControl.flickerMode = 0;
+ command_flags |= COMMAND_SETFLICKERCTRL;
+ } else retval = -EINVAL;
+ }
+ FIND_END
+ } else if(MATCH("coarse_exp")) {
+ FIND_VALUE
+ if(!retval) { val = VALUE }
+ if(!retval) {
+ if(val < 65536) {
+ /* 1-02 firmware limits
+ * coarseExp to 255 */
+ if(FIRMWARE_VERSION(1,2) && val > 255)
+ val = 255;
+ new_params.exposure.coarseExpLo =
+ val & 0xff;
+ new_params.exposure.coarseExpHi =
+ val >> 8;
+ new_params.exposure.expMode = 1;
+ command_flags |= COMMAND_SETEXPOSURE;
+ new_params.flickerControl.flickerMode = 0;
+ command_flags |= COMMAND_SETFLICKERCTRL;
+ } else retval = -EINVAL;
+ }
+ FIND_END
+ } else if(MATCH("red_comp")) {
+ FIND_VALUE
+ if(!retval) { val = VALUE }
+ if(!retval) {
+ if(val >= 220 && val <= 255) {
+ new_params.exposure.redComp = val;
+ command_flags |= COMMAND_SETEXPOSURE;
+ } else retval = -EINVAL;
+ }
+ FIND_END
+ } else if(MATCH("green1_comp")) {
+ FIND_VALUE
+ if(!retval) { val = VALUE }
+ if(!retval) {
+ if(val >= 214 && val <= 255) {
+ new_params.exposure.green1Comp = val;
+ command_flags |= COMMAND_SETEXPOSURE;
+ } else retval = -EINVAL;
+ }
+ FIND_END
+ } else if(MATCH("green2_comp")) {
+ FIND_VALUE
+ if(!retval) { val = VALUE }
+ if(!retval) {
+ if(val >= 214 && val <= 255) {
+ new_params.exposure.green2Comp = val;
+ command_flags |= COMMAND_SETEXPOSURE;
+ } else retval = -EINVAL;
+ }
+ FIND_END
+ } else if(MATCH("blue_comp")) {
+ FIND_VALUE
+ if(!retval) { val = VALUE }
+ if(!retval) {
+ if(val >= 230 && val <= 255) {
+ new_params.exposure.blueComp = val;
+ command_flags |= COMMAND_SETEXPOSURE;
+ } else retval = -EINVAL;
+ }
+ FIND_END
+ } else if(MATCH("apcor_gain1")) {
+ FIND_VALUE
+ if(!retval) { val = VALUE }
+ if(!retval) {
+ command_flags |= COMMAND_SETAPCOR;
+ if(val <= 0xff) new_params.apcor.gain1 = val;
+ else retval = -EINVAL;
+ }
+ FIND_END
+ } else if(MATCH("apcor_gain2")) {
+ FIND_VALUE
+ if(!retval) { val = VALUE }
+ if(!retval) {
+ command_flags |= COMMAND_SETAPCOR;
+ if(val <= 0xff) new_params.apcor.gain2 = val;
+ else retval = -EINVAL;
+ }
+ FIND_END
+ } else if(MATCH("apcor_gain4")) {
+ FIND_VALUE
+ if(!retval) { val = VALUE }
+ if(!retval) {
+ command_flags |= COMMAND_SETAPCOR;
+ if(val <= 0xff) new_params.apcor.gain4 = val;
+ else retval = -EINVAL;
+ }
+ FIND_END
+ } else if(MATCH("apcor_gain8")) {
+ FIND_VALUE
+ if(!retval) { val = VALUE }
+ if(!retval) {
+ command_flags |= COMMAND_SETAPCOR;
+ if(val <= 0xff) new_params.apcor.gain8 = val;
+ else retval = -EINVAL;
+ }
+ FIND_END
+ } else if(MATCH("vl_offset_gain1")) {
+ FIND_VALUE
+ if(!retval) { val = VALUE }
+ if(!retval) {
+ if(val <= 0xff) new_params.vlOffset.gain1 = val;
+ else retval = -EINVAL;
+ }
+ FIND_END
+ command_flags |= COMMAND_SETVLOFFSET;
+ } else if(MATCH("vl_offset_gain2")) {
+ FIND_VALUE
+ if(!retval) { val = VALUE }
+ if(!retval) {
+ if(val <= 0xff) new_params.vlOffset.gain2 = val;
+ else retval = -EINVAL;
+ }
+ FIND_END
+ command_flags |= COMMAND_SETVLOFFSET;
+ } else if(MATCH("vl_offset_gain4")) {
+ FIND_VALUE
+ if(!retval) { val = VALUE }
+ if(!retval) {
+ if(val <= 0xff) new_params.vlOffset.gain4 = val;
+ else retval = -EINVAL;
+ }
+ FIND_END
+ command_flags |= COMMAND_SETVLOFFSET;
+ } else if(MATCH("vl_offset_gain8")) {
+ FIND_VALUE
+ if(!retval) { val = VALUE }
+ if(!retval) {
+ if(val <= 0xff) new_params.vlOffset.gain8 = val;
+ else retval = -EINVAL;
+ }
+ FIND_END
+ command_flags |= COMMAND_SETVLOFFSET;
+ } else if(MATCH("flicker_control")) {
+ FIND_VALUE
+ if(!retval && MATCH("on")) {
+ buffer += len;
+ count -= len;
+ new_params.flickerControl.flickerMode = 1;
+ new_params.exposure.expMode = 2;
+ command_flags |= COMMAND_SETEXPOSURE;
+ } else if(!retval && MATCH("off")) {
+ buffer += len;
+ count -= len;
+ new_params.flickerControl.flickerMode = 0;
+ } else {
+ retval = -EINVAL;
+ }
+ command_flags |= COMMAND_SETFLICKERCTRL;
+ FIND_END
+ } else if(MATCH("mains_frequency")) {
+ FIND_VALUE
+ if(!retval && MATCH("50")) {
+ buffer += len;
+ count -= len;
+ 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")) {
+ buffer += len;
+ count -= len;
+ 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;
+ }
+ FIND_END
+ } else if(MATCH("allowable_overexposure")) {
+ FIND_VALUE
+ if(!retval) { val = VALUE }
+ if(!retval) {
+ if(val <= 0xff) {
+ new_params.flickerControl.
+ allowableOverExposure = val;
+ command_flags |= COMMAND_SETFLICKERCTRL;
+ } else retval = -EINVAL;
+ }
+ FIND_END
+ } else if(MATCH("compression_mode")) {
+ FIND_VALUE
+ if(!retval && MATCH("none")) {
+ buffer += len;
+ count -= len;
+ new_params.compression.mode =
+ CPIA_COMPRESSION_NONE;
+ } else if(!retval && MATCH("auto")) {
+ buffer += len;
+ count -= len;
+ new_params.compression.mode =
+ CPIA_COMPRESSION_AUTO;
+ } else if(!retval && MATCH("manual")) {
+ buffer += len;
+ count -= len;
+ new_params.compression.mode =
+ CPIA_COMPRESSION_MANUAL;
+ } else {
+ retval = -EINVAL;
+ }
+ command_flags |= COMMAND_SETCOMPRESSION;
+ FIND_END
+ } else if(MATCH("decimation")) {
+ FIND_VALUE
+ if(!retval && MATCH("off")) {
+ buffer += len;
+ count -= len;
+ new_params.compression.decimation = 0;
+ } else {
+ retval = -EINVAL;
+ }
+ command_flags |= COMMAND_SETCOMPRESSION;
+ FIND_END
+ } else if(MATCH("compression_target")) {
+ FIND_VALUE
+ if(!retval && MATCH("quality")) {
+ buffer += len;
+ count -= len;
+ new_params.compressionTarget.frTargeting =
+ CPIA_COMPRESSION_TARGET_QUALITY;
+ } else if(!retval && MATCH("framerate")) {
+ buffer += len;
+ count -= len;
+ new_params.compressionTarget.frTargeting =
+ CPIA_COMPRESSION_TARGET_FRAMERATE;
+ } else {
+ retval = -EINVAL;
+ }
+ command_flags |= COMMAND_SETCOMPRESSIONTARGET;
+ FIND_END
+ } else if(MATCH("target_framerate")) {
+ FIND_VALUE
+ if(!retval) { val = VALUE }
+ if(!retval)
+ new_params.compressionTarget.targetFR = val;
+ command_flags |= COMMAND_SETCOMPRESSIONTARGET;
+ FIND_END
+ } else if(MATCH("target_quality")) {
+ FIND_VALUE
+ if(!retval) { val = VALUE }
+ if(!retval) new_params.compressionTarget.targetQ = val;
+ command_flags |= COMMAND_SETCOMPRESSIONTARGET;
+ FIND_END
+ } else if(MATCH("y_threshold")) {
+ FIND_VALUE
+ if(!retval) { val = VALUE }
+ if(!retval) {
+ if(val < 32)
+ new_params.yuvThreshold.yThreshold=val;
+ else retval = -EINVAL;
+ }
+ command_flags |= COMMAND_SETYUVTHRESH;
+ FIND_END
+ } else if(MATCH("uv_threshold")) {
+ FIND_VALUE
+ if(!retval) { val = VALUE }
+ if(!retval) {
+ if(val < 32)
+ new_params.yuvThreshold.uvThreshold=val;
+ else retval = -EINVAL;
+ }
+ command_flags |= COMMAND_SETYUVTHRESH;
+ FIND_END
+ } else if(MATCH("hysteresis")) {
+ FIND_VALUE
+ if(!retval) { val = VALUE }
+ if(!retval) {
+ if(retval <= 0xff)
+ new_params.compressionParams.hysteresis=val;
+ else retval = -EINVAL;
+ }
+ command_flags |= COMMAND_SETCOMPRESSIONPARAMS;
+ FIND_END
+ } else if(MATCH("threshold_max")) {
+ FIND_VALUE
+ if(!retval) { val = VALUE }
+ if(!retval) {
+ if(retval <= 0xff)
+ new_params.compressionParams.threshMax=val;
+ else retval = -EINVAL;
+ }
+ command_flags |= COMMAND_SETCOMPRESSIONPARAMS;
+ FIND_END
+ } else if(MATCH("small_step")) {
+ FIND_VALUE
+ if(!retval) { val = VALUE }
+ if(!retval) {
+ if(retval <= 0xff)
+ new_params.compressionParams.smallStep=val;
+ else retval = -EINVAL;
+ }
+ command_flags |= COMMAND_SETCOMPRESSIONPARAMS;
+ FIND_END
+ } else if(MATCH("large_step")) {
+ FIND_VALUE
+ if(!retval) { val = VALUE }
+ if(!retval) {
+ if(retval <= 0xff)
+ new_params.compressionParams.largeStep=val;
+ else retval = -EINVAL;
+ }
+ command_flags |= COMMAND_SETCOMPRESSIONPARAMS;
+ FIND_END
+ } else if(MATCH("decimation_hysteresis")) {
+ FIND_VALUE
+ if(!retval) { val = VALUE }
+ if(!retval) {
+ if(retval <= 0xff)
+ new_params.compressionParams.
+ decimationHysteresis = val;
+ else retval = -EINVAL;
+ }
+ command_flags |= COMMAND_SETCOMPRESSIONPARAMS;
+ FIND_END
+ } else if(MATCH("fr_diff_step_thresh")) {
+ FIND_VALUE
+ if(!retval) { val = VALUE }
+ if(!retval) {
+ if(retval <= 0xff)
+ new_params.compressionParams.
+ frDiffStepThresh = val;
+ else retval = -EINVAL;
+ }
+ command_flags |= COMMAND_SETCOMPRESSIONPARAMS;
+ FIND_END
+ } else if(MATCH("q_diff_step_thresh")) {
+ FIND_VALUE
+ if(!retval) { val = VALUE }
+ if(!retval) {
+ if(retval <= 0xff)
+ new_params.compressionParams.
+ qDiffStepThresh = val;
+ else retval = -EINVAL;
+ command_flags |= COMMAND_SETCOMPRESSIONPARAMS;
+ FIND_END
+ } else if(MATCH("decimation_thresh_mod")) {
+ }
+ FIND_VALUE
+ if(!retval) { val = VALUE }
+ if(!retval) {
+ if(retval <= 0xff)
+ new_params.compressionParams.
+ decimationThreshMod = val;
+ else retval = -EINVAL;
+ }
+ command_flags |= COMMAND_SETCOMPRESSIONPARAMS;
+ FIND_END
+ } else {
+ retval = -EINVAL;
+ }
+ }
+#undef MATCH
+#undef FIRMWARE_VERSION
+#undef VALUE
+#undef FIND_VALUE
+#undef FIND_END
+ if(retval == 0) {
+ if(command_flags & COMMAND_SETCOLOURPARAMS) {
+ /* Adjust cam->vp to reflect these changes */
+ cam->vp.brightness =
+ new_params.colourParams.brightness*65535/100;
+ cam->vp.contrast =
+ new_params.colourParams.contrast*65535/100;
+ cam->vp.colour =
+ new_params.colourParams.saturation*65535/100;
+ }
+
+ memcpy(&cam->params, &new_params, sizeof(struct cam_params));
+ cam->mainsFreq = new_mains;
+ cam->cmd_queue |= command_flags;
+ retval = size;
+ } else {
+ DBG("error: %d\n", retval);
+ }
+
+ up(&cam->param_lock);
+
+ return retval;
+}
+
+static void create_proc_cpia_cam(struct cam_data *cam)
+{
+ char name[7];
+ struct proc_dir_entry *ent;
+
+ if(cpia_proc_root == NULL || cam == NULL) {
+ return;
+ }
+ sprintf(name, "video%d", cam->vdev.minor);
+
+ ent = create_proc_entry(name, S_IFREG|S_IRUGO|S_IWUSR, cpia_proc_root);
+ if (!ent) return;
+ ent->data = cam;
+ ent->read_proc = cpia_read_proc;
+ ent->write_proc = cpia_write_proc;
+ ent->size = 3623;
+ cam->proc_entry = ent;
+}
+
+static void destroy_proc_cpia_cam(struct cam_data *cam)
+{
+ char name[7];
+
+ if(cam->proc_entry == NULL) return;
+
+ sprintf(name, "video%d", cam->vdev.minor);
+ remove_proc_entry(name, cpia_proc_root);
+}
+
+static void proc_cpia_create(void)
+{
+ cpia_proc_root = create_proc_entry("cpia", S_IFDIR, 0);
+}
+
+#ifdef MODULE
+static void proc_cpia_destroy(void)
+{
+ remove_proc_entry("cpia", 0);
+}
+#endif
+
+/* ----------------------- debug functions ---------------------- */
+
+#define printstatus(cam) \
+ DBG("%02x %02x %02x %02x %02x %02x %02x %02x\n",\
+ cam->params.status.systemState, cam->params.status.grabState, \
+ cam->params.status.streamState, cam->params.status.fatalError, \
+ cam->params.status.cmdError, cam->params.status.debugFlags, \
+ cam->params.status.vpStatus, cam->params.status.errorCode);
+
+/* ----------------------- v4l helpers -------------------------- */
+
+/* supported frame palettes and depths */
+static inline int valid_mode(u16 palette, u16 depth)
+{
+ return (palette == VIDEO_PALETTE_GREY && depth == 8) ||
+ (palette == VIDEO_PALETTE_RGB555 && depth == 16) ||
+ (palette == VIDEO_PALETTE_RGB565 && depth == 16) ||
+ (palette == VIDEO_PALETTE_RGB24 && depth == 24) ||
+ (palette == VIDEO_PALETTE_RGB32 && depth == 32) ||
+ (palette == VIDEO_PALETTE_YUV422 && depth == 16) ||
+ (palette == VIDEO_PALETTE_YUYV && depth == 16) ||
+ (palette == VIDEO_PALETTE_UYVY && depth == 16);
+}
+
+static int match_videosize( int width, int height )
+{
+ /* return the best match, where 'best' is as always
+ * the largest that is not bigger than what is requested. */
+ if(width>=352 && height>=288) {
+ return VIDEOSIZE_352_288; /* CIF */
+ }
+ if(width>=320 && height>=240) {
+ return VIDEOSIZE_320_240; /* SIF */
+ }
+ if(width>=288 && height>=216) {
+ return VIDEOSIZE_288_216;
+ }
+ if(width>=256 && height>=192) {
+ return VIDEOSIZE_256_192;
+ }
+ if(width>=224 && height>=168) {
+ return VIDEOSIZE_224_168;
+ }
+ if(width>=192 && height>=144) {
+ return VIDEOSIZE_192_144;
+ }
+ if(width>=176 && height>=144) {
+ return VIDEOSIZE_176_144; /* QCIF */
+ }
+ if(width>=160 && height>=120) {
+ return VIDEOSIZE_160_120; /* QSIF */
+ }
+ if(width>=128 && height>=96) {
+ return VIDEOSIZE_128_96;
+ }
+ if(width>=64 && height>=48) {
+ return VIDEOSIZE_64_48;
+ }
+ if(width>=48 && height>=48) {
+ return VIDEOSIZE_48_48;
+ }
+
+ return -1;
+}
+
+/* these are the capture sizes we support */
+static void set_vw_size(struct cam_data *cam)
+{
+ /* the col/row/start/end values are the result of simple math */
+ /* study the SetROI-command in cpia developers guide p 2-22 */
+ /* streamStartLine is set to the recommended value in the cpia */
+ /* developers guide p 3-37 */
+ switch(cam->video_size) {
+ case VIDEOSIZE_CIF:
+ cam->vw.width = 352;
+ cam->vw.height = 288;
+ cam->params.format.videoSize=VIDEOSIZE_CIF;
+ cam->params.roi.colStart=0;
+ cam->params.roi.colEnd=44;
+ cam->params.roi.rowStart=0;
+ cam->params.roi.rowEnd=72;
+ cam->params.streamStartLine = 120;
+ break;
+ case VIDEOSIZE_SIF:
+ cam->vw.width = 320;
+ cam->vw.height = 240;
+ cam->params.format.videoSize=VIDEOSIZE_CIF;
+ cam->params.roi.colStart=2;
+ cam->params.roi.colEnd=42;
+ cam->params.roi.rowStart=6;
+ cam->params.roi.rowEnd=66;
+ cam->params.streamStartLine = 120;
+ break;
+ case VIDEOSIZE_288_216:
+ cam->vw.width = 288;
+ cam->vw.height = 216;
+ cam->params.format.videoSize=VIDEOSIZE_CIF;
+ cam->params.roi.colStart=4;
+ cam->params.roi.colEnd=40;
+ cam->params.roi.rowStart=9;
+ cam->params.roi.rowEnd=63;
+ cam->params.streamStartLine = 120;
+ break;
+ case VIDEOSIZE_256_192:
+ cam->vw.width = 256;
+ cam->vw.height = 192;
+ cam->params.format.videoSize=VIDEOSIZE_CIF;
+ cam->params.roi.colStart=6;
+ cam->params.roi.colEnd=38;
+ cam->params.roi.rowStart=12;
+ cam->params.roi.rowEnd=60;
+ cam->params.streamStartLine = 120;
+ break;
+ case VIDEOSIZE_224_168:
+ cam->vw.width = 224;
+ cam->vw.height = 168;
+ cam->params.format.videoSize=VIDEOSIZE_CIF;
+ cam->params.roi.colStart=8;
+ cam->params.roi.colEnd=36;
+ cam->params.roi.rowStart=15;
+ cam->params.roi.rowEnd=57;
+ cam->params.streamStartLine = 120;
+ break;
+ case VIDEOSIZE_192_144:
+ cam->vw.width = 192;
+ cam->vw.height = 144;
+ cam->params.format.videoSize=VIDEOSIZE_CIF;
+ cam->params.roi.colStart=10;
+ cam->params.roi.colEnd=34;
+ cam->params.roi.rowStart=18;
+ cam->params.roi.rowEnd=54;
+ cam->params.streamStartLine = 120;
+ break;
+ case VIDEOSIZE_QCIF:
+ cam->vw.width = 176;
+ cam->vw.height = 144;
+ cam->params.format.videoSize=VIDEOSIZE_QCIF;
+ cam->params.roi.colStart=0;
+ cam->params.roi.colEnd=22;
+ cam->params.roi.rowStart=0;
+ cam->params.roi.rowEnd=36;
+ cam->params.streamStartLine = 60;
+ break;
+ case VIDEOSIZE_QSIF:
+ cam->vw.width = 160;
+ cam->vw.height = 120;
+ cam->params.format.videoSize=VIDEOSIZE_QCIF;
+ cam->params.roi.colStart=1;
+ cam->params.roi.colEnd=21;
+ cam->params.roi.rowStart=3;
+ cam->params.roi.rowEnd=33;
+ cam->params.streamStartLine = 60;
+ break;
+ case VIDEOSIZE_128_96:
+ cam->vw.width = 128;
+ cam->vw.height = 96;
+ cam->params.format.videoSize=VIDEOSIZE_QCIF;
+ cam->params.roi.colStart=3;
+ cam->params.roi.colEnd=19;
+ cam->params.roi.rowStart=6;
+ cam->params.roi.rowEnd=30;
+ cam->params.streamStartLine = 60;
+ break;
+ case VIDEOSIZE_64_48:
+ cam->vw.width = 64;
+ cam->vw.height = 48;
+ cam->params.format.videoSize=VIDEOSIZE_QCIF;
+ cam->params.roi.colStart=7;
+ cam->params.roi.colEnd=15;
+ cam->params.roi.rowStart=12;
+ cam->params.roi.rowEnd=24;
+ cam->params.streamStartLine = 60;
+ break;
+ case VIDEOSIZE_48_48:
+ cam->vw.width = 48;
+ cam->vw.height = 48;
+ cam->params.format.videoSize=VIDEOSIZE_QCIF;
+ cam->params.roi.colStart=8;
+ cam->params.roi.colEnd=14;
+ cam->params.roi.rowStart=6;
+ cam->params.roi.rowEnd=30;
+ cam->params.streamStartLine = 60;
+ break;
+ default:
+ LOG("bad videosize value: %d\n", cam->video_size);
+ }
+
+ return;
+}
+
+static int allocate_frame_buf(struct cam_data *cam) {
+ int i;
+
+ cam->frame_buf = rvmalloc(FRAME_NUM*CPIA_MAX_FRAME_SIZE);
+ if (!cam->frame_buf) {
+ return -ENOBUFS;
+ }
+ for( i=0; i<FRAME_NUM; i++ ) {
+ cam->frame[i].data = cam->frame_buf + i*CPIA_MAX_FRAME_SIZE;
+ }
+ return 0;
+}
+
+static int free_frame_buf(struct cam_data *cam) {
+ int i;
+
+ rvfree(cam->frame_buf, FRAME_NUM*CPIA_MAX_FRAME_SIZE);
+ cam->frame_buf=0;
+ for( i=0; i<FRAME_NUM; i++ ) {
+ cam->frame[i].data = NULL;
+ }
+ return 0;
+}
+
+
+static void inline free_frames(struct cpia_frame frame[FRAME_NUM])
+{
+ int i;
+ for( i=0; i<FRAME_NUM; i++ ) {
+ frame[i].state=FRAME_UNUSED;
+ }
+ return;
+}
+
+/**********************************************************************
+ *
+ * General functions
+ *
+ **********************************************************************/
+/* send an arbitrary command to the camera */
+static int do_command(struct cam_data *cam, u16 command, u8 a, u8 b, u8 c, u8 d)
+{
+ int retval, datasize;
+ u8 cmd[8], data[8];
+
+ switch(command) {
+ case CPIA_COMMAND_GetCPIAVersion:
+ case CPIA_COMMAND_GetPnPID:
+ case CPIA_COMMAND_GetCameraStatus:
+ case CPIA_COMMAND_GetVPVersion:
+ datasize=8;
+ break;
+ case CPIA_COMMAND_GetColourParams:
+ case CPIA_COMMAND_GetColourBalance:
+ case CPIA_COMMAND_GetExposure:
+ down(&cam->param_lock);
+ datasize=8;
+ break;
+ default:
+ datasize=0;
+ break;
+ }
+
+ cmd[0] = command>>8;
+ cmd[1] = command&0xff;
+ cmd[2] = a;
+ cmd[3] = b;
+ cmd[4] = c;
+ cmd[5] = d;
+ cmd[6] = datasize;
+ cmd[7] = 0;
+
+ retval = cam->ops->transferCmd(cam->lowlevel_data, cmd, data);
+ if(retval) {
+ LOG("%x - failed\n", command);
+ } else {
+ switch(command) {
+ case CPIA_COMMAND_GetCPIAVersion:
+ cam->params.version.firmwareVersion = data[0];
+ cam->params.version.firmwareRevision = data[1];
+ cam->params.version.vcVersion = data[2];
+ cam->params.version.vcRevision = data[3];
+ break;
+ case CPIA_COMMAND_GetPnPID:
+ cam->params.pnpID.vendor = data[0]+(((u16)data[1])<<8);
+ cam->params.pnpID.product = data[2]+(((u16)data[3])<<8);
+ cam->params.pnpID.deviceRevision =
+ data[4]+(((u16)data[5])<<8);
+ break;
+ case CPIA_COMMAND_GetCameraStatus:
+ cam->params.status.systemState = data[0];
+ cam->params.status.grabState = data[1];
+ cam->params.status.streamState = data[2];
+ cam->params.status.fatalError = data[3];
+ cam->params.status.cmdError = data[4];
+ cam->params.status.debugFlags = data[5];
+ cam->params.status.vpStatus = data[6];
+ cam->params.status.errorCode = data[7];
+ break;
+ case CPIA_COMMAND_GetVPVersion:
+ cam->params.vpVersion.vpVersion = data[0];
+ cam->params.vpVersion.vpRevision = data[1];
+ cam->params.vpVersion.cameraHeadID =
+ data[2]+(((u16)data[3])<<8);
+ break;
+ case CPIA_COMMAND_GetColourParams:
+ cam->params.colourParams.brightness = data[0];
+ cam->params.colourParams.contrast = data[1];
+ cam->params.colourParams.saturation = data[2];
+ up(&cam->param_lock);
+ break;
+ case CPIA_COMMAND_GetColourBalance:
+ cam->params.colourBalance.redGain = data[0];
+ cam->params.colourBalance.greenGain = data[1];
+ cam->params.colourBalance.blueGain = data[2];
+ up(&cam->param_lock);
+ break;
+ case CPIA_COMMAND_GetExposure:
+ cam->params.exposure.gain = data[0];
+ cam->params.exposure.fineExp = data[1];
+ cam->params.exposure.coarseExpLo = data[2];
+ cam->params.exposure.coarseExpHi = data[3];
+ cam->params.exposure.redComp = data[4];
+ cam->params.exposure.green1Comp = data[5];
+ cam->params.exposure.green2Comp = data[6];
+ cam->params.exposure.blueComp = data[7];
+ up(&cam->param_lock);
+ break;
+ default:
+ break;
+ }
+ }
+ return retval;
+}
+
+/* send a command to the camera with an additional data transaction */
+static int do_command_extended(struct cam_data *cam, u16 command,
+ u8 a, u8 b, u8 c, u8 d,
+ u8 e, u8 f, u8 g, u8 h,
+ u8 i, u8 j, u8 k, u8 l)
+{
+ int retval;
+ u8 cmd[8], data[8];
+ cmd[0] = command>>8;
+ cmd[1] = command&0xff;
+ cmd[2] = a;
+ cmd[3] = b;
+ cmd[4] = c;
+ cmd[5] = d;
+ cmd[6] = 8;
+ cmd[7] = 0;
+ data[0] = e;
+ data[1] = f;
+ data[2] = g;
+ data[3] = h;
+ data[4] = i;
+ data[5] = j;
+ data[6] = k;
+ data[7] = l;
+
+ retval = cam->ops->transferCmd(cam->lowlevel_data, cmd, data);
+ if(retval) {
+ LOG("%x - failed\n", command);
+ }
+ return retval;
+}
+
+/**********************************************************************
+ *
+ * Colorspace conversion
+ *
+ **********************************************************************/
+#define LIMIT(x) ((((x)>0xffffff)?0xff0000:(((x)<=0xffff)?0:(x)&0xff0000))>>16)
+
+static int yuvconvert(unsigned char *yuv, unsigned char *rgb, int out_fmt,
+ int in_uyvy)
+{
+ int 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:
+ *rgb++ = LIMIT(b+y);
+ *rgb++ = LIMIT(g+y);
+ *rgb++ = LIMIT(r+y);
+ *rgb++ = LIMIT(b+y1);
+ *rgb++ = LIMIT(g+y1);
+ *rgb = LIMIT(r+y1);
+ return 6;
+ case VIDEO_PALETTE_RGB32:
+ *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);
+ 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:
+ return 0;
+ }
+}
+
+static int skipcount(int count, int fmt) {
+ switch(fmt) {
+ case VIDEO_PALETTE_GREY:
+ case VIDEO_PALETTE_RGB555:
+ case VIDEO_PALETTE_RGB565:
+ case VIDEO_PALETTE_YUV422:
+ case VIDEO_PALETTE_YUYV:
+ case VIDEO_PALETTE_UYVY:
+ return 2*count;
+ case VIDEO_PALETTE_RGB24:
+ return 3*count;
+ case VIDEO_PALETTE_RGB32:
+ return 4*count;
+ default:
+ return 0;
+ }
+}
+
+static int parse_picture(struct cam_data *cam, int size)
+{
+ u8 *obuf, *ibuf, *end_obuf;
+ int ll, in_uyvy, compressed, origsize, out_fmt;
+
+ /* make sure params don't change while we are decoding */
+ down(&cam->param_lock);
+
+ obuf = cam->decompressed_frame.data;
+ end_obuf = obuf+CPIA_MAX_FRAME_SIZE;
+ ibuf = cam->raw_image;
+ origsize = size;
+ out_fmt = cam->vp.palette;
+
+ if((ibuf[0] != MAGIC_0) || (ibuf[1] != MAGIC_1)) {
+ LOG("header not found\n");
+ up(&cam->param_lock);
+ return -1;
+ }
+
+ if((ibuf[16] != VIDEOSIZE_QCIF) && (ibuf[16] != VIDEOSIZE_CIF)) {
+ LOG("wrong video size\n");
+ up(&cam->param_lock);
+ return -1;
+ }
+
+ if(ibuf[17] != SUBSAMPLE_422) {
+ LOG("illegal subtype %d\n",ibuf[17]);
+ up(&cam->param_lock);
+ return -1;
+ }
+
+ if(ibuf[18] != YUVORDER_YUYV && ibuf[18] != YUVORDER_UYVY) {
+ LOG("illegal yuvorder %d\n",ibuf[18]);
+ up(&cam->param_lock);
+ return -1;
+ }
+ in_uyvy = ibuf[18] == YUVORDER_UYVY;
+
+#if 0
+ /* FIXME: ROI mismatch occurs when switching capture sizes */
+ if((ibuf[24] != cam->params.roi.colStart) ||
+ (ibuf[25] != cam->params.roi.colEnd) ||
+ (ibuf[26] != cam->params.roi.rowStart) ||
+ (ibuf[27] != cam->params.roi.rowEnd)) {
+ LOG("ROI mismatch\n");
+ up(&cam->param_lock);
+ return -1;
+ }
+#endif
+
+ if((ibuf[28] != NOT_COMPRESSED) && (ibuf[28] != COMPRESSED)) {
+ LOG("illegal compression %d\n",ibuf[28]);
+ up(&cam->param_lock);
+ return -1;
+ }
+ compressed = (ibuf[28] == COMPRESSED);
+
+ if(ibuf[29] != NO_DECIMATION) {
+ LOG("decimation not supported\n");
+ up(&cam->param_lock);
+ return -1;
+ }
+
+ cam->params.yuvThreshold.yThreshold = ibuf[30];
+ cam->params.yuvThreshold.uvThreshold = ibuf[31];
+ cam->params.status.systemState = ibuf[32];
+ cam->params.status.grabState = ibuf[33];
+ cam->params.status.streamState = ibuf[34];
+ cam->params.status.fatalError = ibuf[35];
+ cam->params.status.cmdError = ibuf[36];
+ cam->params.status.debugFlags = ibuf[37];
+ cam->params.status.vpStatus = ibuf[38];
+ cam->params.status.errorCode = ibuf[39];
+ cam->fps = ibuf[41];
+
+ ibuf += 64;
+ size -= 64;
+ ll = ibuf[0] | (ibuf[1] << 8);
+ ibuf += 2;
+ while(size > 0) {
+ size -= (ll+2);
+ if(size < 0) {
+ LOG("Insufficient data in buffer\n");
+ up(&cam->param_lock);
+ return -1;
+ }
+ while(ll > 1) {
+ if(!compressed || (compressed && !(*ibuf & 1))) {
+ obuf += yuvconvert(ibuf,obuf,out_fmt,in_uyvy);
+ ibuf += 4;
+ ll -= 4;
+ } else {
+ /*skip compressed interval from previous frame*/
+ int skipsize = skipcount(*ibuf >> 1, out_fmt);
+ obuf += skipsize;
+ if( obuf > end_obuf ) {
+ LOG("Insufficient data in buffer\n");
+ up(&cam->param_lock);
+ return -1;
+ }
+ ++ibuf;
+ ll--;
+ }
+ }
+ if(ll == 1) {
+ if(*ibuf != EOL) {
+ LOG("EOL not found giving up after %d/%d"
+ " bytes\n", origsize-size, origsize);
+ up(&cam->param_lock);
+ return -1;
+ }
+
+ ibuf++; /* skip over EOL */
+
+ if((size > 3) && (ibuf[0] == EOI) && (ibuf[1] == EOI) &&
+ (ibuf[2] == EOI) && (ibuf[3] == EOI)) {
+ size -= 4;
+ break;
+ }
+
+ if(size > 1) {
+ ll = ibuf[0] | (ibuf[1] << 8);
+ ibuf += 2; /* skip over line length */
+ }
+ } else {
+ LOG("line length was not 1 but %d after %d/%d bytes\n",
+ ll, origsize-size, origsize);
+ up(&cam->param_lock);
+ return -1;
+ }
+ }
+
+ cam->decompressed_frame.count = obuf-cam->decompressed_frame.data;
+
+ up(&cam->param_lock);
+
+ return cam->decompressed_frame.count;
+}
+
+/* InitStreamCap wrapper to select correct start line */
+static inline int init_stream_cap(struct cam_data *cam) {
+ return do_command(cam, CPIA_COMMAND_InitStreamCap,
+ 0, cam->params.streamStartLine, 0, 0);
+}
+
+/* update various camera modes and settings */
+static void dispatch_commands(struct cam_data *cam ) {
+ down(&cam->param_lock);
+ if( cam->cmd_queue==COMMAND_NONE ) {
+ up(&cam->param_lock);
+ return;
+ }
+ DEB_BYTE(cam->cmd_queue);
+ DEB_BYTE(cam->cmd_queue>>8);
+ if( cam->cmd_queue & COMMAND_SETCOLOURPARAMS ) {
+ do_command(cam, CPIA_COMMAND_SetColourParams,
+ cam->params.colourParams.brightness,
+ cam->params.colourParams.contrast,
+ cam->params.colourParams.saturation, 0);
+ }
+ if( cam->cmd_queue & COMMAND_SETCOMPRESSION ) {
+ do_command(cam, CPIA_COMMAND_SetCompression,
+ cam->params.compression.mode,
+ cam->params.compression.decimation, 0, 0);
+ }
+ if( cam->cmd_queue & COMMAND_SETFORMAT ) {
+ do_command(cam, CPIA_COMMAND_SetFormat,
+ cam->params.format.videoSize,
+ cam->params.format.subSample,
+ cam->params.format.yuvOrder, 0);
+ do_command(cam, CPIA_COMMAND_SetROI,
+ cam->params.roi.colStart, cam->params.roi.colEnd,
+ cam->params.roi.rowStart, cam->params.roi.rowEnd);
+ cam->first_frame = 1;
+ }
+ if( cam->cmd_queue & COMMAND_SETCOMPRESSIONTARGET ) {
+ do_command(cam, CPIA_COMMAND_SetCompressionTarget,
+ cam->params.compressionTarget.frTargeting,
+ cam->params.compressionTarget.targetFR,
+ cam->params.compressionTarget.targetQ, 0);
+ }
+ if( cam->cmd_queue & COMMAND_SETYUVTHRESH ) {
+ do_command(cam, CPIA_COMMAND_SetYUVThresh,
+ cam->params.yuvThreshold.yThreshold,
+ cam->params.yuvThreshold.uvThreshold, 0, 0);
+ }
+ if( cam->cmd_queue & COMMAND_SETECPTIMING ) {
+ do_command(cam, CPIA_COMMAND_SetECPTiming,
+ cam->params.ecpTiming, 0, 0, 0);
+ }
+ if( cam->cmd_queue & COMMAND_SETCOMPRESSIONPARAMS ) {
+ do_command_extended(cam, CPIA_COMMAND_SetCompressionParams,
+ 0, 0, 0, 0,
+ cam->params.compressionParams.hysteresis,
+ cam->params.compressionParams.threshMax,
+ cam->params.compressionParams.smallStep,
+ cam->params.compressionParams.largeStep,
+ cam->params.compressionParams.decimationHysteresis,
+ cam->params.compressionParams.frDiffStepThresh,
+ cam->params.compressionParams.qDiffStepThresh,
+ cam->params.compressionParams.decimationThreshMod);
+ }
+ if( cam->cmd_queue & COMMAND_SETEXPOSURE ) {
+ do_command_extended(cam, CPIA_COMMAND_SetExposure,
+ cam->params.exposure.gainMode,
+ cam->params.exposure.expMode,
+ cam->params.exposure.compMode,
+ cam->params.exposure.centreWeight,
+ cam->params.exposure.gain,
+ cam->params.exposure.fineExp,
+ cam->params.exposure.coarseExpLo,
+ cam->params.exposure.coarseExpHi,
+ cam->params.exposure.redComp,
+ cam->params.exposure.green1Comp,
+ cam->params.exposure.green2Comp,
+ cam->params.exposure.blueComp);
+ }
+ if( cam->cmd_queue & COMMAND_SETCOLOURBALANCE ) {
+ do_command(cam, CPIA_COMMAND_SetColourBalance,
+ cam->params.colourBalance.balanceMode,
+ cam->params.colourBalance.redGain,
+ cam->params.colourBalance.greenGain,
+ cam->params.colourBalance.blueGain);
+ }
+ if( cam->cmd_queue & COMMAND_SETSENSORFPS ) {
+ do_command(cam, CPIA_COMMAND_SetSensorFPS,
+ cam->params.sensorFps.divisor,
+ cam->params.sensorFps.baserate, 0, 0);
+ }
+ if( cam->cmd_queue & COMMAND_SETAPCOR ) {
+ do_command(cam, CPIA_COMMAND_SetApcor,
+ cam->params.apcor.gain1,
+ cam->params.apcor.gain2,
+ cam->params.apcor.gain4,
+ cam->params.apcor.gain8);
+ }
+ if( cam->cmd_queue & COMMAND_SETFLICKERCTRL ) {
+ do_command(cam, CPIA_COMMAND_SetFlickerCtrl,
+ cam->params.flickerControl.flickerMode,
+ cam->params.flickerControl.coarseJump,
+ cam->params.flickerControl.allowableOverExposure, 0);
+ }
+ if( cam->cmd_queue & COMMAND_SETVLOFFSET ) {
+ do_command(cam, CPIA_COMMAND_SetVLOffset,
+ cam->params.vlOffset.gain1,
+ cam->params.vlOffset.gain2,
+ cam->params.vlOffset.gain4,
+ cam->params.vlOffset.gain8);
+ }
+ if( cam->cmd_queue & COMMAND_PAUSE ) {
+ do_command(cam, CPIA_COMMAND_EndStreamCap, 0, 0, 0, 0);
+ }
+ if( cam->cmd_queue & COMMAND_RESUME ) {
+ init_stream_cap( cam );
+ }
+ up(&cam->param_lock);
+ cam->cmd_queue=COMMAND_NONE;
+ return;
+}
+
+/* kernel thread function to read image from camera */
+static void fetch_frame(void *data)
+{
+ int image_size;
+ struct cam_data *cam = (struct cam_data *)data;
+ unsigned long oldjif, rate, diff;
+
+ /* load first frame always uncompressed */
+ if( cam->first_frame &&
+ cam->params.compression.mode != CPIA_COMPRESSION_NONE ) {
+ do_command(cam, CPIA_COMMAND_SetCompression,
+ CPIA_COMPRESSION_NONE,
+ NO_DECIMATION, 0, 0);
+ }
+
+ /* init camera upload */
+ if(do_command(cam, CPIA_COMMAND_SetGrabMode, CPIA_GRAB_CONTINUOUS,
+ 0, 0, 0)) goto end;
+ if(do_command(cam, CPIA_COMMAND_GrabFrame, 0,
+ cam->params.streamStartLine, 0, 0)) goto end;
+
+ /* loop until image ready */
+ do_command(cam, CPIA_COMMAND_GetCameraStatus, 0, 0, 0, 0);
+ while(cam->params.status.streamState != STREAM_READY) {
+ if(current->need_resched) {schedule();}
+ current->state=TASK_INTERRUPTIBLE;
+ schedule_timeout(10*HZ/1000); /* 10 ms, hopefully ;) */
+ if(signal_pending(current)) {
+ goto end;
+ }
+ do_command(cam, CPIA_COMMAND_GetCameraStatus, 0, 0, 0, 0);
+ }
+
+ /* grab image from camera */
+ if(current->need_resched) {schedule();}
+ oldjif = jiffies;
+ image_size=cam->ops->streamRead(cam->lowlevel_data, cam->raw_image, 0);
+ if(image_size<=0) {
+ DBG("read frame failed\n");
+ goto end;
+ }
+ rate = image_size * HZ / 1024;
+ diff = jiffies-oldjif;
+ rate = diff==0 ? rate : rate/diff; /* unlikely but possible */
+ /* Keep track of transfer_rate as a runnung average over 3 frames
+ * to smooth out any inconsistencies */
+ cam->transfer_rate = (2*cam->transfer_rate + rate) / 3;
+
+ /* camera idle now so dispatch queued commands */
+ dispatch_commands( cam );
+
+ /* Update our knowledge of the camera state - FIXME: necessary? */
+ do_command(cam, CPIA_COMMAND_GetColourBalance, 0, 0, 0, 0);
+ do_command(cam, CPIA_COMMAND_GetExposure, 0, 0, 0, 0);
+
+ /* decompress and convert image to by copying it from
+ * raw_image to decompressed_frame
+ */
+ if(current->need_resched) {schedule();}
+ cam->image_size = parse_picture(cam, image_size);
+ if( cam->image_size <= 0 ) {
+ DBG("parse frame failed\n");
+ goto end;
+ }
+
+ /* FIXME: this only works for double buffering */
+ if( cam->frame[cam->curframe].state == FRAME_READY ) {
+ memcpy(cam->frame[cam->curframe].data,
+ cam->decompressed_frame.data,
+ cam->decompressed_frame.count);
+ cam->frame[cam->curframe].state = FRAME_DONE;
+ } else {
+ cam->decompressed_frame.state = FRAME_DONE;
+ }
+
+#if 0
+ if( cam->first_frame &&
+ cam->params.compression.mode != CPIA_COMPRESSION_NONE ) {
+ cam->first_frame = 0;
+ cam->cmd_queue |= COMMAND_SETCOMPRESSION;
+ }
+#else
+ if( cam->first_frame ) {
+ cam->first_frame = 0;
+ cam->cmd_queue |= COMMAND_SETCOMPRESSION;
+ cam->cmd_queue |= COMMAND_SETEXPOSURE;
+ }
+#endif
+end:
+ return;
+}
+
+static int capture_frame(struct cam_data *cam, struct video_mmap *vm)
+{
+ int retval = 0;
+
+ if( cam->frame_buf == NULL ) { /* we do lazy allocation */
+ if( (retval = allocate_frame_buf(cam)) ) {
+ return retval;
+ }
+ }
+
+ /* FIXME: the first frame seems to be captured by the camera
+ without regards to any initial settings, so we throw away
+ that one, the next one is generated with our settings
+ (exposure, color balance, ...)
+ */
+ if(cam->first_frame) {
+ cam->curframe = vm->frame;
+ cam->frame[cam->curframe].state = FRAME_READY;
+ fetch_frame(cam);
+ if(cam->frame[cam->curframe].state != FRAME_DONE) retval=-EIO;
+ }
+ cam->curframe = vm->frame;
+ cam->frame[cam->curframe].state = FRAME_READY;
+ fetch_frame(cam);
+ if(cam->frame[cam->curframe].state != FRAME_DONE) retval=-EIO;
+
+ return retval;
+}
+
+static int goto_high_power(struct cam_data *cam)
+{
+ if(do_command(cam, CPIA_COMMAND_GotoHiPower, 0, 0, 0, 0)) return -1;
+ if(do_command(cam, CPIA_COMMAND_GetCameraStatus, 0, 0, 0, 0)) return -1;
+ if(cam->params.status.systemState == HI_POWER_STATE) {
+ DBG("camera now in HIGH power state\n");
+ return 0;
+ }
+ printstatus(cam);
+ return -1;
+}
+
+static int goto_low_power(struct cam_data *cam)
+{
+ if(do_command(cam, CPIA_COMMAND_GotoLoPower, 0, 0, 0, 0)) return -1;
+ if(do_command(cam, CPIA_COMMAND_GetCameraStatus, 0, 0, 0, 0)) return -1;
+ if(cam->params.status.systemState == LO_POWER_STATE) {
+ DBG("camera now in LOW power state\n");
+ return 0;
+ }
+ printstatus(cam);
+ return -1;
+}
+
+static void save_camera_state(struct cam_data *cam)
+{
+ do_command(cam, CPIA_COMMAND_GetColourBalance, 0, 0, 0, 0);
+ do_command(cam, CPIA_COMMAND_GetExposure, 0, 0, 0, 0);
+
+ DBG("%d/%d/%d/%d/%d/%d/%d/%d\n",
+ cam->params.exposure.gain,
+ cam->params.exposure.fineExp,
+ cam->params.exposure.coarseExpLo,
+ cam->params.exposure.coarseExpHi,
+ cam->params.exposure.redComp,
+ cam->params.exposure.green1Comp,
+ cam->params.exposure.green2Comp,
+ cam->params.exposure.blueComp);
+ DBG("%d/%d/%d\n",
+ cam->params.colourBalance.redGain,
+ cam->params.colourBalance.greenGain,
+ cam->params.colourBalance.blueGain);
+
+ return;
+}
+
+static void set_camera_state(struct cam_data *cam)
+{
+ 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);
+ }
+ if(cam->params.colourBalance.balanceMode == 0)
+ cam->params.colourBalance.balanceMode = 2;
+
+ do_command_extended(cam, CPIA_COMMAND_SetExposure,
+ cam->params.exposure.gainMode, 1, 1,
+ cam->params.exposure.centreWeight,
+ cam->params.exposure.gain,
+ cam->params.exposure.fineExp,
+ cam->params.exposure.coarseExpLo,
+ cam->params.exposure.coarseExpHi,
+ cam->params.exposure.redComp,
+ cam->params.exposure.green1Comp,
+ cam->params.exposure.green2Comp,
+ cam->params.exposure.blueComp);
+ do_command_extended(cam, CPIA_COMMAND_SetExposure,
+ 0, 3, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0);
+
+ if(cam->params.exposure.gainMode == 0)
+ cam->params.exposure.gainMode = 2;
+ if(cam->params.exposure.expMode == 0)
+ cam->params.exposure.expMode = 2;
+ if(cam->params.exposure.centreWeight == 0)
+ cam->params.exposure.centreWeight = 1;
+
+ cam->cmd_queue = COMMAND_SETCOMPRESSION |
+ COMMAND_SETCOMPRESSIONTARGET |
+ COMMAND_SETCOLOURPARAMS |
+ COMMAND_SETFORMAT |
+ COMMAND_SETYUVTHRESH |
+ COMMAND_SETECPTIMING |
+ COMMAND_SETCOMPRESSIONPARAMS |
+#if 0
+ COMMAND_SETEXPOSURE |
+#endif
+ COMMAND_SETCOLOURBALANCE |
+ COMMAND_SETSENSORFPS |
+ COMMAND_SETAPCOR |
+ COMMAND_SETFLICKERCTRL |
+ COMMAND_SETVLOFFSET;
+ dispatch_commands(cam);
+ save_camera_state(cam);
+
+ return;
+}
+
+static void get_version_information(struct cam_data *cam)
+{
+ /* GetCPIAVersion */
+ do_command(cam, CPIA_COMMAND_GetCPIAVersion, 0, 0, 0, 0);
+ printk(KERN_INFO " CPIA Version: %d.%02d (%d.%d)\n",
+ cam->params.version.firmwareVersion,
+ cam->params.version.firmwareRevision,
+ cam->params.version.vcVersion,
+ cam->params.version.vcRevision);
+
+ /* GetPnPID */
+ do_command(cam, CPIA_COMMAND_GetPnPID, 0, 0, 0, 0);
+ printk(KERN_INFO " CPIA PnP-ID: %04x:%04x:%04x\n",
+ cam->params.pnpID.vendor, cam->params.pnpID.product,
+ cam->params.pnpID.deviceRevision);
+}
+
+/* initialize camera */
+static int reset_camera(struct cam_data *cam)
+{
+ /* Start the camera in low power mode */
+ if(goto_low_power(cam)) {
+ if( cam->params.status.systemState != 0x4 ) {
+ return -ENODEV;
+ }
+ /* FIXME: this is just dirty trial and error */
+ reset_camera_struct(cam);
+ goto_high_power(cam);
+ do_command(cam, CPIA_COMMAND_DiscardFrame, 0, 0, 0, 0);
+ if(goto_low_power(cam)) return -NODEV;
+ }
+
+ /* procedure described in developer's guide p3-28 */
+
+ /* Check the firmware version FIXME: should we check PNPID? */
+ cam->params.version.firmwareVersion = 0;
+ get_version_information(cam);
+ if(cam->params.version.firmwareVersion != 1) {
+ return -ENODEV;
+ }
+
+ /* The fatal error checking should be done after the camera powers up (developer's guide p 3-38) */
+
+ /* Set streamState before transition to high power to avoid bug
+ * in firmware 1-02 */
+ do_command(cam, CPIA_COMMAND_ModifyCameraStatus, STREAMSTATE, 0,
+ STREAM_NOT_READY, 0);
+
+ /* GotoHiPower */
+ if(goto_high_power(cam))
+ return -ENODEV;
+
+
+ /* Check the camera status */
+ if(do_command(cam, CPIA_COMMAND_GetCameraStatus, 0, 0, 0, 0))
+ return -EIO;
+ if(cam->params.status.fatalError) {
+ DBG("fatal_error: %#04x\n",cam->params.status.fatalError);
+ DBG("vp_status: %#04x\n",cam->params.status.vpStatus);
+ if(cam->params.status.fatalError & ~(COM_FLAG|CPIA_FLAG)) {
+ /* Fatal error in camera */
+ return -EIO;
+ } else if(cam->params.status.fatalError & (COM_FLAG|CPIA_FLAG)){
+ /* Firmware 1-02 may do this for parallel port cameras,
+ * just clear the flags (developer's guide p 3-38) */
+ do_command(cam, CPIA_COMMAND_ModifyCameraStatus,
+ FATALERROR, ~(COM_FLAG|CPIA_FLAG), 0, 0);
+ }
+ }
+
+ /* Check the camera status again */
+ if(cam->params.status.fatalError) {
+ if(cam->params.status.fatalError)
+ return -EIO;
+ }
+
+ /* VPVersion can't be retrieved before the camera is in HiPower,
+ * so get it here instead of in get_version_information. */
+ do_command(cam, CPIA_COMMAND_GetVPVersion, 0, 0, 0, 0);
+ printk(KERN_INFO " VP-Version: %d.%d %04x\n",
+ cam->params.vpVersion.vpVersion,
+ cam->params.vpVersion.vpRevision,
+ cam->params.vpVersion.cameraHeadID);
+
+ /* set camera to a known state */
+ set_camera_state(cam);
+
+ return 0;
+}
+
+/* ------------------------- V4L interface --------------------- */
+static int cpia_open(struct video_device *dev, int flags)
+{
+ int i;
+ struct cam_data *cam = dev->priv;
+
+ DBG("cpia_open\n");
+
+ if(cam->open_count > 0) {
+ DBG("Camera[%d] already open\n",cam->index);
+ return -EBUSY;
+ }
+
+ if(cam->raw_image == NULL) {
+ if((cam->raw_image=rvmalloc(CPIA_MAX_IMAGE_SIZE)) == NULL){
+ return -ENOMEM;
+ }
+ }
+ if(cam->decompressed_frame.data == NULL) {
+ cam->decompressed_frame.data=rvmalloc(CPIA_MAX_FRAME_SIZE);
+ if(cam->decompressed_frame.data == NULL){
+ rvfree(cam->raw_image, CPIA_MAX_IMAGE_SIZE);
+ cam->raw_image=NULL;
+ return -ENOMEM;
+ }
+ }
+
+ /* open cpia */
+ if (cam->ops->open(cam->index, &cam->lowlevel_data)) {
+ rvfree(cam->decompressed_frame.data, CPIA_MAX_FRAME_SIZE);
+ cam->decompressed_frame.data=NULL;
+ rvfree(cam->raw_image, CPIA_MAX_IMAGE_SIZE);
+ cam->raw_image=NULL;
+ return -ENODEV;
+ }
+
+ /* reset the camera */
+ if((i = reset_camera(cam)) != 0) {
+ cam->ops->close(cam->lowlevel_data);
+ rvfree(cam->decompressed_frame.data, CPIA_MAX_FRAME_SIZE);
+ cam->decompressed_frame.data=NULL;
+ rvfree(cam->raw_image, CPIA_MAX_IMAGE_SIZE);
+ cam->raw_image=NULL;
+ return i;
+ }
+
+ /* Set ownership of /proc/cpia/videoX to current user */
+ cam->proc_entry->uid = current->uid;
+
+ /* set mark for loading first frame uncompressed */
+ cam->first_frame = 1;
+ cam->busy_lock = MUTEX;
+
+ ++cam->open_count;
+#ifdef MODULE
+ MOD_INC_USE_COUNT;
+#endif
+ return 0;
+}
+
+/* FIXME */
+static void cpia_close(struct video_device *dev)
+{
+ struct cam_data *cam;
+
+ DBG("cpia_close\n");
+ cam = dev->priv;
+
+ if(cam->ops == NULL) {
+ if(--cam->open_count == 0) {
+ int i;
+ video_unregister_device(dev);
+ /* Need a lock when adding/removing cameras */
+ lock_kernel();
+ if(cam->raw_image) {
+ rvfree(cam->raw_image, CPIA_MAX_IMAGE_SIZE);
+ cam->raw_image=0;
+ }
+ if(cam->decompressed_frame.data) {
+ rvfree(cam->decompressed_frame.data,
+ CPIA_MAX_FRAME_SIZE);
+ cam->decompressed_frame.data=0;
+ }
+ if(cam->frame_buf) {
+ free_frame_buf(cam);
+ }
+ for(i=0; i<CPIA_MAXCAMS; ++i) {
+ if(camera[i] == cam) break;
+ }
+ kfree(cam);
+ if(i != CPIA_MAXCAMS) { /* should always be true */
+ camera[i] = NULL;
+ }
+ unlock_kernel();
+ }
+#ifdef MODULE
+ MOD_DEC_USE_COUNT;
+#endif
+ return;
+ }
+
+ /* Return ownership of /proc/cpia/videoX to root */
+ 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);
+
+ /* cleanup internal state stuff */
+ free_frames(cam->frame);
+
+ /* Update the camera ststus */
+ do_command(cam, CPIA_COMMAND_GetCameraStatus, 0, 0, 0, 0);
+
+ /* close cpia */
+ cam->ops->close(cam->lowlevel_data);
+
+ if(--cam->open_count == 0) {
+ /* clean up capture-buffers */
+ if(cam->raw_image) {
+ rvfree(cam->raw_image, CPIA_MAX_IMAGE_SIZE);
+ cam->raw_image = 0;
+ }
+ if(cam->decompressed_frame.data) {
+ rvfree(cam->decompressed_frame.data, CPIA_MAX_FRAME_SIZE);
+ cam->decompressed_frame.data = 0;
+ }
+ if(cam->frame_buf) {
+ free_frame_buf(cam);
+ }
+ }
+
+
+#ifdef MODULE
+ MOD_DEC_USE_COUNT;
+#endif
+ return;
+}
+
+static long cpia_read(struct video_device *dev, char *buf,
+ unsigned long count, int noblock)
+{
+ struct cam_data *cam = dev->priv;
+
+ /* make this _really_ smp and multithredi-safe */
+ if( down_interruptible(&cam->busy_lock) ) {
+ return -EINTR;
+ }
+
+ if (!buf) {
+ DBG("buf NULL\n");
+ up(&cam->busy_lock);
+ return -EINVAL;
+ }
+ if(count == 0) {
+ DBG("count 0\n");
+ up(&cam->busy_lock);
+ return 0;
+ }
+ if(!cam->ops) {
+ DBG("ops NULL\n");
+ up(&cam->busy_lock);
+ return -ENODEV;
+ }
+
+ /* upload frame */
+ cam->decompressed_frame.state = FRAME_READY;
+ fetch_frame(cam);
+ if( cam->decompressed_frame.state != FRAME_DONE ) {
+ DBG("upload failed %d/%d\n", cam->decompressed_frame.count,
+ cam->decompressed_frame.state);
+ up(&cam->busy_lock);
+ return -EIO;
+ }
+ cam->decompressed_frame.state = FRAME_UNUSED;
+
+ /* copy data to user space */
+ if(cam->decompressed_frame.count > count) {
+ DBG("count wrong\n");
+ up(&cam->busy_lock);
+ return -EFAULT;
+ }
+ if(copy_to_user(buf, cam->decompressed_frame.data,
+ cam->decompressed_frame.count)) {
+ DBG("copy_to_user failed\n");
+ up(&cam->busy_lock);
+ return -EFAULT;
+ }
+
+ up(&cam->busy_lock);
+ return cam->decompressed_frame.count;
+}
+
+static int cpia_ioctl(struct video_device *dev, unsigned int ioctlnr, void *arg)
+{
+ struct cam_data *cam = dev->priv;
+ int retval = 0;
+
+ /* make this _really_ smp-safe */
+ if( down_interruptible(&cam->busy_lock) ) {
+ return -EINTR;
+ }
+ //DBG("cpia_ioctl: %u\n", ioctlnr);
+
+ switch (ioctlnr) {
+
+ /* query capabilites */
+ case VIDIOCGCAP:
+ {
+ struct video_capability b;
+
+ DBG("VIDIOCGCAP\n");
+ strcpy(b.name, "CPiA Parport Camera");
+ b.type = VID_TYPE_CAPTURE;
+ b.channels = 1;
+ b.audios = 0;
+ b.maxwidth = 352; /* VIDEOSIZE_CIF */
+ b.maxheight = 288;
+ b.minwidth = 48; /* VIDEOSIZE_48_48 */
+ b.minheight = 48;
+
+ if (copy_to_user(arg, &b, sizeof(b)))
+ retval = -EFAULT;
+ break;
+ }
+
+ /* get/set video source - we are a camera and nothing else */
+ case VIDIOCGCHAN:
+ {
+ struct video_channel v;
+
+ DBG("VIDIOCGCHAN\n");
+ if (copy_from_user(&v, arg, sizeof(v))) {
+ retval = -EFAULT;
+ break;
+ }
+ if (v.channel != 0) {
+ retval = -EINVAL;
+ break;
+ }
+
+ v.channel = 0;
+ strcpy(v.name, "Camera");
+ v.tuners = 0;
+ v.flags = 0;
+ v.type = VIDEO_TYPE_CAMERA;
+ v.norm = 0;
+
+ if (copy_to_user(arg, &v, sizeof(v)))
+ retval = -EFAULT;
+ break;
+ }
+
+ case VIDIOCSCHAN:
+ {
+ int v;
+
+ DBG("VIDIOCSCHAN\n");
+ if (copy_from_user(&v, arg, sizeof(v)))
+ retval = -EFAULT;
+
+ if (retval == 0 && v != 0)
+ retval = -EINVAL;
+
+ break;
+ }
+
+ /* image properties */
+ case VIDIOCGPICT:
+ DBG("VIDIOCGPICT\n");
+ if (copy_to_user(arg, &cam->vp, sizeof(struct video_picture)))
+ retval = -EFAULT;
+ break;
+
+ case VIDIOCSPICT:
+ {
+ struct video_picture vp;
+
+ DBG("VIDIOCSPICT\n");
+
+ /* copy_from_user */
+ if (copy_from_user(&vp, arg, sizeof(vp))) {
+ retval = -EFAULT;
+ break;
+ }
+ /* check validity */
+ DBG("palette: %d\n", vp.palette);
+ DBG("depth: %d\n", vp.depth);
+ if ( !valid_mode(vp.palette, vp.depth) ) {
+ retval = -EINVAL;
+ break;
+ }
+ down(&cam->param_lock);
+ /* brightness, colour, contrast need no check 0-65535 */
+ memcpy( &cam->vp, &vp, sizeof(vp) );
+ /* update cam->params.colourParams */
+ cam->params.colourParams.brightness = vp.brightness*100/65535;
+ cam->params.colourParams.contrast = vp.contrast*100/65535;
+ cam->params.colourParams.saturation = vp.colour*100/65535;
+ /* contrast is in steps of 8, so round */
+ cam->params.colourParams.contrast =
+ ((cam->params.colourParams.contrast + 3) / 8) * 8;
+ if(cam->params.version.firmwareVersion == 1 &&
+ cam->params.version.firmwareRevision == 2 &&
+ cam->params.colourParams.contrast > 80) {
+ /* 1-02 firmware limits contrast to 80 */
+ cam->params.colourParams.contrast = 80;
+ }
+ /* queue command to update camera */
+ cam->cmd_queue |= COMMAND_SETCOLOURPARAMS;
+ up(&cam->param_lock);
+ DBG("VIDIOCSPICT: %d / %d // %d / %d / %d / %d\n",
+ vp.depth, vp.palette, vp.brightness, vp.hue, vp.colour,
+ vp.contrast);
+ break;
+ }
+
+ /* get/set capture window */
+ case VIDIOCGWIN:
+ DBG("VIDIOCGWIN\n");
+
+ if (copy_to_user(arg, &cam->vw, sizeof(struct video_window)))
+ retval = -EFAULT;
+ break;
+
+ case VIDIOCSWIN:
+ {
+ /* copy_from_user, check validity, copy to internal structure */
+ struct video_window vw;
+ DBG("VIDIOCSWIN\n");
+ if (copy_from_user(&vw, arg, sizeof(vw))) {
+ retval = -EFAULT;
+ break;
+ }
+
+ if (vw.clipcount != 0) { /* clipping not supported */
+ retval = -EINVAL;
+ break;
+ }
+ if (vw.clips != NULL) { /* clipping not supported */
+ retval = -EINVAL;
+ break;
+ }
+
+ /* we set the video window to something smaller or equal to what
+ * is requested by the user???
+ */
+ down(&cam->param_lock);
+ if(vw.width!=cam->vw.width || vw.height!=cam->vw.height) {
+ int video_size = match_videosize(vw.width, vw.height);
+ if(video_size < 0) {
+ retval = -EINVAL;
+ up(&cam->param_lock);
+ break;
+ }
+ cam->video_size = video_size;
+ set_vw_size(cam);
+ DBG("%d / %d\n", cam->vw.width, cam->vw.height);
+ cam->cmd_queue |= COMMAND_SETFORMAT;
+ }
+
+ // FIXME needed??? memcpy(&cam->vw, &vw, sizeof(vw));
+ up(&cam->param_lock);
+
+ /* setformat ignored by camera during streaming,
+ * so stop/dispatch/start */
+ if(cam->cmd_queue & COMMAND_SETFORMAT) {
+ DBG("\n");
+ dispatch_commands(cam);
+ }
+ DBG("%d/%d:%d\n", cam->video_size,
+ cam->vw.width, cam->vw.height);
+ break;
+ }
+
+ /* mmap interface */
+ case VIDIOCGMBUF:
+ {
+ struct video_mbuf vm;
+ int i;
+
+ DBG("VIDIOCGMBUF\n");
+ memset(&vm, 0, sizeof(vm));
+ vm.size = CPIA_MAX_FRAME_SIZE*FRAME_NUM;
+ vm.frames = FRAME_NUM;
+ for( i=0; i<FRAME_NUM; i++ ) {
+ vm.offsets[i] = CPIA_MAX_FRAME_SIZE*i;
+ }
+ if(copy_to_user((void *)arg, (void *)&vm, sizeof(vm)))
+ retval = -EFAULT;
+
+ break;
+ }
+
+ case VIDIOCMCAPTURE:
+ {
+ struct video_mmap vm;
+ int video_size;
+
+ if (copy_from_user((void *)&vm, (void *)arg, sizeof(vm))) {
+ retval = -EFAULT;
+ break;
+ }
+#if 0
+ DBG("VIDIOCMCAPTURE: %d / %d / %dx%d\n", vm.format, vm.frame,
+ vm.width, vm.height);
+#endif
+ if (vm.frame<0||vm.frame>FRAME_NUM) {
+ retval = -EINVAL;
+ break;
+ }
+
+ /* set video format */
+ cam->vp.palette = vm.format;
+ switch(vm.format) {
+ case VIDEO_PALETTE_GREY:
+ case VIDEO_PALETTE_RGB555:
+ case VIDEO_PALETTE_RGB565:
+ case VIDEO_PALETTE_YUV422:
+ case VIDEO_PALETTE_YUYV:
+ case VIDEO_PALETTE_UYVY:
+ cam->vp.depth = 16;
+ break;
+ case VIDEO_PALETTE_RGB24:
+ cam->vp.depth = 24;
+ break;
+ case VIDEO_PALETTE_RGB32:
+ cam->vp.depth = 32;
+ break;
+ default:
+ retval = -EINVAL;
+ break;
+ }
+ if(retval != 0) break;
+
+ /* set video size */
+ video_size = match_videosize(vm.width, vm.height);
+ if(cam->video_size<0) {
+ retval = -EINVAL;
+ break;
+ }
+ if( video_size != cam->video_size ) {
+ cam->video_size = video_size;
+ set_vw_size(cam);
+ cam->cmd_queue |= COMMAND_SETFORMAT;
+ dispatch_commands(cam);
+ }
+#if 0
+ DBG("VIDIOCMCAPTURE: %d / %d/%d\n", cam->video_size,
+ cam->vw.width, cam->vw.height);
+#endif
+ /* according to v4l-spec we must start streaming here */
+ retval = capture_frame(cam, &vm);
+
+ break;
+ }
+
+ case VIDIOCSYNC:
+ {
+ int frame;
+
+ if (copy_from_user((void *)&frame, arg, sizeof(int))) {
+ retval = -EFAULT;
+ break;
+ }
+ //DBG("VIDIOCSYNC: %d\n", frame);
+
+ if (frame<0||frame>=FRAME_NUM) {
+ retval = -EINVAL;
+ break;
+ }
+
+ switch (cam->frame[frame].state) {
+ case FRAME_UNUSED:
+ case FRAME_READY:
+ case FRAME_GRABBING:
+ DBG("sync to unused frame %d\n", frame);
+ retval = -EINVAL;
+ break;
+
+ case FRAME_DONE:
+ cam->frame[frame].state = FRAME_UNUSED;
+ //DBG("VIDIOCSYNC: %d synced\n", frame);
+ break;
+ }
+ if(retval == -EINTR) {
+ /* FIXME - xawtv does not handle this nice */
+ retval = 0;
+ }
+ break;
+ }
+
+ /* pointless to implement overlay with this camera */
+ case VIDIOCCAPTURE:
+ retval = -EINVAL;
+ break;
+ case VIDIOCGFBUF:
+ retval = -EINVAL;
+ break;
+ case VIDIOCSFBUF:
+ retval = -EINVAL;
+ break;
+ case VIDIOCKEY:
+ retval = -EINVAL;
+ break;
+
+ /* tuner interface - we have none */
+ case VIDIOCGTUNER:
+ retval = -EINVAL;
+ break;
+ case VIDIOCSTUNER:
+ retval = -EINVAL;
+ break;
+ case VIDIOCGFREQ:
+ retval = -EINVAL;
+ break;
+ case VIDIOCSFREQ:
+ retval = -EINVAL;
+ break;
+
+ /* audio interface - we have none */
+ case VIDIOCGAUDIO:
+ retval = -EINVAL;
+ break;
+ case VIDIOCSAUDIO:
+ retval = -EINVAL;
+ break;
+ default:
+ retval = -ENOIOCTLCMD;
+ break;
+ }
+
+ up(&cam->param_lock);
+ up(&cam->busy_lock);
+ return retval;
+}
+
+/* FIXME */
+static int cpia_mmap(struct video_device *dev, const char *adr,
+ unsigned long size)
+{
+ unsigned long start = (unsigned long)adr;
+ unsigned long page, pos;
+ struct cam_data *cam;
+ int retval;
+
+ DBG("cpia_mmap: %ld\n", size);
+
+ if(size > FRAME_NUM*CPIA_MAX_FRAME_SIZE){
+ return -EINVAL;
+ }
+
+ cam = dev->priv;
+
+ /* make this _really_ smp-safe */
+ if( down_interruptible(&cam->busy_lock) ) {
+ return -EINTR;
+ }
+
+ if( cam->frame_buf == NULL ) { /* we do lazy allocation */
+ if( (retval = allocate_frame_buf(cam)) ) {
+ up(&cam->busy_lock);
+ return retval;
+ }
+ }
+ pos = (unsigned long)(cam->frame_buf);
+ while (size > 0) {
+ page = kvirt_to_phys(pos);
+ if (remap_page_range(start, page, PAGE_SIZE, PAGE_SHARED)) {
+ up(&cam->busy_lock);
+ return -EAGAIN;
+ }
+ start+=PAGE_SIZE;
+ pos+=PAGE_SIZE;
+ if (size > PAGE_SIZE) size-=PAGE_SIZE;
+ else size=0;
+ }
+
+ DBG("cpia_mmap: %ld\n", size);
+ up(&cam->busy_lock);
+ return 0;
+}
+
+int cpia_video_init(struct video_device *vdev)
+{
+ create_proc_cpia_cam(vdev->priv);
+ return 0;
+}
+
+static struct video_device cpia_template = {
+ "CPiA Camera",
+ VID_TYPE_CAPTURE,
+ VID_HARDWARE_CPIA, /* FIXME */
+ cpia_open, /* open */
+ cpia_close, /* close */
+ cpia_read, /* read */
+ NULL, /* no write */
+ NULL, /* no poll */
+ cpia_ioctl, /* ioctl */
+ cpia_mmap, /* mmap */
+ cpia_video_init, /* initialize */
+ NULL, /* priv */
+ 0, /* busy */
+ -1 /* minor - unset */
+};
+
+/* initialise cam_data structure */
+static void reset_camera_struct(struct cam_data *cam)
+{
+ /* The following parameter values are the defaults from
+ * "Software Developer's Guide for CPiA Cameras". Any changes
+ * to the defaults are noted in comments. */
+ cam->params.colourParams.brightness = 50;
+ cam->params.colourParams.contrast = 48;
+ cam->params.colourParams.saturation = 50;
+ cam->params.exposure.gainMode = 2;
+ cam->params.exposure.expMode = 2; /* AEC */
+ cam->params.exposure.compMode = 1;
+ cam->params.exposure.centreWeight = 1;
+ cam->params.exposure.gain = 0;
+ cam->params.exposure.fineExp = 0;
+ cam->params.exposure.coarseExpLo = 185;
+ cam->params.exposure.coarseExpHi = 0;
+ cam->params.exposure.redComp = 220;
+ cam->params.exposure.green1Comp = 214;
+ cam->params.exposure.green2Comp = 214;
+ cam->params.exposure.blueComp = 230;
+ cam->params.colourBalance.balanceMode = 2; /* auto color balance */
+ cam->params.colourBalance.redGain = 32;
+ cam->params.colourBalance.greenGain = 6;
+ cam->params.colourBalance.blueGain = 92;
+ cam->params.apcor.gain1 = 0x1c;
+ cam->params.apcor.gain2 = 0x1a;
+ cam->params.apcor.gain4 = 0x2d;
+ cam->params.apcor.gain8 = 0x2a;
+ cam->params.flickerControl.flickerMode = 0;
+ cam->params.flickerControl.coarseJump =
+ flicker_jumps[cam->mainsFreq]
+ [cam->params.sensorFps.baserate]
+ [cam->params.sensorFps.divisor];
+ cam->params.vlOffset.gain1 = 24;
+ cam->params.vlOffset.gain2 = 28;
+ cam->params.vlOffset.gain4 = 30;
+ cam->params.vlOffset.gain8 = 30;
+ cam->params.compressionParams.hysteresis = 3;
+ cam->params.compressionParams.threshMax = 11;
+ cam->params.compressionParams.smallStep = 1;
+ cam->params.compressionParams.largeStep = 3;
+ cam->params.compressionParams.decimationHysteresis = 2;
+ cam->params.compressionParams.frDiffStepThresh = 5;
+ cam->params.compressionParams.qDiffStepThresh = 3;
+ cam->params.compressionParams.decimationThreshMod = 2;
+ /* End of default values from Software Developer's Guide */
+
+ /* Start with a reasonable transfer rate */
+ cam->transfer_rate = 500;
+
+ /* Set Sensor FPS to 15fps. This seems better than 30fps
+ * for indoor lighting. */
+ cam->params.sensorFps.divisor = 1;
+ cam->params.sensorFps.baserate = 1;
+
+ cam->params.yuvThreshold.yThreshold = 15; /* FIXME? */
+ cam->params.yuvThreshold.uvThreshold = 15; /* FIXME? */
+
+ cam->params.format.subSample = SUBSAMPLE_422;
+ cam->params.format.yuvOrder = YUVORDER_YUYV;
+
+ cam->params.compression.mode = CPIA_COMPRESSION_AUTO;
+ cam->params.compressionTarget.frTargeting =
+ CPIA_COMPRESSION_TARGET_QUALITY;
+ cam->params.compressionTarget.targetFR = 7; /* FIXME? */
+ cam->params.compressionTarget.targetQ = 10; /* FIXME? */
+
+ cam->video_size = VIDEOSIZE_CIF;
+
+ cam->vp.colour = 32768; /* 50% */
+ cam->vp.hue = 32768; /* 50% */
+ cam->vp.brightness = 32768; /* 50% */
+ cam->vp.contrast = 32768; /* 50% */
+ cam->vp.whiteness = 0; /* not used -> grayscale only */
+ cam->vp.depth = 0; /* FIXME: to be set by user? */
+ cam->vp.palette = 0; /* FIXME: to be set by user? */
+
+ cam->vw.x = 0;
+ cam->vw.y = 0;
+ set_vw_size(cam);
+ cam->vw.chromakey = 0;
+ /* PP NOTE: my extension to use vw.flags for this, bear it! */
+ cam->vw.flags = 0;
+ cam->vw.clipcount = 0;
+ cam->vw.clips = NULL;
+
+ cam->cmd_queue = COMMAND_NONE;
+ cam->first_frame = 0;
+
+ return;
+}
+
+/* initialize cam_data structure */
+static void init_camera_struct(struct cam_data *cam,
+ struct cpia_camera_ops *ops )
+{
+ int i;
+ /* Default everything to 0 */
+ memset(cam, 0, sizeof(struct cam_data));
+
+ cam->ops = ops;
+ cam->param_lock = MUTEX;
+
+ reset_camera_struct(cam);
+
+ cam->proc_entry = NULL;
+
+ memcpy(&cam->vdev, &cpia_template, sizeof(cpia_template));
+ cam->vdev.priv = cam;
+
+ cam->curframe = 0;
+ for( i=0; i<FRAME_NUM; i++) {
+ cam->frame[i].width = 0;
+ cam->frame[i].height = 0;
+ cam->frame[i].state = FRAME_UNUSED;
+ cam->frame[i].data = NULL;
+ }
+ cam->decompressed_frame.width = 0;
+ cam->decompressed_frame.height = 0;
+ cam->decompressed_frame.state = FRAME_UNUSED;
+ cam->decompressed_frame.data = NULL;
+
+ return;
+}
+
+int cpia_register_camera(struct cpia_camera_ops *ops)
+{
+ int i;
+ struct cam_data *cam;
+ /* Need a lock when adding/removing cameras. This doesn't happen
+ * often and doesn't take very long, so grabbing the kernel lock
+ * should be OK. */
+ lock_kernel();
+ for(i=0; i < CPIA_MAXCAMS && camera[i] != NULL; ++i); /* no loop body */
+ if(i == CPIA_MAXCAMS) {
+ unlock_kernel();
+ return -ENODEV;
+ }
+
+ if((camera[i] = kmalloc(sizeof(struct cam_data), GFP_KERNEL)) == NULL) {
+ unlock_kernel();
+ return -ENOMEM;
+ }
+
+ init_camera_struct( camera[i], ops );
+ camera[i]->index = i;
+
+ /* register v4l device */
+ if (video_register_device(&camera[i]->vdev, VFL_TYPE_GRABBER) == -1) {
+ kfree(camera[i]);
+ camera[i] = NULL;
+ unlock_kernel();
+ printk(KERN_DEBUG "video_register_device failed\n");
+ return -ENODEV;
+ }
+
+ unlock_kernel();
+
+ /* FIXME */
+#if 1
+ cam = camera[i];
+ /* open cpia */
+ if (cam->ops->open(cam->index, &cam->lowlevel_data)) {
+ rvfree(cam->decompressed_frame.data, CPIA_MAX_FRAME_SIZE);
+ cam->decompressed_frame.data=NULL;
+ rvfree(cam->raw_image, CPIA_MAX_IMAGE_SIZE);
+ cam->raw_image=NULL;
+ return -ENODEV;
+ }
+
+ /* reset the camera */
+ if((i = reset_camera(cam)) != 0) {
+ cam->ops->close(cam->lowlevel_data);
+ rvfree(cam->decompressed_frame.data, CPIA_MAX_FRAME_SIZE);
+ cam->decompressed_frame.data=NULL;
+ rvfree(cam->raw_image, CPIA_MAX_IMAGE_SIZE);
+ cam->raw_image=NULL;
+ return i;
+ }
+#endif
+ return i;
+}
+
+void cpia_unregister_camera(int camnr)
+{
+ struct cam_data *cam;
+ if(camnr >= CPIA_MAXCAMS || camera[camnr] == NULL) return;
+ cam = camera[camnr];
+
+ DBG("unregistering video\n");
+ /* FIXME: Is this safe if the device is open? */
+ video_unregister_device(&cam->vdev);
+
+ /* Need a lock when adding/removing cameras. This doesn't happen
+ * often and doesn't take very long, so grabbing the kernel lock
+ * should be OK. */
+ lock_kernel();
+
+ DBG("destroying /proc/cpia/video%d\n", cam->vdev.minor);
+ destroy_proc_cpia_cam(cam);
+
+ if(cam->open_count == 0) {
+ DBG("freeing camera\n");
+ kfree(camera[camnr]);
+ camera[camnr] = NULL;
+ } else {
+ DBG("camera open -- setting ops to NULL\n");
+ cam->ops = NULL;
+ }
+ unlock_kernel();
+}
+
+/****************************************************************************
+ *
+ * Module routines
+ *
+ ***************************************************************************/
+
+#ifdef MODULE
+int init_module(void)
+{
+ printk(KERN_INFO "%s v%d.%d.%d\n", ABOUT,
+ CPIA_MAJ_VER, CPIA_MIN_VER, CPIA_PATCH_VER);
+ proc_cpia_create();
+#ifdef CONFIG_KMOD
+#ifdef CONFIG_VIDEO_CPIA_PP_MODULE
+ request_module("cpia_pp");
+#endif
+#ifdef CONFIG_VIDEO_CPIA_USB_MODULE
+ request_module("cpia_usb");
+#endif
+#endif
+return 0;
+}
+
+void cleanup_module(void)
+{
+ int i;
+ for(i=0; i<CPIA_MAXCAMS; ++i) {
+ if(camera[i]) {
+ video_unregister_device(&camera[i]->vdev);
+ }
+ }
+
+ proc_cpia_destroy();
+}
+
+#else
+
+int cpia_init(struct video_init *unused)
+{
+ printk(KERN_INFO "%s v%d.%d.%d\n", ABOUT,
+ CPIA_MAJ_VER, CPIA_MIN_VER, CPIA_PATCH_VER);
+ proc_cpia_create();
+
+#ifdef MODULE
+#ifdef CONFIG_KMOD
+#ifdef CONFIG_VIDEO_CPIA_PP_MODULE
+ request_module("cpia_pp");
+#endif
+#ifdef CONFIG_VIDEO_CPIA_USB_MODULE
+ request_module("cpia_usb");
+#endif
+#endif /* CONFIG_KMOD */
+#else /* !MODULE */
+#ifdef CONFIG_VIDEO_CPIA_PP
+ cpia_pp_init();
+#endif
+#ifdef CONFIG_VIDEO_CPIA_USB
+ cpia_usb_init();
+#endif
+#endif /* !MODULE */
+ return 0;
+}
+
+/* Exported symbols for modules. */
+
+EXPORT_SYMBOL(cpia_register_camera);
+EXPORT_SYMBOL(cpia_unregister_camera);
+
+#endif
FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen (who was at: slshen@lbl.gov)