2 * Roccat Kova[+] driver for Linux
4 * Copyright (c) 2011 Stefan Achatz <erazor_de@users.sourceforge.net>
8 * This program is free software; you can redistribute it and/or modify it
9 * under the terms of the GNU General Public License as published by the Free
10 * Software Foundation; either version 2 of the License, or (at your option)
15 * Roccat Kova[+] is a bigger version of the Pyra with two more side buttons.
18 #include <linux/device.h>
19 #include <linux/input.h>
20 #include <linux/hid.h>
21 #include <linux/module.h>
22 #include <linux/slab.h>
23 #include <linux/hid-roccat.h>
25 #include "hid-roccat-common.h"
26 #include "hid-roccat-kovaplus.h"
28 static uint profile_numbers
[5] = {0, 1, 2, 3, 4};
30 static struct class *kovaplus_class
;
32 static uint
kovaplus_convert_event_cpi(uint value
)
34 return (value
== 7 ? 4 : (value
== 4 ? 3 : value
));
37 static void kovaplus_profile_activated(struct kovaplus_device
*kovaplus
,
38 uint new_profile_index
)
40 kovaplus
->actual_profile
= new_profile_index
;
41 kovaplus
->actual_cpi
= kovaplus
->profile_settings
[new_profile_index
].cpi_startup_level
;
42 kovaplus
->actual_x_sensitivity
= kovaplus
->profile_settings
[new_profile_index
].sensitivity_x
;
43 kovaplus
->actual_y_sensitivity
= kovaplus
->profile_settings
[new_profile_index
].sensitivity_y
;
46 static int kovaplus_send_control(struct usb_device
*usb_dev
, uint value
,
47 enum kovaplus_control_requests request
)
50 struct kovaplus_control control
;
52 if ((request
== KOVAPLUS_CONTROL_REQUEST_PROFILE_SETTINGS
||
53 request
== KOVAPLUS_CONTROL_REQUEST_PROFILE_BUTTONS
) &&
57 control
.command
= KOVAPLUS_COMMAND_CONTROL
;
58 control
.value
= value
;
59 control
.request
= request
;
61 retval
= roccat_common_send(usb_dev
, KOVAPLUS_COMMAND_CONTROL
,
62 &control
, sizeof(struct kovaplus_control
));
67 static int kovaplus_receive_control_status(struct usb_device
*usb_dev
)
70 struct kovaplus_control control
;
73 retval
= roccat_common_receive(usb_dev
, KOVAPLUS_COMMAND_CONTROL
,
74 &control
, sizeof(struct kovaplus_control
));
76 /* check if we get a completely wrong answer */
80 if (control
.value
== KOVAPLUS_CONTROL_REQUEST_STATUS_OK
)
83 /* indicates that hardware needs some more time to complete action */
84 if (control
.value
== KOVAPLUS_CONTROL_REQUEST_STATUS_WAIT
) {
85 msleep(500); /* windows driver uses 1000 */
89 /* seems to be critical - replug necessary */
90 if (control
.value
== KOVAPLUS_CONTROL_REQUEST_STATUS_OVERLOAD
)
93 hid_err(usb_dev
, "roccat_common_receive_control_status: "
94 "unknown response value 0x%x\n", control
.value
);
99 static int kovaplus_send(struct usb_device
*usb_dev
, uint command
,
100 void const *buf
, uint size
)
104 retval
= roccat_common_send(usb_dev
, command
, buf
, size
);
110 return kovaplus_receive_control_status(usb_dev
);
113 static int kovaplus_select_profile(struct usb_device
*usb_dev
, uint number
,
114 enum kovaplus_control_requests request
)
116 return kovaplus_send_control(usb_dev
, number
, request
);
119 static int kovaplus_get_info(struct usb_device
*usb_dev
,
120 struct kovaplus_info
*buf
)
122 return roccat_common_receive(usb_dev
, KOVAPLUS_COMMAND_INFO
,
123 buf
, sizeof(struct kovaplus_info
));
126 static int kovaplus_get_profile_settings(struct usb_device
*usb_dev
,
127 struct kovaplus_profile_settings
*buf
, uint number
)
131 retval
= kovaplus_select_profile(usb_dev
, number
,
132 KOVAPLUS_CONTROL_REQUEST_PROFILE_SETTINGS
);
136 return roccat_common_receive(usb_dev
, KOVAPLUS_COMMAND_PROFILE_SETTINGS
,
137 buf
, sizeof(struct kovaplus_profile_settings
));
140 static int kovaplus_set_profile_settings(struct usb_device
*usb_dev
,
141 struct kovaplus_profile_settings
const *settings
)
143 return kovaplus_send(usb_dev
, KOVAPLUS_COMMAND_PROFILE_SETTINGS
,
144 settings
, sizeof(struct kovaplus_profile_settings
));
147 static int kovaplus_get_profile_buttons(struct usb_device
*usb_dev
,
148 struct kovaplus_profile_buttons
*buf
, int number
)
152 retval
= kovaplus_select_profile(usb_dev
, number
,
153 KOVAPLUS_CONTROL_REQUEST_PROFILE_BUTTONS
);
157 return roccat_common_receive(usb_dev
, KOVAPLUS_COMMAND_PROFILE_BUTTONS
,
158 buf
, sizeof(struct kovaplus_profile_buttons
));
161 static int kovaplus_set_profile_buttons(struct usb_device
*usb_dev
,
162 struct kovaplus_profile_buttons
const *buttons
)
164 return kovaplus_send(usb_dev
, KOVAPLUS_COMMAND_PROFILE_BUTTONS
,
165 buttons
, sizeof(struct kovaplus_profile_buttons
));
168 /* retval is 0-4 on success, < 0 on error */
169 static int kovaplus_get_actual_profile(struct usb_device
*usb_dev
)
171 struct kovaplus_actual_profile buf
;
174 retval
= roccat_common_receive(usb_dev
, KOVAPLUS_COMMAND_ACTUAL_PROFILE
,
175 &buf
, sizeof(struct kovaplus_actual_profile
));
177 return retval
? retval
: buf
.actual_profile
;
180 static int kovaplus_set_actual_profile(struct usb_device
*usb_dev
,
183 struct kovaplus_actual_profile buf
;
185 buf
.command
= KOVAPLUS_COMMAND_ACTUAL_PROFILE
;
186 buf
.size
= sizeof(struct kovaplus_actual_profile
);
187 buf
.actual_profile
= new_profile
;
189 return kovaplus_send(usb_dev
, KOVAPLUS_COMMAND_ACTUAL_PROFILE
,
190 &buf
, sizeof(struct kovaplus_actual_profile
));
193 static ssize_t
kovaplus_sysfs_read_profilex_settings(struct file
*fp
,
194 struct kobject
*kobj
, struct bin_attribute
*attr
, char *buf
,
195 loff_t off
, size_t count
)
198 container_of(kobj
, struct device
, kobj
)->parent
->parent
;
199 struct kovaplus_device
*kovaplus
= hid_get_drvdata(dev_get_drvdata(dev
));
201 if (off
>= sizeof(struct kovaplus_profile_settings
))
204 if (off
+ count
> sizeof(struct kovaplus_profile_settings
))
205 count
= sizeof(struct kovaplus_profile_settings
) - off
;
207 mutex_lock(&kovaplus
->kovaplus_lock
);
208 memcpy(buf
, ((char const *)&kovaplus
->profile_settings
[*(uint
*)(attr
->private)]) + off
,
210 mutex_unlock(&kovaplus
->kovaplus_lock
);
215 static ssize_t
kovaplus_sysfs_write_profile_settings(struct file
*fp
,
216 struct kobject
*kobj
, struct bin_attribute
*attr
, char *buf
,
217 loff_t off
, size_t count
)
220 container_of(kobj
, struct device
, kobj
)->parent
->parent
;
221 struct kovaplus_device
*kovaplus
= hid_get_drvdata(dev_get_drvdata(dev
));
222 struct usb_device
*usb_dev
= interface_to_usbdev(to_usb_interface(dev
));
226 struct kovaplus_profile_settings
*profile_settings
;
228 if (off
!= 0 || count
!= sizeof(struct kovaplus_profile_settings
))
231 profile_index
= ((struct kovaplus_profile_settings
const *)buf
)->profile_index
;
232 profile_settings
= &kovaplus
->profile_settings
[profile_index
];
234 mutex_lock(&kovaplus
->kovaplus_lock
);
235 difference
= memcmp(buf
, profile_settings
,
236 sizeof(struct kovaplus_profile_settings
));
238 retval
= kovaplus_set_profile_settings(usb_dev
,
239 (struct kovaplus_profile_settings
const *)buf
);
241 memcpy(profile_settings
, buf
,
242 sizeof(struct kovaplus_profile_settings
));
244 mutex_unlock(&kovaplus
->kovaplus_lock
);
249 return sizeof(struct kovaplus_profile_settings
);
252 static ssize_t
kovaplus_sysfs_read_profilex_buttons(struct file
*fp
,
253 struct kobject
*kobj
, struct bin_attribute
*attr
, char *buf
,
254 loff_t off
, size_t count
)
257 container_of(kobj
, struct device
, kobj
)->parent
->parent
;
258 struct kovaplus_device
*kovaplus
= hid_get_drvdata(dev_get_drvdata(dev
));
260 if (off
>= sizeof(struct kovaplus_profile_buttons
))
263 if (off
+ count
> sizeof(struct kovaplus_profile_buttons
))
264 count
= sizeof(struct kovaplus_profile_buttons
) - off
;
266 mutex_lock(&kovaplus
->kovaplus_lock
);
267 memcpy(buf
, ((char const *)&kovaplus
->profile_buttons
[*(uint
*)(attr
->private)]) + off
,
269 mutex_unlock(&kovaplus
->kovaplus_lock
);
274 static ssize_t
kovaplus_sysfs_write_profile_buttons(struct file
*fp
,
275 struct kobject
*kobj
, struct bin_attribute
*attr
, char *buf
,
276 loff_t off
, size_t count
)
279 container_of(kobj
, struct device
, kobj
)->parent
->parent
;
280 struct kovaplus_device
*kovaplus
= hid_get_drvdata(dev_get_drvdata(dev
));
281 struct usb_device
*usb_dev
= interface_to_usbdev(to_usb_interface(dev
));
285 struct kovaplus_profile_buttons
*profile_buttons
;
287 if (off
!= 0 || count
!= sizeof(struct kovaplus_profile_buttons
))
290 profile_index
= ((struct kovaplus_profile_buttons
const *)buf
)->profile_index
;
291 profile_buttons
= &kovaplus
->profile_buttons
[profile_index
];
293 mutex_lock(&kovaplus
->kovaplus_lock
);
294 difference
= memcmp(buf
, profile_buttons
,
295 sizeof(struct kovaplus_profile_buttons
));
297 retval
= kovaplus_set_profile_buttons(usb_dev
,
298 (struct kovaplus_profile_buttons
const *)buf
);
300 memcpy(profile_buttons
, buf
,
301 sizeof(struct kovaplus_profile_buttons
));
303 mutex_unlock(&kovaplus
->kovaplus_lock
);
308 return sizeof(struct kovaplus_profile_buttons
);
311 static ssize_t
kovaplus_sysfs_show_actual_profile(struct device
*dev
,
312 struct device_attribute
*attr
, char *buf
)
314 struct kovaplus_device
*kovaplus
=
315 hid_get_drvdata(dev_get_drvdata(dev
->parent
->parent
));
316 return snprintf(buf
, PAGE_SIZE
, "%d\n", kovaplus
->actual_profile
);
319 static ssize_t
kovaplus_sysfs_set_actual_profile(struct device
*dev
,
320 struct device_attribute
*attr
, char const *buf
, size_t size
)
322 struct kovaplus_device
*kovaplus
;
323 struct usb_device
*usb_dev
;
324 unsigned long profile
;
327 dev
= dev
->parent
->parent
;
328 kovaplus
= hid_get_drvdata(dev_get_drvdata(dev
));
329 usb_dev
= interface_to_usbdev(to_usb_interface(dev
));
331 retval
= strict_strtoul(buf
, 10, &profile
);
338 mutex_lock(&kovaplus
->kovaplus_lock
);
339 retval
= kovaplus_set_actual_profile(usb_dev
, profile
);
340 kovaplus_profile_activated(kovaplus
, profile
);
341 mutex_unlock(&kovaplus
->kovaplus_lock
);
348 static ssize_t
kovaplus_sysfs_show_actual_cpi(struct device
*dev
,
349 struct device_attribute
*attr
, char *buf
)
351 struct kovaplus_device
*kovaplus
=
352 hid_get_drvdata(dev_get_drvdata(dev
->parent
->parent
));
353 return snprintf(buf
, PAGE_SIZE
, "%d\n", kovaplus
->actual_cpi
);
356 static ssize_t
kovaplus_sysfs_show_actual_sensitivity_x(struct device
*dev
,
357 struct device_attribute
*attr
, char *buf
)
359 struct kovaplus_device
*kovaplus
=
360 hid_get_drvdata(dev_get_drvdata(dev
->parent
->parent
));
361 return snprintf(buf
, PAGE_SIZE
, "%d\n", kovaplus
->actual_x_sensitivity
);
364 static ssize_t
kovaplus_sysfs_show_actual_sensitivity_y(struct device
*dev
,
365 struct device_attribute
*attr
, char *buf
)
367 struct kovaplus_device
*kovaplus
=
368 hid_get_drvdata(dev_get_drvdata(dev
->parent
->parent
));
369 return snprintf(buf
, PAGE_SIZE
, "%d\n", kovaplus
->actual_y_sensitivity
);
372 static ssize_t
kovaplus_sysfs_show_firmware_version(struct device
*dev
,
373 struct device_attribute
*attr
, char *buf
)
375 struct kovaplus_device
*kovaplus
=
376 hid_get_drvdata(dev_get_drvdata(dev
->parent
->parent
));
377 return snprintf(buf
, PAGE_SIZE
, "%d\n", kovaplus
->info
.firmware_version
);
380 static struct device_attribute kovaplus_attributes
[] = {
381 __ATTR(actual_cpi
, 0440,
382 kovaplus_sysfs_show_actual_cpi
, NULL
),
383 __ATTR(firmware_version
, 0440,
384 kovaplus_sysfs_show_firmware_version
, NULL
),
385 __ATTR(actual_profile
, 0660,
386 kovaplus_sysfs_show_actual_profile
,
387 kovaplus_sysfs_set_actual_profile
),
388 __ATTR(actual_sensitivity_x
, 0440,
389 kovaplus_sysfs_show_actual_sensitivity_x
, NULL
),
390 __ATTR(actual_sensitivity_y
, 0440,
391 kovaplus_sysfs_show_actual_sensitivity_y
, NULL
),
395 static struct bin_attribute kovaplus_bin_attributes
[] = {
397 .attr
= { .name
= "profile_settings", .mode
= 0220 },
398 .size
= sizeof(struct kovaplus_profile_settings
),
399 .write
= kovaplus_sysfs_write_profile_settings
402 .attr
= { .name
= "profile1_settings", .mode
= 0440 },
403 .size
= sizeof(struct kovaplus_profile_settings
),
404 .read
= kovaplus_sysfs_read_profilex_settings
,
405 .private = &profile_numbers
[0]
408 .attr
= { .name
= "profile2_settings", .mode
= 0440 },
409 .size
= sizeof(struct kovaplus_profile_settings
),
410 .read
= kovaplus_sysfs_read_profilex_settings
,
411 .private = &profile_numbers
[1]
414 .attr
= { .name
= "profile3_settings", .mode
= 0440 },
415 .size
= sizeof(struct kovaplus_profile_settings
),
416 .read
= kovaplus_sysfs_read_profilex_settings
,
417 .private = &profile_numbers
[2]
420 .attr
= { .name
= "profile4_settings", .mode
= 0440 },
421 .size
= sizeof(struct kovaplus_profile_settings
),
422 .read
= kovaplus_sysfs_read_profilex_settings
,
423 .private = &profile_numbers
[3]
426 .attr
= { .name
= "profile5_settings", .mode
= 0440 },
427 .size
= sizeof(struct kovaplus_profile_settings
),
428 .read
= kovaplus_sysfs_read_profilex_settings
,
429 .private = &profile_numbers
[4]
432 .attr
= { .name
= "profile_buttons", .mode
= 0220 },
433 .size
= sizeof(struct kovaplus_profile_buttons
),
434 .write
= kovaplus_sysfs_write_profile_buttons
437 .attr
= { .name
= "profile1_buttons", .mode
= 0440 },
438 .size
= sizeof(struct kovaplus_profile_buttons
),
439 .read
= kovaplus_sysfs_read_profilex_buttons
,
440 .private = &profile_numbers
[0]
443 .attr
= { .name
= "profile2_buttons", .mode
= 0440 },
444 .size
= sizeof(struct kovaplus_profile_buttons
),
445 .read
= kovaplus_sysfs_read_profilex_buttons
,
446 .private = &profile_numbers
[1]
449 .attr
= { .name
= "profile3_buttons", .mode
= 0440 },
450 .size
= sizeof(struct kovaplus_profile_buttons
),
451 .read
= kovaplus_sysfs_read_profilex_buttons
,
452 .private = &profile_numbers
[2]
455 .attr
= { .name
= "profile4_buttons", .mode
= 0440 },
456 .size
= sizeof(struct kovaplus_profile_buttons
),
457 .read
= kovaplus_sysfs_read_profilex_buttons
,
458 .private = &profile_numbers
[3]
461 .attr
= { .name
= "profile5_buttons", .mode
= 0440 },
462 .size
= sizeof(struct kovaplus_profile_buttons
),
463 .read
= kovaplus_sysfs_read_profilex_buttons
,
464 .private = &profile_numbers
[4]
469 static int kovaplus_init_kovaplus_device_struct(struct usb_device
*usb_dev
,
470 struct kovaplus_device
*kovaplus
)
473 static uint wait
= 70; /* device will freeze with just 60 */
475 mutex_init(&kovaplus
->kovaplus_lock
);
477 retval
= kovaplus_get_info(usb_dev
, &kovaplus
->info
);
481 for (i
= 0; i
< 5; ++i
) {
483 retval
= kovaplus_get_profile_settings(usb_dev
,
484 &kovaplus
->profile_settings
[i
], i
);
489 retval
= kovaplus_get_profile_buttons(usb_dev
,
490 &kovaplus
->profile_buttons
[i
], i
);
496 retval
= kovaplus_get_actual_profile(usb_dev
);
499 kovaplus_profile_activated(kovaplus
, retval
);
504 static int kovaplus_init_specials(struct hid_device
*hdev
)
506 struct usb_interface
*intf
= to_usb_interface(hdev
->dev
.parent
);
507 struct usb_device
*usb_dev
= interface_to_usbdev(intf
);
508 struct kovaplus_device
*kovaplus
;
511 if (intf
->cur_altsetting
->desc
.bInterfaceProtocol
512 == USB_INTERFACE_PROTOCOL_MOUSE
) {
514 kovaplus
= kzalloc(sizeof(*kovaplus
), GFP_KERNEL
);
516 hid_err(hdev
, "can't alloc device descriptor\n");
519 hid_set_drvdata(hdev
, kovaplus
);
521 retval
= kovaplus_init_kovaplus_device_struct(usb_dev
, kovaplus
);
523 hid_err(hdev
, "couldn't init struct kovaplus_device\n");
527 retval
= roccat_connect(kovaplus_class
, hdev
,
528 sizeof(struct kovaplus_roccat_report
));
530 hid_err(hdev
, "couldn't init char dev\n");
532 kovaplus
->chrdev_minor
= retval
;
533 kovaplus
->roccat_claimed
= 1;
537 hid_set_drvdata(hdev
, NULL
);
546 static void kovaplus_remove_specials(struct hid_device
*hdev
)
548 struct usb_interface
*intf
= to_usb_interface(hdev
->dev
.parent
);
549 struct kovaplus_device
*kovaplus
;
551 if (intf
->cur_altsetting
->desc
.bInterfaceProtocol
552 == USB_INTERFACE_PROTOCOL_MOUSE
) {
553 kovaplus
= hid_get_drvdata(hdev
);
554 if (kovaplus
->roccat_claimed
)
555 roccat_disconnect(kovaplus
->chrdev_minor
);
560 static int kovaplus_probe(struct hid_device
*hdev
,
561 const struct hid_device_id
*id
)
565 retval
= hid_parse(hdev
);
567 hid_err(hdev
, "parse failed\n");
571 retval
= hid_hw_start(hdev
, HID_CONNECT_DEFAULT
);
573 hid_err(hdev
, "hw start failed\n");
577 retval
= kovaplus_init_specials(hdev
);
579 hid_err(hdev
, "couldn't install mouse\n");
591 static void kovaplus_remove(struct hid_device
*hdev
)
593 kovaplus_remove_specials(hdev
);
597 static void kovaplus_keep_values_up_to_date(struct kovaplus_device
*kovaplus
,
600 struct kovaplus_mouse_report_button
const *button_report
;
602 if (data
[0] != KOVAPLUS_MOUSE_REPORT_NUMBER_BUTTON
)
605 button_report
= (struct kovaplus_mouse_report_button
const *)data
;
607 switch (button_report
->type
) {
608 case KOVAPLUS_MOUSE_REPORT_BUTTON_TYPE_PROFILE_1
:
609 kovaplus_profile_activated(kovaplus
, button_report
->data1
- 1);
611 case KOVAPLUS_MOUSE_REPORT_BUTTON_TYPE_CPI
:
612 kovaplus
->actual_cpi
= kovaplus_convert_event_cpi(button_report
->data1
);
613 case KOVAPLUS_MOUSE_REPORT_BUTTON_TYPE_SENSITIVITY
:
614 kovaplus
->actual_x_sensitivity
= button_report
->data1
;
615 kovaplus
->actual_y_sensitivity
= button_report
->data2
;
619 static void kovaplus_report_to_chrdev(struct kovaplus_device
const *kovaplus
,
622 struct kovaplus_roccat_report roccat_report
;
623 struct kovaplus_mouse_report_button
const *button_report
;
625 if (data
[0] != KOVAPLUS_MOUSE_REPORT_NUMBER_BUTTON
)
628 button_report
= (struct kovaplus_mouse_report_button
const *)data
;
630 if (button_report
->type
== KOVAPLUS_MOUSE_REPORT_BUTTON_TYPE_PROFILE_2
)
633 roccat_report
.type
= button_report
->type
;
634 roccat_report
.profile
= kovaplus
->actual_profile
+ 1;
636 if (roccat_report
.type
== KOVAPLUS_MOUSE_REPORT_BUTTON_TYPE_MACRO
||
637 roccat_report
.type
== KOVAPLUS_MOUSE_REPORT_BUTTON_TYPE_SHORTCUT
||
638 roccat_report
.type
== KOVAPLUS_MOUSE_REPORT_BUTTON_TYPE_QUICKLAUNCH
||
639 roccat_report
.type
== KOVAPLUS_MOUSE_REPORT_BUTTON_TYPE_TIMER
)
640 roccat_report
.button
= button_report
->data1
;
642 roccat_report
.button
= 0;
644 if (roccat_report
.type
== KOVAPLUS_MOUSE_REPORT_BUTTON_TYPE_CPI
)
645 roccat_report
.data1
= kovaplus_convert_event_cpi(button_report
->data1
);
647 roccat_report
.data1
= button_report
->data1
;
649 roccat_report
.data2
= button_report
->data2
;
651 roccat_report_event(kovaplus
->chrdev_minor
,
652 (uint8_t const *)&roccat_report
);
655 static int kovaplus_raw_event(struct hid_device
*hdev
,
656 struct hid_report
*report
, u8
*data
, int size
)
658 struct usb_interface
*intf
= to_usb_interface(hdev
->dev
.parent
);
659 struct kovaplus_device
*kovaplus
= hid_get_drvdata(hdev
);
661 if (intf
->cur_altsetting
->desc
.bInterfaceProtocol
662 != USB_INTERFACE_PROTOCOL_MOUSE
)
665 if (kovaplus
== NULL
)
668 kovaplus_keep_values_up_to_date(kovaplus
, data
);
670 if (kovaplus
->roccat_claimed
)
671 kovaplus_report_to_chrdev(kovaplus
, data
);
676 static const struct hid_device_id kovaplus_devices
[] = {
677 { HID_USB_DEVICE(USB_VENDOR_ID_ROCCAT
, USB_DEVICE_ID_ROCCAT_KOVAPLUS
) },
681 MODULE_DEVICE_TABLE(hid
, kovaplus_devices
);
683 static struct hid_driver kovaplus_driver
= {
685 .id_table
= kovaplus_devices
,
686 .probe
= kovaplus_probe
,
687 .remove
= kovaplus_remove
,
688 .raw_event
= kovaplus_raw_event
691 static int __init
kovaplus_init(void)
695 kovaplus_class
= class_create(THIS_MODULE
, "kovaplus");
696 if (IS_ERR(kovaplus_class
))
697 return PTR_ERR(kovaplus_class
);
698 kovaplus_class
->dev_attrs
= kovaplus_attributes
;
699 kovaplus_class
->dev_bin_attrs
= kovaplus_bin_attributes
;
701 retval
= hid_register_driver(&kovaplus_driver
);
703 class_destroy(kovaplus_class
);
707 static void __exit
kovaplus_exit(void)
709 hid_unregister_driver(&kovaplus_driver
);
710 class_destroy(kovaplus_class
);
713 module_init(kovaplus_init
);
714 module_exit(kovaplus_exit
);
716 MODULE_AUTHOR("Stefan Achatz");
717 MODULE_DESCRIPTION("USB Roccat Kova[+] driver");
718 MODULE_LICENSE("GPL v2");