Hey all!
I'm on Linux, not Windows, so the Corsair Link software is not usable for me. Instead, my goal is to write a kernelspace driver to integrate the Corsair Link sensors and fan control into the Linux hwmon subsystem. This would make Corsair Link devices show up in lm_sensors. :biggrin:
To that end, I've started prodding around with the USB protocol to see how the software interacts with the hardware. I'm using a Windows XP VM with the Corsair Link 2 software, and USB pass-through on my H100i. The H100i is pretty much the only device at my disposal, so I may end up making some generalizations here. :)
It should be noted that this is being done purely through observation. No reverse engineering of the software is being done and no licenses are being violated.
Corsair Link protocol overview (unverified)
The USB device is actually an HID report device. This simplifies things, because the HID protocol is concerned purely with sending and receiving fixed-length buffers. It also means no specialized drivers are required. On Windows, you can just use the HidD_SetFeature/HidD_GetFeature calls. On Linux, open /dev/hidraw* and perform ordinary reads and writes.
In this case, both the input and output are zero-padded to 64 bytes even.
The device contains a number of registers, identified by a single byte, that can be read from and written to.
Output (sending commands from host->USB):
The HID reports are formatted with a one-byte length tag (indicating how many significant bytes the message contains) followed by that many bytes, then zero-padded to a length of 64.
For example, if I wanted to send a 5-byte message to my device, I would send:
05 XX XX XX XX XX 00 00 00 00 00 00 00 [...64 bytes]
The message payload contains one or more commands. A command consists of the command ID (one byte, used to identify the command when the device responds), the command opcode (one byte), and any arguments that the command expects.
Here are the commands that I have found so far:
06 AA BB - Write BB into one-byte register AA
07 AA - Read from one-byte register AA
08 AA BB CC - Write BB CC into two-byte register AA
09 AA - Read from two-byte register AA
0A AA 03 00 11 22 - Write 3-byte sequence (00 11 22) into 3-byte register AA
0B AA 03 - Read from 3-byte register AA
Input (receiving replies from USB):
Like with the output, the input reports are also zero-padded 64 bytes.
Unlike the output, there is no length tag at the beginning of the message. The message consists purely of replies.
A "reply" consists of the command ID (one byte), followed by one or more bytes. There is no length tag, so the host must know how long each command's reply will be.
Typically, (though not always), the first byte of the reply will be the command opcode. Therefore, if I issue "B7 09 AA", the reply will be "B7 09 XX XX" (where XX XX are the contents of register AA).
The responses to the commands above are:
06 - A single '06' is sent in reply, as an acknowledgement of the write
07 - A single '07', followed by the byte in the register requested.
08 - A single '08', as an acknowledgement.
09 - A single '09', followed by the two-byte contents of the register.
0A - A single '0A', as acknowledgement.
0B - A single '0B', followed by the length byte, followed by the register contents.
Registers (on my H100i):
[TABLE=head]Number | R/W | Length | Description
00 | R | 1 byte | Identifier 1 - for my H100i, 0x3c
01 | R | 2 bytes | Identifier 2 - for my H100i, 0x05 0x10
02 | R | 32 bytes? | Product name, zero-terminated - for me, the string "H100i"
03 | ? | ?? | Unknown, possibly reserved
04 | RW | 1 byte | Select current LED
05 | R | 1 byte | Number of LEDs
06 | RW | 1 byte | LED mode - 00 for static color, 4b for 2-color cycle, 8b for 4-color, c0 for temperature mode
07 | R | 3 bytes | LED current color, RGB color of the selected LED
08 | ? | ?? | ?? Unknown, reserved?
09 | RW | 6 bytes | LED temperature-mode temperatures: 3 temperatures; used when cycle mode is 0xc0
0a | RW | 9 bytes | LED temperature-mode colors: RGBx3 colors, corresponding to temperatures in register above
0b | RW | 12 bytes | LED cycle colors: RGBx4 colors (only first color used if cycle mode set to 00, first two if 4b, ignored if c0)
0c | RW | 1 byte | Select active temperature sensor
0d | R | 1 byte | Number of temperature sensors
0e | R | 2 bytes | Temperature as measured by selected sensor
0f | ? | ?? | ?? Unknown, reserved?
10 | RW | 1 byte | Select current fan; for H100i, 0-3 are the fans, 4 is pump
11 | R | 1 byte | Number of fans
12 | RW | 1 byte | Fan mode; 02=fixed PWM, 04=fixed RPM, 06=default, 08=quiet, 0a=balanced, 0c=performance, 0e=custom
13 | RW | 1 byte | Fan fixed PWM, 0-255, only used if fan mode is 02
14 | RW | 2 bytes | Fan fixed RPM; when fan mode is 04, controller will target this RPM
15 | RW | 2 bytes | Report external temperature to fan controller - used for controlling fans via external sensors
16 | R | 2 bytes | Current fan RPM
17 | R | 2 bytes | ?? Some sort of fan sensor - no clue what this does.
18 | ? | ?? | ?? Unknown, reserved?
19 | RW | 10 bytes | Fan RPM table, for custom (0e) mode: array of 5 RPMs
1a | RW | 10 bytes | Fan temp table, for custom (0e) mode: array of 5 temperatures
[/TABLE]
Note: All data is little-endian.
Temperatures are reported in units of 1/256th of a degree Celsius.
Example:
If I send:
18 02 06 06 00 03 0a 0b 0c 00 00 00 ff ff ff ff
ff ff ff ff ff 04 0b 07 03 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
This is a 0x18-long message containing the following commmands:
ID 02, opcode 06: Set register 06 to 00 (turn off LED color cycling)
ID 03, opcode 0a: Set register 0c (LED colors) so the first color is black (off)
ID 04, opcode 0b: Read 3 bytes from register 07 (verify LED color changed)
The device will reply:
02 06 03 0a 04 0b 03 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 00 00 00 00 00
Broken down:
02 06 (device acknowledges command ID 02 with an 06)
03 0a (device acknowledges command ID 03 with an 0a)
04 0b 03 00 00 00 (replies to command ID 04 with 3 bytes: 00 00 00, confirming LED is now off)