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
);
494 if (unlikely(ret
< 0))
497 if (unlikely(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
)) {
930 return sn9c20x_set_camera_control(dev
,
940 * @return 0 or negative error code
943 int sn9c20x_vidioc_enum_fmt_cap(struct file
*file
, void *priv
,
944 struct v4l2_fmtdesc
*fmt
)
946 struct usb_sn9c20x
*dev
;
948 dev
= video_get_drvdata(priv
);
950 UDIA_DEBUG("VIDIOC_ENUM_FMT %d\n", fmt
->index
);
952 if (fmt
->index
>= SN9C20X_N_FMTS
)
956 fmt
->pixelformat
= sn9c20x_fmts
[fmt
->index
].pix_fmt
;
958 if (fmt
->pixelformat
== V4L2_PIX_FMT_JPEG
&& dev
->jpeg
== 0)
961 memcpy(fmt
->description
, sn9c20x_fmts
[fmt
->index
].desc
, 32);
971 * @return 0 or negative error code
974 int sn9c20x_vidioc_try_fmt_cap(struct file
*file
, void *priv
,
975 struct v4l2_format
*fmt
)
978 struct usb_sn9c20x
*dev
;
980 dev
= video_get_drvdata(priv
);
981 UDIA_DEBUG("TRY FMT %d\n", fmt
->type
);
983 /* when this code is used prevents mplayer from setting outfmt
984 if(fmt->fmt.pix.field != V4L2_FIELD_NONE)
987 if (fmt
->fmt
.pix
.pixelformat
== V4L2_PIX_FMT_JPEG
&& dev
->jpeg
== 0)
990 for (index
= 0; index
< SN9C20X_N_FMTS
; index
++)
991 if (sn9c20x_fmts
[index
].pix_fmt
== fmt
->fmt
.pix
.pixelformat
)
994 if (index
>= SN9C20X_N_FMTS
)
997 sn9c20x_get_closest_resolution(dev
, &fmt
->fmt
.pix
.width
,
998 &fmt
->fmt
.pix
.height
);
1000 fmt
->fmt
.pix
.bytesperline
= fmt
->fmt
.pix
.width
*
1001 sn9c20x_fmts
[index
].depth
/ 8;
1003 fmt
->fmt
.pix
.sizeimage
= fmt
->fmt
.pix
.height
*
1004 fmt
->fmt
.pix
.bytesperline
;
1006 fmt
->fmt
.pix
.colorspace
= V4L2_COLORSPACE_SRGB
;
1007 fmt
->fmt
.pix
.priv
= index
;
1020 int sn9c20x_vidioc_g_fmt_cap(struct file
*file
, void *priv
,
1021 struct v4l2_format
*fmt
)
1023 struct usb_sn9c20x
*dev
;
1025 dev
= video_get_drvdata(priv
);
1027 UDIA_DEBUG("GET FMT %d\n", fmt
->type
);
1029 memcpy(&(fmt
->fmt
.pix
), &(dev
->vsettings
.format
), sizeof(fmt
->fmt
.pix
));
1040 * @return 0 or negative error code
1043 int sn9c20x_vidioc_s_fmt_cap(struct file
*file
, void *priv
,
1044 struct v4l2_format
*fmt
)
1046 struct usb_sn9c20x
*dev
;
1049 dev
= video_get_drvdata(priv
);
1051 UDIA_DEBUG("SET FMT %d : %d\n", fmt
->type
, fmt
->fmt
.pix
.pixelformat
);
1053 if (v4l_get_privileges(file
) < 0)
1056 if (sn9c20x_queue_streaming(&dev
->queue
))
1059 ret
= sn9c20x_vidioc_try_fmt_cap(file
, priv
, fmt
);
1063 sn9c20x_set_resolution(dev
, fmt
->fmt
.pix
.width
, fmt
->fmt
.pix
.height
);
1064 sn9c20x_set_format(dev
, fmt
->fmt
.pix
.pixelformat
);
1065 memcpy(&(dev
->vsettings
.format
), &(fmt
->fmt
.pix
), sizeof(fmt
->fmt
.pix
));
1075 * @return 0 or negative error code
1078 int sn9c20x_vidioc_reqbufs(struct file
*file
, void *priv
,
1079 struct v4l2_requestbuffers
*request
)
1082 struct usb_sn9c20x
*dev
;
1084 dev
= video_get_drvdata(priv
);
1086 if (v4l_get_privileges(file
) < 0) {
1091 if (request
->memory
!= V4L2_MEMORY_MMAP
||
1092 request
->type
!= V4L2_BUF_TYPE_VIDEO_CAPTURE
) {
1097 if (sn9c20x_queue_streaming(&dev
->queue
)) {
1102 ret
= sn9c20x_alloc_buffers(&dev
->queue
, request
->count
,
1103 dev
->vsettings
.format
.sizeimage
);
1107 request
->count
= ret
;
1109 UDIA_DEBUG("Buffers Allocated %d\n", request
->count
);
1119 * @return 0 or negative error code
1122 int sn9c20x_vidioc_querybuf(struct file
*file
, void *priv
,
1123 struct v4l2_buffer
*buffer
)
1125 struct usb_sn9c20x
*dev
;
1127 dev
= video_get_drvdata(priv
);
1129 UDIA_DEBUG("QUERY BUFFERS %d %d\n", buffer
->index
, dev
->queue
.count
);
1131 if (buffer
->memory
!= V4L2_MEMORY_MMAP
||
1132 buffer
->type
!= V4L2_BUF_TYPE_VIDEO_CAPTURE
)
1135 if (!v4l_has_privileges(file
))
1138 return sn9c20x_query_buffer(&dev
->queue
, buffer
);
1146 * @return 0 or negative error code
1149 int sn9c20x_vidioc_qbuf(struct file
*file
, void *priv
,
1150 struct v4l2_buffer
*buffer
)
1152 struct usb_sn9c20x
*dev
;
1154 dev
= video_get_drvdata(priv
);
1156 UDIA_DEBUG("VIDIOC_QBUF\n");
1158 if (!v4l_has_privileges(file
))
1161 return sn9c20x_queue_buffer(&dev
->queue
, buffer
);
1169 * @return 0 or negative error code
1172 int sn9c20x_vidioc_dqbuf(struct file
*file
, void *priv
,
1173 struct v4l2_buffer
*buffer
)
1175 struct usb_sn9c20x
*dev
;
1178 dev
= video_get_drvdata(priv
);
1180 UDIA_DEBUG("VIDIOC_DQBUF\n");
1182 if (!v4l_has_privileges(file
))
1185 ret
= sn9c20x_dequeue_buffer(&dev
->queue
, buffer
,
1186 file
->f_flags
& O_NONBLOCK
);
1190 if (dev
->vsettings
.format
.pixelformat
== V4L2_PIX_FMT_JPEG
) {
1191 UDIA_DEBUG("Adding JPEG Header\n");
1192 v4l_add_jpegheader(dev
, dev
->queue
.mem
+ buffer
->m
.offset
,
1194 buffer
->bytesused
+= 589;
1197 dev_sn9c20x_call_constantly(dev
);
1207 * @return 0 or negative error code
1210 int sn9c20x_vidioc_streamon(struct file
*file
, void *priv
,
1211 enum v4l2_buf_type type
)
1213 struct usb_sn9c20x
*dev
;
1215 dev
= video_get_drvdata(priv
);
1217 UDIA_DEBUG("VIDIOC_STREAMON\n");
1219 if (!v4l_has_privileges(file
))
1222 if (dev
->mode
!= SN9C20X_MODE_IDLE
)
1225 return v4l2_enable_video(dev
, SN9C20X_MODE_STREAM
);
1233 * @return 0 or negative error code
1236 int sn9c20x_vidioc_streamoff(struct file
*file
, void *priv
,
1237 enum v4l2_buf_type type
)
1239 struct usb_sn9c20x
*dev
;
1241 dev
= video_get_drvdata(priv
);
1243 UDIA_DEBUG("VIDIOC_STREAMOFF\n");
1245 if (!v4l_has_privileges(file
))
1248 return v4l2_enable_video(dev
, SN9C20X_MODE_IDLE
);
1256 * @return 0 or negative error code
1259 int sn9c20x_vidioc_g_param(struct file
*file
, void *priv
,
1260 struct v4l2_streamparm
*param
)
1262 struct usb_sn9c20x
*dev
;
1265 dev
= video_get_drvdata(priv
);
1267 if (param
->type
!= V4L2_BUF_TYPE_VIDEO_CAPTURE
)
1270 param
->parm
.capture
.capability
= 0;
1271 param
->parm
.capture
.capturemode
= 0;
1272 param
->parm
.capture
.timeperframe
.numerator
= 1;
1273 param
->parm
.capture
.timeperframe
.denominator
= 30;
1274 param
->parm
.capture
.readbuffers
= 2;
1275 param
->parm
.capture
.extendedmode
= 0;
1285 * @return 0 or negative error code
1288 int sn9c20x_vidioc_s_param(struct file
*file
, void *priv
,
1289 struct v4l2_streamparm
*param
)
1291 struct usb_sn9c20x
*dev
;
1293 dev
= video_get_drvdata(priv
);
1295 if (v4l_get_privileges(file
))
1298 if (param
->type
!= V4L2_BUF_TYPE_VIDEO_CAPTURE
)
1305 * @param inode Inode pointer
1306 * @param fp File pointer
1307 * @param cmd Command
1308 * @param arg Arguements of the command
1310 * @returns 0 if all is OK
1312 * @brief Manage IOCTL
1314 * This function permits to manage all the IOCTL from the application.
1316 #if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 29)
1317 static int v4l_sn9c20x_ioctl(struct inode
*inode
, struct file
*fp
,
1318 unsigned int cmd
, unsigned long arg
)
1320 static long v4l_sn9c20x_ioctl(struct file
*fp
,
1321 unsigned int cmd
, unsigned long arg
)
1325 struct usb_sn9c20x
*dev
;
1326 struct video_device
*vdev
;
1328 vdev
= video_devdata(fp
);
1329 dev
= video_get_drvdata(video_devdata(fp
));
1331 UDIA_DEBUG("v4l_sn9c20x_ioctl %02X\n", (unsigned char) cmd
);
1333 if (dev
== NULL
|| vdev
== NULL
)
1336 #if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 29)
1337 err
= video_ioctl2(inode
, fp
, cmd
, arg
);
1339 err
= video_ioctl2(fp
, cmd
, arg
);
1346 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27)
1347 static const struct v4l2_ioctl_ops sn9c20x_v4l2_ioctl_ops
= {
1348 .vidioc_querycap
= sn9c20x_vidioc_querycap
,
1349 .vidioc_enum_fmt_vid_cap
= sn9c20x_vidioc_enum_fmt_cap
,
1350 .vidioc_try_fmt_vid_cap
= sn9c20x_vidioc_try_fmt_cap
,
1351 .vidioc_s_fmt_vid_cap
= sn9c20x_vidioc_s_fmt_cap
,
1352 .vidioc_g_fmt_vid_cap
= sn9c20x_vidioc_g_fmt_cap
,
1353 .vidioc_enum_input
= sn9c20x_vidioc_enum_input
,
1354 .vidioc_g_input
= sn9c20x_vidioc_g_input
,
1355 .vidioc_s_input
= sn9c20x_vidioc_s_input
,
1356 .vidioc_streamon
= sn9c20x_vidioc_streamon
,
1357 .vidioc_streamoff
= sn9c20x_vidioc_streamoff
,
1358 .vidioc_queryctrl
= sn9c20x_vidioc_queryctrl
,
1359 .vidioc_g_ctrl
= sn9c20x_vidioc_g_ctrl
,
1360 .vidioc_s_ctrl
= sn9c20x_vidioc_s_ctrl
,
1361 .vidioc_g_parm
= sn9c20x_vidioc_g_param
,
1362 .vidioc_s_parm
= sn9c20x_vidioc_s_param
,
1363 .vidioc_reqbufs
= sn9c20x_vidioc_reqbufs
,
1364 .vidioc_qbuf
= sn9c20x_vidioc_qbuf
,
1365 .vidioc_dqbuf
= sn9c20x_vidioc_dqbuf
,
1366 .vidioc_querybuf
= sn9c20x_vidioc_querybuf
,
1371 * @param dev Device structure
1373 * @returns 0 if all is OK
1375 * @brief Register the video device
1377 * This function permits to register the USB device to the video device.
1379 int v4l_sn9c20x_register_video_device(struct usb_sn9c20x
*dev
)
1383 strcpy(dev
->vdev
->name
, DRIVER_DESC
);
1385 #if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 27)
1386 dev
->vdev
->dev
= &dev
->interface
->dev
;
1387 dev
->vdev
->owner
= THIS_MODULE
;
1388 dev
->vdev
->type
= VID_TYPE_CAPTURE
;
1390 dev
->vdev
->parent
= &dev
->interface
->dev
;
1392 dev
->vdev
->current_norm
= 0;
1393 dev
->vdev
->tvnorms
= 0;
1394 dev
->vdev
->fops
= &v4l_sn9c20x_fops
;
1395 dev
->vdev
->release
= video_device_release
;
1396 dev
->vdev
->minor
= -1;
1398 if (log_level
& SN9C20X_DEBUG
)
1399 dev
->vdev
->debug
= V4L2_DEBUG_IOCTL_ARG
;
1401 #if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 27)
1402 dev
->vdev
->vidioc_querycap
= sn9c20x_vidioc_querycap
;
1403 dev
->vdev
->vidioc_enum_fmt_cap
= sn9c20x_vidioc_enum_fmt_cap
;
1404 dev
->vdev
->vidioc_try_fmt_cap
= sn9c20x_vidioc_try_fmt_cap
;
1405 dev
->vdev
->vidioc_s_fmt_cap
= sn9c20x_vidioc_s_fmt_cap
;
1406 dev
->vdev
->vidioc_g_fmt_cap
= sn9c20x_vidioc_g_fmt_cap
;
1407 dev
->vdev
->vidioc_enum_input
= sn9c20x_vidioc_enum_input
;
1408 dev
->vdev
->vidioc_g_input
= sn9c20x_vidioc_g_input
;
1409 dev
->vdev
->vidioc_s_input
= sn9c20x_vidioc_s_input
;
1410 dev
->vdev
->vidioc_streamon
= sn9c20x_vidioc_streamon
;
1411 dev
->vdev
->vidioc_streamoff
= sn9c20x_vidioc_streamoff
;
1412 dev
->vdev
->vidioc_queryctrl
= sn9c20x_vidioc_queryctrl
;
1413 dev
->vdev
->vidioc_g_ctrl
= sn9c20x_vidioc_g_ctrl
;
1414 dev
->vdev
->vidioc_s_ctrl
= sn9c20x_vidioc_s_ctrl
;
1415 dev
->vdev
->vidioc_g_parm
= sn9c20x_vidioc_g_param
;
1416 dev
->vdev
->vidioc_s_parm
= sn9c20x_vidioc_s_param
;
1417 dev
->vdev
->vidioc_reqbufs
= sn9c20x_vidioc_reqbufs
;
1418 dev
->vdev
->vidioc_qbuf
= sn9c20x_vidioc_qbuf
;
1419 dev
->vdev
->vidioc_dqbuf
= sn9c20x_vidioc_dqbuf
;
1420 dev
->vdev
->vidioc_querybuf
= sn9c20x_vidioc_querybuf
;
1422 dev
->vdev
->ioctl_ops
= &sn9c20x_v4l2_ioctl_ops
;
1425 video_set_drvdata(dev
->vdev
, dev
);
1427 sn9c20x_queue_init(&dev
->queue
);
1429 err
= video_register_device(dev
->vdev
, VFL_TYPE_GRABBER
, -1);
1432 UDIA_ERROR("Video register fail !\n");
1434 UDIA_INFO("Webcam device %04X:%04X is now controlling video "
1435 "device /dev/video%d\n",
1436 le16_to_cpu(dev
->udev
->descriptor
.idVendor
),
1437 le16_to_cpu(dev
->udev
->descriptor
.idProduct
),
1445 * @param dev Device structure
1447 * @returns 0 if all is OK
1449 * @brief Unregister the video device
1451 * This function permits to unregister the video device.
1453 int v4l_sn9c20x_unregister_video_device(struct usb_sn9c20x
*dev
)
1455 UDIA_INFO("SN9C20X USB 2.0 Webcam releases control of video "
1456 "device /dev/video%d\n", dev
->vdev
->minor
);
1458 video_set_drvdata(dev
->vdev
, NULL
);
1459 video_unregister_device(dev
->vdev
);
1466 * @var v4l_sn9c20x_fops
1468 * This variable contains some callback
1471 #if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 29)
1472 static struct file_operations v4l_sn9c20x_fops
= {
1474 static struct v4l2_file_operations v4l_sn9c20x_fops
= {
1476 .owner
= THIS_MODULE
,
1477 .open
= v4l_sn9c20x_open
,
1478 .release
= v4l_sn9c20x_release
,
1479 .read
= v4l_sn9c20x_read
,
1480 .poll
= v4l_sn9c20x_poll
,
1481 .mmap
= v4l_sn9c20x_mmap
,
1482 .ioctl
= v4l_sn9c20x_ioctl
,
1483 #ifdef CONFIG_COMPAT
1484 .compat_ioctl
= v4l_compat_ioctl32
,
1486 #if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 29)