cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

hid-roccat-kovaplus.c (18666B)


      1// SPDX-License-Identifier: GPL-2.0-or-later
      2/*
      3 * Roccat Kova[+] driver for Linux
      4 *
      5 * Copyright (c) 2011 Stefan Achatz <erazor_de@users.sourceforge.net>
      6 */
      7
      8/*
      9 */
     10
     11/*
     12 * Roccat Kova[+] is a bigger version of the Pyra with two more side buttons.
     13 */
     14
     15#include <linux/device.h>
     16#include <linux/input.h>
     17#include <linux/hid.h>
     18#include <linux/module.h>
     19#include <linux/slab.h>
     20#include <linux/hid-roccat.h>
     21#include "hid-ids.h"
     22#include "hid-roccat-common.h"
     23#include "hid-roccat-kovaplus.h"
     24
     25static uint profile_numbers[5] = {0, 1, 2, 3, 4};
     26
     27static struct class *kovaplus_class;
     28
     29static uint kovaplus_convert_event_cpi(uint value)
     30{
     31	return (value == 7 ? 4 : (value == 4 ? 3 : value));
     32}
     33
     34static void kovaplus_profile_activated(struct kovaplus_device *kovaplus,
     35		uint new_profile_index)
     36{
     37	if (new_profile_index >= ARRAY_SIZE(kovaplus->profile_settings))
     38		return;
     39	kovaplus->actual_profile = new_profile_index;
     40	kovaplus->actual_cpi = kovaplus->profile_settings[new_profile_index].cpi_startup_level;
     41	kovaplus->actual_x_sensitivity = kovaplus->profile_settings[new_profile_index].sensitivity_x;
     42	kovaplus->actual_y_sensitivity = kovaplus->profile_settings[new_profile_index].sensitivity_y;
     43}
     44
     45static int kovaplus_send_control(struct usb_device *usb_dev, uint value,
     46		enum kovaplus_control_requests request)
     47{
     48	int retval;
     49	struct roccat_common2_control control;
     50
     51	if ((request == KOVAPLUS_CONTROL_REQUEST_PROFILE_SETTINGS ||
     52			request == KOVAPLUS_CONTROL_REQUEST_PROFILE_BUTTONS) &&
     53			value > 4)
     54		return -EINVAL;
     55
     56	control.command = ROCCAT_COMMON_COMMAND_CONTROL;
     57	control.value = value;
     58	control.request = request;
     59
     60	retval = roccat_common2_send(usb_dev, ROCCAT_COMMON_COMMAND_CONTROL,
     61			&control, sizeof(struct roccat_common2_control));
     62
     63	return retval;
     64}
     65
     66static int kovaplus_select_profile(struct usb_device *usb_dev, uint number,
     67		enum kovaplus_control_requests request)
     68{
     69	return kovaplus_send_control(usb_dev, number, request);
     70}
     71
     72static int kovaplus_get_profile_settings(struct usb_device *usb_dev,
     73		struct kovaplus_profile_settings *buf, uint number)
     74{
     75	int retval;
     76
     77	retval = kovaplus_select_profile(usb_dev, number,
     78			KOVAPLUS_CONTROL_REQUEST_PROFILE_SETTINGS);
     79	if (retval)
     80		return retval;
     81
     82	return roccat_common2_receive(usb_dev, KOVAPLUS_COMMAND_PROFILE_SETTINGS,
     83			buf, KOVAPLUS_SIZE_PROFILE_SETTINGS);
     84}
     85
     86static int kovaplus_get_profile_buttons(struct usb_device *usb_dev,
     87		struct kovaplus_profile_buttons *buf, int number)
     88{
     89	int retval;
     90
     91	retval = kovaplus_select_profile(usb_dev, number,
     92			KOVAPLUS_CONTROL_REQUEST_PROFILE_BUTTONS);
     93	if (retval)
     94		return retval;
     95
     96	return roccat_common2_receive(usb_dev, KOVAPLUS_COMMAND_PROFILE_BUTTONS,
     97			buf, KOVAPLUS_SIZE_PROFILE_BUTTONS);
     98}
     99
    100/* retval is 0-4 on success, < 0 on error */
    101static int kovaplus_get_actual_profile(struct usb_device *usb_dev)
    102{
    103	struct kovaplus_actual_profile buf;
    104	int retval;
    105
    106	retval = roccat_common2_receive(usb_dev, KOVAPLUS_COMMAND_ACTUAL_PROFILE,
    107			&buf, sizeof(struct kovaplus_actual_profile));
    108
    109	return retval ? retval : buf.actual_profile;
    110}
    111
    112static int kovaplus_set_actual_profile(struct usb_device *usb_dev,
    113		int new_profile)
    114{
    115	struct kovaplus_actual_profile buf;
    116
    117	buf.command = KOVAPLUS_COMMAND_ACTUAL_PROFILE;
    118	buf.size = sizeof(struct kovaplus_actual_profile);
    119	buf.actual_profile = new_profile;
    120
    121	return roccat_common2_send_with_status(usb_dev,
    122			KOVAPLUS_COMMAND_ACTUAL_PROFILE,
    123			&buf, sizeof(struct kovaplus_actual_profile));
    124}
    125
    126static ssize_t kovaplus_sysfs_read(struct file *fp, struct kobject *kobj,
    127		char *buf, loff_t off, size_t count,
    128		size_t real_size, uint command)
    129{
    130	struct device *dev = kobj_to_dev(kobj)->parent->parent;
    131	struct kovaplus_device *kovaplus = hid_get_drvdata(dev_get_drvdata(dev));
    132	struct usb_device *usb_dev = interface_to_usbdev(to_usb_interface(dev));
    133	int retval;
    134
    135	if (off >= real_size)
    136		return 0;
    137
    138	if (off != 0 || count != real_size)
    139		return -EINVAL;
    140
    141	mutex_lock(&kovaplus->kovaplus_lock);
    142	retval = roccat_common2_receive(usb_dev, command, buf, real_size);
    143	mutex_unlock(&kovaplus->kovaplus_lock);
    144
    145	if (retval)
    146		return retval;
    147
    148	return real_size;
    149}
    150
    151static ssize_t kovaplus_sysfs_write(struct file *fp, struct kobject *kobj,
    152		void const *buf, loff_t off, size_t count,
    153		size_t real_size, uint command)
    154{
    155	struct device *dev = kobj_to_dev(kobj)->parent->parent;
    156	struct kovaplus_device *kovaplus = hid_get_drvdata(dev_get_drvdata(dev));
    157	struct usb_device *usb_dev = interface_to_usbdev(to_usb_interface(dev));
    158	int retval;
    159
    160	if (off != 0 || count != real_size)
    161		return -EINVAL;
    162
    163	mutex_lock(&kovaplus->kovaplus_lock);
    164	retval = roccat_common2_send_with_status(usb_dev, command,
    165			buf, real_size);
    166	mutex_unlock(&kovaplus->kovaplus_lock);
    167
    168	if (retval)
    169		return retval;
    170
    171	return real_size;
    172}
    173
    174#define KOVAPLUS_SYSFS_W(thingy, THINGY) \
    175static ssize_t kovaplus_sysfs_write_ ## thingy(struct file *fp, \
    176		struct kobject *kobj, struct bin_attribute *attr, char *buf, \
    177		loff_t off, size_t count) \
    178{ \
    179	return kovaplus_sysfs_write(fp, kobj, buf, off, count, \
    180			KOVAPLUS_SIZE_ ## THINGY, KOVAPLUS_COMMAND_ ## THINGY); \
    181}
    182
    183#define KOVAPLUS_SYSFS_R(thingy, THINGY) \
    184static ssize_t kovaplus_sysfs_read_ ## thingy(struct file *fp, \
    185		struct kobject *kobj, struct bin_attribute *attr, char *buf, \
    186		loff_t off, size_t count) \
    187{ \
    188	return kovaplus_sysfs_read(fp, kobj, buf, off, count, \
    189			KOVAPLUS_SIZE_ ## THINGY, KOVAPLUS_COMMAND_ ## THINGY); \
    190}
    191
    192#define KOVAPLUS_SYSFS_RW(thingy, THINGY) \
    193KOVAPLUS_SYSFS_W(thingy, THINGY) \
    194KOVAPLUS_SYSFS_R(thingy, THINGY)
    195
    196#define KOVAPLUS_BIN_ATTRIBUTE_RW(thingy, THINGY) \
    197KOVAPLUS_SYSFS_RW(thingy, THINGY); \
    198static struct bin_attribute bin_attr_##thingy = { \
    199	.attr = { .name = #thingy, .mode = 0660 }, \
    200	.size = KOVAPLUS_SIZE_ ## THINGY, \
    201	.read = kovaplus_sysfs_read_ ## thingy, \
    202	.write = kovaplus_sysfs_write_ ## thingy \
    203}
    204
    205#define KOVAPLUS_BIN_ATTRIBUTE_W(thingy, THINGY) \
    206KOVAPLUS_SYSFS_W(thingy, THINGY); \
    207static struct bin_attribute bin_attr_##thingy = { \
    208	.attr = { .name = #thingy, .mode = 0220 }, \
    209	.size = KOVAPLUS_SIZE_ ## THINGY, \
    210	.write = kovaplus_sysfs_write_ ## thingy \
    211}
    212KOVAPLUS_BIN_ATTRIBUTE_W(control, CONTROL);
    213KOVAPLUS_BIN_ATTRIBUTE_RW(info, INFO);
    214KOVAPLUS_BIN_ATTRIBUTE_RW(profile_settings, PROFILE_SETTINGS);
    215KOVAPLUS_BIN_ATTRIBUTE_RW(profile_buttons, PROFILE_BUTTONS);
    216
    217static ssize_t kovaplus_sysfs_read_profilex_settings(struct file *fp,
    218		struct kobject *kobj, struct bin_attribute *attr, char *buf,
    219		loff_t off, size_t count)
    220{
    221	struct device *dev = kobj_to_dev(kobj)->parent->parent;
    222	struct usb_device *usb_dev = interface_to_usbdev(to_usb_interface(dev));
    223	ssize_t retval;
    224
    225	retval = kovaplus_select_profile(usb_dev, *(uint *)(attr->private),
    226			KOVAPLUS_CONTROL_REQUEST_PROFILE_SETTINGS);
    227	if (retval)
    228		return retval;
    229
    230	return kovaplus_sysfs_read(fp, kobj, buf, off, count,
    231			KOVAPLUS_SIZE_PROFILE_SETTINGS,
    232			KOVAPLUS_COMMAND_PROFILE_SETTINGS);
    233}
    234
    235static ssize_t kovaplus_sysfs_read_profilex_buttons(struct file *fp,
    236		struct kobject *kobj, struct bin_attribute *attr, char *buf,
    237		loff_t off, size_t count)
    238{
    239	struct device *dev = kobj_to_dev(kobj)->parent->parent;
    240	struct usb_device *usb_dev = interface_to_usbdev(to_usb_interface(dev));
    241	ssize_t retval;
    242
    243	retval = kovaplus_select_profile(usb_dev, *(uint *)(attr->private),
    244			KOVAPLUS_CONTROL_REQUEST_PROFILE_BUTTONS);
    245	if (retval)
    246		return retval;
    247
    248	return kovaplus_sysfs_read(fp, kobj, buf, off, count,
    249			KOVAPLUS_SIZE_PROFILE_BUTTONS,
    250			KOVAPLUS_COMMAND_PROFILE_BUTTONS);
    251}
    252
    253#define PROFILE_ATTR(number)						\
    254static struct bin_attribute bin_attr_profile##number##_settings = {	\
    255	.attr = { .name = "profile" #number "_settings", .mode = 0440 },	\
    256	.size = KOVAPLUS_SIZE_PROFILE_SETTINGS,				\
    257	.read = kovaplus_sysfs_read_profilex_settings,			\
    258	.private = &profile_numbers[number-1],				\
    259};									\
    260static struct bin_attribute bin_attr_profile##number##_buttons = {	\
    261	.attr = { .name = "profile" #number "_buttons", .mode = 0440 },	\
    262	.size = KOVAPLUS_SIZE_PROFILE_BUTTONS,				\
    263	.read = kovaplus_sysfs_read_profilex_buttons,			\
    264	.private = &profile_numbers[number-1],				\
    265};
    266PROFILE_ATTR(1);
    267PROFILE_ATTR(2);
    268PROFILE_ATTR(3);
    269PROFILE_ATTR(4);
    270PROFILE_ATTR(5);
    271
    272static ssize_t kovaplus_sysfs_show_actual_profile(struct device *dev,
    273		struct device_attribute *attr, char *buf)
    274{
    275	struct kovaplus_device *kovaplus =
    276			hid_get_drvdata(dev_get_drvdata(dev->parent->parent));
    277	return snprintf(buf, PAGE_SIZE, "%d\n", kovaplus->actual_profile);
    278}
    279
    280static ssize_t kovaplus_sysfs_set_actual_profile(struct device *dev,
    281		struct device_attribute *attr, char const *buf, size_t size)
    282{
    283	struct kovaplus_device *kovaplus;
    284	struct usb_device *usb_dev;
    285	unsigned long profile;
    286	int retval;
    287	struct kovaplus_roccat_report roccat_report;
    288
    289	dev = dev->parent->parent;
    290	kovaplus = hid_get_drvdata(dev_get_drvdata(dev));
    291	usb_dev = interface_to_usbdev(to_usb_interface(dev));
    292
    293	retval = kstrtoul(buf, 10, &profile);
    294	if (retval)
    295		return retval;
    296
    297	if (profile >= 5)
    298		return -EINVAL;
    299
    300	mutex_lock(&kovaplus->kovaplus_lock);
    301	retval = kovaplus_set_actual_profile(usb_dev, profile);
    302	if (retval) {
    303		mutex_unlock(&kovaplus->kovaplus_lock);
    304		return retval;
    305	}
    306
    307	kovaplus_profile_activated(kovaplus, profile);
    308
    309	roccat_report.type = KOVAPLUS_MOUSE_REPORT_BUTTON_TYPE_PROFILE_1;
    310	roccat_report.profile = profile + 1;
    311	roccat_report.button = 0;
    312	roccat_report.data1 = profile + 1;
    313	roccat_report.data2 = 0;
    314	roccat_report_event(kovaplus->chrdev_minor,
    315			(uint8_t const *)&roccat_report);
    316
    317	mutex_unlock(&kovaplus->kovaplus_lock);
    318
    319	return size;
    320}
    321static DEVICE_ATTR(actual_profile, 0660,
    322		   kovaplus_sysfs_show_actual_profile,
    323		   kovaplus_sysfs_set_actual_profile);
    324
    325static ssize_t kovaplus_sysfs_show_actual_cpi(struct device *dev,
    326		struct device_attribute *attr, char *buf)
    327{
    328	struct kovaplus_device *kovaplus =
    329			hid_get_drvdata(dev_get_drvdata(dev->parent->parent));
    330	return snprintf(buf, PAGE_SIZE, "%d\n", kovaplus->actual_cpi);
    331}
    332static DEVICE_ATTR(actual_cpi, 0440, kovaplus_sysfs_show_actual_cpi, NULL);
    333
    334static ssize_t kovaplus_sysfs_show_actual_sensitivity_x(struct device *dev,
    335		struct device_attribute *attr, char *buf)
    336{
    337	struct kovaplus_device *kovaplus =
    338			hid_get_drvdata(dev_get_drvdata(dev->parent->parent));
    339	return snprintf(buf, PAGE_SIZE, "%d\n", kovaplus->actual_x_sensitivity);
    340}
    341static DEVICE_ATTR(actual_sensitivity_x, 0440,
    342		   kovaplus_sysfs_show_actual_sensitivity_x, NULL);
    343
    344static ssize_t kovaplus_sysfs_show_actual_sensitivity_y(struct device *dev,
    345		struct device_attribute *attr, char *buf)
    346{
    347	struct kovaplus_device *kovaplus =
    348			hid_get_drvdata(dev_get_drvdata(dev->parent->parent));
    349	return snprintf(buf, PAGE_SIZE, "%d\n", kovaplus->actual_y_sensitivity);
    350}
    351static DEVICE_ATTR(actual_sensitivity_y, 0440,
    352		   kovaplus_sysfs_show_actual_sensitivity_y, NULL);
    353
    354static ssize_t kovaplus_sysfs_show_firmware_version(struct device *dev,
    355		struct device_attribute *attr, char *buf)
    356{
    357	struct kovaplus_device *kovaplus;
    358	struct usb_device *usb_dev;
    359	struct kovaplus_info info;
    360
    361	dev = dev->parent->parent;
    362	kovaplus = hid_get_drvdata(dev_get_drvdata(dev));
    363	usb_dev = interface_to_usbdev(to_usb_interface(dev));
    364
    365	mutex_lock(&kovaplus->kovaplus_lock);
    366	roccat_common2_receive(usb_dev, KOVAPLUS_COMMAND_INFO,
    367			&info, KOVAPLUS_SIZE_INFO);
    368	mutex_unlock(&kovaplus->kovaplus_lock);
    369
    370	return snprintf(buf, PAGE_SIZE, "%d\n", info.firmware_version);
    371}
    372static DEVICE_ATTR(firmware_version, 0440,
    373		   kovaplus_sysfs_show_firmware_version, NULL);
    374
    375static struct attribute *kovaplus_attrs[] = {
    376	&dev_attr_actual_cpi.attr,
    377	&dev_attr_firmware_version.attr,
    378	&dev_attr_actual_profile.attr,
    379	&dev_attr_actual_sensitivity_x.attr,
    380	&dev_attr_actual_sensitivity_y.attr,
    381	NULL,
    382};
    383
    384static struct bin_attribute *kovaplus_bin_attributes[] = {
    385	&bin_attr_control,
    386	&bin_attr_info,
    387	&bin_attr_profile_settings,
    388	&bin_attr_profile_buttons,
    389	&bin_attr_profile1_settings,
    390	&bin_attr_profile2_settings,
    391	&bin_attr_profile3_settings,
    392	&bin_attr_profile4_settings,
    393	&bin_attr_profile5_settings,
    394	&bin_attr_profile1_buttons,
    395	&bin_attr_profile2_buttons,
    396	&bin_attr_profile3_buttons,
    397	&bin_attr_profile4_buttons,
    398	&bin_attr_profile5_buttons,
    399	NULL,
    400};
    401
    402static const struct attribute_group kovaplus_group = {
    403	.attrs = kovaplus_attrs,
    404	.bin_attrs = kovaplus_bin_attributes,
    405};
    406
    407static const struct attribute_group *kovaplus_groups[] = {
    408	&kovaplus_group,
    409	NULL,
    410};
    411
    412static int kovaplus_init_kovaplus_device_struct(struct usb_device *usb_dev,
    413		struct kovaplus_device *kovaplus)
    414{
    415	int retval, i;
    416	static uint wait = 70; /* device will freeze with just 60 */
    417
    418	mutex_init(&kovaplus->kovaplus_lock);
    419
    420	for (i = 0; i < 5; ++i) {
    421		msleep(wait);
    422		retval = kovaplus_get_profile_settings(usb_dev,
    423				&kovaplus->profile_settings[i], i);
    424		if (retval)
    425			return retval;
    426
    427		msleep(wait);
    428		retval = kovaplus_get_profile_buttons(usb_dev,
    429				&kovaplus->profile_buttons[i], i);
    430		if (retval)
    431			return retval;
    432	}
    433
    434	msleep(wait);
    435	retval = kovaplus_get_actual_profile(usb_dev);
    436	if (retval < 0)
    437		return retval;
    438	kovaplus_profile_activated(kovaplus, retval);
    439
    440	return 0;
    441}
    442
    443static int kovaplus_init_specials(struct hid_device *hdev)
    444{
    445	struct usb_interface *intf = to_usb_interface(hdev->dev.parent);
    446	struct usb_device *usb_dev = interface_to_usbdev(intf);
    447	struct kovaplus_device *kovaplus;
    448	int retval;
    449
    450	if (intf->cur_altsetting->desc.bInterfaceProtocol
    451			== USB_INTERFACE_PROTOCOL_MOUSE) {
    452
    453		kovaplus = kzalloc(sizeof(*kovaplus), GFP_KERNEL);
    454		if (!kovaplus) {
    455			hid_err(hdev, "can't alloc device descriptor\n");
    456			return -ENOMEM;
    457		}
    458		hid_set_drvdata(hdev, kovaplus);
    459
    460		retval = kovaplus_init_kovaplus_device_struct(usb_dev, kovaplus);
    461		if (retval) {
    462			hid_err(hdev, "couldn't init struct kovaplus_device\n");
    463			goto exit_free;
    464		}
    465
    466		retval = roccat_connect(kovaplus_class, hdev,
    467				sizeof(struct kovaplus_roccat_report));
    468		if (retval < 0) {
    469			hid_err(hdev, "couldn't init char dev\n");
    470		} else {
    471			kovaplus->chrdev_minor = retval;
    472			kovaplus->roccat_claimed = 1;
    473		}
    474
    475	} else {
    476		hid_set_drvdata(hdev, NULL);
    477	}
    478
    479	return 0;
    480exit_free:
    481	kfree(kovaplus);
    482	return retval;
    483}
    484
    485static void kovaplus_remove_specials(struct hid_device *hdev)
    486{
    487	struct usb_interface *intf = to_usb_interface(hdev->dev.parent);
    488	struct kovaplus_device *kovaplus;
    489
    490	if (intf->cur_altsetting->desc.bInterfaceProtocol
    491			== USB_INTERFACE_PROTOCOL_MOUSE) {
    492		kovaplus = hid_get_drvdata(hdev);
    493		if (kovaplus->roccat_claimed)
    494			roccat_disconnect(kovaplus->chrdev_minor);
    495		kfree(kovaplus);
    496	}
    497}
    498
    499static int kovaplus_probe(struct hid_device *hdev,
    500		const struct hid_device_id *id)
    501{
    502	int retval;
    503
    504	if (!hid_is_usb(hdev))
    505		return -EINVAL;
    506
    507	retval = hid_parse(hdev);
    508	if (retval) {
    509		hid_err(hdev, "parse failed\n");
    510		goto exit;
    511	}
    512
    513	retval = hid_hw_start(hdev, HID_CONNECT_DEFAULT);
    514	if (retval) {
    515		hid_err(hdev, "hw start failed\n");
    516		goto exit;
    517	}
    518
    519	retval = kovaplus_init_specials(hdev);
    520	if (retval) {
    521		hid_err(hdev, "couldn't install mouse\n");
    522		goto exit_stop;
    523	}
    524
    525	return 0;
    526
    527exit_stop:
    528	hid_hw_stop(hdev);
    529exit:
    530	return retval;
    531}
    532
    533static void kovaplus_remove(struct hid_device *hdev)
    534{
    535	kovaplus_remove_specials(hdev);
    536	hid_hw_stop(hdev);
    537}
    538
    539static void kovaplus_keep_values_up_to_date(struct kovaplus_device *kovaplus,
    540		u8 const *data)
    541{
    542	struct kovaplus_mouse_report_button const *button_report;
    543
    544	if (data[0] != KOVAPLUS_MOUSE_REPORT_NUMBER_BUTTON)
    545		return;
    546
    547	button_report = (struct kovaplus_mouse_report_button const *)data;
    548
    549	switch (button_report->type) {
    550	case KOVAPLUS_MOUSE_REPORT_BUTTON_TYPE_PROFILE_1:
    551		kovaplus_profile_activated(kovaplus, button_report->data1 - 1);
    552		break;
    553	case KOVAPLUS_MOUSE_REPORT_BUTTON_TYPE_CPI:
    554		kovaplus->actual_cpi = kovaplus_convert_event_cpi(button_report->data1);
    555		break;
    556	case KOVAPLUS_MOUSE_REPORT_BUTTON_TYPE_SENSITIVITY:
    557		kovaplus->actual_x_sensitivity = button_report->data1;
    558		kovaplus->actual_y_sensitivity = button_report->data2;
    559		break;
    560	default:
    561		break;
    562	}
    563}
    564
    565static void kovaplus_report_to_chrdev(struct kovaplus_device const *kovaplus,
    566		u8 const *data)
    567{
    568	struct kovaplus_roccat_report roccat_report;
    569	struct kovaplus_mouse_report_button const *button_report;
    570
    571	if (data[0] != KOVAPLUS_MOUSE_REPORT_NUMBER_BUTTON)
    572		return;
    573
    574	button_report = (struct kovaplus_mouse_report_button const *)data;
    575
    576	if (button_report->type == KOVAPLUS_MOUSE_REPORT_BUTTON_TYPE_PROFILE_2)
    577		return;
    578
    579	roccat_report.type = button_report->type;
    580	roccat_report.profile = kovaplus->actual_profile + 1;
    581
    582	if (roccat_report.type == KOVAPLUS_MOUSE_REPORT_BUTTON_TYPE_MACRO ||
    583			roccat_report.type == KOVAPLUS_MOUSE_REPORT_BUTTON_TYPE_SHORTCUT ||
    584			roccat_report.type == KOVAPLUS_MOUSE_REPORT_BUTTON_TYPE_QUICKLAUNCH ||
    585			roccat_report.type == KOVAPLUS_MOUSE_REPORT_BUTTON_TYPE_TIMER)
    586		roccat_report.button = button_report->data1;
    587	else
    588		roccat_report.button = 0;
    589
    590	if (roccat_report.type == KOVAPLUS_MOUSE_REPORT_BUTTON_TYPE_CPI)
    591		roccat_report.data1 = kovaplus_convert_event_cpi(button_report->data1);
    592	else
    593		roccat_report.data1 = button_report->data1;
    594
    595	roccat_report.data2 = button_report->data2;
    596
    597	roccat_report_event(kovaplus->chrdev_minor,
    598			(uint8_t const *)&roccat_report);
    599}
    600
    601static int kovaplus_raw_event(struct hid_device *hdev,
    602		struct hid_report *report, u8 *data, int size)
    603{
    604	struct usb_interface *intf = to_usb_interface(hdev->dev.parent);
    605	struct kovaplus_device *kovaplus = hid_get_drvdata(hdev);
    606
    607	if (intf->cur_altsetting->desc.bInterfaceProtocol
    608			!= USB_INTERFACE_PROTOCOL_MOUSE)
    609		return 0;
    610
    611	if (kovaplus == NULL)
    612		return 0;
    613
    614	kovaplus_keep_values_up_to_date(kovaplus, data);
    615
    616	if (kovaplus->roccat_claimed)
    617		kovaplus_report_to_chrdev(kovaplus, data);
    618
    619	return 0;
    620}
    621
    622static const struct hid_device_id kovaplus_devices[] = {
    623	{ HID_USB_DEVICE(USB_VENDOR_ID_ROCCAT, USB_DEVICE_ID_ROCCAT_KOVAPLUS) },
    624	{ }
    625};
    626
    627MODULE_DEVICE_TABLE(hid, kovaplus_devices);
    628
    629static struct hid_driver kovaplus_driver = {
    630		.name = "kovaplus",
    631		.id_table = kovaplus_devices,
    632		.probe = kovaplus_probe,
    633		.remove = kovaplus_remove,
    634		.raw_event = kovaplus_raw_event
    635};
    636
    637static int __init kovaplus_init(void)
    638{
    639	int retval;
    640
    641	kovaplus_class = class_create(THIS_MODULE, "kovaplus");
    642	if (IS_ERR(kovaplus_class))
    643		return PTR_ERR(kovaplus_class);
    644	kovaplus_class->dev_groups = kovaplus_groups;
    645
    646	retval = hid_register_driver(&kovaplus_driver);
    647	if (retval)
    648		class_destroy(kovaplus_class);
    649	return retval;
    650}
    651
    652static void __exit kovaplus_exit(void)
    653{
    654	hid_unregister_driver(&kovaplus_driver);
    655	class_destroy(kovaplus_class);
    656}
    657
    658module_init(kovaplus_init);
    659module_exit(kovaplus_exit);
    660
    661MODULE_AUTHOR("Stefan Achatz");
    662MODULE_DESCRIPTION("USB Roccat Kova[+] driver");
    663MODULE_LICENSE("GPL v2");