Allow AWB and blue/red gain to be set at the same time
[microdia.git] / sn9c20x-v4l2.c
blob6b8918fa60554cff86a3136e1b1edcfdc97d0e9d
1 /**
2 * @file sn9c20x-v4l2.c
3 * @author Nicolas VIVIEN
4 * @date 2008-02-01
6 * @brief V4L2 interface and functions
8 * @note Copyright (C) Nicolas VIVIEN
10 * @par Licences
12 * This program is free software; you can redistribute it and/or modify
13 * it under the terms of the GNU General Public License as published by
14 * the Free Software Foundation; either version 2 of the License, or
15 * any later version.
17 * This program is distributed in the hope that it will be useful,
18 * but WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 * GNU General Public License for more details.
22 * You should have received a copy of the GNU General Public License
23 * along with this program; if not, write to the Free Software
24 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
27 #include <linux/module.h>
28 #include <linux/init.h>
29 #include <linux/kernel.h>
30 #include <linux/version.h>
31 #include <linux/errno.h>
32 #include <linux/slab.h>
33 #include <linux/kref.h>
34 #include <linux/vmalloc.h>
35 #include <linux/usb.h>
36 #include <linux/mm.h>
38 #include "sn9c20x.h"
39 #include "sn9c20x-bridge.h"
41 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27)
42 #include <media/v4l2-ioctl.h>
43 #endif
45 #if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 29)
46 static struct file_operations v4l_sn9c20x_fops;
47 #else
48 static struct v4l2_file_operations v4l_sn9c20x_fops;
49 #endif
50 /**
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,
58 .name = "Brightness",
59 .minimum = 0,
60 .maximum = 0xff,
61 .step = 1,
64 .id = V4L2_CID_GAMMA,
65 .type = V4L2_CTRL_TYPE_INTEGER,
66 .name = "Gamma",
67 .minimum = 0,
68 .maximum = 0xff,
69 .step = 1,
73 .id = V4L2_CID_SATURATION,
74 .type = V4L2_CTRL_TYPE_INTEGER,
75 .name = "Saturation",
76 .minimum = 0,
77 .maximum = 0xff,
78 .step = 1,
82 .id = V4L2_CID_CONTRAST,
83 .type = V4L2_CTRL_TYPE_INTEGER,
84 .name = "Contrast",
85 .minimum = 0,
86 .maximum = 0xff,
87 .step = 1,
90 .id = V4L2_CID_EXPOSURE,
91 .type = V4L2_CTRL_TYPE_INTEGER,
92 .name = "Exposure",
93 .minimum = 0,
94 .maximum = 0xff,
95 .step = 1,
98 .id = V4L2_CID_GAIN,
99 .type = V4L2_CTRL_TYPE_INTEGER,
100 .name = "Gain",
101 .minimum = 0,
102 .maximum = 0xff,
103 .step = 1,
106 .id = V4L2_CID_HFLIP,
107 .type = V4L2_CTRL_TYPE_BOOLEAN,
108 .name = "Horizontal flip",
109 .minimum = 0,
110 .maximum = 1,
111 .step = 1,
114 .id = V4L2_CID_VFLIP,
115 .type = V4L2_CTRL_TYPE_BOOLEAN,
116 .name = "Vertical flip",
117 .minimum = 0,
118 .maximum = 1,
119 .step = 1,
122 .id = V4L2_CID_SHARPNESS,
123 .type = V4L2_CTRL_TYPE_INTEGER,
124 .name = "Sharpness",
125 .minimum = 0,
126 .maximum = 0x3f,
127 .step = 1,
130 .id = V4L2_CID_RED_BALANCE,
131 .type = V4L2_CTRL_TYPE_INTEGER,
132 .name = "Red Balance",
133 .minimum = 0,
134 .maximum = 0x7f,
135 .step = 1,
138 .id = V4L2_CID_BLUE_BALANCE,
139 .type = V4L2_CTRL_TYPE_INTEGER,
140 .name = "Blue Balance",
141 .minimum = 0,
142 .maximum = 0x7f,
143 .step = 1,
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
147 * instead.
150 .id = V4L2_CID_EXPOSURE_AUTO,
151 .type = V4L2_CTRL_TYPE_BOOLEAN,
152 .name = "Automatic exposure control",
153 .minimum = 0,
154 .maximum = 1,
155 .step = 1,
158 .id = V4L2_CID_AUTOGAIN,
159 .type = V4L2_CTRL_TYPE_BOOLEAN,
160 .name = "Automatic gain control",
161 .minimum = 0,
162 .maximum = 1,
163 .step = 1,
166 .id = V4L2_CID_AUTO_WHITE_BALANCE,
167 .type = V4L2_CTRL_TYPE_BOOLEAN,
168 .name = "Automatic whitbalance control",
169 .minimum = 0,
170 .maximum = 1,
171 .step = 1,
175 void v4l2_set_control_default(struct usb_sn9c20x *dev, __u32 ctrl, __u16 value)
177 int i;
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,
182 ctrl,
183 value);
184 break;
189 void v4l_add_jpegheader(struct usb_sn9c20x *dev, __u8 *buffer,
190 __u32 buffer_size)
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
288 * @param file
290 * @return 0 or negative error code
293 int v4l_get_privileges(struct file *file)
295 struct usb_sn9c20x *dev;
296 int ret = 0;
298 dev = video_get_drvdata(video_devdata(file));
300 if (dev->owner == file)
301 return 0;
303 mutex_lock(&open_lock);
304 if (dev->owner != NULL) {
305 ret = -EBUSY;
306 goto done;
308 dev->owner = file;
309 done:
310 mutex_unlock(&open_lock);
311 return ret;
315 * @brief Check whether there are V4L privileges
317 * @param file
319 * @return 0 or 1
322 int v4l_has_privileges(struct file *file)
324 struct usb_sn9c20x *dev;
325 int ret = 0;
327 dev = video_get_drvdata(video_devdata(file));
329 if (dev->owner == file)
330 ret = 1;
332 return ret;
336 * @brief Drop V4L privileges
338 * @param file
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)
348 dev->owner = NULL;
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)
362 int ret;
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);
368 dev->mode = mode;
369 return 0;
372 if (dev->mode != SN9C20X_MODE_IDLE)
373 return -EBUSY;
375 if (sn9c20x_queue_enable(&dev->queue, 1) < 0)
376 return -EBUSY;
378 ret = usb_sn9c20x_init_urbs(dev);
380 if (ret)
381 return ret;
383 sn9c20x_enable_video(dev, 1);
384 dev->mode = mode;
386 if (dev->vsettings.format.pixelformat == V4L2_PIX_FMT_JPEG)
387 dev->queue.flags &= ~SN9C20X_QUEUE_DROP_INCOMPLETE;
388 else
389 dev->queue.flags |= SN9C20X_QUEUE_DROP_INCOMPLETE;
391 return 0;
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)
406 #else
407 static int v4l_sn9c20x_open(struct file *fp)
408 #endif
410 int ret = 0;
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);
425 return ret;
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)
441 #else
442 static int v4l_sn9c20x_release(struct file *fp)
443 #endif
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);
466 return 0;
471 * @param fp File pointer
473 * @retval buf Buffer in user space
474 * @retval count
475 * @retval f_pos
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)
486 int i, ret;
487 int nbuffers;
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 (ret < 0)
495 return ret;
497 if (dev->mode != SN9C20X_MODE_IDLE &&
498 dev->mode != SN9C20X_MODE_READ)
499 return -EBUSY;
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);
506 if (nbuffers < 0)
507 return nbuffers;
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);
515 if (ret < 0)
516 return ret;
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);
524 if (ret < 0)
525 return ret;
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,
530 buffer.bytesused);
531 buffer.bytesused += 589;
534 dev->queue.read_buffer = &dev->queue.buffer[buffer.index];
535 } else {
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))
541 return -EFAULT;
543 *f_pos += count;
544 if (*f_pos >= buffer.bytesused) {
545 dev->queue.read_buffer = NULL;
546 sn9c20x_queue_buffer(&dev->queue, &buffer);
547 *f_pos = 0;
549 return count;
554 * @param fp File pointer
555 * @param wait
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)
572 return -EFAULT;
574 return sn9c20x_queue_poll(&dev->queue, fp, wait);
578 * @param vma
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++;
589 * @param vma
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
609 * @brief Memory map
611 * This function permits to map a memory space.
613 static int v4l_sn9c20x_mmap(struct file *fp, struct vm_area_struct *vma)
615 struct page *page;
616 unsigned long addr, start, size;
617 unsigned int i;
618 int ret = 0;
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)
637 break;
640 if (i == dev->queue.count || size != dev->queue.buf_size) {
641 ret = -EINVAL;
642 goto done;
645 vma->vm_flags |= VM_IO;
647 addr = (unsigned long)dev->queue.mem + buffer->buf.m.offset;
648 while (size > 0) {
649 page = vmalloc_to_page((void *)addr);
650 ret = vm_insert_page(vma, start, page);
651 if (ret < 0)
652 goto done;
654 start += PAGE_SIZE;
655 addr += PAGE_SIZE;
656 size -= PAGE_SIZE;
659 vma->vm_ops = &sn9c20x_vm_ops;
660 vma->vm_private_data = buffer;
661 sn9c20x_vm_open(vma);
662 done:
663 mutex_unlock(&dev->queue.mutex);
664 return ret;
668 * @param file
669 * @param priv
670 * @param cap
672 * @return 0
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));
692 return 0;
696 * @param file
697 * @param priv
698 * @param input
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);
708 if (input->index)
709 return -EINVAL;
711 strlcpy(input->name, "Webcam", sizeof(input->name));
712 input->type = V4L2_INPUT_TYPE_CAMERA;
713 input->std = 0;
715 return 0;
719 * @param file
720 * @param priv
721 * @param index
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);
730 if (index)
731 return -EINVAL;
733 return 0;
737 * @param file
738 * @param priv
739 * @param 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)
749 return -EBUSY;
751 if (index != 0)
752 return -EINVAL;
754 return 0;
758 * @param file
759 * @param priv
760 * @param ctrl
762 * @return 0 or negative error code
765 int sn9c20x_vidioc_queryctrl(struct file *file, void *priv,
766 struct v4l2_queryctrl *ctrl)
768 int i;
769 int ret = 0;
770 #ifdef V4L2_CTRL_FLAG_NEXT_CTRL
771 int min;
772 __u32 idnew, idlast;
773 #endif
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) {
782 min = 0;
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;
789 min = i;
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));
796 goto done;
797 } else {
798 goto error;
800 } else
801 #endif
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));
808 goto done;
813 error:
814 ret = -EINVAL;
815 done:
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;
823 return ret;
827 * @param file
828 * @param priv
829 * @param ctrl
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);
843 switch (ctrl->id) {
844 case V4L2_CID_BRIGHTNESS:
845 ctrl->value = dev->vsettings.brightness;
846 break;
848 case V4L2_CID_EXPOSURE:
849 ctrl->value = dev->vsettings.exposure;
850 break;
852 case V4L2_CID_GAIN:
853 ctrl->value = dev->vsettings.gain;
854 break;
856 case V4L2_CID_GAMMA:
857 ctrl->value = dev->vsettings.gamma;
858 break;
860 case V4L2_CID_SATURATION:
861 ctrl->value = dev->vsettings.colour;
862 break;
864 case V4L2_CID_CONTRAST:
865 ctrl->value = dev->vsettings.contrast;
866 break;
868 case V4L2_CID_HFLIP:
869 ctrl->value = dev->vsettings.hflip;
870 break;
872 case V4L2_CID_VFLIP:
873 ctrl->value = dev->vsettings.vflip;
874 break;
876 case V4L2_CID_SHARPNESS:
877 ctrl->value = dev->vsettings.sharpness;
878 break;
880 case V4L2_CID_RED_BALANCE:
881 ctrl->value = dev->vsettings.red_gain;
882 break;
884 case V4L2_CID_BLUE_BALANCE:
885 ctrl->value = dev->vsettings.blue_gain;
886 break;
888 case V4L2_CID_EXPOSURE_AUTO:
889 ctrl->value = dev->vsettings.auto_exposure;
890 break;
892 case V4L2_CID_AUTOGAIN:
893 ctrl->value = dev->vsettings.auto_gain;
894 break;
896 case V4L2_CID_AUTO_WHITE_BALANCE:
897 ctrl->value = dev->vsettings.auto_whitebalance;
898 break;
900 default:
901 return -EINVAL;
903 return 0;
907 * @brief Apply v4l2 settings on camera
909 * @param file
910 * @param priv
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 return -EBUSY;
930 return sn9c20x_set_camera_control(dev,
931 ctrl->id,
932 ctrl->value);
936 * @param file
937 * @param priv
938 * @param fmt
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)
953 return -EINVAL;
955 fmt->flags = 0;
956 fmt->pixelformat = sn9c20x_fmts[fmt->index].pix_fmt;
958 if (fmt->pixelformat == V4L2_PIX_FMT_JPEG && dev->jpeg == 0)
959 return -EINVAL;
961 memcpy(fmt->description, sn9c20x_fmts[fmt->index].desc, 32);
963 return 0;
967 * @param file
968 * @param priv
969 * @param fmt
971 * @return 0 or negative error code
974 int sn9c20x_vidioc_try_fmt_cap(struct file *file, void *priv,
975 struct v4l2_format *fmt)
977 int index;
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)
985 return -EINVAL;
987 if (fmt->fmt.pix.pixelformat == V4L2_PIX_FMT_JPEG && dev->jpeg == 0)
988 return -EINVAL;
990 for (index = 0; index < SN9C20X_N_FMTS; index++)
991 if (sn9c20x_fmts[index].pix_fmt == fmt->fmt.pix.pixelformat)
992 break;
994 if (index >= SN9C20X_N_FMTS)
995 return -EINVAL;
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;
1009 return 0;
1013 * @param file
1014 * @param priv
1015 * @param fmt
1017 * @return 0
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));
1032 return 0;
1036 * @param file
1037 * @param priv
1038 * @param fmt
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;
1047 int ret;
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)
1054 return -EBUSY;
1056 if (sn9c20x_queue_streaming(&dev->queue))
1057 return -EBUSY;
1059 ret = sn9c20x_vidioc_try_fmt_cap(file, priv, fmt);
1060 if (ret)
1061 return -EINVAL;
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));
1067 return 0;
1071 * @param file
1072 * @param priv
1073 * @param request
1075 * @return 0 or negative error code
1078 int sn9c20x_vidioc_reqbufs(struct file *file, void *priv,
1079 struct v4l2_requestbuffers *request)
1081 int ret = 0;
1082 struct usb_sn9c20x *dev;
1084 dev = video_get_drvdata(priv);
1086 if (v4l_get_privileges(file) < 0) {
1087 ret = -EBUSY;
1088 goto done;
1091 if (request->memory != V4L2_MEMORY_MMAP ||
1092 request->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) {
1093 ret = -EINVAL;
1094 goto done;
1097 if (sn9c20x_queue_streaming(&dev->queue)) {
1098 ret = -EBUSY;
1099 goto done;
1102 ret = sn9c20x_alloc_buffers(&dev->queue, request->count,
1103 dev->vsettings.format.sizeimage);
1104 if (ret < 0)
1105 goto done;
1107 request->count = ret;
1108 ret = 0;
1109 UDIA_DEBUG("Buffers Allocated %d\n", request->count);
1110 done:
1111 return ret;
1115 * @param file
1116 * @param priv
1117 * @param buffer
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)
1133 return -EINVAL;
1135 if (!v4l_has_privileges(file))
1136 return -EBUSY;
1138 return sn9c20x_query_buffer(&dev->queue, buffer);
1142 * @param file
1143 * @param priv
1144 * @param 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))
1159 return -EBUSY;
1161 return sn9c20x_queue_buffer(&dev->queue, buffer);
1165 * @param file
1166 * @param priv
1167 * @param 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;
1176 int ret = 0;
1178 dev = video_get_drvdata(priv);
1180 UDIA_DEBUG("VIDIOC_DQBUF\n");
1182 if (!v4l_has_privileges(file))
1183 return -EBUSY;
1185 ret = sn9c20x_dequeue_buffer(&dev->queue, buffer,
1186 file->f_flags & O_NONBLOCK);
1187 if (ret < 0)
1188 return ret;
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,
1193 buffer->bytesused);
1194 buffer->bytesused += 589;
1197 dev_sn9c20x_call_constantly(dev);
1199 return ret;
1203 * @param file
1204 * @param priv
1205 * @param type
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))
1220 return -EBUSY;
1222 if (dev->mode != SN9C20X_MODE_IDLE)
1223 return -EBUSY;
1225 return v4l2_enable_video(dev, SN9C20X_MODE_STREAM);
1229 * @param file
1230 * @param priv
1231 * @param type
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))
1246 return -EBUSY;
1248 return v4l2_enable_video(dev, SN9C20X_MODE_IDLE);
1252 * @param file
1253 * @param priv
1254 * @param param
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)
1268 return -EINVAL;
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;
1277 return 0;
1281 * @param file
1282 * @param priv
1283 * @param param
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))
1296 return -EBUSY;
1298 if (param->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
1299 return -EINVAL;
1301 return 0;
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)
1319 #else
1320 static long v4l_sn9c20x_ioctl(struct file *fp,
1321 unsigned int cmd, unsigned long arg)
1322 #endif
1324 int err;
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)
1334 return -EFAULT;
1336 #if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 29)
1337 err = video_ioctl2(inode, fp, cmd, arg);
1338 #else
1339 err = video_ioctl2(fp, cmd, arg);
1340 #endif
1342 return err;
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,
1368 #endif
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)
1381 int err;
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;
1389 #else
1390 dev->vdev->parent = &dev->interface->dev;
1391 #endif
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;
1421 #else
1422 dev->vdev->ioctl_ops = &sn9c20x_v4l2_ioctl_ops;
1423 #endif
1425 video_set_drvdata(dev->vdev, dev);
1427 sn9c20x_queue_init(&dev->queue);
1429 err = video_register_device(dev->vdev, VFL_TYPE_GRABBER, -1);
1431 if (err)
1432 UDIA_ERROR("Video register fail !\n");
1433 else
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),
1438 dev->vdev->minor);
1440 return err;
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);
1461 return 0;
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 = {
1473 #else
1474 static struct v4l2_file_operations v4l_sn9c20x_fops = {
1475 #endif
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,
1485 #endif
1486 #if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 29)
1487 .llseek = no_llseek
1488 #endif