linux/drivers/hid/hid-roccat-kovaplus.c
<<
>>
Prefs
   1/*
   2 * Roccat Kova[+] driver for Linux
   3 *
   4 * Copyright (c) 2011 Stefan Achatz <erazor_de@users.sourceforge.net>
   5 */
   6
   7/*
   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)
  11 * any later version.
  12 */
  13
  14/*
  15 * Roccat Kova[+] is a bigger version of the Pyra with two more side buttons.
  16 */
  17
  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>
  24#include "hid-ids.h"
  25#include "hid-roccat-common.h"
  26#include "hid-roccat-kovaplus.h"
  27
  28static uint profile_numbers[5] = {0, 1, 2, 3, 4};
  29
  30static struct class *kovaplus_class;
  31
  32static uint kovaplus_convert_event_cpi(uint value)
  33{
  34        return (value == 7 ? 4 : (value == 4 ? 3 : value));
  35}
  36
  37static void kovaplus_profile_activated(struct kovaplus_device *kovaplus,
  38                uint new_profile_index)
  39{
  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;
  44}
  45
  46static int kovaplus_send_control(struct usb_device *usb_dev, uint value,
  47                enum kovaplus_control_requests request)
  48{
  49        int retval;
  50        struct kovaplus_control control;
  51
  52        if ((request == KOVAPLUS_CONTROL_REQUEST_PROFILE_SETTINGS ||
  53                        request == KOVAPLUS_CONTROL_REQUEST_PROFILE_BUTTONS) &&
  54                        value > 4)
  55                return -EINVAL;
  56
  57        control.command = KOVAPLUS_COMMAND_CONTROL;
  58        control.value = value;
  59        control.request = request;
  60
  61        retval = roccat_common_send(usb_dev, KOVAPLUS_COMMAND_CONTROL,
  62                        &control, sizeof(struct kovaplus_control));
  63
  64        return retval;
  65}
  66
  67static int kovaplus_receive_control_status(struct usb_device *usb_dev)
  68{
  69        int retval;
  70        struct kovaplus_control control;
  71
  72        do {
  73                retval = roccat_common_receive(usb_dev, KOVAPLUS_COMMAND_CONTROL,
  74                                &control, sizeof(struct kovaplus_control));
  75
  76                /* check if we get a completely wrong answer */
  77                if (retval)
  78                        return retval;
  79
  80                if (control.value == KOVAPLUS_CONTROL_REQUEST_STATUS_OK)
  81                        return 0;
  82
  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 */
  86                        continue;
  87                }
  88
  89                /* seems to be critical - replug necessary */
  90                if (control.value == KOVAPLUS_CONTROL_REQUEST_STATUS_OVERLOAD)
  91                        return -EINVAL;
  92
  93                hid_err(usb_dev, "roccat_common_receive_control_status: "
  94                                "unknown response value 0x%x\n", control.value);
  95                return -EINVAL;
  96        } while (1);
  97}
  98
  99static int kovaplus_send(struct usb_device *usb_dev, uint command,
 100                void const *buf, uint size)
 101{
 102        int retval;
 103
 104        retval = roccat_common_send(usb_dev, command, buf, size);
 105        if (retval)
 106                return retval;
 107
 108        msleep(100);
 109
 110        return kovaplus_receive_control_status(usb_dev);
 111}
 112
 113static int kovaplus_select_profile(struct usb_device *usb_dev, uint number,
 114                enum kovaplus_control_requests request)
 115{
 116        return kovaplus_send_control(usb_dev, number, request);
 117}
 118
 119static int kovaplus_get_info(struct usb_device *usb_dev,
 120                struct kovaplus_info *buf)
 121{
 122        return roccat_common_receive(usb_dev, KOVAPLUS_COMMAND_INFO,
 123                        buf, sizeof(struct kovaplus_info));
 124}
 125
 126static int kovaplus_get_profile_settings(struct usb_device *usb_dev,
 127                struct kovaplus_profile_settings *buf, uint number)
 128{
 129        int retval;
 130
 131        retval = kovaplus_select_profile(usb_dev, number,
 132                        KOVAPLUS_CONTROL_REQUEST_PROFILE_SETTINGS);
 133        if (retval)
 134                return retval;
 135
 136        return roccat_common_receive(usb_dev, KOVAPLUS_COMMAND_PROFILE_SETTINGS,
 137                        buf, sizeof(struct kovaplus_profile_settings));
 138}
 139
 140static int kovaplus_set_profile_settings(struct usb_device *usb_dev,
 141                struct kovaplus_profile_settings const *settings)
 142{
 143        return kovaplus_send(usb_dev, KOVAPLUS_COMMAND_PROFILE_SETTINGS,
 144                        settings, sizeof(struct kovaplus_profile_settings));
 145}
 146
 147static int kovaplus_get_profile_buttons(struct usb_device *usb_dev,
 148                struct kovaplus_profile_buttons *buf, int number)
 149{
 150        int retval;
 151
 152        retval = kovaplus_select_profile(usb_dev, number,
 153                        KOVAPLUS_CONTROL_REQUEST_PROFILE_BUTTONS);
 154        if (retval)
 155                return retval;
 156
 157        return roccat_common_receive(usb_dev, KOVAPLUS_COMMAND_PROFILE_BUTTONS,
 158                        buf, sizeof(struct kovaplus_profile_buttons));
 159}
 160
 161static int kovaplus_set_profile_buttons(struct usb_device *usb_dev,
 162                struct kovaplus_profile_buttons const *buttons)
 163{
 164        return kovaplus_send(usb_dev, KOVAPLUS_COMMAND_PROFILE_BUTTONS,
 165                        buttons, sizeof(struct kovaplus_profile_buttons));
 166}
 167
 168/* retval is 0-4 on success, < 0 on error */
 169static int kovaplus_get_actual_profile(struct usb_device *usb_dev)
 170{
 171        struct kovaplus_actual_profile buf;
 172        int retval;
 173
 174        retval = roccat_common_receive(usb_dev, KOVAPLUS_COMMAND_ACTUAL_PROFILE,
 175                        &buf, sizeof(struct kovaplus_actual_profile));
 176
 177        return retval ? retval : buf.actual_profile;
 178}
 179
 180static int kovaplus_set_actual_profile(struct usb_device *usb_dev,
 181                int new_profile)
 182{
 183        struct kovaplus_actual_profile buf;
 184
 185        buf.command = KOVAPLUS_COMMAND_ACTUAL_PROFILE;
 186        buf.size = sizeof(struct kovaplus_actual_profile);
 187        buf.actual_profile = new_profile;
 188
 189        return kovaplus_send(usb_dev, KOVAPLUS_COMMAND_ACTUAL_PROFILE,
 190                        &buf, sizeof(struct kovaplus_actual_profile));
 191}
 192
 193static 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)
 196{
 197        struct device *dev =
 198                        container_of(kobj, struct device, kobj)->parent->parent;
 199        struct kovaplus_device *kovaplus = hid_get_drvdata(dev_get_drvdata(dev));
 200
 201        if (off >= sizeof(struct kovaplus_profile_settings))
 202                return 0;
 203
 204        if (off + count > sizeof(struct kovaplus_profile_settings))
 205                count = sizeof(struct kovaplus_profile_settings) - off;
 206
 207        mutex_lock(&kovaplus->kovaplus_lock);
 208        memcpy(buf, ((char const *)&kovaplus->profile_settings[*(uint *)(attr->private)]) + off,
 209                        count);
 210        mutex_unlock(&kovaplus->kovaplus_lock);
 211
 212        return count;
 213}
 214
 215static 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)
 218{
 219        struct device *dev =
 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));
 223        int retval = 0;
 224        int difference;
 225        int profile_index;
 226        struct kovaplus_profile_settings *profile_settings;
 227
 228        if (off != 0 || count != sizeof(struct kovaplus_profile_settings))
 229                return -EINVAL;
 230
 231        profile_index = ((struct kovaplus_profile_settings const *)buf)->profile_index;
 232        profile_settings = &kovaplus->profile_settings[profile_index];
 233
 234        mutex_lock(&kovaplus->kovaplus_lock);
 235        difference = memcmp(buf, profile_settings,
 236                        sizeof(struct kovaplus_profile_settings));
 237        if (difference) {
 238                retval = kovaplus_set_profile_settings(usb_dev,
 239                                (struct kovaplus_profile_settings const *)buf);
 240                if (!retval)
 241                        memcpy(profile_settings, buf,
 242                                        sizeof(struct kovaplus_profile_settings));
 243        }
 244        mutex_unlock(&kovaplus->kovaplus_lock);
 245
 246        if (retval)
 247                return retval;
 248
 249        return sizeof(struct kovaplus_profile_settings);
 250}
 251
 252static 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)
 255{
 256        struct device *dev =
 257                        container_of(kobj, struct device, kobj)->parent->parent;
 258        struct kovaplus_device *kovaplus = hid_get_drvdata(dev_get_drvdata(dev));
 259
 260        if (off >= sizeof(struct kovaplus_profile_buttons))
 261                return 0;
 262
 263        if (off + count > sizeof(struct kovaplus_profile_buttons))
 264                count = sizeof(struct kovaplus_profile_buttons) - off;
 265
 266        mutex_lock(&kovaplus->kovaplus_lock);
 267        memcpy(buf, ((char const *)&kovaplus->profile_buttons[*(uint *)(attr->private)]) + off,
 268                        count);
 269        mutex_unlock(&kovaplus->kovaplus_lock);
 270
 271        return count;
 272}
 273
 274static 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)
 277{
 278        struct device *dev =
 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));
 282        int retval = 0;
 283        int difference;
 284        uint profile_index;
 285        struct kovaplus_profile_buttons *profile_buttons;
 286
 287        if (off != 0 || count != sizeof(struct kovaplus_profile_buttons))
 288                return -EINVAL;
 289
 290        profile_index = ((struct kovaplus_profile_buttons const *)buf)->profile_index;
 291        profile_buttons = &kovaplus->profile_buttons[profile_index];
 292
 293        mutex_lock(&kovaplus->kovaplus_lock);
 294        difference = memcmp(buf, profile_buttons,
 295                        sizeof(struct kovaplus_profile_buttons));
 296        if (difference) {
 297                retval = kovaplus_set_profile_buttons(usb_dev,
 298                                (struct kovaplus_profile_buttons const *)buf);
 299                if (!retval)
 300                        memcpy(profile_buttons, buf,
 301                                        sizeof(struct kovaplus_profile_buttons));
 302        }
 303        mutex_unlock(&kovaplus->kovaplus_lock);
 304
 305        if (retval)
 306                return retval;
 307
 308        return sizeof(struct kovaplus_profile_buttons);
 309}
 310
 311static ssize_t kovaplus_sysfs_show_actual_profile(struct device *dev,
 312                struct device_attribute *attr, char *buf)
 313{
 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);
 317}
 318
 319static ssize_t kovaplus_sysfs_set_actual_profile(struct device *dev,
 320                struct device_attribute *attr, char const *buf, size_t size)
 321{
 322        struct kovaplus_device *kovaplus;
 323        struct usb_device *usb_dev;
 324        unsigned long profile;
 325        int retval;
 326        struct kovaplus_roccat_report roccat_report;
 327
 328        dev = dev->parent->parent;
 329        kovaplus = hid_get_drvdata(dev_get_drvdata(dev));
 330        usb_dev = interface_to_usbdev(to_usb_interface(dev));
 331
 332        retval = strict_strtoul(buf, 10, &profile);
 333        if (retval)
 334                return retval;
 335
 336        if (profile >= 5)
 337                return -EINVAL;
 338
 339        mutex_lock(&kovaplus->kovaplus_lock);
 340        retval = kovaplus_set_actual_profile(usb_dev, profile);
 341        if (retval) {
 342                mutex_unlock(&kovaplus->kovaplus_lock);
 343                return retval;
 344        }
 345
 346        kovaplus_profile_activated(kovaplus, profile);
 347
 348        roccat_report.type = KOVAPLUS_MOUSE_REPORT_BUTTON_TYPE_PROFILE_1;
 349        roccat_report.profile = profile + 1;
 350        roccat_report.button = 0;
 351        roccat_report.data1 = profile + 1;
 352        roccat_report.data2 = 0;
 353        roccat_report_event(kovaplus->chrdev_minor,
 354                        (uint8_t const *)&roccat_report);
 355
 356        mutex_unlock(&kovaplus->kovaplus_lock);
 357
 358        return size;
 359}
 360
 361static ssize_t kovaplus_sysfs_show_actual_cpi(struct device *dev,
 362                struct device_attribute *attr, char *buf)
 363{
 364        struct kovaplus_device *kovaplus =
 365                        hid_get_drvdata(dev_get_drvdata(dev->parent->parent));
 366        return snprintf(buf, PAGE_SIZE, "%d\n", kovaplus->actual_cpi);
 367}
 368
 369static ssize_t kovaplus_sysfs_show_actual_sensitivity_x(struct device *dev,
 370                struct device_attribute *attr, char *buf)
 371{
 372        struct kovaplus_device *kovaplus =
 373                        hid_get_drvdata(dev_get_drvdata(dev->parent->parent));
 374        return snprintf(buf, PAGE_SIZE, "%d\n", kovaplus->actual_x_sensitivity);
 375}
 376
 377static ssize_t kovaplus_sysfs_show_actual_sensitivity_y(struct device *dev,
 378                struct device_attribute *attr, char *buf)
 379{
 380        struct kovaplus_device *kovaplus =
 381                        hid_get_drvdata(dev_get_drvdata(dev->parent->parent));
 382        return snprintf(buf, PAGE_SIZE, "%d\n", kovaplus->actual_y_sensitivity);
 383}
 384
 385static ssize_t kovaplus_sysfs_show_firmware_version(struct device *dev,
 386                struct device_attribute *attr, char *buf)
 387{
 388        struct kovaplus_device *kovaplus =
 389                        hid_get_drvdata(dev_get_drvdata(dev->parent->parent));
 390        return snprintf(buf, PAGE_SIZE, "%d\n", kovaplus->info.firmware_version);
 391}
 392
 393static struct device_attribute kovaplus_attributes[] = {
 394        __ATTR(actual_cpi, 0440,
 395                kovaplus_sysfs_show_actual_cpi, NULL),
 396        __ATTR(firmware_version, 0440,
 397                kovaplus_sysfs_show_firmware_version, NULL),
 398        __ATTR(actual_profile, 0660,
 399                kovaplus_sysfs_show_actual_profile,
 400                kovaplus_sysfs_set_actual_profile),
 401        __ATTR(actual_sensitivity_x, 0440,
 402                kovaplus_sysfs_show_actual_sensitivity_x, NULL),
 403        __ATTR(actual_sensitivity_y, 0440,
 404                kovaplus_sysfs_show_actual_sensitivity_y, NULL),
 405        __ATTR_NULL
 406};
 407
 408static struct bin_attribute kovaplus_bin_attributes[] = {
 409        {
 410                .attr = { .name = "profile_settings", .mode = 0220 },
 411                .size = sizeof(struct kovaplus_profile_settings),
 412                .write = kovaplus_sysfs_write_profile_settings
 413        },
 414        {
 415                .attr = { .name = "profile1_settings", .mode = 0440 },
 416                .size = sizeof(struct kovaplus_profile_settings),
 417                .read = kovaplus_sysfs_read_profilex_settings,
 418                .private = &profile_numbers[0]
 419        },
 420        {
 421                .attr = { .name = "profile2_settings", .mode = 0440 },
 422                .size = sizeof(struct kovaplus_profile_settings),
 423                .read = kovaplus_sysfs_read_profilex_settings,
 424                .private = &profile_numbers[1]
 425        },
 426        {
 427                .attr = { .name = "profile3_settings", .mode = 0440 },
 428                .size = sizeof(struct kovaplus_profile_settings),
 429                .read = kovaplus_sysfs_read_profilex_settings,
 430                .private = &profile_numbers[2]
 431        },
 432        {
 433                .attr = { .name = "profile4_settings", .mode = 0440 },
 434                .size = sizeof(struct kovaplus_profile_settings),
 435                .read = kovaplus_sysfs_read_profilex_settings,
 436                .private = &profile_numbers[3]
 437        },
 438        {
 439                .attr = { .name = "profile5_settings", .mode = 0440 },
 440                .size = sizeof(struct kovaplus_profile_settings),
 441                .read = kovaplus_sysfs_read_profilex_settings,
 442                .private = &profile_numbers[4]
 443        },
 444        {
 445                .attr = { .name = "profile_buttons", .mode = 0220 },
 446                .size = sizeof(struct kovaplus_profile_buttons),
 447                .write = kovaplus_sysfs_write_profile_buttons
 448        },
 449        {
 450                .attr = { .name = "profile1_buttons", .mode = 0440 },
 451                .size = sizeof(struct kovaplus_profile_buttons),
 452                .read = kovaplus_sysfs_read_profilex_buttons,
 453                .private = &profile_numbers[0]
 454        },
 455        {
 456                .attr = { .name = "profile2_buttons", .mode = 0440 },
 457                .size = sizeof(struct kovaplus_profile_buttons),
 458                .read = kovaplus_sysfs_read_profilex_buttons,
 459                .private = &profile_numbers[1]
 460        },
 461        {
 462                .attr = { .name = "profile3_buttons", .mode = 0440 },
 463                .size = sizeof(struct kovaplus_profile_buttons),
 464                .read = kovaplus_sysfs_read_profilex_buttons,
 465                .private = &profile_numbers[2]
 466        },
 467        {
 468                .attr = { .name = "profile4_buttons", .mode = 0440 },
 469                .size = sizeof(struct kovaplus_profile_buttons),
 470                .read = kovaplus_sysfs_read_profilex_buttons,
 471                .private = &profile_numbers[3]
 472        },
 473        {
 474                .attr = { .name = "profile5_buttons", .mode = 0440 },
 475                .size = sizeof(struct kovaplus_profile_buttons),
 476                .read = kovaplus_sysfs_read_profilex_buttons,
 477                .private = &profile_numbers[4]
 478        },
 479        __ATTR_NULL
 480};
 481
 482static int kovaplus_init_kovaplus_device_struct(struct usb_device *usb_dev,
 483                struct kovaplus_device *kovaplus)
 484{
 485        int retval, i;
 486        static uint wait = 70; /* device will freeze with just 60 */
 487
 488        mutex_init(&kovaplus->kovaplus_lock);
 489
 490        retval = kovaplus_get_info(usb_dev, &kovaplus->info);
 491        if (retval)
 492                return retval;
 493
 494        for (i = 0; i < 5; ++i) {
 495                msleep(wait);
 496                retval = kovaplus_get_profile_settings(usb_dev,
 497                                &kovaplus->profile_settings[i], i);
 498                if (retval)
 499                        return retval;
 500
 501                msleep(wait);
 502                retval = kovaplus_get_profile_buttons(usb_dev,
 503                                &kovaplus->profile_buttons[i], i);
 504                if (retval)
 505                        return retval;
 506        }
 507
 508        msleep(wait);
 509        retval = kovaplus_get_actual_profile(usb_dev);
 510        if (retval < 0)
 511                return retval;
 512        kovaplus_profile_activated(kovaplus, retval);
 513
 514        return 0;
 515}
 516
 517static int kovaplus_init_specials(struct hid_device *hdev)
 518{
 519        struct usb_interface *intf = to_usb_interface(hdev->dev.parent);
 520        struct usb_device *usb_dev = interface_to_usbdev(intf);
 521        struct kovaplus_device *kovaplus;
 522        int retval;
 523
 524        if (intf->cur_altsetting->desc.bInterfaceProtocol
 525                        == USB_INTERFACE_PROTOCOL_MOUSE) {
 526
 527                kovaplus = kzalloc(sizeof(*kovaplus), GFP_KERNEL);
 528                if (!kovaplus) {
 529                        hid_err(hdev, "can't alloc device descriptor\n");
 530                        return -ENOMEM;
 531                }
 532                hid_set_drvdata(hdev, kovaplus);
 533
 534                retval = kovaplus_init_kovaplus_device_struct(usb_dev, kovaplus);
 535                if (retval) {
 536                        hid_err(hdev, "couldn't init struct kovaplus_device\n");
 537                        goto exit_free;
 538                }
 539
 540                retval = roccat_connect(kovaplus_class, hdev,
 541                                sizeof(struct kovaplus_roccat_report));
 542                if (retval < 0) {
 543                        hid_err(hdev, "couldn't init char dev\n");
 544                } else {
 545                        kovaplus->chrdev_minor = retval;
 546                        kovaplus->roccat_claimed = 1;
 547                }
 548
 549        } else {
 550                hid_set_drvdata(hdev, NULL);
 551        }
 552
 553        return 0;
 554exit_free:
 555        kfree(kovaplus);
 556        return retval;
 557}
 558
 559static void kovaplus_remove_specials(struct hid_device *hdev)
 560{
 561        struct usb_interface *intf = to_usb_interface(hdev->dev.parent);
 562        struct kovaplus_device *kovaplus;
 563
 564        if (intf->cur_altsetting->desc.bInterfaceProtocol
 565                        == USB_INTERFACE_PROTOCOL_MOUSE) {
 566                kovaplus = hid_get_drvdata(hdev);
 567                if (kovaplus->roccat_claimed)
 568                        roccat_disconnect(kovaplus->chrdev_minor);
 569                kfree(kovaplus);
 570        }
 571}
 572
 573static int kovaplus_probe(struct hid_device *hdev,
 574                const struct hid_device_id *id)
 575{
 576        int retval;
 577
 578        retval = hid_parse(hdev);
 579        if (retval) {
 580                hid_err(hdev, "parse failed\n");
 581                goto exit;
 582        }
 583
 584        retval = hid_hw_start(hdev, HID_CONNECT_DEFAULT);
 585        if (retval) {
 586                hid_err(hdev, "hw start failed\n");
 587                goto exit;
 588        }
 589
 590        retval = kovaplus_init_specials(hdev);
 591        if (retval) {
 592                hid_err(hdev, "couldn't install mouse\n");
 593                goto exit_stop;
 594        }
 595
 596        return 0;
 597
 598exit_stop:
 599        hid_hw_stop(hdev);
 600exit:
 601        return retval;
 602}
 603
 604static void kovaplus_remove(struct hid_device *hdev)
 605{
 606        kovaplus_remove_specials(hdev);
 607        hid_hw_stop(hdev);
 608}
 609
 610static void kovaplus_keep_values_up_to_date(struct kovaplus_device *kovaplus,
 611                u8 const *data)
 612{
 613        struct kovaplus_mouse_report_button const *button_report;
 614
 615        if (data[0] != KOVAPLUS_MOUSE_REPORT_NUMBER_BUTTON)
 616                return;
 617
 618        button_report = (struct kovaplus_mouse_report_button const *)data;
 619
 620        switch (button_report->type) {
 621        case KOVAPLUS_MOUSE_REPORT_BUTTON_TYPE_PROFILE_1:
 622                kovaplus_profile_activated(kovaplus, button_report->data1 - 1);
 623                break;
 624        case KOVAPLUS_MOUSE_REPORT_BUTTON_TYPE_CPI:
 625                kovaplus->actual_cpi = kovaplus_convert_event_cpi(button_report->data1);
 626        case KOVAPLUS_MOUSE_REPORT_BUTTON_TYPE_SENSITIVITY:
 627                kovaplus->actual_x_sensitivity = button_report->data1;
 628                kovaplus->actual_y_sensitivity = button_report->data2;
 629        }
 630}
 631
 632static void kovaplus_report_to_chrdev(struct kovaplus_device const *kovaplus,
 633                u8 const *data)
 634{
 635        struct kovaplus_roccat_report roccat_report;
 636        struct kovaplus_mouse_report_button const *button_report;
 637
 638        if (data[0] != KOVAPLUS_MOUSE_REPORT_NUMBER_BUTTON)
 639                return;
 640
 641        button_report = (struct kovaplus_mouse_report_button const *)data;
 642
 643        if (button_report->type == KOVAPLUS_MOUSE_REPORT_BUTTON_TYPE_PROFILE_2)
 644                return;
 645
 646        roccat_report.type = button_report->type;
 647        roccat_report.profile = kovaplus->actual_profile + 1;
 648
 649        if (roccat_report.type == KOVAPLUS_MOUSE_REPORT_BUTTON_TYPE_MACRO ||
 650                        roccat_report.type == KOVAPLUS_MOUSE_REPORT_BUTTON_TYPE_SHORTCUT ||
 651                        roccat_report.type == KOVAPLUS_MOUSE_REPORT_BUTTON_TYPE_QUICKLAUNCH ||
 652                        roccat_report.type == KOVAPLUS_MOUSE_REPORT_BUTTON_TYPE_TIMER)
 653                roccat_report.button = button_report->data1;
 654        else
 655                roccat_report.button = 0;
 656
 657        if (roccat_report.type == KOVAPLUS_MOUSE_REPORT_BUTTON_TYPE_CPI)
 658                roccat_report.data1 = kovaplus_convert_event_cpi(button_report->data1);
 659        else
 660                roccat_report.data1 = button_report->data1;
 661
 662        roccat_report.data2 = button_report->data2;
 663
 664        roccat_report_event(kovaplus->chrdev_minor,
 665                        (uint8_t const *)&roccat_report);
 666}
 667
 668static int kovaplus_raw_event(struct hid_device *hdev,
 669                struct hid_report *report, u8 *data, int size)
 670{
 671        struct usb_interface *intf = to_usb_interface(hdev->dev.parent);
 672        struct kovaplus_device *kovaplus = hid_get_drvdata(hdev);
 673
 674        if (intf->cur_altsetting->desc.bInterfaceProtocol
 675                        != USB_INTERFACE_PROTOCOL_MOUSE)
 676                return 0;
 677
 678        if (kovaplus == NULL)
 679                return 0;
 680
 681        kovaplus_keep_values_up_to_date(kovaplus, data);
 682
 683        if (kovaplus->roccat_claimed)
 684                kovaplus_report_to_chrdev(kovaplus, data);
 685
 686        return 0;
 687}
 688
 689static const struct hid_device_id kovaplus_devices[] = {
 690        { HID_USB_DEVICE(USB_VENDOR_ID_ROCCAT, USB_DEVICE_ID_ROCCAT_KOVAPLUS) },
 691        { }
 692};
 693
 694MODULE_DEVICE_TABLE(hid, kovaplus_devices);
 695
 696static struct hid_driver kovaplus_driver = {
 697                .name = "kovaplus",
 698                .id_table = kovaplus_devices,
 699                .probe = kovaplus_probe,
 700                .remove = kovaplus_remove,
 701                .raw_event = kovaplus_raw_event
 702};
 703
 704static int __init kovaplus_init(void)
 705{
 706        int retval;
 707
 708        kovaplus_class = class_create(THIS_MODULE, "kovaplus");
 709        if (IS_ERR(kovaplus_class))
 710                return PTR_ERR(kovaplus_class);
 711        kovaplus_class->dev_attrs = kovaplus_attributes;
 712        kovaplus_class->dev_bin_attrs = kovaplus_bin_attributes;
 713
 714        retval = hid_register_driver(&kovaplus_driver);
 715        if (retval)
 716                class_destroy(kovaplus_class);
 717        return retval;
 718}
 719
 720static void __exit kovaplus_exit(void)
 721{
 722        hid_unregister_driver(&kovaplus_driver);
 723        class_destroy(kovaplus_class);
 724}
 725
 726module_init(kovaplus_init);
 727module_exit(kovaplus_exit);
 728
 729MODULE_AUTHOR("Stefan Achatz");
 730MODULE_DESCRIPTION("USB Roccat Kova[+] driver");
 731MODULE_LICENSE("GPL v2");
 732