Skip to content

Commit

Permalink
Merge pull request #16 from Ionic/feature/support-steelseries-arctis7
Browse files Browse the repository at this point in the history
Add support for SteelSeries Arctis 7.
  • Loading branch information
Sapd authored Apr 23, 2018
2 parents 14c0c0a + b6f6f4e commit 4e8d256
Show file tree
Hide file tree
Showing 9 changed files with 196 additions and 5 deletions.
4 changes: 3 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ Sidetone:
- Logitech G930
- Logitech G533
- Logitech G430 (Last working on macOS in commit 41be99379f)
- SteelSeries Arctis 7


## Other Features

Expand All @@ -37,7 +39,7 @@ If you want to be able to call HeadsetControl from every folder type:
```
make install
```
This whille copy the binary to a local folder globally accessable via path. You may need to run it with sudo/root on Linux.
This will copy the binary to a local folder globally accessable via path. You may need to run it with sudo/root on Linux.

Also in Linux, you need udev rules if you don't want to start the application with root. Those rules reside in the udev folder of this repository. Typing make install on Linux copies them automatically to /etc/udev/rules.d/.

Expand Down
3 changes: 3 additions & 0 deletions src/device.h
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@

#define VENDOR_CORSAIR 0x1b1c
#define VENDOR_LOGITECH 0x046d
#define VENDOR_STEELSERIES 0x1038

/** @brief A list of all features settable/queryable
* for headsets
Expand All @@ -31,6 +32,8 @@ struct device
uint16_t idVendor;
/// USB Product id
uint16_t idProduct;
/// Interface ID - zero means first enumerated interface!
int idInterface;

/// Name of device, used as information for the user
char device_name[32];
Expand Down
4 changes: 3 additions & 1 deletion src/device_registry.c
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,12 @@
#include "devices/logitech_g430.h"
#include "devices/logitech_g533.h"
#include "devices/logitech_g930.h"
#include "devices/steelseries_arctis7.h"

#include <string.h>


#define NUMDEVICES 5
#define NUMDEVICES 6
// array of pointers to device
static struct device *(devicelist[NUMDEVICES]);

Expand All @@ -20,6 +21,7 @@ void init_devices()
g430_init(&devicelist[2]);
g533_init(&devicelist[3]);
g930_init(&devicelist[4]);
arctis7_init(&devicelist[5]);
}

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
Expand Up @@ -9,4 +9,6 @@ set(SOURCE_FILES ${SOURCE_FILES}
${CMAKE_CURRENT_SOURCE_DIR}/logitech_g430.h
${CMAKE_CURRENT_SOURCE_DIR}/logitech_g533.c
${CMAKE_CURRENT_SOURCE_DIR}/logitech_g533.h
${CMAKE_CURRENT_SOURCE_DIR}/steelseries_arctis7.c
${CMAKE_CURRENT_SOURCE_DIR}/steelseries_arctis7.h
PARENT_SCOPE)
57 changes: 57 additions & 0 deletions src/devices/steelseries_arctis7.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
#include "../device.h"
#include "../utility.h"

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

static struct device device_arctis7;

static int arctis7_send_sidetone(hid_device *device_handle, uint8_t num);

void arctis7_init(struct device** device)
{
device_arctis7.idVendor = VENDOR_STEELSERIES;
device_arctis7.idProduct = 0x1260;
device_arctis7.idInterface = 0x05;

strcpy(device_arctis7.device_name, "SteelSeries Arctis 7");

device_arctis7.capabilities = CAP_SIDETONE;
device_arctis7.send_sidetone = &arctis7_send_sidetone;

*device = &device_arctis7;
}

static int arctis7_send_sidetone(hid_device *device_handle, uint8_t num)
{
int ret = -1;

// the range of the Arctis 7 seems to be from 0 to 0x12 (18)
num = map(num, 0, 128, 0x00, 0x12);

unsigned char *buf = calloc(31, 1);

if (!buf)
{
return ret;
}

const unsigned char data_on[5] = {0x06, 0x35, 0x01, 0x00, num};
const unsigned char data_off[2] = {0x06, 0x35};

if (num)
{
memmove(buf, data_on, sizeof(data_on));
}
else
{
memmove(buf, data_off, sizeof(data_off));
}

ret = hid_write(device_handle, buf, 31);

SAFE_FREE(buf);

return ret;
}
3 changes: 3 additions & 0 deletions src/devices/steelseries_arctis7.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
#pragma once

void arctis7_init(struct device** device);
120 changes: 117 additions & 3 deletions src/main.c
Original file line number Diff line number Diff line change
Expand Up @@ -18,12 +18,14 @@
***/

#include "device_registry.h"
#include "utility.h"

#include <hidapi.h>

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>


// describes the device, when a headset was found
Expand Down Expand Up @@ -57,8 +59,90 @@ int find_device()
return found;
}

/**
* Helper freeing HID data and terminating HID usage.
*/
/* This function is explicitly called terminate_hid to avoid HIDAPI clashes. */
static void terminate_hid(hid_device **handle, char **path)
{
if (handle)
{
if (*handle)
{
hid_close(*handle);
}

*handle = NULL;
}

if (path)
{
SAFE_FREE(*path);
}

hid_exit();
}

