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_7_2018_request_status(struct hid_device * hdev)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
steelseries_arctis_9_request_status(struct hid_device * hdev)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
steelseries_arctis_nova_request_status(struct hid_device * hdev)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
battery_capacity_to_level(int capacity)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
steelseries_map_capacity(u8 capacity,u8 min_in,u8 max_in)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
steelseries_arctis_1_parse_status(struct steelseries_device * sd,u8 * data,int size)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
steelseries_arctis_7_2018_parse_status(struct steelseries_device * sd,u8 * data,int size)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
steelseries_arctis_9_parse_status(struct steelseries_device * sd,u8 * data,int size)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
steelseries_arctis_nova_parse_status(struct steelseries_device * sd,u8 * data,int size)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
steelseries_arctis_nova_7_parse_status(struct steelseries_device * sd,u8 * data,int size)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
steelseries_headset_set_wireless_status(struct hid_device * hdev,bool connected)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
steelseries_battery_get_property(struct power_supply * psy,enum power_supply_property psp,union power_supply_propval * val)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
steelseries_status_timer_work_handler(struct work_struct * work)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
steelseries_battery_register(struct steelseries_device * sd)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 *
steelseries_get_sibling_sd(struct hid_device * hdev,int interface_num)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
steelseries_arctis_probe(struct hid_device * hdev,const struct hid_device_id * id)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
steelseries_arctis_remove(struct hid_device * hdev)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
steelseries_arctis_raw_event(struct hid_device * hdev,struct hid_report * report,u8 * data,int size)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