Make auto exposure a boolean variable.
[microdia.git] / sn9c20x-v4l2.c
blobcfaf20ccf42bdd14ce071de1a791a44161a6eefe
1 /**
2 * @file sn9c20x-v4l2.c
3 * @author Nicolas VIVIEN
4 * @date 2008-02-01
6 * @brief V4L2 interface and functions
8 * @note Copyright (C) Nicolas VIVIEN
10 * @par Licences
12 * This program is free software; you can redistribute it and/or modify
13 * it under the terms of the GNU General Public License as published by
14 * the Free Software Foundation; either version 2 of the License, or
15 * any later version.
17 * This program is distributed in the hope that it will be useful,
18 * but WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 * GNU General Public License for more details.
22 * You should have received a copy of the GNU General Public License
23 * along with this program; if not, write to the Free Software
24 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
27 #include <linux/module.h>
28 #include <linux/init.h>
29 #include <linux/kernel.h>
30 #include <linux/version.h>
31 #include <linux/errno.h>
32 #include <linux/slab.h>
33 #include <linux/kref.h>
34 #include <linux/vmalloc.h>
35 #include <linux/usb.h>
36 #include <linux/mm.h>
38 #include "sn9c20x.h"
39 #include "sn9c20x-bridge.h"
41 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27)
42 #include <media/v4l2-ioctl.h>
43 #endif
45 static struct file_operations v4l_sn9c20x_fops;
46 /**
47 * @var sn9c20x_controls
48 * List of all V4Lv2 controls supported by the driver
50 static struct v4l2_queryctrl sn9c20x_controls[] = {
52 .id = V4L2_CID_BRIGHTNESS,
53 .type = V4L2_CTRL_TYPE_INTEGER,
54 .name = "Brightness",
55 .minimum = 0,
56 .maximum = 0xff,
57 .step = 1,
60 .id = V4L2_CID_GAMMA,
61 .type = V4L2_CTRL_TYPE_INTEGER,
62 .name = "Gamma",
63 .minimum = 0,
64 .maximum = 0xff,
65 .step = 1,
69 .id = V4L2_CID_SATURATION,
70 .type = V4L2_CTRL_TYPE_INTEGER,
71 .name = "Saturation",
72 .minimum = 0,
73 .maximum = 0xff,
74 .step = 1,
78 .id = V4L2_CID_CONTRAST,
79 .type = V4L2_CTRL_TYPE_INTEGER,
80 .name = "Contrast",
81 .minimum = 0,
82 .maximum = 0xff,
83 .step = 1,
86 .id = V4L2_CID_EXPOSURE,
87 .type = V4L2_CTRL_TYPE_INTEGER,
88 .name = "Exposure",
89 .minimum = 0,
90 .maximum = 0xff,
91 .step = 1,
94 .id = V4L2_CID_GAIN,
95 .type = V4L2_CTRL_TYPE_INTEGER,
96 .name = "Gain",
97 .minimum = 0,
98 .maximum = 0xff,
99 .step = 1,
102 .id = V4L2_CID_HFLIP,
103 .type = V4L2_CTRL_TYPE_BOOLEAN,
104 .name = "Horizontal flip",
105 .minimum = 0,
106 .maximum = 1,
107 .step = 1,
110 .id = V4L2_CID_VFLIP,
111 .type = V4L2_CTRL_TYPE_BOOLEAN,
112 .name = "Vertical flip",
113 .minimum = 0,
114 .maximum = 1,
115 .step = 1,
118 .id = V4L2_CID_SHARPNESS,
119 .type = V4L2_CTRL_TYPE_INTEGER,
120 .name = "Sharpness",
121 .minimum = 0,
122 .maximum = 0x3f,
123 .step = 1,
126 .id = V4L2_CID_RED_BALANCE,
127 .type = V4L2_CTRL_TYPE_INTEGER,
128 .name = "Red Balance",
129 .minimum = 0,
130 .maximum = 0x7f,
131 .step = 1,
134 .id = V4L2_CID_BLUE_BALANCE,
135 .type = V4L2_CTRL_TYPE_INTEGER,
136 .name = "Blue Balance",
137 .minimum = 0,
138 .maximum = 0x7f,
139 .step = 1,
141 /* According to v4l2 specs auto exposure should be a 4 step value.
142 * This make little since for webcams however so a boolean is used
143 * instead.
146 .id = V4L2_CID_EXPOSURE_AUTO,
147 .type = V4L2_CTRL_TYPE_BOOLEAN,
148 .name = "Automatic exposure control",
149 .minimum = 0,
150 .maximum = 1,
151 .step = 1,
154 .id = V4L2_CID_AUTOGAIN,
155 .type = V4L2_CTRL_TYPE_BOOLEAN,
156 .name = "Automatic gain control",
157 .minimum = 0,
158 .maximum = 1,
159 .step = 1,
162 .id = V4L2_CID_AUTO_WHITE_BALANCE,
163 .type = V4L2_CTRL_TYPE_BOOLEAN,
164 .name = "Automatic whitbalance control",
165 .minimum = 0,
166 .maximum = 1,
167 .step = 1,
171 void v4l2_set_control_default(struct usb_sn9c20x *dev, __u32 ctrl, __u16 value)
173 int i;
174 for (i = 0; i < ARRAY_SIZE(sn9c20x_controls); i++) {
175 if (sn9c20x_controls[i].id == ctrl) {
176 sn9c20x_controls[i].default_value = value;
177 sn9c20x_set_camera_control(dev,
178 ctrl,
179 value);
180 break;
185 void v4l_add_jpegheader(struct usb_sn9c20x *dev, __u8 *buffer,
186 __u32 buffer_size)
188 __u8 jpeg_header[589] = {
189 0xff, 0xd8, 0xff, 0xdb, 0x00, 0x84, 0x00, 0x06, 0x04, 0x05,
190 0x06, 0x05, 0x04, 0x06, 0x06, 0x05, 0x06, 0x07, 0x07, 0x06,
191 0x08, 0x0a, 0x10, 0x0a, 0x0a, 0x09, 0x09, 0x0a, 0x14, 0x0e,
192 0x0f, 0x0c, 0x10, 0x17, 0x14, 0x18, 0x18, 0x17, 0x14, 0x16,
193 0x16, 0x1a, 0x1d, 0x25, 0x1f, 0x1a, 0x1b, 0x23, 0x1c, 0x16,
194 0x16, 0x20, 0x2c, 0x20, 0x23, 0x26, 0x27, 0x29, 0x2a, 0x29,
195 0x19, 0x1f, 0x2d, 0x30, 0x2d, 0x28, 0x30, 0x25, 0x28, 0x29,
196 0x28, 0x01, 0x07, 0x07, 0x07, 0x0a, 0x08, 0x0a, 0x13, 0x0a,
197 0x0a, 0x13, 0x28, 0x1a, 0x16, 0x1a, 0x28, 0x28, 0x28, 0x28,
198 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28,
199 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28,
200 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28,
201 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28,
202 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0xff, 0xc4, 0x01, 0xa2,
203 0x00, 0x00, 0x01, 0x05, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
204 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x02,
205 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x01,
206 0x00, 0x03, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
207 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x03,
208 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x10, 0x00,
209 0x02, 0x01, 0x03, 0x03, 0x02, 0x04, 0x03, 0x05, 0x05, 0x04,
210 0x04, 0x00, 0x00, 0x01, 0x7d, 0x01, 0x02, 0x03, 0x00, 0x04,
211 0x11, 0x05, 0x12, 0x21, 0x31, 0x41, 0x06, 0x13, 0x51, 0x61,
212 0x07, 0x22, 0x71, 0x14, 0x32, 0x81, 0x91, 0xa1, 0x08, 0x23,
213 0x42, 0xb1, 0xc1, 0x15, 0x52, 0xd1, 0xf0, 0x24, 0x33, 0x62,
214 0x72, 0x82, 0x09, 0x0a, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x25,
215 0x26, 0x27, 0x28, 0x29, 0x2a, 0x34, 0x35, 0x36, 0x37, 0x38,
216 0x39, 0x3a, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a,
217 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5a, 0x63, 0x64,
218 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x73, 0x74, 0x75, 0x76,
219 0x77, 0x78, 0x79, 0x7a, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88,
220 0x89, 0x8a, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99,
221 0x9a, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, 0xa8, 0xa9, 0xaa,
222 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, 0xb8, 0xb9, 0xba, 0xc2,
223 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xd2, 0xd3,
224 0xd4, 0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda, 0xe1, 0xe2, 0xe3,
225 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, 0xea, 0xf1, 0xf2, 0xf3,
226 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9, 0xfa, 0x11, 0x00, 0x02,
227 0x01, 0x02, 0x04, 0x04, 0x03, 0x04, 0x07, 0x05, 0x04, 0x04,
228 0x00, 0x01, 0x02, 0x77, 0x00, 0x01, 0x02, 0x03, 0x11, 0x04,
229 0x05, 0x21, 0x31, 0x06, 0x12, 0x41, 0x51, 0x07, 0x61, 0x71,
230 0x13, 0x22, 0x32, 0x81, 0x08, 0x14, 0x42, 0x91, 0xa1, 0xb1,
231 0xc1, 0x09, 0x23, 0x33, 0x52, 0xf0, 0x15, 0x62, 0x72, 0xd1,
232 0x0a, 0x16, 0x24, 0x34, 0xe1, 0x25, 0xf1, 0x17, 0x18, 0x19,
233 0x1a, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x35, 0x36, 0x37, 0x38,
234 0x39, 0x3a, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a,
235 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5a, 0x63, 0x64,
236 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x73, 0x74, 0x75, 0x76,
237 0x77, 0x78, 0x79, 0x7a, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87,
238 0x88, 0x89, 0x8a, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98,
239 0x99, 0x9a, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, 0xa8, 0xa9,
240 0xaa, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, 0xb8, 0xb9, 0xba,
241 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xd2,
242 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda, 0xe2, 0xe3,
243 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, 0xea, 0xf2, 0xf3, 0xf4,
244 0xf5, 0xf6, 0xf7, 0xf8, 0xf9, 0xfa, 0xff, 0xc0, 0x00, 0x11,
245 0x08, 0x01, 0xe0, 0x02, 0x80, 0x03, 0x01, 0x21, 0x00, 0x02,
246 0x11, 0x01, 0x03, 0x11, 0x01, 0xff, 0xda, 0x00, 0x0c, 0x03,
247 0x01, 0x00, 0x02, 0x11, 0x03, 0x11, 0x00, 0x3f, 0x00
249 __u8 qtable1[128] = {
250 0x0d, 0x08, 0x08, 0x0d, 0x08, 0x08, 0x0d, 0x0d,
251 0x0d, 0x0d, 0x11, 0x0d, 0x0d, 0x11, 0x15, 0x21,
252 0x15, 0x15, 0x11, 0x11, 0x15, 0x2a, 0x1d, 0x1d,
253 0x19, 0x21, 0x32, 0x2a, 0x32, 0x32, 0x2e, 0x2a,
254 0x2e, 0x2e, 0x36, 0x3a, 0x4b, 0x43, 0x36, 0x3a,
255 0x47, 0x3a, 0x2e, 0x2e, 0x43, 0x5c, 0x43, 0x47,
256 0x4f, 0x54, 0x58, 0x58, 0x58, 0x32, 0x3f, 0x60,
257 0x64, 0x5c, 0x54, 0x64, 0x4b, 0x54, 0x58, 0x54,
258 0x0d, 0x11, 0x11, 0x15, 0x11, 0x15, 0x26, 0x15,
259 0x15, 0x26, 0x54, 0x36, 0x2e, 0x36, 0x54, 0x54,
260 0x54, 0x54, 0x54, 0x54, 0x54, 0x54, 0x54, 0x54,
261 0x54, 0x54, 0x54, 0x54, 0x54, 0x54, 0x54, 0x54,
262 0x54, 0x54, 0x54, 0x54, 0x54, 0x54, 0x54, 0x54,
263 0x54, 0x54, 0x54, 0x54, 0x54, 0x54, 0x54, 0x54,
264 0x54, 0x54, 0x54, 0x54, 0x54, 0x54, 0x54, 0x54,
265 0x54, 0x54, 0x54, 0x54, 0x54, 0x54, 0x54, 0x54
268 jpeg_header[6] = 0x00;
269 jpeg_header[71] = 0x01;
270 memcpy(jpeg_header + 7, qtable1, 64);
271 memcpy(jpeg_header + 8 + 64, qtable1+64, 64);
272 jpeg_header[564] = dev->vsettings.format.width & 0xFF;
273 jpeg_header[563] = (dev->vsettings.format.width >> 8) & 0xFF;
274 jpeg_header[562] = dev->vsettings.format.height & 0xFF;
275 jpeg_header[561] = (dev->vsettings.format.height >> 8) & 0xFF;
276 jpeg_header[567] = 0x21;
278 memmove(buffer+589, buffer, buffer_size);
279 memcpy(buffer, jpeg_header, 589);
282 * @brief Get V4L privileges
284 * @param file
286 * @return 0 or negative error code
289 int v4l_get_privileges(struct file *file)
291 struct usb_sn9c20x *dev;
292 int ret = 0;
294 dev = video_get_drvdata(video_devdata(file));
296 if (dev->owner == file)
297 return 0;
299 mutex_lock(&open_lock);
300 if (dev->owner != NULL) {
301 ret = -EBUSY;
302 goto done;
304 dev->owner = file;
305 done:
306 mutex_unlock(&open_lock);
307 return ret;
311 * @brief Check whether there are V4L privileges
313 * @param file
315 * @return 0 or 1
318 int v4l_has_privileges(struct file *file)
320 struct usb_sn9c20x *dev;
321 int ret = 0;
323 dev = video_get_drvdata(video_devdata(file));
325 if (dev->owner == file)
326 ret = 1;
328 return ret;
332 * @brief Drop V4L privileges
334 * @param file
337 void v4l_drop_privileges(struct file *file)
339 struct usb_sn9c20x *dev;
341 dev = video_get_drvdata(video_devdata(file));
343 if (dev->owner == file)
344 dev->owner = NULL;
348 * @brief Enable video stream
350 * @param dev Pointer to device structure
351 * @param mode Mode for video stream
353 * @returns 0 or negative error value
356 int v4l2_enable_video(struct usb_sn9c20x *dev, int mode)
358 int ret;
360 if (mode == SN9C20X_MODE_IDLE) {
361 sn9c20x_enable_video(dev, 0);
362 usb_sn9c20x_uninit_urbs(dev);
363 sn9c20x_queue_enable(&dev->queue, 0);
364 dev->mode = mode;
365 return 0;
368 if (dev->mode != SN9C20X_MODE_IDLE)
369 return -EBUSY;
371 if (sn9c20x_queue_enable(&dev->queue, 1) < 0)
372 return -EBUSY;
374 ret = usb_sn9c20x_init_urbs(dev);
376 if (ret)
377 return ret;
379 sn9c20x_enable_video(dev, 1);
380 dev->mode = mode;
382 if (dev->vsettings.format.pixelformat == V4L2_PIX_FMT_JPEG)
383 dev->queue.flags &= ~SN9C20X_QUEUE_DROP_INCOMPLETE;
384 else
385 dev->queue.flags |= SN9C20X_QUEUE_DROP_INCOMPLETE;
387 return 0;
391 * @param inode Pointer on an inode
392 * @param fp File pointer
394 * @returns 0 if all is OK
396 * @brief Open the video device
398 * This function permits to open a video device (/dev/videoX)
400 static int v4l_sn9c20x_open(struct inode *inode, struct file *fp)
402 int ret = 0;
404 struct usb_sn9c20x *dev;
405 struct video_device *vdev;
407 mutex_lock(&open_lock);
409 vdev = video_devdata(fp);
410 dev = video_get_drvdata(video_devdata(fp));
412 fp->private_data = vdev;
414 kref_get(&dev->vopen);
416 mutex_unlock(&open_lock);
417 return ret;
422 * @param inode Pointer on inode
423 * @param fp File pointer
425 * @returns 0 if all is OK
427 * @brief Release an opened file.
429 * This function permits to release an opened file with the 'open' method.
431 static int v4l_sn9c20x_release(struct inode *inode, struct file *fp)
433 struct usb_sn9c20x *dev;
434 struct video_device *vdev;
436 mutex_lock(&open_lock);
438 vdev = video_devdata(fp);
439 dev = video_get_drvdata(video_devdata(fp));
441 if (v4l_has_privileges(fp)) {
442 v4l2_enable_video(dev, SN9C20X_MODE_IDLE);
444 mutex_lock(&dev->queue.mutex);
445 sn9c20x_free_buffers(&dev->queue);
446 mutex_unlock(&dev->queue.mutex);
449 v4l_drop_privileges(fp);
451 kref_put(&dev->vopen, usb_sn9c20x_delete);
453 mutex_unlock(&open_lock);
454 return 0;
459 * @param fp File pointer
461 * @retval buf Buffer in user space
462 * @retval count
463 * @retval f_pos
465 * @returns Count value
467 * @brief Read the video device
469 * This function is called by the application is reading the video device.
471 static ssize_t v4l_sn9c20x_read(struct file *fp, char __user *buf,
472 size_t count, loff_t *f_pos)
474 int i, ret;
475 int nbuffers;
476 struct v4l2_buffer buffer;
477 struct usb_sn9c20x *dev;
479 dev = video_get_drvdata(video_devdata(fp));
481 ret = v4l_get_privileges(fp);
482 if (ret < 0)
483 return ret;
485 if (dev->mode != SN9C20X_MODE_IDLE &&
486 dev->mode != SN9C20X_MODE_READ)
487 return -EBUSY;
489 buffer.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
490 buffer.memory = V4L2_MEMORY_MMAP;
491 if (dev->mode == SN9C20X_MODE_IDLE) {
492 nbuffers = sn9c20x_alloc_buffers(&dev->queue, 2,
493 dev->vsettings.format.sizeimage);
494 if (nbuffers < 0)
495 return nbuffers;
497 for (i = 0; i < nbuffers; i++) {
498 buffer = dev->queue.buffer[i].buf;
499 sn9c20x_queue_buffer(&dev->queue, &buffer);
502 ret = v4l2_enable_video(dev, SN9C20X_MODE_READ);
503 if (ret < 0)
504 return ret;
507 dev_sn9c20x_call_constantly(dev);
509 if (dev->queue.read_buffer == NULL) {
510 ret = sn9c20x_dequeue_buffer(&dev->queue, &buffer,
511 fp->f_flags & O_NONBLOCK);
512 if (ret < 0)
513 return ret;
515 if (dev->vsettings.format.pixelformat == V4L2_PIX_FMT_JPEG) {
516 UDIA_DEBUG("Adding JPEG Header\n");
517 v4l_add_jpegheader(dev, dev->queue.mem + buffer.m.offset,
518 buffer.bytesused);
519 buffer.bytesused += 589;
522 dev->queue.read_buffer = &dev->queue.buffer[buffer.index];
523 } else {
524 buffer = dev->queue.read_buffer->buf;
527 count = min((size_t)(buffer.bytesused - *f_pos), count);
528 if (copy_to_user(buf, dev->queue.mem + buffer.m.offset + *f_pos, count))
529 return -EFAULT;
531 *f_pos += count;
532 if (*f_pos >= buffer.bytesused) {
533 dev->queue.read_buffer = NULL;
534 sn9c20x_queue_buffer(&dev->queue, &buffer);
535 *f_pos = 0;
537 return count;
542 * @param fp File pointer
543 * @param wait
545 * @returns 0 if all is OK
547 * @brief Polling function
549 static unsigned int v4l_sn9c20x_poll(struct file *fp, poll_table *wait)
551 struct usb_sn9c20x *dev;
552 struct video_device *vdev;
554 vdev = video_devdata(fp);
555 dev = video_get_drvdata(video_devdata(fp));
557 UDIA_STREAM("Poll\n");
559 if (vdev == NULL || dev == NULL)
560 return -EFAULT;
562 return sn9c20x_queue_poll(&dev->queue, fp, wait);
566 * @param vma
569 static void sn9c20x_vm_open(struct vm_area_struct *vma)
571 struct sn9c20x_buffer *buffer = vma->vm_private_data;
572 buffer->vma_use_count++;
577 * @param vma
580 static void sn9c20x_vm_close(struct vm_area_struct *vma)
582 struct sn9c20x_buffer *buffer = vma->vm_private_data;
583 buffer->vma_use_count--;
586 struct vm_operations_struct sn9c20x_vm_ops = {
587 .open = sn9c20x_vm_open,
588 .close = sn9c20x_vm_close
592 * @param fp File pointer
593 * @param vma VMA structure
595 * @returns 0 if all is OK
597 * @brief Memory map
599 * This function permits to map a memory space.
601 static int v4l_sn9c20x_mmap(struct file *fp, struct vm_area_struct *vma)
603 struct page *page;
604 unsigned long addr, start, size;
605 unsigned int i;
606 int ret = 0;
608 struct usb_sn9c20x *dev;
609 struct video_device *vdev;
610 struct sn9c20x_buffer *buffer = NULL;
612 vdev = video_devdata(fp);
613 dev = video_get_drvdata(video_devdata(fp));
615 UDIA_STREAM("mmap\n");
617 start = vma->vm_start;
618 size = vma->vm_end - vma->vm_start;
620 mutex_lock(&dev->queue.mutex);
622 for (i = 0; i < dev->queue.count; ++i) {
623 buffer = &dev->queue.buffer[i];
624 if ((buffer->buf.m.offset >> PAGE_SHIFT) == vma->vm_pgoff)
625 break;
628 if (i == dev->queue.count || size != dev->queue.buf_size) {
629 ret = -EINVAL;
630 goto done;
633 vma->vm_flags |= VM_IO;
635 addr = (unsigned long)dev->queue.mem + buffer->buf.m.offset;
636 while (size > 0) {
637 page = vmalloc_to_page((void *)addr);
638 ret = vm_insert_page(vma, start, page);
639 if (ret < 0)
640 goto done;
642 start += PAGE_SIZE;
643 addr += PAGE_SIZE;
644 size -= PAGE_SIZE;
647 vma->vm_ops = &sn9c20x_vm_ops;
648 vma->vm_private_data = buffer;
649 sn9c20x_vm_open(vma);
650 done:
651 mutex_unlock(&dev->queue.mutex);
652 return ret;
656 * @param file
657 * @param priv
658 * @param cap
660 * @return 0
663 int sn9c20x_vidioc_querycap(struct file *file, void *priv,
664 struct v4l2_capability *cap)
666 struct usb_sn9c20x *dev;
668 dev = video_get_drvdata(priv);
670 UDIA_DEBUG("VIDIOC_QUERYCAP\n");
672 strlcpy(cap->driver, "sn9c20x", sizeof(cap->driver));
673 cap->capabilities = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING
674 | V4L2_CAP_READWRITE;
675 cap->version = (__u32) DRIVER_VERSION_NUM,
676 strlcpy(cap->card, dev->vdev->name, sizeof(cap->card));
678 if (usb_make_path(dev->udev, cap->bus_info, sizeof(cap->bus_info)) < 0)
679 strlcpy(cap->bus_info, dev->vdev->name, sizeof(cap->bus_info));
680 return 0;
684 * @param file
685 * @param priv
686 * @param input
688 * @return 0 or negative error code
691 int sn9c20x_vidioc_enum_input(struct file *file, void *priv,
692 struct v4l2_input *input)
694 UDIA_DEBUG("VIDIOC_ENUMINPUT %d\n", input->index);
696 if (input->index)
697 return -EINVAL;
699 strlcpy(input->name, "Webcam", sizeof(input->name));
700 input->type = V4L2_INPUT_TYPE_CAMERA;
701 input->std = 0;
703 return 0;
707 * @param file
708 * @param priv
709 * @param index
711 * @return 0 or negative error code
714 int sn9c20x_vidioc_g_input(struct file *file, void *priv, unsigned int *index)
716 UDIA_DEBUG("GET INPUT %d\n", *index);
718 if (index)
719 return -EINVAL;
721 return 0;
725 * @param file
726 * @param priv
727 * @param index
729 * @return 0 or negative error code
732 int sn9c20x_vidioc_s_input(struct file *file, void *priv, unsigned int index)
734 UDIA_DEBUG("SET INPUT %d\n", index);
736 if (v4l_get_privileges(file) < 0)
737 return -EBUSY;
739 if (index != 0)
740 return -EINVAL;
742 return 0;
746 * @param file
747 * @param priv
748 * @param ctrl
750 * @return 0 or negative error code
753 int sn9c20x_vidioc_queryctrl(struct file *file, void *priv,
754 struct v4l2_queryctrl *ctrl)
756 int i;
757 #ifdef V4L2_CTRL_FLAG_NEXT_CTRL
758 int min;
759 __u32 idnew, idlast;
760 #endif
762 UDIA_DEBUG("VIDIOC_QUERYCTRL id = %d\n", ctrl->id);
764 #ifdef V4L2_CTRL_FLAG_NEXT_CTRL
765 if (ctrl->id & V4L2_CTRL_FLAG_NEXT_CTRL) {
766 min = 0;
767 idnew = V4L2_CTRL_FLAG_NEXT_CTRL;
768 idlast = ctrl->id & ~V4L2_CTRL_FLAG_NEXT_CTRL;
769 for (i = 0; i < ARRAY_SIZE(sn9c20x_controls); i++) {
770 if ((sn9c20x_controls[i].id < idnew) &&
771 (sn9c20x_controls[i].id > idlast)) {
772 idnew = sn9c20x_controls[i].id;
773 min = i;
776 if (idnew != V4L2_CTRL_FLAG_NEXT_CTRL) {
777 UDIA_DEBUG("VIDIOC_QUERYCTRL found\n");
778 memcpy(ctrl, &sn9c20x_controls[min],
779 sizeof(struct v4l2_queryctrl));
780 return 0;
781 } else
782 return -EINVAL;
783 } else
784 #endif
786 for (i = 0; i < ARRAY_SIZE(sn9c20x_controls); i++) {
787 if (sn9c20x_controls[i].id == ctrl->id) {
788 UDIA_DEBUG("VIDIOC_QUERYCTRL found\n");
789 memcpy(ctrl, &sn9c20x_controls[i],
790 sizeof(struct v4l2_queryctrl));
791 break;
796 if (i >= ARRAY_SIZE(sn9c20x_controls))
797 return -EINVAL;
799 return 0;
803 * @param file
804 * @param priv
805 * @param ctrl
807 * @return 0 or negative error code
810 int sn9c20x_vidioc_g_ctrl(struct file *file, void *priv,
811 struct v4l2_control *ctrl)
813 struct usb_sn9c20x *dev;
815 dev = video_get_drvdata(priv);
817 UDIA_DEBUG("GET CTRL id=%d\n", ctrl->id);
819 switch (ctrl->id) {
820 case V4L2_CID_BRIGHTNESS:
821 ctrl->value = dev->vsettings.brightness;
822 break;
824 case V4L2_CID_EXPOSURE:
825 ctrl->value = dev->vsettings.exposure;
826 break;
828 case V4L2_CID_GAIN:
829 ctrl->value = dev->vsettings.gain;
830 break;
832 case V4L2_CID_GAMMA:
833 ctrl->value = dev->vsettings.gamma;
834 break;
836 case V4L2_CID_SATURATION:
837 ctrl->value = dev->vsettings.colour;
838 break;
840 case V4L2_CID_CONTRAST:
841 ctrl->value = dev->vsettings.contrast;
842 break;
844 case V4L2_CID_HFLIP:
845 ctrl->value = dev->vsettings.hflip;
846 break;
848 case V4L2_CID_VFLIP:
849 ctrl->value = dev->vsettings.vflip;
850 break;
852 case V4L2_CID_SHARPNESS:
853 ctrl->value = dev->vsettings.sharpness;
854 break;
856 case V4L2_CID_RED_BALANCE:
857 ctrl->value = dev->vsettings.red_gain;
858 break;
860 case V4L2_CID_BLUE_BALANCE:
861 ctrl->value = dev->vsettings.blue_gain;
862 break;
864 case V4L2_CID_EXPOSURE_AUTO:
865 ctrl->value = dev->vsettings.auto_exposure;
866 break;
868 case V4L2_CID_AUTOGAIN:
869 ctrl->value = dev->vsettings.auto_gain;
870 break;
872 case V4L2_CID_AUTO_WHITE_BALANCE:
873 ctrl->value = dev->vsettings.auto_whitebalance;
874 break;
876 default:
877 return -EINVAL;
879 return 0;
883 * @brief Apply v4l2 settings on camera
885 * @param file
886 * @param priv
887 * @param ctrl V4L2 control structure
889 * @returns 0 or negative error value
892 int sn9c20x_vidioc_s_ctrl(struct file *file, void *priv,
893 struct v4l2_control *ctrl)
895 struct usb_sn9c20x *dev;
897 dev = video_get_drvdata(priv);
899 UDIA_DEBUG("SET CTRL id=%d value=%d\n", ctrl->id, ctrl->value);
901 return sn9c20x_set_camera_control(dev,
902 ctrl->id,
903 ctrl->value);
907 * @param file
908 * @param priv
909 * @param fmt
911 * @return 0 or negative error code
914 int sn9c20x_vidioc_enum_fmt_cap(struct file *file, void *priv,
915 struct v4l2_fmtdesc *fmt)
917 struct usb_sn9c20x *dev;
919 dev = video_get_drvdata(priv);
921 UDIA_DEBUG("VIDIOC_ENUM_FMT %d\n", fmt->index);
923 if (fmt->index >= SN9C20X_N_FMTS)
924 return -EINVAL;
926 fmt->flags = 0;
927 fmt->pixelformat = sn9c20x_fmts[fmt->index].pix_fmt;
929 if (fmt->pixelformat == V4L2_PIX_FMT_JPEG && dev->jpeg == 0)
930 return -EINVAL;
932 memcpy(fmt->description, sn9c20x_fmts[fmt->index].desc, 32);
934 return 0;
938 * @param file
939 * @param priv
940 * @param fmt
942 * @return 0 or negative error code
945 int sn9c20x_vidioc_try_fmt_cap(struct file *file, void *priv,
946 struct v4l2_format *fmt)
948 int index;
949 struct usb_sn9c20x *dev;
951 dev = video_get_drvdata(priv);
952 UDIA_DEBUG("TRY FMT %d\n", fmt->type);
954 /* when this code is used prevents mplayer from setting outfmt
955 if(fmt->fmt.pix.field != V4L2_FIELD_NONE)
956 return -EINVAL;
958 if (fmt->fmt.pix.pixelformat == V4L2_PIX_FMT_JPEG && dev->jpeg == 0)
959 return -EINVAL;
961 for (index = 0; index < SN9C20X_N_FMTS; index++)
962 if (sn9c20x_fmts[index].pix_fmt == fmt->fmt.pix.pixelformat)
963 break;
965 if (index >= SN9C20X_N_FMTS)
966 return -EINVAL;
968 sn9c20x_get_closest_resolution(dev, &fmt->fmt.pix.width,
969 &fmt->fmt.pix.height);
971 fmt->fmt.pix.bytesperline = fmt->fmt.pix.width *
972 sn9c20x_fmts[index].depth / 8;
974 fmt->fmt.pix.sizeimage = fmt->fmt.pix.height *
975 fmt->fmt.pix.bytesperline;
977 fmt->fmt.pix.colorspace = V4L2_COLORSPACE_SRGB;
978 fmt->fmt.pix.priv = index;
980 return 0;
984 * @param file
985 * @param priv
986 * @param fmt
988 * @return 0
991 int sn9c20x_vidioc_g_fmt_cap(struct file *file, void *priv,
992 struct v4l2_format *fmt)
994 struct usb_sn9c20x *dev;
996 dev = video_get_drvdata(priv);
998 UDIA_DEBUG("GET FMT %d\n", fmt->type);
1000 memcpy(&(fmt->fmt.pix), &(dev->vsettings.format), sizeof(fmt->fmt.pix));
1003 return 0;
1007 * @param file
1008 * @param priv
1009 * @param fmt
1011 * @return 0 or negative error code
1014 int sn9c20x_vidioc_s_fmt_cap(struct file *file, void *priv,
1015 struct v4l2_format *fmt)
1017 struct usb_sn9c20x *dev;
1018 int ret;
1020 dev = video_get_drvdata(priv);
1022 UDIA_DEBUG("SET FMT %d : %d\n", fmt->type, fmt->fmt.pix.pixelformat);
1024 if (v4l_get_privileges(file) < 0)
1025 return -EBUSY;
1027 if (sn9c20x_queue_streaming(&dev->queue))
1028 return -EBUSY;
1030 ret = sn9c20x_vidioc_try_fmt_cap(file, priv, fmt);
1031 if (ret)
1032 return -EINVAL;
1034 sn9c20x_set_resolution(dev, fmt->fmt.pix.width, fmt->fmt.pix.height);
1035 sn9c20x_set_format(dev, fmt->fmt.pix.pixelformat);
1036 memcpy(&(dev->vsettings.format), &(fmt->fmt.pix), sizeof(fmt->fmt.pix));
1038 return 0;
1042 * @param file
1043 * @param priv
1044 * @param request
1046 * @return 0 or negative error code
1049 int sn9c20x_vidioc_reqbufs(struct file *file, void *priv,
1050 struct v4l2_requestbuffers *request)
1052 int ret = 0;
1053 struct usb_sn9c20x *dev;
1055 dev = video_get_drvdata(priv);
1057 if (v4l_get_privileges(file) < 0) {
1058 ret = -EBUSY;
1059 goto done;
1062 if (request->memory != V4L2_MEMORY_MMAP ||
1063 request->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) {
1064 ret = -EINVAL;
1065 goto done;
1068 if (sn9c20x_queue_streaming(&dev->queue)) {
1069 ret = -EBUSY;
1070 goto done;
1073 ret = sn9c20x_alloc_buffers(&dev->queue, request->count,
1074 dev->vsettings.format.sizeimage);
1075 if (ret < 0)
1076 goto done;
1078 request->count = ret;
1079 ret = 0;
1080 UDIA_DEBUG("Buffers Allocated %d\n", request->count);
1081 done:
1082 return ret;
1086 * @param file
1087 * @param priv
1088 * @param buffer
1090 * @return 0 or negative error code
1093 int sn9c20x_vidioc_querybuf(struct file *file, void *priv,
1094 struct v4l2_buffer *buffer)
1096 struct usb_sn9c20x *dev;
1098 dev = video_get_drvdata(priv);
1100 UDIA_DEBUG("QUERY BUFFERS %d %d\n", buffer->index, dev->queue.count);
1102 if (buffer->memory != V4L2_MEMORY_MMAP ||
1103 buffer->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
1104 return -EINVAL;
1106 if (!v4l_has_privileges(file))
1107 return -EBUSY;
1109 return sn9c20x_query_buffer(&dev->queue, buffer);
1113 * @param file
1114 * @param priv
1115 * @param buffer
1117 * @return 0 or negative error code
1120 int sn9c20x_vidioc_qbuf(struct file *file, void *priv,
1121 struct v4l2_buffer *buffer)
1123 struct usb_sn9c20x *dev;
1125 dev = video_get_drvdata(priv);
1127 UDIA_DEBUG("VIDIOC_QBUF\n");
1129 if (!v4l_has_privileges(file))
1130 return -EBUSY;
1132 return sn9c20x_queue_buffer(&dev->queue, buffer);
1136 * @param file
1137 * @param priv
1138 * @param buffer
1140 * @return 0 or negative error code
1143 int sn9c20x_vidioc_dqbuf(struct file *file, void *priv,
1144 struct v4l2_buffer *buffer)
1146 struct usb_sn9c20x *dev;
1147 int ret = 0;
1149 dev = video_get_drvdata(priv);
1151 UDIA_DEBUG("VIDIOC_DQBUF\n");
1153 if (!v4l_has_privileges(file))
1154 return -EBUSY;
1156 ret = sn9c20x_dequeue_buffer(&dev->queue, buffer,
1157 file->f_flags & O_NONBLOCK);
1158 if (ret < 0)
1159 return ret;
1161 if (dev->vsettings.format.pixelformat == V4L2_PIX_FMT_JPEG) {
1162 UDIA_DEBUG("Adding JPEG Header\n");
1163 v4l_add_jpegheader(dev, dev->queue.mem + buffer->m.offset,
1164 buffer->bytesused);
1165 buffer->bytesused += 589;
1168 dev_sn9c20x_call_constantly(dev);
1170 return ret;
1174 * @param file
1175 * @param priv
1176 * @param type
1178 * @return 0 or negative error code
1181 int sn9c20x_vidioc_streamon(struct file *file, void *priv,
1182 enum v4l2_buf_type type)
1184 struct usb_sn9c20x *dev;
1186 dev = video_get_drvdata(priv);
1188 UDIA_DEBUG("VIDIOC_STREAMON\n");
1190 if (!v4l_has_privileges(file))
1191 return -EBUSY;
1193 if (dev->mode != SN9C20X_MODE_IDLE)
1194 return -EBUSY;
1196 return v4l2_enable_video(dev, SN9C20X_MODE_STREAM);
1200 * @param file
1201 * @param priv
1202 * @param type
1204 * @return 0 or negative error code
1207 int sn9c20x_vidioc_streamoff(struct file *file, void *priv,
1208 enum v4l2_buf_type type)
1210 struct usb_sn9c20x *dev;
1212 dev = video_get_drvdata(priv);
1214 UDIA_DEBUG("VIDIOC_STREAMOFF\n");
1216 if (!v4l_has_privileges(file))
1217 return -EBUSY;
1219 return v4l2_enable_video(dev, SN9C20X_MODE_IDLE);
1223 * @param file
1224 * @param priv
1225 * @param param
1227 * @return 0 or negative error code
1230 int sn9c20x_vidioc_g_param(struct file *file, void *priv,
1231 struct v4l2_streamparm *param)
1233 struct usb_sn9c20x *dev;
1236 dev = video_get_drvdata(priv);
1238 if (param->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
1239 return -EINVAL;
1241 param->parm.capture.capability = 0;
1242 param->parm.capture.capturemode = 0;
1243 param->parm.capture.timeperframe.numerator = 1;
1244 param->parm.capture.timeperframe.denominator = 30;
1245 param->parm.capture.readbuffers = 2;
1246 param->parm.capture.extendedmode = 0;
1248 return 0;
1252 * @param file
1253 * @param priv
1254 * @param param
1256 * @return 0 or negative error code
1259 int sn9c20x_vidioc_s_param(struct file *file, void *priv,
1260 struct v4l2_streamparm *param)
1262 struct usb_sn9c20x *dev;
1264 dev = video_get_drvdata(priv);
1266 if (v4l_get_privileges(file))
1267 return -EBUSY;
1269 if (param->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
1270 return -EINVAL;
1272 return 0;
1276 * @param inode Inode pointer
1277 * @param fp File pointer
1278 * @param cmd Command
1279 * @param arg Arguements of the command
1281 * @returns 0 if all is OK
1283 * @brief Manage IOCTL
1285 * This function permits to manage all the IOCTL from the application.
1287 static int v4l_sn9c20x_ioctl(struct inode *inode, struct file *fp,
1288 unsigned int cmd, unsigned long arg)
1290 int err;
1291 struct usb_sn9c20x *dev;
1292 struct video_device *vdev;
1294 vdev = video_devdata(fp);
1295 dev = video_get_drvdata(video_devdata(fp));
1297 UDIA_DEBUG("v4l_sn9c20x_ioctl %02X\n", (unsigned char) cmd);
1299 if (dev == NULL || vdev == NULL)
1300 return -EFAULT;
1302 err = video_ioctl2(inode, fp, cmd, arg);
1304 return err;
1308 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27)
1309 static const struct v4l2_ioctl_ops sn9c20x_v4l2_ioctl_ops = {
1310 .vidioc_querycap = sn9c20x_vidioc_querycap,
1311 .vidioc_enum_fmt_vid_cap = sn9c20x_vidioc_enum_fmt_cap,
1312 .vidioc_try_fmt_vid_cap = sn9c20x_vidioc_try_fmt_cap,
1313 .vidioc_s_fmt_vid_cap = sn9c20x_vidioc_s_fmt_cap,
1314 .vidioc_g_fmt_vid_cap = sn9c20x_vidioc_g_fmt_cap,
1315 .vidioc_enum_input = sn9c20x_vidioc_enum_input,
1316 .vidioc_g_input = sn9c20x_vidioc_g_input,
1317 .vidioc_s_input = sn9c20x_vidioc_s_input,
1318 .vidioc_streamon = sn9c20x_vidioc_streamon,
1319 .vidioc_streamoff = sn9c20x_vidioc_streamoff,
1320 .vidioc_queryctrl = sn9c20x_vidioc_queryctrl,
1321 .vidioc_g_ctrl = sn9c20x_vidioc_g_ctrl,
1322 .vidioc_s_ctrl = sn9c20x_vidioc_s_ctrl,
1323 .vidioc_g_parm = sn9c20x_vidioc_g_param,
1324 .vidioc_s_parm = sn9c20x_vidioc_s_param,
1325 .vidioc_reqbufs = sn9c20x_vidioc_reqbufs,
1326 .vidioc_qbuf = sn9c20x_vidioc_qbuf,
1327 .vidioc_dqbuf = sn9c20x_vidioc_dqbuf,
1328 .vidioc_querybuf = sn9c20x_vidioc_querybuf,
1330 #endif
1333 * @param dev Device structure
1335 * @returns 0 if all is OK
1337 * @brief Register the video device
1339 * This function permits to register the USB device to the video device.
1341 int v4l_sn9c20x_register_video_device(struct usb_sn9c20x *dev)
1343 int err;
1345 strcpy(dev->vdev->name, DRIVER_DESC);
1347 #if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 27)
1348 dev->vdev->dev = &dev->interface->dev;
1349 dev->vdev->owner = THIS_MODULE;
1350 dev->vdev->type = VID_TYPE_CAPTURE;
1351 #else
1352 dev->vdev->parent = &dev->interface->dev;
1353 #endif
1354 dev->vdev->current_norm = 0;
1355 dev->vdev->tvnorms = 0;
1356 dev->vdev->fops = &v4l_sn9c20x_fops;
1357 dev->vdev->release = video_device_release;
1358 dev->vdev->minor = -1;
1360 if (log_level & SN9C20X_DEBUG)
1361 dev->vdev->debug = V4L2_DEBUG_IOCTL_ARG;
1363 #if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 27)
1364 dev->vdev->vidioc_querycap = sn9c20x_vidioc_querycap;
1365 dev->vdev->vidioc_enum_fmt_cap = sn9c20x_vidioc_enum_fmt_cap;
1366 dev->vdev->vidioc_try_fmt_cap = sn9c20x_vidioc_try_fmt_cap;
1367 dev->vdev->vidioc_s_fmt_cap = sn9c20x_vidioc_s_fmt_cap;
1368 dev->vdev->vidioc_g_fmt_cap = sn9c20x_vidioc_g_fmt_cap;
1369 dev->vdev->vidioc_enum_input = sn9c20x_vidioc_enum_input;
1370 dev->vdev->vidioc_g_input = sn9c20x_vidioc_g_input;
1371 dev->vdev->vidioc_s_input = sn9c20x_vidioc_s_input;
1372 dev->vdev->vidioc_streamon = sn9c20x_vidioc_streamon;
1373 dev->vdev->vidioc_streamoff = sn9c20x_vidioc_streamoff;
1374 dev->vdev->vidioc_queryctrl = sn9c20x_vidioc_queryctrl;
1375 dev->vdev->vidioc_g_ctrl = sn9c20x_vidioc_g_ctrl;
1376 dev->vdev->vidioc_s_ctrl = sn9c20x_vidioc_s_ctrl;
1377 dev->vdev->vidioc_g_parm = sn9c20x_vidioc_g_param;
1378 dev->vdev->vidioc_s_parm = sn9c20x_vidioc_s_param;
1379 dev->vdev->vidioc_reqbufs = sn9c20x_vidioc_reqbufs;
1380 dev->vdev->vidioc_qbuf = sn9c20x_vidioc_qbuf;
1381 dev->vdev->vidioc_dqbuf = sn9c20x_vidioc_dqbuf;
1382 dev->vdev->vidioc_querybuf = sn9c20x_vidioc_querybuf;
1383 #else
1384 dev->vdev->ioctl_ops = &sn9c20x_v4l2_ioctl_ops;
1385 #endif
1387 video_set_drvdata(dev->vdev, dev);
1389 sn9c20x_queue_init(&dev->queue);
1391 err = video_register_device(dev->vdev, VFL_TYPE_GRABBER, -1);
1393 if (err)
1394 UDIA_ERROR("Video register fail !\n");
1395 else
1396 UDIA_INFO("Webcam device %04X:%04X is now controlling video "
1397 "device /dev/video%d\n",
1398 le16_to_cpu(dev->udev->descriptor.idVendor),
1399 le16_to_cpu(dev->udev->descriptor.idProduct),
1400 dev->vdev->minor);
1402 return err;
1407 * @param dev Device structure
1409 * @returns 0 if all is OK
1411 * @brief Unregister the video device
1413 * This function permits to unregister the video device.
1415 int v4l_sn9c20x_unregister_video_device(struct usb_sn9c20x *dev)
1417 UDIA_INFO("SN9C20X USB 2.0 Webcam releases control of video "
1418 "device /dev/video%d\n", dev->vdev->minor);
1420 video_set_drvdata(dev->vdev, NULL);
1421 video_unregister_device(dev->vdev);
1423 return 0;
1428 * @var v4l_sn9c20x_fops
1430 * This variable contains some callback
1432 static struct file_operations v4l_sn9c20x_fops = {
1433 .owner = THIS_MODULE,
1434 .open = v4l_sn9c20x_open,
1435 .release = v4l_sn9c20x_release,
1436 .read = v4l_sn9c20x_read,
1437 .poll = v4l_sn9c20x_poll,
1438 .mmap = v4l_sn9c20x_mmap,
1439 .ioctl = v4l_sn9c20x_ioctl,
1440 #ifdef CONFIG_COMPAT
1441 .compat_ioctl = v4l_compat_ioctl32,
1442 #endif
1443 .llseek = no_llseek