1 // SPDX-License-Identifier: GPL-2.0-or-later
2 /*
3 * HID driver for Corsair Void headsets
4 *
5 * Copyright (C) 2023-2024 Stuart Hayhurst
6 */
7
8 /* -------------------------------------------------------------------------- */
9 /* Receiver report information: (ID 100) */
10 /* -------------------------------------------------------------------------- */
11 /*
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
16 *
17 * 0: REPORT ID
18 * 100 for the battery packet
19 *
20 * 1: POWER BUTTON + (?)
21 * Largest bit is 1 when power button pressed
22 *
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
31 *
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
39 *
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
47 */
48 /* -------------------------------------------------------------------------- */
49
50 /* -------------------------------------------------------------------------- */
51 /* Receiver report information: (ID 102) */
52 /* -------------------------------------------------------------------------- */
53 /*
54 * When queried, the receiver responds with 5 bytes to describe the firmware
55 * The first byte is the ID, then 2 bytes for the receiver, then 2 for the headset
56 * The headset firmware version will be 0 if no headset is connected
57 *
58 * 0: Report ID
59 * 102 for the firmware packet
60 *
61 * 1: Receiver firmware major version
62 * Major version of the receiver's firmware
63 *
64 * 2: Receiver firmware minor version
65 * Minor version of the receiver's firmware
66 *
67 * 3: Headset firmware major version
68 * Major version of the headset's firmware
69 *
70 * 4: Headset firmware minor version
71 * Minor version of the headset's firmware
72 */
73 /* -------------------------------------------------------------------------- */
74
75 #include <linux/bitfield.h>
76 #include <linux/bitops.h>
77 #include <linux/device.h>
78 #include <linux/hid.h>
79 #include <linux/module.h>
80 #include <linux/power_supply.h>
81 #include <linux/usb.h>
82 #include <linux/workqueue.h>
83 #include <asm/byteorder.h>
84
85 #include "hid-ids.h"
86
87 #define CORSAIR_VOID_DEVICE(id, type) { HID_USB_DEVICE(USB_VENDOR_ID_CORSAIR, (id)), \
88 .driver_data = (type) }
89 #define CORSAIR_VOID_WIRELESS_DEVICE(id) CORSAIR_VOID_DEVICE((id), CORSAIR_VOID_WIRELESS)
90 #define CORSAIR_VOID_WIRED_DEVICE(id) CORSAIR_VOID_DEVICE((id), CORSAIR_VOID_WIRED)
91
92 #define CORSAIR_VOID_STATUS_REQUEST_ID 0xC9
93 #define CORSAIR_VOID_NOTIF_REQUEST_ID 0xCA
94 #define CORSAIR_VOID_SIDETONE_REQUEST_ID 0xFF
95 #define CORSAIR_VOID_STATUS_REPORT_ID 0x64
96 #define CORSAIR_VOID_FIRMWARE_REPORT_ID 0x66
97
98 #define CORSAIR_VOID_STATUS_REPORT_SIZE 5
99 #define CORSAIR_VOID_FIRMWARE_REPORT_SIZE 5
100
101 #define CORSAIR_VOID_USB_SIDETONE_REQUEST 0x1
102 #define CORSAIR_VOID_USB_SIDETONE_REQUEST_TYPE 0x21
103 #define CORSAIR_VOID_USB_SIDETONE_VALUE 0x200
104 #define CORSAIR_VOID_USB_SIDETONE_INDEX 0xB00
105
106 #define CORSAIR_VOID_MIC_MASK GENMASK(7, 7)
107 #define CORSAIR_VOID_CAPACITY_MASK GENMASK(6, 0)
108
109 #define CORSAIR_VOID_WIRELESS_CONNECTED 177
110
111 #define CORSAIR_VOID_SIDETONE_MAX_WIRELESS 55
112 #define CORSAIR_VOID_SIDETONE_MAX_WIRED 4096
113
114 enum {
115 CORSAIR_VOID_WIRELESS,
116 CORSAIR_VOID_WIRED,
117 };
118
119 enum {
120 CORSAIR_VOID_BATTERY_NORMAL = 1,
121 CORSAIR_VOID_BATTERY_LOW = 2,
122 CORSAIR_VOID_BATTERY_CRITICAL = 3,
123 CORSAIR_VOID_BATTERY_CHARGED = 4,
124 CORSAIR_VOID_BATTERY_CHARGING = 5,
125 };
126
127 enum {
128 CORSAIR_VOID_ADD_BATTERY = 0,
129 CORSAIR_VOID_REMOVE_BATTERY = 1,
130 CORSAIR_VOID_UPDATE_BATTERY = 2,
131 };
132
133 static enum power_supply_property corsair_void_battery_props[] = {
134 POWER_SUPPLY_PROP_STATUS,
135 POWER_SUPPLY_PROP_PRESENT,
136 POWER_SUPPLY_PROP_CAPACITY,
137 POWER_SUPPLY_PROP_CAPACITY_LEVEL,
138 POWER_SUPPLY_PROP_SCOPE,
139 POWER_SUPPLY_PROP_MODEL_NAME,
140 POWER_SUPPLY_PROP_MANUFACTURER,
141 };
142
143 struct corsair_void_battery_data {
144 int status;
145 bool present;
146 int capacity;
147 int capacity_level;
148 };
149
150 struct corsair_void_drvdata {
151 struct hid_device *hid_dev;
152 struct device *dev;
153
154 char *name;
155 bool is_wired;
156 unsigned int sidetone_max;
157
158 struct corsair_void_battery_data battery_data;
159 bool mic_up;
160 bool connected;
161 int fw_receiver_major;
162 int fw_receiver_minor;
163 int fw_headset_major;
164 int fw_headset_minor;
165
166 struct power_supply *battery;
167 struct power_supply_desc battery_desc;
168
169 struct delayed_work delayed_status_work;
170 struct delayed_work delayed_firmware_work;
171
172 unsigned long battery_work_flags;
173 struct work_struct battery_work;
174 };
175
176 /*
177 * Functions to process receiver data
178 */
179
corsair_void_set_wireless_status(struct corsair_void_drvdata * drvdata)180 static void corsair_void_set_wireless_status(struct corsair_void_drvdata *drvdata)
181 {
182 struct usb_interface *usb_if = to_usb_interface(drvdata->dev->parent);
183
184 if (drvdata->is_wired)
185 return;
186
187 usb_set_wireless_status(usb_if, drvdata->connected ?
188 USB_WIRELESS_STATUS_CONNECTED :
189 USB_WIRELESS_STATUS_DISCONNECTED);
190 }
191
corsair_void_set_unknown_batt(struct corsair_void_drvdata * drvdata)192 static void corsair_void_set_unknown_batt(struct corsair_void_drvdata *drvdata)
193 {
194 struct corsair_void_battery_data *battery_data = &drvdata->battery_data;
195
196 battery_data->status = POWER_SUPPLY_STATUS_UNKNOWN;
197 battery_data->present = false;
198 battery_data->capacity = 0;
199 battery_data->capacity_level = POWER_SUPPLY_CAPACITY_LEVEL_UNKNOWN;
200 }
201
202 /* Reset data that may change between wireless connections */
corsair_void_set_unknown_wireless_data(struct corsair_void_drvdata * drvdata)203 static void corsair_void_set_unknown_wireless_data(struct corsair_void_drvdata *drvdata)
204 {
205 /* Only 0 out headset, receiver is always known if relevant */
206 drvdata->fw_headset_major = 0;
207 drvdata->fw_headset_minor = 0;
208
209 drvdata->connected = false;
210 drvdata->mic_up = false;
211
212 corsair_void_set_wireless_status(drvdata);
213 }
214
corsair_void_process_receiver(struct corsair_void_drvdata * drvdata,int raw_battery_capacity,int raw_connection_status,int raw_battery_status)215 static void corsair_void_process_receiver(struct corsair_void_drvdata *drvdata,
216 int raw_battery_capacity,
217 int raw_connection_status,
218 int raw_battery_status)
219 {
220 struct corsair_void_battery_data *battery_data = &drvdata->battery_data;
221 struct corsair_void_battery_data orig_battery_data;
222
223 /* Save initial battery data, to compare later */
224 orig_battery_data = *battery_data;
225
226 /* Headset not connected, or it's wired */
227 if (raw_connection_status != CORSAIR_VOID_WIRELESS_CONNECTED)
228 goto unknown_battery;
229
230 /* Battery information unavailable */
231 if (raw_battery_status == 0)
232 goto unknown_battery;
233
234 /* Battery must be connected then */
235 battery_data->present = true;
236 battery_data->capacity_level = POWER_SUPPLY_CAPACITY_LEVEL_NORMAL;
237
238 /* Set battery status */
239 switch (raw_battery_status) {
240 case CORSAIR_VOID_BATTERY_NORMAL:
241 case CORSAIR_VOID_BATTERY_LOW:
242 case CORSAIR_VOID_BATTERY_CRITICAL:
243 battery_data->status = POWER_SUPPLY_STATUS_DISCHARGING;
244 if (raw_battery_status == CORSAIR_VOID_BATTERY_LOW)
245 battery_data->capacity_level = POWER_SUPPLY_CAPACITY_LEVEL_LOW;
246 else if (raw_battery_status == CORSAIR_VOID_BATTERY_CRITICAL)
247 battery_data->capacity_level = POWER_SUPPLY_CAPACITY_LEVEL_CRITICAL;
248
249 break;
250 case CORSAIR_VOID_BATTERY_CHARGED:
251 battery_data->status = POWER_SUPPLY_STATUS_FULL;
252 break;
253 case CORSAIR_VOID_BATTERY_CHARGING:
254 battery_data->status = POWER_SUPPLY_STATUS_CHARGING;
255 break;
256 default:
257 hid_warn(drvdata->hid_dev, "unknown battery status '%d'",
258 raw_battery_status);
259 goto unknown_battery;
260 break;
261 }
262
263 battery_data->capacity = raw_battery_capacity;
264 corsair_void_set_wireless_status(drvdata);
265
266 goto success;
267 unknown_battery:
268 corsair_void_set_unknown_batt(drvdata);
269 success:
270
271 /* Inform power supply if battery values changed */
272 if (memcmp(&orig_battery_data, battery_data, sizeof(*battery_data))) {
273 set_bit(CORSAIR_VOID_UPDATE_BATTERY,
274 &drvdata->battery_work_flags);
275 schedule_work(&drvdata->battery_work);
276 }
277 }
278
279 /*
280 * Functions to report stored data
281 */
282
corsair_void_battery_get_property(struct power_supply * psy,enum power_supply_property prop,union power_supply_propval * val)283 static int corsair_void_battery_get_property(struct power_supply *psy,
284 enum power_supply_property prop,
285 union power_supply_propval *val)
286 {
287 struct corsair_void_drvdata *drvdata = power_supply_get_drvdata(psy);
288
289 switch (prop) {
290 case POWER_SUPPLY_PROP_SCOPE:
291 val->intval = POWER_SUPPLY_SCOPE_DEVICE;
292 break;
293 case POWER_SUPPLY_PROP_MODEL_NAME:
294 if (!strncmp(drvdata->hid_dev->name, "Corsair ", 8))
295 val->strval = drvdata->hid_dev->name + 8;
296 else
297 val->strval = drvdata->hid_dev->name;
298 break;
299 case POWER_SUPPLY_PROP_MANUFACTURER:
300 val->strval = "Corsair";
301 break;
302 case POWER_SUPPLY_PROP_STATUS:
303 val->intval = drvdata->battery_data.status;
304 break;
305 case POWER_SUPPLY_PROP_PRESENT:
306 val->intval = drvdata->battery_data.present;
307 break;
308 case POWER_SUPPLY_PROP_CAPACITY:
309 val->intval = drvdata->battery_data.capacity;
310 break;
311 case POWER_SUPPLY_PROP_CAPACITY_LEVEL:
312 val->intval = drvdata->battery_data.capacity_level;
313 break;
314 default:
315 return -EINVAL;
316 }
317
318 return 0;
319 }
320
microphone_up_show(struct device * dev,struct device_attribute * attr,char * buf)321 static ssize_t microphone_up_show(struct device *dev,
322 struct device_attribute *attr, char *buf)
323 {
324 struct corsair_void_drvdata *drvdata = dev_get_drvdata(dev);
325
326 if (!drvdata->connected)
327 return -ENODEV;
328
329 return sysfs_emit(buf, "%d\n", drvdata->mic_up);
330 }
331
fw_version_receiver_show(struct device * dev,struct device_attribute * attr,char * buf)332 static ssize_t fw_version_receiver_show(struct device *dev,
333 struct device_attribute *attr,
334 char *buf)
335 {
336 struct corsair_void_drvdata *drvdata = dev_get_drvdata(dev);
337
338 if (drvdata->fw_receiver_major == 0 && drvdata->fw_receiver_minor == 0)
339 return -ENODATA;
340
341 return sysfs_emit(buf, "%d.%02d\n", drvdata->fw_receiver_major,
342 drvdata->fw_receiver_minor);
343 }
344
345
fw_version_headset_show(struct device * dev,struct device_attribute * attr,char * buf)346 static ssize_t fw_version_headset_show(struct device *dev,
347 struct device_attribute *attr,
348 char *buf)
349 {
350 struct corsair_void_drvdata *drvdata = dev_get_drvdata(dev);
351
352 if (drvdata->fw_headset_major == 0 && drvdata->fw_headset_minor == 0)
353 return -ENODATA;
354
355 return sysfs_emit(buf, "%d.%02d\n", drvdata->fw_headset_major,
356 drvdata->fw_headset_minor);
357 }
358
sidetone_max_show(struct device * dev,struct device_attribute * attr,char * buf)359 static ssize_t sidetone_max_show(struct device *dev,
360 struct device_attribute *attr,
361 char *buf)
362 {
363 struct corsair_void_drvdata *drvdata = dev_get_drvdata(dev);
364
365 return sysfs_emit(buf, "%d\n", drvdata->sidetone_max);
366 }
367
368 /*
369 * Functions to send data to headset
370 */
371
send_alert_store(struct device * dev,struct device_attribute * attr,const char * buf,size_t count)372 static ssize_t send_alert_store(struct device *dev,
373 struct device_attribute *attr,
374 const char *buf, size_t count)
375 {
376 struct corsair_void_drvdata *drvdata = dev_get_drvdata(dev);
377 struct hid_device *hid_dev = drvdata->hid_dev;
378 unsigned char alert_id;
379 unsigned char *send_buf __free(kfree) = NULL;
380 int ret;
381
382 if (!drvdata->connected || drvdata->is_wired)
383 return -ENODEV;
384
385 /* Only accept 0 or 1 for alert ID */
386 if (kstrtou8(buf, 10, &alert_id) || alert_id >= 2)
387 return -EINVAL;
388
389 send_buf = kmalloc(3, GFP_KERNEL);
390 if (!send_buf)
391 return -ENOMEM;
392
393 /* Packet format to send alert with ID alert_id */
394 send_buf[0] = CORSAIR_VOID_NOTIF_REQUEST_ID;
395 send_buf[1] = 0x02;
396 send_buf[2] = alert_id;
397
398 ret = hid_hw_raw_request(hid_dev, CORSAIR_VOID_NOTIF_REQUEST_ID,
399 send_buf, 3, HID_OUTPUT_REPORT,
400 HID_REQ_SET_REPORT);
401 if (ret < 0)
402 hid_warn(hid_dev, "failed to send alert request (reason: %d)",
403 ret);
404 else
405 ret = count;
406
407 return ret;
408 }
409
corsair_void_set_sidetone_wired(struct device * dev,const char * buf,unsigned int sidetone)410 static int corsair_void_set_sidetone_wired(struct device *dev, const char *buf,
411 unsigned int sidetone)
412 {
413 struct usb_interface *usb_if = to_usb_interface(dev->parent);
414 struct usb_device *usb_dev = interface_to_usbdev(usb_if);
415
416 /* Packet format to set sidetone for wired headsets */
417 __le16 sidetone_le = cpu_to_le16(sidetone);
418
419 return usb_control_msg_send(usb_dev, 0,
420 CORSAIR_VOID_USB_SIDETONE_REQUEST,
421 CORSAIR_VOID_USB_SIDETONE_REQUEST_TYPE,
422 CORSAIR_VOID_USB_SIDETONE_VALUE,
423 CORSAIR_VOID_USB_SIDETONE_INDEX,
424 &sidetone_le, 2, USB_CTRL_SET_TIMEOUT,
425 GFP_KERNEL);
426 }
427
corsair_void_set_sidetone_wireless(struct device * dev,const char * buf,unsigned char sidetone)428 static int corsair_void_set_sidetone_wireless(struct device *dev,
429 const char *buf,
430 unsigned char sidetone)
431 {
432 struct corsair_void_drvdata *drvdata = dev_get_drvdata(dev);
433 struct hid_device *hid_dev = drvdata->hid_dev;
434 unsigned char *send_buf __free(kfree) = NULL;
435
436 send_buf = kmalloc(12, GFP_KERNEL);
437 if (!send_buf)
438 return -ENOMEM;
439
440 /* Packet format to set sidetone for wireless headsets */
441 send_buf[0] = CORSAIR_VOID_SIDETONE_REQUEST_ID;
442 send_buf[1] = 0x0B;
443 send_buf[2] = 0x00;
444 send_buf[3] = 0xFF;
445 send_buf[4] = 0x04;
446 send_buf[5] = 0x0E;
447 send_buf[6] = 0xFF;
448 send_buf[7] = 0x05;
449 send_buf[8] = 0x01;
450 send_buf[9] = 0x04;
451 send_buf[10] = 0x00;
452 send_buf[11] = sidetone + 200;
453
454 return hid_hw_raw_request(hid_dev, CORSAIR_VOID_SIDETONE_REQUEST_ID,
455 send_buf, 12, HID_FEATURE_REPORT,
456 HID_REQ_SET_REPORT);
457 }
458
set_sidetone_store(struct device * dev,struct device_attribute * attr,const char * buf,size_t count)459 static ssize_t set_sidetone_store(struct device *dev,
460 struct device_attribute *attr,
461 const char *buf, size_t count)
462 {
463 struct corsair_void_drvdata *drvdata = dev_get_drvdata(dev);
464 struct hid_device *hid_dev = drvdata->hid_dev;
465 unsigned int sidetone;
466 int ret;
467
468 if (!drvdata->connected)
469 return -ENODEV;
470
471 /* sidetone must be between 0 and drvdata->sidetone_max inclusive */
472 if (kstrtouint(buf, 10, &sidetone) || sidetone > drvdata->sidetone_max)
473 return -EINVAL;
474
475 if (drvdata->is_wired)
476 ret = corsair_void_set_sidetone_wired(dev, buf, sidetone);
477 else
478 ret = corsair_void_set_sidetone_wireless(dev, buf, sidetone);
479
480 if (ret < 0)
481 hid_warn(hid_dev, "failed to send sidetone (reason: %d)", ret);
482 else
483 ret = count;
484
485 return ret;
486 }
487
corsair_void_request_status(struct hid_device * hid_dev,int id)488 static int corsair_void_request_status(struct hid_device *hid_dev, int id)
489 {
490 unsigned char *send_buf __free(kfree) = NULL;
491
492 send_buf = kmalloc(2, GFP_KERNEL);
493 if (!send_buf)
494 return -ENOMEM;
495
496 /* Packet format to request data item (status / firmware) refresh */
497 send_buf[0] = CORSAIR_VOID_STATUS_REQUEST_ID;
498 send_buf[1] = id;
499
500 /* Send request for data refresh */
501 return hid_hw_raw_request(hid_dev, CORSAIR_VOID_STATUS_REQUEST_ID,
502 send_buf, 2, HID_OUTPUT_REPORT,
503 HID_REQ_SET_REPORT);
504 }
505
506 /*
507 * Headset connect / disconnect handlers and work handlers
508 */
509
corsair_void_status_work_handler(struct work_struct * work)510 static void corsair_void_status_work_handler(struct work_struct *work)
511 {
512 struct corsair_void_drvdata *drvdata;
513 struct delayed_work *delayed_work;
514 int battery_ret;
515
516 delayed_work = to_delayed_work(work);
517 drvdata = container_of(delayed_work, struct corsair_void_drvdata,
518 delayed_status_work);
519
520 battery_ret = corsair_void_request_status(drvdata->hid_dev,
521 CORSAIR_VOID_STATUS_REPORT_ID);
522 if (battery_ret < 0) {
523 hid_warn(drvdata->hid_dev,
524 "failed to request battery (reason: %d)", battery_ret);
525 }
526 }
527
corsair_void_firmware_work_handler(struct work_struct * work)528 static void corsair_void_firmware_work_handler(struct work_struct *work)
529 {
530 struct corsair_void_drvdata *drvdata;
531 struct delayed_work *delayed_work;
532 int firmware_ret;
533
534 delayed_work = to_delayed_work(work);
535 drvdata = container_of(delayed_work, struct corsair_void_drvdata,
536 delayed_firmware_work);
537
538 firmware_ret = corsair_void_request_status(drvdata->hid_dev,
539 CORSAIR_VOID_FIRMWARE_REPORT_ID);
540 if (firmware_ret < 0) {
541 hid_warn(drvdata->hid_dev,
542 "failed to request firmware (reason: %d)", firmware_ret);
543 }
544
545 }
546
corsair_void_add_battery(struct corsair_void_drvdata * drvdata)547 static void corsair_void_add_battery(struct corsair_void_drvdata *drvdata)
548 {
549 struct power_supply_config psy_cfg = {};
550 struct power_supply *new_supply;
551
552 if (drvdata->battery)
553 return;
554
555 psy_cfg.drv_data = drvdata;
556 new_supply = power_supply_register(drvdata->dev,
557 &drvdata->battery_desc,
558 &psy_cfg);
559
560 if (IS_ERR(new_supply)) {
561 hid_err(drvdata->hid_dev,
562 "failed to register battery '%s' (reason: %pe)\n",
563 drvdata->battery_desc.name, new_supply);
564 return;
565 }
566
567 if (power_supply_powers(new_supply, drvdata->dev)) {
568 power_supply_unregister(new_supply);
569 return;
570 }
571
572 drvdata->battery = new_supply;
573 }
574
corsair_void_battery_work_handler(struct work_struct * work)575 static void corsair_void_battery_work_handler(struct work_struct *work)
576 {
577 struct corsair_void_drvdata *drvdata = container_of(work,
578 struct corsair_void_drvdata, battery_work);
579
580 bool add_battery = test_and_clear_bit(CORSAIR_VOID_ADD_BATTERY,
581 &drvdata->battery_work_flags);
582 bool remove_battery = test_and_clear_bit(CORSAIR_VOID_REMOVE_BATTERY,
583 &drvdata->battery_work_flags);
584 bool update_battery = test_and_clear_bit(CORSAIR_VOID_UPDATE_BATTERY,
585 &drvdata->battery_work_flags);
586
587 if (add_battery && !remove_battery) {
588 corsair_void_add_battery(drvdata);
589 } else if (remove_battery && !add_battery && drvdata->battery) {
590 power_supply_unregister(drvdata->battery);
591 drvdata->battery = NULL;
592 }
593
594 if (update_battery && drvdata->battery)
595 power_supply_changed(drvdata->battery);
596
597 }
598
corsair_void_headset_connected(struct corsair_void_drvdata * drvdata)599 static void corsair_void_headset_connected(struct corsair_void_drvdata *drvdata)
600 {
601 set_bit(CORSAIR_VOID_ADD_BATTERY, &drvdata->battery_work_flags);
602 schedule_work(&drvdata->battery_work);
603 schedule_delayed_work(&drvdata->delayed_firmware_work,
604 msecs_to_jiffies(100));
605 }
606
corsair_void_headset_disconnected(struct corsair_void_drvdata * drvdata)607 static void corsair_void_headset_disconnected(struct corsair_void_drvdata *drvdata)
608 {
609 set_bit(CORSAIR_VOID_REMOVE_BATTERY, &drvdata->battery_work_flags);
610 schedule_work(&drvdata->battery_work);
611
612 corsair_void_set_unknown_wireless_data(drvdata);
613 corsair_void_set_unknown_batt(drvdata);
614 }
615
616 /*
617 * Driver setup, probing and HID event handling
618 */
619
620 static DEVICE_ATTR_RO(fw_version_receiver);
621 static DEVICE_ATTR_RO(fw_version_headset);
622 static DEVICE_ATTR_RO(microphone_up);
623 static DEVICE_ATTR_RO(sidetone_max);
624
625 static DEVICE_ATTR_WO(send_alert);
626 static DEVICE_ATTR_WO(set_sidetone);
627
628 static struct attribute *corsair_void_attrs[] = {
629 &dev_attr_fw_version_receiver.attr,
630 &dev_attr_fw_version_headset.attr,
631 &dev_attr_microphone_up.attr,
632 &dev_attr_send_alert.attr,
633 &dev_attr_set_sidetone.attr,
634 &dev_attr_sidetone_max.attr,
635 NULL,
636 };
637
638 static const struct attribute_group corsair_void_attr_group = {
639 .attrs = corsair_void_attrs,
640 };
641
corsair_void_probe(struct hid_device * hid_dev,const struct hid_device_id * hid_id)642 static int corsair_void_probe(struct hid_device *hid_dev,
643 const struct hid_device_id *hid_id)
644 {
645 int ret;
646 struct corsair_void_drvdata *drvdata;
647 char *name;
648
649 if (!hid_is_usb(hid_dev))
650 return -EINVAL;
651
652 drvdata = devm_kzalloc(&hid_dev->dev, sizeof(*drvdata),
653 GFP_KERNEL);
654 if (!drvdata)
655 return -ENOMEM;
656
657 hid_set_drvdata(hid_dev, drvdata);
658 dev_set_drvdata(&hid_dev->dev, drvdata);
659
660 drvdata->dev = &hid_dev->dev;
661 drvdata->hid_dev = hid_dev;
662 drvdata->is_wired = hid_id->driver_data == CORSAIR_VOID_WIRED;
663
664 drvdata->sidetone_max = CORSAIR_VOID_SIDETONE_MAX_WIRELESS;
665 if (drvdata->is_wired)
666 drvdata->sidetone_max = CORSAIR_VOID_SIDETONE_MAX_WIRED;
667
668 /* Set initial values for no wireless headset attached */
669 /* If a headset is attached, it'll be prompted later */
670 corsair_void_set_unknown_wireless_data(drvdata);
671 corsair_void_set_unknown_batt(drvdata);
672
673 /* Receiver version won't be reset after init */
674 /* Headset version already set via set_unknown_wireless_data */
675 drvdata->fw_receiver_major = 0;
676 drvdata->fw_receiver_minor = 0;
677
678 ret = hid_parse(hid_dev);
679 if (ret) {
680 hid_err(hid_dev, "parse failed (reason: %d)\n", ret);
681 return ret;
682 }
683
684 name = devm_kasprintf(drvdata->dev, GFP_KERNEL,
685 "corsair-void-%d-battery", hid_dev->id);
686 if (!name)
687 return -ENOMEM;
688
689 drvdata->battery_desc.name = name;
690 drvdata->battery_desc.type = POWER_SUPPLY_TYPE_BATTERY;
691 drvdata->battery_desc.properties = corsair_void_battery_props;
692 drvdata->battery_desc.num_properties = ARRAY_SIZE(corsair_void_battery_props);
693 drvdata->battery_desc.get_property = corsair_void_battery_get_property;
694
695 drvdata->battery = NULL;
696 INIT_WORK(&drvdata->battery_work, corsair_void_battery_work_handler);
697
698 ret = sysfs_create_group(&hid_dev->dev.kobj, &corsair_void_attr_group);
699 if (ret)
700 return ret;
701
702 /* Any failures after here will need to call hid_hw_stop */
703 ret = hid_hw_start(hid_dev, HID_CONNECT_DEFAULT);
704 if (ret) {
705 hid_err(hid_dev, "hid_hw_start failed (reason: %d)\n", ret);
706 goto failed_after_sysfs;
707 }
708
709 /* Refresh battery data, in case wireless headset is already connected */
710 INIT_DELAYED_WORK(&drvdata->delayed_status_work,
711 corsair_void_status_work_handler);
712 schedule_delayed_work(&drvdata->delayed_status_work,
713 msecs_to_jiffies(100));
714
715 /* Refresh firmware versions */
716 INIT_DELAYED_WORK(&drvdata->delayed_firmware_work,
717 corsair_void_firmware_work_handler);
718 schedule_delayed_work(&drvdata->delayed_firmware_work,
719 msecs_to_jiffies(100));
720
721 return 0;
722
723 failed_after_sysfs:
724 sysfs_remove_group(&hid_dev->dev.kobj, &corsair_void_attr_group);
725 return ret;
726 }
727
corsair_void_remove(struct hid_device * hid_dev)728 static void corsair_void_remove(struct hid_device *hid_dev)
729 {
730 struct corsair_void_drvdata *drvdata = hid_get_drvdata(hid_dev);
731
732 hid_hw_stop(hid_dev);
733 cancel_work_sync(&drvdata->battery_work);
734 if (drvdata->battery)
735 power_supply_unregister(drvdata->battery);
736
737 cancel_delayed_work_sync(&drvdata->delayed_status_work);
738 cancel_delayed_work_sync(&drvdata->delayed_firmware_work);
739 sysfs_remove_group(&hid_dev->dev.kobj, &corsair_void_attr_group);
740 }
741
corsair_void_raw_event(struct hid_device * hid_dev,struct hid_report * hid_report,u8 * data,int size)742 static int corsair_void_raw_event(struct hid_device *hid_dev,
743 struct hid_report *hid_report,
744 u8 *data, int size)
745 {
746 struct corsair_void_drvdata *drvdata = hid_get_drvdata(hid_dev);
747 bool was_connected = drvdata->connected;
748
749 /* Description of packets are documented at the top of this file */
750 if (hid_report->id == CORSAIR_VOID_STATUS_REPORT_ID) {
751 if (size < CORSAIR_VOID_STATUS_REPORT_SIZE) {
752 hid_warn_ratelimited(hid_dev,
753 "unexpected status report of size %d",
754 size);
755 return 1;
756 }
757
758 drvdata->mic_up = FIELD_GET(CORSAIR_VOID_MIC_MASK, data[2]);
759 drvdata->connected = (data[3] == CORSAIR_VOID_WIRELESS_CONNECTED) ||
760 drvdata->is_wired;
761
762 corsair_void_process_receiver(drvdata,
763 FIELD_GET(CORSAIR_VOID_CAPACITY_MASK, data[2]),
764 data[3], data[4]);
765 } else if (hid_report->id == CORSAIR_VOID_FIRMWARE_REPORT_ID) {
766 if (size < CORSAIR_VOID_FIRMWARE_REPORT_SIZE) {
767 hid_warn_ratelimited(hid_dev,
768 "unexpected firmware report of size %d",
769 size);
770 return 1;
771 }
772
773 drvdata->fw_receiver_major = data[1];
774 drvdata->fw_receiver_minor = data[2];
775 drvdata->fw_headset_major = data[3];
776 drvdata->fw_headset_minor = data[4];
777 }
778
779 /* Handle wireless headset connect / disconnect */
780 if ((was_connected != drvdata->connected) && !drvdata->is_wired) {
781 if (drvdata->connected)
782 corsair_void_headset_connected(drvdata);
783 else
784 corsair_void_headset_disconnected(drvdata);
785 }
786
787 return 0;
788 }
789
790 static const struct hid_device_id corsair_void_devices[] = {
791 /* Corsair Void Wireless */
792 CORSAIR_VOID_WIRELESS_DEVICE(0x0a0c),
793 CORSAIR_VOID_WIRELESS_DEVICE(0x0a2b),
794 CORSAIR_VOID_WIRELESS_DEVICE(0x1b23),
795 CORSAIR_VOID_WIRELESS_DEVICE(0x1b25),
796 CORSAIR_VOID_WIRELESS_DEVICE(0x1b27),
797
798 /* Corsair Void USB */
799 CORSAIR_VOID_WIRED_DEVICE(0x0a0f),
800 CORSAIR_VOID_WIRED_DEVICE(0x1b1c),
801 CORSAIR_VOID_WIRED_DEVICE(0x1b29),
802 CORSAIR_VOID_WIRED_DEVICE(0x1b2a),
803
804 /* Corsair Void Surround */
805 CORSAIR_VOID_WIRED_DEVICE(0x0a30),
806 CORSAIR_VOID_WIRED_DEVICE(0x0a31),
807
808 /* Corsair Void Pro Wireless */
809 CORSAIR_VOID_WIRELESS_DEVICE(0x0a14),
810 CORSAIR_VOID_WIRELESS_DEVICE(0x0a16),
811 CORSAIR_VOID_WIRELESS_DEVICE(0x0a1a),
812
813 /* Corsair Void Pro USB */
814 CORSAIR_VOID_WIRED_DEVICE(0x0a17),
815 CORSAIR_VOID_WIRED_DEVICE(0x0a1d),
816
817 /* Corsair Void Pro Surround */
818 CORSAIR_VOID_WIRED_DEVICE(0x0a18),
819 CORSAIR_VOID_WIRED_DEVICE(0x0a1e),
820 CORSAIR_VOID_WIRED_DEVICE(0x0a1f),
821
822 /* Corsair Void Elite Wireless */
823 CORSAIR_VOID_WIRELESS_DEVICE(0x0a51),
824 CORSAIR_VOID_WIRELESS_DEVICE(0x0a55),
825 CORSAIR_VOID_WIRELESS_DEVICE(0x0a75),
826
827 /* Corsair Void Elite USB */
828 CORSAIR_VOID_WIRED_DEVICE(0x0a52),
829 CORSAIR_VOID_WIRED_DEVICE(0x0a56),
830
831 /* Corsair Void Elite Surround */
832 CORSAIR_VOID_WIRED_DEVICE(0x0a53),
833 CORSAIR_VOID_WIRED_DEVICE(0x0a57),
834
835 {}
836 };
837
838 MODULE_DEVICE_TABLE(hid, corsair_void_devices);
839
840 static struct hid_driver corsair_void_driver = {
841 .name = "hid-corsair-void",
842 .id_table = corsair_void_devices,
843 .probe = corsair_void_probe,
844 .remove = corsair_void_remove,
845 .raw_event = corsair_void_raw_event,
846 };
847
848 module_hid_driver(corsair_void_driver);
849
850 MODULE_LICENSE("GPL");
851 MODULE_AUTHOR("Stuart Hayhurst <stuart.a.hayhurst@gmail.com>");
852 MODULE_DESCRIPTION("HID driver for Corsair Void headsets");
853