/****************************************************************************
 *
 *  Filename: cpia2_v4l.c
 *
 *  Copyright 2001, STMicrolectronics, Inc.
 *      Contact:  steve.miller@st.com
 *  Copyright 2001, Scott J. Bertin <sbertin@securenym.net>
 *
 *  Description:
 *     This is a USB driver for CPia2 based video cameras.
 *     The infrastructure of this driver is based on the cpia usb driver by
 *     Jochen Scharrlach and Johannes Erdfeldt.
 *
 *  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.
 *
 ****************************************************************************/
/* $Id: cpia2_v4l.c,v 1.21 2002/07/23 04:47:55 sbertin Exp $ */

//#define _CPIA2_DEBUG_

#include <linux/config.h>

#include <linux/module.h>
#include <linux/time.h>
#include <linux/sched.h>
#include <linux/slab.h>
#include <linux/init.h>

#include "videodev_mjpeg.h"	/* FIXME: should be part of the kernel */

#include "cpia2.h"
#include "cpia2dev.h"

#define MAKE_STRING_1(x)	#x
#define MAKE_STRING(x)	MAKE_STRING_1(x)

#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,5))
static int video_nr = -1;
MODULE_PARM(video_nr, "i");
MODULE_PARM_DESC(video_nr,"video device to register (0=/dev/video0, etc)");
#endif

static int buffer_size = 68*1024;
MODULE_PARM(buffer_size, "i");
MODULE_PARM_DESC(buffer_size, "Size for each frame buffer in bytes (default 68k)");

static int num_buffers = 3;
MODULE_PARM(num_buffers, "i");
MODULE_PARM_DESC(num_buffers, "Number of frame buffers (1-"
                 MAKE_STRING(VIDEO_MAX_FRAME) ", default 3)");

static int alternate = DEFAULT_ALT;
MODULE_PARM(alternate, "i");
MODULE_PARM_DESC(alternate, "USB Alternate (" MAKE_STRING(USBIF_ISO_1) "-"
                 MAKE_STRING(USBIF_ISO_6) ", default "
		 MAKE_STRING(DEFAULT_ALT) ")");

static int flicker_freq = 60;
MODULE_PARM(flicker_freq, "i");
MODULE_PARM_DESC(flicker_freq, "Flicker frequency (" MAKE_STRING(50) "or"
                 MAKE_STRING(60) ", default "
		 MAKE_STRING(60) ")");

static int flicker_mode = NEVER_FLICKER;
MODULE_PARM(flicker_mode, "i");
MODULE_PARM_DESC(flicker_mode,
		 "Flicker supression (" MAKE_STRING(NEVER_FLICKER) "or"
                 MAKE_STRING(ANTI_FLICKER_ON) ", default "
		 MAKE_STRING(NEVER_FLICKER) ")");

MODULE_AUTHOR("Steve Miller (STMicroelectronics) <steve.miller@st.com>");
MODULE_DESCRIPTION("V4L-driver for STMicroelectronics CPiA2 based cameras");
MODULE_SUPPORTED_DEVICE("video");
#ifdef MODULE_LICENSE
MODULE_LICENSE("GPL");
#endif

#define ABOUT "V4L-Driver for Vision CPiA2 based cameras"

#ifndef VID_HARDWARE_CPIA2
#define VID_HARDWARE_CPIA2 33	/* from linux/videodev.h */
#endif


static int cpia2_open(struct video_device *dev, int flags);
static void cpia2_close(struct video_device *dev);
static long cpia2_v4l_read(struct video_device *dev,
		           char *buf, unsigned long count, int noblock);
static unsigned int cpia2_v4l_poll(struct video_device *dev,
                                   struct file *filp, poll_table *wait);
static int cpia2_ioctl(struct video_device *dev,
		       unsigned int ioctl_nr, void *arg);
static int cpia2_mmap(struct video_device *dev,
		      const char *adr, unsigned long size);
static int cpia2_video_init(struct video_device *vdev);
static int ioctl_cap_query(void *arg, struct camera_data *cam);
static int ioctl_get_channel(void *arg);
static int ioctl_set_channel(void *arg);
static int ioctl_set_image_prop(void *arg, struct camera_data *cam);
static int ioctl_set_window_size(void *arg, struct camera_data *cam);
static int ioctl_get_mbuf(void *arg, struct camera_data *cam);
static int ioctl_mcapture(void *arg, struct camera_data *cam);
static int ioctl_sync(void *arg, struct camera_data *cam);
static int ioctl_mjpeg_sync(void *arg, struct camera_data *cam);
static int ioctl_mjpeg_reqbufs(void *arg, struct camera_data *cam);
static int ioctl_mjpeg_status(void *arg, struct camera_data *cam);
static int ioctl_mjpeg_get_params(void *arg, struct camera_data *cam);
static int ioctl_mjpeg_set_params(void *arg, struct camera_data *cam);
static int ioctl_mjpeg_capture(void *arg, struct camera_data *cam);
static int ioctl_set_flicker_mode(void *arg, struct camera_data *cam);
static int ioctl_set_user_effects(void *arg,
				  struct camera_data *cam,
				  int property);
static int ioctl_get_cam_state(void *arg,
			       struct camera_data *cam);
static int ioctl_set_usb_alternate(void *arg, struct camera_data *cam);
static int ioctl_get_usb_alternate(void *arg, struct camera_data *cam);
static int ioctl_set_framerate(void *arg, struct camera_data *cam);
static int ioctl_get_framerate(void *arg, struct camera_data *cam);
static int ioctl_set_target_kb(void *arg, struct camera_data *cam);
static int ioctl_get_target_kb(void *arg, struct camera_data *cam);

/***
 * The v4l video device structure initialized for this device
 ***/
static struct video_device cpia2_template = {
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,3))
	owner:      THIS_MODULE,
#endif
	name:       "CPiA2 Camera",
	type:       VID_TYPE_CAPTURE,
	hardware:   VID_HARDWARE_CPIA2,
	open:       cpia2_open,
	close:      cpia2_close,
	read:       cpia2_v4l_read,
	poll:       cpia2_v4l_poll,
	ioctl:      cpia2_ioctl,
	mmap:       cpia2_mmap,
	initialize: cpia2_video_init,
	minor:      -1
};

/******************************************************************************
 *
 *  cpia_open
 *
 *****************************************************************************/
static int cpia2_open(struct video_device *dev, int flags)
{
	int status;
	struct camera_data *cam = dev->priv;

	if (!cam) {
		ERR("Internal error, camera_data not found!\n");
		return -EBUSY;
	}

	if (cam->open_count > 0) {
		LOG("Attempt to open camera twice\n");
		return -EBUSY;
	}

	cpia2_allocate_buffers(cam);
	
	/* open lowlevel (usb) device */
	if (cpia2_usb_open(cam)) {
		return -ENODEV;
	}

	/* reset the camera */
	if ((status = cpia2_reset_camera(cam)) < 0) {
		cpia2_usb_close(cam);
		return -EIO;
	}
	
	cam->APP_len = 0;
	cam->COM_len = 0;

	/* Set ownership of /proc/driver/cpia2/videoX to current user */
	cpia2_proc_set_owner(cam, current->uid);

	++cam->open_count;
#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,4,3))
	MOD_INC_USE_COUNT;
#endif

	cpia2_dbg_dump_registers(cam);

	/* Start streaming */
	cpia2_usb_stream_start(cam, cam->params.camera_state.stream_mode);

	return 0;
}

/******************************************************************************
 *
 *  cpia2_close
 *
 *****************************************************************************/
static void cpia2_close(struct video_device *dev)
{
	struct camera_data *cam = dev->priv;

	if (cam->present) {
		/* Return ownership of /proc/driver/cpia2/videoX to root */
		cpia2_proc_set_owner(cam, 0);

		/* save camera state for later open */
		cpia2_save_camera_state(cam);

		cpia2_usb_stream_stop(cam, false);
		cpia2_usb_camera_start(cam, 0);

		cpia2_set_low_power(cam);

		cpia2_free_buffers(cam);

		/* close cpia */
		cpia2_usb_close(cam);
	}

	if (--cam->open_count == 0) {
		cpia2_free_buffers(cam);
		if (!cam->present) {
			video_unregister_device(dev);
			kfree(cam);
		}
	}
#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,4,3))
	MOD_DEC_USE_COUNT;
#endif
	return;
}

/******************************************************************************
 *
 *  cpia2_v4l_read
 *
 *****************************************************************************/
static long cpia2_v4l_read(struct video_device *dev,
		           char *buf, unsigned long count, int noblock)
{
	struct camera_data *cam = dev->priv;

	return cpia2_read(cam, buf, count, noblock);
}


/******************************************************************************
 *
 *  cpia2_v4l_poll
 *
 *****************************************************************************/
static unsigned int cpia2_v4l_poll(struct video_device *dev,
		                   struct file *filp, poll_table *wait)
{
	struct camera_data *cam = dev->priv;

	return cpia2_poll(cam, filp, wait);
}


/******************************************************************************
 *
 *  cpia2_ioctl
 *
 *****************************************************************************/
