V4L/DVB (13310): uvcvideo: Return -EINVAL instead of -ENODEV in read()
[safe/jmp/linux-2.6] / drivers / media / video / stk-webcam.c
index f84e8d5..6b41865 100644 (file)
 #include <linux/kernel.h>
 #include <linux/errno.h>
 #include <linux/slab.h>
-#include <linux/kref.h>
+#include <linux/smp_lock.h>
 
 #include <linux/usb.h>
+#include <linux/mm.h>
 #include <linux/vmalloc.h>
 #include <linux/videodev2.h>
 #include <media/v4l2-common.h>
+#include <media/v4l2-ioctl.h>
 
 #include "stk-webcam.h"
 
@@ -63,22 +65,6 @@ static struct usb_device_id stkwebcam_table[] = {
 };
 MODULE_DEVICE_TABLE(usb, stkwebcam_table);
 
-void stk_camera_cleanup(struct kref *kref)
-{
-       struct stk_camera *dev = to_stk_camera(kref);
-
-       STK_INFO("Syntek USB2.0 Camera release resources"
-               " video device /dev/video%d\n", dev->vdev.minor);
-       video_unregister_device(&dev->vdev);
-       dev->vdev.priv = NULL;
-
-       if (dev->sio_bufs != NULL || dev->isobufs != NULL)
-               STK_ERROR("We are leaking memory\n");
-       usb_put_intf(dev->interface);
-       kfree(dev);
-}
-
-
 /*
  * Basic stuff
  */
@@ -245,6 +231,8 @@ static int stk_initialise(struct stk_camera *dev)
                return -1;
 }
 
+#ifdef CONFIG_VIDEO_V4L1_COMPAT
+
 /* sysfs functions */
 /*FIXME cleanup this */
 
@@ -337,19 +325,25 @@ static int stk_create_sysfs_files(struct video_device *vdev)
 {
        int ret;
 
-       ret = video_device_create_file(vdev, &dev_attr_brightness);
-       ret += video_device_create_file(vdev, &dev_attr_hflip);
-       ret += video_device_create_file(vdev, &dev_attr_vflip);
+       ret = device_create_file(&vdev->dev, &dev_attr_brightness);
+       ret += device_create_file(&vdev->dev, &dev_attr_hflip);
+       ret += device_create_file(&vdev->dev, &dev_attr_vflip);
+       if (ret)
+               STK_WARNING("Could not create sysfs files\n");
        return ret;
 }
 
 static void stk_remove_sysfs_files(struct video_device *vdev)
 {
-       video_device_remove_file(vdev, &dev_attr_brightness);
-       video_device_remove_file(vdev, &dev_attr_hflip);
-       video_device_remove_file(vdev, &dev_attr_vflip);
+       device_remove_file(&vdev->dev, &dev_attr_brightness);
+       device_remove_file(&vdev->dev, &dev_attr_hflip);
+       device_remove_file(&vdev->dev, &dev_attr_vflip);
 }
 
+#else
+#define stk_create_sysfs_files(a)
+#define stk_remove_sysfs_files(a)
+#endif
 
 /* *********************************************** */
 /*
@@ -435,18 +429,19 @@ static void stk_isoc_handler(struct urb *urb)
                                fb->v4lbuf.bytesused = 0;
                                fill = fb->buffer;
                        } else if (fb->v4lbuf.bytesused == dev->frame_size) {
-                               list_move_tail(dev->sio_avail.next,
-                                       &dev->sio_full);
-                               wake_up(&dev->wait_frame);
-                               if (list_empty(&dev->sio_avail)) {
-                                       (void) (printk_ratelimit() &&
-                                       STK_ERROR("No buffer available\n"));
-                                       goto resubmit;
+                               if (list_is_singular(&dev->sio_avail)) {
+                                       /* Always reuse the last buffer */
+                                       fb->v4lbuf.bytesused = 0;
+                                       fill = fb->buffer;
+                               } else {
+                                       list_move_tail(dev->sio_avail.next,
+                                               &dev->sio_full);
+                                       wake_up(&dev->wait_frame);
+                                       fb = list_first_entry(&dev->sio_avail,
+                                               struct stk_sio_buffer, list);
+                                       fb->v4lbuf.bytesused = 0;
+                                       fill = fb->buffer;
                                }
