10e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz/*
20e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz * Roccat Kova[+] driver for Linux
30e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz *
40e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz * Copyright (c) 2011 Stefan Achatz <erazor_de@users.sourceforge.net>
50e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz */
60e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz
70e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz/*
80e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz * This program is free software; you can redistribute it and/or modify it
90e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz * under the terms of the GNU General Public License as published by the Free
100e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz * Software Foundation; either version 2 of the License, or (at your option)
110e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz * any later version.
120e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz */
130e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz
140e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz/*
150e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz * Roccat Kova[+] is a bigger version of the Pyra with two more side buttons.
160e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz */
170e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz
180e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz#include <linux/device.h>
190e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz#include <linux/input.h>
200e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz#include <linux/hid.h>
210e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz#include <linux/module.h>
220e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz#include <linux/slab.h>
235dc0c9835fb96c75c8dbf657393764bd0abbac04Stefan Achatz#include <linux/hid-roccat.h>
240e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz#include "hid-ids.h"
250e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz#include "hid-roccat-common.h"
260e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz#include "hid-roccat-kovaplus.h"
270e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz
280e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatzstatic uint profile_numbers[5] = {0, 1, 2, 3, 4};
290e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz
300e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatzstatic struct class *kovaplus_class;
310e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz
320e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatzstatic uint kovaplus_convert_event_cpi(uint value)
330e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz{
340e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz	return (value == 7 ? 4 : (value == 4 ? 3 : value));
350e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz}
360e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz
370e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatzstatic void kovaplus_profile_activated(struct kovaplus_device *kovaplus,
380e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz		uint new_profile_index)
390e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz{
400e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz	kovaplus->actual_profile = new_profile_index;
410e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz	kovaplus->actual_cpi = kovaplus->profile_settings[new_profile_index].cpi_startup_level;
420e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz	kovaplus->actual_x_sensitivity = kovaplus->profile_settings[new_profile_index].sensitivity_x;
430e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz	kovaplus->actual_y_sensitivity = kovaplus->profile_settings[new_profile_index].sensitivity_y;
440e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz}
450e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz
460e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatzstatic int kovaplus_send_control(struct usb_device *usb_dev, uint value,
470e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz		enum kovaplus_control_requests request)
480e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz{
490e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz	int retval;
500e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz	struct kovaplus_control control;
510e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz
520e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz	if ((request == KOVAPLUS_CONTROL_REQUEST_PROFILE_SETTINGS ||
530e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz			request == KOVAPLUS_CONTROL_REQUEST_PROFILE_BUTTONS) &&
540e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz			value > 4)
550e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz		return -EINVAL;
560e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz
570e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz	control.command = KOVAPLUS_COMMAND_CONTROL;
580e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz	control.value = value;
590e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz	control.request = request;
600e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz
611edd5b42a6631b1b1f147e9018e309bde8d96a05Stefan Achatz	retval = roccat_common_send(usb_dev, KOVAPLUS_COMMAND_CONTROL,
620e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz			&control, sizeof(struct kovaplus_control));
630e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz
640e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz	return retval;
650e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz}
660e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz
670e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatzstatic int kovaplus_receive_control_status(struct usb_device *usb_dev)
680e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz{
690e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz	int retval;
700e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz	struct kovaplus_control control;
710e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz
720e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz	do {
731edd5b42a6631b1b1f147e9018e309bde8d96a05Stefan Achatz		retval = roccat_common_receive(usb_dev, KOVAPLUS_COMMAND_CONTROL,
740e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz				&control, sizeof(struct kovaplus_control));
750e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz
760e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz		/* check if we get a completely wrong answer */
770e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz		if (retval)
780e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz			return retval;
790e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz
800e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz		if (control.value == KOVAPLUS_CONTROL_REQUEST_STATUS_OK)
810e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz			return 0;
820e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz
830e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz		/* indicates that hardware needs some more time to complete action */
840e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz		if (control.value == KOVAPLUS_CONTROL_REQUEST_STATUS_WAIT) {
850e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz			msleep(500); /* windows driver uses 1000 */
860e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz			continue;
870e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz		}
880e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz
890e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz		/* seems to be critical - replug necessary */
900e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz		if (control.value == KOVAPLUS_CONTROL_REQUEST_STATUS_OVERLOAD)
910e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz			return -EINVAL;
920e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz
931edd5b42a6631b1b1f147e9018e309bde8d96a05Stefan Achatz		hid_err(usb_dev, "roccat_common_receive_control_status: "
940e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz				"unknown response value 0x%x\n", control.value);
950e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz		return -EINVAL;
960e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz	} while (1);
970e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz}
980e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz
990e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatzstatic int kovaplus_send(struct usb_device *usb_dev, uint command,
1000e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz		void const *buf, uint size)
1010e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz{
1020e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz	int retval;
1030e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz
1040e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz	retval = roccat_common_send(usb_dev, command, buf, size);
1050e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz	if (retval)
1060e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz		return retval;
1070e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz
1080e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz	msleep(100);
1090e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz
1100e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz	return kovaplus_receive_control_status(usb_dev);
1110e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz}
1120e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz
1130e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatzstatic int kovaplus_select_profile(struct usb_device *usb_dev, uint number,
1140e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz		enum kovaplus_control_requests request)
1150e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz{
1160e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz	return kovaplus_send_control(usb_dev, number, request);
1170e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz}
1180e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz
1190e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatzstatic int kovaplus_get_info(struct usb_device *usb_dev,
1200e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz		struct kovaplus_info *buf)
1210e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz{
1221edd5b42a6631b1b1f147e9018e309bde8d96a05Stefan Achatz	return roccat_common_receive(usb_dev, KOVAPLUS_COMMAND_INFO,
1230e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz			buf, sizeof(struct kovaplus_info));
1240e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz}
1250e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz
1260e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatzstatic int kovaplus_get_profile_settings(struct usb_device *usb_dev,
1270e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz		struct kovaplus_profile_settings *buf, uint number)
1280e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz{
1290e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz	int retval;
1300e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz
1310e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz	retval = kovaplus_select_profile(usb_dev, number,
1320e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz			KOVAPLUS_CONTROL_REQUEST_PROFILE_SETTINGS);
1330e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz	if (retval)
1340e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz		return retval;
1350e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz
1361edd5b42a6631b1b1f147e9018e309bde8d96a05Stefan Achatz	return roccat_common_receive(usb_dev, KOVAPLUS_COMMAND_PROFILE_SETTINGS,
1370e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz			buf, sizeof(struct kovaplus_profile_settings));
1380e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz}
1390e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz
1400e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatzstatic int kovaplus_set_profile_settings(struct usb_device *usb_dev,
1410e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz		struct kovaplus_profile_settings const *settings)
1420e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz{
1431edd5b42a6631b1b1f147e9018e309bde8d96a05Stefan Achatz	return kovaplus_send(usb_dev, KOVAPLUS_COMMAND_PROFILE_SETTINGS,
1440e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz			settings, sizeof(struct kovaplus_profile_settings));
1450e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz}
1460e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz
1470e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatzstatic int kovaplus_get_profile_buttons(struct usb_device *usb_dev,
1480e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz		struct kovaplus_profile_buttons *buf, int number)
1490e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz{
1500e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz	int retval;
1510e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz
1520e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz	retval = kovaplus_select_profile(usb_dev, number,
1530e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz			KOVAPLUS_CONTROL_REQUEST_PROFILE_BUTTONS);
1540e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz	if (retval)
1550e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz		return retval;
1560e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz
1571edd5b42a6631b1b1f147e9018e309bde8d96a05Stefan Achatz	return roccat_common_receive(usb_dev, KOVAPLUS_COMMAND_PROFILE_BUTTONS,
1580e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz			buf, sizeof(struct kovaplus_profile_buttons));
1590e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz}
1600e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz
1610e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatzstatic int kovaplus_set_profile_buttons(struct usb_device *usb_dev,
1620e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz		struct kovaplus_profile_buttons const *buttons)
1630e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz{
1641edd5b42a6631b1b1f147e9018e309bde8d96a05Stefan Achatz	return kovaplus_send(usb_dev, KOVAPLUS_COMMAND_PROFILE_BUTTONS,
1650e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz			buttons, sizeof(struct kovaplus_profile_buttons));
1660e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz}
1670e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz
1680e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz/* retval is 0-4 on success, < 0 on error */
1690e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatzstatic int kovaplus_get_actual_profile(struct usb_device *usb_dev)
1700e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz{
1710e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz	struct kovaplus_actual_profile buf;
1720e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz	int retval;
1730e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz
1741edd5b42a6631b1b1f147e9018e309bde8d96a05Stefan Achatz	retval = roccat_common_receive(usb_dev, KOVAPLUS_COMMAND_ACTUAL_PROFILE,
1750e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz			&buf, sizeof(struct kovaplus_actual_profile));
1760e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz
1770e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz	return retval ? retval : buf.actual_profile;
1780e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz}
1790e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz
1800e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatzstatic int kovaplus_set_actual_profile(struct usb_device *usb_dev,
1810e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz		int new_profile)
1820e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz{
1830e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz	struct kovaplus_actual_profile buf;
1840e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz
1850e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz	buf.command = KOVAPLUS_COMMAND_ACTUAL_PROFILE;
1860e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz	buf.size = sizeof(struct kovaplus_actual_profile);
1870e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz	buf.actual_profile = new_profile;
1880e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz
1891edd5b42a6631b1b1f147e9018e309bde8d96a05Stefan Achatz	return kovaplus_send(usb_dev, KOVAPLUS_COMMAND_ACTUAL_PROFILE,
1900e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz			&buf, sizeof(struct kovaplus_actual_profile));
1910e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz}
1920e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz
1930e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatzstatic ssize_t kovaplus_sysfs_read_profilex_settings(struct file *fp,
1940e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz		struct kobject *kobj, struct bin_attribute *attr, char *buf,
1950e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz		loff_t off, size_t count)
1960e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz{
1970e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz	struct device *dev =
1980e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz			container_of(kobj, struct device, kobj)->parent->parent;
1990e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz	struct kovaplus_device *kovaplus = hid_get_drvdata(dev_get_drvdata(dev));
2000e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz
2010e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz	if (off >= sizeof(struct kovaplus_profile_settings))
2020e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz		return 0;
2030e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz
2040e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz	if (off + count > sizeof(struct kovaplus_profile_settings))
2050e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz		count = sizeof(struct kovaplus_profile_settings) - off;
2060e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz
2070e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz	mutex_lock(&kovaplus->kovaplus_lock);
2080e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz	memcpy(buf, ((char const *)&kovaplus->profile_settings[*(uint *)(attr->private)]) + off,
2090e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz			count);
2100e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz	mutex_unlock(&kovaplus->kovaplus_lock);
2110e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz
2120e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz	return count;
2130e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz}
2140e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz
2150e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatzstatic ssize_t kovaplus_sysfs_write_profile_settings(struct file *fp,
2160e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz		struct kobject *kobj, struct bin_attribute *attr, char *buf,
2170e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz		loff_t off, size_t count)
2180e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz{
2190e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz	struct device *dev =
2200e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz			container_of(kobj, struct device, kobj)->parent->parent;
2210e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz	struct kovaplus_device *kovaplus = hid_get_drvdata(dev_get_drvdata(dev));
2220e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz	struct usb_device *usb_dev = interface_to_usbdev(to_usb_interface(dev));
2230e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz	int retval = 0;
2240e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz	int difference;
2250e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz	int profile_index;
2260e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz	struct kovaplus_profile_settings *profile_settings;
2270e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz
2280e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz	if (off != 0 || count != sizeof(struct kovaplus_profile_settings))
2290e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz		return -EINVAL;
2300e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz
2310e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz	profile_index = ((struct kovaplus_profile_settings const *)buf)->profile_index;
2320e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz	profile_settings = &kovaplus->profile_settings[profile_index];
2330e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz
2340e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz	mutex_lock(&kovaplus->kovaplus_lock);
2350e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz	difference = memcmp(buf, profile_settings,
2360e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz			sizeof(struct kovaplus_profile_settings));
2370e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz	if (difference) {
2380e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz		retval = kovaplus_set_profile_settings(usb_dev,
2390e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz				(struct kovaplus_profile_settings const *)buf);
2400e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz		if (!retval)
2410e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz			memcpy(profile_settings, buf,
2420e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz					sizeof(struct kovaplus_profile_settings));
2430e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz	}
2440e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz	mutex_unlock(&kovaplus->kovaplus_lock);
2450e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz
2460e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz	if (retval)
2470e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz		return retval;
2480e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz
2490e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz	return sizeof(struct kovaplus_profile_settings);
2500e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz}
2510e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz
2520e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatzstatic ssize_t kovaplus_sysfs_read_profilex_buttons(struct file *fp,
2530e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz		struct kobject *kobj, struct bin_attribute *attr, char *buf,
2540e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz		loff_t off, size_t count)
2550e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz{
2560e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz	struct device *dev =
2570e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz			container_of(kobj, struct device, kobj)->parent->parent;
2580e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz	struct kovaplus_device *kovaplus = hid_get_drvdata(dev_get_drvdata(dev));
2590e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz
2600e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz	if (off >= sizeof(struct kovaplus_profile_buttons))
2610e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz		return 0;
2620e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz
2630e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz	if (off + count > sizeof(struct kovaplus_profile_buttons))
2640e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz		count = sizeof(struct kovaplus_profile_buttons) - off;
2650e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz
2660e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz	mutex_lock(&kovaplus->kovaplus_lock);
2670e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz	memcpy(buf, ((char const *)&kovaplus->profile_buttons[*(uint *)(attr->private)]) + off,
2680e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz			count);
2690e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz	mutex_unlock(&kovaplus->kovaplus_lock);
2700e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz
2710e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz	return count;
2720e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz}
2730e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz
2740e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatzstatic ssize_t kovaplus_sysfs_write_profile_buttons(struct file *fp,
2750e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz		struct kobject *kobj, struct bin_attribute *attr, char *buf,
2760e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz		loff_t off, size_t count)
2770e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz{
2780e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz	struct device *dev =
2790e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz			container_of(kobj, struct device, kobj)->parent->parent;
2800e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz	struct kovaplus_device *kovaplus = hid_get_drvdata(dev_get_drvdata(dev));
2810e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz	struct usb_device *usb_dev = interface_to_usbdev(to_usb_interface(dev));
2820e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz	int retval = 0;
2830e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz	int difference;
2840e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz	uint profile_index;
2850e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz	struct kovaplus_profile_buttons *profile_buttons;
2860e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz
2870e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz	if (off != 0 || count != sizeof(struct kovaplus_profile_buttons))
2880e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz		return -EINVAL;
2890e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz
2900e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz	profile_index = ((struct kovaplus_profile_buttons const *)buf)->profile_index;
2910e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz	profile_buttons = &kovaplus->profile_buttons[profile_index];
2920e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz
2930e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz	mutex_lock(&kovaplus->kovaplus_lock);
2940e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz	difference = memcmp(buf, profile_buttons,
2950e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz			sizeof(struct kovaplus_profile_buttons));
2960e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz	if (difference) {
2970e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz		retval = kovaplus_set_profile_buttons(usb_dev,
2980e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz				(struct kovaplus_profile_buttons const *)buf);
2990e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz		if (!retval)
3000e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz			memcpy(profile_buttons, buf,
3010e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz					sizeof(struct kovaplus_profile_buttons));
3020e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz	}
3030e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz	mutex_unlock(&kovaplus->kovaplus_lock);
3040e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz
3050e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz	if (retval)
3060e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz		return retval;
3070e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz
3080e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz	return sizeof(struct kovaplus_profile_buttons);
3090e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz}
3100e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz
3110e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatzstatic ssize_t kovaplus_sysfs_show_actual_profile(struct device *dev,
3120e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz		struct device_attribute *attr, char *buf)
3130e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz{
3140e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz	struct kovaplus_device *kovaplus =
3150e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz			hid_get_drvdata(dev_get_drvdata(dev->parent->parent));
3160e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz	return snprintf(buf, PAGE_SIZE, "%d\n", kovaplus->actual_profile);
3170e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz}
3180e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz
3190e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatzstatic ssize_t kovaplus_sysfs_set_actual_profile(struct device *dev,
3200e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz		struct device_attribute *attr, char const *buf, size_t size)
3210e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz{
3220e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz	struct kovaplus_device *kovaplus;
3230e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz	struct usb_device *usb_dev;
3240e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz	unsigned long profile;
3250e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz	int retval;
3266b9a57b9fb8194e00d49779bd0d1130844db6a84Stefan Achatz	struct kovaplus_roccat_report roccat_report;
3270e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz
3280e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz	dev = dev->parent->parent;
3290e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz	kovaplus = hid_get_drvdata(dev_get_drvdata(dev));
3300e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz	usb_dev = interface_to_usbdev(to_usb_interface(dev));
3310e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz
3320e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz	retval = strict_strtoul(buf, 10, &profile);
3330e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz	if (retval)
3340e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz		return retval;
3350e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz
3360e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz	if (profile >= 5)
3370e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz		return -EINVAL;
3380e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz
3390e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz	mutex_lock(&kovaplus->kovaplus_lock);
3400e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz	retval = kovaplus_set_actual_profile(usb_dev, profile);
3416b9a57b9fb8194e00d49779bd0d1130844db6a84Stefan Achatz	if (retval) {
3426b9a57b9fb8194e00d49779bd0d1130844db6a84Stefan Achatz		mutex_unlock(&kovaplus->kovaplus_lock);
3436b9a57b9fb8194e00d49779bd0d1130844db6a84Stefan Achatz		return retval;
3446b9a57b9fb8194e00d49779bd0d1130844db6a84Stefan Achatz	}
3456b9a57b9fb8194e00d49779bd0d1130844db6a84Stefan Achatz
346901e64dbdb5998b9248c372a401c921bbdf662f6Stefan Achatz	kovaplus_profile_activated(kovaplus, profile);
3476b9a57b9fb8194e00d49779bd0d1130844db6a84Stefan Achatz
3486b9a57b9fb8194e00d49779bd0d1130844db6a84Stefan Achatz	roccat_report.type = KOVAPLUS_MOUSE_REPORT_BUTTON_TYPE_PROFILE_1;
3496b9a57b9fb8194e00d49779bd0d1130844db6a84Stefan Achatz	roccat_report.profile = profile + 1;
3506b9a57b9fb8194e00d49779bd0d1130844db6a84Stefan Achatz	roccat_report.button = 0;
3516b9a57b9fb8194e00d49779bd0d1130844db6a84Stefan Achatz	roccat_report.data1 = profile + 1;
3526b9a57b9fb8194e00d49779bd0d1130844db6a84Stefan Achatz	roccat_report.data2 = 0;
3536b9a57b9fb8194e00d49779bd0d1130844db6a84Stefan Achatz	roccat_report_event(kovaplus->chrdev_minor,
3546b9a57b9fb8194e00d49779bd0d1130844db6a84Stefan Achatz			(uint8_t const *)&roccat_report);
3556b9a57b9fb8194e00d49779bd0d1130844db6a84Stefan Achatz
3560e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz	mutex_unlock(&kovaplus->kovaplus_lock);
3570e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz
3580e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz	return size;
3590e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz}
3600e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz
3610e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatzstatic ssize_t kovaplus_sysfs_show_actual_cpi(struct device *dev,
3620e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz		struct device_attribute *attr, char *buf)
3630e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz{
3640e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz	struct kovaplus_device *kovaplus =
3650e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz			hid_get_drvdata(dev_get_drvdata(dev->parent->parent));
3660e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz	return snprintf(buf, PAGE_SIZE, "%d\n", kovaplus->actual_cpi);
3670e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz}
3680e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz
3690e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatzstatic ssize_t kovaplus_sysfs_show_actual_sensitivity_x(struct device *dev,
3700e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz		struct device_attribute *attr, char *buf)
3710e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz{
3720e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz	struct kovaplus_device *kovaplus =
3730e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz			hid_get_drvdata(dev_get_drvdata(dev->parent->parent));
3740e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz	return snprintf(buf, PAGE_SIZE, "%d\n", kovaplus->actual_x_sensitivity);
3750e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz}
3760e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz
3770e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatzstatic ssize_t kovaplus_sysfs_show_actual_sensitivity_y(struct device *dev,
3780e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz		struct device_attribute *attr, char *buf)
3790e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz{
3800e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz	struct kovaplus_device *kovaplus =
3810e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz			hid_get_drvdata(dev_get_drvdata(dev->parent->parent));
3820e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz	return snprintf(buf, PAGE_SIZE, "%d\n", kovaplus->actual_y_sensitivity);
3830e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz}
3840e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz
3850e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatzstatic ssize_t kovaplus_sysfs_show_firmware_version(struct device *dev,
3860e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz		struct device_attribute *attr, char *buf)
3870e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz{
3880e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz	struct kovaplus_device *kovaplus =
3890e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz			hid_get_drvdata(dev_get_drvdata(dev->parent->parent));
3900e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz	return snprintf(buf, PAGE_SIZE, "%d\n", kovaplus->info.firmware_version);
3910e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz}
3920e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz
3930e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatzstatic struct device_attribute kovaplus_attributes[] = {
3940e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz	__ATTR(actual_cpi, 0440,
3950e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz		kovaplus_sysfs_show_actual_cpi, NULL),
3960e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz	__ATTR(firmware_version, 0440,
3970e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz		kovaplus_sysfs_show_firmware_version, NULL),
3980e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz	__ATTR(actual_profile, 0660,
3990e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz		kovaplus_sysfs_show_actual_profile,
4000e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz		kovaplus_sysfs_set_actual_profile),
4010e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz	__ATTR(actual_sensitivity_x, 0440,
4020e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz		kovaplus_sysfs_show_actual_sensitivity_x, NULL),
4030e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz	__ATTR(actual_sensitivity_y, 0440,
4040e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz		kovaplus_sysfs_show_actual_sensitivity_y, NULL),
4050e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz	__ATTR_NULL
4060e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz};
4070e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz
4080e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatzstatic struct bin_attribute kovaplus_bin_attributes[] = {
4090e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz	{
4100e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz		.attr = { .name = "profile_settings", .mode = 0220 },
4110e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz		.size = sizeof(struct kovaplus_profile_settings),
4120e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz		.write = kovaplus_sysfs_write_profile_settings
4130e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz	},
4140e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz	{
4150e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz		.attr = { .name = "profile1_settings", .mode = 0440 },
4160e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz		.size = sizeof(struct kovaplus_profile_settings),
4170e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz		.read = kovaplus_sysfs_read_profilex_settings,
4180e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz		.private = &profile_numbers[0]
4190e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz	},
4200e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz	{
4210e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz		.attr = { .name = "profile2_settings", .mode = 0440 },
4220e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz		.size = sizeof(struct kovaplus_profile_settings),
4230e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz		.read = kovaplus_sysfs_read_profilex_settings,
4240e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz		.private = &profile_numbers[1]
4250e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz	},
4260e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz	{
4270e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz		.attr = { .name = "profile3_settings", .mode = 0440 },
4280e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz		.size = sizeof(struct kovaplus_profile_settings),
4290e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz		.read = kovaplus_sysfs_read_profilex_settings,
4300e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz		.private = &profile_numbers[2]
4310e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz	},
4320e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz	{
4330e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz		.attr = { .name = "profile4_settings", .mode = 0440 },
4340e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz		.size = sizeof(struct kovaplus_profile_settings),
4350e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz		.read = kovaplus_sysfs_read_profilex_settings,
4360e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz		.private = &profile_numbers[3]
4370e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz	},
4380e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz	{
4390e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz		.attr = { .name = "profile5_settings", .mode = 0440 },
4400e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz		.size = sizeof(struct kovaplus_profile_settings),
4410e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz		.read = kovaplus_sysfs_read_profilex_settings,
4420e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz		.private = &profile_numbers[4]
4430e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz	},
4440e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz	{
4450e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz		.attr = { .name = "profile_buttons", .mode = 0220 },
4460e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz		.size = sizeof(struct kovaplus_profile_buttons),
4470e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz		.write = kovaplus_sysfs_write_profile_buttons
4480e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz	},
4490e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz	{
4500e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz		.attr = { .name = "profile1_buttons", .mode = 0440 },
4510e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz		.size = sizeof(struct kovaplus_profile_buttons),
4520e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz		.read = kovaplus_sysfs_read_profilex_buttons,
4530e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz		.private = &profile_numbers[0]
4540e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz	},
4550e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz	{
4560e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz		.attr = { .name = "profile2_buttons", .mode = 0440 },
4570e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz		.size = sizeof(struct kovaplus_profile_buttons),
4580e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz		.read = kovaplus_sysfs_read_profilex_buttons,
4590e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz		.private = &profile_numbers[1]
4600e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz	},
4610e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz	{
4620e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz		.attr = { .name = "profile3_buttons", .mode = 0440 },
4630e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz		.size = sizeof(struct kovaplus_profile_buttons),
4640e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz		.read = kovaplus_sysfs_read_profilex_buttons,
4650e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz		.private = &profile_numbers[2]
4660e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz	},
4670e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz	{
4680e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz		.attr = { .name = "profile4_buttons", .mode = 0440 },
4690e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz		.size = sizeof(struct kovaplus_profile_buttons),
4700e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz		.read = kovaplus_sysfs_read_profilex_buttons,
4710e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz		.private = &profile_numbers[3]
4720e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz	},
4730e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz	{
4740e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz		.attr = { .name = "profile5_buttons", .mode = 0440 },
4750e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz		.size = sizeof(struct kovaplus_profile_buttons),
4760e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz		.read = kovaplus_sysfs_read_profilex_buttons,
4770e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz		.private = &profile_numbers[4]
4780e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz	},
4790e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz	__ATTR_NULL
4800e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz};
4810e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz
4820e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatzstatic int kovaplus_init_kovaplus_device_struct(struct usb_device *usb_dev,
4830e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz		struct kovaplus_device *kovaplus)
4840e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz{
4850e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz	int retval, i;
4860e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz	static uint wait = 70; /* device will freeze with just 60 */
4870e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz
4880e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz	mutex_init(&kovaplus->kovaplus_lock);
4890e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz
4900e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz	retval = kovaplus_get_info(usb_dev, &kovaplus->info);
4910e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz	if (retval)
4920e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz		return retval;
4930e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz
4940e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz	for (i = 0; i < 5; ++i) {
4950e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz		msleep(wait);
4960e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz		retval = kovaplus_get_profile_settings(usb_dev,
4970e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz				&kovaplus->profile_settings[i], i);
4980e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz		if (retval)
4990e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz			return retval;
5000e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz
5010e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz		msleep(wait);
5020e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz		retval = kovaplus_get_profile_buttons(usb_dev,
5030e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz				&kovaplus->profile_buttons[i], i);
5040e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz		if (retval)
5050e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz			return retval;
5060e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz	}
5070e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz
5080e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz	msleep(wait);
5090e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz	retval = kovaplus_get_actual_profile(usb_dev);
5100e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz	if (retval < 0)
5110e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz		return retval;
5120e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz	kovaplus_profile_activated(kovaplus, retval);
5130e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz
5140e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz	return 0;
5150e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz}
5160e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz
5170e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatzstatic int kovaplus_init_specials(struct hid_device *hdev)
5180e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz{
5190e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz	struct usb_interface *intf = to_usb_interface(hdev->dev.parent);
5200e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz	struct usb_device *usb_dev = interface_to_usbdev(intf);
5210e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz	struct kovaplus_device *kovaplus;
5220e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz	int retval;
5230e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz
5240e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz	if (intf->cur_altsetting->desc.bInterfaceProtocol
5250e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz			== USB_INTERFACE_PROTOCOL_MOUSE) {
5260e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz
5270e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz		kovaplus = kzalloc(sizeof(*kovaplus), GFP_KERNEL);
5280e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz		if (!kovaplus) {
5290e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz			hid_err(hdev, "can't alloc device descriptor\n");
5300e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz			return -ENOMEM;
5310e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz		}
5320e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz		hid_set_drvdata(hdev, kovaplus);
5330e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz
5340e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz		retval = kovaplus_init_kovaplus_device_struct(usb_dev, kovaplus);
5350e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz		if (retval) {
5360e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz			hid_err(hdev, "couldn't init struct kovaplus_device\n");
5370e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz			goto exit_free;
5380e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz		}
5390e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz
5408211e46004518c977f70f2661da961d5ba617399Stefan Achatz		retval = roccat_connect(kovaplus_class, hdev,
5418211e46004518c977f70f2661da961d5ba617399Stefan Achatz				sizeof(struct kovaplus_roccat_report));
5420e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz		if (retval < 0) {
5430e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz			hid_err(hdev, "couldn't init char dev\n");
5440e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz		} else {
5450e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz			kovaplus->chrdev_minor = retval;
5460e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz			kovaplus->roccat_claimed = 1;
5470e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz		}
5480e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz
5490e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz	} else {
5500e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz		hid_set_drvdata(hdev, NULL);
5510e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz	}
5520e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz
5530e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz	return 0;
5540e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatzexit_free:
5550e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz	kfree(kovaplus);
5560e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz	return retval;
5570e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz}
5580e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz
5590e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatzstatic void kovaplus_remove_specials(struct hid_device *hdev)
5600e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz{
5610e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz	struct usb_interface *intf = to_usb_interface(hdev->dev.parent);
5620e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz	struct kovaplus_device *kovaplus;
5630e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz
5640e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz	if (intf->cur_altsetting->desc.bInterfaceProtocol
5650e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz			== USB_INTERFACE_PROTOCOL_MOUSE) {
5660e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz		kovaplus = hid_get_drvdata(hdev);
5670e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz		if (kovaplus->roccat_claimed)
5680e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz			roccat_disconnect(kovaplus->chrdev_minor);
5690e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz		kfree(kovaplus);
5700e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz	}
5710e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz}
5720e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz
5730e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatzstatic int kovaplus_probe(struct hid_device *hdev,
5740e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz		const struct hid_device_id *id)
5750e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz{
5760e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz	int retval;
5770e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz
5780e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz	retval = hid_parse(hdev);
5790e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz	if (retval) {
5800e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz		hid_err(hdev, "parse failed\n");
5810e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz		goto exit;
5820e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz	}
5830e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz
5840e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz	retval = hid_hw_start(hdev, HID_CONNECT_DEFAULT);
5850e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz	if (retval) {
5860e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz		hid_err(hdev, "hw start failed\n");
5870e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz		goto exit;
5880e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz	}
5890e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz
5900e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz	retval = kovaplus_init_specials(hdev);
5910e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz	if (retval) {
5920e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz		hid_err(hdev, "couldn't install mouse\n");
5930e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz		goto exit_stop;
5940e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz	}
5950e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz
5960e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz	return 0;
5970e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz
5980e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatzexit_stop:
5990e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz	hid_hw_stop(hdev);
6000e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatzexit:
6010e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz	return retval;
6020e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz}
6030e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz
6040e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatzstatic void kovaplus_remove(struct hid_device *hdev)
6050e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz{
6060e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz	kovaplus_remove_specials(hdev);
6070e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz	hid_hw_stop(hdev);
6080e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz}
6090e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz
6100e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatzstatic void kovaplus_keep_values_up_to_date(struct kovaplus_device *kovaplus,
6110e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz		u8 const *data)
6120e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz{
6130e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz	struct kovaplus_mouse_report_button const *button_report;
6140e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz
6150e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz	if (data[0] != KOVAPLUS_MOUSE_REPORT_NUMBER_BUTTON)
6160e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz		return;
6170e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz
6180e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz	button_report = (struct kovaplus_mouse_report_button const *)data;
6190e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz
6200e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz	switch (button_report->type) {
6210e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz	case KOVAPLUS_MOUSE_REPORT_BUTTON_TYPE_PROFILE_1:
6220e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz		kovaplus_profile_activated(kovaplus, button_report->data1 - 1);
6230e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz		break;
6240e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz	case KOVAPLUS_MOUSE_REPORT_BUTTON_TYPE_CPI:
6250e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz		kovaplus->actual_cpi = kovaplus_convert_event_cpi(button_report->data1);
6260e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz	case KOVAPLUS_MOUSE_REPORT_BUTTON_TYPE_SENSITIVITY:
6270e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz		kovaplus->actual_x_sensitivity = button_report->data1;
6280e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz		kovaplus->actual_y_sensitivity = button_report->data2;
6290e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz	}
6300e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz}
6310e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz
6320e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatzstatic void kovaplus_report_to_chrdev(struct kovaplus_device const *kovaplus,
6330e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz		u8 const *data)
6340e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz{
6350e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz	struct kovaplus_roccat_report roccat_report;
6360e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz	struct kovaplus_mouse_report_button const *button_report;
6370e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz
6380e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz	if (data[0] != KOVAPLUS_MOUSE_REPORT_NUMBER_BUTTON)
6390e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz		return;
6400e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz
6410e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz	button_report = (struct kovaplus_mouse_report_button const *)data;
6420e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz
6430e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz	if (button_report->type == KOVAPLUS_MOUSE_REPORT_BUTTON_TYPE_PROFILE_2)
6440e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz		return;
6450e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz
6460e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz	roccat_report.type = button_report->type;
6470e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz	roccat_report.profile = kovaplus->actual_profile + 1;
6480e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz
6490e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz	if (roccat_report.type == KOVAPLUS_MOUSE_REPORT_BUTTON_TYPE_MACRO ||
6500e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz			roccat_report.type == KOVAPLUS_MOUSE_REPORT_BUTTON_TYPE_SHORTCUT ||
6510e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz			roccat_report.type == KOVAPLUS_MOUSE_REPORT_BUTTON_TYPE_QUICKLAUNCH ||
6520e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz			roccat_report.type == KOVAPLUS_MOUSE_REPORT_BUTTON_TYPE_TIMER)
6530e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz		roccat_report.button = button_report->data1;
6540e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz	else
6550e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz		roccat_report.button = 0;
6560e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz
6570e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz	if (roccat_report.type == KOVAPLUS_MOUSE_REPORT_BUTTON_TYPE_CPI)
6580e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz		roccat_report.data1 = kovaplus_convert_event_cpi(button_report->data1);
6590e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz	else
6600e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz		roccat_report.data1 = button_report->data1;
6610e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz
6620e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz	roccat_report.data2 = button_report->data2;
6630e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz
6640e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz	roccat_report_event(kovaplus->chrdev_minor,
6658211e46004518c977f70f2661da961d5ba617399Stefan Achatz			(uint8_t const *)&roccat_report);
6660e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz}
6670e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz
6680e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatzstatic int kovaplus_raw_event(struct hid_device *hdev,
6690e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz		struct hid_report *report, u8 *data, int size)
6700e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz{
6710e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz	struct usb_interface *intf = to_usb_interface(hdev->dev.parent);
6720e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz	struct kovaplus_device *kovaplus = hid_get_drvdata(hdev);
6730e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz
6740e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz	if (intf->cur_altsetting->desc.bInterfaceProtocol
6750e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz			!= USB_INTERFACE_PROTOCOL_MOUSE)
6760e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz		return 0;
6770e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz
678901e64dbdb5998b9248c372a401c921bbdf662f6Stefan Achatz	if (kovaplus == NULL)
679901e64dbdb5998b9248c372a401c921bbdf662f6Stefan Achatz		return 0;
680901e64dbdb5998b9248c372a401c921bbdf662f6Stefan Achatz
6810e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz	kovaplus_keep_values_up_to_date(kovaplus, data);
6820e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz
6830e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz	if (kovaplus->roccat_claimed)
6840e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz		kovaplus_report_to_chrdev(kovaplus, data);
6850e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz
6860e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz	return 0;
6870e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz}
6880e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz
6890e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatzstatic const struct hid_device_id kovaplus_devices[] = {
6900e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz	{ HID_USB_DEVICE(USB_VENDOR_ID_ROCCAT, USB_DEVICE_ID_ROCCAT_KOVAPLUS) },
6910e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz	{ }
6920e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz};
6930e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz
6940e70f97f257edcef4daa92ab9371a9aac0c851edStefan AchatzMODULE_DEVICE_TABLE(hid, kovaplus_devices);
6950e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz
6960e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatzstatic struct hid_driver kovaplus_driver = {
6970e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz		.name = "kovaplus",
6980e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz		.id_table = kovaplus_devices,
6990e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz		.probe = kovaplus_probe,
7000e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz		.remove = kovaplus_remove,
7010e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz		.raw_event = kovaplus_raw_event
7020e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz};
7030e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz
7040e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatzstatic int __init kovaplus_init(void)
7050e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz{
7060e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz	int retval;
7070e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz
7080e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz	kovaplus_class = class_create(THIS_MODULE, "kovaplus");
7090e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz	if (IS_ERR(kovaplus_class))
7100e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz		return PTR_ERR(kovaplus_class);
7110e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz	kovaplus_class->dev_attrs = kovaplus_attributes;
7120e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz	kovaplus_class->dev_bin_attrs = kovaplus_bin_attributes;
7130e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz
7140e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz	retval = hid_register_driver(&kovaplus_driver);
7150e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz	if (retval)
7160e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz		class_destroy(kovaplus_class);
7170e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz	return retval;
7180e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz}
7190e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz
7200e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatzstatic void __exit kovaplus_exit(void)
7210e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz{
7220e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz	hid_unregister_driver(&kovaplus_driver);
72374b643dac475e29f53f4132d2349ec1dba3c9e44Stefan Achatz	class_destroy(kovaplus_class);
7240e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz}
7250e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz
7260e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatzmodule_init(kovaplus_init);
7270e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatzmodule_exit(kovaplus_exit);
7280e70f97f257edcef4daa92ab9371a9aac0c851edStefan Achatz
7290e70f97f257edcef4daa92ab9371a9aac0c851edStefan AchatzMODULE_AUTHOR("Stefan Achatz");
7300e70f97f257edcef4daa92ab9371a9aac0c851edStefan AchatzMODULE_DESCRIPTION("USB Roccat Kova[+] driver");
7310e70f97f257edcef4daa92ab9371a9aac0c851edStefan AchatzMODULE_LICENSE("GPL v2");
732