static int cpia2_ioctl(struct video_device *dev,
		       unsigned int ioctl_nr, void *arg)
{
	struct camera_data *cam = dev->priv;
	int retval = 0;

	if (!cam)
		return -ENOTTY;

	/* make this _really_ smp-safe */
	if (down_interruptible(&cam->busy_lock))
		return -ERESTARTSYS;

	if (!cam->present) {
		up(&cam->busy_lock);
		return -ENOTTY;
	}

	switch (ioctl_nr) {
	case VIDIOCGCAP:	/* query capabilites */
		retval = ioctl_cap_query(arg, cam);
		break;

	case VIDIOCGCHAN:	/* get video source - we are a camera, nothing else */
		retval = ioctl_get_channel(arg);
		break;
	case VIDIOCSCHAN:	/* set video source - we are a camera, nothing else */
		retval = ioctl_set_channel(arg);
		break;
	case VIDIOCGPICT:	/* image properties */
		if (copy_to_user
		    (arg, &cam->vp, sizeof(struct video_picture)))
			retval = -EFAULT;
		break;
	case VIDIOCSPICT:
		retval = ioctl_set_image_prop(arg, cam);
		break;
	case VIDIOCGWIN:	/* get/set capture window */
		if (copy_to_user(arg, &cam->vw, sizeof(struct video_window)))
			retval = -EFAULT;
		break;
	case VIDIOCSWIN:
		retval = ioctl_set_window_size(arg, cam);
		break;
	case VIDIOCGMBUF:	/* mmap interface */
		retval = ioctl_get_mbuf(arg, cam);
		break;
	case VIDIOCMCAPTURE:
		retval = ioctl_mcapture(arg, cam);
		break;
	case VIDIOCSYNC:
		up(&cam->busy_lock);
		return ioctl_sync(arg, cam);

		/* pointless to implement overlay with this camera */
	case VIDIOCCAPTURE:
	case VIDIOCGFBUF:
	case VIDIOCSFBUF:
	case VIDIOCKEY:
		retval = -EINVAL;
		break;

		/* tuner interface - we have none */
	case VIDIOCGTUNER:
	case VIDIOCSTUNER:
	case VIDIOCGFREQ:
	case VIDIOCSFREQ:
		retval = -EINVAL;
		break;

		/* audio interface - we have none */
	case VIDIOCGAUDIO:
	case VIDIOCSAUDIO:
		retval = -EINVAL;
		break;

		/* MJPEG API extensions for the Video4Linux API */
	case MJPIOC_G_PARAMS:
		retval = ioctl_mjpeg_get_params(arg, cam);
		break;
	case MJPIOC_S_PARAMS:
		retval = ioctl_mjpeg_set_params(arg, cam);
		break;
	case MJPIOC_REQBUFS:
		retval = ioctl_mjpeg_reqbufs(arg, cam);
		break;
	case MJPIOC_QBUF_CAPT:
		retval = ioctl_mjpeg_capture(arg, cam);
		break;
	case MJPIOC_QBUF_PLAY:
		/* We can't support uncompressed video */
		retval = -EINVAL;
		break;
	case MJPIOC_SYNC:
		retval = ioctl_mjpeg_sync(arg, cam);
		break;
	case MJPIOC_G_STATUS:
		retval = ioctl_mjpeg_status(arg, cam);
		break;
	
		/* CPIA2 extension to Video4Linux API */
	case CPIA2_IOC_SET_FRAMERATE:
		retval = ioctl_set_framerate(arg, cam);
		break;
	case CPIA2_IOC_GET_FRAMERATE:
		retval = ioctl_get_framerate(arg, cam);
		break;
	case CPIA2_IOC_SET_USB_ALT:
		retval = ioctl_set_usb_alternate(arg, cam);
		break;
	case CPIA2_IOC_GET_USB_ALT:
		retval = ioctl_get_usb_alternate(arg, cam);
		break;
	case CPIA2_IOC_SET_FLICKER_MODE:
		retval = ioctl_set_flicker_mode(arg, cam);
		break;
	case CPIA2_IOC_SET_FLIP:
		retval = ioctl_set_user_effects(arg,
						cam,
						CPIA2_PROPERTY_FLIP);
		break;
	case CPIA2_IOC_SET_MIRROR:
	        retval = ioctl_set_user_effects(arg,
						cam,
						CPIA2_PROPERTY_MIRROR);
		break;
	case CPIA2_IOC_GET_CAM_STATE:
	        retval = ioctl_get_cam_state(arg, cam);
		break;
	case CPIA2_IOC_SET_TARGET_KB:
		retval = ioctl_set_target_kb(arg, cam);
		break;
	case CPIA2_IOC_GET_TARGET_KB:
		retval = ioctl_get_target_kb(arg, cam);
		break;
	default:
		retval = -ENOTTY;
		break;
	}

	up(&cam->busy_lock);
	return retval;
}

/******************************************************************************
 *
 *  ioctl_cap_query
 *
 *****************************************************************************/
