]> www.pilppa.org Git - linux-2.6-omap-h63xx.git/blobdiff - drivers/media/video/et61x251/et61x251_core.c
V4L/DVB (6293): V4L: convert struct class_device to struct device
[linux-2.6-omap-h63xx.git] / drivers / media / video / et61x251 / et61x251_core.c
index 49792ae8c61c15253b291a0d63e3058e1e3f936f..d5fef4c01c87a9ff0f100a34f9925670245a32eb 100644 (file)
@@ -1,7 +1,7 @@
 /***************************************************************************
  * V4L2 driver for ET61X[12]51 PC Camera Controllers                       *
  *                                                                         *
- * Copyright (C) 2006 by Luca Risolia <luca.risolia@studio.unibo.it>       *
+ * Copyright (C) 2006-2007 by Luca Risolia <luca.risolia@studio.unibo.it>  *
  *                                                                         *
  * This program is free software; you can redistribute it and/or modify    *
  * it under the terms of the GNU General Public License as published by    *
@@ -22,7 +22,6 @@
 #include <linux/init.h>
 #include <linux/kernel.h>
 #include <linux/param.h>
-#include <linux/moduleparam.h>
 #include <linux/errno.h>
 #include <linux/slab.h>
 #include <linux/device.h>
 
 #define ET61X251_MODULE_NAME    "V4L2 driver for ET61X[12]51 "                \
                                "PC Camera Controllers"
-#define ET61X251_MODULE_AUTHOR  "(C) 2006 Luca Risolia"
+#define ET61X251_MODULE_AUTHOR  "(C) 2006-2007 Luca Risolia"
 #define ET61X251_AUTHOR_EMAIL   "<luca.risolia@studio.unibo.it>"
 #define ET61X251_MODULE_LICENSE "GPL"
-#define ET61X251_MODULE_VERSION "1:1.02"
-#define ET61X251_MODULE_VERSION_CODE  KERNEL_VERSION(1, 0, 2)
+#define ET61X251_MODULE_VERSION "1:1.09"
+#define ET61X251_MODULE_VERSION_CODE  KERNEL_VERSION(1, 1, 9)
 
 /*****************************************************************************/
 
