Skip to content

Commit c850a50

Browse files
lukasgr90jastBytesSapd
authored
Added particular support for the Roccat Elo 7.1 Air. (#147)
* Added support to switch on / off the lights of the Roccat Elo 7.1 Air. Signed-off-by: Lukas Grundmann <supportmhh@lukas-grundmann.de> * Fixed possible numeric overflow. * Add udev rules for roccat elo air * Remove unnecessary clearing of array * Add ROCCAT Elo 7.1 Air capabilities * Roccat Elo 7.1 Air: Improvements regarding C type handling (optimized initialization / passing of local variables & use of portable 8bit/byte data type for data arrays). Also code to set inactive time was added. * Added Elo 7.1 Air current state to README * Fixed whitespaces and format and numIdProducts Co-authored-by: Lukas Grundmann <supportmhh@lukas-grundmann.de> Co-authored-by: Jan Steffen <jan.steffen@finleap.com> Co-authored-by: Denis Arnst <sapd@sapd.eu>
1 parent 972ef78 commit c850a50

7 files changed

Lines changed: 130 additions & 3 deletions

File tree

README.md

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,8 @@ talking. This differs from a simple loopback via PulseAudio as you won't have an
3333
- Sidetone
3434
- Logitech Zone Wired
3535
- Sidetone, Voice prompts, Rotate to mute
36+
- Roccat Elo 7.1 Air
37+
- LED on/off, Inactive time (Note for Linux: Sidetone is handled by sound driver => use AlsaMixer)
3638

3739
For non-supported headsets on Linux: There is a chance that you can set the sidetone via AlsaMixer
3840

@@ -103,9 +105,11 @@ make
103105
```
104106

105107
If you want to be able to call HeadsetControl from every folder type:
108+
106109
```bash
107110
make install
108111
```
112+
109113
This will copy the binary to a folder globally accessible via path.
110114

111115
### Access without root
@@ -138,16 +142,18 @@ Following options don't work on all devices yet:
138142
`headsetcontrol -m` retrieves the current chat-mix-dial level setting.
139143

140144
### Third Party
145+
141146
The following additional software can be used to enable control via a GUI
142147

143148
#### Linux
149+
144150
[headsetcontrol-notifcationd](https://github.com/Manawyrm/headsetcontrol-notificationd) provides notifications on the battery status of connected headsets (PHP based)
145151

146152
[headset-charge-indicator](https://github.com/centic9/headset-charge-indicator/) adds a system tray icon, displaying the current amount of battery. Also provides controls via the icon's menu (Python based)
147153

148154
#### Windows
149-
[HeadsetControl-SystemTray](https://github.com/zampierilucas/HeadsetControl-SystemTray) adds a system tray icon, displaying the current amount of battery. (Python based)
150155

156+
[HeadsetControl-SystemTray](https://github.com/zampierilucas/HeadsetControl-SystemTray) adds a system tray icon, displaying the current amount of battery. (Python based)
151157

152158
## Development
153159

src/device.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
#define VENDOR_CORSAIR 0x1b1c
77
#define VENDOR_LOGITECH 0x046d
88
#define VENDOR_STEELSERIES 0x1038
9+
#define VENDOR_ROCCAT 0x1e7d
910

1011
/** @brief A list of all features settable/queryable
1112
* for headsets

src/device_registry.c

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,14 +8,15 @@
88
#include "devices/logitech_g930.h"
99
#include "devices/logitech_gpro.h"
1010
#include "devices/logitech_zone_wired.h"
11+
#include "devices/roccat_elo_7_1_air.h"
1112
#include "devices/steelseries_arctis_1.h"
1213
#include "devices/steelseries_arctis_1_xbox.h"
1314
#include "devices/steelseries_arctis_7.h"
1415
#include "devices/steelseries_arctis_9.h"
1516

1617
#include <string.h>
1718

18-
#define NUMDEVICES 12
19+
#define NUMDEVICES 13
1920

2021
// array of pointers to device
2122
static struct device*(devicelist[NUMDEVICES]);
@@ -33,7 +34,8 @@ void init_devices()
3334
gpro_init(&devicelist[8]);
3435
arctis_1_xbox_init(&devicelist[9]);
3536
zone_wired_init(&devicelist[10]);
36-
g432_init(&devicelist[11]);
37+
elo71Air_init(&devicelist[11]);
38+
g432_init(&devicelist[12]);
3739
}
3840

3941
int get_device(struct device* device_found, uint16_t idVendor, uint16_t idProduct)

src/devices/CMakeLists.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@ set(SOURCE_FILES ${SOURCE_FILES}
1717
${CMAKE_CURRENT_SOURCE_DIR}/steelseries_arctis_7.h
1818
${CMAKE_CURRENT_SOURCE_DIR}/steelseries_arctis_9.c
1919
${CMAKE_CURRENT_SOURCE_DIR}/steelseries_arctis_9.h
20+
${CMAKE_CURRENT_SOURCE_DIR}/roccat_elo_7_1_air.h
21+
${CMAKE_CURRENT_SOURCE_DIR}/roccat_elo_7_1_air.c
2022
${CMAKE_CURRENT_SOURCE_DIR}/logitech_g633_g933_935.c
2123
${CMAKE_CURRENT_SOURCE_DIR}/logitech_g633_g933_935.h
2224
${CMAKE_CURRENT_SOURCE_DIR}/logitech_gpro.c

src/devices/roccat_elo_7_1_air.c

Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,110 @@
1+
#include "../device.h"
2+
3+
#include <inttypes.h>
4+
#include <stdio.h>
5+
#include <string.h>
6+
#include <time.h>
7+
8+
static struct device device_elo71Air;
9+
10+
static const uint16_t PRODUCT_ID = 0x3a37;
11+
12+
static int elo71Air_switch_lights(hid_device* hid_device, uint8_t on);
13+
static int elo71Air_send_inactive_time(hid_device* hid_device, uint8_t num);
14+
15+
void elo71Air_init(struct device** device)
16+
{
17+
device_elo71Air.idVendor = VENDOR_ROCCAT;
18+
device_elo71Air.idProductsSupported = &PRODUCT_ID;
19+
device_elo71Air.numIdProducts = 1;
20+
device_elo71Air.idInterface = 0;
21+
22+
strncpy(device_elo71Air.device_name, "ROCCAT Elo 7.1 Air", sizeof(device_elo71Air.device_name));
23+
24+
device_elo71Air.capabilities = CAP_LIGHTS | CAP_INACTIVE_TIME;
25+
device_elo71Air.switch_lights = &elo71Air_switch_lights;
26+
device_elo71Air.send_inactive_time = &elo71Air_send_inactive_time;
27+
28+
*device = &device_elo71Air;
29+
}
30+
31+
/*
32+
* Sends size bytes from data to the headset.
33+
* If out_buffer points to 64 byte of preallocated memory, send_receive will
34+
* receive 64 byte into id over the HID stream.
35+
*
36+
* The Roccat Elo 71 Air hates, if it gets 64 bytes blocks to quickly. The
37+
* windows software seems just to wait a bit, so send_receive does as well. :-/
38+
*
39+
* This function returns the code from hid_read or hid_write, if either no read
40+
* was requested or hid_write already failed.
41+
*/
42+
static int send_receive(hid_device* hid_device, const uint8_t* data,
43+
int size, uint8_t* out_buffer)
44+
{
45+
struct timespec ts;
46+
int r;
47+
48+
r = hid_write(hid_device, data, size);
49+
if ((out_buffer != NULL) && (r >= 0)) {
50+
r = hid_read(hid_device, out_buffer, 64);
51+
}
52+
53+
ts.tv_sec = 0;
54+
ts.tv_nsec = 75 * 1000000;
55+
nanosleep(&ts, NULL);
56+
57+
return r;
58+
}
59+
60+
static int elo71Air_switch_lights(hid_device* hid_device, uint8_t on)
61+
{
62+
// All commands are related to the LEDs, the following Hex codes cause
63+
// the LEDs to become 'static', the exact meaning of most commands & their
64+
// parameters is further unknown.
65+
uint8_t cmd92[64] = { 0xff, 0x02 };
66+
uint8_t cmd93[64] = { 0xff, 0x03, 0x00, 0x01, 0x00, 0x01 };
67+
68+
// sets LED color as RGB value
69+
uint8_t cmd94[64] = { 0xff, 0x04, 0x00, 0x00,
70+
on ? 0xff : 0, on ? 0xff : 0, on ? 0xff : 0 };
71+
uint8_t cmd95[64] = { 0xff, 0x01 };
72+
int r;
73+
74+
r = send_receive(hid_device, cmd92, sizeof(cmd92), NULL);
75+
if (r <= 0) {
76+
return r;
77+
}
78+
79+
r = send_receive(hid_device, cmd93, sizeof(cmd93), NULL);
80+
if (r <= 0) {
81+
return r;
82+
}
83+
84+
r = send_receive(hid_device, cmd94, sizeof(cmd94), NULL);
85+
if (r <= 0) {
86+
return r;
87+
}
88+
89+
r = send_receive(hid_device, cmd95, sizeof(cmd95), NULL);
90+
if (r <= 0) {
91+
return r;
92+
}
93+
94+
return 0;
95+
}
96+
97+
static int elo71Air_send_inactive_time(hid_device* hid_device, uint8_t num)
98+
{
99+
if (num > 60) {
100+
printf("Device only supports up to 60 minutes.\n");
101+
return -2;
102+
}
103+
104+
int seconds = num * 60;
105+
uint8_t cmd[64] = { 0xa1, 0x04, 0x06, 0x54, seconds >> 8, seconds & 0xFF };
106+
uint8_t response[64];
107+
108+
return send_receive(hid_device, cmd, sizeof(cmd), response);
109+
//meaning of response of headset is not clear yet, fetch & ignore it for now
110+
}

src/devices/roccat_elo_7_1_air.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
#pragma once
2+
3+
void elo71Air_init(struct device** device);

udev/70-headsets.rules

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,4 +81,7 @@ KERNEL=="hidraw*", SUBSYSTEM=="hidraw", ATTRS{idVendor}=="1038", ATTRS{idProduct
8181
# SteelSeries ARCTIS 9
8282
KERNEL=="hidraw*", SUBSYSTEM=="hidraw", ATTRS{idVendor}=="1038", ATTRS{idProduct}=="12c2", TAG+="uaccess"
8383

84+
# ROCCAT Elo 7.1 Air
85+
KERNEL=="hidraw*", SUBSYSTEM=="hidraw", ATTRS{idVendor}=="1e7d", ATTRS{idProduct}=="3a37", TAG+="uaccess"
86+
8487
LABEL="headset_end"

0 commit comments

Comments
 (0)