/* -*- c-file-style: "linux" -*- */
/*
 * webcamstudio.c  --  video4linux2 loopback driver
 *
 * Copyright (C) 2005-2009 Vasily Levin (vasaka@gmail.com)
 * Copyright (C) 2010-2012 IOhannes m zmoelnig (zmoelnig@iem.at)
 * Copyright (C) 2011 Stefan Diewald (stefan.diewald@mytum.de)
 * Copyright (C) 2012 Anton Novikov (random.plant@gmail.com)
 *
 * 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.
 *
 * webcamstudio.c -- modified for WebcamStudio integration
 * from v0.9.1 of v4l2loopback (https://github.com/umlaeute/v4l2loopback)
 * commit f8cccb964727fdb697f6190986d76d750781ef81
 * by:
 * Patrick Balleux (patrick.balleux@gmail.com)
 * PhobosK (phobosk@kbfx.net)
 * Karl Ellis (soylent.tv@gmail.com)
 */
#include <linux/version.h>
#include <linux/vmalloc.h>
#include <linux/mm.h>
#include <linux/time.h>
#include <linux/module.h>
#include <linux/videodev2.h>
#include <linux/sched.h>
#include <linux/slab.h>
#include <media/v4l2-ioctl.h>
#include <media/v4l2-common.h>
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 29)
# define HAVE__V4L2_DEVICE
# include <media/v4l2-device.h>
#endif
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 36)
# define HAVE__V4L2_CTRLS
# include <media/v4l2-ctrls.h>
#endif

#if LINUX_VERSION_CODE < KERNEL_VERSION(3,6,1)
# define kstrtoul strict_strtoul
#endif

#define WEBCAMSTUDIO_VERSION_CODE KERNEL_VERSION(1,1,5)


MODULE_DESCRIPTION("WebcamStudio virtual video device");
MODULE_AUTHOR("Vasily Levin <vasaka@gmail.com>, " \
				"IOhannes m zmoelnig <zmoelnig@iem.at>, " \
				"Stefan Diewald <stefan.diewald@mytum.de>, " \
				"Anton Novikov <random.plant@gmail.com> " \
				"Modified by: " \
				"Patrick Balleux <patrick.balleux@gmail.com>, " \
				"PhobosK <phobosk@kbfx.net>, "
                                "Karl Ellis <soylent.tv@gmail.com>");
MODULE_LICENSE("GPL");

/*
 * helpers
 */
#define STRINGIFY(s) #s
#define STRINGIFY2(s) STRINGIFY(s)

