xref: /linux/drivers/hid/hid-steelseries-arctis.c (revision 1fc5a74b108fc90951890ec513ac81869f5eaff1)
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