Staging: poch: Fetch Flush IOCTL interface

Change user space interface to an IOCTL based interface instead of a
memory mapped circular buffer. The circular buffer had some serious
cache(?) issues and never worked.

Signed-off-by: Vijay Kumar B. <vijaykumar@bravegnu.org>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>

diff --git a/drivers/staging/poch/poch.c b/drivers/staging/poch/poch.c
index 1308c7b..6b4aa5f1 100644
--- a/drivers/staging/poch/poch.c
+++ b/drivers/staging/poch/poch.c
@@ -201,6 +201,8 @@
 	struct page *header_pg;
 	unsigned long header_size;
 
+	/* Last group consumed by user space. */
+	unsigned int consumed;
 	/* Last group indicated as 'complete' to user space. */
 	unsigned int transfer;
 
@@ -589,6 +591,7 @@
 	if (ret != 0)
 		goto out;
 
+	channel->consumed = 0;
 	channel->transfer = 0;
 
 	/* Allocate memory to hold group information. */
@@ -1033,6 +1036,51 @@
 			break;
 		}
 		break;
+	case POCH_IOC_CONSUME:
+	{
+		int available;
+		int nfetch;
+		unsigned int from;
+		unsigned int count;
+		unsigned int i, j;
+		struct poch_consume consume;
+		struct poch_consume *uconsume;
+
+		uconsume = argp;
+		ret = copy_from_user(&consume, uconsume, sizeof(consume));
+		if (ret)
+			return ret;
+
+		spin_lock_irq(&channel->group_offsets_lock);
+
+		channel->consumed += consume.nflush;
+		channel->consumed %= channel->group_count;
+
+		available = channel->transfer - channel->consumed;
+		if (available < 0)
+			available += channel->group_count;
+
+		from = channel->consumed;
+
+		spin_unlock_irq(&channel->group_offsets_lock);
+
+		nfetch = consume.nfetch;
+		count = min(available, nfetch);
+
+		for (i = 0; i < count; i++) {
+			j = (from + i) % channel->group_count;
+			ret = put_user(channel->groups[j].user_offset,
+				       &consume.offsets[i]);
+			if (ret)
+				return -EFAULT;
+		}
+
+		ret = put_user(count, &uconsume->nfetch);
+		if (ret)
+			return -EFAULT;
+
+		break;
+	}
 	case POCH_IOC_GET_COUNTERS:
 		if (!access_ok(VERIFY_WRITE, argp, sizeof(struct poch_counters)))
 			return -EFAULT;
@@ -1108,12 +1156,18 @@
 	for (i = 0; i < groups_done; i++) {
 		j = (prev_transfer + i) % channel->group_count;
 		group_offsets[j] = groups[j].user_offset;
+
+		channel->transfer += 1;
+		channel->transfer %= channel->group_count;
+
+		if (channel->transfer == channel->consumed) {
+			channel->consumed += 1;
+			channel->consumed %= channel->group_count;
+		}
 	}
 
 	spin_unlock(&channel->group_offsets_lock);
 
-	channel->transfer = curr_transfer;
-
 	wake_up_interruptible(&channel->wq);
 }
 
diff --git a/drivers/staging/poch/poch.h b/drivers/staging/poch/poch.h
index 51a2d14..ba1490b 100644
--- a/drivers/staging/poch/poch.h
+++ b/drivers/staging/poch/poch.h
@@ -19,6 +19,12 @@
 	__u32 pll_unlock;
 };
 
+struct poch_consume {
+	__u32 __user *offsets;
+	__u32 nfetch;
+	__u32 nflush;
+};
+
 #define POCH_IOC_NUM			'9'
 
 #define POCH_IOC_TRANSFER_START		_IO(POCH_IOC_NUM, 0)
@@ -27,3 +33,6 @@
 					     struct poch_counters)
 #define POCH_IOC_SYNC_GROUP_FOR_USER	_IO(POCH_IOC_NUM, 3)
 #define POCH_IOC_SYNC_GROUP_FOR_DEVICE	_IO(POCH_IOC_NUM, 4)
+
+#define POCH_IOC_CONSUME		_IOWR(POCH_IOC_NUM, 5, \
+					      struct poch_consume)