V4L/DVB (6583): Fix em28xx read stream locking

On some situations, closing an streaming application and re-opening were
returning -EBUSY.

Uses the same locking schema also present on cx88.

Signed-off-by: Mauro Carvalho Chehab <mchehab@infradead.org>
diff --git a/drivers/media/video/em28xx/em28xx-video.c b/drivers/media/video/em28xx/em28xx-video.c
index 5b17ca9..f019177 100644
--- a/drivers/media/video/em28xx/em28xx-video.c
+++ b/drivers/media/video/em28xx/em28xx-video.c
@@ -225,6 +225,44 @@
 	}
 }
 
+/* Usage lock check functions */
+static int res_get(struct em28xx_fh *fh)
+{
+	struct em28xx    *dev = fh->dev;
+	int		 rc   = 0;
+
+	/* This instance already has stream_on */
+	if (fh->stream_on)
+		return rc;
+
+	mutex_lock(&dev->lock);
+
+	if (dev->stream_on)
+		rc = -EINVAL;
+	else {
+		dev->stream_on = 1;
+		fh->stream_on  = 1;
+	}
+
+	mutex_unlock(&dev->lock);
+	return rc;
+}
+
+static int res_check(struct em28xx_fh *fh)
+{
+	return (fh->stream_on);
+}
+
+static void res_free(struct em28xx_fh *fh)
+{
+	struct em28xx    *dev = fh->dev;
+
+	mutex_lock(&dev->lock);
+	fh->stream_on = 0;
+	dev->stream_on = 0;
+	mutex_unlock(&dev->lock);
+}
+
 /*
  * em28xx_v4l2_open()
  * inits the device and starts isoc transfer
@@ -328,15 +366,16 @@
 
 	em28xx_videodbg("users=%d\n", dev->users);
 
+
+	if (res_check(fh))
+		res_free(fh);
+
 	mutex_lock(&dev->lock);
-	if (fh->reader == 1)
-	       fh->reader = 0;
 
 	if (dev->users == 1) {
-		dev->reader = 0;
-
 		em28xx_uninit_isoc(dev);
 		em28xx_release_buffers(dev);
+		dev->io = IO_NONE;
 
 		/* the device is already disconnect,
 		   free the remaining resources */
@@ -377,16 +416,15 @@
 	struct em28xx_fh *fh = filp->private_data;
 	struct em28xx *dev = fh->dev;
 
+
+	if (unlikely(res_get(fh) < 0))
+		return -EBUSY;
+
 	mutex_lock(&dev->lock);
 
 	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");
@@ -425,9 +463,6 @@
 				" 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) {
@@ -499,6 +534,9 @@
 	struct em28xx_fh *fh = filp->private_data;
 	struct em28xx *dev = fh->dev;
 
+	if (unlikely(res_get(fh) < 0))
+		return POLLERR;
+
 	mutex_lock(&dev->lock);
 
 	if (dev->state & DEV_DISCONNECTED) {
@@ -572,15 +610,10 @@
 	void 		 *pos;
 	u32		 i;
 
-	mutex_lock(&dev->lock);
-
-	if (dev->reader > 0 && fh->reader == 0) {
-		mutex_unlock(&dev->lock);
+	if (unlikely(res_get(fh) < 0))
 		return -EBUSY;
-	} else {
-		dev->reader = 1;
-		fh->reader = 1;
-	}
+
+	mutex_lock(&dev->lock);
 
 	if (dev->state & DEV_DISCONNECTED) {
 		em28xx_videodbg("mmap: device not present\n");
@@ -1219,6 +1252,9 @@
 		if (list_empty(&dev->inqueue))
 			return -EINVAL;
 
+		if (unlikely(res_get(fh) < 0))
+			return -EBUSY;
+
 		dev->stream = STREAM_ON;	/* FIXME: Start video capture here? */
 
 		em28xx_videodbg("VIDIOC_STREAMON: starting stream\n");
@@ -1243,7 +1279,6 @@
 			}
 		}
 
-		fh->reader = 0;
 		em28xx_empty_framequeues(dev);
 		mutex_unlock(&dev->lock);
 
diff --git a/drivers/media/video/em28xx/em28xx.h b/drivers/media/video/em28xx/em28xx.h
index f8ad0f4a..3efc05d 100644
--- a/drivers/media/video/em28xx/em28xx.h
+++ b/drivers/media/video/em28xx/em28xx.h
@@ -224,6 +224,8 @@
 	unsigned int has_msp34xx:1;
 	unsigned int has_tda9887:1;
 
+	unsigned int stream_on:1;	/* Locks streams */
+
 	u32 i2s_speed;		/* I2S speed for audio digital stream */
 
 	enum em28xx_decoder decoder;
@@ -258,7 +260,6 @@
 	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 */
@@ -297,7 +298,7 @@
 
 struct em28xx_fh {
 	struct em28xx *dev;
-	unsigned int  reader:1;
+	unsigned int  stream_on:1;	/* Locks streams */
 };
 
 /* Provided by em28xx-i2c.c */