]> pilppa.org Git - linux-2.6-omap-h63xx.git/commitdiff
V4L/DVB (6585): Convert em28xx to video_ioctl2
authorMauro Carvalho Chehab <mchehab@infradead.org>
Sun, 11 Nov 2007 16:17:17 +0000 (13:17 -0300)
committerMauro Carvalho Chehab <mchehab@infradead.org>
Fri, 25 Jan 2008 21:02:07 +0000 (19:02 -0200)
Uses the newer ioctl handler at videodev. This patch also cleans up some
bad logic at the driver and do CodingStyle and other cleanups at the
resulting driver.

Also, since VIDIOCMBUF were not working, the V4L1 compat code were removed.
The compat code will eventually be re-inserted, if we find a clean way for
implementing compatibility with the old API.

Signed-off-by: Mauro Carvalho Chehab <mchehab@infradead.org>
drivers/media/video/em28xx/em28xx-video.c

index 8a4d221a4fe4d30e28ebae3de6c43e4cd4978435..022afb7b98c4cdf282c985d33298c64d360d72d8 100644 (file)
@@ -47,7 +47,7 @@
 
 #define DRIVER_NAME         "em28xx"
 #define DRIVER_DESC         "Empia em28xx based USB video device driver"
-#define EM28XX_VERSION_CODE  KERNEL_VERSION(0, 0, 1)
+#define EM28XX_VERSION_CODE  KERNEL_VERSION(0, 1, 0)
 
 #define em28xx_videodbg(fmt, arg...) do {\
        if (video_debug) \
@@ -202,8 +202,6 @@ static void video_mux(struct em28xx *dev, int index)
 
        em28xx_i2c_call_clients(dev, VIDIOC_INT_S_VIDEO_ROUTING, &route);
 
-       em28xx_videodbg("Setting input index=%d, vmux=%d, amux=%d\n",index,route.input,dev->ctl_ainput);
-
        if (dev->has_msp34xx) {
                if (dev->i2s_speed)
                        em28xx_i2c_call_clients(dev, VIDIOC_INT_I2S_CLOCK_FREQ, &dev->i2s_speed);
@@ -264,1297 +262,1391 @@ static void res_free(struct em28xx_fh *fh)
 }
 
 /*
- * em28xx_v4l2_open()
- * inits the device and starts isoc transfer
+ * em28xx_vm_open()
  */
-static int em28xx_v4l2_open(struct inode *inode, struct file *filp)
+static void em28xx_vm_open(struct vm_area_struct *vma)
 {
-       int minor = iminor(inode);
-       int errCode = 0;
-       struct em28xx *h,*dev = NULL;
-       struct em28xx_fh *fh;
+       struct em28xx_frame_t *f = vma->vm_private_data;
+       f->vma_use_count++;
+}
 
-       list_for_each_entry(h, &em28xx_devlist, devlist) {
-               if (h->vdev->minor == minor) {
-                       dev  = h;
-                       dev->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
-               }
-               if (h->vbi_dev->minor == minor) {
-                       dev  = h;
-                       dev->type = V4L2_BUF_TYPE_VBI_CAPTURE;
-               }
-       }
-       if (NULL == dev)
-               return -ENODEV;
+/*
+ * em28xx_vm_close()
+ */
+static void em28xx_vm_close(struct vm_area_struct *vma)
+{
+       /* NOTE: buffers are not freed here */
+       struct em28xx_frame_t *f = vma->vm_private_data;
 
-       em28xx_videodbg("open minor=%d type=%s users=%d\n",
-                               minor,v4l2_type_names[dev->type],dev->users);
+       if (f->vma_use_count)
+               f->vma_use_count--;
+}
 
-       fh = kzalloc(sizeof(struct em28xx_fh), GFP_KERNEL);
+static struct vm_operations_struct em28xx_vm_ops = {
+       .open = em28xx_vm_open,
+       .close = em28xx_vm_close,
+};
 
