root/drivers/hid/hid-roccat-koneplus.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. koneplus_profile_activated
  2. koneplus_send_control
  3. koneplus_get_actual_profile
  4. koneplus_set_actual_profile
  5. koneplus_sysfs_read
  6. koneplus_sysfs_write
  7. koneplus_sysfs_read_profilex_settings
  8. koneplus_sysfs_read_profilex_buttons
  9. koneplus_sysfs_show_actual_profile
  10. koneplus_sysfs_set_actual_profile
  11. koneplus_sysfs_show_firmware_version
  12. koneplus_init_koneplus_device_struct
  13. koneplus_init_specials
  14. koneplus_remove_specials
  15. koneplus_probe
  16. koneplus_remove
  17. koneplus_keep_values_up_to_date
  18. koneplus_report_to_chrdev
  19. koneplus_raw_event
  20. koneplus_init
  21. koneplus_exit

   1 // SPDX-License-Identifier: GPL-2.0-or-later
   2 /*
   3  * Roccat Kone[+] driver for Linux
   4  *
   5  * Copyright (c) 2010 Stefan Achatz <erazor_de@users.sourceforge.net>
   6  */
   7 
   8 /*
   9  */
  10 
  11 /*
  12  * Roccat Kone[+] is an updated/improved version of the Kone with more memory
  13  * and functionality and without the non-standard behaviours the Kone had.
  14  * KoneXTD has same capabilities but updated sensor.
  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-koneplus.h"
  26 
  27 static uint profile_numbers[5] = {0, 1, 2, 3, 4};
  28 
  29 static struct class *koneplus_class;
  30 
  31 static void koneplus_profile_activated(struct koneplus_device *koneplus,
  32                 uint new_profile)
  33 {
  34         koneplus->actual_profile = new_profile;
  35 }
  36 
  37 static int koneplus_send_control(struct usb_device *usb_dev, uint value,
  38                 enum koneplus_control_requests request)
  39 {
  40         struct roccat_common2_control control;
  41 
  42         if ((request == KONEPLUS_CONTROL_REQUEST_PROFILE_SETTINGS ||
  43                         request == KONEPLUS_CONTROL_REQUEST_PROFILE_BUTTONS) &&
  44                         value > 4)
  45                 return -EINVAL;
  46 
  47         control.command = ROCCAT_COMMON_COMMAND_CONTROL;
  48         control.value = value;
  49         control.request = request;
  50 
  51         return roccat_common2_send_with_status(usb_dev,
  52                         ROCCAT_COMMON_COMMAND_CONTROL,
  53                         &control, sizeof(struct roccat_common2_control));
  54 }
  55 
  56 
  57 /* retval is 0-4 on success, < 0 on error */
  58 static int koneplus_get_actual_profile(struct usb_device *usb_dev)
  59 {
  60         struct koneplus_actual_profile buf;
  61         int retval;
  62 
  63         retval = roccat_common2_receive(usb_dev, KONEPLUS_COMMAND_ACTUAL_PROFILE,
  64                         &buf, KONEPLUS_SIZE_ACTUAL_PROFILE);
  65 
  66         return retval ? retval : buf.actual_profile;
  67 }
  68 
  69 static int koneplus_set_actual_profile(struct usb_device *usb_dev,
  70                 int new_profile)
  71 {
  72         struct koneplus_actual_profile buf;
  73 
  74         buf.command = KONEPLUS_COMMAND_ACTUAL_PROFILE;
  75         buf.size = KONEPLUS_SIZE_ACTUAL_PROFILE;
  76         buf.actual_profile = new_profile;
  77 
  78         return roccat_common2_send_with_status(usb_dev,
  79                         KONEPLUS_COMMAND_ACTUAL_PROFILE,
  80                         &buf, KONEPLUS_SIZE_ACTUAL_PROFILE);
  81 }
  82 
  83 static ssize_t koneplus_sysfs_read(struct file *fp, struct kobject *kobj,
  84                 char *buf, loff_t off, size_t count,
  85                 size_t real_size, uint command)
  86 {
  87         struct device *dev = kobj_to_dev(kobj)->parent->parent;
  88         struct koneplus_device *koneplus = hid_get_drvdata(dev_get_drvdata(dev));
  89         struct usb_device *usb_dev = interface_to_usbdev(to_usb_interface(dev));
  90         int retval;
  91 
  92         if (off >= real_size)
  93                 return 0;
  94 
  95         if (off != 0 || count != real_size)
  96                 return -EINVAL;
  97 
  98         mutex_lock(&koneplus->koneplus_lock);
  99         retval = roccat_common2_receive(usb_dev, command, buf, real_size);
 100         mutex_unlock(&koneplus->koneplus_lock);
 101 
 102         if (retval)
 103                 return retval;
 104 
 105         return real_size;
 106 }
 107 
 108 static ssize_t koneplus_sysfs_write(struct file *fp, struct kobject *kobj,
 109                 void const *buf, loff_t off, size_t count,
 110                 size_t real_size, uint command)
 111 {
 112         struct device *dev = kobj_to_dev(kobj)->parent->parent;
 113         struct koneplus_device *koneplus = hid_get_drvdata(dev_get_drvdata(dev));
 114         struct usb_device *usb_dev = interface_to_usbdev(to_usb_interface(dev));
 115         int retval;
 116 
 117         if (off != 0 || count != real_size)
 118                 return -EINVAL;
 119 
 120         mutex_lock(&koneplus->koneplus_lock);
 121         retval = roccat_common2_send_with_status(usb_dev, command,
 122                         buf, real_size);
 123         mutex_unlock(&koneplus->koneplus_lock);
 124 
 125         if (retval)
 126                 return retval;
 127 
 128         return real_size;
 129 }
 130 
 131 #define KONEPLUS_SYSFS_W(thingy, THINGY) \
 132 static ssize_t koneplus_sysfs_write_ ## thingy(struct file *fp, \
 133                 struct kobject *kobj, struct bin_attribute *attr, char *buf, \
 134                 loff_t off, size_t count) \
 135 { \
 136         return koneplus_sysfs_write(fp, kobj, buf, off, count, \
 137                         KONEPLUS_SIZE_ ## THINGY, KONEPLUS_COMMAND_ ## THINGY); \
 138 }
 139 
 140 #define KONEPLUS_SYSFS_R(thingy, THINGY) \
 141 static ssize_t koneplus_sysfs_read_ ## thingy(struct file *fp, \
 142                 struct kobject *kobj, struct bin_attribute *attr, char *buf, \
 143                 loff_t off, size_t count) \
 144 { \
 145         return koneplus_sysfs_read(fp, kobj, buf, off, count, \
 146                         KONEPLUS_SIZE_ ## THINGY, KONEPLUS_COMMAND_ ## THINGY); \
 147 }
 148 
 149 #define KONEPLUS_SYSFS_RW(thingy, THINGY) \
 150 KONEPLUS_SYSFS_W(thingy, THINGY) \
 151 KONEPLUS_SYSFS_R(thingy, THINGY)
 152 
 153 #define KONEPLUS_BIN_ATTRIBUTE_RW(thingy, THINGY) \
 154 KONEPLUS_SYSFS_RW(thingy, THINGY); \
 155 static struct bin_attribute bin_attr_##thingy = { \
 156         .attr = { .name = #thingy, .mode = 0660 }, \
 157         .size = KONEPLUS_SIZE_ ## THINGY, \
 158         .read = koneplus_sysfs_read_ ## thingy, \
 159         .write = koneplus_sysfs_write_ ## thingy \
 160 }
 161 
 162 #define KONEPLUS_BIN_ATTRIBUTE_R(thingy, THINGY) \
 163 KONEPLUS_SYSFS_R(thingy, THINGY); \
 164 static struct bin_attribute bin_attr_##thingy = { \
 165         .attr = { .name = #thingy, .mode = 0440 }, \
 166         .size = KONEPLUS_SIZE_ ## THINGY, \
 167         .read = koneplus_sysfs_read_ ## thingy, \
 168 }
 169 
 170 #define KONEPLUS_BIN_ATTRIBUTE_W(thingy, THINGY) \
 171 KONEPLUS_SYSFS_W(thingy, THINGY); \
 172 static struct bin_attribute bin_attr_##thingy = { \
 173         .attr = { .name = #thingy, .mode = 0220 }, \
 174         .size = KONEPLUS_SIZE_ ## THINGY, \
 175         .write = koneplus_sysfs_write_ ## thingy \
 176 }
 177 KONEPLUS_BIN_ATTRIBUTE_W(control, CONTROL);
 178 KONEPLUS_BIN_ATTRIBUTE_W(talk, TALK);
 179 KONEPLUS_BIN_ATTRIBUTE_W(macro, MACRO);
 180 KONEPLUS_BIN_ATTRIBUTE_R(tcu_image, TCU_IMAGE);
 181 KONEPLUS_BIN_ATTRIBUTE_RW(info, INFO);
 182 KONEPLUS_BIN_ATTRIBUTE_RW(sensor, SENSOR);
 183 KONEPLUS_BIN_ATTRIBUTE_RW(tcu, TCU);
 184 KONEPLUS_BIN_ATTRIBUTE_RW(profile_settings, PROFILE_SETTINGS);
 185 KONEPLUS_BIN_ATTRIBUTE_RW(profile_buttons, PROFILE_BUTTONS);
 186 
 187 static ssize_t koneplus_sysfs_read_profilex_settings(struct file *fp,
 188                 struct kobject *kobj, struct bin_attribute *attr, char *buf,
 189                 loff_t off, size_t count)
 190 {
 191         struct device *dev = kobj_to_dev(kobj)->parent->parent;
 192         struct usb_device *usb_dev = interface_to_usbdev(to_usb_interface(dev));
 193         ssize_t retval;
 194 
 195         retval = koneplus_send_control(usb_dev, *(uint *)(attr->private),
 196                         KONEPLUS_CONTROL_REQUEST_PROFILE_SETTINGS);
 197         if (retval)
 198                 return retval;
 199 
 200         return koneplus_sysfs_read(fp, kobj, buf, off, count,
 201                         KONEPLUS_SIZE_PROFILE_SETTINGS,
 202                         KONEPLUS_COMMAND_PROFILE_SETTINGS);
 203 }
 204 
 205 static ssize_t koneplus_sysfs_read_profilex_buttons(struct file *fp,
 206                 struct kobject *kobj, struct bin_attribute *attr, char *buf,
 207                 loff_t off, size_t count)
 208 {
 209         struct device *dev = kobj_to_dev(kobj)->parent->parent;
 210         struct usb_device *usb_dev = interface_to_usbdev(to_usb_interface(dev));
 211         ssize_t retval;
 212 
 213         retval = koneplus_send_control(usb_dev, *(uint *)(attr->private),
 214                         KONEPLUS_CONTROL_REQUEST_PROFILE_BUTTONS);
 215         if (retval)
 216                 return retval;
 217 
 218         return koneplus_sysfs_read(fp, kobj, buf, off, count,
 219                         KONEPLUS_SIZE_PROFILE_BUTTONS,
 220                         KONEPLUS_COMMAND_PROFILE_BUTTONS);
 221 }
 222 
 223 #define PROFILE_ATTR(number)                                            \
 224 static struct bin_attribute bin_attr_profile##number##_settings = {     \
 225         .attr = { .name = "profile" #number "_settings", .mode = 0440 },        \
 226         .size = KONEPLUS_SIZE_PROFILE_SETTINGS,                         \
 227         .read = koneplus_sysfs_read_profilex_settings,                  \
 228         .private = &profile_numbers[number-1],                          \
 229 };                                                                      \
 230 static struct bin_attribute bin_attr_profile##number##_buttons = {      \
 231         .attr = { .name = "profile" #number "_buttons", .mode = 0440 }, \
 232         .size = KONEPLUS_SIZE_PROFILE_BUTTONS,                          \
 233         .read = koneplus_sysfs_read_profilex_buttons,                   \
 234         .private = &profile_numbers[number-1],                          \
 235 };
 236 PROFILE_ATTR(1);
 237 PROFILE_ATTR(2);
 238 PROFILE_ATTR(3);
 239 PROFILE_ATTR(4);
 240 PROFILE_ATTR(5);
 241 
 242 static ssize_t koneplus_sysfs_show_actual_profile(struct device *dev,
 243                 struct device_attribute *attr, char *buf)
 244 {
 245         struct koneplus_device *koneplus =
 246                         hid_get_drvdata(dev_get_drvdata(dev->parent->parent));
 247         return snprintf(buf, PAGE_SIZE, "%d\n", koneplus->actual_profile);
 248 }
 249 
 250 static ssize_t koneplus_sysfs_set_actual_profile(struct device *dev,
 251                 struct device_attribute *attr, char const *buf, size_t size)
 252 {
 253         struct koneplus_device *koneplus;
 254         struct usb_device *usb_dev;
 255         unsigned long profile;
 256         int retval;
 257         struct koneplus_roccat_report roccat_report;
 258 
 259         dev = dev->parent->parent;
 260         koneplus = hid_get_drvdata(dev_get_drvdata(dev));
 261         usb_dev = interface_to_usbdev(to_usb_interface(dev));
 262 
 263         retval = kstrtoul(buf, 10, &profile);
 264         if (retval)
 265                 return retval;
 266 
 267         if (profile > 4)
 268                 return -EINVAL;
 269 
 270         mutex_lock(&koneplus->koneplus_lock);
 271 
 272         retval = koneplus_set_actual_profile(usb_dev, profile);
 273         if (retval) {
 274                 mutex_unlock(&koneplus->koneplus_lock);
 275                 return retval;
 276         }
 277 
 278         koneplus_profile_activated(koneplus, profile);
 279 
 280         roccat_report.type = KONEPLUS_MOUSE_REPORT_BUTTON_TYPE_PROFILE;
 281         roccat_report.data1 = profile + 1;
 282         roccat_report.data2 = 0;
 283         roccat_report.profile = profile + 1;
 284         roccat_report_event(koneplus->chrdev_minor,
 285                         (uint8_t const *)&roccat_report);
 286 
 287         mutex_unlock(&koneplus->koneplus_lock);
 288 
 289         return size;
 290 }
 291 static DEVICE_ATTR(actual_profile, 0660,
 292                    koneplus_sysfs_show_actual_profile,
 293                    koneplus_sysfs_set_actual_profile);
 294 static DEVICE_ATTR(startup_profile, 0660,
 295                    koneplus_sysfs_show_actual_profile,
 296                    koneplus_sysfs_set_actual_profile);
 297 
 298 static ssize_t koneplus_sysfs_show_firmware_version(struct device *dev,
 299                 struct device_attribute *attr, char *buf)
 300 {
 301         struct koneplus_device *koneplus;
 302         struct usb_device *usb_dev;
 303         struct koneplus_info info;
 304 
 305         dev = dev->parent->parent;
 306         koneplus = hid_get_drvdata(dev_get_drvdata(dev));
 307         usb_dev = interface_to_usbdev(to_usb_interface(dev));
 308 
 309         mutex_lock(&koneplus->koneplus_lock);
 310         roccat_common2_receive(usb_dev, KONEPLUS_COMMAND_INFO,
 311                         &info, KONEPLUS_SIZE_INFO);
 312         mutex_unlock(&koneplus->koneplus_lock);
 313 
 314         return snprintf(buf, PAGE_SIZE, "%d\n", info.firmware_version);
 315 }
 316 static DEVICE_ATTR(firmware_version, 0440,
 317                    koneplus_sysfs_show_firmware_version, NULL);
 318 
 319 static struct attribute *koneplus_attrs[] = {
 320         &dev_attr_actual_profile.attr,
 321         &dev_attr_startup_profile.attr,
 322         &dev_attr_firmware_version.attr,
 323         NULL,
 324 };
 325 
 326 static struct bin_attribute *koneplus_bin_attributes[] = {
 327         &bin_attr_control,
 328         &bin_attr_talk,
 329         &bin_attr_macro,
 330         &bin_attr_tcu_image,
 331         &bin_attr_info,
 332         &bin_attr_sensor,
 333         &bin_attr_tcu,
 334         &bin_attr_profile_settings,
 335         &bin_attr_profile_buttons,
 336         &bin_attr_profile1_settings,
 337         &bin_attr_profile2_settings,
 338         &bin_attr_profile3_settings,
 339         &bin_attr_profile4_settings,
 340         &bin_attr_profile5_settings,
 341         &bin_attr_profile1_buttons,
 342         &bin_attr_profile2_buttons,
 343         &bin_attr_profile3_buttons,
 344         &bin_attr_profile4_buttons,
 345         &bin_attr_profile5_buttons,
 346         NULL,
 347 };
 348 
 349 static const struct attribute_group koneplus_group = {
 350         .attrs = koneplus_attrs,
 351         .bin_attrs = koneplus_bin_attributes,
 352 };
 353 
 354 static const struct attribute_group *koneplus_groups[] = {
 355         &koneplus_group,
 356         NULL,
 357 };
 358 
 359 static int koneplus_init_koneplus_device_struct(struct usb_device *usb_dev,
 360                 struct koneplus_device *koneplus)
 361 {
 362         int retval;
 363 
 364         mutex_init(&koneplus->koneplus_lock);
 365 
 366         retval = koneplus_get_actual_profile(usb_dev);
 367         if (retval < 0)
 368                 return retval;
 369         koneplus_profile_activated(koneplus, retval);
 370 
 371         return 0;
 372 }
 373 
 374 static int koneplus_init_specials(struct hid_device *hdev)
 375 {
 376         struct usb_interface *intf = to_usb_interface(hdev->dev.parent);
 377         struct usb_device *usb_dev = interface_to_usbdev(intf);
 378         struct koneplus_device *koneplus;
 379         int retval;
 380 
 381         if (intf->cur_altsetting->desc.bInterfaceProtocol
 382                         == USB_INTERFACE_PROTOCOL_MOUSE) {
 383 
 384                 koneplus = kzalloc(sizeof(*koneplus), GFP_KERNEL);
 385                 if (!koneplus) {
 386                         hid_err(hdev, "can't alloc device descriptor\n");
 387                         return -ENOMEM;
 388                 }
 389                 hid_set_drvdata(hdev, koneplus);
 390 
 391                 retval = koneplus_init_koneplus_device_struct(usb_dev, koneplus);
 392                 if (retval) {
 393                         hid_err(hdev, "couldn't init struct koneplus_device\n");
 394                         goto exit_free;
 395                 }
 396 
 397                 retval = roccat_connect(koneplus_class, hdev,
 398                                 sizeof(struct koneplus_roccat_report));
 399                 if (retval < 0) {
 400                         hid_err(hdev, "couldn't init char dev\n");
 401                 } else {
 402                         koneplus->chrdev_minor = retval;
 403                         koneplus->roccat_claimed = 1;
 404                 }
 405         } else {
 406                 hid_set_drvdata(hdev, NULL);
 407         }
 408 
 409         return 0;
 410 exit_free:
 411         kfree(koneplus);
 412         return retval;
 413 }
 414 
 415 static void koneplus_remove_specials(struct hid_device *hdev)
 416 {
 417         struct usb_interface *intf = to_usb_interface(hdev->dev.parent);
 418         struct koneplus_device *koneplus;
 419 
 420         if (intf->cur_altsetting->desc.bInterfaceProtocol
 421                         == USB_INTERFACE_PROTOCOL_MOUSE) {
 422                 koneplus = hid_get_drvdata(hdev);
 423                 if (koneplus->roccat_claimed)
 424                         roccat_disconnect(koneplus->chrdev_minor);
 425                 kfree(koneplus);
 426         }
 427 }
 428 
 429 static int koneplus_probe(struct hid_device *hdev,
 430                 const struct hid_device_id *id)
 431 {
 432         int retval;
 433 
 434         retval = hid_parse(hdev);
 435         if (retval) {
 436                 hid_err(hdev, "parse failed\n");
 437                 goto exit;
 438         }
 439 
 440         retval = hid_hw_start(hdev, HID_CONNECT_DEFAULT);
 441         if (retval) {
 442                 hid_err(hdev, "hw start failed\n");
 443                 goto exit;
 444         }
 445 
 446         retval = koneplus_init_specials(hdev);
 447         if (retval) {
 448                 hid_err(hdev, "couldn't install mouse\n");
 449                 goto exit_stop;
 450         }
 451 
 452         return 0;
 453 
 454 exit_stop:
 455         hid_hw_stop(hdev);
 456 exit:
 457         return retval;
 458 }
 459 
 460 static void koneplus_remove(struct hid_device *hdev)
 461 {
 462         koneplus_remove_specials(hdev);
 463         hid_hw_stop(hdev);
 464 }
 465 
 466 static void koneplus_keep_values_up_to_date(struct koneplus_device *koneplus,
 467                 u8 const *data)
 468 {
 469         struct koneplus_mouse_report_button const *button_report;
 470 
 471         switch (data[0]) {
 472         case KONEPLUS_MOUSE_REPORT_NUMBER_BUTTON:
 473                 button_report = (struct koneplus_mouse_report_button const *)data;
 474                 switch (button_report->type) {
 475                 case KONEPLUS_MOUSE_REPORT_BUTTON_TYPE_PROFILE:
 476                         koneplus_profile_activated(koneplus, button_report->data1 - 1);
 477                         break;
 478                 }
 479                 break;
 480         }
 481 }
 482 
 483 static void koneplus_report_to_chrdev(struct koneplus_device const *koneplus,
 484                 u8 const *data)
 485 {
 486         struct koneplus_roccat_report roccat_report;
 487         struct koneplus_mouse_report_button const *button_report;
 488 
 489         if (data[0] != KONEPLUS_MOUSE_REPORT_NUMBER_BUTTON)
 490                 return;
 491 
 492         button_report = (struct koneplus_mouse_report_button const *)data;
 493 
 494         if ((button_report->type == KONEPLUS_MOUSE_REPORT_BUTTON_TYPE_QUICKLAUNCH ||
 495                         button_report->type == KONEPLUS_MOUSE_REPORT_BUTTON_TYPE_TIMER) &&
 496                         button_report->data2 != KONEPLUS_MOUSE_REPORT_BUTTON_ACTION_PRESS)
 497                 return;
 498 
 499         roccat_report.type = button_report->type;
 500         roccat_report.data1 = button_report->data1;
 501         roccat_report.data2 = button_report->data2;
 502         roccat_report.profile = koneplus->actual_profile + 1;
 503         roccat_report_event(koneplus->chrdev_minor,
 504                         (uint8_t const *)&roccat_report);
 505 }
 506 
 507 static int koneplus_raw_event(struct hid_device *hdev,
 508                 struct hid_report *report, u8 *data, int size)
 509 {
 510         struct usb_interface *intf = to_usb_interface(hdev->dev.parent);
 511         struct koneplus_device *koneplus = hid_get_drvdata(hdev);
 512 
 513         if (intf->cur_altsetting->desc.bInterfaceProtocol
 514                         != USB_INTERFACE_PROTOCOL_MOUSE)
 515                 return 0;
 516 
 517         if (koneplus == NULL)
 518                 return 0;
 519 
 520         koneplus_keep_values_up_to_date(koneplus, data);
 521 
 522         if (koneplus->roccat_claimed)
 523                 koneplus_report_to_chrdev(koneplus, data);
 524 
 525         return 0;
 526 }
 527 
 528 static const struct hid_device_id koneplus_devices[] = {
 529         { HID_USB_DEVICE(USB_VENDOR_ID_ROCCAT, USB_DEVICE_ID_ROCCAT_KONEPLUS) },
 530         { HID_USB_DEVICE(USB_VENDOR_ID_ROCCAT, USB_DEVICE_ID_ROCCAT_KONEXTD) },
 531         { }
 532 };
 533 
 534 MODULE_DEVICE_TABLE(hid, koneplus_devices);
 535 
 536 static struct hid_driver koneplus_driver = {
 537                 .name = "koneplus",
 538                 .id_table = koneplus_devices,
 539                 .probe = koneplus_probe,
 540                 .remove = koneplus_remove,
 541                 .raw_event = koneplus_raw_event
 542 };
 543 
 544 static int __init koneplus_init(void)
 545 {
 546         int retval;
 547 
 548         /* class name has to be same as driver name */
 549         koneplus_class = class_create(THIS_MODULE, "koneplus");
 550         if (IS_ERR(koneplus_class))
 551                 return PTR_ERR(koneplus_class);
 552         koneplus_class->dev_groups = koneplus_groups;
 553 
 554         retval = hid_register_driver(&koneplus_driver);
 555         if (retval)
 556                 class_destroy(koneplus_class);
 557         return retval;
 558 }
 559 
 560 static void __exit koneplus_exit(void)
 561 {
 562         hid_unregister_driver(&koneplus_driver);
 563         class_destroy(koneplus_class);
 564 }
 565 
 566 module_init(koneplus_init);
 567 module_exit(koneplus_exit);
 568 
 569 MODULE_AUTHOR("Stefan Achatz");
 570 MODULE_DESCRIPTION("USB Roccat Kone[+]/XTD driver");
 571 MODULE_LICENSE("GPL v2");

/* [<][>][^][v][top][bottom][index][help] */