linux/drivers/hid/hid-roccat-kovaplus.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0-or-later
   2/*
   3 * Roccat Kova[+] driver for Linux
   4 *
   5 * Copyright (c) 2011 Stefan Achatz <erazor_de@users.sourceforge.net>
   6 */
   7
   8/*
   9 */
  10
  11/*
  12 * Roccat Kova[+] is a bigger version of the Pyra with two more side buttons.
  13 */
  14
  15#include <linux/device.h>
  16#include <linux/input.h>
  17#include <linux/hid.h>
  18#include <linux/module.h>
  19#include <linux/slab.h>
  20#include <linux/hid-roccat.h>
  21#include "hid-ids.h"
  22#include "hid-roccat-common.h"
  23#include "hid-roccat-kovaplus.h"
  24
  25static uint profile_numbers[5] = {0, 1, 2, 3, 4};
  26
  27static struct class *kovaplus_class;
  28
  29static uint kovaplus_convert_event_cpi(uint value)
  30{
  31        return (value == 7 ? 4 : (value == 4 ? 3 : value));
  32}
  33
  34static void kovaplus_profile_activated(struct kovaplus_device *kovaplus,
  35                uint new_profile_index)
  36{
  37        if (new_profile_index >= ARRAY_SIZE(kovaplus->profile_settings))
  38                return;
  39        kovaplus->actual_profile = new_profile_index;
  40        kovaplus->actual_cpi = kovaplus->profile_settings[new_profile_index].cpi_startup_level;
  41        kovaplus->actual_x_sensitivity = kovaplus->profile_settings[new_profile_index].sensitivity_x;
  42        kovaplus->actual_y_sensitivity = kovaplus->profile_settings[new_profile_index].sensitivity_y;
  43}
  44
  45static int kovaplus_send_control(struct usb_device *usb_dev, uint value,
  46                enum kovaplus_control_requests request)
  47{
  48        int retval;
  49        struct roccat_common2_control control;
  50
  51        if ((request == KOVAPLUS_CONTROL_REQUEST_PROFILE_SETTINGS ||
  52                        request == KOVAPLUS_CONTROL_REQUEST_PROFILE_BUTTONS) &&
  53                        value > 4)
  54                return -EINVAL;
  55
  56        control.command = ROCCAT_COMMON_COMMAND_CONTROL;
  57        control.value = value;
  58        control.request = request;
  59
  60        retval = roccat_common2_send(usb_dev, ROCCAT_COMMON_COMMAND_CONTROL,
  61                        &control, sizeof(struct roccat_common2_control));
  62
  63        return retval;
  64}
  65
  66static int kovaplus_select_profile(struct usb_device *usb_dev, uint number,
  67                enum kovaplus_control_requests request)
  68{
  69        return kovaplus_send_control(usb_dev, number, request);
  70}
  71
  72static int kovaplus_get_profile_settings(struct usb_device *usb_dev,
  73                struct kovaplus_profile_settings *buf, uint number)
  74{
  75        int retval;
  76
  77        retval = kovaplus_select_profile(usb_dev, number,
  78                        KOVAPLUS_CONTROL_REQUEST_PROFILE_SETTINGS);
  79        if (retval)
  80                return retval;
  81
  82        return roccat_common2_receive(usb_dev, KOVAPLUS_COMMAND_PROFILE_SETTINGS,
  83                        buf, KOVAPLUS_SIZE_PROFILE_SETTINGS);
  84}
  85
  86static int kovaplus_get_profile_buttons(struct usb_device *usb_dev,
  87                struct kovaplus_profile_buttons *buf, int number)
  88{
  89        int retval;
  90
  91        retval = kovaplus_select_profile(usb_dev, number,
  92                        KOVAPLUS_CONTROL_REQUEST_PROFILE_BUTTONS);
  93        if (retval)
  94                return retval;
  95
  96        return roccat_common2_receive(usb_dev, KOVAPLUS_COMMAND_PROFILE_BUTTONS,
  97                        buf, KOVAPLUS_SIZE_PROFILE_BUTTONS);
  98}
  99
 100/* retval is 0-4 on success, < 0 on error */
 101static int kovaplus_get_actual_profile(struct usb_device *usb_dev)
 102{
 103        struct kovaplus_actual_profile buf;
 104        int retval;
 105
 106        retval = roccat_common2_receive(usb_dev, KOVAPLUS_COMMAND_ACTUAL_PROFILE,
 107                        &buf, sizeof(struct kovaplus_actual_profile));
 108
 109        return retval ? retval : buf.actual_profile;
 110}
 111
 112static int kovaplus_set_actual_profile(struct usb_device *usb_dev,
 113                int new_profile)
 114{
 115        struct kovaplus_actual_profile buf;
 116
 117        buf.command = KOVAPLUS_COMMAND_ACTUAL_PROFILE;
 118        buf.size = sizeof(struct kovaplus_actual_profile);
 119        buf.actual_profile = new_profile;
 120
 121        return roccat_common2_send_with_status(usb_dev,
 122                        KOVAPLUS_COMMAND_ACTUAL_PROFILE,
 123                        &buf, sizeof(struct kovaplus_actual_profile));
 124}
 125
 126static ssize_t kovaplus_sysfs_read(struct file *fp, struct kobject *kobj,
 127                char *buf, loff_t off, size_t count,
 128                size_t real_size, uint command)
 129{
 130        struct device *dev = kobj_to_dev(kobj)->parent->parent;
 131        struct kovaplus_device *kovaplus = hid_get_drvdata(dev_get_drvdata(dev));
 132        struct usb_device *usb_dev = interface_to_usbdev(to_usb_interface(dev));
 133        int retval;
 134
 135        if (off >= real_size)
 136                return 0;
 137
 138        if (off != 0 || count != real_size)
 139                return -EINVAL;
 140
 141        mutex_lock(&kovaplus->kovaplus_lock);
 142        retval = roccat_common2_receive(usb_dev, command, buf, real_size);
 143        mutex_unlock(&kovaplus->kovaplus_lock);
 144
 145        if (retval)
 146                return retval;
 147
 148        return real_size;
 149}
 150
 151static ssize_t kovaplus_sysfs_write(struct file *fp, struct kobject *kobj,
 152                void const *buf, loff_t off, size_t count,
 153                size_t real_size, uint command)
 154{
 155        struct device *dev = kobj_to_dev(kobj)->parent->parent;
 156        struct kovaplus_device *kovaplus = hid_get_drvdata(dev_get_drvdata(dev));
 157        struct usb_device *usb_dev = interface_to_usbdev(to_usb_interface(dev));
 158        int retval;
 159
 160        if (off != 0 || count != real_size)
 161                return -EINVAL;
 162
 163        mutex_lock(&kovaplus->kovaplus_lock);
 164        retval = roccat_common2_send_with_status(usb_dev, command,
 165                        buf, real_size);
 166        mutex_unlock(&kovaplus->kovaplus_lock);
 167
 168        if (retval)
 169                return retval;
 170
 171        return real_size;
 172}
 173
 174#define KOVAPLUS_SYSFS_W(thingy, THINGY) \
 175static ssize_t kovaplus_sysfs_write_ ## thingy(struct file *fp, \
 176                struct kobject *kobj, struct bin_attribute *attr, char *buf, \
 177                loff_t off, size_t count) \
 178{ \
 179        return kovaplus_sysfs_write(fp, kobj, buf, off, count, \
 180                        KOVAPLUS_SIZE_ ## THINGY, KOVAPLUS_COMMAND_ ## THINGY); \
 181}
 182
 183#define KOVAPLUS_SYSFS_R(thingy, THINGY) \
 184static ssize_t kovaplus_sysfs_read_ ## thingy(struct file *fp, \
 185                struct kobject *kobj, struct bin_attribute *attr, char *buf, \
 186                loff_t off, size_t count) \
 187{ \
 188        return kovaplus_sysfs_read(fp, kobj, buf, off, count, \
 189                        KOVAPLUS_SIZE_ ## THINGY, KOVAPLUS_COMMAND_ ## THINGY); \
 190}
 191
 192#define KOVAPLUS_SYSFS_RW(thingy, THINGY) \
 193KOVAPLUS_SYSFS_W(thingy, THINGY) \
 194KOVAPLUS_SYSFS_R(thingy, THINGY)
 195
 196#define KOVAPLUS_BIN_ATTRIBUTE_RW(thingy, THINGY) \
 197KOVAPLUS_SYSFS_RW(thingy, THINGY); \
 198static struct bin_attribute bin_attr_##thingy = { \
 199        .attr = { .name = #thingy, .mode = 0660 }, \
 200        .size = KOVAPLUS_SIZE_ ## THINGY, \
 201        .read = kovaplus_sysfs_read_ ## thingy, \
 202        .write = kovaplus_sysfs_write_ ## thingy \
 203}
 204
 205#define KOVAPLUS_BIN_ATTRIBUTE_W(thingy, THINGY) \
 206KOVAPLUS_SYSFS_W(thingy, THINGY); \
 207static struct bin_attribute bin_attr_##thingy = { \
 208        .attr = { .name = #thingy, .mode = 0220 }, \
 209        .size = KOVAPLUS_SIZE_ ## THINGY, \
 210        .write = kovaplus_sysfs_write_ ## thingy \
 211}
 212KOVAPLUS_BIN_ATTRIBUTE_W(control, CONTROL);
 213KOVAPLUS_BIN_ATTRIBUTE_RW(info, INFO);
 214KOVAPLUS_BIN_ATTRIBUTE_RW(profile_settings, PROFILE_SETTINGS);
 215KOVAPLUS_BIN_ATTRIBUTE_RW(profile_buttons, PROFILE_BUTTONS);
 216
 217static ssize_t kovaplus_sysfs_read_profilex_settings(struct file *fp,
 218                struct kobject *kobj, struct bin_attribute *attr, char *buf,
 219                loff_t off, size_t count)
 220{
 221        struct device *dev = kobj_to_dev(kobj)->parent->parent;
 222        struct usb_device *usb_dev = interface_to_usbdev(to_usb_interface(dev));
 223        ssize_t retval;
 224
 225        retval = kovaplus_select_profile(usb_dev, *(uint *)(attr->private),
 226                        KOVAPLUS_CONTROL_REQUEST_PROFILE_SETTINGS);
 227        if (retval)
 228                return retval;
 229
 230        return kovaplus_sysfs_read(fp, kobj, buf, off, count,
 231                        KOVAPLUS_SIZE_PROFILE_SETTINGS,
 232                        KOVAPLUS_COMMAND_PROFILE_SETTINGS);
 233}
 234
 235static ssize_t kovaplus_sysfs_read_profilex_buttons(struct file *fp,
 236                struct kobject *kobj, struct bin_attribute *attr, char *buf,
 237                loff_t off, size_t count)
 238{
 239        struct device *dev = kobj_to_dev(kobj)->parent->parent;
 240        struct usb_device *usb_dev = interface_to_usbdev(to_usb_interface(dev));
 241        ssize_t retval;
 242
 243        retval = kovaplus_select_profile(usb_dev, *(uint *)(attr->private),
 244                        KOVAPLUS_CONTROL_REQUEST_PROFILE_BUTTONS);
 245        if (retval)
 246                return retval;
 247
 248        return kovaplus_sysfs_read(fp, kobj, buf, off, count,
 249                        KOVAPLUS_SIZE_PROFILE_BUTTONS,
 250                        KOVAPLUS_COMMAND_PROFILE_BUTTONS);
 251}
 252
 253#define PROFILE_ATTR(number)                                            \
 254static struct bin_attribute bin_attr_profile##number##_settings = {     \
 255        .attr = { .name = "profile" #number "_settings", .mode = 0440 },        \
 256        .size = KOVAPLUS_SIZE_PROFILE_SETTINGS,                         \
 257        .read = kovaplus_sysfs_read_profilex_settings,                  \
 258        .private = &profile_numbers[number-1],                          \
 259};                                                                      \
 260static struct bin_attribute bin_attr_profile##number##_buttons = {      \
 261        .attr = { .name = "profile" #number "_buttons", .mode = 0440 }, \
 262        .size = KOVAPLUS_SIZE_PROFILE_BUTTONS,                          \
 263        .read = kovaplus_sysfs_read_profilex_buttons,                   \
 264        .private = &profile_numbers[number-1],                          \
 265};
 266PROFILE_ATTR(1);
 267PROFILE_ATTR(2);
 268PROFILE_ATTR(3);
 269PROFILE_ATTR(4);
 270PROFILE_ATTR(5);
 271
 272static ssize_t kovaplus_sysfs_show_actual_profile(struct device *dev,
 273                struct device_attribute *attr, char *buf)
 274{
 275        struct kovaplus_device *kovaplus =
 276                        hid_get_drvdata(dev_get_drvdata(dev->parent->parent));
 277        return snprintf(buf, PAGE_SIZE, "%d\n", kovaplus->actual_profile);
 278}
 279
 280static ssize_t kovaplus_sysfs_set_actual_profile(struct device *dev,
 281                struct device_attribute *attr, char const *buf, size_t size)
 282{
 283        struct kovaplus_device *kovaplus;
 284        struct usb_device *usb_dev;
 285        unsigned long profile;
 286        int retval;
 287        struct kovaplus_roccat_report roccat_report;
 288
 289        dev = dev->parent->parent;
 290        kovaplus = hid_get_drvdata(dev_get_drvdata(dev));
 291        usb_dev = interface_to_usbdev(to_usb_interface(dev));
 292
 293        retval = kstrtoul(buf, 10, &profile);
 294        if (retval)
 295                return retval;
 296
 297        if (profile >= 5)
 298                return -EINVAL;
 299
 300        mutex_lock(&kovaplus->kovaplus_lock);
 301        retval = kovaplus_set_actual_profile(usb_dev, profile);
 302        if (retval) {
 303                mutex_unlock(&kovaplus->kovaplus_lock);
 304                return retval;
 305        }
 306
 307        kovaplus_profile_activated(kovaplus, profile);
 308
 309        roccat_report.type = KOVAPLUS_MOUSE_REPORT_BUTTON_TYPE_PROFILE_1;
 310        roccat_report.profile = profile + 1;
 311        roccat_report.button = 0;
 312        roccat_report.data1 = profile + 1;
 313        roccat_report.data2 = 0;
 314        roccat_report_event(kovaplus->chrdev_minor,
 315                        (uint8_t const *)&roccat_report);
 316
 317        mutex_unlock(&kovaplus->kovaplus_lock);
 318
 319        return size;
 320}
 321static DEVICE_ATTR(actual_profile, 0660,
 322                   kovaplus_sysfs_show_actual_profile,
 323                   kovaplus_sysfs_set_actual_profile);
 324
 325static ssize_t kovaplus_sysfs_show_actual_cpi(struct device *dev,
 326                struct device_attribute *attr, char *buf)
 327{
 328        struct kovaplus_device *kovaplus =
 329                        hid_get_drvdata(dev_get_drvdata(dev->parent->parent));
 330        return snprintf(buf, PAGE_SIZE, "%d\n", kovaplus->actual_cpi);
 331}
 332static DEVICE_ATTR(actual_cpi, 0440, kovaplus_sysfs_show_actual_cpi, NULL);
 333
 334static ssize_t kovaplus_sysfs_show_actual_sensitivity_x(struct device *dev,
 335                struct device_attribute *attr, char *buf)
 336{
 337        struct kovaplus_device *kovaplus =
 338                        hid_get_drvdata(dev_get_drvdata(dev->parent->parent));
 339        return snprintf(buf, PAGE_SIZE, "%d\n", kovaplus->actual_x_sensitivity);
 340}
 341static DEVICE_ATTR(actual_sensitivity_x, 0440,
 342                   kovaplus_sysfs_show_actual_sensitivity_x, NULL);
 343
 344static ssize_t kovaplus_sysfs_show_actual_sensitivity_y(struct device *dev,
 345                struct device_attribute *attr, char *buf)
 346{
 347        struct kovaplus_device *kovaplus =
 348                        hid_get_drvdata(dev_get_drvdata(dev->parent->parent));
 349        return snprintf(buf, PAGE_SIZE, "%d\n", kovaplus->actual_y_sensitivity);
 350}
 351static DEVICE_ATTR(actual_sensitivity_y, 0440,
 352                   kovaplus_sysfs_show_actual_sensitivity_y, NULL);
 353
 354static ssize_t kovaplus_sysfs_show_firmware_version(struct device *dev,
 355                struct device_attribute *attr, char *buf)
 356{
 357        struct kovaplus_device *kovaplus;
 358        struct usb_device *usb_dev;
 359        struct kovaplus_info info;
 360
 361        dev = dev->parent->parent;
 362        kovaplus = hid_get_drvdata(dev_get_drvdata(dev));
 363        usb_dev = interface_to_usbdev(to_usb_interface(dev));
 364
 365        mutex_lock(&kovaplus->kovaplus_lock);
 366        roccat_common2_receive(usb_dev, KOVAPLUS_COMMAND_INFO,
 367                        &info, KOVAPLUS_SIZE_INFO);
 368        mutex_unlock(&kovaplus->kovaplus_lock);
 369
 370        return snprintf(buf, PAGE_SIZE, "%d\n", info.firmware_version);
 371}
 372static DEVICE_ATTR(firmware_version, 0440,
 373                   kovaplus_sysfs_show_firmware_version, NULL);
 374
 375static struct attribute *kovaplus_attrs[] = {
 376        &dev_attr_actual_cpi.attr,
 377        &dev_attr_firmware_version.attr,
 378        &dev_attr_actual_profile.attr,
 379        &dev_attr_actual_sensitivity_x.attr,
 380        &dev_attr_actual_sensitivity_y.attr,
 381        NULL,
 382};
 383
 384static struct bin_attribute *kovaplus_bin_attributes[] = {
 385        &bin_attr_control,
 386        &bin_attr_info,
 387        &bin_attr_profile_settings,
 388        &bin_attr_profile_buttons,
 389        &bin_attr_profile1_settings,
 390        &bin_attr_profile2_settings,
 391        &bin_attr_profile3_settings,
 392        &bin_attr_profile4_settings,
 393        &bin_attr_profile5_settings,
 394        &bin_attr_profile1_buttons,
 395        &bin_attr_profile2_buttons,
 396        &bin_attr_profile3_buttons,
 397        &bin_attr_profile4_buttons,
 398        &bin_attr_profile5_buttons,
 399        NULL,
 400};
 401
 402static const struct attribute_group kovaplus_group = {
 403        .attrs = kovaplus_attrs,
 404        .bin_attrs = kovaplus_bin_attributes,
 405};
 406
 407static const struct attribute_group *kovaplus_groups[] = {
 408        &kovaplus_group,
 409        NULL,
 410};
 411
 412static int kovaplus_init_kovaplus_device_struct(struct usb_device *usb_dev,
 413                struct kovaplus_device *kovaplus)
 414{
 415        int retval, i;
 416        static uint wait = 70; /* device will freeze with just 60 */
 417
 418        mutex_init(&kovaplus->kovaplus_lock);
 419
 420        for (i = 0; i < 5; ++i) {
 421                msleep(wait);
 422                retval = kovaplus_get_profile_settings(usb_dev,
 423                                &kovaplus->profile_settings[i], i);
 424                if (retval)
 425                        return retval;
 426
 427                msleep(wait);
 428                retval = kovaplus_get_profile_buttons(usb_dev,
 429                                &kovaplus->profile_buttons[i], i);
 430                if (retval)
 431                        return retval;
 432        }
 433
 434        msleep(wait);
 435        retval = kovaplus_get_actual_profile(usb_dev);
 436        if (retval < 0)
 437                return retval;
 438        kovaplus_profile_activated(kovaplus, retval);
 439
 440        return 0;
 441}
 442
 443static int kovaplus_init_specials(struct hid_device *hdev)
 444{
 445        struct usb_interface *intf = to_usb_interface(hdev->dev.parent);
 446        struct usb_device *usb_dev = interface_to_usbdev(intf);
 447        struct kovaplus_device *kovaplus;
 448        int retval;
 449
 450        if (intf->cur_altsetting->desc.bInterfaceProtocol
 451                        == USB_INTERFACE_PROTOCOL_MOUSE) {
 452
 453                kovaplus = kzalloc(sizeof(*kovaplus), GFP_KERNEL);
 454                if (!kovaplus) {
 455                        hid_err(hdev, "can't alloc device descriptor\n");
 456                        return -ENOMEM;
 457                }
 458                hid_set_drvdata(hdev, kovaplus);
 459
 460                retval = kovaplus_init_kovaplus_device_struct(usb_dev, kovaplus);
 461                if (retval) {
 462                        hid_err(hdev, "couldn't init struct kovaplus_device\n");
 463                        goto exit_free;
 464                }
 465
 466                retval = roccat_connect(kovaplus_class, hdev,
 467                                sizeof(struct kovaplus_roccat_report));
 468                if (retval < 0) {
 469                        hid_err(hdev, "couldn't init char dev\n");
 470                } else {
 471                        kovaplus->chrdev_minor = retval;
 472                        kovaplus->roccat_claimed = 1;
 473                }
 474
 475        } else {
 476                hid_set_drvdata(hdev, NULL);
 477        }
 478
 479        return 0;
 480exit_free:
 481        kfree(kovaplus);
 482        return retval;
 483}
 484
 485static void kovaplus_remove_specials(struct hid_device *hdev)
 486{
 487        struct usb_interface *intf = to_usb_interface(hdev->dev.parent);
 488        struct kovaplus_device *kovaplus;
 489
 490        if (intf->cur_altsetting->desc.bInterfaceProtocol
 491                        == USB_INTERFACE_PROTOCOL_MOUSE) {
 492                kovaplus = hid_get_drvdata(hdev);
 493                if (kovaplus->roccat_claimed)
 494                        roccat_disconnect(kovaplus->chrdev_minor);
 495                kfree(kovaplus);
 496        }
 497}
 498
 499static int kovaplus_probe(struct hid_device *hdev,
 500                const struct hid_device_id *id)
 501{
 502        int retval;
 503
 504        retval = hid_parse(hdev);
 505        if (retval) {
 506                hid_err(hdev, "parse failed\n");
 507                goto exit;
 508        }
 509
 510        retval = hid_hw_start(hdev, HID_CONNECT_DEFAULT);
 511        if (retval) {
 512                hid_err(hdev, "hw start failed\n");
 513                goto exit;
 514        }
 515
 516        retval = kovaplus_init_specials(hdev);
 517        if (retval) {
 518                hid_err(hdev, "couldn't install mouse\n");
 519                goto exit_stop;
 520        }
 521
 522        return 0;
 523
 524exit_stop:
 525        hid_hw_stop(hdev);
 526exit:
 527        return retval;
 528}
 529
 530static void kovaplus_remove(struct hid_device *hdev)
 531{
 532        kovaplus_remove_specials(hdev);
 533        hid_hw_stop(hdev);
 534}
 535
 536static void kovaplus_keep_values_up_to_date(struct kovaplus_device *kovaplus,
 537                u8 const *data)
 538{
 539        struct kovaplus_mouse_report_button const *button_report;
 540
 541        if (data[0] != KOVAPLUS_MOUSE_REPORT_NUMBER_BUTTON)
 542                return;
 543
 544        button_report = (struct kovaplus_mouse_report_button const *)data;
 545
 546        switch (button_report->type) {
 547        case KOVAPLUS_MOUSE_REPORT_BUTTON_TYPE_PROFILE_1:
 548                kovaplus_profile_activated(kovaplus, button_report->data1 - 1);
 549                break;
 550        case KOVAPLUS_MOUSE_REPORT_BUTTON_TYPE_CPI:
 551                kovaplus->actual_cpi = kovaplus_convert_event_cpi(button_report->data1);
 552                break;
 553        case KOVAPLUS_MOUSE_REPORT_BUTTON_TYPE_SENSITIVITY:
 554                kovaplus->actual_x_sensitivity = button_report->data1;
 555                kovaplus->actual_y_sensitivity = button_report->data2;
 556                break;
 557        default:
 558                break;
 559        }
 560}
 561
 562static void kovaplus_report_to_chrdev(struct kovaplus_device const *kovaplus,
 563                u8 const *data)
 564{
 565        struct kovaplus_roccat_report roccat_report;
 566        struct kovaplus_mouse_report_button const *button_report;
 567
 568        if (data[0] != KOVAPLUS_MOUSE_REPORT_NUMBER_BUTTON)
 569                return;
 570
 571        button_report = (struct kovaplus_mouse_report_button const *)data;
 572
 573        if (button_report->type == KOVAPLUS_MOUSE_REPORT_BUTTON_TYPE_PROFILE_2)
 574                return;
 575
 576        roccat_report.type = button_report->type;
 577        roccat_report.profile = kovaplus->actual_profile + 1;
 578
 579        if (roccat_report.type == KOVAPLUS_MOUSE_REPORT_BUTTON_TYPE_MACRO ||
 580                        roccat_report.type == KOVAPLUS_MOUSE_REPORT_BUTTON_TYPE_SHORTCUT ||
 581                        roccat_report.type == KOVAPLUS_MOUSE_REPORT_BUTTON_TYPE_QUICKLAUNCH ||
 582                        roccat_report.type == KOVAPLUS_MOUSE_REPORT_BUTTON_TYPE_TIMER)
 583                roccat_report.button = button_report->data1;
 584        else
 585                roccat_report.button = 0;
 586
 587        if (roccat_report.type == KOVAPLUS_MOUSE_REPORT_BUTTON_TYPE_CPI)
 588                roccat_report.data1 = kovaplus_convert_event_cpi(button_report->data1);
 589        else
 590                roccat_report.data1 = button_report->data1;
 591
 592        roccat_report.data2 = button_report->data2;
 593
 594        roccat_report_event(kovaplus->chrdev_minor,
 595                        (uint8_t const *)&roccat_report);
 596}
 597
 598static int kovaplus_raw_event(struct hid_device *hdev,
 599                struct hid_report *report, u8 *data, int size)
 600{
 601        struct usb_interface *intf = to_usb_interface(hdev->dev.parent);
 602        struct kovaplus_device *kovaplus = hid_get_drvdata(hdev);
 603
 604        if (intf->cur_altsetting->desc.bInterfaceProtocol
 605                        != USB_INTERFACE_PROTOCOL_MOUSE)
 606                return 0;
 607
 608        if (kovaplus == NULL)
 609                return 0;
 610
 611        kovaplus_keep_values_up_to_date(kovaplus, data);
 612
 613        if (kovaplus->roccat_claimed)
 614                kovaplus_report_to_chrdev(kovaplus, data);
 615
 616        return 0;
 617}
 618
 619static const struct hid_device_id kovaplus_devices[] = {
 620        { HID_USB_DEVICE(USB_VENDOR_ID_ROCCAT, USB_DEVICE_ID_ROCCAT_KOVAPLUS) },
 621        { }
 622};
 623
 624MODULE_DEVICE_TABLE(hid, kovaplus_devices);
 625
 626static struct hid_driver kovaplus_driver = {
 627                .name = "kovaplus",
 628                .id_table = kovaplus_devices,
 629                .probe = kovaplus_probe,
 630                .remove = kovaplus_remove,
 631                .raw_event = kovaplus_raw_event
 632};
 633
 634static int __init kovaplus_init(void)
 635{
 636        int retval;
 637
 638        kovaplus_class = class_create(THIS_MODULE, "kovaplus");
 639        if (IS_ERR(kovaplus_class))
 640                return PTR_ERR(kovaplus_class);
 641        kovaplus_class->dev_groups = kovaplus_groups;
 642
 643        retval = hid_register_driver(&kovaplus_driver);
 644        if (retval)
 645                class_destroy(kovaplus_class);
 646        return retval;
 647}
 648
 649static void __exit kovaplus_exit(void)
 650{
 651        hid_unregister_driver(&kovaplus_driver);
 652        class_destroy(kovaplus_class);
 653}
 654
 655module_init(kovaplus_init);
 656module_exit(kovaplus_exit);
 657
 658MODULE_AUTHOR("Stefan Achatz");
 659MODULE_DESCRIPTION("USB Roccat Kova[+] driver");
 660MODULE_LICENSE("GPL v2");
 661