18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * Roccat Pyra driver for Linux
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * Copyright (c) 2010 Stefan Achatz <erazor_de@users.sourceforge.net>
68c2ecf20Sopenharmony_ci */
78c2ecf20Sopenharmony_ci
88c2ecf20Sopenharmony_ci/*
98c2ecf20Sopenharmony_ci */
108c2ecf20Sopenharmony_ci
118c2ecf20Sopenharmony_ci/*
128c2ecf20Sopenharmony_ci * Roccat Pyra is a mobile gamer mouse which comes in wired and wireless
138c2ecf20Sopenharmony_ci * variant. Wireless variant is not tested.
148c2ecf20Sopenharmony_ci * Userland tools can be found at http://sourceforge.net/projects/roccat
158c2ecf20Sopenharmony_ci */
168c2ecf20Sopenharmony_ci
178c2ecf20Sopenharmony_ci#include <linux/device.h>
188c2ecf20Sopenharmony_ci#include <linux/input.h>
198c2ecf20Sopenharmony_ci#include <linux/hid.h>
208c2ecf20Sopenharmony_ci#include <linux/module.h>
218c2ecf20Sopenharmony_ci#include <linux/slab.h>
228c2ecf20Sopenharmony_ci#include <linux/hid-roccat.h>
238c2ecf20Sopenharmony_ci#include "hid-ids.h"
248c2ecf20Sopenharmony_ci#include "hid-roccat-common.h"
258c2ecf20Sopenharmony_ci#include "hid-roccat-pyra.h"
268c2ecf20Sopenharmony_ci
278c2ecf20Sopenharmony_cistatic uint profile_numbers[5] = {0, 1, 2, 3, 4};
288c2ecf20Sopenharmony_ci
298c2ecf20Sopenharmony_ci/* pyra_class is used for creating sysfs attributes via roccat char device */
308c2ecf20Sopenharmony_cistatic struct class *pyra_class;
318c2ecf20Sopenharmony_ci
328c2ecf20Sopenharmony_cistatic void profile_activated(struct pyra_device *pyra,
338c2ecf20Sopenharmony_ci		unsigned int new_profile)
348c2ecf20Sopenharmony_ci{
358c2ecf20Sopenharmony_ci	if (new_profile >= ARRAY_SIZE(pyra->profile_settings))
368c2ecf20Sopenharmony_ci		return;
378c2ecf20Sopenharmony_ci	pyra->actual_profile = new_profile;
388c2ecf20Sopenharmony_ci	pyra->actual_cpi = pyra->profile_settings[pyra->actual_profile].y_cpi;
398c2ecf20Sopenharmony_ci}
408c2ecf20Sopenharmony_ci
418c2ecf20Sopenharmony_cistatic int pyra_send_control(struct usb_device *usb_dev, int value,
428c2ecf20Sopenharmony_ci		enum pyra_control_requests request)
438c2ecf20Sopenharmony_ci{
448c2ecf20Sopenharmony_ci	struct roccat_common2_control control;
458c2ecf20Sopenharmony_ci
468c2ecf20Sopenharmony_ci	if ((request == PYRA_CONTROL_REQUEST_PROFILE_SETTINGS ||
478c2ecf20Sopenharmony_ci			request == PYRA_CONTROL_REQUEST_PROFILE_BUTTONS) &&
488c2ecf20Sopenharmony_ci			(value < 0 || value > 4))
498c2ecf20Sopenharmony_ci		return -EINVAL;
508c2ecf20Sopenharmony_ci
518c2ecf20Sopenharmony_ci	control.command = ROCCAT_COMMON_COMMAND_CONTROL;
528c2ecf20Sopenharmony_ci	control.value = value;
538c2ecf20Sopenharmony_ci	control.request = request;
548c2ecf20Sopenharmony_ci
558c2ecf20Sopenharmony_ci	return roccat_common2_send(usb_dev, ROCCAT_COMMON_COMMAND_CONTROL,
568c2ecf20Sopenharmony_ci			&control, sizeof(struct roccat_common2_control));
578c2ecf20Sopenharmony_ci}
588c2ecf20Sopenharmony_ci
598c2ecf20Sopenharmony_cistatic int pyra_get_profile_settings(struct usb_device *usb_dev,
608c2ecf20Sopenharmony_ci		struct pyra_profile_settings *buf, int number)
618c2ecf20Sopenharmony_ci{
628c2ecf20Sopenharmony_ci	int retval;
638c2ecf20Sopenharmony_ci	retval = pyra_send_control(usb_dev, number,
648c2ecf20Sopenharmony_ci			PYRA_CONTROL_REQUEST_PROFILE_SETTINGS);
658c2ecf20Sopenharmony_ci	if (retval)
668c2ecf20Sopenharmony_ci		return retval;
678c2ecf20Sopenharmony_ci	return roccat_common2_receive(usb_dev, PYRA_COMMAND_PROFILE_SETTINGS,
688c2ecf20Sopenharmony_ci			buf, PYRA_SIZE_PROFILE_SETTINGS);
698c2ecf20Sopenharmony_ci}
708c2ecf20Sopenharmony_ci
718c2ecf20Sopenharmony_cistatic int pyra_get_settings(struct usb_device *usb_dev,
728c2ecf20Sopenharmony_ci		struct pyra_settings *buf)
738c2ecf20Sopenharmony_ci{
748c2ecf20Sopenharmony_ci	return roccat_common2_receive(usb_dev, PYRA_COMMAND_SETTINGS,
758c2ecf20Sopenharmony_ci			buf, PYRA_SIZE_SETTINGS);
768c2ecf20Sopenharmony_ci}
778c2ecf20Sopenharmony_ci
788c2ecf20Sopenharmony_cistatic int pyra_set_settings(struct usb_device *usb_dev,
798c2ecf20Sopenharmony_ci		struct pyra_settings const *settings)
808c2ecf20Sopenharmony_ci{
818c2ecf20Sopenharmony_ci	return roccat_common2_send_with_status(usb_dev,
828c2ecf20Sopenharmony_ci			PYRA_COMMAND_SETTINGS, settings,
838c2ecf20Sopenharmony_ci			PYRA_SIZE_SETTINGS);
848c2ecf20Sopenharmony_ci}
858c2ecf20Sopenharmony_ci
868c2ecf20Sopenharmony_cistatic ssize_t pyra_sysfs_read(struct file *fp, struct kobject *kobj,
878c2ecf20Sopenharmony_ci		char *buf, loff_t off, size_t count,
888c2ecf20Sopenharmony_ci		size_t real_size, uint command)
898c2ecf20Sopenharmony_ci{
908c2ecf20Sopenharmony_ci	struct device *dev = kobj_to_dev(kobj)->parent->parent;
918c2ecf20Sopenharmony_ci	struct pyra_device *pyra = hid_get_drvdata(dev_get_drvdata(dev));
928c2ecf20Sopenharmony_ci	struct usb_device *usb_dev = interface_to_usbdev(to_usb_interface(dev));
938c2ecf20Sopenharmony_ci	int retval;
948c2ecf20Sopenharmony_ci
958c2ecf20Sopenharmony_ci	if (off >= real_size)
968c2ecf20Sopenharmony_ci		return 0;
978c2ecf20Sopenharmony_ci
988c2ecf20Sopenharmony_ci	if (off != 0 || count != real_size)
998c2ecf20Sopenharmony_ci		return -EINVAL;
1008c2ecf20Sopenharmony_ci
1018c2ecf20Sopenharmony_ci	mutex_lock(&pyra->pyra_lock);
1028c2ecf20Sopenharmony_ci	retval = roccat_common2_receive(usb_dev, command, buf, real_size);
1038c2ecf20Sopenharmony_ci	mutex_unlock(&pyra->pyra_lock);
1048c2ecf20Sopenharmony_ci
1058c2ecf20Sopenharmony_ci	if (retval)
1068c2ecf20Sopenharmony_ci		return retval;
1078c2ecf20Sopenharmony_ci
1088c2ecf20Sopenharmony_ci	return real_size;
1098c2ecf20Sopenharmony_ci}
1108c2ecf20Sopenharmony_ci
1118c2ecf20Sopenharmony_cistatic ssize_t pyra_sysfs_write(struct file *fp, struct kobject *kobj,
1128c2ecf20Sopenharmony_ci		void const *buf, loff_t off, size_t count,
1138c2ecf20Sopenharmony_ci		size_t real_size, uint command)
1148c2ecf20Sopenharmony_ci{
1158c2ecf20Sopenharmony_ci	struct device *dev = kobj_to_dev(kobj)->parent->parent;
1168c2ecf20Sopenharmony_ci	struct pyra_device *pyra = hid_get_drvdata(dev_get_drvdata(dev));
1178c2ecf20Sopenharmony_ci	struct usb_device *usb_dev = interface_to_usbdev(to_usb_interface(dev));
1188c2ecf20Sopenharmony_ci	int retval;
1198c2ecf20Sopenharmony_ci
1208c2ecf20Sopenharmony_ci	if (off != 0 || count != real_size)
1218c2ecf20Sopenharmony_ci		return -EINVAL;
1228c2ecf20Sopenharmony_ci
1238c2ecf20Sopenharmony_ci	mutex_lock(&pyra->pyra_lock);
1248c2ecf20Sopenharmony_ci	retval = roccat_common2_send_with_status(usb_dev, command, (void *)buf, real_size);
1258c2ecf20Sopenharmony_ci	mutex_unlock(&pyra->pyra_lock);
1268c2ecf20Sopenharmony_ci
1278c2ecf20Sopenharmony_ci	if (retval)
1288c2ecf20Sopenharmony_ci		return retval;
1298c2ecf20Sopenharmony_ci
1308c2ecf20Sopenharmony_ci	return real_size;
1318c2ecf20Sopenharmony_ci}
1328c2ecf20Sopenharmony_ci
1338c2ecf20Sopenharmony_ci#define PYRA_SYSFS_W(thingy, THINGY) \
1348c2ecf20Sopenharmony_cistatic ssize_t pyra_sysfs_write_ ## thingy(struct file *fp, \
1358c2ecf20Sopenharmony_ci		struct kobject *kobj, struct bin_attribute *attr, char *buf, \
1368c2ecf20Sopenharmony_ci		loff_t off, size_t count) \
1378c2ecf20Sopenharmony_ci{ \
1388c2ecf20Sopenharmony_ci	return pyra_sysfs_write(fp, kobj, buf, off, count, \
1398c2ecf20Sopenharmony_ci			PYRA_SIZE_ ## THINGY, PYRA_COMMAND_ ## THINGY); \
1408c2ecf20Sopenharmony_ci}
1418c2ecf20Sopenharmony_ci
1428c2ecf20Sopenharmony_ci#define PYRA_SYSFS_R(thingy, THINGY) \
1438c2ecf20Sopenharmony_cistatic ssize_t pyra_sysfs_read_ ## thingy(struct file *fp, \
1448c2ecf20Sopenharmony_ci		struct kobject *kobj, struct bin_attribute *attr, char *buf, \
1458c2ecf20Sopenharmony_ci		loff_t off, size_t count) \
1468c2ecf20Sopenharmony_ci{ \
1478c2ecf20Sopenharmony_ci	return pyra_sysfs_read(fp, kobj, buf, off, count, \
1488c2ecf20Sopenharmony_ci			PYRA_SIZE_ ## THINGY, PYRA_COMMAND_ ## THINGY); \
1498c2ecf20Sopenharmony_ci}
1508c2ecf20Sopenharmony_ci
1518c2ecf20Sopenharmony_ci#define PYRA_SYSFS_RW(thingy, THINGY) \
1528c2ecf20Sopenharmony_ciPYRA_SYSFS_W(thingy, THINGY) \
1538c2ecf20Sopenharmony_ciPYRA_SYSFS_R(thingy, THINGY)
1548c2ecf20Sopenharmony_ci
1558c2ecf20Sopenharmony_ci#define PYRA_BIN_ATTRIBUTE_RW(thingy, THINGY) \
1568c2ecf20Sopenharmony_ciPYRA_SYSFS_RW(thingy, THINGY); \
1578c2ecf20Sopenharmony_cistatic struct bin_attribute bin_attr_##thingy = { \
1588c2ecf20Sopenharmony_ci	.attr = { .name = #thingy, .mode = 0660 }, \
1598c2ecf20Sopenharmony_ci	.size = PYRA_SIZE_ ## THINGY, \
1608c2ecf20Sopenharmony_ci	.read = pyra_sysfs_read_ ## thingy, \
1618c2ecf20Sopenharmony_ci	.write = pyra_sysfs_write_ ## thingy \
1628c2ecf20Sopenharmony_ci}
1638c2ecf20Sopenharmony_ci
1648c2ecf20Sopenharmony_ci#define PYRA_BIN_ATTRIBUTE_R(thingy, THINGY) \
1658c2ecf20Sopenharmony_ciPYRA_SYSFS_R(thingy, THINGY); \
1668c2ecf20Sopenharmony_cistatic struct bin_attribute bin_attr_##thingy = { \
1678c2ecf20Sopenharmony_ci	.attr = { .name = #thingy, .mode = 0440 }, \
1688c2ecf20Sopenharmony_ci	.size = PYRA_SIZE_ ## THINGY, \
1698c2ecf20Sopenharmony_ci	.read = pyra_sysfs_read_ ## thingy, \
1708c2ecf20Sopenharmony_ci}
1718c2ecf20Sopenharmony_ci
1728c2ecf20Sopenharmony_ci#define PYRA_BIN_ATTRIBUTE_W(thingy, THINGY) \
1738c2ecf20Sopenharmony_ciPYRA_SYSFS_W(thingy, THINGY); \
1748c2ecf20Sopenharmony_cistatic struct bin_attribute bin_attr_##thingy = { \
1758c2ecf20Sopenharmony_ci	.attr = { .name = #thingy, .mode = 0220 }, \
1768c2ecf20Sopenharmony_ci	.size = PYRA_SIZE_ ## THINGY, \
1778c2ecf20Sopenharmony_ci	.write = pyra_sysfs_write_ ## thingy \
1788c2ecf20Sopenharmony_ci}
1798c2ecf20Sopenharmony_ci
1808c2ecf20Sopenharmony_ciPYRA_BIN_ATTRIBUTE_W(control, CONTROL);
1818c2ecf20Sopenharmony_ciPYRA_BIN_ATTRIBUTE_RW(info, INFO);
1828c2ecf20Sopenharmony_ciPYRA_BIN_ATTRIBUTE_RW(profile_settings, PROFILE_SETTINGS);
1838c2ecf20Sopenharmony_ciPYRA_BIN_ATTRIBUTE_RW(profile_buttons, PROFILE_BUTTONS);
1848c2ecf20Sopenharmony_ci
1858c2ecf20Sopenharmony_cistatic ssize_t pyra_sysfs_read_profilex_settings(struct file *fp,
1868c2ecf20Sopenharmony_ci		struct kobject *kobj, struct bin_attribute *attr, char *buf,
1878c2ecf20Sopenharmony_ci		loff_t off, size_t count)
1888c2ecf20Sopenharmony_ci{
1898c2ecf20Sopenharmony_ci	struct device *dev = kobj_to_dev(kobj)->parent->parent;
1908c2ecf20Sopenharmony_ci	struct usb_device *usb_dev = interface_to_usbdev(to_usb_interface(dev));
1918c2ecf20Sopenharmony_ci	ssize_t retval;
1928c2ecf20Sopenharmony_ci
1938c2ecf20Sopenharmony_ci	retval = pyra_send_control(usb_dev, *(uint *)(attr->private),
1948c2ecf20Sopenharmony_ci			PYRA_CONTROL_REQUEST_PROFILE_SETTINGS);
1958c2ecf20Sopenharmony_ci	if (retval)
1968c2ecf20Sopenharmony_ci		return retval;
1978c2ecf20Sopenharmony_ci
1988c2ecf20Sopenharmony_ci	return pyra_sysfs_read(fp, kobj, buf, off, count,
1998c2ecf20Sopenharmony_ci			PYRA_SIZE_PROFILE_SETTINGS,
2008c2ecf20Sopenharmony_ci			PYRA_COMMAND_PROFILE_SETTINGS);
2018c2ecf20Sopenharmony_ci}
2028c2ecf20Sopenharmony_ci
2038c2ecf20Sopenharmony_cistatic ssize_t pyra_sysfs_read_profilex_buttons(struct file *fp,
2048c2ecf20Sopenharmony_ci		struct kobject *kobj, struct bin_attribute *attr, char *buf,
2058c2ecf20Sopenharmony_ci		loff_t off, size_t count)
2068c2ecf20Sopenharmony_ci{
2078c2ecf20Sopenharmony_ci	struct device *dev = kobj_to_dev(kobj)->parent->parent;
2088c2ecf20Sopenharmony_ci	struct usb_device *usb_dev = interface_to_usbdev(to_usb_interface(dev));
2098c2ecf20Sopenharmony_ci	ssize_t retval;
2108c2ecf20Sopenharmony_ci
2118c2ecf20Sopenharmony_ci	retval = pyra_send_control(usb_dev, *(uint *)(attr->private),
2128c2ecf20Sopenharmony_ci			PYRA_CONTROL_REQUEST_PROFILE_BUTTONS);
2138c2ecf20Sopenharmony_ci	if (retval)
2148c2ecf20Sopenharmony_ci		return retval;
2158c2ecf20Sopenharmony_ci
2168c2ecf20Sopenharmony_ci	return pyra_sysfs_read(fp, kobj, buf, off, count,
2178c2ecf20Sopenharmony_ci			PYRA_SIZE_PROFILE_BUTTONS,
2188c2ecf20Sopenharmony_ci			PYRA_COMMAND_PROFILE_BUTTONS);
2198c2ecf20Sopenharmony_ci}
2208c2ecf20Sopenharmony_ci
2218c2ecf20Sopenharmony_ci#define PROFILE_ATTR(number)						\
2228c2ecf20Sopenharmony_cistatic struct bin_attribute bin_attr_profile##number##_settings = {	\
2238c2ecf20Sopenharmony_ci	.attr = { .name = "profile" #number "_settings", .mode = 0440 },	\
2248c2ecf20Sopenharmony_ci	.size = PYRA_SIZE_PROFILE_SETTINGS,				\
2258c2ecf20Sopenharmony_ci	.read = pyra_sysfs_read_profilex_settings,			\
2268c2ecf20Sopenharmony_ci	.private = &profile_numbers[number-1],				\
2278c2ecf20Sopenharmony_ci};									\
2288c2ecf20Sopenharmony_cistatic struct bin_attribute bin_attr_profile##number##_buttons = {	\
2298c2ecf20Sopenharmony_ci	.attr = { .name = "profile" #number "_buttons", .mode = 0440 },	\
2308c2ecf20Sopenharmony_ci	.size = PYRA_SIZE_PROFILE_BUTTONS,				\
2318c2ecf20Sopenharmony_ci	.read = pyra_sysfs_read_profilex_buttons,			\
2328c2ecf20Sopenharmony_ci	.private = &profile_numbers[number-1],				\
2338c2ecf20Sopenharmony_ci};
2348c2ecf20Sopenharmony_ciPROFILE_ATTR(1);
2358c2ecf20Sopenharmony_ciPROFILE_ATTR(2);
2368c2ecf20Sopenharmony_ciPROFILE_ATTR(3);
2378c2ecf20Sopenharmony_ciPROFILE_ATTR(4);
2388c2ecf20Sopenharmony_ciPROFILE_ATTR(5);
2398c2ecf20Sopenharmony_ci
2408c2ecf20Sopenharmony_cistatic ssize_t pyra_sysfs_write_settings(struct file *fp,
2418c2ecf20Sopenharmony_ci		struct kobject *kobj, struct bin_attribute *attr, char *buf,
2428c2ecf20Sopenharmony_ci		loff_t off, size_t count)
2438c2ecf20Sopenharmony_ci{
2448c2ecf20Sopenharmony_ci	struct device *dev = kobj_to_dev(kobj)->parent->parent;
2458c2ecf20Sopenharmony_ci	struct pyra_device *pyra = hid_get_drvdata(dev_get_drvdata(dev));
2468c2ecf20Sopenharmony_ci	struct usb_device *usb_dev = interface_to_usbdev(to_usb_interface(dev));
2478c2ecf20Sopenharmony_ci	int retval = 0;
2488c2ecf20Sopenharmony_ci	struct pyra_roccat_report roccat_report;
2498c2ecf20Sopenharmony_ci	struct pyra_settings const *settings;
2508c2ecf20Sopenharmony_ci
2518c2ecf20Sopenharmony_ci	if (off != 0 || count != PYRA_SIZE_SETTINGS)
2528c2ecf20Sopenharmony_ci		return -EINVAL;
2538c2ecf20Sopenharmony_ci
2548c2ecf20Sopenharmony_ci	settings = (struct pyra_settings const *)buf;
2558c2ecf20Sopenharmony_ci	if (settings->startup_profile >= ARRAY_SIZE(pyra->profile_settings))
2568c2ecf20Sopenharmony_ci		return -EINVAL;
2578c2ecf20Sopenharmony_ci
2588c2ecf20Sopenharmony_ci	mutex_lock(&pyra->pyra_lock);
2598c2ecf20Sopenharmony_ci
2608c2ecf20Sopenharmony_ci	retval = pyra_set_settings(usb_dev, settings);
2618c2ecf20Sopenharmony_ci	if (retval) {
2628c2ecf20Sopenharmony_ci		mutex_unlock(&pyra->pyra_lock);
2638c2ecf20Sopenharmony_ci		return retval;
2648c2ecf20Sopenharmony_ci	}
2658c2ecf20Sopenharmony_ci
2668c2ecf20Sopenharmony_ci	profile_activated(pyra, settings->startup_profile);
2678c2ecf20Sopenharmony_ci
2688c2ecf20Sopenharmony_ci	roccat_report.type = PYRA_MOUSE_EVENT_BUTTON_TYPE_PROFILE_2;
2698c2ecf20Sopenharmony_ci	roccat_report.value = settings->startup_profile + 1;
2708c2ecf20Sopenharmony_ci	roccat_report.key = 0;
2718c2ecf20Sopenharmony_ci	roccat_report_event(pyra->chrdev_minor,
2728c2ecf20Sopenharmony_ci			(uint8_t const *)&roccat_report);
2738c2ecf20Sopenharmony_ci
2748c2ecf20Sopenharmony_ci	mutex_unlock(&pyra->pyra_lock);
2758c2ecf20Sopenharmony_ci	return PYRA_SIZE_SETTINGS;
2768c2ecf20Sopenharmony_ci}
2778c2ecf20Sopenharmony_ci
2788c2ecf20Sopenharmony_ciPYRA_SYSFS_R(settings, SETTINGS);
2798c2ecf20Sopenharmony_cistatic struct bin_attribute bin_attr_settings =
2808c2ecf20Sopenharmony_ci	__BIN_ATTR(settings, (S_IWUSR | S_IRUGO),
2818c2ecf20Sopenharmony_ci		   pyra_sysfs_read_settings, pyra_sysfs_write_settings,
2828c2ecf20Sopenharmony_ci		   PYRA_SIZE_SETTINGS);
2838c2ecf20Sopenharmony_ci
2848c2ecf20Sopenharmony_cistatic ssize_t pyra_sysfs_show_actual_cpi(struct device *dev,
2858c2ecf20Sopenharmony_ci		struct device_attribute *attr, char *buf)
2868c2ecf20Sopenharmony_ci{
2878c2ecf20Sopenharmony_ci	struct pyra_device *pyra =
2888c2ecf20Sopenharmony_ci			hid_get_drvdata(dev_get_drvdata(dev->parent->parent));
2898c2ecf20Sopenharmony_ci	return snprintf(buf, PAGE_SIZE, "%d\n", pyra->actual_cpi);
2908c2ecf20Sopenharmony_ci}
2918c2ecf20Sopenharmony_cistatic DEVICE_ATTR(actual_cpi, 0440, pyra_sysfs_show_actual_cpi, NULL);
2928c2ecf20Sopenharmony_ci
2938c2ecf20Sopenharmony_cistatic ssize_t pyra_sysfs_show_actual_profile(struct device *dev,
2948c2ecf20Sopenharmony_ci		struct device_attribute *attr, char *buf)
2958c2ecf20Sopenharmony_ci{
2968c2ecf20Sopenharmony_ci	struct pyra_device *pyra =
2978c2ecf20Sopenharmony_ci			hid_get_drvdata(dev_get_drvdata(dev->parent->parent));
2988c2ecf20Sopenharmony_ci	struct usb_device *usb_dev = interface_to_usbdev(to_usb_interface(dev));
2998c2ecf20Sopenharmony_ci	struct pyra_settings settings;
3008c2ecf20Sopenharmony_ci
3018c2ecf20Sopenharmony_ci	mutex_lock(&pyra->pyra_lock);
3028c2ecf20Sopenharmony_ci	roccat_common2_receive(usb_dev, PYRA_COMMAND_SETTINGS,
3038c2ecf20Sopenharmony_ci			&settings, PYRA_SIZE_SETTINGS);
3048c2ecf20Sopenharmony_ci	mutex_unlock(&pyra->pyra_lock);
3058c2ecf20Sopenharmony_ci
3068c2ecf20Sopenharmony_ci	return snprintf(buf, PAGE_SIZE, "%d\n", settings.startup_profile);
3078c2ecf20Sopenharmony_ci}
3088c2ecf20Sopenharmony_cistatic DEVICE_ATTR(actual_profile, 0440, pyra_sysfs_show_actual_profile, NULL);
3098c2ecf20Sopenharmony_cistatic DEVICE_ATTR(startup_profile, 0440, pyra_sysfs_show_actual_profile, NULL);
3108c2ecf20Sopenharmony_ci
3118c2ecf20Sopenharmony_cistatic ssize_t pyra_sysfs_show_firmware_version(struct device *dev,
3128c2ecf20Sopenharmony_ci		struct device_attribute *attr, char *buf)
3138c2ecf20Sopenharmony_ci{
3148c2ecf20Sopenharmony_ci	struct pyra_device *pyra;
3158c2ecf20Sopenharmony_ci	struct usb_device *usb_dev;
3168c2ecf20Sopenharmony_ci	struct pyra_info info;
3178c2ecf20Sopenharmony_ci
3188c2ecf20Sopenharmony_ci	dev = dev->parent->parent;
3198c2ecf20Sopenharmony_ci	pyra = hid_get_drvdata(dev_get_drvdata(dev));
3208c2ecf20Sopenharmony_ci	usb_dev = interface_to_usbdev(to_usb_interface(dev));
3218c2ecf20Sopenharmony_ci
3228c2ecf20Sopenharmony_ci	mutex_lock(&pyra->pyra_lock);
3238c2ecf20Sopenharmony_ci	roccat_common2_receive(usb_dev, PYRA_COMMAND_INFO,
3248c2ecf20Sopenharmony_ci			&info, PYRA_SIZE_INFO);
3258c2ecf20Sopenharmony_ci	mutex_unlock(&pyra->pyra_lock);
3268c2ecf20Sopenharmony_ci
3278c2ecf20Sopenharmony_ci	return snprintf(buf, PAGE_SIZE, "%d\n", info.firmware_version);
3288c2ecf20Sopenharmony_ci}
3298c2ecf20Sopenharmony_cistatic DEVICE_ATTR(firmware_version, 0440, pyra_sysfs_show_firmware_version,
3308c2ecf20Sopenharmony_ci		   NULL);
3318c2ecf20Sopenharmony_ci
3328c2ecf20Sopenharmony_cistatic struct attribute *pyra_attrs[] = {
3338c2ecf20Sopenharmony_ci	&dev_attr_actual_cpi.attr,
3348c2ecf20Sopenharmony_ci	&dev_attr_actual_profile.attr,
3358c2ecf20Sopenharmony_ci	&dev_attr_firmware_version.attr,
3368c2ecf20Sopenharmony_ci	&dev_attr_startup_profile.attr,
3378c2ecf20Sopenharmony_ci	NULL,
3388c2ecf20Sopenharmony_ci};
3398c2ecf20Sopenharmony_ci
3408c2ecf20Sopenharmony_cistatic struct bin_attribute *pyra_bin_attributes[] = {
3418c2ecf20Sopenharmony_ci	&bin_attr_control,
3428c2ecf20Sopenharmony_ci	&bin_attr_info,
3438c2ecf20Sopenharmony_ci	&bin_attr_profile_settings,
3448c2ecf20Sopenharmony_ci	&bin_attr_profile_buttons,
3458c2ecf20Sopenharmony_ci	&bin_attr_settings,
3468c2ecf20Sopenharmony_ci	&bin_attr_profile1_settings,
3478c2ecf20Sopenharmony_ci	&bin_attr_profile2_settings,
3488c2ecf20Sopenharmony_ci	&bin_attr_profile3_settings,
3498c2ecf20Sopenharmony_ci	&bin_attr_profile4_settings,
3508c2ecf20Sopenharmony_ci	&bin_attr_profile5_settings,
3518c2ecf20Sopenharmony_ci	&bin_attr_profile1_buttons,
3528c2ecf20Sopenharmony_ci	&bin_attr_profile2_buttons,
3538c2ecf20Sopenharmony_ci	&bin_attr_profile3_buttons,
3548c2ecf20Sopenharmony_ci	&bin_attr_profile4_buttons,
3558c2ecf20Sopenharmony_ci	&bin_attr_profile5_buttons,
3568c2ecf20Sopenharmony_ci	NULL,
3578c2ecf20Sopenharmony_ci};
3588c2ecf20Sopenharmony_ci
3598c2ecf20Sopenharmony_cistatic const struct attribute_group pyra_group = {
3608c2ecf20Sopenharmony_ci	.attrs = pyra_attrs,
3618c2ecf20Sopenharmony_ci	.bin_attrs = pyra_bin_attributes,
3628c2ecf20Sopenharmony_ci};
3638c2ecf20Sopenharmony_ci
3648c2ecf20Sopenharmony_cistatic const struct attribute_group *pyra_groups[] = {
3658c2ecf20Sopenharmony_ci	&pyra_group,
3668c2ecf20Sopenharmony_ci	NULL,
3678c2ecf20Sopenharmony_ci};
3688c2ecf20Sopenharmony_ci
3698c2ecf20Sopenharmony_cistatic int pyra_init_pyra_device_struct(struct usb_device *usb_dev,
3708c2ecf20Sopenharmony_ci		struct pyra_device *pyra)
3718c2ecf20Sopenharmony_ci{
3728c2ecf20Sopenharmony_ci	struct pyra_settings settings;
3738c2ecf20Sopenharmony_ci	int retval, i;
3748c2ecf20Sopenharmony_ci
3758c2ecf20Sopenharmony_ci	mutex_init(&pyra->pyra_lock);
3768c2ecf20Sopenharmony_ci
3778c2ecf20Sopenharmony_ci	retval = pyra_get_settings(usb_dev, &settings);
3788c2ecf20Sopenharmony_ci	if (retval)
3798c2ecf20Sopenharmony_ci		return retval;
3808c2ecf20Sopenharmony_ci
3818c2ecf20Sopenharmony_ci	for (i = 0; i < 5; ++i) {
3828c2ecf20Sopenharmony_ci		retval = pyra_get_profile_settings(usb_dev,
3838c2ecf20Sopenharmony_ci				&pyra->profile_settings[i], i);
3848c2ecf20Sopenharmony_ci		if (retval)
3858c2ecf20Sopenharmony_ci			return retval;
3868c2ecf20Sopenharmony_ci	}
3878c2ecf20Sopenharmony_ci
3888c2ecf20Sopenharmony_ci	profile_activated(pyra, settings.startup_profile);
3898c2ecf20Sopenharmony_ci
3908c2ecf20Sopenharmony_ci	return 0;
3918c2ecf20Sopenharmony_ci}
3928c2ecf20Sopenharmony_ci
3938c2ecf20Sopenharmony_cistatic int pyra_init_specials(struct hid_device *hdev)
3948c2ecf20Sopenharmony_ci{
3958c2ecf20Sopenharmony_ci	struct usb_interface *intf = to_usb_interface(hdev->dev.parent);
3968c2ecf20Sopenharmony_ci	struct usb_device *usb_dev = interface_to_usbdev(intf);
3978c2ecf20Sopenharmony_ci	struct pyra_device *pyra;
3988c2ecf20Sopenharmony_ci	int retval;
3998c2ecf20Sopenharmony_ci
4008c2ecf20Sopenharmony_ci	if (intf->cur_altsetting->desc.bInterfaceProtocol
4018c2ecf20Sopenharmony_ci			== USB_INTERFACE_PROTOCOL_MOUSE) {
4028c2ecf20Sopenharmony_ci
4038c2ecf20Sopenharmony_ci		pyra = kzalloc(sizeof(*pyra), GFP_KERNEL);
4048c2ecf20Sopenharmony_ci		if (!pyra) {
4058c2ecf20Sopenharmony_ci			hid_err(hdev, "can't alloc device descriptor\n");
4068c2ecf20Sopenharmony_ci			return -ENOMEM;
4078c2ecf20Sopenharmony_ci		}
4088c2ecf20Sopenharmony_ci		hid_set_drvdata(hdev, pyra);
4098c2ecf20Sopenharmony_ci
4108c2ecf20Sopenharmony_ci		retval = pyra_init_pyra_device_struct(usb_dev, pyra);
4118c2ecf20Sopenharmony_ci		if (retval) {
4128c2ecf20Sopenharmony_ci			hid_err(hdev, "couldn't init struct pyra_device\n");
4138c2ecf20Sopenharmony_ci			goto exit_free;
4148c2ecf20Sopenharmony_ci		}
4158c2ecf20Sopenharmony_ci
4168c2ecf20Sopenharmony_ci		retval = roccat_connect(pyra_class, hdev,
4178c2ecf20Sopenharmony_ci				sizeof(struct pyra_roccat_report));
4188c2ecf20Sopenharmony_ci		if (retval < 0) {
4198c2ecf20Sopenharmony_ci			hid_err(hdev, "couldn't init char dev\n");
4208c2ecf20Sopenharmony_ci		} else {
4218c2ecf20Sopenharmony_ci			pyra->chrdev_minor = retval;
4228c2ecf20Sopenharmony_ci			pyra->roccat_claimed = 1;
4238c2ecf20Sopenharmony_ci		}
4248c2ecf20Sopenharmony_ci	} else {
4258c2ecf20Sopenharmony_ci		hid_set_drvdata(hdev, NULL);
4268c2ecf20Sopenharmony_ci	}
4278c2ecf20Sopenharmony_ci
4288c2ecf20Sopenharmony_ci	return 0;
4298c2ecf20Sopenharmony_ciexit_free:
4308c2ecf20Sopenharmony_ci	kfree(pyra);
4318c2ecf20Sopenharmony_ci	return retval;
4328c2ecf20Sopenharmony_ci}
4338c2ecf20Sopenharmony_ci
4348c2ecf20Sopenharmony_cistatic void pyra_remove_specials(struct hid_device *hdev)
4358c2ecf20Sopenharmony_ci{
4368c2ecf20Sopenharmony_ci	struct usb_interface *intf = to_usb_interface(hdev->dev.parent);
4378c2ecf20Sopenharmony_ci	struct pyra_device *pyra;
4388c2ecf20Sopenharmony_ci
4398c2ecf20Sopenharmony_ci	if (intf->cur_altsetting->desc.bInterfaceProtocol
4408c2ecf20Sopenharmony_ci			== USB_INTERFACE_PROTOCOL_MOUSE) {
4418c2ecf20Sopenharmony_ci		pyra = hid_get_drvdata(hdev);
4428c2ecf20Sopenharmony_ci		if (pyra->roccat_claimed)
4438c2ecf20Sopenharmony_ci			roccat_disconnect(pyra->chrdev_minor);
4448c2ecf20Sopenharmony_ci		kfree(hid_get_drvdata(hdev));
4458c2ecf20Sopenharmony_ci	}
4468c2ecf20Sopenharmony_ci}
4478c2ecf20Sopenharmony_ci
4488c2ecf20Sopenharmony_cistatic int pyra_probe(struct hid_device *hdev, const struct hid_device_id *id)
4498c2ecf20Sopenharmony_ci{
4508c2ecf20Sopenharmony_ci	int retval;
4518c2ecf20Sopenharmony_ci
4528c2ecf20Sopenharmony_ci	if (!hid_is_usb(hdev))
4538c2ecf20Sopenharmony_ci		return -EINVAL;
4548c2ecf20Sopenharmony_ci
4558c2ecf20Sopenharmony_ci	retval = hid_parse(hdev);
4568c2ecf20Sopenharmony_ci	if (retval) {
4578c2ecf20Sopenharmony_ci		hid_err(hdev, "parse failed\n");
4588c2ecf20Sopenharmony_ci		goto exit;
4598c2ecf20Sopenharmony_ci	}
4608c2ecf20Sopenharmony_ci
4618c2ecf20Sopenharmony_ci	retval = hid_hw_start(hdev, HID_CONNECT_DEFAULT);
4628c2ecf20Sopenharmony_ci	if (retval) {
4638c2ecf20Sopenharmony_ci		hid_err(hdev, "hw start failed\n");
4648c2ecf20Sopenharmony_ci		goto exit;
4658c2ecf20Sopenharmony_ci	}
4668c2ecf20Sopenharmony_ci
4678c2ecf20Sopenharmony_ci	retval = pyra_init_specials(hdev);
4688c2ecf20Sopenharmony_ci	if (retval) {
4698c2ecf20Sopenharmony_ci		hid_err(hdev, "couldn't install mouse\n");
4708c2ecf20Sopenharmony_ci		goto exit_stop;
4718c2ecf20Sopenharmony_ci	}
4728c2ecf20Sopenharmony_ci	return 0;
4738c2ecf20Sopenharmony_ci
4748c2ecf20Sopenharmony_ciexit_stop:
4758c2ecf20Sopenharmony_ci	hid_hw_stop(hdev);
4768c2ecf20Sopenharmony_ciexit:
4778c2ecf20Sopenharmony_ci	return retval;
4788c2ecf20Sopenharmony_ci}
4798c2ecf20Sopenharmony_ci
4808c2ecf20Sopenharmony_cistatic void pyra_remove(struct hid_device *hdev)
4818c2ecf20Sopenharmony_ci{
4828c2ecf20Sopenharmony_ci	pyra_remove_specials(hdev);
4838c2ecf20Sopenharmony_ci	hid_hw_stop(hdev);
4848c2ecf20Sopenharmony_ci}
4858c2ecf20Sopenharmony_ci
4868c2ecf20Sopenharmony_cistatic void pyra_keep_values_up_to_date(struct pyra_device *pyra,
4878c2ecf20Sopenharmony_ci		u8 const *data)
4888c2ecf20Sopenharmony_ci{
4898c2ecf20Sopenharmony_ci	struct pyra_mouse_event_button const *button_event;
4908c2ecf20Sopenharmony_ci
4918c2ecf20Sopenharmony_ci	switch (data[0]) {
4928c2ecf20Sopenharmony_ci	case PYRA_MOUSE_REPORT_NUMBER_BUTTON:
4938c2ecf20Sopenharmony_ci		button_event = (struct pyra_mouse_event_button const *)data;
4948c2ecf20Sopenharmony_ci		switch (button_event->type) {
4958c2ecf20Sopenharmony_ci		case PYRA_MOUSE_EVENT_BUTTON_TYPE_PROFILE_2:
4968c2ecf20Sopenharmony_ci			profile_activated(pyra, button_event->data1 - 1);
4978c2ecf20Sopenharmony_ci			break;
4988c2ecf20Sopenharmony_ci		case PYRA_MOUSE_EVENT_BUTTON_TYPE_CPI:
4998c2ecf20Sopenharmony_ci			pyra->actual_cpi = button_event->data1;
5008c2ecf20Sopenharmony_ci			break;
5018c2ecf20Sopenharmony_ci		}
5028c2ecf20Sopenharmony_ci		break;
5038c2ecf20Sopenharmony_ci	}
5048c2ecf20Sopenharmony_ci}
5058c2ecf20Sopenharmony_ci
5068c2ecf20Sopenharmony_cistatic void pyra_report_to_chrdev(struct pyra_device const *pyra,
5078c2ecf20Sopenharmony_ci		u8 const *data)
5088c2ecf20Sopenharmony_ci{
5098c2ecf20Sopenharmony_ci	struct pyra_roccat_report roccat_report;
5108c2ecf20Sopenharmony_ci	struct pyra_mouse_event_button const *button_event;
5118c2ecf20Sopenharmony_ci
5128c2ecf20Sopenharmony_ci	if (data[0] != PYRA_MOUSE_REPORT_NUMBER_BUTTON)
5138c2ecf20Sopenharmony_ci		return;
5148c2ecf20Sopenharmony_ci
5158c2ecf20Sopenharmony_ci	button_event = (struct pyra_mouse_event_button const *)data;
5168c2ecf20Sopenharmony_ci
5178c2ecf20Sopenharmony_ci	switch (button_event->type) {
5188c2ecf20Sopenharmony_ci	case PYRA_MOUSE_EVENT_BUTTON_TYPE_PROFILE_2:
5198c2ecf20Sopenharmony_ci	case PYRA_MOUSE_EVENT_BUTTON_TYPE_CPI:
5208c2ecf20Sopenharmony_ci		roccat_report.type = button_event->type;
5218c2ecf20Sopenharmony_ci		roccat_report.value = button_event->data1;
5228c2ecf20Sopenharmony_ci		roccat_report.key = 0;
5238c2ecf20Sopenharmony_ci		roccat_report_event(pyra->chrdev_minor,
5248c2ecf20Sopenharmony_ci				(uint8_t const *)&roccat_report);
5258c2ecf20Sopenharmony_ci		break;
5268c2ecf20Sopenharmony_ci	case PYRA_MOUSE_EVENT_BUTTON_TYPE_MACRO:
5278c2ecf20Sopenharmony_ci	case PYRA_MOUSE_EVENT_BUTTON_TYPE_SHORTCUT:
5288c2ecf20Sopenharmony_ci	case PYRA_MOUSE_EVENT_BUTTON_TYPE_QUICKLAUNCH:
5298c2ecf20Sopenharmony_ci		if (button_event->data2 == PYRA_MOUSE_EVENT_BUTTON_PRESS) {
5308c2ecf20Sopenharmony_ci			roccat_report.type = button_event->type;
5318c2ecf20Sopenharmony_ci			roccat_report.key = button_event->data1;
5328c2ecf20Sopenharmony_ci			/*
5338c2ecf20Sopenharmony_ci			 * pyra reports profile numbers with range 1-5.
5348c2ecf20Sopenharmony_ci			 * Keeping this behaviour.
5358c2ecf20Sopenharmony_ci			 */
5368c2ecf20Sopenharmony_ci			roccat_report.value = pyra->actual_profile + 1;
5378c2ecf20Sopenharmony_ci			roccat_report_event(pyra->chrdev_minor,
5388c2ecf20Sopenharmony_ci					(uint8_t const *)&roccat_report);
5398c2ecf20Sopenharmony_ci		}
5408c2ecf20Sopenharmony_ci		break;
5418c2ecf20Sopenharmony_ci	}
5428c2ecf20Sopenharmony_ci}
5438c2ecf20Sopenharmony_ci
5448c2ecf20Sopenharmony_cistatic int pyra_raw_event(struct hid_device *hdev, struct hid_report *report,
5458c2ecf20Sopenharmony_ci		u8 *data, int size)
5468c2ecf20Sopenharmony_ci{
5478c2ecf20Sopenharmony_ci	struct usb_interface *intf = to_usb_interface(hdev->dev.parent);
5488c2ecf20Sopenharmony_ci	struct pyra_device *pyra = hid_get_drvdata(hdev);
5498c2ecf20Sopenharmony_ci
5508c2ecf20Sopenharmony_ci	if (intf->cur_altsetting->desc.bInterfaceProtocol
5518c2ecf20Sopenharmony_ci			!= USB_INTERFACE_PROTOCOL_MOUSE)
5528c2ecf20Sopenharmony_ci		return 0;
5538c2ecf20Sopenharmony_ci
5548c2ecf20Sopenharmony_ci	if (pyra == NULL)
5558c2ecf20Sopenharmony_ci		return 0;
5568c2ecf20Sopenharmony_ci
5578c2ecf20Sopenharmony_ci	pyra_keep_values_up_to_date(pyra, data);
5588c2ecf20Sopenharmony_ci
5598c2ecf20Sopenharmony_ci	if (pyra->roccat_claimed)
5608c2ecf20Sopenharmony_ci		pyra_report_to_chrdev(pyra, data);
5618c2ecf20Sopenharmony_ci
5628c2ecf20Sopenharmony_ci	return 0;
5638c2ecf20Sopenharmony_ci}
5648c2ecf20Sopenharmony_ci
5658c2ecf20Sopenharmony_cistatic const struct hid_device_id pyra_devices[] = {
5668c2ecf20Sopenharmony_ci	{ HID_USB_DEVICE(USB_VENDOR_ID_ROCCAT,
5678c2ecf20Sopenharmony_ci			USB_DEVICE_ID_ROCCAT_PYRA_WIRED) },
5688c2ecf20Sopenharmony_ci	{ HID_USB_DEVICE(USB_VENDOR_ID_ROCCAT,
5698c2ecf20Sopenharmony_ci			USB_DEVICE_ID_ROCCAT_PYRA_WIRELESS) },
5708c2ecf20Sopenharmony_ci	{ }
5718c2ecf20Sopenharmony_ci};
5728c2ecf20Sopenharmony_ci
5738c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(hid, pyra_devices);
5748c2ecf20Sopenharmony_ci
5758c2ecf20Sopenharmony_cistatic struct hid_driver pyra_driver = {
5768c2ecf20Sopenharmony_ci		.name = "pyra",
5778c2ecf20Sopenharmony_ci		.id_table = pyra_devices,
5788c2ecf20Sopenharmony_ci		.probe = pyra_probe,
5798c2ecf20Sopenharmony_ci		.remove = pyra_remove,
5808c2ecf20Sopenharmony_ci		.raw_event = pyra_raw_event
5818c2ecf20Sopenharmony_ci};
5828c2ecf20Sopenharmony_ci
5838c2ecf20Sopenharmony_cistatic int __init pyra_init(void)
5848c2ecf20Sopenharmony_ci{
5858c2ecf20Sopenharmony_ci	int retval;
5868c2ecf20Sopenharmony_ci
5878c2ecf20Sopenharmony_ci	/* class name has to be same as driver name */
5888c2ecf20Sopenharmony_ci	pyra_class = class_create(THIS_MODULE, "pyra");
5898c2ecf20Sopenharmony_ci	if (IS_ERR(pyra_class))
5908c2ecf20Sopenharmony_ci		return PTR_ERR(pyra_class);
5918c2ecf20Sopenharmony_ci	pyra_class->dev_groups = pyra_groups;
5928c2ecf20Sopenharmony_ci
5938c2ecf20Sopenharmony_ci	retval = hid_register_driver(&pyra_driver);
5948c2ecf20Sopenharmony_ci	if (retval)
5958c2ecf20Sopenharmony_ci		class_destroy(pyra_class);
5968c2ecf20Sopenharmony_ci	return retval;
5978c2ecf20Sopenharmony_ci}
5988c2ecf20Sopenharmony_ci
5998c2ecf20Sopenharmony_cistatic void __exit pyra_exit(void)
6008c2ecf20Sopenharmony_ci{
6018c2ecf20Sopenharmony_ci	hid_unregister_driver(&pyra_driver);
6028c2ecf20Sopenharmony_ci	class_destroy(pyra_class);
6038c2ecf20Sopenharmony_ci}
6048c2ecf20Sopenharmony_ci
6058c2ecf20Sopenharmony_cimodule_init(pyra_init);
6068c2ecf20Sopenharmony_cimodule_exit(pyra_exit);
6078c2ecf20Sopenharmony_ci
6088c2ecf20Sopenharmony_ciMODULE_AUTHOR("Stefan Achatz");
6098c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("USB Roccat Pyra driver");
6108c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2");
611