Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 8936aa31 authored by Stefan Achatz's avatar Stefan Achatz Committed by Jiri Kosina
Browse files

HID: roccat: add support for Roccat Kone Pure gaming mouse

Userland-tools can already be found at http://sourceforge.net/projects/roccat



Signed-off-by: default avatarStefan Achatz <erazor_de@users.sourceforge.net>
Signed-off-by: default avatarJiri Kosina <jkosina@suse.cz>
parent d381f45c
Loading
Loading
Loading
Loading
+105 −0
Original line number Diff line number Diff line
What:		/sys/bus/usb/devices/<busnum>-<devnum>:<config num>.<interface num>/<hid-bus>:<vendor-id>:<product-id>.<num>/konepure/roccatkonepure<minor>/actual_profile
Date:		December 2012
Contact:	Stefan Achatz <erazor_de@users.sourceforge.net>
Description:	The mouse can store 5 profiles which can be switched by the
		press of a button. actual_profile holds number of actual profile.
		This value is persistent, so its value determines the profile
		that's active when the mouse is powered on next time.
		When written, the mouse activates the set profile immediately.
		The data has to be 3 bytes long.
		The mouse will reject invalid data.
Users:		http://roccat.sourceforge.net

What:		/sys/bus/usb/devices/<busnum>-<devnum>:<config num>.<interface num>/<hid-bus>:<vendor-id>:<product-id>.<num>/konepure/roccatkonepure<minor>/control
Date:		December 2012
Contact:	Stefan Achatz <erazor_de@users.sourceforge.net>
Description:	When written, this file lets one select which data from which
		profile will be	read next. The data has to be 3 bytes long.
		This file is writeonly.
Users:		http://roccat.sourceforge.net

What:		/sys/bus/usb/devices/<busnum>-<devnum>:<config num>.<interface num>/<hid-bus>:<vendor-id>:<product-id>.<num>/konepure/roccatkonepure<minor>/info
Date:		December 2012
Contact:	Stefan Achatz <erazor_de@users.sourceforge.net>
Description:	When read, this file returns general data like firmware version.
		When written, the device can be reset.
		The data is 6 bytes long.
Users:		http://roccat.sourceforge.net

What:		/sys/bus/usb/devices/<busnum>-<devnum>:<config num>.<interface num>/<hid-bus>:<vendor-id>:<product-id>.<num>/konepure/roccatkonepure<minor>/macro
Date:		December 2012
Contact:	Stefan Achatz <erazor_de@users.sourceforge.net>
Description:	The mouse can store a macro with max 500 key/button strokes
		internally.
		When written, this file lets one set the sequence for a specific
		button for a specific profile. Button and profile numbers are
		included in written data. The data has to be 2082 bytes long.
		This file is writeonly.
Users:		http://roccat.sourceforge.net

What:		/sys/bus/usb/devices/<busnum>-<devnum>:<config num>.<interface num>/<hid-bus>:<vendor-id>:<product-id>.<num>/konepure/roccatkonepure<minor>/profile_buttons
Date:		December 2012
Contact:	Stefan Achatz <erazor_de@users.sourceforge.net>
Description:	The mouse can store 5 profiles which can be switched by the
		press of a button. A profile is split in settings and buttons.
		profile_buttons holds information about button layout.
		When written, this file lets one write the respective profile
		buttons back to the mouse. The data has to be 59 bytes long.
		The mouse will reject invalid data.
		Which profile to write is determined by the profile number
		contained in the data.
		Before reading this file, control has to be written to select
		which profile to read.
Users:		http://roccat.sourceforge.net

What:		/sys/bus/usb/devices/<busnum>-<devnum>:<config num>.<interface num>/<hid-bus>:<vendor-id>:<product-id>.<num>/konepure/roccatkonepure<minor>/profile_settings
Date:		December 2012
Contact:	Stefan Achatz <erazor_de@users.sourceforge.net>
Description:	The mouse can store 5 profiles which can be switched by the
		press of a button. A profile is split in settings and buttons.
		profile_settings holds information like resolution, sensitivity
		and light effects.
		When written, this file lets one write the respective profile
		settings back to the mouse. The data has to be 31 bytes long.
		The mouse will reject invalid data.
		Which profile to write is determined by the profile number
		contained in the data.
		Before reading this file, control has to be written to select
		which profile to read.