#define dprintk(fmt, args...)                                           \
	do { if (debug > 0) {                                                 \
		printk(KERN_INFO "webcamstudio[" STRINGIFY2(__LINE__) "]: " fmt, ##args); \
	} } while (0)

#define MARK()                                                          \
	do { if (debug > 1) {                                                  \
		printk(KERN_INFO "%s:%d[%s]\n", __FILE__, __LINE__, __func__);	\
	} } while (0)

#define dprintkrw(fmt, args...)                                         \
	do { if (debug > 2) {                                                 \
		printk(KERN_INFO "webcamstudio[" STRINGIFY2(__LINE__)"]: " fmt, ##args); \
	} } while (0)

/*
 * compatibility hacks
 */

#ifndef HAVE__V4L2_CTRLS
struct v4l2_ctrl_handler {
	int error;
};
struct v4l2_ctrl_config {
        void *ops;
        u32 id;
	const char *name;
	int type;
        s32 min;
        s32 max;
        u32 step;
        s32 def;
};
int v4l2_ctrl_handler_init(struct v4l2_ctrl_handler*hdl,
			   unsigned nr_of_controls_hint)
{
	hdl->error=0;
	return 0;
}
void v4l2_ctrl_handler_free(struct v4l2_ctrl_handler *hdl)
{
}
void*v4l2_ctrl_new_custom(struct v4l2_ctrl_handler *hdl,
			  const struct v4l2_ctrl_config*conf,
			  void*priv)
{
	return NULL;
}
#endif /* HAVE__V4L2_CTRLS */


#ifndef HAVE__V4L2_DEVICE
/* dummy v4l2_device struct/functions */
# define V4L2_DEVICE_NAME_SIZE (20 + 16)
struct v4l2_device {
	char name[V4L2_DEVICE_NAME_SIZE];
	struct v4l2_ctrl_handler*ctrl_handler;
};
static inline int  v4l2_device_register  (void *dev, void *v4l2_dev) { return 0; }
static inline void v4l2_device_unregister(struct v4l2_device *v4l2_dev) { return; }
#endif /*  HAVE__V4L2_DEVICE */

#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 29)
# define v4l2_file_operations file_operations
#endif
#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 37)
void *v4l2l_vzalloc(unsigned long size)
{
	void *data = vmalloc(size);

	memset(data, 0, size);
	return data;
}
#else
# define v4l2l_vzalloc vzalloc
#endif


/* module constants
 *  can be overridden during he build process using something like
 *      make KCPPFLAGS="-DMAX_DEVICES=100"
 */


/* maximum number of v4l2loopback devices that can be created */
#ifndef MAX_DEVICES
# define MAX_DEVICES 8
#endif

/* when a producer is considered to have gone stale */
#ifndef MAX_TIMEOUT
# define MAX_TIMEOUT (100 * 1000) /* in msecs */
#endif

/* max buffers that can be mapped, actually they
 * are all mapped to max_buffers buffers */
#ifndef MAX_BUFFERS
# define MAX_BUFFERS 32
#endif

/* module parameters */
static int debug = 0;
module_param(debug, int, S_IRUGO | S_IWUSR);
MODULE_PARM_DESC(debug, "debugging level (higher values == more verbose)");

static int max_buffers = 8;
module_param(max_buffers, int, S_IRUGO);
MODULE_PARM_DESC(max_buffers, "how many buffers should be allocated");

/* how many times a device can be opened
 * the per-module default value can be overridden on a per-device basis using
 * the /sys/devices interface
 *
 * note that max_openers should be at least 2 in order to get a working system:
 *   one opener for the producer and one opener for the consumer
 *   however, we leave that to the user
 */
static int max_openers = 10;
module_param(max_openers, int, S_IRUGO | S_IWUSR);
MODULE_PARM_DESC(max_openers, "how many users can open loopback device");


static int devices = -1;
module_param(devices, int, 0);
MODULE_PARM_DESC(devices, "how many devices should be created");


static int video_nr[MAX_DEVICES] = { [0 ... (MAX_DEVICES - 1)] = -1 };
module_param_array(video_nr, int, NULL, 0444);
MODULE_PARM_DESC(video_nr, "video device numbers (-1=auto, 0=/dev/video0, etc.)");

static char *card_label[MAX_DEVICES];
module_param_array(card_label, charp, NULL, 0000);
MODULE_PARM_DESC(card_label, "card labels for every device");

static bool exclusive_caps[MAX_DEVICES] = { [0 ... (MAX_DEVICES - 1)] = 1 };
module_param_array(exclusive_caps, bool, NULL, 0444);
/* FIXXME: wording */
MODULE_PARM_DESC(exclusive_caps, "whether to announce OUTPUT/CAPTURE capabilities exclusively or not");


/* format specifications */
#define WEBCAMSTUDIO_SIZE_MIN_WIDTH   48
#define WEBCAMSTUDIO_SIZE_MIN_HEIGHT  32
#define WEBCAMSTUDIO_SIZE_MAX_WIDTH   8192
#define WEBCAMSTUDIO_SIZE_MAX_HEIGHT  8192

#define WEBCAMSTUDIO_SIZE_DEFAULT_WIDTH   640
#define WEBCAMSTUDIO_SIZE_DEFAULT_HEIGHT  480

static int max_width = WEBCAMSTUDIO_SIZE_MAX_WIDTH;
module_param(max_width, int, S_IRUGO);
MODULE_PARM_DESC(max_width, "maximum frame width");
static int max_height = WEBCAMSTUDIO_SIZE_MAX_HEIGHT;
module_param(max_height, int, S_IRUGO);
MODULE_PARM_DESC(max_height, "maximum frame height");


/* control IDs */
#ifndef HAVE__V4L2_CTRLS
# define V4L2LOOPBACK_CID_BASE  (V4L2_CID_PRIVATE_BASE)
#else
# define V4L2LOOPBACK_CID_BASE  (V4L2_CID_USER_BASE | 0xf000)
#endif
#define CID_KEEP_FORMAT        (V4L2LOOPBACK_CID_BASE + 0)
#define CID_SUSTAIN_FRAMERATE  (V4L2LOOPBACK_CID_BASE + 1)
#define CID_TIMEOUT            (V4L2LOOPBACK_CID_BASE + 2)
#define CID_TIMEOUT_IMAGE_IO   (V4L2LOOPBACK_CID_BASE + 3)

static int webcamstudio_s_ctrl(struct v4l2_ctrl *ctrl);
static const struct v4l2_ctrl_ops v4l2loopback_ctrl_ops = {
	.s_ctrl = webcamstudio_s_ctrl,
};
static const struct v4l2_ctrl_config v4l2loopback_ctrl_keepformat = {
        .ops = &v4l2loopback_ctrl_ops,
        .id = CID_KEEP_FORMAT,
        .name = "keep_format",
        .type = V4L2_CTRL_TYPE_BOOLEAN,
        .min = 0,
        .max = 1,
        .step = 1,
        .def = 0,
};
static const struct v4l2_ctrl_config v4l2loopback_ctrl_sustainframerate = {
        .ops = &v4l2loopback_ctrl_ops,
        .id = CID_SUSTAIN_FRAMERATE,
        .name = "sustain_framerate",
        .type = V4L2_CTRL_TYPE_BOOLEAN,
        .min = 0,
        .max = 1,
        .step = 1,
        .def = 0,
};
static const struct v4l2_ctrl_config v4l2loopback_ctrl_timeout = {
        .ops = &v4l2loopback_ctrl_ops,
        .id = CID_TIMEOUT,
        .name = "timeout",
        .type = V4L2_CTRL_TYPE_INTEGER,
        .min = 0,
        .max = MAX_TIMEOUT,
        .step = 1,
        .def = 0,
};
static const struct v4l2_ctrl_config v4l2loopback_ctrl_timeoutimageio = {
        .ops = &v4l2loopback_ctrl_ops,
        .id = CID_TIMEOUT_IMAGE_IO,
        .name = "timeout_image_io",
        .type = V4L2_CTRL_TYPE_BOOLEAN,
        .min = 0,
        .max = 1,
        .step = 1,
        .def = 0,
};


/* module structures */
struct webcamstudio_private {
	int devicenr;
};

/* TODO(vasaka) use typenames which are common to kernel, but first find out if
 * it is needed */
/* struct keeping state and settings of loopback device */

struct v4l2l_buffer {
	struct v4l2_buffer buffer;
	struct list_head list_head;
	int use_count;
};

struct webcamstudio_device {
	struct v4l2_device v4l2_dev;
	struct v4l2_ctrl_handler ctrl_handler;
	struct video_device *vdev;
	/* pixel and stream format */
	struct v4l2_pix_format pix_format;
	struct v4l2_captureparm capture_param;
	unsigned long frame_jiffies;

	/* ctrls */
	int keep_format; /* CID_KEEP_FORMAT; stay ready_for_capture even when all
			    openers close() the device */
	int sustain_framerate; /* CID_SUSTAIN_FRAMERATE; duplicate frames to maintain
				  (close to) nominal framerate */

	/* buffers stuff */
	u8 *image;         /* pointer to actual buffers data */
	unsigned long int imagesize;  /* size of buffers data */
	int buffers_number;  /* should not be big, 4 is a good choice */
	struct v4l2l_buffer buffers[MAX_BUFFERS];	/* inner driver buffers */
	int used_buffers; /* number of the actually used buffers */
	int max_openers;  /* how many times can this device be opened */

	int write_position; /* number of last written frame + 1 */
	struct list_head outbufs_list; /* buffers in output DQBUF order */
	int bufpos2index[MAX_BUFFERS]; /* mapping of (read/write_position % used_buffers)
					* to inner buffer index */
	long buffer_size;

	/* sustain_framerate stuff */
	struct timer_list sustain_timer;
	unsigned int reread_count;

	/* timeout stuff */
	unsigned long timeout_jiffies; /* CID_TIMEOUT; 0 means disabled */
	int timeout_image_io; /* CID_TIMEOUT_IMAGE_IO; next opener will
			       * read/write to timeout_image */
	u8 *timeout_image; /* copy of it will be captured when timeout passes */
	struct v4l2l_buffer timeout_image_buffer;
	struct timer_list timeout_timer;
	int timeout_happened;

	/* sync stuff */
	atomic_t open_count;


	int ready_for_capture;/* set to true when at least one writer opened
			       * device and negotiated format */
	int ready_for_output; /* set to true when no writer is currently attached
			       * this differs slightly from !ready_for_capture,
			       * e.g. when using fallback images */
	int announce_all_caps;/* set to false, if device caps (OUTPUT/CAPTURE)
			       * should only be announced if the resp. "ready"
			       * flag is set; default=TRUE */

	wait_queue_head_t read_event;
	spinlock_t lock;
};

/* types of opener shows what opener wants to do with loopback */
enum opener_type {
	UNNEGOTIATED = 0,
	READER = 1,
	WRITER = 2,
};

/* struct keeping state and type of opener */
struct webcamstudio_opener {
	enum opener_type type;
	int vidioc_enum_frameintervals_calls;
	int read_position; /* number of last processed frame + 1 or
			    * write_position - 1 if reader went out of sync */
	unsigned int reread_count;
	struct v4l2_buffer *buffers;
	int buffers_number;  /* should not be big, 4 is a good choice */
	int timeout_image_io;
};

/* this is heavily inspired by the bttv driver found in the linux kernel */
struct v4l2l_format {
	char *name;
	int  fourcc;          /* video4linux 2      */
	int  depth;           /* bit/pixel          */
	int  flags;
};
/* set the v4l2l_format.flags to PLANAR for non-packed formats */
#define FORMAT_FLAGS_PLANAR       0x01
#define FORMAT_FLAGS_COMPRESSED   0x02

static const struct v4l2l_format formats[] = {
  /* here come the packed formats */
{
   .name     = "32 bpp RGB, le",
     .fourcc   = V4L2_PIX_FMT_BGR32,
     .depth    = 32,
     .flags    = 0,
     },
{
   .name     = "32 bpp RGB, be",
     .fourcc   = V4L2_PIX_FMT_RGB32,
     .depth    = 32,
     .flags    = 0,
     },
{
   .name     = "24 bpp RGB, le",
     .fourcc   = V4L2_PIX_FMT_BGR24,
     .depth    = 24,
     .flags    = 0,
     },
{
   .name     = "24 bpp RGB, be",
     .fourcc   = V4L2_PIX_FMT_RGB24,
     .depth    = 24,
     .flags    = 0,
     },
#ifdef V4L2_PIX_FMT_RGB332
{
   .name     = "8 bpp RGB-3-3-2",
     .fourcc   = V4L2_PIX_FMT_RGB332,
     .depth    = 8,
     .flags    = 0,
     },
#endif /* V4L2_PIX_FMT_RGB332 */
#ifdef V4L2_PIX_FMT_RGB444
{
   .name     = "16 bpp RGB (xxxxrrrr ggggbbbb)",
     .fourcc   = V4L2_PIX_FMT_RGB444,
     .depth    = 16,
     .flags    = 0,
     },
#endif /* V4L2_PIX_FMT_RGB444 */
#ifdef V4L2_PIX_FMT_RGB555
{
   .name     = "16 bpp RGB-5-5-5",
     .fourcc   = V4L2_PIX_FMT_RGB555,
     .depth    = 16,
     .flags    = 0,
     },
#endif /* V4L2_PIX_FMT_RGB555 */
#ifdef V4L2_PIX_FMT_RGB565
{
   .name     = "16 bpp  RGB-5-6-5",
     .fourcc   = V4L2_PIX_FMT_RGB565,
     .depth    = 16,
     .flags    = 0,
     },
#endif /* V4L2_PIX_FMT_RGB565 */
#ifdef V4L2_PIX_FMT_RGB555X
{
   .name     = "16 bpp RGB-5-5-5 BE",
     .fourcc   = V4L2_PIX_FMT_RGB555X,
     .depth    = 16,
     .flags    = 0,
     },
#endif /* V4L2_PIX_FMT_RGB555X */
#ifdef V4L2_PIX_FMT_RGB565X
{
   .name     = "16 bpp RGB-5-6-5 BE",
     .fourcc   = V4L2_PIX_FMT_RGB565X,
     .depth    = 16,
     .flags    = 0,
     },
#endif /* V4L2_PIX_FMT_RGB565X */
#ifdef V4L2_PIX_FMT_BGR666
{
   .name     = "18 bpp BGR-6-6-6",
     .fourcc   = V4L2_PIX_FMT_BGR666,
     .depth    = 18,
     .flags    = 0,
     },
#endif /* V4L2_PIX_FMT_BGR666 */
{
   .name     = "4:2:2, packed, YUYV",
     .fourcc   = V4L2_PIX_FMT_YUYV,
     .depth    = 16,
     .flags    = 0,
     },
{
   .name     = "4:2:2, packed, UYVY",
     .fourcc   = V4L2_PIX_FMT_UYVY,
     .depth    = 16,
     .flags    = 0,
     },
#ifdef V4L2_PIX_FMT_YVYU
{
   .name     = "4:2:2, packed YVYU",
     .fourcc   = V4L2_PIX_FMT_YVYU,
     .depth    = 16,
     .flags    = 0,
     },
#endif
#ifdef V4L2_PIX_FMT_VYUY
{
   .name     = "4:2:2, packed VYUY",
     .fourcc   = V4L2_PIX_FMT_VYUY,
     .depth    = 16,
     .flags    = 0,
     },
#endif
{
   .name     = "4:2:2, packed YYUV",
     .fourcc   = V4L2_PIX_FMT_YYUV,
     .depth    = 16,
     .flags    = 0,
     },
{
   .name     = "YUV-8-8-8-8",
     .fourcc   = V4L2_PIX_FMT_YUV32,
     .depth    = 32,
     .flags    = 0,
     },
{
   .name     = "8 bpp, Greyscale",
     .fourcc   = V4L2_PIX_FMT_GREY,
     .depth    = 8,
     .flags    = 0,
     },
#ifdef V4L2_PIX_FMT_Y4
{
   .name     = "4 bpp Greyscale",
     .fourcc   = V4L2_PIX_FMT_Y4,
     .depth    = 4,
     .flags    = 0,
     },
#endif /* V4L2_PIX_FMT_Y4 */
#ifdef V4L2_PIX_FMT_Y6
{
   .name     = "6 bpp Greyscale",
     .fourcc   = V4L2_PIX_FMT_Y6,
     .depth    = 6,
     .flags    = 0,
     },
#endif /* V4L2_PIX_FMT_Y6 */
#ifdef V4L2_PIX_FMT_Y10
{
   .name     = "10 bpp Greyscale",
     .fourcc   = V4L2_PIX_FMT_Y10,
     .depth    = 10,
     .flags    = 0,
     },
#endif /* V4L2_PIX_FMT_Y10 */
#ifdef V4L2_PIX_FMT_Y12
{
   .name     = "12 bpp Greyscale",
     .fourcc   = V4L2_PIX_FMT_Y12,
     .depth    = 12,
     .flags    = 0,
     },
#endif /* V4L2_PIX_FMT_Y12 */
{
   .name     = "16 bpp, Greyscale",
     .fourcc   = V4L2_PIX_FMT_Y16,
     .depth    = 16,
     .flags    = 0,
     },
#ifdef V4L2_PIX_FMT_YUV444
{
   .name     = "16 bpp xxxxyyyy uuuuvvvv",
     .fourcc   = V4L2_PIX_FMT_YUV444,
     .depth    = 16,
     .flags    = 0,
     },
#endif /* V4L2_PIX_FMT_YUV444 */
#ifdef V4L2_PIX_FMT_YUV555
{
   .name     = "16 bpp YUV-5-5-5",
     .fourcc   = V4L2_PIX_FMT_YUV555,
     .depth    = 16,
     .flags    = 0,
     },
#endif /* V4L2_PIX_FMT_YUV555 */
#ifdef V4L2_PIX_FMT_YUV565
{
   .name     = "16 bpp YUV-5-6-5",
     .fourcc   = V4L2_PIX_FMT_YUV565,
     .depth    = 16,
     .flags    = 0,
     },
#endif /* V4L2_PIX_FMT_YUV565 */


 /* here come the planar formats */
{
   .name     = "4:1:0, planar, Y-Cr-Cb",
     .fourcc   = V4L2_PIX_FMT_YVU410,
     .depth    = 9,
     .flags    = FORMAT_FLAGS_PLANAR,
     },
{
   .name     = "4:2:0, planar, Y-Cr-Cb",
     .fourcc   = V4L2_PIX_FMT_YVU420,
     .depth    = 12,
     .flags    = FORMAT_FLAGS_PLANAR,
     },
{
   .name     = "4:1:0, planar, Y-Cb-Cr",
     .fourcc   = V4L2_PIX_FMT_YUV410,
     .depth    = 9,
     .flags    = FORMAT_FLAGS_PLANAR,
     },
{
   .name     = "4:2:0, planar, Y-Cb-Cr",
     .fourcc   = V4L2_PIX_FMT_YUV420,
     .depth    = 12,
     .flags    = FORMAT_FLAGS_PLANAR,
     },
#ifdef V4L2_PIX_FMT_YUV422P
{
   .name     = "16 bpp YVU422 planar",
     .fourcc   = V4L2_PIX_FMT_YUV422P,
     .depth    = 16,
     .flags    = FORMAT_FLAGS_PLANAR,
     },
#endif /* V4L2_PIX_FMT_YUV422P */
#ifdef V4L2_PIX_FMT_YUV411P
{
   .name     = "16 bpp YVU411 planar",
     .fourcc   = V4L2_PIX_FMT_YUV411P,
     .depth    = 16,
     .flags    = FORMAT_FLAGS_PLANAR,
     },
#endif /* V4L2_PIX_FMT_YUV411P */
#ifdef V4L2_PIX_FMT_Y41P
{
   .name     = "12 bpp YUV 4:1:1",
     .fourcc   = V4L2_PIX_FMT_Y41P,
     .depth    = 12,
     .flags    = FORMAT_FLAGS_PLANAR,
     },
#endif /* V4L2_PIX_FMT_Y41P */

 /* here come the compressed formats */

#ifdef V4L2_PIX_FMT_MJPEG
{
   .name     = "Motion-JPEG",
     .fourcc   = V4L2_PIX_FMT_MJPEG,
     .depth    = 32,
     .flags    = FORMAT_FLAGS_COMPRESSED,
     },
#endif /* V4L2_PIX_FMT_MJPEG */
#ifdef V4L2_PIX_FMT_JPEG
{
   .name     = "JFIF JPEG",
     .fourcc   = V4L2_PIX_FMT_JPEG,
     .depth    = 32,
     .flags    = FORMAT_FLAGS_COMPRESSED,
     },
#endif /* V4L2_PIX_FMT_JPEG */
#ifdef V4L2_PIX_FMT_DV
{
   .name     = "DV1394",
     .fourcc   = V4L2_PIX_FMT_DV,
     .depth    = 32,
     .flags    = FORMAT_FLAGS_COMPRESSED,
     },
#endif /* V4L2_PIX_FMT_DV */
#ifdef V4L2_PIX_FMT_MPEG
{
   .name     = "MPEG-1/2/4 Multiplexed",
     .fourcc   = V4L2_PIX_FMT_MPEG,
     .depth    = 32,
     .flags    = FORMAT_FLAGS_COMPRESSED,
#endif /* V4L2_PIX_FMT_MPEG */
#ifdef V4L2_PIX_FMT_H264
     },
{
   .name     = "H264 with start codes",
     .fourcc   = V4L2_PIX_FMT_H264,
     .depth    = 32,
     .flags    = FORMAT_FLAGS_COMPRESSED,
     },
#endif /* V4L2_PIX_FMT_H264 */
#ifdef V4L2_PIX_FMT_H264_NO_SC
{
   .name     = "H264 without start codes",
     .fourcc   = V4L2_PIX_FMT_H264_NO_SC,
     .depth    = 32,
     .flags    = FORMAT_FLAGS_COMPRESSED,
     },
#endif /* V4L2_PIX_FMT_H264_NO_SC */
#ifdef V4L2_PIX_FMT_H264_MVC
{
   .name     = "H264 MVC",
     .fourcc   = V4L2_PIX_FMT_H264_MVC,
     .depth    = 32,
     .flags    = FORMAT_FLAGS_COMPRESSED,
     },
#endif /* V4L2_PIX_FMT_H264_MVC */
#ifdef V4L2_PIX_FMT_H263
{
   .name     = "H263",
     .fourcc   = V4L2_PIX_FMT_H263,
     .depth    = 32,
     .flags    = FORMAT_FLAGS_COMPRESSED,
     },
#endif /* V4L2_PIX_FMT_H263 */
#ifdef V4L2_PIX_FMT_MPEG1
{
   .name     = "MPEG-1 ES",
     .fourcc   = V4L2_PIX_FMT_MPEG1,
     .depth    = 32,
     .flags    = FORMAT_FLAGS_COMPRESSED,
     },
#endif /* V4L2_PIX_FMT_MPEG1 */
#ifdef V4L2_PIX_FMT_MPEG2
{
   .name     = "MPEG-2 ES",
     .fourcc   = V4L2_PIX_FMT_MPEG2,
     .depth    = 32,
     .flags    = FORMAT_FLAGS_COMPRESSED,
     },
#endif /* V4L2_PIX_FMT_MPEG2 */
#ifdef V4L2_PIX_FMT_MPEG4
{
   .name     = "MPEG-4 part 2 ES",
     .fourcc   = V4L2_PIX_FMT_MPEG4,
     .depth    = 32,
     .flags    = FORMAT_FLAGS_COMPRESSED,
     },
#endif /* V4L2_PIX_FMT_MPEG4 */
#ifdef V4L2_PIX_FMT_XVID
{
   .name     = "Xvid",
     .fourcc   = V4L2_PIX_FMT_XVID,
     .depth    = 32,
     .flags    = FORMAT_FLAGS_COMPRESSED,
     },
#endif /* V4L2_PIX_FMT_XVID */
#ifdef V4L2_PIX_FMT_VC1_ANNEX_G
{
   .name     = "SMPTE 421M Annex G compliant stream",
     .fourcc   = V4L2_PIX_FMT_VC1_ANNEX_G,
     .depth    = 32,
     .flags    = FORMAT_FLAGS_COMPRESSED,
     },
#endif /* V4L2_PIX_FMT_VC1_ANNEX_G */
#ifdef V4L2_PIX_FMT_VC1_ANNEX_L
{
   .name     = "SMPTE 421M Annex L compliant stream",
     .fourcc   = V4L2_PIX_FMT_VC1_ANNEX_L,
     .depth    = 32,
     .flags    = FORMAT_FLAGS_COMPRESSED,
     },
#endif /* V4L2_PIX_FMT_VC1_ANNEX_L */
#ifdef V4L2_PIX_FMT_VP8
{
   .name     = "VP8",
     .fourcc   = V4L2_PIX_FMT_VP8,
     .depth    = 32,
     .flags    = FORMAT_FLAGS_COMPRESSED,
     },
#endif /* V4L2_PIX_FMT_VP8 */
};

static const unsigned int FORMATS = ARRAY_SIZE(formats);


static char *fourcc2str(unsigned int fourcc, char buf[4])
{
	buf[0] = (fourcc >>  0) & 0xFF;
	buf[1] = (fourcc >>  8) & 0xFF;
	buf[2] = (fourcc >> 16) & 0xFF;
	buf[3] = (fourcc >> 24) & 0xFF;

	return buf;
}

static const struct v4l2l_format *format_by_fourcc(int fourcc)
{
	unsigned int i;

	for (i = 0; i < FORMATS; i++) {
		if (formats[i].fourcc == fourcc)
			return formats + i;
	}

	dprintk("unsupported format '%c%c%c%c'\n",
			(fourcc >>  0) & 0xFF,
			(fourcc >>  8) & 0xFF,
			(fourcc >> 16) & 0xFF,
			(fourcc >> 24) & 0xFF);
	return NULL;
}

static void pix_format_set_size(struct v4l2_pix_format *f,
		const struct v4l2l_format *fmt,
		unsigned int width, unsigned int height)
{
	f->width = width;
	f->height = height;

	if (fmt->flags & FORMAT_FLAGS_PLANAR) {
		f->bytesperline = width; /* Y plane */
		f->sizeimage = (width * height * fmt->depth) >> 3;
	} else if (fmt->flags & FORMAT_FLAGS_COMPRESSED) {
	        /* doesn't make sense for compressed formats */
		f->bytesperline = 0;
		f->sizeimage = (width * height * fmt->depth) >> 3;
	} else {
		f->bytesperline = (width * fmt->depth) >> 3;
		f->sizeimage = height * f->bytesperline;
	}
}

static int set_timeperframe(struct webcamstudio_device *dev,
		struct v4l2_fract *tpf)
{
        if((tpf->denominator < 1) || (tpf->numerator < 1)) {
	  return -EINVAL;
	}
	dev->capture_param.timeperframe = *tpf;
	dev->frame_jiffies = max(1UL,
		msecs_to_jiffies(1000) * tpf->numerator / tpf->denominator);
	return 0;
}

static struct webcamstudio_device *webcamstudio_cd2dev(struct device *cd);

/* device attributes */
/* available via sysfs: /sys/devices/virtual/video4linux/video* */

static ssize_t attr_show_format(struct device *cd,
		struct device_attribute *attr, char *buf)
{
	/* gets the current format as "FOURCC:WxH@f/s", e.g. "YUYV:320x240@1000/30" */
	struct webcamstudio_device *dev = webcamstudio_cd2dev(cd);
	const struct v4l2_fract *tpf;
	char buf4cc[5], buf_fps[32];

	if (!dev || !dev->ready_for_capture)
		return 0;
	tpf = &dev->capture_param.timeperframe;

	fourcc2str(dev->pix_format.pixelformat, buf4cc);
	if (tpf->numerator == 1)
		snprintf(buf_fps, sizeof(buf_fps), "%d", tpf->denominator);
	else
		snprintf(buf_fps, sizeof(buf_fps), "%d/%d",
				tpf->denominator, tpf->numerator);

	return sprintf(buf, "%4s:%dx%d@%s\n",
		buf4cc, dev->pix_format.width, dev->pix_format.height, buf_fps);
}

static ssize_t attr_store_format(struct device *cd,
		struct device_attribute *attr, const char *buf, size_t len)
{
	struct webcamstudio_device *dev = webcamstudio_cd2dev(cd);
	int fps_num = 0, fps_den = 1;

	/* only fps changing is supported */
	if (sscanf(buf, "@%d/%d", &fps_num, &fps_den) > 0) {
		struct v4l2_fract f = {
			.numerator   = fps_den,
			.denominator = fps_num
		};
		int err=0;
		if((err=set_timeperframe(dev, &f)) < 0)
			return err;
		return len;
	}
	return -EINVAL;
}

static DEVICE_ATTR(format, S_IRUGO | S_IWUSR, attr_show_format, attr_store_format);

static ssize_t attr_show_buffers(struct device *cd,
		struct device_attribute *attr, char *buf)
{
	struct webcamstudio_device *dev = webcamstudio_cd2dev(cd);

	return sprintf(buf, "%d\n", dev->used_buffers);
}

static DEVICE_ATTR(buffers, S_IRUGO, attr_show_buffers, NULL);

static ssize_t attr_show_maxopeners(struct device *cd,
		struct device_attribute *attr, char *buf)
{
	struct webcamstudio_device *dev = webcamstudio_cd2dev(cd);

	return sprintf(buf, "%d\n", dev->max_openers);
}

static ssize_t attr_store_maxopeners(struct device *cd,
		struct device_attribute *attr, const char *buf, size_t len)
{
	struct webcamstudio_device *dev = NULL;
	unsigned long curr = 0;

	if (kstrtoul(buf, 0, &curr))
		return -EINVAL;
	
	dev = webcamstudio_cd2dev(cd);

	if (dev->max_openers == curr)
		return len;

	if (dev->open_count.counter > curr) {
		/* request to limit to less openers as are currently attached to us */
		return -EINVAL;
	}

	dev->max_openers = (int)curr;

	return len;
}


static DEVICE_ATTR(max_openers, S_IRUGO | S_IWUSR, attr_show_maxopeners, attr_store_maxopeners);

static void webcamstudio_remove_sysfs(struct video_device *vdev)
{
#define V4L2_SYSFS_DESTROY(x) device_remove_file(&vdev->dev, &dev_attr_##x)

	if (vdev) {
		V4L2_SYSFS_DESTROY(format);
		V4L2_SYSFS_DESTROY(buffers);
		V4L2_SYSFS_DESTROY(max_openers);
		/* ... */
	}
}

static void webcamstudio_create_sysfs(struct video_device *vdev)
{
	int res = 0;

#define V4L2_SYSFS_CREATE(x)     res = device_create_file(&vdev->dev, &dev_attr_##x); if (res < 0) break
	if (!vdev)
		return;
	do {
		V4L2_SYSFS_CREATE(format);
		V4L2_SYSFS_CREATE(buffers);
		V4L2_SYSFS_CREATE(max_openers);
		/* ... */
	} while (0);

	if (res >= 0)
		return;
	dev_err(&vdev->dev, "%s error: %d\n", __func__, res);
}

/* global module data */
struct webcamstudio_device *devs[MAX_DEVICES];

static struct webcamstudio_device *webcamstudio_cd2dev(struct device *cd)
{
	struct video_device *loopdev = to_video_device(cd);
	struct webcamstudio_private *ptr =
		(struct webcamstudio_private *)video_get_drvdata(loopdev);
	int nr = ptr->devicenr;

	if (nr < 0 || nr >= devices) {
		printk(KERN_ERR "webcamstudio: illegal device %d\n", nr);
		return NULL;
	}
	return devs[nr];
}

static struct webcamstudio_device *webcamstudio_getdevice(struct file *f)
{
	struct video_device *loopdev = video_devdata(f);
	struct webcamstudio_private *ptr =
		(struct webcamstudio_private *)video_get_drvdata(loopdev);
	int nr = ptr->devicenr;

	if (nr < 0 || nr >= devices) {
		printk(KERN_ERR "webcamstudio: illegal device %d\n",nr);
		return NULL;
	}
	return devs[nr];
}

/* forward declarations */
static void init_buffers(struct webcamstudio_device *dev);
static int allocate_buffers(struct webcamstudio_device *dev);
static int free_buffers(struct webcamstudio_device *dev);
static void try_free_buffers(struct webcamstudio_device *dev);
static int allocate_timeout_image(struct webcamstudio_device *dev);
static void check_timers(struct webcamstudio_device *dev);
static const struct v4l2_file_operations webcamstudio_fops;
static const struct v4l2_ioctl_ops webcamstudio_ioctl_ops;

/* Queue helpers */
/* next functions sets buffer flags and adjusts counters accordingly */
static inline void set_done(struct v4l2l_buffer *buffer)
{
	buffer->buffer.flags &= ~V4L2_BUF_FLAG_QUEUED;
	buffer->buffer.flags |= V4L2_BUF_FLAG_DONE;
}

static inline void set_queued(struct v4l2l_buffer *buffer)
{
	buffer->buffer.flags &= ~V4L2_BUF_FLAG_DONE;
	buffer->buffer.flags |= V4L2_BUF_FLAG_QUEUED;
}

static inline void unset_flags(struct v4l2l_buffer *buffer)
{
	buffer->buffer.flags &= ~V4L2_BUF_FLAG_QUEUED;
	buffer->buffer.flags &= ~V4L2_BUF_FLAG_DONE;
}

/* V4L2 ioctl caps and params calls */
/* returns device capabilities
 * called on VIDIOC_QUERYCAP
 */
static int vidioc_querycap(struct file *file, void *priv, struct v4l2_capability *cap)
{
	struct webcamstudio_device *dev = webcamstudio_getdevice(file);
	int devnr = ((struct webcamstudio_private *)video_get_drvdata(dev->vdev))->devicenr;

	strlcpy(cap->driver, "WebcamStudio", sizeof(cap->driver));

	if (card_label[devnr] != NULL) {
		snprintf(cap->card, sizeof(cap->card), card_label[devnr]);
	} else {
	snprintf(cap->card, sizeof(cap->card), "WSVideo (0x%04X)", devnr);
	}

	snprintf(cap->bus_info, sizeof(cap->bus_info), "webcamstudio:%d", devnr);

#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 1, 0)
	/* since 3.1.0, the v4l2-core system is supposed to set the version */
	cap->version = WEBCAMSTUDIO_VERSION_CODE;
#endif
	cap->capabilities =
#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 19, 0)
		V4L2_CAP_DEVICE_CAPS |
#endif
		V4L2_CAP_STREAMING | V4L2_CAP_READWRITE;

#ifdef V4L2_CAP_VIDEO_M2M
	cap->capabilities |= V4L2_CAP_VIDEO_M2M;
#endif /* V4L2_CAP_VIDEO_M2M */
	if (dev->announce_all_caps) {
		cap->capabilities |= V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_VIDEO_OUTPUT;
	} else {

		if (dev->ready_for_capture) {
			cap->capabilities |= V4L2_CAP_VIDEO_CAPTURE;
		}
		if (dev->ready_for_output) {
			cap->capabilities |= V4L2_CAP_VIDEO_OUTPUT;
		}
	}
#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 19, 0)
	cap->device_caps = (cap->capabilities & ~V4L2_CAP_DEVICE_CAPS);
#endif

#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 3, 0)
	cap->device_caps = cap->capabilities;
	cap->capabilities |= V4L2_CAP_DEVICE_CAPS;
#endif

	memset(cap->reserved, 0, sizeof(cap->reserved));
	return 0;
}

static int vidioc_enum_framesizes(struct file *file, void *fh, struct v4l2_frmsizeenum *argp)
{
	struct webcamstudio_device *dev;

	/* LATER: what does the index really  mean?
	 * if it's about enumerating formats, we can safely ignore it
	 * (CHECK)
	 */

	/* there can be only one... */
	if (argp->index)
		return -EINVAL;

	dev=webcamstudio_getdevice(file);
	if (dev->ready_for_capture) {
		/* format has already been negotiated
		 * cannot change during runtime
		 */
		argp->type = V4L2_FRMSIZE_TYPE_DISCRETE;

		argp->discrete.width = dev->pix_format.width;
		argp->discrete.height = dev->pix_format.height;
	} else {
		/* if the format has not been negotiated yet, we accept anything
		 */
		argp->type = V4L2_FRMSIZE_TYPE_CONTINUOUS;

		argp->stepwise.min_width = WEBCAMSTUDIO_SIZE_MIN_WIDTH;
		argp->stepwise.min_height = WEBCAMSTUDIO_SIZE_MIN_HEIGHT;

		argp->stepwise.max_width = max_width;
		argp->stepwise.max_height = max_height;

		argp->stepwise.step_width = 1;
		argp->stepwise.step_height = 1;
	}
	return 0;
}

/* returns frameinterval (fps) for the set resolution
 * called on VIDIOC_ENUM_FRAMEINTERVALS
 */
static int vidioc_enum_frameintervals(struct file *file, void *fh, struct v4l2_frmivalenum *argp)
{
	struct webcamstudio_device *dev = webcamstudio_getdevice(file);
	struct webcamstudio_opener *opener = file->private_data;

	if (dev->ready_for_capture) {
		if (opener->vidioc_enum_frameintervals_calls > 0)
			return -EINVAL;
		if (argp->width == dev->pix_format.width &&
				argp->height == dev->pix_format.height) {
			argp->type = V4L2_FRMIVAL_TYPE_DISCRETE;
			argp->discrete = dev->capture_param.timeperframe;
			opener->vidioc_enum_frameintervals_calls++;
			return 0;
		}
		return -EINVAL;
	}
	return 0;
}

/* ------------------ CAPTURE ----------------------- */

/* returns device formats
 * called on VIDIOC_ENUM_FMT, with v4l2_buf_type set to V4L2_BUF_TYPE_VIDEO_CAPTURE
 */
static int vidioc_enum_fmt_cap(struct file *file, void *fh, struct v4l2_fmtdesc *f)
{
	struct webcamstudio_device *dev;
	MARK();

	dev = webcamstudio_getdevice(file);

	if (f->index)
		return -EINVAL;
	if (dev->ready_for_capture) {
		const __u32 format = dev->pix_format.pixelformat;

		snprintf(f->description, sizeof(f->description),
				"[%c%c%c%c]",
				(format >>  0) & 0xFF,
				(format >>  8) & 0xFF,
				(format >> 16) & 0xFF,
				(format >> 24) & 0xFF);

		f->pixelformat = dev->pix_format.pixelformat;
	} else {
		return -EINVAL;
	}
	f->flags = 0;
	MARK();
	return 0;
}

/* returns current video format format fmt
 * called on VIDIOC_G_FMT, with v4l2_buf_type set to V4L2_BUF_TYPE_VIDEO_CAPTURE
 */
static int vidioc_g_fmt_cap(struct file *file, void *priv, struct v4l2_format *fmt)
{
	struct webcamstudio_device *dev;
	MARK();

	dev = webcamstudio_getdevice(file);

	if (!dev->ready_for_capture)
		return -EINVAL;

	fmt->fmt.pix = dev->pix_format;
	MARK();
	return 0;
}

/* checks if it is OK to change to format fmt;
 * actual check is done by inner_try_fmt_cap
 * just checking that pixelformat is OK and set other parameters, app should
 * obey this decision
 * called on VIDIOC_TRY_FMT, with v4l2_buf_type set to V4L2_BUF_TYPE_VIDEO_CAPTURE
 */
static int vidioc_try_fmt_cap(struct file *file, void *priv, struct v4l2_format *fmt)
{
	struct webcamstudio_opener *opener;
	struct webcamstudio_device *dev;
	char buf[5];

	opener = file->private_data;
	opener->type = READER;

	dev = webcamstudio_getdevice(file);

	if (0 == dev->ready_for_capture) {
		dprintk("setting fmt_cap not possible yet\n");
		return -EBUSY;
	}

	if (fmt->fmt.pix.pixelformat != dev->pix_format.pixelformat)
		return -EINVAL;

	fmt->fmt.pix = dev->pix_format;

	buf[4] = 0;
	dprintk("capFOURCC=%s\n", fourcc2str(dev->pix_format.pixelformat, buf));
	return 0;
}

/* sets new output format, if possible
 * actually format is set  by input and we even do not check it, just return
 * current one, but it is possible to set subregions of input TODO(vasaka)
 * called on VIDIOC_S_FMT, with v4l2_buf_type set to V4L2_BUF_TYPE_VIDEO_CAPTURE
 */
static int vidioc_s_fmt_cap(struct file *file, void *priv, struct v4l2_format *fmt)
{
	return vidioc_try_fmt_cap(file, priv, fmt);
}


/* ------------------ OUTPUT ----------------------- */

/* returns device formats;
 * LATER: allow all formats
 * called on VIDIOC_ENUM_FMT, with v4l2_buf_type set to V4L2_BUF_TYPE_VIDEO_OUTPUT
 */
static int vidioc_enum_fmt_out(struct file *file, void *fh, struct v4l2_fmtdesc *f)
{
	struct webcamstudio_device *dev;
	const struct v4l2l_format *fmt;

	dev = webcamstudio_getdevice(file);

	if (dev->ready_for_capture) {
		const __u32 format = dev->pix_format.pixelformat;

		/* format has been fixed by the writer, so only one single format is supported */
		if (f->index)
			return -EINVAL;

		fmt = format_by_fourcc(format);
		if (NULL == fmt)
			return -EINVAL;

		f->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
		/* f->flags = ??; */
		snprintf(f->description, sizeof(f->description), "%s", fmt->name);

		f->pixelformat = dev->pix_format.pixelformat;
	} else {
		__u32 format;
		/* fill in a dummy format */
                /* coverity[unsigned_compare] */
		if (f->index < 0 || f->index >= FORMATS)
			return -EINVAL;

		fmt = &formats[f->index];

		f->pixelformat = fmt->fourcc;
		format = f->pixelformat;

		/* strlcpy(f->description, "dummy OUT format", sizeof(f->description)); */
		snprintf(f->description, sizeof(f->description), "%s", fmt->name);

	}
	f->flags = 0;

	return 0;
}

/* returns current video format format fmt */
/* NOTE: this is called from the producer
 * so if format has not been negotiated yet,
 * it should return ALL of available formats,
 * called on VIDIOC_G_FMT, with v4l2_buf_type set to V4L2_BUF_TYPE_VIDEO_OUTPUT
 */
static int vidioc_g_fmt_out(struct file *file, void *priv, struct v4l2_format *fmt)
{
	struct webcamstudio_device *dev;
	struct webcamstudio_opener *opener;

	MARK();

	dev = webcamstudio_getdevice(file);
	opener = file->private_data;
	opener->type = WRITER;
	dev->ready_for_output = 1;
	/*
	 * LATER: this should return the currently valid format
	 * gstreamer doesn't like it, if this returns -EINVAL, as it
	 * then concludes that there is _no_ valid format
	 * CHECK whether this assumption is wrong,
	 * or whether we have to always provide a valid format
	 */

	fmt->fmt.pix = dev->pix_format;
	return 0;
}

/* checks if it is OK to change to format fmt;
 * if format is negotiated do not change it
 * called on VIDIOC_TRY_FMT with v4l2_buf_type set to V4L2_BUF_TYPE_VIDEO_OUTPUT
 */
static int vidioc_try_fmt_out(struct file *file, void *priv, struct v4l2_format *fmt)
{
	struct webcamstudio_opener *opener;
	struct webcamstudio_device *dev;
	MARK();

	opener = file->private_data;
	opener->type = WRITER;

	dev = webcamstudio_getdevice(file);
	dev->ready_for_output = 1;

	/* TODO(vasaka) loopback does not care about formats writer want to set,
	 * maybe it is a good idea to restrict format somehow */
	if (dev->ready_for_capture) {
		fmt->fmt.pix = dev->pix_format;
	} else {
		__u32 w = fmt->fmt.pix.width;
		__u32 h = fmt->fmt.pix.height;
		__u32 pixfmt = fmt->fmt.pix.pixelformat;
		const struct v4l2l_format *format = format_by_fourcc(pixfmt);

		if (w > max_width)
			w = max_width;
		if (h > max_height)
			h = max_height;

		dprintk("trying image %dx%d\n", w, h);

		if (w < 1)
			w = WEBCAMSTUDIO_SIZE_DEFAULT_WIDTH;

		if (h < 1)
			h = WEBCAMSTUDIO_SIZE_DEFAULT_HEIGHT;

		if (NULL == format)
			format = &formats[0];

		pix_format_set_size(&fmt->fmt.pix, format, w, h);

		fmt->fmt.pix.pixelformat = format->fourcc;
		fmt->fmt.pix.colorspace = V4L2_COLORSPACE_SRGB;

		if (V4L2_FIELD_ANY == fmt->fmt.pix.field)
			fmt->fmt.pix.field = V4L2_FIELD_NONE;

		/* FIXXME: try_fmt should never modify the device-state */
		dev->pix_format = fmt->fmt.pix;
	}
	return 0;
}

/* sets new output format, if possible;
 * allocate data here because we do not know if it will be streaming or
 * read/write IO
 * called on VIDIOC_S_FMT with v4l2_buf_type set to V4L2_BUF_TYPE_VIDEO_OUTPUT
 */
static int vidioc_s_fmt_out(struct file *file, void *priv, struct v4l2_format *fmt)
{
	struct webcamstudio_device *dev;
	char buf[5];
	int ret;
	MARK();

	dev = webcamstudio_getdevice(file);
	ret = vidioc_try_fmt_out(file, priv, fmt);

	dprintk("s_fmt_out(%d) %d...%d\n", ret, dev->ready_for_capture, dev->pix_format.sizeimage);

	buf[4] = 0;
	dprintk("outFOURCC=%s\n", fourcc2str(dev->pix_format.pixelformat, buf));

	if (ret < 0)
		return ret;

	if (!dev->ready_for_capture) {
		dev->buffer_size = PAGE_ALIGN(dev->pix_format.sizeimage);
		fmt->fmt.pix.sizeimage = dev->buffer_size;
		allocate_buffers(dev);
	}
	return ret;
}

/*#define V4L2L_OVERLAY*/
#ifdef V4L2L_OVERLAY
/* ------------------ OVERLAY ----------------------- */
/* currently unsupported */
/* GSTreamer's v4l2sink is buggy, as it requires the overlay to work
 * while it should only require it, if overlay is requested
 * once the gstreamer element is fixed, remove the overlay dummies
 */
#warning OVERLAY dummies
static int vidioc_g_fmt_overlay(struct file *file, void *priv, struct v4l2_format *fmt)
{
	return 0;
}

static int vidioc_s_fmt_overlay(struct file *file, void *priv, struct v4l2_format *fmt)
{
	return 0;
}
#endif /* V4L2L_OVERLAY */


/* ------------------ PARAMs ----------------------- */

/* get some data flow parameters, only capability, fps and readbuffers has
 * effect on this driver
 * called on VIDIOC_G_PARM
 */
static int vidioc_g_parm(struct file *file, void *priv, struct v4l2_streamparm *parm)
{
	/* do not care about type of opener, hope this enums would always be
	 * compatible */
	struct webcamstudio_device *dev;
	MARK();

	dev = webcamstudio_getdevice(file);
	parm->parm.capture = dev->capture_param;
	return 0;
}

/* get some data flow parameters, only capability, fps and readbuffers has
 * effect on this driver
 * called on VIDIOC_S_PARM
 */
static int vidioc_s_parm(struct file *file, void *priv, struct v4l2_streamparm *parm)
{
	struct webcamstudio_device *dev;
	int err=0;
	MARK();

	dev = webcamstudio_getdevice(file);
	dprintk("vidioc_s_parm called frate=%d/%d\n",
			parm->parm.capture.timeperframe.numerator,
			parm->parm.capture.timeperframe.denominator);

	switch (parm->type) {
	case V4L2_BUF_TYPE_VIDEO_CAPTURE:
                if ((err=set_timeperframe(dev, &parm->parm.capture.timeperframe)) < 0)
		  return err;
		break;
	case V4L2_BUF_TYPE_VIDEO_OUTPUT:
                if ((err=set_timeperframe(dev, &parm->parm.capture.timeperframe)) < 0)
		  return err;
		break;
	default:
		return -1;
	}

	parm->parm.capture = dev->capture_param;
	return 0;
}

#ifdef WEBCAMSTUDIO_WITH_STD
/* sets a tv standard, actually we do not need to handle this any special way
 * added to support effecttv
 * called on VIDIOC_S_STD
 */
static int vidioc_s_std(struct file *file, void *private_data, v4l2_std_id *_std)
{
	v4l2_std_id req_std = 0, supported_std = 0;
	const v4l2_std_id all_std = V4L2_STD_ALL, no_std = 0;

	if (_std) {
		req_std = *_std;
		*_std = all_std;
	}

	/* we support everything in V4L2_STD_ALL, but not more... */
	supported_std = (all_std & req_std);
	if (no_std == supported_std)
		return -EINVAL;

	return 0;
}


/* gets a fake video standard
 * called on VIDIOC_G_STD
 */
static int vidioc_g_std(struct file *file, void *private_data, v4l2_std_id *norm)
{
	if (norm)
		*norm = V4L2_STD_ALL;
	return 0;
}
/* gets a fake video standard
 * called on VIDIOC_QUERYSTD
 */
static int vidioc_querystd(struct file *file, void *private_data, v4l2_std_id *norm)
{
	if (norm)
		*norm = V4L2_STD_ALL;
	return 0;
}
#endif /* WEBCAMSTUDIO_WITH_STD */

/* get ctrls info
 * called on VIDIOC_QUERYCTRL
 */
static int vidioc_queryctrl(struct file *file, void *fh, struct v4l2_queryctrl *q)
{
	const struct v4l2_ctrl_config *cnf = 0;
	switch (q->id) {
	case CID_KEEP_FORMAT:
		cnf = &v4l2loopback_ctrl_keepformat;
		break;
	case CID_SUSTAIN_FRAMERATE:
		cnf = &v4l2loopback_ctrl_sustainframerate;
		break;
	case CID_TIMEOUT:
		cnf = &v4l2loopback_ctrl_timeout;
		break;
	case CID_TIMEOUT_IMAGE_IO:
		cnf = &v4l2loopback_ctrl_timeoutimageio;
		break;
	default:
		return -EINVAL;
	}
	if (!cnf)
		BUG();

	strcpy(q->name, cnf->name);
	q->default_value=cnf->def;
	q->type = cnf->type;
	q->minimum = cnf->min;
	q->maximum = cnf->max;
	q->step = cnf->step;

	memset(q->reserved, 0, sizeof(q->reserved));
	return 0;
}


static int vidioc_g_ctrl(struct file *file, void *fh, struct v4l2_control *c)
{
	struct webcamstudio_device *dev = webcamstudio_getdevice(file);

	switch (c->id) {
	case CID_KEEP_FORMAT:
		c->value = dev->keep_format;
		break;
	case CID_SUSTAIN_FRAMERATE:
		c->value = dev->sustain_framerate;
		break;
	case CID_TIMEOUT:
		c->value = jiffies_to_msecs(dev->timeout_jiffies);
		break;
	case CID_TIMEOUT_IMAGE_IO:
		c->value = dev->timeout_image_io;
		break;
	default:
		return -EINVAL;
	}

	return 0;
}


static int webcamstudio_set_ctrl( struct webcamstudio_device *dev,
				  u32 id,
				  s64 val)
{
	switch (id) {
	case CID_KEEP_FORMAT:
		if (val < 0 || val > 1)
			return -EINVAL;
		dev->keep_format = val;
		try_free_buffers(dev);
		break;
	case CID_SUSTAIN_FRAMERATE:
		if (val < 0 || val > 1)
			return -EINVAL;
		spin_lock_bh(&dev->lock);
		dev->sustain_framerate = val;
		check_timers(dev);
		spin_unlock_bh(&dev->lock);
		break;
	case CID_TIMEOUT:
		if (val < 0 || val > MAX_TIMEOUT)
			return -EINVAL;
		spin_lock_bh(&dev->lock);
		dev->timeout_jiffies = msecs_to_jiffies(val);
		check_timers(dev);
		spin_unlock_bh(&dev->lock);
		allocate_timeout_image(dev);
		break;
	case CID_TIMEOUT_IMAGE_IO:
		if (val < 0 || val > 1)
			return -EINVAL;
		dev->timeout_image_io = val;
		break;
	default:
		return -EINVAL;
	}
	return 0;
}

static int webcamstudio_s_ctrl(struct v4l2_ctrl *ctrl)
{
	struct webcamstudio_device *dev = container_of(ctrl->handler, struct webcamstudio_device, ctrl_handler);
	return webcamstudio_set_ctrl(dev, ctrl->id, ctrl->val);
}
static int vidioc_s_ctrl(struct file *file, void *fh, struct v4l2_control *c)
{
	struct webcamstudio_device *dev = webcamstudio_getdevice(file);
	return webcamstudio_set_ctrl(dev, c->id, c->value);
}

/* returns set of device outputs, in our case there is only one
 * called on VIDIOC_ENUMOUTPUT
 */
static int vidioc_enum_output(struct file *file, void *fh, struct v4l2_output *outp)
{
	__u32 index = outp->index;
	struct webcamstudio_device *dev = webcamstudio_getdevice(file);
	MARK();

        if (!dev->announce_all_caps && !dev->ready_for_output)
		return -ENOTTY;

	if (0 != index)
		return -EINVAL;

	/* clear all data (including the reserved fields) */
	memset(outp, 0, sizeof(*outp));

	outp->index = index;
	strlcpy(outp->name, "loopback in", sizeof(outp->name));
	outp->type = V4L2_OUTPUT_TYPE_ANALOG;
	outp->audioset = 0;
	outp->modulator = 0;
#ifdef WEBCAMSTUDIO_WITH_STD
	outp->std = V4L2_STD_ALL;
# ifdef V4L2_OUT_CAP_STD
	outp->capabilities |= V4L2_OUT_CAP_STD;
# endif /*  V4L2_OUT_CAP_STD */
#endif /* WEBCAMSTUDIO_WITH_STD */

	return 0;
}

/* which output is currently active,
 * called on VIDIOC_G_OUTPUT
 */
static int vidioc_g_output(struct file *file, void *fh, unsigned int *i)
{
	struct webcamstudio_device *dev = webcamstudio_getdevice(file);
        if (!dev->announce_all_caps && !dev->ready_for_output)
		return -ENOTTY;
	if (i)
		*i = 0;
	return 0;
}

/* set output, can make sense if we have more than one video src,
 * called on VIDIOC_S_OUTPUT
 */
static int vidioc_s_output(struct file *file, void *fh, unsigned int i)
{
	struct webcamstudio_device *dev = webcamstudio_getdevice(file);
        if (!dev->announce_all_caps && !dev->ready_for_output)
		return -ENOTTY;

	if (i)
		return -EINVAL;

	return 0;
}


/* returns set of device inputs, in our case there is only one,
 * but later I may add more
 * called on VIDIOC_ENUMINPUT
 */
static int vidioc_enum_input(struct file *file, void *fh, struct v4l2_input *inp)
{
	__u32 index = inp->index;
	struct webcamstudio_device *dev = webcamstudio_getdevice(file);
	MARK();
        if (!dev->announce_all_caps && !dev->ready_for_capture)
		return -ENOTTY;

	if (0 != index)
		return -EINVAL;

	/* clear all data (including the reserved fields) */
	memset(inp, 0, sizeof(*inp));

	inp->index = index;
	strlcpy(inp->name, "loopback", sizeof(inp->name));
	inp->type = V4L2_INPUT_TYPE_CAMERA;
	inp->audioset = 0;
	inp->tuner = 0;
	inp->status = 0;

#ifdef WEBCAMSTUDIO_WITH_STD
	inp->std = V4L2_STD_ALL;
# ifdef V4L2_IN_CAP_STD
	inp->capabilities |= V4L2_IN_CAP_STD;
# endif
#endif /* WEBCAMSTUDIO_WITH_STD */

	return 0;
}

/* which input is currently active,
 * called on VIDIOC_G_INPUT
 */
static int vidioc_g_input(struct file *file, void *fh, unsigned int *i)
{
	struct webcamstudio_device *dev = webcamstudio_getdevice(file);
        if (!dev->announce_all_caps && !dev->ready_for_capture)
		return -ENOTTY;
	if (i)
		*i = 0;
	return 0;
}

/* set input, can make sense if we have more than one video src,
 * called on VIDIOC_S_INPUT
 */
static int vidioc_s_input(struct file *file, void *fh, unsigned int i)
{
	struct webcamstudio_device *dev = webcamstudio_getdevice(file);
        if (!dev->announce_all_caps && !dev->ready_for_capture)
		return -ENOTTY;
	if (i == 0)
		return 0;
	return -EINVAL;
}

/* --------------- V4L2 ioctl buffer related calls ----------------- */

/* negotiate buffer type
 * only mmap streaming supported
 * called on VIDIOC_REQBUFS
 */
static int vidioc_reqbufs(struct file *file, void *fh, struct v4l2_requestbuffers *b)
{
	struct webcamstudio_device *dev;
	struct webcamstudio_opener *opener;
	int i;
	MARK();

	dev = webcamstudio_getdevice(file);
	opener = file->private_data;

	dprintk("reqbufs: %d\t%d=%d\n", b->memory, b->count, dev->buffers_number);
	if (opener->timeout_image_io) {
		if (b->memory != V4L2_MEMORY_MMAP)
			return -EINVAL;
		b->count = 1;
		return 0;
	}

	init_buffers(dev);
	switch (b->memory) {
	case V4L2_MEMORY_MMAP:
		/* do nothing here, buffers are always allocated*/
		if (b->count < 1 || dev->buffers_number < 1)
			return 0;

		if (b->count > dev->buffers_number)
			b->count = dev->buffers_number;

		/* make sure that outbufs_list contains buffers from 0 to used_buffers-1
		 * actually, it will have been already populated via webcamstudio_init()
		 * at this point */
		if (list_empty(&dev->outbufs_list)) {
			for (i = 0; i < dev->used_buffers; ++i)
				list_add_tail(&dev->buffers[i].list_head, &dev->outbufs_list);
		}

		/* also, if dev->used_buffers is going to be decreased, we should remove
		 * out-of-range buffers from outbufs_list, and fix bufpos2index mapping */
		if (b->count < dev->used_buffers) {
			struct v4l2l_buffer *pos, *n;

			list_for_each_entry_safe(pos, n, &dev->outbufs_list, list_head) {
				if (pos->buffer.index >= b->count)
					list_del(&pos->list_head);
			}

			/* after we update dev->used_buffers, buffers in outbufs_list will
			 * correspond to dev->write_position + [0;b->count-1] range */
			i = dev->write_position;
			list_for_each_entry(pos, &dev->outbufs_list, list_head) {
				dev->bufpos2index[i % b->count] = pos->buffer.index;
				++i;
			}
		}

		opener->buffers_number = b->count;
		if (opener->buffers_number < dev->used_buffers)
			dev->used_buffers = opener->buffers_number;
		return 0;
	default:
		return -EINVAL;
	}
}

/* returns buffer asked for;
 * give app as many buffers as it wants, if it less than MAX,
 * but map them in our inner buffers
 * called on VIDIOC_QUERYBUF
 */
static int vidioc_querybuf(struct file *file, void *fh, struct v4l2_buffer *b)
{
	enum v4l2_buf_type type;
	int index;
	struct webcamstudio_device *dev;
	struct webcamstudio_opener *opener;

	MARK();

	type = b->type;
	index = b->index;
	dev = webcamstudio_getdevice(file);
	opener = file->private_data;

	if ((b->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) &&
			(b->type != V4L2_BUF_TYPE_VIDEO_OUTPUT)) {
		return -EINVAL;
	}
	if (b->index > max_buffers)
		return -EINVAL;

	if (opener->timeout_image_io)
		*b = dev->timeout_image_buffer.buffer;
	else
		*b = dev->buffers[b->index % dev->used_buffers].buffer;

	b->type = type;
	b->index = index;
	dprintkrw("buffer type: %d (of %d with size=%ld)\n", b->memory, dev->buffers_number, dev->buffer_size);
	return 0;
}

static void buffer_written(struct webcamstudio_device *dev, struct v4l2l_buffer *buf)
{
	del_timer_sync(&dev->sustain_timer);
	del_timer_sync(&dev->timeout_timer);
	spin_lock_bh(&dev->lock);

	dev->bufpos2index[dev->write_position % dev->used_buffers] = buf->buffer.index;
	list_move_tail(&buf->list_head, &dev->outbufs_list);
	++dev->write_position;
	dev->reread_count = 0;

	check_timers(dev);
	spin_unlock_bh(&dev->lock);
}

/* put buffer to queue
 * called on VIDIOC_QBUF
 */
static int vidioc_qbuf(struct file *file, void *private_data, struct v4l2_buffer *buf)
{
	struct webcamstudio_device *dev;
	struct webcamstudio_opener *opener;
	struct v4l2l_buffer *b;
	int index;

	dev = webcamstudio_getdevice(file);
	opener = file->private_data;

	if (buf->index > max_buffers)
		return -EINVAL;
	if (opener->timeout_image_io)
		return 0;

	index = buf->index % dev->used_buffers;
	b = &dev->buffers[index];

	switch (buf->type) {
	case V4L2_BUF_TYPE_VIDEO_CAPTURE:
		dprintkrw("capture QBUF index: %d\n", index);
		set_queued(b);
		return 0;
	case V4L2_BUF_TYPE_VIDEO_OUTPUT:
		dprintkrw("output QBUF pos: %d index: %d\n", dev->write_position, index);
		if (buf->timestamp.tv_sec == 0 && buf->timestamp.tv_usec == 0)
			do_gettimeofday(&b->buffer.timestamp);
		else
			b->buffer.timestamp = buf->timestamp;
		b->buffer.bytesused = buf->bytesused;
		set_done(b);
		buffer_written(dev, b);
		wake_up_all(&dev->read_event);
		return 0;
	default:
		return -EINVAL;
	}
}

static int can_read(struct webcamstudio_device *dev, struct webcamstudio_opener *opener)
{
	int ret;

	spin_lock_bh(&dev->lock);
	check_timers(dev);
	ret = dev->write_position > opener->read_position
		|| dev->reread_count > opener->reread_count
		|| dev->timeout_happened;
	spin_unlock_bh(&dev->lock);
	return ret;
}

static int get_capture_buffer(struct file *file)
{
	struct webcamstudio_device *dev = webcamstudio_getdevice(file);
	struct webcamstudio_opener *opener = file->private_data;
	int pos, ret;
	int timeout_happened;

	if ((file->f_flags & O_NONBLOCK) &&
	    (dev->write_position <= opener->read_position &&
	     dev->reread_count <= opener->reread_count && !dev->timeout_happened))
		return -EAGAIN;
	wait_event_interruptible(dev->read_event, can_read(dev, opener));

	spin_lock_bh(&dev->lock);
	if (dev->write_position == opener->read_position) {
		if (dev->reread_count > opener->reread_count + 2)
			opener->reread_count = dev->reread_count - 1;
		++opener->reread_count;
		pos = (opener->read_position + dev->used_buffers - 1) % dev->used_buffers;
	} else {
		opener->reread_count = 0;
		if (dev->write_position > opener->read_position + 2)
			opener->read_position = dev->write_position - 1;
		pos = opener->read_position % dev->used_buffers;
		++opener->read_position;
	}
	timeout_happened = dev->timeout_happened;
	dev->timeout_happened = 0;
	spin_unlock_bh(&dev->lock);

	ret = dev->bufpos2index[pos];
	if (timeout_happened) {
		/* although allocated on-demand, timeout_image is freed only
		 * in free_buffers(), so we don't need to worry about it being
		 * deallocated suddenly */
		memcpy(dev->image + dev->buffers[ret].buffer.m.offset,
		       dev->timeout_image, dev->buffer_size);
	}
	return ret;
}

/* put buffer to dequeue
 * called on VIDIOC_DQBUF
 */
static int vidioc_dqbuf(struct file *file, void *private_data, struct v4l2_buffer *buf)
{
	struct webcamstudio_device *dev;
	struct webcamstudio_opener *opener;
	int index;
	struct v4l2l_buffer *b;

	dev = webcamstudio_getdevice(file);
	opener = file->private_data;
	if (opener->timeout_image_io) {
		*buf = dev->timeout_image_buffer.buffer;
		return 0;
	}

	switch (buf->type) {
	case V4L2_BUF_TYPE_VIDEO_CAPTURE:
		index = get_capture_buffer(file);
		if (index < 0)
			return index;
		dprintkrw("capture DQBUF pos: %d index: %d\n", opener->read_position - 1, index);
		if (!(dev->buffers[index].buffer.flags&V4L2_BUF_FLAG_MAPPED)) {
			dprintk("trying to return not mapped buf[%d]\n", index);
			return -EINVAL;
		}
		unset_flags(&dev->buffers[index]);
		*buf = dev->buffers[index].buffer;
		return 0;
	case V4L2_BUF_TYPE_VIDEO_OUTPUT:
		b = list_entry(dev->outbufs_list.next, struct v4l2l_buffer, list_head);
		list_move_tail(&b->list_head, &dev->outbufs_list);
		dprintkrw("output DQBUF index: %d\n", b->buffer.index);
		unset_flags(b);
		*buf = b->buffer;
		buf->type = V4L2_BUF_TYPE_VIDEO_OUTPUT;
		return 0;
	default:
		return -EINVAL;
	}
}

/* ------------- STREAMING ------------------- */

/* start streaming
 * called on VIDIOC_STREAMON
 */
static int vidioc_streamon(struct file *file, void *private_data, enum v4l2_buf_type type)
{
	struct webcamstudio_device *dev;
	int ret;
	MARK();

	dev = webcamstudio_getdevice(file);

	switch (type) {
	case V4L2_BUF_TYPE_VIDEO_OUTPUT:
		dev->ready_for_output = 0;
		if (!dev->ready_for_capture) {
			ret = allocate_buffers(dev);
			if (ret < 0)
				return ret;
			dev->ready_for_capture = 1;
		}
		return 0;
	case V4L2_BUF_TYPE_VIDEO_CAPTURE:
		if (!dev->ready_for_capture)
			return -EIO;
		return 0;
	default:
		return -EINVAL;
	}
}

/* stop streaming
 * called on VIDIOC_STREAMOFF
 */
static int vidioc_streamoff(struct file *file, void *private_data, enum v4l2_buf_type type)
{
	MARK();
	dprintk("%d\n", type);
	return 0;
}

#ifdef CONFIG_VIDEO_V4L1_COMPAT
static int vidiocgmbuf(struct file *file, void *fh, struct video_mbuf *p)
{
	struct webcamstudio_device *dev;
	MARK();

	dev = webcamstudio_getdevice(file);
	p->frames = dev->buffers_number;
	p->offsets[0] = 0;
	p->offsets[1] = 0;
	p->size = dev->buffer_size;
	return 0;
}
#endif

/* file operations */
static void vm_open(struct vm_area_struct *vma)
{
	struct v4l2l_buffer *buf;
	MARK();

	buf = vma->vm_private_data;
	buf->use_count++;
}

static void vm_close(struct vm_area_struct *vma)
{
	struct v4l2l_buffer *buf;
	MARK();

	buf = vma->vm_private_data;
	buf->use_count--;
}

static struct vm_operations_struct vm_ops = {
	.open = vm_open,
	.close = vm_close,
};

static int webcamstudio_mmap(struct file *file, struct vm_area_struct *vma)
{
	int i;
	unsigned long addr;
	unsigned long start;
	unsigned long size;
	struct webcamstudio_device *dev;
	struct webcamstudio_opener *opener;
	struct v4l2l_buffer *buffer = NULL;
	MARK();

	start = (unsigned long) vma->vm_start;
	size = (unsigned long) (vma->vm_end - vma->vm_start);

	dev = webcamstudio_getdevice(file);
	opener = file->private_data;

	if (size > dev->buffer_size) {
		dprintk("userspace tries to mmap too much, fail\n");
		return -EINVAL;
	}
	if (opener->timeout_image_io) {
		/* we are going to map the timeout_image_buffer */
		if ((vma->vm_pgoff << PAGE_SHIFT) != dev->buffer_size * MAX_BUFFERS) {
			dprintk("invalid mmap offset for timeout_image_io mode\n");
			return -EINVAL;
		}
	} else if ((vma->vm_pgoff << PAGE_SHIFT) >
			dev->buffer_size * (dev->buffers_number - 1)) {
		dprintk("userspace tries to mmap too far, fail\n");
		return -EINVAL;
	}

	/* FIXXXXXME: allocation should not happen here! */
	if (NULL == dev->image)
		if (allocate_buffers(dev) < 0)
			return -EINVAL;

	if (opener->timeout_image_io) {
		buffer = &dev->timeout_image_buffer;
		addr = (unsigned long)dev->timeout_image;
	} else {
		for (i = 0; i < dev->buffers_number; ++i) {
			buffer = &dev->buffers[i];
			if ((buffer->buffer.m.offset >> PAGE_SHIFT) == vma->vm_pgoff)
				break;
		}

		if (NULL == buffer)
			return -EINVAL;

		addr = (unsigned long) dev->image + (vma->vm_pgoff << PAGE_SHIFT);
	}

	while (size > 0) {
		struct page *page;

		page = (void *)vmalloc_to_page((void *)addr);

		if (vm_insert_page(vma, start, page) < 0)
			return -EAGAIN;

		start += PAGE_SIZE;
		addr += PAGE_SIZE;
		size -= PAGE_SIZE;
	}

	vma->vm_ops = &vm_ops;
	vma->vm_private_data = buffer;
	buffer->buffer.flags |= V4L2_BUF_FLAG_MAPPED;

	vm_open(vma);

	MARK();
	return 0;
}

static unsigned int webcamstudio_poll(struct file *file, struct poll_table_struct *pts)
{
	struct webcamstudio_opener *opener;
	struct webcamstudio_device *dev;
	int ret_mask = 0;
	MARK();

	opener = file->private_data;
	dev    = webcamstudio_getdevice(file);

	switch (opener->type) {
	case WRITER:
		ret_mask = POLLOUT | POLLWRNORM;
		break;
	case READER:
		poll_wait(file, &dev->read_event, pts);
		if (can_read(dev, opener))
			ret_mask =  POLLIN | POLLRDNORM;
		break;
	default:
		ret_mask = -POLLERR;
	}
	MARK();

	return ret_mask;
}

/* do not want to limit device opens, it can be as many readers as user want,
 * writers are limited by means of setting writer field */
static int webcamstudio_open(struct file *file)
{
	struct webcamstudio_device *dev;
	struct webcamstudio_opener *opener;
	MARK();

	dev = webcamstudio_getdevice(file);

	if (dev->open_count.counter >= dev->max_openers)
		return -EBUSY;
	/* kfree on close */
	opener = kzalloc(sizeof(*opener), GFP_KERNEL);
	if (opener == NULL)
		return -ENOMEM;
	file->private_data = opener;
	atomic_inc(&dev->open_count);

	opener->timeout_image_io = dev->timeout_image_io;
	dev->timeout_image_io = 0;

	if (opener->timeout_image_io) {
		int r = allocate_timeout_image(dev);

		if (r < 0) {
			dprintk("timeout image allocation failed\n");
			return r;
		}
	}
	dprintk("opened dev:%p with image:%p\n", dev, dev ? dev->image : NULL);
	MARK();
	return 0;
}

static int webcamstudio_close(struct file *file)
{
	struct webcamstudio_opener *opener;
	struct webcamstudio_device *dev;
	int iswriter=0;
	MARK();

	opener = file->private_data;
	dev    = webcamstudio_getdevice(file);

	if(WRITER == opener->type)
		iswriter = 1;

	atomic_dec(&dev->open_count);
	if (dev->open_count.counter == 0) {
		del_timer_sync(&dev->sustain_timer);
		del_timer_sync(&dev->timeout_timer);
	}
	try_free_buffers(dev);
	kfree(opener);
	if(iswriter) {
		dev->ready_for_output = 1;
	}
	MARK();
	return 0;
}

static ssize_t webcamstudio_read(struct file *file,
		char __user *buf, size_t count, loff_t *ppos)
{
	int read_index;
	struct webcamstudio_opener *opener;
	struct webcamstudio_device *dev;
	struct v4l2_buffer *b;
	MARK();

	opener = file->private_data;
	dev    = webcamstudio_getdevice(file);

	read_index = get_capture_buffer(file);
        if (read_index < 0)
          return read_index;
	if (count > dev->buffer_size)
		count = dev->buffer_size;
	b = &dev->buffers[read_index].buffer;
	if (count > b->bytesused)
		count = b->bytesused;
	if (copy_to_user((void *)buf, (void *)(dev->image + b->m.offset), count)) {
		printk(KERN_ERR
			"webcamstudio: failed copy_from_user() in write buf\n");
		return -EFAULT;
	}
	dprintkrw("leave webcamstudio_read()\n");
	return count;
}

static ssize_t webcamstudio_write(struct file *file,
		const char __user *buf, size_t count, loff_t *ppos)
{
	struct webcamstudio_device *dev;
	int write_index;
	struct v4l2_buffer *b;
	int ret;
	MARK();

	dev = webcamstudio_getdevice(file);

	/* there's at least one writer, so don'stop announcing output capabilities */
	dev->ready_for_output = 0;

	if (!dev->ready_for_capture) {
		ret = allocate_buffers(dev);
		if (ret < 0)
			return ret;
		dev->ready_for_capture = 1;
	}
	dprintkrw("webcamstudio_write() trying to write %zu bytes\n", count);
	if (count > dev->buffer_size)
		count = dev->buffer_size;

	write_index = dev->write_position % dev->used_buffers;
	b = &dev->buffers[write_index].buffer;

	if (copy_from_user((void *)(dev->image + b->m.offset), (void *)buf, count)) {
		printk(KERN_ERR
			"webcamstudio: failed copy_from_user() in write buf, could not write %zu\n",
			count);
		return -EFAULT;
	}
	do_gettimeofday(&b->timestamp);
	b->bytesused = count;
	b->sequence = dev->write_position;
	buffer_written(dev, &dev->buffers[write_index]);
	wake_up_all(&dev->read_event);
	dprintkrw("leave webcamstudio_write()\n");
	return count;
}

/* init functions */
/* frees buffers, if already allocated */
static int free_buffers(struct webcamstudio_device *dev)
{
	MARK();
	dprintk("freeing image@%p for dev:%p\n", dev ? dev->image : NULL, dev);
	if (dev->image) {
		vfree(dev->image);
		dev->image = NULL;
	}
	if (dev->timeout_image) {
		vfree(dev->timeout_image);
		dev->timeout_image = NULL;
	}
	dev->imagesize = 0;

	return 0;
}
/* frees buffers, if they are no longer needed */
static void try_free_buffers(struct webcamstudio_device *dev)
{
	MARK();
	if (0 == dev->open_count.counter && !dev->keep_format) {
		free_buffers(dev);
		dev->ready_for_capture = 0;
		dev->buffer_size = 0;
		dev->write_position = 0;
	}
}
/* allocates buffers, if buffer_size is set */
static int allocate_buffers(struct webcamstudio_device *dev)
{
	MARK();
	/* vfree on close file operation in case no open handles left */
	if (0 == dev->buffer_size)
		return -EINVAL;

	if (dev->image) {
		dprintk("allocating buffers again: %ld %ld\n",
			dev->buffer_size * dev->buffers_number, dev->imagesize);
		/* FIXME: prevent double allocation more intelligently! */
		if (dev->buffer_size * dev->buffers_number == dev->imagesize)
			return 0;

		/* if there is only one writer, no problem should occur */
		if (dev->open_count.counter == 1)
			free_buffers(dev);
		else
			return -EINVAL;
	}

	dev->imagesize = dev->buffer_size * dev->buffers_number;

	dprintk("allocating %ld = %ldx%d\n", dev->imagesize, dev->buffer_size, dev->buffers_number);

	dev->image = vmalloc(dev->imagesize);
	if (dev->timeout_jiffies > 0)
		allocate_timeout_image(dev);

	if (dev->image == NULL)
		return -ENOMEM;
	dprintk("vmallocated %ld bytes\n", dev->imagesize);
	MARK();
	init_buffers(dev);
	return 0;
}

/* init inner buffers, they are capture mode and flags are set as
 * for capture mod buffers */
static void init_buffers(struct webcamstudio_device *dev)
{
	int i;
	int buffer_size;
	int bytesused;
	MARK();

	buffer_size = dev->buffer_size;
	bytesused = dev->pix_format.sizeimage;

	for (i = 0; i < dev->buffers_number; ++i) {
		struct v4l2_buffer *b = &dev->buffers[i].buffer;
		b->index             = i;
		b->bytesused         = bytesused;
		b->length            = buffer_size;
		b->field             = V4L2_FIELD_NONE;
		b->flags             = 0;
#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 6, 1)
		b->input             = 0;
#endif
		b->m.offset          = i * buffer_size;
		b->memory            = V4L2_MEMORY_MMAP;
		b->sequence          = 0;
		b->timestamp.tv_sec  = 0;
		b->timestamp.tv_usec = 0;
		b->type              = V4L2_BUF_TYPE_VIDEO_CAPTURE;

		do_gettimeofday(&b->timestamp);
	}
	dev->timeout_image_buffer = dev->buffers[0];
	dev->timeout_image_buffer.buffer.m.offset = MAX_BUFFERS * buffer_size;
	MARK();
}

static int allocate_timeout_image(struct webcamstudio_device *dev)
{
	MARK();
	if (dev->buffer_size <= 0)
		return -EINVAL;

	if (dev->timeout_image == NULL) {
		dev->timeout_image = v4l2l_vzalloc(dev->buffer_size);
		if (dev->timeout_image == NULL)
			return -ENOMEM;
	}
	return 0;
}

/* fills and register video device */
static void init_vdev(struct video_device *vdev, int nr)
{
	MARK();
	snprintf(vdev->name, sizeof(vdev->name), "WebcamStudio virtual video device %X", nr);

#ifdef WEBCAMSTUDIO_WITH_STD
	vdev->tvnorms      = V4L2_STD_ALL;
	vdev->current_norm = V4L2_STD_ALL;
#endif /* WEBCAMSTUDIO_WITH_STD */

	vdev->vfl_type     = VFL_TYPE_GRABBER;
	vdev->fops         = &webcamstudio_fops;
	vdev->ioctl_ops    = &webcamstudio_ioctl_ops;
	vdev->release      = &video_device_release;
	vdev->minor        = -1;
	if (debug > 1)
		#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 20, 0)
			vdev->debug = V4L2_DEBUG_IOCTL | V4L2_DEBUG_IOCTL_ARG;
		#else
			vdev->dev_debug = V4L2_DEV_DEBUG_IOCTL | V4L2_DEV_DEBUG_IOCTL_ARG;
		#endif

	/* since kernel-3.7, there is a new field 'vfl_dir' that has to be
	 * set to VFL_DIR_M2M for bidrectional devices */
#ifdef VFL_DIR_M2M
	vdev->vfl_dir = VFL_DIR_M2M;
#endif

	MARK();
}

/* init default capture parameters, only fps may be changed in future */
static void init_capture_param(struct v4l2_captureparm *capture_param)
{
	MARK();
	capture_param->capability               = 0;
	capture_param->capturemode              = 0;
	capture_param->extendedmode             = 0;
	capture_param->readbuffers              = max_buffers;
	capture_param->timeperframe.numerator   = 1;
	capture_param->timeperframe.denominator = 30;
}

static void check_timers(struct webcamstudio_device *dev)
{
	if (!dev->ready_for_capture)
		return;

	if (dev->timeout_jiffies > 0 && !timer_pending(&dev->timeout_timer))
		mod_timer(&dev->timeout_timer, jiffies + dev->timeout_jiffies);
	if (dev->sustain_framerate && !timer_pending(&dev->sustain_timer))
		mod_timer(&dev->sustain_timer, jiffies + dev->frame_jiffies * 3 / 2);
}

static void sustain_timer_clb(unsigned long nr)
{
	struct webcamstudio_device *dev = devs[nr];

	spin_lock(&dev->lock);
	if (dev->sustain_framerate) {
		dev->reread_count++;
		dprintkrw("reread: %d %d\n", dev->write_position, dev->reread_count);
		if (dev->reread_count == 1)
			mod_timer(&dev->sustain_timer, jiffies + max(1UL, dev->frame_jiffies / 2));
		else
			mod_timer(&dev->sustain_timer, jiffies + dev->frame_jiffies);
		wake_up_all(&dev->read_event);
	}
	spin_unlock(&dev->lock);
}

static void timeout_timer_clb(unsigned long nr)
{
	struct webcamstudio_device *dev = devs[nr];

	spin_lock(&dev->lock);
	if (dev->timeout_jiffies > 0) {
		dev->timeout_happened = 1;
		mod_timer(&dev->timeout_timer, jiffies + dev->timeout_jiffies);
		wake_up_all(&dev->read_event);
	}
	spin_unlock(&dev->lock);
}

/* init loopback main structure */
static int webcamstudio_init(struct webcamstudio_device *dev, int nr)
{
	int ret;
	struct v4l2_ctrl_handler *hdl = &dev->ctrl_handler;
	snprintf(dev->v4l2_dev.name, sizeof(dev->v4l2_dev.name),
                        "webcamstudio-%03d", nr);
        ret = v4l2_device_register(NULL, &dev->v4l2_dev);
        if (ret)
                return ret;

	MARK();
	dev->vdev = video_device_alloc();
	if (dev->vdev == NULL) {
		ret=-ENOMEM;
		goto error;
	}

	video_set_drvdata(dev->vdev, kzalloc(sizeof(struct webcamstudio_private), GFP_KERNEL));
	if (video_get_drvdata(dev->vdev) == NULL) {
		ret=-ENOMEM;
		goto error;
	}
	((struct webcamstudio_private *)video_get_drvdata(dev->vdev))->devicenr = nr;

	init_vdev(dev->vdev, nr);
	dev->vdev->v4l2_dev = &dev->v4l2_dev;
	init_capture_param(&dev->capture_param);
	set_timeperframe(dev, &dev->capture_param.timeperframe);
	dev->keep_format = 0;
	dev->sustain_framerate = 0;
	dev->buffers_number = max_buffers;
	dev->used_buffers = max_buffers;
	dev->max_openers = max_openers;
	dev->write_position = 0;
	spin_lock_init(&dev->lock);
	INIT_LIST_HEAD(&dev->outbufs_list);
	if (list_empty(&dev->outbufs_list)) {
		int i;

		for (i = 0; i < dev->used_buffers; ++i)
			list_add_tail(&dev->buffers[i].list_head, &dev->outbufs_list);
	}
	memset(dev->bufpos2index, 0, sizeof(dev->bufpos2index));
	atomic_set(&dev->open_count, 0);
	dev->ready_for_capture = 0;
	dev->ready_for_output  = 1;
	dev->announce_all_caps = (!exclusive_caps[nr]);

	dev->buffer_size = 0;
	dev->image = NULL;
	dev->imagesize = 0;
	setup_timer(&dev->sustain_timer, sustain_timer_clb, nr);
	dev->reread_count = 0;
	setup_timer(&dev->timeout_timer, timeout_timer_clb, nr);
	dev->timeout_jiffies = 0;
	dev->timeout_image = NULL;
	dev->timeout_happened = 0;

	ret = v4l2_ctrl_handler_init(hdl, 1);
	if(ret)
		goto error;
	v4l2_ctrl_new_custom(hdl, &v4l2loopback_ctrl_keepformat, NULL);
	v4l2_ctrl_new_custom(hdl, &v4l2loopback_ctrl_sustainframerate, NULL);
	v4l2_ctrl_new_custom(hdl, &v4l2loopback_ctrl_timeout, NULL);
	v4l2_ctrl_new_custom(hdl, &v4l2loopback_ctrl_timeoutimageio, NULL);
        if (hdl->error) {
                ret = hdl->error;
                goto error;
        }
	dev->v4l2_dev.ctrl_handler = hdl;

	/* FIXME set buffers to 0 */

	/* Set initial format */
	dev->pix_format.width = 0; /* WEBCAMSTUDIO_SIZE_DEFAULT_WIDTH; */
	dev->pix_format.height = 0; /* WEBCAMSTUDIO_SIZE_DEFAULT_HEIGHT; */
	dev->pix_format.pixelformat = formats[0].fourcc;
	dev->pix_format.colorspace = V4L2_COLORSPACE_SRGB; /* do we need to set this ? */
	dev->pix_format.field = V4L2_FIELD_NONE;

	dev->buffer_size = PAGE_ALIGN(dev->pix_format.sizeimage);
	dprintk("buffer_size = %ld (=%d)\n", dev->buffer_size, dev->pix_format.sizeimage);
	allocate_buffers(dev);

	init_waitqueue_head(&dev->read_event);

	MARK();
	return 0;

error:
        v4l2_ctrl_handler_free(&dev->ctrl_handler);
        v4l2_device_unregister(&dev->v4l2_dev);
	kfree(dev->vdev);
        return ret;

};

/* LINUX KERNEL */
static const struct v4l2_file_operations webcamstudio_fops = {
	.owner   = THIS_MODULE,
	.open    = webcamstudio_open,
	.release = webcamstudio_close,
	.read    = webcamstudio_read,
	.write   = webcamstudio_write,
	.poll    = webcamstudio_poll,
	.mmap    = webcamstudio_mmap,
	.unlocked_ioctl   = video_ioctl2,
};

static const struct v4l2_ioctl_ops webcamstudio_ioctl_ops = {
	.vidioc_querycap         = &vidioc_querycap,
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 29)
	.vidioc_enum_framesizes  = &vidioc_enum_framesizes,
	.vidioc_enum_frameintervals = &vidioc_enum_frameintervals,
#endif

#ifndef HAVE__V4L2_CTRLS
	.vidioc_queryctrl         = &vidioc_queryctrl,
	.vidioc_g_ctrl            = &vidioc_g_ctrl,
	.vidioc_s_ctrl            = &vidioc_s_ctrl,
#endif /* HAVE__V4L2_CTRLS */

	.vidioc_enum_output       = &vidioc_enum_output,
	.vidioc_g_output          = &vidioc_g_output,
	.vidioc_s_output          = &vidioc_s_output,

	.vidioc_enum_input       = &vidioc_enum_input,
	.vidioc_g_input          = &vidioc_g_input,
	.vidioc_s_input          = &vidioc_s_input,

	.vidioc_enum_fmt_vid_cap = &vidioc_enum_fmt_cap,
	.vidioc_g_fmt_vid_cap    = &vidioc_g_fmt_cap,
	.vidioc_s_fmt_vid_cap    = &vidioc_s_fmt_cap,
	.vidioc_try_fmt_vid_cap  = &vidioc_try_fmt_cap,

	.vidioc_enum_fmt_vid_out = &vidioc_enum_fmt_out,
	.vidioc_s_fmt_vid_out    = &vidioc_s_fmt_out,
	.vidioc_g_fmt_vid_out    = &vidioc_g_fmt_out,
	.vidioc_try_fmt_vid_out  = &vidioc_try_fmt_out,

#ifdef V4L2L_OVERLAY
	.vidioc_s_fmt_vid_overlay = &vidioc_s_fmt_overlay,
	.vidioc_g_fmt_vid_overlay = &vidioc_g_fmt_overlay,
#endif

#ifdef WEBCAMSTUDIO_WITH_STD
	.vidioc_s_std            = &vidioc_s_std,
	.vidioc_g_std            = &vidioc_g_std,
	.vidioc_querystd         = &vidioc_querystd,
#endif /* WEBCAMSTUDIO_WITH_STD */

	.vidioc_g_parm           = &vidioc_g_parm,
	.vidioc_s_parm           = &vidioc_s_parm,

	.vidioc_reqbufs          = &vidioc_reqbufs,
	.vidioc_querybuf         = &vidioc_querybuf,
	.vidioc_qbuf             = &vidioc_qbuf,
	.vidioc_dqbuf            = &vidioc_dqbuf,

	.vidioc_streamon         = &vidioc_streamon,
	.vidioc_streamoff        = &vidioc_streamoff,

#ifdef CONFIG_VIDEO_V4L1_COMPAT
	.vidiocgmbuf             = &vidiocgmbuf,
#endif
};

static void zero_devices(void)
{
	int i;

	MARK();
	for (i = 0; i < MAX_DEVICES; i++)
		devs[i] = NULL;
}

static void free_devices(void)
{
	int i;

	MARK();
	for (i = 0; i < devices; i++) {
		if (NULL != devs[i]) {
			free_buffers(devs[i]);
			webcamstudio_remove_sysfs(devs[i]->vdev);
			kfree(video_get_drvdata(devs[i]->vdev));
			video_unregister_device(devs[i]->vdev);
			v4l2_device_unregister(&devs[i]->v4l2_dev);
			v4l2_ctrl_handler_free(&devs[i]->ctrl_handler);
			kfree(devs[i]);
			devs[i] = NULL;
		}
	}
}

int __init init_module(void)
{
	int ret;
	int i;
	MARK();

	zero_devices();
	if (devices < 0) {
		devices = 1;

		/* try guessing the devices from the "video_nr" parameter */
		for (i = MAX_DEVICES - 1; i >= 0; i--) {
			if (video_nr[i] >= 0) {
				devices = i + 1;
				break;
			}
		}
	}

	if (devices > MAX_DEVICES) {
		devices = MAX_DEVICES;
		printk(KERN_INFO "webcamstudio: number of devices is limited to: %d\n", MAX_DEVICES);
	}

	if (max_buffers > MAX_BUFFERS) {
		max_buffers = MAX_BUFFERS;
		printk(KERN_INFO "webcamstudio: number of buffers is limited to: %d\n", MAX_BUFFERS);
	}

	if (max_openers < 0) {
		printk(KERN_INFO "webcamstudio: allowing %d openers rather than %d\n", 2, max_openers);
		max_openers = 2;
	}

	if (max_width < 1) {
		max_width = WEBCAMSTUDIO_SIZE_MAX_WIDTH;
		printk(KERN_INFO "webcamstudio: using max_width %d\n", max_width);
	}
	if (max_height < 1) {
		max_height = WEBCAMSTUDIO_SIZE_MAX_HEIGHT;
		printk(KERN_INFO "webcamstudio: using max_height %d\n", max_height);
	}

	/* kfree on module release */
	for (i = 0; i < devices; i++) {
		dprintk("creating webcamstudio-device #%d\n", i);
		devs[i] = kzalloc(sizeof(*devs[i]), GFP_KERNEL);
		if (devs[i] == NULL) {
			free_devices();
			return -ENOMEM;
		}
		ret = webcamstudio_init(devs[i], i);
		if (ret < 0) {
			free_devices();
			return ret;
		}
		/* register the device -> it creates /dev/video* */
		if (video_register_device(devs[i]->vdev, VFL_TYPE_GRABBER, video_nr[i]) < 0) {
			video_device_release(devs[i]->vdev);
			printk(KERN_ERR "webcamstudio: failed video_register_device()\n");
			free_devices();
			return -EFAULT;
		}
		webcamstudio_create_sysfs(devs[i]->vdev);
	}

	dprintk("module installed\n");

	printk(KERN_INFO "webcamstudio driver version %d.%d.%d loaded\n",
			(WEBCAMSTUDIO_VERSION_CODE >> 16) & 0xff,
			(WEBCAMSTUDIO_VERSION_CODE >>  8) & 0xff,
			(WEBCAMSTUDIO_VERSION_CODE) & 0xff);

	return 0;
}

void __exit cleanup_module(void)
{
	MARK();
	/* unregister the device -> it deletes /dev/video* */
	free_devices();
	dprintk("module removed\n");
}



/*
 * fake usage of unused functions
 */
#ifdef HAVE__V4L2_CTRLS
static int vidioc_queryctrl(struct file *file, void *fh, struct v4l2_queryctrl *q)  __attribute__ ((unused));
static int vidioc_g_ctrl(struct file *file, void *fh, struct v4l2_control *c)  __attribute__ ((unused));
static int vidioc_s_ctrl(struct file *file, void *fh, struct v4l2_control *c)  __attribute__ ((unused));
#endif /* HAVE__V4L2_CTRLS */
