Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
torvalds
GitHub Repository: torvalds/linux
Path: blob/master/drivers/hid/hid-corsair-void.c
26278 views
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
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
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 */
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
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
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
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
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
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
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
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
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
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
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
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
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 = to_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
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 = to_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
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
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
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
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
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
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
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 <[email protected]>");
833
MODULE_DESCRIPTION("HID driver for Corsair Void headsets");
834
835