Users:		http://roccat.sourceforge.net

What:		/sys/bus/usb/devices/<busnum>-<devnum>:<config num>.<interface num>/<hid-bus>:<vendor-id>:<product-id>.<num>/konepure/roccatkonepure<minor>/sensor
Date:		December 2012
Contact:	Stefan Achatz <erazor_de@users.sourceforge.net>
Description:	The mouse has a tracking- and a distance-control-unit. These
		can be activated/deactivated and the lift-off distance can be
		set. The data has to be 6 bytes long.
		This file is writeonly.
Users:		http://roccat.sourceforge.net

What:		/sys/bus/usb/devices/<busnum>-<devnum>:<config num>.<interface num>/<hid-bus>:<vendor-id>:<product-id>.<num>/konepure/roccatkonepure<minor>/talk
Date:		December 2012
Contact:	Stefan Achatz <erazor_de@users.sourceforge.net>
Description:	Used to active some easy* functions of the mouse from outside.
		The data has to be 16 bytes long.
		This file is writeonly.
Users:		http://roccat.sourceforge.net

What:		/sys/bus/usb/devices/<busnum>-<devnum>:<config num>.<interface num>/<hid-bus>:<vendor-id>:<product-id>.<num>/konepure/roccatkonepure<minor>/tcu
Date:		December 2012
Contact:	Stefan Achatz <erazor_de@users.sourceforge.net>
Description:	When written a calibration process for the tracking control unit
		can be initiated/cancelled. Also lets one read/write sensor
		registers.
		The data has to be 4 bytes long.
Users:		http://roccat.sourceforge.net

What:		/sys/bus/usb/devices/<busnum>-<devnum>:<config num>.<interface num>/<hid-bus>:<vendor-id>:<product-id>.<num>/konepure/roccatkonepure<minor>/tcu_image
Date:		December 2012
Contact:	Stefan Achatz <erazor_de@users.sourceforge.net>
Description:	When read the mouse returns a 30x30 pixel image of the
		sampled underground. This works only in the course of a
		calibration process initiated with tcu.
		The returned data is 1028 bytes in size.
		This file is readonly.
Users:		http://roccat.sourceforge.net
+2 −2
Original line number Diff line number Diff line
@@ -94,8 +94,8 @@ obj-$(CONFIG_HID_PRIMAX) += hid-primax.o
obj-$(CONFIG_HID_PS3REMOTE)	+= hid-ps3remote.o
obj-$(CONFIG_HID_ROCCAT)	+= hid-roccat.o hid-roccat-common.o \
	hid-roccat-arvo.o hid-roccat-isku.o hid-roccat-kone.o \
	hid-roccat-koneplus.o hid-roccat-kovaplus.o hid-roccat-lua.o \
	hid-roccat-pyra.o hid-roccat-savu.o
	hid-roccat-koneplus.o hid-roccat-konepure.o hid-roccat-kovaplus.o \
	hid-roccat-lua.o hid-roccat-pyra.o hid-roccat-savu.o
