printf: Remove unused 'bprintf'
[drm/drm-misc.git] / drivers / hid / hid-corsair-void.c
blob6ece56b850fc0213ef1715c726172088352706f8
1 // SPDX-License-Identifier: GPL-2.0-or-later
2 /*
3 * HID driver for Corsair Void headsets
5 * Copyright (C) 2023-2024 Stuart Hayhurst
6 */
8 /* -------------------------------------------------------------------------- */
9 /* Receiver report information: (ID 100) */
10 /* -------------------------------------------------------------------------- */
12 * When queried, the receiver reponds with 5 bytes to describe the battery
13 * The power button, mute button and moving the mic also trigger this report
14 * This includes power button + mic + connection + battery status and capacity
15 * The information below may not be perfect, it's been gathered through guesses
17 * 0: REPORT ID
18 * 100 for the battery packet
20 * 1: POWER BUTTON + (?)
21 * Largest bit is 1 when power button pressed
23 * 2: BATTERY CAPACITY + MIC STATUS
24 * Battery capacity:
25 * Seems to report ~54 higher than reality when charging
26 * Capped at 100, charging or not
27 * Microphone status:
28 * Largest bit is set to 1 when the mic is physically up
29 * No bits change when the mic is muted, only when physically moved
30 * This report is sent every time the mic is moved, no polling required
32 * 3: CONNECTION STATUS
33 * 16: Wired headset
34 * 38: Initialising
35 * 49: Lost connection
36 * 51: Disconnected, searching
37 * 52: Disconnected, not searching
38 * 177: Normal
40 * 4: BATTERY STATUS
41 * 0: Disconnected
42 * 1: Normal
43 * 2: Low
44 * 3: Critical - sent during shutdown
45 * 4: Fully charged
46 * 5: Charging
48 /* -------------------------------------------------------------------------- */
50 /* -------------------------------------------------------------------------- */
51 /* Receiver report information: (ID 102) */
52 /* -------------------------------------------------------------------------- */
54 * When queried, the recevier responds with 4 bytes to describe the firmware
55 * The first 2 bytes are for the receiver, the second 2 are the headset
56 * The headset firmware version will be 0 if no headset is connected
58 * 0: Recevier firmware major version
59 * Major version of the receiver's firmware
61 * 1: Recevier firmware minor version
62 * Minor version of the receiver's firmware
64 * 2: Headset firmware major version
65 * Major version of the headset's firmware
67 * 3: Headset firmware minor version
68 * Minor version of the headset's firmware
70 /* -------------------------------------------------------------------------- */
72 #include <linux/bitfield.h>
73 #include <linux/bitops.h>
74 #include <linux/cleanup.h>
75 #include <linux/device.h>
76 #include <linux/hid.h>
77 #include <linux/module.h>
78 #include <linux/mutex.h>
79 #include <linux/power_supply.h>
80 #include <linux/usb.h>
81 #include <linux/workqueue.h>
82 #include <asm/byteorder.h>
84 #include "hid-ids.h"
86 #define CORSAIR_VOID_DEVICE(id, type) { HID_USB_DEVICE(USB_VENDOR_ID_CORSAIR, (id)), \
87 .driver_data = (type) }
88 #define CORSAIR_VOID_WIRELESS_DEVICE(id) CORSAIR_VOID_DEVICE((id), CORSAIR_VOID_WIRELESS)
89 #define CORSAIR_VOID_WIRED_DEVICE(id) CORSAIR_VOID_DEVICE((id), CORSAIR_VOID_WIRED)
91 #define CORSAIR_VOID_STATUS_REQUEST_ID 0xC9
92 #define CORSAIR_VOID_NOTIF_REQUEST_ID 0xCA
93 #define CORSAIR_VOID_SIDETONE_REQUEST_ID 0xFF
94 #define CORSAIR_VOID_STATUS_REPORT_ID 0x64
95 #define CORSAIR_VOID_FIRMWARE_REPORT_ID 0x66
97 #define CORSAIR_VOID_USB_SIDETONE_REQUEST 0x1
98 #define CORSAIR_VOID_USB_SIDETONE_REQUEST_TYPE 0x21
99 #define CORSAIR_VOID_USB_SIDETONE_VALUE 0x200
100 #define CORSAIR_VOID_USB_SIDETONE_INDEX 0xB00
102 #define CORSAIR_VOID_MIC_MASK GENMASK(7, 7)
103 #define CORSAIR_VOID_CAPACITY_MASK GENMASK(6, 0)
105 #define CORSAIR_VOID_WIRELESS_CONNECTED 177
107 #define CORSAIR_VOID_SIDETONE_MAX_WIRELESS 55
108 #define CORSAIR_VOID_SIDETONE_MAX_WIRED 4096
110 enum {
111 CORSAIR_VOID_WIRELESS,
112 CORSAIR_VOID_WIRED,
115 enum {
116 CORSAIR_VOID_BATTERY_NORMAL = 1,
117 CORSAIR_VOID_BATTERY_LOW = 2,
118 CORSAIR_VOID_BATTERY_CRITICAL = 3,
119 CORSAIR_VOID_BATTERY_CHARGED = 4,
120 CORSAIR_VOID_BATTERY_CHARGING = 5,
123 static enum power_supply_property corsair_void_battery_props[] = {
124 POWER_SUPPLY_PROP_STATUS,
125 POWER_SUPPLY_PROP_PRESENT,
126 POWER_SUPPLY_PROP_CAPACITY,
127 POWER_SUPPLY_PROP_CAPACITY_LEVEL,
128 POWER_SUPPLY_PROP_SCOPE,
129 POWER_SUPPLY_PROP_MODEL_NAME,
130 POWER_SUPPLY_PROP_MANUFACTURER,
133 struct corsair_void_battery_data {
134 int status;
135 bool present;
136 int capacity;
137 int capacity_level;
140 struct corsair_void_drvdata {
141 struct hid_device *hid_dev;
142 struct device *dev;
144 char *name;
145 bool is_wired;
146 unsigned int sidetone_max;
148 struct corsair_void_battery_data battery_data;
149 bool mic_up;
150 bool connected;
151 int fw_receiver_major;
152 int fw_receiver_minor;
153 int fw_headset_major;
154 int fw_headset_minor;
156 struct power_supply *battery;
157 struct power_supply_desc battery_desc;
158 struct mutex battery_mutex;
160 struct delayed_work delayed_status_work;
161 struct delayed_work delayed_firmware_work;
162 struct work_struct battery_remove_work;
163 struct work_struct battery_add_work;
167 * Functions to process receiver data
170 static void corsair_void_set_wireless_status(struct corsair_void_drvdata *drvdata)
172 struct usb_interface *usb_if = to_usb_interface(drvdata->dev->parent);
174 if (drvdata->is_wired)
175 return;
177 usb_set_wireless_status(usb_if, drvdata->connected ?
178 USB_WIRELESS_STATUS_CONNECTED :
179 USB_WIRELESS_STATUS_DISCONNECTED);
182 static void corsair_void_set_unknown_batt(struct corsair_void_drvdata *drvdata)
184 struct corsair_void_battery_data *battery_data = &drvdata->battery_data;
186 battery_data->status = POWER_SUPPLY_STATUS_UNKNOWN;
187 battery_data->present = false;
188 battery_data->capacity = 0;
189 battery_data->capacity_level = POWER_SUPPLY_CAPACITY_LEVEL_UNKNOWN;
192 /* Reset data that may change between wireless connections */
193 static void corsair_void_set_unknown_wireless_data(struct corsair_void_drvdata *drvdata)
195 /* Only 0 out headset, receiver is always known if relevant */
196 drvdata->fw_headset_major = 0;
197 drvdata->fw_headset_minor = 0;
199 drvdata->connected = false;
200 drvdata->mic_up = false;
202 corsair_void_set_wireless_status(drvdata);
205 static void corsair_void_process_receiver(struct corsair_void_drvdata *drvdata,
206 int raw_battery_capacity,
207 int raw_connection_status,
208 int raw_battery_status)
210 struct corsair_void_battery_data *battery_data = &drvdata->battery_data;
211 struct corsair_void_battery_data orig_battery_data;
213 /* Save initial battery data, to compare later */
214 orig_battery_data = *battery_data;
216 /* Headset not connected, or it's wired */
217 if (raw_connection_status != CORSAIR_VOID_WIRELESS_CONNECTED)
218 goto unknown_battery;
220 /* Battery information unavailable */
221 if (raw_battery_status == 0)
222 goto unknown_battery;
224 /* Battery must be connected then */
225 battery_data->present = true;
226 battery_data->capacity_level = POWER_SUPPLY_CAPACITY_LEVEL_NORMAL;
228 /* Set battery status */
229 switch (raw_battery_status) {
230 case CORSAIR_VOID_BATTERY_NORMAL:
231 case CORSAIR_VOID_BATTERY_LOW:
232 case CORSAIR_VOID_BATTERY_CRITICAL:
233 battery_data->status = POWER_SUPPLY_STATUS_DISCHARGING;
234 if (raw_battery_status == CORSAIR_VOID_BATTERY_LOW)
235 battery_data->capacity_level = POWER_SUPPLY_CAPACITY_LEVEL_LOW;
236 else if (raw_battery_status == CORSAIR_VOID_BATTERY_CRITICAL)
237 battery_data->capacity_level = POWER_SUPPLY_CAPACITY_LEVEL_CRITICAL;
239 break;
240 case CORSAIR_VOID_BATTERY_CHARGED:
241 battery_data->status = POWER_SUPPLY_STATUS_FULL;
242 break;
243 case CORSAIR_VOID_BATTERY_CHARGING:
244 battery_data->status = POWER_SUPPLY_STATUS_CHARGING;
245 break;
246 default:
247 hid_warn(drvdata->hid_dev, "unknown battery status '%d'",
248 raw_battery_status);
249 goto unknown_battery;
250 break;
253 battery_data->capacity = raw_battery_capacity;
254 corsair_void_set_wireless_status(drvdata);
256 goto success;
257 unknown_battery:
258 corsair_void_set_unknown_batt(drvdata);
259 success:
261 /* Inform power supply if battery values changed */
262 if (memcmp(&orig_battery_data, battery_data, sizeof(*battery_data))) {
263 scoped_guard(mutex, &drvdata->battery_mutex) {
264 if (drvdata->battery) {
265 power_supply_changed(drvdata->battery);
272 * Functions to report stored data
275 static int corsair_void_battery_get_property(struct power_supply *psy,
276 enum power_supply_property prop,
277 union power_supply_propval *val)
279 struct corsair_void_drvdata *drvdata = power_supply_get_drvdata(psy);
281 switch (prop) {
282 case POWER_SUPPLY_PROP_SCOPE:
283 val->intval = POWER_SUPPLY_SCOPE_DEVICE;
284 break;
285 case POWER_SUPPLY_PROP_MODEL_NAME:
286 if (!strncmp(drvdata->hid_dev->name, "Corsair ", 8))
287 val->strval = drvdata->hid_dev->name + 8;
288 else
289 val->strval = drvdata->hid_dev->name;
290 break;
291 case POWER_SUPPLY_PROP_MANUFACTURER:
292 val->strval = "Corsair";
293 break;
294 case POWER_SUPPLY_PROP_STATUS:
295 val->intval = drvdata->battery_data.status;
296 break;
297 case POWER_SUPPLY_PROP_PRESENT:
298 val->intval = drvdata->battery_data.present;
299 break;
300 case POWER_SUPPLY_PROP_CAPACITY:
301 val->intval = drvdata->battery_data.capacity;
302 break;
303 case POWER_SUPPLY_PROP_CAPACITY_LEVEL:
304 val->intval = drvdata->battery_data.capacity_level;
305 break;
306 default:
307 return -EINVAL;
310 return 0;
313 static ssize_t microphone_up_show(struct device *dev,
314 struct device_attribute *attr, char *buf)
316 struct corsair_void_drvdata *drvdata = dev_get_drvdata(dev);
318 if (!drvdata->connected)
319 return -ENODEV;
321 return sysfs_emit(buf, "%d\n", drvdata->mic_up);
324 static ssize_t fw_version_receiver_show(struct device *dev,
325 struct device_attribute *attr,
326 char *buf)
328 struct corsair_void_drvdata *drvdata = dev_get_drvdata(dev);
330 if (drvdata->fw_receiver_major == 0 && drvdata->fw_receiver_minor == 0)
331 return -ENODATA;
333 return sysfs_emit(buf, "%d.%02d\n", drvdata->fw_receiver_major,
334 drvdata->fw_receiver_minor);
338 static ssize_t fw_version_headset_show(struct device *dev,
339 struct device_attribute *attr,
340 char *buf)
342 struct corsair_void_drvdata *drvdata = dev_get_drvdata(dev);
344 if (drvdata->fw_headset_major == 0 && drvdata->fw_headset_minor == 0)
345 return -ENODATA;
347 return sysfs_emit(buf, "%d.%02d\n", drvdata->fw_headset_major,
348 drvdata->fw_headset_minor);
351 static ssize_t sidetone_max_show(struct device *dev,
352 struct device_attribute *attr,
353 char *buf)
355 struct corsair_void_drvdata *drvdata = dev_get_drvdata(dev);
357 return sysfs_emit(buf, "%d\n", drvdata->sidetone_max);
361 * Functions to send data to headset
364 static ssize_t send_alert_store(struct device *dev,
365 struct device_attribute *attr,
366 const char *buf, size_t count)
368 struct corsair_void_drvdata *drvdata = dev_get_drvdata(dev);
369 struct hid_device *hid_dev = drvdata->hid_dev;
370 unsigned char alert_id;
371 unsigned char *send_buf __free(kfree) = NULL;
372 int ret;
374 if (!drvdata->connected || drvdata->is_wired)
375 return -ENODEV;
377 /* Only accept 0 or 1 for alert ID */
378 if (kstrtou8(buf, 10, &alert_id) || alert_id >= 2)
379 return -EINVAL;
381 send_buf = kmalloc(3, GFP_KERNEL);
382 if (!send_buf)
383 return -ENOMEM;
385 /* Packet format to send alert with ID alert_id */
386 send_buf[0] = CORSAIR_VOID_NOTIF_REQUEST_ID;
387 send_buf[1] = 0x02;
388 send_buf[2] = alert_id;
390 ret = hid_hw_raw_request(hid_dev, CORSAIR_VOID_NOTIF_REQUEST_ID,
391 send_buf, 3, HID_OUTPUT_REPORT,
392 HID_REQ_SET_REPORT);
393 if (ret < 0)
394 hid_warn(hid_dev, "failed to send alert request (reason: %d)",
395 ret);
396 else
397 ret = count;
399 return ret;
402 static int corsair_void_set_sidetone_wired(struct device *dev, const char *buf,
403 unsigned int sidetone)
405 struct usb_interface *usb_if = to_usb_interface(dev->parent);
406 struct usb_device *usb_dev = interface_to_usbdev(usb_if);
408 /* Packet format to set sidetone for wired headsets */
409 __le16 sidetone_le = cpu_to_le16(sidetone);
411 return usb_control_msg_send(usb_dev, 0,
412 CORSAIR_VOID_USB_SIDETONE_REQUEST,
413 CORSAIR_VOID_USB_SIDETONE_REQUEST_TYPE,
414 CORSAIR_VOID_USB_SIDETONE_VALUE,
415 CORSAIR_VOID_USB_SIDETONE_INDEX,
416 &sidetone_le, 2, USB_CTRL_SET_TIMEOUT,
417 GFP_KERNEL);
420 static int corsair_void_set_sidetone_wireless(struct device *dev,
421 const char *buf,
422 unsigned char sidetone)
424 struct corsair_void_drvdata *drvdata = dev_get_drvdata(dev);
425 struct hid_device *hid_dev = drvdata->hid_dev;
426 unsigned char *send_buf __free(kfree) = NULL;
428 send_buf = kmalloc(12, GFP_KERNEL);
429 if (!send_buf)
430 return -ENOMEM;
432 /* Packet format to set sidetone for wireless headsets */
433 send_buf[0] = CORSAIR_VOID_SIDETONE_REQUEST_ID;
434 send_buf[1] = 0x0B;
435 send_buf[2] = 0x00;
436 send_buf[3] = 0xFF;
437 send_buf[4] = 0x04;
438 send_buf[5] = 0x0E;
439 send_buf[6] = 0xFF;
440 send_buf[7] = 0x05;
441 send_buf[8] = 0x01;
442 send_buf[9] = 0x04;
443 send_buf[10] = 0x00;
444 send_buf[11] = sidetone + 200;
446 return hid_hw_raw_request(hid_dev, CORSAIR_VOID_SIDETONE_REQUEST_ID,
447 send_buf, 12, HID_FEATURE_REPORT,
448 HID_REQ_SET_REPORT);
451 static ssize_t set_sidetone_store(struct device *dev,
452 struct device_attribute *attr,
453 const char *buf, size_t count)
455 struct corsair_void_drvdata *drvdata = dev_get_drvdata(dev);
456 struct hid_device *hid_dev = drvdata->hid_dev;
457 unsigned int sidetone;
458 int ret;
460 if (!drvdata->connected)
461 return -ENODEV;
463 /* sidetone must be between 0 and drvdata->sidetone_max inclusive */
464 if (kstrtouint(buf, 10, &sidetone) || sidetone > drvdata->sidetone_max)
465 return -EINVAL;
467 if (drvdata->is_wired)
468 ret = corsair_void_set_sidetone_wired(dev, buf, sidetone);
469 else
470 ret = corsair_void_set_sidetone_wireless(dev, buf, sidetone);
472 if (ret < 0)
473 hid_warn(hid_dev, "failed to send sidetone (reason: %d)", ret);
474 else
475 ret = count;
477 return ret;
480 static int corsair_void_request_status(struct hid_device *hid_dev, int id)
482 unsigned char *send_buf __free(kfree) = NULL;
484 send_buf = kmalloc(2, GFP_KERNEL);
485 if (!send_buf)
486 return -ENOMEM;
488 /* Packet format to request data item (status / firmware) refresh */
489 send_buf[0] = CORSAIR_VOID_STATUS_REQUEST_ID;
490 send_buf[1] = id;
492 /* Send request for data refresh */
493 return hid_hw_raw_request(hid_dev, CORSAIR_VOID_STATUS_REQUEST_ID,
494 send_buf, 2, HID_OUTPUT_REPORT,
495 HID_REQ_SET_REPORT);
499 * Headset connect / disconnect handlers and work handlers
502 static void corsair_void_status_work_handler(struct work_struct *work)
504 struct corsair_void_drvdata *drvdata;
505 struct delayed_work *delayed_work;
506 int battery_ret;
508 delayed_work = container_of(work, struct delayed_work, work);
509 drvdata = container_of(delayed_work, struct corsair_void_drvdata,
510 delayed_status_work);
512 battery_ret = corsair_void_request_status(drvdata->hid_dev,
513 CORSAIR_VOID_STATUS_REPORT_ID);
514 if (battery_ret < 0) {
515 hid_warn(drvdata->hid_dev,
516 "failed to request battery (reason: %d)", battery_ret);
520 static void corsair_void_firmware_work_handler(struct work_struct *work)
522 struct corsair_void_drvdata *drvdata;
523 struct delayed_work *delayed_work;
524 int firmware_ret;
526 delayed_work = container_of(work, struct delayed_work, work);
527 drvdata = container_of(delayed_work, struct corsair_void_drvdata,
528 delayed_firmware_work);
530 firmware_ret = corsair_void_request_status(drvdata->hid_dev,
531 CORSAIR_VOID_FIRMWARE_REPORT_ID);
532 if (firmware_ret < 0) {
533 hid_warn(drvdata->hid_dev,
534 "failed to request firmware (reason: %d)", firmware_ret);
539 static void corsair_void_battery_remove_work_handler(struct work_struct *work)
541 struct corsair_void_drvdata *drvdata;
543 drvdata = container_of(work, struct corsair_void_drvdata,
544 battery_remove_work);
545 scoped_guard(mutex, &drvdata->battery_mutex) {
546 if (drvdata->battery) {
547 power_supply_unregister(drvdata->battery);
548 drvdata->battery = NULL;
553 static void corsair_void_battery_add_work_handler(struct work_struct *work)
555 struct corsair_void_drvdata *drvdata;
556 struct power_supply_config psy_cfg;
557 struct power_supply *new_supply;
559 drvdata = container_of(work, struct corsair_void_drvdata,
560 battery_add_work);
561 guard(mutex)(&drvdata->battery_mutex);
562 if (drvdata->battery)
563 return;
565 psy_cfg.drv_data = drvdata;
566 new_supply = power_supply_register(drvdata->dev,
567 &drvdata->battery_desc,
568 &psy_cfg);
570 if (IS_ERR(new_supply)) {
571 hid_err(drvdata->hid_dev,
572 "failed to register battery '%s' (reason: %ld)\n",
573 drvdata->battery_desc.name,
574 PTR_ERR(new_supply));
575 return;
578 if (power_supply_powers(new_supply, drvdata->dev)) {
579 power_supply_unregister(new_supply);
580 return;
583 drvdata->battery = new_supply;
586 static void corsair_void_headset_connected(struct corsair_void_drvdata *drvdata)
588 schedule_work(&drvdata->battery_add_work);
589 schedule_delayed_work(&drvdata->delayed_firmware_work,
590 msecs_to_jiffies(100));
593 static void corsair_void_headset_disconnected(struct corsair_void_drvdata *drvdata)
595 schedule_work(&drvdata->battery_remove_work);
597 corsair_void_set_unknown_wireless_data(drvdata);
598 corsair_void_set_unknown_batt(drvdata);
602 * Driver setup, probing and HID event handling
605 static DEVICE_ATTR_RO(fw_version_receiver);
606 static DEVICE_ATTR_RO(fw_version_headset);
607 static DEVICE_ATTR_RO(microphone_up);
608 static DEVICE_ATTR_RO(sidetone_max);
610 static DEVICE_ATTR_WO(send_alert);
611 static DEVICE_ATTR_WO(set_sidetone);
613 static struct attribute *corsair_void_attrs[] = {
614 &dev_attr_fw_version_receiver.attr,
615 &dev_attr_fw_version_headset.attr,
616 &dev_attr_microphone_up.attr,
617 &dev_attr_send_alert.attr,
618 &dev_attr_set_sidetone.attr,
619 &dev_attr_sidetone_max.attr,
620 NULL,
623 static const struct attribute_group corsair_void_attr_group = {
624 .attrs = corsair_void_attrs,
627 static int corsair_void_probe(struct hid_device *hid_dev,
628 const struct hid_device_id *hid_id)
630 int ret;
631 struct corsair_void_drvdata *drvdata;
632 char *name;
634 if (!hid_is_usb(hid_dev))
635 return -EINVAL;
637 drvdata = devm_kzalloc(&hid_dev->dev, sizeof(*drvdata),
638 GFP_KERNEL);
639 if (!drvdata)
640 return -ENOMEM;
642 hid_set_drvdata(hid_dev, drvdata);
643 dev_set_drvdata(&hid_dev->dev, drvdata);
645 drvdata->dev = &hid_dev->dev;
646 drvdata->hid_dev = hid_dev;
647 drvdata->is_wired = hid_id->driver_data == CORSAIR_VOID_WIRED;
649 drvdata->sidetone_max = CORSAIR_VOID_SIDETONE_MAX_WIRELESS;
650 if (drvdata->is_wired)
651 drvdata->sidetone_max = CORSAIR_VOID_SIDETONE_MAX_WIRED;
653 /* Set initial values for no wireless headset attached */
654 /* If a headset is attached, it'll be prompted later */
655 corsair_void_set_unknown_wireless_data(drvdata);
656 corsair_void_set_unknown_batt(drvdata);
658 /* Receiver version won't be reset after init */
659 /* Headset version already set via set_unknown_wireless_data */
660 drvdata->fw_receiver_major = 0;
661 drvdata->fw_receiver_minor = 0;
663 ret = hid_parse(hid_dev);
664 if (ret) {
665 hid_err(hid_dev, "parse failed (reason: %d)\n", ret);
666 return ret;
669 name = devm_kasprintf(drvdata->dev, GFP_KERNEL,
670 "corsair-void-%d-battery", hid_dev->id);
671 if (!name)
672 return -ENOMEM;
674 drvdata->battery_desc.name = name;
675 drvdata->battery_desc.type = POWER_SUPPLY_TYPE_BATTERY;
676 drvdata->battery_desc.properties = corsair_void_battery_props;
677 drvdata->battery_desc.num_properties = ARRAY_SIZE(corsair_void_battery_props);
678 drvdata->battery_desc.get_property = corsair_void_battery_get_property;
680 drvdata->battery = NULL;
681 INIT_WORK(&drvdata->battery_remove_work,
682 corsair_void_battery_remove_work_handler);
683 INIT_WORK(&drvdata->battery_add_work,
684 corsair_void_battery_add_work_handler);
685 ret = devm_mutex_init(drvdata->dev, &drvdata->battery_mutex);
686 if (ret)
687 return ret;
689 ret = sysfs_create_group(&hid_dev->dev.kobj, &corsair_void_attr_group);
690 if (ret)
691 return ret;
693 /* Any failures after here will need to call hid_hw_stop */
694 ret = hid_hw_start(hid_dev, HID_CONNECT_DEFAULT);
695 if (ret) {
696 hid_err(hid_dev, "hid_hw_start failed (reason: %d)\n", ret);
697 goto failed_after_sysfs;
700 /* Refresh battery data, in case wireless headset is already connected */
701 INIT_DELAYED_WORK(&drvdata->delayed_status_work,
702 corsair_void_status_work_handler);
703 schedule_delayed_work(&drvdata->delayed_status_work,
704 msecs_to_jiffies(100));
706 /* Refresh firmware versions */
707 INIT_DELAYED_WORK(&drvdata->delayed_firmware_work,
708 corsair_void_firmware_work_handler);
709 schedule_delayed_work(&drvdata->delayed_firmware_work,
710 msecs_to_jiffies(100));
712 return 0;
714 failed_after_sysfs:
715 sysfs_remove_group(&hid_dev->dev.kobj, &corsair_void_attr_group);
716 return ret;
719 static void corsair_void_remove(struct hid_device *hid_dev)
721 struct corsair_void_drvdata *drvdata = hid_get_drvdata(hid_dev);
723 hid_hw_stop(hid_dev);
724 cancel_work_sync(&drvdata->battery_remove_work);
725 cancel_work_sync(&drvdata->battery_add_work);
726 if (drvdata->battery)
727 power_supply_unregister(drvdata->battery);
729 cancel_delayed_work_sync(&drvdata->delayed_firmware_work);
730 sysfs_remove_group(&hid_dev->dev.kobj, &corsair_void_attr_group);
733 static int corsair_void_raw_event(struct hid_device *hid_dev,
734 struct hid_report *hid_report,
735 u8 *data, int size)
737 struct corsair_void_drvdata *drvdata = hid_get_drvdata(hid_dev);
738 bool was_connected = drvdata->connected;
740 /* Description of packets are documented at the top of this file */
741 if (hid_report->id == CORSAIR_VOID_STATUS_REPORT_ID) {
742 drvdata->mic_up = FIELD_GET(CORSAIR_VOID_MIC_MASK, data[2]);
743 drvdata->connected = (data[3] == CORSAIR_VOID_WIRELESS_CONNECTED) ||
744 drvdata->is_wired;
746 corsair_void_process_receiver(drvdata,
747 FIELD_GET(CORSAIR_VOID_CAPACITY_MASK, data[2]),
748 data[3], data[4]);
749 } else if (hid_report->id == CORSAIR_VOID_FIRMWARE_REPORT_ID) {
750 drvdata->fw_receiver_major = data[1];
751 drvdata->fw_receiver_minor = data[2];
752 drvdata->fw_headset_major = data[3];
753 drvdata->fw_headset_minor = data[4];
756 /* Handle wireless headset connect / disconnect */
757 if ((was_connected != drvdata->connected) && !drvdata->is_wired) {
758 if (drvdata->connected)
759 corsair_void_headset_connected(drvdata);
760 else
761 corsair_void_headset_disconnected(drvdata);
764 return 0;
767 static const struct hid_device_id corsair_void_devices[] = {
768 /* Corsair Void Wireless */
769 CORSAIR_VOID_WIRELESS_DEVICE(0x0a0c),
770 CORSAIR_VOID_WIRELESS_DEVICE(0x0a2b),
771 CORSAIR_VOID_WIRELESS_DEVICE(0x1b23),
772 CORSAIR_VOID_WIRELESS_DEVICE(0x1b25),
773 CORSAIR_VOID_WIRELESS_DEVICE(0x1b27),
775 /* Corsair Void USB */
776 CORSAIR_VOID_WIRED_DEVICE(0x0a0f),
777 CORSAIR_VOID_WIRED_DEVICE(0x1b1c),
778 CORSAIR_VOID_WIRED_DEVICE(0x1b29),
779 CORSAIR_VOID_WIRED_DEVICE(0x1b2a),
781 /* Corsair Void Surround */
782 CORSAIR_VOID_WIRED_DEVICE(0x0a30),
783 CORSAIR_VOID_WIRED_DEVICE(0x0a31),
785 /* Corsair Void Pro Wireless */
786 CORSAIR_VOID_WIRELESS_DEVICE(0x0a14),
787 CORSAIR_VOID_WIRELESS_DEVICE(0x0a16),
788 CORSAIR_VOID_WIRELESS_DEVICE(0x0a1a),
790 /* Corsair Void Pro USB */
791 CORSAIR_VOID_WIRED_DEVICE(0x0a17),
792 CORSAIR_VOID_WIRED_DEVICE(0x0a1d),
794 /* Corsair Void Pro Surround */
795 CORSAIR_VOID_WIRED_DEVICE(0x0a18),
796 CORSAIR_VOID_WIRED_DEVICE(0x0a1e),
797 CORSAIR_VOID_WIRED_DEVICE(0x0a1f),
799 /* Corsair Void Elite Wireless */
800 CORSAIR_VOID_WIRELESS_DEVICE(0x0a51),
801 CORSAIR_VOID_WIRELESS_DEVICE(0x0a55),
802 CORSAIR_VOID_WIRELESS_DEVICE(0x0a75),
804 /* Corsair Void Elite USB */
805 CORSAIR_VOID_WIRED_DEVICE(0x0a52),
806 CORSAIR_VOID_WIRED_DEVICE(0x0a56),
808 /* Corsair Void Elite Surround */
809 CORSAIR_VOID_WIRED_DEVICE(0x0a53),
810 CORSAIR_VOID_WIRED_DEVICE(0x0a57),
815 MODULE_DEVICE_TABLE(hid, corsair_void_devices);
817 static struct hid_driver corsair_void_driver = {
818 .name = "hid-corsair-void",
819 .id_table = corsair_void_devices,
820 .probe = corsair_void_probe,
821 .remove = corsair_void_remove,
822 .raw_event = corsair_void_raw_event,
825 module_hid_driver(corsair_void_driver);
827 MODULE_LICENSE("GPL");
828 MODULE_AUTHOR("Stuart Hayhurst <stuart.a.hayhurst@gmail.com>");
829 MODULE_DESCRIPTION("HID driver for Corsair Void headsets");