162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Roccat Pyra driver for Linux
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Copyright (c) 2010 Stefan Achatz <erazor_de@users.sourceforge.net>
662306a36Sopenharmony_ci */
762306a36Sopenharmony_ci
862306a36Sopenharmony_ci/*
962306a36Sopenharmony_ci */
1062306a36Sopenharmony_ci
1162306a36Sopenharmony_ci/*
1262306a36Sopenharmony_ci * Roccat Pyra is a mobile gamer mouse which comes in wired and wireless
1362306a36Sopenharmony_ci * variant. Wireless variant is not tested.
1462306a36Sopenharmony_ci * Userland tools can be found at http://sourceforge.net/projects/roccat
1562306a36Sopenharmony_ci */
1662306a36Sopenharmony_ci
1762306a36Sopenharmony_ci#include <linux/device.h>
1862306a36Sopenharmony_ci#include <linux/input.h>
1962306a36Sopenharmony_ci#include <linux/hid.h>
2062306a36Sopenharmony_ci#include <linux/module.h>
2162306a36Sopenharmony_ci#include <linux/slab.h>
2262306a36Sopenharmony_ci#include <linux/hid-roccat.h>
2362306a36Sopenharmony_ci#include "hid-ids.h"
2462306a36Sopenharmony_ci#include "hid-roccat-common.h"
2562306a36Sopenharmony_ci#include "hid-roccat-pyra.h"
2662306a36Sopenharmony_ci
2762306a36Sopenharmony_cistatic uint profile_numbers[5] = {0, 1, 2, 3, 4};
2862306a36Sopenharmony_ci
2962306a36Sopenharmony_cistatic void profile_activated(struct pyra_device *pyra,
3062306a36Sopenharmony_ci		unsigned int new_profile)
3162306a36Sopenharmony_ci{
3262306a36Sopenharmony_ci	if (new_profile >= ARRAY_SIZE(pyra->profile_settings))
3362306a36Sopenharmony_ci		return;
3462306a36Sopenharmony_ci	pyra->actual_profile = new_profile;
3562306a36Sopenharmony_ci	pyra->actual_cpi = pyra->profile_settings[pyra->actual_profile].y_cpi;
3662306a36Sopenharmony_ci}
3762306a36Sopenharmony_ci
3862306a36Sopenharmony_cistatic int pyra_send_control(struct usb_device *usb_dev, int value,
3962306a36Sopenharmony_ci		enum pyra_control_requests request)
4062306a36Sopenharmony_ci{
4162306a36Sopenharmony_ci	struct roccat_common2_control control;
4262306a36Sopenharmony_ci
4362306a36Sopenharmony_ci	if ((request == PYRA_CONTROL_REQUEST_PROFILE_SETTINGS ||
4462306a36Sopenharmony_ci			request == PYRA_CONTROL_REQUEST_PROFILE_BUTTONS) &&
4562306a36Sopenharmony_ci			(value < 0 || value > 4))
4662306a36Sopenharmony_ci		return -EINVAL;
4762306a36Sopenharmony_ci
4862306a36Sopenharmony_ci	control.command = ROCCAT_COMMON_COMMAND_CONTROL;
4962306a36Sopenharmony_ci	control.value = value;
5062306a36Sopenharmony_ci	control.request = request;
5162306a36Sopenharmony_ci
5262306a36Sopenharmony_ci	return roccat_common2_send(usb_dev, ROCCAT_COMMON_COMMAND_CONTROL,
5362306a36Sopenharmony_ci			&control, sizeof(struct roccat_common2_control));
5462306a36Sopenharmony_ci}
5562306a36Sopenharmony_ci
5662306a36Sopenharmony_cistatic int pyra_get_profile_settings(struct usb_device *usb_dev,
5762306a36Sopenharmony_ci		struct pyra_profile_settings *buf, int number)
5862306a36Sopenharmony_ci{
5962306a36Sopenharmony_ci	int retval;
6062306a36Sopenharmony_ci	retval = pyra_send_control(usb_dev, number,
6162306a36Sopenharmony_ci			PYRA_CONTROL_REQUEST_PROFILE_SETTINGS);
6262306a36Sopenharmony_ci	if (retval)
6362306a36Sopenharmony_ci		return retval;
6462306a36Sopenharmony_ci	return roccat_common2_receive(usb_dev, PYRA_COMMAND_PROFILE_SETTINGS,
6562306a36Sopenharmony_ci			buf, PYRA_SIZE_PROFILE_SETTINGS);
6662306a36Sopenharmony_ci}
6762306a36Sopenharmony_ci
6862306a36Sopenharmony_cistatic int pyra_get_settings(struct usb_device *usb_dev,
6962306a36Sopenharmony_ci		struct pyra_settings *buf)
7062306a36Sopenharmony_ci{
7162306a36Sopenharmony_ci	return roccat_common2_receive(usb_dev, PYRA_COMMAND_SETTINGS,
7262306a36Sopenharmony_ci			buf, PYRA_SIZE_SETTINGS);
7362306a36Sopenharmony_ci}
7462306a36Sopenharmony_ci
7562306a36Sopenharmony_cistatic int pyra_set_settings(struct usb_device *usb_dev,
7662306a36Sopenharmony_ci		struct pyra_settings const *settings)
7762306a36Sopenharmony_ci{
7862306a36Sopenharmony_ci	return roccat_common2_send_with_status(usb_dev,
7962306a36Sopenharmony_ci			PYRA_COMMAND_SETTINGS, settings,
8062306a36Sopenharmony_ci			PYRA_SIZE_SETTINGS);
8162306a36Sopenharmony_ci}
8262306a36Sopenharmony_ci
8362306a36Sopenharmony_cistatic ssize_t pyra_sysfs_read(struct file *fp, struct kobject *kobj,
8462306a36Sopenharmony_ci		char *buf, loff_t off, size_t count,
8562306a36Sopenharmony_ci		size_t real_size, uint command)
8662306a36Sopenharmony_ci{
8762306a36Sopenharmony_ci	struct device *dev = kobj_to_dev(kobj)->parent->parent;
8862306a36Sopenharmony_ci	struct pyra_device *pyra = hid_get_drvdata(dev_get_drvdata(dev));
8962306a36Sopenharmony_ci	struct usb_device *usb_dev = interface_to_usbdev(to_usb_interface(dev));
9062306a36Sopenharmony_ci	int retval;
9162306a36Sopenharmony_ci
9262306a36Sopenharmony_ci	if (off >= real_size)
9362306a36Sopenharmony_ci		return 0;
9462306a36Sopenharmony_ci
9562306a36Sopenharmony_ci	if (off != 0 || count != real_size)
9662306a36Sopenharmony_ci		return -EINVAL;
9762306a36Sopenharmony_ci
9862306a36Sopenharmony_ci	mutex_lock(&pyra->pyra_lock);
9962306a36Sopenharmony_ci	retval = roccat_common2_receive(usb_dev, command, buf, real_size);
10062306a36Sopenharmony_ci	mutex_unlock(&pyra->pyra_lock);
10162306a36Sopenharmony_ci
10262306a36Sopenharmony_ci	if (retval)
10362306a36Sopenharmony_ci		return retval;
10462306a36Sopenharmony_ci
10562306a36Sopenharmony_ci	return real_size;
10662306a36Sopenharmony_ci}
10762306a36Sopenharmony_ci
10862306a36Sopenharmony_cistatic ssize_t pyra_sysfs_write(struct file *fp, struct kobject *kobj,
10962306a36Sopenharmony_ci		void const *buf, loff_t off, size_t count,
11062306a36Sopenharmony_ci		size_t real_size, uint command)
11162306a36Sopenharmony_ci{
11262306a36Sopenharmony_ci	struct device *dev = kobj_to_dev(kobj)->parent->parent;
11362306a36Sopenharmony_ci	struct pyra_device *pyra = hid_get_drvdata(dev_get_drvdata(dev));
11462306a36Sopenharmony_ci	struct usb_device *usb_dev = interface_to_usbdev(to_usb_interface(dev));
11562306a36Sopenharmony_ci	int retval;
11662306a36Sopenharmony_ci
11762306a36Sopenharmony_ci	if (off != 0 || count != real_size)
11862306a36Sopenharmony_ci		return -EINVAL;
11962306a36Sopenharmony_ci
12062306a36Sopenharmony_ci	mutex_lock(&pyra->pyra_lock);
12162306a36Sopenharmony_ci	retval = roccat_common2_send_with_status(usb_dev, command, (void *)buf, real_size);
12262306a36Sopenharmony_ci	mutex_unlock(&pyra->pyra_lock);
12362306a36Sopenharmony_ci
12462306a36Sopenharmony_ci	if (retval)
12562306a36Sopenharmony_ci		return retval;
12662306a36Sopenharmony_ci
12762306a36Sopenharmony_ci	return real_size;
12862306a36Sopenharmony_ci}
12962306a36Sopenharmony_ci
13062306a36Sopenharmony_ci#define PYRA_SYSFS_W(thingy, THINGY) \
13162306a36Sopenharmony_cistatic ssize_t pyra_sysfs_write_ ## thingy(struct file *fp, \
13262306a36Sopenharmony_ci		struct kobject *kobj, struct bin_attribute *attr, char *buf, \
13362306a36Sopenharmony_ci		loff_t off, size_t count) \
13462306a36Sopenharmony_ci{ \
13562306a36Sopenharmony_ci	return pyra_sysfs_write(fp, kobj, buf, off, count, \
13662306a36Sopenharmony_ci			PYRA_SIZE_ ## THINGY, PYRA_COMMAND_ ## THINGY); \
13762306a36Sopenharmony_ci}
13862306a36Sopenharmony_ci
13962306a36Sopenharmony_ci#define PYRA_SYSFS_R(thingy, THINGY) \
14062306a36Sopenharmony_cistatic ssize_t pyra_sysfs_read_ ## thingy(struct file *fp, \
14162306a36Sopenharmony_ci		struct kobject *kobj, struct bin_attribute *attr, char *buf, \
14262306a36Sopenharmony_ci		loff_t off, size_t count) \
14362306a36Sopenharmony_ci{ \
14462306a36Sopenharmony_ci	return pyra_sysfs_read(fp, kobj, buf, off, count, \
14562306a36Sopenharmony_ci			PYRA_SIZE_ ## THINGY, PYRA_COMMAND_ ## THINGY); \
14662306a36Sopenharmony_ci}
14762306a36Sopenharmony_ci
14862306a36Sopenharmony_ci#define PYRA_SYSFS_RW(thingy, THINGY) \
14962306a36Sopenharmony_ciPYRA_SYSFS_W(thingy, THINGY) \
15062306a36Sopenharmony_ciPYRA_SYSFS_R(thingy, THINGY)
15162306a36Sopenharmony_ci
15262306a36Sopenharmony_ci#define PYRA_BIN_ATTRIBUTE_RW(thingy, THINGY) \
15362306a36Sopenharmony_ciPYRA_SYSFS_RW(thingy, THINGY); \
15462306a36Sopenharmony_cistatic struct bin_attribute bin_attr_##thingy = { \
15562306a36Sopenharmony_ci	.attr = { .name = #thingy, .mode = 0660 }, \
15662306a36Sopenharmony_ci	.size = PYRA_SIZE_ ## THINGY, \
15762306a36Sopenharmony_ci	.read = pyra_sysfs_read_ ## thingy, \
15862306a36Sopenharmony_ci	.write = pyra_sysfs_write_ ## thingy \
15962306a36Sopenharmony_ci}
16062306a36Sopenharmony_ci
16162306a36Sopenharmony_ci#define PYRA_BIN_ATTRIBUTE_R(thingy, THINGY) \
16262306a36Sopenharmony_ciPYRA_SYSFS_R(thingy, THINGY); \
16362306a36Sopenharmony_cistatic struct bin_attribute bin_attr_##thingy = { \
16462306a36Sopenharmony_ci	.attr = { .name = #thingy, .mode = 0440 }, \
16562306a36Sopenharmony_ci	.size = PYRA_SIZE_ ## THINGY, \
16662306a36Sopenharmony_ci	.read = pyra_sysfs_read_ ## thingy, \
16762306a36Sopenharmony_ci}
16862306a36Sopenharmony_ci
16962306a36Sopenharmony_ci#define PYRA_BIN_ATTRIBUTE_W(thingy, THINGY) \
17062306a36Sopenharmony_ciPYRA_SYSFS_W(thingy, THINGY); \
17162306a36Sopenharmony_cistatic struct bin_attribute bin_attr_##thingy = { \
17262306a36Sopenharmony_ci	.attr = { .name = #thingy, .mode = 0220 }, \
17362306a36Sopenharmony_ci	.size = PYRA_SIZE_ ## THINGY, \
17462306a36Sopenharmony_ci	.write = pyra_sysfs_write_ ## thingy \
17562306a36Sopenharmony_ci}
17662306a36Sopenharmony_ci
17762306a36Sopenharmony_ciPYRA_BIN_ATTRIBUTE_W(control, CONTROL);
17862306a36Sopenharmony_ciPYRA_BIN_ATTRIBUTE_RW(info, INFO);
17962306a36Sopenharmony_ciPYRA_BIN_ATTRIBUTE_RW(profile_settings, PROFILE_SETTINGS);
18062306a36Sopenharmony_ciPYRA_BIN_ATTRIBUTE_RW(profile_buttons, PROFILE_BUTTONS);
18162306a36Sopenharmony_ci
18262306a36Sopenharmony_cistatic ssize_t pyra_sysfs_read_profilex_settings(struct file *fp,
18362306a36Sopenharmony_ci		struct kobject *kobj, struct bin_attribute *attr, char *buf,
18462306a36Sopenharmony_ci		loff_t off, size_t count)
18562306a36Sopenharmony_ci{
18662306a36Sopenharmony_ci	struct device *dev = kobj_to_dev(kobj)->parent->parent;
18762306a36Sopenharmony_ci	struct usb_device *usb_dev = interface_to_usbdev(to_usb_interface(dev));
18862306a36Sopenharmony_ci	ssize_t retval;
18962306a36Sopenharmony_ci
19062306a36Sopenharmony_ci	retval = pyra_send_control(usb_dev, *(uint *)(attr->private),
19162306a36Sopenharmony_ci			PYRA_CONTROL_REQUEST_PROFILE_SETTINGS);
19262306a36Sopenharmony_ci	if (retval)
19362306a36Sopenharmony_ci		return retval;
19462306a36Sopenharmony_ci
19562306a36Sopenharmony_ci	return pyra_sysfs_read(fp, kobj, buf, off, count,
19662306a36Sopenharmony_ci			PYRA_SIZE_PROFILE_SETTINGS,
19762306a36Sopenharmony_ci			PYRA_COMMAND_PROFILE_SETTINGS);
19862306a36Sopenharmony_ci}
19962306a36Sopenharmony_ci
20062306a36Sopenharmony_cistatic ssize_t pyra_sysfs_read_profilex_buttons(struct file *fp,
20162306a36Sopenharmony_ci		struct kobject *kobj, struct bin_attribute *attr, char *buf,
20262306a36Sopenharmony_ci		loff_t off, size_t count)
20362306a36Sopenharmony_ci{
20462306a36Sopenharmony_ci	struct device *dev = kobj_to_dev(kobj)->parent->parent;
20562306a36Sopenharmony_ci	struct usb_device *usb_dev = interface_to_usbdev(to_usb_interface(dev));
20662306a36Sopenharmony_ci	ssize_t retval;
20762306a36Sopenharmony_ci
20862306a36Sopenharmony_ci	retval = pyra_send_control(usb_dev, *(uint *)(attr->private),
20962306a36Sopenharmony_ci			PYRA_CONTROL_REQUEST_PROFILE_BUTTONS);
21062306a36Sopenharmony_ci	if (retval)
21162306a36Sopenharmony_ci		return retval;
21262306a36Sopenharmony_ci
21362306a36Sopenharmony_ci	return pyra_sysfs_read(fp, kobj, buf, off, count,
21462306a36Sopenharmony_ci			PYRA_SIZE_PROFILE_BUTTONS,
21562306a36Sopenharmony_ci			PYRA_COMMAND_PROFILE_BUTTONS);
21662306a36Sopenharmony_ci}
21762306a36Sopenharmony_ci
21862306a36Sopenharmony_ci#define PROFILE_ATTR(number)						\
21962306a36Sopenharmony_cistatic struct bin_attribute bin_attr_profile##number##_settings = {	\
22062306a36Sopenharmony_ci	.attr = { .name = "profile" #number "_settings", .mode = 0440 },	\
22162306a36Sopenharmony_ci	.size = PYRA_SIZE_PROFILE_SETTINGS,				\
22262306a36Sopenharmony_ci	.read = pyra_sysfs_read_profilex_settings,			\
22362306a36Sopenharmony_ci	.private = &profile_numbers[number-1],				\
22462306a36Sopenharmony_ci};									\
22562306a36Sopenharmony_cistatic struct bin_attribute bin_attr_profile##number##_buttons = {	\
22662306a36Sopenharmony_ci	.attr = { .name = "profile" #number "_buttons", .mode = 0440 },	\
22762306a36Sopenharmony_ci	.size = PYRA_SIZE_PROFILE_BUTTONS,				\
22862306a36Sopenharmony_ci	.read = pyra_sysfs_read_profilex_buttons,			\
22962306a36Sopenharmony_ci	.private = &profile_numbers[number-1],				\
23062306a36Sopenharmony_ci};
23162306a36Sopenharmony_ciPROFILE_ATTR(1);
23262306a36Sopenharmony_ciPROFILE_ATTR(2);
23362306a36Sopenharmony_ciPROFILE_ATTR(3);
23462306a36Sopenharmony_ciPROFILE_ATTR(4);
23562306a36Sopenharmony_ciPROFILE_ATTR(5);
23662306a36Sopenharmony_ci
23762306a36Sopenharmony_cistatic ssize_t pyra_sysfs_write_settings(struct file *fp,
23862306a36Sopenharmony_ci		struct kobject *kobj, struct bin_attribute *attr, char *buf,
23962306a36Sopenharmony_ci		loff_t off, size_t count)
24062306a36Sopenharmony_ci{
24162306a36Sopenharmony_ci	struct device *dev = kobj_to_dev(kobj)->parent->parent;
24262306a36Sopenharmony_ci	struct pyra_device *pyra = hid_get_drvdata(dev_get_drvdata(dev));
24362306a36Sopenharmony_ci	struct usb_device *usb_dev = interface_to_usbdev(to_usb_interface(dev));
24462306a36Sopenharmony_ci	int retval = 0;
24562306a36Sopenharmony_ci	struct pyra_roccat_report roccat_report;
24662306a36Sopenharmony_ci	struct pyra_settings const *settings;
24762306a36Sopenharmony_ci
24862306a36Sopenharmony_ci	if (off != 0 || count != PYRA_SIZE_SETTINGS)
24962306a36Sopenharmony_ci		return -EINVAL;
25062306a36Sopenharmony_ci
25162306a36Sopenharmony_ci	settings = (struct pyra_settings const *)buf;
25262306a36Sopenharmony_ci	if (settings->startup_profile >= ARRAY_SIZE(pyra->profile_settings))
25362306a36Sopenharmony_ci		return -EINVAL;
25462306a36Sopenharmony_ci
25562306a36Sopenharmony_ci	mutex_lock(&pyra->pyra_lock);
25662306a36Sopenharmony_ci
25762306a36Sopenharmony_ci	retval = pyra_set_settings(usb_dev, settings);
25862306a36Sopenharmony_ci	if (retval) {
25962306a36Sopenharmony_ci		mutex_unlock(&pyra->pyra_lock);
26062306a36Sopenharmony_ci		return retval;
26162306a36Sopenharmony_ci	}
26262306a36Sopenharmony_ci
26362306a36Sopenharmony_ci	profile_activated(pyra, settings->startup_profile);
26462306a36Sopenharmony_ci
26562306a36Sopenharmony_ci	roccat_report.type = PYRA_MOUSE_EVENT_BUTTON_TYPE_PROFILE_2;
26662306a36Sopenharmony_ci	roccat_report.value = settings->startup_profile + 1;
26762306a36Sopenharmony_ci	roccat_report.key = 0;
26862306a36Sopenharmony_ci	roccat_report_event(pyra->chrdev_minor,
26962306a36Sopenharmony_ci			(uint8_t const *)&roccat_report);
27062306a36Sopenharmony_ci
27162306a36Sopenharmony_ci	mutex_unlock(&pyra->pyra_lock);
27262306a36Sopenharmony_ci	return PYRA_SIZE_SETTINGS;
27362306a36Sopenharmony_ci}
27462306a36Sopenharmony_ci
27562306a36Sopenharmony_ciPYRA_SYSFS_R(settings, SETTINGS);
27662306a36Sopenharmony_cistatic struct bin_attribute bin_attr_settings =
27762306a36Sopenharmony_ci	__BIN_ATTR(settings, (S_IWUSR | S_IRUGO),
27862306a36Sopenharmony_ci		   pyra_sysfs_read_settings, pyra_sysfs_write_settings,
27962306a36Sopenharmony_ci		   PYRA_SIZE_SETTINGS);
28062306a36Sopenharmony_ci
28162306a36Sopenharmony_cistatic ssize_t pyra_sysfs_show_actual_cpi(struct device *dev,
28262306a36Sopenharmony_ci		struct device_attribute *attr, char *buf)
28362306a36Sopenharmony_ci{
28462306a36Sopenharmony_ci	struct pyra_device *pyra =
28562306a36Sopenharmony_ci			hid_get_drvdata(dev_get_drvdata(dev->parent->parent));
28662306a36Sopenharmony_ci	return snprintf(buf, PAGE_SIZE, "%d\n", pyra->actual_cpi);
28762306a36Sopenharmony_ci}
28862306a36Sopenharmony_cistatic DEVICE_ATTR(actual_cpi, 0440, pyra_sysfs_show_actual_cpi, NULL);
28962306a36Sopenharmony_ci
29062306a36Sopenharmony_cistatic ssize_t pyra_sysfs_show_actual_profile(struct device *dev,
29162306a36Sopenharmony_ci		struct device_attribute *attr, char *buf)
29262306a36Sopenharmony_ci{
29362306a36Sopenharmony_ci	struct pyra_device *pyra =
29462306a36Sopenharmony_ci			hid_get_drvdata(dev_get_drvdata(dev->parent->parent));
29562306a36Sopenharmony_ci	struct usb_device *usb_dev = interface_to_usbdev(to_usb_interface(dev));
29662306a36Sopenharmony_ci	struct pyra_settings settings;
29762306a36Sopenharmony_ci
29862306a36Sopenharmony_ci	mutex_lock(&pyra->pyra_lock);
29962306a36Sopenharmony_ci	roccat_common2_receive(usb_dev, PYRA_COMMAND_SETTINGS,
30062306a36Sopenharmony_ci			&settings, PYRA_SIZE_SETTINGS);
30162306a36Sopenharmony_ci	mutex_unlock(&pyra->pyra_lock);
30262306a36Sopenharmony_ci
30362306a36Sopenharmony_ci	return snprintf(buf, PAGE_SIZE, "%d\n", settings.startup_profile);
30462306a36Sopenharmony_ci}
30562306a36Sopenharmony_cistatic DEVICE_ATTR(actual_profile, 0440, pyra_sysfs_show_actual_profile, NULL);
30662306a36Sopenharmony_cistatic DEVICE_ATTR(startup_profile, 0440, pyra_sysfs_show_actual_profile, NULL);
30762306a36Sopenharmony_ci
30862306a36Sopenharmony_cistatic ssize_t pyra_sysfs_show_firmware_version(struct device *dev,
30962306a36Sopenharmony_ci		struct device_attribute *attr, char *buf)
31062306a36Sopenharmony_ci{
31162306a36Sopenharmony_ci	struct pyra_device *pyra;
31262306a36Sopenharmony_ci	struct usb_device *usb_dev;
31362306a36Sopenharmony_ci	struct pyra_info info;
31462306a36Sopenharmony_ci
31562306a36Sopenharmony_ci	dev = dev->parent->parent;
31662306a36Sopenharmony_ci	pyra = hid_get_drvdata(dev_get_drvdata(dev));
31762306a36Sopenharmony_ci	usb_dev = interface_to_usbdev(to_usb_interface(dev));
31862306a36Sopenharmony_ci
31962306a36Sopenharmony_ci	mutex_lock(&pyra->pyra_lock);
32062306a36Sopenharmony_ci	roccat_common2_receive(usb_dev, PYRA_COMMAND_INFO,
32162306a36Sopenharmony_ci			&info, PYRA_SIZE_INFO);
32262306a36Sopenharmony_ci	mutex_unlock(&pyra->pyra_lock);
32362306a36Sopenharmony_ci
32462306a36Sopenharmony_ci	return snprintf(buf, PAGE_SIZE, "%d\n", info.firmware_version);
32562306a36Sopenharmony_ci}
32662306a36Sopenharmony_cistatic DEVICE_ATTR(firmware_version, 0440, pyra_sysfs_show_firmware_version,
32762306a36Sopenharmony_ci		   NULL);
32862306a36Sopenharmony_ci
32962306a36Sopenharmony_cistatic struct attribute *pyra_attrs[] = {
33062306a36Sopenharmony_ci	&dev_attr_actual_cpi.attr,
33162306a36Sopenharmony_ci	&dev_attr_actual_profile.attr,
33262306a36Sopenharmony_ci	&dev_attr_firmware_version.attr,
33362306a36Sopenharmony_ci	&dev_attr_startup_profile.attr,
33462306a36Sopenharmony_ci	NULL,
33562306a36Sopenharmony_ci};
33662306a36Sopenharmony_ci
33762306a36Sopenharmony_cistatic struct bin_attribute *pyra_bin_attributes[] = {
33862306a36Sopenharmony_ci	&bin_attr_control,
33962306a36Sopenharmony_ci	&bin_attr_info,
34062306a36Sopenharmony_ci	&bin_attr_profile_settings,
34162306a36Sopenharmony_ci	&bin_attr_profile_buttons,
34262306a36Sopenharmony_ci	&bin_attr_settings,
34362306a36Sopenharmony_ci	&bin_attr_profile1_settings,
34462306a36Sopenharmony_ci	&bin_attr_profile2_settings,
34562306a36Sopenharmony_ci	&bin_attr_profile3_settings,
34662306a36Sopenharmony_ci	&bin_attr_profile4_settings,
34762306a36Sopenharmony_ci	&bin_attr_profile5_settings,
34862306a36Sopenharmony_ci	&bin_attr_profile1_buttons,
34962306a36Sopenharmony_ci	&bin_attr_profile2_buttons,
35062306a36Sopenharmony_ci	&bin_attr_profile3_buttons,
35162306a36Sopenharmony_ci	&bin_attr_profile4_buttons,
35262306a36Sopenharmony_ci	&bin_attr_profile5_buttons,
35362306a36Sopenharmony_ci	NULL,
35462306a36Sopenharmony_ci};
35562306a36Sopenharmony_ci
35662306a36Sopenharmony_cistatic const struct attribute_group pyra_group = {
35762306a36Sopenharmony_ci	.attrs = pyra_attrs,
35862306a36Sopenharmony_ci	.bin_attrs = pyra_bin_attributes,
35962306a36Sopenharmony_ci};
36062306a36Sopenharmony_ci
36162306a36Sopenharmony_cistatic const struct attribute_group *pyra_groups[] = {
36262306a36Sopenharmony_ci	&pyra_group,
36362306a36Sopenharmony_ci	NULL,
36462306a36Sopenharmony_ci};
36562306a36Sopenharmony_ci
36662306a36Sopenharmony_ci/* pyra_class is used for creating sysfs attributes via roccat char device */
36762306a36Sopenharmony_cistatic const struct class pyra_class = {
36862306a36Sopenharmony_ci	.name = "pyra",
36962306a36Sopenharmony_ci	.dev_groups = pyra_groups,
37062306a36Sopenharmony_ci};
37162306a36Sopenharmony_ci
37262306a36Sopenharmony_cistatic int pyra_init_pyra_device_struct(struct usb_device *usb_dev,
37362306a36Sopenharmony_ci		struct pyra_device *pyra)
37462306a36Sopenharmony_ci{
37562306a36Sopenharmony_ci	struct pyra_settings settings;
37662306a36Sopenharmony_ci	int retval, i;
37762306a36Sopenharmony_ci
37862306a36Sopenharmony_ci	mutex_init(&pyra->pyra_lock);
37962306a36Sopenharmony_ci
38062306a36Sopenharmony_ci	retval = pyra_get_settings(usb_dev, &settings);
38162306a36Sopenharmony_ci	if (retval)
38262306a36Sopenharmony_ci		return retval;
38362306a36Sopenharmony_ci
38462306a36Sopenharmony_ci	for (i = 0; i < 5; ++i) {
38562306a36Sopenharmony_ci		retval = pyra_get_profile_settings(usb_dev,
38662306a36Sopenharmony_ci				&pyra->profile_settings[i], i);
38762306a36Sopenharmony_ci		if (retval)
38862306a36Sopenharmony_ci			return retval;
38962306a36Sopenharmony_ci	}
39062306a36Sopenharmony_ci
39162306a36Sopenharmony_ci	profile_activated(pyra, settings.startup_profile);
39262306a36Sopenharmony_ci
39362306a36Sopenharmony_ci	return 0;
39462306a36Sopenharmony_ci}
39562306a36Sopenharmony_ci
39662306a36Sopenharmony_cistatic int pyra_init_specials(struct hid_device *hdev)
39762306a36Sopenharmony_ci{
39862306a36Sopenharmony_ci	struct usb_interface *intf = to_usb_interface(hdev->dev.parent);
39962306a36Sopenharmony_ci	struct usb_device *usb_dev = interface_to_usbdev(intf);
40062306a36Sopenharmony_ci	struct pyra_device *pyra;
40162306a36Sopenharmony_ci	int retval;
40262306a36Sopenharmony_ci
40362306a36Sopenharmony_ci	if (intf->cur_altsetting->desc.bInterfaceProtocol
40462306a36Sopenharmony_ci			== USB_INTERFACE_PROTOCOL_MOUSE) {
40562306a36Sopenharmony_ci
40662306a36Sopenharmony_ci		pyra = kzalloc(sizeof(*pyra), GFP_KERNEL);
40762306a36Sopenharmony_ci		if (!pyra) {
40862306a36Sopenharmony_ci			hid_err(hdev, "can't alloc device descriptor\n");
40962306a36Sopenharmony_ci			return -ENOMEM;
41062306a36Sopenharmony_ci		}
41162306a36Sopenharmony_ci		hid_set_drvdata(hdev, pyra);
41262306a36Sopenharmony_ci
41362306a36Sopenharmony_ci		retval = pyra_init_pyra_device_struct(usb_dev, pyra);
41462306a36Sopenharmony_ci		if (retval) {
41562306a36Sopenharmony_ci			hid_err(hdev, "couldn't init struct pyra_device\n");
41662306a36Sopenharmony_ci			goto exit_free;
41762306a36Sopenharmony_ci		}
41862306a36Sopenharmony_ci
41962306a36Sopenharmony_ci		retval = roccat_connect(&pyra_class, hdev,
42062306a36Sopenharmony_ci				sizeof(struct pyra_roccat_report));
42162306a36Sopenharmony_ci		if (retval < 0) {
42262306a36Sopenharmony_ci			hid_err(hdev, "couldn't init char dev\n");
42362306a36Sopenharmony_ci		} else {
42462306a36Sopenharmony_ci			pyra->chrdev_minor = retval;
42562306a36Sopenharmony_ci			pyra->roccat_claimed = 1;
42662306a36Sopenharmony_ci		}
42762306a36Sopenharmony_ci	} else {
42862306a36Sopenharmony_ci		hid_set_drvdata(hdev, NULL);
42962306a36Sopenharmony_ci	}
43062306a36Sopenharmony_ci
43162306a36Sopenharmony_ci	return 0;
43262306a36Sopenharmony_ciexit_free:
43362306a36Sopenharmony_ci	kfree(pyra);
43462306a36Sopenharmony_ci	return retval;
43562306a36Sopenharmony_ci}
43662306a36Sopenharmony_ci
43762306a36Sopenharmony_cistatic void pyra_remove_specials(struct hid_device *hdev)
43862306a36Sopenharmony_ci{
43962306a36Sopenharmony_ci	struct usb_interface *intf = to_usb_interface(hdev->dev.parent);
44062306a36Sopenharmony_ci	struct pyra_device *pyra;
44162306a36Sopenharmony_ci
44262306a36Sopenharmony_ci	if (intf->cur_altsetting->desc.bInterfaceProtocol
44362306a36Sopenharmony_ci			== USB_INTERFACE_PROTOCOL_MOUSE) {
44462306a36Sopenharmony_ci		pyra = hid_get_drvdata(hdev);
44562306a36Sopenharmony_ci		if (pyra->roccat_claimed)
44662306a36Sopenharmony_ci			roccat_disconnect(pyra->chrdev_minor);
44762306a36Sopenharmony_ci		kfree(hid_get_drvdata(hdev));
44862306a36Sopenharmony_ci	}
44962306a36Sopenharmony_ci}
45062306a36Sopenharmony_ci
45162306a36Sopenharmony_cistatic int pyra_probe(struct hid_device *hdev, const struct hid_device_id *id)
45262306a36Sopenharmony_ci{
45362306a36Sopenharmony_ci	int retval;
45462306a36Sopenharmony_ci
45562306a36Sopenharmony_ci	if (!hid_is_usb(hdev))
45662306a36Sopenharmony_ci		return -EINVAL;
45762306a36Sopenharmony_ci
45862306a36Sopenharmony_ci	retval = hid_parse(hdev);
45962306a36Sopenharmony_ci	if (retval) {
46062306a36Sopenharmony_ci		hid_err(hdev, "parse failed\n");
46162306a36Sopenharmony_ci		goto exit;
46262306a36Sopenharmony_ci	}
46362306a36Sopenharmony_ci
46462306a36Sopenharmony_ci	retval = hid_hw_start(hdev, HID_CONNECT_DEFAULT);
46562306a36Sopenharmony_ci	if (retval) {
46662306a36Sopenharmony_ci		hid_err(hdev, "hw start failed\n");
46762306a36Sopenharmony_ci		goto exit;
46862306a36Sopenharmony_ci	}
46962306a36Sopenharmony_ci
47062306a36Sopenharmony_ci	retval = pyra_init_specials(hdev);
47162306a36Sopenharmony_ci	if (retval) {
47262306a36Sopenharmony_ci		hid_err(hdev, "couldn't install mouse\n");
47362306a36Sopenharmony_ci		goto exit_stop;
47462306a36Sopenharmony_ci	}
47562306a36Sopenharmony_ci	return 0;
47662306a36Sopenharmony_ci
47762306a36Sopenharmony_ciexit_stop:
47862306a36Sopenharmony_ci	hid_hw_stop(hdev);
47962306a36Sopenharmony_ciexit:
48062306a36Sopenharmony_ci	return retval;
48162306a36Sopenharmony_ci}
48262306a36Sopenharmony_ci
48362306a36Sopenharmony_cistatic void pyra_remove(struct hid_device *hdev)
48462306a36Sopenharmony_ci{
48562306a36Sopenharmony_ci	pyra_remove_specials(hdev);
48662306a36Sopenharmony_ci	hid_hw_stop(hdev);
48762306a36Sopenharmony_ci}
48862306a36Sopenharmony_ci
48962306a36Sopenharmony_cistatic void pyra_keep_values_up_to_date(struct pyra_device *pyra,
49062306a36Sopenharmony_ci		u8 const *data)
49162306a36Sopenharmony_ci{
49262306a36Sopenharmony_ci	struct pyra_mouse_event_button const *button_event;
49362306a36Sopenharmony_ci
49462306a36Sopenharmony_ci	switch (data[0]) {
49562306a36Sopenharmony_ci	case PYRA_MOUSE_REPORT_NUMBER_BUTTON:
49662306a36Sopenharmony_ci		button_event = (struct pyra_mouse_event_button const *)data;
49762306a36Sopenharmony_ci		switch (button_event->type) {
49862306a36Sopenharmony_ci		case PYRA_MOUSE_EVENT_BUTTON_TYPE_PROFILE_2:
49962306a36Sopenharmony_ci			profile_activated(pyra, button_event->data1 - 1);
50062306a36Sopenharmony_ci			break;
50162306a36Sopenharmony_ci		case PYRA_MOUSE_EVENT_BUTTON_TYPE_CPI:
50262306a36Sopenharmony_ci			pyra->actual_cpi = button_event->data1;
50362306a36Sopenharmony_ci			break;
50462306a36Sopenharmony_ci		}
50562306a36Sopenharmony_ci		break;
50662306a36Sopenharmony_ci	}
50762306a36Sopenharmony_ci}
50862306a36Sopenharmony_ci
50962306a36Sopenharmony_cistatic void pyra_report_to_chrdev(struct pyra_device const *pyra,
51062306a36Sopenharmony_ci		u8 const *data)
51162306a36Sopenharmony_ci{
51262306a36Sopenharmony_ci	struct pyra_roccat_report roccat_report;
51362306a36Sopenharmony_ci	struct pyra_mouse_event_button const *button_event;
51462306a36Sopenharmony_ci
51562306a36Sopenharmony_ci	if (data[0] != PYRA_MOUSE_REPORT_NUMBER_BUTTON)
51662306a36Sopenharmony_ci		return;
51762306a36Sopenharmony_ci
51862306a36Sopenharmony_ci	button_event = (struct pyra_mouse_event_button const *)data;
51962306a36Sopenharmony_ci
52062306a36Sopenharmony_ci	switch (button_event->type) {
52162306a36Sopenharmony_ci	case PYRA_MOUSE_EVENT_BUTTON_TYPE_PROFILE_2:
52262306a36Sopenharmony_ci	case PYRA_MOUSE_EVENT_BUTTON_TYPE_CPI:
52362306a36Sopenharmony_ci		roccat_report.type = button_event->type;
52462306a36Sopenharmony_ci		roccat_report.value = button_event->data1;
52562306a36Sopenharmony_ci		roccat_report.key = 0;
52662306a36Sopenharmony_ci		roccat_report_event(pyra->chrdev_minor,
52762306a36Sopenharmony_ci				(uint8_t const *)&roccat_report);
52862306a36Sopenharmony_ci		break;
52962306a36Sopenharmony_ci	case PYRA_MOUSE_EVENT_BUTTON_TYPE_MACRO:
53062306a36Sopenharmony_ci	case PYRA_MOUSE_EVENT_BUTTON_TYPE_SHORTCUT:
53162306a36Sopenharmony_ci	case PYRA_MOUSE_EVENT_BUTTON_TYPE_QUICKLAUNCH:
53262306a36Sopenharmony_ci		if (button_event->data2 == PYRA_MOUSE_EVENT_BUTTON_PRESS) {
53362306a36Sopenharmony_ci			roccat_report.type = button_event->type;
53462306a36Sopenharmony_ci			roccat_report.key = button_event->data1;
53562306a36Sopenharmony_ci			/*
53662306a36Sopenharmony_ci			 * pyra reports profile numbers with range 1-5.
53762306a36Sopenharmony_ci			 * Keeping this behaviour.
53862306a36Sopenharmony_ci			 */
53962306a36Sopenharmony_ci			roccat_report.value = pyra->actual_profile + 1;
54062306a36Sopenharmony_ci			roccat_report_event(pyra->chrdev_minor,
54162306a36Sopenharmony_ci					(uint8_t const *)&roccat_report);
54262306a36Sopenharmony_ci		}
54362306a36Sopenharmony_ci		break;
54462306a36Sopenharmony_ci	}
54562306a36Sopenharmony_ci}
54662306a36Sopenharmony_ci
54762306a36Sopenharmony_cistatic int pyra_raw_event(struct hid_device *hdev, struct hid_report *report,
54862306a36Sopenharmony_ci		u8 *data, int size)
54962306a36Sopenharmony_ci{
55062306a36Sopenharmony_ci	struct usb_interface *intf = to_usb_interface(hdev->dev.parent);
55162306a36Sopenharmony_ci	struct pyra_device *pyra = hid_get_drvdata(hdev);
55262306a36Sopenharmony_ci
55362306a36Sopenharmony_ci	if (intf->cur_altsetting->desc.bInterfaceProtocol
55462306a36Sopenharmony_ci			!= USB_INTERFACE_PROTOCOL_MOUSE)
55562306a36Sopenharmony_ci		return 0;
55662306a36Sopenharmony_ci
55762306a36Sopenharmony_ci	if (pyra == NULL)
55862306a36Sopenharmony_ci		return 0;
55962306a36Sopenharmony_ci
56062306a36Sopenharmony_ci	pyra_keep_values_up_to_date(pyra, data);
56162306a36Sopenharmony_ci
56262306a36Sopenharmony_ci	if (pyra->roccat_claimed)
56362306a36Sopenharmony_ci		pyra_report_to_chrdev(pyra, data);
56462306a36Sopenharmony_ci
56562306a36Sopenharmony_ci	return 0;
56662306a36Sopenharmony_ci}
56762306a36Sopenharmony_ci
56862306a36Sopenharmony_cistatic const struct hid_device_id pyra_devices[] = {
56962306a36Sopenharmony_ci	{ HID_USB_DEVICE(USB_VENDOR_ID_ROCCAT,
57062306a36Sopenharmony_ci			USB_DEVICE_ID_ROCCAT_PYRA_WIRED) },
57162306a36Sopenharmony_ci	{ HID_USB_DEVICE(USB_VENDOR_ID_ROCCAT,
57262306a36Sopenharmony_ci			USB_DEVICE_ID_ROCCAT_PYRA_WIRELESS) },
57362306a36Sopenharmony_ci	{ }
57462306a36Sopenharmony_ci};
57562306a36Sopenharmony_ci
57662306a36Sopenharmony_ciMODULE_DEVICE_TABLE(hid, pyra_devices);
57762306a36Sopenharmony_ci
57862306a36Sopenharmony_cistatic struct hid_driver pyra_driver = {
57962306a36Sopenharmony_ci		.name = "pyra",
58062306a36Sopenharmony_ci		.id_table = pyra_devices,
58162306a36Sopenharmony_ci		.probe = pyra_probe,
58262306a36Sopenharmony_ci		.remove = pyra_remove,
58362306a36Sopenharmony_ci		.raw_event = pyra_raw_event
58462306a36Sopenharmony_ci};
58562306a36Sopenharmony_ci
58662306a36Sopenharmony_cistatic int __init pyra_init(void)
58762306a36Sopenharmony_ci{
58862306a36Sopenharmony_ci	int retval;
58962306a36Sopenharmony_ci
59062306a36Sopenharmony_ci	/* class name has to be same as driver name */
59162306a36Sopenharmony_ci	retval = class_register(&pyra_class);
59262306a36Sopenharmony_ci	if (retval)
59362306a36Sopenharmony_ci		return retval;
59462306a36Sopenharmony_ci
59562306a36Sopenharmony_ci	retval = hid_register_driver(&pyra_driver);
59662306a36Sopenharmony_ci	if (retval)
59762306a36Sopenharmony_ci		class_unregister(&pyra_class);
59862306a36Sopenharmony_ci	return retval;
59962306a36Sopenharmony_ci}
60062306a36Sopenharmony_ci
60162306a36Sopenharmony_cistatic void __exit pyra_exit(void)
60262306a36Sopenharmony_ci{
60362306a36Sopenharmony_ci	hid_unregister_driver(&pyra_driver);
60462306a36Sopenharmony_ci	class_unregister(&pyra_class);
60562306a36Sopenharmony_ci}
60662306a36Sopenharmony_ci
60762306a36Sopenharmony_cimodule_init(pyra_init);
60862306a36Sopenharmony_cimodule_exit(pyra_exit);
60962306a36Sopenharmony_ci
61062306a36Sopenharmony_ciMODULE_AUTHOR("Stefan Achatz");
61162306a36Sopenharmony_ciMODULE_DESCRIPTION("USB Roccat Pyra driver");
61262306a36Sopenharmony_ciMODULE_LICENSE("GPL v2");
613