static int ioctl_cap_query(void *arg, struct camera_data *cam)
{
	struct video_capability vc;
	int retval = 0;

	strcpy(vc.name, "CPiA2 Camera");
	vc.type = VID_TYPE_CAPTURE | VID_TYPE_MJPEG_ENCODER;
	vc.channels = 1;
	vc.audios = 0;
	vc.minwidth = 48;	/* VIDEOSIZE_48_48 */
	vc.minheight = 48;
	switch (cam->params.version.sensor_flags) {
	case CPIA2_VP_SENSOR_FLAGS_500:
		vc.maxwidth = STV_IMAGE_VGA_COLS;
		vc.maxheight = STV_IMAGE_VGA_ROWS;
		break;
	case CPIA2_VP_SENSOR_FLAGS_410:
		vc.maxwidth = STV_IMAGE_CIF_COLS;
		vc.maxheight = STV_IMAGE_CIF_ROWS;
		break;
	default:
		return -EINVAL;
	}

	if (copy_to_user(arg, &vc, sizeof(vc)))
		retval = -EFAULT;

	return retval;
}

/******************************************************************************
 *
 *  ioctl_get_channel
 *
 *****************************************************************************/
static int ioctl_get_channel(void *arg)
{
	int retval = 0;
	struct video_channel v;

	if (copy_from_user(&v, arg, sizeof(v)))
		return -EFAULT;

	if (v.channel != 0)
		return -EINVAL;

	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;

	return retval;
}

/******************************************************************************
 *
 *  ioctl_set_channel
 *
 *****************************************************************************/
static int ioctl_set_channel(void *arg)
{
	struct video_channel v;
	int retval = 0;

	if (copy_from_user(&v, arg, sizeof(v)))
		retval = -EFAULT;

	if (retval == 0 && v.channel != 0)
		retval = -EINVAL;

	return retval;
}

/******************************************************************************
 *
 *  ioctl_set_image_prop
 *
 *****************************************************************************/
static int ioctl_set_image_prop(void *arg, struct camera_data *cam)
{
	struct video_picture vp;
	int retval = 0;

	/* copy_from_user */
	if (copy_from_user(&vp, arg, sizeof(vp))) {
		return -EFAULT;
	}
	
	if(down_interruptible(&cam->param_lock))
		return -ERESTARTSYS;

	/* brightness, color, contrast need no check 0-65535 */
	memcpy(&cam->vp, &vp, sizeof(vp));

	/* update cam->params.colorParams */
	cam->params.color_params.brightness = vp.brightness / 256;
	cam->params.color_params.saturation = vp.colour / 256;
	cam->params.color_params.contrast = vp.contrast / 256;

	DBG("Requested params: bright 0x%X, sat 0x%X, contrast 0x%X\n", 
	    cam->params.color_params.brightness,
	    cam->params.color_params.saturation,
	    cam->params.color_params.contrast);

	cpia2_set_color_params(cam);
	
	up(&cam->param_lock);

	return retval;
}


/******************************************************************************
 *
 *  ioctl_set_window_size
 *
 *****************************************************************************/
static int ioctl_set_window_size(void *arg, struct camera_data *cam)
{
	/* copy_from_user, check validity, copy to internal structure */
	struct video_window vw;

	if (copy_from_user(&vw, arg, sizeof(vw)))
		return -EFAULT;

	if (vw.clipcount != 0)	/* clipping not supported */
		return -EINVAL;

	if (vw.clips != NULL)	/* clipping not supported */
		return -EINVAL;

	/* we set the video window to something smaller or equal to what
	 * is requested by the user???
	 */
	if(down_interruptible(&cam->param_lock))
		return -ERESTARTSYS;
	
	DBG("Requested width = %d, height = %d\n", vw.width, vw.height);
	if (vw.width != cam->vw.width || vw.height != cam->vw.height) {
		cam->vw.width = vw.width;
		cam->vw.height = vw.height;
		cam->params.roi.width = vw.width;
		cam->params.roi.height = vw.height;
		cpia2_set_format(cam);
	}

	up(&cam->param_lock);
	return 0;
}

/******************************************************************************
 *
 *  ioctl_get_mbuf
 *
 *****************************************************************************/
static int ioctl_get_mbuf(void *arg, struct camera_data *cam)
{
	struct video_mbuf vm;
	int i;

	memset(&vm, 0, sizeof(vm));
	vm.size = cam->frame_size*cam->num_frames;
	vm.frames = cam->num_frames;
	for (i = 0; i < cam->num_frames; i++)
		vm.offsets[i] = cam->frame_size * i;

	if (copy_to_user((void *)arg, (void *)&vm, sizeof(vm)))
		return -EFAULT;

	return 0;
}

/******************************************************************************
 *
 *  ioctl_mcapture
 *
 *****************************************************************************/
