lists.openwall.net   lists  /  announce  owl-users  owl-dev  john-users  john-dev  passwdqc-users  yescrypt  popa3d-users  /  oss-security  kernel-hardening  musl  sabotage  tlsify  passwords  /  crypt-dev  xvendor  /  Bugtraq  Full-Disclosure  linux-kernel  linux-netdev  linux-ext4  linux-hardening  linux-cve-announce  PHC 
Open Source and information security mailing list archives
 
Hash Suite: Windows password security audit tool. GUI, reports in PDF.
[<prev] [next>] [thread-next>] [day] [month] [year] [list]
Message-ID: <20080514205927.GA13134@kroah.com>
Date:	Wed, 14 May 2008 13:59:27 -0700
From:	Greg KH <greg@...ah.com>
To:	mchehab@...radead.org, v4l-dvb-maintainer@...uxtv.org
Cc:	linux-usb@...r.kernel.org, linux-kernel@...r.kernel.org,
	video4linux-list@...hat.com
Subject: [PATCH] USB: add Sensoray 2255 v4l driver

From: Dean Anderson <dean@...soray.com>

This driver adds support for the Sensoray 2255 devices.

It was primarily developed by Dean Anderson with only a little bit of
guidance and cleanup by Greg.

From: Dean Anderson <dean@...soray.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@...e.de>

---
 drivers/media/video/Kconfig    |    9 
 drivers/media/video/Makefile   |    1 
 drivers/media/video/s2255drv.c | 2719 +++++++++++++++++++++++++++++++++++++++++
 3 files changed, 2729 insertions(+)

--- a/drivers/media/video/Kconfig
+++ b/drivers/media/video/Kconfig
@@ -893,6 +893,15 @@ config USB_STKWEBCAM
 	  To compile this driver as a module, choose M here: the
 	  module will be called stkwebcam.
 
+config USB_S2255
+	tristate "USB Sensoray 2255 video capture device"
+	depends on VIDEO_V4L2
+	select VIDEOBUF_VMALLOC
+	default n
+	help
+	  Say Y here if you want support for the Sensoray 2255 USB device.
+	  This driver can be compiled as a module, called s2255drv.
+
 endif # V4L_USB_DRIVERS
 
 config SOC_CAMERA
--- a/drivers/media/video/Makefile
+++ b/drivers/media/video/Makefile
@@ -122,6 +122,7 @@ obj-$(CONFIG_USB_IBMCAM)        += usbvi
 obj-$(CONFIG_USB_KONICAWC)      += usbvideo/
 obj-$(CONFIG_USB_VICAM)         += usbvideo/
 obj-$(CONFIG_USB_QUICKCAM_MESSENGER)	+= usbvideo/
+obj-$(CONFIG_USB_S2255)		+= s2255drv.o
 
 obj-$(CONFIG_VIDEO_IVTV) += ivtv/
 obj-$(CONFIG_VIDEO_CX18) += cx18/
