almost works, but apparently I need to implement the buffer mode.
[vdummy] / vdummy.c
1 #include <linux/module.h>
2 #include <linux/kernel.h>
3 #include <linux/version.h>
4 #include <linux/fs.h>
5 #include <linux/slab.h>
6 #include <asm/uaccess.h>
7 #include <linux/videodev2.h>
8 #include <media/v4l2-dev.h>
9 #include <media/v4l2-ioctl.h>
10 #include <media/v4l2-device.h>
11
12 static int init(void);
13 static void shutdown(void);
14 static int open(struct file *file);
15 static int close(struct file *file);
16 static ssize_t read(struct file *file, char *ubuf, size_t bufsz, loff_t *offs);
17 //static unsigned int poll(struct file *file, struct poll_table_struct *ptab);
18
19 static int ioctl_querycap(struct file *file, void *fh, struct v4l2_capability *cap);
20 static int ioctl_enum_fmt(struct file *file, void *fh, struct v4l2_fmtdesc *fdesc);
21 static int ioctl_get_fmt(struct file *file, void *fh, struct v4l2_format *fmt);
22 static int ioctl_set_fmt(struct file *file, void *fh, struct v4l2_format *fmt);
23 static int ioctl_get_parm(struct file *file, void *fh, struct v4l2_streamparm *fmt);
24 static int ioctl_set_parm(struct file *file, void *fh, struct v4l2_streamparm *fmt);
25 static int ioctl_queryctl(struct file *file, void *fh, struct v4l2_queryctrl *ctl);
26
27 static int update_frame(int xsz, int ysz);
28
29
30 module_init(init);
31 module_exit(shutdown);
32
33 MODULE_LICENSE("GPL");
34 MODULE_AUTHOR("John Tsiombikas");
35 MODULE_DESCRIPTION("v4l2 test module");
36
37 static struct video_device *vdev;
38
39 static struct v4l2_device v4l2_dev;
40 static struct v4l2_file_operations fops;
41 static struct v4l2_ioctl_ops iops;
42 static int width, height;
43 static unsigned char *frame;
44 static int frame_size;
45
46
47 static int init(void)
48 {
49         int res;
50
51         strcpy(v4l2_dev.name, "vdummy");
52         if((res = v4l2_device_register(0, &v4l2_dev)) != 0) {
53                 return res;
54         }
55
56         if(!(vdev = video_device_alloc())) {
57                 return -ENOMEM;
58         }
59         vdev->release = video_device_release;
60         strcpy(vdev->name, KBUILD_MODNAME);
61         vdev->fops = &fops;
62         vdev->ioctl_ops = &iops;
63         vdev->vfl_type = VFL_TYPE_GRABBER;
64         vdev->v4l2_dev = &v4l2_dev;
65
66         fops.owner = THIS_MODULE;
67         fops.open = open;
68         fops.release = close;
69         fops.read = read;
70         //fops.poll = poll;
71         fops.unlocked_ioctl = video_ioctl2;
72
73         iops.vidioc_querycap = ioctl_querycap;
74         iops.vidioc_enum_fmt_vid_cap = ioctl_enum_fmt;
75         iops.vidioc_g_fmt_vid_cap = ioctl_get_fmt;
76         iops.vidioc_s_fmt_vid_cap = ioctl_set_fmt;
77         iops.vidioc_g_parm = ioctl_get_parm;
78         iops.vidioc_s_parm = ioctl_set_parm;
79         iops.vidioc_queryctrl = ioctl_queryctl;
80
81         if((res = video_register_device(vdev, VFL_TYPE_GRABBER, -1)) != 0) {
82                 video_device_release(vdev);
83                 return res;
84         }
85
86         printk(KERN_INFO "vdummy device initialized\n");
87         return 0;
88 }
89
90 static void shutdown(void)
91 {
92         video_unregister_device(vdev);
93         v4l2_device_unregister(&v4l2_dev);
94         kfree(frame);
95 }
96
97 static int open(struct file *file)
98 {
99         int res;
100         try_module_get(THIS_MODULE);
101         if((res = update_frame(640, 480)) != 0) {
102                 return res;
103         }
104         return 0;
105 }
106
107 static int close(struct file *file)
108 {
109         module_put(THIS_MODULE);
110         return 0;
111 }
112
113 static ssize_t read(struct file *file, char *ubuf, size_t ubufsz, loff_t *offs)
114 {
115         int sz = frame_size - *offs;
116
117         if(sz <= 0) return 0;
118         if(ubufsz < sz) sz = ubufsz;
119
120         if(copy_to_user(ubuf, frame + *offs, sz) != 0) {
121                 return -EFAULT;
122         }
123
124         *offs += sz;
125         if(*offs >= frame_size) {
126                 *offs = 0;
127         }
128         return sz;
129 }
130
131 /*
132 static unsigned int poll(struct file *file, struct poll_table_struct *ptab)
133 {
134 }
135 */
136
137 static int ioctl_querycap(struct file *file, void *fh, struct v4l2_capability *cap)
138 {
139         strcpy(cap->driver, KBUILD_MODNAME);
140         strcpy(cap->card, "dummy v4l2 dev");
141         strcpy(cap->bus_info, "nobus");
142         cap->device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_READWRITE;
143         cap->capabilities = cap->device_caps | V4L2_CAP_DEVICE_CAPS;
144         return 0;
145 }
146
147 static int ioctl_enum_fmt(struct file *file, void *fh, struct v4l2_fmtdesc *fdesc)
148 {
149         if(fdesc->index != 0 || fdesc->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) {
150                 return -EINVAL;
151         }
152         fdesc->flags = 0;
153         strcpy(fdesc->description, "RGB24");
154         fdesc->pixelformat = V4L2_PIX_FMT_RGB24;
155         return 0;
156 }
157
158 static int ioctl_get_fmt(struct file *file, void *fh, struct v4l2_format *fmt)
159 {
160         if(fmt->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) {
161                 return -EINVAL;
162         }
163         fmt->fmt.pix.width = width;
164         fmt->fmt.pix.height = height;
165         fmt->fmt.pix.pixelformat = V4L2_PIX_FMT_RGB24;
166         fmt->fmt.pix.field = V4L2_FIELD_NONE;
167         fmt->fmt.pix.bytesperline = 0;
168         fmt->fmt.pix.sizeimage = frame_size;
169         fmt->fmt.pix.colorspace = V4L2_COLORSPACE_SRGB;
170         fmt->fmt.pix.priv = 0;
171         fmt->fmt.pix.flags = 0;
172         fmt->fmt.pix.ycbcr_enc = 0;
173         fmt->fmt.pix.quantization = V4L2_QUANTIZATION_FULL_RANGE;
174         fmt->fmt.pix.xfer_func = V4L2_XFER_FUNC_NONE;
175         return 0;
176 }
177
178 static int ioctl_set_fmt(struct file *file, void *fh, struct v4l2_format *fmt)
179 {
180         int res;
181
182         printk(KERN_INFO "ioctl_set_fmt called\n");
183
184         if(fmt->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) {
185                 return -EINVAL;
186         }
187         if((res = update_frame(fmt->fmt.pix.width, fmt->fmt.pix.height)) != 0) {
188                 return res;
189         }
190         return ioctl_get_fmt(file, fh, fmt);
191 }
192
193 static int ioctl_get_parm(struct file *file, void *fh, struct v4l2_streamparm *parm)
194 {
195         if(parm->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) {
196                 return -EINVAL;
197         }
198         parm->parm.capture.capability = 0;
199         parm->parm.capture.capturemode = 0;
200         parm->parm.capture.timeperframe.numerator = 1;
201         parm->parm.capture.timeperframe.denominator = 30;
202         parm->parm.capture.extendedmode = 0;
203         parm->parm.capture.readbuffers = 1;
204         return 0;
205 }
206
207 static int ioctl_set_parm(struct file *file, void *fh, struct v4l2_streamparm *parm)
208 {
209         if(parm->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) {
210                 return -EINVAL;
211         }
212         return ioctl_get_parm(file, fh, parm);
213 }
214
215 static int ioctl_queryctl(struct file *file, void *fh, struct v4l2_queryctrl *ctl)
216 {
217         return -EINVAL;
218 }
219
220 static int update_frame(int xsz, int ysz)
221 {
222         unsigned char *tmp;
223         int i, j;
224         int new_fsz = xsz * ysz * 3;
225
226         printk(KERN_INFO "update_frame(%d, %d)\n", xsz, ysz);
227
228         if(xsz == width && ysz == height) {
229                 return 0;
230         }
231
232         if(!frame || frame_size < new_fsz) {
233                 if(!(tmp = kmalloc(new_fsz, GFP_KERNEL))) {
234                         return -ENOMEM;
235                 }
236                 kfree(frame);
237                 frame_size = new_fsz;
238                 width = xsz;
239                 height = ysz;
240                 frame = tmp;
241         }
242
243         tmp = frame;
244         for(i=0; i<height; i++) {
245                 for(j=0; j<width; j++) {
246                         int val = i ^ j;
247                         *tmp++ = val;
248                         *tmp++ = (val << 1) & 0xff;
249                         *tmp++ = (val << 2) & 0xff;
250                 }
251         }
252         return 0;
253 }