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