@@ -85,7 +84,7 @@ MODULE_PARM_DESC(force_munmap,
                 "\ndetected camera."
                 "\n 0 = do not force memory unmapping"
                 "\n 1 = force memory unmapping (save memory)"
-                "\nDefault value is "__MODULE_STRING(SN9C102_FORCE_MUNMAP)"."
+                "\nDefault value is "__MODULE_STRING(ET61X251_FORCE_MUNMAP)"."
                 "\n");
 
 static unsigned int frame_timeout[] = {[0 ... ET61X251_MAX_DEVICES-1] =
@@ -133,7 +132,8 @@ et61x251_request_buffers(struct et61x251_device* cam, u32 count,
 
        cam->nbuffers = count;
        while (cam->nbuffers > 0) {
-               if ((buff = vmalloc_32(cam->nbuffers * PAGE_ALIGN(imagesize))))
+               if ((buff = vmalloc_32_user(cam->nbuffers *
+                                           PAGE_ALIGN(imagesize))))
                        break;
                cam->nbuffers--;
        }
@@ -244,7 +244,8 @@ int et61x251_read_reg(struct et61x251_device* cam, u16 index)
 
 
 static int
-et61x251_i2c_wait(struct et61x251_device* cam, struct et61x251_sensor* sensor)
+et61x251_i2c_wait(struct et61x251_device* cam,
+                 const struct et61x251_sensor* sensor)
 {
        int i, r;
 
@@ -269,7 +270,7 @@ et61x251_i2c_wait(struct et61x251_device* cam, struct et61x251_sensor* sensor)
 
 int
 et61x251_i2c_try_read(struct et61x251_device* cam,
-                     struct et61x251_sensor* sensor, u8 address)
+                     const struct et61x251_sensor* sensor, u8 address)
 {
        struct usb_device* udev = cam->usbdev;
        u8* data = cam->control_buffer;
@@ -302,7 +303,8 @@ et61x251_i2c_try_read(struct et61x251_device* cam,
 
 int
 et61x251_i2c_try_write(struct et61x251_device* cam,
-                      struct et61x251_sensor* sensor, u8 address, u8 value)
+                      const struct et61x251_sensor* sensor, u8 address,
+                      u8 value)
 {
        struct usb_device* udev = cam->usbdev;
        u8* data = cam->control_buffer;
@@ -543,10 +545,11 @@ static int et61x251_start_transfer(struct et61x251_device* cam)
 {
        struct usb_device *udev = cam->usbdev;
        struct urb* urb;
-       const unsigned int wMaxPacketSize[] = {0, 256, 384, 512, 640, 768, 832,
-                                              864, 896, 920, 956, 980, 1000,
-                                              1022};
-       const unsigned int psz = wMaxPacketSize[ET61X251_ALTERNATE_SETTING];
+       struct usb_host_interface* altsetting = usb_altnum_to_altsetting(
+                                                  usb_ifnum_to_if(udev, 0),
+                                                  ET61X251_ALTERNATE_SETTING);
+       const unsigned int psz = le16_to_cpu(altsetting->
+                                            endpoint[0].desc.wMaxPacketSize);
        s8 i, j;
        int err = 0;
 
@@ -613,7 +616,7 @@ static int et61x251_start_transfer(struct et61x251_device* cam)
        return 0;
 
 free_urbs:
-       for (i = 0; (i < ET61X251_URBS) &&  cam->urb[i]; i++)
+       for (i = 0; (i < ET61X251_URBS) && cam->urb[i]; i++)
                usb_free_urb(cam->urb[i]);
 
 free_buffers:
@@ -680,7 +683,7 @@ static u8 et61x251_strtou8(const char* buff, size_t len, ssize_t* count)
 
        if (len < 4) {
                strncpy(str, buff, len);
-               str[len+1] = '\0';
+               str[len] = '\0';
        } else {
                strncpy(str, buff, 4);
                str[4] = '\0';
@@ -703,7 +706,8 @@ static u8 et61x251_strtou8(const char* buff, size_t len, ssize_t* count)
    NOTE 2: buffers are PAGE_SIZE long
 */
 
-static ssize_t et61x251_show_reg(struct class_device* cd, char* buf)
+static ssize_t et61x251_show_reg(struct device* cd,
+                                struct device_attribute *attr, char* buf)
 {
        struct et61x251_device* cam;
        ssize_t count;
@@ -726,7 +730,8 @@ static ssize_t et61x251_show_reg(struct class_device* cd, char* buf)
 
 
 static ssize_t
-et61x251_store_reg(struct class_device* cd, const char* buf, size_t len)
+et61x251_store_reg(struct device* cd,
+                  struct device_attribute *attr, const char* buf, size_t len)
 {
        struct et61x251_device* cam;
        u8 index;
@@ -758,7 +763,8 @@ et61x251_store_reg(struct class_device* cd, const char* buf, size_t len)
 }
 
 
-static ssize_t et61x251_show_val(struct class_device* cd, char* buf)
+static ssize_t et61x251_show_val(struct device* cd,
+                                struct device_attribute *attr, char* buf)
 {
        struct et61x251_device* cam;
        ssize_t count;
@@ -789,7 +795,8 @@ static ssize_t et61x251_show_val(struct class_device* cd, char* buf)
 
 
 static ssize_t
-et61x251_store_val(struct class_device* cd, const char* buf, size_t len)
+et61x251_store_val(struct device* cd, struct device_attribute *attr,
+                  const char* buf, size_t len)
 {
        struct et61x251_device* cam;
        u8 value;
@@ -827,7 +834,8 @@ et61x251_store_val(struct class_device* cd, const char* buf, size_t len)
 }
 
 
-static ssize_t et61x251_show_i2c_reg(struct class_device* cd, char* buf)
+static ssize_t et61x251_show_i2c_reg(struct device* cd,
+                                    struct device_attribute *attr, char* buf)
 {
        struct et61x251_device* cam;
        ssize_t count;
@@ -852,7 +860,8 @@ static ssize_t et61x251_show_i2c_reg(struct class_device* cd, char* buf)
 
 
 static ssize_t
-et61x251_store_i2c_reg(struct class_device* cd, const char* buf, size_t len)
+et61x251_store_i2c_reg(struct device* cd, struct device_attribute *attr,
+                      const char* buf, size_t len)
 {
        struct et61x251_device* cam;
        u8 index;
@@ -884,7 +893,8 @@ et61x251_store_i2c_reg(struct class_device* cd, const char* buf, size_t len)
 }
 
 
-static ssize_t et61x251_show_i2c_val(struct class_device* cd, char* buf)
+static ssize_t et61x251_show_i2c_val(struct device* cd,
+                                    struct device_attribute *attr, char* buf)
 {
        struct et61x251_device* cam;
        ssize_t count;
@@ -920,7 +930,8 @@ static ssize_t et61x251_show_i2c_val(struct class_device* cd, char* buf)
 
 
 static ssize_t
-et61x251_store_i2c_val(struct class_device* cd, const char* buf, size_t len)
+et61x251_store_i2c_val(struct device* cd, struct device_attribute *attr,
+                      const char* buf, size_t len)
 {
        struct et61x251_device* cam;
        u8 value;
@@ -963,42 +974,42 @@ et61x251_store_i2c_val(struct class_device* cd, const char* buf, size_t len)
 }
 
 
-static CLASS_DEVICE_ATTR(reg, S_IRUGO | S_IWUSR,
-                        et61x251_show_reg, et61x251_store_reg);
-static CLASS_DEVICE_ATTR(val, S_IRUGO | S_IWUSR,
-                        et61x251_show_val, et61x251_store_val);
-static CLASS_DEVICE_ATTR(i2c_reg, S_IRUGO | S_IWUSR,
-                        et61x251_show_i2c_reg, et61x251_store_i2c_reg);
-static CLASS_DEVICE_ATTR(i2c_val, S_IRUGO | S_IWUSR,
-                        et61x251_show_i2c_val, et61x251_store_i2c_val);
+static DEVICE_ATTR(reg, S_IRUGO | S_IWUSR,
+                  et61x251_show_reg, et61x251_store_reg);
+static DEVICE_ATTR(val, S_IRUGO | S_IWUSR,
+                  et61x251_show_val, et61x251_store_val);
+static DEVICE_ATTR(i2c_reg, S_IRUGO | S_IWUSR,
+                  et61x251_show_i2c_reg, et61x251_store_i2c_reg);
+static DEVICE_ATTR(i2c_val, S_IRUGO | S_IWUSR,
+                  et61x251_show_i2c_val, et61x251_store_i2c_val);
 
 
 static int et61x251_create_sysfs(struct et61x251_device* cam)
 {
-       struct video_device *v4ldev = cam->v4ldev;
-       int rc;
+       struct device *classdev = &(cam->v4ldev->class_dev);
+       int err = 0;
+
+       if ((err = device_create_file(classdev, &dev_attr_reg)))
+               goto err_out;
+       if ((err = device_create_file(classdev, &dev_attr_val)))
+               goto err_reg;
 
-       rc = video_device_create_file(v4ldev, &class_device_attr_reg);
-       if (rc) goto err;
-       rc = video_device_create_file(v4ldev, &class_device_attr_val);
-       if (rc) goto err_reg;
        if (cam->sensor.sysfs_ops) {
-               rc = video_device_create_file(v4ldev, &class_device_attr_i2c_reg);
-               if (rc) goto err_val;
-               rc = video_device_create_file(v4ldev, &class_device_attr_i2c_val);
-               if (rc) goto err_i2c_reg;
+               if ((err = device_create_file(classdev, &dev_attr_i2c_reg)))
+                       goto err_val;
+               if ((err = device_create_file(classdev, &dev_attr_i2c_val)))
+                       goto err_i2c_reg;
        }
 
-       return 0;
-
 err_i2c_reg:
-       video_device_remove_file(v4ldev, &class_device_attr_i2c_reg);
+       if (cam->sensor.sysfs_ops)
+               device_remove_file(classdev, &dev_attr_i2c_reg);
 err_val:
-       video_device_remove_file(v4ldev, &class_device_attr_val);
+       device_remove_file(classdev, &dev_attr_val);
 err_reg:
-       video_device_remove_file(v4ldev, &class_device_attr_reg);
-err:
-       return rc;
+       device_remove_file(classdev, &dev_attr_reg);
+err_out:
+       return err;
 }
 #endif /* CONFIG_VIDEO_ADV_DEBUG */
 
@@ -1099,7 +1110,8 @@ static int et61x251_init(struct et61x251_device* cam)
        int err = 0;
 
        if (!(cam->state & DEV_INITIALIZED)) {
-               init_waitqueue_head(&cam->open);
+               mutex_init(&cam->open_mutex);
+               init_waitqueue_head(&cam->wait_open);
                qctrl = s->qctrl;
                rect = &(s->cropcap.defrect);
                cam->compression.quality = ET61X251_COMPRESSION_QUALITY;
@@ -1173,64 +1185,80 @@ static int et61x251_init(struct et61x251_device* cam)
        return 0;
 }
 
+/*****************************************************************************/
 
-static void et61x251_release_resources(struct et61x251_device* cam)
+static void et61x251_release_resources(struct kref *kref)
 {
+       struct et61x251_device *cam;
+
        mutex_lock(&et61x251_sysfs_lock);
 
+       cam = container_of(kref, struct et61x251_device, kref);
+
        DBG(2, "V4L2 device /dev/video%d deregistered", cam->v4ldev->minor);
        video_set_drvdata(cam->v4ldev, NULL);
        video_unregister_device(cam->v4ldev);
+       usb_put_dev(cam->usbdev);
+       kfree(cam->control_buffer);
+       kfree(cam);
 
        mutex_unlock(&et61x251_sysfs_lock);
-
-       kfree(cam->control_buffer);
 }
 
-/*****************************************************************************/
 
 static int et61x251_open(struct inode* inode, struct file* filp)
 {
        struct et61x251_device* cam;
        int err = 0;
 
-       /*
-          This is the only safe way to prevent race conditions with
-          disconnect
-       */
-       if (!down_read_trylock(&et61x251_disconnect))
+       if (!down_read_trylock(&et61x251_dev_lock))
                return -ERESTARTSYS;
 
        cam = video_get_drvdata(video_devdata(filp));
 
-       if (mutex_lock_interruptible(&cam->dev_mutex)) {
-               up_read(&et61x251_disconnect);
+       if (wait_for_completion_interruptible(&cam->probe)) {
+               up_read(&et61x251_dev_lock);
+               return -ERESTARTSYS;
+       }
+
+       kref_get(&cam->kref);
+
+       if (mutex_lock_interruptible(&cam->open_mutex)) {
+               kref_put(&cam->kref, et61x251_release_resources);
+               up_read(&et61x251_dev_lock);
                return -ERESTARTSYS;
        }
 
+       if (cam->state & DEV_DISCONNECTED) {
+               DBG(1, "Device not present");
+               err = -ENODEV;
+               goto out;
+       }
+
        if (cam->users) {
-               DBG(2, "Device /dev/video%d is busy...", cam->v4ldev->minor);
+               DBG(2, "Device /dev/video%d is already in use",
+                      cam->v4ldev->minor);
+               DBG(3, "Simultaneous opens are not supported");
                if ((filp->f_flags & O_NONBLOCK) ||
                    (filp->f_flags & O_NDELAY)) {
                        err = -EWOULDBLOCK;
                        goto out;
                }
-               mutex_unlock(&cam->dev_mutex);
-               err = wait_event_interruptible_exclusive(cam->open,
-                                                 cam->state & DEV_DISCONNECTED
+               DBG(2, "A blocking open() has been requested. Wait for the "
+                      "device to be released...");
+               up_read(&et61x251_dev_lock);
+               err = wait_event_interruptible_exclusive(cam->wait_open,
+                                               (cam->state & DEV_DISCONNECTED)
                                                         || !cam->users);
-               if (err) {
-                       up_read(&et61x251_disconnect);
-                       return err;
-               }
+               down_read(&et61x251_dev_lock);
+               if (err)
+                       goto out;
                if (cam->state & DEV_DISCONNECTED) {
-                       up_read(&et61x251_disconnect);
-                       return -ENODEV;
+                       err = -ENODEV;
+                       goto out;
                }
-               mutex_lock(&cam->dev_mutex);
        }
 
-
        if (cam->state & DEV_MISCONFIGURED) {
                err = et61x251_init(cam);
                if (err) {
@@ -1255,36 +1283,32 @@ static int et61x251_open(struct inode* inode, struct file* filp)
        DBG(3, "Video device /dev/video%d is open", cam->v4ldev->minor);
 
 out:
-       mutex_unlock(&cam->dev_mutex);
-       up_read(&et61x251_disconnect);
+       mutex_unlock(&cam->open_mutex);
+       if (err)
+               kref_put(&cam->kref, et61x251_release_resources);
+       up_read(&et61x251_dev_lock);
        return err;
 }
 
 
 static int et61x251_release(struct inode* inode, struct file* filp)
 {
-       struct et61x251_device* cam = video_get_drvdata(video_devdata(filp));
+       struct et61x251_device* cam;
 
-       mutex_lock(&cam->dev_mutex); /* prevent disconnect() to be called */
+       down_write(&et61x251_dev_lock);
 
-       et61x251_stop_transfer(cam);
+       cam = video_get_drvdata(video_devdata(filp));
 
+       et61x251_stop_transfer(cam);
        et61x251_release_buffers(cam);
-
-       if (cam->state & DEV_DISCONNECTED) {
-               et61x251_release_resources(cam);
-               usb_put_dev(cam->usbdev);
-               mutex_unlock(&cam->dev_mutex);
-               kfree(cam);
-               return 0;
-       }
-
        cam->users--;
-       wake_up_interruptible_nr(&cam->open, 1);
+       wake_up_interruptible_nr(&cam->wait_open, 1);
 
        DBG(3, "Video device /dev/video%d closed", cam->v4ldev->minor);
 
-       mutex_unlock(&cam->dev_mutex);
+       kref_put(&cam->kref, et61x251_release_resources);
+
+       up_write(&et61x251_dev_lock);
 
        return 0;
 }
@@ -1320,7 +1344,7 @@ et61x251_read(struct file* filp, char __user * buf,
                DBG(3, "Close and open the device again to choose the read "
                       "method");
                mutex_unlock(&cam->fileop_mutex);
-               return -EINVAL;
+               return -EBUSY;
        }
 
        if (cam->io == IO_NONE) {
@@ -1500,7 +1524,12 @@ static int et61x251_mmap(struct file* filp, struct vm_area_struct *vma)
                return -EIO;
        }
 
-       if (cam->io != IO_MMAP || !(vma->vm_flags & VM_WRITE) ||
+       if (!(vma->vm_flags & (VM_WRITE | VM_READ))) {
+               mutex_unlock(&cam->fileop_mutex);
+               return -EACCES;
+       }
+
+       if (cam->io != IO_MMAP ||
            size != PAGE_ALIGN(cam->frame[0].buf.length)) {
                mutex_unlock(&cam->fileop_mutex);
                return -EINVAL;
@@ -1531,7 +1560,6 @@ static int et61x251_mmap(struct file* filp, struct vm_area_struct *vma)
 
        vma->vm_ops = &et61x251_vm_ops;
        vma->vm_private_data = &cam->frame[i];
-
        et61x251_vm_open(vma);
 
        mutex_unlock(&cam->fileop_mutex);
@@ -1760,17 +1788,17 @@ et61x251_vidioc_s_crop(struct et61x251_device* cam, void __user * arg)
                        if (cam->frame[i].vma_use_count) {
                                DBG(3, "VIDIOC_S_CROP failed. "
                                       "Unmap the buffers first.");
-                               return -EINVAL;
+                               return -EBUSY;
                        }
 
        /* Preserve R,G or B origin */
        rect->left = (s->_rect.left & 1L) ? rect->left | 1L : rect->left & ~1L;
        rect->top = (s->_rect.top & 1L) ? rect->top | 1L : rect->top & ~1L;
 
-       if (rect->width < 4)
-               rect->width = 4;
-       if (rect->height < 4)
-               rect->height = 4;
+       if (rect->width < 16)
+               rect->width = 16;
+       if (rect->height < 16)
+               rect->height = 16;
        if (rect->width > bounds->width)
                rect->width = bounds->width;
        if (rect->height > bounds->height)
@@ -1784,8 +1812,8 @@ et61x251_vidioc_s_crop(struct et61x251_device* cam, void __user * arg)
        if (rect->top + rect->height > bounds->top + bounds->height)
                rect->top = bounds->top+bounds->height - rect->height;
 
-       rect->width &= ~3L;
-       rect->height &= ~3L;
+       rect->width &= ~15L;
+       rect->height &= ~15L;
 
        if (ET61X251_PRESERVE_IMGSCALE) {
                /* Calculate the actual scaling factor */
@@ -1845,6 +1873,35 @@ et61x251_vidioc_s_crop(struct et61x251_device* cam, void __user * arg)
 }
 
 
+static int
+et61x251_vidioc_enum_framesizes(struct et61x251_device* cam, void __user * arg)
+{
+       struct v4l2_frmsizeenum frmsize;
+
+       if (copy_from_user(&frmsize, arg, sizeof(frmsize)))
+               return -EFAULT;
+
+       if (frmsize.index != 0)
+               return -EINVAL;
+
+       if (frmsize.pixel_format != V4L2_PIX_FMT_ET61X251 &&
+           frmsize.pixel_format != V4L2_PIX_FMT_SBGGR8)
+               return -EINVAL;
+
+       frmsize.type = V4L2_FRMSIZE_TYPE_STEPWISE;
+       frmsize.stepwise.min_width = frmsize.stepwise.step_width = 16;
+       frmsize.stepwise.min_height = frmsize.stepwise.step_height = 16;
+       frmsize.stepwise.max_width = cam->sensor.cropcap.bounds.width;
+       frmsize.stepwise.max_height = cam->sensor.cropcap.bounds.height;
+       memset(&frmsize.reserved, 0, sizeof(frmsize.reserved));
+
+       if (copy_to_user(arg, &frmsize, sizeof(frmsize)))
+               return -EFAULT;
+
+       return 0;
+}
+
+
 static int
 et61x251_vidioc_enum_fmt(struct et61x251_device* cam, void __user * arg)
 {
@@ -1853,6 +1910,9 @@ et61x251_vidioc_enum_fmt(struct et61x251_device* cam, void __user * arg)
        if (copy_from_user(&fmtd, arg, sizeof(fmtd)))
                return -EFAULT;
 
+       if (fmtd.type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+               return -EINVAL;
+
        if (fmtd.index == 0) {
                strcpy(fmtd.description, "bayer rgb");
                fmtd.pixelformat = V4L2_PIX_FMT_SBGGR8;
@@ -1885,6 +1945,8 @@ et61x251_vidioc_g_fmt(struct et61x251_device* cam, void __user * arg)
        if (format.type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
                return -EINVAL;
 
+       pfmt->colorspace = (pfmt->pixelformat == V4L2_PIX_FMT_ET61X251) ?
+                          0 : V4L2_COLORSPACE_SRGB;
        pfmt->bytesperline = (pfmt->pixelformat==V4L2_PIX_FMT_ET61X251)
                             ? 0 : (pfmt->width * pfmt->priv) / 8;
        pfmt->sizeimage = pfmt->height * ((pfmt->width*pfmt->priv)/8);
@@ -1934,17 +1996,17 @@ et61x251_vidioc_try_s_fmt(struct et61x251_device* cam, unsigned int cmd,
        rect.width = scale * pix->width;
        rect.height = scale * pix->height;
 
-       if (rect.width < 4)
-               rect.width = 4;
-       if (rect.height < 4)
-               rect.height = 4;
+       if (rect.width < 16)
+               rect.width = 16;
+       if (rect.height < 16)
+               rect.height = 16;
        if (rect.width > bounds->left + bounds->width - rect.left)
                rect.width = bounds->left + bounds->width - rect.left;
        if (rect.height > bounds->top + bounds->height - rect.top)
                rect.height = bounds->top + bounds->height - rect.top;
 
-       rect.width &= ~3L;
-       rect.height &= ~3L;
+       rect.width &= ~15L;
+       rect.height &= ~15L;
 
        { /* adjust the scaling factor */
                u32 a, b;
@@ -1960,6 +2022,8 @@ et61x251_vidioc_try_s_fmt(struct et61x251_device* cam, unsigned int cmd,
            pix->pixelformat != V4L2_PIX_FMT_SBGGR8)
                pix->pixelformat = pfmt->pixelformat;
        pix->priv = pfmt->priv; /* bpp */
+       pix->colorspace = (pix->pixelformat == V4L2_PIX_FMT_ET61X251) ?
+                         0 : V4L2_COLORSPACE_SRGB;
        pix->colorspace = pfmt->colorspace;
        pix->bytesperline = (pix->pixelformat == V4L2_PIX_FMT_ET61X251)
                            ? 0 : (pix->width * pix->priv) / 8;
@@ -1977,7 +2041,7 @@ et61x251_vidioc_try_s_fmt(struct et61x251_device* cam, unsigned int cmd,
                        if (cam->frame[i].vma_use_count) {
                                DBG(3, "VIDIOC_S_FMT failed. "
                                       "Unmap the buffers first.");
-                               return -EINVAL;
+                               return -EBUSY;
                        }
 
        if (cam->stream == STREAM_ON)
@@ -2093,14 +2157,14 @@ et61x251_vidioc_reqbufs(struct et61x251_device* cam, void __user * arg)
        if (cam->io == IO_READ) {
                DBG(3, "Close and open the device again to choose the mmap "
                       "I/O method");
-               return -EINVAL;
+               return -EBUSY;
        }
 
        for (i = 0; i < cam->nbuffers; i++)
                if (cam->frame[i].vma_use_count) {
                        DBG(3, "VIDIOC_REQBUFS failed. "
                               "Previous buffers are still mapped.");
-                       return -EINVAL;
+                       return -EBUSY;
                }
 
        if (cam->stream == STREAM_ON)
@@ -2248,9 +2312,6 @@ et61x251_vidioc_streamon(struct et61x251_device* cam, void __user * arg)
        if (type != V4L2_BUF_TYPE_VIDEO_CAPTURE || cam->io != IO_MMAP)
                return -EINVAL;
 
-       if (list_empty(&cam->inqueue))
-               return -EINVAL;
-
        cam->stream = STREAM_ON;
 
        DBG(3, "Stream on");
@@ -2378,6 +2439,9 @@ static int et61x251_ioctl_v4l2(struct inode* inode, struct file* filp,
        case VIDIOC_S_FMT:
                return et61x251_vidioc_try_s_fmt(cam, cmd, arg);
 
+       case VIDIOC_ENUM_FRAMESIZES:
+               return et61x251_vidioc_enum_framesizes(cam, arg);
+
        case VIDIOC_G_JPEGCOMP:
                return et61x251_vidioc_g_jpegcomp(cam, arg);
 
@@ -2413,6 +2477,7 @@ static int et61x251_ioctl_v4l2(struct inode* inode, struct file* filp,
        case VIDIOC_QUERYSTD:
        case VIDIOC_ENUMSTD:
        case VIDIOC_QUERYMENU:
+       case VIDIOC_ENUM_FRAMEINTERVALS:
                return -EINVAL;
 
        default:
@@ -2459,6 +2524,7 @@ static const struct file_operations et61x251_fops = {
        .open =    et61x251_open,
        .release = et61x251_release,
        .ioctl =   et61x251_ioctl,
+       .compat_ioctl = v4l_compat_ioctl32,
        .read =    et61x251_read,
        .poll =    et61x251_poll,
        .mmap =    et61x251_mmap,
@@ -2494,10 +2560,8 @@ et61x251_usb_probe(struct usb_interface* intf, const struct usb_device_id* id)
                goto fail;
        }
 
-       mutex_init(&cam->dev_mutex);
-
        DBG(2, "ET61X[12]51 PC Camera Controller detected "
-              "(vid/pid 0x%04X/0x%04X)",id->idVendor, id->idProduct);
+              "(vid/pid 0x%04X:0x%04X)",id->idVendor, id->idProduct);
 
        for  (i = 0; et61x251_sensor_table[i]; i++) {
                err = et61x251_sensor_table[i](cam);
@@ -2527,7 +2591,7 @@ et61x251_usb_probe(struct usb_interface* intf, const struct usb_device_id* id)
        cam->v4ldev->release = video_device_release;
        video_set_drvdata(cam->v4ldev, cam);
 
-       mutex_lock(&cam->dev_mutex);
+       init_completion(&cam->probe);
 
        err = video_register_device(cam->v4ldev, VFL_TYPE_GRABBER,
                                    video_nr[dev_nr]);
@@ -2537,7 +2601,7 @@ et61x251_usb_probe(struct usb_interface* intf, const struct usb_device_id* id)
                        DBG(1, "Free /dev/videoX node not found");
                video_nr[dev_nr] = -1;
                dev_nr = (dev_nr < ET61X251_MAX_DEVICES-1) ? dev_nr+1 : 0;
-               mutex_unlock(&cam->dev_mutex);
+               complete_all(&cam->probe);
                goto fail;
        }
 
@@ -2550,24 +2614,26 @@ et61x251_usb_probe(struct usb_interface* intf, const struct usb_device_id* id)
 
 #ifdef CONFIG_VIDEO_ADV_DEBUG
        err = et61x251_create_sysfs(cam);
-       if (err)
-               goto fail2;
-       DBG(2, "Optional device control through 'sysfs' interface ready");
+       if (!err)
+               DBG(2, "Optional device control through 'sysfs' "
+                      "interface ready");
+       else
+               DBG(2, "Failed to create 'sysfs' interface for optional "
+                      "device controlling. Error #%d", err);
+#else
+       DBG(2, "Optional device control through 'sysfs' interface disabled");
+       DBG(3, "Compile the kernel with the 'CONFIG_VIDEO_ADV_DEBUG' "
+              "configuration option to enable it.");
 #endif
 
        usb_set_intfdata(intf, cam);
+       kref_init(&cam->kref);
+       usb_get_dev(cam->usbdev);
 
-       mutex_unlock(&cam->dev_mutex);
+       complete_all(&cam->probe);
 
        return 0;
 
-#ifdef CONFIG_VIDEO_ADV_DEBUG
-fail2:
-       video_nr[dev_nr] = -1;
-       dev_nr = (dev_nr < ET61X251_MAX_DEVICES-1) ? dev_nr+1 : 0;
-       mutex_unlock(&cam->dev_mutex);
-       video_unregister_device(cam->v4ldev);
-#endif
 fail:
        if (cam) {
                kfree(cam->control_buffer);
@@ -2581,40 +2647,31 @@ fail:
 
 static void et61x251_usb_disconnect(struct usb_interface* intf)
 {
-       struct et61x251_device* cam = usb_get_intfdata(intf);
-
-       if (!cam)
-               return;
+       struct et61x251_device* cam;
 
-       down_write(&et61x251_disconnect);
+       down_write(&et61x251_dev_lock);
 
-       mutex_lock(&cam->dev_mutex);
+       cam = usb_get_intfdata(intf);
 
        DBG(2, "Disconnecting %s...", cam->v4ldev->name);
 
-       wake_up_interruptible_all(&cam->open);
-
        if (cam->users) {
                DBG(2, "Device /dev/video%d is open! Deregistration and "
-                      "memory deallocation are deferred on close.",
+                      "memory deallocation are deferred.",
                    cam->v4ldev->minor);
                cam->state |= DEV_MISCONFIGURED;
                et61x251_stop_transfer(cam);
                cam->state |= DEV_DISCONNECTED;
                wake_up_interruptible(&cam->wait_frame);
                wake_up(&cam->wait_stream);
-               usb_get_dev(cam->usbdev);
-       } else {
+       } else
                cam->state |= DEV_DISCONNECTED;
-               et61x251_release_resources(cam);
-       }
 
-       mutex_unlock(&cam->dev_mutex);
+       wake_up_interruptible_all(&cam->wait_open);
 
-       if (!cam->users)
-               kfree(cam);
+       kref_put(&cam->kref, et61x251_release_resources);
 
-       up_write(&et61x251_disconnect);
+       up_write(&et61x251_dev_lock);
 }