HID: roccat: Add ioctl command to retreive report size from chardev

Roccat chardev was reworked to support only a defined report size per
device and this can be retreived by an ioctl now to enable future changes
in report definitions.
Header was moved/renamed from drivers/hid to include/linux for accessibility.

Signed-off-by: Stefan Achatz <erazor_de@users.sourceforge.net>
Signed-off-by: Jiri Kosina <jkosina@suse.cz>
diff --git a/drivers/hid/hid-roccat.c b/drivers/hid/hid-roccat.c
index a14c579..0fa23de 100644
--- a/drivers/hid/hid-roccat.c
+++ b/drivers/hid/hid-roccat.c
@@ -26,8 +26,7 @@
 #include <linux/cdev.h>
 #include <linux/poll.h>
 #include <linux/sched.h>
-
-#include "hid-roccat.h"
+#include <linux/roccat.h>
 
 #define ROCCAT_FIRST_MINOR 0
 #define ROCCAT_MAX_DEVICES 8
@@ -37,11 +36,11 @@
 
 struct roccat_report {
 	uint8_t *value;
-	int len;
 };
 
 struct roccat_device {
 	unsigned int minor;
+	int report_size;
 	int open;
 	int exist;
 	wait_queue_head_t wait;
@@ -123,7 +122,7 @@
 	 * If report is larger than requested amount of data, rest of report
 	 * is lost!
 	 */
-	len = report->len > count ? count : report->len;
+	len = device->report_size > count ? count : device->report_size;
 
 	if (copy_to_user(buffer, report->value, len)) {
 		retval = -EFAULT;
@@ -248,26 +247,25 @@
  *
  * This is called from interrupt handler.
  */
-int roccat_report_event(int minor, u8 const *data, int len)
+int roccat_report_event(int minor, u8 const *data)
 {
 	struct roccat_device *device;
 	struct roccat_reader *reader;
 	struct roccat_report *report;
 	uint8_t *new_value;
 
-	new_value = kmemdup(data, len, GFP_ATOMIC);
+	device = devices[minor];
+
+	new_value = kmemdup(data, device->report_size, GFP_ATOMIC);
 	if (!new_value)
 		return -ENOMEM;
 
-	device = devices[minor];
-
 	report = &device->cbuf[device->cbuf_end];
 
 	/* passing NULL is safe */
 	kfree(report->value);
 
 	report->value = new_value;
-	report->len = len;
 	device->cbuf_end = (device->cbuf_end + 1) % ROCCAT_CBUF_SIZE;
 
 	list_for_each_entry(reader, &device->readers, node) {
@@ -295,7 +293,7 @@
  * Return value is minor device number in Range [0, ROCCAT_MAX_DEVICES] on
  * success, a negative error code on failure.
  */
-int roccat_connect(struct class *klass, struct hid_device *hid)
+int roccat_connect(struct class *klass, struct hid_device *hid, int report_size)
 {
 	unsigned int minor;
 	struct roccat_device *device;
@@ -343,6 +341,7 @@
 	device->hid = hid;
 	device->exist = 1;
 	device->cbuf_end = 0;
+	device->report_size = report_size;
 
 	return minor;
 }
@@ -373,6 +372,34 @@
 }
 EXPORT_SYMBOL_GPL(roccat_disconnect);
 
+static long roccat_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
+{
+	struct inode *inode = file->f_path.dentry->d_inode;
+	struct roccat_device *device;
+	unsigned int minor = iminor(inode);
+	long retval = 0;
+
+	mutex_lock(&devices_lock);
+
+	device = devices[minor];
+	if (!device) {
+		retval = -ENODEV;
+		goto out;
+	}
+
+	switch (cmd) {
+	case ROCCATIOCGREPSIZE:
+		if (put_user(device->report_size, (int __user *)arg))
+			retval = -EFAULT;
+		break;
+	default:
+		retval = -ENOTTY;
+	}
+out:
+	mutex_unlock(&devices_lock);
+	return retval;
+}
+
 static const struct file_operations roccat_ops = {
 	.owner = THIS_MODULE,
 	.read = roccat_read,
@@ -380,6 +407,7 @@
 	.open = roccat_open,
 	.release = roccat_release,
 	.llseek = noop_llseek,
+	.unlocked_ioctl = roccat_ioctl,
 };
 
 static int __init roccat_init(void)