Skip to content

Development 1 ‐ Adding a device

Denis Arnst edited this page Apr 1, 2024 · 2 revisions

This document describes how you can implement a new device into HeadsetControl

Check alsamixer on Linux

If the Sidetone of your headset is available as audio channel, it cannot be implemented here in Headsetcontrol, as HID does not support changing audio channels. You can easily check this with alsamixer.

You can however still implement support for battery etc.

Check if a similar device already exists

For example, if you have the wireless version of an implemented wired device, you might get it working by simply adding the ID of your headset to the current implementation.

Run headsetcontrol with this parameters: headsetcontrol --dev -- --list

You should get an output with a lot of devices. There should be your headset. E.g. for Corsair Void:

Device Found
  VendorID: 0x1b1c
 ProductID: 0x1b27
  path: IOService:/AppleACPIPlatformExpert/PCI0@0/AppleACPIPCI/XHC1@14/XHC1@14000000/HS02@14200000/USB2.0 Hub             @14200000/AppleUSB20Hub@14200000/AppleUSB20HubPort@14220000/USB2.0 Hub             @14220000/AppleUSB20Hub@14220000/AppleUSB20HubPort@14224000/Corsair VOID Wireless Gaming Dongle@14224000/Hid Interface@3/AppleUserUSBHostHIDDevice
  serial_number:
  Manufacturer: Corsair
  Product:      Corsair VOID Wireless Gaming Dongle
  Interface:    3
  Usage-Page: 0xff00 Usageid: 0x1

Take note of the ProductID. (0x1b27 in this case)

Adding the deviceid to the implementation

  1. Open the implementation of the similar device src/devices/the_similar_device.c create a new #define for your device where your specify the new device id.
  2. Insert the name of the define into the array PRODUCT_IDS.
  3. Recompile, by cd'ing back to your build folder and simply type make (and possibly make install).
  4. If it works, please also contribute by opening an issue or creating a pull request.

Making a pull request

If your device works, you can contribute like this: (This will help us greatly!)

  1. Fork the repository
  2. Apply your changes to your fork (Please don't do any other changes - do them in a separate request)
    • If you have no git knowledge, simply edit it online at GitHub in your fork.
  3. Do a pull request https://help.github.com/en/articles/creating-a-pull-request-from-a-fork

Don't hesitate to ask questions by opening up an issue.

Adding a completely new headset

You need a bit experience in programming. Start by looking into the devices/ folder, and see how the structure of a device file is set up (ignore the actual raw package bytes for now).

Also you need the original driver-software installed in a Windows machine. This can als be a Windows installation in a virtual machine with USB passthrough.

Use a capturing software like Wireshark or USBLyzer. When installing Wireshark, make sure to select USB support. When Capturing, you will probably see your device in the list as "Composite device". Select "USB Input Device" for capturing and start it; your device is easily identifiable, as it must be named/tagged as "HID" device.

While capturing, start to slide the slider of the sidetone option in your driver's software up and down and stop it afterwards.

Take a look at the following screenshots. Note that Wireshark and USBLyzer show different captures here.

Wireshark Screenshots

Sidetone set to 0 Screenshot Wireshark (big version)

Sidetone set to full Screenshot Wireshark2 (big version)

USBLyzer Screenshot

Screenshot (big version)

For Wireshark, you can use a filter like this: usb.bInterfaceClass == HID && usb.endpoint_address.direction != IN && usb.src == host This will only show HID packets, going from the computer to the headset. Also in the screenshot I added && usb.setup.wLength == 64; as I noticed that a lot of other packets/noise was sent on a different length.

When you look into the WireShark screenshot, you see a SET_REPORT packet with following data:

ff 0b 00 ff  04 0e 01 05
01 04 00 *f4*  00 00 00 00
00 00 00 00  00 00 00 00 
00 00 00 00  00 00 00 00
00 00 00 00  00 00 00 00  
00 00 00 00  00 00 00 00
00 00 00 00  00 00 00 00
00 00 00 00  00 00 00 00

Note that the 12th-byte (counting from 1) is changing when setting sidetone. When it's set to full, it seems to be f4, which is 244 in decimal, when off, it is 0.

Also note that the length is 64-byte (as can be read in wLength).

When it's a Set Report, you need to use hid_send_feature_report, and for a write hid_write !!. You can call those functions directly with the raw data you see at the capturing software. So the following is an example using the data gained:

static int headset_send_sidetone(hid_device* device_handle, uint8_t num)
{
    // num, will be from 0 to 128, we need to map it to the correct value
    // the range of the void seems to be from 200 to 244
    // Note that we don't use 0 as minimal target range, as it would behave weirdly
    //    for the user (lower numbers do nothing then)
    num = map(num, 0, 128, 200, 244);

    // When initialising this way (on stack), missing values will be automatically set to 0
    // The size of the array should be correctly set to the value, the capture software shows
    uint8_t sidetone_data[64] = {0xff, 0x0b, 0, 0xff, 0x04, 0x0e, 0x01, 0x05,
                                 0x01, 0x04, 0x00, num};

    // Errors will be handled by the main function of HeadsetControl
    return hid_send_feature_report(device_handle, sidetone_data, sizeof(sidetone_data)/sizeof(sidetone_data[0]));
}

Note that the code style above is preferred (some code is older and e.g. initialises the array explicitly to 0; however this is unnecessary).

For the full implementation look into other device files, to see how they are set up. When creating new source code, make sure to add your files to src/devices/CMakeLists.txt and call your init function at src/device_registry.c. After creating new files, you'll have to delete your build folder and rerun cmake.

Make sure that you send the correct amount of bytes (like shown in the capturing software), don't cut ending 0-bytes or the Windows implementation will fail. Also try to set idUsagePage and idUsage for the Windows implementation, they can be listed using ./headsetcontrol --dev -- --list --device VENDORID:PRODUCTID (see the Windows section below).

Before implementing it in code, you can also test the packet with the --dev commands. E.g.:

./headsetcontrol --dev -- --device 0x1b1c:0x1b27 --send-feature "
0xff, 0x0B, 0, 0xFF, 0x04, 0x0E, 0xFF, 0x05, 0x01, 0x04, 0x00, 200, 0, 0, 0, 0
0x0, 0x0, 0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x00, 0, 0, 0, 0, 0
0x0, 0x0, 0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x00, 0, 0, 0, 0, 0
0x0, 0x0, 0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x00, 0, 0, 0, 0, 0"

In the dev command line, you can seperate the numbers with spaces, or commas, or new-lines. Also you can write the numbers in either hex (with 0x) or directly as decimal. Also you have the possibility of receiving data, see ./headsetcontrol --dev -- --dev-help: