HID: roccat: propagate special events of roccat hardware to userspace

Module roccat is a char device used to report special events of roccat hardware
to userland. These events include requests for on-screen-display of profile or
dpi settings or requests for execution of macro sequences that are not stored
in device. The information in these events depends on hid device implementation
and contains data that is not available in a single hid event or else hidraw
could have been used.

It is inspired by hidraw, but uses only one circular buffer for all readers.
The device is as generic as possible so that the functionality is usable by all
(kone and upcomming) roccat device drivers.

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-kone.c b/drivers/hid/hid-roccat-kone.c
index b6371d3..17f2dc0 100644
--- a/drivers/hid/hid-roccat-kone.c
+++ b/drivers/hid/hid-roccat-kone.c
@@ -37,6 +37,7 @@
 #include <linux/module.h>
 #include <linux/slab.h>
 #include "hid-ids.h"
+#include "hid-roccat.h"
 #include "hid-roccat-kone.h"
 
 static void kone_set_settings_checksum(struct kone_settings *settings)
@@ -849,6 +850,16 @@
 					"couldn't init struct kone_device\n");
 			goto exit_free;
 		}
+
+		retval = roccat_connect(hdev);
+		if (retval < 0) {
+			dev_err(&hdev->dev, "couldn't init char dev\n");
+			/* be tolerant about not getting chrdev */
+		} else {
+			kone->roccat_claimed = 1;
+			kone->chrdev_minor = retval;
+		}
+
 		retval = kone_create_sysfs_attributes(intf);
 		if (retval) {
 			dev_err(&hdev->dev, "cannot create sysfs files\n");
@@ -868,10 +879,14 @@
 static void kone_remove_specials(struct hid_device *hdev)
 {
 	struct usb_interface *intf = to_usb_interface(hdev->dev.parent);
+	struct kone_device *kone;
 
 	if (intf->cur_altsetting->desc.bInterfaceProtocol
 			== USB_INTERFACE_PROTOCOL_MOUSE) {
 		kone_remove_sysfs_attributes(intf);
+		kone = hid_get_drvdata(hdev);
+		if (kone->roccat_claimed)
+			roccat_disconnect(kone->chrdev_minor);
 		kfree(hid_get_drvdata(hdev));
 	}
 }
@@ -930,6 +945,37 @@
 	}
 }
 
+static void kone_report_to_chrdev(struct kone_device const *kone,
+		struct kone_mouse_event const *event)
+{
+	struct kone_roccat_report roccat_report;
+
+	switch (event->event) {
+	case kone_mouse_event_switch_profile:
+	case kone_mouse_event_switch_dpi:
+	case kone_mouse_event_osd_profile:
+	case kone_mouse_event_osd_dpi:
+		roccat_report.event = event->event;
+		roccat_report.value = event->value;
+		roccat_report.key = 0;
+		roccat_report_event(kone->chrdev_minor,
+				(uint8_t *)&roccat_report,
+				sizeof(struct kone_roccat_report));
+		break;
+	case kone_mouse_event_call_overlong_macro:
+		if (event->value == kone_keystroke_action_press) {
+			roccat_report.event = kone_mouse_event_call_overlong_macro;
+			roccat_report.value = kone->actual_profile;
+			roccat_report.key = event->macro_key;
+			roccat_report_event(kone->chrdev_minor,
+					(uint8_t *)&roccat_report,
+					sizeof(struct kone_roccat_report));
+		}
+		break;
+	}
+
+}
+
 /*
  * Is called for keyboard- and mousepart.
  * Only mousepart gets informations about special events in its extended event
@@ -958,6 +1004,9 @@
 
 	kone_keep_values_up_to_date(kone, event);
 
+	if (kone->roccat_claimed)
+		kone_report_to_chrdev(kone, event);
+
 	return 0; /* always do further processing */
 }