1 // SPDX-License-Identifier: GPL-2.0-or-later 2 /* 3 * HID driver for Steelseries arctis headsets 4 * 5 * Copyright (c) 2023 Bastien Nocera 6 * Copyright (c) 2026 Sriman Achanta 7 */ 8 9 #include <linux/device.h> 10 #include <linux/hid.h> 11 #include <linux/kref.h> 12 #include <linux/slab.h> 13 #include <linux/module.h> 14 #include <linux/power_supply.h> 15 #include <linux/spinlock.h> 16 #include <linux/usb.h> 17 #include <linux/workqueue.h> 18 19 #include "hid-ids.h" 20 21 #define SS_CAP_BATTERY BIT(0) 22 23 struct steelseries_device; 24 25 struct steelseries_device_info { 26 unsigned long capabilities; 27 28 u8 sync_interface; 29 u8 async_interface; 30 31 int (*request_status)(struct hid_device *hdev); 32 void (*parse_status)(struct steelseries_device *sd, u8 *data, int size); 33 }; 34 35 struct steelseries_device { 36 struct kref refcnt; 37 38 struct hid_device *hdev; 39 const struct steelseries_device_info *info; 40 41 struct delayed_work status_work; 42 43 struct power_supply_desc battery_desc; 44 struct power_supply *battery; 45 bool headset_connected; 46 u8 battery_capacity; 47 bool battery_charging; 48 49 spinlock_t lock; 50 bool removed; 51 }; 52 53 static void steelseries_device_release(struct kref *ref) 54 { 55 struct steelseries_device *sd = 56 container_of(ref, struct steelseries_device, refcnt); 57 58 kfree(sd); 59 } 60 61 /* 62 * Headset report helpers 63 */ 64 65 static int steelseries_send_report(struct hid_device *hdev, const u8 *data, 66 int len, enum hid_report_type type) 67 { 68 u8 *buf; 69 int ret; 70 71 buf = kmemdup(data, len, GFP_KERNEL); 72 if (!buf) 73 return -ENOMEM; 74 75 ret = hid_hw_raw_request(hdev, data[0], buf, len, type, 76 HID_REQ_SET_REPORT); 77 kfree(buf); 78 79 if (ret < 0) 80 return ret; 81 if (ret < len) 82 return -EIO; 83 84 return 0; 85 } 86 87 static inline int steelseries_send_output_report(struct hid_device *hdev, 88 const u8 *data, int len) 89 { 90 return steelseries_send_report(hdev, data, len, HID_OUTPUT_REPORT); 91 } 92 93 /* 94 * Headset status request functions 95 */ 96 97 static int steelseries_arctis_1_request_status(struct hid_device *hdev) 98 { 99 const u8 data[] = { 0x06, 0x12 }; 100 101 return steelseries_send_output_report(hdev, data, sizeof(data)); 102 } 103 104 static int steelseries_arctis_7_2018_request_status(struct hid_device *hdev) 105 { 106 const u8 connection[] = { 0x06, 0x14 }; 107 const u8 battery[] = { 0x06, 0x18 }; 108 int ret; 109 110 ret = steelseries_send_output_report(hdev, connection, sizeof(connection)); 111 if (ret) 112 return ret; 113 114 return steelseries_send_output_report(hdev, battery, sizeof(battery)); 115 } 116 117 static int steelseries_arctis_9_request_status(struct hid_device *hdev) 118 { 119 const u8 data[] = { 0x00, 0x20 }; 120 121 return steelseries_send_output_report(hdev, data, sizeof(data)); 122 } 123 124 static int steelseries_arctis_nova_request_status(struct hid_device *hdev) 125 { 126 const u8 data[] = { 0x00, 0xb0 }; 127 128 return steelseries_send_output_report(hdev, data, sizeof(data)); 129 } 130 131 /* 132 * Headset battery helpers 133 */ 134 135 static int battery_capacity_to_level(int capacity) 136 { 137 if (capacity >= 50) 138 return POWER_SUPPLY_CAPACITY_LEVEL_NORMAL; 139 if (capacity >= 20) 140 return POWER_SUPPLY_CAPACITY_LEVEL_LOW; 141 return POWER_SUPPLY_CAPACITY_LEVEL_CRITICAL; 142 } 143 144 static u8 steelseries_map_capacity(u8 capacity, u8 min_in, u8 max_in) 145 { 146 if (capacity >= max_in) 147 return 100; 148 if (capacity <= min_in) 149 return 0; 150 return (capacity - min_in) * 100 / (max_in - min_in); 151 } 152 153 /* 154 * Headset status parse functions 155 */ 156 157 static void steelseries_arctis_1_parse_status(struct steelseries_device *sd, 158 u8 *data, int size) 159 { 160 /* Only the battery status report echoes the request header. */ 161 if (size < 8 || data[0] != 0x06 || data[1] != 0x12) 162 return; 163 164 sd->headset_connected = (data[2] != 0x01); 165 sd->battery_capacity = data[3]; 166 } 167 168 static void steelseries_arctis_7_2018_parse_status(struct steelseries_device *sd, 169 u8 *data, int size) 170 { 171 if (size < 3 || data[0] != 0x06) 172 return; 173 174 switch (data[1]) { 175 case 0x14: 176 /* 0x03 means that the headset is connected to the transmitter. */ 177 sd->headset_connected = data[2] == 0x03; 178 break; 179 case 0x18: 180 if (!sd->headset_connected) 181 break; 182 183 /* The Arctis 7 sometimes overreports battery. Cap to 100. */ 184 sd->battery_capacity = steelseries_map_capacity(data[2], 0, 100); 185 break; 186 } 187 } 188 189 static void steelseries_arctis_9_parse_status(struct steelseries_device *sd, 190 u8 *data, int size) 191 { 192 if (size < 5) 193 return; 194 195 if (data[0] == 0xaa && data[1] == 0x01) { 196 sd->headset_connected = true; 197 sd->battery_charging = (data[4] == 0x01); 198 sd->battery_capacity = steelseries_map_capacity(data[3], 0x64, 0x9a); 199 } else { 200 /* Device off: 0x55 (no status) or 0x03 (stale status). */ 201 sd->headset_connected = false; 202 sd->battery_charging = false; 203 } 204 } 205 206 static void steelseries_arctis_nova_parse_status(struct steelseries_device *sd, 207 u8 *data, int size) 208 { 209 if (size < 2) 210 return; 211 212 switch (data[0]) { 213 case 0xb0: 214 if (size < 4) 215 return; 216 sd->headset_connected = (data[1] == 0x03); 217 sd->battery_capacity = data[2]; 218 sd->battery_charging = (data[3] == 0x01); 219 break; 220 case 0xb7: 221 sd->battery_capacity = data[1]; 222 break; 223 case 0xb9: 224 sd->headset_connected = (data[1] == 0x03); 225 break; 226 case 0xbb: 227 sd->battery_charging = (data[1] == 0x01); 228 break; 229 } 230 } 231 232 static void steelseries_arctis_nova_7_parse_status(struct steelseries_device *sd, 233 u8 *data, int size) 234 { 235 if (size < 2) 236 return; 237 238 switch (data[0]) { 239 case 0xb0: 240 if (size < 4) 241 return; 242 sd->headset_connected = (data[1] == 0x03); 243 sd->battery_capacity = steelseries_map_capacity(data[2], 0, 4); 244 sd->battery_charging = (data[3] == 0x01); 245 break; 246 case 0xb7: 247 sd->battery_capacity = steelseries_map_capacity(data[1], 0, 4); 248 break; 249 case 0xb9: 250 sd->headset_connected = (data[1] == 0x03); 251 break; 252 case 0xbb: 253 sd->battery_charging = (data[1] == 0x01); 254 break; 255 } 256 } 257 258 /* 259 * Device info definitions 260 */ 261 262 static const struct steelseries_device_info arctis_1_info = { 263 .sync_interface = 3, 264 .capabilities = SS_CAP_BATTERY, 265 .request_status = steelseries_arctis_1_request_status, 266 .parse_status = steelseries_arctis_1_parse_status, 267 }; 268 269 static const struct steelseries_device_info arctis_7_2018_info = { 270 .sync_interface = 5, 271 .capabilities = SS_CAP_BATTERY, 272 .request_status = steelseries_arctis_7_2018_request_status, 273 .parse_status = steelseries_arctis_7_2018_parse_status, 274 }; 275 276 static const struct steelseries_device_info arctis_9_info = { 277 .sync_interface = 0, 278 .capabilities = SS_CAP_BATTERY, 279 .request_status = steelseries_arctis_9_request_status, 280 .parse_status = steelseries_arctis_9_parse_status, 281 }; 282 283 static const struct steelseries_device_info arctis_nova_info = { 284 .sync_interface = 3, 285 .async_interface = 5, 286 .capabilities = SS_CAP_BATTERY, 287 .request_status = steelseries_arctis_nova_request_status, 288 .parse_status = steelseries_arctis_nova_parse_status, 289 }; 290 291 static const struct steelseries_device_info arctis_nova_7_info = { 292 .sync_interface = 3, 293 .async_interface = 5, 294 .capabilities = SS_CAP_BATTERY, 295 .request_status = steelseries_arctis_nova_request_status, 296 .parse_status = steelseries_arctis_nova_7_parse_status, 297 }; 298 299 /* 300 * Headset wireless status and battery infrastructure 301 */ 302 303 #define STEELSERIES_HEADSET_STATUS_TIMEOUT_MS 3000 304 305 static void 306 steelseries_headset_set_wireless_status(struct hid_device *hdev, 307 bool connected) 308 { 309 struct usb_interface *intf; 310 311 if (!hid_is_usb(hdev)) 312 return; 313 314 intf = to_usb_interface(hdev->dev.parent); 315 usb_set_wireless_status(intf, connected ? 316 USB_WIRELESS_STATUS_CONNECTED : 317 USB_WIRELESS_STATUS_DISCONNECTED); 318 } 319 320 #define STEELSERIES_PREFIX "SteelSeries " 321 322 static int steelseries_battery_get_property(struct power_supply *psy, 323 enum power_supply_property psp, 324 union power_supply_propval *val) 325 { 326 struct steelseries_device *sd = power_supply_get_drvdata(psy); 327 size_t prefix_len; 328 int ret = 0; 329 330 switch (psp) { 331 case POWER_SUPPLY_PROP_MODEL_NAME: 332 val->strval = sd->hdev->name; 333 while ((prefix_len = str_has_prefix(val->strval, STEELSERIES_PREFIX))) 334 val->strval += prefix_len; 335 break; 336 case POWER_SUPPLY_PROP_MANUFACTURER: 337 val->strval = "SteelSeries"; 338 break; 339 case POWER_SUPPLY_PROP_PRESENT: 340 val->intval = 1; 341 break; 342 case POWER_SUPPLY_PROP_STATUS: 343 if (!sd->headset_connected) 344 val->intval = POWER_SUPPLY_STATUS_UNKNOWN; 345 else if (sd->battery_charging) 346 val->intval = sd->battery_capacity >= 100 ? 347 POWER_SUPPLY_STATUS_FULL : 348 POWER_SUPPLY_STATUS_CHARGING; 349 else 350 val->intval = POWER_SUPPLY_STATUS_DISCHARGING; 351 break; 352 case POWER_SUPPLY_PROP_SCOPE: 353 val->intval = POWER_SUPPLY_SCOPE_DEVICE; 354 break; 355 case POWER_SUPPLY_PROP_CAPACITY: 356 val->intval = sd->battery_capacity; 357 break; 358 case POWER_SUPPLY_PROP_CAPACITY_LEVEL: 359 val->intval = battery_capacity_to_level(sd->battery_capacity); 360 break; 361 default: 362 ret = -EINVAL; 363 break; 364 } 365 return ret; 366 } 367 368 static enum power_supply_property steelseries_battery_props[] = { 369 POWER_SUPPLY_PROP_MODEL_NAME, 370 POWER_SUPPLY_PROP_MANUFACTURER, 371 POWER_SUPPLY_PROP_PRESENT, 372 POWER_SUPPLY_PROP_STATUS, 373 POWER_SUPPLY_PROP_SCOPE, 374 POWER_SUPPLY_PROP_CAPACITY, 375 POWER_SUPPLY_PROP_CAPACITY_LEVEL, 376 }; 377 378 /* 379 * Delayed work handlers for status polling 380 */ 381 382 static void steelseries_status_timer_work_handler(struct work_struct *work) 383 { 384 struct steelseries_device *sd = container_of( 385 work, struct steelseries_device, status_work.work); 386 unsigned long flags; 387 388 sd->info->request_status(sd->hdev); 389 390 spin_lock_irqsave(&sd->lock, flags); 391 /* Async devices push status events themselves; only poll once. */ 392 if (!sd->removed && !sd->info->async_interface) 393 schedule_delayed_work(&sd->status_work, 394 msecs_to_jiffies(STEELSERIES_HEADSET_STATUS_TIMEOUT_MS)); 395 spin_unlock_irqrestore(&sd->lock, flags); 396 } 397 398 static int steelseries_battery_register(struct steelseries_device *sd) 399 { 400 struct power_supply_config battery_cfg = { .drv_data = sd, }; 401 struct power_supply *battery; 402 int ret; 403 404 sd->battery_desc.type = POWER_SUPPLY_TYPE_BATTERY; 405 sd->battery_desc.properties = steelseries_battery_props; 406 sd->battery_desc.num_properties = ARRAY_SIZE(steelseries_battery_props); 407 sd->battery_desc.get_property = steelseries_battery_get_property; 408 sd->battery_desc.use_for_apm = 0; 409 sd->battery_desc.name = devm_kasprintf(&sd->hdev->dev, GFP_KERNEL, 410 "steelseries_headset_battery_%s", 411 sd->hdev->uniq[0] ? sd->hdev->uniq : 412 dev_name(&sd->hdev->dev)); 413 if (!sd->battery_desc.name) 414 return -ENOMEM; 415 416 /* avoid the warning of 0% battery while waiting for the first info */ 417 sd->battery_capacity = 100; 418 sd->battery_charging = false; 419 sd->headset_connected = false; 420 steelseries_headset_set_wireless_status(sd->hdev, false); 421 422 battery = power_supply_register(&sd->hdev->dev, 423 &sd->battery_desc, &battery_cfg); 424 if (IS_ERR(battery)) { 425 ret = PTR_ERR(battery); 426 hid_err(sd->hdev, 427 "%s:power_supply_register failed with error %d\n", 428 __func__, ret); 429 return ret; 430 } 431 power_supply_powers(battery, &sd->hdev->dev); 432 433 /* Assign on success only, so a concurrent raw_event never sees an ERR_PTR. */ 434 sd->battery = battery; 435 436 return 0; 437 } 438 439 static struct hid_driver steelseries_arctis_driver; 440 441 static struct steelseries_device * 442 steelseries_get_sibling_sd(struct hid_device *hdev, int interface_num) 443 { 444 struct usb_interface *intf = to_usb_interface(hdev->dev.parent); 445 struct usb_device *usb_dev = interface_to_usbdev(intf); 446 struct usb_interface *sibling_intf; 447 struct hid_device *sibling_hdev; 448 struct steelseries_device *sd = NULL; 449 450 sibling_intf = usb_ifnum_to_if(usb_dev, interface_num); 451 if (!sibling_intf) 452 return NULL; 453 454 /* 455 * usb_get_intfdata() only yields a hid_device when usbhid is bound; 456 * gate on the descriptor class so a non-HID sibling (e.g. a crafted 457 * device exposing storage or audio here) is never treated as one. 458 */ 459 if (sibling_intf->cur_altsetting->desc.bInterfaceClass != USB_INTERFACE_CLASS_HID) 460 return NULL; 461 462 /* 463 * Take the sibling's device lock across the intfdata read and the 464 * kref_get so a concurrent unbind cannot free the hid_device underneath 465 * us; usbhid leaves intfdata dangling on disconnect, so dev.driver is 466 * the reliable "still bound" test under this lock. Use device_trylock() 467 * to stay off the lockdep chain of the interface being probed and let 468 * the caller retry via -EPROBE_DEFER if the sibling is momentarily busy. 469 */ 470 if (!device_trylock(&sibling_intf->dev)) 471 return NULL; 472 if (sibling_intf->dev.driver) { 473 sibling_hdev = usb_get_intfdata(sibling_intf); 474 if (sibling_hdev && 475 sibling_hdev->driver == &steelseries_arctis_driver) { 476 sd = hid_get_drvdata(sibling_hdev); 477 if (sd) 478 kref_get(&sd->refcnt); 479 } 480 } 481 device_unlock(&sibling_intf->dev); 482 483 return sd; 484 } 485 486 static int steelseries_arctis_probe(struct hid_device *hdev, 487 const struct hid_device_id *id) 488 { 489 const struct steelseries_device_info *info = 490 (const struct steelseries_device_info *)id->driver_data; 491 struct steelseries_device *sd; 492 struct usb_interface *intf; 493 u8 interface_num; 494 int ret; 495 496 if (hid_is_usb(hdev)) { 497 intf = to_usb_interface(hdev->dev.parent); 498 interface_num = intf->cur_altsetting->desc.bInterfaceNumber; 499 } else { 500 return -ENODEV; 501 } 502 503 ret = hid_parse(hdev); 504 if (ret) 505 return ret; 506 507 /* Let hid-generic handle non-vendor or unknown interfaces */ 508 if (interface_num != info->sync_interface && 509 (!info->async_interface || interface_num != info->async_interface)) 510 return hid_hw_start(hdev, HID_CONNECT_DEFAULT); 511 512 if (interface_num == info->sync_interface) { 513 sd = kzalloc_obj(*sd); 514 if (!sd) 515 return -ENOMEM; 516 517 kref_init(&sd->refcnt); 518 sd->hdev = hdev; 519 sd->info = info; 520 spin_lock_init(&sd->lock); 521 INIT_DELAYED_WORK(&sd->status_work, steelseries_status_timer_work_handler); 522 523 ret = hid_hw_start(hdev, HID_CONNECT_DEFAULT); 524 if (ret) 525 goto err_free; 526 527 ret = hid_hw_open(hdev); 528 if (ret) 529 goto err_stop; 530 531 if (info->capabilities & SS_CAP_BATTERY) { 532 ret = steelseries_battery_register(sd); 533 if (ret < 0) 534 hid_warn(hdev, "Failed to register battery: %d\n", ret); 535 } 536 537 /* 538 * Publish drvdata only once fully initialised: the async sibling 539 * attaches by reading it, so it must never observe a half-built or 540 * failed instance. A failed probe never gets here, so the error 541 * path below has nothing to unpublish. 542 */ 543 hid_set_drvdata(hdev, sd); 544 schedule_delayed_work(&sd->status_work, msecs_to_jiffies(100)); 545 546 return 0; 547 } 548 549 /* 550 * The async interface shares the steelseries_device created by the 551 * sync interface. Defer until the sync interface has probed and 552 * published its drvdata. 553 */ 554 if (info->async_interface && interface_num == info->async_interface) { 555 sd = steelseries_get_sibling_sd(hdev, info->sync_interface); 556 if (!sd) 557 return -EPROBE_DEFER; 558 559 hid_set_drvdata(hdev, sd); 560 561 ret = hid_hw_start(hdev, HID_CONNECT_DEFAULT); 562 if (ret) { 563 kref_put(&sd->refcnt, steelseries_device_release); 564 return ret; 565 } 566 567 ret = hid_hw_open(hdev); 568 if (ret) { 569 hid_hw_stop(hdev); 570 kref_put(&sd->refcnt, steelseries_device_release); 571 return ret; 572 } 573 return 0; 574 } 575 576 return -ENODEV; 577 578 err_stop: 579 hid_hw_stop(hdev); 580 err_free: 581 /* drvdata is unpublished until full success, so no sibling can hold sd. */ 582 kref_put(&sd->refcnt, steelseries_device_release); 583 return ret; 584 } 585 586 static void steelseries_arctis_remove(struct hid_device *hdev) 587 { 588 struct steelseries_device *sd; 589 struct power_supply *battery; 590 unsigned long flags; 591 struct usb_interface *intf; 592 u8 interface_num; 593 594 if (hid_is_usb(hdev)) { 595 intf = to_usb_interface(hdev->dev.parent); 596 interface_num = intf->cur_altsetting->desc.bInterfaceNumber; 597 } else { 598 return; 599 } 600 601 sd = hid_get_drvdata(hdev); 602 603 if (!sd) { 604 hid_hw_stop(hdev); 605 return; 606 } 607 608 if (interface_num == sd->info->sync_interface) { 609 spin_lock_irqsave(&sd->lock, flags); 610 sd->removed = true; 611 battery = sd->battery; 612 sd->battery = NULL; 613 spin_unlock_irqrestore(&sd->lock, flags); 614 615 cancel_delayed_work_sync(&sd->status_work); 616 617 if (battery) 618 power_supply_unregister(battery); 619 } 620 621 hid_hw_close(hdev); 622 hid_hw_stop(hdev); 623 624 kref_put(&sd->refcnt, steelseries_device_release); 625 } 626 627 static int steelseries_arctis_raw_event(struct hid_device *hdev, 628 struct hid_report *report, u8 *data, int size) 629 { 630 struct steelseries_device *sd = hid_get_drvdata(hdev); 631 u8 old_capacity; 632 bool old_connected; 633 bool old_charging; 634 bool is_async_interface; 635 unsigned long flags; 636 637 if (!sd) 638 return 0; 639 640 is_async_interface = (hdev != sd->hdev); 641 642 spin_lock_irqsave(&sd->lock, flags); 643 644 if (sd->removed) { 645 spin_unlock_irqrestore(&sd->lock, flags); 646 return 0; 647 } 648 649 old_capacity = sd->battery_capacity; 650 old_connected = sd->headset_connected; 651 old_charging = sd->battery_charging; 652 653 sd->info->parse_status(sd, data, size); 654 655 if (sd->headset_connected != old_connected) { 656 hid_dbg(hdev, 657 "Connected status changed from %sconnected to %sconnected\n", 658 old_connected ? "" : "not ", 659 sd->headset_connected ? "" : "not "); 660 661 if (sd->headset_connected && !old_connected && 662 sd->info->async_interface && is_async_interface) 663 schedule_delayed_work(&sd->status_work, 0); 664 665 if (sd->battery) { 666 steelseries_headset_set_wireless_status(sd->hdev, 667 sd->headset_connected); 668 power_supply_changed(sd->battery); 669 } 670 } 671 672 if (sd->battery_capacity != old_capacity) { 673 hid_dbg(hdev, "Battery capacity changed from %d%% to %d%%\n", 674 old_capacity, sd->battery_capacity); 675 if (sd->battery) 676 power_supply_changed(sd->battery); 677 } 678 679 if (sd->battery_charging != old_charging) { 680 hid_dbg(hdev, 681 "Battery charging status changed from %scharging to %scharging\n", 682 old_charging ? "" : "not ", 683 sd->battery_charging ? "" : "not "); 684 if (sd->battery) 685 power_supply_changed(sd->battery); 686 } 687 688 spin_unlock_irqrestore(&sd->lock, flags); 689 690 return 0; 691 } 692 693 static const struct hid_device_id steelseries_arctis_devices[] = { 694 { HID_USB_DEVICE(USB_VENDOR_ID_STEELSERIES, 695 USB_DEVICE_ID_STEELSERIES_ARCTIS_1_X), 696 .driver_data = (unsigned long)&arctis_1_info }, 697 { HID_USB_DEVICE(USB_VENDOR_ID_STEELSERIES, 698 USB_DEVICE_ID_STEELSERIES_ARCTIS_7_2018), 699 .driver_data = (unsigned long)&arctis_7_2018_info }, 700 { HID_USB_DEVICE(USB_VENDOR_ID_STEELSERIES, 701 USB_DEVICE_ID_STEELSERIES_ARCTIS_9), 702 .driver_data = (unsigned long)&arctis_9_info }, 703 { HID_USB_DEVICE(USB_VENDOR_ID_STEELSERIES, 704 USB_DEVICE_ID_STEELSERIES_ARCTIS_NOVA_5_X), 705 .driver_data = (unsigned long)&arctis_nova_info }, 706 { HID_USB_DEVICE(USB_VENDOR_ID_STEELSERIES, 707 USB_DEVICE_ID_STEELSERIES_ARCTIS_NOVA_7), 708 .driver_data = (unsigned long)&arctis_nova_7_info }, 709 { HID_USB_DEVICE(USB_VENDOR_ID_STEELSERIES, 710 USB_DEVICE_ID_STEELSERIES_ARCTIS_NOVA_7_X), 711 .driver_data = (unsigned long)&arctis_nova_7_info }, 712 { HID_USB_DEVICE(USB_VENDOR_ID_STEELSERIES, 713 USB_DEVICE_ID_STEELSERIES_ARCTIS_NOVA_7_X_2), 714 .driver_data = (unsigned long)&arctis_nova_7_info }, 715 { HID_USB_DEVICE(USB_VENDOR_ID_STEELSERIES, 716 USB_DEVICE_ID_STEELSERIES_ARCTIS_NOVA_7_DIABLO), 717 .driver_data = (unsigned long)&arctis_nova_7_info }, 718 { HID_USB_DEVICE(USB_VENDOR_ID_STEELSERIES, 719 USB_DEVICE_ID_STEELSERIES_ARCTIS_NOVA_7_WOW), 720 .driver_data = (unsigned long)&arctis_nova_7_info }, 721 { HID_USB_DEVICE(USB_VENDOR_ID_STEELSERIES, 722 USB_DEVICE_ID_STEELSERIES_ARCTIS_NOVA_7_2026), 723 .driver_data = (unsigned long)&arctis_nova_info }, 724 { HID_USB_DEVICE(USB_VENDOR_ID_STEELSERIES, 725 USB_DEVICE_ID_STEELSERIES_ARCTIS_NOVA_7_P_2026), 726 .driver_data = (unsigned long)&arctis_nova_info }, 727 { HID_USB_DEVICE(USB_VENDOR_ID_STEELSERIES, 728 USB_DEVICE_ID_STEELSERIES_ARCTIS_NOVA_7_X_2026), 729 .driver_data = (unsigned long)&arctis_nova_info }, 730 { HID_USB_DEVICE(USB_VENDOR_ID_STEELSERIES, 731 USB_DEVICE_ID_STEELSERIES_ARCTIS_NOVA_7_DIABLO_2026), 732 .driver_data = (unsigned long)&arctis_nova_info }, 733 { HID_USB_DEVICE(USB_VENDOR_ID_STEELSERIES, 734 USB_DEVICE_ID_STEELSERIES_ARCTIS_NOVA_7_GEN2), 735 .driver_data = (unsigned long)&arctis_nova_info }, 736 { HID_USB_DEVICE(USB_VENDOR_ID_STEELSERIES, 737 USB_DEVICE_ID_STEELSERIES_ARCTIS_NOVA_7_X_GEN2), 738 .driver_data = (unsigned long)&arctis_nova_info }, 739 { HID_USB_DEVICE(USB_VENDOR_ID_STEELSERIES, 740 USB_DEVICE_ID_STEELSERIES_ARCTIS_NOVA_7_X_GEN2_2), 741 .driver_data = (unsigned long)&arctis_nova_info }, 742 { HID_USB_DEVICE(USB_VENDOR_ID_STEELSERIES, 743 USB_DEVICE_ID_STEELSERIES_ARCTIS_NOVA_7_X_GEN2_3), 744 .driver_data = (unsigned long)&arctis_nova_info }, 745 {} 746 }; 747 MODULE_DEVICE_TABLE(hid, steelseries_arctis_devices); 748 749 static struct hid_driver steelseries_arctis_driver = { 750 .name = "hid-steelseries-arctis", 751 .id_table = steelseries_arctis_devices, 752 .probe = steelseries_arctis_probe, 753 .remove = steelseries_arctis_remove, 754 .raw_event = steelseries_arctis_raw_event, 755 }; 756 757 module_hid_driver(steelseries_arctis_driver); 758 MODULE_DESCRIPTION("HID driver for Steelseries arctis headsets"); 759 MODULE_LICENSE("GPL"); 760 MODULE_AUTHOR("Christian Mayer <git@mayer-bgk.de>"); 761 MODULE_AUTHOR("Bastien Nocera <hadess@hadess.net>"); 762 MODULE_AUTHOR("Sriman Achanta <srimanachanta@gmail.com>"); 763