-       if (!fh) {
-               em28xx_errdev("em28xx-video.c: Out of memory?!\n");
-               return -ENOMEM;
+
+/*
+ * em28xx_get_ctrl()
+ * return the current saturation, brightness or contrast, mute state
+ */
+static int em28xx_get_ctrl(struct em28xx *dev, struct v4l2_control *ctrl)
+{
+       switch (ctrl->id) {
+       case V4L2_CID_AUDIO_MUTE:
+               ctrl->value = dev->mute;
+               return 0;
+       case V4L2_CID_AUDIO_VOLUME:
+               ctrl->value = dev->volume;
+               return 0;
+       default:
+               return -EINVAL;
        }
-       mutex_lock(&dev->lock);
-       fh->dev = dev;
-       filp->private_data = fh;
+}
 
-       if (dev->type == V4L2_BUF_TYPE_VIDEO_CAPTURE && dev->users == 0) {
-               em28xx_set_alternate(dev);
+/*
+ * em28xx_set_ctrl()
+ * mute or set new saturation, brightness or contrast
+ */
+static int em28xx_set_ctrl(struct em28xx *dev, const struct v4l2_control *ctrl)
+{
+       switch (ctrl->id) {
+       case V4L2_CID_AUDIO_MUTE:
+               if (ctrl->value != dev->mute) {
+                       dev->mute = ctrl->value;
+                       em28xx_audio_usb_mute(dev, ctrl->value);
+                       return em28xx_audio_analog_set(dev);
+               }
+               return 0;
+       case V4L2_CID_AUDIO_VOLUME:
+               dev->volume = ctrl->value;
+               return em28xx_audio_analog_set(dev);
+       default:
+               return -EINVAL;
+       }
+}
 
-               dev->width = norm_maxw(dev);
-               dev->height = norm_maxh(dev);
-               dev->frame_size = dev->width * dev->height * 2;
              dev->field_size = dev->frame_size >> 1; /*both_fileds ? dev->frame_size>>1 : dev->frame_size; */
-               dev->bytesperline = dev->width * 2;
-               dev->hscale = 0;
-               dev->vscale = 0;
+/*
+ * em28xx_stream_interrupt()
+ * stops streaming
+ */
+static int em28xx_stream_interrupt(struct em28xx *dev)
+{
+       int rc = 0;
 
-               em28xx_capture_start(dev, 1);
-               em28xx_resolution_set(dev);
+       /* stop reading from the device */
 
+       dev->stream = STREAM_INTERRUPT;
+       rc = wait_event_timeout(dev->wait_stream,
+                               (dev->stream == STREAM_OFF) ||
+                               (dev->state & DEV_DISCONNECTED),
+                               EM28XX_URB_TIMEOUT);
 
-               /* start the transfer */
-               errCode = em28xx_init_isoc(dev);
-               if (errCode)
-                       goto err;
+       if (rc) {
+               dev->state |= DEV_MISCONFIGURED;
+               em28xx_videodbg("device is misconfigured; close and "
+                       "open /dev/video%d again\n",
+                               dev->vdev->minor-MINOR_VFL_TYPE_GRABBER_MIN);
+               return rc;
+       }
 
-               em28xx_empty_framequeues(dev);
+       return 0;
+}
+
+
+static int check_dev(struct em28xx *dev)
+{
+       if (dev->state & DEV_DISCONNECTED) {
+               em28xx_errdev("v4l2 ioctl: device not present\n");
+               return -ENODEV;
        }
 
-       dev->users++;
+       if (dev->state & DEV_MISCONFIGURED) {
+               em28xx_errdev("v4l2 ioctl: device is misconfigured; "
+                             "close and open it again\n");
+               return -EIO;
+       }
+       return 0;
+}
 
-err:
-       mutex_unlock(&dev->lock);
-       return errCode;
+static void get_scale(struct em28xx *dev,
+                       unsigned int width, unsigned int height,
+                       unsigned int *hscale, unsigned int *vscale)
+{
+       unsigned int          maxw   = norm_maxw(dev);
+       unsigned int          maxh   = norm_maxh(dev);
+
+       *hscale = (((unsigned long)maxw) << 12) / width - 4096L;
+       if (*hscale >= 0x4000)
+               *hscale = 0x3fff;
+
+       *vscale = (((unsigned long)maxh) << 12) / height - 4096L;
+       if (*vscale >= 0x4000)
+               *vscale = 0x3fff;
 }
 
-/*
- * em28xx_realease_resources()
- * unregisters the v4l2,i2c and usb devices
- * called when the device gets disconected or at module unload
-*/
-static void em28xx_release_resources(struct em28xx *dev)
+/* ------------------------------------------------------------------
+       IOCTL vidioc handling
+   ------------------------------------------------------------------*/
+
+static int vidioc_g_fmt_cap(struct file *file, void *priv,
+                                       struct v4l2_format *f)
 {
+       struct em28xx_fh      *fh  = priv;
+       struct em28xx         *dev = fh->dev;
 
-       /*FIXME: I2C IR should be disconnected */
+       mutex_lock(&dev->lock);
 
-       em28xx_info("V4L2 devices /dev/video%d and /dev/vbi%d deregistered\n",
-                               dev->vdev->minor-MINOR_VFL_TYPE_GRABBER_MIN,
-                               dev->vbi_dev->minor-MINOR_VFL_TYPE_VBI_MIN);
-       list_del(&dev->devlist);
-       video_unregister_device(dev->vdev);
-       video_unregister_device(dev->vbi_dev);
-       em28xx_i2c_unregister(dev);
-       usb_put_dev(dev->udev);
+       f->fmt.pix.width = dev->width;
+       f->fmt.pix.height = dev->height;
+       f->fmt.pix.pixelformat = V4L2_PIX_FMT_YUYV;
+       f->fmt.pix.bytesperline = dev->bytesperline;
+       f->fmt.pix.sizeimage = dev->frame_size;
+       f->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M;
 
+       /* FIXME: TOP? NONE? BOTTOM? ALTENATE? */
+       f->fmt.pix.field = dev->interlaced ?
+                          V4L2_FIELD_INTERLACED : V4L2_FIELD_TOP;
 
-       /* Mark device as unused */
-       em28xx_devused&=~(1<<dev->devno);
+       mutex_unlock(&dev->lock);
+       return 0;
 }
 
-/*
- * em28xx_v4l2_close()
- * stops streaming and deallocates all resources allocated by the v4l2 calls and ioctls
- */
-static int em28xx_v4l2_close(struct inode *inode, struct file *filp)
+static int vidioc_try_fmt_cap(struct file *file, void *priv,
+                       struct v4l2_format *f)
 {
-       struct em28xx_fh *fh  = filp->private_data;
-       struct em28xx    *dev = fh->dev;
-       int              errCode;
+       struct em28xx_fh      *fh    = priv;
+       struct em28xx         *dev   = fh->dev;
+       int                   width  = f->fmt.pix.width;
+       int                   height = f->fmt.pix.height;
+       unsigned int          maxw   = norm_maxw(dev);
+       unsigned int          maxh   = norm_maxh(dev);
+       unsigned int          hscale, vscale;
+
+       /* width must even because of the YUYV format
+          height must be even because of interlacing */
+       height &= 0xfffe;
+       width &= 0xfffe;
 
-       em28xx_videodbg("users=%d\n", dev->users);
+       if (height < 32)
+               height = 32;
+       if (height > maxh)
+               height = maxh;
+       if (width < 48)
+               width = 48;
+       if (width > maxw)
+               width = maxw;
 
+       mutex_lock(&dev->lock);
 
-       if (res_check(fh))
-               res_free(fh);
+       if (dev->is_em2800) {
+               /* the em2800 can only scale down to 50% */
+               if (height % (maxh / 2))
+                       height = maxh;
+               if (width % (maxw / 2))
+                       width = maxw;
+               /* according to empiatech support */
+               /* the MaxPacketSize is to small to support */
+               /* framesizes larger than 640x480 @ 30 fps */
+               /* or 640x576 @ 25 fps. As this would cut */
+               /* of a part of the image we prefer */
+               /* 360x576 or 360x480 for now */
+               if (width == maxw && height == maxh)
+                       width /= 2;
+       }
 
-       mutex_lock(&dev->lock);
+       get_scale(dev, width, height, &hscale, &vscale);
 
-       if (dev->users == 1) {
-               em28xx_uninit_isoc(dev);
-               em28xx_release_buffers(dev);
-               dev->io = IO_NONE;
+       width = (((unsigned long)maxw) << 12) / (hscale + 4096L);
+       height = (((unsigned long)maxh) << 12) / (vscale + 4096L);
 
-               /* the device is already disconnect,
-                  free the remaining resources */
-               if (dev->state & DEV_DISCONNECTED) {
-                       em28xx_release_resources(dev);
-                       mutex_unlock(&dev->lock);
-                       kfree(dev);
-                       return 0;
-               }
+       f->fmt.pix.width = width;
+       f->fmt.pix.height = height;
+       f->fmt.pix.pixelformat = V4L2_PIX_FMT_YUYV;
+       f->fmt.pix.bytesperline = width * 2;
+       f->fmt.pix.sizeimage = width * 2 * height;
+       f->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M;
+       f->fmt.pix.field = V4L2_FIELD_INTERLACED;
 
-               /* set alternate 0 */
-               dev->alt = 0;
-               em28xx_videodbg("setting alternate 0\n");
-               errCode = usb_set_interface(dev->udev, 0, 0);
-               if (errCode < 0) {
-                       em28xx_errdev("cannot change alternate number to "
-                                       "0 (error=%i)\n", errCode);
-               }
-       }
-       kfree(fh);
-       dev->users--;
-       wake_up_interruptible_nr(&dev->open, 1);
        mutex_unlock(&dev->lock);
        return 0;
 }
 
-/*
- * em28xx_v4l2_read()
- * will allocate buffers when called for the first time
- */
-static ssize_t
-em28xx_v4l2_read(struct file *filp, char __user * buf, size_t count,
-                loff_t * f_pos)
+static int vidioc_s_fmt_cap(struct file *file, void *priv,
+                       struct v4l2_format *f)
 {
-       struct em28xx_frame_t *f, *i;
-       unsigned long lock_flags;
-       int ret = 0;
-       struct em28xx_fh *fh = filp->private_data;
-       struct em28xx *dev = fh->dev;
+       struct em28xx_fh      *fh  = priv;
+       struct em28xx         *dev = fh->dev;
+       int                   rc, i;
 
-       /* FIXME: read() is not prepared to allow changing the video
-          resolution while streaming. Seems a bug at em28xx_set_fmt
-        */
+       rc = check_dev(dev);
+       if (rc < 0)
+               return rc;
 
-       if (unlikely(res_get(fh) < 0))
-               return -EBUSY;
+       vidioc_try_fmt_cap(file, priv, f);
 
        mutex_lock(&dev->lock);
 
-       if (dev->type == V4L2_BUF_TYPE_VIDEO_CAPTURE)
-               em28xx_videodbg("V4l2_Buf_type_videocapture is set\n");
-
-       if (dev->type == V4L2_BUF_TYPE_VBI_CAPTURE) {
-               em28xx_videodbg("V4L2_BUF_TYPE_VBI_CAPTURE is set\n");
-               em28xx_videodbg("not supported yet! ...\n");
-               if (copy_to_user(buf, "", 1)) {
-                       mutex_unlock(&dev->lock);
-                       return -EFAULT;
+       for (i = 0; i < dev->num_frames; i++)
+               if (dev->frame[i].vma_use_count) {
+                       em28xx_videodbg("VIDIOC_S_FMT failed. "
+                                       "Unmap the buffers first.\n");
+                       rc = -EINVAL;
+                       goto err;
                }
-               mutex_unlock(&dev->lock);
-               return (1);
-       }
-       if (dev->type == V4L2_BUF_TYPE_SLICED_VBI_CAPTURE) {
-               em28xx_videodbg("V4L2_BUF_TYPE_SLICED_VBI_CAPTURE is set\n");
-               em28xx_videodbg("not supported yet! ...\n");
-               if (copy_to_user(buf, "", 1)) {
-                       mutex_unlock(&dev->lock);
-                       return -EFAULT;
-               }
-               mutex_unlock(&dev->lock);
-               return (1);
+
+       /* stop io in case it is already in progress */
+       if (dev->stream == STREAM_ON) {
+               em28xx_videodbg("VIDIOC_SET_FMT: interrupting stream\n");
+               rc = em28xx_stream_interrupt(dev);
+               if (rc < 0)
+                       goto err;
        }
 
-       if (dev->state & DEV_DISCONNECTED) {
-               em28xx_videodbg("device not present\n");
-               mutex_unlock(&dev->lock);
-               return -ENODEV;
-       }
+       em28xx_release_buffers(dev);
+       dev->io = IO_NONE;
 
-       if (dev->state & DEV_MISCONFIGURED) {
-               em28xx_videodbg("device misconfigured; close and open it again\n");
-               mutex_unlock(&dev->lock);
-               return -EIO;
-       }
+       /* set new image size */
+       dev->width = f->fmt.pix.width;
+       dev->height = f->fmt.pix.height;
+       dev->frame_size = dev->width * dev->height * 2;
+       dev->field_size = dev->frame_size >> 1;
+       dev->bytesperline = dev->width * 2;
+       get_scale(dev, dev->width, dev->height, &dev->hscale, &dev->vscale);
 
-       if (dev->io == IO_MMAP) {
-               em28xx_videodbg ("IO method is set to mmap; close and open"
-                               " the device again to choose the read method\n");
-               mutex_unlock(&dev->lock);
+       /* FIXME: This is really weird! Why capture is starting with
+          this ioctl ???
+        */
+       em28xx_uninit_isoc(dev);
+       em28xx_set_alternate(dev);
+       em28xx_capture_start(dev, 1);
+       em28xx_resolution_set(dev);
+       em28xx_init_isoc(dev);
+       rc = 0;
+
+err:
+       mutex_unlock(&dev->lock);
+       return rc;
+}
+
+static int vidioc_s_std(struct file *file, void *priv, v4l2_std_id *norm)
+{
+       struct em28xx_fh   *fh  = priv;
+       struct em28xx      *dev = fh->dev;
+       struct v4l2_format f;
+       unsigned int       i;
+       int                rc;
+
+       rc = check_dev(dev);
+       if (rc < 0)
+               return rc;
+
+       for (i = 0; i < TVNORMS; i++)
+               if (*norm == tvnorms[i].id)
+                       break;
+       if (i == TVNORMS)
+               for (i = 0; i < TVNORMS; i++)
+                       if (*norm & tvnorms[i].id)
+                               break;
+       if (i == TVNORMS)
                return -EINVAL;
-       }
 
-       if (dev->io == IO_NONE) {
-               if (!em28xx_request_buffers(dev, EM28XX_NUM_READ_FRAMES)) {
-                       em28xx_errdev("read failed, not enough memory\n");
-                       mutex_unlock(&dev->lock);
-                       return -ENOMEM;
-               }
-               dev->io = IO_READ;
-               dev->stream = STREAM_ON;
-               em28xx_queue_unusedframes(dev);
-       }
+       *norm = tvnorms[i].id;
 
-       if (!count) {
-               mutex_unlock(&dev->lock);
-               return 0;
-       }
+       mutex_lock(&dev->lock);
+       dev->tvnorm = &tvnorms[i];
+       mutex_unlock(&dev->lock);
 
-       if (list_empty(&dev->outqueue)) {
-               if (filp->f_flags & O_NONBLOCK) {
-                       mutex_unlock(&dev->lock);
-                       return -EAGAIN;
-               }
-               ret = wait_event_interruptible
-                   (dev->wait_frame,
-                    (!list_empty(&dev->outqueue)) ||
-                    (dev->state & DEV_DISCONNECTED));
-               if (ret) {
-                       mutex_unlock(&dev->lock);
-                       return ret;
-               }
-               if (dev->state & DEV_DISCONNECTED) {
-                       mutex_unlock(&dev->lock);
-                       return -ENODEV;
-               }
-               dev->video_bytesread = 0;
-       }
+       /* Adjusts width/height, if needed */
+       f.fmt.pix.width = dev->width;
+       f.fmt.pix.height = dev->height;
+       vidioc_try_fmt_cap(file, priv, &f);
 
-       f = list_entry(dev->outqueue.prev, struct em28xx_frame_t, frame);
+       mutex_lock(&dev->lock);
 
-       em28xx_queue_unusedframes(dev);
+       /* set new image size */
+       dev->width = f.fmt.pix.width;
+       dev->height = f.fmt.pix.height;
+       dev->frame_size = dev->width * dev->height * 2;
+       dev->field_size = dev->frame_size >> 1;
+       dev->bytesperline = dev->width * 2;
+       get_scale(dev, dev->width, dev->height, &dev->hscale, &dev->vscale);
 
-       if (count > f->buf.length)
-               count = f->buf.length;
+       em28xx_resolution_set(dev);
+       em28xx_i2c_call_clients(dev, VIDIOC_S_STD, &dev->tvnorm->id);
 
-       if ((dev->video_bytesread + count) > dev->frame_size)
-               count = dev->frame_size - dev->video_bytesread;
+       mutex_unlock(&dev->lock);
+       return 0;
+}
 
-       if (copy_to_user(buf, f->bufmem+dev->video_bytesread, count)) {
-               em28xx_err("Error while copying to user\n");
-               return -EFAULT;
-       }
-       dev->video_bytesread += count;
+static const char *iname[] = {
+       [EM28XX_VMUX_COMPOSITE1] = "Composite1",
+       [EM28XX_VMUX_COMPOSITE2] = "Composite2",
+       [EM28XX_VMUX_COMPOSITE3] = "Composite3",
+       [EM28XX_VMUX_COMPOSITE4] = "Composite4",
+       [EM28XX_VMUX_SVIDEO]     = "S-Video",
+       [EM28XX_VMUX_TELEVISION] = "Television",
+       [EM28XX_VMUX_CABLE]      = "Cable TV",
+       [EM28XX_VMUX_DVB]        = "DVB",
+       [EM28XX_VMUX_DEBUG]      = "for debug only",
+};
 
-       if (dev->video_bytesread == dev->frame_size) {
-               spin_lock_irqsave(&dev->queue_lock, lock_flags);
-               list_for_each_entry(i, &dev->outqueue, frame)
-                                   i->state = F_UNUSED;
-               INIT_LIST_HEAD(&dev->outqueue);
-               spin_unlock_irqrestore(&dev->queue_lock, lock_flags);
+static int vidioc_enum_input(struct file *file, void *priv,
+                               struct v4l2_input *i)
+{
+       struct em28xx_fh   *fh  = priv;
+       struct em28xx      *dev = fh->dev;
+       unsigned int       n;
 
-               em28xx_queue_unusedframes(dev);
-               dev->video_bytesread = 0;
-       }
+       n = i->index;
+       if (n >= MAX_EM28XX_INPUT)
+               return -EINVAL;
+       if (0 == INPUT(n)->type)
+               return -EINVAL;
 
-       *f_pos += count;
+       i->index = n;
+       i->type = V4L2_INPUT_TYPE_CAMERA;
 
-       mutex_unlock(&dev->lock);
+       strcpy(i->name, iname[INPUT(n)->type]);
 
-       return count;
+       if ((EM28XX_VMUX_TELEVISION == INPUT(n)->type) ||
+               (EM28XX_VMUX_CABLE == INPUT(n)->type))
+               i->type = V4L2_INPUT_TYPE_TUNER;
+
+       for (n = 0; n < ARRAY_SIZE(tvnorms); n++)
+               i->std |= tvnorms[n].id;
+
+       return 0;
 }
 
-/*
- * em28xx_v4l2_poll()
- * will allocate buffers when called for the first time
- */
-static unsigned int em28xx_v4l2_poll(struct file *filp, poll_table * wait)
+static int vidioc_g_input(struct file *file, void *priv, unsigned int *i)
 {
-       unsigned int mask = 0;
-       struct em28xx_fh *fh = filp->private_data;
-       struct em28xx *dev = fh->dev;
+       struct em28xx_fh   *fh  = priv;
+       struct em28xx      *dev = fh->dev;
 
-       if (unlikely(res_get(fh) < 0))
-               return POLLERR;
+       *i = dev->ctl_input;
+
+       return 0;
+}
+
+static int vidioc_s_input(struct file *file, void *priv, unsigned int i)
+{
+       struct em28xx_fh   *fh  = priv;
+       struct em28xx      *dev = fh->dev;
+       int                rc;
+
+       rc = check_dev(dev);
+       if (rc < 0)
+               return rc;
+
+       if (i >= MAX_EM28XX_INPUT)
+               return -EINVAL;
+       if (0 == INPUT(i)->type)
+               return -EINVAL;
 
        mutex_lock(&dev->lock);
 
-       if (dev->state & DEV_DISCONNECTED) {
-               em28xx_videodbg("device not present\n");
-       } else if (dev->state & DEV_MISCONFIGURED) {
-               em28xx_videodbg("device is misconfigured; close and open it again\n");
-       } else {
-               if (dev->io == IO_NONE) {
-                       if (!em28xx_request_buffers
-                           (dev, EM28XX_NUM_READ_FRAMES)) {
-                               em28xx_warn
-                                   ("poll() failed, not enough memory\n");
-                       } else {
-                               dev->io = IO_READ;
-                               dev->stream = STREAM_ON;
-                       }
-               }
+       video_mux(dev, i);
 
-               if (dev->io == IO_READ) {
-                       em28xx_queue_unusedframes(dev);
-                       poll_wait(filp, &dev->wait_frame, wait);
+       mutex_unlock(&dev->lock);
+       return 0;
+}
 
-                       if (!list_empty(&dev->outqueue))
-                               mask |= POLLIN | POLLRDNORM;
+static int vidioc_g_audio(struct file *file, void *priv, struct v4l2_audio *a)
+{
+       struct em28xx_fh   *fh    = priv;
+       struct em28xx      *dev   = fh->dev;
+       unsigned int        index = a->index;
 
-                       mutex_unlock(&dev->lock);
+       if (a->index > 1)
+               return -EINVAL;
 
-                       return mask;
-               }
+       index = dev->ctl_ainput;
+
+       if (index == 0) {
+               strcpy(a->name, "Television");
+       } else {
+               strcpy(a->name, "Line In");
        }
+       a->capability = V4L2_AUDCAP_STEREO;
+       a->index = index;
 
-       mutex_unlock(&dev->lock);
-       return POLLERR;
+       return 0;
 }
 
-/*
- * em28xx_vm_open()
- */
-static void em28xx_vm_open(struct vm_area_struct *vma)
+static int vidioc_s_audio(struct file *file, void *priv, struct v4l2_audio *a)
 {
-       struct em28xx_frame_t *f = vma->vm_private_data;
-       f->vma_use_count++;
+       struct em28xx_fh   *fh  = priv;
+       struct em28xx      *dev = fh->dev;
+
+       if (a->index != dev->ctl_ainput)
+               return -EINVAL;
+
+       return 0;
 }
 
-/*
- * em28xx_vm_close()
- */
-static void em28xx_vm_close(struct vm_area_struct *vma)
+static int vidioc_queryctrl(struct file *file, void *priv,
+                               struct v4l2_queryctrl *qc)
 {
-       /* NOTE: buffers are not freed here */
-       struct em28xx_frame_t *f = vma->vm_private_data;
+       struct em28xx_fh      *fh  = priv;
+       struct em28xx         *dev = fh->dev;
+       int                   id  = qc->id;
+       int                   i;
+       int                   rc;
+
+       rc = check_dev(dev);
+       if (rc < 0)
+               return rc;
 
-       if (f->vma_use_count)
-               f->vma_use_count--;
+       memset(qc, 0, sizeof(*qc));
+
+       qc->id = id;
+
+       if (!dev->has_msp34xx) {
+               for (i = 0; i < ARRAY_SIZE(em28xx_qctrl); i++) {
+                       if (qc->id && qc->id == em28xx_qctrl[i].id) {
+                               memcpy(qc, &(em28xx_qctrl[i]), sizeof(*qc));
+                               return 0;
+                       }
+               }
+       }
+       mutex_lock(&dev->lock);
+       em28xx_i2c_call_clients(dev, VIDIOC_QUERYCTRL, qc);
+       mutex_unlock(&dev->lock);
+
+       if (qc->type)
+               return 0;
+       else
+               return -EINVAL;
 }
 
-static struct vm_operations_struct em28xx_vm_ops = {
-       .open = em28xx_vm_open,
-       .close = em28xx_vm_close,
-};
+static int vidioc_g_ctrl(struct file *file, void *priv,
+                               struct v4l2_control *ctrl)
+{
+       struct em28xx_fh      *fh  = priv;
+       struct em28xx         *dev = fh->dev;
+       int                   rc;
 
-/*
- * em28xx_v4l2_mmap()
- */
-static int em28xx_v4l2_mmap(struct file *filp, struct vm_area_struct *vma)
+       rc = check_dev(dev);
+       if (rc < 0)
+               return rc;
+       mutex_lock(&dev->lock);
+
+       if (!dev->has_msp34xx)
+               rc = em28xx_get_ctrl(dev, ctrl);
+       else
+               rc = -EINVAL;
+
+       if (rc == -EINVAL) {
+               em28xx_i2c_call_clients(dev, VIDIOC_G_CTRL, ctrl);
+               rc = 0;
+       }
+
+       mutex_unlock(&dev->lock);
+       return rc;
+}
+
+static int vidioc_s_ctrl(struct file *file, void *priv,
+                               struct v4l2_control *ctrl)
 {
-       struct em28xx_fh *fh    = filp->private_data;
-       struct em28xx    *dev   = fh->dev;
-       unsigned long    size   = vma->vm_end - vma->vm_start;
-       unsigned long    start  = vma->vm_start;
-       void             *pos;
-       u32              i;
+       struct em28xx_fh      *fh  = priv;
+       struct em28xx         *dev = fh->dev;
+       u8                    i;
+       int                   rc;
 
-       if (unlikely(res_get(fh) < 0))
-               return -EBUSY;
+       rc = check_dev(dev);
+       if (rc < 0)
+               return rc;
 
        mutex_lock(&dev->lock);
 
-       if (dev->state & DEV_DISCONNECTED) {
-               em28xx_videodbg("mmap: device not present\n");
-               mutex_unlock(&dev->lock);
-               return -ENODEV;
+       if (dev->has_msp34xx)
+               em28xx_i2c_call_clients(dev, VIDIOC_S_CTRL, ctrl);
+       else {
+               rc = 1;
+               for (i = 0; i < ARRAY_SIZE(em28xx_qctrl); i++) {
+                       if (ctrl->id == em28xx_qctrl[i].id) {
+                               if (ctrl->value < em28xx_qctrl[i].minimum ||
+                                   ctrl->value > em28xx_qctrl[i].maximum) {
+                                       rc = -ERANGE;
+                                       break;
+                               }
+
+                               rc = em28xx_set_ctrl(dev, ctrl);
+                               break;
+                       }
+               }
        }
 
-       if (dev->state & DEV_MISCONFIGURED) {
-               em28xx_videodbg ("mmap: Device is misconfigured; close and "
-                                               "open it again\n");
-               mutex_unlock(&dev->lock);
-               return -EIO;
+       /* Control not found - try to send it to the attached devices */
+       if (rc == 1) {
+               em28xx_i2c_call_clients(dev, VIDIOC_S_CTRL, ctrl);
+               rc = 0;
        }
 
-       if (dev->io != IO_MMAP || !(vma->vm_flags & VM_WRITE) ||
-           size != PAGE_ALIGN(dev->frame[0].buf.length)) {
-               mutex_unlock(&dev->lock);
+       mutex_unlock(&dev->lock);
+       return rc;
+}
+
+static int vidioc_g_tuner(struct file *file, void *priv,
+                               struct v4l2_tuner *t)
+{
+       struct em28xx_fh      *fh  = priv;
+       struct em28xx         *dev = fh->dev;
+       int                   rc;
+
+       rc = check_dev(dev);
+       if (rc < 0)
+               return rc;
+
+       if (0 != t->index)
                return -EINVAL;
-       }
 
-       for (i = 0; i < dev->num_frames; i++) {
-               if ((dev->frame[i].buf.m.offset >> PAGE_SHIFT) == vma->vm_pgoff)
-                       break;
-       }
-       if (i == dev->num_frames) {
-               em28xx_videodbg("mmap: user supplied mapping address is out of range\n");
-               mutex_unlock(&dev->lock);
+       strcpy(t->name, "Tuner");
+
+       mutex_lock(&dev->lock);
+
+       em28xx_i2c_call_clients(dev, VIDIOC_G_TUNER, t);
+
+       mutex_unlock(&dev->lock);
+       return 0;
+}
+
+static int vidioc_s_tuner(struct file *file, void *priv,
+                               struct v4l2_tuner *t)
+{
+       struct em28xx_fh      *fh  = priv;
+       struct em28xx         *dev = fh->dev;
+       int                   rc;
+
+       rc = check_dev(dev);
+       if (rc < 0)
+               return rc;
+
+       if (0 != t->index)
                return -EINVAL;
-       }
 
-       /* VM_IO is eventually going to replace PageReserved altogether */
-       vma->vm_flags |= VM_IO;
-       vma->vm_flags |= VM_RESERVED;   /* avoid to swap out this VMA */
+       mutex_lock(&dev->lock);
 
-       pos = dev->frame[i].bufmem;
-       while (size > 0) {      /* size is page-aligned */
-               if (vm_insert_page(vma, start, vmalloc_to_page(pos))) {
-                       em28xx_videodbg("mmap: vm_insert_page failed\n");
-                       mutex_unlock(&dev->lock);
-                       return -EAGAIN;
-               }
-               start += PAGE_SIZE;
-               pos += PAGE_SIZE;
-               size -= PAGE_SIZE;
-       }
+       em28xx_i2c_call_clients(dev, VIDIOC_S_TUNER, t);
 
-       vma->vm_ops = &em28xx_vm_ops;
-       vma->vm_private_data = &dev->frame[i];
+       mutex_unlock(&dev->lock);
+       return 0;
+}
+
+static int vidioc_g_frequency(struct file *file, void *priv,
+                               struct v4l2_frequency *f)
+{
+       struct em28xx_fh      *fh  = priv;
+       struct em28xx         *dev = fh->dev;
+
+       f->type = V4L2_TUNER_ANALOG_TV;
+       f->frequency = dev->ctl_freq;
+
+       return 0;
+}
+
+static int vidioc_s_frequency(struct file *file, void *priv,
+                               struct v4l2_frequency *f)
+{
+       struct em28xx_fh      *fh  = priv;
+       struct em28xx         *dev = fh->dev;
+       int                   rc;
+
+       rc = check_dev(dev);
+       if (rc < 0)
+               return rc;
+
+       if (0 != f->tuner)
+               return -EINVAL;
+
+       if (V4L2_TUNER_ANALOG_TV != f->type)
+               return -EINVAL;
+
+       mutex_lock(&dev->lock);
+
+       dev->ctl_freq = f->frequency;
+       em28xx_i2c_call_clients(dev, VIDIOC_S_FREQUENCY, f);
 
-       em28xx_vm_open(vma);
        mutex_unlock(&dev->lock);
        return 0;
 }
 
-/*
- * em28xx_get_ctrl()
- * return the current saturation, brightness or contrast, mute state
- */
-static int em28xx_get_ctrl(struct em28xx *dev, struct v4l2_control *ctrl)
+static int vidioc_cropcap(struct file *file, void *priv,
+                                       struct v4l2_cropcap *cc)
 {
-       switch (ctrl->id) {
-       case V4L2_CID_AUDIO_MUTE:
-               ctrl->value = dev->mute;
-               return 0;
-       case V4L2_CID_AUDIO_VOLUME:
-               ctrl->value = dev->volume;
-               return 0;
-       default:
+       struct em28xx_fh      *fh  = priv;
+       struct em28xx         *dev = fh->dev;
+
+       if (cc->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
                return -EINVAL;
-       }
+
+       cc->bounds.left = 0;
+       cc->bounds.top = 0;
+       cc->bounds.width = dev->width;
+       cc->bounds.height = dev->height;
+       cc->defrect = cc->bounds;
+       cc->pixelaspect.numerator = 54; /* 4:3 FIXME: remove magic numbers */
+       cc->pixelaspect.denominator = 59;
+
+       return 0;
 }
 
-/*
- * em28xx_set_ctrl()
- * mute or set new saturation, brightness or contrast
- */
-static int em28xx_set_ctrl(struct em28xx *dev, const struct v4l2_control *ctrl)
+static int vidioc_streamon(struct file *file, void *priv,
+                                       enum v4l2_buf_type type)
 {
-       switch (ctrl->id) {
-       case V4L2_CID_AUDIO_MUTE:
-               if (ctrl->value != dev->mute) {
-                       dev->mute = ctrl->value;
-                       em28xx_audio_usb_mute(dev, ctrl->value);
-                       return em28xx_audio_analog_set(dev);
-               }
-               return 0;
-       case V4L2_CID_AUDIO_VOLUME:
-               dev->volume = ctrl->value;
-               return em28xx_audio_analog_set(dev);
-       default:
+       struct em28xx_fh      *fh  = priv;
+       struct em28xx         *dev = fh->dev;
+       int                   rc;
+
+       rc = check_dev(dev);
+       if (rc < 0)
+               return rc;
+
+       if (type != V4L2_BUF_TYPE_VIDEO_CAPTURE || dev->io != IO_MMAP)
+               return -EINVAL;
+
+       if (list_empty(&dev->inqueue))
                return -EINVAL;
+
+       mutex_lock(&dev->lock);
+
+       if (unlikely(res_get(fh) < 0)) {
+               mutex_unlock(&dev->lock);
+               return -EBUSY;
        }
+
+       dev->stream = STREAM_ON;        /* FIXME: Start video capture here? */
+
+       mutex_unlock(&dev->lock);
+       return 0;
 }
 
-/*
- * em28xx_stream_interrupt()
- * stops streaming
- */
-static int em28xx_stream_interrupt(struct em28xx *dev)
+static int vidioc_streamoff(struct file *file, void *priv,
+                                       enum v4l2_buf_type type)
 {
-       int ret = 0;
+       struct em28xx_fh      *fh  = priv;
+       struct em28xx         *dev = fh->dev;
+       int                   rc;
 
-       /* stop reading from the device */
+       rc = check_dev(dev);
+       if (rc < 0)
+               return rc;
 
-       dev->stream = STREAM_INTERRUPT;
-       ret = wait_event_timeout(dev->wait_stream,
-                                (dev->stream == STREAM_OFF) ||
-                                (dev->state & DEV_DISCONNECTED),
-                                EM28XX_URB_TIMEOUT);
-       if (dev->state & DEV_DISCONNECTED)
-               return -ENODEV;
-       else if (ret) {
-               dev->state |= DEV_MISCONFIGURED;
-               em28xx_videodbg("device is misconfigured; close and "
-                       "open /dev/video%d again\n",
-                               dev->vdev->minor-MINOR_VFL_TYPE_GRABBER_MIN);
-               return ret;
+       if (type != V4L2_BUF_TYPE_VIDEO_CAPTURE || dev->io != IO_MMAP)
+               return -EINVAL;
+
+       mutex_lock(&dev->lock);
+
+       if (dev->stream == STREAM_ON) {
+               em28xx_videodbg("VIDIOC_STREAMOFF: interrupting stream\n");
+               rc = em28xx_stream_interrupt(dev);
+               if (rc < 0) {
+                       mutex_unlock(&dev->lock);
+                       return rc;
+               }
        }
 
+       em28xx_empty_framequeues(dev);
+
+       mutex_unlock(&dev->lock);
        return 0;
 }
 
-static int em28xx_set_norm(struct em28xx *dev, int width, int height)
+static int vidioc_querycap(struct file *file, void  *priv,
+                                       struct v4l2_capability *cap)
 {
-       unsigned int hscale, vscale;
-       unsigned int maxh, maxw;
+       struct em28xx_fh      *fh  = priv;
+       struct em28xx         *dev = fh->dev;
 
-       maxw = norm_maxw(dev);
-       maxh = norm_maxh(dev);
+       strlcpy(cap->driver, "em28xx", sizeof(cap->driver));
+       strlcpy(cap->card, em28xx_boards[dev->model].name, sizeof(cap->card));
+       strlcpy(cap->bus_info, dev->udev->dev.bus_id, sizeof(cap->bus_info));
 
-       /* width must even because of the YUYV format */
-       /* height must be even because of interlacing */
-       height &= 0xfffe;
-       width &= 0xfffe;
+       cap->version = EM28XX_VERSION_CODE;
 
-       if (height < 32)
-               height = 32;
-       if (height > maxh)
-               height = maxh;
-       if (width < 48)
-               width = 48;
-       if (width > maxw)
-               width = maxw;
+       cap->capabilities =
+                       V4L2_CAP_SLICED_VBI_CAPTURE |
+                       V4L2_CAP_VIDEO_CAPTURE |
+                       V4L2_CAP_AUDIO |
+                       V4L2_CAP_READWRITE | V4L2_CAP_STREAMING;
 
-       if ((hscale = (((unsigned long)maxw) << 12) / width - 4096L) >= 0x4000)
-               hscale = 0x3fff;
-       width = (((unsigned long)maxw) << 12) / (hscale + 4096L);
+       if (dev->has_tuner)
+               cap->capabilities |= V4L2_CAP_TUNER;
 
-       if ((vscale = (((unsigned long)maxh) << 12) / height - 4096L) >= 0x4000)
-               vscale = 0x3fff;
-       height = (((unsigned long)maxh) << 12) / (vscale + 4096L);
+       return 0;
+}
 
-       /* set new image size */
-       dev->width = width;
-       dev->height = height;
-       dev->frame_size = dev->width * dev->height * 2;
-       dev->field_size = dev->frame_size >> 1; /*both_fileds ? dev->frame_size>>1 : dev->frame_size; */
-       dev->bytesperline = dev->width * 2;
-       dev->hscale = hscale;
-       dev->vscale = vscale;
+static int vidioc_enum_fmt_cap(struct file *file, void  *priv,
+                                       struct v4l2_fmtdesc *fmtd)
+{
+       if (fmtd->index != 0)
+               return -EINVAL;
 
-       em28xx_resolution_set(dev);
+       fmtd->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+       strcpy(fmtd->description, "Packed YUY2");
+       fmtd->pixelformat = V4L2_PIX_FMT_YUYV;
+       memset(fmtd->reserved, 0, sizeof(fmtd->reserved));
 
        return 0;
 }
 
-static int em28xx_get_fmt(struct em28xx *dev, struct v4l2_format *format)
+/* Sliced VBI ioctls */
+static int vidioc_g_fmt_vbi_capture(struct file *file, void *priv,
+                                       struct v4l2_format *f)
 {
-       em28xx_videodbg("VIDIOC_G_FMT: type=%s\n",
-               (format->type ==V4L2_BUF_TYPE_VIDEO_CAPTURE) ?
-               "V4L2_BUF_TYPE_VIDEO_CAPTURE" :
-               (format->type ==V4L2_BUF_TYPE_VBI_CAPTURE) ?
-               "V4L2_BUF_TYPE_VBI_CAPTURE" :
-               (format->type ==V4L2_CAP_SLICED_VBI_CAPTURE) ?
-               "V4L2_BUF_TYPE_SLICED_VBI_CAPTURE " :
-               "not supported");
-
-       switch (format->type) {
-       case V4L2_BUF_TYPE_VIDEO_CAPTURE:
-       {
-               format->fmt.pix.width = dev->width;
-               format->fmt.pix.height = dev->height;
-               format->fmt.pix.pixelformat = V4L2_PIX_FMT_YUYV;
-               format->fmt.pix.bytesperline = dev->bytesperline;
-               format->fmt.pix.sizeimage = dev->frame_size;
-               format->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M;
-               format->fmt.pix.field = dev->interlaced ? V4L2_FIELD_INTERLACED : V4L2_FIELD_TOP;       /* FIXME: TOP? NONE? BOTTOM? ALTENATE? */
-
-               em28xx_videodbg("VIDIOC_G_FMT: %dx%d\n", dev->width,
-                       dev->height);
-               break;
-       }
+       struct em28xx_fh      *fh  = priv;
+       struct em28xx         *dev = fh->dev;
+       int                   rc;
 
-       case V4L2_BUF_TYPE_SLICED_VBI_CAPTURE:
-       {
-               format->fmt.sliced.service_set=0;
+       rc = check_dev(dev);
+       if (rc < 0)
+               return rc;
 
-               em28xx_i2c_call_clients(dev,VIDIOC_G_FMT,format);
+       mutex_lock(&dev->lock);
 
-               if (format->fmt.sliced.service_set==0)
-                       return -EINVAL;
+       f->fmt.sliced.service_set = 0;
 
-               break;
-       }
+       em28xx_i2c_call_clients(dev, VIDIOC_G_FMT, f);
 
-       default:
+       if (f->fmt.sliced.service_set == 0)
+               rc = -EINVAL;
+
+       mutex_unlock(&dev->lock);
+       return rc;
+}
+
+static int vidioc_try_set_vbi_capture(struct file *file, void *priv,
+                       struct v4l2_format *f)
+{
+       struct em28xx_fh      *fh  = priv;
+       struct em28xx         *dev = fh->dev;
+       int                   rc;
+
+       rc = check_dev(dev);
+       if (rc < 0)
+               return rc;
+
+       mutex_lock(&dev->lock);
+       em28xx_i2c_call_clients(dev, VIDIOC_G_FMT, f);
+       mutex_unlock(&dev->lock);
+
+       if (f->fmt.sliced.service_set == 0)
                return -EINVAL;
-       }
-       return (0);
+
+       return 0;
 }
 
-static int em28xx_set_fmt(struct em28xx *dev, unsigned int cmd, struct v4l2_format *format)
+
+static int vidioc_reqbufs(struct file *file, void *priv,
+                         struct v4l2_requestbuffers *rb)
 {
-       u32 i;
-       int ret = 0;
-       int width = format->fmt.pix.width;
-       int height = format->fmt.pix.height;
-       unsigned int hscale, vscale;
-       unsigned int maxh, maxw;
+       struct em28xx_fh      *fh  = priv;
+       struct em28xx         *dev = fh->dev;
+       u32                   i;
+       int                   rc;
 
-       maxw = norm_maxw(dev);
-       maxh = norm_maxh(dev);
+       rc = check_dev(dev);
+       if (rc < 0)
+               return rc;
 
-       em28xx_videodbg("%s: type=%s\n",
-                       cmd == VIDIOC_TRY_FMT ?
-                       "VIDIOC_TRY_FMT" : "VIDIOC_S_FMT",
-                       format->type == V4L2_BUF_TYPE_VIDEO_CAPTURE ?
-                       "V4L2_BUF_TYPE_VIDEO_CAPTURE" :
-                       format->type == V4L2_BUF_TYPE_VBI_CAPTURE ?
-                       "V4L2_BUF_TYPE_VBI_CAPTURE " :
-                       "not supported");
+       if (rb->type != V4L2_BUF_TYPE_VIDEO_CAPTURE ||
+               rb->memory != V4L2_MEMORY_MMAP)
+               return -EINVAL;
 
-       if (format->type == V4L2_BUF_TYPE_SLICED_VBI_CAPTURE) {
-               em28xx_i2c_call_clients(dev,VIDIOC_G_FMT,format);
+       if (dev->io == IO_READ) {
+               em28xx_videodbg("method is set to read;"
+                               " close and open the device again to"
+                               " choose the mmap I/O method\n");
+               return -EINVAL;
+       }
 
-               if (format->fmt.sliced.service_set==0)
+       for (i = 0; i < dev->num_frames; i++)
+               if (dev->frame[i].vma_use_count) {
+                       em28xx_videodbg("VIDIOC_REQBUFS failed; "
+                                       "previous buffers are still mapped\n");
                        return -EINVAL;
+               }
 
-               return 0;
+       mutex_lock(&dev->lock);
+
+       if (dev->stream == STREAM_ON) {
+               em28xx_videodbg("VIDIOC_REQBUFS: interrupting stream\n");
+               rc = em28xx_stream_interrupt(dev);
+               if (rc < 0) {
+                       mutex_unlock(&dev->lock);
+                       return rc;
+               }
        }
 
+       em28xx_empty_framequeues(dev);
+
+       em28xx_release_buffers(dev);
+       if (rb->count)
+               rb->count = em28xx_request_buffers(dev, rb->count);
+
+       dev->frame_current = NULL;
+       dev->io = rb->count ? IO_MMAP : IO_NONE;
+
+       mutex_unlock(&dev->lock);
+       return 0;
+}
 
-       if (format->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+static int vidioc_querybuf(struct file *file, void *priv,
+                          struct v4l2_buffer *b)
+{
+       struct em28xx_fh      *fh  = priv;
+       struct em28xx         *dev = fh->dev;
+       int                   rc;
+
+       rc = check_dev(dev);
+       if (rc < 0)
+               return rc;
+
+       if (b->type != V4L2_BUF_TYPE_VIDEO_CAPTURE ||
+               b->index >= dev->num_frames || dev->io != IO_MMAP)
                return -EINVAL;
 
-       em28xx_videodbg("%s: requested %dx%d\n",
-               cmd == VIDIOC_TRY_FMT ?
-               "VIDIOC_TRY_FMT" : "VIDIOC_S_FMT",
-               format->fmt.pix.width, format->fmt.pix.height);
+       mutex_lock(&dev->lock);
 
-       /* FIXME: Move some code away from here */
-       /* width must even because of the YUYV format */
-       /* height must be even because of interlacing */
-       height &= 0xfffe;
-       width &= 0xfffe;
+       memcpy(b, &dev->frame[b->index].buf, sizeof(*b));
 
-       if (height < 32)
-               height = 32;
-       if (height > maxh)
-               height = maxh;
-       if (width < 48)
-               width = 48;
-       if (width > maxw)
-               width = maxw;
+       if (dev->frame[b->index].vma_use_count)
+               b->flags |= V4L2_BUF_FLAG_MAPPED;
+
+       if (dev->frame[b->index].state == F_DONE)
+               b->flags |= V4L2_BUF_FLAG_DONE;
+       else if (dev->frame[b->index].state != F_UNUSED)
+               b->flags |= V4L2_BUF_FLAG_QUEUED;
+
+       mutex_unlock(&dev->lock);
+       return 0;
+}
+
+static int vidioc_qbuf(struct file *file, void *priv, struct v4l2_buffer *b)
+{
+       struct em28xx_fh      *fh  = priv;
+       struct em28xx         *dev = fh->dev;
+       unsigned long         lock_flags;
+       int                   rc;
 
-       if(dev->is_em2800){
-               /* the em2800 can only scale down to 50% */
-               if(height % (maxh / 2))
-                       height=maxh;
-               if(width % (maxw / 2))
-                       width=maxw;
-               /* according to empiatech support */
-               /* the MaxPacketSize is to small to support */
-               /* framesizes larger than 640x480 @ 30 fps */
-               /* or 640x576 @ 25 fps. As this would cut */
-               /* of a part of the image we prefer */
-               /* 360x576 or 360x480 for now */
-               if(width == maxw && height == maxh)
-                       width /= 2;
-       }
+       rc = check_dev(dev);
+       if (rc < 0)
+               return rc;
 
-       if ((hscale = (((unsigned long)maxw) << 12) / width - 4096L) >= 0x4000)
-               hscale = 0x3fff;
+       if (b->type != V4L2_BUF_TYPE_VIDEO_CAPTURE  || dev->io != IO_MMAP ||
+                                               b->index >= dev->num_frames)
+               return -EINVAL;
 
-       width = (((unsigned long)maxw) << 12) / (hscale + 4096L);
+       if (dev->frame[b->index].state != F_UNUSED)
+               return -EAGAIN;
 
-       if ((vscale = (((unsigned long)maxh) << 12) / height - 4096L) >= 0x4000)
-               vscale = 0x3fff;
+       dev->frame[b->index].state = F_QUEUED;
 
-       height = (((unsigned long)maxh) << 12) / (vscale + 4096L);
+       /* add frame to fifo */
+       spin_lock_irqsave(&dev->queue_lock, lock_flags);
+       list_add_tail(&dev->frame[b->index].frame, &dev->inqueue);
+       spin_unlock_irqrestore(&dev->queue_lock, lock_flags);
 
-       format->fmt.pix.width = width;
-       format->fmt.pix.height = height;
-       format->fmt.pix.pixelformat = V4L2_PIX_FMT_YUYV;
-       format->fmt.pix.bytesperline = width * 2;
-       format->fmt.pix.sizeimage = width * 2 * height;
-       format->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M;
-       format->fmt.pix.field = V4L2_FIELD_INTERLACED;
+       return 0;
+}
 
-       em28xx_videodbg("%s: returned %dx%d (%d, %d)\n",
-               cmd == VIDIOC_TRY_FMT ?
-               "VIDIOC_TRY_FMT" :"VIDIOC_S_FMT",
-               format->fmt.pix.width, format->fmt.pix.height, hscale, vscale);
+static int vidioc_dqbuf(struct file *file, void *priv, struct v4l2_buffer *b)
+{
+       struct em28xx_fh      *fh  = priv;
+       struct em28xx         *dev = fh->dev;
+       int                   rc;
+       struct em28xx_frame_t *f;
+       unsigned long         lock_flags;
+
+       rc = check_dev(dev);
+       if (rc < 0)
+               return rc;
 
-       if (cmd == VIDIOC_TRY_FMT)
-               return 0;
+       if (b->type != V4L2_BUF_TYPE_VIDEO_CAPTURE || dev->io != IO_MMAP)
+               return -EINVAL;
 
-       for (i = 0; i < dev->num_frames; i++)
-               if (dev->frame[i].vma_use_count) {
-                       em28xx_videodbg("VIDIOC_S_FMT failed. "
-                               "Unmap the buffers first.\n");
+       if (list_empty(&dev->outqueue)) {
+               if (dev->stream == STREAM_OFF)
                        return -EINVAL;
-               }
 
-       /* stop io in case it is already in progress */
-       if (dev->stream == STREAM_ON) {
-               em28xx_videodbg("VIDIOC_SET_FMT: interrupting stream\n");
-               if ((ret = em28xx_stream_interrupt(dev)))
-                       return ret;
+               if (file->f_flags & O_NONBLOCK)
+                       return -EAGAIN;
+
+               rc = wait_event_interruptible(dev->wait_frame,
+                                       (!list_empty(&dev->outqueue)) ||
+                                       (dev->state & DEV_DISCONNECTED));
+               if (rc)
+                       return rc;
+
+               if (dev->state & DEV_DISCONNECTED)
+                       return -ENODEV;
        }
 
-       em28xx_release_buffers(dev);
-       dev->io = IO_NONE;
+       spin_lock_irqsave(&dev->queue_lock, lock_flags);
+       f = list_entry(dev->outqueue.next, struct em28xx_frame_t, frame);
+       list_del(dev->outqueue.next);
+       spin_unlock_irqrestore(&dev->queue_lock, lock_flags);
 
-       /* set new image size */
-       dev->width = width;
-       dev->height = height;
-       dev->frame_size = dev->width * dev->height * 2;
-       dev->field_size = dev->frame_size >> 1;
-       dev->bytesperline = dev->width * 2;
-       dev->hscale = hscale;
-       dev->vscale = vscale;
-       em28xx_uninit_isoc(dev);
-       em28xx_set_alternate(dev);
-       em28xx_capture_start(dev, 1);
-       em28xx_resolution_set(dev);
-       em28xx_init_isoc(dev);
+       f->state = F_UNUSED;
+       memcpy(b, &f->buf, sizeof(*b));
+
+       if (f->vma_use_count)
+               b->flags |= V4L2_BUF_FLAG_MAPPED;
 
        return 0;
 }
 
 /*
- * em28xx_v4l2_do_ioctl()
- * This function is _not_ called directly, but from
- * em28xx_v4l2_ioctl. Userspace
- * copying is done already, arg is a kernel pointer.
+ * em28xx_v4l2_open()
+ * inits the device and starts isoc transfer
  */
-static int em28xx_do_ioctl(struct inode *inode, struct file *filp,
-                          struct em28xx *dev, unsigned int cmd, void *arg,
-                          v4l2_kioctl driver_ioctl)
+static int em28xx_v4l2_open(struct inode *inode, struct file *filp)
 {
-       struct em28xx_fh *fh = filp->private_data;
-       int ret;
-
-       switch (cmd) {
-               /* ---------- tv norms ---------- */
-       case VIDIOC_ENUMSTD:
-       {
-               struct v4l2_standard *e = arg;
-               unsigned int i;
+       int minor = iminor(inode);
+       int errCode = 0;
+       struct em28xx *h,*dev = NULL;
+       struct em28xx_fh *fh;
 
-               i = e->index;
-               if (i >= TVNORMS)
-                       return -EINVAL;
-               ret = v4l2_video_std_construct(e, tvnorms[e->index].id,
-                                               tvnorms[e->index].name);
-               e->index = i;
-               if (ret < 0)
-                       return ret;
-               return 0;
+       list_for_each_entry(h, &em28xx_devlist, devlist) {
+               if (h->vdev->minor == minor) {
+                       dev  = h;
+                       dev->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+               }
+               if (h->vbi_dev->minor == minor) {
+                       dev  = h;
+                       dev->type = V4L2_BUF_TYPE_VBI_CAPTURE;
+               }
        }
-       case VIDIOC_G_STD:
-       {
-               v4l2_std_id *id = arg;
+       if (NULL == dev)
+               return -ENODEV;
 
-               *id = dev->tvnorm->id;
-               return 0;
+       em28xx_videodbg("open minor=%d type=%s users=%d\n",
+                               minor,v4l2_type_names[dev->type],dev->users);
+
+       fh = kzalloc(sizeof(struct em28xx_fh), GFP_KERNEL);
+
+       if (!fh) {
+               em28xx_errdev("em28xx-video.c: Out of memory?!\n");
+               return -ENOMEM;
        }
-       case VIDIOC_S_STD:
-       {
-               v4l2_std_id *id = arg;
-               unsigned int i;
+       mutex_lock(&dev->lock);
+       fh->dev = dev;
+       filp->private_data = fh;
 
-               for (i = 0; i < TVNORMS; i++)
-                       if (*id == tvnorms[i].id)
-                               break;
-               if (i == TVNORMS)
-                       for (i = 0; i < TVNORMS; i++)
-                               if (*id & tvnorms[i].id)
-                                       break;
-               if (i == TVNORMS)
-                       return -EINVAL;
+       if (dev->type == V4L2_BUF_TYPE_VIDEO_CAPTURE && dev->users == 0) {
+               em28xx_set_alternate(dev);
 
-               mutex_lock(&dev->lock);
-               dev->tvnorm = &tvnorms[i];
+               dev->width = norm_maxw(dev);
+               dev->height = norm_maxh(dev);
+               dev->frame_size = dev->width * dev->height * 2;
+               dev->field_size = dev->frame_size >> 1; /*both_fileds ? dev->frame_size>>1 : dev->frame_size; */
+               dev->bytesperline = dev->width * 2;
+               dev->hscale = 0;
+               dev->vscale = 0;
 
-               em28xx_set_norm(dev, dev->width, dev->height);
+               em28xx_capture_start(dev, 1);
+               em28xx_resolution_set(dev);
 
-               em28xx_i2c_call_clients(dev, VIDIOC_S_STD,
-                                       &dev->tvnorm->id);
 
-               mutex_unlock(&dev->lock);
+               /* start the transfer */
+               errCode = em28xx_init_isoc(dev);
+               if (errCode)
+                       goto err;
 
-               return 0;
+               em28xx_empty_framequeues(dev);
        }
 
-       /* ------ input switching ---------- */
-       case VIDIOC_ENUMINPUT:
-       {
-               struct v4l2_input *i = arg;
-               unsigned int n;
-               static const char *iname[] = {
-                       [EM28XX_VMUX_COMPOSITE1] = "Composite1",
-                       [EM28XX_VMUX_COMPOSITE2] = "Composite2",
-                       [EM28XX_VMUX_COMPOSITE3] = "Composite3",
-                       [EM28XX_VMUX_COMPOSITE4] = "Composite4",
-                       [EM28XX_VMUX_SVIDEO] = "S-Video",
-                       [EM28XX_VMUX_TELEVISION] = "Television",
-                       [EM28XX_VMUX_CABLE] = "Cable TV",
-                       [EM28XX_VMUX_DVB] = "DVB",
-                       [EM28XX_VMUX_DEBUG] = "for debug only",
-               };
-
-               n = i->index;
-               if (n >= MAX_EM28XX_INPUT)
-                       return -EINVAL;
-               if (0 == INPUT(n)->type)
-                       return -EINVAL;
-               memset(i, 0, sizeof(*i));
-               i->index = n;
-               i->type = V4L2_INPUT_TYPE_CAMERA;
-               strcpy(i->name, iname[INPUT(n)->type]);
-               if ((EM28XX_VMUX_TELEVISION == INPUT(n)->type) ||
-                       (EM28XX_VMUX_CABLE == INPUT(n)->type))
-                       i->type = V4L2_INPUT_TYPE_TUNER;
-               for (n = 0; n < ARRAY_SIZE(tvnorms); n++)
-                       i->std |= tvnorms[n].id;
-               return 0;
-       }
-       case VIDIOC_G_INPUT:
-       {
-               int *i = arg;
-               *i = dev->ctl_input;
+       dev->users++;
 
-               return 0;
-       }
-       case VIDIOC_S_INPUT:
-       {
-               int *index = arg;
+err:
+       mutex_unlock(&dev->lock);
+       return errCode;
+}
 
-               if (*index >= MAX_EM28XX_INPUT)
-                       return -EINVAL;
-               if (0 == INPUT(*index)->type)
-                       return -EINVAL;
+/*
+ * em28xx_realease_resources()
+ * unregisters the v4l2,i2c and usb devices
+ * called when the device gets disconected or at module unload
+*/
+static void em28xx_release_resources(struct em28xx *dev)
+{
 
-               mutex_lock(&dev->lock);
-               video_mux(dev, *index);
-               mutex_unlock(&dev->lock);
+       /*FIXME: I2C IR should be disconnected */
 
-               return 0;
-       }
-       case VIDIOC_G_AUDIO:
-       {
-               struct v4l2_audio *a = arg;
-               unsigned int index = a->index;
+       em28xx_info("V4L2 devices /dev/video%d and /dev/vbi%d deregistered\n",
+                               dev->vdev->minor-MINOR_VFL_TYPE_GRABBER_MIN,
+                               dev->vbi_dev->minor-MINOR_VFL_TYPE_VBI_MIN);
+       list_del(&dev->devlist);
+       video_unregister_device(dev->vdev);
+       video_unregister_device(dev->vbi_dev);
+       em28xx_i2c_unregister(dev);
+       usb_put_dev(dev->udev);
 
-               if (a->index > 1)
-                       return -EINVAL;
-               memset(a, 0, sizeof(*a));
-               index = dev->ctl_ainput;
 
-               if (index == 0) {
-                       strcpy(a->name, "Television");
-               } else {
-                       strcpy(a->name, "Line In");
+       /* Mark device as unused */
+       em28xx_devused&=~(1<<dev->devno);
+}
+
+/*
+ * em28xx_v4l2_close()
+ * stops streaming and deallocates all resources allocated by the v4l2 calls and ioctls
+ */
+static int em28xx_v4l2_close(struct inode *inode, struct file *filp)
+{
+       struct em28xx_fh *fh  = filp->private_data;
+       struct em28xx    *dev = fh->dev;
+       int              errCode;
+
+       em28xx_videodbg("users=%d\n", dev->users);
+
+
+       if (res_check(fh))
+               res_free(fh);
+
+       mutex_lock(&dev->lock);
+
+       if (dev->users == 1) {
+               em28xx_uninit_isoc(dev);
+               em28xx_release_buffers(dev);
+               dev->io = IO_NONE;
+
+               /* the device is already disconnect,
+                  free the remaining resources */
+               if (dev->state & DEV_DISCONNECTED) {
+                       em28xx_release_resources(dev);
+                       mutex_unlock(&dev->lock);
+                       kfree(dev);
+                       return 0;
+               }
+
+               /* set alternate 0 */
+               dev->alt = 0;
+               em28xx_videodbg("setting alternate 0\n");
+               errCode = usb_set_interface(dev->udev, 0, 0);
+               if (errCode < 0) {
+                       em28xx_errdev("cannot change alternate number to "
+                                       "0 (error=%i)\n", errCode);
                }
-               a->capability = V4L2_AUDCAP_STEREO;
-               a->index = index;
-               return 0;
        }
-       case VIDIOC_S_AUDIO:
-       {
-               struct v4l2_audio *a = arg;
+       kfree(fh);
+       dev->users--;
+       wake_up_interruptible_nr(&dev->open, 1);
+       mutex_unlock(&dev->lock);
+       return 0;
+}
 
-               if (a->index != dev->ctl_ainput)
-                       return -EINVAL;
+/*
+ * em28xx_v4l2_read()
+ * will allocate buffers when called for the first time
+ */
+static ssize_t
+em28xx_v4l2_read(struct file *filp, char __user * buf, size_t count,
+                loff_t * f_pos)
+{
+       struct em28xx_frame_t *f, *i;
+       unsigned long lock_flags;
+       int ret = 0;
+       struct em28xx_fh *fh = filp->private_data;
+       struct em28xx *dev = fh->dev;
 
-               return 0;
-       }
+       /* FIXME: read() is not prepared to allow changing the video
+          resolution while streaming. Seems a bug at em28xx_set_fmt
+        */
 
-       /* --- controls ---------------------------------------------- */
-       case VIDIOC_QUERYCTRL:
-       {
-               struct v4l2_queryctrl *qc = arg;
-               int i, id=qc->id;
-
-               memset(qc,0,sizeof(*qc));
-               qc->id=id;
-
-               if (!dev->has_msp34xx) {
-                       for (i = 0; i < ARRAY_SIZE(em28xx_qctrl); i++) {
-                               if (qc->id && qc->id == em28xx_qctrl[i].id) {
-                                       memcpy(qc, &(em28xx_qctrl[i]),
-                                       sizeof(*qc));
-                                       return 0;
-                               }
-                       }
+       if (unlikely(res_get(fh) < 0))
+               return -EBUSY;
+
+       mutex_lock(&dev->lock);
+
+       if (dev->type == V4L2_BUF_TYPE_VIDEO_CAPTURE)
+               em28xx_videodbg("V4l2_Buf_type_videocapture is set\n");
+
+       if (dev->type == V4L2_BUF_TYPE_VBI_CAPTURE) {
+               em28xx_videodbg("V4L2_BUF_TYPE_VBI_CAPTURE is set\n");
+               em28xx_videodbg("not supported yet! ...\n");
+               if (copy_to_user(buf, "", 1)) {
+                       mutex_unlock(&dev->lock);
+                       return -EFAULT;
                }
-               mutex_lock(&dev->lock);
-               em28xx_i2c_call_clients(dev,cmd,qc);
                mutex_unlock(&dev->lock);
-               if (qc->type)
-                       return 0;
-               else
-                       return -EINVAL;
+               return (1);
        }
-       case VIDIOC_G_CTRL:
-       {
-               struct v4l2_control *ctrl = arg;
-               int retval=-EINVAL;
-
-               if (!dev->has_msp34xx)
-                       retval=em28xx_get_ctrl(dev, ctrl);
-               if (retval==-EINVAL) {
-                       mutex_lock(&dev->lock);
-                       em28xx_i2c_call_clients(dev,cmd,arg);
+       if (dev->type == V4L2_BUF_TYPE_SLICED_VBI_CAPTURE) {
+               em28xx_videodbg("V4L2_BUF_TYPE_SLICED_VBI_CAPTURE is set\n");
+               em28xx_videodbg("not supported yet! ...\n");
+               if (copy_to_user(buf, "", 1)) {
                        mutex_unlock(&dev->lock);
-                       return 0;
-               } else return retval;
-       }
-       case VIDIOC_S_CTRL:
-       {
-               struct v4l2_control *ctrl = arg;
-               u8 i;
-               mutex_lock(&dev->lock);
-
-               if (!dev->has_msp34xx){
-                       for (i = 0; i < ARRAY_SIZE(em28xx_qctrl); i++) {
-                               if (ctrl->id == em28xx_qctrl[i].id) {
-                                       int retval=-EINVAL;
-                                       if (ctrl->value <
-                                       em28xx_qctrl[i].minimum
-                                       || ctrl->value >
-                                       em28xx_qctrl[i].maximum)
-                                               return -ERANGE;
-                                       retval = em28xx_set_ctrl(dev, ctrl);
-                                       mutex_unlock(&dev->lock);
-                                       return retval;
-                               }
-                       }
+                       return -EFAULT;
                }
-
-               em28xx_i2c_call_clients(dev,cmd,arg);
                mutex_unlock(&dev->lock);
-               return 0;
+               return (1);
        }
-       /* --- tuner ioctls ------------------------------------------ */
-       case VIDIOC_G_TUNER:
-       {
-               struct v4l2_tuner *t = arg;
 
-               if (0 != t->index)
-                       return -EINVAL;
+       if (dev->state & DEV_DISCONNECTED) {
+               em28xx_videodbg("device not present\n");
+               mutex_unlock(&dev->lock);
+               return -ENODEV;
+       }
 
-               memset(t, 0, sizeof(*t));
-               strcpy(t->name, "Tuner");
-               mutex_lock(&dev->lock);
-               /* let clients fill in the remainder of this struct */
-               em28xx_i2c_call_clients(dev, cmd, t);
+       if (dev->state & DEV_MISCONFIGURED) {
+               em28xx_videodbg("device misconfigured; close and open it again\n");
                mutex_unlock(&dev->lock);
-               em28xx_videodbg("VIDIO_G_TUNER: signal=%x, afc=%x\n", t->signal,
-                               t->afc);
-               return 0;
+               return -EIO;
        }
-       case VIDIOC_S_TUNER:
-       {
-               struct v4l2_tuner *t = arg;
 
-               if (0 != t->index)
-                       return -EINVAL;
-               mutex_lock(&dev->lock);
-               /* let clients handle this */
-               em28xx_i2c_call_clients(dev, cmd, t);
+       if (dev->io == IO_MMAP) {
+               em28xx_videodbg ("IO method is set to mmap; close and open"
+                               " the device again to choose the read method\n");
                mutex_unlock(&dev->lock);
-               return 0;
+               return -EINVAL;
        }
-       case VIDIOC_G_FREQUENCY:
-       {
-               struct v4l2_frequency *f = arg;
-
-               memset(f, 0, sizeof(*f));
-               f->type = V4L2_TUNER_ANALOG_TV;
-               f->frequency = dev->ctl_freq;
 
-               return 0;
+       if (dev->io == IO_NONE) {
+               if (!em28xx_request_buffers(dev, EM28XX_NUM_READ_FRAMES)) {
+                       em28xx_errdev("read failed, not enough memory\n");
+                       mutex_unlock(&dev->lock);
+                       return -ENOMEM;
+               }
+               dev->io = IO_READ;
+               dev->stream = STREAM_ON;
+               em28xx_queue_unusedframes(dev);
        }
-       case VIDIOC_S_FREQUENCY:
-       {
-               struct v4l2_frequency *f = arg;
-
-               if (0 != f->tuner)
-                       return -EINVAL;
-
-               if (V4L2_TUNER_ANALOG_TV != f->type)
-                       return -EINVAL;
 
-               mutex_lock(&dev->lock);
-               dev->ctl_freq = f->frequency;
-               em28xx_i2c_call_clients(dev, VIDIOC_S_FREQUENCY, f);
+       if (!count) {
                mutex_unlock(&dev->lock);
                return 0;
        }
-       case VIDIOC_CROPCAP:
-       {
-               struct v4l2_cropcap *cc = arg;
 
-               if (cc->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
-                       return -EINVAL;
-               cc->bounds.left = 0;
-               cc->bounds.top = 0;
-               cc->bounds.width = dev->width;
-               cc->bounds.height = dev->height;
-               cc->defrect = cc->bounds;
-               cc->pixelaspect.numerator = 54; /* 4:3 FIXME: remove magic numbers */
-               cc->pixelaspect.denominator = 59;
-               return 0;
+       if (list_empty(&dev->outqueue)) {
+               if (filp->f_flags & O_NONBLOCK) {
+                       mutex_unlock(&dev->lock);
+                       return -EAGAIN;
+               }
+               ret = wait_event_interruptible
+                   (dev->wait_frame,
+                    (!list_empty(&dev->outqueue)) ||
+                    (dev->state & DEV_DISCONNECTED));
+               if (ret) {
+                       mutex_unlock(&dev->lock);
+                       return ret;
+               }
+               if (dev->state & DEV_DISCONNECTED) {
+                       mutex_unlock(&dev->lock);
+                       return -ENODEV;
+               }
+               dev->video_bytesread = 0;
        }
-       case VIDIOC_STREAMON:
-       {
-               int *type = arg;
 
-               if (*type != V4L2_BUF_TYPE_VIDEO_CAPTURE
-                       || dev->io != IO_MMAP)
-                       return -EINVAL;
-
-               if (list_empty(&dev->inqueue))
-                       return -EINVAL;
+       f = list_entry(dev->outqueue.prev, struct em28xx_frame_t, frame);
 
-               if (unlikely(res_get(fh) < 0))
-                       return -EBUSY;
+       em28xx_queue_unusedframes(dev);
 
-               dev->stream = STREAM_ON;        /* FIXME: Start video capture here? */
+       if (count > f->buf.length)
+               count = f->buf.length;
 
-               em28xx_videodbg("VIDIOC_STREAMON: starting stream\n");
+       if ((dev->video_bytesread + count) > dev->frame_size)
+               count = dev->frame_size - dev->video_bytesread;
 
-               return 0;
+       if (copy_to_user(buf, f->bufmem+dev->video_bytesread, count)) {
+               em28xx_err("Error while copying to user\n");
+               return -EFAULT;
        }
-       case VIDIOC_STREAMOFF:
-       {
-               int *type = arg;
-               int ret;
+       dev->video_bytesread += count;
 
-               if (*type != V4L2_BUF_TYPE_VIDEO_CAPTURE
-                       || dev->io != IO_MMAP)
-                       return -EINVAL;
+       if (dev->video_bytesread == dev->frame_size) {
+               spin_lock_irqsave(&dev->queue_lock, lock_flags);
+               list_for_each_entry(i, &dev->outqueue, frame)
+                                   i->state = F_UNUSED;
+               INIT_LIST_HEAD(&dev->outqueue);
+               spin_unlock_irqrestore(&dev->queue_lock, lock_flags);
 
-               mutex_lock(&dev->lock);
-               if (dev->stream == STREAM_ON) {
-                       em28xx_videodbg ("VIDIOC_STREAMOFF: interrupting stream\n");
-                       if ((ret = em28xx_stream_interrupt(dev))){
-                               mutex_unlock(&dev->lock);
-                               return ret;
-                       }
-               }
+               em28xx_queue_unusedframes(dev);
+               dev->video_bytesread = 0;
+       }
 
-               em28xx_empty_framequeues(dev);
-               mutex_unlock(&dev->lock);
+       *f_pos += count;
 
-               return 0;
-       }
-       default:
-               return v4l_compat_translate_ioctl(inode, filp, cmd, arg,
-                                                 driver_ioctl);
-       }
-       return 0;
+       mutex_unlock(&dev->lock);
+
+       return count;
 }
 
 /*
- * em28xx_v4l2_do_ioctl()
- * This function is _not_ called directly, but from
- * em28xx_v4l2_ioctl. Userspace
- * copying is done already, arg is a kernel pointer.
+ * em28xx_v4l2_poll()
+ * will allocate buffers when called for the first time
  */
-static int em28xx_video_do_ioctl(struct inode *inode, struct file *filp,
-                                unsigned int cmd, void *arg)
+static unsigned int em28xx_v4l2_poll(struct file *filp, poll_table * wait)
 {
+       unsigned int mask = 0;
        struct em28xx_fh *fh = filp->private_data;
-       struct em28xx *dev   = fh->dev;
+       struct em28xx *dev = fh->dev;
 
-       if (!dev)
-               return -ENODEV;
+       if (unlikely(res_get(fh) < 0))
+               return POLLERR;
 
-       if (video_debug > 1)
-               v4l_print_ioctl(dev->name,cmd);
-
-       switch (cmd) {
-
-               /* --- capabilities ------------------------------------------ */
-       case VIDIOC_QUERYCAP:
-               {
-               struct v4l2_capability *cap = arg;
-
-               memset(cap, 0, sizeof(*cap));
-               strlcpy(cap->driver, "em28xx", sizeof(cap->driver));
-               strlcpy(cap->card, em28xx_boards[dev->model].name,
-                       sizeof(cap->card));
-               strlcpy(cap->bus_info, dev->udev->dev.bus_id,
-                       sizeof(cap->bus_info));
-               cap->version = EM28XX_VERSION_CODE;
-               cap->capabilities =
-                               V4L2_CAP_SLICED_VBI_CAPTURE |
-                               V4L2_CAP_VIDEO_CAPTURE |
-                               V4L2_CAP_AUDIO |
-                               V4L2_CAP_READWRITE | V4L2_CAP_STREAMING;
-               if (dev->has_tuner)
-                       cap->capabilities |= V4L2_CAP_TUNER;
-               return 0;
-       }
-       /* --- capture ioctls ---------------------------------------- */
-       case VIDIOC_ENUM_FMT:
-       {
-               struct v4l2_fmtdesc *fmtd = arg;
+       mutex_lock(&dev->lock);
 
-               if (fmtd->index != 0)
-                       return -EINVAL;
-               memset(fmtd, 0, sizeof(*fmtd));
-               fmtd->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
-               strcpy(fmtd->description, "Packed YUY2");
-               fmtd->pixelformat = V4L2_PIX_FMT_YUYV;
-               memset(fmtd->reserved, 0, sizeof(fmtd->reserved));
-               return 0;
-       }
-       case VIDIOC_G_FMT:
-       {
-               int retval;
-               mutex_lock(&dev->lock);
-               retval = em28xx_get_fmt(dev, (struct v4l2_format *) arg);
-               mutex_unlock(&dev->lock);
-               return retval;
+       if (dev->state & DEV_DISCONNECTED) {
+               em28xx_videodbg("device not present\n");
+       } else if (dev->state & DEV_MISCONFIGURED) {
+               em28xx_videodbg("device is misconfigured; close and open it again\n");
+       } else {
+               if (dev->io == IO_NONE) {
+                       if (!em28xx_request_buffers
+                           (dev, EM28XX_NUM_READ_FRAMES)) {
+                               em28xx_warn
+                                   ("poll() failed, not enough memory\n");
+                       } else {
+                               dev->io = IO_READ;
+                               dev->stream = STREAM_ON;
+                       }
+               }
 
-       }
-       case VIDIOC_TRY_FMT:
-       case VIDIOC_S_FMT:
-       {
-               int retval;
-               mutex_lock(&dev->lock);
-               retval = em28xx_set_fmt(dev, cmd, (struct v4l2_format *)arg);
-               mutex_unlock(&dev->lock);
-               return retval;
-       }
+               if (dev->io == IO_READ) {
+                       em28xx_queue_unusedframes(dev);
+                       poll_wait(filp, &dev->wait_frame, wait);
 
-       case VIDIOC_REQBUFS:
-       {
-               struct v4l2_requestbuffers *rb = arg;
-               u32 i;
-               int ret;
+                       if (!list_empty(&dev->outqueue))
+                               mask |= POLLIN | POLLRDNORM;
 
-               if (rb->type != V4L2_BUF_TYPE_VIDEO_CAPTURE ||
-                       rb->memory != V4L2_MEMORY_MMAP)
-                       return -EINVAL;
+                       mutex_unlock(&dev->lock);
 
-               if (dev->io == IO_READ) {
-                       em28xx_videodbg ("method is set to read;"
-                               " close and open the device again to"
-                               " choose the mmap I/O method\n");
-                       return -EINVAL;
+                       return mask;
                }
+       }
 
-               for (i = 0; i < dev->num_frames; i++)
-                       if (dev->frame[i].vma_use_count) {
-                               em28xx_videodbg ("VIDIOC_REQBUFS failed; previous buffers are still mapped\n");
-                               return -EINVAL;
-                       }
-
-               mutex_lock(&dev->lock);
-               if (dev->stream == STREAM_ON) {
-                       em28xx_videodbg("VIDIOC_REQBUFS: interrupting stream\n");
-                       if ((ret = em28xx_stream_interrupt(dev))){
-                               mutex_unlock(&dev->lock);
-                               return ret;
-                       }
-               }
+       mutex_unlock(&dev->lock);
+       return POLLERR;
+}
 
-               em28xx_empty_framequeues(dev);
+/*
+ * em28xx_v4l2_mmap()
+ */
+static int em28xx_v4l2_mmap(struct file *filp, struct vm_area_struct *vma)
+{
+       struct em28xx_fh *fh    = filp->private_data;
+       struct em28xx    *dev   = fh->dev;
+       unsigned long    size   = vma->vm_end - vma->vm_start;
+       unsigned long    start  = vma->vm_start;
+       void             *pos;
+       u32              i;
 
-               em28xx_release_buffers(dev);
-               if (rb->count)
-                       rb->count =
-                               em28xx_request_buffers(dev, rb->count);
+       if (unlikely(res_get(fh) < 0))
+               return -EBUSY;
 
-               dev->frame_current = NULL;
+       mutex_lock(&dev->lock);
 
-               em28xx_videodbg ("VIDIOC_REQBUFS: setting io method to mmap: num bufs %i\n",
-                                               rb->count);
-               dev->io = rb->count ? IO_MMAP : IO_NONE;
+       if (dev->state & DEV_DISCONNECTED) {
+               em28xx_videodbg("mmap: device not present\n");
                mutex_unlock(&dev->lock);
-               return 0;
+               return -ENODEV;
        }
-       case VIDIOC_QUERYBUF:
-       {
-               struct v4l2_buffer *b = arg;
-
-               if (b->type != V4L2_BUF_TYPE_VIDEO_CAPTURE ||
-                       b->index >= dev->num_frames || dev->io != IO_MMAP)
-                       return -EINVAL;
-
-               memcpy(b, &dev->frame[b->index].buf, sizeof(*b));
 
-               if (dev->frame[b->index].vma_use_count) {
-                       b->flags |= V4L2_BUF_FLAG_MAPPED;
-               }
-               if (dev->frame[b->index].state == F_DONE)
-                       b->flags |= V4L2_BUF_FLAG_DONE;
-               else if (dev->frame[b->index].state != F_UNUSED)
-                       b->flags |= V4L2_BUF_FLAG_QUEUED;
-               return 0;
+       if (dev->state & DEV_MISCONFIGURED) {
+               em28xx_videodbg ("mmap: Device is misconfigured; close and "
+                                               "open it again\n");
+               mutex_unlock(&dev->lock);
+               return -EIO;
        }
-       case VIDIOC_QBUF:
-       {
-               struct v4l2_buffer *b = arg;
-               unsigned long lock_flags;
-
-               if (b->type != V4L2_BUF_TYPE_VIDEO_CAPTURE ||
-                       b->index >= dev->num_frames || dev->io != IO_MMAP) {
-                       return -EINVAL;
-               }
-
-               if (dev->frame[b->index].state != F_UNUSED) {
-                       return -EAGAIN;
-               }
-               dev->frame[b->index].state = F_QUEUED;
 
-               /* add frame to fifo */
-               spin_lock_irqsave(&dev->queue_lock, lock_flags);
-               list_add_tail(&dev->frame[b->index].frame,
-                               &dev->inqueue);
-               spin_unlock_irqrestore(&dev->queue_lock, lock_flags);
-
-               return 0;
+       if (dev->io != IO_MMAP || !(vma->vm_flags & VM_WRITE)) {
+               mutex_unlock(&dev->lock);
+               return -EINVAL;
        }
-       case VIDIOC_DQBUF:
-       {
-               struct v4l2_buffer *b = arg;
-               struct em28xx_frame_t *f;
-               unsigned long lock_flags;
-               int ret = 0;
-
-               if (b->type != V4L2_BUF_TYPE_VIDEO_CAPTURE
-                       || dev->io != IO_MMAP)
-                       return -EINVAL;
-
-               if (list_empty(&dev->outqueue)) {
-                       if (dev->stream == STREAM_OFF)
-                               return -EINVAL;
-                       if (filp->f_flags & O_NONBLOCK)
-                               return -EAGAIN;
-                       ret = wait_event_interruptible
-                               (dev->wait_frame,
-                               (!list_empty(&dev->outqueue)) ||
-                               (dev->state & DEV_DISCONNECTED));
-                       if (ret)
-                               return ret;
-                       if (dev->state & DEV_DISCONNECTED)
-                               return -ENODEV;
-               }
-
-               spin_lock_irqsave(&dev->queue_lock, lock_flags);
-               f = list_entry(dev->outqueue.next,
-                               struct em28xx_frame_t, frame);
-               list_del(dev->outqueue.next);
-               spin_unlock_irqrestore(&dev->queue_lock, lock_flags);
 
-               f->state = F_UNUSED;
-               memcpy(b, &f->buf, sizeof(*b));
+       if (size > PAGE_ALIGN(dev->frame[0].buf.length))
+               size = PAGE_ALIGN(dev->frame[0].buf.length);
 
-               if (f->vma_use_count)
-                       b->flags |= V4L2_BUF_FLAG_MAPPED;
-
-               return 0;
+       for (i = 0; i < dev->num_frames; i++) {
+               if ((dev->frame[i].buf.m.offset >> PAGE_SHIFT) == vma->vm_pgoff)
+                       break;
        }
-       default:
-               return em28xx_do_ioctl(inode, filp, dev, cmd, arg,
-                                      em28xx_video_do_ioctl);
+       if (i == dev->num_frames) {
+               em28xx_videodbg("mmap: user supplied mapping address is out of range\n");
+               mutex_unlock(&dev->lock);
+               return -EINVAL;
        }
-       return 0;
-}
-
-/*
- * em28xx_v4l2_ioctl()
- * handle v4l2 ioctl the main action happens in em28xx_v4l2_do_ioctl()
- */
-static int em28xx_v4l2_ioctl(struct inode *inode, struct file *filp,
-                            unsigned int cmd, unsigned long arg)
-{
-       int ret = 0;
-       struct em28xx_fh *fh = filp->private_data;
-       struct em28xx *dev   = fh->dev;
 
-       if (dev->state & DEV_DISCONNECTED) {
-               em28xx_errdev("v4l2 ioctl: device not present\n");
-               return -ENODEV;
-       }
+       /* VM_IO is eventually going to replace PageReserved altogether */
+       vma->vm_flags |= VM_IO;
+       vma->vm_flags |= VM_RESERVED;   /* avoid to swap out this VMA */
 
-       if (dev->state & DEV_MISCONFIGURED) {
-               em28xx_errdev
-                   ("v4l2 ioctl: device is misconfigured; close and open it again\n");
-               return -EIO;
+       pos = dev->frame[i].bufmem;
+       while (size > 0) {      /* size is page-aligned */
+               if (vm_insert_page(vma, start, vmalloc_to_page(pos))) {
+                       em28xx_videodbg("mmap: vm_insert_page failed\n");
+                       mutex_unlock(&dev->lock);
+                       return -EAGAIN;
+               }
+               start += PAGE_SIZE;
+               pos += PAGE_SIZE;
+               size -= PAGE_SIZE;
        }
 
-       ret = video_usercopy(inode, filp, cmd, arg, em28xx_video_do_ioctl);
+       vma->vm_ops = &em28xx_vm_ops;
+       vma->vm_private_data = &dev->frame[i];
 
-       return ret;
+       em28xx_vm_open(vma);
+       mutex_unlock(&dev->lock);
+       return 0;
 }
 
 static const struct file_operations em28xx_v4l_fops = {
-       .owner = THIS_MODULE,
-       .open = em28xx_v4l2_open,
-       .release = em28xx_v4l2_close,
-       .ioctl = em28xx_v4l2_ioctl,
-       .read = em28xx_v4l2_read,
-       .poll = em28xx_v4l2_poll,
-       .mmap = em28xx_v4l2_mmap,
-       .llseek = no_llseek,
-       .compat_ioctl   = v4l_compat_ioctl32,
+       .owner         = THIS_MODULE,
+       .open          = em28xx_v4l2_open,
+       .release       = em28xx_v4l2_close,
+       .read          = em28xx_v4l2_read,
+       .poll          = em28xx_v4l2_poll,
+       .mmap          = em28xx_v4l2_mmap,
+       .ioctl         = video_ioctl2,
+       .llseek        = no_llseek,
+       .compat_ioctl  = v4l_compat_ioctl32,
+};
 
+static const struct video_device em28xx_video_template = {
+       .fops                       = &em28xx_v4l_fops,
+       .release                    = video_device_release,
+
+       .minor                      = -1,
+       .vidioc_querycap            = vidioc_querycap,
+       .vidioc_enum_fmt_cap        = vidioc_enum_fmt_cap,
+       .vidioc_g_fmt_cap           = vidioc_g_fmt_cap,
+       .vidioc_try_fmt_cap         = vidioc_try_fmt_cap,
+       .vidioc_s_fmt_cap           = vidioc_s_fmt_cap,
+       .vidioc_g_audio             = vidioc_g_audio,
+       .vidioc_s_audio             = vidioc_s_audio,
+       .vidioc_cropcap             = vidioc_cropcap,
+
+       .vidioc_g_fmt_vbi_capture   = vidioc_g_fmt_vbi_capture,
+       .vidioc_try_fmt_vbi_capture = vidioc_try_set_vbi_capture,
+       .vidioc_s_fmt_vbi_capture   = vidioc_try_set_vbi_capture,
+
+       .vidioc_reqbufs             = vidioc_reqbufs,
+       .vidioc_querybuf            = vidioc_querybuf,
+       .vidioc_qbuf                = vidioc_qbuf,
+       .vidioc_dqbuf               = vidioc_dqbuf,
+       .vidioc_s_std               = vidioc_s_std,
+       .vidioc_enum_input          = vidioc_enum_input,
+       .vidioc_g_input             = vidioc_g_input,
+       .vidioc_s_input             = vidioc_s_input,
+       .vidioc_queryctrl           = vidioc_queryctrl,
+       .vidioc_g_ctrl              = vidioc_g_ctrl,
+       .vidioc_s_ctrl              = vidioc_s_ctrl,
+       .vidioc_streamon            = vidioc_streamon,
+       .vidioc_streamoff           = vidioc_streamoff,
+       .vidioc_g_tuner             = vidioc_g_tuner,
+       .vidioc_s_tuner             = vidioc_s_tuner,
+       .vidioc_g_frequency         = vidioc_g_frequency,
+       .vidioc_s_frequency         = vidioc_s_frequency,
+
+       .tvnorms                    = V4L2_STD_ALL,
+       .current_norm               = V4L2_STD_NTSC_M,
 };
 
+
 /******************************** usb interface *****************************************/
 
 /*
@@ -1639,7 +1731,7 @@ static int em28xx_init_dev(struct em28xx **devhandle, struct usb_device *udev,
 
        errCode = em28xx_config(dev);
 
-       /* allocate and fill v4l2 device struct */
+       /* allocate and fill video video_device struct */
        dev->vdev = video_device_alloc();
        if (NULL == dev->vdev) {
                em28xx_errdev("cannot allocate video_device.\n");
@@ -1647,7 +1739,17 @@ static int em28xx_init_dev(struct em28xx **devhandle, struct usb_device *udev,
                kfree(dev);
                return -ENOMEM;
        }
+       memcpy(dev->vdev, &em28xx_video_template,
+                                       sizeof(em28xx_video_template));
+       dev->vdev->type = VID_TYPE_CAPTURE;
+       if (dev->has_tuner)
+               dev->vdev->type |= VID_TYPE_TUNER;
+       dev->vdev->dev = &dev->udev->dev;
+       snprintf(dev->vdev->name, sizeof(dev->vbi_dev->name),
+                "%s#%d %s", "em28xx", dev->devno, "video");
+       dev->vdev->current_norm = dev->tvnorm->id;
 
+       /* Allocate and fill vbi video_device struct */
        dev->vbi_dev = video_device_alloc();
        if (NULL == dev->vbi_dev) {
                em28xx_errdev("cannot allocate video_device.\n");
@@ -1656,41 +1758,27 @@ static int em28xx_init_dev(struct em28xx **devhandle, struct usb_device *udev,
                kfree(dev);
                return -ENOMEM;
        }
-
-       /* Fills VBI device info */
+       memcpy(dev->vbi_dev, &em28xx_video_template,
+                                       sizeof(em28xx_video_template));
        dev->vbi_dev->type = VFL_TYPE_VBI;
-       dev->vbi_dev->fops = &em28xx_v4l_fops;
-       dev->vbi_dev->minor = -1;
        dev->vbi_dev->dev = &dev->udev->dev;
-       dev->vbi_dev->release = video_device_release;
-       snprintf(dev->vbi_dev->name, sizeof(dev->vbi_dev->name), "%s#%d %s",
-                                                        "em28xx",dev->devno,"vbi");
-
-       /* Fills CAPTURE device info */
-       dev->vdev->type = VID_TYPE_CAPTURE;
-       if (dev->has_tuner)
-               dev->vdev->type |= VID_TYPE_TUNER;
-       dev->vdev->fops = &em28xx_v4l_fops;
-       dev->vdev->minor = -1;
-       dev->vdev->dev = &dev->udev->dev;
-       dev->vdev->release = video_device_release;
-       snprintf(dev->vdev->name, sizeof(dev->vbi_dev->name), "%s#%d %s",
-                                                        "em28xx",dev->devno,"video");
+       snprintf(dev->vbi_dev->name, sizeof(dev->vbi_dev->name),
+                               "%s#%d %s", "em28xx", dev->devno, "vbi");
+       dev->vbi_dev->current_norm = dev->tvnorm->id;
 
        list_add_tail(&dev->devlist,&em28xx_devlist);
 
-
        if (dev->has_msp34xx) {
                /* Send a reset to other chips via gpio */
                em28xx_write_regs_req(dev, 0x00, 0x08, "\xf7", 1);
                msleep(3);
                em28xx_write_regs_req(dev, 0x00, 0x08, "\xff", 1);
                msleep(3);
-
        }
+
        video_mux(dev, 0);
 
-       /* register v4l2 device */
+       /* register v4l2 video video_device */
        if ((retval = video_register_device(dev->vdev, VFL_TYPE_GRABBER,
                                         video_nr[dev->devno]))) {
                em28xx_errdev("unable to register video device (error=%i).\n",
@@ -1703,6 +1791,7 @@ static int em28xx_init_dev(struct em28xx **devhandle, struct usb_device *udev,
                return -ENODEV;
        }
 
+       /* register v4l2 vbi video_device */
        if (video_register_device(dev->vbi_dev, VFL_TYPE_VBI,
                                        vbi_nr[dev->devno]) < 0) {
                printk("unable to register vbi device\n");
@@ -1713,8 +1802,6 @@ static int em28xx_init_dev(struct em28xx **devhandle, struct usb_device *udev,
                em28xx_devused&=~(1<<dev->devno);
                kfree(dev);
                return -ENODEV;
-       } else {
-               printk("registered VBI\n");
        }
 
        em28xx_info("V4L2 device registered as /dev/video%d and /dev/vbi%d\n",