static int ioctl_mcapture(void *arg, struct camera_data *cam)
{
	struct video_mmap vm;
	int video_size;

	if (copy_from_user(&vm, arg, sizeof(vm)))
		return -EFAULT;

	if (vm.frame < 0 || vm.frame >= cam->num_frames)
		return -EINVAL;

	/* set video size */
	video_size = cpia2_match_video_size(vm.width, vm.height);
	if (cam->video_size < 0) {
		return -EINVAL;
	}

	if (video_size != cam->video_size) {
		cam->video_size = video_size;
		cam->params.roi.width = vm.width;
		cam->params.roi.height = vm.height;
		cpia2_set_format(cam);
	}

	if(cam->buffers[vm.frame].status == FRAME_READY)
		cam->buffers[vm.frame].status = FRAME_EMPTY;
	
	if(cpia2_usb_stream_start(cam,cam->params.camera_state.stream_mode) < 0)
		return -EIO;
	
	return 0;
}

/******************************************************************************
 *
 *  ioctl_sync
 *
 *****************************************************************************/
static int ioctl_sync(void *arg, struct camera_data *cam)
{
	int frame;

	if (copy_from_user(&frame, arg, sizeof(int)))
		return -EFAULT;

	if (frame < 0 || frame >= cam->num_frames)
		return -EINVAL;

	wait_event_interruptible(cam->wq_stream,
	                         !cam->present ||
	                         cam->buffers[frame].status == FRAME_READY);
	if (signal_pending(current))
		return -ERESTARTSYS;
	if(!cam->present)
		return -ENOTTY;
	return 0;
}

/******************************************************************************
 *
 *  ioctl_mjpeg_reqbufs
 *
 *  NOTE: This is a request, I'm just ignoring what the user wants.
 *
 *****************************************************************************/
static int ioctl_mjpeg_reqbufs(void *arg, struct camera_data *cam)
{
	int retval = 0;
	struct mjpeg_requestbuffers reqbufs;
	
	reqbufs.count = cam->num_frames;
	reqbufs.size = cam->frame_size;
	
	if(copy_to_user(arg, (void *)&reqbufs, sizeof(reqbufs)))
		return -EFAULT;

	return retval;
}

/******************************************************************************
 *
 *  ioctl_mjpeg_sync
 *
 *****************************************************************************/
static int ioctl_mjpeg_sync(void *arg, struct camera_data *cam)
{
	int retval = 0;
	struct framebuf *buf=cam->curbuff;
	struct mjpeg_sync sync;

	wait_event_interruptible(cam->wq_stream,
	                         !cam->present ||
	                         (buf=cam->curbuff)->status == FRAME_READY);
	if (signal_pending(current))
		return -ERESTARTSYS;
	if(!cam->present)
		return -ENOTTY;
	
	sync.frame = buf->num;
	sync.length = buf->length;
	sync.seq = buf->seq;
	memcpy(&sync.timestamp, &buf->timestamp, sizeof(sync.timestamp));

	if (copy_to_user(arg, (void *) &sync, sizeof(sync)))
		return -EFAULT;

	return retval;
}

/******************************************************************************
 *
 *  ioctl_mjpeg_status
 *
 *****************************************************************************/
static int ioctl_mjpeg_status(void *arg, struct camera_data *cam)
{
	int retval = 0;
	struct mjpeg_status status;
	
	if (copy_from_user(&status, arg, sizeof(status)))
		return -EFAULT;

	if(status.input == 0) {
		status.signal = 1;
		status.color = 1;
		/* This protocol wasn't designed for cameras, fake NTSC */
		status.norm = VIDEO_MODE_NTSC;
	} else {
		status.signal = 0;
		status.color = 0;
		status.norm = 0;
	}
	
	if (copy_to_user(arg, (void *) &status, sizeof(status)))
		return -EFAULT;
	
	return retval;
}

/******************************************************************************
 *
 *  ioctl_mjpeg_get_params
 *
 *****************************************************************************/
static int ioctl_mjpeg_get_params(void *arg, struct camera_data *cam)
{
	int retval = 0;
	struct mjpeg_params params;
	
	memset((void *)&params, 0, sizeof(params));
	
	params.major_version = CPIA2_MAJ_VER;
	params.minor_version = CPIA2_MIN_VER;
	params.input = 0;
	params.norm = VIDEO_MODE_NTSC;
	params.decimation = 1;
	params.HorDcm = 1;
	params.VerDcm = 1;
	params.TmpDcm = 1;
	params.field_per_buff = 1;
	params.img_x = 0;
	params.img_y = 0;
	params.img_width = cam->params.roi.width;
	params.img_height = cam->params.roi.height;
	params.quality = 80; //XXXXX
	params.odd_even = 0;
	params.jpeg_markers = JPEG_MARKER_DHT|JPEG_MARKER_DQT|JPEG_MARKER_DRI;
	
	params.APPn = cam->APPn;
	params.APP_len = cam->APP_len;
	if(cam->APP_len > 0) {
		memcpy(params.APP_data, cam->APP_data, cam->APP_len);
		params.jpeg_markers |= JPEG_MARKER_APP;
	}
	
	params.COM_len = cam->COM_len;
	if(cam->COM_len > 0) {
		memcpy(params.COM_data, cam->COM_data, cam->COM_len);
		params.jpeg_markers |= JPEG_MARKER_COM;
	}
	
	params.VFIFO_FB = 0;
	
	if (copy_to_user(arg, (void *) &params, sizeof(params)))
		return -EFAULT;
	
	return retval;
}

