Fix SXGA support to enforce bayer format
[microdia.git] / sn9c20x-v4l2.c
blob7a01657c1e3ab5002d23bd675d2f92b215287165
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_HUE,
83 .type = V4L2_CTRL_TYPE_INTEGER,
84 .name = "Hue",
85 .minimum = -180,
86 .maximum = 180,
87 .step = 1,
91 .id = V4L2_CID_CONTRAST,
92 .type = V4L2_CTRL_TYPE_INTEGER,
93 .name = "Contrast",
94 .minimum = 0,
95 .maximum = 0xff,
96 .step = 1,
99 .id = V4L2_CID_EXPOSURE,
100 .type = V4L2_CTRL_TYPE_INTEGER,
101 .name = "Exposure",
102 .minimum = 0,
103 .maximum = 0xff,
104 .step = 1,
107 .id = V4L2_CID_GAIN,
108 .type = V4L2_CTRL_TYPE_INTEGER,
109 .name = "Gain",
110 .minimum = 0,
111 .maximum = 0xff,
112 .step = 1,
115 .id = V4L2_CID_HFLIP,
116 .type = V4L2_CTRL_TYPE_BOOLEAN,
117 .name = "Horizontal flip",
118 .minimum = 0,
119 .maximum = 1,
120 .step = 1,
123 .id = V4L2_CID_VFLIP,
124 .type = V4L2_CTRL_TYPE_BOOLEAN,
125 .name = "Vertical flip",
126 .minimum = 0,
127 .maximum = 1,
128 .step = 1,
131 .id = V4L2_CID_SHARPNESS,
132 .type = V4L2_CTRL_TYPE_INTEGER,
133 .name = "Sharpness",
134 .minimum = 0,
135 .maximum = 0x3f,
136 .step = 1,
139 .id = V4L2_CID_RED_BALANCE,
140 .type = V4L2_CTRL_TYPE_INTEGER,
141 .name = "Red Balance",
142 .minimum = 0,
143 .maximum = 0x7f,
144 .step = 1,
147 .id = V4L2_CID_BLUE_BALANCE,
148 .type = V4L2_CTRL_TYPE_INTEGER,
149 .name = "Blue Balance",
150 .minimum = 0,
151 .maximum = 0x7f,
152 .step = 1,
154 /* According to v4l2 specs auto exposure should be a 4 step value.
155 * This make little since for webcams however so a boolean is used
156 * instead.
159 .id = V4L2_CID_EXPOSURE_AUTO,
160 .type = V4L2_CTRL_TYPE_BOOLEAN,
161 .name = "Automatic exposure control",
162 .minimum = 0,
163 .maximum = 1,
164 .step = 1,
167 .id = V4L2_CID_AUTOGAIN,
168 .type = V4L2_CTRL_TYPE_BOOLEAN,
169 .name = "Automatic gain control",
170 .minimum = 0,
171 .maximum = 1,
172 .step = 1,
175 .id = V4L2_CID_AUTO_WHITE_BALANCE,
176 .type = V4L2_CTRL_TYPE_BOOLEAN,
177 .name = "Automatic whitbalance control",
178 .minimum = 0,
179 .maximum = 1,
180 .step = 1,
184 void v4l2_set_control_default(struct usb_sn9c20x *dev, __u32 ctrl, __u16 value)
186 int i;
187 for (i = 0; i < ARRAY_SIZE(sn9c20x_controls); i++) {
188 if (sn9c20x_controls[i].id == ctrl) {
189 sn9c20x_controls[i].default_value = value;
190 sn9c20x_set_camera_control(dev,
191 ctrl,
192 value);
193 break;
198 void v4l_add_jpegheader(struct usb_sn9c20x *dev, __u8 *buffer,
199 __u32 buffer_size)
201 static __u8 jpeg_header[589] = {
202 0xff, 0xd8, 0xff, 0xdb, 0x00, 0x84, 0x00, 0x06, 0x04, 0x05,
203 0x06, 0x05, 0x04, 0x06, 0x06, 0x05, 0x06, 0x07, 0x07, 0x06,
204 0x08, 0x0a, 0x10, 0x0a, 0x0a, 0x09, 0x09, 0x0a, 0x14, 0x0e,
205 0x0f, 0x0c, 0x10, 0x17, 0x14, 0x18, 0x18, 0x17, 0x14, 0x16,
206 0x16, 0x1a, 0x1d, 0x25, 0x1f, 0x1a, 0x1b, 0x23, 0x1c, 0x16,
207 0x16, 0x20, 0x2c, 0x20, 0x23, 0x26, 0x27, 0x29, 0x2a, 0x29,
208 0x19, 0x1f, 0x2d, 0x30, 0x2d, 0x28, 0x30, 0x25, 0x28, 0x29,
209 0x28, 0x01, 0x07, 0x07, 0x07, 0x0a, 0x08, 0x0a, 0x13, 0x0a,
210 0x0a, 0x13, 0x28, 0x1a, 0x16, 0x1a, 0x28, 0x28, 0x28, 0x28,
211 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28,
212 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28,
213 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28,
214 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28,
215 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0xff, 0xc4, 0x01, 0xa2,
216 0x00, 0x00, 0x01, 0x05, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
217 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x02,
218 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x01,
219 0x00, 0x03, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
220 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x03,
221 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x10, 0x00,
222 0x02, 0x01, 0x03, 0x03, 0x02, 0x04, 0x03, 0x05, 0x05, 0x04,
223 0x04, 0x00, 0x00, 0x01, 0x7d, 0x01, 0x02, 0x03, 0x00, 0x04,
224 0x11, 0x05, 0x12, 0x21, 0x31, 0x41, 0x06, 0x13, 0x51, 0x61,
225 0x07, 0x22, 0x71, 0x14, 0x32, 0x81, 0x91, 0xa1, 0x08, 0x23,
226 0x42, 0xb1, 0xc1, 0x15, 0x52, 0xd1, 0xf0, 0x24, 0x33, 0x62,
227 0x72, 0x82, 0x09, 0x0a, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x25,
228 0x26, 0x27, 0x28, 0x29, 0x2a, 0x34, 0x35, 0x36, 0x37, 0x38,
229 0x39, 0x3a, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a,
230 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5a, 0x63, 0x64,
231 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x73, 0x74, 0x75, 0x76,
232 0x77, 0x78, 0x79, 0x7a, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88,
233 0x89, 0x8a, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99,
234 0x9a, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, 0xa8, 0xa9, 0xaa,
235 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, 0xb8, 0xb9, 0xba, 0xc2,
236 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xd2, 0xd3,
237 0xd4, 0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda, 0xe1, 0xe2, 0xe3,
238 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, 0xea, 0xf1, 0xf2, 0xf3,
239 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9, 0xfa, 0x11, 0x00, 0x02,
240 0x01, 0x02, 0x04, 0x04, 0x03, 0x04, 0x07, 0x05, 0x04, 0x04,
241 0x00, 0x01, 0x02, 0x77, 0x00, 0x01, 0x02, 0x03, 0x11, 0x04,
242 0x05, 0x21, 0x31, 0x06, 0x12, 0x41, 0x51, 0x07, 0x61, 0x71,
243 0x13, 0x22, 0x32, 0x81, 0x08, 0x14, 0x42, 0x91, 0xa1, 0xb1,
244 0xc1, 0x09, 0x23, 0x33, 0x52, 0xf0, 0x15, 0x62, 0x72, 0xd1,
245 0x0a, 0x16, 0x24, 0x34, 0xe1, 0x25, 0xf1, 0x17, 0x18, 0x19,
246 0x1a, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x35, 0x36, 0x37, 0x38,
247 0x39, 0x3a, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a,
248 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5a, 0x63, 0x64,
249 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x73, 0x74, 0x75, 0x76,
250 0x77, 0x78, 0x79, 0x7a, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87,
251 0x88, 0x89, 0x8a, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98,
252 0x99, 0x9a, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, 0xa8, 0xa9,
253 0xaa, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, 0xb8, 0xb9, 0xba,
254 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xd2,
255 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda, 0xe2, 0xe3,
256 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, 0xea, 0xf2, 0xf3, 0xf4,
257 0xf5, 0xf6, 0xf7, 0xf8, 0xf9, 0xfa, 0xff, 0xc0, 0x00, 0x11,
258 0x08, 0x01, 0xe0, 0x02, 0x80, 0x03, 0x01, 0x21, 0x00, 0x02,
259 0x11, 0x01, 0x03, 0x11, 0x01, 0xff, 0xda, 0x00, 0x0c, 0x03,
260 0x01, 0x00, 0x02, 0x11, 0x03, 0x11, 0x00, 0x3f, 0x00
262 static __u8 qtable1[128] = {
263 0x0d, 0x08, 0x08, 0x0d, 0x08, 0x08, 0x0d, 0x0d,
264 0x0d, 0x0d, 0x11, 0x0d, 0x0d, 0x11, 0x15, 0x21,
265 0x15, 0x15, 0x11, 0x11, 0x15, 0x2a, 0x1d, 0x1d,
266 0x19, 0x21, 0x32, 0x2a, 0x32, 0x32, 0x2e, 0x2a,
267 0x2e, 0x2e, 0x36, 0x3a, 0x4b, 0x43, 0x36, 0x3a,
268 0x47, 0x3a, 0x2e, 0x2e, 0x43, 0x5c, 0x43, 0x47,
269 0x4f, 0x54, 0x58, 0x58, 0x58, 0x32, 0x3f, 0x60,
270 0x64, 0x5c, 0x54, 0x64, 0x4b, 0x54, 0x58, 0x54,
271 0x0d, 0x11, 0x11, 0x15, 0x11, 0x15, 0x26, 0x15,
272 0x15, 0x26, 0x54, 0x36, 0x2e, 0x36, 0x54, 0x54,
273 0x54, 0x54, 0x54, 0x54, 0x54, 0x54, 0x54, 0x54,
274 0x54, 0x54, 0x54, 0x54, 0x54, 0x54, 0x54, 0x54,
275 0x54, 0x54, 0x54, 0x54, 0x54, 0x54, 0x54, 0x54,
276 0x54, 0x54, 0x54, 0x54, 0x54, 0x54, 0x54, 0x54,
277 0x54, 0x54, 0x54, 0x54, 0x54, 0x54, 0x54, 0x54,
278 0x54, 0x54, 0x54, 0x54, 0x54, 0x54, 0x54, 0x54
281 jpeg_header[6] = 0x00;
282 jpeg_header[71] = 0x01;
283 memcpy(jpeg_header + 7, qtable1, 64);
284 memcpy(jpeg_header + 8 + 64, qtable1+64, 64);
285 jpeg_header[564] = dev->vsettings.format.width & 0xFF;
286 jpeg_header[563] = (dev->vsettings.format.width >> 8) & 0xFF;
287 jpeg_header[562] = dev->vsettings.format.height & 0xFF;
288 jpeg_header[561] = (dev->vsettings.format.height >> 8) & 0xFF;
289 jpeg_header[567] = 0x21;
291 memmove(buffer+589, buffer, buffer_size);
292 memcpy(buffer, jpeg_header, 589);
295 * @brief Get V4L privileges
297 * @param file
299 * @return 0 or negative error code
302 int v4l_get_privileges(struct file *file)
304 struct usb_sn9c20x *dev;
305 int ret = 0;
307 dev = video_get_drvdata(video_devdata(file));
309 if (dev->owner == file)
310 return 0;
312 mutex_lock(&open_lock);
313 if (dev->owner != NULL) {
314 ret = -EBUSY;
315 goto done;
317 dev->owner = file;
318 done:
319 mutex_unlock(&open_lock);
320 return ret;
324 * @brief Check whether there are V4L privileges
326 * @param file
328 * @return 0 or 1
331 int v4l_has_privileges(struct file *file)
333 struct usb_sn9c20x *dev;
334 int ret = 0;
336 dev = video_get_drvdata(video_devdata(file));
338 if (dev->owner == file)
339 ret = 1;
341 return ret;
345 * @brief Drop V4L privileges
347 * @param file
350 void v4l_drop_privileges(struct file *file)
352 struct usb_sn9c20x *dev;
354 dev = video_get_drvdata(video_devdata(file));
356 if (dev->owner == file)
357 dev->owner = NULL;
361 * @brief Enable video stream
363 * @param dev Pointer to device structure
364 * @param mode Mode for video stream
366 * @returns 0 or negative error value
369 int v4l2_enable_video(struct usb_sn9c20x *dev, int mode)
371 int ret;
373 if (mode == SN9C20X_MODE_IDLE) {
374 sn9c20x_enable_video(dev, 0);
375 usb_sn9c20x_uninit_urbs(dev, 1);
376 sn9c20x_queue_enable(&dev->queue, 0);
377 dev->mode = mode;
378 return 0;
381 if (dev->mode != SN9C20X_MODE_IDLE)
382 return -EBUSY;
384 if (sn9c20x_queue_enable(&dev->queue, 1) < 0)
385 return -EBUSY;
387 ret = usb_sn9c20x_init_urbs(dev);
389 if (ret)
390 return ret;
392 sn9c20x_enable_video(dev, 1);
393 dev->mode = mode;
395 if (dev->vsettings.format.pixelformat == V4L2_PIX_FMT_JPEG)
396 dev->queue.flags &= ~SN9C20X_QUEUE_DROP_INCOMPLETE;
397 else
398 dev->queue.flags |= SN9C20X_QUEUE_DROP_INCOMPLETE;
400 return 0;
404 * @param inode Pointer on an inode
405 * @param fp File pointer
407 * @returns 0 if all is OK
409 * @brief Open the video device
411 * This function permits to open a video device (/dev/videoX)
413 #if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 29)
414 static int v4l_sn9c20x_open(struct inode *inode, struct file *fp)
415 #else
416 static int v4l_sn9c20x_open(struct file *fp)
417 #endif
419 int ret = 0;
421 struct usb_sn9c20x *dev;
422 struct video_device *vdev;
424 mutex_lock(&open_lock);
426 vdev = video_devdata(fp);
427 dev = video_get_drvdata(video_devdata(fp));
429 fp->private_data = vdev;
431 kref_get(&dev->vopen);
433 mutex_unlock(&open_lock);
434 return ret;
439 * @param inode Pointer on inode
440 * @param fp File pointer
442 * @returns 0 if all is OK
444 * @brief Release an opened file.
446 * This function permits to release an opened file with the 'open' method.
448 #if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 29)
449 static int v4l_sn9c20x_release(struct inode *inode, struct file *fp)
450 #else
451 static int v4l_sn9c20x_release(struct file *fp)
452 #endif
454 struct usb_sn9c20x *dev;
455 struct video_device *vdev;
457 mutex_lock(&open_lock);
459 vdev = video_devdata(fp);
460 dev = video_get_drvdata(video_devdata(fp));
462 if (v4l_has_privileges(fp)) {
463 v4l2_enable_video(dev, SN9C20X_MODE_IDLE);
465 mutex_lock(&dev->queue.mutex);
466 sn9c20x_free_buffers(&dev->queue);
467 mutex_unlock(&dev->queue.mutex);
470 v4l_drop_privileges(fp);
472 kref_put(&dev->vopen, usb_sn9c20x_delete);
474 mutex_unlock(&open_lock);
475 return 0;
480 * @param fp File pointer
482 * @retval buf Buffer in user space
483 * @retval count
484 * @retval f_pos
486 * @returns Count value
488 * @brief Read the video device
490 * This function is called by the application is reading the video device.
492 static ssize_t v4l_sn9c20x_read(struct file *fp, char __user *buf,
493 size_t count, loff_t *f_pos)
495 int i, ret;
496 int nbuffers;
497 struct v4l2_buffer buffer;
498 struct usb_sn9c20x *dev;
500 dev = video_get_drvdata(video_devdata(fp));
502 ret = v4l_get_privileges(fp);
503 if (unlikely(ret < 0))
504 return ret;
506 if (unlikely(dev->mode != SN9C20X_MODE_IDLE &&
507 dev->mode != SN9C20X_MODE_READ))
508 return -EBUSY;
510 buffer.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
511 buffer.memory = V4L2_MEMORY_MMAP;
512 if (dev->mode == SN9C20X_MODE_IDLE) {
513 nbuffers = sn9c20x_alloc_buffers(&dev->queue, 2,
514 dev->vsettings.format.sizeimage);
515 if (nbuffers < 0)
516 return nbuffers;
518 for (i = 0; i < nbuffers; i++) {
519 buffer = dev->queue.buffer[i].buf;
520 sn9c20x_queue_buffer(&dev->queue, &buffer);
523 ret = v4l2_enable_video(dev, SN9C20X_MODE_READ);
524 if (ret < 0)
525 return ret;
528 dev_sn9c20x_call_constantly(dev);
530 if (dev->queue.read_buffer == NULL) {
531 ret = sn9c20x_dequeue_buffer(&dev->queue, &buffer,
532 fp->f_flags & O_NONBLOCK);
533 if (ret < 0)
534 return ret;
536 if (dev->vsettings.format.pixelformat == V4L2_PIX_FMT_JPEG) {
537 UDIA_DEBUG("Adding JPEG Header\n");
538 v4l_add_jpegheader(dev, dev->queue.mem + buffer.m.offset,
539 buffer.bytesused);
540 buffer.bytesused += 589;
543 dev->queue.read_buffer = &dev->queue.buffer[buffer.index];
544 } else {
545 buffer = dev->queue.read_buffer->buf;
548 count = min((size_t)(buffer.bytesused - *f_pos), count);
549 if (copy_to_user(buf, dev->queue.mem + buffer.m.offset + *f_pos, count))
550 return -EFAULT;
552 *f_pos += count;
553 if (*f_pos >= buffer.bytesused) {
554 dev->queue.read_buffer = NULL;
555 sn9c20x_queue_buffer(&dev->queue, &buffer);
556 *f_pos = 0;
558 return count;
563 * @param fp File pointer
564 * @param wait
566 * @returns 0 if all is OK
568 * @brief Polling function
570 static unsigned int v4l_sn9c20x_poll(struct file *fp, poll_table *wait)
572 struct usb_sn9c20x *dev;
573 struct video_device *vdev;
575 vdev = video_devdata(fp);
576 dev = video_get_drvdata(video_devdata(fp));
578 UDIA_STREAM("Poll\n");
580 if (vdev == NULL || dev == NULL)
581 return -EFAULT;
583 return sn9c20x_queue_poll(&dev->queue, fp, wait);
587 * @param vma
590 static void sn9c20x_vm_open(struct vm_area_struct *vma)
592 struct sn9c20x_buffer *buffer = vma->vm_private_data;
593 buffer->vma_use_count++;
598 * @param vma
601 static void sn9c20x_vm_close(struct vm_area_struct *vma)
603 struct sn9c20x_buffer *buffer = vma->vm_private_data;
604 buffer->vma_use_count--;
607 struct vm_operations_struct sn9c20x_vm_ops = {
608 .open = sn9c20x_vm_open,
609 .close = sn9c20x_vm_close
613 * @param fp File pointer
614 * @param vma VMA structure
616 * @returns 0 if all is OK
618 * @brief Memory map
620 * This function permits to map a memory space.
622 static int v4l_sn9c20x_mmap(struct file *fp, struct vm_area_struct *vma)
624 struct page *page;
625 unsigned long addr, start, size;
626 unsigned int i;
627 int ret = 0;
629 struct usb_sn9c20x *dev;
630 struct video_device *vdev;
631 struct sn9c20x_buffer *buffer = NULL;
633 vdev = video_devdata(fp);
634 dev = video_get_drvdata(video_devdata(fp));
636 UDIA_STREAM("mmap\n");
638 start = vma->vm_start;
639 size = vma->vm_end - vma->vm_start;
641 mutex_lock(&dev->queue.mutex);
643 for (i = 0; i < dev->queue.count; ++i) {
644 buffer = &dev->queue.buffer[i];
645 if ((buffer->buf.m.offset >> PAGE_SHIFT) == vma->vm_pgoff)
646 break;
649 if (i == dev->queue.count || size != dev->queue.buf_size) {
650 ret = -EINVAL;
651 goto done;
654 vma->vm_flags |= VM_IO;
656 addr = (unsigned long)dev->queue.mem + buffer->buf.m.offset;
657 while (size > 0) {
658 page = vmalloc_to_page((void *)addr);
659 ret = vm_insert_page(vma, start, page);
660 if (ret < 0)
661 goto done;
663 start += PAGE_SIZE;
664 addr += PAGE_SIZE;
665 size -= PAGE_SIZE;
668 vma->vm_ops = &sn9c20x_vm_ops;
669 vma->vm_private_data = buffer;
670 sn9c20x_vm_open(vma);
671 done:
672 mutex_unlock(&dev->queue.mutex);
673 return ret;
677 * @param file
678 * @param priv
679 * @param cap
681 * @return 0
684 int sn9c20x_vidioc_querycap(struct file *file, void *priv,
685 struct v4l2_capability *cap)
687 struct usb_sn9c20x *dev;
689 dev = video_get_drvdata(priv);
691 UDIA_DEBUG("VIDIOC_QUERYCAP\n");
693 strlcpy(cap->driver, "sn9c20x", sizeof(cap->driver));
694 cap->capabilities = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING
695 | V4L2_CAP_READWRITE;
696 cap->version = (__u32) DRIVER_VERSION_NUM,
697 strlcpy(cap->card, dev->vdev->name, sizeof(cap->card));
699 if (usb_make_path(dev->udev, cap->bus_info, sizeof(cap->bus_info)) < 0)
700 strlcpy(cap->bus_info, dev->vdev->name, sizeof(cap->bus_info));
701 return 0;
705 * @param file
706 * @param priv
707 * @param cap
709 * @return 0
712 int sn9c20x_vidioc_enum_framesizes(struct file *file, void *priv,
713 struct v4l2_frmsizeenum *size)
715 struct usb_sn9c20x *dev;
717 dev = video_get_drvdata(priv);
719 UDIA_DEBUG("ENUM_FRAMESIZES\n");
721 if (size->index >= SN9C20X_N_MODES)
722 return -EINVAL;
724 size->type = V4L2_FRMSIZE_TYPE_DISCRETE;
725 size->discrete.width = sn9c20x_modes[size->index].width;
726 size->discrete.height = sn9c20x_modes[size->index].height;
728 if (dev->camera.set_sxga_mode == NULL &&
729 (size->discrete.width > 640 && size->discrete.height > 480))
730 return -EINVAL;
732 if (size->pixel_format != V4L2_PIX_FMT_SBGGR8 &&
733 (size->discrete.width > 640 && size->discrete.height > 480))
734 return -EINVAL;
736 UDIA_DEBUG("Framesize: %dx%d, FMT: %X\n", size->discrete.width,
737 size->discrete.height,
738 size->pixel_format);
740 return 0;
744 * @param file
745 * @param priv
746 * @param input
748 * @return 0 or negative error code
751 int sn9c20x_vidioc_enum_input(struct file *file, void *priv,
752 struct v4l2_input *input)
754 UDIA_DEBUG("VIDIOC_ENUMINPUT %d\n", input->index);
756 if (input->index)
757 return -EINVAL;
759 strlcpy(input->name, "Webcam", sizeof(input->name));
760 input->type = V4L2_INPUT_TYPE_CAMERA;
761 input->std = 0;
763 return 0;
767 * @param file
768 * @param priv
769 * @param index
771 * @return 0 or negative error code
774 int sn9c20x_vidioc_g_input(struct file *file, void *priv, unsigned int *index)
776 UDIA_DEBUG("GET INPUT %d\n", *index);
778 if (index)
779 return -EINVAL;
781 return 0;
785 * @param file
786 * @param priv
787 * @param index
789 * @return 0 or negative error code
792 int sn9c20x_vidioc_s_input(struct file *file, void *priv, unsigned int index)
794 UDIA_DEBUG("SET INPUT %d\n", index);
796 if (v4l_get_privileges(file) < 0)
797 return -EBUSY;
799 if (index != 0)
800 return -EINVAL;
802 return 0;
806 * @param file
807 * @param priv
808 * @param ctrl
810 * @return 0 or negative error code
813 int sn9c20x_vidioc_queryctrl(struct file *file, void *priv,
814 struct v4l2_queryctrl *ctrl)
816 int i;
817 int ret = 0;
818 #ifdef V4L2_CTRL_FLAG_NEXT_CTRL
819 int min;
820 __u32 idnew, idlast;
821 #endif
822 struct usb_sn9c20x *dev;
824 dev = video_get_drvdata(priv);
826 UDIA_DEBUG("VIDIOC_QUERYCTRL id = %d\n", ctrl->id);
828 #ifdef V4L2_CTRL_FLAG_NEXT_CTRL
829 if (ctrl->id & V4L2_CTRL_FLAG_NEXT_CTRL) {
830 min = 0;
831 idnew = V4L2_CTRL_FLAG_NEXT_CTRL;
832 idlast = ctrl->id & ~V4L2_CTRL_FLAG_NEXT_CTRL;
833 for (i = 0; i < ARRAY_SIZE(sn9c20x_controls); i++) {
834 if ((sn9c20x_controls[i].id < idnew) &&
835 (sn9c20x_controls[i].id > idlast)) {
836 idnew = sn9c20x_controls[i].id;
837 min = i;
840 if (idnew != V4L2_CTRL_FLAG_NEXT_CTRL) {
841 UDIA_DEBUG("VIDIOC_QUERYCTRL found\n");
842 memcpy(ctrl, &sn9c20x_controls[min],
843 sizeof(struct v4l2_queryctrl));
844 goto done;
845 } else {
846 goto error;
848 } else
849 #endif
851 for (i = 0; i < ARRAY_SIZE(sn9c20x_controls); i++) {
852 if (sn9c20x_controls[i].id == ctrl->id) {
853 UDIA_DEBUG("VIDIOC_QUERYCTRL found\n");
854 memcpy(ctrl, &sn9c20x_controls[i],
855 sizeof(struct v4l2_queryctrl));
856 goto done;
861 error:
862 ret = -EINVAL;
863 done:
864 if ((ctrl->id == V4L2_CID_GAIN && dev->vsettings.auto_gain) ||
865 (ctrl->id == V4L2_CID_EXPOSURE && dev->vsettings.auto_exposure) ||
866 ((ctrl->id == V4L2_CID_BLUE_BALANCE ||
867 ctrl->id == V4L2_CID_RED_BALANCE) &&
868 dev->vsettings.auto_whitebalance)) {
869 ctrl->flags |= V4L2_CTRL_FLAG_GRABBED;
871 return ret;
875 * @param file
876 * @param priv
877 * @param ctrl
879 * @return 0 or negative error code
882 int sn9c20x_vidioc_g_ctrl(struct file *file, void *priv,
883 struct v4l2_control *ctrl)
885 struct usb_sn9c20x *dev;
887 dev = video_get_drvdata(priv);
889 UDIA_DEBUG("GET CTRL id=%d\n", ctrl->id);
891 switch (ctrl->id) {
892 case V4L2_CID_BRIGHTNESS:
893 ctrl->value = dev->vsettings.brightness;
894 break;
896 case V4L2_CID_EXPOSURE:
897 ctrl->value = dev->vsettings.exposure;
898 break;
900 case V4L2_CID_GAIN:
901 ctrl->value = dev->vsettings.gain;
902 break;
904 case V4L2_CID_GAMMA:
905 ctrl->value = dev->vsettings.gamma;
906 break;
908 case V4L2_CID_SATURATION:
909 ctrl->value = dev->vsettings.colour;
910 break;
912 case V4L2_CID_HUE:
913 ctrl->value = dev->vsettings.hue;
914 break;
916 case V4L2_CID_CONTRAST:
917 ctrl->value = dev->vsettings.contrast;
918 break;
920 case V4L2_CID_HFLIP:
921 ctrl->value = dev->vsettings.hflip;
922 break;
924 case V4L2_CID_VFLIP:
925 ctrl->value = dev->vsettings.vflip;
926 break;
928 case V4L2_CID_SHARPNESS:
929 ctrl->value = dev->vsettings.sharpness;
930 break;
932 case V4L2_CID_RED_BALANCE:
933 ctrl->value = dev->vsettings.red_gain;
934 break;
936 case V4L2_CID_BLUE_BALANCE:
937 ctrl->value = dev->vsettings.blue_gain;
938 break;
940 case V4L2_CID_EXPOSURE_AUTO:
941 ctrl->value = dev->vsettings.auto_exposure;
942 break;
944 case V4L2_CID_AUTOGAIN:
945 ctrl->value = dev->vsettings.auto_gain;
946 break;
948 case V4L2_CID_AUTO_WHITE_BALANCE:
949 ctrl->value = dev->vsettings.auto_whitebalance;
950 break;
952 default:
953 return -EINVAL;
955 return 0;
959 * @brief Apply v4l2 settings on camera
961 * @param file
962 * @param priv
963 * @param ctrl V4L2 control structure
965 * @returns 0 or negative error value
968 int sn9c20x_vidioc_s_ctrl(struct file *file, void *priv,
969 struct v4l2_control *ctrl)
971 struct usb_sn9c20x *dev;
973 dev = video_get_drvdata(priv);
975 UDIA_DEBUG("SET CTRL id=%d value=%d\n", ctrl->id, ctrl->value);
977 if ((ctrl->id == V4L2_CID_GAIN && dev->vsettings.auto_gain) ||
978 (ctrl->id == V4L2_CID_EXPOSURE && dev->vsettings.auto_exposure)) {
979 return -EBUSY;
982 return sn9c20x_set_camera_control(dev,
983 ctrl->id,
984 ctrl->value);
988 * @param file
989 * @param priv
990 * @param fmt
992 * @return 0 or negative error code
995 int sn9c20x_vidioc_enum_fmt_cap(struct file *file, void *priv,
996 struct v4l2_fmtdesc *fmt)
998 struct usb_sn9c20x *dev;
1000 dev = video_get_drvdata(priv);
1002 UDIA_DEBUG("VIDIOC_ENUM_FMT %d\n", fmt->index);
1004 if (fmt->index >= SN9C20X_N_FMTS)
1005 return -EINVAL;
1007 fmt->flags = 0;
1008 fmt->pixelformat = sn9c20x_fmts[fmt->index].pix_fmt;
1010 if (fmt->pixelformat == V4L2_PIX_FMT_JPEG && dev->jpeg == 0)
1011 return -EINVAL;
1013 memcpy(fmt->description, sn9c20x_fmts[fmt->index].desc, 32);
1015 return 0;
1019 * @param file
1020 * @param priv
1021 * @param fmt
1023 * @return 0 or negative error code
1026 int sn9c20x_vidioc_try_fmt_cap(struct file *file, void *priv,
1027 struct v4l2_format *fmt)
1029 int index;
1030 struct usb_sn9c20x *dev;
1032 dev = video_get_drvdata(priv);
1033 UDIA_DEBUG("TRY FMT %d\n", fmt->type);
1035 /* when this code is used prevents mplayer from setting outfmt
1036 if(fmt->fmt.pix.field != V4L2_FIELD_NONE)
1037 return -EINVAL;
1039 if (fmt->fmt.pix.pixelformat == V4L2_PIX_FMT_JPEG && dev->jpeg == 0)
1040 return -EINVAL;
1042 sn9c20x_get_closest_resolution(dev, &fmt->fmt.pix.width,
1043 &fmt->fmt.pix.height);
1045 if (fmt->fmt.pix.pixelformat != V4L2_PIX_FMT_SBGGR8 &&
1046 (fmt->fmt.pix.width > 640 && fmt->fmt.pix.height > 480))
1047 fmt->fmt.pix.pixelformat = V4L2_PIX_FMT_SBGGR8;
1049 for (index = 0; index < SN9C20X_N_FMTS; index++)
1050 if (sn9c20x_fmts[index].pix_fmt == fmt->fmt.pix.pixelformat)
1051 break;
1053 if (index >= SN9C20X_N_FMTS)
1054 return -EINVAL;
1056 fmt->fmt.pix.bytesperline = fmt->fmt.pix.width *
1057 sn9c20x_fmts[index].depth / 8;
1059 fmt->fmt.pix.sizeimage = fmt->fmt.pix.height *
1060 fmt->fmt.pix.bytesperline;
1062 fmt->fmt.pix.colorspace = V4L2_COLORSPACE_SRGB;
1063 fmt->fmt.pix.priv = index;
1065 return 0;
1069 * @param file
1070 * @param priv
1071 * @param fmt
1073 * @return 0
1076 int sn9c20x_vidioc_g_fmt_cap(struct file *file, void *priv,
1077 struct v4l2_format *fmt)
1079 struct usb_sn9c20x *dev;
1081 dev = video_get_drvdata(priv);
1083 UDIA_DEBUG("GET FMT %d\n", fmt->type);
1085 memcpy(&(fmt->fmt.pix), &(dev->vsettings.format), sizeof(fmt->fmt.pix));
1088 return 0;
1092 * @param file
1093 * @param priv
1094 * @param fmt
1096 * @return 0 or negative error code
1099 int sn9c20x_vidioc_s_fmt_cap(struct file *file, void *priv,
1100 struct v4l2_format *fmt)
1102 struct usb_sn9c20x *dev;
1103 int ret;
1105 dev = video_get_drvdata(priv);
1107 UDIA_DEBUG("SET FMT %d : %d\n", fmt->type, fmt->fmt.pix.pixelformat);
1109 if (v4l_get_privileges(file) < 0)
1110 return -EBUSY;
1112 if (sn9c20x_queue_streaming(&dev->queue))
1113 return -EBUSY;
1115 ret = sn9c20x_vidioc_try_fmt_cap(file, priv, fmt);
1116 if (ret)
1117 return -EINVAL;
1119 sn9c20x_set_resolution(dev, fmt->fmt.pix.width, fmt->fmt.pix.height);
1120 sn9c20x_set_format(dev, fmt->fmt.pix.pixelformat);
1121 memcpy(&(dev->vsettings.format), &(fmt->fmt.pix), sizeof(fmt->fmt.pix));
1123 return 0;
1127 * @param file
1128 * @param priv
1129 * @param request
1131 * @return 0 or negative error code
1134 int sn9c20x_vidioc_reqbufs(struct file *file, void *priv,
1135 struct v4l2_requestbuffers *request)
1137 int ret = 0;
1138 struct usb_sn9c20x *dev;
1140 dev = video_get_drvdata(priv);
1142 if (v4l_get_privileges(file) < 0) {
1143 ret = -EBUSY;
1144 goto done;
1147 if (request->memory != V4L2_MEMORY_MMAP ||
1148 request->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) {
1149 ret = -EINVAL;
1150 goto done;
1153 if (sn9c20x_queue_streaming(&dev->queue)) {
1154 ret = -EBUSY;
1155 goto done;
1158 ret = sn9c20x_alloc_buffers(&dev->queue, request->count,
1159 dev->vsettings.format.sizeimage);
1160 if (ret < 0)
1161 goto done;
1163 request->count = ret;
1164 ret = 0;
1165 UDIA_DEBUG("Buffers Allocated %d\n", request->count);
1166 done:
1167 return ret;
1171 * @param file
1172 * @param priv
1173 * @param buffer
1175 * @return 0 or negative error code
1178 int sn9c20x_vidioc_querybuf(struct file *file, void *priv,
1179 struct v4l2_buffer *buffer)
1181 struct usb_sn9c20x *dev;
1183 dev = video_get_drvdata(priv);
1185 UDIA_DEBUG("QUERY BUFFERS %d %d\n", buffer->index, dev->queue.count);
1187 if (buffer->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
1188 return -EINVAL;
1190 if (!v4l_has_privileges(file))
1191 return -EBUSY;
1193 return sn9c20x_query_buffer(&dev->queue, buffer);
1197 * @param file
1198 * @param priv
1199 * @param buffer
1201 * @return 0 or negative error code
1204 int sn9c20x_vidioc_qbuf(struct file *file, void *priv,
1205 struct v4l2_buffer *buffer)
1207 struct usb_sn9c20x *dev;
1209 dev = video_get_drvdata(priv);
1211 UDIA_DEBUG("VIDIOC_QBUF\n");
1213 if (!v4l_has_privileges(file))
1214 return -EBUSY;
1216 return sn9c20x_queue_buffer(&dev->queue, buffer);
1220 * @param file
1221 * @param priv
1222 * @param buffer
1224 * @return 0 or negative error code
1227 int sn9c20x_vidioc_dqbuf(struct file *file, void *priv,
1228 struct v4l2_buffer *buffer)
1230 struct usb_sn9c20x *dev;
1231 int ret = 0;
1233 dev = video_get_drvdata(priv);
1235 UDIA_DEBUG("VIDIOC_DQBUF\n");
1237 if (!v4l_has_privileges(file))
1238 return -EBUSY;
1240 ret = sn9c20x_dequeue_buffer(&dev->queue, buffer,
1241 file->f_flags & O_NONBLOCK);
1242 if (ret < 0)
1243 return ret;
1245 if (dev->vsettings.format.pixelformat == V4L2_PIX_FMT_JPEG) {
1246 UDIA_DEBUG("Adding JPEG Header\n");
1247 v4l_add_jpegheader(dev, dev->queue.mem + buffer->m.offset,
1248 buffer->bytesused);
1249 buffer->bytesused += 589;
1252 dev_sn9c20x_call_constantly(dev);
1254 return ret;
1258 * @param file
1259 * @param priv
1260 * @param type
1262 * @return 0 or negative error code
1265 int sn9c20x_vidioc_streamon(struct file *file, void *priv,
1266 enum v4l2_buf_type type)
1268 struct usb_sn9c20x *dev;
1270 dev = video_get_drvdata(priv);
1272 UDIA_DEBUG("VIDIOC_STREAMON\n");
1274 if (!v4l_has_privileges(file))
1275 return -EBUSY;
1277 if (dev->mode != SN9C20X_MODE_IDLE)
1278 return -EBUSY;
1280 return v4l2_enable_video(dev, SN9C20X_MODE_STREAM);
1284 * @param file
1285 * @param priv
1286 * @param type
1288 * @return 0 or negative error code
1291 int sn9c20x_vidioc_streamoff(struct file *file, void *priv,
1292 enum v4l2_buf_type type)
1294 struct usb_sn9c20x *dev;
1296 dev = video_get_drvdata(priv);
1298 UDIA_DEBUG("VIDIOC_STREAMOFF\n");
1300 if (!v4l_has_privileges(file))
1301 return -EBUSY;
1303 return v4l2_enable_video(dev, SN9C20X_MODE_IDLE);
1307 * @param file
1308 * @param priv
1309 * @param param
1311 * @return 0 or negative error code
1314 int sn9c20x_vidioc_g_param(struct file *file, void *priv,
1315 struct v4l2_streamparm *param)
1317 struct usb_sn9c20x *dev;
1320 dev = video_get_drvdata(priv);
1322 if (param->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
1323 return -EINVAL;
1325 param->parm.capture.capability = 0;
1326 param->parm.capture.capturemode = 0;
1327 param->parm.capture.timeperframe.numerator = 1;
1328 param->parm.capture.timeperframe.denominator = 30;
1329 param->parm.capture.readbuffers = 2;
1330 param->parm.capture.extendedmode = 0;
1332 return 0;
1336 * @param file
1337 * @param priv
1338 * @param param
1340 * @return 0 or negative error code
1343 int sn9c20x_vidioc_s_param(struct file *file, void *priv,
1344 struct v4l2_streamparm *param)
1346 struct usb_sn9c20x *dev;
1348 dev = video_get_drvdata(priv);
1350 if (v4l_get_privileges(file))
1351 return -EBUSY;
1353 if (param->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
1354 return -EINVAL;
1356 return 0;
1360 * @param inode Inode pointer
1361 * @param fp File pointer
1362 * @param cmd Command
1363 * @param arg Arguements of the command
1365 * @returns 0 if all is OK
1367 * @brief Manage IOCTL
1369 * This function permits to manage all the IOCTL from the application.
1371 #if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 29)
1372 static int v4l_sn9c20x_ioctl(struct inode *inode, struct file *fp,
1373 unsigned int cmd, unsigned long arg)
1374 #else
1375 static long v4l_sn9c20x_ioctl(struct file *fp,
1376 unsigned int cmd, unsigned long arg)
1377 #endif
1379 int err;
1380 struct usb_sn9c20x *dev;
1381 struct video_device *vdev;
1383 vdev = video_devdata(fp);
1384 dev = video_get_drvdata(video_devdata(fp));
1386 UDIA_DEBUG("v4l_sn9c20x_ioctl %02X\n", (unsigned char) cmd);
1388 if (dev == NULL || vdev == NULL)
1389 return -EFAULT;
1391 #if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 29)
1392 switch (cmd) {
1393 case VIDIOC_ENUM_FRAMESIZES:
1395 struct v4l2_frmsizeenum size;
1396 if (copy_from_user(&size, (void __user *)arg, sizeof(size)))
1397 return -EFAULT;
1398 err = sn9c20x_vidioc_enum_framesizes(fp,
1399 fp->private_data,
1400 &size);
1401 if (copy_to_user((void __user *)arg, &size, sizeof(size)))
1402 return -EFAULT;
1403 break;
1405 default:
1406 err = video_ioctl2(inode, fp, cmd, arg);
1408 #else
1409 err = video_ioctl2(fp, cmd, arg);
1410 #endif
1412 return err;
1416 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27)
1417 static const struct v4l2_ioctl_ops sn9c20x_v4l2_ioctl_ops = {
1418 .vidioc_querycap = sn9c20x_vidioc_querycap,
1419 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 29)
1420 .vidioc_enum_framesizes = sn9c20x_vidioc_enum_framesizes,
1421 #endif
1422 .vidioc_enum_fmt_vid_cap = sn9c20x_vidioc_enum_fmt_cap,
1423 .vidioc_try_fmt_vid_cap = sn9c20x_vidioc_try_fmt_cap,
1424 .vidioc_s_fmt_vid_cap = sn9c20x_vidioc_s_fmt_cap,
1425 .vidioc_g_fmt_vid_cap = sn9c20x_vidioc_g_fmt_cap,
1426 .vidioc_enum_input = sn9c20x_vidioc_enum_input,
1427 .vidioc_g_input = sn9c20x_vidioc_g_input,
1428 .vidioc_s_input = sn9c20x_vidioc_s_input,
1429 .vidioc_streamon = sn9c20x_vidioc_streamon,
1430 .vidioc_streamoff = sn9c20x_vidioc_streamoff,
1431 .vidioc_queryctrl = sn9c20x_vidioc_queryctrl,
1432 .vidioc_g_ctrl = sn9c20x_vidioc_g_ctrl,
1433 .vidioc_s_ctrl = sn9c20x_vidioc_s_ctrl,
1434 .vidioc_g_parm = sn9c20x_vidioc_g_param,
1435 .vidioc_s_parm = sn9c20x_vidioc_s_param,
1436 .vidioc_reqbufs = sn9c20x_vidioc_reqbufs,
1437 .vidioc_qbuf = sn9c20x_vidioc_qbuf,
1438 .vidioc_dqbuf = sn9c20x_vidioc_dqbuf,
1439 .vidioc_querybuf = sn9c20x_vidioc_querybuf,
1441 #endif
1444 * @param dev Device structure
1446 * @returns 0 if all is OK
1448 * @brief Register the video device
1450 * This function permits to register the USB device to the video device.
1452 int v4l_sn9c20x_register_video_device(struct usb_sn9c20x *dev)
1454 int err;
1456 strcpy(dev->vdev->name, DRIVER_DESC);
1458 #if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 27)
1459 dev->vdev->dev = &dev->interface->dev;
1460 dev->vdev->owner = THIS_MODULE;
1461 dev->vdev->type = VID_TYPE_CAPTURE;
1462 #else
1463 dev->vdev->parent = &dev->interface->dev;
1464 #endif
1465 dev->vdev->current_norm = 0;
1466 dev->vdev->tvnorms = 0;
1467 dev->vdev->fops = &v4l_sn9c20x_fops;
1468 dev->vdev->release = video_device_release;
1469 dev->vdev->minor = -1;
1471 if (log_level & SN9C20X_DEBUG)
1472 dev->vdev->debug = V4L2_DEBUG_IOCTL_ARG;
1474 #if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 27)
1475 dev->vdev->vidioc_querycap = sn9c20x_vidioc_querycap;
1476 dev->vdev->vidioc_enum_fmt_cap = sn9c20x_vidioc_enum_fmt_cap;
1477 dev->vdev->vidioc_try_fmt_cap = sn9c20x_vidioc_try_fmt_cap;
1478 dev->vdev->vidioc_s_fmt_cap = sn9c20x_vidioc_s_fmt_cap;
1479 dev->vdev->vidioc_g_fmt_cap = sn9c20x_vidioc_g_fmt_cap;
1480 dev->vdev->vidioc_enum_input = sn9c20x_vidioc_enum_input;
1481 dev->vdev->vidioc_g_input = sn9c20x_vidioc_g_input;
1482 dev->vdev->vidioc_s_input = sn9c20x_vidioc_s_input;
1483 dev->vdev->vidioc_streamon = sn9c20x_vidioc_streamon;
1484 dev->vdev->vidioc_streamoff = sn9c20x_vidioc_streamoff;
1485 dev->vdev->vidioc_queryctrl = sn9c20x_vidioc_queryctrl;
1486 dev->vdev->vidioc_g_ctrl = sn9c20x_vidioc_g_ctrl;
1487 dev->vdev->vidioc_s_ctrl = sn9c20x_vidioc_s_ctrl;
1488 dev->vdev->vidioc_g_parm = sn9c20x_vidioc_g_param;
1489 dev->vdev->vidioc_s_parm = sn9c20x_vidioc_s_param;
1490 dev->vdev->vidioc_reqbufs = sn9c20x_vidioc_reqbufs;
1491 dev->vdev->vidioc_qbuf = sn9c20x_vidioc_qbuf;
1492 dev->vdev->vidioc_dqbuf = sn9c20x_vidioc_dqbuf;
1493 dev->vdev->vidioc_querybuf = sn9c20x_vidioc_querybuf;
1494 #else
1495 dev->vdev->ioctl_ops = &sn9c20x_v4l2_ioctl_ops;
1496 #endif
1498 video_set_drvdata(dev->vdev, dev);
1500 sn9c20x_queue_init(&dev->queue);
1502 err = video_register_device(dev->vdev, VFL_TYPE_GRABBER, -1);
1504 if (err)
1505 UDIA_ERROR("Video register fail !\n");
1506 else
1507 UDIA_INFO("Webcam device %04X:%04X is now controlling video "
1508 "device /dev/video%d\n",
1509 le16_to_cpu(dev->udev->descriptor.idVendor),
1510 le16_to_cpu(dev->udev->descriptor.idProduct),
1511 dev->vdev->minor);
1513 return err;
1518 * @param dev Device structure
1520 * @returns 0 if all is OK
1522 * @brief Unregister the video device
1524 * This function permits to unregister the video device.
1526 int v4l_sn9c20x_unregister_video_device(struct usb_sn9c20x *dev)
1528 UDIA_INFO("SN9C20X USB 2.0 Webcam releases control of video "
1529 "device /dev/video%d\n", dev->vdev->minor);
1531 video_set_drvdata(dev->vdev, NULL);
1532 video_unregister_device(dev->vdev);
1534 return 0;
1539 * @var v4l_sn9c20x_fops
1541 * This variable contains some callback
1544 #if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 29)
1545 static struct file_operations v4l_sn9c20x_fops = {
1546 #else
1547 static struct v4l2_file_operations v4l_sn9c20x_fops = {
1548 #endif
1549 .owner = THIS_MODULE,
1550 .open = v4l_sn9c20x_open,
1551 .release = v4l_sn9c20x_release,
1552 .read = v4l_sn9c20x_read,
1553 .poll = v4l_sn9c20x_poll,
1554 .mmap = v4l_sn9c20x_mmap,
1555 .ioctl = v4l_sn9c20x_ioctl,
1556 #if defined(CONFIG_COMPAT) && LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 29)
1557 .compat_ioctl = v4l_compat_ioctl32,
1558 #endif
1559 #if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 29)
1560 .llseek = no_llseek
1561 #endif