162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * HID driver for Corsair devices
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Supported devices:
662306a36Sopenharmony_ci *  - Vengeance K70 Keyboard
762306a36Sopenharmony_ci *  - K70 RAPIDFIRE Keyboard
862306a36Sopenharmony_ci *  - Vengeance K90 Keyboard
962306a36Sopenharmony_ci *  - Scimitar PRO RGB Gaming Mouse
1062306a36Sopenharmony_ci *
1162306a36Sopenharmony_ci * Copyright (c) 2015 Clement Vuchener
1262306a36Sopenharmony_ci * Copyright (c) 2017 Oscar Campos
1362306a36Sopenharmony_ci * Copyright (c) 2017 Aaron Bottegal
1462306a36Sopenharmony_ci */
1562306a36Sopenharmony_ci
1662306a36Sopenharmony_ci/*
1762306a36Sopenharmony_ci */
1862306a36Sopenharmony_ci
1962306a36Sopenharmony_ci#include <linux/hid.h>
2062306a36Sopenharmony_ci#include <linux/module.h>
2162306a36Sopenharmony_ci#include <linux/usb.h>
2262306a36Sopenharmony_ci#include <linux/leds.h>
2362306a36Sopenharmony_ci
2462306a36Sopenharmony_ci#include "hid-ids.h"
2562306a36Sopenharmony_ci
2662306a36Sopenharmony_ci#define CORSAIR_USE_K90_MACRO	(1<<0)
2762306a36Sopenharmony_ci#define CORSAIR_USE_K90_BACKLIGHT	(1<<1)
2862306a36Sopenharmony_ci
2962306a36Sopenharmony_cistruct k90_led {
3062306a36Sopenharmony_ci	struct led_classdev cdev;
3162306a36Sopenharmony_ci	int brightness;
3262306a36Sopenharmony_ci	struct work_struct work;
3362306a36Sopenharmony_ci	bool removed;
3462306a36Sopenharmony_ci};
3562306a36Sopenharmony_ci
3662306a36Sopenharmony_cistruct k90_drvdata {
3762306a36Sopenharmony_ci	struct k90_led record_led;
3862306a36Sopenharmony_ci};
3962306a36Sopenharmony_ci
4062306a36Sopenharmony_cistruct corsair_drvdata {
4162306a36Sopenharmony_ci	unsigned long quirks;
4262306a36Sopenharmony_ci	struct k90_drvdata *k90;
4362306a36Sopenharmony_ci	struct k90_led *backlight;
4462306a36Sopenharmony_ci};
4562306a36Sopenharmony_ci
4662306a36Sopenharmony_ci#define K90_GKEY_COUNT	18
4762306a36Sopenharmony_ci
4862306a36Sopenharmony_cistatic int corsair_usage_to_gkey(unsigned int usage)
4962306a36Sopenharmony_ci{
5062306a36Sopenharmony_ci	/* G1 (0xd0) to G16 (0xdf) */
5162306a36Sopenharmony_ci	if (usage >= 0xd0 && usage <= 0xdf)
5262306a36Sopenharmony_ci		return usage - 0xd0 + 1;
5362306a36Sopenharmony_ci	/* G17 (0xe8) to G18 (0xe9) */
5462306a36Sopenharmony_ci	if (usage >= 0xe8 && usage <= 0xe9)
5562306a36Sopenharmony_ci		return usage - 0xe8 + 17;
5662306a36Sopenharmony_ci	return 0;
5762306a36Sopenharmony_ci}
5862306a36Sopenharmony_ci
5962306a36Sopenharmony_cistatic unsigned short corsair_gkey_map[K90_GKEY_COUNT] = {
6062306a36Sopenharmony_ci	BTN_TRIGGER_HAPPY1,
6162306a36Sopenharmony_ci	BTN_TRIGGER_HAPPY2,
6262306a36Sopenharmony_ci	BTN_TRIGGER_HAPPY3,
6362306a36Sopenharmony_ci	BTN_TRIGGER_HAPPY4,
6462306a36Sopenharmony_ci	BTN_TRIGGER_HAPPY5,
6562306a36Sopenharmony_ci	BTN_TRIGGER_HAPPY6,
6662306a36Sopenharmony_ci	BTN_TRIGGER_HAPPY7,
6762306a36Sopenharmony_ci	BTN_TRIGGER_HAPPY8,
6862306a36Sopenharmony_ci	BTN_TRIGGER_HAPPY9,
6962306a36Sopenharmony_ci	BTN_TRIGGER_HAPPY10,
7062306a36Sopenharmony_ci	BTN_TRIGGER_HAPPY11,
7162306a36Sopenharmony_ci	BTN_TRIGGER_HAPPY12,
7262306a36Sopenharmony_ci	BTN_TRIGGER_HAPPY13,
7362306a36Sopenharmony_ci	BTN_TRIGGER_HAPPY14,
7462306a36Sopenharmony_ci	BTN_TRIGGER_HAPPY15,
7562306a36Sopenharmony_ci	BTN_TRIGGER_HAPPY16,
7662306a36Sopenharmony_ci	BTN_TRIGGER_HAPPY17,
7762306a36Sopenharmony_ci	BTN_TRIGGER_HAPPY18,
7862306a36Sopenharmony_ci};
7962306a36Sopenharmony_ci
8062306a36Sopenharmony_cimodule_param_array_named(gkey_codes, corsair_gkey_map, ushort, NULL, S_IRUGO);
8162306a36Sopenharmony_ciMODULE_PARM_DESC(gkey_codes, "Key codes for the G-keys");
8262306a36Sopenharmony_ci
8362306a36Sopenharmony_cistatic unsigned short corsair_record_keycodes[2] = {
8462306a36Sopenharmony_ci	BTN_TRIGGER_HAPPY19,
8562306a36Sopenharmony_ci	BTN_TRIGGER_HAPPY20
8662306a36Sopenharmony_ci};
8762306a36Sopenharmony_ci
8862306a36Sopenharmony_cimodule_param_array_named(recordkey_codes, corsair_record_keycodes, ushort,
8962306a36Sopenharmony_ci			 NULL, S_IRUGO);
9062306a36Sopenharmony_ciMODULE_PARM_DESC(recordkey_codes, "Key codes for the MR (start and stop record) button");
9162306a36Sopenharmony_ci
9262306a36Sopenharmony_cistatic unsigned short corsair_profile_keycodes[3] = {
9362306a36Sopenharmony_ci	BTN_TRIGGER_HAPPY21,
9462306a36Sopenharmony_ci	BTN_TRIGGER_HAPPY22,
9562306a36Sopenharmony_ci	BTN_TRIGGER_HAPPY23
9662306a36Sopenharmony_ci};
9762306a36Sopenharmony_ci
9862306a36Sopenharmony_cimodule_param_array_named(profilekey_codes, corsair_profile_keycodes, ushort,
9962306a36Sopenharmony_ci			 NULL, S_IRUGO);
10062306a36Sopenharmony_ciMODULE_PARM_DESC(profilekey_codes, "Key codes for the profile buttons");
10162306a36Sopenharmony_ci
10262306a36Sopenharmony_ci#define CORSAIR_USAGE_SPECIAL_MIN 0xf0
10362306a36Sopenharmony_ci#define CORSAIR_USAGE_SPECIAL_MAX 0xff
10462306a36Sopenharmony_ci
10562306a36Sopenharmony_ci#define CORSAIR_USAGE_MACRO_RECORD_START 0xf6
10662306a36Sopenharmony_ci#define CORSAIR_USAGE_MACRO_RECORD_STOP 0xf7
10762306a36Sopenharmony_ci
10862306a36Sopenharmony_ci#define CORSAIR_USAGE_PROFILE 0xf1
10962306a36Sopenharmony_ci#define CORSAIR_USAGE_M1 0xf1
11062306a36Sopenharmony_ci#define CORSAIR_USAGE_M2 0xf2
11162306a36Sopenharmony_ci#define CORSAIR_USAGE_M3 0xf3
11262306a36Sopenharmony_ci#define CORSAIR_USAGE_PROFILE_MAX 0xf3
11362306a36Sopenharmony_ci
11462306a36Sopenharmony_ci#define CORSAIR_USAGE_META_OFF 0xf4
11562306a36Sopenharmony_ci#define CORSAIR_USAGE_META_ON  0xf5
11662306a36Sopenharmony_ci
11762306a36Sopenharmony_ci#define CORSAIR_USAGE_LIGHT 0xfa
11862306a36Sopenharmony_ci#define CORSAIR_USAGE_LIGHT_OFF 0xfa
11962306a36Sopenharmony_ci#define CORSAIR_USAGE_LIGHT_DIM 0xfb
12062306a36Sopenharmony_ci#define CORSAIR_USAGE_LIGHT_MEDIUM 0xfc
12162306a36Sopenharmony_ci#define CORSAIR_USAGE_LIGHT_BRIGHT 0xfd
12262306a36Sopenharmony_ci#define CORSAIR_USAGE_LIGHT_MAX 0xfd
12362306a36Sopenharmony_ci
12462306a36Sopenharmony_ci/* USB control protocol */
12562306a36Sopenharmony_ci
12662306a36Sopenharmony_ci#define K90_REQUEST_BRIGHTNESS 49
12762306a36Sopenharmony_ci#define K90_REQUEST_MACRO_MODE 2
12862306a36Sopenharmony_ci#define K90_REQUEST_STATUS 4
12962306a36Sopenharmony_ci#define K90_REQUEST_GET_MODE 5
13062306a36Sopenharmony_ci#define K90_REQUEST_PROFILE 20
13162306a36Sopenharmony_ci
13262306a36Sopenharmony_ci#define K90_MACRO_MODE_SW 0x0030
13362306a36Sopenharmony_ci#define K90_MACRO_MODE_HW 0x0001
13462306a36Sopenharmony_ci
13562306a36Sopenharmony_ci#define K90_MACRO_LED_ON  0x0020
13662306a36Sopenharmony_ci#define K90_MACRO_LED_OFF 0x0040
13762306a36Sopenharmony_ci
13862306a36Sopenharmony_ci/*
13962306a36Sopenharmony_ci * LED class devices
14062306a36Sopenharmony_ci */
14162306a36Sopenharmony_ci
14262306a36Sopenharmony_ci#define K90_BACKLIGHT_LED_SUFFIX "::backlight"
14362306a36Sopenharmony_ci#define K90_RECORD_LED_SUFFIX "::record"
14462306a36Sopenharmony_ci
14562306a36Sopenharmony_cistatic enum led_brightness k90_backlight_get(struct led_classdev *led_cdev)
14662306a36Sopenharmony_ci{
14762306a36Sopenharmony_ci	int ret;
14862306a36Sopenharmony_ci	struct k90_led *led = container_of(led_cdev, struct k90_led, cdev);
14962306a36Sopenharmony_ci	struct device *dev = led->cdev.dev->parent;
15062306a36Sopenharmony_ci	struct usb_interface *usbif = to_usb_interface(dev->parent);
15162306a36Sopenharmony_ci	struct usb_device *usbdev = interface_to_usbdev(usbif);
15262306a36Sopenharmony_ci	int brightness;
15362306a36Sopenharmony_ci	char *data;
15462306a36Sopenharmony_ci
15562306a36Sopenharmony_ci	data = kmalloc(8, GFP_KERNEL);
15662306a36Sopenharmony_ci	if (!data)
15762306a36Sopenharmony_ci		return -ENOMEM;
15862306a36Sopenharmony_ci
15962306a36Sopenharmony_ci	ret = usb_control_msg(usbdev, usb_rcvctrlpipe(usbdev, 0),
16062306a36Sopenharmony_ci			      K90_REQUEST_STATUS,
16162306a36Sopenharmony_ci			      USB_DIR_IN | USB_TYPE_VENDOR |
16262306a36Sopenharmony_ci			      USB_RECIP_DEVICE, 0, 0, data, 8,
16362306a36Sopenharmony_ci			      USB_CTRL_SET_TIMEOUT);
16462306a36Sopenharmony_ci	if (ret < 5) {
16562306a36Sopenharmony_ci		dev_warn(dev, "Failed to get K90 initial state (error %d).\n",
16662306a36Sopenharmony_ci			 ret);
16762306a36Sopenharmony_ci		ret = -EIO;
16862306a36Sopenharmony_ci		goto out;
16962306a36Sopenharmony_ci	}
17062306a36Sopenharmony_ci	brightness = data[4];
17162306a36Sopenharmony_ci	if (brightness < 0 || brightness > 3) {
17262306a36Sopenharmony_ci		dev_warn(dev,
17362306a36Sopenharmony_ci			 "Read invalid backlight brightness: %02hhx.\n",
17462306a36Sopenharmony_ci			 data[4]);
17562306a36Sopenharmony_ci		ret = -EIO;
17662306a36Sopenharmony_ci		goto out;
17762306a36Sopenharmony_ci	}
17862306a36Sopenharmony_ci	ret = brightness;
17962306a36Sopenharmony_ciout:
18062306a36Sopenharmony_ci	kfree(data);
18162306a36Sopenharmony_ci
18262306a36Sopenharmony_ci	return ret;
18362306a36Sopenharmony_ci}
18462306a36Sopenharmony_ci
18562306a36Sopenharmony_cistatic enum led_brightness k90_record_led_get(struct led_classdev *led_cdev)
18662306a36Sopenharmony_ci{
18762306a36Sopenharmony_ci	struct k90_led *led = container_of(led_cdev, struct k90_led, cdev);
18862306a36Sopenharmony_ci
18962306a36Sopenharmony_ci	return led->brightness;
19062306a36Sopenharmony_ci}
19162306a36Sopenharmony_ci
19262306a36Sopenharmony_cistatic void k90_brightness_set(struct led_classdev *led_cdev,
19362306a36Sopenharmony_ci			       enum led_brightness brightness)
19462306a36Sopenharmony_ci{
19562306a36Sopenharmony_ci	struct k90_led *led = container_of(led_cdev, struct k90_led, cdev);
19662306a36Sopenharmony_ci
19762306a36Sopenharmony_ci	led->brightness = brightness;
19862306a36Sopenharmony_ci	schedule_work(&led->work);
19962306a36Sopenharmony_ci}
20062306a36Sopenharmony_ci
20162306a36Sopenharmony_cistatic void k90_backlight_work(struct work_struct *work)
20262306a36Sopenharmony_ci{
20362306a36Sopenharmony_ci	int ret;
20462306a36Sopenharmony_ci	struct k90_led *led = container_of(work, struct k90_led, work);
20562306a36Sopenharmony_ci	struct device *dev;
20662306a36Sopenharmony_ci	struct usb_interface *usbif;
20762306a36Sopenharmony_ci	struct usb_device *usbdev;
20862306a36Sopenharmony_ci
20962306a36Sopenharmony_ci	if (led->removed)
21062306a36Sopenharmony_ci		return;
21162306a36Sopenharmony_ci
21262306a36Sopenharmony_ci	dev = led->cdev.dev->parent;
21362306a36Sopenharmony_ci	usbif = to_usb_interface(dev->parent);
21462306a36Sopenharmony_ci	usbdev = interface_to_usbdev(usbif);
21562306a36Sopenharmony_ci
21662306a36Sopenharmony_ci	ret = usb_control_msg(usbdev, usb_sndctrlpipe(usbdev, 0),
21762306a36Sopenharmony_ci			      K90_REQUEST_BRIGHTNESS,
21862306a36Sopenharmony_ci			      USB_DIR_OUT | USB_TYPE_VENDOR |
21962306a36Sopenharmony_ci			      USB_RECIP_DEVICE, led->brightness, 0,
22062306a36Sopenharmony_ci			      NULL, 0, USB_CTRL_SET_TIMEOUT);
22162306a36Sopenharmony_ci	if (ret != 0)
22262306a36Sopenharmony_ci		dev_warn(dev, "Failed to set backlight brightness (error: %d).\n",
22362306a36Sopenharmony_ci			 ret);
22462306a36Sopenharmony_ci}
22562306a36Sopenharmony_ci
22662306a36Sopenharmony_cistatic void k90_record_led_work(struct work_struct *work)
22762306a36Sopenharmony_ci{
22862306a36Sopenharmony_ci	int ret;
22962306a36Sopenharmony_ci	struct k90_led *led = container_of(work, struct k90_led, work);
23062306a36Sopenharmony_ci	struct device *dev;
23162306a36Sopenharmony_ci	struct usb_interface *usbif;
23262306a36Sopenharmony_ci	struct usb_device *usbdev;
23362306a36Sopenharmony_ci	int value;
23462306a36Sopenharmony_ci
23562306a36Sopenharmony_ci	if (led->removed)
23662306a36Sopenharmony_ci		return;
23762306a36Sopenharmony_ci
23862306a36Sopenharmony_ci	dev = led->cdev.dev->parent;
23962306a36Sopenharmony_ci	usbif = to_usb_interface(dev->parent);
24062306a36Sopenharmony_ci	usbdev = interface_to_usbdev(usbif);
24162306a36Sopenharmony_ci
24262306a36Sopenharmony_ci	if (led->brightness > 0)
24362306a36Sopenharmony_ci		value = K90_MACRO_LED_ON;
24462306a36Sopenharmony_ci	else
24562306a36Sopenharmony_ci		value = K90_MACRO_LED_OFF;
24662306a36Sopenharmony_ci
24762306a36Sopenharmony_ci	ret = usb_control_msg(usbdev, usb_sndctrlpipe(usbdev, 0),
24862306a36Sopenharmony_ci			      K90_REQUEST_MACRO_MODE,
24962306a36Sopenharmony_ci			      USB_DIR_OUT | USB_TYPE_VENDOR |
25062306a36Sopenharmony_ci			      USB_RECIP_DEVICE, value, 0, NULL, 0,
25162306a36Sopenharmony_ci			      USB_CTRL_SET_TIMEOUT);
25262306a36Sopenharmony_ci	if (ret != 0)
25362306a36Sopenharmony_ci		dev_warn(dev, "Failed to set record LED state (error: %d).\n",
25462306a36Sopenharmony_ci			 ret);
25562306a36Sopenharmony_ci}
25662306a36Sopenharmony_ci
25762306a36Sopenharmony_ci/*
25862306a36Sopenharmony_ci * Keyboard attributes
25962306a36Sopenharmony_ci */
26062306a36Sopenharmony_ci
26162306a36Sopenharmony_cistatic ssize_t k90_show_macro_mode(struct device *dev,
26262306a36Sopenharmony_ci				   struct device_attribute *attr, char *buf)
26362306a36Sopenharmony_ci{
26462306a36Sopenharmony_ci	int ret;
26562306a36Sopenharmony_ci	struct usb_interface *usbif = to_usb_interface(dev->parent);
26662306a36Sopenharmony_ci	struct usb_device *usbdev = interface_to_usbdev(usbif);
26762306a36Sopenharmony_ci	const char *macro_mode;
26862306a36Sopenharmony_ci	char *data;
26962306a36Sopenharmony_ci
27062306a36Sopenharmony_ci	data = kmalloc(2, GFP_KERNEL);
27162306a36Sopenharmony_ci	if (!data)
27262306a36Sopenharmony_ci		return -ENOMEM;
27362306a36Sopenharmony_ci
27462306a36Sopenharmony_ci	ret = usb_control_msg(usbdev, usb_rcvctrlpipe(usbdev, 0),
27562306a36Sopenharmony_ci			      K90_REQUEST_GET_MODE,
27662306a36Sopenharmony_ci			      USB_DIR_IN | USB_TYPE_VENDOR |
27762306a36Sopenharmony_ci			      USB_RECIP_DEVICE, 0, 0, data, 2,
27862306a36Sopenharmony_ci			      USB_CTRL_SET_TIMEOUT);
27962306a36Sopenharmony_ci	if (ret < 1) {
28062306a36Sopenharmony_ci		dev_warn(dev, "Failed to get K90 initial mode (error %d).\n",
28162306a36Sopenharmony_ci			 ret);
28262306a36Sopenharmony_ci		ret = -EIO;
28362306a36Sopenharmony_ci		goto out;
28462306a36Sopenharmony_ci	}
28562306a36Sopenharmony_ci
28662306a36Sopenharmony_ci	switch (data[0]) {
28762306a36Sopenharmony_ci	case K90_MACRO_MODE_HW:
28862306a36Sopenharmony_ci		macro_mode = "HW";
28962306a36Sopenharmony_ci		break;
29062306a36Sopenharmony_ci
29162306a36Sopenharmony_ci	case K90_MACRO_MODE_SW:
29262306a36Sopenharmony_ci		macro_mode = "SW";
29362306a36Sopenharmony_ci		break;
29462306a36Sopenharmony_ci	default:
29562306a36Sopenharmony_ci		dev_warn(dev, "K90 in unknown mode: %02hhx.\n",
29662306a36Sopenharmony_ci			 data[0]);
29762306a36Sopenharmony_ci		ret = -EIO;
29862306a36Sopenharmony_ci		goto out;
29962306a36Sopenharmony_ci	}
30062306a36Sopenharmony_ci
30162306a36Sopenharmony_ci	ret = snprintf(buf, PAGE_SIZE, "%s\n", macro_mode);
30262306a36Sopenharmony_ciout:
30362306a36Sopenharmony_ci	kfree(data);
30462306a36Sopenharmony_ci
30562306a36Sopenharmony_ci	return ret;
30662306a36Sopenharmony_ci}
30762306a36Sopenharmony_ci
30862306a36Sopenharmony_cistatic ssize_t k90_store_macro_mode(struct device *dev,
30962306a36Sopenharmony_ci				    struct device_attribute *attr,
31062306a36Sopenharmony_ci				    const char *buf, size_t count)
31162306a36Sopenharmony_ci{
31262306a36Sopenharmony_ci	int ret;
31362306a36Sopenharmony_ci	struct usb_interface *usbif = to_usb_interface(dev->parent);
31462306a36Sopenharmony_ci	struct usb_device *usbdev = interface_to_usbdev(usbif);
31562306a36Sopenharmony_ci	__u16 value;
31662306a36Sopenharmony_ci
31762306a36Sopenharmony_ci	if (strncmp(buf, "SW", 2) == 0)
31862306a36Sopenharmony_ci		value = K90_MACRO_MODE_SW;
31962306a36Sopenharmony_ci	else if (strncmp(buf, "HW", 2) == 0)
32062306a36Sopenharmony_ci		value = K90_MACRO_MODE_HW;
32162306a36Sopenharmony_ci	else
32262306a36Sopenharmony_ci		return -EINVAL;
32362306a36Sopenharmony_ci
32462306a36Sopenharmony_ci	ret = usb_control_msg(usbdev, usb_sndctrlpipe(usbdev, 0),
32562306a36Sopenharmony_ci			      K90_REQUEST_MACRO_MODE,
32662306a36Sopenharmony_ci			      USB_DIR_OUT | USB_TYPE_VENDOR |
32762306a36Sopenharmony_ci			      USB_RECIP_DEVICE, value, 0, NULL, 0,
32862306a36Sopenharmony_ci			      USB_CTRL_SET_TIMEOUT);
32962306a36Sopenharmony_ci	if (ret != 0) {
33062306a36Sopenharmony_ci		dev_warn(dev, "Failed to set macro mode.\n");
33162306a36Sopenharmony_ci		return ret;
33262306a36Sopenharmony_ci	}
33362306a36Sopenharmony_ci
33462306a36Sopenharmony_ci	return count;
33562306a36Sopenharmony_ci}
33662306a36Sopenharmony_ci
33762306a36Sopenharmony_cistatic ssize_t k90_show_current_profile(struct device *dev,
33862306a36Sopenharmony_ci					struct device_attribute *attr,
33962306a36Sopenharmony_ci					char *buf)
34062306a36Sopenharmony_ci{
34162306a36Sopenharmony_ci	int ret;
34262306a36Sopenharmony_ci	struct usb_interface *usbif = to_usb_interface(dev->parent);
34362306a36Sopenharmony_ci	struct usb_device *usbdev = interface_to_usbdev(usbif);
34462306a36Sopenharmony_ci	int current_profile;
34562306a36Sopenharmony_ci	char *data;
34662306a36Sopenharmony_ci
34762306a36Sopenharmony_ci	data = kmalloc(8, GFP_KERNEL);
34862306a36Sopenharmony_ci	if (!data)
34962306a36Sopenharmony_ci		return -ENOMEM;
35062306a36Sopenharmony_ci
35162306a36Sopenharmony_ci	ret = usb_control_msg(usbdev, usb_rcvctrlpipe(usbdev, 0),
35262306a36Sopenharmony_ci			      K90_REQUEST_STATUS,
35362306a36Sopenharmony_ci			      USB_DIR_IN | USB_TYPE_VENDOR |
35462306a36Sopenharmony_ci			      USB_RECIP_DEVICE, 0, 0, data, 8,
35562306a36Sopenharmony_ci			      USB_CTRL_SET_TIMEOUT);
35662306a36Sopenharmony_ci	if (ret < 8) {
35762306a36Sopenharmony_ci		dev_warn(dev, "Failed to get K90 initial state (error %d).\n",
35862306a36Sopenharmony_ci			 ret);
35962306a36Sopenharmony_ci		ret = -EIO;
36062306a36Sopenharmony_ci		goto out;
36162306a36Sopenharmony_ci	}
36262306a36Sopenharmony_ci	current_profile = data[7];
36362306a36Sopenharmony_ci	if (current_profile < 1 || current_profile > 3) {
36462306a36Sopenharmony_ci		dev_warn(dev, "Read invalid current profile: %02hhx.\n",
36562306a36Sopenharmony_ci			 data[7]);
36662306a36Sopenharmony_ci		ret = -EIO;
36762306a36Sopenharmony_ci		goto out;
36862306a36Sopenharmony_ci	}
36962306a36Sopenharmony_ci
37062306a36Sopenharmony_ci	ret = snprintf(buf, PAGE_SIZE, "%d\n", current_profile);
37162306a36Sopenharmony_ciout:
37262306a36Sopenharmony_ci	kfree(data);
37362306a36Sopenharmony_ci
37462306a36Sopenharmony_ci	return ret;
37562306a36Sopenharmony_ci}
37662306a36Sopenharmony_ci
37762306a36Sopenharmony_cistatic ssize_t k90_store_current_profile(struct device *dev,
37862306a36Sopenharmony_ci					 struct device_attribute *attr,
37962306a36Sopenharmony_ci					 const char *buf, size_t count)
38062306a36Sopenharmony_ci{
38162306a36Sopenharmony_ci	int ret;
38262306a36Sopenharmony_ci	struct usb_interface *usbif = to_usb_interface(dev->parent);
38362306a36Sopenharmony_ci	struct usb_device *usbdev = interface_to_usbdev(usbif);
38462306a36Sopenharmony_ci	int profile;
38562306a36Sopenharmony_ci
38662306a36Sopenharmony_ci	if (kstrtoint(buf, 10, &profile))
38762306a36Sopenharmony_ci		return -EINVAL;
38862306a36Sopenharmony_ci	if (profile < 1 || profile > 3)
38962306a36Sopenharmony_ci		return -EINVAL;
39062306a36Sopenharmony_ci
39162306a36Sopenharmony_ci	ret = usb_control_msg(usbdev, usb_sndctrlpipe(usbdev, 0),
39262306a36Sopenharmony_ci			      K90_REQUEST_PROFILE,
39362306a36Sopenharmony_ci			      USB_DIR_OUT | USB_TYPE_VENDOR |
39462306a36Sopenharmony_ci			      USB_RECIP_DEVICE, profile, 0, NULL, 0,
39562306a36Sopenharmony_ci			      USB_CTRL_SET_TIMEOUT);
39662306a36Sopenharmony_ci	if (ret != 0) {
39762306a36Sopenharmony_ci		dev_warn(dev, "Failed to change current profile (error %d).\n",
39862306a36Sopenharmony_ci			 ret);
39962306a36Sopenharmony_ci		return ret;
40062306a36Sopenharmony_ci	}
40162306a36Sopenharmony_ci
40262306a36Sopenharmony_ci	return count;
40362306a36Sopenharmony_ci}
40462306a36Sopenharmony_ci
40562306a36Sopenharmony_cistatic DEVICE_ATTR(macro_mode, 0644, k90_show_macro_mode, k90_store_macro_mode);
40662306a36Sopenharmony_cistatic DEVICE_ATTR(current_profile, 0644, k90_show_current_profile,
40762306a36Sopenharmony_ci		   k90_store_current_profile);
40862306a36Sopenharmony_ci
40962306a36Sopenharmony_cistatic struct attribute *k90_attrs[] = {
41062306a36Sopenharmony_ci	&dev_attr_macro_mode.attr,
41162306a36Sopenharmony_ci	&dev_attr_current_profile.attr,
41262306a36Sopenharmony_ci	NULL
41362306a36Sopenharmony_ci};
41462306a36Sopenharmony_ci
41562306a36Sopenharmony_cistatic const struct attribute_group k90_attr_group = {
41662306a36Sopenharmony_ci	.attrs = k90_attrs,
41762306a36Sopenharmony_ci};
41862306a36Sopenharmony_ci
41962306a36Sopenharmony_ci/*
42062306a36Sopenharmony_ci * Driver functions
42162306a36Sopenharmony_ci */
42262306a36Sopenharmony_ci
42362306a36Sopenharmony_cistatic int k90_init_backlight(struct hid_device *dev)
42462306a36Sopenharmony_ci{
42562306a36Sopenharmony_ci	int ret;
42662306a36Sopenharmony_ci	struct corsair_drvdata *drvdata = hid_get_drvdata(dev);
42762306a36Sopenharmony_ci	size_t name_sz;
42862306a36Sopenharmony_ci	char *name;
42962306a36Sopenharmony_ci
43062306a36Sopenharmony_ci	drvdata->backlight = kzalloc(sizeof(struct k90_led), GFP_KERNEL);
43162306a36Sopenharmony_ci	if (!drvdata->backlight) {
43262306a36Sopenharmony_ci		ret = -ENOMEM;
43362306a36Sopenharmony_ci		goto fail_backlight_alloc;
43462306a36Sopenharmony_ci	}
43562306a36Sopenharmony_ci
43662306a36Sopenharmony_ci	name_sz =
43762306a36Sopenharmony_ci	    strlen(dev_name(&dev->dev)) + sizeof(K90_BACKLIGHT_LED_SUFFIX);
43862306a36Sopenharmony_ci	name = kzalloc(name_sz, GFP_KERNEL);
43962306a36Sopenharmony_ci	if (!name) {
44062306a36Sopenharmony_ci		ret = -ENOMEM;
44162306a36Sopenharmony_ci		goto fail_name_alloc;
44262306a36Sopenharmony_ci	}
44362306a36Sopenharmony_ci	snprintf(name, name_sz, "%s" K90_BACKLIGHT_LED_SUFFIX,
44462306a36Sopenharmony_ci		 dev_name(&dev->dev));
44562306a36Sopenharmony_ci	drvdata->backlight->removed = false;
44662306a36Sopenharmony_ci	drvdata->backlight->cdev.name = name;
44762306a36Sopenharmony_ci	drvdata->backlight->cdev.max_brightness = 3;
44862306a36Sopenharmony_ci	drvdata->backlight->cdev.brightness_set = k90_brightness_set;
44962306a36Sopenharmony_ci	drvdata->backlight->cdev.brightness_get = k90_backlight_get;
45062306a36Sopenharmony_ci	INIT_WORK(&drvdata->backlight->work, k90_backlight_work);
45162306a36Sopenharmony_ci	ret = led_classdev_register(&dev->dev, &drvdata->backlight->cdev);
45262306a36Sopenharmony_ci	if (ret != 0)
45362306a36Sopenharmony_ci		goto fail_register_cdev;
45462306a36Sopenharmony_ci
45562306a36Sopenharmony_ci	return 0;
45662306a36Sopenharmony_ci
45762306a36Sopenharmony_cifail_register_cdev:
45862306a36Sopenharmony_ci	kfree(drvdata->backlight->cdev.name);
45962306a36Sopenharmony_cifail_name_alloc:
46062306a36Sopenharmony_ci	kfree(drvdata->backlight);
46162306a36Sopenharmony_ci	drvdata->backlight = NULL;
46262306a36Sopenharmony_cifail_backlight_alloc:
46362306a36Sopenharmony_ci	return ret;
46462306a36Sopenharmony_ci}
46562306a36Sopenharmony_ci
46662306a36Sopenharmony_cistatic int k90_init_macro_functions(struct hid_device *dev)
46762306a36Sopenharmony_ci{
46862306a36Sopenharmony_ci	int ret;
46962306a36Sopenharmony_ci	struct corsair_drvdata *drvdata = hid_get_drvdata(dev);
47062306a36Sopenharmony_ci	struct k90_drvdata *k90;
47162306a36Sopenharmony_ci	size_t name_sz;
47262306a36Sopenharmony_ci	char *name;
47362306a36Sopenharmony_ci
47462306a36Sopenharmony_ci	k90 = kzalloc(sizeof(struct k90_drvdata), GFP_KERNEL);
47562306a36Sopenharmony_ci	if (!k90) {
47662306a36Sopenharmony_ci		ret = -ENOMEM;
47762306a36Sopenharmony_ci		goto fail_drvdata;
47862306a36Sopenharmony_ci	}
47962306a36Sopenharmony_ci	drvdata->k90 = k90;
48062306a36Sopenharmony_ci
48162306a36Sopenharmony_ci	/* Init LED device for record LED */
48262306a36Sopenharmony_ci	name_sz = strlen(dev_name(&dev->dev)) + sizeof(K90_RECORD_LED_SUFFIX);
48362306a36Sopenharmony_ci	name = kzalloc(name_sz, GFP_KERNEL);
48462306a36Sopenharmony_ci	if (!name) {
48562306a36Sopenharmony_ci		ret = -ENOMEM;
48662306a36Sopenharmony_ci		goto fail_record_led_alloc;
48762306a36Sopenharmony_ci	}
48862306a36Sopenharmony_ci	snprintf(name, name_sz, "%s" K90_RECORD_LED_SUFFIX,
48962306a36Sopenharmony_ci		 dev_name(&dev->dev));
49062306a36Sopenharmony_ci	k90->record_led.removed = false;
49162306a36Sopenharmony_ci	k90->record_led.cdev.name = name;
49262306a36Sopenharmony_ci	k90->record_led.cdev.max_brightness = 1;
49362306a36Sopenharmony_ci	k90->record_led.cdev.brightness_set = k90_brightness_set;
49462306a36Sopenharmony_ci	k90->record_led.cdev.brightness_get = k90_record_led_get;
49562306a36Sopenharmony_ci	INIT_WORK(&k90->record_led.work, k90_record_led_work);
49662306a36Sopenharmony_ci	k90->record_led.brightness = 0;
49762306a36Sopenharmony_ci	ret = led_classdev_register(&dev->dev, &k90->record_led.cdev);
49862306a36Sopenharmony_ci	if (ret != 0)
49962306a36Sopenharmony_ci		goto fail_record_led;
50062306a36Sopenharmony_ci
50162306a36Sopenharmony_ci	/* Init attributes */
50262306a36Sopenharmony_ci	ret = sysfs_create_group(&dev->dev.kobj, &k90_attr_group);
50362306a36Sopenharmony_ci	if (ret != 0)
50462306a36Sopenharmony_ci		goto fail_sysfs;
50562306a36Sopenharmony_ci
50662306a36Sopenharmony_ci	return 0;
50762306a36Sopenharmony_ci
50862306a36Sopenharmony_cifail_sysfs:
50962306a36Sopenharmony_ci	k90->record_led.removed = true;
51062306a36Sopenharmony_ci	led_classdev_unregister(&k90->record_led.cdev);
51162306a36Sopenharmony_ci	cancel_work_sync(&k90->record_led.work);
51262306a36Sopenharmony_cifail_record_led:
51362306a36Sopenharmony_ci	kfree(k90->record_led.cdev.name);
51462306a36Sopenharmony_cifail_record_led_alloc:
51562306a36Sopenharmony_ci	kfree(k90);
51662306a36Sopenharmony_cifail_drvdata:
51762306a36Sopenharmony_ci	drvdata->k90 = NULL;
51862306a36Sopenharmony_ci	return ret;
51962306a36Sopenharmony_ci}
52062306a36Sopenharmony_ci
52162306a36Sopenharmony_cistatic void k90_cleanup_backlight(struct hid_device *dev)
52262306a36Sopenharmony_ci{
52362306a36Sopenharmony_ci	struct corsair_drvdata *drvdata = hid_get_drvdata(dev);
52462306a36Sopenharmony_ci
52562306a36Sopenharmony_ci	if (drvdata->backlight) {
52662306a36Sopenharmony_ci		drvdata->backlight->removed = true;
52762306a36Sopenharmony_ci		led_classdev_unregister(&drvdata->backlight->cdev);
52862306a36Sopenharmony_ci		cancel_work_sync(&drvdata->backlight->work);
52962306a36Sopenharmony_ci		kfree(drvdata->backlight->cdev.name);
53062306a36Sopenharmony_ci		kfree(drvdata->backlight);
53162306a36Sopenharmony_ci	}
53262306a36Sopenharmony_ci}
53362306a36Sopenharmony_ci
53462306a36Sopenharmony_cistatic void k90_cleanup_macro_functions(struct hid_device *dev)
53562306a36Sopenharmony_ci{
53662306a36Sopenharmony_ci	struct corsair_drvdata *drvdata = hid_get_drvdata(dev);
53762306a36Sopenharmony_ci	struct k90_drvdata *k90 = drvdata->k90;
53862306a36Sopenharmony_ci
53962306a36Sopenharmony_ci	if (k90) {
54062306a36Sopenharmony_ci		sysfs_remove_group(&dev->dev.kobj, &k90_attr_group);
54162306a36Sopenharmony_ci
54262306a36Sopenharmony_ci		k90->record_led.removed = true;
54362306a36Sopenharmony_ci		led_classdev_unregister(&k90->record_led.cdev);
54462306a36Sopenharmony_ci		cancel_work_sync(&k90->record_led.work);
54562306a36Sopenharmony_ci		kfree(k90->record_led.cdev.name);
54662306a36Sopenharmony_ci
54762306a36Sopenharmony_ci		kfree(k90);
54862306a36Sopenharmony_ci	}
54962306a36Sopenharmony_ci}
55062306a36Sopenharmony_ci
55162306a36Sopenharmony_cistatic int corsair_probe(struct hid_device *dev, const struct hid_device_id *id)
55262306a36Sopenharmony_ci{
55362306a36Sopenharmony_ci	int ret;
55462306a36Sopenharmony_ci	unsigned long quirks = id->driver_data;
55562306a36Sopenharmony_ci	struct corsair_drvdata *drvdata;
55662306a36Sopenharmony_ci	struct usb_interface *usbif;
55762306a36Sopenharmony_ci
55862306a36Sopenharmony_ci	if (!hid_is_usb(dev))
55962306a36Sopenharmony_ci		return -EINVAL;
56062306a36Sopenharmony_ci
56162306a36Sopenharmony_ci	usbif = to_usb_interface(dev->dev.parent);
56262306a36Sopenharmony_ci
56362306a36Sopenharmony_ci	drvdata = devm_kzalloc(&dev->dev, sizeof(struct corsair_drvdata),
56462306a36Sopenharmony_ci			       GFP_KERNEL);
56562306a36Sopenharmony_ci	if (drvdata == NULL)
56662306a36Sopenharmony_ci		return -ENOMEM;
56762306a36Sopenharmony_ci	drvdata->quirks = quirks;
56862306a36Sopenharmony_ci	hid_set_drvdata(dev, drvdata);
56962306a36Sopenharmony_ci
57062306a36Sopenharmony_ci	ret = hid_parse(dev);
57162306a36Sopenharmony_ci	if (ret != 0) {
57262306a36Sopenharmony_ci		hid_err(dev, "parse failed\n");
57362306a36Sopenharmony_ci		return ret;
57462306a36Sopenharmony_ci	}
57562306a36Sopenharmony_ci	ret = hid_hw_start(dev, HID_CONNECT_DEFAULT);
57662306a36Sopenharmony_ci	if (ret != 0) {
57762306a36Sopenharmony_ci		hid_err(dev, "hw start failed\n");
57862306a36Sopenharmony_ci		return ret;
57962306a36Sopenharmony_ci	}
58062306a36Sopenharmony_ci
58162306a36Sopenharmony_ci	if (usbif->cur_altsetting->desc.bInterfaceNumber == 0) {
58262306a36Sopenharmony_ci		if (quirks & CORSAIR_USE_K90_MACRO) {
58362306a36Sopenharmony_ci			ret = k90_init_macro_functions(dev);
58462306a36Sopenharmony_ci			if (ret != 0)
58562306a36Sopenharmony_ci				hid_warn(dev, "Failed to initialize K90 macro functions.\n");
58662306a36Sopenharmony_ci		}
58762306a36Sopenharmony_ci		if (quirks & CORSAIR_USE_K90_BACKLIGHT) {
58862306a36Sopenharmony_ci			ret = k90_init_backlight(dev);
58962306a36Sopenharmony_ci			if (ret != 0)
59062306a36Sopenharmony_ci				hid_warn(dev, "Failed to initialize K90 backlight.\n");
59162306a36Sopenharmony_ci		}
59262306a36Sopenharmony_ci	}
59362306a36Sopenharmony_ci
59462306a36Sopenharmony_ci	return 0;
59562306a36Sopenharmony_ci}
59662306a36Sopenharmony_ci
59762306a36Sopenharmony_cistatic void corsair_remove(struct hid_device *dev)
59862306a36Sopenharmony_ci{
59962306a36Sopenharmony_ci	k90_cleanup_macro_functions(dev);
60062306a36Sopenharmony_ci	k90_cleanup_backlight(dev);
60162306a36Sopenharmony_ci
60262306a36Sopenharmony_ci	hid_hw_stop(dev);
60362306a36Sopenharmony_ci}
60462306a36Sopenharmony_ci
60562306a36Sopenharmony_cistatic int corsair_event(struct hid_device *dev, struct hid_field *field,
60662306a36Sopenharmony_ci			 struct hid_usage *usage, __s32 value)
60762306a36Sopenharmony_ci{
60862306a36Sopenharmony_ci	struct corsair_drvdata *drvdata = hid_get_drvdata(dev);
60962306a36Sopenharmony_ci
61062306a36Sopenharmony_ci	if (!drvdata->k90)
61162306a36Sopenharmony_ci		return 0;
61262306a36Sopenharmony_ci
61362306a36Sopenharmony_ci	switch (usage->hid & HID_USAGE) {
61462306a36Sopenharmony_ci	case CORSAIR_USAGE_MACRO_RECORD_START:
61562306a36Sopenharmony_ci		drvdata->k90->record_led.brightness = 1;
61662306a36Sopenharmony_ci		break;
61762306a36Sopenharmony_ci	case CORSAIR_USAGE_MACRO_RECORD_STOP:
61862306a36Sopenharmony_ci		drvdata->k90->record_led.brightness = 0;
61962306a36Sopenharmony_ci		break;
62062306a36Sopenharmony_ci	default:
62162306a36Sopenharmony_ci		break;
62262306a36Sopenharmony_ci	}
62362306a36Sopenharmony_ci
62462306a36Sopenharmony_ci	return 0;
62562306a36Sopenharmony_ci}
62662306a36Sopenharmony_ci
62762306a36Sopenharmony_cistatic int corsair_input_mapping(struct hid_device *dev,
62862306a36Sopenharmony_ci				 struct hid_input *input,
62962306a36Sopenharmony_ci				 struct hid_field *field,
63062306a36Sopenharmony_ci				 struct hid_usage *usage, unsigned long **bit,
63162306a36Sopenharmony_ci				 int *max)
63262306a36Sopenharmony_ci{
63362306a36Sopenharmony_ci	int gkey;
63462306a36Sopenharmony_ci
63562306a36Sopenharmony_ci	if ((usage->hid & HID_USAGE_PAGE) != HID_UP_KEYBOARD)
63662306a36Sopenharmony_ci		return 0;
63762306a36Sopenharmony_ci
63862306a36Sopenharmony_ci	gkey = corsair_usage_to_gkey(usage->hid & HID_USAGE);
63962306a36Sopenharmony_ci	if (gkey != 0) {
64062306a36Sopenharmony_ci		hid_map_usage_clear(input, usage, bit, max, EV_KEY,
64162306a36Sopenharmony_ci				    corsair_gkey_map[gkey - 1]);
64262306a36Sopenharmony_ci		return 1;
64362306a36Sopenharmony_ci	}
64462306a36Sopenharmony_ci	if ((usage->hid & HID_USAGE) >= CORSAIR_USAGE_SPECIAL_MIN &&
64562306a36Sopenharmony_ci	    (usage->hid & HID_USAGE) <= CORSAIR_USAGE_SPECIAL_MAX) {
64662306a36Sopenharmony_ci		switch (usage->hid & HID_USAGE) {
64762306a36Sopenharmony_ci		case CORSAIR_USAGE_MACRO_RECORD_START:
64862306a36Sopenharmony_ci			hid_map_usage_clear(input, usage, bit, max, EV_KEY,
64962306a36Sopenharmony_ci					    corsair_record_keycodes[0]);
65062306a36Sopenharmony_ci			return 1;
65162306a36Sopenharmony_ci
65262306a36Sopenharmony_ci		case CORSAIR_USAGE_MACRO_RECORD_STOP:
65362306a36Sopenharmony_ci			hid_map_usage_clear(input, usage, bit, max, EV_KEY,
65462306a36Sopenharmony_ci					    corsair_record_keycodes[1]);
65562306a36Sopenharmony_ci			return 1;
65662306a36Sopenharmony_ci
65762306a36Sopenharmony_ci		case CORSAIR_USAGE_M1:
65862306a36Sopenharmony_ci			hid_map_usage_clear(input, usage, bit, max, EV_KEY,
65962306a36Sopenharmony_ci					    corsair_profile_keycodes[0]);
66062306a36Sopenharmony_ci			return 1;
66162306a36Sopenharmony_ci
66262306a36Sopenharmony_ci		case CORSAIR_USAGE_M2:
66362306a36Sopenharmony_ci			hid_map_usage_clear(input, usage, bit, max, EV_KEY,
66462306a36Sopenharmony_ci					    corsair_profile_keycodes[1]);
66562306a36Sopenharmony_ci			return 1;
66662306a36Sopenharmony_ci
66762306a36Sopenharmony_ci		case CORSAIR_USAGE_M3:
66862306a36Sopenharmony_ci			hid_map_usage_clear(input, usage, bit, max, EV_KEY,
66962306a36Sopenharmony_ci					    corsair_profile_keycodes[2]);
67062306a36Sopenharmony_ci			return 1;
67162306a36Sopenharmony_ci
67262306a36Sopenharmony_ci		default:
67362306a36Sopenharmony_ci			return -1;
67462306a36Sopenharmony_ci		}
67562306a36Sopenharmony_ci	}
67662306a36Sopenharmony_ci
67762306a36Sopenharmony_ci	return 0;
67862306a36Sopenharmony_ci}
67962306a36Sopenharmony_ci
68062306a36Sopenharmony_ci/*
68162306a36Sopenharmony_ci * The report descriptor of some of the Corsair gaming mice is
68262306a36Sopenharmony_ci * non parseable as they define two consecutive Logical Minimum for
68362306a36Sopenharmony_ci * the Usage Page (Consumer) in rdescs bytes 75 and 77 being 77 0x16
68462306a36Sopenharmony_ci * that should be obviousy 0x26 for Logical Magimum of 16 bits. This
68562306a36Sopenharmony_ci * prevents poper parsing of the report descriptor due Logical
68662306a36Sopenharmony_ci * Minimum being larger than Logical Maximum.
68762306a36Sopenharmony_ci *
68862306a36Sopenharmony_ci * This driver fixes the report descriptor for:
68962306a36Sopenharmony_ci * - USB ID 1b1c:1b34, sold as GLAIVE RGB Gaming mouse
69062306a36Sopenharmony_ci * - USB ID 1b1c:1b3e, sold as Scimitar RGB Pro Gaming mouse
69162306a36Sopenharmony_ci */
69262306a36Sopenharmony_ci
69362306a36Sopenharmony_cistatic __u8 *corsair_mouse_report_fixup(struct hid_device *hdev, __u8 *rdesc,
69462306a36Sopenharmony_ci        unsigned int *rsize)
69562306a36Sopenharmony_ci{
69662306a36Sopenharmony_ci	struct usb_interface *intf = to_usb_interface(hdev->dev.parent);
69762306a36Sopenharmony_ci
69862306a36Sopenharmony_ci	if (intf->cur_altsetting->desc.bInterfaceNumber == 1) {
69962306a36Sopenharmony_ci		/*
70062306a36Sopenharmony_ci		 * Corsair GLAIVE RGB and Scimitar RGB Pro report descriptor is
70162306a36Sopenharmony_ci		 * broken and defines two different Logical Minimum for the
70262306a36Sopenharmony_ci		 * Consumer Application. The byte 77 should be a 0x26 defining
70362306a36Sopenharmony_ci		 * a 16 bits integer for the Logical Maximum but it is a 0x16
70462306a36Sopenharmony_ci		 * instead (Logical Minimum)
70562306a36Sopenharmony_ci		 */
70662306a36Sopenharmony_ci		switch (hdev->product) {
70762306a36Sopenharmony_ci		case USB_DEVICE_ID_CORSAIR_GLAIVE_RGB:
70862306a36Sopenharmony_ci		case USB_DEVICE_ID_CORSAIR_SCIMITAR_PRO_RGB:
70962306a36Sopenharmony_ci			if (*rsize >= 172 && rdesc[75] == 0x15 && rdesc[77] == 0x16
71062306a36Sopenharmony_ci			&& rdesc[78] == 0xff && rdesc[79] == 0x0f) {
71162306a36Sopenharmony_ci				hid_info(hdev, "Fixing up report descriptor\n");
71262306a36Sopenharmony_ci				rdesc[77] = 0x26;
71362306a36Sopenharmony_ci			}
71462306a36Sopenharmony_ci			break;
71562306a36Sopenharmony_ci		}
71662306a36Sopenharmony_ci
71762306a36Sopenharmony_ci	}
71862306a36Sopenharmony_ci	return rdesc;
71962306a36Sopenharmony_ci}
72062306a36Sopenharmony_ci
72162306a36Sopenharmony_cistatic const struct hid_device_id corsair_devices[] = {
72262306a36Sopenharmony_ci	{ HID_USB_DEVICE(USB_VENDOR_ID_CORSAIR, USB_DEVICE_ID_CORSAIR_K90),
72362306a36Sopenharmony_ci		.driver_data = CORSAIR_USE_K90_MACRO |
72462306a36Sopenharmony_ci			       CORSAIR_USE_K90_BACKLIGHT },
72562306a36Sopenharmony_ci	{ HID_USB_DEVICE(USB_VENDOR_ID_CORSAIR,
72662306a36Sopenharmony_ci            USB_DEVICE_ID_CORSAIR_GLAIVE_RGB) },
72762306a36Sopenharmony_ci	{ HID_USB_DEVICE(USB_VENDOR_ID_CORSAIR,
72862306a36Sopenharmony_ci            USB_DEVICE_ID_CORSAIR_SCIMITAR_PRO_RGB) },
72962306a36Sopenharmony_ci	/*
73062306a36Sopenharmony_ci	 * Vengeance K70 and K70 RAPIDFIRE share product IDs.
73162306a36Sopenharmony_ci	 */
73262306a36Sopenharmony_ci	{ HID_USB_DEVICE(USB_VENDOR_ID_CORSAIR,
73362306a36Sopenharmony_ci            USB_DEVICE_ID_CORSAIR_K70R) },
73462306a36Sopenharmony_ci	{}
73562306a36Sopenharmony_ci};
73662306a36Sopenharmony_ci
73762306a36Sopenharmony_ciMODULE_DEVICE_TABLE(hid, corsair_devices);
73862306a36Sopenharmony_ci
73962306a36Sopenharmony_cistatic struct hid_driver corsair_driver = {
74062306a36Sopenharmony_ci	.name = "corsair",
74162306a36Sopenharmony_ci	.id_table = corsair_devices,
74262306a36Sopenharmony_ci	.probe = corsair_probe,
74362306a36Sopenharmony_ci	.event = corsair_event,
74462306a36Sopenharmony_ci	.remove = corsair_remove,
74562306a36Sopenharmony_ci	.input_mapping = corsair_input_mapping,
74662306a36Sopenharmony_ci	.report_fixup = corsair_mouse_report_fixup,
74762306a36Sopenharmony_ci};
74862306a36Sopenharmony_ci
74962306a36Sopenharmony_cimodule_hid_driver(corsair_driver);
75062306a36Sopenharmony_ci
75162306a36Sopenharmony_ciMODULE_LICENSE("GPL");
75262306a36Sopenharmony_ci/* Original K90 driver author */
75362306a36Sopenharmony_ciMODULE_AUTHOR("Clement Vuchener");
75462306a36Sopenharmony_ci/* Scimitar PRO RGB driver author */
75562306a36Sopenharmony_ciMODULE_AUTHOR("Oscar Campos");
75662306a36Sopenharmony_ciMODULE_DESCRIPTION("HID driver for Corsair devices");
757