/******************************************************************************
 *
 *  ioctl_mjpeg_set_params
 *
 *****************************************************************************/
static int ioctl_mjpeg_set_params(void *arg, struct camera_data *cam)
{
	struct mjpeg_params params;
	
	if (copy_from_user(&params, arg, sizeof(params)))
		return -EFAULT;
	
	if(params.APP_len != 0) {
		if(params.APP_len > 0 &&
		   params.APP_len <= sizeof(cam->APP_data) &&
		   params.APPn >= 0 && params.APPn <= 15) {
			cam->APPn = params.APPn;
			cam->APP_len = params.APP_len;
			memcpy(cam->APP_data, params.APP_data, params.APP_len);
		} else {
			LOG("Bad APPn Params n=%d len=%d\n", params.APPn, params.APP_len);
			return -EINVAL;
		}
	} else {
		cam->APP_len = 0;
	}

	if(params.COM_len != 0) {
		if(params.COM_len > 0 &&
		   params.COM_len <= sizeof(cam->COM_data)) {
			cam->COM_len = params.COM_len;
			memcpy(cam->COM_data, params.COM_data, params.COM_len);
		} else {
			LOG("Bad COM_len=%d\n", params.COM_len);
			return -EINVAL;
		}
	}

	return ioctl_mjpeg_get_params(arg, cam);
}

/******************************************************************************
 *
 *  ioctl_mjpeg_capture
 *
 *****************************************************************************/
static int ioctl_mjpeg_capture(void *arg, struct camera_data *cam)
{
	int frame;

	if (copy_from_user(&frame, arg, sizeof(frame)))
		return -EFAULT;

	if (frame < 0 || frame >= cam->num_frames)
		return -EINVAL;

	if(cam->buffers[frame].status == FRAME_READY)
		cam->buffers[frame].status = FRAME_EMPTY;
	
	if(cpia2_usb_stream_start(cam,cam->params.camera_state.stream_mode) < 0)
		return -EIO;
	
	return 0;
}

/******************************************************************************
 *
 *  ioctl_set_user_effects
 *
 *****************************************************************************/
static int ioctl_set_user_effects(void *arg,
				  struct camera_data *cam,
				  int property)
{
	int prop_val;

	if (copy_from_user((void *)&prop_val, arg, sizeof(int)))
		return -EFAULT;

	if (property == CPIA2_PROPERTY_FLIP) {
	        cam->state.flip_on = prop_val;
		cpia2_set_property_flip(cam, prop_val);
	}
	else if (property == CPIA2_PROPERTY_MIRROR) {
	        cam->state.mirror_on = prop_val;
		cpia2_set_property_mirror(cam, prop_val);
	}
	else
	        return -EINVAL;
	return 0;
}

/******************************************************************************
 *
 *  ioctl_set_usb_alternate
 *
 *****************************************************************************/
static int ioctl_set_usb_alternate(void *arg, struct camera_data *cam)
{
	int alt_val, err;

	if (copy_from_user((void *)&alt_val, arg, sizeof(int)))
		return -EFAULT;

	if (alt_val < USBIF_ISO_1 || alt_val > USBIF_ISO_6)
		return -EINVAL;

	err = cpia2_usb_set_alternate(cam, alt_val);
	if(err == 0) {
		cam->params.camera_state.stream_mode = alt_val;
	}
	return err;
}

/******************************************************************************
 *
 *  ioctl_get_usb_alternate
 *
 *****************************************************************************/
static int ioctl_get_usb_alternate(void *arg, struct camera_data *cam)
{
	int alt_val;
	int retval = 0;
	
	alt_val = cam->params.camera_state.stream_mode;
	if (copy_to_user(arg, &alt_val, sizeof(int)))
		retval = -EINVAL;
	
	return retval;
}

/******************************************************************************
 *
 *  ioctl_set_framerate
 *
 *****************************************************************************/
static int ioctl_set_framerate(void *arg, struct camera_data *cam)
{
        int framerate;
	int retval = 0;

	if (copy_from_user((void *)&framerate, arg, sizeof(int))) {
                retval = -EINVAL;
	} else {
		retval = cpia2_set_fps(cam, framerate);
	}

	if(retval == 0)
		cam->state.current_framerate = framerate;

	return retval;
}

/******************************************************************************
 *
 *  ioctl_get_framerate
 *
 *****************************************************************************/
static int ioctl_get_framerate(void *arg, struct camera_data *cam)
{
	if (copy_to_user(arg, &cam->state.current_framerate, sizeof(int)))
		return -EINVAL;

	return 0;
}


/******************************************************************************
 *
 *  ioctl_get_cam_state
 *
 *****************************************************************************/
static int ioctl_get_cam_state(void *arg, struct camera_data *cam)
{
	int retval = 0;
        if (copy_to_user(arg, &cam->state,
			 sizeof(struct cpia2_cam_state))) {
	   retval = -EFAULT;
	}

	return retval;
}

/******************************************************************************
 *
 *  ioctl_set_flicker_mode
 *
 *****************************************************************************/
static int ioctl_set_flicker_mode(void *arg, struct camera_data *cam)
{
	int mode;
	int retval;
	
	if(copy_from_user(&mode, arg, sizeof(mode)))
		retval = -EFAULT;
	else
		retval = cpia2_set_flicker_mode(cam, mode);
	
	return retval;
}

/******************************************************************************
 *
 *  ioctl_set_target_kb
 *
 *  Requires camera reset
 *****************************************************************************/

static int ioctl_set_target_kb(void *arg, struct camera_data *cam)
{
	int target_val;

	if (copy_from_user((void *)&target_val, arg, sizeof(int)))
		return -EFAULT;
	if (target_val > 0xFF || target_val < 0)
		return -EINVAL;


	if(down_interruptible(&cam->param_lock))
		return -ERESTARTSYS;
	
	DBG("Requested target_kb = %d\n", target_val);
	if (target_val != cam->params.vc_params.target_kb) {

		cam->params.vc_params.target_kb = target_val;
		cpia2_set_target_kb(cam);
	}

	up(&cam->param_lock);
	return 0;
}

/******************************************************************************
 *
 *  ioctl_get_target_kb
 *
 *****************************************************************************/

static int ioctl_get_target_kb(void *arg, struct camera_data *cam)
{
	int target_val;
	int retval = 0;
	
	target_val = cam->params.vc_params.target_kb;
	if (copy_to_user(arg, &target_val, sizeof(int)))
		retval = -EINVAL;
	
	return retval;
}

/******************************************************************************
 *
 *  cpia2_mmap
 *
 *****************************************************************************/
static int cpia2_mmap(struct video_device *dev,
		      const char *adr, unsigned long size)
{
	struct camera_data *cam = dev->priv;
	return cpia2_remap_buffer(cam, adr, size);
}

/******************************************************************************
 *
 *  cpia2_VideoInit
 *
 *****************************************************************************/
int cpia2_video_init(struct video_device *vdev)
{
	cpia2_proc_create_cam(vdev->priv);
	return 0;
}

/******************************************************************************
 *
 *  reset_camera_struct_v4l
 *
 *  Sets all values to the defaults
 *****************************************************************************/
static void reset_camera_struct_v4l(struct camera_data *cam)
{
	/***
	 * Fill in the v4l structures.  video_cap is filled in inside the VIDIOCCAP
	 * Ioctl.  Here, just do the window and picture stucts.
	 ***/
	cam->vp.palette = (u16) VIDEO_PALETTE_RGB24;	/* Is this right? */
	cam->vp.brightness = (u16) cam->params.color_params.brightness * 256;
	cam->vp.colour = (u16) cam->params.color_params.saturation * 256;
	cam->vp.contrast = (u16) cam->params.color_params.contrast * 256;

	cam->vw.x = 0;
	cam->vw.y = 0;
	cam->vw.width = cam->params.roi.width;
	cam->vw.height = cam->params.roi.height;
	cam->vw.flags = 0;
	cam->vw.clipcount = 0;
	
	cam->frame_size = buffer_size;
	cam->num_frames = num_buffers;

	/* FlickerModes */
	cam->params.flicker_control.flicker_mode_req = flicker_mode;
	cam->params.flicker_control.mains_frequency = flicker_freq;
	if (flicker_mode == NEVER_FLICKER) {
	        cam->state.flicker_mode = NEVER_FLICKER;
	} else {
  	        cam->state.flicker_mode = flicker_freq;
	}

	/* streamMode */
	cam->params.camera_state.stream_mode = alternate;
	cam->state.current_alternate = alternate;

	return;
}

/******************************************************************************
 *
 *  cpia2_register_camera
 *
 *****************************************************************************/
int cpia2_register_camera(struct camera_data *cam)
{
	memcpy(&cam->vdev, &cpia2_template, sizeof(cpia2_template));
	cam->vdev.priv = cam;

	reset_camera_struct_v4l(cam);
	
	/* register v4l device */
#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,4,5))
	if (video_register_device(&cam->vdev, VFL_TYPE_GRABBER) == -1)
#else
	if (video_register_device
	    (&cam->vdev, VFL_TYPE_GRABBER, video_nr) == -1)
#endif
	{
		ERR("video_register_device failed\n");
		return -ENODEV;
	}

	return 0;
}

