xref: /linux/drivers/hid/hid-steelseries.c (revision f4cdf7ca9a1fdcca413157df19753f388a5a224e)
1 // SPDX-License-Identifier: GPL-2.0-or-later
2 /*
3  *  HID driver for Steelseries devices
4  *
5  *  Copyright (c) 2013 Simon Wood
6  */
7 
8 /*
9  */
10 
11 #include <linux/device.h>
12 #include <linux/dmi.h>
13 #include <linux/hid.h>
14 #include <linux/module.h>
15 #include <linux/usb.h>
16 #include <linux/leds.h>
17 #include <linux/led-class-multicolor.h>
18 #include <linux/slab.h>
19 
20 #include "hid-ids.h"
21 
22 #define STEELSERIES_SRWS1		BIT(0)
23 #define STEELSERIES_MSI_RGB		BIT(1)
24 
25 #define STEELSERIES_MSI_RGB_WVALUE 0x0300 /* Feature report, ID 0 */
26 #define STEELSERIES_MSI_RGB_REPORT_LEN 524
27 #define STEELSERIES_MSI_RGB_OPCODE 0x0c
28 #define STEELSERIES_MSI_RGB_KLC_MODE 0x66
29 #define STEELSERIES_MSI_RGB_ALC_MODE 0x06
30 
31 #define STEELSERIES_HAS_LEDS_MULTICOLOR \
32 	(IS_BUILTIN(CONFIG_LEDS_CLASS_MULTICOLOR) || \
33 	 (IS_MODULE(CONFIG_LEDS_CLASS_MULTICOLOR) && IS_MODULE(CONFIG_HID_STEELSERIES)))
34 
35 struct steelseries_device {
36 	struct hid_device *hdev;
37 	unsigned long quirks;
38 
39 #if STEELSERIES_HAS_LEDS_MULTICOLOR
40 	struct led_classdev_mc mc_cdev;
41 	struct mc_subled subled_info[3];
42 	struct mutex rgb_lock; /* protects rgb_buf */
43 	u8 *rgb_buf;
44 #endif
45 };
46 
47 #if IS_BUILTIN(CONFIG_LEDS_CLASS) || \
48     (IS_MODULE(CONFIG_LEDS_CLASS) && IS_MODULE(CONFIG_HID_STEELSERIES))
49 #define SRWS1_NUMBER_LEDS 15
50 struct steelseries_srws1_data {
51 	__u16 led_state;
52 	/* the last element is used for setting all leds simultaneously */
53 	struct led_classdev *led[SRWS1_NUMBER_LEDS + 1];
54 };
55 #endif
56 
57 /* Fixed report descriptor for Steelseries SRW-S1 wheel controller
58  *
59  * The original descriptor hides the sensitivity and assists dials
60  * a custom vendor usage page. This inserts a patch to make them
61  * appear in the 'Generic Desktop' usage.
62  */
63 
64 static const __u8 steelseries_srws1_rdesc_fixed[] = {
65 0x05, 0x01,         /*  Usage Page (Desktop)                */
66 0x09, 0x08,         /*  Usage (MultiAxis), Changed          */
67 0xA1, 0x01,         /*  Collection (Application),           */
68 0xA1, 0x02,         /*      Collection (Logical),           */
69 0x95, 0x01,         /*          Report Count (1),           */
70 0x05, 0x01,         /* Changed  Usage Page (Desktop),       */
71 0x09, 0x30,         /* Changed  Usage (X),                  */
72 0x16, 0xF8, 0xF8,   /*          Logical Minimum (-1800),    */
73 0x26, 0x08, 0x07,   /*          Logical Maximum (1800),     */
74 0x65, 0x14,         /*          Unit (Degrees),             */
75 0x55, 0x0F,         /*          Unit Exponent (15),         */
76 0x75, 0x10,         /*          Report Size (16),           */
77 0x81, 0x02,         /*          Input (Variable),           */
78 0x09, 0x31,         /* Changed  Usage (Y),                  */
79 0x15, 0x00,         /*          Logical Minimum (0),        */
80 0x26, 0xFF, 0x03,   /*          Logical Maximum (1023),     */
81 0x75, 0x0C,         /*          Report Size (12),           */
82 0x81, 0x02,         /*          Input (Variable),           */
83 0x09, 0x32,         /* Changed  Usage (Z),                  */
84 0x15, 0x00,         /*          Logical Minimum (0),        */
85 0x26, 0xFF, 0x03,   /*          Logical Maximum (1023),     */
86 0x75, 0x0C,         /*          Report Size (12),           */
87 0x81, 0x02,         /*          Input (Variable),           */
88 0x05, 0x01,         /*          Usage Page (Desktop),       */
89 0x09, 0x39,         /*          Usage (Hat Switch),         */
90 0x25, 0x07,         /*          Logical Maximum (7),        */
91 0x35, 0x00,         /*          Physical Minimum (0),       */
92 0x46, 0x3B, 0x01,   /*          Physical Maximum (315),     */
93 0x65, 0x14,         /*          Unit (Degrees),             */
94 0x75, 0x04,         /*          Report Size (4),            */
95 0x95, 0x01,         /*          Report Count (1),           */
96 0x81, 0x02,         /*          Input (Variable),           */
97 0x25, 0x01,         /*          Logical Maximum (1),        */
98 0x45, 0x01,         /*          Physical Maximum (1),       */
99 0x65, 0x00,         /*          Unit,                       */
100 0x75, 0x01,         /*          Report Size (1),            */
101 0x95, 0x03,         /*          Report Count (3),           */
102 0x81, 0x01,         /*          Input (Constant),           */
103 0x05, 0x09,         /*          Usage Page (Button),        */
104 0x19, 0x01,         /*          Usage Minimum (01h),        */
105 0x29, 0x11,         /*          Usage Maximum (11h),        */
106 0x95, 0x11,         /*          Report Count (17),          */
107 0x81, 0x02,         /*          Input (Variable),           */
108                     /*   ---- Dial patch starts here ----   */
109 0x05, 0x01,         /*          Usage Page (Desktop),       */
110 0x09, 0x33,         /*          Usage (RX),                 */
111 0x75, 0x04,         /*          Report Size (4),            */
112 0x95, 0x02,         /*          Report Count (2),           */
113 0x15, 0x00,         /*          Logical Minimum (0),        */
114 0x25, 0x0b,         /*          Logical Maximum (b),        */
115 0x81, 0x02,         /*          Input (Variable),           */
116 0x09, 0x35,         /*          Usage (RZ),                 */
117 0x75, 0x04,         /*          Report Size (4),            */
118 0x95, 0x01,         /*          Report Count (1),           */
119 0x25, 0x03,         /*          Logical Maximum (3),        */
120 0x81, 0x02,         /*          Input (Variable),           */
121                     /*    ---- Dial patch ends here ----    */
122 0x06, 0x00, 0xFF,   /*          Usage Page (FF00h),         */
123 0x09, 0x01,         /*          Usage (01h),                */
124 0x75, 0x04,         /* Changed  Report Size (4),            */
125 0x95, 0x0D,         /* Changed  Report Count (13),          */
126 0x81, 0x02,         /*          Input (Variable),           */
127 0xC0,               /*      End Collection,                 */
128 0xA1, 0x02,         /*      Collection (Logical),           */
129 0x09, 0x02,         /*          Usage (02h),                */
130 0x75, 0x08,         /*          Report Size (8),            */
131 0x95, 0x10,         /*          Report Count (16),          */
132 0x91, 0x02,         /*          Output (Variable),          */
133 0xC0,               /*      End Collection,                 */
134 0xC0                /*  End Collection                      */
135 };
136 
137 #if IS_BUILTIN(CONFIG_LEDS_CLASS) || \
138     (IS_MODULE(CONFIG_LEDS_CLASS) && IS_MODULE(CONFIG_HID_STEELSERIES))
139 static void steelseries_srws1_set_leds(struct hid_device *hdev, __u16 leds)
140 {
141 	struct list_head *report_list = &hdev->report_enum[HID_OUTPUT_REPORT].report_list;
142 	struct hid_report *report = list_entry(report_list->next, struct hid_report, list);
143 	__s32 *value = report->field[0]->value;
144 
145 	value[0] = 0x40;
146 	value[1] = leds & 0xFF;
147 	value[2] = leds >> 8;
148 	value[3] = 0x00;
149 	value[4] = 0x00;
150 	value[5] = 0x00;
151 	value[6] = 0x00;
152 	value[7] = 0x00;
153 	value[8] = 0x00;
154 	value[9] = 0x00;
155 	value[10] = 0x00;
156 	value[11] = 0x00;
157 	value[12] = 0x00;
158 	value[13] = 0x00;
159 	value[14] = 0x00;
160 	value[15] = 0x00;
161 
162 	hid_hw_request(hdev, report, HID_REQ_SET_REPORT);
163 
164 	/* Note: LED change does not show on device until the device is read/polled */
165 }
166 
167 static void steelseries_srws1_led_all_set_brightness(struct led_classdev *led_cdev,
168 			enum led_brightness value)
169 {
170 	struct device *dev = led_cdev->dev->parent;
171 	struct hid_device *hid = to_hid_device(dev);
172 	struct steelseries_srws1_data *drv_data = hid_get_drvdata(hid);
173 
174 	if (!drv_data) {
175 		hid_err(hid, "Device data not found.");
176 		return;
177 	}
178 
179 	if (value == LED_OFF)
180 		drv_data->led_state = 0;
181 	else
182 		drv_data->led_state = (1 << (SRWS1_NUMBER_LEDS + 1)) - 1;
183 
184 	steelseries_srws1_set_leds(hid, drv_data->led_state);
185 }
186 
187 static enum led_brightness steelseries_srws1_led_all_get_brightness(struct led_classdev *led_cdev)
188 {
189 	struct device *dev = led_cdev->dev->parent;
190 	struct hid_device *hid = to_hid_device(dev);
191 	struct steelseries_srws1_data *drv_data;
192 
193 	drv_data = hid_get_drvdata(hid);
194 
195 	if (!drv_data) {
196 		hid_err(hid, "Device data not found.");
197 		return LED_OFF;
198 	}
199 
200 	return (drv_data->led_state >> SRWS1_NUMBER_LEDS) ? LED_FULL : LED_OFF;
201 }
202 
203 static void steelseries_srws1_led_set_brightness(struct led_classdev *led_cdev,
204 			enum led_brightness value)
205 {
206 	struct device *dev = led_cdev->dev->parent;
207 	struct hid_device *hid = to_hid_device(dev);
208 	struct steelseries_srws1_data *drv_data = hid_get_drvdata(hid);
209 	int i, state = 0;
210 
211 	if (!drv_data) {
212 		hid_err(hid, "Device data not found.");
213 		return;
214 	}
215 
216 	for (i = 0; i < SRWS1_NUMBER_LEDS; i++) {
217 		if (led_cdev != drv_data->led[i])
218 			continue;
219 
220 		state = (drv_data->led_state >> i) & 1;
221 		if (value == LED_OFF && state) {
222 			drv_data->led_state &= ~(1 << i);
223 			steelseries_srws1_set_leds(hid, drv_data->led_state);
224 		} else if (value != LED_OFF && !state) {
225 			drv_data->led_state |= 1 << i;
226 			steelseries_srws1_set_leds(hid, drv_data->led_state);
227 		}
228 		break;
229 	}
230 }
231 
232 static enum led_brightness steelseries_srws1_led_get_brightness(struct led_classdev *led_cdev)
233 {
234 	struct device *dev = led_cdev->dev->parent;
235 	struct hid_device *hid = to_hid_device(dev);
236 	struct steelseries_srws1_data *drv_data;
237 	int i, value = 0;
238 
239 	drv_data = hid_get_drvdata(hid);
240 
241 	if (!drv_data) {
242 		hid_err(hid, "Device data not found.");
243 		return LED_OFF;
244 	}
245 
246 	for (i = 0; i < SRWS1_NUMBER_LEDS; i++)
247 		if (led_cdev == drv_data->led[i]) {
248 			value = (drv_data->led_state >> i) & 1;
249 			break;
250 		}
251 
252 	return value ? LED_FULL : LED_OFF;
253 }
254 
255 static int steelseries_srws1_probe(struct hid_device *hdev,
256 		const struct hid_device_id *id)
257 {
258 	int ret, i;
259 	struct led_classdev *led;
260 	struct steelseries_srws1_data *drv_data;
261 	size_t name_sz;
262 	char *name;
263 
264 	drv_data = devm_kzalloc(&hdev->dev, sizeof(*drv_data), GFP_KERNEL);
265 	if (drv_data == NULL) {
266 		hid_err(hdev, "can't alloc SRW-S1 memory\n");
267 		return -ENOMEM;
268 	}
269 
270 	hid_set_drvdata(hdev, drv_data);
271 
272 	ret = hid_parse(hdev);
273 	if (ret) {
274 		hid_err(hdev, "parse failed\n");
275 		goto err;
276 	}
277 
278 	if (!hid_validate_values(hdev, HID_OUTPUT_REPORT, 0, 0, 16)) {
279 		ret = -ENODEV;
280 		goto err;
281 	}
282 
283 	ret = hid_hw_start(hdev, HID_CONNECT_DEFAULT);
284 	if (ret) {
285 		hid_err(hdev, "hw start failed\n");
286 		goto err;
287 	}
288 
289 	/* register led subsystem */
290 	drv_data->led_state = 0;
291 	for (i = 0; i < SRWS1_NUMBER_LEDS + 1; i++)
292 		drv_data->led[i] = NULL;
293 
294 	steelseries_srws1_set_leds(hdev, 0);
295 
296 	name_sz = strlen(hdev->uniq) + 16;
297 
298 	/* 'ALL', for setting all LEDs simultaneously */
299 	led = devm_kzalloc(&hdev->dev, sizeof(struct led_classdev)+name_sz, GFP_KERNEL);
300 	if (!led) {
301 		hid_err(hdev, "can't allocate memory for LED ALL\n");
302 		goto out;
303 	}
304 
305 	name = (void *)(&led[1]);
306 	snprintf(name, name_sz, "SRWS1::%s::RPMALL", hdev->uniq);
307 	led->name = name;
308 	led->brightness = 0;
309 	led->max_brightness = 1;
310 	led->brightness_get = steelseries_srws1_led_all_get_brightness;
311 	led->brightness_set = steelseries_srws1_led_all_set_brightness;
312 
313 	drv_data->led[SRWS1_NUMBER_LEDS] = led;
314 	ret = devm_led_classdev_register(&hdev->dev, led);
315 	if (ret) {
316 		hid_err(hdev, "failed to register LED %d. Aborting.\n", SRWS1_NUMBER_LEDS);
317 		goto out; /* let the driver continue without LEDs */
318 	}
319 
320 	/* Each individual LED */
321 	for (i = 0; i < SRWS1_NUMBER_LEDS; i++) {
322 		led = devm_kzalloc(&hdev->dev, sizeof(struct led_classdev)+name_sz, GFP_KERNEL);
323 		if (!led) {
324 			hid_err(hdev, "can't allocate memory for LED %d\n", i);
325 			break;
326 		}
327 
328 		name = (void *)(&led[1]);
329 		snprintf(name, name_sz, "SRWS1::%s::RPM%d", hdev->uniq, i+1);
330 		led->name = name;
331 		led->brightness = 0;
332 		led->max_brightness = 1;
333 		led->brightness_get = steelseries_srws1_led_get_brightness;
334 		led->brightness_set = steelseries_srws1_led_set_brightness;
335 
336 		drv_data->led[i] = led;
337 		ret = devm_led_classdev_register(&hdev->dev, led);
338 
339 		if (ret) {
340 			hid_err(hdev, "failed to register LED %d. Aborting.\n", i);
341 			break;	/* but let the driver continue without LEDs */
342 		}
343 	}
344 out:
345 	return 0;
346 err:
347 	return ret;
348 }
349 #endif
350 
351 static const struct dmi_system_id steelseries_msi_rgb_dmi_table[] = {
352 	{
353 		.matches = {
354 			DMI_MATCH(DMI_SYS_VENDOR, "Micro-Star International Co., Ltd."),
355 			DMI_MATCH(DMI_PRODUCT_NAME, "Raider A18 HX A9WJG"),
356 			DMI_MATCH(DMI_BOARD_NAME, "MS-182L"),
357 		},
358 	},
359 	{ }
360 };
361 
362 static struct usb_interface *steelseries_hid_to_usb_intf(struct hid_device *hdev)
363 {
364 	if (!hid_is_usb(hdev))
365 		return NULL;
366 
367 	return to_usb_interface(hdev->dev.parent);
368 }
369 
370 static bool steelseries_msi_rgb_is_interface0(struct hid_device *hdev)
371 {
372 	struct usb_interface *intf = steelseries_hid_to_usb_intf(hdev);
373 	struct usb_device *udev;
374 
375 	if (!intf)
376 		return false;
377 
378 	udev = interface_to_usbdev(intf);
379 
380 	return intf == usb_ifnum_to_if(udev, 0);
381 }
382 
383 #if STEELSERIES_HAS_LEDS_MULTICOLOR
384 
385 static struct usb_device *steelseries_hid_to_usb_dev(struct hid_device *hdev)
386 {
387 	struct usb_interface *intf = steelseries_hid_to_usb_intf(hdev);
388 
389 	if (!intf)
390 		return NULL;
391 
392 	return interface_to_usbdev(intf);
393 }
394 
395 static int steelseries_msi_rgb_set_blocking(struct led_classdev *led_cdev,
396 					    enum led_brightness brightness)
397 {
398 	struct led_classdev_mc *mc_cdev = lcdev_to_mccdev(led_cdev);
399 	struct steelseries_device *sd = container_of(mc_cdev,
400 						    struct steelseries_device,
401 						    mc_cdev);
402 	struct hid_device *hdev = sd->hdev;
403 	struct usb_device *udev = steelseries_hid_to_usb_dev(hdev);
404 	int i, ret;
405 	u8 r, g, b;
406 
407 	static const u8 keys[] = {
408 		0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b,
409 		0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13,
410 		0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b,
411 		0x1c, 0x1d, 0x1e, 0x1f, 0x20, 0x21, 0x22, 0x23,
412 		0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b,
413 		0x2c, 0x2d, 0x2e, 0x2f, 0x30, 0x31, 0x33, 0x34,
414 		0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c,
415 		0x3d, 0x3e, 0x3f, 0x40, 0x41, 0x42, 0x43, 0x44,
416 		0x45, 0x46, 0x47, 0x49, 0x4b, 0x4c, 0x4e, 0x4f,
417 		0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57,
418 		0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f,
419 		0x60, 0x61, 0x62, 0x63, 0x64, 0x66, 0xe0, 0xe1,
420 		0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xf0
421 	};
422 	static const u8 alc_zones[] = { 0x00, 0x01, 0x02, 0x03 };
423 
424 	if (!udev)
425 		return -ENODEV;
426 
427 	mutex_lock(&sd->rgb_lock);
428 
429 	led_mc_calc_color_components(mc_cdev, brightness);
430 
431 	r = mc_cdev->subled_info[0].brightness;
432 	g = mc_cdev->subled_info[1].brightness;
433 	b = mc_cdev->subled_info[2].brightness;
434 
435 	/*
436 	 * Report layout (524 bytes):
437 	 * Byte 0: Opcode (0x0c)
438 	 * Byte 1: 0x00
439 	 * Byte 2: Mode (0x66 for Keyboard, 0x06 for Lightbar)
440 	 * Byte 3: 0x00
441 	 * Bytes 4+: 4-byte chunks per LED (Index, R, G, B)
442 	 */
443 	memset(sd->rgb_buf, 0, STEELSERIES_MSI_RGB_REPORT_LEN);
444 	sd->rgb_buf[0] = STEELSERIES_MSI_RGB_OPCODE;
445 	sd->rgb_buf[1] = 0x00;
446 	sd->rgb_buf[3] = 0x00;
447 
448 	for (i = 0; i < (STEELSERIES_MSI_RGB_REPORT_LEN - 4) / 4; i++)
449 		sd->rgb_buf[4 + i * 4] = 0xff;
450 
451 	if (hdev->product == USB_DEVICE_ID_STEELSERIES_MSI_KLC) {
452 		sd->rgb_buf[2] = STEELSERIES_MSI_RGB_KLC_MODE;
453 		for (i = 0; i < ARRAY_SIZE(keys); i++) {
454 			sd->rgb_buf[4 + i * 4] = keys[i];
455 			sd->rgb_buf[5 + i * 4] = r;
456 			sd->rgb_buf[6 + i * 4] = g;
457 			sd->rgb_buf[7 + i * 4] = b;
458 		}
459 	} else {
460 		sd->rgb_buf[2] = STEELSERIES_MSI_RGB_ALC_MODE;
461 		for (i = 0; i < ARRAY_SIZE(alc_zones); i++) {
462 			sd->rgb_buf[4 + i * 4] = alc_zones[i];
463 			sd->rgb_buf[5 + i * 4] = r;
464 			sd->rgb_buf[6 + i * 4] = g;
465 			sd->rgb_buf[7 + i * 4] = b;
466 		}
467 	}
468 
469 	/*
470 	 * Send the vendor report verbatim with usb_control_msg(): byte 0 is a
471 	 * protocol opcode (0x0c), not a HID report ID, and the controller
472 	 * expects it under report ID 0 (wValue 0x0300). hid_hw_raw_request()
473 	 * would write the report number into byte 0, so the direct control
474 	 * transfer is used to keep the payload byte-identical to the tested
475 	 * userspace implementation.
476 	 */
477 	ret = hid_hw_power(hdev, PM_HINT_FULLON);
478 	if (ret < 0)
479 		goto out_unlock;
480 
481 	ret = usb_control_msg(udev, usb_sndctrlpipe(udev, 0),
482 			      HID_REQ_SET_REPORT,
483 			      USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE,
484 			      STEELSERIES_MSI_RGB_WVALUE, 0,
485 			      sd->rgb_buf, STEELSERIES_MSI_RGB_REPORT_LEN,
486 			      USB_CTRL_SET_TIMEOUT);
487 
488 	hid_hw_power(hdev, PM_HINT_NORMAL);
489 
490 out_unlock:
491 	mutex_unlock(&sd->rgb_lock);
492 	return ret < 0 ? ret : 0;
493 }
494 
495 static void steelseries_msi_rgb_free_buf(void *data)
496 {
497 	kfree(data);
498 }
499 
500 static int steelseries_msi_rgb_register(struct steelseries_device *sd)
501 {
502 	struct hid_device *hdev = sd->hdev;
503 	struct led_classdev *led_cdev;
504 	int ret;
505 
506 	sd->rgb_buf = kzalloc(STEELSERIES_MSI_RGB_REPORT_LEN, GFP_KERNEL);
507 	if (!sd->rgb_buf)
508 		return -ENOMEM;
509 
510 	ret = devm_add_action_or_reset(&hdev->dev,
511 				       steelseries_msi_rgb_free_buf,
512 				       sd->rgb_buf);
513 	if (ret) {
514 		sd->rgb_buf = NULL;
515 		return ret;
516 	}
517 
518 	ret = devm_mutex_init(&hdev->dev, &sd->rgb_lock);
519 	if (ret) {
520 		devm_remove_action(&hdev->dev, steelseries_msi_rgb_free_buf,
521 				   sd->rgb_buf);
522 		kfree(sd->rgb_buf);
523 		sd->rgb_buf = NULL;
524 		return ret;
525 	}
526 
527 	sd->subled_info[0].color_index = LED_COLOR_ID_RED;
528 	sd->subled_info[1].color_index = LED_COLOR_ID_GREEN;
529 	sd->subled_info[2].color_index = LED_COLOR_ID_BLUE;
530 	sd->subled_info[0].intensity = 255;
531 	sd->subled_info[1].intensity = 255;
532 	sd->subled_info[2].intensity = 255;
533 	sd->subled_info[0].channel = 0;
534 	sd->subled_info[1].channel = 1;
535 	sd->subled_info[2].channel = 2;
536 
537 	sd->mc_cdev.subled_info = sd->subled_info;
538 	sd->mc_cdev.num_colors = 3;
539 
540 	led_cdev = &sd->mc_cdev.led_cdev;
541 	if (hdev->product == USB_DEVICE_ID_STEELSERIES_MSI_KLC)
542 		led_cdev->name = "steelseries::kbd_backlight";
543 	else
544 		led_cdev->name = "steelseries::lightbar";
545 
546 	led_cdev->max_brightness = 255;
547 	led_cdev->brightness_set_blocking = steelseries_msi_rgb_set_blocking;
548 
549 	ret = devm_led_classdev_multicolor_register(&hdev->dev, &sd->mc_cdev);
550 	if (ret) {
551 		devm_remove_action(&hdev->dev, steelseries_msi_rgb_free_buf,
552 				   sd->rgb_buf);
553 		kfree(sd->rgb_buf);
554 		sd->rgb_buf = NULL;
555 		return ret;
556 	}
557 
558 	return 0;
559 }
560 #else
561 static int steelseries_msi_rgb_register(struct steelseries_device *sd)
562 {
563 	return -ENODEV;
564 }
565 #endif
566 
567 static int steelseries_probe(struct hid_device *hdev, const struct hid_device_id *id)
568 {
569 	struct steelseries_device *sd;
570 	int ret;
571 
572 	if (hdev->product == USB_DEVICE_ID_STEELSERIES_SRWS1) {
573 #if IS_BUILTIN(CONFIG_LEDS_CLASS) || \
574     (IS_MODULE(CONFIG_LEDS_CLASS) && IS_MODULE(CONFIG_HID_STEELSERIES))
575 		return steelseries_srws1_probe(hdev, id);
576 #else
577 		return -ENODEV;
578 #endif
579 	}
580 
581 	sd = devm_kzalloc(&hdev->dev, sizeof(*sd), GFP_KERNEL);
582 	if (!sd)
583 		return -ENOMEM;
584 	hid_set_drvdata(hdev, sd);
585 	sd->hdev = hdev;
586 	sd->quirks = id->driver_data;
587 
588 	if (sd->quirks & STEELSERIES_MSI_RGB) {
589 		if (!dmi_check_system(steelseries_msi_rgb_dmi_table) ||
590 		    !steelseries_msi_rgb_is_interface0(hdev)) {
591 			hid_dbg(hdev, "MSI RGB quirk not applicable, using generic HID path\n");
592 			sd->quirks &= ~STEELSERIES_MSI_RGB;
593 		}
594 	}
595 
596 	ret = hid_parse(hdev);
597 	if (ret)
598 		return ret;
599 
600 	ret = hid_hw_start(hdev, HID_CONNECT_DEFAULT);
601 	if (ret)
602 		return ret;
603 
604 	ret = hid_hw_open(hdev);
605 	if (ret)
606 		goto err_stop;
607 
608 	if (sd->quirks & STEELSERIES_MSI_RGB) {
609 		ret = steelseries_msi_rgb_register(sd);
610 		if (ret) {
611 			hid_warn(hdev,
612 				 "Failed to register MSI RGB LEDs: %d, continuing without RGB support\n",
613 				 ret);
614 			sd->quirks &= ~STEELSERIES_MSI_RGB;
615 		}
616 	}
617 
618 	return 0;
619 
620 err_stop:
621 	hid_hw_stop(hdev);
622 	return ret;
623 }
624 
625 static void steelseries_remove(struct hid_device *hdev)
626 {
627 	if (hdev->product == USB_DEVICE_ID_STEELSERIES_SRWS1) {
628 #if IS_BUILTIN(CONFIG_LEDS_CLASS) || \
629     (IS_MODULE(CONFIG_LEDS_CLASS) && IS_MODULE(CONFIG_HID_STEELSERIES))
630 		hid_hw_stop(hdev);
631 #endif
632 		return;
633 	}
634 
635 	hid_hw_close(hdev);
636 	hid_hw_stop(hdev);
637 }
638 
639 static const __u8 *steelseries_srws1_report_fixup(struct hid_device *hdev,
640 		__u8 *rdesc, unsigned int *rsize)
641 {
642 	if (hdev->vendor != USB_VENDOR_ID_STEELSERIES ||
643 	    hdev->product != USB_DEVICE_ID_STEELSERIES_SRWS1)
644 		return rdesc;
645 
646 	if (*rsize >= 115 && rdesc[11] == 0x02 && rdesc[13] == 0xc8
647 			&& rdesc[29] == 0xbb && rdesc[40] == 0xc5) {
648 		hid_info(hdev, "Fixing up Steelseries SRW-S1 report descriptor\n");
649 		*rsize = sizeof(steelseries_srws1_rdesc_fixed);
650 		return steelseries_srws1_rdesc_fixed;
651 	}
652 	return rdesc;
653 }
654 
655 static const struct hid_device_id steelseries_devices[] = {
656 	{ HID_USB_DEVICE(USB_VENDOR_ID_STEELSERIES, USB_DEVICE_ID_STEELSERIES_SRWS1),
657 	  .driver_data = STEELSERIES_SRWS1 },
658 
659 #if STEELSERIES_HAS_LEDS_MULTICOLOR
660 	{ /* MSI Raider A18 KLC */
661 	  HID_USB_DEVICE(USB_VENDOR_ID_STEELSERIES, USB_DEVICE_ID_STEELSERIES_MSI_KLC),
662 	  .driver_data = STEELSERIES_MSI_RGB },
663 
664 	{ /* MSI Raider A18 ALC */
665 	  HID_USB_DEVICE(USB_VENDOR_ID_STEELSERIES, USB_DEVICE_ID_STEELSERIES_MSI_ALC),
666 	  .driver_data = STEELSERIES_MSI_RGB },
667 #endif
668 
669 	{ }
670 };
671 MODULE_DEVICE_TABLE(hid, steelseries_devices);
672 
673 static struct hid_driver steelseries_driver = {
674 	.name = "steelseries",
675 	.id_table = steelseries_devices,
676 	.probe = steelseries_probe,
677 	.remove = steelseries_remove,
678 	.report_fixup = steelseries_srws1_report_fixup,
679 };
680 
681 module_hid_driver(steelseries_driver);
682 MODULE_DESCRIPTION("HID driver for Steelseries devices");
683 MODULE_LICENSE("GPL");
684 MODULE_AUTHOR("Simon Wood <simon@mungewell.org>");
685