-                               fb = list_first_entry(&dev->sio_avail,
-                                       struct stk_sio_buffer, list);
-                               fb->v4lbuf.bytesused = 0;
-                               fill = fb->buffer;
                        }
                } else {
                        framelen -= 4;
@@ -565,7 +560,7 @@ static void stk_clean_iso(struct stk_camera *dev)
 
                urb = dev->isobufs[i].urb;
                if (urb) {
-                       if (atomic_read(&dev->urbs_used))
+                       if (atomic_read(&dev->urbs_used) && is_present(dev))
                                usb_kill_urb(urb);
                        usb_free_urb(urb);
                }
@@ -670,7 +665,7 @@ static void stk_free_buffers(struct stk_camera *dev)
 
 /* v4l file operations */
 
-static int v4l_stk_open(struct inode *inode, struct file *fp)
+static int v4l_stk_open(struct file *fp)
 {
        struct stk_camera *dev;
        struct video_device *vdev;
@@ -678,45 +673,30 @@ static int v4l_stk_open(struct inode *inode, struct file *fp)
        vdev = video_devdata(fp);
        dev = vdev_to_camera(vdev);
 
-       if (dev == NULL || !is_present(dev))
+       lock_kernel();
+       if (dev == NULL || !is_present(dev)) {
+               unlock_kernel();
                return -ENXIO;
-       fp->private_data = vdev;
-       kref_get(&dev->kref);
+       }
+       fp->private_data = dev;
        usb_autopm_get_interface(dev->interface);
+       unlock_kernel();
 
        return 0;
 }
 
-static int v4l_stk_release(struct inode *inode, struct file *fp)
+static int v4l_stk_release(struct file *fp)
 {
-       struct stk_camera *dev;
-       struct video_device *vdev;
+       struct stk_camera *dev = fp->private_data;
 
-       vdev = video_devdata(fp);
-       if (vdev == NULL) {
-               STK_ERROR("v4l_release called w/o video devdata\n");
-               return -EFAULT;
-       }
-       dev = vdev_to_camera(vdev);
-       if (dev == NULL) {
-               STK_ERROR("v4l_release called on removed device\n");
-               return -ENODEV;
+       if (dev->owner == fp) {
+               stk_stop_stream(dev);
+               stk_free_buffers(dev);
+               dev->owner = NULL;
        }
 
-       if (dev->owner != fp) {
+       if(is_present(dev))
                usb_autopm_put_interface(dev->interface);
-               kref_put(&dev->kref, stk_camera_cleanup);
-               return 0;
-       }
-
-       stk_stop_stream(dev);
-
-       stk_free_buffers(dev);
-
-       dev->owner = NULL;
-
-       usb_autopm_put_interface(dev->interface);
-       kref_put(&dev->kref, stk_camera_cleanup);
 
        return 0;
 }
@@ -727,17 +707,8 @@ static ssize_t v4l_stk_read(struct file *fp, char __user *buf,
        int i;
        int ret;
        unsigned long flags;
-       struct stk_camera *dev;
-       struct video_device *vdev;
        struct stk_sio_buffer *sbuf;
-
-       vdev = video_devdata(fp);
-       if (vdev == NULL)
-               return -EFAULT;
-       dev = vdev_to_camera(vdev);
-
-       if (dev == NULL)
-               return -EIO;
+       struct stk_camera *dev = fp->private_data;
 
        if (!is_present(dev))
                return -EIO;
@@ -793,17 +764,7 @@ static ssize_t v4l_stk_read(struct file *fp, char __user *buf,
 
 static unsigned int v4l_stk_poll(struct file *fp, poll_table *wait)
 {
-       struct stk_camera *dev;
-       struct video_device *vdev;
-
-       vdev = video_devdata(fp);
-
-       if (vdev == NULL)
-               return -EFAULT;
-
-       dev = vdev_to_camera(vdev);
-       if (dev == NULL)
-               return -ENODEV;
+       struct stk_camera *dev = fp->private_data;
 
        poll_wait(fp, &dev->wait_frame, wait);
 
@@ -829,7 +790,7 @@ static void stk_v4l_vm_close(struct vm_area_struct *vma)
        if (sbuf->mapcount == 0)
                sbuf->v4lbuf.flags &= ~V4L2_BUF_FLAG_MAPPED;
 }
-static struct vm_operations_struct stk_v4l_vm_ops = {
+static const struct vm_operations_struct stk_v4l_vm_ops = {
        .open = stk_v4l_vm_open,
        .close = stk_v4l_vm_close
 };
@@ -839,16 +800,12 @@ static int v4l_stk_mmap(struct file *fp, struct vm_area_struct *vma)
        unsigned int i;
        int ret;
        unsigned long offset = vma->vm_pgoff << PAGE_SHIFT;
-       struct stk_camera *dev;
-       struct video_device *vdev;
+       struct stk_camera *dev = fp->private_data;
        struct stk_sio_buffer *sbuf = NULL;
 
        if (!(vma->vm_flags & VM_WRITE) || !(vma->vm_flags & VM_SHARED))
                return -EINVAL;
 
-       vdev = video_devdata(fp);
-       dev = vdev_to_camera(vdev);
-
        for (i = 0; i < dev->n_sbufs; i++) {
                if (dev->sio_bufs[i].v4lbuf.m.offset == offset) {
                        sbuf = dev->sio_bufs + i;
@@ -974,11 +931,9 @@ static int stk_vidioc_s_ctrl(struct file *filp,
 }
 
 
-static int stk_vidioc_enum_fmt_cap(struct file *filp,
+static int stk_vidioc_enum_fmt_vid_cap(struct file *filp,
                void *priv, struct v4l2_fmtdesc *fmtd)
 {
-       fmtd->flags = 0;
-
        switch (fmtd->index) {
        case 0:
                fmtd->pixelformat = V4L2_PIX_FMT_RGB565;
@@ -1018,7 +973,7 @@ static struct stk_size {
        { .w = 176,  .h = 144,  .m = MODE_QCIF, },
 };
 
-static int stk_vidioc_g_fmt_cap(struct file *filp,
+static int stk_vidioc_g_fmt_vid_cap(struct file *filp,
                void *priv, struct v4l2_format *f)
 {
        struct v4l2_pix_format *pix_format = &f->fmt.pix;
@@ -1036,7 +991,6 @@ static int stk_vidioc_g_fmt_cap(struct file *filp,
        pix_format->height = stk_sizes[i].h;
        pix_format->field = V4L2_FIELD_NONE;
        pix_format->colorspace = V4L2_COLORSPACE_SRGB;
-       pix_format->priv = 0;
        pix_format->pixelformat = dev->vsettings.palette;
        if (dev->vsettings.palette == V4L2_PIX_FMT_SBGGR8)
                pix_format->bytesperline = pix_format->width;
@@ -1047,7 +1001,7 @@ static int stk_vidioc_g_fmt_cap(struct file *filp,
        return 0;
 }
 
-static int stk_vidioc_try_fmt_cap(struct file *filp,
+static int stk_vidioc_try_fmt_vid_cap(struct file *filp,
                void *priv, struct v4l2_format *fmtd)
 {
        int i;
@@ -1096,11 +1050,11 @@ static int stk_setup_format(struct stk_camera *dev)
                depth = 1;
        else
                depth = 2;
-       while (stk_sizes[i].m != dev->vsettings.mode
-                       && i < ARRAY_SIZE(stk_sizes))
+       while (i < ARRAY_SIZE(stk_sizes) &&
+                       stk_sizes[i].m != dev->vsettings.mode)
                i++;
        if (i == ARRAY_SIZE(stk_sizes)) {
-               STK_ERROR("Something is broken in %s\n", __FUNCTION__);
+               STK_ERROR("Something is broken in %s\n", __func__);
                return -EFAULT;
        }
        /* This registers controls some timings, not sure of what. */
@@ -1124,7 +1078,7 @@ static int stk_setup_format(struct stk_camera *dev)
        return stk_sensor_configure(dev);
 }
 
-static int stk_vidioc_s_fmt_cap(struct file *filp,
+static int stk_vidioc_s_fmt_vid_cap(struct file *filp,
                void *priv, struct v4l2_format *fmtd)
 {
        int ret;
@@ -1138,7 +1092,7 @@ static int stk_vidioc_s_fmt_cap(struct file *filp,
                return -EBUSY;
        if (dev->owner && dev->owner != filp)
                return -EBUSY;
-       ret = stk_vidioc_try_fmt_cap(filp, priv, fmtd);
+       ret = stk_vidioc_try_fmt_vid_cap(filp, priv, fmtd);
        if (ret)
                return ret;
        dev->owner = filp;
@@ -1159,8 +1113,6 @@ static int stk_vidioc_reqbufs(struct file *filp,
 
        if (dev == NULL)
                return -ENODEV;
-       if (rb->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
-               return -EINVAL;
        if (rb->memory != V4L2_MEMORY_MMAP)
                return -EINVAL;
        if (is_streaming(dev)
@@ -1183,16 +1135,10 @@ static int stk_vidioc_reqbufs(struct file *filp,
 static int stk_vidioc_querybuf(struct file *filp,
                void *priv, struct v4l2_buffer *buf)
 {
-       int index;
        struct stk_camera *dev = priv;
        struct stk_sio_buffer *sbuf;
 
-       if (buf->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
-               return -EINVAL;
-
-       index = buf->index;
-
-       if (index < 0 || index >= dev->n_sbufs)
+       if (buf->index >= dev->n_sbufs)
                return -EINVAL;
        sbuf = dev->sio_bufs + buf->index;
        *buf = sbuf->v4lbuf;
@@ -1205,13 +1151,11 @@ static int stk_vidioc_qbuf(struct file *filp,
        struct stk_camera *dev = priv;
        struct stk_sio_buffer *sbuf;
        unsigned long flags;
-       if (buf->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
-               return -EINVAL;
 
        if (buf->memory != V4L2_MEMORY_MMAP)
                return -EINVAL;
 
-       if (buf->index < 0 || buf->index >= dev->n_sbufs)
+       if (buf->index >= dev->n_sbufs)
                return -EINVAL;
        sbuf = dev->sio_bufs + buf->index;
        if (sbuf->v4lbuf.flags & V4L2_BUF_FLAG_QUEUED)
@@ -1233,8 +1177,7 @@ static int stk_vidioc_dqbuf(struct file *filp,
        unsigned long flags;
        int ret;
 
-       if (buf->type != V4L2_BUF_TYPE_VIDEO_CAPTURE
-               || !is_streaming(dev))
+       if (!is_streaming(dev))
                return -EINVAL;
 
        if (filp->f_flags & O_NONBLOCK && list_empty(&dev->sio_full))
@@ -1293,20 +1236,33 @@ static int stk_vidioc_streamoff(struct file *filp,
 static int stk_vidioc_g_parm(struct file *filp,
                void *priv, struct v4l2_streamparm *sp)
 {
-       if (sp->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
-               return -EINVAL;
-
-       sp->parm.capture.capability = 0;
-       sp->parm.capture.capturemode = 0;
        /*FIXME This is not correct */
        sp->parm.capture.timeperframe.numerator = 1;
        sp->parm.capture.timeperframe.denominator = 30;
        sp->parm.capture.readbuffers = 2;
-       sp->parm.capture.extendedmode = 0;
        return 0;
 }
 
-static struct file_operations v4l_stk_fops = {
+static int stk_vidioc_enum_framesizes(struct file *filp,
+               void *priv, struct v4l2_frmsizeenum *frms)
+{
+       if (frms->index >= ARRAY_SIZE(stk_sizes))
+               return -EINVAL;
+       switch (frms->pixel_format) {
+       case V4L2_PIX_FMT_RGB565:
+       case V4L2_PIX_FMT_RGB565X:
+       case V4L2_PIX_FMT_UYVY:
+       case V4L2_PIX_FMT_YUYV:
+       case V4L2_PIX_FMT_SBGGR8:
+               frms->type = V4L2_FRMSIZE_TYPE_DISCRETE;
+               frms->discrete.width = stk_sizes[frms->index].w;
+               frms->discrete.height = stk_sizes[frms->index].h;
+               return 0;
+       default: return -EINVAL;
+       }
+}
+
+static struct v4l2_file_operations v4l_stk_fops = {
        .owner = THIS_MODULE,
        .open = v4l_stk_open,
        .release = v4l_stk_release,
@@ -1314,28 +1270,14 @@ static struct file_operations v4l_stk_fops = {
        .poll = v4l_stk_poll,
        .mmap = v4l_stk_mmap,
        .ioctl = video_ioctl2,
-       .llseek = no_llseek
 };
 
-static void stk_v4l_dev_release(struct video_device *vd)
-{
-}
-
-static struct video_device stk_v4l_data = {
-       .name = "stkwebcam",
-       .type = VFL_TYPE_GRABBER,
-       .type2 = VID_TYPE_CAPTURE,
-       .minor = -1,
-       .tvnorms = V4L2_STD_UNKNOWN,
-       .current_norm = V4L2_STD_UNKNOWN,
-       .fops = &v4l_stk_fops,
-       .release = stk_v4l_dev_release,
-
+static const struct v4l2_ioctl_ops v4l_stk_ioctl_ops = {
        .vidioc_querycap = stk_vidioc_querycap,
-       .vidioc_enum_fmt_cap = stk_vidioc_enum_fmt_cap,
-       .vidioc_try_fmt_cap = stk_vidioc_try_fmt_cap,
-       .vidioc_s_fmt_cap = stk_vidioc_s_fmt_cap,
-       .vidioc_g_fmt_cap = stk_vidioc_g_fmt_cap,
+       .vidioc_enum_fmt_vid_cap = stk_vidioc_enum_fmt_vid_cap,
+       .vidioc_try_fmt_vid_cap = stk_vidioc_try_fmt_vid_cap,
+       .vidioc_s_fmt_vid_cap = stk_vidioc_s_fmt_vid_cap,
+       .vidioc_g_fmt_vid_cap = stk_vidioc_g_fmt_vid_cap,
        .vidioc_enum_input = stk_vidioc_enum_input,
        .vidioc_s_input = stk_vidioc_s_input,
        .vidioc_g_input = stk_vidioc_g_input,
@@ -1350,6 +1292,27 @@ static struct video_device stk_v4l_data = {
        .vidioc_g_ctrl = stk_vidioc_g_ctrl,
        .vidioc_s_ctrl = stk_vidioc_s_ctrl,
        .vidioc_g_parm = stk_vidioc_g_parm,
+       .vidioc_enum_framesizes = stk_vidioc_enum_framesizes,
+};
+
+static void stk_v4l_dev_release(struct video_device *vd)
+{
+       struct stk_camera *dev = vdev_to_camera(vd);
+
+       if (dev->sio_bufs != NULL || dev->isobufs != NULL)
+               STK_ERROR("We are leaking memory\n");
+       usb_put_intf(dev->interface);
+       kfree(dev);
+}
+
+static struct video_device stk_v4l_data = {
+       .name = "stkwebcam",
+       .minor = -1,
+       .tvnorms = V4L2_STD_UNKNOWN,
+       .current_norm = V4L2_STD_UNKNOWN,
+       .fops = &v4l_stk_fops,
+       .ioctl_ops = &v4l_stk_ioctl_ops,
+       .release = stk_v4l_dev_release,
 };
 
 
@@ -1359,14 +1322,13 @@ static int stk_register_video_device(struct stk_camera *dev)
 
        dev->vdev = stk_v4l_data;
        dev->vdev.debug = debug;
-       dev->vdev.dev = &dev->interface->dev;
-       dev->vdev.priv = dev;
+       dev->vdev.parent = &dev->interface->dev;
        err = video_register_device(&dev->vdev, VFL_TYPE_GRABBER, -1);
        if (err)
                STK_ERROR("v4l registration failed\n");
        else
                STK_INFO("Syntek USB2.0 Camera is now controlling video device"
-                       " /dev/video%d\n", dev->vdev.minor);
+                       " /dev/video%d\n", dev->vdev.num);
        return err;
 }
 
@@ -1377,7 +1339,7 @@ static int stk_camera_probe(struct usb_interface *interface,
                const struct usb_device_id *id)
 {
        int i;
-       int err;
+       int err = 0;
 
        struct stk_camera *dev = NULL;
        struct usb_device *udev = interface_to_usbdev(interface);
@@ -1390,7 +1352,6 @@ static int stk_camera_probe(struct usb_interface *interface,
                return -ENOMEM;
        }
 
-       kref_init(&dev->kref);
        spin_lock_init(&dev->spinlock);
        init_waitqueue_head(&dev->wait_frame);
 
@@ -1412,19 +1373,16 @@ static int stk_camera_probe(struct usb_interface *interface,
                endpoint = &iface_desc->endpoint[i].desc;
 
                if (!dev->isoc_ep
-                       && ((endpoint->bEndpointAddress
-                               & USB_ENDPOINT_DIR_MASK) == USB_DIR_IN)
-                       && ((endpoint->bmAttributes
-                               & USB_ENDPOINT_XFERTYPE_MASK) == USB_ENDPOINT_XFER_ISOC)) {
+                       && usb_endpoint_is_isoc_in(endpoint)) {
                        /* we found an isoc in endpoint */
-                       dev->isoc_ep = (endpoint->bEndpointAddress & 0xF);
+                       dev->isoc_ep = usb_endpoint_num(endpoint);
                        break;
                }
        }
        if (!dev->isoc_ep) {
                STK_ERROR("Could not find isoc-in endpoint");
-               kref_put(&dev->kref, stk_camera_cleanup);
-               return -ENODEV;
+               err = -ENODEV;
+               goto error;
        }
        dev->vsettings.brightness = 0x7fff;
        dev->vsettings.palette = V4L2_PIX_FMT_RGB565;
@@ -1438,14 +1396,16 @@ static int stk_camera_probe(struct usb_interface *interface,
 
        err = stk_register_video_device(dev);
        if (err) {
-               kref_put(&dev->kref, stk_camera_cleanup);
-               return err;
+               goto error;
        }
 
        stk_create_sysfs_files(&dev->vdev);
-       usb_autopm_enable(dev->interface);
 
        return 0;
+
+error:
+       kfree(dev);
+       return err;
 }
 
 static void stk_camera_disconnect(struct usb_interface *interface)
@@ -1458,11 +1418,14 @@ static void stk_camera_disconnect(struct usb_interface *interface)
        wake_up_interruptible(&dev->wait_frame);
        stk_remove_sysfs_files(&dev->vdev);
 
-       kref_put(&dev->kref, stk_camera_cleanup);
+       STK_INFO("Syntek USB2.0 Camera release resources "
+               "video device /dev/video%d\n", dev->vdev.num);
+
+       video_unregister_device(&dev->vdev);
 }
 
 #ifdef CONFIG_PM
-int stk_camera_suspend(struct usb_interface *intf, pm_message_t message)
+static int stk_camera_suspend(struct usb_interface *intf, pm_message_t message)
 {
        struct stk_camera *dev = usb_get_intfdata(intf);
        if (is_streaming(dev)) {
@@ -1473,7 +1436,7 @@ int stk_camera_suspend(struct usb_interface *intf, pm_message_t message)
        return 0;
 }
 
-int stk_camera_resume(struct usb_interface *intf)
+static int stk_camera_resume(struct usb_interface *intf)
 {
        struct stk_camera *dev = usb_get_intfdata(intf);
        if (!is_initialised(dev))