--- /dev/null
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/version.h>
+#include <linux/fs.h>
+#include <linux/slab.h>
+#include <asm/uaccess.h>
+#include <linux/videodev2.h>
+#include <media/v4l2-dev.h>
+#include <media/v4l2-ioctl.h>
+
+static int init(void);
+static void shutdown(void);
+static int open(struct file *file);
+static int close(struct file *file);
+static ssize_t read(struct file *file, char *ubuf, size_t bufsz, loff_t *offs);
+
+static int ioctl_querycap(struct file *file, void *fh, struct v4l2_capability *cap);
+static int ioctl_enum_fmt(struct file *file, void *fh, struct v4l2_fmtdesc *fdesc);
+static int ioctl_get_fmt(struct file *file, void *fh, struct v4l2_format *fmt);
+static int ioctl_set_fmt(struct file *file, void *fh, struct v4l2_format *fmt);
+static int ioctl_queryctl(struct file *file, void *fh, struct v4l2_queryctrl *ctl);
+
+static int update_frame(int xsz, int ysz);
+
+
+module_init(init);
+module_exit(shutdown);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("John Tsiombikas");
+MODULE_DESCRIPTION("v4l2 test module");
+
+#define DEVNAME "video42"
+
+struct video_device *vdev;
+
+static struct v4l2_file_operations fops;
+static struct v4l2_ioctl_ops iops;
+static int width, height;
+static unsigned char *frame;
+static int frame_size;
+
+
+static int init(void)
+{
+ int res;
+
+ if(!(vdev = video_device_alloc())) {
+ return -ENOMEM;
+ }
+ vdev->release = video_device_release;
+ strcpy(vdev->name, "video test");
+ vdev->fops = &fops;
+ vdev->ioctl_ops = &iops;
+ vdev->vfl_type = VFL_TYPE_GRABBER;
+
+ fops.open = open;
+ fops.release = close;
+ fops.read = read;
+ fops.unlocked_ioctl = video_ioctl2;
+
+ iops.vidioc_querycap = ioctl_querycap;
+ iops.vidioc_enum_fmt_vid_cap = ioctl_enum_fmt;
+ iops.vidioc_g_fmt_vid_cap = ioctl_get_fmt;
+ iops.vidioc_s_fmt_vid_cap = ioctl_set_fmt;
+ iops.vidioc_queryctrl = ioctl_queryctl;
+
+ if((res = video_register_device(vdev, VFL_TYPE_GRABBER, -1)) != 0) {
+ video_device_release(vdev);
+ return res;
+ }
+
+ return 0;
+}
+
+static void shutdown(void)
+{
+ video_unregister_device(vdev);
+ kfree(frame);
+}
+
+static int open(struct file *file)
+{
+ int res;
+ try_module_get(THIS_MODULE);
+ if((res = update_frame(640, 480)) != 0) {
+ return res;
+ }
+ return 0;
+}
+
+static int close(struct file *file)
+{
+ module_put(THIS_MODULE);
+ return 0;
+}
+
+static ssize_t read(struct file *file, char *ubuf, size_t ubufsz, loff_t *offs)
+{
+ int sz = frame_size - *offs;
+
+ if(sz <= 0) return 0;
+ if(ubufsz < sz) sz = ubufsz;
+
+ if(copy_to_user(ubuf, frame + *offs, sz) != 0) {
+ return -EFAULT;
+ }
+
+ *offs += sz;
+ if(*offs >= frame_size) {
+ *offs = 0;
+ }
+ return sz;
+}
+
+static int ioctl_querycap(struct file *file, void *fh, struct v4l2_capability *cap)
+{
+ strcpy(cap->driver, "vidtest");
+ strcpy(cap->card, "fake");
+ strcpy(cap->bus_info, "nobus");
+ cap->version = 0;
+ cap->capabilities = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_READWRITE;
+ return 0;
+}
+
+static int ioctl_enum_fmt(struct file *file, void *fh, struct v4l2_fmtdesc *fdesc)
+{
+ if(fdesc->index != 0 || fdesc->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) {
+ return -EINVAL;
+ }
+ fdesc->flags = 0;
+ strcpy(fdesc->description, "RGB24");
+ fdesc->pixelformat = V4L2_PIX_FMT_RGB24;
+ return 0;
+}
+
+static int ioctl_get_fmt(struct file *file, void *fh, struct v4l2_format *fmt)
+{
+ if(fmt->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) {
+ return -EINVAL;
+ }
+ fmt->fmt.pix.width = width;
+ fmt->fmt.pix.height = height;
+ fmt->fmt.pix.pixelformat = V4L2_PIX_FMT_RGB24;
+ fmt->fmt.pix.field = V4L2_FIELD_NONE;
+ fmt->fmt.pix.bytesperline = 0;
+ fmt->fmt.pix.sizeimage = frame_size;
+ fmt->fmt.pix.colorspace = V4L2_COLORSPACE_SRGB;
+ fmt->fmt.pix.priv = 0;
+ fmt->fmt.pix.flags = 0;
+ fmt->fmt.pix.ycbcr_enc = 0;
+ fmt->fmt.pix.quantization = V4L2_QUANTIZATION_FULL_RANGE;
+ fmt->fmt.pix.xfer_func = V4L2_XFER_FUNC_NONE;
+ return 0;
+}
+
+static int ioctl_set_fmt(struct file *file, void *fh, struct v4l2_format *fmt)
+{
+ int res;
+
+ if(fmt->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) {
+ return -EINVAL;
+ }
+ if((res = update_frame(fmt->fmt.pix.width, fmt->fmt.pix.height)) != 0) {
+ return res;
+ }
+ return ioctl_get_fmt(file, fh, fmt);
+}
+
+static int ioctl_queryctl(struct file *file, void *fh, struct v4l2_queryctrl *ctl)
+{
+ return -EINVAL;
+}
+
+static int update_frame(int xsz, int ysz)
+{
+ unsigned char *tmp;
+ int i, j;
+ int new_fsz = xsz * ysz;
+
+ if(xsz == width && ysz == height) {
+ return 0;
+ }
+
+ if(!frame || frame_size < new_fsz) {
+ if(!(tmp = kmalloc(new_fsz, GFP_KERNEL))) {
+ return -ENOMEM;
+ }
+ kfree(frame);
+ frame_size = new_fsz;
+ width = xsz;
+ height = ysz;
+ frame = tmp;
+ }
+
+ tmp = frame;
+ for(i=0; i<height; i++) {
+ for(j=0; j<width; j++) {
+ int val = i ^ j;
+ *tmp++ = val;
+ *tmp++ = (val << 1) & 0xff;
+ *tmp++ = (val << 2) & 0xff;
+ }
+ }
+ return 0;
+}