obj-$(CONFIG_HID_SAITEK)	+= hid-saitek.o
obj-$(CONFIG_HID_SAMSUNG)	+= hid-samsung.o
obj-$(CONFIG_HID_SMARTJOYPLUS)	+= hid-sjoy.o
+1 −0
Original line number Diff line number Diff line
@@ -1687,6 +1687,7 @@ static const struct hid_device_id hid_have_special_driver[] = {
	{ HID_USB_DEVICE(USB_VENDOR_ID_ROCCAT, USB_DEVICE_ID_ROCCAT_ARVO) },
	{ HID_USB_DEVICE(USB_VENDOR_ID_ROCCAT, USB_DEVICE_ID_ROCCAT_ISKU) },
	{ HID_USB_DEVICE(USB_VENDOR_ID_ROCCAT, USB_DEVICE_ID_ROCCAT_KONEPLUS) },
	{ HID_USB_DEVICE(USB_VENDOR_ID_ROCCAT, USB_DEVICE_ID_ROCCAT_KONEPURE) },
	{ HID_USB_DEVICE(USB_VENDOR_ID_ROCCAT, USB_DEVICE_ID_ROCCAT_KOVAPLUS) },
	{ HID_USB_DEVICE(USB_VENDOR_ID_ROCCAT, USB_DEVICE_ID_ROCCAT_LUA) },
	{ HID_USB_DEVICE(USB_VENDOR_ID_ROCCAT, USB_DEVICE_ID_ROCCAT_PYRA_WIRED) },
+1 −0
Original line number Diff line number Diff line
@@ -689,6 +689,7 @@
#define USB_DEVICE_ID_ROCCAT_ISKU	0x319c
#define USB_DEVICE_ID_ROCCAT_KONE	0x2ced
#define USB_DEVICE_ID_ROCCAT_KONEPLUS	0x2d51
#define USB_DEVICE_ID_ROCCAT_KONEPURE	0x2dbe
#define USB_DEVICE_ID_ROCCAT_KONEXTD	0x2e22
#define USB_DEVICE_ID_ROCCAT_KOVAPLUS	0x2d50
#define USB_DEVICE_ID_ROCCAT_LUA	0x2c2e
+304 −0
Original line number Diff line number Diff line
/*
 * Roccat KonePure driver for Linux
 *
 * Copyright (c) 2012 Stefan Achatz <erazor_de@users.sourceforge.net>
 */

/*
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by the Free
 * Software Foundation; either version 2 of the License, or (at your option)
 * any later version.
 */

/*
 * Roccat KonePure is a smaller version of KoneXTD with less buttons and lights.
 */

#include <linux/device.h>
#include <linux/input.h>
#include <linux/hid.h>
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/hid-roccat.h>
#include "hid-ids.h"
#include "hid-roccat-common.h"
#include "hid-roccat-konepure.h"

static struct class *konepure_class;

static ssize_t konepure_sysfs_read(struct file *fp, struct kobject *kobj,
		char *buf, loff_t off, size_t count,
		size_t real_size, uint command)
{
	struct device *dev =
			container_of(kobj, struct device, kobj)->parent->parent;
	struct konepure_device *konepure = hid_get_drvdata(dev_get_drvdata(dev));
	struct usb_device *usb_dev = interface_to_usbdev(to_usb_interface(dev));
	int retval;

	if (off >= real_size)
		return 0;

	if (off != 0 || count != real_size)
		return -EINVAL;

	mutex_lock(&konepure->konepure_lock);
	retval = roccat_common2_receive(usb_dev, command, buf, real_size);
	mutex_unlock(&konepure->konepure_lock);

	return retval ? retval : real_size;
}

static ssize_t konepure_sysfs_write(struct file *fp, struct kobject *kobj,
		void const *buf, loff_t off, size_t count,
		size_t real_size, uint command)
{
	struct device *dev =
			container_of(kobj, struct device, kobj)->parent->parent;
	struct konepure_device *konepure = hid_get_drvdata(dev_get_drvdata(dev));
	struct usb_device *usb_dev = interface_to_usbdev(to_usb_interface(dev));
	int retval;

	if (off != 0 || count != real_size)
		return -EINVAL;

	mutex_lock(&konepure->konepure_lock);
	retval = roccat_common2_send_with_status(usb_dev, command,
			(void *)buf, real_size);
	mutex_unlock(&konepure->konepure_lock);

	return retval ? retval : real_size;
}

#define KONEPURE_SYSFS_W(thingy, THINGY) \
static ssize_t konepure_sysfs_write_ ## thingy(struct file *fp, \
		struct kobject *kobj, struct bin_attribute *attr, char *buf, \
		loff_t off, size_t count) \
{ \
	return konepure_sysfs_write(fp, kobj, buf, off, count, \
			KONEPURE_SIZE_ ## THINGY, KONEPURE_COMMAND_ ## THINGY); \
}

#define KONEPURE_SYSFS_R(thingy, THINGY) \
static ssize_t konepure_sysfs_read_ ## thingy(struct file *fp, \
		struct kobject *kobj, struct bin_attribute *attr, char *buf, \
		loff_t off, size_t count) \
{ \
	return konepure_sysfs_read(fp, kobj, buf, off, count, \
			KONEPURE_SIZE_ ## THINGY, KONEPURE_COMMAND_ ## THINGY); \
}

#define KONEPURE_SYSFS_RW(thingy, THINGY) \
KONEPURE_SYSFS_W(thingy, THINGY) \
KONEPURE_SYSFS_R(thingy, THINGY)

#define KONEPURE_BIN_ATTRIBUTE_RW(thingy, THINGY) \
{ \
	.attr = { .name = #thingy, .mode = 0660 }, \
	.size = KONEPURE_SIZE_ ## THINGY, \
	.read = konepure_sysfs_read_ ## thingy, \
	.write = konepure_sysfs_write_ ## thingy \
}

#define KONEPURE_BIN_ATTRIBUTE_R(thingy, THINGY) \
{ \
	.attr = { .name = #thingy, .mode = 0440 }, \
	.size = KONEPURE_SIZE_ ## THINGY, \
	.read = konepure_sysfs_read_ ## thingy, \
}

#define KONEPURE_BIN_ATTRIBUTE_W(thingy, THINGY) \
{ \
	.attr = { .name = #thingy, .mode = 0220 }, \
	.size = KONEPURE_SIZE_ ## THINGY, \
	.write = konepure_sysfs_write_ ## thingy \
}

KONEPURE_SYSFS_RW(actual_profile, ACTUAL_PROFILE)
KONEPURE_SYSFS_W(control, CONTROL)
KONEPURE_SYSFS_RW(info, INFO)
KONEPURE_SYSFS_W(talk, TALK)
KONEPURE_SYSFS_W(macro, MACRO)
KONEPURE_SYSFS_RW(sensor, SENSOR)
KONEPURE_SYSFS_RW(tcu, TCU)
KONEPURE_SYSFS_R(tcu_image, TCU_IMAGE)
KONEPURE_SYSFS_RW(profile_settings, PROFILE_SETTINGS)
KONEPURE_SYSFS_RW(profile_buttons, PROFILE_BUTTONS)

static struct bin_attribute konepure_bin_attributes[] = {
	KONEPURE_BIN_ATTRIBUTE_RW(actual_profile, ACTUAL_PROFILE),
	KONEPURE_BIN_ATTRIBUTE_W(control, CONTROL),
	KONEPURE_BIN_ATTRIBUTE_RW(info, INFO),
	KONEPURE_BIN_ATTRIBUTE_W(talk, TALK),
	KONEPURE_BIN_ATTRIBUTE_W(macro, MACRO),
	KONEPURE_BIN_ATTRIBUTE_RW(sensor, SENSOR),
	KONEPURE_BIN_ATTRIBUTE_RW(tcu, TCU),
	KONEPURE_BIN_ATTRIBUTE_R(tcu_image, TCU_IMAGE),
	KONEPURE_BIN_ATTRIBUTE_RW(profile_settings, PROFILE_SETTINGS),
	KONEPURE_BIN_ATTRIBUTE_RW(profile_buttons, PROFILE_BUTTONS),
	__ATTR_NULL
};

static int konepure_init_konepure_device_struct(struct usb_device *usb_dev,
		struct konepure_device *konepure)
{
	mutex_init(&konepure->konepure_lock);

	return 0;
}

static int konepure_init_specials(struct hid_device *hdev)
{
	struct usb_interface *intf = to_usb_interface(hdev->dev.parent);
	struct usb_device *usb_dev = interface_to_usbdev(intf);
	struct konepure_device *konepure;
	int retval;

	if (intf->cur_altsetting->desc.bInterfaceProtocol
			!= USB_INTERFACE_PROTOCOL_MOUSE) {
		hid_set_drvdata(hdev, NULL);
		return 0;
	}

	konepure = kzalloc(sizeof(*konepure), GFP_KERNEL);
	if (!konepure) {
		hid_err(hdev, "can't alloc device descriptor\n");
		return -ENOMEM;
	}
	hid_set_drvdata(hdev, konepure);

	retval = konepure_init_konepure_device_struct(usb_dev, konepure);
	if (retval) {
		hid_err(hdev, "couldn't init struct konepure_device\n");
		goto exit_free;
	}

	retval = roccat_connect(konepure_class, hdev,
			sizeof(struct konepure_mouse_report_button));
	if (retval < 0) {
		hid_err(hdev, "couldn't init char dev\n");
	} else {
		konepure->chrdev_minor = retval;
		konepure->roccat_claimed = 1;
	}

	return 0;
exit_free:
	kfree(konepure);
	return retval;
}

static void konepure_remove_specials(struct hid_device *hdev)
{
	struct usb_interface *intf = to_usb_interface(hdev->dev.parent);
	struct konepure_device *konepure;

	if (intf->cur_altsetting->desc.bInterfaceProtocol
			!= USB_INTERFACE_PROTOCOL_MOUSE)
		return;

	konepure = hid_get_drvdata(hdev);
	if (konepure->roccat_claimed)
		roccat_disconnect(konepure->chrdev_minor);
	kfree(konepure);
}

static int konepure_probe(struct hid_device *hdev,
		const struct hid_device_id *id)
{
	int retval;

	retval = hid_parse(hdev);
	if (retval) {
		hid_err(hdev, "parse failed\n");
		goto exit;
	}

	retval = hid_hw_start(hdev, HID_CONNECT_DEFAULT);
	if (retval) {
		hid_err(hdev, "hw start failed\n");
		goto exit;
	}

	retval = konepure_init_specials(hdev);
	if (retval) {
		hid_err(hdev, "couldn't install mouse\n");
		goto exit_stop;
	}

	return 0;

exit_stop:
	hid_hw_stop(hdev);
exit:
	return retval;
}

static void konepure_remove(struct hid_device *hdev)
{
	konepure_remove_specials(hdev);
	hid_hw_stop(hdev);
}

static int konepure_raw_event(struct hid_device *hdev,
		struct hid_report *report, u8 *data, int size)
{
	struct usb_interface *intf = to_usb_interface(hdev->dev.parent);
	struct konepure_device *konepure = hid_get_drvdata(hdev);

	if (intf->cur_altsetting->desc.bInterfaceProtocol
			!= USB_INTERFACE_PROTOCOL_MOUSE)
		return 0;

	if (data[0] != KONEPURE_MOUSE_REPORT_NUMBER_BUTTON)
		return 0;

	if (konepure != NULL && konepure->roccat_claimed)
		roccat_report_event(konepure->chrdev_minor, data);

	return 0;
}

static const struct hid_device_id konepure_devices[] = {
	{ HID_USB_DEVICE(USB_VENDOR_ID_ROCCAT, USB_DEVICE_ID_ROCCAT_KONEPURE) },
	{ }
};

MODULE_DEVICE_TABLE(hid, konepure_devices);

static struct hid_driver konepure_driver = {
		.name = "konepure",
		.id_table = konepure_devices,
		.probe = konepure_probe,
		.remove = konepure_remove,
		.raw_event = konepure_raw_event
};

static int __init konepure_init(void)
{
	int retval;

	konepure_class = class_create(THIS_MODULE, "konepure");
	if (IS_ERR(konepure_class))
		return PTR_ERR(konepure_class);
	konepure_class->dev_bin_attrs = konepure_bin_attributes;

	retval = hid_register_driver(&konepure_driver);
	if (retval)
		class_destroy(konepure_class);
	return retval;
}

static void __exit konepure_exit(void)
{
	hid_unregister_driver(&konepure_driver);
	class_destroy(konepure_class);
}

module_init(konepure_init);
module_exit(konepure_exit);

MODULE_AUTHOR("Stefan Achatz");
MODULE_DESCRIPTION("USB Roccat KonePure driver");
MODULE_LICENSE("GPL v2");
Loading