cachepc-linux

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

hid-roccat-pyra.c (16571B)


      1// SPDX-License-Identifier: GPL-2.0-or-later
      2/*
      3 * Roccat Pyra driver for Linux
      4 *
      5 * Copyright (c) 2010 Stefan Achatz <erazor_de@users.sourceforge.net>
      6 */
      7
      8/*
      9 */
     10
     11/*
     12 * Roccat Pyra is a mobile gamer mouse which comes in wired and wireless
     13 * variant. Wireless variant is not tested.
     14 * Userland tools can be found at http://sourceforge.net/projects/roccat
     15 */
     16
     17#include <linux/device.h>
     18#include <linux/input.h>
     19#include <linux/hid.h>
     20#include <linux/module.h>
     21#include <linux/slab.h>
     22#include <linux/hid-roccat.h>
     23#include "hid-ids.h"
     24#include "hid-roccat-common.h"
     25#include "hid-roccat-pyra.h"
     26
     27static uint profile_numbers[5] = {0, 1, 2, 3, 4};
     28
     29/* pyra_class is used for creating sysfs attributes via roccat char device */
     30static struct class *pyra_class;
     31
     32static void profile_activated(struct pyra_device *pyra,
     33		unsigned int new_profile)
     34{
     35	if (new_profile >= ARRAY_SIZE(pyra->profile_settings))
     36		return;
     37	pyra->actual_profile = new_profile;
     38	pyra->actual_cpi = pyra->profile_settings[pyra->actual_profile].y_cpi;
     39}
     40
     41static int pyra_send_control(struct usb_device *usb_dev, int value,
     42		enum pyra_control_requests request)
     43{
     44	struct roccat_common2_control control;
     45
     46	if ((request == PYRA_CONTROL_REQUEST_PROFILE_SETTINGS ||
     47			request == PYRA_CONTROL_REQUEST_PROFILE_BUTTONS) &&
     48			(value < 0 || value > 4))
     49		return -EINVAL;
     50
     51	control.command = ROCCAT_COMMON_COMMAND_CONTROL;
     52	control.value = value;
     53	control.request = request;
     54
     55	return roccat_common2_send(usb_dev, ROCCAT_COMMON_COMMAND_CONTROL,
     56			&control, sizeof(struct roccat_common2_control));
     57}
     58
     59static int pyra_get_profile_settings(struct usb_device *usb_dev,
     60		struct pyra_profile_settings *buf, int number)
     61{
     62	int retval;
     63	retval = pyra_send_control(usb_dev, number,
     64			PYRA_CONTROL_REQUEST_PROFILE_SETTINGS);
     65	if (retval)
     66		return retval;
     67	return roccat_common2_receive(usb_dev, PYRA_COMMAND_PROFILE_SETTINGS,
     68			buf, PYRA_SIZE_PROFILE_SETTINGS);
     69}
     70
     71static int pyra_get_settings(struct usb_device *usb_dev,
     72		struct pyra_settings *buf)
     73{
     74	return roccat_common2_receive(usb_dev, PYRA_COMMAND_SETTINGS,
     75			buf, PYRA_SIZE_SETTINGS);
     76}
     77
     78static int pyra_set_settings(struct usb_device *usb_dev,
     79		struct pyra_settings const *settings)
     80{
     81	return roccat_common2_send_with_status(usb_dev,
     82			PYRA_COMMAND_SETTINGS, settings,
     83			PYRA_SIZE_SETTINGS);
     84}
     85
     86static ssize_t pyra_sysfs_read(struct file *fp, struct kobject *kobj,
     87		char *buf, loff_t off, size_t count,
     88		size_t real_size, uint command)
     89{
     90	struct device *dev = kobj_to_dev(kobj)->parent->parent;
     91	struct pyra_device *pyra = hid_get_drvdata(dev_get_drvdata(dev));
     92	struct usb_device *usb_dev = interface_to_usbdev(to_usb_interface(dev));
     93	int retval;
     94
     95	if (off >= real_size)
     96		return 0;
     97
     98	if (off != 0 || count != real_size)
     99		return -EINVAL;
    100
    101	mutex_lock(&pyra->pyra_lock);
    102	retval = roccat_common2_receive(usb_dev, command, buf, real_size);
    103	mutex_unlock(&pyra->pyra_lock);
    104
    105	if (retval)
    106		return retval;
    107
    108	return real_size;
    109}
    110
    111static ssize_t pyra_sysfs_write(struct file *fp, struct kobject *kobj,
    112		void const *buf, loff_t off, size_t count,
    113		size_t real_size, uint command)
    114{
    115	struct device *dev = kobj_to_dev(kobj)->parent->parent;
    116	struct pyra_device *pyra = hid_get_drvdata(dev_get_drvdata(dev));
    117	struct usb_device *usb_dev = interface_to_usbdev(to_usb_interface(dev));
    118	int retval;
    119
    120	if (off != 0 || count != real_size)
    121		return -EINVAL;
    122
    123	mutex_lock(&pyra->pyra_lock);
    124	retval = roccat_common2_send_with_status(usb_dev, command, (void *)buf, real_size);
    125	mutex_unlock(&pyra->pyra_lock);
    126
    127	if (retval)
    128		return retval;
    129
    130	return real_size;
    131}
    132
    133#define PYRA_SYSFS_W(thingy, THINGY) \
    134static ssize_t pyra_sysfs_write_ ## thingy(struct file *fp, \
    135		struct kobject *kobj, struct bin_attribute *attr, char *buf, \
    136		loff_t off, size_t count) \
    137{ \
    138	return pyra_sysfs_write(fp, kobj, buf, off, count, \
    139			PYRA_SIZE_ ## THINGY, PYRA_COMMAND_ ## THINGY); \
    140}
    141
    142#define PYRA_SYSFS_R(thingy, THINGY) \
    143static ssize_t pyra_sysfs_read_ ## thingy(struct file *fp, \
    144		struct kobject *kobj, struct bin_attribute *attr, char *buf, \
    145		loff_t off, size_t count) \
    146{ \
    147	return pyra_sysfs_read(fp, kobj, buf, off, count, \
    148			PYRA_SIZE_ ## THINGY, PYRA_COMMAND_ ## THINGY); \
    149}
    150
    151#define PYRA_SYSFS_RW(thingy, THINGY) \
    152PYRA_SYSFS_W(thingy, THINGY) \
    153PYRA_SYSFS_R(thingy, THINGY)
    154
    155#define PYRA_BIN_ATTRIBUTE_RW(thingy, THINGY) \
    156PYRA_SYSFS_RW(thingy, THINGY); \
    157static struct bin_attribute bin_attr_##thingy = { \
    158	.attr = { .name = #thingy, .mode = 0660 }, \
    159	.size = PYRA_SIZE_ ## THINGY, \
    160	.read = pyra_sysfs_read_ ## thingy, \
    161	.write = pyra_sysfs_write_ ## thingy \
    162}
    163
    164#define PYRA_BIN_ATTRIBUTE_R(thingy, THINGY) \
    165PYRA_SYSFS_R(thingy, THINGY); \
    166static struct bin_attribute bin_attr_##thingy = { \
    167	.attr = { .name = #thingy, .mode = 0440 }, \
    168	.size = PYRA_SIZE_ ## THINGY, \
    169	.read = pyra_sysfs_read_ ## thingy, \
    170}
    171
    172#define PYRA_BIN_ATTRIBUTE_W(thingy, THINGY) \
    173PYRA_SYSFS_W(thingy, THINGY); \
    174static struct bin_attribute bin_attr_##thingy = { \
    175	.attr = { .name = #thingy, .mode = 0220 }, \
    176	.size = PYRA_SIZE_ ## THINGY, \
    177	.write = pyra_sysfs_write_ ## thingy \
    178}
    179
    180PYRA_BIN_ATTRIBUTE_W(control, CONTROL);
    181PYRA_BIN_ATTRIBUTE_RW(info, INFO);
    182PYRA_BIN_ATTRIBUTE_RW(profile_settings, PROFILE_SETTINGS);
    183PYRA_BIN_ATTRIBUTE_RW(profile_buttons, PROFILE_BUTTONS);
    184
    185static ssize_t pyra_sysfs_read_profilex_settings(struct file *fp,
    186		struct kobject *kobj, struct bin_attribute *attr, char *buf,
    187		loff_t off, size_t count)
    188{
    189	struct device *dev = kobj_to_dev(kobj)->parent->parent;
    190	struct usb_device *usb_dev = interface_to_usbdev(to_usb_interface(dev));
    191	ssize_t retval;
    192
    193	retval = pyra_send_control(usb_dev, *(uint *)(attr->private),
    194			PYRA_CONTROL_REQUEST_PROFILE_SETTINGS);
    195	if (retval)
    196		return retval;
    197
    198	return pyra_sysfs_read(fp, kobj, buf, off, count,
    199			PYRA_SIZE_PROFILE_SETTINGS,
    200			PYRA_COMMAND_PROFILE_SETTINGS);
    201}
    202
    203static ssize_t pyra_sysfs_read_profilex_buttons(struct file *fp,
    204		struct kobject *kobj, struct bin_attribute *attr, char *buf,
    205		loff_t off, size_t count)
    206{
    207	struct device *dev = kobj_to_dev(kobj)->parent->parent;
    208	struct usb_device *usb_dev = interface_to_usbdev(to_usb_interface(dev));
    209	ssize_t retval;
    210
    211	retval = pyra_send_control(usb_dev, *(uint *)(attr->private),
    212			PYRA_CONTROL_REQUEST_PROFILE_BUTTONS);
    213	if (retval)
    214		return retval;
    215
    216	return pyra_sysfs_read(fp, kobj, buf, off, count,
    217			PYRA_SIZE_PROFILE_BUTTONS,
    218			PYRA_COMMAND_PROFILE_BUTTONS);
    219}
    220
    221#define PROFILE_ATTR(number)						\
    222static struct bin_attribute bin_attr_profile##number##_settings = {	\
    223	.attr = { .name = "profile" #number "_settings", .mode = 0440 },	\
    224	.size = PYRA_SIZE_PROFILE_SETTINGS,				\
    225	.read = pyra_sysfs_read_profilex_settings,			\
    226	.private = &profile_numbers[number-1],				\
    227};									\
    228static struct bin_attribute bin_attr_profile##number##_buttons = {	\
    229	.attr = { .name = "profile" #number "_buttons", .mode = 0440 },	\
    230	.size = PYRA_SIZE_PROFILE_BUTTONS,				\
    231	.read = pyra_sysfs_read_profilex_buttons,			\
    232	.private = &profile_numbers[number-1],				\
    233};
    234PROFILE_ATTR(1);
    235PROFILE_ATTR(2);
    236PROFILE_ATTR(3);
    237PROFILE_ATTR(4);
    238PROFILE_ATTR(5);
    239
    240static ssize_t pyra_sysfs_write_settings(struct file *fp,
    241		struct kobject *kobj, struct bin_attribute *attr, char *buf,
    242		loff_t off, size_t count)
    243{
    244	struct device *dev = kobj_to_dev(kobj)->parent->parent;
    245	struct pyra_device *pyra = hid_get_drvdata(dev_get_drvdata(dev));
    246	struct usb_device *usb_dev = interface_to_usbdev(to_usb_interface(dev));
    247	int retval = 0;
    248	struct pyra_roccat_report roccat_report;
    249	struct pyra_settings const *settings;
    250
    251	if (off != 0 || count != PYRA_SIZE_SETTINGS)
    252		return -EINVAL;
    253
    254	settings = (struct pyra_settings const *)buf;
    255	if (settings->startup_profile >= ARRAY_SIZE(pyra->profile_settings))
    256		return -EINVAL;
    257
    258	mutex_lock(&pyra->pyra_lock);
    259
    260	retval = pyra_set_settings(usb_dev, settings);
    261	if (retval) {
    262		mutex_unlock(&pyra->pyra_lock);
    263		return retval;
    264	}
    265
    266	profile_activated(pyra, settings->startup_profile);
    267
    268	roccat_report.type = PYRA_MOUSE_EVENT_BUTTON_TYPE_PROFILE_2;
    269	roccat_report.value = settings->startup_profile + 1;
    270	roccat_report.key = 0;
    271	roccat_report_event(pyra->chrdev_minor,
    272			(uint8_t const *)&roccat_report);
    273
    274	mutex_unlock(&pyra->pyra_lock);
    275	return PYRA_SIZE_SETTINGS;
    276}
    277
    278PYRA_SYSFS_R(settings, SETTINGS);
    279static struct bin_attribute bin_attr_settings =
    280	__BIN_ATTR(settings, (S_IWUSR | S_IRUGO),
    281		   pyra_sysfs_read_settings, pyra_sysfs_write_settings,
    282		   PYRA_SIZE_SETTINGS);
    283
    284static ssize_t pyra_sysfs_show_actual_cpi(struct device *dev,
    285		struct device_attribute *attr, char *buf)
    286{
    287	struct pyra_device *pyra =
    288			hid_get_drvdata(dev_get_drvdata(dev->parent->parent));
    289	return snprintf(buf, PAGE_SIZE, "%d\n", pyra->actual_cpi);
    290}
    291static DEVICE_ATTR(actual_cpi, 0440, pyra_sysfs_show_actual_cpi, NULL);
    292
    293static ssize_t pyra_sysfs_show_actual_profile(struct device *dev,
    294		struct device_attribute *attr, char *buf)
    295{
    296	struct pyra_device *pyra =
    297			hid_get_drvdata(dev_get_drvdata(dev->parent->parent));
    298	struct usb_device *usb_dev = interface_to_usbdev(to_usb_interface(dev));
    299	struct pyra_settings settings;
    300
    301	mutex_lock(&pyra->pyra_lock);
    302	roccat_common2_receive(usb_dev, PYRA_COMMAND_SETTINGS,
    303			&settings, PYRA_SIZE_SETTINGS);
    304	mutex_unlock(&pyra->pyra_lock);
    305
    306	return snprintf(buf, PAGE_SIZE, "%d\n", settings.startup_profile);
    307}
    308static DEVICE_ATTR(actual_profile, 0440, pyra_sysfs_show_actual_profile, NULL);
    309static DEVICE_ATTR(startup_profile, 0440, pyra_sysfs_show_actual_profile, NULL);
    310
    311static ssize_t pyra_sysfs_show_firmware_version(struct device *dev,
    312		struct device_attribute *attr, char *buf)
    313{
    314	struct pyra_device *pyra;
    315	struct usb_device *usb_dev;
    316	struct pyra_info info;
    317
    318	dev = dev->parent->parent;
    319	pyra = hid_get_drvdata(dev_get_drvdata(dev));
    320	usb_dev = interface_to_usbdev(to_usb_interface(dev));
    321
    322	mutex_lock(&pyra->pyra_lock);
    323	roccat_common2_receive(usb_dev, PYRA_COMMAND_INFO,
    324			&info, PYRA_SIZE_INFO);
    325	mutex_unlock(&pyra->pyra_lock);
    326
    327	return snprintf(buf, PAGE_SIZE, "%d\n", info.firmware_version);
    328}
    329static DEVICE_ATTR(firmware_version, 0440, pyra_sysfs_show_firmware_version,
    330		   NULL);
    331
    332static struct attribute *pyra_attrs[] = {
    333	&dev_attr_actual_cpi.attr,
    334	&dev_attr_actual_profile.attr,
    335	&dev_attr_firmware_version.attr,
    336	&dev_attr_startup_profile.attr,
    337	NULL,
    338};
    339
    340static struct bin_attribute *pyra_bin_attributes[] = {
    341	&bin_attr_control,
    342	&bin_attr_info,
    343	&bin_attr_profile_settings,
    344	&bin_attr_profile_buttons,
    345	&bin_attr_settings,
    346	&bin_attr_profile1_settings,
    347	&bin_attr_profile2_settings,
    348	&bin_attr_profile3_settings,
    349	&bin_attr_profile4_settings,
    350	&bin_attr_profile5_settings,
    351	&bin_attr_profile1_buttons,
    352	&bin_attr_profile2_buttons,
    353	&bin_attr_profile3_buttons,
    354	&bin_attr_profile4_buttons,
    355	&bin_attr_profile5_buttons,
    356	NULL,
    357};
    358
    359static const struct attribute_group pyra_group = {
    360	.attrs = pyra_attrs,
    361	.bin_attrs = pyra_bin_attributes,
    362};
    363
    364static const struct attribute_group *pyra_groups[] = {
    365	&pyra_group,
    366	NULL,
    367};
    368
    369static int pyra_init_pyra_device_struct(struct usb_device *usb_dev,
    370		struct pyra_device *pyra)
    371{
    372	struct pyra_settings settings;
    373	int retval, i;
    374
    375	mutex_init(&pyra->pyra_lock);
    376
    377	retval = pyra_get_settings(usb_dev, &settings);
    378	if (retval)
    379		return retval;
    380
    381	for (i = 0; i < 5; ++i) {
    382		retval = pyra_get_profile_settings(usb_dev,
    383				&pyra->profile_settings[i], i);
    384		if (retval)
    385			return retval;
    386	}
    387
    388	profile_activated(pyra, settings.startup_profile);
    389
    390	return 0;
    391}
    392
    393static int pyra_init_specials(struct hid_device *hdev)
    394{
    395	struct usb_interface *intf = to_usb_interface(hdev->dev.parent);
    396	struct usb_device *usb_dev = interface_to_usbdev(intf);
    397	struct pyra_device *pyra;
    398	int retval;
    399
    400	if (intf->cur_altsetting->desc.bInterfaceProtocol
    401			== USB_INTERFACE_PROTOCOL_MOUSE) {
    402
    403		pyra = kzalloc(sizeof(*pyra), GFP_KERNEL);
    404		if (!pyra) {
    405			hid_err(hdev, "can't alloc device descriptor\n");
    406			return -ENOMEM;
    407		}
    408		hid_set_drvdata(hdev, pyra);
    409
    410		retval = pyra_init_pyra_device_struct(usb_dev, pyra);
    411		if (retval) {
    412			hid_err(hdev, "couldn't init struct pyra_device\n");
    413			goto exit_free;
    414		}
    415
    416		retval = roccat_connect(pyra_class, hdev,
    417				sizeof(struct pyra_roccat_report));
    418		if (retval < 0) {
    419			hid_err(hdev, "couldn't init char dev\n");
    420		} else {
    421			pyra->chrdev_minor = retval;
    422			pyra->roccat_claimed = 1;
    423		}
    424	} else {
    425		hid_set_drvdata(hdev, NULL);
    426	}
    427
    428	return 0;
    429exit_free:
    430	kfree(pyra);
    431	return retval;
    432}
    433
    434static void pyra_remove_specials(struct hid_device *hdev)
    435{
    436	struct usb_interface *intf = to_usb_interface(hdev->dev.parent);
    437	struct pyra_device *pyra;
    438
    439	if (intf->cur_altsetting->desc.bInterfaceProtocol
    440			== USB_INTERFACE_PROTOCOL_MOUSE) {
    441		pyra = hid_get_drvdata(hdev);
    442		if (pyra->roccat_claimed)
    443			roccat_disconnect(pyra->chrdev_minor);
    444		kfree(hid_get_drvdata(hdev));
    445	}
    446}
    447
    448static int pyra_probe(struct hid_device *hdev, const struct hid_device_id *id)
    449{
    450	int retval;
    451
    452	if (!hid_is_usb(hdev))
    453		return -EINVAL;
    454
    455	retval = hid_parse(hdev);
    456	if (retval) {
    457		hid_err(hdev, "parse failed\n");
    458		goto exit;
    459	}
    460
    461	retval = hid_hw_start(hdev, HID_CONNECT_DEFAULT);
    462	if (retval) {
    463		hid_err(hdev, "hw start failed\n");
    464		goto exit;
    465	}
    466
    467	retval = pyra_init_specials(hdev);
    468	if (retval) {
    469		hid_err(hdev, "couldn't install mouse\n");
    470		goto exit_stop;
    471	}
    472	return 0;
    473
    474exit_stop:
    475	hid_hw_stop(hdev);
    476exit:
    477	return retval;
    478}
    479
    480static void pyra_remove(struct hid_device *hdev)
    481{
    482	pyra_remove_specials(hdev);
    483	hid_hw_stop(hdev);
    484}
    485
    486static void pyra_keep_values_up_to_date(struct pyra_device *pyra,
    487		u8 const *data)
    488{
    489	struct pyra_mouse_event_button const *button_event;
    490
    491	switch (data[0]) {
    492	case PYRA_MOUSE_REPORT_NUMBER_BUTTON:
    493		button_event = (struct pyra_mouse_event_button const *)data;
    494		switch (button_event->type) {
    495		case PYRA_MOUSE_EVENT_BUTTON_TYPE_PROFILE_2:
    496			profile_activated(pyra, button_event->data1 - 1);
    497			break;
    498		case PYRA_MOUSE_EVENT_BUTTON_TYPE_CPI:
    499			pyra->actual_cpi = button_event->data1;
    500			break;
    501		}
    502		break;
    503	}
    504}
    505
    506static void pyra_report_to_chrdev(struct pyra_device const *pyra,
    507		u8 const *data)
    508{
    509	struct pyra_roccat_report roccat_report;
    510	struct pyra_mouse_event_button const *button_event;
    511
    512	if (data[0] != PYRA_MOUSE_REPORT_NUMBER_BUTTON)
    513		return;
    514
    515	button_event = (struct pyra_mouse_event_button const *)data;
    516
    517	switch (button_event->type) {
    518	case PYRA_MOUSE_EVENT_BUTTON_TYPE_PROFILE_2:
    519	case PYRA_MOUSE_EVENT_BUTTON_TYPE_CPI:
    520		roccat_report.type = button_event->type;
    521		roccat_report.value = button_event->data1;
    522		roccat_report.key = 0;
    523		roccat_report_event(pyra->chrdev_minor,
    524				(uint8_t const *)&roccat_report);
    525		break;
    526	case PYRA_MOUSE_EVENT_BUTTON_TYPE_MACRO:
    527	case PYRA_MOUSE_EVENT_BUTTON_TYPE_SHORTCUT:
    528	case PYRA_MOUSE_EVENT_BUTTON_TYPE_QUICKLAUNCH:
    529		if (button_event->data2 == PYRA_MOUSE_EVENT_BUTTON_PRESS) {
    530			roccat_report.type = button_event->type;
    531			roccat_report.key = button_event->data1;
    532			/*
    533			 * pyra reports profile numbers with range 1-5.
    534			 * Keeping this behaviour.
    535			 */
    536			roccat_report.value = pyra->actual_profile + 1;
    537			roccat_report_event(pyra->chrdev_minor,
    538					(uint8_t const *)&roccat_report);
    539		}
    540		break;
    541	}
    542}
    543
    544static int pyra_raw_event(struct hid_device *hdev, struct hid_report *report,
    545		u8 *data, int size)
    546{
    547	struct usb_interface *intf = to_usb_interface(hdev->dev.parent);
    548	struct pyra_device *pyra = hid_get_drvdata(hdev);
    549
    550	if (intf->cur_altsetting->desc.bInterfaceProtocol
    551			!= USB_INTERFACE_PROTOCOL_MOUSE)
    552		return 0;
    553
    554	if (pyra == NULL)
    555		return 0;
    556
    557	pyra_keep_values_up_to_date(pyra, data);
    558
    559	if (pyra->roccat_claimed)
    560		pyra_report_to_chrdev(pyra, data);
    561
    562	return 0;
    563}
    564
    565static const struct hid_device_id pyra_devices[] = {
    566	{ HID_USB_DEVICE(USB_VENDOR_ID_ROCCAT,
    567			USB_DEVICE_ID_ROCCAT_PYRA_WIRED) },
    568	{ HID_USB_DEVICE(USB_VENDOR_ID_ROCCAT,
    569			USB_DEVICE_ID_ROCCAT_PYRA_WIRELESS) },
    570	{ }
    571};
    572
    573MODULE_DEVICE_TABLE(hid, pyra_devices);
    574
    575static struct hid_driver pyra_driver = {
    576		.name = "pyra",
    577		.id_table = pyra_devices,
    578		.probe = pyra_probe,
    579		.remove = pyra_remove,
    580		.raw_event = pyra_raw_event
    581};
    582
    583static int __init pyra_init(void)
    584{
    585	int retval;
    586
    587	/* class name has to be same as driver name */
    588	pyra_class = class_create(THIS_MODULE, "pyra");
    589	if (IS_ERR(pyra_class))
    590		return PTR_ERR(pyra_class);
    591	pyra_class->dev_groups = pyra_groups;
    592
    593	retval = hid_register_driver(&pyra_driver);
    594	if (retval)
    595		class_destroy(pyra_class);
    596	return retval;
    597}
    598
    599static void __exit pyra_exit(void)
    600{
    601	hid_unregister_driver(&pyra_driver);
    602	class_destroy(pyra_class);
    603}
    604
    605module_init(pyra_init);
    606module_exit(pyra_exit);
    607
    608MODULE_AUTHOR("Stefan Achatz");
    609MODULE_DESCRIPTION("USB Roccat Pyra driver");
    610MODULE_LICENSE("GPL v2");