757 lines · c
1// SPDX-License-Identifier: GPL-2.0-or-later2/*3 * HID driver for Corsair devices4 *5 * Supported devices:6 * - Vengeance K70 Keyboard7 * - K70 RAPIDFIRE Keyboard8 * - Vengeance K90 Keyboard9 * - Scimitar PRO RGB Gaming Mouse10 *11 * Copyright (c) 2015 Clement Vuchener12 * Copyright (c) 2017 Oscar Campos13 * Copyright (c) 2017 Aaron Bottegal14 */15 16/*17 */18 19#include <linux/hid.h>20#include <linux/module.h>21#include <linux/usb.h>22#include <linux/leds.h>23 24#include "hid-ids.h"25 26#define CORSAIR_USE_K90_MACRO (1<<0)27#define CORSAIR_USE_K90_BACKLIGHT (1<<1)28 29struct k90_led {30 struct led_classdev cdev;31 int brightness;32 struct work_struct work;33 bool removed;34};35 36struct k90_drvdata {37 struct k90_led record_led;38};39 40struct corsair_drvdata {41 unsigned long quirks;42 struct k90_drvdata *k90;43 struct k90_led *backlight;44};45 46#define K90_GKEY_COUNT 1847 48static int corsair_usage_to_gkey(unsigned int usage)49{50 /* G1 (0xd0) to G16 (0xdf) */51 if (usage >= 0xd0 && usage <= 0xdf)52 return usage - 0xd0 + 1;53 /* G17 (0xe8) to G18 (0xe9) */54 if (usage >= 0xe8 && usage <= 0xe9)55 return usage - 0xe8 + 17;56 return 0;57}58 59static unsigned short corsair_gkey_map[K90_GKEY_COUNT] = {60 BTN_TRIGGER_HAPPY1,61 BTN_TRIGGER_HAPPY2,62 BTN_TRIGGER_HAPPY3,63 BTN_TRIGGER_HAPPY4,64 BTN_TRIGGER_HAPPY5,65 BTN_TRIGGER_HAPPY6,66 BTN_TRIGGER_HAPPY7,67 BTN_TRIGGER_HAPPY8,68 BTN_TRIGGER_HAPPY9,69 BTN_TRIGGER_HAPPY10,70 BTN_TRIGGER_HAPPY11,71 BTN_TRIGGER_HAPPY12,72 BTN_TRIGGER_HAPPY13,73 BTN_TRIGGER_HAPPY14,74 BTN_TRIGGER_HAPPY15,75 BTN_TRIGGER_HAPPY16,76 BTN_TRIGGER_HAPPY17,77 BTN_TRIGGER_HAPPY18,78};79 80module_param_array_named(gkey_codes, corsair_gkey_map, ushort, NULL, S_IRUGO);81MODULE_PARM_DESC(gkey_codes, "Key codes for the G-keys");82 83static unsigned short corsair_record_keycodes[2] = {84 BTN_TRIGGER_HAPPY19,85 BTN_TRIGGER_HAPPY2086};87 88module_param_array_named(recordkey_codes, corsair_record_keycodes, ushort,89 NULL, S_IRUGO);90MODULE_PARM_DESC(recordkey_codes, "Key codes for the MR (start and stop record) button");91 92static unsigned short corsair_profile_keycodes[3] = {93 BTN_TRIGGER_HAPPY21,94 BTN_TRIGGER_HAPPY22,95 BTN_TRIGGER_HAPPY2396};97 98module_param_array_named(profilekey_codes, corsair_profile_keycodes, ushort,99 NULL, S_IRUGO);100MODULE_PARM_DESC(profilekey_codes, "Key codes for the profile buttons");101 102#define CORSAIR_USAGE_SPECIAL_MIN 0xf0103#define CORSAIR_USAGE_SPECIAL_MAX 0xff104 105#define CORSAIR_USAGE_MACRO_RECORD_START 0xf6106#define CORSAIR_USAGE_MACRO_RECORD_STOP 0xf7107 108#define CORSAIR_USAGE_PROFILE 0xf1109#define CORSAIR_USAGE_M1 0xf1110#define CORSAIR_USAGE_M2 0xf2111#define CORSAIR_USAGE_M3 0xf3112#define CORSAIR_USAGE_PROFILE_MAX 0xf3113 114#define CORSAIR_USAGE_META_OFF 0xf4115#define CORSAIR_USAGE_META_ON 0xf5116 117#define CORSAIR_USAGE_LIGHT 0xfa118#define CORSAIR_USAGE_LIGHT_OFF 0xfa119#define CORSAIR_USAGE_LIGHT_DIM 0xfb120#define CORSAIR_USAGE_LIGHT_MEDIUM 0xfc121#define CORSAIR_USAGE_LIGHT_BRIGHT 0xfd122#define CORSAIR_USAGE_LIGHT_MAX 0xfd123 124/* USB control protocol */125 126#define K90_REQUEST_BRIGHTNESS 49127#define K90_REQUEST_MACRO_MODE 2128#define K90_REQUEST_STATUS 4129#define K90_REQUEST_GET_MODE 5130#define K90_REQUEST_PROFILE 20131 132#define K90_MACRO_MODE_SW 0x0030133#define K90_MACRO_MODE_HW 0x0001134 135#define K90_MACRO_LED_ON 0x0020136#define K90_MACRO_LED_OFF 0x0040137 138/*139 * LED class devices140 */141 142#define K90_BACKLIGHT_LED_SUFFIX "::backlight"143#define K90_RECORD_LED_SUFFIX "::record"144 145static enum led_brightness k90_backlight_get(struct led_classdev *led_cdev)146{147 int ret;148 struct k90_led *led = container_of(led_cdev, struct k90_led, cdev);149 struct device *dev = led->cdev.dev->parent;150 struct usb_interface *usbif = to_usb_interface(dev->parent);151 struct usb_device *usbdev = interface_to_usbdev(usbif);152 int brightness;153 char *data;154 155 data = kmalloc(8, GFP_KERNEL);156 if (!data)157 return -ENOMEM;158 159 ret = usb_control_msg(usbdev, usb_rcvctrlpipe(usbdev, 0),160 K90_REQUEST_STATUS,161 USB_DIR_IN | USB_TYPE_VENDOR |162 USB_RECIP_DEVICE, 0, 0, data, 8,163 USB_CTRL_SET_TIMEOUT);164 if (ret < 5) {165 dev_warn(dev, "Failed to get K90 initial state (error %d).\n",166 ret);167 ret = -EIO;168 goto out;169 }170 brightness = data[4];171 if (brightness < 0 || brightness > 3) {172 dev_warn(dev,173 "Read invalid backlight brightness: %02hhx.\n",174 data[4]);175 ret = -EIO;176 goto out;177 }178 ret = brightness;179out:180 kfree(data);181 182 return ret;183}184 185static enum led_brightness k90_record_led_get(struct led_classdev *led_cdev)186{187 struct k90_led *led = container_of(led_cdev, struct k90_led, cdev);188 189 return led->brightness;190}191 192static void k90_brightness_set(struct led_classdev *led_cdev,193 enum led_brightness brightness)194{195 struct k90_led *led = container_of(led_cdev, struct k90_led, cdev);196 197 led->brightness = brightness;198 schedule_work(&led->work);199}200 201static void k90_backlight_work(struct work_struct *work)202{203 int ret;204 struct k90_led *led = container_of(work, struct k90_led, work);205 struct device *dev;206 struct usb_interface *usbif;207 struct usb_device *usbdev;208 209 if (led->removed)210 return;211 212 dev = led->cdev.dev->parent;213 usbif = to_usb_interface(dev->parent);214 usbdev = interface_to_usbdev(usbif);215 216 ret = usb_control_msg(usbdev, usb_sndctrlpipe(usbdev, 0),217 K90_REQUEST_BRIGHTNESS,218 USB_DIR_OUT | USB_TYPE_VENDOR |219 USB_RECIP_DEVICE, led->brightness, 0,220 NULL, 0, USB_CTRL_SET_TIMEOUT);221 if (ret != 0)222 dev_warn(dev, "Failed to set backlight brightness (error: %d).\n",223 ret);224}225 226static void k90_record_led_work(struct work_struct *work)227{228 int ret;229 struct k90_led *led = container_of(work, struct k90_led, work);230 struct device *dev;231 struct usb_interface *usbif;232 struct usb_device *usbdev;233 int value;234 235 if (led->removed)236 return;237 238 dev = led->cdev.dev->parent;239 usbif = to_usb_interface(dev->parent);240 usbdev = interface_to_usbdev(usbif);241 242 if (led->brightness > 0)243 value = K90_MACRO_LED_ON;244 else245 value = K90_MACRO_LED_OFF;246 247 ret = usb_control_msg(usbdev, usb_sndctrlpipe(usbdev, 0),248 K90_REQUEST_MACRO_MODE,249 USB_DIR_OUT | USB_TYPE_VENDOR |250 USB_RECIP_DEVICE, value, 0, NULL, 0,251 USB_CTRL_SET_TIMEOUT);252 if (ret != 0)253 dev_warn(dev, "Failed to set record LED state (error: %d).\n",254 ret);255}256 257/*258 * Keyboard attributes259 */260 261static ssize_t k90_show_macro_mode(struct device *dev,262 struct device_attribute *attr, char *buf)263{264 int ret;265 struct usb_interface *usbif = to_usb_interface(dev->parent);266 struct usb_device *usbdev = interface_to_usbdev(usbif);267 const char *macro_mode;268 char *data;269 270 data = kmalloc(2, GFP_KERNEL);271 if (!data)272 return -ENOMEM;273 274 ret = usb_control_msg(usbdev, usb_rcvctrlpipe(usbdev, 0),275 K90_REQUEST_GET_MODE,276 USB_DIR_IN | USB_TYPE_VENDOR |277 USB_RECIP_DEVICE, 0, 0, data, 2,278 USB_CTRL_SET_TIMEOUT);279 if (ret < 1) {280 dev_warn(dev, "Failed to get K90 initial mode (error %d).\n",281 ret);282 ret = -EIO;283 goto out;284 }285 286 switch (data[0]) {287 case K90_MACRO_MODE_HW:288 macro_mode = "HW";289 break;290 291 case K90_MACRO_MODE_SW:292 macro_mode = "SW";293 break;294 default:295 dev_warn(dev, "K90 in unknown mode: %02hhx.\n",296 data[0]);297 ret = -EIO;298 goto out;299 }300 301 ret = sysfs_emit(buf, "%s\n", macro_mode);302out:303 kfree(data);304 305 return ret;306}307 308static ssize_t k90_store_macro_mode(struct device *dev,309 struct device_attribute *attr,310 const char *buf, size_t count)311{312 int ret;313 struct usb_interface *usbif = to_usb_interface(dev->parent);314 struct usb_device *usbdev = interface_to_usbdev(usbif);315 __u16 value;316 317 if (strncmp(buf, "SW", 2) == 0)318 value = K90_MACRO_MODE_SW;319 else if (strncmp(buf, "HW", 2) == 0)320 value = K90_MACRO_MODE_HW;321 else322 return -EINVAL;323 324 ret = usb_control_msg(usbdev, usb_sndctrlpipe(usbdev, 0),325 K90_REQUEST_MACRO_MODE,326 USB_DIR_OUT | USB_TYPE_VENDOR |327 USB_RECIP_DEVICE, value, 0, NULL, 0,328 USB_CTRL_SET_TIMEOUT);329 if (ret != 0) {330 dev_warn(dev, "Failed to set macro mode.\n");331 return ret;332 }333 334 return count;335}336 337static ssize_t k90_show_current_profile(struct device *dev,338 struct device_attribute *attr,339 char *buf)340{341 int ret;342 struct usb_interface *usbif = to_usb_interface(dev->parent);343 struct usb_device *usbdev = interface_to_usbdev(usbif);344 int current_profile;345 char *data;346 347 data = kmalloc(8, GFP_KERNEL);348 if (!data)349 return -ENOMEM;350 351 ret = usb_control_msg(usbdev, usb_rcvctrlpipe(usbdev, 0),352 K90_REQUEST_STATUS,353 USB_DIR_IN | USB_TYPE_VENDOR |354 USB_RECIP_DEVICE, 0, 0, data, 8,355 USB_CTRL_SET_TIMEOUT);356 if (ret < 8) {357 dev_warn(dev, "Failed to get K90 initial state (error %d).\n",358 ret);359 ret = -EIO;360 goto out;361 }362 current_profile = data[7];363 if (current_profile < 1 || current_profile > 3) {364 dev_warn(dev, "Read invalid current profile: %02hhx.\n",365 data[7]);366 ret = -EIO;367 goto out;368 }369 370 ret = sysfs_emit(buf, "%d\n", current_profile);371out:372 kfree(data);373 374 return ret;375}376 377static ssize_t k90_store_current_profile(struct device *dev,378 struct device_attribute *attr,379 const char *buf, size_t count)380{381 int ret;382 struct usb_interface *usbif = to_usb_interface(dev->parent);383 struct usb_device *usbdev = interface_to_usbdev(usbif);384 int profile;385 386 if (kstrtoint(buf, 10, &profile))387 return -EINVAL;388 if (profile < 1 || profile > 3)389 return -EINVAL;390 391 ret = usb_control_msg(usbdev, usb_sndctrlpipe(usbdev, 0),392 K90_REQUEST_PROFILE,393 USB_DIR_OUT | USB_TYPE_VENDOR |394 USB_RECIP_DEVICE, profile, 0, NULL, 0,395 USB_CTRL_SET_TIMEOUT);396 if (ret != 0) {397 dev_warn(dev, "Failed to change current profile (error %d).\n",398 ret);399 return ret;400 }401 402 return count;403}404 405static DEVICE_ATTR(macro_mode, 0644, k90_show_macro_mode, k90_store_macro_mode);406static DEVICE_ATTR(current_profile, 0644, k90_show_current_profile,407 k90_store_current_profile);408 409static struct attribute *k90_attrs[] = {410 &dev_attr_macro_mode.attr,411 &dev_attr_current_profile.attr,412 NULL413};414 415static const struct attribute_group k90_attr_group = {416 .attrs = k90_attrs,417};418 419/*420 * Driver functions421 */422 423static int k90_init_backlight(struct hid_device *dev)424{425 int ret;426 struct corsair_drvdata *drvdata = hid_get_drvdata(dev);427 size_t name_sz;428 char *name;429 430 drvdata->backlight = kzalloc(sizeof(struct k90_led), GFP_KERNEL);431 if (!drvdata->backlight) {432 ret = -ENOMEM;433 goto fail_backlight_alloc;434 }435 436 name_sz =437 strlen(dev_name(&dev->dev)) + sizeof(K90_BACKLIGHT_LED_SUFFIX);438 name = kzalloc(name_sz, GFP_KERNEL);439 if (!name) {440 ret = -ENOMEM;441 goto fail_name_alloc;442 }443 snprintf(name, name_sz, "%s" K90_BACKLIGHT_LED_SUFFIX,444 dev_name(&dev->dev));445 drvdata->backlight->removed = false;446 drvdata->backlight->cdev.name = name;447 drvdata->backlight->cdev.max_brightness = 3;448 drvdata->backlight->cdev.brightness_set = k90_brightness_set;449 drvdata->backlight->cdev.brightness_get = k90_backlight_get;450 INIT_WORK(&drvdata->backlight->work, k90_backlight_work);451 ret = led_classdev_register(&dev->dev, &drvdata->backlight->cdev);452 if (ret != 0)453 goto fail_register_cdev;454 455 return 0;456 457fail_register_cdev:458 kfree(drvdata->backlight->cdev.name);459fail_name_alloc:460 kfree(drvdata->backlight);461 drvdata->backlight = NULL;462fail_backlight_alloc:463 return ret;464}465 466static int k90_init_macro_functions(struct hid_device *dev)467{468 int ret;469 struct corsair_drvdata *drvdata = hid_get_drvdata(dev);470 struct k90_drvdata *k90;471 size_t name_sz;472 char *name;473 474 k90 = kzalloc(sizeof(struct k90_drvdata), GFP_KERNEL);475 if (!k90) {476 ret = -ENOMEM;477 goto fail_drvdata;478 }479 drvdata->k90 = k90;480 481 /* Init LED device for record LED */482 name_sz = strlen(dev_name(&dev->dev)) + sizeof(K90_RECORD_LED_SUFFIX);483 name = kzalloc(name_sz, GFP_KERNEL);484 if (!name) {485 ret = -ENOMEM;486 goto fail_record_led_alloc;487 }488 snprintf(name, name_sz, "%s" K90_RECORD_LED_SUFFIX,489 dev_name(&dev->dev));490 k90->record_led.removed = false;491 k90->record_led.cdev.name = name;492 k90->record_led.cdev.max_brightness = 1;493 k90->record_led.cdev.brightness_set = k90_brightness_set;494 k90->record_led.cdev.brightness_get = k90_record_led_get;495 INIT_WORK(&k90->record_led.work, k90_record_led_work);496 k90->record_led.brightness = 0;497 ret = led_classdev_register(&dev->dev, &k90->record_led.cdev);498 if (ret != 0)499 goto fail_record_led;500 501 /* Init attributes */502 ret = sysfs_create_group(&dev->dev.kobj, &k90_attr_group);503 if (ret != 0)504 goto fail_sysfs;505 506 return 0;507 508fail_sysfs:509 k90->record_led.removed = true;510 led_classdev_unregister(&k90->record_led.cdev);511 cancel_work_sync(&k90->record_led.work);512fail_record_led:513 kfree(k90->record_led.cdev.name);514fail_record_led_alloc:515 kfree(k90);516fail_drvdata:517 drvdata->k90 = NULL;518 return ret;519}520 521static void k90_cleanup_backlight(struct hid_device *dev)522{523 struct corsair_drvdata *drvdata = hid_get_drvdata(dev);524 525 if (drvdata->backlight) {526 drvdata->backlight->removed = true;527 led_classdev_unregister(&drvdata->backlight->cdev);528 cancel_work_sync(&drvdata->backlight->work);529 kfree(drvdata->backlight->cdev.name);530 kfree(drvdata->backlight);531 }532}533 534static void k90_cleanup_macro_functions(struct hid_device *dev)535{536 struct corsair_drvdata *drvdata = hid_get_drvdata(dev);537 struct k90_drvdata *k90 = drvdata->k90;538 539 if (k90) {540 sysfs_remove_group(&dev->dev.kobj, &k90_attr_group);541 542 k90->record_led.removed = true;543 led_classdev_unregister(&k90->record_led.cdev);544 cancel_work_sync(&k90->record_led.work);545 kfree(k90->record_led.cdev.name);546 547 kfree(k90);548 }549}550 551static int corsair_probe(struct hid_device *dev, const struct hid_device_id *id)552{553 int ret;554 unsigned long quirks = id->driver_data;555 struct corsair_drvdata *drvdata;556 struct usb_interface *usbif;557 558 if (!hid_is_usb(dev))559 return -EINVAL;560 561 usbif = to_usb_interface(dev->dev.parent);562 563 drvdata = devm_kzalloc(&dev->dev, sizeof(struct corsair_drvdata),564 GFP_KERNEL);565 if (drvdata == NULL)566 return -ENOMEM;567 drvdata->quirks = quirks;568 hid_set_drvdata(dev, drvdata);569 570 ret = hid_parse(dev);571 if (ret != 0) {572 hid_err(dev, "parse failed\n");573 return ret;574 }575 ret = hid_hw_start(dev, HID_CONNECT_DEFAULT);576 if (ret != 0) {577 hid_err(dev, "hw start failed\n");578 return ret;579 }580 581 if (usbif->cur_altsetting->desc.bInterfaceNumber == 0) {582 if (quirks & CORSAIR_USE_K90_MACRO) {583 ret = k90_init_macro_functions(dev);584 if (ret != 0)585 hid_warn(dev, "Failed to initialize K90 macro functions.\n");586 }587 if (quirks & CORSAIR_USE_K90_BACKLIGHT) {588 ret = k90_init_backlight(dev);589 if (ret != 0)590 hid_warn(dev, "Failed to initialize K90 backlight.\n");591 }592 }593 594 return 0;595}596 597static void corsair_remove(struct hid_device *dev)598{599 k90_cleanup_macro_functions(dev);600 k90_cleanup_backlight(dev);601 602 hid_hw_stop(dev);603}604 605static int corsair_event(struct hid_device *dev, struct hid_field *field,606 struct hid_usage *usage, __s32 value)607{608 struct corsair_drvdata *drvdata = hid_get_drvdata(dev);609 610 if (!drvdata->k90)611 return 0;612 613 switch (usage->hid & HID_USAGE) {614 case CORSAIR_USAGE_MACRO_RECORD_START:615 drvdata->k90->record_led.brightness = 1;616 break;617 case CORSAIR_USAGE_MACRO_RECORD_STOP:618 drvdata->k90->record_led.brightness = 0;619 break;620 default:621 break;622 }623 624 return 0;625}626 627static int corsair_input_mapping(struct hid_device *dev,628 struct hid_input *input,629 struct hid_field *field,630 struct hid_usage *usage, unsigned long **bit,631 int *max)632{633 int gkey;634 635 if ((usage->hid & HID_USAGE_PAGE) != HID_UP_KEYBOARD)636 return 0;637 638 gkey = corsair_usage_to_gkey(usage->hid & HID_USAGE);639 if (gkey != 0) {640 hid_map_usage_clear(input, usage, bit, max, EV_KEY,641 corsair_gkey_map[gkey - 1]);642 return 1;643 }644 if ((usage->hid & HID_USAGE) >= CORSAIR_USAGE_SPECIAL_MIN &&645 (usage->hid & HID_USAGE) <= CORSAIR_USAGE_SPECIAL_MAX) {646 switch (usage->hid & HID_USAGE) {647 case CORSAIR_USAGE_MACRO_RECORD_START:648 hid_map_usage_clear(input, usage, bit, max, EV_KEY,649 corsair_record_keycodes[0]);650 return 1;651 652 case CORSAIR_USAGE_MACRO_RECORD_STOP:653 hid_map_usage_clear(input, usage, bit, max, EV_KEY,654 corsair_record_keycodes[1]);655 return 1;656 657 case CORSAIR_USAGE_M1:658 hid_map_usage_clear(input, usage, bit, max, EV_KEY,659 corsair_profile_keycodes[0]);660 return 1;661 662 case CORSAIR_USAGE_M2:663 hid_map_usage_clear(input, usage, bit, max, EV_KEY,664 corsair_profile_keycodes[1]);665 return 1;666 667 case CORSAIR_USAGE_M3:668 hid_map_usage_clear(input, usage, bit, max, EV_KEY,669 corsair_profile_keycodes[2]);670 return 1;671 672 default:673 return -1;674 }675 }676 677 return 0;678}679 680/*681 * The report descriptor of some of the Corsair gaming mice is682 * non parseable as they define two consecutive Logical Minimum for683 * the Usage Page (Consumer) in rdescs bytes 75 and 77 being 77 0x16684 * that should be obviousy 0x26 for Logical Magimum of 16 bits. This685 * prevents poper parsing of the report descriptor due Logical686 * Minimum being larger than Logical Maximum.687 *688 * This driver fixes the report descriptor for:689 * - USB ID 1b1c:1b34, sold as GLAIVE RGB Gaming mouse690 * - USB ID 1b1c:1b3e, sold as Scimitar RGB Pro Gaming mouse691 */692 693static const __u8 *corsair_mouse_report_fixup(struct hid_device *hdev,694 __u8 *rdesc, unsigned int *rsize)695{696 struct usb_interface *intf = to_usb_interface(hdev->dev.parent);697 698 if (intf->cur_altsetting->desc.bInterfaceNumber == 1) {699 /*700 * Corsair GLAIVE RGB and Scimitar RGB Pro report descriptor is701 * broken and defines two different Logical Minimum for the702 * Consumer Application. The byte 77 should be a 0x26 defining703 * a 16 bits integer for the Logical Maximum but it is a 0x16704 * instead (Logical Minimum)705 */706 switch (hdev->product) {707 case USB_DEVICE_ID_CORSAIR_GLAIVE_RGB:708 case USB_DEVICE_ID_CORSAIR_SCIMITAR_PRO_RGB:709 if (*rsize >= 172 && rdesc[75] == 0x15 && rdesc[77] == 0x16710 && rdesc[78] == 0xff && rdesc[79] == 0x0f) {711 hid_info(hdev, "Fixing up report descriptor\n");712 rdesc[77] = 0x26;713 }714 break;715 }716 717 }718 return rdesc;719}720 721static const struct hid_device_id corsair_devices[] = {722 { HID_USB_DEVICE(USB_VENDOR_ID_CORSAIR, USB_DEVICE_ID_CORSAIR_K90),723 .driver_data = CORSAIR_USE_K90_MACRO |724 CORSAIR_USE_K90_BACKLIGHT },725 { HID_USB_DEVICE(USB_VENDOR_ID_CORSAIR,726 USB_DEVICE_ID_CORSAIR_GLAIVE_RGB) },727 { HID_USB_DEVICE(USB_VENDOR_ID_CORSAIR,728 USB_DEVICE_ID_CORSAIR_SCIMITAR_PRO_RGB) },729 /*730 * Vengeance K70 and K70 RAPIDFIRE share product IDs.731 */732 { HID_USB_DEVICE(USB_VENDOR_ID_CORSAIR,733 USB_DEVICE_ID_CORSAIR_K70R) },734 {}735};736 737MODULE_DEVICE_TABLE(hid, corsair_devices);738 739static struct hid_driver corsair_driver = {740 .name = "corsair",741 .id_table = corsair_devices,742 .probe = corsair_probe,743 .event = corsair_event,744 .remove = corsair_remove,745 .input_mapping = corsair_input_mapping,746 .report_fixup = corsair_mouse_report_fixup,747};748 749module_hid_driver(corsair_driver);750 751MODULE_LICENSE("GPL");752/* Original K90 driver author */753MODULE_AUTHOR("Clement Vuchener");754/* Scimitar PRO RGB driver author */755MODULE_AUTHOR("Oscar Campos");756MODULE_DESCRIPTION("HID driver for Corsair devices");757