--- /dev/null
+++ b/drivers/media/video/s2255drv.c
@@ -0,0 +1,2719 @@
+/*
+ * Sensoray 2255 USB Video for Linux driver
+ *
+ * Copyright (C) 2007-2008 by Sensoray Company Inc.
+ *                            Dean Anderson
+ *
+ *      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, version 2.
+ *
+ * Some video buffer code based on vivi driver:
+ *
+ * TODO: Incorporate videodev2 frame rate(FR) enumeration
+ *       (currently experimental.)
+ *
+ *       2255 device supports 4 simultaneous channels.
+ *       The channels are not "crossbar" inputs, they are physically
+ *       attached to separate video decoders.
+ *       Because of USB2.0 bandwidth limitations. There is only a
+ *       certain amount of data which may be transferred at one time
+ *       Because FR control is not in V4L yet, we may want to
+ *       limit the cases:
+ *       1) full size, color mode YUYV or YUV422P:
+ *          2 video_devices allowed at full size.
+ *       2) full or half size Grey scale:
+ *          4 video_devices
+ *       3) half size, color mode YUYV or YUV422P
+ *          4 video_devices
+ */
+#include <linux/module.h>
+#include <linux/firmware.h>
+#include <linux/delay.h>
+#include <linux/errno.h>
+#include <linux/fs.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/mm.h>
+#include <linux/ioport.h>
+#include <linux/init.h>
+#include <linux/sched.h>
+#include <linux/pci.h>
+#include <linux/random.h>
+#include <linux/mutex.h>
+#include <linux/videodev2.h>
+#include <linux/version.h>
+#include <linux/dma-mapping.h>
+#include <linux/interrupt.h>
+#include <media/videobuf-vmalloc.h>
+#include <media/v4l2-common.h>
+#include <linux/kthread.h>
+#include <linux/highmem.h>
+#include <linux/freezer.h>
+#include <linux/vmalloc.h>
+#include <linux/usb.h>
+
+#define FIRMWARE_FILE_NAME "f2255usb.bin"
+
+#define DIR_IN			0
+#define DIR_OUT			1
+
+/* firmware query */
+#define VX_FW 			0x30
+
+#define MAX_CHANNELS		4
+#define FRAME_MARKER		0x2255DA4AL
+#define MAX_PIPE_USBBLOCK	(40*1024)
+#define DEFAULT_PIPE_USBBLOCK	(16*1024)
+#define MAX_CHANNELS		4
+#define MAX_PIPE_BUFFERS	1
+#define SYS_FRAMES		4
+/* maximum size is PAL full size plus room for the marker header(s) */
+#define SYS_FRAMES_MAXSIZE	(720*288*2*2 + 4096)
+#define DEF_USB_BLOCK		(4096)
+#define LINE_SZ_4CIFS_NTSC	640
+#define LINE_SZ_2CIFS_NTSC	640
+#define LINE_SZ_1CIFS_NTSC	320
+#define LINE_SZ_4CIFS_PAL	704
+#define LINE_SZ_2CIFS_PAL	704
+#define LINE_SZ_1CIFS_PAL	352
+#define NUM_LINES_4CIFS_NTSC	240
+#define NUM_LINES_2CIFS_NTSC	240
+#define NUM_LINES_1CIFS_NTSC	240
+#define NUM_LINES_4CIFS_PAL	288
+#define NUM_LINES_2CIFS_PAL	288
+#define NUM_LINES_1CIFS_PAL	288
+#define LINE_SZ_DEF		640
+#define NUM_LINES_DEF		240
+
+
+/* predefined settings */
+#define FORMAT_NTSC	1
+#define FORMAT_PAL	2
+
+#define SCALE_4CIFS	1	/* 640x480(NTSC) or 704x576(PAL) */
+#define SCALE_2CIFS	2	/* 640x240(NTSC) or 704x288(PAL) */
+#define SCALE_1CIFS	3	/* 320x240(NTSC) or 352x288(PAL) */
+
+#define COLOR_YUVPL	1	/* YUV planar */
+#define COLOR_YUVPK	2	/* YUV packed */
+#define COLOR_RGB	3	/* RGB */
+#define COLOR_Y8	4	/* monochrome */
+
+/* frame decimation. Not implemented by V4L yet(experimental in V4L) */
+#define FDEC_1		1	/* capture every frame. default */
+#define FDEC_2		2	/* capture every 2nd frame */
+#define FDEC_3		3	/* capture every 3rd frame */
+#define FDEC_5		5	/* capture every 5th frame */
+
+/*-------------------------------------------------------
+ * Default mode parameters.
+ *-------------------------------------------------------*/
+#define DEF_SCALE	SCALE_4CIFS
+#define DEF_COLOR	COLOR_YUVPL
+#define DEF_FDEC	FDEC_1
+#define DEF_BRIGHT	0
+#define DEF_CONTRAST	0x5c
+#define DEF_SATURATION	0x80
+#define DEF_HUE		0
+
+/* usb config commands */
+#define IN_DATA_TOKEN	0x2255c0de
+#define CMD_2255	0xc2255000
+#define CMD_SET_MODE	(CMD_2255 | 0x10)
+#define CMD_START	(CMD_2255 | 0x20)
+#define CMD_STOP	(CMD_2255 | 0x30)
+#define CMD_STATUS	(CMD_2255 | 0x40)
+
+struct mode2255i {
+	u32 format;	/* input video format (NTSC, PAL) */
+	u32 scale;	/* output video scale */
+	u32 color;	/* output video color format */
+	u32 fdec;	/* frame decimation */
+	u32 bright;	/* brightness */
+	u32 contrast;	/* contrast */
+	u32 saturation;	/* saturation */
+	u32 hue;	/* hue (NTSC only)*/
+	u32 single;	/* capture 1 frame at a time (!=0), continuously (==0)*/
+	u32 usb_block;	/* block size. should be 4096 of DEF_USB_BLOCK */
+	u32 restart;	/* if DSP requires restart */
+};
+
+/* frame structure */
+#define FRAME_STATE_UNUSED	0
+#define FRAME_STATE_FILLING	1
+#define FRAME_STATE_FULL	2
+
+
+struct framei {
+	unsigned long size;
+
+	unsigned long ulState;	/* ulState ==0 unused, 1 being filled, 2 full */
+	void *lpvbits;		/* image data */
+	unsigned long cur_size;	/* current data copied to it */
+};
+
+/* image buffer structure */
+struct bufferi {
+	unsigned long dwFrames;			/* number of frames in buffer */
+	struct framei frame[SYS_FRAMES];	/* array of FRAME structures */
+};
+
+#define DEF_MODEI_NTSC_CONT	FORMAT_NTSC, DEF_SCALE, DEF_COLOR, \
+		DEF_FDEC, DEF_BRIGHT, DEF_CONTRAST, DEF_SATURATION, \
+		DEF_HUE, 0, DEF_USB_BLOCK, 0
+
+#define DEF_MODEI_PAL_CONT	FORMAT_PAL, DEF_SCALE, DEF_COLOR, DEF_FDEC,\
+		DEF_BRIGHT, DEF_CONTRAST, DEF_SATURATION, DEF_HUE, 0, \
+		DEF_USB_BLOCK, 0
+
+#define DEF_MODEI_NTSC_SING	FORMAT_NTSC, DEF_SCALE, DEF_COLOR, DEF_FDEC,\
+		DEF_BRIGHT, DEF_CONTRAST, DEF_SATURATION, DEF_HUE, 1,\
+		DEF_USB_BLOCK, 0
+
+#define DEF_MODEI_PAL_SING	FORMAT_PAL, DEF_SCALE, DEF_COLOR, DEF_FDEC, \
+		DEF_BRIGHT, DEF_CONTRAST, DEF_SATURATION, DEF_HUE, 1,\
+		DEF_USB_BLOCK, 0
+
+struct s2255_dmaqueue {
+	struct list_head	active;
+	struct list_head	queued;
+	struct timer_list	timeout;
+	/* thread for acquisition */
+	struct task_struct	*kthread;
+	int			frame;
+	struct s2255_dev	*dev;
+	int			channel;
+};
+
+/* for firmware loading */
+#define FWSTATE_NOTLOADED	0
+#define FWSTATE_SUCCESS		1
+#define FWSTATE_FAILED		2
+
+struct complete_data {
+	int		fw_loaded;
+	int		fw_size;
+	struct urb	*fw_urb;
+	int		fw_state;
+	void		*pfw_data;
+	const struct firmware *fw;
+};
+
+struct s2255_pipeinfo {
+	u32 max_transfer_size;
+	u32 cur_transfer_size;
+	u8 *transfer_buffer;
+	u32 transfer_flags;;
+	u32 state;
+	u32 prev_state;
+	u32 urb_size;
+	void *stream_urb;
+	void *dev;	/* back pointer to s2255_dev struct*/
+	u32 err_count;
+	u32 buf_index;
+	u32 idx;
+	u32 priority_set;
+};
+
+struct s2255_fmt; /*forward declaration */
+
+struct s2255_dev {
+	int			frames;
+	int			users[MAX_CHANNELS];
+	struct mutex		lock;
+	int			resources[MAX_CHANNELS];
+	struct usb_device	*udev;
+	struct usb_interface	*interface;
+	u8			read_endpoint;
+
+	struct s2255_dmaqueue	vidq[MAX_CHANNELS];
+	struct video_device	*vdev[MAX_CHANNELS];
+	struct list_head	s2255_devlist;
+	struct timer_list	timer;
+	struct complete_data	*fw_data;
+	int			board_num;
+	int			is_open;
+	struct s2255_pipeinfo	pipes[MAX_PIPE_BUFFERS];
+	struct bufferi		buffer[MAX_CHANNELS];
+	struct mode2255i	mode[MAX_CHANNELS];
+	const struct s2255_fmt	*cur_fmt[MAX_CHANNELS];
+	int			cur_frame[MAX_CHANNELS];
+	int			last_frame[MAX_CHANNELS];
+	u32			cc;	/* current channel */
+	int			b_acquire[MAX_CHANNELS];
+	unsigned long		req_image_size[MAX_CHANNELS];
+	int			bad_payload[MAX_CHANNELS];
+	unsigned long		frame_count[MAX_CHANNELS];
+	int			frame_ready;
+	struct kref		kref;
+	spinlock_t              slock;
+};
+#define to_s2255_dev(d) container_of(d, struct s2255_dev, kref)
+
+struct s2255_fmt {
+	char *name;
+	u32 fourcc;
+	int depth;
+};
+
+/* buffer for one video frame */
+struct s2255_buffer {
+	/* common v4l buffer stuff -- must be first */
+	struct videobuf_buffer vb;
+	const struct s2255_fmt *fmt;
+	/* future use */
+	int reserved[32];
+};
+
+struct s2255_fh {
+	struct s2255_dev	*dev;
+	unsigned int		resources;
+	const struct s2255_fmt	*fmt;
+	unsigned int		width;
+	unsigned int		height;
+	struct videobuf_queue	vb_vidq;
+	enum v4l2_buf_type	type;
+	int			channel;
+	/* mode below is the desired mode.
+	   mode in s2255_dev is the current mode that was last set */
+	struct mode2255i	mode;
+};
+
+
+#define CUR_USB_FWVER	774	/* current cypress EEPROM firmware version */
+#define S2255_MAJOR_VERSION	1
+#define S2255_MINOR_VERSION	13
+#define S2255_RELEASE		0
+#define S2255_VERSION		KERNEL_VERSION(S2255_MAJOR_VERSION, \
+					       S2255_MINOR_VERSION, \
+					       S2255_RELEASE)
+
+/* vendor ids */
+#define USB_S2255_VENDOR_ID	0x1943
+#define USB_S2255_PRODUCT_ID	0x2255
+#define S2255_NORMS		(V4L2_STD_PAL_B | V4L2_STD_NTSC_M)
+/* frame prefix size (sent once every frame) */
+#define PREFIX_SIZE		512
+
+/* Because the channels were physically printed on the box in
+   reverse order than originally planned */
+static unsigned long G_chnmap[MAX_CHANNELS] = { 3, 2, 1, 0 };
+
+static LIST_HEAD(s2255_devlist);
+
+static int debug;
+static int *s2255_debug = &debug;
+
+static int s2255_start_readpipe(struct s2255_dev *dev);
+static void s2255_stop_readpipe(struct s2255_dev *dev);
+static int s2255_start_acquire(struct s2255_dev *dev, unsigned long chn);
+static int s2255_stop_acquire(struct s2255_dev *dev, unsigned long chn);
+static void s2255_fillbuff(struct s2255_dev *dev, struct s2255_buffer *buf,
+			   int chn);
+static int s2255_set_mode(struct s2255_dev *dev, unsigned long chn,
+			  struct mode2255i *mode);
+
+#define dprintk(level, fmt, arg...)					\
+	do {								\
+		if (*s2255_debug >= (level)) {				\
+			printk(KERN_DEBUG "s2255: " fmt, ##arg);	\
+		}							\
+	} while (0)
+
+
+static DEFINE_MUTEX(usb_s2255_open_mutex);
+static struct usb_driver s2255_driver;
+
+/* Declare static vars that will be used as parameters */
+static unsigned int vid_limit = 16;	/* Video memory limit, in Mb */
+
+/* start video number */
+static int video_nr = -1;	/* /dev/videoN, -1 for autodetect */
+
+module_param(debug, int, 0);
+MODULE_PARM_DESC(debug, "Debug level(0-100) default 0");
+module_param(vid_limit, int, 0);
+MODULE_PARM_DESC(vid_limit, "video memory limit(Mb)");
+module_param(video_nr, int, 0);
+MODULE_PARM_DESC(video_nr, "start video minor(-1 default autodetect)");
+
+/* USB device table */
+static struct usb_device_id s2255_table[] = {
+	{USB_DEVICE(USB_S2255_VENDOR_ID, USB_S2255_PRODUCT_ID)},
+	{ }			/* Terminating entry */
+};
+MODULE_DEVICE_TABLE(usb, s2255_table);
+
+
+#define BUFFER_TIMEOUT msecs_to_jiffies(400)
+
+/* supported controls */
+static struct v4l2_queryctrl s2255_qctrl[] = {
+	{
+	.id = V4L2_CID_BRIGHTNESS,
+	.type = V4L2_CTRL_TYPE_INTEGER,
+	.name = "Brightness",
+	.minimum = -127,
+	.maximum = 128,
+	.step = 1,
+	.default_value = 0,
+	.flags = 0,
+	},
+	{
+	.id = V4L2_CID_CONTRAST,
+	.type = V4L2_CTRL_TYPE_INTEGER,
+	.name = "Contrast",
+	.minimum = 0,
+	.maximum = 255,
+	.step = 0x1,
+	.default_value = DEF_CONTRAST,
+	.flags = 0,
+	},
+	{
+	.id = V4L2_CID_SATURATION,
+	.type = V4L2_CTRL_TYPE_INTEGER,
+	.name = "Saturation",
+	.minimum = 0,
+	.maximum = 255,
+	.step = 0x1,
+	.default_value = DEF_SATURATION,
+	.flags = 0,
+	},
+	{
+	.id = V4L2_CID_HUE,
+	.type = V4L2_CTRL_TYPE_INTEGER,
+	.name = "Hue",
+	.minimum = 0,
+	.maximum = 255,
+	.step = 0x1,
+	.default_value = DEF_HUE,
+	.flags = 0,
+	}
+};
+
+static int qctl_regs[ARRAY_SIZE(s2255_qctrl)];
+
+/* image formats.  Note RGB formats are software converted.
+ *  because the 2255 transfers in YUV for maximum USB efficiency
+ * in order to allow 2 full size color channels at full frame rate
+ */
+static const struct s2255_fmt formats[] = {
+	{
+	.name = "4:2:2, planar, YUV422P",
+	.fourcc = V4L2_PIX_FMT_YUV422P,
+	.depth = 16
+	},
+	{
+	.name = "4:2:2, packed, YUYV",
+	.fourcc = V4L2_PIX_FMT_YUYV,
+	.depth = 16
+	},
+	{
+	.name = "BGR24",
+	.fourcc = V4L2_PIX_FMT_BGR24,
+	.depth = 24
+	},
+	{
+	.name = "RGB24",
+	.fourcc = V4L2_PIX_FMT_RGB24,
+	.depth = 24
+	},
+	{
+	.name = "BGR32",
+	.fourcc = V4L2_PIX_FMT_BGR32,
+	.depth = 32
+	},
+	{
+	.name = "RGB32",
+	.fourcc = V4L2_PIX_FMT_RGB32,
+	.depth = 32
+	},
+	{
+	.name = "RGB565",
+	.fourcc = V4L2_PIX_FMT_RGB565,
+	.depth = 16
+	},
+	{
+	.name = "RGB565 big endian",
+	.fourcc = V4L2_PIX_FMT_RGB565X,
+	.depth = 16
+	},
+	{
+	.name = "8bpp GREY",
+	.fourcc = V4L2_PIX_FMT_GREY,
+	.depth = 8
+	},
+};
+
+static int norm_maxw(struct video_device *vdev)
+{
+	return (vdev->current_norm != V4L2_STD_PAL_B) ?
+	    LINE_SZ_4CIFS_NTSC : LINE_SZ_4CIFS_PAL;
+}
+
+static int norm_maxh(struct video_device *vdev)
+{
+	return (vdev->current_norm != V4L2_STD_PAL_B) ?
+	    (NUM_LINES_1CIFS_NTSC * 2) : (NUM_LINES_1CIFS_PAL * 2);
+}
+
+static int norm_minw(struct video_device *vdev)
+{
+	return (vdev->current_norm != V4L2_STD_PAL_B) ?
+	    LINE_SZ_1CIFS_NTSC : LINE_SZ_1CIFS_PAL;
+}
+
+static int norm_minh(struct video_device *vdev)
+{
+	return (vdev->current_norm != V4L2_STD_PAL_B) ?
+	    (NUM_LINES_1CIFS_NTSC) : (NUM_LINES_1CIFS_PAL);
+}
+
+/*
+ * convert from YUV(YCrCb) to RGB
+ * 65536 R = 76533(Y-16) + 104936 * (Cr-128)
+ * 65536 G = 76533(Y-16) - 53451(Cr-128) - 25703(Cb -128)
+ * 65536 B = 76533(Y-16) + 132677(Cb-128)
+ */
+static void YCrCb2RGB(int Y, int Cr, int Cb, unsigned char *pR,
+		      unsigned char *pG, unsigned char *pB)
+{
+	int R, G, B;
+
+	Y = Y - 16;
+	Cr = Cr - 128;
+	Cb = Cb - 128;
+
+	R = (76533 * Y + 104936 * Cr) >> 16;
+	G = ((76533 * Y) - (53451 * Cr) - (25703 * Cb)) >> 16;
+	B = ((76533 * Y) + (132677 * Cb)) >> 16;
+	/* even with proper conversion, some values still need clipping. */
+	if (R > 255)
+		R = 255;
+	if (G > 255)
+		G = 255;
+	if (B > 255)
+		B = 255;
+	if (R < 0)
+		R = 0;
+	if (G < 0)
+		G = 0;
+	if (B < 0)
+		B = 0;
+	*pR = R;
+	*pG = G;
+	*pB = B;
+	return;
+}
+
+/* converts 2255 planar format to yuyv */
+static void planar422p_to_yuy2(const unsigned char *in, unsigned char *out,
+			       int width, int height)
+{
+	unsigned char *pY;
+	unsigned char *pCb;
+	unsigned char *pCr;
+	unsigned long size = height * width;
+	unsigned int i;
+	pY = (unsigned char *)in;
+	pCr = (unsigned char *)in + height * width;
+	pCb = (unsigned char *)in + height * width + (height * width / 2);
+	for (i = 0; i < size * 2; i += 4) {
+		out[i] = *pY++;
+		out[i + 1] = *pCr++;
+		out[i + 2] = *pY++;
+		out[i + 3] = *pCb++;
+	}
+	return;
+}
+
+/*
+ * basic 422 planar to RGB24 or BGR24 software conversion.
+ * This is best done with MMX. Update to kernel function
+ * when image conversion functions added to kernel.
+ */
+static void planar422p_to_rgb24(const unsigned char *in,
+				unsigned char *out, int width,
+				int height, int rev_order)
+{
+	unsigned char *pY;
+	unsigned char *pYEND;
+	unsigned char *pCb;
+	unsigned char *pCr;
+	unsigned char Cr, Cb, Y, r, g, b;
+	unsigned long k = 0;
+	pY = (unsigned char *)in;
+	pCb = (unsigned char *)in + (height * width);
+	pCr = (unsigned char *)in + (height * width) + (height * width / 2);
+	pYEND = pCb;
+	while (pY < pYEND) {
+		Y = *pY++;
+		Cr = *pCr;
+		Cb = *pCb;
+		YCrCb2RGB(Y, Cr, Cb, &r, &g, &b);
+		out[k++] = !rev_order ? b : r;
+		out[k++] = g;
+		out[k++] = !rev_order ? r : b;
+		if (pY >= pYEND)
+			break;
+		Y = *pY++;
+		Cr = *pCr++;
+		Cb = *pCb++;
+		YCrCb2RGB(Y, Cr, Cb, &r, &g, &b);
+		out[k++] = !rev_order ? b : r;
+		out[k++] = g;
+		out[k++] = !rev_order ? r : b;
+	}
+	return;
+}
+
+static void planar422p_to_rgb32(const unsigned char *in, unsigned char *out,
+				int width, int height, int rev_order)
+{
+	unsigned char *pY;
+	unsigned char *pYEND;
+	unsigned char *pCb;
+	unsigned char *pCr;
+	unsigned char Cr, Cb, Y, r, g, b;
+	unsigned long k = 0;
+	pY = (unsigned char *)in;
+	pCb = (unsigned char *)in + (height * width);
+	pCr = (unsigned char *)in + (height * width) + (height * width / 2);
+	pYEND = pCb;
+	while (pY < pYEND) {
+		Y = *pY++;
+		Cr = *pCr;
+		Cb = *pCb;
+		YCrCb2RGB(Y, Cr, Cb, &r, &g, &b);
+		out[k++] = rev_order ? b : r;
+		out[k++] = g;
+		out[k++] = rev_order ? r : b;
+		out[k++] = 0;
+		if (pY >= pYEND)
+			break;
+		Y = *pY++;
+		Cr = *pCr++;
+		Cb = *pCb++;
+		YCrCb2RGB(Y, Cr, Cb, &r, &g, &b);
+		out[k++] = rev_order ? b : r;
+		out[k++] = g;
+		out[k++] = rev_order ? r : b;
+		out[k++] = 0;
+	}
+
+	return;
+}
+
+static void planar422p_to_rgb565(unsigned char const *in, unsigned char *out,
+				 int width, int height, int rev_order)
+{
+	unsigned char *pY;
+	unsigned char *pYEND;
+	unsigned char *pCb;
+	unsigned char *pCr;
+	unsigned char Cr, Cb, Y, r, g, b;
+	unsigned long k = 0;
+	unsigned short rgbbytes;
+	pY = (unsigned char *)in;
+	pCb = (unsigned char *)in + (height * width);
+	pCr = (unsigned char *)in + (height * width) + (height * width / 2);
+	pYEND = pCb;
+	while (pY < pYEND) {
+		Y = *pY++;
+		Cr = *pCr;
+		Cb = *pCb;
+		YCrCb2RGB(Y, Cr, Cb, &r, &g, &b);
+		r = r >> 3;
+		g = g >> 2;
+		b = b >> 3;
+		if (rev_order)
+			rgbbytes = b + (g << 5) + (r << (5 + 6));
+		else
+			rgbbytes = r + (g << 5) + (b << (5 + 6));
+		out[k++] = rgbbytes & 0xff;
+		out[k++] = (rgbbytes >> 8) & 0xff;
+		Y = *pY++;
+		Cr = *pCr++;
+		Cb = *pCb++;
+		YCrCb2RGB(Y, Cr, Cb, &r, &g, &b);
+		r = r >> 3;
+		g = g >> 2;
+		b = b >> 3;
+		if (rev_order)
+			rgbbytes = b + (g << 5) + (r << (5 + 6));
+		else
+			rgbbytes = r + (g << 5) + (b << (5 + 6));
+		out[k++] = rgbbytes & 0xff;
+		out[k++] = (rgbbytes >> 8) & 0xff;
+	}
+	return;
+}
+
+/* kickstarts the firmware loading. from probe
+ */
+static void s2255_timer(unsigned long user_data)
+{
+	struct complete_data *data = (struct complete_data *)user_data;
+	dprintk(100, "s2255 timer\n");
+	if (usb_submit_urb(data->fw_urb, GFP_ATOMIC) < 0) {
+		printk(KERN_ERR "s2255: can't submit urb\n");
+		if (data->fw) {
+			release_firmware(data->fw);
+			data->fw = NULL;
+		}
+		return;
+	}
+}
+
+/* this loads the firmware asynchronously.
+   Originally this was done synchroously in probe.
+   But it is better to load it asynchronously here than block
+   inside the probe function. Blocking inside probe affects boot time.
+   FW loading is triggered by the timer in the probe function
+*/
+static void s2255_fwchunk_complete(struct urb *urb)
+{
+	struct complete_data *data = urb->context;
+	struct usb_device *udev = urb->dev;
+	int len;
+	dprintk(100, "udev %p urb %p", udev, urb);
+
+	if (urb->status) {
+		dev_err(&udev->dev, "URB failed with status %d", urb->status);
+		return;
+	}
+	if (data->fw_urb == NULL) {
+		dev_err(&udev->dev, "early disconncect\n");
+		return;
+	}
+#define CHUNK_SIZE 512
+	/* all USB transfers must be done with continuous kernel memory.
+	   can't allocate more than 128k in current linux kernel, so
+	   upload the firmware in chunks
+	 */
+	if (data->fw_loaded < data->fw_size) {
+		len = (data->fw_loaded + CHUNK_SIZE) > data->fw_size ?
+		    data->fw_size % CHUNK_SIZE : CHUNK_SIZE;
+
+		if (len < CHUNK_SIZE)
+			memset(data->pfw_data, 0, CHUNK_SIZE);
+
+		dprintk(100, "completed len %d, loaded %d \n", len,
+			data->fw_loaded);
+
+		memcpy(data->pfw_data,
+		       (char *) data->fw->data + data->fw_loaded, len);
+		usb_fill_bulk_urb(data->fw_urb, udev, usb_sndbulkpipe(udev, 2),
+				  data->pfw_data, CHUNK_SIZE,
+				  s2255_fwchunk_complete, data);
+		if (usb_submit_urb(data->fw_urb, GFP_ATOMIC) < 0) {
+			dev_err(&udev->dev, "failed submit URB\n");
+			data->fw_state = FWSTATE_FAILED;
+			return;
+		}
+		data->fw_loaded += len;
+	} else {
+		data->fw_state = FWSTATE_SUCCESS;
+		dev_info(&udev->dev, "firmware loaded successfully\n");
+	}
+
+	dprintk(100, "2255 complete done\n");
+	return;
+
+}
+
+static int s2255_got_frame(struct s2255_dev *dev, int chn)
+{
+	struct s2255_dmaqueue *dma_q = &dev->vidq[chn];
+	struct s2255_buffer *buf;
+
+	dprintk(2, "wakeup: %p channel: %d\n", &dma_q, chn);
+	if (list_empty(&dma_q->active)) {
+		dprintk(1, "No active queue to serve\n");
+		return -1;
+	}
+	buf = list_entry(dma_q->active.next,
+			 struct s2255_buffer, vb.queue);
+
+	if (!waitqueue_active(&buf->vb.done)) {
+		/* no one active,
+		   add timer here as process may be suspended(ctrl-z).
+		   once it becomes active again, we want to recover.
+		   resetting the timer allows this.
+		*/
+		mod_timer(&dma_q->timeout, jiffies + BUFFER_TIMEOUT);
+		return -1;
+	}
+
+	do_gettimeofday(&buf->vb.ts);
+	dprintk(100, "[%p/%d] wakeup\n", buf, buf->vb.i);
+	mod_timer(&dma_q->timeout, jiffies + BUFFER_TIMEOUT);
+	s2255_fillbuff(dev, buf, dma_q->channel);
+	return 0;
+}
+
+
+static const struct s2255_fmt *format_by_fourcc(int fourcc)
+{
+	unsigned int i;
+
+	for (i = 0; i < ARRAY_SIZE(formats); i++) {
+		if (-1 == formats[i].fourcc)
+			continue;
+		if (formats[i].fourcc == fourcc)
+			return formats + i;
+	}
+	return NULL;
+}
+
+
+
+
+/* video buffer thread and vmalloc based partly on VIVI driver which is
+ *          Copyright (c) 2006 by
+ *                  Mauro Carvalho Chehab <mchehab--a.t--infradead.org>
+ *                  Ted Walther <ted--a.t--enumera.com>
+ *                  John Sokol <sokol--a.t--videotechnology.com>
+ *                  http://v4l.videotechnology.com/
+ *
+ */
+static void s2255_fillbuff(struct s2255_dev *dev, struct s2255_buffer *buf,
+			   int chn)
+{
+	int pos = 0;
+	struct timeval ts;
+	const char *tmpbuf;
+	char *vbuf = videobuf_to_vmalloc(&buf->vb);
+	unsigned long last_frame;
+	struct framei *frm;
+	last_frame = dev->last_frame[chn];
+	if ((last_frame != -1) && (vbuf != NULL)) {
+		frm = &dev->buffer[chn].frame[last_frame];
+		tmpbuf =
+		    (const char *)dev->buffer[chn].frame[last_frame].lpvbits;
+		if (buf->fmt->fourcc == V4L2_PIX_FMT_YUYV) {
+			planar422p_to_yuy2((const unsigned char *)tmpbuf,
+					   vbuf, buf->vb.width, buf->vb.height);
+		} else if (buf->fmt->fourcc == V4L2_PIX_FMT_GREY) {
+			memcpy(vbuf, tmpbuf, buf->vb.width * buf->vb.height);
+		} else if (buf->fmt->fourcc == V4L2_PIX_FMT_YUV422P) {
+			memcpy(vbuf, tmpbuf,
+			       buf->vb.width * buf->vb.height * 2);
+		} else if (buf->fmt->fourcc == V4L2_PIX_FMT_RGB24) {
+			planar422p_to_rgb24(tmpbuf, vbuf, buf->vb.width,
+					    buf->vb.height, 1);
+		} else if (buf->fmt->fourcc == V4L2_PIX_FMT_BGR24) {
+			planar422p_to_rgb24(tmpbuf, vbuf, buf->vb.width,
+					    buf->vb.height, 0);
+		} else if (buf->fmt->fourcc == V4L2_PIX_FMT_RGB32) {
+			planar422p_to_rgb32(tmpbuf, vbuf, buf->vb.width,
+					    buf->vb.height, 0);
+		} else if (buf->fmt->fourcc == V4L2_PIX_FMT_BGR32) {
+			planar422p_to_rgb32(tmpbuf, vbuf, buf->vb.width,
+					    buf->vb.height, 1);
+		} else if (buf->fmt->fourcc == V4L2_PIX_FMT_RGB565) {
+			planar422p_to_rgb565(tmpbuf, vbuf, buf->vb.width,
+					     buf->vb.height, 1);
+		} else if (buf->fmt->fourcc == V4L2_PIX_FMT_RGB565X) {
+			planar422p_to_rgb565(tmpbuf, vbuf, buf->vb.width,
+					     buf->vb.height, 0);
+		} else {
+			printk(KERN_DEBUG "s2255: unknown format?\n");
+		}
+		dev->last_frame[chn] = -1;
+		/* done with the frame, free it */
+		frm->ulState = 0;
+		dprintk(4, "freeing buffer\n");
+	} else {
+		printk(KERN_ERR "s2255: =======no frame\n");
+		return;
+
+	}
+	dprintk(2, "s2255fill at : Buffer 0x%08lx size= %d\n",
+		(unsigned long)vbuf, pos);
+	/* tell v4l buffer was filled */
+	buf->vb.state = VIDEOBUF_DONE;
+	buf->vb.field_count++;
+	do_gettimeofday(&ts);
+	buf->vb.ts = ts;
+	list_del(&buf->vb.queue);
+	wake_up(&buf->vb.done);
+}
+
+
+static int restart_video_queue(struct s2255_dmaqueue *dma_q)
+{
+	struct s2255_buffer *buf, *prev;
+	dprintk(1, "%s dma_q=0x%08lx chan %d\n", __func__,
+		(unsigned long)dma_q, dma_q->channel);
+	if (!list_empty(&dma_q->active)) {
+		buf =
+		    list_entry(dma_q->active.next, struct s2255_buffer,
+			       vb.queue);
+		dprintk(2, "restart_queue [%p/%d]: restart dma\n", buf,
+			buf->vb.i);
+		/* cancel all outstanding capture requests */
+		list_for_each_entry_safe(buf, prev, &dma_q->active, vb.queue) {
+			list_del(&buf->vb.queue);
+			buf->vb.state = VIDEOBUF_ERROR;
+			wake_up(&buf->vb.done);
+		}
+		mod_timer(&dma_q->timeout, jiffies + BUFFER_TIMEOUT);
+
+		return 0;
+	}
+
+	prev = NULL;
+	for (;;) {
+		if (list_empty(&dma_q->queued)) {
+			dprintk(1, "exiting nothing queued\n");
+			return 0;
+		}
+		buf =
+		    list_entry(dma_q->queued.next, struct s2255_buffer,
+			       vb.queue);
+		if (NULL == prev) {
+			list_del(&buf->vb.queue);
+			list_add_tail(&buf->vb.queue, &dma_q->active);
+			buf->vb.state = VIDEOBUF_ACTIVE;
+			dprintk(2, "[%p/%d] restart_queue - first active\n",
+				buf, buf->vb.i);
+		} else if (prev->vb.width == buf->vb.width &&
+			   prev->vb.height == buf->vb.height &&
+			   prev->fmt == buf->fmt) {
+			list_del(&buf->vb.queue);
+			list_add_tail(&buf->vb.queue, &dma_q->active);
+			buf->vb.state = VIDEOBUF_ACTIVE;
+			mod_timer(&dma_q->timeout, jiffies + BUFFER_TIMEOUT);
+			dprintk(2, "[%p/%d] restart_queue - move to active\n",
+				buf, buf->vb.i);
+		} else {
+			return 0;
+		}
+		prev = buf;
+	}
+}
+
+static void s2255_vid_timeout(unsigned long data)
+{
+	struct s2255_dmaqueue *vidq = (struct s2255_dmaqueue *)data;
+	struct s2255_buffer *buf;
+	dprintk(1, "[%d]vid timeout %p\n", vidq->channel, vidq);
+	while (!list_empty(&vidq->active)) {
+		buf =
+		    list_entry(vidq->active.next, struct s2255_buffer,
+			       vb.queue);
+		list_del(&buf->vb.queue);
+		buf->vb.state = VIDEOBUF_ERROR;
+		wake_up(&buf->vb.done);
+	}
+	restart_video_queue(vidq);
+}
+
+/* ------------------------------------------------------------------
+   Videobuf operations
+   ------------------------------------------------------------------*/
+static int buffer_setup(struct videobuf_queue *vq, unsigned int *count,
+			unsigned int *size)
+{
+	struct s2255_fh *fh = vq->priv_data;
+
+	*size = fh->width * fh->height * (fh->fmt->depth >> 3);
+
+	if (0 == *count)
+		*count = 8;
+
+	while (*size * *count > vid_limit * 1024 * 1024)
+		(*count)--;
+
+	return 0;
+}
+
+static void free_buffer(struct videobuf_queue *vq, struct s2255_buffer *buf)
+{
+	dprintk(4, "%s\n", __func__);
+
+	videobuf_waiton(&buf->vb, 0, 0);
+	videobuf_vmalloc_free(&buf->vb);
+	buf->vb.state = VIDEOBUF_NEEDS_INIT;
+}
+
+static int buffer_prepare(struct videobuf_queue *vq, struct videobuf_buffer *vb,
+			  enum v4l2_field field)
+{
+	struct s2255_fh *fh = vq->priv_data;
+	struct s2255_buffer *buf = container_of(vb, struct s2255_buffer, vb);
+	int rc, init_buffer = 0;
+	dprintk(4, "%s, field=%d\n", __func__, field);
+	if (fh->fmt == NULL)
+		return -EINVAL;
+	if ((fh->width < norm_minw(fh->dev->vdev[fh->channel])) ||
+	    (fh->width > norm_maxw(fh->dev->vdev[fh->channel])) ||
+	    (fh->height < norm_minh(fh->dev->vdev[fh->channel])) ||
+	    (fh->height > norm_maxh(fh->dev->vdev[fh->channel]))) {
+		dprintk(4, "invalid buffer prepare\n");
+		return -EINVAL;
+	}
+
+	buf->vb.size = fh->width * fh->height * (fh->fmt->depth >> 3);
+
+	if (0 != buf->vb.baddr && buf->vb.bsize < buf->vb.size) {
+		dprintk(4, "invalid buffer prepare\n");
+		return -EINVAL;
+	}
+
+	if (buf->fmt != fh->fmt ||
+	    buf->vb.width != fh->width ||
+	    buf->vb.height != fh->height || buf->vb.field != field) {
+		buf->fmt = fh->fmt;
+		buf->vb.width = fh->width;
+		buf->vb.height = fh->height;
+		buf->vb.field = field;
+		init_buffer = 1;
+	}
+
+	if (VIDEOBUF_NEEDS_INIT == buf->vb.state) {
+		rc = videobuf_iolock(vq, &buf->vb, NULL);
+		if (rc)
+			goto fail;
+	}
+
+	buf->vb.state = VIDEOBUF_PREPARED;
+	return 0;
+fail:
+	free_buffer(vq, buf);
+	return rc;
+}
+
+static void buffer_queue(struct videobuf_queue *vq, struct videobuf_buffer *vb)
+{
+	struct s2255_buffer *buf = container_of(vb, struct s2255_buffer, vb);
+	struct s2255_fh *fh = vq->priv_data;
+	struct s2255_dev *dev = fh->dev;
+	struct s2255_dmaqueue *vidq = &dev->vidq[fh->channel];
+	struct s2255_buffer *prev;
+
+	if (!list_empty(&vidq->queued)) {
+		dprintk(1, "adding vb queue=0x%08lx\n",
+			(unsigned long)&buf->vb.queue);
+		list_add_tail(&buf->vb.queue, &vidq->queued);
+		buf->vb.state = VIDEOBUF_QUEUED;
+		dprintk(2, "[%p/%d] buffer_queue - append to queued\n",
+			buf, buf->vb.i);
+	} else if (list_empty(&vidq->active)) {
+		list_add_tail(&buf->vb.queue, &vidq->active);
+		buf->vb.state = VIDEOBUF_ACTIVE;
+		dprintk(2, "[%p/%d] buffer_queue - first active\n",
+			buf, buf->vb.i);
+	} else {
+		prev =
+		    list_entry(vidq->active.prev, struct s2255_buffer,
+			       vb.queue);
+		if (prev->vb.width == buf->vb.width
+		    && prev->vb.height == buf->vb.height
+		    && prev->fmt == buf->fmt) {
+			list_add_tail(&buf->vb.queue, &vidq->active);
+			buf->vb.state = VIDEOBUF_ACTIVE;
+			dprintk(2, "[%p/%d] buffer_queue - append to active\n",
+				buf, buf->vb.i);
+
+		} else {
+			list_add_tail(&buf->vb.queue, &vidq->queued);
+			buf->vb.state = VIDEOBUF_QUEUED;
+			dprintk(2, "[%p/%d] buffer_queue - first queued\n",
+				buf, buf->vb.i);
+		}
+	}
+}
+
+static void buffer_release(struct videobuf_queue *vq,
+			   struct videobuf_buffer *vb)
+{
+	struct s2255_buffer *buf = container_of(vb, struct s2255_buffer, vb);
+	struct s2255_fh *fh = vq->priv_data;
+	dprintk(4, "%s %d\n", __func__, fh->channel);
+	free_buffer(vq, buf);
+}
+
+static struct videobuf_queue_ops s2255_video_qops = {
+	.buf_setup = buffer_setup,
+	.buf_prepare = buffer_prepare,
+	.buf_queue = buffer_queue,
+	.buf_release = buffer_release,
+};
+
+/* ------------------------------------------------------------------
+   IOCTL handling
+   ------------------------------------------------------------------*/
+
+static int res_get(struct s2255_dev *dev, struct s2255_fh *fh)
+{
+	/* is it free? */
+	mutex_lock(&dev->lock);
+	if (dev->resources[fh->channel]) {
+		/* no, someone else uses it */
+		mutex_unlock(&dev->lock);
+		return 0;
+	}
+	/* it's free, grab it */
+	dev->resources[fh->channel] = 1;
+	dprintk(1, "res: get\n");
+	mutex_unlock(&dev->lock);
+	return 1;
+}
+
+static int res_locked(struct s2255_dev *dev, struct s2255_fh *fh)
+{
+	return (dev->resources[fh->channel]);
+}
+
+static void res_free(struct s2255_dev *dev, struct s2255_fh *fh)
+{
+	dev->resources[fh->channel] = 0;
+	dprintk(1, "res: put\n");
+}
+
+/* ------------------------------------------------------------------
+   IOCTL vidioc handling
+   ------------------------------------------------------------------*/
+static int vidioc_querycap(struct file *file, void *priv,
+			   struct v4l2_capability *cap)
+{
+	strcpy(cap->driver, "s2255");
+	strcpy(cap->card, "s2255");
+	cap->version = S2255_VERSION;
+	cap->capabilities = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING;
+	return 0;
+}
+
+static int vidioc_enum_fmt_cap(struct file *file, void *priv,
+			       struct v4l2_fmtdesc *f)
+{
+	int index = 0;
+	if (f)
+		index = f->index;
+
+	if (index >= ARRAY_SIZE(formats))
+		return -EINVAL;
+
+	dprintk(4, "name %s\n", formats[index].name);
+	strlcpy(f->description, formats[index].name, sizeof(f->description));
+	f->pixelformat = formats[index].fourcc;
+	return 0;
+}
+
+static int vidioc_g_fmt_cap(struct file *file, void *priv,
+			    struct v4l2_format *f)
+{
+	struct s2255_fh *fh = priv;
+
+	f->fmt.pix.width = fh->width;
+	f->fmt.pix.height = fh->height;
+	f->fmt.pix.field = fh->vb_vidq.field;
+	f->fmt.pix.pixelformat = fh->fmt->fourcc;
+	f->fmt.pix.bytesperline = f->fmt.pix.width * (fh->fmt->depth >> 3);
+	f->fmt.pix.sizeimage = f->fmt.pix.height * f->fmt.pix.bytesperline;
+
+	return (0);
+}
+
+static int vidioc_try_fmt_cap(struct file *file, void *priv,
+			      struct v4l2_format *f)
+{
+	const struct s2255_fmt *fmt;
+	enum v4l2_field field;
+	int  b_any_field = 0;
+	struct s2255_fh *fh = priv;
+	struct s2255_dev *dev = fh->dev;
+	int is_ntsc;
+
+	is_ntsc =
+	    (dev->vdev[fh->channel]->current_norm != V4L2_STD_PAL_B) ? 1 : 0;
+
+	fmt = format_by_fourcc(f->fmt.pix.pixelformat);
+
+	if (fmt == NULL)
+		return -EINVAL;
+
+	field = f->fmt.pix.field;
+	if (field == V4L2_FIELD_ANY)
+		b_any_field = 1;
+
+	dprintk(4, "try format %d \n", is_ntsc);
+	/* supports 3 sizes. see s2255drv.h */
+	dprintk(50, "width test %d, height %d\n",
+		f->fmt.pix.width, f->fmt.pix.height);
+	if (is_ntsc) {
+		/* NTSC */
+		if (f->fmt.pix.height >= NUM_LINES_1CIFS_NTSC * 2) {
+			f->fmt.pix.height = NUM_LINES_1CIFS_NTSC * 2;
+			if (b_any_field) {
+				field = V4L2_FIELD_SEQ_TB;
+			} else if (!((field == V4L2_FIELD_INTERLACED) ||
+				      (field == V4L2_FIELD_SEQ_TB) ||
+				      (field == V4L2_FIELD_INTERLACED_TB))) {
+				dprintk(1, "unsupported field setting\n");
+				return -EINVAL;
+			}
+		} else {
+			f->fmt.pix.height = NUM_LINES_1CIFS_NTSC;
+			if (b_any_field) {
+				field = V4L2_FIELD_TOP;
+			} else if (!((field == V4L2_FIELD_TOP) ||
+				      (field == V4L2_FIELD_BOTTOM))) {
+				dprintk(1, "unsupported field setting\n");
+				return -EINVAL;
+			}
+
+		}
+		if (f->fmt.pix.width >= LINE_SZ_4CIFS_NTSC)
+			f->fmt.pix.width = LINE_SZ_4CIFS_NTSC;
+		else if (f->fmt.pix.width >= LINE_SZ_2CIFS_NTSC)
+			f->fmt.pix.width = LINE_SZ_2CIFS_NTSC;
+		else if (f->fmt.pix.width >= LINE_SZ_1CIFS_NTSC)
+			f->fmt.pix.width = LINE_SZ_1CIFS_NTSC;
+		else
+			f->fmt.pix.width = LINE_SZ_1CIFS_NTSC;
+	} else {
+		/* PAL */
+		if (f->fmt.pix.height >= NUM_LINES_1CIFS_PAL * 2) {
+			f->fmt.pix.height = NUM_LINES_1CIFS_PAL * 2;
+			if (b_any_field) {
+				field = V4L2_FIELD_SEQ_TB;
+			} else if (!((field == V4L2_FIELD_INTERLACED) ||
+				      (field == V4L2_FIELD_SEQ_TB) ||
+				      (field == V4L2_FIELD_INTERLACED_TB))) {
+				dprintk(1, "unsupported field setting\n");
+				return -EINVAL;
+			}
+		} else {
+			f->fmt.pix.height = NUM_LINES_1CIFS_PAL;
+			if (b_any_field) {
+				field = V4L2_FIELD_TOP;
+			} else if (!((field == V4L2_FIELD_TOP) ||
+				     (field == V4L2_FIELD_BOTTOM))) {
+				dprintk(1, "unsupported field setting\n");
+				return -EINVAL;
+			}
+		}
+		if (f->fmt.pix.width >= LINE_SZ_4CIFS_PAL) {
+			dprintk(50, "pal 704\n");
+			f->fmt.pix.width = LINE_SZ_4CIFS_PAL;
+			field = V4L2_FIELD_SEQ_TB;
+		} else if (f->fmt.pix.width >= LINE_SZ_2CIFS_PAL) {
+			dprintk(50, "pal 352A\n");
+			f->fmt.pix.width = LINE_SZ_2CIFS_PAL;
+			field = V4L2_FIELD_TOP;
+		} else if (f->fmt.pix.width >= LINE_SZ_1CIFS_PAL) {
+			dprintk(50, "pal 352B\n");
+			f->fmt.pix.width = LINE_SZ_1CIFS_PAL;
+			field = V4L2_FIELD_TOP;
+		} else {
+			dprintk(50, "pal 352C\n");
+			f->fmt.pix.width = LINE_SZ_1CIFS_PAL;
+			field = V4L2_FIELD_TOP;
+		}
+	}
+
+	dprintk(50, "width %d height %d field %d \n", f->fmt.pix.width,
+		f->fmt.pix.height, f->fmt.pix.field);
+	f->fmt.pix.field = field;
+	f->fmt.pix.bytesperline = (f->fmt.pix.width * fmt->depth) >> 3;
+	f->fmt.pix.sizeimage = f->fmt.pix.height * f->fmt.pix.bytesperline;
+	return 0;
+}
+
+/* FIXME: This seems to be generic enough to be at videodev2 */
+static int vidioc_s_fmt_cap(struct file *file, void *priv,
+			    struct v4l2_format *f)
+{
+	struct s2255_fh *fh = priv;
+	const struct s2255_fmt *fmt;
+	int ret;
+	int norm;
+
+	if (res_locked(fh->dev, fh)) {
+		dprintk(1, "can't change format after started\n");
+		return -EBUSY;
+	}
+
+	ret = vidioc_try_fmt_cap(file, fh, f);
+
+	if (ret < 0)
+		return (ret);
+
+	fmt = format_by_fourcc(f->fmt.pix.pixelformat);
+	if (fmt == NULL)
+		return -EINVAL;
+
+	fh->fmt = fmt;
+	fh->width = f->fmt.pix.width;
+	fh->height = f->fmt.pix.height;
+	fh->vb_vidq.field = f->fmt.pix.field;
+	fh->type = f->type;
+	norm = norm_minw(fh->dev->vdev[fh->channel]);
+	if (fh->width > norm_minw(fh->dev->vdev[fh->channel])) {
+		if (fh->height > norm_minh(fh->dev->vdev[fh->channel]))
+			fh->mode.scale = SCALE_4CIFS;
+		else
+			fh->mode.scale = SCALE_2CIFS;
+
+	} else {
+		fh->mode.scale = SCALE_1CIFS;
+	}
+
+	/* color mode */
+	if (fh->fmt->fourcc == V4L2_PIX_FMT_GREY) {
+		fh->mode.color = COLOR_Y8;
+	} else if (fh->fmt->fourcc == V4L2_PIX_FMT_YUV422P) {
+		fh->mode.color = COLOR_YUVPL;
+	} else if (fh->fmt->fourcc == V4L2_PIX_FMT_YUYV) {
+		/* Note: software conversion from YUV422P to YUYV */
+		fh->mode.color = COLOR_YUVPK;
+	} else if ((fh->fmt->fourcc == V4L2_PIX_FMT_RGB24) ||
+		   (fh->fmt->fourcc == V4L2_PIX_FMT_BGR24) ||
+		   (fh->fmt->fourcc == V4L2_PIX_FMT_RGB32) ||
+		   (fh->fmt->fourcc == V4L2_PIX_FMT_RGB565) ||
+		   (fh->fmt->fourcc == V4L2_PIX_FMT_RGB565X) ||
+		   (fh->fmt->fourcc == V4L2_PIX_FMT_BGR32)) {
+		/* Note:software conversion from YUV422P to RGB(s) */
+		dprintk(2, "mode supported with software conversion.\n");
+		dprintk(2, "for lower CPU usage, use V4L2_PIX_FMT_YUV422P"
+			"V4L2_PIX_FMT_YUVV(minimal software reordering) or"
+			" V4L2_PIX_FMT_GREY\n");
+		fh->mode.color = COLOR_YUVPL;
+	}
+	return (0);
+}
+
+static int vidioc_reqbufs(struct file *file, void *priv,
+			  struct v4l2_requestbuffers *p)
+{
+	int rc;
+	struct s2255_fh *fh = priv;
+	rc = videobuf_reqbufs(&fh->vb_vidq, p);
+	return rc;
+}
+
+static int vidioc_querybuf(struct file *file, void *priv, struct v4l2_buffer *p)
+{
+	int rc;
+	struct s2255_fh *fh = priv;
+	rc = videobuf_querybuf(&fh->vb_vidq, p);
+	return rc;
+}
+
+static int vidioc_qbuf(struct file *file, void *priv, struct v4l2_buffer *p)
+{
+	int rc;
+	struct s2255_fh *fh = priv;
+	rc = videobuf_qbuf(&fh->vb_vidq, p);
+	return rc;
+}
+
+static int vidioc_dqbuf(struct file *file, void *priv, struct v4l2_buffer *p)
+{
+	int rc;
+	struct s2255_fh *fh = priv;
+	rc = videobuf_dqbuf(&fh->vb_vidq, p, file->f_flags & O_NONBLOCK);
+	return rc;
+}
+
+#ifdef CONFIG_VIDEO_V4L1_COMPAT
+static int vidiocgmbuf(struct file *file, void *priv, struct video_mbuf *mbuf)
+{
+	struct s2255_fh *fh = priv;
+	struct videobuf_queue *q = &fh->vb_vidq;
+	struct v4l2_requestbuffers req;
+	unsigned int i;
+	int ret;
+
+	req.type = q->type;
+	req.count = 8;
+	req.memory = V4L2_MEMORY_MMAP;
+	ret = videobuf_reqbufs(q, &req);
+	if (ret < 0)
+		return (ret);
+
+	mbuf->frames = req.count;
+	mbuf->size = 0;
+	for (i = 0; i < mbuf->frames; i++) {
+		mbuf->offsets[i] = q->bufs[i]->boff;
+		mbuf->size += q->bufs[i]->bsize;
+	}
+	return (0);
+}
+#endif
+
+
+#define EP_NUM_CONFIG 2
+/* write to the configuration pipe, synchronously */
+static int s2255_write_config(struct usb_device *udev, unsigned char *pbuf,
+			      int size)
+{
+	int pipe;
+	int done;
+	long retval = -1;
+	if (udev) {
+		pipe = usb_sndbulkpipe(udev, EP_NUM_CONFIG);
+		retval = usb_bulk_msg(udev, pipe, pbuf, size, &done, 500);
+	}
+	return retval;
+}
+
+static u32 get_transfer_size(struct mode2255i *mode)
+{
+	int linesPerFrame = LINE_SZ_DEF;
+	int pixelsPerLine = NUM_LINES_DEF;
+	u32 outImageSize;
+	u32 usbInSize;
+	unsigned int mask_mult;
+
+	if (mode == NULL)
+		return 0;
+
+	if (mode->format == FORMAT_NTSC) {
+		switch (mode->scale) {
+		case SCALE_4CIFS:
+			linesPerFrame = NUM_LINES_4CIFS_NTSC * 2;
+			pixelsPerLine = LINE_SZ_4CIFS_NTSC;
+			break;
+		case SCALE_2CIFS:
+			linesPerFrame = NUM_LINES_2CIFS_NTSC;
+			pixelsPerLine = LINE_SZ_2CIFS_NTSC;
+			break;
+		case SCALE_1CIFS:
+			linesPerFrame = NUM_LINES_1CIFS_NTSC;
+			pixelsPerLine = LINE_SZ_1CIFS_NTSC;
+			break;
+		default:
+			break;
+		}
+	} else if (mode->format == FORMAT_PAL) {
+		switch (mode->scale) {
+		case SCALE_4CIFS:
+			linesPerFrame = NUM_LINES_4CIFS_PAL * 2;
+			pixelsPerLine = LINE_SZ_4CIFS_PAL;
+			break;
+		case SCALE_2CIFS:
+			linesPerFrame = NUM_LINES_2CIFS_PAL;
+			pixelsPerLine = LINE_SZ_2CIFS_PAL;
+			break;
+		case SCALE_1CIFS:
+			linesPerFrame = NUM_LINES_1CIFS_PAL;
+			pixelsPerLine = LINE_SZ_1CIFS_PAL;
+			break;
+		default:
+			break;
+		}
+	}
+	outImageSize = linesPerFrame * pixelsPerLine;
+	if (mode->color != COLOR_Y8) {
+		/* 2 bytes/pixel if not monochrome */
+		outImageSize *= 2;
+	}
+
+	/* total bytes to send including prefix and 4K padding;
+	   must be a multiple of USB_READ_SIZE */
+	usbInSize = outImageSize + PREFIX_SIZE;	/* always send prefix */
+	mask_mult = 0xffffffffUL - DEF_USB_BLOCK + 1;
+	/* if size not a multiple of USB_READ_SIZE */
+	if (usbInSize & ~mask_mult)
+		usbInSize = (usbInSize & mask_mult) + (DEF_USB_BLOCK);
+	return usbInSize;
+}
+
+static void dump_verify_mode(struct s2255_dev *sdev, struct mode2255i *mode)
+{
+	struct device *dev = &sdev->udev->dev;
+	dev_info(dev, "------------------------------------------------\n");
+	dev_info(dev, "verify mode\n");
+	dev_info(dev, "format: %d\n", mode->format);
+	dev_info(dev, "scale: %d\n", mode->scale);
+	dev_info(dev, "fdec: %d\n", mode->fdec);
+	dev_info(dev, "color: %d\n", mode->color);
+	dev_info(dev, "bright: 0x%x\n", mode->bright);
+	dev_info(dev, "restart: 0x%x\n", mode->restart);
+	dev_info(dev, "usb_block: 0x%x\n", mode->usb_block);
+	dev_info(dev, "single: 0x%x\n", mode->single);
+	dev_info(dev, "------------------------------------------------\n");
+}
+
+/*
+ * set mode is the function which controls the DSP.
+ * the restart parameter in struct mode2255i should be set whenever
+ * the image size could change via color format, video system or image
+ * size.
+ * When the restart parameter is set, we sleep for ONE frame to allow the
+ * DSP time to get the new frame
+ */
+static int s2255_set_mode(struct s2255_dev *dev, unsigned long chn,
+			  struct mode2255i *mode)
+{
+	int res;
+	u32 *buffer;
+	unsigned long chn_rev;
+
+	chn_rev = G_chnmap[chn];
+	dprintk(3, "mode scale [%ld] %p %d\n", chn, mode, mode->scale);
+	dprintk(3, "mode scale [%ld] %p %d\n", chn, &dev->mode[chn],
+		dev->mode[chn].scale);
+	dprintk(2, "mode contrast %x\n", mode->contrast);
+
+	/* save the mode */
+	dev->mode[chn] = *mode;
+	dev->req_image_size[chn] = get_transfer_size(mode);
+	dprintk(1, "transfer size %ld\n", dev->req_image_size[chn]);
+
+	buffer = kzalloc(512, GFP_KERNEL);
+	if (buffer == NULL) {
+		dev_err(&dev->udev->dev, "out of mem\n");
+		return -ENOMEM;
+	}
+
+	/* set the mode */
+	buffer[0] = IN_DATA_TOKEN;
+	buffer[1] = (u32) chn_rev;
+	buffer[2] = CMD_SET_MODE;
+	memcpy(&buffer[3], &dev->mode[chn], sizeof(struct mode2255i));
+	res = s2255_write_config(dev->udev, (unsigned char *)buffer, 512);
+	if (debug)
+		dump_verify_mode(dev, mode);
+	kfree(buffer);
+	dprintk(1, "set mode done chn %lu, %d\n", chn, res);
+
+	/* wait at least 3 frames before continuing */
+	if (mode->restart)
+		msleep(125);
+
+	/* clear the restart flag */
+	dev->mode[chn].restart = 0;
+
+	return res;
+}
+
+static int vidioc_streamon(struct file *file, void *priv, enum v4l2_buf_type i)
+{
+	int res;
+	struct s2255_fh *fh = priv;
+	struct s2255_dev *dev = fh->dev;
+	struct s2255_dmaqueue *dmaq = &dev->vidq[fh->channel];
+	struct mode2255i *new_mode;
+	struct mode2255i *old_mode;
+	int chn;
+	int j;
+	dprintk(4, "%s\n", __func__);
+	if (fh->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) {
+		dev_err(&dev->udev->dev, "invalid fh type0\n");
+		return -EINVAL;
+	}
+	if (i != fh->type) {
+		dev_err(&dev->udev->dev, "invalid fh type1\n");
+		return -EINVAL;
+	}
+
+	if (!res_get(dev, fh)) {
+		dev_err(&dev->udev->dev, "res get busy\n");
+		return -EBUSY;
+	}
+	/* send a set mode command everytime with restart.
+	   in case we switch resolutions or other parameters */
+	chn = fh->channel;
+	new_mode = &fh->mode;
+	old_mode = &fh->dev->mode[chn];
+
+	if (new_mode->color != old_mode->color)
+		new_mode->restart = 1;
+	else if (new_mode->scale != old_mode->scale)
+		new_mode->restart = 1;
+	else if (new_mode->format != old_mode->format)
+		new_mode->restart = 1;
+
+	s2255_set_mode(dev, chn, new_mode);
+	new_mode->restart = 0;
+	*old_mode = *new_mode;
+	dev->cur_fmt[chn] = fh->fmt;
+	dprintk(1, "%s[%d]\n", __func__, chn);
+	dev->last_frame[chn] = -1;
+	dev->bad_payload[chn] = 0;
+	dev->cur_frame[chn] = 0;
+	for (j = 0; j < SYS_FRAMES; j++) {
+		dev->buffer[chn].frame[j].ulState = 0;
+		dev->buffer[chn].frame[j].cur_size = 0;
+	}
+	mod_timer(&dmaq->timeout, jiffies + BUFFER_TIMEOUT);
+	res = videobuf_streamon(&fh->vb_vidq);
+	if (res == 0) {
+		s2255_start_acquire(dev, chn);
+		dev->b_acquire[chn] = 1;
+	} else {
+		res_free(dev, fh);
+	}
+	return res;
+}
+
+static int vidioc_streamoff(struct file *file, void *priv, enum v4l2_buf_type i)
+{
+	int res;
+	struct s2255_fh *fh = priv;
+	struct s2255_dev *dev = fh->dev;
+	struct s2255_dmaqueue *dmaq = &dev->vidq[fh->channel];
+	dprintk(4, "%s\n, channel: %d", __func__, fh->channel);
+	if (fh->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) {
+		printk(KERN_ERR "invalid fh type0\n");
+		return -EINVAL;
+	}
+	if (i != fh->type) {
+		printk(KERN_ERR "invalid type i\n");
+		return -EINVAL;
+	}
+	s2255_stop_acquire(dev, fh->channel);
+	/* give time for last frame to be received/flush in the usb
+	   receive pipe*/
+	msleep(50);
+	res = videobuf_streamoff(&fh->vb_vidq);
+	res_free(dev, fh);
+	/* expire the queue timer now in case still active */
+	mod_timer(&dmaq->timeout, jiffies + HZ);
+	return res;
+}
+
+static int vidioc_s_std(struct file *file, void *priv, v4l2_std_id *i)
+{
+	struct s2255_fh *fh = priv;
+	struct mode2255i *mode;
+
+	if (res_locked(fh->dev, fh)) {
+		dprintk(1, "can't change standard after started\n");
+		return -EBUSY;
+	}
+	mode = &fh->mode;
+
+	if (*i == V4L2_STD_NTSC_M) {
+		dprintk(4, "vidioc_s_std NTSC\n");
+		mode->format = FORMAT_NTSC;
+	} else if (*i == V4L2_STD_PAL_B) {
+		dprintk(4, "vidioc_s_std PAL\n");
+		mode->format = FORMAT_PAL;
+	} else {
+		return -EINVAL;
+	}
+	return 0;
+}
+
+/* Sensoray 2255 is a multiple channel capture device.
+   It does not have a "crossbar" of inputs.
+   We use one V4L device per channel. The user must
+   be aware that certain combinations are not allowed.
+   For instance, you cannot do full FPS on more than 2 channels(2 videodevs)
+   at once in color(you can do full fps on 4 channels with greyscale.
+*/
+static int vidioc_enum_input(struct file *file, void *priv,
+			     struct v4l2_input *inp)
+{
+	if (inp->index != 0)
+		return -EINVAL;
+
+	inp->type = V4L2_INPUT_TYPE_CAMERA;
+	inp->std = S2255_NORMS;
+	strcpy(inp->name, "Camera");
+	return (0);
+}
+
+static int vidioc_g_input(struct file *file, void *priv, unsigned int *i)
+{
+	*i = 0;
+	return 0;
+}
+static int vidioc_s_input(struct file *file, void *priv, unsigned int i)
+{
+	if (i > 0)
+		return -EINVAL;
+	return 0;
+}
+
+/* --- controls ---------------------------------------------- */
+static int vidioc_queryctrl(struct file *file, void *priv,
+			    struct v4l2_queryctrl *qc)
+{
+	int i;
+
+	for (i = 0; i < ARRAY_SIZE(s2255_qctrl); i++)
+		if (qc->id && qc->id == s2255_qctrl[i].id) {
+			memcpy(qc, &(s2255_qctrl[i]), sizeof(*qc));
+			return (0);
+		}
+
+	dprintk(4, "query_ctrl -EINVAL %d\n", qc->id);
+	return -EINVAL;
+}
+
+static int vidioc_g_ctrl(struct file *file, void *priv,
+			 struct v4l2_control *ctrl)
+{
+	int i;
+
+	for (i = 0; i < ARRAY_SIZE(s2255_qctrl); i++)
+		if (ctrl->id == s2255_qctrl[i].id) {
+			ctrl->value = qctl_regs[i];
+			return (0);
+		}
+	dprintk(4, "g_ctrl -EINVAL\n");
+
+	return -EINVAL;
+}
+
+static int vidioc_s_ctrl(struct file *file, void *priv,
+			 struct v4l2_control *ctrl)
+{
+	int i;
+	struct s2255_fh *fh = priv;
+	struct s2255_dev *dev = fh->dev;
+	struct mode2255i *mode;
+	mode = &fh->mode;
+	dprintk(4, "vidioc_s_ctrl\n");
+	for (i = 0; i < ARRAY_SIZE(s2255_qctrl); i++) {
+		if (ctrl->id == s2255_qctrl[i].id) {
+			if (ctrl->value < s2255_qctrl[i].minimum ||
+			    ctrl->value > s2255_qctrl[i].maximum)
+				return (-ERANGE);
+
+			qctl_regs[i] = ctrl->value;
+			/* update the mode to the corresponding value */
+			if (ctrl->id == V4L2_CID_BRIGHTNESS)
+				mode->bright = ctrl->value;
+			else if (ctrl->id == V4L2_CID_CONTRAST)
+				mode->contrast = ctrl->value;
+			else if (ctrl->id == V4L2_CID_HUE)
+				mode->hue = ctrl->value;
+			else if (ctrl->id == V4L2_CID_SATURATION)
+				mode->saturation = ctrl->value;
+#if 1
+			mode->restart = 0;
+			/* set mode here.  Note: stream does not need restarted.
+			   some V4L programs restart stream unnecessarily
+			   after a s_crtl.
+			 */
+			s2255_set_mode(dev, fh->channel, mode);
+#endif
+			return 0;
+		}
+	}
+	return -EINVAL;
+}
+
+static int s2255_open_v4l(struct inode *inode, struct file *file)
+{
+	int minor = iminor(inode);
+	struct s2255_dev *h, *dev = NULL;
+	struct s2255_fh *fh;
+	struct list_head *list;
+	enum v4l2_buf_type type = 0;
+	int i = 0;
+	int cur_channel = -1;
+	dprintk(1, "s2255: open called (minor=%d)\n", minor);
+	list_for_each(list, &s2255_devlist) {
+		h = list_entry(list, struct s2255_dev, s2255_devlist);
+		for (i = 0; i < MAX_CHANNELS; i++) {
+			if (h->vdev[i]->minor == minor) {
+				cur_channel = i;
+				dev = h;
+				type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+			}
+		}
+	}
+
+	if ((NULL == dev) || (cur_channel == -1)) {
+		dprintk(1, "s2255: openv4l no dev\n");
+		return -ENODEV;
+	}
+
+	mutex_lock(&usb_s2255_open_mutex);
+
+
+
+	if (dev->fw_data->fw_state == FWSTATE_FAILED) {
+		err("2255 firmware wasn't loaded\n");
+		mutex_unlock(&usb_s2255_open_mutex);
+		return -ENODEV;
+	}
+
+	if (dev->fw_data->fw_state == FWSTATE_NOTLOADED) {
+		/* give 1 second for firmware to load in case
+		   driver loaded and then device immediately opened */
+		msleep(1000);
+		if (dev->fw_data->fw_state == FWSTATE_NOTLOADED) {
+			err("2255 firmware loading stalled\n");
+			mutex_unlock(&usb_s2255_open_mutex);
+			return -EAGAIN;
+		}
+	}
+	/* if first open after firmware loaded, release the firmware */
+	if (dev->fw_data->fw) {
+		release_firmware(dev->fw_data->fw);
+		dev->fw_data->fw = NULL;
+	}
+
+	dev->users[cur_channel]++;
+
+	if (dev->users[cur_channel] > 1) {
+		dev->users[cur_channel]--;
+		dev_err(&dev->udev->dev, "one user at a time\n");
+		mutex_unlock(&usb_s2255_open_mutex);
+		return -EAGAIN;
+	}
+
+	dprintk(1, "open minor=%d type=%s users=%d\n",
+		minor, v4l2_type_names[type], dev->users[cur_channel]);
+
+	/* allocate + initialize per filehandle data */
+	fh = kzalloc(sizeof(*fh), GFP_KERNEL);
+	if (NULL == fh) {
+		dev->users[cur_channel]--;
+		mutex_unlock(&usb_s2255_open_mutex);
+		return -ENOMEM;
+	}
+
+	file->private_data = fh;
+	fh->dev = dev;
+	fh->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+	fh->mode = dev->mode[cur_channel];
+	fh->fmt = dev->cur_fmt[cur_channel];
+	/* default 1CIF NTSC */
+	fh->width = 640;
+	fh->height = 480;
+	fh->channel = cur_channel;
+
+	/* Put all controls at a sane state */
+	for (i = 0; i < ARRAY_SIZE(s2255_qctrl); i++)
+		qctl_regs[i] = s2255_qctrl[i].default_value;
+
+	dprintk(1, "Open: fh=0x%08lx, dev=0x%08lx, dev->vidq=0x%08lx\n",
+		(unsigned long)fh, (unsigned long)dev,
+		(unsigned long)&dev->vidq[cur_channel]);
+	dprintk(1, "Open: list_empty queued=%d\n",
+		list_empty(&dev->vidq[cur_channel].queued));
+	dprintk(1, "Open: list_empty active=%d\n",
+		list_empty(&dev->vidq[cur_channel].active));
+	dprintk(1, "s2255core_board_open\n");
+
+	videobuf_queue_vmalloc_init(&fh->vb_vidq, &s2255_video_qops,
+				    NULL, NULL,
+				    fh->type,
+				    V4L2_FIELD_INTERLACED,
+				    sizeof(struct s2255_buffer), fh);
+
+	kref_get(&dev->kref);
+	mutex_unlock(&usb_s2255_open_mutex);
+	dprintk(2, "v4l open done\n");
+	return 0;
+}
+
+
+static unsigned int s2255_poll(struct file *file,
+			       struct poll_table_struct *wait)
+{
+	struct s2255_fh *fh = file->private_data;
+	int rc;
+	dprintk(100, "%s\n", __func__);
+
+	if (V4L2_BUF_TYPE_VIDEO_CAPTURE != fh->type)
+		return POLLERR;
+
+	rc = videobuf_poll_stream(file, &fh->vb_vidq, wait);
+	return rc;
+}
+
+static void s2255_destroy(struct kref *kref)
+{
+	struct s2255_dev *dev = to_s2255_dev(kref);
+	usb_put_dev(dev->udev);
+	dprintk(1, "s2255_destroy\n");
+	kfree(dev);
+}
+
+static int s2255_release_v4l(struct inode *inode, struct file *file)
+{
+	struct s2255_fh *fh = file->private_data;
+	struct s2255_dev *dev = fh->dev;
+	int minor = iminor(inode);
+
+	dev->users[fh->channel]--;
+	if (dev->b_acquire[fh->channel])
+		s2255_stop_acquire(dev, fh->channel);
+	res_free(dev, fh);
+	videobuf_mmap_free(&fh->vb_vidq);
+	kfree(fh);
+	kref_put(&dev->kref, s2255_destroy);
+	dprintk(1, "s2255: close called (minor=%d, users=%d)\n", minor,
+	       dev->users[fh->channel]);
+	return 0;
+}
+
+static int s2255_mmap_v4l(struct file *file, struct vm_area_struct *vma)
+{
+	struct s2255_fh *fh = file->private_data;
+	int ret;
+
+	dprintk(4, "mmap called, vma=0x%08lx\n", (unsigned long)vma);
+
+	ret = videobuf_mmap_mapper(&fh->vb_vidq, vma);
+
+	dprintk(4, "vma start=0x%08lx, size=%ld, ret=%d\n",
+		(unsigned long)vma->vm_start,
+		(unsigned long)vma->vm_end - (unsigned long)vma->vm_start, ret);
+
+	return ret;
+}
+
+static const struct file_operations s2255_fops_v4l = {
+	.owner = THIS_MODULE,
+	.open = s2255_open_v4l,
+	.release = s2255_release_v4l,
+	.poll = s2255_poll,
+	.ioctl = video_ioctl2,	/* V4L2 ioctl handler */
+	.mmap = s2255_mmap_v4l,
+	.llseek = no_llseek,
+};
+
+static struct video_device template = {
+	.name = "s2255v",
+	.type = VID_TYPE_CAPTURE,
+	.fops = &s2255_fops_v4l,
+	.minor = -1,
+	.vidioc_querycap = vidioc_querycap,
+	.vidioc_enum_fmt_cap = vidioc_enum_fmt_cap,
+	.vidioc_g_fmt_cap = vidioc_g_fmt_cap,
+	.vidioc_try_fmt_cap = vidioc_try_fmt_cap,
+	.vidioc_s_fmt_cap = vidioc_s_fmt_cap,
+	.vidioc_reqbufs = vidioc_reqbufs,
+	.vidioc_querybuf = vidioc_querybuf,
+	.vidioc_qbuf = vidioc_qbuf,
+	.vidioc_dqbuf = vidioc_dqbuf,
+	.vidioc_s_std = vidioc_s_std,
+	.vidioc_enum_input = vidioc_enum_input,
+	.vidioc_g_input = vidioc_g_input,
+	.vidioc_s_input = vidioc_s_input,
+	.vidioc_queryctrl = vidioc_queryctrl,
+	.vidioc_g_ctrl = vidioc_g_ctrl,
+	.vidioc_s_ctrl = vidioc_s_ctrl,
+	.vidioc_streamon = vidioc_streamon,
+	.vidioc_streamoff = vidioc_streamoff,
+#ifdef CONFIG_VIDEO_V4L1_COMPAT
+	.vidiocgmbuf = vidiocgmbuf,
+#endif
+	.tvnorms = S2255_NORMS,
+	.current_norm = V4L2_STD_NTSC_M,
+};
+
+static int s2255_probe_v4l(struct s2255_dev *dev)
+{
+	int ret;
+	int i;
+	int cur_nr = video_nr;
+
+	/* initialize all video 4 linux */
+	list_add_tail(&dev->s2255_devlist, &s2255_devlist);
+	/* register 4 video devices */
+	for (i = 0; i < MAX_CHANNELS; i++) {
+		INIT_LIST_HEAD(&dev->vidq[i].active);
+		INIT_LIST_HEAD(&dev->vidq[i].queued);
+		dev->vidq[i].timeout.function = s2255_vid_timeout;
+		dev->vidq[i].timeout.data = (unsigned long)&dev->vidq[i];
+		dev->vidq[i].dev = dev;
+		dev->vidq[i].channel = i;
+		dev->vidq[i].kthread = NULL;
+		init_timer(&dev->vidq[i].timeout);
+		/* register 4 video devices */
+		dev->vdev[i] = video_device_alloc();
+		memcpy(dev->vdev[i], &template, sizeof(struct video_device));
+		dev->vdev[i]->dev = &dev->interface->dev;
+		if (video_nr == -1)
+			ret = video_register_device(dev->vdev[i],
+						    VFL_TYPE_GRABBER,
+						    video_nr);
+		else
+			ret = video_register_device(dev->vdev[i],
+						    VFL_TYPE_GRABBER,
+						    cur_nr + i);
+		dev->vdev[i]->priv = dev;
+
+		if (ret != 0) {
+			dev_err(&dev->udev->dev,
+				"failed to register video device!\n");
+			return ret;
+		}
+	}
+	printk(KERN_INFO "Sensoray 2255 V4L driver\n");
+	return ret;
+}
+
+static void s2255_exit_v4l(struct s2255_dev *dev)
+{
+	struct list_head *list;
+	int i;
+	/* unregister the video devices */
+	while (!list_empty(&s2255_devlist)) {
+		list = s2255_devlist.next;
+		list_del(list);
+	}
+
+	for (i = 0; i < MAX_CHANNELS; i++) {
+		video_unregister_device(dev->vdev[i]);
+		del_timer(&dev->vidq[i].timeout);
+	}
+}
+
+/* this function moves the usb stream read pipe data
+ * into the system buffers.
+ * returns 0 on success, EAGAIN if more data to process( call this
+ * function again).
+ *
+ * Received frame structure:
+ * bytes 0-3:  marker : 0x2255DA4AL (FRAME_MARKER)
+ * bytes 4-7:  channel: 0-3
+ * bytes 8-11: payload size:  size of the frame
+ * bytes 12-payloadsize+12:  frame data
+ */
+static int save_frame(struct s2255_dev *dev, struct s2255_pipeinfo *pipe_info)
+{
+	static int dbgsync; /* = 0; */
+	char *pdest;
+	u32 offset = 0;
+	int bsync = 0;
+	int btrunc = 0;
+	char *psrc;
+	unsigned long copy_size;
+	unsigned long size;
+	s32 idx = -1;
+	struct framei *frm;
+	unsigned char *pdata;
+	unsigned long cur_size;
+	int bsearch = 0;
+	struct bufferi *buf;
+	dprintk(100, "buffer to user\n");
+
+	idx = dev->cur_frame[dev->cc];
+	buf = &dev->buffer[dev->cc];
+	frm = &buf->frame[idx];
+
+	if (frm->ulState == 0) {
+		frm->ulState = 1;
+		frm->cur_size = 0;
+		bsearch = 1;
+	} else if (frm->ulState == 2) {
+		/* system frame was not freed */
+		dprintk(2, "sys frame not free.  overrun ringbuf\n");
+		bsearch = 1;
+		frm->ulState = 1;
+		frm->cur_size = 0;
+	}
+
+	if (bsearch) {
+		if (*(s32 *) pipe_info->transfer_buffer != FRAME_MARKER) {
+			u32 jj;
+			if (dbgsync == 0) {
+				dprintk(3, "not synched, discarding all packets"
+					"until marker\n");
+
+				dbgsync++;
+			}
+			pdata = (unsigned char *)pipe_info->transfer_buffer;
+			for (jj = 0; jj < (pipe_info->cur_transfer_size - 12);
+			     jj++) {
+				if (*(s32 *) pdata == FRAME_MARKER) {
+					int cc;
+					dprintk(3,
+						"found frame marker at offset:"
+						" %d [%x %x]\n", jj, pdata[0],
+						pdata[1]);
+					offset = jj;
+					bsync = 1;
+					cc = *(u32 *) (pdata + sizeof(u32));
+					if (cc >= MAX_CHANNELS) {
+						printk(KERN_ERR
+						       "bad channel\n");
+						return -EINVAL;
+					}
+					/* reverse it */
+					dev->cc = G_chnmap[cc];
+					break;
+				}
+				pdata++;
+			}
+			if (bsync == 0)
+				return -EINVAL;
+		} else {
+			u32 *pword;
+			u32 payload;
+			int cc;
+			dbgsync = 0;
+			bsync = 1;
+			pword = (u32 *) pipe_info->transfer_buffer;
+			cc = pword[1];
+
+			if (cc >= MAX_CHANNELS) {
+				printk("invalid channel found. "
+					"throwing out data!\n");
+				return -EINVAL;
+			}
+			dev->cc = G_chnmap[cc];
+			payload = pword[2];
+			if (payload != dev->req_image_size[dev->cc]) {
+				dprintk(1, "[%d][%d]unexpected payload: %d"
+					"required: %lu \n", cc, dev->cc,
+					payload, dev->req_image_size[dev->cc]);
+				dev->bad_payload[dev->cc]++;
+				/* discard the bad frame */
+				return -EINVAL;
+			}
+
+		}
+	}
+	/* search done.  now find out if should be acquiring
+	   on this channel */
+	if (!dev->b_acquire[dev->cc]) {
+		frm->ulState = 0;
+		return -EINVAL;
+	}
+
+	idx = dev->cur_frame[dev->cc];
+	frm = &dev->buffer[dev->cc].frame[idx];
+
+	if (frm->ulState == 0) {
+		frm->ulState = 1;
+		frm->cur_size = 0;
+	} else if (frm->ulState == 2) {
+		/* system frame ring buffer overrun */
+		dprintk(2, "sys frame overrun.  overwriting frame %d %d\n",
+			dev->cc, idx);
+		frm->ulState = 1;
+		frm->cur_size = 0;
+	}
+
+	if (bsync) {
+		/* skip the marker 512 bytes (and offset if out of sync) */
+		psrc = (u8 *)pipe_info->transfer_buffer + offset + PREFIX_SIZE;
+	} else {
+		psrc = (u8 *)pipe_info->transfer_buffer;
+	}
+
+	if (frm->lpvbits == NULL) {
+		dprintk(1, "s2255 frame buffer == NULL.%p %p %d %d",
+			frm, dev, dev->cc, idx);
+		return -ENOMEM;
+	}
+
+	pdest = frm->lpvbits + frm->cur_size;
+
+	if (bsync) {
+		copy_size =
+		    (pipe_info->cur_transfer_size - offset) - PREFIX_SIZE;
+		if (copy_size > pipe_info->cur_transfer_size) {
+			printk("invalid copy size, overflow!\n");
+			return -ENOMEM;
+		}
+	} else {
+		copy_size = pipe_info->cur_transfer_size;
+	}
+
+	cur_size = frm->cur_size;
+	size = dev->req_image_size[dev->cc];
+
+	if ((copy_size + cur_size) > size) {
+		copy_size = size - cur_size;
+		btrunc = 1;
+	}
+
+	memcpy(pdest, psrc, copy_size);
+	cur_size += copy_size;
+	frm->cur_size += copy_size;
+	dprintk(50, "cur_size size %lu size %lu \n", cur_size, size);
+
+	if (cur_size >= (size - PREFIX_SIZE)) {
+		u32 cc = dev->cc;
+		frm->ulState = 2;
+		dprintk(2, "****************[%d]Buffer[%d]full*************\n",
+			cc, idx);
+		dev->last_frame[cc] = dev->cur_frame[cc];
+		dev->cur_frame[cc]++;
+		/* end of system frame ring buffer, start at zero */
+		if ((dev->cur_frame[cc] == SYS_FRAMES) ||
+		    (dev->cur_frame[cc] == dev->buffer[cc].dwFrames))
+			dev->cur_frame[cc] = 0;
+
+		/* signal the semaphore for this channel */
+		if (dev->b_acquire[cc])
+			s2255_got_frame(dev, cc);
+		dev->frame_count[cc]++;
+	}
+	/* frame was truncated */
+	if (btrunc) {
+		/* return more data to process */
+		return EAGAIN;
+	}
+	/* done successfully */
+	return 0;
+}
+
+static void s2255_read_video_callback(struct s2255_dev *dev,
+				      struct s2255_pipeinfo *pipe_info)
+{
+	int res;
+	dprintk(50, "callback read video \n");
+
+	if (dev->cc >= MAX_CHANNELS) {
+		dev->cc = 0;
+		dev_err(&dev->udev->dev, "invalid channel\n");
+		return;
+	}
+	/* otherwise copy to the system buffers */
+	res = save_frame(dev, pipe_info);
+	if (res == EAGAIN)
+		save_frame(dev, pipe_info);
+
+	dprintk(50, "callback read video done\n");
+	return;
+}
+
+static long s2255_vendor_req(struct s2255_dev *dev, unsigned char Request,
+			     u16 Index, u16 Value, void *TransferBuffer,
+			     s32 TransferBufferLength, int bOut)
+{
+	int r;
+	if (!bOut) {
+		r = usb_control_msg(dev->udev, usb_rcvctrlpipe(dev->udev, 0),
+				    Request,
+				    USB_TYPE_VENDOR | USB_RECIP_DEVICE |
+				    USB_DIR_IN,
+				    Value, Index, TransferBuffer,
+				    TransferBufferLength, HZ * 5);
+	} else {
+		r = usb_control_msg(dev->udev, usb_sndctrlpipe(dev->udev, 0),
+				    Request, USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+				    Value, Index, TransferBuffer,
+				    TransferBufferLength, HZ * 5);
+	}
+	return r;
+}
+
+/*
+ * retrieve FX2 firmware version. future use.
+ * @param dev pointer to device extension
+ * @return -1 for fail, else returns firmware version as an int(16 bits)
+ */
+static int s2255_get_fx2fw(struct s2255_dev *dev)
+{
+	int fw;
+	int ret;
+	unsigned char transBuffer[64];
+	ret = s2255_vendor_req(dev, VX_FW, 0, 0, transBuffer, 2, DIR_IN);
+	if (ret < 0)
+		dprintk(2, "get fw error: %x\n", ret);
+	fw = transBuffer[0] + (transBuffer[1] << 8);
+	dprintk(2, "Get FW %x %x\n", transBuffer[0], transBuffer[1]);
+	return fw;
+}
+
+/*
+ * Create the system ring buffer to copy frames into from the
+ * usb read pipe.
+ */
+static int s2255_create_sys_buffers(struct s2255_dev *dev, unsigned long chn)
+{
+	unsigned long i;
+	unsigned long reqsize;
+	dprintk(1, "create sys buffers\n");
+	if (chn >= MAX_CHANNELS)
+		return -1;
+
+	dev->buffer[chn].dwFrames = SYS_FRAMES;
+
+	/* always allocate maximum size(PAL) for system buffers */
+	reqsize = SYS_FRAMES_MAXSIZE;
+
+	if (reqsize > SYS_FRAMES_MAXSIZE)
+		reqsize = SYS_FRAMES_MAXSIZE;
+
+	for (i = 0; i < SYS_FRAMES; i++) {
+		/* allocate the frames */
+		dev->buffer[chn].frame[i].lpvbits = vmalloc(reqsize);
+
+		dprintk(1, "valloc %p chan %lu, idx %lu, pdata %p\n",
+			&dev->buffer[chn].frame[i], chn, i,
+			dev->buffer[chn].frame[i].lpvbits);
+		dev->buffer[chn].frame[i].size = reqsize;
+		if (dev->buffer[chn].frame[i].lpvbits == NULL) {
+			printk(KERN_INFO "out of memory.  using less frames\n");
+			dev->buffer[chn].dwFrames = i;
+			break;
+		}
+	}
+
+	/* make sure internal states are set */
+	for (i = 0; i < SYS_FRAMES; i++) {
+		dev->buffer[chn].frame[i].ulState = 0;
+		dev->buffer[chn].frame[i].cur_size = 0;
+	}
+
+	dev->cur_frame[chn] = 0;
+	dev->last_frame[chn] = -1;
+	return 0;
+}
+
+static int s2255_release_sys_buffers(struct s2255_dev *dev,
+				     unsigned long channel)
+{
+	unsigned long i;
+	dprintk(1, "release sys buffers\n");
+	for (i = 0; i < SYS_FRAMES; i++) {
+		if (dev->buffer[channel].frame[i].lpvbits) {
+			dprintk(1, "vfree %p\n",
+				dev->buffer[channel].frame[i].lpvbits);
+			vfree(dev->buffer[channel].frame[i].lpvbits);
+		}
+		dev->buffer[channel].frame[i].lpvbits = NULL;
+	}
+	return 0;
+}
+
+static int s2255_board_init(struct s2255_dev *dev)
+{
+	int j;
+	struct mode2255i mode_def = { DEF_MODEI_NTSC_CONT };
+	int fw_ver;
+	dprintk(4, "board init: %p", dev);
+
+	for (j = 0; j < MAX_PIPE_BUFFERS; j++) {
+		struct s2255_pipeinfo *pipe = &dev->pipes[j];
+
+		memset(pipe, 0, sizeof(*pipe));
+		pipe->dev = dev;
+		pipe->cur_transfer_size = DEFAULT_PIPE_USBBLOCK;
+		pipe->max_transfer_size = MAX_PIPE_USBBLOCK;
+
+		if (pipe->cur_transfer_size > pipe->max_transfer_size)
+			pipe->cur_transfer_size = pipe->max_transfer_size;
+		pipe->transfer_buffer = kzalloc(pipe->max_transfer_size,
+						GFP_KERNEL);
+		if (pipe->transfer_buffer == NULL) {
+			dprintk(1, "out of memory!\n");
+			return -ENOMEM;
+		}
+
+	}
+
+	/* query the firmware */
+	fw_ver = s2255_get_fx2fw(dev);
+
+	printk(KERN_INFO "2255 usb firmware version %d \n", fw_ver);
+	if (fw_ver < CUR_USB_FWVER)
+		err("usb firmware not up to date %d\n", fw_ver);
+
+	for (j = 0; j < MAX_CHANNELS; j++) {
+		dev->b_acquire[j] = 0;
+		dev->mode[j] = mode_def;
+		dev->cur_fmt[j] = &formats[0];
+		dev->mode[j].restart = 1;
+		dev->req_image_size[j] = get_transfer_size(&mode_def);
+		dev->frame_count[j] = 0;
+		/* create the system buffers */
+		s2255_create_sys_buffers(dev, j);
+	}
+	/* start read pipe */
+	s2255_start_readpipe(dev);
+
+	dprintk(1, "S2255: board initialized\n");
+	return 0;
+}
+
+static int s2255_board_shutdown(struct s2255_dev *dev)
+{
+	u32 i;
+
+	dprintk(1, "S2255: board shutdown: %p", dev);
+
+	for (i = 0; i < MAX_CHANNELS; i++) {
+		if (dev->b_acquire[i])
+			s2255_stop_acquire(dev, i);
+	}
+
+	s2255_stop_readpipe(dev);
+
+	for (i = 0; i < MAX_CHANNELS; i++)
+		s2255_release_sys_buffers(dev, i);
+
+	/* release transfer buffers */
+	for (i = 0; i < MAX_PIPE_BUFFERS; i++) {
+		struct s2255_pipeinfo *pipe = &dev->pipes[i];
+		kfree(pipe->transfer_buffer);
+	}
+	return 0;
+}
+
+static void read_pipe_completion(struct urb *purb)
+{
+	struct s2255_pipeinfo *pipe_info;
+	struct s2255_dev *dev;
+	int status;
+	int pipe;
+
+	pipe_info = purb->context;
+	dprintk(100, "read pipe completion %p, status %d\n", purb,
+		purb->status);
+	if (pipe_info == NULL) {
+		err("no context !");
+		return;
+	}
+
+	dev = pipe_info->dev;
+	if (dev == NULL) {
+		err("no context !");
+		return;
+	}
+	status = purb->status;
+	if (status != 0) {
+		dprintk(2, "read_pipe_completion: err\n");
+		return;
+	}
+
+	if (pipe_info->state == 0) {
+		dprintk(2, "exiting USB pipe");
+		return;
+	}
+
+	s2255_read_video_callback(dev, pipe_info);
+
+	pipe_info->err_count = 0;
+	pipe = usb_rcvbulkpipe(dev->udev, dev->read_endpoint);
+	/* reuse urb */
+	usb_fill_bulk_urb(pipe_info->stream_urb, dev->udev,
+			  pipe,
+			  pipe_info->transfer_buffer,
+			  pipe_info->cur_transfer_size,
+			  read_pipe_completion, pipe_info);
+
+	if (pipe_info->state != 0) {
+		if (usb_submit_urb(pipe_info->stream_urb, GFP_KERNEL)) {
+			dev_err(&dev->udev->dev, "error submitting urb\n");
+			usb_free_urb(pipe_info->stream_urb);
+		}
+	} else {
+		dprintk(2, "read pipe complete state 0\n");
+	}
+	return;
+}
+
+static int s2255_start_readpipe(struct s2255_dev *dev)
+{
+	int pipe;
+	int retval;
+	int i;
+	struct s2255_pipeinfo *pipe_info = dev->pipes;
+	pipe = usb_rcvbulkpipe(dev->udev, dev->read_endpoint);
+	dprintk(2, "start pipe IN %d\n", dev->read_endpoint);
+
+	for (i = 0; i < MAX_PIPE_BUFFERS; i++) {
+		pipe_info->state = 1;
+		pipe_info->buf_index = (u32) i;
+		pipe_info->priority_set = 0;
+		pipe_info->stream_urb = usb_alloc_urb(0, GFP_KERNEL);
+		if (!pipe_info->stream_urb) {
+			dev_err(&dev->udev->dev,
+				"ReadStream: Unable to alloc URB");
+			return -ENOMEM;
+		}
+		/* transfer buffer allocated in board_init */
+		usb_fill_bulk_urb(pipe_info->stream_urb, dev->udev,
+				  pipe,
+				  pipe_info->transfer_buffer,
+				  pipe_info->cur_transfer_size,
+				  read_pipe_completion, pipe_info);
+
+		pipe_info->urb_size = sizeof(pipe_info->stream_urb);
+		dprintk(4, "submitting URB %p\n", pipe_info->stream_urb);
+		retval = usb_submit_urb(pipe_info->stream_urb, GFP_KERNEL);
+		if (retval) {
+			printk(KERN_ERR "s2255: start read pipe failed\n");
+			return retval;
+		}
+	}
+
+	return 0;
+}
+
+/* starts acquisition process */
+static int s2255_start_acquire(struct s2255_dev *dev, unsigned long chn)
+{
+	unsigned char *buffer;
+	int res;
+	unsigned long chn_rev;
+	int j;
+	if (chn >= MAX_CHANNELS) {
+		dprintk(2, "start acquire failed, bad channel %lu\n", chn);
+		return -1;
+	}
+
+	chn_rev = G_chnmap[chn];
+	dprintk(1, "S2255: start acquire %lu \n", chn);
+
+	buffer = kzalloc(512, GFP_KERNEL);
+	if (buffer == NULL) {
+		dev_err(&dev->udev->dev, "out of mem\n");
+		return -ENOMEM;
+	}
+
+	dev->last_frame[chn] = -1;
+	dev->bad_payload[chn] = 0;
+	dev->cur_frame[chn] = 0;
+	for (j = 0; j < SYS_FRAMES; j++) {
+		dev->buffer[chn].frame[j].ulState = 0;
+		dev->buffer[chn].frame[j].cur_size = 0;
+	}
+
+	/* send the start command */
+	*(u32 *) buffer = IN_DATA_TOKEN;
+	*((u32 *) buffer + 1) = (u32) chn_rev;
+	*((u32 *) buffer + 2) = (u32) CMD_START;
+	res = s2255_write_config(dev->udev, (unsigned char *)buffer, 512);
+	if (res != 0)
+		dev_err(&dev->udev->dev, "CMD_START error\n");
+
+	dprintk(2, "start acquire exit[%lu] %d \n", chn, res);
+	kfree(buffer);
+	return 0;
+}
+
+static int s2255_stop_acquire(struct s2255_dev *dev, unsigned long chn)
+{
+	unsigned char *buffer;
+	int res;
+	unsigned long chn_rev;
+
+	if (chn >= MAX_CHANNELS) {
+		dprintk(2, "stop acquire failed, bad channel %lu\n", chn);
+		return -1;
+	}
+	chn_rev = G_chnmap[chn];
+
+	buffer = kzalloc(512, GFP_KERNEL);
+	if (buffer == NULL) {
+		dev_err(&dev->udev->dev, "out of mem\n");
+		return -ENOMEM;
+	}
+
+	/* send the stop command */
+	dprintk(4, "stop acquire %lu\n", chn);
+	*(u32 *) buffer = IN_DATA_TOKEN;
+	*((u32 *) buffer + 1) = (u32) chn_rev;
+	*((u32 *) buffer + 2) = CMD_STOP;
+	res = s2255_write_config(dev->udev, (unsigned char *)buffer, 512);
+
+	if (res != 0)
+		dev_err(&dev->udev->dev, "CMD_STOP error\n");
+
+	dprintk(4, "stop acquire: releasing states \n");
+
+	kfree(buffer);
+	dev->b_acquire[chn] = 0;
+
+	return 0;
+}
+
+static void s2255_stop_readpipe(struct s2255_dev *dev)
+{
+	int j;
+
+	if (dev == NULL) {
+		err("s2255: invalid device");
+		return;
+	}
+	dprintk(4, "stop read pipe\n");
+	for (j = 0; j < MAX_PIPE_BUFFERS; j++) {
+		struct s2255_pipeinfo *pipe_info = &dev->pipes[j];
+		if (pipe_info) {
+			if (pipe_info->state == 0)
+				continue;
+			pipe_info->state = 0;
+			pipe_info->prev_state = 1;
+
+		}
+	}
+
+	for (j = 0; j < MAX_PIPE_BUFFERS; j++) {
+		struct s2255_pipeinfo *pipe_info = &dev->pipes[j];
+		if (pipe_info->stream_urb) {
+			/* cancel urb */
+			usb_kill_urb(pipe_info->stream_urb);
+			usb_free_urb(pipe_info->stream_urb);
+			pipe_info->stream_urb = NULL;
+		}
+	}
+	dprintk(2, "s2255 stop read pipe: %d\n", j);
+	return;
+}
+
+/* standard usb probe function */
+static int s2255_probe(struct usb_interface *interface,
+		       const struct usb_device_id *id)
+{
+	struct s2255_dev *dev = NULL;
+	struct usb_host_interface *iface_desc;
+	struct usb_endpoint_descriptor *endpoint;
+	int i;
+	int retval = -ENOMEM;
+
+	dprintk(100, "s2255: probe\n");
+
+	/* allocate memory for our device state and initialize it to zero */
+	dev = kzalloc(sizeof(struct s2255_dev), GFP_KERNEL);
+	if (dev == NULL) {
+		err("s2255: out of memory");
+		goto error;
+	}
+
+	/* grab usb_device and save it */
+	dev->udev = usb_get_dev(interface_to_usbdev(interface));
+	if (dev->udev == NULL) {
+		dev_err(&interface->dev, "null usb device\n");
+		goto error;
+	}
+
+	kref_init(&dev->kref);
+	dprintk(1, "dev: %p, kref: %p udev %p interface %p\n", dev, &dev->kref,
+		dev->udev, interface);
+	dev->interface = interface;
+	/* set up the endpoint information  */
+	iface_desc = interface->cur_altsetting;
+	dprintk(1, "num endpoints %d\n", iface_desc->desc.bNumEndpoints);
+	for (i = 0; i < iface_desc->desc.bNumEndpoints; ++i) {
+		endpoint = &iface_desc->endpoint[i].desc;
+		if (!dev->read_endpoint && usb_endpoint_is_bulk_in(endpoint)) {
+			/* we found the bulk in endpoint */
+			dev->read_endpoint = endpoint->bEndpointAddress;
+		}
+	}
+
+	if (!dev->read_endpoint) {
+		dev_err(&interface->dev, "Could not find bulk-in endpoint");
+		goto error;
+	}
+
+	/* set intfdata */
+	usb_set_intfdata(interface, dev);
+
+	dprintk(100, "after intfdata %p\n", dev);
+
+	/* initialize device mutex */
+	mutex_init(&dev->lock);
+
+	init_timer(&dev->timer);
+	dev->timer.function = s2255_timer;
+	dev->fw_data = kzalloc(sizeof(struct complete_data), GFP_KERNEL);
+	if (!dev->fw_data)
+		goto error;
+
+	dev->timer.data = (unsigned long)dev->fw_data;
+	dev->fw_data->fw_urb = usb_alloc_urb(0, GFP_KERNEL);
+
+	if (!dev->fw_data->fw_urb) {
+		dev_err(&interface->dev, "out of memory!\n");
+		goto error;
+	}
+	dev->fw_data->pfw_data = kzalloc(CHUNK_SIZE, GFP_KERNEL);
+	if (!dev->fw_data->pfw_data) {
+		dev_err(&interface->dev, "out of memory!\n");
+		goto error;
+	}
+	/* load the first chunk */
+	if (request_firmware(&dev->fw_data->fw,
+			     FIRMWARE_FILE_NAME, &dev->udev->dev)) {
+		printk(KERN_ERR "sensoray 2255 failed to get firmware\n");
+		goto error;
+	}
+
+	dev->fw_data->fw_size = dev->fw_data->fw->size;
+	memcpy(dev->fw_data->pfw_data, dev->fw_data->fw->data, CHUNK_SIZE);
+	dev->fw_data->fw_loaded = CHUNK_SIZE;
+	usb_fill_bulk_urb(dev->fw_data->fw_urb, dev->udev,
+			  usb_sndbulkpipe(dev->udev, 2), dev->fw_data->pfw_data,
+			  CHUNK_SIZE, s2255_fwchunk_complete, dev->fw_data);
+	/* loads v4l specific */
+	s2255_probe_v4l(dev);
+	/* load 2255 board specific */
+	s2255_board_init(dev);
+
+	dev_info(&interface->dev, "Sensoray 2255 successfully loaded\n");
+	dprintk(4, "before probe done %p\n", dev);
+
+	mod_timer(&dev->timer, jiffies + HZ);
+	spin_lock_init(&dev->slock);
+	kref_get(&dev->kref);
+	return 0;
+error:
+	return retval;
+}
+
+/* disconnect routine. when board is removed physically or with rmmod */
+static void s2255_disconnect(struct usb_interface *interface)
+{
+	struct s2255_dev *dev = NULL;
+
+	/* lock to prevent s2255_open() from racing s2255_disconnect() */
+	mutex_lock(&usb_s2255_open_mutex);
+	dprintk(1, "s2255: disconnect interface %p\n", interface);
+	dev = usb_get_intfdata(interface);
+	s2255_board_shutdown(dev);
+	if (dev->fw_data->fw) {
+		release_firmware(dev->fw_data->fw);
+		dev->fw_data->fw = NULL;
+	}
+	if (dev->fw_data->fw_urb) {
+		dprintk(2, "kill URB\n");
+		usb_kill_urb(dev->fw_data->fw_urb);
+		usb_free_urb(dev->fw_data->fw_urb);
+
+	}
+	s2255_exit_v4l(dev);
+	if (dev->fw_data) {
+		kfree(dev->fw_data->pfw_data);
+		kfree(dev->fw_data);
+	}
+	usb_set_intfdata(interface, NULL);
+	kref_put(&dev->kref, s2255_destroy);
+	mutex_unlock(&usb_s2255_open_mutex);
+	dev_info(&interface->dev, "s2255usb now disconnected\n");
+}
+
+static struct usb_driver s2255_driver = {
+	.name = "s2255",
+	.probe = s2255_probe,
+	.disconnect = s2255_disconnect,
+	.id_table = s2255_table,
+};
+
+static int __init usb_s2255_init(void)
+{
+	int result;
+
+	/* register this driver with the USB subsystem */
+	result = usb_register(&s2255_driver);
+
+	if (result)
+		err("usb_register failed. Error number %d", result);
+
+	dprintk(2, "s2255_init: done\n");
+	return result;
+}
+
+static void __exit usb_s2255_exit(void)
+{
+	usb_deregister(&s2255_driver);
+}
+
+module_init(usb_s2255_init);
+module_exit(usb_s2255_exit);
+MODULE_DESCRIPTION("Sensoray 2255 Video for Linux driver");
+MODULE_AUTHOR("D.A.(Sensoray)");
+MODULE_LICENSE("GPL");


--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@...r.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Please read the FAQ at  http://www.tux.org/lkml/

Powered by blists - more mailing lists

Powered by Openwall GNU/*/Linux Powered by OpenVZ