/***************************************************************************
* Video4Linux2 driver for ZC0301[P] Image Processor and Control Chip *
* *
- * Copyright (C) 2006 by Luca Risolia <luca.risolia@studio.unibo.it> *
+ * Copyright (C) 2006-2007 by Luca Risolia <luca.risolia@studio.unibo.it> *
* *
* Informations about the chip internals needed to enable the I2C protocol *
* have been taken from the documentation of the ZC030x Video4Linux1 *
#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 ZC0301_MODULE_NAME "V4L2 driver for ZC0301[P] " \
"Image Processor and Control Chip"
-#define ZC0301_MODULE_AUTHOR "(C) 2006 Luca Risolia"
+#define ZC0301_MODULE_AUTHOR "(C) 2006-2007 Luca Risolia"
#define ZC0301_AUTHOR_EMAIL "<luca.risolia@studio.unibo.it>"
#define ZC0301_MODULE_LICENSE "GPL"
-#define ZC0301_MODULE_VERSION "1:1.05"
-#define ZC0301_MODULE_VERSION_CODE KERNEL_VERSION(1, 0, 5)
+#define ZC0301_MODULE_VERSION "1:1.10"
+#define ZC0301_MODULE_VERSION_CODE KERNEL_VERSION(1, 1, 10)
/*****************************************************************************/
"\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(ZC0301_FORCE_MUNMAP)"."
"\n");
static unsigned int frame_timeout[] = {[0 ... ZC0301_MAX_DEVICES-1] =
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--;
}
struct usb_host_interface* altsetting = usb_altnum_to_altsetting(
usb_ifnum_to_if(udev, 0),
ZC0301_ALTERNATE_SETTING);
- const unsigned int psz = altsetting->endpoint[0].desc.wMaxPacketSize;
+ const unsigned int psz = le16_to_cpu(altsetting->
+ endpoint[0].desc.wMaxPacketSize);
struct urb* urb;
s8 i, j;
int err = 0;
return 0;
free_urbs:
- for (i = 0; i < ZC0301_URBS; i++)
+ for (i = 0; (i < ZC0301_URBS) && cam->urb[i]; i++)
usb_free_urb(cam->urb[i]);
free_buffers:
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 = ZC0301_COMPRESSION_QUALITY;
return 0;
}
+/*****************************************************************************/
-static void zc0301_release_resources(struct zc0301_device* cam)
+static void zc0301_release_resources(struct kref *kref)
{
+ struct zc0301_device *cam = container_of(kref, struct zc0301_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);
}
-/*****************************************************************************/
static int zc0301_open(struct inode* inode, struct file* filp)
{
struct zc0301_device* cam;
int err = 0;
- /*
- This is the only safe way to prevent race conditions with
- disconnect
- */
- if (!down_read_trylock(&zc0301_disconnect))
- return -ERESTARTSYS;
+ if (!down_read_trylock(&zc0301_dev_lock))
+ return -EAGAIN;
cam = video_get_drvdata(video_devdata(filp));
- if (mutex_lock_interruptible(&cam->dev_mutex)) {
- up_read(&zc0301_disconnect);
+ if (wait_for_completion_interruptible(&cam->probe)) {
+ up_read(&zc0301_dev_lock);
+ return -ERESTARTSYS;
+ }
+
+ kref_get(&cam->kref);
+
+ if (mutex_lock_interruptible(&cam->open_mutex)) {
+ kref_put(&cam->kref, zc0301_release_resources);
+ up_read(&zc0301_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(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(&zc0301_dev_lock);
+ err = wait_event_interruptible_exclusive(cam->wait_open,
+ (cam->state & DEV_DISCONNECTED)
|| !cam->users);
- if (err) {
- up_read(&zc0301_disconnect);
- return err;
- }
+ down_read(&zc0301_dev_lock);
+ if (err)
+ goto out;
if (cam->state & DEV_DISCONNECTED) {
- up_read(&zc0301_disconnect);
- return -ENODEV;
+ err = -ENODEV;
+ goto out;
}
- mutex_lock(&cam->dev_mutex);
}
-
if (cam->state & DEV_MISCONFIGURED) {
err = zc0301_init(cam);
if (err) {
DBG(3, "Video device /dev/video%d is open", cam->v4ldev->minor);
out:
- mutex_unlock(&cam->dev_mutex);
- up_read(&zc0301_disconnect);
+ mutex_unlock(&cam->open_mutex);
+ if (err)
+ kref_put(&cam->kref, zc0301_release_resources);
+ up_read(&zc0301_dev_lock);
return err;
}
static int zc0301_release(struct inode* inode, struct file* filp)
{
- struct zc0301_device* cam = video_get_drvdata(video_devdata(filp));
+ struct zc0301_device* cam;
- mutex_lock(&cam->dev_mutex); /* prevent disconnect() to be called */
+ down_write(&zc0301_dev_lock);
- zc0301_stop_transfer(cam);
+ cam = video_get_drvdata(video_devdata(filp));
+ zc0301_stop_transfer(cam);
zc0301_release_buffers(cam);
-
- if (cam->state & DEV_DISCONNECTED) {
- zc0301_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, zc0301_release_resources);
+
+ up_write(&zc0301_dev_lock);
return 0;
}
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) {
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;
vma->vm_ops = &zc0301_vm_ops;
vma->vm_private_data = &cam->frame[i];
-
zc0301_vm_open(vma);
mutex_unlock(&cam->fileop_mutex);
if (cam->frame[i].vma_use_count) {
DBG(3, "VIDIOC_S_CROP failed. "
"Unmap the buffers first.");
- return -EINVAL;
+ return -EBUSY;
}
if (!s->set_crop) {
}
+static int
+zc0301_vidioc_enum_framesizes(struct zc0301_device* cam, void __user * arg)
+{
+ struct v4l2_frmsizeenum frmsize;
+
+ if (copy_from_user(&frmsize, arg, sizeof(frmsize)))
+ return -EFAULT;
+
+ if (frmsize.index != 0 && frmsize.index != 1)
+ return -EINVAL;
+
+ if (frmsize.pixel_format != V4L2_PIX_FMT_JPEG)
+ return -EINVAL;
+
+ frmsize.type = V4L2_FRMSIZE_TYPE_DISCRETE;
+
+ if (frmsize.index == 1) {
+ frmsize.discrete.width = cam->sensor.cropcap.defrect.width;
+ frmsize.discrete.height = cam->sensor.cropcap.defrect.height;
+ }
+ memset(&frmsize.reserved, 0, sizeof(frmsize.reserved));
+
+ if (copy_to_user(arg, &frmsize, sizeof(frmsize)))
+ return -EFAULT;
+
+ return 0;
+}
+
+
static int
zc0301_vidioc_enum_fmt(struct zc0301_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, "JPEG");
fmtd.pixelformat = V4L2_PIX_FMT_JPEG;
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)
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)
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");
case VIDIOC_S_FMT:
return zc0301_vidioc_try_s_fmt(cam, cmd, arg);
+ case VIDIOC_ENUM_FRAMESIZES:
+ return zc0301_vidioc_enum_framesizes(cam, arg);
+
case VIDIOC_G_JPEGCOMP:
return zc0301_vidioc_g_jpegcomp(cam, arg);
case VIDIOC_QUERYSTD:
case VIDIOC_ENUMSTD:
case VIDIOC_QUERYMENU:
+ case VIDIOC_ENUM_FRAMEINTERVALS:
return -EINVAL;
default:
.open = zc0301_open,
.release = zc0301_release,
.ioctl = zc0301_ioctl,
+ .compat_ioctl = v4l_compat_ioctl32,
.read = zc0301_read,
.poll = zc0301_poll,
.mmap = zc0301_mmap,
goto fail;
}
- mutex_init(&cam->dev_mutex);
-
DBG(2, "ZC0301[P] Image Processor and Control Chip detected "
- "(vid/pid 0x%04X/0x%04X)",id->idVendor, id->idProduct);
+ "(vid/pid 0x%04X:0x%04X)",id->idVendor, id->idProduct);
for (i = 0; zc0301_sensor_table[i]; i++) {
err = zc0301_sensor_table[i](cam);
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]);
DBG(1, "Free /dev/videoX node not found");
video_nr[dev_nr] = -1;
dev_nr = (dev_nr < ZC0301_MAX_DEVICES-1) ? dev_nr+1 : 0;
- mutex_unlock(&cam->dev_mutex);
+ complete_all(&cam->probe);
goto fail;
}
dev_nr = (dev_nr < ZC0301_MAX_DEVICES-1) ? dev_nr+1 : 0;
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;
static void zc0301_usb_disconnect(struct usb_interface* intf)
{
- struct zc0301_device* cam = usb_get_intfdata(intf);
-
- if (!cam)
- return;
+ struct zc0301_device* cam;
- down_write(&zc0301_disconnect);
+ down_write(&zc0301_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;
zc0301_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;
- zc0301_release_resources(cam);
- }
- mutex_unlock(&cam->dev_mutex);
+ wake_up_interruptible_all(&cam->wait_open);
- if (!cam->users)
- kfree(cam);
+ kref_put(&cam->kref, zc0301_release_resources);
- up_write(&zc0301_disconnect);
+ up_write(&zc0301_dev_lock);
}