Commit a3a048ce authored by Mauro Carvalho Chehab's avatar Mauro Carvalho Chehab

V4L/DVB (6582): Fix em28xx to allow multiple open

Allows shared access support for em28xx. Just one userspace application is
allowed to get stream. The other(s) application(s) can change V4L2 controls,
set video standards, etc.

This patch were splited from Markus Rechberger's tree and backported to 2.6.17
by Pádraig Brady.

The original patch were ported to the latest em28xx version and had CodingStyle
corrected to solve the issues pointed by scripts/checkpatch.pl.

Thanks to Pádraig Brady <P@draigBrady.com> for pointing this.
Signed-off-by: default avatarMauro Carvalho Chehab <mchehab@infradead.org>
parent 2c94a674
...@@ -234,6 +234,7 @@ static int em28xx_v4l2_open(struct inode *inode, struct file *filp) ...@@ -234,6 +234,7 @@ static int em28xx_v4l2_open(struct inode *inode, struct file *filp)
int minor = iminor(inode); int minor = iminor(inode);
int errCode = 0; int errCode = 0;
struct em28xx *h,*dev = NULL; struct em28xx *h,*dev = NULL;
struct em28xx_fh *fh;
list_for_each_entry(h, &em28xx_devlist, devlist) { list_for_each_entry(h, &em28xx_devlist, devlist) {
if (h->vdev->minor == minor) { if (h->vdev->minor == minor) {
...@@ -251,19 +252,17 @@ static int em28xx_v4l2_open(struct inode *inode, struct file *filp) ...@@ -251,19 +252,17 @@ static int em28xx_v4l2_open(struct inode *inode, struct file *filp)
em28xx_videodbg("open minor=%d type=%s users=%d\n", em28xx_videodbg("open minor=%d type=%s users=%d\n",
minor,v4l2_type_names[dev->type],dev->users); minor,v4l2_type_names[dev->type],dev->users);
mutex_lock(&dev->lock); fh = kzalloc(sizeof(struct em28xx_fh), GFP_KERNEL);
if (dev->users) { if (!fh) {
em28xx_warn("this driver can be opened only once\n"); em28xx_errdev("em28xx-video.c: Out of memory?!\n");
mutex_unlock(&dev->lock); return -ENOMEM;
return -EBUSY;
} }
mutex_lock(&dev->lock);
fh->dev = dev;
filp->private_data = fh;
spin_lock_init(&dev->queue_lock); if (dev->type == V4L2_BUF_TYPE_VIDEO_CAPTURE && dev->users == 0) {
init_waitqueue_head(&dev->wait_frame);
init_waitqueue_head(&dev->wait_stream);
if (dev->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) {
em28xx_set_alternate(dev); em28xx_set_alternate(dev);
dev->width = norm_maxw(dev); dev->width = norm_maxw(dev);
...@@ -277,26 +276,16 @@ static int em28xx_v4l2_open(struct inode *inode, struct file *filp) ...@@ -277,26 +276,16 @@ static int em28xx_v4l2_open(struct inode *inode, struct file *filp)
em28xx_capture_start(dev, 1); em28xx_capture_start(dev, 1);
em28xx_resolution_set(dev); em28xx_resolution_set(dev);
/* device needs to be initialized before isoc transfer */
video_mux(dev, 0);
/* start the transfer */ /* start the transfer */
errCode = em28xx_init_isoc(dev); errCode = em28xx_init_isoc(dev);
if (errCode) if (errCode)
goto err; goto err;
em28xx_empty_framequeues(dev);
} }
dev->users++; dev->users++;
filp->private_data = dev;
dev->io = IO_NONE;
dev->stream = STREAM_OFF;
dev->num_frames = 0;
/* prepare queues */
em28xx_empty_framequeues(dev);
dev->state |= DEV_INITIALIZED;
err: err:
mutex_unlock(&dev->lock); mutex_unlock(&dev->lock);
...@@ -333,34 +322,41 @@ static void em28xx_release_resources(struct em28xx *dev) ...@@ -333,34 +322,41 @@ static void em28xx_release_resources(struct em28xx *dev)
*/ */
static int em28xx_v4l2_close(struct inode *inode, struct file *filp) static int em28xx_v4l2_close(struct inode *inode, struct file *filp)
{ {
int errCode; struct em28xx_fh *fh = filp->private_data;
struct em28xx *dev=filp->private_data; struct em28xx *dev = fh->dev;
int errCode;
em28xx_videodbg("users=%d\n", dev->users); em28xx_videodbg("users=%d\n", dev->users);
mutex_lock(&dev->lock); mutex_lock(&dev->lock);
if (fh->reader == 1)
fh->reader = 0;
em28xx_uninit_isoc(dev); if (dev->users == 1) {
dev->reader = 0;
em28xx_release_buffers(dev); em28xx_uninit_isoc(dev);
em28xx_release_buffers(dev);
/* the device is already disconnect, free the remaining resources */ /* the device is already disconnect,
if (dev->state & DEV_DISCONNECTED) { free the remaining resources */
em28xx_release_resources(dev); if (dev->state & DEV_DISCONNECTED) {
mutex_unlock(&dev->lock); em28xx_release_resources(dev);
kfree(dev); mutex_unlock(&dev->lock);
return 0; kfree(dev);
} return 0;
}
/* set alternate 0 */ /* set alternate 0 */
dev->alt = 0; dev->alt = 0;
em28xx_videodbg("setting alternate 0\n"); em28xx_videodbg("setting alternate 0\n");
errCode = usb_set_interface(dev->udev, 0, 0); errCode = usb_set_interface(dev->udev, 0, 0);
if (errCode < 0) { if (errCode < 0) {
em28xx_errdev ("cannot change alternate number to 0 (error=%i)\n", em28xx_errdev("cannot change alternate number to "
errCode); "0 (error=%i)\n", errCode);
}
} }
kfree(fh);
dev->users--; dev->users--;
wake_up_interruptible_nr(&dev->open, 1); wake_up_interruptible_nr(&dev->open, 1);
mutex_unlock(&dev->lock); mutex_unlock(&dev->lock);
...@@ -378,13 +374,19 @@ em28xx_v4l2_read(struct file *filp, char __user * buf, size_t count, ...@@ -378,13 +374,19 @@ em28xx_v4l2_read(struct file *filp, char __user * buf, size_t count,
struct em28xx_frame_t *f, *i; struct em28xx_frame_t *f, *i;
unsigned long lock_flags; unsigned long lock_flags;
int ret = 0; int ret = 0;
struct em28xx *dev = filp->private_data; struct em28xx_fh *fh = filp->private_data;
struct em28xx *dev = fh->dev;
mutex_lock(&dev->lock); mutex_lock(&dev->lock);
if (dev->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) { if (dev->type == V4L2_BUF_TYPE_VIDEO_CAPTURE)
em28xx_videodbg("V4l2_Buf_type_videocapture is set\n"); em28xx_videodbg("V4l2_Buf_type_videocapture is set\n");
if (dev->reader > 0 && fh->reader == 0) {
mutex_unlock(&dev->lock);
return -EBUSY;
} }
if (dev->type == V4L2_BUF_TYPE_VBI_CAPTURE) { if (dev->type == V4L2_BUF_TYPE_VBI_CAPTURE) {
em28xx_videodbg("V4L2_BUF_TYPE_VBI_CAPTURE is set\n"); em28xx_videodbg("V4L2_BUF_TYPE_VBI_CAPTURE is set\n");
em28xx_videodbg("not supported yet! ...\n"); em28xx_videodbg("not supported yet! ...\n");
...@@ -423,6 +425,9 @@ em28xx_v4l2_read(struct file *filp, char __user * buf, size_t count, ...@@ -423,6 +425,9 @@ em28xx_v4l2_read(struct file *filp, char __user * buf, size_t count,
" the device again to choose the read method\n"); " the device again to choose the read method\n");
mutex_unlock(&dev->lock); mutex_unlock(&dev->lock);
return -EINVAL; return -EINVAL;
} else {
dev->reader = 1;
fh->reader = 1;
} }
if (dev->io == IO_NONE) { if (dev->io == IO_NONE) {
...@@ -491,7 +496,8 @@ em28xx_v4l2_read(struct file *filp, char __user * buf, size_t count, ...@@ -491,7 +496,8 @@ em28xx_v4l2_read(struct file *filp, char __user * buf, size_t count,
static unsigned int em28xx_v4l2_poll(struct file *filp, poll_table * wait) static unsigned int em28xx_v4l2_poll(struct file *filp, poll_table * wait)
{ {
unsigned int mask = 0; unsigned int mask = 0;
struct em28xx *dev = filp->private_data; struct em28xx_fh *fh = filp->private_data;
struct em28xx *dev = fh->dev;
mutex_lock(&dev->lock); mutex_lock(&dev->lock);
...@@ -559,15 +565,23 @@ static struct vm_operations_struct em28xx_vm_ops = { ...@@ -559,15 +565,23 @@ static struct vm_operations_struct em28xx_vm_ops = {
*/ */
static int em28xx_v4l2_mmap(struct file *filp, struct vm_area_struct *vma) static int em28xx_v4l2_mmap(struct file *filp, struct vm_area_struct *vma)
{ {
unsigned long size = vma->vm_end - vma->vm_start, struct em28xx_fh *fh = filp->private_data;
start = vma->vm_start; struct em28xx *dev = fh->dev;
void *pos; unsigned long size = vma->vm_end - vma->vm_start;
u32 i; unsigned long start = vma->vm_start;
void *pos;
struct em28xx *dev = filp->private_data; u32 i;
mutex_lock(&dev->lock); mutex_lock(&dev->lock);
if (dev->reader > 0 && fh->reader == 0) {
mutex_unlock(&dev->lock);
return -EBUSY;
} else {
dev->reader = 1;
fh->reader = 1;
}
if (dev->state & DEV_DISCONNECTED) { if (dev->state & DEV_DISCONNECTED) {
em28xx_videodbg("mmap: device not present\n"); em28xx_videodbg("mmap: device not present\n");
mutex_unlock(&dev->lock); mutex_unlock(&dev->lock);
...@@ -918,6 +932,7 @@ static int em28xx_do_ioctl(struct inode *inode, struct file *filp, ...@@ -918,6 +932,7 @@ static int em28xx_do_ioctl(struct inode *inode, struct file *filp,
struct em28xx *dev, unsigned int cmd, void *arg, struct em28xx *dev, unsigned int cmd, void *arg,
v4l2_kioctl driver_ioctl) v4l2_kioctl driver_ioctl)
{ {
struct em28xx_fh *fh = filp->private_data;
int ret; int ret;
switch (cmd) { switch (cmd) {
...@@ -1227,6 +1242,8 @@ static int em28xx_do_ioctl(struct inode *inode, struct file *filp, ...@@ -1227,6 +1242,8 @@ static int em28xx_do_ioctl(struct inode *inode, struct file *filp,
return ret; return ret;
} }
} }
fh->reader = 0;
em28xx_empty_framequeues(dev); em28xx_empty_framequeues(dev);
mutex_unlock(&dev->lock); mutex_unlock(&dev->lock);
...@@ -1248,7 +1265,8 @@ static int em28xx_do_ioctl(struct inode *inode, struct file *filp, ...@@ -1248,7 +1265,8 @@ static int em28xx_do_ioctl(struct inode *inode, struct file *filp,
static int em28xx_video_do_ioctl(struct inode *inode, struct file *filp, static int em28xx_video_do_ioctl(struct inode *inode, struct file *filp,
unsigned int cmd, void *arg) unsigned int cmd, void *arg)
{ {
struct em28xx *dev = filp->private_data; struct em28xx_fh *fh = filp->private_data;
struct em28xx *dev = fh->dev;
if (!dev) if (!dev)
return -ENODEV; return -ENODEV;
...@@ -1456,7 +1474,8 @@ static int em28xx_v4l2_ioctl(struct inode *inode, struct file *filp, ...@@ -1456,7 +1474,8 @@ static int em28xx_v4l2_ioctl(struct inode *inode, struct file *filp,
unsigned int cmd, unsigned long arg) unsigned int cmd, unsigned long arg)
{ {
int ret = 0; int ret = 0;
struct em28xx *dev = filp->private_data; struct em28xx_fh *fh = filp->private_data;
struct em28xx *dev = fh->dev;
if (dev->state & DEV_DISCONNECTED) { if (dev->state & DEV_DISCONNECTED) {
em28xx_errdev("v4l2 ioctl: device not present\n"); em28xx_errdev("v4l2 ioctl: device not present\n");
...@@ -1503,7 +1522,10 @@ static int em28xx_init_dev(struct em28xx **devhandle, struct usb_device *udev, ...@@ -1503,7 +1522,10 @@ static int em28xx_init_dev(struct em28xx **devhandle, struct usb_device *udev,
dev->udev = udev; dev->udev = udev;
mutex_init(&dev->lock); mutex_init(&dev->lock);
spin_lock_init(&dev->queue_lock);
init_waitqueue_head(&dev->open); init_waitqueue_head(&dev->open);
init_waitqueue_head(&dev->wait_frame);
init_waitqueue_head(&dev->wait_stream);
dev->em28xx_write_regs = em28xx_write_regs; dev->em28xx_write_regs = em28xx_write_regs;
dev->em28xx_read_reg = em28xx_read_reg; dev->em28xx_read_reg = em28xx_read_reg;
......
...@@ -258,6 +258,7 @@ struct em28xx { ...@@ -258,6 +258,7 @@ struct em28xx {
int vscale; /* vertical scale factor (see datasheet) */ int vscale; /* vertical scale factor (see datasheet) */
int interlaced; /* 1=interlace fileds, 0=just top fileds */ int interlaced; /* 1=interlace fileds, 0=just top fileds */
int type; int type;
unsigned int reader:1;
unsigned long hash; /* eeprom hash - for boards with generic ID */ unsigned long hash; /* eeprom hash - for boards with generic ID */
unsigned long i2c_hash; /* i2c devicelist hash - for boards with generic ID */ unsigned long i2c_hash; /* i2c devicelist hash - for boards with generic ID */
...@@ -294,6 +295,11 @@ struct em28xx { ...@@ -294,6 +295,11 @@ struct em28xx {
int (*em28xx_read_reg_req) (struct em28xx * dev, u8 req, u16 reg); int (*em28xx_read_reg_req) (struct em28xx * dev, u8 req, u16 reg);
}; };
struct em28xx_fh {
struct em28xx *dev;
unsigned int reader:1;
};
/* Provided by em28xx-i2c.c */ /* Provided by em28xx-i2c.c */
void em28xx_i2c_call_clients(struct em28xx *dev, unsigned int cmd, void *arg); void em28xx_i2c_call_clients(struct em28xx *dev, unsigned int cmd, void *arg);
......
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