Merge git://git.kernel.org/pub/scm/linux/kernel/git/mchehab/v4l-dvb
[sfrench/cifs-2.6.git] / drivers / media / video / pwc / pwc-v4l.c
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 #ifdef CONFIG_USB_PWC_DEBUG
354         if (PWC_DEBUG_LEVEL_IOCTL & pwc_trace) {
355                 v4l_printk_ioctl(cmd);
356                 printk("\n");
357         }
358 #endif
359
360
361         switch (cmd) {
362                 /* Query cabapilities */
363                 case VIDIOCGCAP:
364                 {
365                         struct video_capability *caps = arg;
366
367                         strcpy(caps->name, vdev->name);
368                         caps->type = VID_TYPE_CAPTURE;
369                         caps->channels = 1;
370                         caps->audios = 1;
371                         caps->minwidth  = pdev->view_min.x;
372                         caps->minheight = pdev->view_min.y;
373                         caps->maxwidth  = pdev->view_max.x;
374                         caps->maxheight = pdev->view_max.y;
375                         break;
376                 }
377
378                 /* Channel functions (simulate 1 channel) */
379                 case VIDIOCGCHAN:
380                 {
381                         struct video_channel *v = arg;
382
383                         if (v->channel != 0)
384                                 return -EINVAL;
385                         v->flags = 0;
386                         v->tuners = 0;
387                         v->type = VIDEO_TYPE_CAMERA;
388                         strcpy(v->name, "Webcam");
389                         return 0;
390                 }
391
392                 case VIDIOCSCHAN:
393                 {
394                         /* The spec says the argument is an integer, but
395                            the bttv driver uses a video_channel arg, which
396                            makes sense becasue it also has the norm flag.
397                          */
398                         struct video_channel *v = arg;
399                         if (v->channel != 0)
400                                 return -EINVAL;
401                         return 0;
402                 }
403
404
405                 /* Picture functions; contrast etc. */
406                 case VIDIOCGPICT:
407                 {
408                         struct video_picture *p = arg;
409                         int val;
410
411                         val = pwc_get_brightness(pdev);
412                         if (val >= 0)
413                                 p->brightness = (val<<9);
414                         else
415                                 p->brightness = 0xffff;
416                         val = pwc_get_contrast(pdev);
417                         if (val >= 0)
418                                 p->contrast = (val<<10);
419                         else
420                                 p->contrast = 0xffff;
421                         /* Gamma, Whiteness, what's the difference? :) */
422                         val = pwc_get_gamma(pdev);
423                         if (val >= 0)
424                                 p->whiteness = (val<<11);
425                         else
426                                 p->whiteness = 0xffff;
427                         if (pwc_get_saturation(pdev, &val)<0)
428                                 p->colour = 0xffff;
429                         else
430                                 p->colour = 32768 + val * 327;
431                         p->depth = 24;
432                         p->palette = pdev->vpalette;
433                         p->hue = 0xFFFF; /* N/A */
434                         break;
435                 }
436
437                 case VIDIOCSPICT:
438                 {
439                         struct video_picture *p = arg;
440                         /*
441                          *      FIXME:  Suppose we are mid read
442                                 ANSWER: No problem: the firmware of the camera
443                                         can handle brightness/contrast/etc
444                                         changes at _any_ time, and the palette
445                                         is used exactly once in the uncompress
446                                         routine.
447                          */
448                         pwc_set_brightness(pdev, p->brightness);
449                         pwc_set_contrast(pdev, p->contrast);
450                         pwc_set_gamma(pdev, p->whiteness);
451                         pwc_set_saturation(pdev, (p->colour-32768)/327);
452                         if (p->palette && p->palette != pdev->vpalette) {
453                                 switch (p->palette) {
454                                         case VIDEO_PALETTE_YUV420P:
455                                         case VIDEO_PALETTE_RAW:
456                                                 pdev->vpalette = p->palette;
457                                                 return pwc_try_video_mode(pdev, pdev->image.x, pdev->image.y, pdev->vframes, pdev->vcompression, pdev->vsnapshot);
458                                                 break;
459                                         default:
460                                                 return -EINVAL;
461                                                 break;
462                                 }
463                         }
464                         break;
465                 }
466
467                 /* Window/size parameters */
468                 case VIDIOCGWIN:
469                 {
470                         struct video_window *vw = arg;
471
472                         vw->x = 0;
473                         vw->y = 0;
474                         vw->width = pdev->view.x;
475                         vw->height = pdev->view.y;
476                         vw->chromakey = 0;
477                         vw->flags = (pdev->vframes << PWC_FPS_SHIFT) |
478                                    (pdev->vsnapshot ? PWC_FPS_SNAPSHOT : 0);
479                         break;
480                 }
481
482                 case VIDIOCSWIN:
483                 {
484                         struct video_window *vw = arg;
485                         int fps, snapshot, ret;
486
487                         fps = (vw->flags & PWC_FPS_FRMASK) >> PWC_FPS_SHIFT;
488                         snapshot = vw->flags & PWC_FPS_SNAPSHOT;
489                         if (fps == 0)
490                                 fps = pdev->vframes;
491                         if (pdev->view.x == vw->width && pdev->view.y && fps == pdev->vframes && snapshot == pdev->vsnapshot)
492                                 return 0;
493                         ret = pwc_try_video_mode(pdev, vw->width, vw->height, fps, pdev->vcompression, snapshot);
494                         if (ret)
495                                 return ret;
496                         break;
497                 }
498
499                 /* We don't have overlay support (yet) */
500                 case VIDIOCGFBUF:
501                 {
502                         struct video_buffer *vb = arg;
503
504                         memset(vb,0,sizeof(*vb));
505                         break;
506                 }
507
508                 /* mmap() functions */
509                 case VIDIOCGMBUF:
510                 {
511                         /* Tell the user program how much memory is needed for a mmap() */
512                         struct video_mbuf *vm = arg;
513                         int i;
514
515                         memset(vm, 0, sizeof(*vm));
516                         vm->size = pwc_mbufs * pdev->len_per_image;
517                         vm->frames = pwc_mbufs; /* double buffering should be enough for most applications */
518                         for (i = 0; i < pwc_mbufs; i++)
519                                 vm->offsets[i] = i * pdev->len_per_image;
520                         break;
521                 }
522
523                 case VIDIOCMCAPTURE:
524                 {
525                         /* Start capture into a given image buffer (called 'frame' in video_mmap structure) */
526                         struct video_mmap *vm = arg;
527
528                         PWC_DEBUG_READ("VIDIOCMCAPTURE: %dx%d, frame %d, format %d\n", vm->width, vm->height, vm->frame, vm->format);
529                         if (vm->frame < 0 || vm->frame >= pwc_mbufs)
530                                 return -EINVAL;
531
532                         /* xawtv is nasty. It probes the available palettes
533                            by setting a very small image size and trying
534                            various palettes... The driver doesn't support
535                            such small images, so I'm working around it.
536                          */
537                         if (vm->format)
538                         {
539                                 switch (vm->format)
540                                 {
541                                         case VIDEO_PALETTE_YUV420P:
542                                         case VIDEO_PALETTE_RAW:
543                                                 break;
544                                         default:
545                                                 return -EINVAL;
546                                                 break;
547                                 }
548                         }
549
550                         if ((vm->width != pdev->view.x || vm->height != pdev->view.y) &&
551                             (vm->width >= pdev->view_min.x && vm->height >= pdev->view_min.y)) {
552                                 int ret;
553
554                                 PWC_DEBUG_OPEN("VIDIOCMCAPTURE: changing size to please xawtv :-(.\n");
555                                 ret = pwc_try_video_mode(pdev, vm->width, vm->height, pdev->vframes, pdev->vcompression, pdev->vsnapshot);
556                                 if (ret)
557                                         return ret;
558                         } /* ... size mismatch */
559
560                         /* FIXME: should we lock here? */
561                         if (pdev->image_used[vm->frame])
562                                 return -EBUSY;  /* buffer wasn't available. Bummer */
563                         pdev->image_used[vm->frame] = 1;
564
565                         /* Okay, we're done here. In the SYNC call we wait until a
566                            frame comes available, then expand image into the given
567                            buffer.
568                            In contrast to the CPiA cam the Philips cams deliver a
569                            constant stream, almost like a grabber card. Also,
570                            we have separate buffers for the rawdata and the image,
571                            meaning we can nearly always expand into the requested buffer.
572                          */
573                         PWC_DEBUG_READ("VIDIOCMCAPTURE done.\n");
574                         break;
575                 }
576
577                 case VIDIOCSYNC:
578                 {
579                         /* The doc says: "Whenever a buffer is used it should
580                            call VIDIOCSYNC to free this frame up and continue."
581
582                            The only odd thing about this whole procedure is
583                            that MCAPTURE flags the buffer as "in use", and
584                            SYNC immediately unmarks it, while it isn't
585                            after SYNC that you know that the buffer actually
586                            got filled! So you better not start a CAPTURE in
587                            the same frame immediately (use double buffering).
588                            This is not a problem for this cam, since it has
589                            extra intermediate buffers, but a hardware
590                            grabber card will then overwrite the buffer
591                            you're working on.
592                          */
593                         int *mbuf = arg;
594                         int ret;
595
596                         PWC_DEBUG_READ("VIDIOCSYNC called (%d).\n", *mbuf);
597
598                         /* bounds check */
599                         if (*mbuf < 0 || *mbuf >= pwc_mbufs)
600                                 return -EINVAL;
601                         /* check if this buffer was requested anyway */
602                         if (pdev->image_used[*mbuf] == 0)
603                                 return -EINVAL;
604
605                         /* Add ourselves to the frame wait-queue.
606
607                            FIXME: needs auditing for safety.
608                            QUESTION: In what respect? I think that using the
609                                      frameq is safe now.
610                          */
611                         add_wait_queue(&pdev->frameq, &wait);
612                         while (pdev->full_frames == NULL) {
613                                 /* Check for unplugged/etc. here */
614                                 if (pdev->error_status) {
615                                         remove_wait_queue(&pdev->frameq, &wait);
616                                         set_current_state(TASK_RUNNING);
617                                         return -pdev->error_status;
618                                 }
619
620                                 if (signal_pending(current)) {
621                                         remove_wait_queue(&pdev->frameq, &wait);
622                                         set_current_state(TASK_RUNNING);
623                                         return -ERESTARTSYS;
624                                 }
625                                 schedule();
626                                 set_current_state(TASK_INTERRUPTIBLE);
627                         }
628                         remove_wait_queue(&pdev->frameq, &wait);
629                         set_current_state(TASK_RUNNING);
630
631                         /* The frame is ready. Expand in the image buffer
632                            requested by the user. I don't care if you
633                            mmap() 5 buffers and request data in this order:
634                            buffer 4 2 3 0 1 2 3 0 4 3 1 . . .
635                            Grabber hardware may not be so forgiving.
636                          */
637                         PWC_DEBUG_READ("VIDIOCSYNC: frame ready.\n");
638                         pdev->fill_image = *mbuf; /* tell in which buffer we want the image to be expanded */
639                         /* Decompress, etc */
640                         ret = pwc_handle_frame(pdev);
641                         pdev->image_used[*mbuf] = 0;
642                         if (ret)
643                                 return -EFAULT;
644                         break;
645                 }
646
647                 case VIDIOCGAUDIO:
648                 {
649                         struct video_audio *v = arg;
650
651                         strcpy(v->name, "Microphone");
652                         v->audio = -1; /* unknown audio minor */
653                         v->flags = 0;
654                         v->mode = VIDEO_SOUND_MONO;
655                         v->volume = 0;
656                         v->bass = 0;
657                         v->treble = 0;
658                         v->balance = 0x8000;
659                         v->step = 1;
660                         break;
661                 }
662
663                 case VIDIOCSAUDIO:
664                 {
665                         /* Dummy: nothing can be set */
666                         break;
667                 }
668
669                 case VIDIOCGUNIT:
670                 {
671                         struct video_unit *vu = arg;
672
673                         vu->video = pdev->vdev->minor & 0x3F;
674                         vu->audio = -1; /* not known yet */
675                         vu->vbi = -1;
676                         vu->radio = -1;
677                         vu->teletext = -1;
678                         break;
679                 }
680
681                 /* V4L2 Layer */
682                 case VIDIOC_QUERYCAP:
683                 {
684                     struct v4l2_capability *cap = arg;
685
686                     PWC_DEBUG_IOCTL("ioctl(VIDIOC_QUERYCAP) This application "\
687                                        "try to use the v4l2 layer\n");
688                     strcpy(cap->driver,PWC_NAME);
689                     strlcpy(cap->card, vdev->name, sizeof(cap->card));
690                     usb_make_path(pdev->udev,cap->bus_info,sizeof(cap->bus_info));
691                     cap->version = PWC_VERSION_CODE;
692                     cap->capabilities =
693                         V4L2_CAP_VIDEO_CAPTURE  |
694                         V4L2_CAP_STREAMING      |
695                         V4L2_CAP_READWRITE;
696                     return 0;
697                 }
698
699                 case VIDIOC_ENUMINPUT:
700                 {
701                     struct v4l2_input *i = arg;
702
703                     if ( i->index )     /* Only one INPUT is supported */
704                           return -EINVAL;
705
706                     memset(i, 0, sizeof(struct v4l2_input));
707                     strcpy(i->name, "usb");
708                     return 0;
709                 }
710
711                 case VIDIOC_G_INPUT:
712                 {
713                     int *i = arg;
714                     *i = 0;     /* Only one INPUT is supported */
715                     return 0;
716                 }
717                 case VIDIOC_S_INPUT:
718                 {
719                         int *i = arg;
720
721                         if ( *i ) {     /* Only one INPUT is supported */
722                                 PWC_DEBUG_IOCTL("Only one input source is"\
723                                         " supported with this webcam.\n");
724                                 return -EINVAL;
725                         }
726                         return 0;
727                 }
728
729                 /* TODO: */
730                 case VIDIOC_QUERYCTRL:
731                 {
732                         struct v4l2_queryctrl *c = arg;
733                         int i;
734
735                         PWC_DEBUG_IOCTL("ioctl(VIDIOC_QUERYCTRL) query id=%d\n", c->id);
736                         for (i=0; i<sizeof(pwc_controls)/sizeof(struct v4l2_queryctrl); i++) {
737                                 if (pwc_controls[i].id == c->id) {
738                                         PWC_DEBUG_IOCTL("ioctl(VIDIOC_QUERYCTRL) found\n");
739                                         memcpy(c,&pwc_controls[i],sizeof(struct v4l2_queryctrl));
740                                         return 0;
741                                 }
742                         }
743                         PWC_DEBUG_IOCTL("ioctl(VIDIOC_QUERYCTRL) not found\n");
744
745                         return -EINVAL;
746                 }
747                 case VIDIOC_G_CTRL:
748                 {
749                         struct v4l2_control *c = arg;
750                         int ret;
751
752                         switch (c->id)
753                         {
754                                 case V4L2_CID_BRIGHTNESS:
755                                         c->value = pwc_get_brightness(pdev);
756                                         if (c->value<0)
757                                                 return -EINVAL;
758                                         return 0;
759                                 case V4L2_CID_CONTRAST:
760                                         c->value = pwc_get_contrast(pdev);
761                                         if (c->value<0)
762                                                 return -EINVAL;
763                                         return 0;
764                                 case V4L2_CID_SATURATION:
765                                         ret = pwc_get_saturation(pdev, &c->value);
766                                         if (ret<0)
767                                                 return -EINVAL;
768                                         return 0;
769                                 case V4L2_CID_GAMMA:
770                                         c->value = pwc_get_gamma(pdev);
771                                         if (c->value<0)
772                                                 return -EINVAL;
773                                         return 0;
774                                 case V4L2_CID_RED_BALANCE:
775                                         ret = pwc_get_red_gain(pdev, &c->value);
776                                         if (ret<0)
777                                                 return -EINVAL;
778                                         c->value >>= 8;
779                                         return 0;
780                                 case V4L2_CID_BLUE_BALANCE:
781                                         ret = pwc_get_blue_gain(pdev, &c->value);
782                                         if (ret<0)
783                                                 return -EINVAL;
784                                         c->value >>= 8;
785                                         return 0;
786                                 case V4L2_CID_AUTO_WHITE_BALANCE:
787                                         ret = pwc_get_awb(pdev);
788                                         if (ret<0)
789                                                 return -EINVAL;
790                                         c->value = (ret == PWC_WB_MANUAL)?0:1;
791                                         return 0;
792                                 case V4L2_CID_GAIN:
793                                         ret = pwc_get_agc(pdev, &c->value);
794                                         if (ret<0)
795                                                 return -EINVAL;
796                                         c->value >>= 8;
797                                         return 0;
798                                 case V4L2_CID_AUTOGAIN:
799                                         ret = pwc_get_agc(pdev, &c->value);
800                                         if (ret<0)
801                                                 return -EINVAL;
802                                         c->value = (c->value < 0)?1:0;
803                                         return 0;
804                                 case V4L2_CID_EXPOSURE:
805                                         ret = pwc_get_shutter_speed(pdev, &c->value);
806                                         if (ret<0)
807                                                 return -EINVAL;
808                                         return 0;
809                                 case V4L2_CID_PRIVATE_COLOUR_MODE:
810                                         ret = pwc_get_colour_mode(pdev, &c->value);
811                                         if (ret < 0)
812                                                 return -EINVAL;
813                                         return 0;
814                                 case V4L2_CID_PRIVATE_AUTOCONTOUR:
815                                         ret = pwc_get_contour(pdev, &c->value);
816                                         if (ret < 0)
817                                                 return -EINVAL;
818                                         c->value=(c->value == -1?1:0);
819                                         return 0;
820                                 case V4L2_CID_PRIVATE_CONTOUR:
821                                         ret = pwc_get_contour(pdev, &c->value);
822                                         if (ret < 0)
823                                                 return -EINVAL;
824                                         c->value >>= 10;
825                                         return 0;
826                                 case V4L2_CID_PRIVATE_BACKLIGHT:
827                                         ret = pwc_get_backlight(pdev, &c->value);
828                                         if (ret < 0)
829                                                 return -EINVAL;
830                                         return 0;
831                                 case V4L2_CID_PRIVATE_FLICKERLESS:
832                                         ret = pwc_get_flicker(pdev, &c->value);
833                                         if (ret < 0)
834                                                 return -EINVAL;
835                                         c->value=(c->value?1:0);
836                                         return 0;
837                                 case V4L2_CID_PRIVATE_NOISE_REDUCTION:
838                                         ret = pwc_get_dynamic_noise(pdev, &c->value);
839                                         if (ret < 0)
840                                                 return -EINVAL;
841                                         return 0;
842
843                                 case V4L2_CID_PRIVATE_SAVE_USER:
844                                 case V4L2_CID_PRIVATE_RESTORE_USER:
845                                 case V4L2_CID_PRIVATE_RESTORE_FACTORY:
846                                         return -EINVAL;
847                         }
848                         return -EINVAL;
849                 }
850                 case VIDIOC_S_CTRL:
851                 {
852                         struct v4l2_control *c = arg;
853                         int ret;
854
855                         switch (c->id)
856                         {
857                                 case V4L2_CID_BRIGHTNESS:
858                                         c->value <<= 9;
859                                         ret = pwc_set_brightness(pdev, c->value);
860                                         if (ret<0)
861                                                 return -EINVAL;
862                                         return 0;
863                                 case V4L2_CID_CONTRAST:
864                                         c->value <<= 10;
865                                         ret = pwc_set_contrast(pdev, c->value);
866                                         if (ret<0)
867                                                 return -EINVAL;
868                                         return 0;
869                                 case V4L2_CID_SATURATION:
870                                         ret = pwc_set_saturation(pdev, c->value);
871                                         if (ret<0)
872                                           return -EINVAL;
873                                         return 0;
874                                 case V4L2_CID_GAMMA:
875                                         c->value <<= 11;
876                                         ret = pwc_set_gamma(pdev, c->value);
877                                         if (ret<0)
878                                                 return -EINVAL;
879                                         return 0;
880                                 case V4L2_CID_RED_BALANCE:
881                                         c->value <<= 8;
882                                         ret = pwc_set_red_gain(pdev, c->value);
883                                         if (ret<0)
884                                                 return -EINVAL;
885                                         return 0;
886                                 case V4L2_CID_BLUE_BALANCE:
887                                         c->value <<= 8;
888                                         ret = pwc_set_blue_gain(pdev, c->value);
889                                         if (ret<0)
890                                                 return -EINVAL;
891                                         return 0;
892                                 case V4L2_CID_AUTO_WHITE_BALANCE:
893                                         c->value = (c->value == 0)?PWC_WB_MANUAL:PWC_WB_AUTO;
894                                         ret = pwc_set_awb(pdev, c->value);
895                                         if (ret<0)
896                                                 return -EINVAL;
897                                         return 0;
898                                 case V4L2_CID_EXPOSURE:
899                                         c->value <<= 8;
900                                         ret = pwc_set_shutter_speed(pdev, c->value?0:1, c->value);
901                                         if (ret<0)
902                                                 return -EINVAL;
903                                         return 0;
904                                 case V4L2_CID_AUTOGAIN:
905                                         /* autogain off means nothing without a gain */
906                                         if (c->value == 0)
907                                                 return 0;
908                                         ret = pwc_set_agc(pdev, c->value, 0);
909                                         if (ret<0)
910                                                 return -EINVAL;
911                                         return 0;
912                                 case V4L2_CID_GAIN:
913                                         c->value <<= 8;
914                                         ret = pwc_set_agc(pdev, 0, c->value);
915                                         if (ret<0)
916                                                 return -EINVAL;
917                                         return 0;
918                                 case V4L2_CID_PRIVATE_SAVE_USER:
919                                         if (pwc_save_user(pdev))
920                                                 return -EINVAL;
921                                         return 0;
922                                 case V4L2_CID_PRIVATE_RESTORE_USER:
923                                         if (pwc_restore_user(pdev))
924                                                 return -EINVAL;
925                                         return 0;
926                                 case V4L2_CID_PRIVATE_RESTORE_FACTORY:
927                                         if (pwc_restore_factory(pdev))
928                                                 return -EINVAL;
929                                         return 0;
930                                 case V4L2_CID_PRIVATE_COLOUR_MODE:
931                                         ret = pwc_set_colour_mode(pdev, c->value);
932                                         if (ret < 0)
933                                           return -EINVAL;
934                                         return 0;
935                                 case V4L2_CID_PRIVATE_AUTOCONTOUR:
936                                   c->value=(c->value == 1)?-1:0;
937                                   ret = pwc_set_contour(pdev, c->value);
938                                   if (ret < 0)
939                                     return -EINVAL;
940                                   return 0;
941                                 case V4L2_CID_PRIVATE_CONTOUR:
942                                   c->value <<= 10;
943                                   ret = pwc_set_contour(pdev, c->value);
944                                   if (ret < 0)
945                                     return -EINVAL;
946                                   return 0;
947                                 case V4L2_CID_PRIVATE_BACKLIGHT:
948                                   ret = pwc_set_backlight(pdev, c->value);
949                                   if (ret < 0)
950                                     return -EINVAL;
951                                   return 0;
952                                 case V4L2_CID_PRIVATE_FLICKERLESS:
953                                   ret = pwc_set_flicker(pdev, c->value);
954                                   if (ret < 0)
955                                     return -EINVAL;
956                                 case V4L2_CID_PRIVATE_NOISE_REDUCTION:
957                                   ret = pwc_set_dynamic_noise(pdev, c->value);
958                                   if (ret < 0)
959                                     return -EINVAL;
960                                   return 0;
961
962                         }
963                         return -EINVAL;
964                 }
965
966                 case VIDIOC_ENUM_FMT:
967                 {
968                         struct v4l2_fmtdesc *f = arg;
969                         int index;
970
971                         if (f->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
972                               return -EINVAL;
973
974                         /* We only support two format: the raw format, and YUV */
975                         index = f->index;
976                         memset(f,0,sizeof(struct v4l2_fmtdesc));
977                         f->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
978                         f->index = index;
979                         switch(index)
980                         {
981                                 case 0:
982                                         /* RAW format */
983                                         f->pixelformat = pdev->type<=646?V4L2_PIX_FMT_PWC1:V4L2_PIX_FMT_PWC2;
984                                         f->flags = V4L2_FMT_FLAG_COMPRESSED;
985                                         strlcpy(f->description,"Raw Philips Webcam",sizeof(f->description));
986                                         break;
987                                 case 1:
988                                         f->pixelformat = V4L2_PIX_FMT_YUV420;
989                                         strlcpy(f->description,"4:2:0, planar, Y-Cb-Cr",sizeof(f->description));
990                                         break;
991                                 default:
992                                         return -EINVAL;
993                         }
994                         return 0;
995                 }
996
997                 case VIDIOC_G_FMT:
998                 {
999                         struct v4l2_format *f = arg;
1000
1001                         PWC_DEBUG_IOCTL("ioctl(VIDIOC_G_FMT) return size %dx%d\n",pdev->image.x,pdev->image.y);
1002                         if (f->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
1003                               return -EINVAL;
1004
1005                         pwc_vidioc_fill_fmt(pdev, f);
1006
1007                         return 0;
1008                 }
1009
1010                 case VIDIOC_TRY_FMT:
1011                         return pwc_vidioc_try_fmt(pdev, arg);
1012
1013                 case VIDIOC_S_FMT:
1014                         return pwc_vidioc_set_fmt(pdev, arg);
1015
1016                 case VIDIOC_G_STD:
1017                 {
1018                         v4l2_std_id *std = arg;
1019                         *std = V4L2_STD_UNKNOWN;
1020                         return 0;
1021                 }
1022
1023                 case VIDIOC_S_STD:
1024                 {
1025                         v4l2_std_id *std = arg;
1026                         if (*std != V4L2_STD_UNKNOWN)
1027                                 return -EINVAL;
1028                         return 0;
1029                 }
1030
1031                 case VIDIOC_ENUMSTD:
1032                 {
1033                         struct v4l2_standard *std = arg;
1034                         if (std->index != 0)
1035                                 return -EINVAL;
1036                         std->id = V4L2_STD_UNKNOWN;
1037                         strncpy(std->name, "webcam", sizeof(std->name));
1038                         return 0;
1039                 }
1040
1041                 case VIDIOC_REQBUFS:
1042                 {
1043                         struct v4l2_requestbuffers *rb = arg;
1044                         int nbuffers;
1045
1046                         PWC_DEBUG_IOCTL("ioctl(VIDIOC_REQBUFS) count=%d\n",rb->count);
1047                         if (rb->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
1048                                 return -EINVAL;
1049                         if (rb->memory != V4L2_MEMORY_MMAP)
1050                                 return -EINVAL;
1051
1052                         nbuffers = rb->count;
1053                         if (nbuffers < 2)
1054                                 nbuffers = 2;
1055                         else if (nbuffers > pwc_mbufs)
1056                                 nbuffers = pwc_mbufs;
1057                         /* Force to use our # of buffers */
1058                         rb->count = pwc_mbufs;
1059                         return 0;
1060                 }
1061
1062                 case VIDIOC_QUERYBUF:
1063                 {
1064                         struct v4l2_buffer *buf = arg;
1065                         int index;
1066
1067                         PWC_DEBUG_IOCTL("ioctl(VIDIOC_QUERYBUF) index=%d\n",buf->index);
1068                         if (buf->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) {
1069                                 PWC_DEBUG_IOCTL("ioctl(VIDIOC_QUERYBUF) Bad type\n");
1070                                 return -EINVAL;
1071                         }
1072                         if (buf->memory != V4L2_MEMORY_MMAP) {
1073                                 PWC_DEBUG_IOCTL("ioctl(VIDIOC_QUERYBUF) Bad memory type\n");
1074                                 return -EINVAL;
1075                         }
1076                         index = buf->index;
1077                         if (index < 0 || index >= pwc_mbufs) {
1078                                 PWC_DEBUG_IOCTL("ioctl(VIDIOC_QUERYBUF) Bad index %d\n", buf->index);
1079                                 return -EINVAL;
1080                         }
1081
1082                         memset(buf, 0, sizeof(struct v4l2_buffer));
1083                         buf->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
1084                         buf->index = index;
1085                         buf->m.offset = index * pdev->len_per_image;
1086                         if (pdev->vpalette == VIDEO_PALETTE_RAW)
1087                                 buf->bytesused = pdev->frame_size + sizeof(struct pwc_raw_frame);
1088                         else
1089                                 buf->bytesused = pdev->view.size;
1090                         buf->field = V4L2_FIELD_NONE;
1091                         buf->memory = V4L2_MEMORY_MMAP;
1092                         //buf->flags = V4L2_BUF_FLAG_MAPPED;
1093                         buf->length = pdev->len_per_image;
1094
1095                         PWC_DEBUG_READ("VIDIOC_QUERYBUF: index=%d\n",buf->index);
1096                         PWC_DEBUG_READ("VIDIOC_QUERYBUF: m.offset=%d\n",buf->m.offset);
1097                         PWC_DEBUG_READ("VIDIOC_QUERYBUF: bytesused=%d\n",buf->bytesused);
1098
1099                         return 0;
1100                 }
1101
1102                 case VIDIOC_QBUF:
1103                 {
1104                         struct v4l2_buffer *buf = arg;
1105
1106                         PWC_DEBUG_IOCTL("ioctl(VIDIOC_QBUF) index=%d\n",buf->index);
1107                         if (buf->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
1108                                 return -EINVAL;
1109                         if (buf->memory != V4L2_MEMORY_MMAP)
1110                                 return -EINVAL;
1111                         if (buf->index < 0 || buf->index >= pwc_mbufs)
1112                                 return -EINVAL;
1113
1114                         buf->flags |= V4L2_BUF_FLAG_QUEUED;
1115                         buf->flags &= ~V4L2_BUF_FLAG_DONE;
1116
1117                         return 0;
1118                 }
1119
1120                 case VIDIOC_DQBUF:
1121                 {
1122                         struct v4l2_buffer *buf = arg;
1123                         int ret;
1124
1125                         PWC_DEBUG_IOCTL("ioctl(VIDIOC_DQBUF)\n");
1126
1127                         if (buf->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
1128                                 return -EINVAL;
1129
1130                         /* Add ourselves to the frame wait-queue.
1131
1132                            FIXME: needs auditing for safety.
1133                            QUESTION: In what respect? I think that using the
1134                                      frameq is safe now.
1135                          */
1136                         add_wait_queue(&pdev->frameq, &wait);
1137                         while (pdev->full_frames == NULL) {
1138                                 if (pdev->error_status) {
1139                                         remove_wait_queue(&pdev->frameq, &wait);
1140                                         set_current_state(TASK_RUNNING);
1141                                         return -pdev->error_status;
1142                                 }
1143
1144                                 if (signal_pending(current)) {
1145                                         remove_wait_queue(&pdev->frameq, &wait);
1146                                         set_current_state(TASK_RUNNING);
1147                                         return -ERESTARTSYS;
1148                                 }
1149                                 schedule();
1150                                 set_current_state(TASK_INTERRUPTIBLE);
1151                         }
1152                         remove_wait_queue(&pdev->frameq, &wait);
1153                         set_current_state(TASK_RUNNING);
1154
1155                         PWC_DEBUG_IOCTL("VIDIOC_DQBUF: frame ready.\n");
1156                         /* Decompress data in pdev->images[pdev->fill_image] */
1157                         ret = pwc_handle_frame(pdev);
1158                         if (ret)
1159                                 return -EFAULT;
1160                         PWC_DEBUG_IOCTL("VIDIOC_DQBUF: after pwc_handle_frame\n");
1161
1162                         buf->index = pdev->fill_image;
1163                         if (pdev->vpalette == VIDEO_PALETTE_RAW)
1164                                 buf->bytesused = pdev->frame_size + sizeof(struct pwc_raw_frame);
1165                         else
1166                                 buf->bytesused = pdev->view.size;
1167                         buf->flags = V4L2_BUF_FLAG_MAPPED;
1168                         buf->field = V4L2_FIELD_NONE;
1169                         do_gettimeofday(&buf->timestamp);
1170                         buf->sequence = 0;
1171                         buf->memory = V4L2_MEMORY_MMAP;
1172                         buf->m.offset = pdev->fill_image * pdev->len_per_image;
1173                         buf->length = pdev->len_per_image;
1174                         pwc_next_image(pdev);
1175
1176                         PWC_DEBUG_IOCTL("VIDIOC_DQBUF: buf->index=%d\n",buf->index);
1177                         PWC_DEBUG_IOCTL("VIDIOC_DQBUF: buf->length=%d\n",buf->length);
1178                         PWC_DEBUG_IOCTL("VIDIOC_DQBUF: m.offset=%d\n",buf->m.offset);
1179                         PWC_DEBUG_IOCTL("VIDIOC_DQBUF: bytesused=%d\n",buf->bytesused);
1180                         PWC_DEBUG_IOCTL("VIDIOC_DQBUF: leaving\n");
1181                         return 0;
1182
1183                 }
1184
1185                 case VIDIOC_STREAMON:
1186                 {
1187                         /* WARNING: pwc_try_video_mode() called pwc_isoc_init */
1188                         pwc_isoc_init(pdev);
1189                         return 0;
1190                 }
1191
1192                 case VIDIOC_STREAMOFF:
1193                 {
1194                         pwc_isoc_cleanup(pdev);
1195                         return 0;
1196                 }
1197
1198                 case VIDIOC_ENUM_FRAMESIZES:
1199                 {
1200                         struct v4l2_frmsizeenum *fsize = arg;
1201                         unsigned int i = 0, index = fsize->index;
1202
1203                         if (fsize->pixel_format == V4L2_PIX_FMT_YUV420) {
1204                                 for (i = 0; i < PSZ_MAX; i++) {
1205                                         if (pdev->image_mask & (1UL << i)) {
1206                                                 if (!index--) {
1207                                                         fsize->type = V4L2_FRMSIZE_TYPE_DISCRETE;
1208                                                         fsize->discrete.width = pwc_image_sizes[i].x;
1209                                                         fsize->discrete.height = pwc_image_sizes[i].y;
1210                                                         return 0;
1211                                                 }
1212                                         }
1213                                 }
1214                         } else if (fsize->index == 0 &&
1215                                    ((fsize->pixel_format == V4L2_PIX_FMT_PWC1 && DEVICE_USE_CODEC1(pdev->type)) ||
1216                                     (fsize->pixel_format == V4L2_PIX_FMT_PWC2 && DEVICE_USE_CODEC23(pdev->type)))) {
1217
1218                                 fsize->type = V4L2_FRMSIZE_TYPE_DISCRETE;
1219                                 fsize->discrete.width = pdev->abs_max.x;
1220                                 fsize->discrete.height = pdev->abs_max.y;
1221                                 return 0;
1222                         }
1223                         return -EINVAL;
1224                 }
1225
1226                 case VIDIOC_ENUM_FRAMEINTERVALS:
1227                 {
1228                         struct v4l2_frmivalenum *fival = arg;
1229                         int size = -1;
1230                         unsigned int i;
1231
1232                         for (i = 0; i < PSZ_MAX; i++) {
1233                                 if (pwc_image_sizes[i].x == fival->width &&
1234                                     pwc_image_sizes[i].y == fival->height) {
1235                                         size = i;
1236                                         break;
1237                                 }
1238                         }
1239
1240                         /* TODO: Support raw format */
1241                         if (size < 0 || fival->pixel_format != V4L2_PIX_FMT_YUV420) {
1242                                 return -EINVAL;
1243                         }
1244
1245                         i = pwc_get_fps(pdev, fival->index, size);
1246                         if (!i)
1247                                 return -EINVAL;
1248
1249                         fival->type = V4L2_FRMIVAL_TYPE_DISCRETE;
1250                         fival->discrete.numerator = 1;
1251                         fival->discrete.denominator = i;
1252
1253                         return 0;
1254                 }
1255
1256                 default:
1257                         return pwc_ioctl(pdev, cmd, arg);
1258         } /* ..switch */
1259         return 0;
1260 }
1261
1262 /* vim: set cino= formatoptions=croql cindent shiftwidth=8 tabstop=8: */