3 * @author Nicolas VIVIEN
6 * @brief V4L2 interface and functions
8 * @note Copyright (C) Nicolas VIVIEN
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
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>
39 #include "sn9c20x-bridge.h"
41 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27)
42 #include <media/v4l2-ioctl.h>
45 #if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 29)
46 static struct file_operations v4l_sn9c20x_fops
;
48 static struct v4l2_file_operations v4l_sn9c20x_fops
;
51 * @var sn9c20x_controls
52 * List of all V4Lv2 controls supported by the driver
54 static struct v4l2_queryctrl sn9c20x_controls
[] = {
56 .id
= V4L2_CID_BRIGHTNESS
,
57 .type
= V4L2_CTRL_TYPE_INTEGER
,
65 .type
= V4L2_CTRL_TYPE_INTEGER
,
73 .id
= V4L2_CID_SATURATION
,
74 .type
= V4L2_CTRL_TYPE_INTEGER
,
83 .type
= V4L2_CTRL_TYPE_INTEGER
,
91 .id
= V4L2_CID_CONTRAST
,
92 .type
= V4L2_CTRL_TYPE_INTEGER
,
99 .id
= V4L2_CID_EXPOSURE
,
100 .type
= V4L2_CTRL_TYPE_INTEGER
,
108 .type
= V4L2_CTRL_TYPE_INTEGER
,
115 .id
= V4L2_CID_HFLIP
,
116 .type
= V4L2_CTRL_TYPE_BOOLEAN
,
117 .name
= "Horizontal flip",
123 .id
= V4L2_CID_VFLIP
,
124 .type
= V4L2_CTRL_TYPE_BOOLEAN
,
125 .name
= "Vertical flip",
131 .id
= V4L2_CID_SHARPNESS
,
132 .type
= V4L2_CTRL_TYPE_INTEGER
,
139 .id
= V4L2_CID_RED_BALANCE
,
140 .type
= V4L2_CTRL_TYPE_INTEGER
,
141 .name
= "Red Balance",
147 .id
= V4L2_CID_BLUE_BALANCE
,
148 .type
= V4L2_CTRL_TYPE_INTEGER
,
149 .name
= "Blue Balance",
154 /* According to v4l2 specs auto exposure should be a 4 step value.
155 * This make little since for webcams however so a boolean is used
159 .id
= V4L2_CID_EXPOSURE_AUTO
,
160 .type
= V4L2_CTRL_TYPE_BOOLEAN
,
161 .name
= "Automatic exposure control",
167 .id
= V4L2_CID_AUTOGAIN
,
168 .type
= V4L2_CTRL_TYPE_BOOLEAN
,
169 .name
= "Automatic gain control",
175 .id
= V4L2_CID_AUTO_WHITE_BALANCE
,
176 .type
= V4L2_CTRL_TYPE_BOOLEAN
,
177 .name
= "Automatic whitbalance control",
184 void v4l2_set_control_default(struct usb_sn9c20x
*dev
, __u32 ctrl
, __u16 value
)
187 for (i
= 0; i
< ARRAY_SIZE(sn9c20x_controls
); i
++) {
188 if (sn9c20x_controls
[i
].id
== ctrl
) {
189 sn9c20x_controls
[i
].default_value
= value
;
190 sn9c20x_set_camera_control(dev
,
198 void v4l_add_jpegheader(struct usb_sn9c20x
*dev
, __u8
*buffer
,
201 static __u8 jpeg_header
[589] = {
202 0xff, 0xd8, 0xff, 0xdb, 0x00, 0x84, 0x00, 0x06, 0x04, 0x05,
203 0x06, 0x05, 0x04, 0x06, 0x06, 0x05, 0x06, 0x07, 0x07, 0x06,
204 0x08, 0x0a, 0x10, 0x0a, 0x0a, 0x09, 0x09, 0x0a, 0x14, 0x0e,
205 0x0f, 0x0c, 0x10, 0x17, 0x14, 0x18, 0x18, 0x17, 0x14, 0x16,
206 0x16, 0x1a, 0x1d, 0x25, 0x1f, 0x1a, 0x1b, 0x23, 0x1c, 0x16,
207 0x16, 0x20, 0x2c, 0x20, 0x23, 0x26, 0x27, 0x29, 0x2a, 0x29,
208 0x19, 0x1f, 0x2d, 0x30, 0x2d, 0x28, 0x30, 0x25, 0x28, 0x29,
209 0x28, 0x01, 0x07, 0x07, 0x07, 0x0a, 0x08, 0x0a, 0x13, 0x0a,
210 0x0a, 0x13, 0x28, 0x1a, 0x16, 0x1a, 0x28, 0x28, 0x28, 0x28,
211 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28,
212 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28,
213 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28,
214 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28,
215 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0xff, 0xc4, 0x01, 0xa2,
216 0x00, 0x00, 0x01, 0x05, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
217 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x02,
218 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x01,
219 0x00, 0x03, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
220 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x03,
221 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x10, 0x00,
222 0x02, 0x01, 0x03, 0x03, 0x02, 0x04, 0x03, 0x05, 0x05, 0x04,
223 0x04, 0x00, 0x00, 0x01, 0x7d, 0x01, 0x02, 0x03, 0x00, 0x04,
224 0x11, 0x05, 0x12, 0x21, 0x31, 0x41, 0x06, 0x13, 0x51, 0x61,
225 0x07, 0x22, 0x71, 0x14, 0x32, 0x81, 0x91, 0xa1, 0x08, 0x23,
226 0x42, 0xb1, 0xc1, 0x15, 0x52, 0xd1, 0xf0, 0x24, 0x33, 0x62,
227 0x72, 0x82, 0x09, 0x0a, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x25,
228 0x26, 0x27, 0x28, 0x29, 0x2a, 0x34, 0x35, 0x36, 0x37, 0x38,
229 0x39, 0x3a, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a,
230 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5a, 0x63, 0x64,
231 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x73, 0x74, 0x75, 0x76,
232 0x77, 0x78, 0x79, 0x7a, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88,
233 0x89, 0x8a, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99,
234 0x9a, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, 0xa8, 0xa9, 0xaa,
235 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, 0xb8, 0xb9, 0xba, 0xc2,
236 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xd2, 0xd3,
237 0xd4, 0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda, 0xe1, 0xe2, 0xe3,
238 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, 0xea, 0xf1, 0xf2, 0xf3,
239 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9, 0xfa, 0x11, 0x00, 0x02,
240 0x01, 0x02, 0x04, 0x04, 0x03, 0x04, 0x07, 0x05, 0x04, 0x04,
241 0x00, 0x01, 0x02, 0x77, 0x00, 0x01, 0x02, 0x03, 0x11, 0x04,
242 0x05, 0x21, 0x31, 0x06, 0x12, 0x41, 0x51, 0x07, 0x61, 0x71,
243 0x13, 0x22, 0x32, 0x81, 0x08, 0x14, 0x42, 0x91, 0xa1, 0xb1,
244 0xc1, 0x09, 0x23, 0x33, 0x52, 0xf0, 0x15, 0x62, 0x72, 0xd1,
245 0x0a, 0x16, 0x24, 0x34, 0xe1, 0x25, 0xf1, 0x17, 0x18, 0x19,
246 0x1a, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x35, 0x36, 0x37, 0x38,
247 0x39, 0x3a, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a,
248 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5a, 0x63, 0x64,
249 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x73, 0x74, 0x75, 0x76,
250 0x77, 0x78, 0x79, 0x7a, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87,
251 0x88, 0x89, 0x8a, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98,
252 0x99, 0x9a, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, 0xa8, 0xa9,
253 0xaa, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, 0xb8, 0xb9, 0xba,
254 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xd2,
255 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda, 0xe2, 0xe3,
256 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, 0xea, 0xf2, 0xf3, 0xf4,
257 0xf5, 0xf6, 0xf7, 0xf8, 0xf9, 0xfa, 0xff, 0xc0, 0x00, 0x11,
258 0x08, 0x01, 0xe0, 0x02, 0x80, 0x03, 0x01, 0x21, 0x00, 0x02,
259 0x11, 0x01, 0x03, 0x11, 0x01, 0xff, 0xda, 0x00, 0x0c, 0x03,
260 0x01, 0x00, 0x02, 0x11, 0x03, 0x11, 0x00, 0x3f, 0x00
262 static __u8 qtable1
[128] = {
263 0x0d, 0x08, 0x08, 0x0d, 0x08, 0x08, 0x0d, 0x0d,
264 0x0d, 0x0d, 0x11, 0x0d, 0x0d, 0x11, 0x15, 0x21,
265 0x15, 0x15, 0x11, 0x11, 0x15, 0x2a, 0x1d, 0x1d,
266 0x19, 0x21, 0x32, 0x2a, 0x32, 0x32, 0x2e, 0x2a,
267 0x2e, 0x2e, 0x36, 0x3a, 0x4b, 0x43, 0x36, 0x3a,
268 0x47, 0x3a, 0x2e, 0x2e, 0x43, 0x5c, 0x43, 0x47,
269 0x4f, 0x54, 0x58, 0x58, 0x58, 0x32, 0x3f, 0x60,
270 0x64, 0x5c, 0x54, 0x64, 0x4b, 0x54, 0x58, 0x54,
271 0x0d, 0x11, 0x11, 0x15, 0x11, 0x15, 0x26, 0x15,
272 0x15, 0x26, 0x54, 0x36, 0x2e, 0x36, 0x54, 0x54,
273 0x54, 0x54, 0x54, 0x54, 0x54, 0x54, 0x54, 0x54,
274 0x54, 0x54, 0x54, 0x54, 0x54, 0x54, 0x54, 0x54,
275 0x54, 0x54, 0x54, 0x54, 0x54, 0x54, 0x54, 0x54,
276 0x54, 0x54, 0x54, 0x54, 0x54, 0x54, 0x54, 0x54,
277 0x54, 0x54, 0x54, 0x54, 0x54, 0x54, 0x54, 0x54,
278 0x54, 0x54, 0x54, 0x54, 0x54, 0x54, 0x54, 0x54
281 jpeg_header
[6] = 0x00;
282 jpeg_header
[71] = 0x01;
283 memcpy(jpeg_header
+ 7, qtable1
, 64);
284 memcpy(jpeg_header
+ 8 + 64, qtable1
+64, 64);
285 jpeg_header
[564] = dev
->vsettings
.format
.width
& 0xFF;
286 jpeg_header
[563] = (dev
->vsettings
.format
.width
>> 8) & 0xFF;
287 jpeg_header
[562] = dev
->vsettings
.format
.height
& 0xFF;
288 jpeg_header
[561] = (dev
->vsettings
.format
.height
>> 8) & 0xFF;
289 jpeg_header
[567] = 0x21;
291 memmove(buffer
+589, buffer
, buffer_size
);
292 memcpy(buffer
, jpeg_header
, 589);
295 * @brief Get V4L privileges
299 * @return 0 or negative error code
302 int v4l_get_privileges(struct file
*file
)
304 struct usb_sn9c20x
*dev
;
307 dev
= video_get_drvdata(video_devdata(file
));
309 if (dev
->owner
== file
)
312 mutex_lock(&open_lock
);
313 if (dev
->owner
!= NULL
) {
319 mutex_unlock(&open_lock
);
324 * @brief Check whether there are V4L privileges
331 int v4l_has_privileges(struct file
*file
)
333 struct usb_sn9c20x
*dev
;
336 dev
= video_get_drvdata(video_devdata(file
));
338 if (dev
->owner
== file
)
345 * @brief Drop V4L privileges
350 void v4l_drop_privileges(struct file
*file
)
352 struct usb_sn9c20x
*dev
;
354 dev
= video_get_drvdata(video_devdata(file
));
356 if (dev
->owner
== file
)
361 * @brief Enable video stream
363 * @param dev Pointer to device structure
364 * @param mode Mode for video stream
366 * @returns 0 or negative error value
369 int v4l2_enable_video(struct usb_sn9c20x
*dev
, int mode
)
373 if (mode
== SN9C20X_MODE_IDLE
) {
374 sn9c20x_enable_video(dev
, 0);
375 usb_sn9c20x_uninit_urbs(dev
, 1);
376 sn9c20x_queue_enable(&dev
->queue
, 0);
381 if (dev
->mode
!= SN9C20X_MODE_IDLE
)
384 if (sn9c20x_queue_enable(&dev
->queue
, 1) < 0)
387 ret
= usb_sn9c20x_init_urbs(dev
);
392 sn9c20x_enable_video(dev
, 1);
395 if (dev
->vsettings
.format
.pixelformat
== V4L2_PIX_FMT_JPEG
)
396 dev
->queue
.flags
&= ~SN9C20X_QUEUE_DROP_INCOMPLETE
;
398 dev
->queue
.flags
|= SN9C20X_QUEUE_DROP_INCOMPLETE
;
404 * @param inode Pointer on an inode
405 * @param fp File pointer
407 * @returns 0 if all is OK
409 * @brief Open the video device
411 * This function permits to open a video device (/dev/videoX)
413 #if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 29)
414 static int v4l_sn9c20x_open(struct inode
*inode
, struct file
*fp
)
416 static int v4l_sn9c20x_open(struct file
*fp
)
421 struct usb_sn9c20x
*dev
;
422 struct video_device
*vdev
;
424 mutex_lock(&open_lock
);
426 vdev
= video_devdata(fp
);
427 dev
= video_get_drvdata(video_devdata(fp
));
429 fp
->private_data
= vdev
;
431 kref_get(&dev
->vopen
);
433 mutex_unlock(&open_lock
);
439 * @param inode Pointer on inode
440 * @param fp File pointer
442 * @returns 0 if all is OK
444 * @brief Release an opened file.
446 * This function permits to release an opened file with the 'open' method.
448 #if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 29)
449 static int v4l_sn9c20x_release(struct inode
*inode
, struct file
*fp
)
451 static int v4l_sn9c20x_release(struct file
*fp
)
454 struct usb_sn9c20x
*dev
;
455 struct video_device
*vdev
;
457 mutex_lock(&open_lock
);
459 vdev
= video_devdata(fp
);
460 dev
= video_get_drvdata(video_devdata(fp
));
462 if (v4l_has_privileges(fp
)) {
463 v4l2_enable_video(dev
, SN9C20X_MODE_IDLE
);
465 mutex_lock(&dev
->queue
.mutex
);
466 sn9c20x_free_buffers(&dev
->queue
);
467 mutex_unlock(&dev
->queue
.mutex
);
470 v4l_drop_privileges(fp
);
472 kref_put(&dev
->vopen
, usb_sn9c20x_delete
);
474 mutex_unlock(&open_lock
);
480 * @param fp File pointer
482 * @retval buf Buffer in user space
486 * @returns Count value
488 * @brief Read the video device
490 * This function is called by the application is reading the video device.
492 static ssize_t
v4l_sn9c20x_read(struct file
*fp
, char __user
*buf
,
493 size_t count
, loff_t
*f_pos
)
497 struct v4l2_buffer buffer
;
498 struct usb_sn9c20x
*dev
;
500 dev
= video_get_drvdata(video_devdata(fp
));
502 ret
= v4l_get_privileges(fp
);
503 if (unlikely(ret
< 0))
506 if (unlikely(dev
->mode
!= SN9C20X_MODE_IDLE
&&
507 dev
->mode
!= SN9C20X_MODE_READ
))
510 buffer
.type
= V4L2_BUF_TYPE_VIDEO_CAPTURE
;
511 buffer
.memory
= V4L2_MEMORY_MMAP
;
512 if (dev
->mode
== SN9C20X_MODE_IDLE
) {
513 nbuffers
= sn9c20x_alloc_buffers(&dev
->queue
, 2,
514 dev
->vsettings
.format
.sizeimage
);
518 for (i
= 0; i
< nbuffers
; i
++) {
519 buffer
= dev
->queue
.buffer
[i
].buf
;
520 sn9c20x_queue_buffer(&dev
->queue
, &buffer
);
523 ret
= v4l2_enable_video(dev
, SN9C20X_MODE_READ
);
528 dev_sn9c20x_call_constantly(dev
);
530 if (dev
->queue
.read_buffer
== NULL
) {
531 ret
= sn9c20x_dequeue_buffer(&dev
->queue
, &buffer
,
532 fp
->f_flags
& O_NONBLOCK
);
536 if (dev
->vsettings
.format
.pixelformat
== V4L2_PIX_FMT_JPEG
) {
537 UDIA_DEBUG("Adding JPEG Header\n");
538 v4l_add_jpegheader(dev
, dev
->queue
.mem
+ buffer
.m
.offset
,
540 buffer
.bytesused
+= 589;
543 dev
->queue
.read_buffer
= &dev
->queue
.buffer
[buffer
.index
];
545 buffer
= dev
->queue
.read_buffer
->buf
;
548 count
= min((size_t)(buffer
.bytesused
- *f_pos
), count
);
549 if (copy_to_user(buf
, dev
->queue
.mem
+ buffer
.m
.offset
+ *f_pos
, count
))
553 if (*f_pos
>= buffer
.bytesused
) {
554 dev
->queue
.read_buffer
= NULL
;
555 sn9c20x_queue_buffer(&dev
->queue
, &buffer
);
563 * @param fp File pointer
566 * @returns 0 if all is OK
568 * @brief Polling function
570 static unsigned int v4l_sn9c20x_poll(struct file
*fp
, poll_table
*wait
)
572 struct usb_sn9c20x
*dev
;
573 struct video_device
*vdev
;
575 vdev
= video_devdata(fp
);
576 dev
= video_get_drvdata(video_devdata(fp
));
578 UDIA_STREAM("Poll\n");
580 if (vdev
== NULL
|| dev
== NULL
)
583 return sn9c20x_queue_poll(&dev
->queue
, fp
, wait
);
590 static void sn9c20x_vm_open(struct vm_area_struct
*vma
)
592 struct sn9c20x_buffer
*buffer
= vma
->vm_private_data
;
593 buffer
->vma_use_count
++;
601 static void sn9c20x_vm_close(struct vm_area_struct
*vma
)
603 struct sn9c20x_buffer
*buffer
= vma
->vm_private_data
;
604 buffer
->vma_use_count
--;
607 struct vm_operations_struct sn9c20x_vm_ops
= {
608 .open
= sn9c20x_vm_open
,
609 .close
= sn9c20x_vm_close
613 * @param fp File pointer
614 * @param vma VMA structure
616 * @returns 0 if all is OK
620 * This function permits to map a memory space.
622 static int v4l_sn9c20x_mmap(struct file
*fp
, struct vm_area_struct
*vma
)
625 unsigned long addr
, start
, size
;
629 struct usb_sn9c20x
*dev
;
630 struct video_device
*vdev
;
631 struct sn9c20x_buffer
*buffer
= NULL
;
633 vdev
= video_devdata(fp
);
634 dev
= video_get_drvdata(video_devdata(fp
));
636 UDIA_STREAM("mmap\n");
638 start
= vma
->vm_start
;
639 size
= vma
->vm_end
- vma
->vm_start
;
641 mutex_lock(&dev
->queue
.mutex
);
643 for (i
= 0; i
< dev
->queue
.count
; ++i
) {
644 buffer
= &dev
->queue
.buffer
[i
];
645 if ((buffer
->buf
.m
.offset
>> PAGE_SHIFT
) == vma
->vm_pgoff
)
649 if (i
== dev
->queue
.count
|| size
!= dev
->queue
.buf_size
) {
654 vma
->vm_flags
|= VM_IO
;
656 addr
= (unsigned long)dev
->queue
.mem
+ buffer
->buf
.m
.offset
;
658 page
= vmalloc_to_page((void *)addr
);
659 ret
= vm_insert_page(vma
, start
, page
);
668 vma
->vm_ops
= &sn9c20x_vm_ops
;
669 vma
->vm_private_data
= buffer
;
670 sn9c20x_vm_open(vma
);
672 mutex_unlock(&dev
->queue
.mutex
);
684 int sn9c20x_vidioc_querycap(struct file
*file
, void *priv
,
685 struct v4l2_capability
*cap
)
687 struct usb_sn9c20x
*dev
;
689 dev
= video_get_drvdata(priv
);
691 UDIA_DEBUG("VIDIOC_QUERYCAP\n");
693 strlcpy(cap
->driver
, "sn9c20x", sizeof(cap
->driver
));
694 cap
->capabilities
= V4L2_CAP_VIDEO_CAPTURE
| V4L2_CAP_STREAMING
695 | V4L2_CAP_READWRITE
;
696 cap
->version
= (__u32
) DRIVER_VERSION_NUM
,
697 strlcpy(cap
->card
, dev
->vdev
->name
, sizeof(cap
->card
));
699 if (usb_make_path(dev
->udev
, cap
->bus_info
, sizeof(cap
->bus_info
)) < 0)
700 strlcpy(cap
->bus_info
, dev
->vdev
->name
, sizeof(cap
->bus_info
));
712 int sn9c20x_vidioc_enum_framesizes(struct file
*file
, void *priv
,
713 struct v4l2_frmsizeenum
*size
)
715 struct usb_sn9c20x
*dev
;
717 dev
= video_get_drvdata(priv
);
719 UDIA_DEBUG("ENUM_FRAMESIZES\n");
721 if (size
->index
>= SN9C20X_N_MODES
)
724 size
->type
= V4L2_FRMSIZE_TYPE_DISCRETE
;
725 size
->discrete
.width
= sn9c20x_modes
[size
->index
].width
;
726 size
->discrete
.height
= sn9c20x_modes
[size
->index
].height
;
728 if (dev
->camera
.set_sxga_mode
== NULL
&&
729 (size
->discrete
.width
> 640 && size
->discrete
.height
> 480))
732 if (size
->pixel_format
!= V4L2_PIX_FMT_SBGGR8
&&
733 (size
->discrete
.width
> 640 && size
->discrete
.height
> 480))
736 UDIA_DEBUG("Framesize: %dx%d, FMT: %X\n", size
->discrete
.width
,
737 size
->discrete
.height
,
748 * @return 0 or negative error code
751 int sn9c20x_vidioc_enum_input(struct file
*file
, void *priv
,
752 struct v4l2_input
*input
)
754 UDIA_DEBUG("VIDIOC_ENUMINPUT %d\n", input
->index
);
759 strlcpy(input
->name
, "Webcam", sizeof(input
->name
));
760 input
->type
= V4L2_INPUT_TYPE_CAMERA
;
771 * @return 0 or negative error code
774 int sn9c20x_vidioc_g_input(struct file
*file
, void *priv
, unsigned int *index
)
776 UDIA_DEBUG("GET INPUT %d\n", *index
);
789 * @return 0 or negative error code
792 int sn9c20x_vidioc_s_input(struct file
*file
, void *priv
, unsigned int index
)
794 UDIA_DEBUG("SET INPUT %d\n", index
);
796 if (v4l_get_privileges(file
) < 0)
810 * @return 0 or negative error code
813 int sn9c20x_vidioc_queryctrl(struct file
*file
, void *priv
,
814 struct v4l2_queryctrl
*ctrl
)
818 #ifdef V4L2_CTRL_FLAG_NEXT_CTRL
822 struct usb_sn9c20x
*dev
;
824 dev
= video_get_drvdata(priv
);
826 UDIA_DEBUG("VIDIOC_QUERYCTRL id = %d\n", ctrl
->id
);
828 #ifdef V4L2_CTRL_FLAG_NEXT_CTRL
829 if (ctrl
->id
& V4L2_CTRL_FLAG_NEXT_CTRL
) {
831 idnew
= V4L2_CTRL_FLAG_NEXT_CTRL
;
832 idlast
= ctrl
->id
& ~V4L2_CTRL_FLAG_NEXT_CTRL
;
833 for (i
= 0; i
< ARRAY_SIZE(sn9c20x_controls
); i
++) {
834 if ((sn9c20x_controls
[i
].id
< idnew
) &&
835 (sn9c20x_controls
[i
].id
> idlast
)) {
836 idnew
= sn9c20x_controls
[i
].id
;
840 if (idnew
!= V4L2_CTRL_FLAG_NEXT_CTRL
) {
841 UDIA_DEBUG("VIDIOC_QUERYCTRL found\n");
842 memcpy(ctrl
, &sn9c20x_controls
[min
],
843 sizeof(struct v4l2_queryctrl
));
851 for (i
= 0; i
< ARRAY_SIZE(sn9c20x_controls
); i
++) {
852 if (sn9c20x_controls
[i
].id
== ctrl
->id
) {
853 UDIA_DEBUG("VIDIOC_QUERYCTRL found\n");
854 memcpy(ctrl
, &sn9c20x_controls
[i
],
855 sizeof(struct v4l2_queryctrl
));
864 if ((ctrl
->id
== V4L2_CID_GAIN
&& dev
->vsettings
.auto_gain
) ||
865 (ctrl
->id
== V4L2_CID_EXPOSURE
&& dev
->vsettings
.auto_exposure
) ||
866 ((ctrl
->id
== V4L2_CID_BLUE_BALANCE
||
867 ctrl
->id
== V4L2_CID_RED_BALANCE
) &&
868 dev
->vsettings
.auto_whitebalance
)) {
869 ctrl
->flags
|= V4L2_CTRL_FLAG_GRABBED
;
879 * @return 0 or negative error code
882 int sn9c20x_vidioc_g_ctrl(struct file
*file
, void *priv
,
883 struct v4l2_control
*ctrl
)
885 struct usb_sn9c20x
*dev
;
887 dev
= video_get_drvdata(priv
);
889 UDIA_DEBUG("GET CTRL id=%d\n", ctrl
->id
);
892 case V4L2_CID_BRIGHTNESS
:
893 ctrl
->value
= dev
->vsettings
.brightness
;
896 case V4L2_CID_EXPOSURE
:
897 ctrl
->value
= dev
->vsettings
.exposure
;
901 ctrl
->value
= dev
->vsettings
.gain
;
905 ctrl
->value
= dev
->vsettings
.gamma
;
908 case V4L2_CID_SATURATION
:
909 ctrl
->value
= dev
->vsettings
.colour
;
913 ctrl
->value
= dev
->vsettings
.hue
;
916 case V4L2_CID_CONTRAST
:
917 ctrl
->value
= dev
->vsettings
.contrast
;
921 ctrl
->value
= dev
->vsettings
.hflip
;
925 ctrl
->value
= dev
->vsettings
.vflip
;
928 case V4L2_CID_SHARPNESS
:
929 ctrl
->value
= dev
->vsettings
.sharpness
;
932 case V4L2_CID_RED_BALANCE
:
933 ctrl
->value
= dev
->vsettings
.red_gain
;
936 case V4L2_CID_BLUE_BALANCE
:
937 ctrl
->value
= dev
->vsettings
.blue_gain
;
940 case V4L2_CID_EXPOSURE_AUTO
:
941 ctrl
->value
= dev
->vsettings
.auto_exposure
;
944 case V4L2_CID_AUTOGAIN
:
945 ctrl
->value
= dev
->vsettings
.auto_gain
;
948 case V4L2_CID_AUTO_WHITE_BALANCE
:
949 ctrl
->value
= dev
->vsettings
.auto_whitebalance
;
959 * @brief Apply v4l2 settings on camera
963 * @param ctrl V4L2 control structure
965 * @returns 0 or negative error value
968 int sn9c20x_vidioc_s_ctrl(struct file
*file
, void *priv
,
969 struct v4l2_control
*ctrl
)
971 struct usb_sn9c20x
*dev
;
973 dev
= video_get_drvdata(priv
);
975 UDIA_DEBUG("SET CTRL id=%d value=%d\n", ctrl
->id
, ctrl
->value
);
977 if ((ctrl
->id
== V4L2_CID_GAIN
&& dev
->vsettings
.auto_gain
) ||
978 (ctrl
->id
== V4L2_CID_EXPOSURE
&& dev
->vsettings
.auto_exposure
)) {
982 return sn9c20x_set_camera_control(dev
,
992 * @return 0 or negative error code
995 int sn9c20x_vidioc_enum_fmt_cap(struct file
*file
, void *priv
,
996 struct v4l2_fmtdesc
*fmt
)
998 struct usb_sn9c20x
*dev
;
1000 dev
= video_get_drvdata(priv
);
1002 UDIA_DEBUG("VIDIOC_ENUM_FMT %d\n", fmt
->index
);
1004 if (fmt
->index
>= SN9C20X_N_FMTS
)
1008 fmt
->pixelformat
= sn9c20x_fmts
[fmt
->index
].pix_fmt
;
1010 if (fmt
->pixelformat
== V4L2_PIX_FMT_JPEG
&& dev
->jpeg
== 0)
1013 memcpy(fmt
->description
, sn9c20x_fmts
[fmt
->index
].desc
, 32);
1023 * @return 0 or negative error code
1026 int sn9c20x_vidioc_try_fmt_cap(struct file
*file
, void *priv
,
1027 struct v4l2_format
*fmt
)
1030 struct usb_sn9c20x
*dev
;
1032 dev
= video_get_drvdata(priv
);
1033 UDIA_DEBUG("TRY FMT %d\n", fmt
->type
);
1035 /* when this code is used prevents mplayer from setting outfmt
1036 if(fmt->fmt.pix.field != V4L2_FIELD_NONE)
1039 if (fmt
->fmt
.pix
.pixelformat
== V4L2_PIX_FMT_JPEG
&& dev
->jpeg
== 0)
1042 sn9c20x_get_closest_resolution(dev
, &fmt
->fmt
.pix
.width
,
1043 &fmt
->fmt
.pix
.height
);
1045 if (fmt
->fmt
.pix
.pixelformat
!= V4L2_PIX_FMT_SBGGR8
&&
1046 (fmt
->fmt
.pix
.width
> 640 && fmt
->fmt
.pix
.height
> 480))
1047 fmt
->fmt
.pix
.pixelformat
= V4L2_PIX_FMT_SBGGR8
;
1049 for (index
= 0; index
< SN9C20X_N_FMTS
; index
++)
1050 if (sn9c20x_fmts
[index
].pix_fmt
== fmt
->fmt
.pix
.pixelformat
)
1053 if (index
>= SN9C20X_N_FMTS
)
1056 fmt
->fmt
.pix
.bytesperline
= fmt
->fmt
.pix
.width
*
1057 sn9c20x_fmts
[index
].depth
/ 8;
1059 fmt
->fmt
.pix
.sizeimage
= fmt
->fmt
.pix
.height
*
1060 fmt
->fmt
.pix
.bytesperline
;
1062 fmt
->fmt
.pix
.colorspace
= V4L2_COLORSPACE_SRGB
;
1063 fmt
->fmt
.pix
.priv
= index
;
1076 int sn9c20x_vidioc_g_fmt_cap(struct file
*file
, void *priv
,
1077 struct v4l2_format
*fmt
)
1079 struct usb_sn9c20x
*dev
;
1081 dev
= video_get_drvdata(priv
);
1083 UDIA_DEBUG("GET FMT %d\n", fmt
->type
);
1085 memcpy(&(fmt
->fmt
.pix
), &(dev
->vsettings
.format
), sizeof(fmt
->fmt
.pix
));
1096 * @return 0 or negative error code
1099 int sn9c20x_vidioc_s_fmt_cap(struct file
*file
, void *priv
,
1100 struct v4l2_format
*fmt
)
1102 struct usb_sn9c20x
*dev
;
1105 dev
= video_get_drvdata(priv
);
1107 UDIA_DEBUG("SET FMT %d : %d\n", fmt
->type
, fmt
->fmt
.pix
.pixelformat
);
1109 if (v4l_get_privileges(file
) < 0)
1112 if (sn9c20x_queue_streaming(&dev
->queue
))
1115 ret
= sn9c20x_vidioc_try_fmt_cap(file
, priv
, fmt
);
1119 sn9c20x_set_resolution(dev
, fmt
->fmt
.pix
.width
, fmt
->fmt
.pix
.height
);
1120 sn9c20x_set_format(dev
, fmt
->fmt
.pix
.pixelformat
);
1121 memcpy(&(dev
->vsettings
.format
), &(fmt
->fmt
.pix
), sizeof(fmt
->fmt
.pix
));
1131 * @return 0 or negative error code
1134 int sn9c20x_vidioc_reqbufs(struct file
*file
, void *priv
,
1135 struct v4l2_requestbuffers
*request
)
1138 struct usb_sn9c20x
*dev
;
1140 dev
= video_get_drvdata(priv
);
1142 if (v4l_get_privileges(file
) < 0) {
1147 if (request
->memory
!= V4L2_MEMORY_MMAP
||
1148 request
->type
!= V4L2_BUF_TYPE_VIDEO_CAPTURE
) {
1153 if (sn9c20x_queue_streaming(&dev
->queue
)) {
1158 ret
= sn9c20x_alloc_buffers(&dev
->queue
, request
->count
,
1159 dev
->vsettings
.format
.sizeimage
);
1163 request
->count
= ret
;
1165 UDIA_DEBUG("Buffers Allocated %d\n", request
->count
);
1175 * @return 0 or negative error code
1178 int sn9c20x_vidioc_querybuf(struct file
*file
, void *priv
,
1179 struct v4l2_buffer
*buffer
)
1181 struct usb_sn9c20x
*dev
;
1183 dev
= video_get_drvdata(priv
);
1185 UDIA_DEBUG("QUERY BUFFERS %d %d\n", buffer
->index
, dev
->queue
.count
);
1187 if (buffer
->type
!= V4L2_BUF_TYPE_VIDEO_CAPTURE
)
1190 if (!v4l_has_privileges(file
))
1193 return sn9c20x_query_buffer(&dev
->queue
, buffer
);
1201 * @return 0 or negative error code
1204 int sn9c20x_vidioc_qbuf(struct file
*file
, void *priv
,
1205 struct v4l2_buffer
*buffer
)
1207 struct usb_sn9c20x
*dev
;
1209 dev
= video_get_drvdata(priv
);
1211 UDIA_DEBUG("VIDIOC_QBUF\n");
1213 if (!v4l_has_privileges(file
))
1216 return sn9c20x_queue_buffer(&dev
->queue
, buffer
);
1224 * @return 0 or negative error code
1227 int sn9c20x_vidioc_dqbuf(struct file
*file
, void *priv
,
1228 struct v4l2_buffer
*buffer
)
1230 struct usb_sn9c20x
*dev
;
1233 dev
= video_get_drvdata(priv
);
1235 UDIA_DEBUG("VIDIOC_DQBUF\n");
1237 if (!v4l_has_privileges(file
))
1240 ret
= sn9c20x_dequeue_buffer(&dev
->queue
, buffer
,
1241 file
->f_flags
& O_NONBLOCK
);
1245 if (dev
->vsettings
.format
.pixelformat
== V4L2_PIX_FMT_JPEG
) {
1246 UDIA_DEBUG("Adding JPEG Header\n");
1247 v4l_add_jpegheader(dev
, dev
->queue
.mem
+ buffer
->m
.offset
,
1249 buffer
->bytesused
+= 589;
1252 dev_sn9c20x_call_constantly(dev
);
1262 * @return 0 or negative error code
1265 int sn9c20x_vidioc_streamon(struct file
*file
, void *priv
,
1266 enum v4l2_buf_type type
)
1268 struct usb_sn9c20x
*dev
;
1270 dev
= video_get_drvdata(priv
);
1272 UDIA_DEBUG("VIDIOC_STREAMON\n");
1274 if (!v4l_has_privileges(file
))
1277 if (dev
->mode
!= SN9C20X_MODE_IDLE
)
1280 return v4l2_enable_video(dev
, SN9C20X_MODE_STREAM
);
1288 * @return 0 or negative error code
1291 int sn9c20x_vidioc_streamoff(struct file
*file
, void *priv
,
1292 enum v4l2_buf_type type
)
1294 struct usb_sn9c20x
*dev
;
1296 dev
= video_get_drvdata(priv
);
1298 UDIA_DEBUG("VIDIOC_STREAMOFF\n");
1300 if (!v4l_has_privileges(file
))
1303 return v4l2_enable_video(dev
, SN9C20X_MODE_IDLE
);
1311 * @return 0 or negative error code
1314 int sn9c20x_vidioc_g_param(struct file
*file
, void *priv
,
1315 struct v4l2_streamparm
*param
)
1317 struct usb_sn9c20x
*dev
;
1320 dev
= video_get_drvdata(priv
);
1322 if (param
->type
!= V4L2_BUF_TYPE_VIDEO_CAPTURE
)
1325 param
->parm
.capture
.capability
= 0;
1326 param
->parm
.capture
.capturemode
= 0;
1327 param
->parm
.capture
.timeperframe
.numerator
= 1;
1328 param
->parm
.capture
.timeperframe
.denominator
= 30;
1329 param
->parm
.capture
.readbuffers
= 2;
1330 param
->parm
.capture
.extendedmode
= 0;
1340 * @return 0 or negative error code
1343 int sn9c20x_vidioc_s_param(struct file
*file
, void *priv
,
1344 struct v4l2_streamparm
*param
)
1346 struct usb_sn9c20x
*dev
;
1348 dev
= video_get_drvdata(priv
);
1350 if (v4l_get_privileges(file
))
1353 if (param
->type
!= V4L2_BUF_TYPE_VIDEO_CAPTURE
)
1360 * @param inode Inode pointer
1361 * @param fp File pointer
1362 * @param cmd Command
1363 * @param arg Arguements of the command
1365 * @returns 0 if all is OK
1367 * @brief Manage IOCTL
1369 * This function permits to manage all the IOCTL from the application.
1371 #if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 29)
1372 static int v4l_sn9c20x_ioctl(struct inode
*inode
, struct file
*fp
,
1373 unsigned int cmd
, unsigned long arg
)
1375 static long v4l_sn9c20x_ioctl(struct file
*fp
,
1376 unsigned int cmd
, unsigned long arg
)
1380 struct usb_sn9c20x
*dev
;
1381 struct video_device
*vdev
;
1383 vdev
= video_devdata(fp
);
1384 dev
= video_get_drvdata(video_devdata(fp
));
1386 UDIA_DEBUG("v4l_sn9c20x_ioctl %02X\n", (unsigned char) cmd
);
1388 if (dev
== NULL
|| vdev
== NULL
)
1391 #if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 29)
1393 case VIDIOC_ENUM_FRAMESIZES
:
1395 struct v4l2_frmsizeenum size
;
1396 if (copy_from_user(&size
, (void __user
*)arg
, sizeof(size
)))
1398 err
= sn9c20x_vidioc_enum_framesizes(fp
,
1401 if (copy_to_user((void __user
*)arg
, &size
, sizeof(size
)))
1406 err
= video_ioctl2(inode
, fp
, cmd
, arg
);
1409 err
= video_ioctl2(fp
, cmd
, arg
);
1416 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27)
1417 static const struct v4l2_ioctl_ops sn9c20x_v4l2_ioctl_ops
= {
1418 .vidioc_querycap
= sn9c20x_vidioc_querycap
,
1419 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 29)
1420 .vidioc_enum_framesizes
= sn9c20x_vidioc_enum_framesizes
,
1422 .vidioc_enum_fmt_vid_cap
= sn9c20x_vidioc_enum_fmt_cap
,
1423 .vidioc_try_fmt_vid_cap
= sn9c20x_vidioc_try_fmt_cap
,
1424 .vidioc_s_fmt_vid_cap
= sn9c20x_vidioc_s_fmt_cap
,
1425 .vidioc_g_fmt_vid_cap
= sn9c20x_vidioc_g_fmt_cap
,
1426 .vidioc_enum_input
= sn9c20x_vidioc_enum_input
,
1427 .vidioc_g_input
= sn9c20x_vidioc_g_input
,
1428 .vidioc_s_input
= sn9c20x_vidioc_s_input
,
1429 .vidioc_streamon
= sn9c20x_vidioc_streamon
,
1430 .vidioc_streamoff
= sn9c20x_vidioc_streamoff
,
1431 .vidioc_queryctrl
= sn9c20x_vidioc_queryctrl
,
1432 .vidioc_g_ctrl
= sn9c20x_vidioc_g_ctrl
,
1433 .vidioc_s_ctrl
= sn9c20x_vidioc_s_ctrl
,
1434 .vidioc_g_parm
= sn9c20x_vidioc_g_param
,
1435 .vidioc_s_parm
= sn9c20x_vidioc_s_param
,
1436 .vidioc_reqbufs
= sn9c20x_vidioc_reqbufs
,
1437 .vidioc_qbuf
= sn9c20x_vidioc_qbuf
,
1438 .vidioc_dqbuf
= sn9c20x_vidioc_dqbuf
,
1439 .vidioc_querybuf
= sn9c20x_vidioc_querybuf
,
1444 * @param dev Device structure
1446 * @returns 0 if all is OK
1448 * @brief Register the video device
1450 * This function permits to register the USB device to the video device.
1452 int v4l_sn9c20x_register_video_device(struct usb_sn9c20x
*dev
)
1456 strcpy(dev
->vdev
->name
, DRIVER_DESC
);
1458 #if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 27)
1459 dev
->vdev
->dev
= &dev
->interface
->dev
;
1460 dev
->vdev
->owner
= THIS_MODULE
;
1461 dev
->vdev
->type
= VID_TYPE_CAPTURE
;
1463 dev
->vdev
->parent
= &dev
->interface
->dev
;
1465 dev
->vdev
->current_norm
= 0;
1466 dev
->vdev
->tvnorms
= 0;
1467 dev
->vdev
->fops
= &v4l_sn9c20x_fops
;
1468 dev
->vdev
->release
= video_device_release
;
1469 dev
->vdev
->minor
= -1;
1471 if (log_level
& SN9C20X_DEBUG
)
1472 dev
->vdev
->debug
= V4L2_DEBUG_IOCTL_ARG
;
1474 #if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 27)
1475 dev
->vdev
->vidioc_querycap
= sn9c20x_vidioc_querycap
;
1476 dev
->vdev
->vidioc_enum_fmt_cap
= sn9c20x_vidioc_enum_fmt_cap
;
1477 dev
->vdev
->vidioc_try_fmt_cap
= sn9c20x_vidioc_try_fmt_cap
;
1478 dev
->vdev
->vidioc_s_fmt_cap
= sn9c20x_vidioc_s_fmt_cap
;
1479 dev
->vdev
->vidioc_g_fmt_cap
= sn9c20x_vidioc_g_fmt_cap
;
1480 dev
->vdev
->vidioc_enum_input
= sn9c20x_vidioc_enum_input
;
1481 dev
->vdev
->vidioc_g_input
= sn9c20x_vidioc_g_input
;
1482 dev
->vdev
->vidioc_s_input
= sn9c20x_vidioc_s_input
;
1483 dev
->vdev
->vidioc_streamon
= sn9c20x_vidioc_streamon
;
1484 dev
->vdev
->vidioc_streamoff
= sn9c20x_vidioc_streamoff
;
1485 dev
->vdev
->vidioc_queryctrl
= sn9c20x_vidioc_queryctrl
;
1486 dev
->vdev
->vidioc_g_ctrl
= sn9c20x_vidioc_g_ctrl
;
1487 dev
->vdev
->vidioc_s_ctrl
= sn9c20x_vidioc_s_ctrl
;
1488 dev
->vdev
->vidioc_g_parm
= sn9c20x_vidioc_g_param
;
1489 dev
->vdev
->vidioc_s_parm
= sn9c20x_vidioc_s_param
;
1490 dev
->vdev
->vidioc_reqbufs
= sn9c20x_vidioc_reqbufs
;
1491 dev
->vdev
->vidioc_qbuf
= sn9c20x_vidioc_qbuf
;
1492 dev
->vdev
->vidioc_dqbuf
= sn9c20x_vidioc_dqbuf
;
1493 dev
->vdev
->vidioc_querybuf
= sn9c20x_vidioc_querybuf
;
1495 dev
->vdev
->ioctl_ops
= &sn9c20x_v4l2_ioctl_ops
;
1498 video_set_drvdata(dev
->vdev
, dev
);
1500 sn9c20x_queue_init(&dev
->queue
);
1502 err
= video_register_device(dev
->vdev
, VFL_TYPE_GRABBER
, -1);
1505 UDIA_ERROR("Video register fail !\n");
1507 UDIA_INFO("Webcam device %04X:%04X is now controlling video "
1508 "device /dev/video%d\n",
1509 le16_to_cpu(dev
->udev
->descriptor
.idVendor
),
1510 le16_to_cpu(dev
->udev
->descriptor
.idProduct
),
1518 * @param dev Device structure
1520 * @returns 0 if all is OK
1522 * @brief Unregister the video device
1524 * This function permits to unregister the video device.
1526 int v4l_sn9c20x_unregister_video_device(struct usb_sn9c20x
*dev
)
1528 UDIA_INFO("SN9C20X USB 2.0 Webcam releases control of video "
1529 "device /dev/video%d\n", dev
->vdev
->minor
);
1531 video_set_drvdata(dev
->vdev
, NULL
);
1532 video_unregister_device(dev
->vdev
);
1539 * @var v4l_sn9c20x_fops
1541 * This variable contains some callback
1544 #if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 29)
1545 static struct file_operations v4l_sn9c20x_fops
= {
1547 static struct v4l2_file_operations v4l_sn9c20x_fops
= {
1549 .owner
= THIS_MODULE
,
1550 .open
= v4l_sn9c20x_open
,
1551 .release
= v4l_sn9c20x_release
,
1552 .read
= v4l_sn9c20x_read
,
1553 .poll
= v4l_sn9c20x_poll
,
1554 .mmap
= v4l_sn9c20x_mmap
,
1555 .ioctl
= v4l_sn9c20x_ioctl
,
1556 #if defined(CONFIG_COMPAT) && LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 29)
1557 .compat_ioctl
= v4l_compat_ioctl32
,
1559 #if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 29)