V4L/DVB (6582): Fix em28xx to allow multiple open
authorMauro Carvalho Chehab <mchehab@infradead.org>
Sun, 11 Nov 2007 01:21:01 +0000 (22:21 -0300)
committerMauro Carvalho Chehab <mchehab@infradead.org>
Fri, 25 Jan 2008 21:02:06 +0000 (19:02 -0200)
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: Mauro Carvalho Chehab <mchehab@infradead.org>
drivers/media/video/em28xx/em28xx-video.c
drivers/media/video/em28xx/em28xx.h

index b43edc3..5b17ca9 100644 (file)
@@ -234,6 +234,7 @@ static int em28xx_v4l2_open(struct inode *inode, struct file *filp)
        int minor = iminor(inode);
        int errCode = 0;
        struct em28xx *h,*dev = NULL;
+       struct em28xx_fh *fh;
 
        list_for_each_entry(h, &em28xx_devlist, devlist) {
                if (h->vdev->minor == minor) {
@@ -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",
                                minor,v4l2_type_names[dev->type],dev->users);
 
-       mutex_lock(&dev->lock);
+       fh = kzalloc(sizeof(struct em28xx_fh), GFP_KERNEL);
 
-       if (dev->users) {
-               em28xx_warn("this driver can be opened only once\n");
-               mutex_unlock(&dev->lock);
-               return -EBUSY;
+       if (!fh) {
+               em28xx_errdev("em28xx-video.c: Out of memory?!\n");
+               return -ENOMEM;
        }
+       mutex_lock(&dev->lock);
+       fh->dev = dev;
+       filp->private_data = fh;
 
-       spin_lock_init(&dev->queue_lock);
-       init_waitqueue_head(&dev->wait_frame);
-       init_waitqueue_head(&dev->wait_stream);
-
-       if (dev->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) {
+       if (dev->type == V4L2_BUF_TYPE_VIDEO_CAPTURE && dev->users == 0) {
                em28xx_set_alternate(dev);
 
                dev->width = norm_maxw(dev);
@@ -277,26 +276,16 @@ static int em28xx_v4l2_open(struct inode *inode, struct file *filp)
                em28xx_capture_start(dev, 1);
                em28xx_resolution_set(dev);
 
-               /* device needs to be initialized before isoc transfer */
-               video_mux(dev, 0);
 
                /* start the transfer */
                errCode = em28xx_init_isoc(dev);
                if (errCode)
                        goto err;
 
+               em28xx_empty_framequeues(dev);
        }
 
        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:
        mutex_unlock(&dev->lock);
@@ -333,34 +322,41 @@ static void em28xx_release_resources(struct em28xx *dev)
  */
 static int em28xx_v4l2_close(struct inode *inode, struct file *filp)
 {
-       int errCode;
-       struct em28xx *dev=filp->private_data;
+       struct em28xx_fh *fh  = filp->private_data;
+       struct em28xx    *dev = fh->dev;
+       int              errCode;
 
        em28xx_videodbg("users=%d\n", dev->users);
 
        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 */
-       if (dev->state & DEV_DISCONNECTED) {
-               em28xx_release_resources(dev);
-               mutex_unlock(&dev->lock);
-               kfree(dev);
-               return 0;
-       }
+               /* the device is already disconnect,
+                  free the remaining resources */
+               if (dev->state & DEV_DISCONNECTED) {
+                       em28xx_release_resources(dev);
+                       mutex_unlock(&dev->lock);
+                       kfree(dev);
+                       return 0;
+               }
 
-       /* set alternate 0 */
-       dev->alt = 0;
-       em28xx_videodbg("setting alternate 0\n");
-       errCode = usb_set_interface(dev->udev, 0, 0);
-       if (errCode < 0) {
-               em28xx_errdev ("cannot change alternate number to 0 (error=%i)\n",
-                    errCode);
+               /* set alternate 0 */
+               dev->alt = 0;
+               em28xx_videodbg("setting alternate 0\n");
+               errCode = usb_set_interface(dev->udev, 0, 0);
+               if (errCode < 0) {
+                       em28xx_errdev("cannot change alternate number to "
+                                       "0 (error=%i)\n", errCode);
+               }
        }
-
+       kfree(fh);
        dev->users--;
        wake_up_interruptible_nr(&dev->open, 1);
        mutex_unlock(&dev->lock);
@@ -378,13 +374,19 @@ em28xx_v4l2_read(struct file *filp, char __user * buf, size_t count,
        struct em28xx_frame_t *f, *i;
        unsigned long lock_flags;
        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);
 
-       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");
+
+       if (dev->reader > 0 && fh->reader == 0) {
+               mutex_unlock(&dev->lock);
+               return -EBUSY;
        }
+
        if (dev->type == V4L2_BUF_TYPE_VBI_CAPTURE) {
                em28xx_videodbg("V4L2_BUF_TYPE_VBI_CAPTURE is set\n");
                em28xx_videodbg("not supported yet! ...\n");
@@ -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");
                mutex_unlock(&dev->lock);
                return -EINVAL;
+       } else {
+               dev->reader = 1;
+               fh->reader = 1;
        }
 
        if (dev->io == IO_NONE) {
@@ -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)
 {
        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);
 
@@ -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)
 {
-       unsigned long size = vma->vm_end - vma->vm_start,
-           start = vma->vm_start;
-       void *pos;
-       u32 i;
-
-       struct em28xx *dev = filp->private_data;
+       struct em28xx_fh *fh    = filp->private_data;
+       struct em28xx    *dev   = fh->dev;
+       unsigned long    size   = vma->vm_end - vma->vm_start;
+       unsigned long    start  = vma->vm_start;
+       void             *pos;
+       u32              i;
 
        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) {
                em28xx_videodbg("mmap: device not present\n");
                mutex_unlock(&dev->lock);
@@ -918,6 +932,7 @@ static int em28xx_do_ioctl(struct inode *inode, struct file *filp,
                           struct em28xx *dev, unsigned int cmd, void *arg,
                           v4l2_kioctl driver_ioctl)
 {
+       struct em28xx_fh *fh = filp->private_data;
        int ret;
 
        switch (cmd) {
@@ -1227,6 +1242,8 @@ static int em28xx_do_ioctl(struct inode *inode, struct file *filp,
                                return ret;
                        }
                }
+
+               fh->reader = 0;
                em28xx_empty_framequeues(dev);
                mutex_unlock(&dev->lock);
 
@@ -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,
                                 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)
                return -ENODEV;
@@ -1456,7 +1474,8 @@ static int em28xx_v4l2_ioctl(struct inode *inode, struct file *filp,
                             unsigned int cmd, unsigned long arg)
 {
        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) {
                em28xx_errdev("v4l2 ioctl: device not present\n");
@@ -1503,7 +1522,10 @@ static int em28xx_init_dev(struct em28xx **devhandle, struct usb_device *udev,
 
        dev->udev = udev;
        mutex_init(&dev->lock);
+       spin_lock_init(&dev->queue_lock);
        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_read_reg = em28xx_read_reg;
index c2531da..f8ad0f4 100644 (file)
@@ -258,6 +258,7 @@ struct em28xx {
        int vscale;             /* vertical scale factor (see datasheet) */
        int interlaced;         /* 1=interlace fileds, 0=just top fileds */
        int type;
+       unsigned int reader:1;
 
        unsigned long hash;     /* eeprom hash - for boards with generic ID */
        unsigned long i2c_hash; /* i2c devicelist hash - for boards with generic ID */
@@ -294,6 +295,11 @@ struct em28xx {
        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 */
 
 void em28xx_i2c_call_clients(struct em28xx *dev, unsigned int cmd, void *arg);