Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ talking. This differs from a simple loopback via PulseAudio as you won't have an

## Supported Headsets

- HyperX Cloud Alpha Wireless
- Battery, Inactive time, Sidetone, Voice Prompts (only tested on Linux)
- HyperX Cloud Flight Wireless
- Battery only (only tested on Linux)
- Corsair **Void** (Every void-version*, regardless whether Elite, Pro, HS70 Wireless)
Expand Down
4 changes: 3 additions & 1 deletion src/device_registry.c
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
#include "device_registry.h"

#include "devices/corsair_void.h"
#include "devices/hyperx_calphaw.h"
#include "devices/hyperx_cflight.h"
#include "devices/logitech_g430.h"
#include "devices/logitech_g432.h"
Expand All @@ -21,7 +22,7 @@

#include <string.h>

#define NUMDEVICES 18
#define NUMDEVICES 19

// array of pointers to device
static struct device*(devicelist[NUMDEVICES]);
Expand All @@ -46,6 +47,7 @@ void init_devices()
cflight_init(&devicelist[15]);
g535_init(&devicelist[16]);
arctis_nova_7_init(&devicelist[17]);
calphaw_init(&devicelist[18]);
}

int get_device(struct device* device_found, uint16_t idVendor, uint16_t idProduct)
Expand Down
2 changes: 2 additions & 0 deletions src/devices/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
set(SOURCE_FILES ${SOURCE_FILES}
${CMAKE_CURRENT_SOURCE_DIR}/hyperx_cflight.c
${CMAKE_CURRENT_SOURCE_DIR}/hyperx_cflight.h
${CMAKE_CURRENT_SOURCE_DIR}/hyperx_calphaw.c
${CMAKE_CURRENT_SOURCE_DIR}/hyperx_calphaw.h
${CMAKE_CURRENT_SOURCE_DIR}/corsair_void.c
${CMAKE_CURRENT_SOURCE_DIR}/corsair_void.h
${CMAKE_CURRENT_SOURCE_DIR}/logitech_g930.c
Expand Down
160 changes: 160 additions & 0 deletions src/devices/hyperx_calphaw.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,160 @@
// Based on hyperx_cflight.c
// Most requests taken off https://github.com/lehnification/hyperx-cloud-alpha-wireless-tray unless stated otherwise

// This file is part of HeadsetControl.

#include "../device.h"
#include "../utility.h"

#include <hidapi.h>
#include <math.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

// #define DEBUG

#ifdef DEBUG
#include <stdio.h>
#endif

static struct device device_calphaw;

// timeout for hidapi requests in milliseconds
#define TIMEOUT 2000

#define VENDOR_HP 0x03f0 // cloud alpha sold with HP, Inc vendor id
#define ID_CALPHAW 0x098d

static const uint16_t PRODUCT_IDS[] = { ID_CALPHAW };

static int calphaw_request_battery(hid_device* device_handle);
static int calphaw_send_inactive_time(hid_device* device_handle, uint8_t num);
static int calphaw_send_sidetone(hid_device* device_handle, uint8_t num);
static int calphaw_switch_voice_prompts(hid_device* device_handle, uint8_t on);

void calphaw_init(struct device** device)
{
device_calphaw.idVendor = VENDOR_HP;
device_calphaw.idProductsSupported = PRODUCT_IDS;
device_calphaw.numIdProducts = sizeof(PRODUCT_IDS) / sizeof(PRODUCT_IDS[0]);
strncpy(device_calphaw.device_name, "HyperX Cloud Alpha Wireless", sizeof(device_calphaw.device_name));

device_calphaw.capabilities = B(CAP_BATTERY_STATUS) | B(CAP_INACTIVE_TIME) | B(CAP_SIDETONE) | B(CAP_VOICE_PROMPTS);
device_calphaw.request_battery = &calphaw_request_battery;
device_calphaw.send_inactive_time = &calphaw_send_inactive_time;
device_calphaw.send_sidetone = &calphaw_send_sidetone;
device_calphaw.switch_voice_prompts = &calphaw_switch_voice_prompts;

*device = &device_calphaw;
}

int calphaw_request_charge(hid_device* device_handle)
{
int r = 0;
// request charge info
uint8_t data_request[31] = { 0x21, 0xbb, 0x0c };

r = hid_write(device_handle, data_request, sizeof(data_request) / sizeof(data_request[0]));
if (r < 0)
return r;

uint8_t data_read[31];
r = hid_read_timeout(device_handle, data_read, 20, TIMEOUT);
if (r < 0)
return r;
if (r == 0) // timeout
return HSC_ERROR;

if (r == 0xf || r == 0x14) {
if (data_read[3] == 0x01)
return 1;

return 0;
}

return HSC_ERROR;
}

static int calphaw_request_battery(hid_device* device_handle)
{
if (calphaw_request_charge(device_handle) == 1)
return BATTERY_CHARGING;

int r = 0;
// request battery info
uint8_t data_request[31] = { 0x21, 0xbb, 0x0b }; // Data request reverse engineered by using USBPcap and Wireshark by @InfiniteLoop

r = hid_write(device_handle, data_request, sizeof(data_request) / sizeof(data_request[0]));
if (r < 0)
return r;

uint8_t data_read[31];
r = hid_read_timeout(device_handle, data_read, 20, TIMEOUT);
if (r < 0)
return r;
if (r == 0) // timeout
return HSC_ERROR;

if (r == 0xf || r == 0x14) {

#ifdef DEBUG
printf("calphaw_request_battery data_read 3: 0x%02x 4: 0x%02x 5: 0x%02x\n", data_read[3], data_read[4], data_read[5]);
#endif

uint8_t batteryPercentage = data_read[3];

#ifdef DEBUG
uint32_t batteryVoltage = (data_read[4] << 8) | data_read[5]; // best assumtion this is voltage in mV
printf("batteryVoltage: %d mV\n", batteryVoltage);
#endif
return (int)(batteryPercentage);
}

return HSC_ERROR;
}

static int calphaw_send_inactive_time(hid_device* device_handle, uint8_t num)
{
// taken from steelseries_arctis_7.c

uint8_t data_request[31] = { 0x21, 0xbb, 0x12, num };

int ret = hid_write(device_handle, data_request, sizeof(data_request) / sizeof(data_request[0]));

return ret;
}

int calphaw_toggle_sidetone(hid_device* device_handle, uint8_t num)
{
uint8_t data_request[31] = { 0x21, 0xbb, 0x10, num };

return hid_write(device_handle, data_request, sizeof(data_request) / sizeof(data_request[0]));
;
}

static int calphaw_send_sidetone(hid_device* device_handle, uint8_t num)
{
if (num == 0) {
return calphaw_toggle_sidetone(device_handle, 0);
} else {
calphaw_toggle_sidetone(device_handle, 1);
uint8_t data_request[31] = { 0x21, 0xbb, 0x11, num };

int ret = hid_write(device_handle, data_request, sizeof(data_request) / sizeof(data_request[0]));

// looks to be a binary setting but I'm not sure

return ret;
}

return HSC_ERROR;
}

static int calphaw_switch_voice_prompts(hid_device* device_handle, uint8_t on)
{
uint8_t data_request[31] = { 0x21, 0xbb, 0x13, on };

return hid_write(device_handle, data_request, sizeof(data_request) / sizeof(data_request[0]));
;
}
1 change: 1 addition & 0 deletions src/devices/hyperx_calphaw.h
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
void calphaw_init(struct device** device);
2 changes: 1 addition & 1 deletion src/devices/hyperx_cflight.c
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
#include <string.h>
#include <unistd.h>

//#define DEBUG
// #define DEBUG

#ifdef DEBUG
#include <stdio.h>
Expand Down
4 changes: 2 additions & 2 deletions src/devices/logitech_g430.c
Original file line number Diff line number Diff line change
Expand Up @@ -41,9 +41,9 @@ static int g430_send_sidetone(hid_device* device_handle, uint8_t num)
0x081E, 0x1E1E, 0x341E, 0x4A1E, 0x601E, 0x761E, 0x8C1E, 0xA21E, 0xB81E, 0xCE1E, 0xE41E, 0xFA1E, 0x101F, 0x261F,
0x3C1F, 0x521F, 0x681F };*/

