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
,
82 .id
= V4L2_CID_CONTRAST
,
83 .type
= V4L2_CTRL_TYPE_INTEGER
,
90 .id
= V4L2_CID_EXPOSURE
,
91 .type
= V4L2_CTRL_TYPE_INTEGER
,
99 .type
= V4L2_CTRL_TYPE_INTEGER
,
106 .id
= V4L2_CID_HFLIP
,
107 .type
= V4L2_CTRL_TYPE_BOOLEAN
,
108 .name
= "Horizontal flip",
114 .id
= V4L2_CID_VFLIP
,
115 .type
= V4L2_CTRL_TYPE_BOOLEAN
,
116 .name
= "Vertical flip",
122 .id
= V4L2_CID_SHARPNESS
,
123 .type
= V4L2_CTRL_TYPE_INTEGER
,
130 .id
= V4L2_CID_RED_BALANCE
,
131 .type
= V4L2_CTRL_TYPE_INTEGER
,
132 .name
= "Red Balance",
138 .id
= V4L2_CID_BLUE_BALANCE
,
139 .type
= V4L2_CTRL_TYPE_INTEGER
,
140 .name
= "Blue Balance",
145 /* According to v4l2 specs auto exposure should be a 4 step value.
146 * This make little since for webcams however so a boolean is used
150 .id
= V4L2_CID_EXPOSURE_AUTO
,
151 .type
= V4L2_CTRL_TYPE_BOOLEAN
,
152 .name
= "Automatic exposure control",
158 .id
= V4L2_CID_AUTOGAIN
,
159 .type
= V4L2_CTRL_TYPE_BOOLEAN
,
160 .name
= "Automatic gain control",
166 .id
= V4L2_CID_AUTO_WHITE_BALANCE
,
167 .type
= V4L2_CTRL_TYPE_BOOLEAN
,
168 .name
= "Automatic whitbalance control",
175 void v4l2_set_control_default(struct usb_sn9c20x
*dev
, __u32 ctrl
, __u16 value
)
178 for (i
= 0; i
< ARRAY_SIZE(sn9c20x_controls
); i
++) {
179 if (sn9c20x_controls
[i
].id
== ctrl
) {
180 sn9c20x_controls
[i
].default_value
= value
;
181 sn9c20x_set_camera_control(dev
,
189 void v4l_add_jpegheader(struct usb_sn9c20x
*dev
, __u8
*buffer
,
192 static __u8 jpeg_header
[589] = {
193 0xff, 0xd8, 0xff, 0xdb, 0x00, 0x84, 0x00, 0x06, 0x04, 0x05,
194 0x06, 0x05, 0x04, 0x06, 0x06, 0x05, 0x06, 0x07, 0x07, 0x06,
195 0x08, 0x0a, 0x10, 0x0a, 0x0a, 0x09, 0x09, 0x0a, 0x14, 0x0e,
196 0x0f, 0x0c, 0x10, 0x17, 0x14, 0x18, 0x18, 0x17, 0x14, 0x16,
197 0x16, 0x1a, 0x1d, 0x25, 0x1f, 0x1a, 0x1b, 0x23, 0x1c, 0x16,
198 0x16, 0x20, 0x2c, 0x20, 0x23, 0x26, 0x27, 0x29, 0x2a, 0x29,
199 0x19, 0x1f, 0x2d, 0x30, 0x2d, 0x28, 0x30, 0x25, 0x28, 0x29,
200 0x28, 0x01, 0x07, 0x07, 0x07, 0x0a, 0x08, 0x0a, 0x13, 0x0a,
201 0x0a, 0x13, 0x28, 0x1a, 0x16, 0x1a, 0x28, 0x28, 0x28, 0x28,
202 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28,
203 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28,
204 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28,
205 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28,
206 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0xff, 0xc4, 0x01, 0xa2,
207 0x00, 0x00, 0x01, 0x05, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
208 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x02,
209 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x01,
210 0x00, 0x03, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
211 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x03,
212 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x10, 0x00,
213 0x02, 0x01, 0x03, 0x03, 0x02, 0x04, 0x03, 0x05, 0x05, 0x04,
214 0x04, 0x00, 0x00, 0x01, 0x7d, 0x01, 0x02, 0x03, 0x00, 0x04,
215 0x11, 0x05, 0x12, 0x21, 0x31, 0x41, 0x06, 0x13, 0x51, 0x61,
216 0x07, 0x22, 0x71, 0x14, 0x32, 0x81, 0x91, 0xa1, 0x08, 0x23,
217 0x42, 0xb1, 0xc1, 0x15, 0x52, 0xd1, 0xf0, 0x24, 0x33, 0x62,
218 0x72, 0x82, 0x09, 0x0a, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x25,
219 0x26, 0x27, 0x28, 0x29, 0x2a, 0x34, 0x35, 0x36, 0x37, 0x38,
220 0x39, 0x3a, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a,
221 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5a, 0x63, 0x64,
222 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x73, 0x74, 0x75, 0x76,
223 0x77, 0x78, 0x79, 0x7a, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88,
224 0x89, 0x8a, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99,
225 0x9a, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, 0xa8, 0xa9, 0xaa,
226 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, 0xb8, 0xb9, 0xba, 0xc2,
227 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xd2, 0xd3,
228 0xd4, 0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda, 0xe1, 0xe2, 0xe3,
229 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, 0xea, 0xf1, 0xf2, 0xf3,
230 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9, 0xfa, 0x11, 0x00, 0x02,
231 0x01, 0x02, 0x04, 0x04, 0x03, 0x04, 0x07, 0x05, 0x04, 0x04,
232 0x00, 0x01, 0x02, 0x77, 0x00, 0x01, 0x02, 0x03, 0x11, 0x04,
233 0x05, 0x21, 0x31, 0x06, 0x12, 0x41, 0x51, 0x07, 0x61, 0x71,
234 0x13, 0x22, 0x32, 0x81, 0x08, 0x14, 0x42, 0x91, 0xa1, 0xb1,
235 0xc1, 0x09, 0x23, 0x33, 0x52, 0xf0, 0x15, 0x62, 0x72, 0xd1,
236 0x0a, 0x16, 0x24, 0x34, 0xe1, 0x25, 0xf1, 0x17, 0x18, 0x19,
237 0x1a, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x35, 0x36, 0x37, 0x38,
238 0x39, 0x3a, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a,
239 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5a, 0x63, 0x64,
240 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x73, 0x74, 0x75, 0x76,
241 0x77, 0x78, 0x79, 0x7a, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87,
242 0x88, 0x89, 0x8a, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98,
243 0x99, 0x9a, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, 0xa8, 0xa9,
244 0xaa, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, 0xb8, 0xb9, 0xba,
245 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xd2,
246 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda, 0xe2, 0xe3,
247 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, 0xea, 0xf2, 0xf3, 0xf4,
248 0xf5, 0xf6, 0xf7, 0xf8, 0xf9, 0xfa, 0xff, 0xc0, 0x00, 0x11,
249 0x08, 0x01, 0xe0, 0x02, 0x80, 0x03, 0x01, 0x21, 0x00, 0x02,
250 0x11, 0x01, 0x03, 0x11, 0x01, 0xff, 0xda, 0x00, 0x0c, 0x03,
251 0x01, 0x00, 0x02, 0x11, 0x03, 0x11, 0x00, 0x3f, 0x00
253 static __u8 qtable1
[128] = {
254 0x0d, 0x08, 0x08, 0x0d, 0x08, 0x08, 0x0d, 0x0d,
255 0x0d, 0x0d, 0x11, 0x0d, 0x0d, 0x11, 0x15, 0x21,
256 0x15, 0x15, 0x11, 0x11, 0x15, 0x2a, 0x1d, 0x1d,
257 0x19, 0x21, 0x32, 0x2a, 0x32, 0x32, 0x2e, 0x2a,
258 0x2e, 0x2e, 0x36, 0x3a, 0x4b, 0x43, 0x36, 0x3a,
259 0x47, 0x3a, 0x2e, 0x2e, 0x43, 0x5c, 0x43, 0x47,
260 0x4f, 0x54, 0x58, 0x58, 0x58, 0x32, 0x3f, 0x60,
261 0x64, 0x5c, 0x54, 0x64, 0x4b, 0x54, 0x58, 0x54,
262 0x0d, 0x11, 0x11, 0x15, 0x11, 0x15, 0x26, 0x15,
263 0x15, 0x26, 0x54, 0x36, 0x2e, 0x36, 0x54, 0x54,
264 0x54, 0x54, 0x54, 0x54, 0x54, 0x54, 0x54, 0x54,
265 0x54, 0x54, 0x54, 0x54, 0x54, 0x54, 0x54, 0x54,
266 0x54, 0x54, 0x54, 0x54, 0x54, 0x54, 0x54, 0x54,
267 0x54, 0x54, 0x54, 0x54, 0x54, 0x54, 0x54, 0x54,
268 0x54, 0x54, 0x54, 0x54, 0x54, 0x54, 0x54, 0x54,
269 0x54, 0x54, 0x54, 0x54, 0x54, 0x54, 0x54, 0x54
272 jpeg_header
[6] = 0x00;
273 jpeg_header
[71] = 0x01;
274 memcpy(jpeg_header
+ 7, qtable1
, 64);
275 memcpy(jpeg_header
+ 8 + 64, qtable1
+64, 64);
276 jpeg_header
[564] = dev
->vsettings
.format
.width
& 0xFF;
277 jpeg_header
[563] = (dev
->vsettings
.format
.width
>> 8) & 0xFF;
278 jpeg_header
[562] = dev
->vsettings
.format
.height
& 0xFF;
279 jpeg_header
[561] = (dev
->vsettings
.format
.height
>> 8) & 0xFF;
280 jpeg_header
[567] = 0x21;
282 memmove(buffer
+589, buffer
, buffer_size
);
283 memcpy(buffer
, jpeg_header
, 589);
286 * @brief Get V4L privileges
290 * @return 0 or negative error code
293 int v4l_get_privileges(struct file
*file
)
295 struct usb_sn9c20x
*dev
;
298 dev
= video_get_drvdata(video_devdata(file
));
300 if (dev
->owner
== file
)
303 mutex_lock(&open_lock
);
304 if (dev
->owner
!= NULL
) {
310 mutex_unlock(&open_lock
);
315 * @brief Check whether there are V4L privileges
322 int v4l_has_privileges(struct file
*file
)
324 struct usb_sn9c20x
*dev
;
327 dev
= video_get_drvdata(video_devdata(file
));
329 if (dev
->owner
== file
)
336 * @brief Drop V4L privileges
341 void v4l_drop_privileges(struct file
*file
)
343 struct usb_sn9c20x
*dev
;
345 dev
= video_get_drvdata(video_devdata(file
));
347 if (dev
->owner
== file
)
352 * @brief Enable video stream
354 * @param dev Pointer to device structure
355 * @param mode Mode for video stream
357 * @returns 0 or negative error value
360 int v4l2_enable_video(struct usb_sn9c20x
*dev
, int mode
)
364 if (mode
== SN9C20X_MODE_IDLE
) {
365 sn9c20x_enable_video(dev
, 0);
366 usb_sn9c20x_uninit_urbs(dev
);
367 sn9c20x_queue_enable(&dev
->queue
, 0);
372 if (dev
->mode
!= SN9C20X_MODE_IDLE
)
375 if (sn9c20x_queue_enable(&dev
->queue
, 1) < 0)
378 ret
= usb_sn9c20x_init_urbs(dev
);
383 sn9c20x_enable_video(dev
, 1);
386 if (dev
->vsettings
.format
.pixelformat
== V4L2_PIX_FMT_JPEG
)
387 dev
->queue
.flags
&= ~SN9C20X_QUEUE_DROP_INCOMPLETE
;
389 dev
->queue
.flags
|= SN9C20X_QUEUE_DROP_INCOMPLETE
;
395 * @param inode Pointer on an inode
396 * @param fp File pointer
398 * @returns 0 if all is OK
400 * @brief Open the video device
402 * This function permits to open a video device (/dev/videoX)
404 #if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 29)
405 static int v4l_sn9c20x_open(struct inode
*inode
, struct file
*fp
)
407 static int v4l_sn9c20x_open(struct file
*fp
)
412 struct usb_sn9c20x
*dev
;
413 struct video_device
*vdev
;
415 mutex_lock(&open_lock
);
417 vdev
= video_devdata(fp
);
418 dev
= video_get_drvdata(video_devdata(fp
));
420 fp
->private_data
= vdev
;
422 kref_get(&dev
->vopen
);
424 mutex_unlock(&open_lock
);
430 * @param inode Pointer on inode
431 * @param fp File pointer
433 * @returns 0 if all is OK
435 * @brief Release an opened file.
437 * This function permits to release an opened file with the 'open' method.
439 #if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 29)
440 static int v4l_sn9c20x_release(struct inode
*inode
, struct file
*fp
)
442 static int v4l_sn9c20x_release(struct file
*fp
)
445 struct usb_sn9c20x
*dev
;
446 struct video_device
*vdev
;
448 mutex_lock(&open_lock
);
450 vdev
= video_devdata(fp
);
451 dev
= video_get_drvdata(video_devdata(fp
));
453 if (v4l_has_privileges(fp
)) {
454 v4l2_enable_video(dev
, SN9C20X_MODE_IDLE
);
456 mutex_lock(&dev
->queue
.mutex
);
457 sn9c20x_free_buffers(&dev
->queue
);
458 mutex_unlock(&dev
->queue
.mutex
);
461 v4l_drop_privileges(fp
);
463 kref_put(&dev
->vopen
, usb_sn9c20x_delete
);
465 mutex_unlock(&open_lock
);
471 * @param fp File pointer
473 * @retval buf Buffer in user space
477 * @returns Count value
479 * @brief Read the video device
481 * This function is called by the application is reading the video device.
483 static ssize_t
v4l_sn9c20x_read(struct file
*fp
, char __user
*buf
,
484 size_t count
, loff_t
*f_pos
)
488 struct v4l2_buffer buffer
;
489 struct usb_sn9c20x
*dev
;
491 dev
= video_get_drvdata(video_devdata(fp
));
493 ret
= v4l_get_privileges(fp
);
497 if (dev
->mode
!= SN9C20X_MODE_IDLE
&&
498 dev
->mode
!= SN9C20X_MODE_READ
)
501 buffer
.type
= V4L2_BUF_TYPE_VIDEO_CAPTURE
;
502 buffer
.memory
= V4L2_MEMORY_MMAP
;
503 if (dev
->mode
== SN9C20X_MODE_IDLE
) {
504 nbuffers
= sn9c20x_alloc_buffers(&dev
->queue
, 2,
505 dev
->vsettings
.format
.sizeimage
);
509 for (i
= 0; i
< nbuffers
; i
++) {
510 buffer
= dev
->queue
.buffer
[i
].buf
;
511 sn9c20x_queue_buffer(&dev
->queue
, &buffer
);
514 ret
= v4l2_enable_video(dev
, SN9C20X_MODE_READ
);
519 dev_sn9c20x_call_constantly(dev
);
521 if (dev
->queue
.read_buffer
== NULL
) {
522 ret
= sn9c20x_dequeue_buffer(&dev
->queue
, &buffer
,
523 fp
->f_flags
& O_NONBLOCK
);
527 if (dev
->vsettings
.format
.pixelformat
== V4L2_PIX_FMT_JPEG
) {
528 UDIA_DEBUG("Adding JPEG Header\n");
529 v4l_add_jpegheader(dev
, dev
->queue
.mem
+ buffer
.m
.offset
,
531 buffer
.bytesused
+= 589;
534 dev
->queue
.read_buffer
= &dev
->queue
.buffer
[buffer
.index
];
536 buffer
= dev
->queue
.read_buffer
->buf
;
539 count
= min((size_t)(buffer
.bytesused
- *f_pos
), count
);
540 if (copy_to_user(buf
, dev
->queue
.mem
+ buffer
.m
.offset
+ *f_pos
, count
))
544 if (*f_pos
>= buffer
.bytesused
) {
545 dev
->queue
.read_buffer
= NULL
;
546 sn9c20x_queue_buffer(&dev
->queue
, &buffer
);
554 * @param fp File pointer
557 * @returns 0 if all is OK
559 * @brief Polling function
561 static unsigned int v4l_sn9c20x_poll(struct file
*fp
, poll_table
*wait
)
563 struct usb_sn9c20x
*dev
;
564 struct video_device
*vdev
;
566 vdev
= video_devdata(fp
);
567 dev
= video_get_drvdata(video_devdata(fp
));
569 UDIA_STREAM("Poll\n");
571 if (vdev
== NULL
|| dev
== NULL
)
574 return sn9c20x_queue_poll(&dev
->queue
, fp
, wait
);
581 static void sn9c20x_vm_open(struct vm_area_struct
*vma
)
583 struct sn9c20x_buffer
*buffer
= vma
->vm_private_data
;
584 buffer
->vma_use_count
++;
592 static void sn9c20x_vm_close(struct vm_area_struct
*vma
)
594 struct sn9c20x_buffer
*buffer
= vma
->vm_private_data
;
595 buffer
->vma_use_count
--;
598 struct vm_operations_struct sn9c20x_vm_ops
= {
599 .open
= sn9c20x_vm_open
,
600 .close
= sn9c20x_vm_close
604 * @param fp File pointer
605 * @param vma VMA structure
607 * @returns 0 if all is OK
611 * This function permits to map a memory space.
613 static int v4l_sn9c20x_mmap(struct file
*fp
, struct vm_area_struct
*vma
)
616 unsigned long addr
, start
, size
;
620 struct usb_sn9c20x
*dev
;
621 struct video_device
*vdev
;
622 struct sn9c20x_buffer
*buffer
= NULL
;
624 vdev
= video_devdata(fp
);
625 dev
= video_get_drvdata(video_devdata(fp
));
627 UDIA_STREAM("mmap\n");
629 start
= vma
->vm_start
;
630 size
= vma
->vm_end
- vma
->vm_start
;
632 mutex_lock(&dev
->queue
.mutex
);
634 for (i
= 0; i
< dev
->queue
.count
; ++i
) {
635 buffer
= &dev
->queue
.buffer
[i
];
636 if ((buffer
->buf
.m
.offset
>> PAGE_SHIFT
) == vma
->vm_pgoff
)
640 if (i
== dev
->queue
.count
|| size
!= dev
->queue
.buf_size
) {
645 vma
->vm_flags
|= VM_IO
;
647 addr
= (unsigned long)dev
->queue
.mem
+ buffer
->buf
.m
.offset
;
649 page
= vmalloc_to_page((void *)addr
);
650 ret
= vm_insert_page(vma
, start
, page
);
659 vma
->vm_ops
= &sn9c20x_vm_ops
;
660 vma
->vm_private_data
= buffer
;
661 sn9c20x_vm_open(vma
);
663 mutex_unlock(&dev
->queue
.mutex
);
675 int sn9c20x_vidioc_querycap(struct file
*file
, void *priv
,
676 struct v4l2_capability
*cap
)
678 struct usb_sn9c20x
*dev
;
680 dev
= video_get_drvdata(priv
);
682 UDIA_DEBUG("VIDIOC_QUERYCAP\n");
684 strlcpy(cap
->driver
, "sn9c20x", sizeof(cap
->driver
));
685 cap
->capabilities
= V4L2_CAP_VIDEO_CAPTURE
| V4L2_CAP_STREAMING
686 | V4L2_CAP_READWRITE
;
687 cap
->version
= (__u32
) DRIVER_VERSION_NUM
,
688 strlcpy(cap
->card
, dev
->vdev
->name
, sizeof(cap
->card
));
690 if (usb_make_path(dev
->udev
, cap
->bus_info
, sizeof(cap
->bus_info
)) < 0)
691 strlcpy(cap
->bus_info
, dev
->vdev
->name
, sizeof(cap
->bus_info
));
700 * @return 0 or negative error code
703 int sn9c20x_vidioc_enum_input(struct file
*file
, void *priv
,
704 struct v4l2_input
*input
)
706 UDIA_DEBUG("VIDIOC_ENUMINPUT %d\n", input
->index
);
711 strlcpy(input
->name
, "Webcam", sizeof(input
->name
));
712 input
->type
= V4L2_INPUT_TYPE_CAMERA
;
723 * @return 0 or negative error code
726 int sn9c20x_vidioc_g_input(struct file
*file
, void *priv
, unsigned int *index
)
728 UDIA_DEBUG("GET INPUT %d\n", *index
);
741 * @return 0 or negative error code
744 int sn9c20x_vidioc_s_input(struct file
*file
, void *priv
, unsigned int index
)
746 UDIA_DEBUG("SET INPUT %d\n", index
);
748 if (v4l_get_privileges(file
) < 0)
762 * @return 0 or negative error code
765 int sn9c20x_vidioc_queryctrl(struct file
*file
, void *priv
,
766 struct v4l2_queryctrl
*ctrl
)
770 #ifdef V4L2_CTRL_FLAG_NEXT_CTRL
774 struct usb_sn9c20x
*dev
;
776 dev
= video_get_drvdata(priv
);
778 UDIA_DEBUG("VIDIOC_QUERYCTRL id = %d\n", ctrl
->id
);
780 #ifdef V4L2_CTRL_FLAG_NEXT_CTRL
781 if (ctrl
->id
& V4L2_CTRL_FLAG_NEXT_CTRL
) {
783 idnew
= V4L2_CTRL_FLAG_NEXT_CTRL
;
784 idlast
= ctrl
->id
& ~V4L2_CTRL_FLAG_NEXT_CTRL
;
785 for (i
= 0; i
< ARRAY_SIZE(sn9c20x_controls
); i
++) {
786 if ((sn9c20x_controls
[i
].id
< idnew
) &&
787 (sn9c20x_controls
[i
].id
> idlast
)) {
788 idnew
= sn9c20x_controls
[i
].id
;
792 if (idnew
!= V4L2_CTRL_FLAG_NEXT_CTRL
) {
793 UDIA_DEBUG("VIDIOC_QUERYCTRL found\n");
794 memcpy(ctrl
, &sn9c20x_controls
[min
],
795 sizeof(struct v4l2_queryctrl
));
803 for (i
= 0; i
< ARRAY_SIZE(sn9c20x_controls
); i
++) {
804 if (sn9c20x_controls
[i
].id
== ctrl
->id
) {
805 UDIA_DEBUG("VIDIOC_QUERYCTRL found\n");
806 memcpy(ctrl
, &sn9c20x_controls
[i
],
807 sizeof(struct v4l2_queryctrl
));
816 if ((ctrl
->id
== V4L2_CID_GAIN
&& dev
->vsettings
.auto_gain
) ||
817 (ctrl
->id
== V4L2_CID_EXPOSURE
&& dev
->vsettings
.auto_exposure
) ||
818 ((ctrl
->id
== V4L2_CID_BLUE_BALANCE
||
819 ctrl
->id
== V4L2_CID_RED_BALANCE
) &&
820 dev
->vsettings
.auto_whitebalance
)) {
821 ctrl
->flags
|= V4L2_CTRL_FLAG_GRABBED
;
831 * @return 0 or negative error code
834 int sn9c20x_vidioc_g_ctrl(struct file
*file
, void *priv
,
835 struct v4l2_control
*ctrl
)
837 struct usb_sn9c20x
*dev
;
839 dev
= video_get_drvdata(priv
);
841 UDIA_DEBUG("GET CTRL id=%d\n", ctrl
->id
);
844 case V4L2_CID_BRIGHTNESS
:
845 ctrl
->value
= dev
->vsettings
.brightness
;
848 case V4L2_CID_EXPOSURE
:
849 ctrl
->value
= dev
->vsettings
.exposure
;
853 ctrl
->value
= dev
->vsettings
.gain
;
857 ctrl
->value
= dev
->vsettings
.gamma
;
860 case V4L2_CID_SATURATION:
861 ctrl->value = dev->vsettings.colour;
864 case V4L2_CID_CONTRAST
:
865 ctrl
->value
= dev
->vsettings
.contrast
;
869 ctrl
->value
= dev
->vsettings
.hflip
;
873 ctrl
->value
= dev
->vsettings
.vflip
;
876 case V4L2_CID_SHARPNESS
:
877 ctrl
->value
= dev
->vsettings
.sharpness
;
880 case V4L2_CID_RED_BALANCE
:
881 ctrl
->value
= dev
->vsettings
.red_gain
;
884 case V4L2_CID_BLUE_BALANCE
:
885 ctrl
->value
= dev
->vsettings
.blue_gain
;
888 case V4L2_CID_EXPOSURE_AUTO
:
889 ctrl
->value
= dev
->vsettings
.auto_exposure
;
892 case V4L2_CID_AUTOGAIN
:
893 ctrl
->value
= dev
->vsettings
.auto_gain
;
896 case V4L2_CID_AUTO_WHITE_BALANCE
:
897 ctrl
->value
= dev
->vsettings
.auto_whitebalance
;
907 * @brief Apply v4l2 settings on camera
911 * @param ctrl V4L2 control structure
913 * @returns 0 or negative error value
916 int sn9c20x_vidioc_s_ctrl(struct file
*file
, void *priv
,
917 struct v4l2_control
*ctrl
)
919 struct usb_sn9c20x
*dev
;
921 dev
= video_get_drvdata(priv
);
923 UDIA_DEBUG("SET CTRL id=%d value=%d\n", ctrl
->id
, ctrl
->value
);
925 if ((ctrl
->id
== V4L2_CID_GAIN
&& dev
->vsettings
.auto_gain
) ||
926 (ctrl
->id
== V4L2_CID_EXPOSURE
&& dev
->vsettings
.auto_exposure
) ||
927 ((ctrl
->id
== V4L2_CID_BLUE_BALANCE
||
928 ctrl
->id
== V4L2_CID_RED_BALANCE
) &&
929 dev
->vsettings
.auto_whitebalance
)) {
933 return sn9c20x_set_camera_control(dev
,
943 * @return 0 or negative error code
946 int sn9c20x_vidioc_enum_fmt_cap(struct file
*file
, void *priv
,
947 struct v4l2_fmtdesc
*fmt
)
949 struct usb_sn9c20x
*dev
;
951 dev
= video_get_drvdata(priv
);
953 UDIA_DEBUG("VIDIOC_ENUM_FMT %d\n", fmt
->index
);
955 if (fmt
->index
>= SN9C20X_N_FMTS
)
959 fmt
->pixelformat
= sn9c20x_fmts
[fmt
->index
].pix_fmt
;
961 if (fmt
->pixelformat
== V4L2_PIX_FMT_JPEG
&& dev
->jpeg
== 0)
964 memcpy(fmt
->description
, sn9c20x_fmts
[fmt
->index
].desc
, 32);
974 * @return 0 or negative error code
977 int sn9c20x_vidioc_try_fmt_cap(struct file
*file
, void *priv
,
978 struct v4l2_format
*fmt
)
981 struct usb_sn9c20x
*dev
;
983 dev
= video_get_drvdata(priv
);
984 UDIA_DEBUG("TRY FMT %d\n", fmt
->type
);
986 /* when this code is used prevents mplayer from setting outfmt
987 if(fmt->fmt.pix.field != V4L2_FIELD_NONE)
990 if (fmt
->fmt
.pix
.pixelformat
== V4L2_PIX_FMT_JPEG
&& dev
->jpeg
== 0)
993 for (index
= 0; index
< SN9C20X_N_FMTS
; index
++)
994 if (sn9c20x_fmts
[index
].pix_fmt
== fmt
->fmt
.pix
.pixelformat
)
997 if (index
>= SN9C20X_N_FMTS
)
1000 sn9c20x_get_closest_resolution(dev
, &fmt
->fmt
.pix
.width
,
1001 &fmt
->fmt
.pix
.height
);
1003 fmt
->fmt
.pix
.bytesperline
= fmt
->fmt
.pix
.width
*
1004 sn9c20x_fmts
[index
].depth
/ 8;
1006 fmt
->fmt
.pix
.sizeimage
= fmt
->fmt
.pix
.height
*
1007 fmt
->fmt
.pix
.bytesperline
;
1009 fmt
->fmt
.pix
.colorspace
= V4L2_COLORSPACE_SRGB
;
1010 fmt
->fmt
.pix
.priv
= index
;
1023 int sn9c20x_vidioc_g_fmt_cap(struct file
*file
, void *priv
,
1024 struct v4l2_format
*fmt
)
1026 struct usb_sn9c20x
*dev
;
1028 dev
= video_get_drvdata(priv
);
1030 UDIA_DEBUG("GET FMT %d\n", fmt
->type
);
1032 memcpy(&(fmt
->fmt
.pix
), &(dev
->vsettings
.format
), sizeof(fmt
->fmt
.pix
));
1043 * @return 0 or negative error code
1046 int sn9c20x_vidioc_s_fmt_cap(struct file
*file
, void *priv
,
1047 struct v4l2_format
*fmt
)
1049 struct usb_sn9c20x
*dev
;
1052 dev
= video_get_drvdata(priv
);
1054 UDIA_DEBUG("SET FMT %d : %d\n", fmt
->type
, fmt
->fmt
.pix
.pixelformat
);
1056 if (v4l_get_privileges(file
) < 0)
1059 if (sn9c20x_queue_streaming(&dev
->queue
))
1062 ret
= sn9c20x_vidioc_try_fmt_cap(file
, priv
, fmt
);
1066 sn9c20x_set_resolution(dev
, fmt
->fmt
.pix
.width
, fmt
->fmt
.pix
.height
);
1067 sn9c20x_set_format(dev
, fmt
->fmt
.pix
.pixelformat
);
1068 memcpy(&(dev
->vsettings
.format
), &(fmt
->fmt
.pix
), sizeof(fmt
->fmt
.pix
));
1078 * @return 0 or negative error code
1081 int sn9c20x_vidioc_reqbufs(struct file
*file
, void *priv
,
1082 struct v4l2_requestbuffers
*request
)
1085 struct usb_sn9c20x
*dev
;
1087 dev
= video_get_drvdata(priv
);
1089 if (v4l_get_privileges(file
) < 0) {
1094 if (request
->memory
!= V4L2_MEMORY_MMAP
||
1095 request
->type
!= V4L2_BUF_TYPE_VIDEO_CAPTURE
) {
1100 if (sn9c20x_queue_streaming(&dev
->queue
)) {
1105 ret
= sn9c20x_alloc_buffers(&dev
->queue
, request
->count
,
1106 dev
->vsettings
.format
.sizeimage
);
1110 request
->count
= ret
;
1112 UDIA_DEBUG("Buffers Allocated %d\n", request
->count
);
1122 * @return 0 or negative error code
1125 int sn9c20x_vidioc_querybuf(struct file
*file
, void *priv
,
1126 struct v4l2_buffer
*buffer
)
1128 struct usb_sn9c20x
*dev
;
1130 dev
= video_get_drvdata(priv
);
1132 UDIA_DEBUG("QUERY BUFFERS %d %d\n", buffer
->index
, dev
->queue
.count
);
1134 if (buffer
->memory
!= V4L2_MEMORY_MMAP
||
1135 buffer
->type
!= V4L2_BUF_TYPE_VIDEO_CAPTURE
)
1138 if (!v4l_has_privileges(file
))
1141 return sn9c20x_query_buffer(&dev
->queue
, buffer
);
1149 * @return 0 or negative error code
1152 int sn9c20x_vidioc_qbuf(struct file
*file
, void *priv
,
1153 struct v4l2_buffer
*buffer
)
1155 struct usb_sn9c20x
*dev
;
1157 dev
= video_get_drvdata(priv
);
1159 UDIA_DEBUG("VIDIOC_QBUF\n");
1161 if (!v4l_has_privileges(file
))
1164 return sn9c20x_queue_buffer(&dev
->queue
, buffer
);
1172 * @return 0 or negative error code
1175 int sn9c20x_vidioc_dqbuf(struct file
*file
, void *priv
,
1176 struct v4l2_buffer
*buffer
)
1178 struct usb_sn9c20x
*dev
;
1181 dev
= video_get_drvdata(priv
);
1183 UDIA_DEBUG("VIDIOC_DQBUF\n");
1185 if (!v4l_has_privileges(file
))
1188 ret
= sn9c20x_dequeue_buffer(&dev
->queue
, buffer
,
1189 file
->f_flags
& O_NONBLOCK
);
1193 if (dev
->vsettings
.format
.pixelformat
== V4L2_PIX_FMT_JPEG
) {
1194 UDIA_DEBUG("Adding JPEG Header\n");
1195 v4l_add_jpegheader(dev
, dev
->queue
.mem
+ buffer
->m
.offset
,
1197 buffer
->bytesused
+= 589;
1200 dev_sn9c20x_call_constantly(dev
);
1210 * @return 0 or negative error code
1213 int sn9c20x_vidioc_streamon(struct file
*file
, void *priv
,
1214 enum v4l2_buf_type type
)
1216 struct usb_sn9c20x
*dev
;
1218 dev
= video_get_drvdata(priv
);
1220 UDIA_DEBUG("VIDIOC_STREAMON\n");
1222 if (!v4l_has_privileges(file
))
1225 if (dev
->mode
!= SN9C20X_MODE_IDLE
)
1228 return v4l2_enable_video(dev
, SN9C20X_MODE_STREAM
);
1236 * @return 0 or negative error code
1239 int sn9c20x_vidioc_streamoff(struct file
*file
, void *priv
,
1240 enum v4l2_buf_type type
)
1242 struct usb_sn9c20x
*dev
;
1244 dev
= video_get_drvdata(priv
);
1246 UDIA_DEBUG("VIDIOC_STREAMOFF\n");
1248 if (!v4l_has_privileges(file
))
1251 return v4l2_enable_video(dev
, SN9C20X_MODE_IDLE
);
1259 * @return 0 or negative error code
1262 int sn9c20x_vidioc_g_param(struct file
*file
, void *priv
,
1263 struct v4l2_streamparm
*param
)
1265 struct usb_sn9c20x
*dev
;
1268 dev
= video_get_drvdata(priv
);
1270 if (param
->type
!= V4L2_BUF_TYPE_VIDEO_CAPTURE
)
1273 param
->parm
.capture
.capability
= 0;
1274 param
->parm
.capture
.capturemode
= 0;
1275 param
->parm
.capture
.timeperframe
.numerator
= 1;
1276 param
->parm
.capture
.timeperframe
.denominator
= 30;
1277 param
->parm
.capture
.readbuffers
= 2;
1278 param
->parm
.capture
.extendedmode
= 0;
1288 * @return 0 or negative error code
1291 int sn9c20x_vidioc_s_param(struct file
*file
, void *priv
,
1292 struct v4l2_streamparm
*param
)
1294 struct usb_sn9c20x
*dev
;
1296 dev
= video_get_drvdata(priv
);
1298 if (v4l_get_privileges(file
))
1301 if (param
->type
!= V4L2_BUF_TYPE_VIDEO_CAPTURE
)
1308 * @param inode Inode pointer
1309 * @param fp File pointer
1310 * @param cmd Command
1311 * @param arg Arguements of the command
1313 * @returns 0 if all is OK
1315 * @brief Manage IOCTL
1317 * This function permits to manage all the IOCTL from the application.
1319 #if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 29)
1320 static int v4l_sn9c20x_ioctl(struct inode
*inode
, struct file
*fp
,
1321 unsigned int cmd
, unsigned long arg
)
1323 static long v4l_sn9c20x_ioctl(struct file
*fp
,
1324 unsigned int cmd
, unsigned long arg
)
1328 struct usb_sn9c20x
*dev
;
1329 struct video_device
*vdev
;
1331 vdev
= video_devdata(fp
);
1332 dev
= video_get_drvdata(video_devdata(fp
));
1334 UDIA_DEBUG("v4l_sn9c20x_ioctl %02X\n", (unsigned char) cmd
);
1336 if (dev
== NULL
|| vdev
== NULL
)
1339 #if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 29)
1340 err
= video_ioctl2(inode
, fp
, cmd
, arg
);
1342 err
= video_ioctl2(fp
, cmd
, arg
);
1349 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27)
1350 static const struct v4l2_ioctl_ops sn9c20x_v4l2_ioctl_ops
= {
1351 .vidioc_querycap
= sn9c20x_vidioc_querycap
,
1352 .vidioc_enum_fmt_vid_cap
= sn9c20x_vidioc_enum_fmt_cap
,
1353 .vidioc_try_fmt_vid_cap
= sn9c20x_vidioc_try_fmt_cap
,
1354 .vidioc_s_fmt_vid_cap
= sn9c20x_vidioc_s_fmt_cap
,
1355 .vidioc_g_fmt_vid_cap
= sn9c20x_vidioc_g_fmt_cap
,
1356 .vidioc_enum_input
= sn9c20x_vidioc_enum_input
,
1357 .vidioc_g_input
= sn9c20x_vidioc_g_input
,
1358 .vidioc_s_input
= sn9c20x_vidioc_s_input
,
1359 .vidioc_streamon
= sn9c20x_vidioc_streamon
,
1360 .vidioc_streamoff
= sn9c20x_vidioc_streamoff
,
1361 .vidioc_queryctrl
= sn9c20x_vidioc_queryctrl
,
1362 .vidioc_g_ctrl
= sn9c20x_vidioc_g_ctrl
,
1363 .vidioc_s_ctrl
= sn9c20x_vidioc_s_ctrl
,
1364 .vidioc_g_parm
= sn9c20x_vidioc_g_param
,
1365 .vidioc_s_parm
= sn9c20x_vidioc_s_param
,
1366 .vidioc_reqbufs
= sn9c20x_vidioc_reqbufs
,
1367 .vidioc_qbuf
= sn9c20x_vidioc_qbuf
,
1368 .vidioc_dqbuf
= sn9c20x_vidioc_dqbuf
,
1369 .vidioc_querybuf
= sn9c20x_vidioc_querybuf
,
1374 * @param dev Device structure
1376 * @returns 0 if all is OK
1378 * @brief Register the video device
1380 * This function permits to register the USB device to the video device.
1382 int v4l_sn9c20x_register_video_device(struct usb_sn9c20x
*dev
)
1386 strcpy(dev
->vdev
->name
, DRIVER_DESC
);
1388 #if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 27)
1389 dev
->vdev
->dev
= &dev
->interface
->dev
;
1390 dev
->vdev
->owner
= THIS_MODULE
;
1391 dev
->vdev
->type
= VID_TYPE_CAPTURE
;
1393 dev
->vdev
->parent
= &dev
->interface
->dev
;
1395 dev
->vdev
->current_norm
= 0;
1396 dev
->vdev
->tvnorms
= 0;
1397 dev
->vdev
->fops
= &v4l_sn9c20x_fops
;
1398 dev
->vdev
->release
= video_device_release
;
1399 dev
->vdev
->minor
= -1;
1401 if (log_level
& SN9C20X_DEBUG
)
1402 dev
->vdev
->debug
= V4L2_DEBUG_IOCTL_ARG
;
1404 #if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 27)
1405 dev
->vdev
->vidioc_querycap
= sn9c20x_vidioc_querycap
;
1406 dev
->vdev
->vidioc_enum_fmt_cap
= sn9c20x_vidioc_enum_fmt_cap
;
1407 dev
->vdev
->vidioc_try_fmt_cap
= sn9c20x_vidioc_try_fmt_cap
;
1408 dev
->vdev
->vidioc_s_fmt_cap
= sn9c20x_vidioc_s_fmt_cap
;
1409 dev
->vdev
->vidioc_g_fmt_cap
= sn9c20x_vidioc_g_fmt_cap
;
1410 dev
->vdev
->vidioc_enum_input
= sn9c20x_vidioc_enum_input
;
1411 dev
->vdev
->vidioc_g_input
= sn9c20x_vidioc_g_input
;
1412 dev
->vdev
->vidioc_s_input
= sn9c20x_vidioc_s_input
;
1413 dev
->vdev
->vidioc_streamon
= sn9c20x_vidioc_streamon
;
1414 dev
->vdev
->vidioc_streamoff
= sn9c20x_vidioc_streamoff
;
1415 dev
->vdev
->vidioc_queryctrl
= sn9c20x_vidioc_queryctrl
;
1416 dev
->vdev
->vidioc_g_ctrl
= sn9c20x_vidioc_g_ctrl
;
1417 dev
->vdev
->vidioc_s_ctrl
= sn9c20x_vidioc_s_ctrl
;
1418 dev
->vdev
->vidioc_g_parm
= sn9c20x_vidioc_g_param
;
1419 dev
->vdev
->vidioc_s_parm
= sn9c20x_vidioc_s_param
;
1420 dev
->vdev
->vidioc_reqbufs
= sn9c20x_vidioc_reqbufs
;
1421 dev
->vdev
->vidioc_qbuf
= sn9c20x_vidioc_qbuf
;
1422 dev
->vdev
->vidioc_dqbuf
= sn9c20x_vidioc_dqbuf
;
1423 dev
->vdev
->vidioc_querybuf
= sn9c20x_vidioc_querybuf
;
1425 dev
->vdev
->ioctl_ops
= &sn9c20x_v4l2_ioctl_ops
;
1428 video_set_drvdata(dev
->vdev
, dev
);
1430 sn9c20x_queue_init(&dev
->queue
);
1432 err
= video_register_device(dev
->vdev
, VFL_TYPE_GRABBER
, -1);
1435 UDIA_ERROR("Video register fail !\n");
1437 UDIA_INFO("Webcam device %04X:%04X is now controlling video "
1438 "device /dev/video%d\n",
1439 le16_to_cpu(dev
->udev
->descriptor
.idVendor
),
1440 le16_to_cpu(dev
->udev
->descriptor
.idProduct
),
1448 * @param dev Device structure
1450 * @returns 0 if all is OK
1452 * @brief Unregister the video device
1454 * This function permits to unregister the video device.
1456 int v4l_sn9c20x_unregister_video_device(struct usb_sn9c20x
*dev
)
1458 UDIA_INFO("SN9C20X USB 2.0 Webcam releases control of video "
1459 "device /dev/video%d\n", dev
->vdev
->minor
);
1461 video_set_drvdata(dev
->vdev
, NULL
);
1462 video_unregister_device(dev
->vdev
);
1469 * @var v4l_sn9c20x_fops
1471 * This variable contains some callback
1474 #if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 29)
1475 static struct file_operations v4l_sn9c20x_fops
= {
1477 static struct v4l2_file_operations v4l_sn9c20x_fops
= {
1479 .owner
= THIS_MODULE
,
1480 .open
= v4l_sn9c20x_open
,
1481 .release
= v4l_sn9c20x_release
,
1482 .read
= v4l_sn9c20x_read
,
1483 .poll
= v4l_sn9c20x_poll
,
1484 .mmap
= v4l_sn9c20x_mmap
,
1485 .ioctl
= v4l_sn9c20x_ioctl
,
1486 #ifdef CONFIG_COMPAT
1487 .compat_ioctl
= v4l_compat_ioctl32
,
1489 #if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 29)