/**
* @brief Helper fetching a copied HID path for a given device description.
*
* This is a convenience function iterating over connected USB devices and
* returning a copy of the HID path belonging to the device described in the
* parameters.
*
* @param vid The device vendor ID.
* @param pid The device product ID.
* @param iid The device interface ID. A value of zero means to take the
* first enumerated (sub-) device.
*
* @return copy of the HID path or NULL on failure
*/
static char* get_hid_path(uint16_t vid, uint16_t pid, int iid)
{
char *ret = NULL;

struct hid_device_info *devs = hid_enumerate(vid, pid);

if (!devs)
{
printf("HID enumeration failure.\n");
return ret;
}

struct hid_device_info *cur_dev = devs;
while (cur_dev)
{
if (!iid || cur_dev->interface_number == iid)
{
ret = strdup(cur_dev->path);

if (!ret)
{
printf("Unable to copy HID path.\n");
hid_free_enumeration(devs);
devs = NULL;
return ret;
}

break;
}

cur_dev = cur_dev->next;
}

hid_free_enumeration(devs);
devs = NULL;

return ret;
}

int main(int argc, char *argv[])
{
{
/* Avoid compiler warning. Pretty stupid. */
(void) map(0, 0, 1, 0, 0);
}

printf("Headsetcontrol written by Sapd (Denis Arnst)\n\thttps://github.com/Sapd\n\n");

int c;
Expand Down Expand Up @@ -121,13 +205,24 @@ int main(int argc, char *argv[])
return 1;
}
printf("\n");

hid_device *device_handle;
device_handle = hid_open(device_found.idVendor, device_found.idProduct, NULL);

hid_device *device_handle = NULL;
char *hid_path = get_hid_path(device_found.idVendor, device_found.idProduct, device_found.idInterface);

if (!hid_path)
{
printf("Requested/supported HID device not found or system error.\n");
terminate_hid(&device_handle, &hid_path);
return 1;
}

device_handle = hid_open_path(hid_path);

// Open libusb device
if (device_handle == NULL)
{
printf("Couldn't open device.\n");
terminate_hid(&device_handle, &hid_path);
return 1;
}

Expand All @@ -138,6 +233,9 @@ int main(int argc, char *argv[])
if ((device_found.capabilities & CAP_SIDETONE) == 0)
{
printf("Error: This headset doesn't support sidetone\n");

terminate_hid(&device_handle, &hid_path);

return 1;
}

Expand All @@ -146,6 +244,9 @@ int main(int argc, char *argv[])
if (ret < 0)
{
printf("Failed to set sidetone. Error: %d: %ls\n", ret, hid_error(device_handle));

terminate_hid(&device_handle, &hid_path);

return 1;
}
else
Expand All @@ -159,6 +260,9 @@ int main(int argc, char *argv[])
if ((device_found.capabilities & CAP_NOTIFICATION_SOUND) == 0)
{
printf("Error: This headset doesn't support notification sound\n");

terminate_hid(&device_handle, &hid_path);

return 1;
}

Expand All @@ -167,6 +271,9 @@ int main(int argc, char *argv[])
if (ret < 0)
{
printf("Failed to send notification sound. Error: %d: %ls\n", ret, hid_error(device_handle));

terminate_hid(&device_handle, &hid_path);

return 1;
}
else
Expand All @@ -180,6 +287,9 @@ int main(int argc, char *argv[])
if ((device_found.capabilities & CAP_BATTERY_STATUS) == 0)
{
printf("Error: This headset doesn't support battery status\n");

terminate_hid(&device_handle, &hid_path);

return 1;
}

Expand All @@ -188,6 +298,9 @@ int main(int argc, char *argv[])
if (ret < 0)
{
printf("Failed to request battery. Error: %d: %ls\n", ret, hid_error(device_handle));

terminate_hid(&device_handle, &hid_path);

return 1;
}

Expand All @@ -202,6 +315,7 @@ int main(int argc, char *argv[])
printf("You didn't set any arguments, so nothing happend.\nType %s -h for help.\n", argv[0]);
}

terminate_hid(&device_handle, &hid_path);

return 0;
}
7 changes: 7 additions & 0 deletions src/utility.h
Original file line number Diff line number Diff line change
@@ -1,5 +1,12 @@
#pragma once

/** @brief Frees and invalidates a pointer.
*
* This is a convenience function freeing and invalidating
* a pointer.
*/
#define SAFE_FREE(ptr) do { free(ptr); ptr = NULL; } while (0)

/** @brief Maps a value x from a given range to another range
*
* The input x is mapped from the range in_min and in_max
Expand Down
1 change: 1 addition & 0 deletions udev/steelseriesarctis7.rules
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
SUBSYSTEM=="usb", ENV{ID_MODEL}=="SteelSeries_Arctis_7", MODE="0666"

0 comments on commit 4e8d256

Please sign in to comment.