//unsigned char data[2] = { volumes[num] >> 8, volumes[num] & 0xFF };
// unsigned char data[2] = { volumes[num] >> 8, volumes[num] & 0xFF };

//int size = libusb_control_transfer(device_handle, LIBUSB_DT_HID, LIBUSB_REQUEST_CLEAR_FEATURE, 0x0201, 0x0600, data, 0x2, 1000);
// int size = libusb_control_transfer(device_handle, LIBUSB_DT_HID, LIBUSB_REQUEST_CLEAR_FEATURE, 0x0201, 0x0600, data, 0x2, 1000);

// unused parameter
(void)(device_handle);
Expand Down
2 changes: 1 addition & 1 deletion src/devices/logitech_g633_g933_935.c
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ static int g933_935_request_battery(hid_device* device_handle)
if (r == 0)
return HSC_READ_TIMEOUT;

//6th byte is state; 0x1 for idle, 0x3 for charging
// 6th byte is state; 0x1 for idle, 0x3 for charging
uint8_t state = data_read[6];
if (state == 0x03)
return BATTERY_CHARGING;
Expand Down
2 changes: 1 addition & 1 deletion src/devices/roccat_elo_7_1_air.c
Original file line number Diff line number Diff line change
Expand Up @@ -113,5 +113,5 @@ static int elo71Air_send_inactive_time(hid_device* hid_device, uint8_t num)
uint8_t response[64];

return send_receive(hid_device, cmd, sizeof(cmd), response);
//meaning of response of headset is not clear yet, fetch & ignore it for now
// meaning of response of headset is not clear yet, fetch & ignore it for now
}