/******************************************************************************
 *
 *  cpia2_unregister_camera
 *
 *****************************************************************************/
void cpia2_unregister_camera(struct camera_data *cam)
{
	if (!cam->open_count) {
		video_unregister_device(&cam->vdev);
	} else {
		LOG("/dev/video%d removed while open, "
		    "deferring video_unregister_device\n",
		    cam->vdev.minor);
	}

	cpia2_proc_destroy_cam(cam);
}

/******************************************************************************
 *
 *  check_parameters
 *
 *  Make sure that all user-supplied parameters are sensible
 *****************************************************************************/
static void __init check_parameters(void)
{
	if(buffer_size < PAGE_SIZE) {
		buffer_size = PAGE_SIZE;
		LOG("buffer_size too small, setting to %d\n", buffer_size);
	} else if(buffer_size > 1024*1024) {
		/* arbitrary upper limiit */
		buffer_size = 1024*1024;
		LOG("buffer_size ridiculously large, setting to %d\n",
		    buffer_size);
	} else {
		buffer_size += PAGE_SIZE-1;
		buffer_size &= ~(PAGE_SIZE-1);
	}
	
	if(num_buffers < 1) {
		num_buffers = 1;
		LOG("num_buffers too small, setting to %d\n", num_buffers);
	} else if(num_buffers > VIDEO_MAX_FRAME) {
		num_buffers = VIDEO_MAX_FRAME;
		LOG("num_buffers too large, setting to %d\n", num_buffers);
	}
	
	if(alternate < USBIF_ISO_1 || alternate > USBIF_ISO_6) {
		alternate = DEFAULT_ALT;
		LOG("alternate specified is invalid, using %d\n", alternate);
	}

	if (flicker_mode != NEVER_FLICKER || flicker_mode != ANTI_FLICKER_ON) {
	        flicker_mode = NEVER_FLICKER;
		LOG("Flicker mode specified is invalid, using %d\n",
		    flicker_mode);
	}
	
	if (flicker_freq != FLICKER_50 || flicker_freq != FLICKER_60) {
	        flicker_freq = FLICKER_60;
		LOG("Flicker mode specified is invalid, using %d\n",
		    flicker_freq);
	}

#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,5))	
	if(video_nr < -1 || video_nr > 64) {
		video_nr = -1;
		LOG("invalid video_nr specified, must be -1 to 64\n");
	}
#endif
	DBG("Using %d buffers, each %d bytes, alternate=%d\n",
	    num_buffers, buffer_size, alternate);
}


/************   Module Stuff ***************/

#ifdef MODULE
/******************************************************************************
 *
 * init_module
 *
 *****************************************************************************/
int __init init_module(void)
{
	EXPORT_NO_SYMBOLS;
	LOG("%s v%d.%d.%d\n",
	    ABOUT, CPIA2_MAJ_VER, CPIA2_MIN_VER, CPIA2_PATCH_VER);
	check_parameters();
	cpia2_proc_create();
	cpia2_usb_init();
	return 0;
}

/******************************************************************************
 *
 * cleanup_module
 *
 *****************************************************************************/
void __exit cleanup_module(void)
{
	cpia2_usb_cleanup();
	schedule_timeout(2 * HZ);
	cpia2_proc_destroy();
}

#else

/******************************************************************************
 *
 *  cpia2_init
 *
 *  Init the driver compiled in the kernel
 *****************************************************************************/
int __init cpia2_init(struct video_init *unused)
{
	LOG("%s v%d.%d.%d\n",
	    ABOUT, CPIA2_MAJ_VER, CPIA2_MIN_VER, CPIA2_PATCH_VER);
	check_parameters();
	cpia2_proc_create();
	cpia2_usb_init();
	return 0;
}

int __init cpia2_setup(char *str)
{
	while(str) {
		if(!strncmp(str, "buffer_size:", 12)) {
			buffer_size = simple_strtoul(str + 13, &str, 10);
		} else if(!strncmp(str, "num_buffers:", 12)) {
			num_buffers = simple_strtoul(str + 13, &str, 10);
		} else if(!strncmp(str, "alternate:", 10)) {
			alternate = simple_strtoul(str + 11, &str, 10);
		} else if(!strncmp(str, "video_nr:", 9)) {
			video_nr = simple_strtoul(str + 10, &str, 10);
		} else if(!strncmp(str, "flicker_freq:",13)) {
		   flicker_freq = simple_strtoul(str + 14, &str, 10);
		} else if(!strncmp(str, "flicker_mode:",13)) {
		   flicker_mode = simple_strtoul(str + 14, &str, 10);
		} else {
			++str;
		}
	}
	return 1;
}

__setup("cpia2=", cpia2_setup);

#endif
