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