diff options
Diffstat (limited to 'drivers/media/video/pwc/pwc-v4l.c')
-rw-r--r-- | drivers/media/video/pwc/pwc-v4l.c | 1202 |
1 files changed, 1202 insertions, 0 deletions
diff --git a/drivers/media/video/pwc/pwc-v4l.c b/drivers/media/video/pwc/pwc-v4l.c new file mode 100644 index 000000000000..b7eb3ce3b968 --- /dev/null +++ b/drivers/media/video/pwc/pwc-v4l.c | |||
@@ -0,0 +1,1202 @@ | |||
1 | /* Linux driver for Philips webcam | ||
2 | USB and Video4Linux interface part. | ||
3 | (C) 1999-2004 Nemosoft Unv. | ||
4 | (C) 2004-2006 Luc Saillard (luc@saillard.org) | ||
5 | |||
6 | NOTE: this version of pwc is an unofficial (modified) release of pwc & pcwx | ||
7 | driver and thus may have bugs that are not present in the original version. | ||
8 | Please send bug reports and support requests to <luc@saillard.org>. | ||
9 | The decompression routines have been implemented by reverse-engineering the | ||
10 | Nemosoft binary pwcx module. Caveat emptor. | ||
11 | |||
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 | (at your option) any later version. | ||
16 | |||
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. | ||
21 | |||
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 | ||
25 | |||
26 | */ | ||
27 | |||
28 | #include <linux/errno.h> | ||
29 | #include <linux/init.h> | ||
30 | #include <linux/mm.h> | ||
31 | #include <linux/module.h> | ||
32 | #include <linux/poll.h> | ||
33 | #include <linux/slab.h> | ||
34 | #include <linux/vmalloc.h> | ||
35 | #include <asm/io.h> | ||
36 | |||
37 | #include "pwc.h" | ||
38 | |||
39 | static struct v4l2_queryctrl pwc_controls[] = { | ||
40 | { | ||
41 | .id = V4L2_CID_BRIGHTNESS, | ||
42 | .type = V4L2_CTRL_TYPE_INTEGER, | ||
43 | .name = "Brightness", | ||
44 | .minimum = 0, | ||
45 | .maximum = 128, | ||
46 | .step = 1, | ||
47 | .default_value = 64, | ||
48 | }, | ||
49 | { | ||
50 | .id = V4L2_CID_CONTRAST, | ||
51 | .type = V4L2_CTRL_TYPE_INTEGER, | ||
52 | .name = "Contrast", | ||
53 | .minimum = 0, | ||
54 | .maximum = 64, | ||
55 | .step = 1, | ||
56 | .default_value = 0, | ||
57 | }, | ||
58 | { | ||
59 | .id = V4L2_CID_SATURATION, | ||
60 | .type = V4L2_CTRL_TYPE_INTEGER, | ||
61 | .name = "Saturation", | ||
62 | .minimum = -100, | ||
63 | .maximum = 100, | ||
64 | .step = 1, | ||
65 | .default_value = 0, | ||
66 | }, | ||
67 | { | ||
68 | .id = V4L2_CID_GAMMA, | ||
69 | .type = V4L2_CTRL_TYPE_INTEGER, | ||
70 | .name = "Gamma", | ||
71 | .minimum = 0, | ||
72 | .maximum = 32, | ||
73 | .step = 1, | ||
74 | .default_value = 0, | ||
75 | }, | ||
76 | { | ||
77 | .id = V4L2_CID_RED_BALANCE, | ||
78 | .type = V4L2_CTRL_TYPE_INTEGER, | ||
79 | .name = "Red Gain", | ||
80 | .minimum = 0, | ||
81 | .maximum = 256, | ||
82 | .step = 1, | ||
83 | .default_value = 0, | ||
84 | }, | ||
85 | { | ||
86 | .id = V4L2_CID_BLUE_BALANCE, | ||
87 | .type = V4L2_CTRL_TYPE_INTEGER, | ||
88 | .name = "Blue Gain", | ||
89 | .minimum = 0, | ||
90 | .maximum = 256, | ||
91 | .step = 1, | ||
92 | .default_value = 0, | ||
93 | }, | ||
94 | { | ||
95 | .id = V4L2_CID_AUTO_WHITE_BALANCE, | ||
96 | .type = V4L2_CTRL_TYPE_BOOLEAN, | ||
97 | .name = "Auto White Balance", | ||
98 | .minimum = 0, | ||
99 | .maximum = 1, | ||
100 | .step = 1, | ||
101 | .default_value = 0, | ||
102 | }, | ||
103 | { | ||
104 | .id = V4L2_CID_EXPOSURE, | ||
105 | .type = V4L2_CTRL_TYPE_INTEGER, | ||
106 | .name = "Shutter Speed (Exposure)", | ||
107 | .minimum = 0, | ||
108 | .maximum = 256, | ||
109 | .step = 1, | ||
110 | .default_value = 200, | ||
111 | }, | ||
112 | { | ||
113 | .id = V4L2_CID_AUTOGAIN, | ||
114 | .type = V4L2_CTRL_TYPE_BOOLEAN, | ||
115 | .name = "Auto Gain Enabled", | ||
116 | .minimum = 0, | ||
117 | .maximum = 1, | ||
118 | .step = 1, | ||
119 | .default_value = 1, | ||
120 | }, | ||
121 | { | ||
122 | .id = V4L2_CID_GAIN, | ||
123 | .type = V4L2_CTRL_TYPE_INTEGER, | ||
124 | .name = "Gain Level", | ||
125 | .minimum = 0, | ||
126 | .maximum = 256, | ||
127 | .step = 1, | ||
128 | .default_value = 0, | ||
129 | }, | ||
130 | { | ||
131 | .id = V4L2_CID_PRIVATE_SAVE_USER, | ||
132 | .type = V4L2_CTRL_TYPE_BUTTON, | ||
133 | .name = "Save User Settings", | ||
134 | .minimum = 0, | ||
135 | .maximum = 0, | ||
136 | .step = 0, | ||
137 | .default_value = 0, | ||
138 | }, | ||
139 | { | ||
140 | .id = V4L2_CID_PRIVATE_RESTORE_USER, | ||
141 | .type = V4L2_CTRL_TYPE_BUTTON, | ||
142 | .name = "Restore User Settings", | ||
143 | .minimum = 0, | ||
144 | .maximum = 0, | ||
145 | .step = 0, | ||
146 | .default_value = 0, | ||
147 | }, | ||
148 | { | ||
149 | .id = V4L2_CID_PRIVATE_RESTORE_FACTORY, | ||
150 | .type = V4L2_CTRL_TYPE_BUTTON, | ||
151 | .name = "Restore Factory Settings", | ||
152 | .minimum = 0, | ||
153 | .maximum = 0, | ||
154 | .step = 0, | ||
155 | .default_value = 0, | ||
156 | }, | ||
157 | { | ||
158 | .id = V4L2_CID_PRIVATE_COLOUR_MODE, | ||
159 | .type = V4L2_CTRL_TYPE_BOOLEAN, | ||
160 | .name = "Colour mode", | ||
161 | .minimum = 0, | ||
162 | .maximum = 1, | ||
163 | .step = 1, | ||
164 | .default_value = 0, | ||
165 | }, | ||
166 | { | ||
167 | .id = V4L2_CID_PRIVATE_AUTOCONTOUR, | ||
168 | .type = V4L2_CTRL_TYPE_BOOLEAN, | ||
169 | .name = "Auto contour", | ||
170 | .minimum = 0, | ||
171 | .maximum = 1, | ||
172 | .step = 1, | ||
173 | .default_value = 0, | ||
174 | }, | ||
175 | { | ||
176 | .id = V4L2_CID_PRIVATE_CONTOUR, | ||
177 | .type = V4L2_CTRL_TYPE_INTEGER, | ||
178 | .name = "Contour", | ||
179 | .minimum = 0, | ||
180 | .maximum = 63, | ||
181 | .step = 1, | ||
182 | .default_value = 0, | ||
183 | }, | ||
184 | { | ||
185 | .id = V4L2_CID_PRIVATE_BACKLIGHT, | ||
186 | .type = V4L2_CTRL_TYPE_BOOLEAN, | ||
187 | .name = "Backlight compensation", | ||
188 | .minimum = 0, | ||
189 | .maximum = 1, | ||
190 | .step = 1, | ||
191 | .default_value = 0, | ||
192 | }, | ||
193 | { | ||
194 | .id = V4L2_CID_PRIVATE_FLICKERLESS, | ||
195 | .type = V4L2_CTRL_TYPE_BOOLEAN, | ||
196 | .name = "Flickerless", | ||
197 | .minimum = 0, | ||
198 | .maximum = 1, | ||
199 | .step = 1, | ||
200 | .default_value = 0, | ||
201 | }, | ||
202 | { | ||
203 | .id = V4L2_CID_PRIVATE_NOISE_REDUCTION, | ||
204 | .type = V4L2_CTRL_TYPE_INTEGER, | ||
205 | .name = "Noise reduction", | ||
206 | .minimum = 0, | ||
207 | .maximum = 3, | ||
208 | .step = 1, | ||
209 | .default_value = 0, | ||
210 | }, | ||
211 | }; | ||
212 | |||
213 | |||
214 | static void pwc_vidioc_fill_fmt(const struct pwc_device *pdev, struct v4l2_format *f) | ||
215 | { | ||
216 | memset(&f->fmt.pix, 0, sizeof(struct v4l2_pix_format)); | ||
217 | f->fmt.pix.width = pdev->view.x; | ||
218 | f->fmt.pix.height = pdev->view.y; | ||
219 | f->fmt.pix.field = V4L2_FIELD_NONE; | ||
220 | if (pdev->vpalette == VIDEO_PALETTE_YUV420P) { | ||
221 | f->fmt.pix.pixelformat = V4L2_PIX_FMT_YUV420; | ||
222 | f->fmt.pix.bytesperline = (f->fmt.pix.width * 3)/2; | ||
223 | f->fmt.pix.sizeimage = f->fmt.pix.height * f->fmt.pix.bytesperline; | ||
224 | } else { | ||
225 | /* vbandlength contains 4 lines ... */ | ||
226 | f->fmt.pix.bytesperline = pdev->vbandlength/4; | ||
227 | f->fmt.pix.sizeimage = pdev->frame_size + sizeof(struct pwc_raw_frame); | ||
228 | if (DEVICE_USE_CODEC1(pdev->type)) | ||
229 | f->fmt.pix.pixelformat = V4L2_PIX_FMT_PWC1; | ||
230 | else | ||
231 | f->fmt.pix.pixelformat = V4L2_PIX_FMT_PWC2; | ||
232 | } | ||
233 | PWC_DEBUG_IOCTL("pwc_vidioc_fill_fmt() " | ||
234 | "width=%d, height=%d, bytesperline=%d, sizeimage=%d, pixelformat=%c%c%c%c\n", | ||
235 | f->fmt.pix.width, | ||
236 | f->fmt.pix.height, | ||
237 | f->fmt.pix.bytesperline, | ||
238 | f->fmt.pix.sizeimage, | ||
239 | (f->fmt.pix.pixelformat)&255, | ||
240 | (f->fmt.pix.pixelformat>>8)&255, | ||
241 | (f->fmt.pix.pixelformat>>16)&255, | ||
242 | (f->fmt.pix.pixelformat>>24)&255); | ||
243 | } | ||
244 | |||
245 | /* ioctl(VIDIOC_TRY_FMT) */ | ||
246 | static int pwc_vidioc_try_fmt(struct pwc_device *pdev, struct v4l2_format *f) | ||
247 | { | ||
248 | if (f->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) { | ||
249 | PWC_DEBUG_IOCTL("Bad video type must be V4L2_BUF_TYPE_VIDEO_CAPTURE\n"); | ||
250 | return -EINVAL; | ||
251 | } | ||
252 | |||
253 | switch (f->fmt.pix.pixelformat) { | ||
254 | case V4L2_PIX_FMT_YUV420: | ||
255 | break; | ||
256 | case V4L2_PIX_FMT_PWC1: | ||
257 | if (DEVICE_USE_CODEC23(pdev->type)) { | ||
258 | PWC_DEBUG_IOCTL("codec1 is only supported for old pwc webcam\n"); | ||
259 | return -EINVAL; | ||
260 | } | ||
261 | break; | ||
262 | case V4L2_PIX_FMT_PWC2: | ||
263 | if (DEVICE_USE_CODEC1(pdev->type)) { | ||
264 | PWC_DEBUG_IOCTL("codec23 is only supported for new pwc webcam\n"); | ||
265 | return -EINVAL; | ||
266 | } | ||
267 | break; | ||
268 | default: | ||
269 | PWC_DEBUG_IOCTL("Unsupported pixel format\n"); | ||
270 | return -EINVAL; | ||
271 | |||
272 | } | ||
273 | |||
274 | if (f->fmt.pix.width > pdev->view_max.x) | ||
275 | f->fmt.pix.width = pdev->view_max.x; | ||
276 | else if (f->fmt.pix.width < pdev->view_min.x) | ||
277 | f->fmt.pix.width = pdev->view_min.x; | ||
278 | |||
279 | if (f->fmt.pix.height > pdev->view_max.y) | ||
280 | f->fmt.pix.height = pdev->view_max.y; | ||
281 | else if (f->fmt.pix.height < pdev->view_min.y) | ||
282 | f->fmt.pix.height = pdev->view_min.y; | ||
283 | |||
284 | return 0; | ||
285 | } | ||
286 | |||
287 | /* ioctl(VIDIOC_SET_FMT) */ | ||
288 | static int pwc_vidioc_set_fmt(struct pwc_device *pdev, struct v4l2_format *f) | ||
289 | { | ||
290 | int ret, fps, snapshot, compression, pixelformat; | ||
291 | |||
292 | ret = pwc_vidioc_try_fmt(pdev, f); | ||
293 | if (ret<0) | ||
294 | return ret; | ||
295 | |||
296 | pixelformat = f->fmt.pix.pixelformat; | ||
297 | compression = pdev->vcompression; | ||
298 | snapshot = 0; | ||
299 | fps = pdev->vframes; | ||
300 | if (f->fmt.pix.priv) { | ||
301 | compression = (f->fmt.pix.priv & PWC_QLT_MASK) >> PWC_QLT_SHIFT; | ||
302 | snapshot = !!(f->fmt.pix.priv & PWC_FPS_SNAPSHOT); | ||
303 | fps = (f->fmt.pix.priv & PWC_FPS_FRMASK) >> PWC_FPS_SHIFT; | ||
304 | if (fps == 0) | ||
305 | fps = pdev->vframes; | ||
306 | } | ||
307 | |||
308 | if (pixelformat == V4L2_PIX_FMT_YUV420) | ||
309 | pdev->vpalette = VIDEO_PALETTE_YUV420P; | ||
310 | else | ||
311 | pdev->vpalette = VIDEO_PALETTE_RAW; | ||
312 | |||
313 | PWC_DEBUG_IOCTL("Try to change format to: width=%d height=%d fps=%d " | ||
314 | "compression=%d snapshot=%d format=%c%c%c%c\n", | ||
315 | f->fmt.pix.width, f->fmt.pix.height, fps, | ||
316 | compression, snapshot, | ||
317 | (pixelformat)&255, | ||
318 | (pixelformat>>8)&255, | ||
319 | (pixelformat>>16)&255, | ||
320 | (pixelformat>>24)&255); | ||
321 | |||
322 | ret = pwc_try_video_mode(pdev, | ||
323 | f->fmt.pix.width, | ||
324 | f->fmt.pix.height, | ||
325 | fps, | ||
326 | compression, | ||
327 | snapshot); | ||
328 | |||
329 | PWC_DEBUG_IOCTL("pwc_try_video_mode(), return=%d\n", ret); | ||
330 | |||
331 | if (ret) | ||
332 | return ret; | ||
333 | |||
334 | pwc_vidioc_fill_fmt(pdev, f); | ||
335 | |||
336 | return 0; | ||
337 | |||
338 | } | ||
339 | |||
340 | int pwc_video_do_ioctl(struct inode *inode, struct file *file, | ||
341 | unsigned int cmd, void *arg) | ||
342 | { | ||
343 | struct video_device *vdev = video_devdata(file); | ||
344 | struct pwc_device *pdev; | ||
345 | DECLARE_WAITQUEUE(wait, current); | ||
346 | |||
347 | if (vdev == NULL) | ||
348 | return -EFAULT; | ||
349 | pdev = vdev->priv; | ||
350 | if (pdev == NULL) | ||
351 | return -EFAULT; | ||
352 | |||
353 | #if CONFIG_PWC_DEBUG | ||
354 | if (PWC_DEBUG_LEVEL_IOCTL & pwc_trace) | ||
355 | v4l_printk_ioctl(cmd); | ||
356 | #endif | ||
357 | |||
358 | |||
359 | switch (cmd) { | ||
360 | /* Query cabapilities */ | ||
361 | case VIDIOCGCAP: | ||
362 | { | ||
363 | struct video_capability *caps = arg; | ||
364 | |||
365 | strcpy(caps->name, vdev->name); | ||
366 | caps->type = VID_TYPE_CAPTURE; | ||
367 | caps->channels = 1; | ||
368 | caps->audios = 1; | ||
369 | caps->minwidth = pdev->view_min.x; | ||
370 | caps->minheight = pdev->view_min.y; | ||
371 | caps->maxwidth = pdev->view_max.x; | ||
372 | caps->maxheight = pdev->view_max.y; | ||
373 | break; | ||
374 | } | ||
375 | |||
376 | /* Channel functions (simulate 1 channel) */ | ||
377 | case VIDIOCGCHAN: | ||
378 | { | ||
379 | struct video_channel *v = arg; | ||
380 | |||
381 | if (v->channel != 0) | ||
382 | return -EINVAL; | ||
383 | v->flags = 0; | ||
384 | v->tuners = 0; | ||
385 | v->type = VIDEO_TYPE_CAMERA; | ||
386 | strcpy(v->name, "Webcam"); | ||
387 | return 0; | ||
388 | } | ||
389 | |||
390 | case VIDIOCSCHAN: | ||
391 | { | ||
392 | /* The spec says the argument is an integer, but | ||
393 | the bttv driver uses a video_channel arg, which | ||
394 | makes sense becasue it also has the norm flag. | ||
395 | */ | ||
396 | struct video_channel *v = arg; | ||
397 | if (v->channel != 0) | ||
398 | return -EINVAL; | ||
399 | return 0; | ||
400 | } | ||
401 | |||
402 | |||
403 | /* Picture functions; contrast etc. */ | ||
404 | case VIDIOCGPICT: | ||
405 | { | ||
406 | struct video_picture *p = arg; | ||
407 | int val; | ||
408 | |||
409 | val = pwc_get_brightness(pdev); | ||
410 | if (val >= 0) | ||
411 | p->brightness = (val<<9); | ||
412 | else | ||
413 | p->brightness = 0xffff; | ||
414 | val = pwc_get_contrast(pdev); | ||
415 | if (val >= 0) | ||
416 | p->contrast = (val<<10); | ||
417 | else | ||
418 | p->contrast = 0xffff; | ||
419 | /* Gamma, Whiteness, what's the difference? :) */ | ||
420 | val = pwc_get_gamma(pdev); | ||
421 | if (val >= 0) | ||
422 | p->whiteness = (val<<11); | ||
423 | else | ||
424 | p->whiteness = 0xffff; | ||
425 | if (pwc_get_saturation(pdev, &val)<0) | ||
426 | p->colour = 0xffff; | ||
427 | else | ||
428 | p->colour = 32768 + val * 327; | ||
429 | p->depth = 24; | ||
430 | p->palette = pdev->vpalette; | ||
431 | p->hue = 0xFFFF; /* N/A */ | ||
432 | break; | ||
433 | } | ||
434 | |||
435 | case VIDIOCSPICT: | ||
436 | { | ||
437 | struct video_picture *p = arg; | ||
438 | /* | ||
439 | * FIXME: Suppose we are mid read | ||
440 | ANSWER: No problem: the firmware of the camera | ||
441 | can handle brightness/contrast/etc | ||
442 | changes at _any_ time, and the palette | ||
443 | is used exactly once in the uncompress | ||
444 | routine. | ||
445 | */ | ||
446 | pwc_set_brightness(pdev, p->brightness); | ||
447 | pwc_set_contrast(pdev, p->contrast); | ||
448 | pwc_set_gamma(pdev, p->whiteness); | ||
449 | pwc_set_saturation(pdev, (p->colour-32768)/327); | ||
450 | if (p->palette && p->palette != pdev->vpalette) { | ||
451 | switch (p->palette) { | ||
452 | case VIDEO_PALETTE_YUV420P: | ||
453 | case VIDEO_PALETTE_RAW: | ||
454 | pdev->vpalette = p->palette; | ||
455 | return pwc_try_video_mode(pdev, pdev->image.x, pdev->image.y, pdev->vframes, pdev->vcompression, pdev->vsnapshot); | ||
456 | break; | ||
457 | default: | ||
458 | return -EINVAL; | ||
459 | break; | ||
460 | } | ||
461 | } | ||
462 | break; | ||
463 | } | ||
464 | |||
465 | /* Window/size parameters */ | ||
466 | case VIDIOCGWIN: | ||
467 | { | ||
468 | struct video_window *vw = arg; | ||
469 | |||
470 | vw->x = 0; | ||
471 | vw->y = 0; | ||
472 | vw->width = pdev->view.x; | ||
473 | vw->height = pdev->view.y; | ||
474 | vw->chromakey = 0; | ||
475 | vw->flags = (pdev->vframes << PWC_FPS_SHIFT) | | ||
476 | (pdev->vsnapshot ? PWC_FPS_SNAPSHOT : 0); | ||
477 | break; | ||
478 | } | ||
479 | |||
480 | case VIDIOCSWIN: | ||
481 | { | ||
482 | struct video_window *vw = arg; | ||
483 | int fps, snapshot, ret; | ||
484 | |||
485 | fps = (vw->flags & PWC_FPS_FRMASK) >> PWC_FPS_SHIFT; | ||
486 | snapshot = vw->flags & PWC_FPS_SNAPSHOT; | ||
487 | if (fps == 0) | ||
488 | fps = pdev->vframes; | ||
489 | if (pdev->view.x == vw->width && pdev->view.y && fps == pdev->vframes && snapshot == pdev->vsnapshot) | ||
490 | return 0; | ||
491 | ret = pwc_try_video_mode(pdev, vw->width, vw->height, fps, pdev->vcompression, snapshot); | ||
492 | if (ret) | ||
493 | return ret; | ||
494 | break; | ||
495 | } | ||
496 | |||
497 | /* We don't have overlay support (yet) */ | ||
498 | case VIDIOCGFBUF: | ||
499 | { | ||
500 | struct video_buffer *vb = arg; | ||
501 | |||
502 | memset(vb,0,sizeof(*vb)); | ||
503 | break; | ||
504 | } | ||
505 | |||
506 | /* mmap() functions */ | ||
507 | case VIDIOCGMBUF: | ||
508 | { | ||
509 | /* Tell the user program how much memory is needed for a mmap() */ | ||
510 | struct video_mbuf *vm = arg; | ||
511 | int i; | ||
512 | |||
513 | memset(vm, 0, sizeof(*vm)); | ||
514 | vm->size = pwc_mbufs * pdev->len_per_image; | ||
515 | vm->frames = pwc_mbufs; /* double buffering should be enough for most applications */ | ||
516 | for (i = 0; i < pwc_mbufs; i++) | ||
517 | vm->offsets[i] = i * pdev->len_per_image; | ||
518 | break; | ||
519 | } | ||
520 | |||
521 | case VIDIOCMCAPTURE: | ||
522 | { | ||
523 | /* Start capture into a given image buffer (called 'frame' in video_mmap structure) */ | ||
524 | struct video_mmap *vm = arg; | ||
525 | |||
526 | PWC_DEBUG_READ("VIDIOCMCAPTURE: %dx%d, frame %d, format %d\n", vm->width, vm->height, vm->frame, vm->format); | ||
527 | if (vm->frame < 0 || vm->frame >= pwc_mbufs) | ||
528 | return -EINVAL; | ||
529 | |||
530 | /* xawtv is nasty. It probes the available palettes | ||
531 | by setting a very small image size and trying | ||
532 | various palettes... The driver doesn't support | ||
533 | such small images, so I'm working around it. | ||
534 | */ | ||
535 | if (vm->format) | ||
536 | { | ||
537 | switch (vm->format) | ||
538 | { | ||
539 | case VIDEO_PALETTE_YUV420P: | ||
540 | case VIDEO_PALETTE_RAW: | ||
541 | break; | ||
542 | default: | ||
543 | return -EINVAL; | ||
544 | break; | ||
545 | } | ||
546 | } | ||
547 | |||
548 | if ((vm->width != pdev->view.x || vm->height != pdev->view.y) && | ||
549 | (vm->width >= pdev->view_min.x && vm->height >= pdev->view_min.y)) { | ||
550 | int ret; | ||
551 | |||
552 | PWC_DEBUG_OPEN("VIDIOCMCAPTURE: changing size to please xawtv :-(.\n"); | ||
553 | ret = pwc_try_video_mode(pdev, vm->width, vm->height, pdev->vframes, pdev->vcompression, pdev->vsnapshot); | ||
554 | if (ret) | ||
555 | return ret; | ||
556 | } /* ... size mismatch */ | ||
557 | |||
558 | /* FIXME: should we lock here? */ | ||
559 | if (pdev->image_used[vm->frame]) | ||
560 | return -EBUSY; /* buffer wasn't available. Bummer */ | ||
561 | pdev->image_used[vm->frame] = 1; | ||
562 | |||
563 | /* Okay, we're done here. In the SYNC call we wait until a | ||
564 | frame comes available, then expand image into the given | ||
565 | buffer. | ||
566 | In contrast to the CPiA cam the Philips cams deliver a | ||
567 | constant stream, almost like a grabber card. Also, | ||
568 | we have separate buffers for the rawdata and the image, | ||
569 | meaning we can nearly always expand into the requested buffer. | ||
570 | */ | ||
571 | PWC_DEBUG_READ("VIDIOCMCAPTURE done.\n"); | ||
572 | break; | ||
573 | } | ||
574 | |||
575 | case VIDIOCSYNC: | ||
576 | { | ||
577 | /* The doc says: "Whenever a buffer is used it should | ||
578 | call VIDIOCSYNC to free this frame up and continue." | ||
579 | |||
580 | The only odd thing about this whole procedure is | ||
581 | that MCAPTURE flags the buffer as "in use", and | ||
582 | SYNC immediately unmarks it, while it isn't | ||
583 | after SYNC that you know that the buffer actually | ||
584 | got filled! So you better not start a CAPTURE in | ||
585 | the same frame immediately (use double buffering). | ||
586 | This is not a problem for this cam, since it has | ||
587 | extra intermediate buffers, but a hardware | ||
588 | grabber card will then overwrite the buffer | ||
589 | you're working on. | ||
590 | */ | ||
591 | int *mbuf = arg; | ||
592 | int ret; | ||
593 | |||
594 | PWC_DEBUG_READ("VIDIOCSYNC called (%d).\n", *mbuf); | ||
595 | |||
596 | /* bounds check */ | ||
597 | if (*mbuf < 0 || *mbuf >= pwc_mbufs) | ||
598 | return -EINVAL; | ||
599 | /* check if this buffer was requested anyway */ | ||
600 | if (pdev->image_used[*mbuf] == 0) | ||
601 | return -EINVAL; | ||
602 | |||
603 | /* Add ourselves to the frame wait-queue. | ||
604 | |||
605 | FIXME: needs auditing for safety. | ||
606 | QUESTION: In what respect? I think that using the | ||
607 | frameq is safe now. | ||
608 | */ | ||
609 | add_wait_queue(&pdev->frameq, &wait); | ||
610 | while (pdev->full_frames == NULL) { | ||
611 | /* Check for unplugged/etc. here */ | ||
612 | if (pdev->error_status) { | ||
613 | remove_wait_queue(&pdev->frameq, &wait); | ||
614 | set_current_state(TASK_RUNNING); | ||
615 | return -pdev->error_status; | ||
616 | } | ||
617 | |||
618 | if (signal_pending(current)) { | ||
619 | remove_wait_queue(&pdev->frameq, &wait); | ||
620 | set_current_state(TASK_RUNNING); | ||
621 | return -ERESTARTSYS; | ||
622 | } | ||
623 | schedule(); | ||
624 | set_current_state(TASK_INTERRUPTIBLE); | ||
625 | } | ||
626 | remove_wait_queue(&pdev->frameq, &wait); | ||
627 | set_current_state(TASK_RUNNING); | ||
628 | |||
629 | /* The frame is ready. Expand in the image buffer | ||
630 | requested by the user. I don't care if you | ||
631 | mmap() 5 buffers and request data in this order: | ||
632 | buffer 4 2 3 0 1 2 3 0 4 3 1 . . . | ||
633 | Grabber hardware may not be so forgiving. | ||
634 | */ | ||
635 | PWC_DEBUG_READ("VIDIOCSYNC: frame ready.\n"); | ||
636 | pdev->fill_image = *mbuf; /* tell in which buffer we want the image to be expanded */ | ||
637 | /* Decompress, etc */ | ||
638 | ret = pwc_handle_frame(pdev); | ||
639 | pdev->image_used[*mbuf] = 0; | ||
640 | if (ret) | ||
641 | return -EFAULT; | ||
642 | break; | ||
643 | } | ||
644 | |||
645 | case VIDIOCGAUDIO: | ||
646 | { | ||
647 | struct video_audio *v = arg; | ||
648 | |||
649 | strcpy(v->name, "Microphone"); | ||
650 | v->audio = -1; /* unknown audio minor */ | ||
651 | v->flags = 0; | ||
652 | v->mode = VIDEO_SOUND_MONO; | ||
653 | v->volume = 0; | ||
654 | v->bass = 0; | ||
655 | v->treble = 0; | ||
656 | v->balance = 0x8000; | ||
657 | v->step = 1; | ||
658 | break; | ||
659 | } | ||
660 | |||
661 | case VIDIOCSAUDIO: | ||
662 | { | ||
663 | /* Dummy: nothing can be set */ | ||
664 | break; | ||
665 | } | ||
666 | |||
667 | case VIDIOCGUNIT: | ||
668 | { | ||
669 | struct video_unit *vu = arg; | ||
670 | |||
671 | vu->video = pdev->vdev->minor & 0x3F; | ||
672 | vu->audio = -1; /* not known yet */ | ||
673 | vu->vbi = -1; | ||
674 | vu->radio = -1; | ||
675 | vu->teletext = -1; | ||
676 | break; | ||
677 | } | ||
678 | |||
679 | /* V4L2 Layer */ | ||
680 | case VIDIOC_QUERYCAP: | ||
681 | { | ||
682 | struct v4l2_capability *cap = arg; | ||
683 | |||
684 | PWC_DEBUG_IOCTL("ioctl(VIDIOC_QUERYCAP) This application "\ | ||
685 | "try to use the v4l2 layer\n"); | ||
686 | strcpy(cap->driver,PWC_NAME); | ||
687 | strlcpy(cap->card, vdev->name, sizeof(cap->card)); | ||
688 | usb_make_path(pdev->udev,cap->bus_info,sizeof(cap->bus_info)); | ||
689 | cap->version = PWC_VERSION_CODE; | ||
690 | cap->capabilities = | ||
691 | V4L2_CAP_VIDEO_CAPTURE | | ||
692 | V4L2_CAP_STREAMING | | ||
693 | V4L2_CAP_READWRITE; | ||
694 | return 0; | ||
695 | } | ||
696 | |||
697 | case VIDIOC_ENUMINPUT: | ||
698 | { | ||
699 | struct v4l2_input *i = arg; | ||
700 | |||
701 | if ( i->index ) /* Only one INPUT is supported */ | ||
702 | return -EINVAL; | ||
703 | |||
704 | memset(i, 0, sizeof(struct v4l2_input)); | ||
705 | strcpy(i->name, "usb"); | ||
706 | return 0; | ||
707 | } | ||
708 | |||
709 | case VIDIOC_G_INPUT: | ||
710 | { | ||
711 | int *i = arg; | ||
712 | *i = 0; /* Only one INPUT is supported */ | ||
713 | return 0; | ||
714 | } | ||
715 | case VIDIOC_S_INPUT: | ||
716 | { | ||
717 | int *i = arg; | ||
718 | |||
719 | if ( *i ) { /* Only one INPUT is supported */ | ||
720 | PWC_DEBUG_IOCTL("Only one input source is"\ | ||
721 | " supported with this webcam.\n"); | ||
722 | return -EINVAL; | ||
723 | } | ||
724 | return 0; | ||
725 | } | ||
726 | |||
727 | /* TODO: */ | ||
728 | case VIDIOC_QUERYCTRL: | ||
729 | { | ||
730 | struct v4l2_queryctrl *c = arg; | ||
731 | int i; | ||
732 | |||
733 | PWC_DEBUG_IOCTL("ioctl(VIDIOC_QUERYCTRL) query id=%d\n", c->id); | ||
734 | for (i=0; i<sizeof(pwc_controls)/sizeof(struct v4l2_queryctrl); i++) { | ||
735 | if (pwc_controls[i].id == c->id) { | ||
736 | PWC_DEBUG_IOCTL("ioctl(VIDIOC_QUERYCTRL) found\n"); | ||
737 | memcpy(c,&pwc_controls[i],sizeof(struct v4l2_queryctrl)); | ||
738 | return 0; | ||
739 | } | ||
740 | } | ||
741 | PWC_DEBUG_IOCTL("ioctl(VIDIOC_QUERYCTRL) not found\n"); | ||
742 | |||
743 | return -EINVAL; | ||
744 | } | ||
745 | case VIDIOC_G_CTRL: | ||
746 | { | ||
747 | struct v4l2_control *c = arg; | ||
748 | int ret; | ||
749 | |||
750 | switch (c->id) | ||
751 | { | ||
752 | case V4L2_CID_BRIGHTNESS: | ||
753 | c->value = pwc_get_brightness(pdev); | ||
754 | if (c->value<0) | ||
755 | return -EINVAL; | ||
756 | return 0; | ||
757 | case V4L2_CID_CONTRAST: | ||
758 | c->value = pwc_get_contrast(pdev); | ||
759 | if (c->value<0) | ||
760 | return -EINVAL; | ||
761 | return 0; | ||
762 | case V4L2_CID_SATURATION: | ||
763 | ret = pwc_get_saturation(pdev, &c->value); | ||
764 | if (ret<0) | ||
765 | return -EINVAL; | ||
766 | return 0; | ||
767 | case V4L2_CID_GAMMA: | ||
768 | c->value = pwc_get_gamma(pdev); | ||
769 | if (c->value<0) | ||
770 | return -EINVAL; | ||
771 | return 0; | ||
772 | case V4L2_CID_RED_BALANCE: | ||
773 | ret = pwc_get_red_gain(pdev, &c->value); | ||
774 | if (ret<0) | ||
775 | return -EINVAL; | ||
776 | c->value >>= 8; | ||
777 | return 0; | ||
778 | case V4L2_CID_BLUE_BALANCE: | ||
779 | ret = pwc_get_blue_gain(pdev, &c->value); | ||
780 | if (ret<0) | ||
781 | return -EINVAL; | ||
782 | c->value >>= 8; | ||
783 | return 0; | ||
784 | case V4L2_CID_AUTO_WHITE_BALANCE: | ||
785 | ret = pwc_get_awb(pdev); | ||
786 | if (ret<0) | ||
787 | return -EINVAL; | ||
788 | c->value = (ret == PWC_WB_MANUAL)?0:1; | ||
789 | return 0; | ||
790 | case V4L2_CID_GAIN: | ||
791 | ret = pwc_get_agc(pdev, &c->value); | ||
792 | if (ret<0) | ||
793 | return -EINVAL; | ||
794 | c->value >>= 8; | ||
795 | return 0; | ||
796 | case V4L2_CID_AUTOGAIN: | ||
797 | ret = pwc_get_agc(pdev, &c->value); | ||
798 | if (ret<0) | ||
799 | return -EINVAL; | ||
800 | c->value = (c->value < 0)?1:0; | ||
801 | return 0; | ||
802 | case V4L2_CID_EXPOSURE: | ||
803 | ret = pwc_get_shutter_speed(pdev, &c->value); | ||
804 | if (ret<0) | ||
805 | return -EINVAL; | ||
806 | return 0; | ||
807 | case V4L2_CID_PRIVATE_COLOUR_MODE: | ||
808 | ret = pwc_get_colour_mode(pdev, &c->value); | ||
809 | if (ret < 0) | ||
810 | return -EINVAL; | ||
811 | return 0; | ||
812 | case V4L2_CID_PRIVATE_AUTOCONTOUR: | ||
813 | ret = pwc_get_contour(pdev, &c->value); | ||
814 | if (ret < 0) | ||
815 | return -EINVAL; | ||
816 | c->value=(c->value == -1?1:0); | ||
817 | return 0; | ||
818 | case V4L2_CID_PRIVATE_CONTOUR: | ||
819 | ret = pwc_get_contour(pdev, &c->value); | ||
820 | if (ret < 0) | ||
821 | return -EINVAL; | ||
822 | c->value >>= 10; | ||
823 | return 0; | ||
824 | case V4L2_CID_PRIVATE_BACKLIGHT: | ||
825 | ret = pwc_get_backlight(pdev, &c->value); | ||
826 | if (ret < 0) | ||
827 | return -EINVAL; | ||
828 | return 0; | ||
829 | case V4L2_CID_PRIVATE_FLICKERLESS: | ||
830 | ret = pwc_get_flicker(pdev, &c->value); | ||
831 | if (ret < 0) | ||
832 | return -EINVAL; | ||
833 | c->value=(c->value?1:0); | ||
834 | return 0; | ||
835 | case V4L2_CID_PRIVATE_NOISE_REDUCTION: | ||
836 | ret = pwc_get_dynamic_noise(pdev, &c->value); | ||
837 | if (ret < 0) | ||
838 | return -EINVAL; | ||
839 | return 0; | ||
840 | |||
841 | case V4L2_CID_PRIVATE_SAVE_USER: | ||
842 | case V4L2_CID_PRIVATE_RESTORE_USER: | ||
843 | case V4L2_CID_PRIVATE_RESTORE_FACTORY: | ||
844 | return -EINVAL; | ||
845 | } | ||
846 | return -EINVAL; | ||
847 | } | ||
848 | case VIDIOC_S_CTRL: | ||
849 | { | ||
850 | struct v4l2_control *c = arg; | ||
851 | int ret; | ||
852 | |||
853 | switch (c->id) | ||
854 | { | ||
855 | case V4L2_CID_BRIGHTNESS: | ||
856 | c->value <<= 9; | ||
857 | ret = pwc_set_brightness(pdev, c->value); | ||
858 | if (ret<0) | ||
859 | return -EINVAL; | ||
860 | return 0; | ||
861 | case V4L2_CID_CONTRAST: | ||
862 | c->value <<= 10; | ||
863 | ret = pwc_set_contrast(pdev, c->value); | ||
864 | if (ret<0) | ||
865 | return -EINVAL; | ||
866 | return 0; | ||
867 | case V4L2_CID_SATURATION: | ||
868 | ret = pwc_set_saturation(pdev, c->value); | ||
869 | if (ret<0) | ||
870 | return -EINVAL; | ||
871 | return 0; | ||
872 | case V4L2_CID_GAMMA: | ||
873 | c->value <<= 11; | ||
874 | ret = pwc_set_gamma(pdev, c->value); | ||
875 | if (ret<0) | ||
876 | return -EINVAL; | ||
877 | return 0; | ||
878 | case V4L2_CID_RED_BALANCE: | ||
879 | c->value <<= 8; | ||
880 | ret = pwc_set_red_gain(pdev, c->value); | ||
881 | if (ret<0) | ||
882 | return -EINVAL; | ||
883 | return 0; | ||
884 | case V4L2_CID_BLUE_BALANCE: | ||
885 | c->value <<= 8; | ||
886 | ret = pwc_set_blue_gain(pdev, c->value); | ||
887 | if (ret<0) | ||
888 | return -EINVAL; | ||
889 | return 0; | ||
890 | case V4L2_CID_AUTO_WHITE_BALANCE: | ||
891 | c->value = (c->value == 0)?PWC_WB_MANUAL:PWC_WB_AUTO; | ||
892 | ret = pwc_set_awb(pdev, c->value); | ||
893 | if (ret<0) | ||
894 | return -EINVAL; | ||
895 | return 0; | ||
896 | case V4L2_CID_EXPOSURE: | ||
897 | c->value <<= 8; | ||
898 | ret = pwc_set_shutter_speed(pdev, c->value?0:1, c->value); | ||
899 | if (ret<0) | ||
900 | return -EINVAL; | ||
901 | return 0; | ||
902 | case V4L2_CID_AUTOGAIN: | ||
903 | /* autogain off means nothing without a gain */ | ||
904 | if (c->value == 0) | ||
905 | return 0; | ||
906 | ret = pwc_set_agc(pdev, c->value, 0); | ||
907 | if (ret<0) | ||
908 | return -EINVAL; | ||
909 | return 0; | ||
910 | case V4L2_CID_GAIN: | ||
911 | c->value <<= 8; | ||
912 | ret = pwc_set_agc(pdev, 0, c->value); | ||
913 | if (ret<0) | ||
914 | return -EINVAL; | ||
915 | return 0; | ||
916 | case V4L2_CID_PRIVATE_SAVE_USER: | ||
917 | if (pwc_save_user(pdev)) | ||
918 | return -EINVAL; | ||
919 | return 0; | ||
920 | case V4L2_CID_PRIVATE_RESTORE_USER: | ||
921 | if (pwc_restore_user(pdev)) | ||
922 | return -EINVAL; | ||
923 | return 0; | ||
924 | case V4L2_CID_PRIVATE_RESTORE_FACTORY: | ||
925 | if (pwc_restore_factory(pdev)) | ||
926 | return -EINVAL; | ||
927 | return 0; | ||
928 | case V4L2_CID_PRIVATE_COLOUR_MODE: | ||
929 | ret = pwc_set_colour_mode(pdev, c->value); | ||
930 | if (ret < 0) | ||
931 | return -EINVAL; | ||
932 | return 0; | ||
933 | case V4L2_CID_PRIVATE_AUTOCONTOUR: | ||
934 | c->value=(c->value == 1)?-1:0; | ||
935 | ret = pwc_set_contour(pdev, c->value); | ||
936 | if (ret < 0) | ||
937 | return -EINVAL; | ||
938 | return 0; | ||
939 | case V4L2_CID_PRIVATE_CONTOUR: | ||
940 | c->value <<= 10; | ||
941 | ret = pwc_set_contour(pdev, c->value); | ||
942 | if (ret < 0) | ||
943 | return -EINVAL; | ||
944 | return 0; | ||
945 | case V4L2_CID_PRIVATE_BACKLIGHT: | ||
946 | ret = pwc_set_backlight(pdev, c->value); | ||
947 | if (ret < 0) | ||
948 | return -EINVAL; | ||
949 | return 0; | ||
950 | case V4L2_CID_PRIVATE_FLICKERLESS: | ||
951 | ret = pwc_set_flicker(pdev, c->value); | ||
952 | if (ret < 0) | ||
953 | return -EINVAL; | ||
954 | case V4L2_CID_PRIVATE_NOISE_REDUCTION: | ||
955 | ret = pwc_set_dynamic_noise(pdev, c->value); | ||
956 | if (ret < 0) | ||
957 | return -EINVAL; | ||
958 | return 0; | ||
959 | |||
960 | } | ||
961 | return -EINVAL; | ||
962 | } | ||
963 | |||
964 | case VIDIOC_ENUM_FMT: | ||
965 | { | ||
966 | struct v4l2_fmtdesc *f = arg; | ||
967 | int index; | ||
968 | |||
969 | if (f->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) | ||
970 | return -EINVAL; | ||
971 | |||
972 | /* We only support two format: the raw format, and YUV */ | ||
973 | index = f->index; | ||
974 | memset(f,0,sizeof(struct v4l2_fmtdesc)); | ||
975 | f->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; | ||
976 | f->index = index; | ||
977 | switch(index) | ||
978 | { | ||
979 | case 0: | ||
980 | /* RAW format */ | ||
981 | f->pixelformat = pdev->type<=646?V4L2_PIX_FMT_PWC1:V4L2_PIX_FMT_PWC2; | ||
982 | f->flags = V4L2_FMT_FLAG_COMPRESSED; | ||
983 | strlcpy(f->description,"Raw Philips Webcam",sizeof(f->description)); | ||
984 | break; | ||
985 | case 1: | ||
986 | f->pixelformat = V4L2_PIX_FMT_YUV420; | ||
987 | strlcpy(f->description,"4:2:0, planar, Y-Cb-Cr",sizeof(f->description)); | ||
988 | break; | ||
989 | default: | ||
990 | return -EINVAL; | ||
991 | } | ||
992 | return 0; | ||
993 | } | ||
994 | |||
995 | case VIDIOC_G_FMT: | ||
996 | { | ||
997 | struct v4l2_format *f = arg; | ||
998 | |||
999 | PWC_DEBUG_IOCTL("ioctl(VIDIOC_G_FMT) return size %dx%d\n",pdev->image.x,pdev->image.y); | ||
1000 | if (f->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) | ||
1001 | return -EINVAL; | ||
1002 | |||
1003 | pwc_vidioc_fill_fmt(pdev, f); | ||
1004 | |||
1005 | return 0; | ||
1006 | } | ||
1007 | |||
1008 | case VIDIOC_TRY_FMT: | ||
1009 | return pwc_vidioc_try_fmt(pdev, arg); | ||
1010 | |||
1011 | case VIDIOC_S_FMT: | ||
1012 | return pwc_vidioc_set_fmt(pdev, arg); | ||
1013 | |||
1014 | case VIDIOC_G_STD: | ||
1015 | { | ||
1016 | v4l2_std_id *std = arg; | ||
1017 | *std = V4L2_STD_UNKNOWN; | ||
1018 | return 0; | ||
1019 | } | ||
1020 | |||
1021 | case VIDIOC_S_STD: | ||
1022 | { | ||
1023 | v4l2_std_id *std = arg; | ||
1024 | if (*std != V4L2_STD_UNKNOWN) | ||
1025 | return -EINVAL; | ||
1026 | return 0; | ||
1027 | } | ||
1028 | |||
1029 | case VIDIOC_ENUMSTD: | ||
1030 | { | ||
1031 | struct v4l2_standard *std = arg; | ||
1032 | if (std->index != 0) | ||
1033 | return -EINVAL; | ||
1034 | std->id = V4L2_STD_UNKNOWN; | ||
1035 | strncpy(std->name, "webcam", sizeof(std->name)); | ||
1036 | return 0; | ||
1037 | } | ||
1038 | |||
1039 | case VIDIOC_REQBUFS: | ||
1040 | { | ||
1041 | struct v4l2_requestbuffers *rb = arg; | ||
1042 | int nbuffers; | ||
1043 | |||
1044 | PWC_DEBUG_IOCTL("ioctl(VIDIOC_REQBUFS) count=%d\n",rb->count); | ||
1045 | if (rb->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) | ||
1046 | return -EINVAL; | ||
1047 | if (rb->memory != V4L2_MEMORY_MMAP) | ||
1048 | return -EINVAL; | ||
1049 | |||
1050 | nbuffers = rb->count; | ||
1051 | if (nbuffers < 2) | ||
1052 | nbuffers = 2; | ||
1053 | else if (nbuffers > pwc_mbufs) | ||
1054 | nbuffers = pwc_mbufs; | ||
1055 | /* Force to use our # of buffers */ | ||
1056 | rb->count = pwc_mbufs; | ||
1057 | return 0; | ||
1058 | } | ||
1059 | |||
1060 | case VIDIOC_QUERYBUF: | ||
1061 | { | ||
1062 | struct v4l2_buffer *buf = arg; | ||
1063 | int index; | ||
1064 | |||
1065 | PWC_DEBUG_IOCTL("ioctl(VIDIOC_QUERYBUF) index=%d\n",buf->index); | ||
1066 | if (buf->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) { | ||
1067 | PWC_DEBUG_IOCTL("ioctl(VIDIOC_QUERYBUF) Bad type\n"); | ||
1068 | return -EINVAL; | ||
1069 | } | ||
1070 | if (buf->memory != V4L2_MEMORY_MMAP) { | ||
1071 | PWC_DEBUG_IOCTL("ioctl(VIDIOC_QUERYBUF) Bad memory type\n"); | ||
1072 | return -EINVAL; | ||
1073 | } | ||
1074 | index = buf->index; | ||
1075 | if (index < 0 || index >= pwc_mbufs) { | ||
1076 | PWC_DEBUG_IOCTL("ioctl(VIDIOC_QUERYBUF) Bad index %d\n", buf->index); | ||
1077 | return -EINVAL; | ||
1078 | } | ||
1079 | |||
1080 | memset(buf, 0, sizeof(struct v4l2_buffer)); | ||
1081 | buf->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; | ||
1082 | buf->index = index; | ||
1083 | buf->m.offset = index * pdev->len_per_image; | ||
1084 | if (pdev->vpalette == VIDEO_PALETTE_RAW) | ||
1085 | buf->bytesused = pdev->frame_size + sizeof(struct pwc_raw_frame); | ||
1086 | else | ||
1087 | buf->bytesused = pdev->view.size; | ||
1088 | buf->field = V4L2_FIELD_NONE; | ||
1089 | buf->memory = V4L2_MEMORY_MMAP; | ||
1090 | //buf->flags = V4L2_BUF_FLAG_MAPPED; | ||
1091 | buf->length = pdev->len_per_image; | ||
1092 | |||
1093 | PWC_DEBUG_READ("VIDIOC_QUERYBUF: index=%d\n",buf->index); | ||
1094 | PWC_DEBUG_READ("VIDIOC_QUERYBUF: m.offset=%d\n",buf->m.offset); | ||
1095 | PWC_DEBUG_READ("VIDIOC_QUERYBUF: bytesused=%d\n",buf->bytesused); | ||
1096 | |||
1097 | return 0; | ||
1098 | } | ||
1099 | |||
1100 | case VIDIOC_QBUF: | ||
1101 | { | ||
1102 | struct v4l2_buffer *buf = arg; | ||
1103 | |||
1104 | PWC_DEBUG_IOCTL("ioctl(VIDIOC_QBUF) index=%d\n",buf->index); | ||
1105 | if (buf->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) | ||
1106 | return -EINVAL; | ||
1107 | if (buf->memory != V4L2_MEMORY_MMAP) | ||
1108 | return -EINVAL; | ||
1109 | if (buf->index < 0 || buf->index >= pwc_mbufs) | ||
1110 | return -EINVAL; | ||
1111 | |||
1112 | buf->flags |= V4L2_BUF_FLAG_QUEUED; | ||
1113 | buf->flags &= ~V4L2_BUF_FLAG_DONE; | ||
1114 | |||
1115 | return 0; | ||
1116 | } | ||
1117 | |||
1118 | case VIDIOC_DQBUF: | ||
1119 | { | ||
1120 | struct v4l2_buffer *buf = arg; | ||
1121 | int ret; | ||
1122 | |||
1123 | PWC_DEBUG_IOCTL("ioctl(VIDIOC_DQBUF)\n"); | ||
1124 | |||
1125 | if (buf->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) | ||
1126 | return -EINVAL; | ||
1127 | |||
1128 | /* Add ourselves to the frame wait-queue. | ||
1129 | |||
1130 | FIXME: needs auditing for safety. | ||
1131 | QUESTION: In what respect? I think that using the | ||
1132 | frameq is safe now. | ||
1133 | */ | ||
1134 | add_wait_queue(&pdev->frameq, &wait); | ||
1135 | while (pdev->full_frames == NULL) { | ||
1136 | if (pdev->error_status) { | ||
1137 | remove_wait_queue(&pdev->frameq, &wait); | ||
1138 | set_current_state(TASK_RUNNING); | ||
1139 | return -pdev->error_status; | ||
1140 | } | ||
1141 | |||
1142 | if (signal_pending(current)) { | ||
1143 | remove_wait_queue(&pdev->frameq, &wait); | ||
1144 | set_current_state(TASK_RUNNING); | ||
1145 | return -ERESTARTSYS; | ||
1146 | } | ||
1147 | schedule(); | ||
1148 | set_current_state(TASK_INTERRUPTIBLE); | ||
1149 | } | ||
1150 | remove_wait_queue(&pdev->frameq, &wait); | ||
1151 | set_current_state(TASK_RUNNING); | ||
1152 | |||
1153 | PWC_DEBUG_IOCTL("VIDIOC_DQBUF: frame ready.\n"); | ||
1154 | /* Decompress data in pdev->images[pdev->fill_image] */ | ||
1155 | ret = pwc_handle_frame(pdev); | ||
1156 | if (ret) | ||
1157 | return -EFAULT; | ||
1158 | PWC_DEBUG_IOCTL("VIDIOC_DQBUF: after pwc_handle_frame\n"); | ||
1159 | |||
1160 | buf->index = pdev->fill_image; | ||
1161 | if (pdev->vpalette == VIDEO_PALETTE_RAW) | ||
1162 | buf->bytesused = pdev->frame_size + sizeof(struct pwc_raw_frame); | ||
1163 | else | ||
1164 | buf->bytesused = pdev->view.size; | ||
1165 | buf->flags = V4L2_BUF_FLAG_MAPPED; | ||
1166 | buf->field = V4L2_FIELD_NONE; | ||
1167 | do_gettimeofday(&buf->timestamp); | ||
1168 | buf->sequence = 0; | ||
1169 | buf->memory = V4L2_MEMORY_MMAP; | ||
1170 | buf->m.offset = pdev->fill_image * pdev->len_per_image; | ||
1171 | buf->length = buf->bytesused; | ||
1172 | pwc_next_image(pdev); | ||
1173 | |||
1174 | PWC_DEBUG_IOCTL("VIDIOC_DQBUF: buf->index=%d\n",buf->index); | ||
1175 | PWC_DEBUG_IOCTL("VIDIOC_DQBUF: buf->length=%d\n",buf->length); | ||
1176 | PWC_DEBUG_IOCTL("VIDIOC_DQBUF: m.offset=%d\n",buf->m.offset); | ||
1177 | PWC_DEBUG_IOCTL("VIDIOC_DQBUF: bytesused=%d\n",buf->bytesused); | ||
1178 | PWC_DEBUG_IOCTL("VIDIOC_DQBUF: leaving\n"); | ||
1179 | return 0; | ||
1180 | |||
1181 | } | ||
1182 | |||
1183 | case VIDIOC_STREAMON: | ||
1184 | { | ||
1185 | /* WARNING: pwc_try_video_mode() called pwc_isoc_init */ | ||
1186 | pwc_isoc_init(pdev); | ||
1187 | return 0; | ||
1188 | } | ||
1189 | |||
1190 | case VIDIOC_STREAMOFF: | ||
1191 | { | ||
1192 | pwc_isoc_cleanup(pdev); | ||
1193 | return 0; | ||
1194 | } | ||
1195 | |||
1196 | default: | ||
1197 | return pwc_ioctl(pdev, cmd, arg); | ||
1198 | } /* ..switch */ | ||
1199 | return 0; | ||
1200 | } | ||
1201 | |||
1202 | /* vim: set cino= formatoptions=croql cindent shiftwidth=8 tabstop=8: */ | ||