Commit 3b2ae0be authored by Luca Risolia's avatar Luca Risolia Committed by Mauro Carvalho Chehab

V4L/DVB (5766): ET61x251 driver updates

- Make the driver depend on V4L2 only (KConfig)
- Better and safe locking mechanism of the device structure on open(),
  close() and disconnect()
- Use kref for handling device deallocation
- Generic cleanups
Signed-off-by: default avatarLuca Risolia <luca.risolia@studio.unibo.it>
Signed-off-by: default avatarMauro Carvalho Chehab <mchehab@infradead.org>
parent 3770be34
config USB_ET61X251 config USB_ET61X251
tristate "USB ET61X[12]51 PC Camera Controller support" tristate "USB ET61X[12]51 PC Camera Controller support"
depends on VIDEO_V4L1 depends on VIDEO_V4L2
---help--- ---help---
Say Y here if you want support for cameras based on Etoms ET61X151 Say Y here if you want support for cameras based on Etoms ET61X151
or ET61X251 PC Camera Controllers. or ET61X251 PC Camera Controllers.
......
...@@ -36,6 +36,7 @@ ...@@ -36,6 +36,7 @@
#include <linux/mutex.h> #include <linux/mutex.h>
#include <linux/stddef.h> #include <linux/stddef.h>
#include <linux/string.h> #include <linux/string.h>
#include <linux/kref.h>
#include "et61x251_sensor.h" #include "et61x251_sensor.h"
...@@ -134,7 +135,7 @@ struct et61x251_module_param { ...@@ -134,7 +135,7 @@ struct et61x251_module_param {
}; };
static DEFINE_MUTEX(et61x251_sysfs_lock); static DEFINE_MUTEX(et61x251_sysfs_lock);
static DECLARE_RWSEM(et61x251_disconnect); static DECLARE_RWSEM(et61x251_dev_lock);
struct et61x251_device { struct et61x251_device {
struct video_device* v4ldev; struct video_device* v4ldev;
...@@ -158,12 +159,14 @@ struct et61x251_device { ...@@ -158,12 +159,14 @@ struct et61x251_device {
struct et61x251_sysfs_attr sysfs; struct et61x251_sysfs_attr sysfs;
struct et61x251_module_param module_param; struct et61x251_module_param module_param;
struct kref kref;
enum et61x251_dev_state state; enum et61x251_dev_state state;
u8 users; u8 users;
struct mutex dev_mutex, fileop_mutex; struct completion probe;
struct mutex open_mutex, fileop_mutex;
spinlock_t queue_lock; spinlock_t queue_lock;
wait_queue_head_t open, wait_frame, wait_stream; wait_queue_head_t wait_open, wait_frame, wait_stream;
}; };
/*****************************************************************************/ /*****************************************************************************/
...@@ -177,7 +180,7 @@ et61x251_match_id(struct et61x251_device* cam, const struct usb_device_id *id) ...@@ -177,7 +180,7 @@ et61x251_match_id(struct et61x251_device* cam, const struct usb_device_id *id)
void void
et61x251_attach_sensor(struct et61x251_device* cam, et61x251_attach_sensor(struct et61x251_device* cam,
struct et61x251_sensor* sensor) const struct et61x251_sensor* sensor)
{ {
memcpy(&cam->sensor, sensor, sizeof(struct et61x251_sensor)); memcpy(&cam->sensor, sensor, sizeof(struct et61x251_sensor));
} }
...@@ -195,8 +198,8 @@ do { \ ...@@ -195,8 +198,8 @@ do { \
else if ((level) == 2) \ else if ((level) == 2) \
dev_info(&cam->usbdev->dev, fmt "\n", ## args); \ dev_info(&cam->usbdev->dev, fmt "\n", ## args); \
else if ((level) >= 3) \ else if ((level) >= 3) \
dev_info(&cam->usbdev->dev, "[%s:%d] " fmt "\n", \ dev_info(&cam->usbdev->dev, "[%s:%s:%d] " fmt "\n", \
__FUNCTION__, __LINE__ , ## args); \ __FILE__, __FUNCTION__, __LINE__ , ## args); \
} \ } \
} while (0) } while (0)
# define KDBG(level, fmt, args...) \ # define KDBG(level, fmt, args...) \
...@@ -205,8 +208,8 @@ do { \ ...@@ -205,8 +208,8 @@ do { \
if ((level) == 1 || (level) == 2) \ if ((level) == 1 || (level) == 2) \
pr_info("et61x251: " fmt "\n", ## args); \ pr_info("et61x251: " fmt "\n", ## args); \
else if ((level) == 3) \ else if ((level) == 3) \
pr_debug("et61x251: [%s:%d] " fmt "\n", __FUNCTION__, \ pr_debug("sn9c102: [%s:%s:%d] " fmt "\n", __FILE__, \
__LINE__ , ## args); \ __FUNCTION__, __LINE__ , ## args); \
} \ } \
} while (0) } while (0)
# define V4LDBG(level, name, cmd) \ # define V4LDBG(level, name, cmd) \
...@@ -222,8 +225,8 @@ do { \ ...@@ -222,8 +225,8 @@ do { \
#undef PDBG #undef PDBG
#define PDBG(fmt, args...) \ #define PDBG(fmt, args...) \
dev_info(&cam->usbdev->dev, "[%s:%d] " fmt "\n", \ dev_info(&cam->usbdev->dev, "[%s:%s:%d] " fmt "\n", __FILE__, __FUNCTION__, \
__FUNCTION__, __LINE__ , ## args) __LINE__ , ## args)
#undef PDBGG #undef PDBGG
#define PDBGG(fmt, args...) do {;} while(0) /* placeholder */ #define PDBGG(fmt, args...) do {;} while(0) /* placeholder */
......
...@@ -45,11 +45,11 @@ ...@@ -45,11 +45,11 @@
#define ET61X251_MODULE_NAME "V4L2 driver for ET61X[12]51 " \ #define ET61X251_MODULE_NAME "V4L2 driver for ET61X[12]51 " \
"PC Camera Controllers" "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_AUTHOR_EMAIL "<luca.risolia@studio.unibo.it>"
#define ET61X251_MODULE_LICENSE "GPL" #define ET61X251_MODULE_LICENSE "GPL"
#define ET61X251_MODULE_VERSION "1:1.04" #define ET61X251_MODULE_VERSION "1:1.09"
#define ET61X251_MODULE_VERSION_CODE KERNEL_VERSION(1, 1, 4) #define ET61X251_MODULE_VERSION_CODE KERNEL_VERSION(1, 1, 9)
/*****************************************************************************/ /*****************************************************************************/
...@@ -245,7 +245,8 @@ int et61x251_read_reg(struct et61x251_device* cam, u16 index) ...@@ -245,7 +245,8 @@ int et61x251_read_reg(struct et61x251_device* cam, u16 index)
static int 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; int i, r;
...@@ -270,7 +271,7 @@ et61x251_i2c_wait(struct et61x251_device* cam, struct et61x251_sensor* sensor) ...@@ -270,7 +271,7 @@ et61x251_i2c_wait(struct et61x251_device* cam, struct et61x251_sensor* sensor)
int int
et61x251_i2c_try_read(struct et61x251_device* cam, 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; struct usb_device* udev = cam->usbdev;
u8* data = cam->control_buffer; u8* data = cam->control_buffer;
...@@ -303,7 +304,8 @@ et61x251_i2c_try_read(struct et61x251_device* cam, ...@@ -303,7 +304,8 @@ et61x251_i2c_try_read(struct et61x251_device* cam,
int int
et61x251_i2c_try_write(struct et61x251_device* cam, 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; struct usb_device* udev = cam->usbdev;
u8* data = cam->control_buffer; u8* data = cam->control_buffer;
...@@ -682,7 +684,7 @@ static u8 et61x251_strtou8(const char* buff, size_t len, ssize_t* count) ...@@ -682,7 +684,7 @@ static u8 et61x251_strtou8(const char* buff, size_t len, ssize_t* count)
if (len < 4) { if (len < 4) {
strncpy(str, buff, len); strncpy(str, buff, len);
str[len+1] = '\0'; str[len] = '\0';
} else { } else {
strncpy(str, buff, 4); strncpy(str, buff, 4);
str[4] = '\0'; str[4] = '\0';
...@@ -977,30 +979,30 @@ static CLASS_DEVICE_ATTR(i2c_val, S_IRUGO | S_IWUSR, ...@@ -977,30 +979,30 @@ static CLASS_DEVICE_ATTR(i2c_val, S_IRUGO | S_IWUSR,
static int et61x251_create_sysfs(struct et61x251_device* cam) static int et61x251_create_sysfs(struct et61x251_device* cam)
{ {
struct video_device *v4ldev = cam->v4ldev; struct class_device *classdev = &(cam->v4ldev->class_dev);
int err = 0; int err = 0;
if ((err = video_device_create_file(v4ldev, &class_device_attr_reg))) if ((err = class_device_create_file(classdev, &class_device_attr_reg)))
goto err_out; goto err_out;
if ((err = video_device_create_file(v4ldev, &class_device_attr_val))) if ((err = class_device_create_file(classdev, &class_device_attr_val)))
goto err_reg; goto err_reg;
if (cam->sensor.sysfs_ops) { if (cam->sensor.sysfs_ops) {
if ((err = video_device_create_file(v4ldev, if ((err = class_device_create_file(classdev,
&class_device_attr_i2c_reg))) &class_device_attr_i2c_reg)))
goto err_val; goto err_val;
if ((err = video_device_create_file(v4ldev, if ((err = class_device_create_file(classdev,
&class_device_attr_i2c_val))) &class_device_attr_i2c_val)))
goto err_i2c_reg; goto err_i2c_reg;
} }
err_i2c_reg: err_i2c_reg:
if (cam->sensor.sysfs_ops) if (cam->sensor.sysfs_ops)
video_device_remove_file(v4ldev, &class_device_attr_i2c_reg); class_device_remove_file(classdev, &class_device_attr_i2c_reg);
err_val: err_val:
video_device_remove_file(v4ldev, &class_device_attr_val); class_device_remove_file(classdev, &class_device_attr_val);
err_reg: err_reg:
video_device_remove_file(v4ldev, &class_device_attr_reg); class_device_remove_file(classdev, &class_device_attr_reg);
err_out: err_out:
return err; return err;
} }
...@@ -1103,7 +1105,8 @@ static int et61x251_init(struct et61x251_device* cam) ...@@ -1103,7 +1105,8 @@ static int et61x251_init(struct et61x251_device* cam)
int err = 0; int err = 0;
if (!(cam->state & DEV_INITIALIZED)) { if (!(cam->state & DEV_INITIALIZED)) {
init_waitqueue_head(&cam->open); mutex_init(&cam->open_mutex);
init_waitqueue_head(&cam->wait_open);
qctrl = s->qctrl; qctrl = s->qctrl;
rect = &(s->cropcap.defrect); rect = &(s->cropcap.defrect);
cam->compression.quality = ET61X251_COMPRESSION_QUALITY; cam->compression.quality = ET61X251_COMPRESSION_QUALITY;
...@@ -1177,64 +1180,80 @@ static int et61x251_init(struct et61x251_device* cam) ...@@ -1177,64 +1180,80 @@ static int et61x251_init(struct et61x251_device* cam)
return 0; 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); mutex_lock(&et61x251_sysfs_lock);
cam = container_of(kref, struct et61x251_device, kref);
DBG(2, "V4L2 device /dev/video%d deregistered", cam->v4ldev->minor); DBG(2, "V4L2 device /dev/video%d deregistered", cam->v4ldev->minor);
video_set_drvdata(cam->v4ldev, NULL); video_set_drvdata(cam->v4ldev, NULL);
video_unregister_device(cam->v4ldev); video_unregister_device(cam->v4ldev);
usb_put_dev(cam->usbdev);
kfree(cam->control_buffer);
kfree(cam);
mutex_unlock(&et61x251_sysfs_lock); mutex_unlock(&et61x251_sysfs_lock);
kfree(cam->control_buffer);
} }
/*****************************************************************************/
static int et61x251_open(struct inode* inode, struct file* filp) static int et61x251_open(struct inode* inode, struct file* filp)
{ {
struct et61x251_device* cam; struct et61x251_device* cam;
int err = 0; int err = 0;
/* if (!down_read_trylock(&et61x251_dev_lock))
This is the only safe way to prevent race conditions with
disconnect
*/
if (!down_read_trylock(&et61x251_disconnect))
return -ERESTARTSYS; return -ERESTARTSYS;
cam = video_get_drvdata(video_devdata(filp)); cam = video_get_drvdata(video_devdata(filp));
if (mutex_lock_interruptible(&cam->dev_mutex)) { if (wait_for_completion_interruptible(&cam->probe)) {
up_read(&et61x251_disconnect); up_read(&et61x251_dev_lock);
return -ERESTARTSYS; 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) { 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) || if ((filp->f_flags & O_NONBLOCK) ||
(filp->f_flags & O_NDELAY)) { (filp->f_flags & O_NDELAY)) {
err = -EWOULDBLOCK; err = -EWOULDBLOCK;
goto out; goto out;
} }
mutex_unlock(&cam->dev_mutex); DBG(2, "A blocking open() has been requested. Wait for the "
err = wait_event_interruptible_exclusive(cam->open, "device to be released...");
cam->state & DEV_DISCONNECTED up_read(&et61x251_dev_lock);
err = wait_event_interruptible_exclusive(cam->wait_open,
(cam->state & DEV_DISCONNECTED)
|| !cam->users); || !cam->users);
if (err) { down_read(&et61x251_dev_lock);
up_read(&et61x251_disconnect); if (err)
return err; goto out;
}
if (cam->state & DEV_DISCONNECTED) { if (cam->state & DEV_DISCONNECTED) {
up_read(&et61x251_disconnect); err = -ENODEV;
return -ENODEV; goto out;
} }
mutex_lock(&cam->dev_mutex);
} }
if (cam->state & DEV_MISCONFIGURED) { if (cam->state & DEV_MISCONFIGURED) {
err = et61x251_init(cam); err = et61x251_init(cam);
if (err) { if (err) {
...@@ -1259,36 +1278,32 @@ static int et61x251_open(struct inode* inode, struct file* filp) ...@@ -1259,36 +1278,32 @@ static int et61x251_open(struct inode* inode, struct file* filp)
DBG(3, "Video device /dev/video%d is open", cam->v4ldev->minor); DBG(3, "Video device /dev/video%d is open", cam->v4ldev->minor);
out: out:
mutex_unlock(&cam->dev_mutex); mutex_unlock(&cam->open_mutex);
up_read(&et61x251_disconnect); if (err)
kref_put(&cam->kref, et61x251_release_resources);
up_read(&et61x251_dev_lock);
return err; return err;
} }
static int et61x251_release(struct inode* inode, struct file* filp) 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); 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--; 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); 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; return 0;
} }
...@@ -1324,7 +1339,7 @@ et61x251_read(struct file* filp, char __user * buf, ...@@ -1324,7 +1339,7 @@ et61x251_read(struct file* filp, char __user * buf,
DBG(3, "Close and open the device again to choose the read " DBG(3, "Close and open the device again to choose the read "
"method"); "method");
mutex_unlock(&cam->fileop_mutex); mutex_unlock(&cam->fileop_mutex);
return -EINVAL; return -EBUSY;
} }
if (cam->io == IO_NONE) { if (cam->io == IO_NONE) {
...@@ -1504,7 +1519,12 @@ static int et61x251_mmap(struct file* filp, struct vm_area_struct *vma) ...@@ -1504,7 +1519,12 @@ static int et61x251_mmap(struct file* filp, struct vm_area_struct *vma)
return -EIO; 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)) { size != PAGE_ALIGN(cam->frame[0].buf.length)) {
mutex_unlock(&cam->fileop_mutex); mutex_unlock(&cam->fileop_mutex);
return -EINVAL; return -EINVAL;
...@@ -1535,7 +1555,6 @@ static int et61x251_mmap(struct file* filp, struct vm_area_struct *vma) ...@@ -1535,7 +1555,6 @@ static int et61x251_mmap(struct file* filp, struct vm_area_struct *vma)
vma->vm_ops = &et61x251_vm_ops; vma->vm_ops = &et61x251_vm_ops;
vma->vm_private_data = &cam->frame[i]; vma->vm_private_data = &cam->frame[i];
et61x251_vm_open(vma); et61x251_vm_open(vma);
mutex_unlock(&cam->fileop_mutex); mutex_unlock(&cam->fileop_mutex);
...@@ -1764,7 +1783,7 @@ et61x251_vidioc_s_crop(struct et61x251_device* cam, void __user * arg) ...@@ -1764,7 +1783,7 @@ et61x251_vidioc_s_crop(struct et61x251_device* cam, void __user * arg)
if (cam->frame[i].vma_use_count) { if (cam->frame[i].vma_use_count) {
DBG(3, "VIDIOC_S_CROP failed. " DBG(3, "VIDIOC_S_CROP failed. "
"Unmap the buffers first."); "Unmap the buffers first.");
return -EINVAL; return -EBUSY;
} }
/* Preserve R,G or B origin */ /* Preserve R,G or B origin */
...@@ -1921,6 +1940,8 @@ et61x251_vidioc_g_fmt(struct et61x251_device* cam, void __user * arg) ...@@ -1921,6 +1940,8 @@ et61x251_vidioc_g_fmt(struct et61x251_device* cam, void __user * arg)
if (format.type != V4L2_BUF_TYPE_VIDEO_CAPTURE) if (format.type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
return -EINVAL; return -EINVAL;
pfmt->colorspace = (pfmt->pixelformat == V4L2_PIX_FMT_ET61X251) ?
0 : V4L2_COLORSPACE_SRGB;
pfmt->bytesperline = (pfmt->pixelformat==V4L2_PIX_FMT_ET61X251) pfmt->bytesperline = (pfmt->pixelformat==V4L2_PIX_FMT_ET61X251)
? 0 : (pfmt->width * pfmt->priv) / 8; ? 0 : (pfmt->width * pfmt->priv) / 8;
pfmt->sizeimage = pfmt->height * ((pfmt->width*pfmt->priv)/8); pfmt->sizeimage = pfmt->height * ((pfmt->width*pfmt->priv)/8);
...@@ -1996,6 +2017,8 @@ et61x251_vidioc_try_s_fmt(struct et61x251_device* cam, unsigned int cmd, ...@@ -1996,6 +2017,8 @@ et61x251_vidioc_try_s_fmt(struct et61x251_device* cam, unsigned int cmd,
pix->pixelformat != V4L2_PIX_FMT_SBGGR8) pix->pixelformat != V4L2_PIX_FMT_SBGGR8)
pix->pixelformat = pfmt->pixelformat; pix->pixelformat = pfmt->pixelformat;
pix->priv = pfmt->priv; /* bpp */ pix->priv = pfmt->priv; /* bpp */
pix->colorspace = (pix->pixelformat == V4L2_PIX_FMT_ET61X251) ?
0 : V4L2_COLORSPACE_SRGB;
pix->colorspace = pfmt->colorspace; pix->colorspace = pfmt->colorspace;
pix->bytesperline = (pix->pixelformat == V4L2_PIX_FMT_ET61X251) pix->bytesperline = (pix->pixelformat == V4L2_PIX_FMT_ET61X251)
? 0 : (pix->width * pix->priv) / 8; ? 0 : (pix->width * pix->priv) / 8;
...@@ -2013,7 +2036,7 @@ et61x251_vidioc_try_s_fmt(struct et61x251_device* cam, unsigned int cmd, ...@@ -2013,7 +2036,7 @@ et61x251_vidioc_try_s_fmt(struct et61x251_device* cam, unsigned int cmd,
if (cam->frame[i].vma_use_count) { if (cam->frame[i].vma_use_count) {
DBG(3, "VIDIOC_S_FMT failed. " DBG(3, "VIDIOC_S_FMT failed. "
"Unmap the buffers first."); "Unmap the buffers first.");
return -EINVAL; return -EBUSY;
} }
if (cam->stream == STREAM_ON) if (cam->stream == STREAM_ON)
...@@ -2129,14 +2152,14 @@ et61x251_vidioc_reqbufs(struct et61x251_device* cam, void __user * arg) ...@@ -2129,14 +2152,14 @@ et61x251_vidioc_reqbufs(struct et61x251_device* cam, void __user * arg)
if (cam->io == IO_READ) { if (cam->io == IO_READ) {
DBG(3, "Close and open the device again to choose the mmap " DBG(3, "Close and open the device again to choose the mmap "
"I/O method"); "I/O method");
return -EINVAL; return -EBUSY;
} }
for (i = 0; i < cam->nbuffers; i++) for (i = 0; i < cam->nbuffers; i++)
if (cam->frame[i].vma_use_count) { if (cam->frame[i].vma_use_count) {
DBG(3, "VIDIOC_REQBUFS failed. " DBG(3, "VIDIOC_REQBUFS failed. "
"Previous buffers are still mapped."); "Previous buffers are still mapped.");
return -EINVAL; return -EBUSY;
} }
if (cam->stream == STREAM_ON) if (cam->stream == STREAM_ON)
...@@ -2284,9 +2307,6 @@ et61x251_vidioc_streamon(struct et61x251_device* cam, void __user * arg) ...@@ -2284,9 +2307,6 @@ et61x251_vidioc_streamon(struct et61x251_device* cam, void __user * arg)
if (type != V4L2_BUF_TYPE_VIDEO_CAPTURE || cam->io != IO_MMAP) if (type != V4L2_BUF_TYPE_VIDEO_CAPTURE || cam->io != IO_MMAP)
return -EINVAL; return -EINVAL;
if (list_empty(&cam->inqueue))
return -EINVAL;
cam->stream = STREAM_ON; cam->stream = STREAM_ON;
DBG(3, "Stream on"); DBG(3, "Stream on");
...@@ -2535,8 +2555,6 @@ et61x251_usb_probe(struct usb_interface* intf, const struct usb_device_id* id) ...@@ -2535,8 +2555,6 @@ et61x251_usb_probe(struct usb_interface* intf, const struct usb_device_id* id)
goto fail; goto fail;
} }
mutex_init(&cam->dev_mutex);
DBG(2, "ET61X[12]51 PC Camera Controller detected " 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);
...@@ -2568,7 +2586,7 @@ et61x251_usb_probe(struct usb_interface* intf, const struct usb_device_id* id) ...@@ -2568,7 +2586,7 @@ et61x251_usb_probe(struct usb_interface* intf, const struct usb_device_id* id)
cam->v4ldev->release = video_device_release; cam->v4ldev->release = video_device_release;
video_set_drvdata(cam->v4ldev, cam); video_set_drvdata(cam->v4ldev, cam);
mutex_lock(&cam->dev_mutex); init_completion(&cam->probe);
err = video_register_device(cam->v4ldev, VFL_TYPE_GRABBER, err = video_register_device(cam->v4ldev, VFL_TYPE_GRABBER,
video_nr[dev_nr]); video_nr[dev_nr]);
...@@ -2578,7 +2596,7 @@ et61x251_usb_probe(struct usb_interface* intf, const struct usb_device_id* id) ...@@ -2578,7 +2596,7 @@ et61x251_usb_probe(struct usb_interface* intf, const struct usb_device_id* id)
DBG(1, "Free /dev/videoX node not found"); DBG(1, "Free /dev/videoX node not found");
video_nr[dev_nr] = -1; video_nr[dev_nr] = -1;
dev_nr = (dev_nr < ET61X251_MAX_DEVICES-1) ? dev_nr+1 : 0; dev_nr = (dev_nr < ET61X251_MAX_DEVICES-1) ? dev_nr+1 : 0;
mutex_unlock(&cam->dev_mutex); complete_all(&cam->probe);
goto fail; goto fail;
} }
...@@ -2599,11 +2617,15 @@ et61x251_usb_probe(struct usb_interface* intf, const struct usb_device_id* id) ...@@ -2599,11 +2617,15 @@ et61x251_usb_probe(struct usb_interface* intf, const struct usb_device_id* id)
"device controlling. Error #%d", err); "device controlling. Error #%d", err);
#else #else
DBG(2, "Optional device control through 'sysfs' interface disabled"); 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 #endif
usb_set_intfdata(intf, cam); 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; return 0;
...@@ -2620,40 +2642,31 @@ et61x251_usb_probe(struct usb_interface* intf, const struct usb_device_id* id) ...@@ -2620,40 +2642,31 @@ et61x251_usb_probe(struct usb_interface* intf, const struct usb_device_id* id)
static void et61x251_usb_disconnect(struct usb_interface* intf) static void et61x251_usb_disconnect(struct usb_interface* intf)
{ {
struct et61x251_device* cam = usb_get_intfdata(intf); struct et61x251_device* cam;
if (!cam)
return;
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); DBG(2, "Disconnecting %s...", cam->v4ldev->name);
wake_up_interruptible_all(&cam->open);
if (cam->users) { if (cam->users) {
DBG(2, "Device /dev/video%d is open! Deregistration and " DBG(2, "Device /dev/video%d is open! Deregistration and "
"memory deallocation are deferred on close.", "memory deallocation are deferred.",
cam->v4ldev->minor); cam->v4ldev->minor);
cam->state |= DEV_MISCONFIGURED; cam->state |= DEV_MISCONFIGURED;
et61x251_stop_transfer(cam); et61x251_stop_transfer(cam);
cam->state |= DEV_DISCONNECTED; cam->state |= DEV_DISCONNECTED;
wake_up_interruptible(&cam->wait_frame); wake_up_interruptible(&cam->wait_frame);
wake_up(&cam->wait_stream); wake_up(&cam->wait_stream);
usb_get_dev(cam->usbdev); } else
} else {
cam->state |= DEV_DISCONNECTED; cam->state |= DEV_DISCONNECTED;
et61x251_release_resources(cam);
}
mutex_unlock(&cam->dev_mutex); wake_up_interruptible_all(&cam->wait_open);
if (!cam->users) kref_put(&cam->kref, et61x251_release_resources);
kfree(cam);
up_write(&et61x251_disconnect); up_write(&et61x251_dev_lock);
} }
......
...@@ -22,7 +22,7 @@ ...@@ -22,7 +22,7 @@
#define _ET61X251_SENSOR_H_ #define _ET61X251_SENSOR_H_
#include <linux/usb.h> #include <linux/usb.h>
#include <linux/videodev.h> #include <linux/videodev2.h>
#include <linux/device.h> #include <linux/device.h>
#include <linux/stddef.h> #include <linux/stddef.h>
#include <linux/errno.h> #include <linux/errno.h>
...@@ -47,7 +47,7 @@ et61x251_match_id(struct et61x251_device* cam, const struct usb_device_id *id); ...@@ -47,7 +47,7 @@ et61x251_match_id(struct et61x251_device* cam, const struct usb_device_id *id);
extern void extern void
et61x251_attach_sensor(struct et61x251_device* cam, et61x251_attach_sensor(struct et61x251_device* cam,
struct et61x251_sensor* sensor); const struct et61x251_sensor* sensor);
/*****************************************************************************/ /*****************************************************************************/
...@@ -56,10 +56,10 @@ extern int et61x251_read_reg(struct et61x251_device*, u16 index); ...@@ -56,10 +56,10 @@ extern int et61x251_read_reg(struct et61x251_device*, u16 index);
extern int et61x251_i2c_write(struct et61x251_device*, u8 address, u8 value); extern int et61x251_i2c_write(struct et61x251_device*, u8 address, u8 value);
extern int et61x251_i2c_read(struct et61x251_device*, u8 address); extern int et61x251_i2c_read(struct et61x251_device*, u8 address);
extern int et61x251_i2c_try_write(struct et61x251_device*, extern int et61x251_i2c_try_write(struct et61x251_device*,
struct et61x251_sensor*, u8 address, const struct et61x251_sensor*, u8 address,
u8 value); u8 value);
extern int et61x251_i2c_try_read(struct et61x251_device*, extern int et61x251_i2c_try_read(struct et61x251_device*,
struct et61x251_sensor*, u8 address); const struct et61x251_sensor*, u8 address);
extern int et61x251_i2c_raw_write(struct et61x251_device*, u8 n, u8 data1, extern int et61x251_i2c_raw_write(struct et61x251_device*, u8 n, u8 data1,
u8 data2, u8 data3, u8 data4, u8 data5, u8 data2, u8 data3, u8 data4, u8 data5,
u8 data6, u8 data7, u8 data8, u8 address); u8 data6, u8 data7, u8 data8, u8 address);
......
...@@ -69,7 +69,7 @@ static int tas5130d1b_set_ctrl(struct et61x251_device* cam, ...@@ -69,7 +69,7 @@ static int tas5130d1b_set_ctrl(struct et61x251_device* cam,
} }
static struct et61x251_sensor tas5130d1b = { static const struct et61x251_sensor tas5130d1b = {
.name = "TAS5130D1B", .name = "TAS5130D1B",
.interface = ET61X251_I2C_3WIRES, .interface = ET61X251_I2C_3WIRES,
.rsta = ET61X251_I2C_RSTA_STOP, .rsta = ET61X251_I2C_RSTA_STOP,
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment