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