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