wytnyt Posted August 18, 2013 Share Posted August 18, 2013 its Corsair policy not to release a estimated eta or possible decision or release date or time frame,only after its released will notice be given unfortunately... Link to comment Share on other sites More sharing options...
r_avital Posted August 27, 2013 Share Posted August 27, 2013 Wow, I actually have people registering on the forum to thank me. Never had that happen before... :) Well, get used to it, bacause it's happening again, may you be blessed with long life! And if you manage to work it out, please inform the Linux community if you could. Good Luck! Link to comment Share on other sites More sharing options...
r_avital Posted August 27, 2013 Share Posted August 27, 2013 I cant get into much detail...but we are strictly limited as to what we can say. RAM GUY, I used to be a support engineer and have been in your shoes more times than I care to remember, so I understand your position perfectly. The last time I heard from your product manager about this (Corsair George, I believe, is is user-name?), he gave some non-committal answers, misunderstood a question as a complaint, and (almost angrily) closed the thread. That was back in June 2011. It's been two years. I know your constraints have not changed, and I'm not complaining about that. I registered today for two purposes. One, was to thank CFSworks, above, for his work. The second, is to ask you to "intimate" to management, that the money of Linux users is just as green as anyone else's, and many of us are tired of being dismissed as "all 31 of them." Nobody is expecting Corsair to create the Linux-compatible software -- although, if you did, I guarantee you that Corsair would face no one's objection to disclose everything about it, down to the last semicolon in the code. Nobody is expecting Corsair to violate agreements with other parties and divulge proprietary information. Nobody is even expecting "Free software" -- a frequent misrepresentation of Linux users. But we did pay the same price as everyone else, and many of us are sick and tired of this attitude. If Corsair George had told us to go pound sand, at least it would have been honest. Thank you. Link to comment Share on other sites More sharing options...
Corsair Employee RAM GUY Posted August 27, 2013 Corsair Employee Share Posted August 27, 2013 r_avital First thanks for adding your thoughts and comments and I cannot speak for George per say. But I can tell you that we would like to have this worked out a long time ago but because of License issues with the Code it was just not possible. So we as a company and we as Enthusiasts with in the company agree with you and want to share it for Open source Not only for Linux users but it would open it up to a wide verity of uses beyond just computers. So it is definitely something we want to do, however it has been more of a brick wall than we would have liked it to be. And I dont think George meant anything personal when he closed the thread he just did not want it to explode into something else. So if it came off that way I know that was not his intent and I apologize for that. I cant make any promises as to what will happen in the future but we would like to be able to open this code up. And as far as I know we have been looking into that for future products but the resources have just not been there to develop our own code which is what will have to happen for us to be able to release an open source product. Link to comment Share on other sites More sharing options...
zen1 Posted September 9, 2013 Share Posted September 9, 2013 I'm glad to see this thread has some activity. I don't want to jump ship to a different solution but if I don't see a new more bug free software or even a kludge thrown together by one of these dedicated enthusiasts, I'm gonna have to abandon all my link gear. It is just getting ridiculous. I'm sorry I'm venting, but I know I'm not alone. I feel the more people speak out, the better the chances that the people at the top will get the point. I used to be a corsair fanboy, but no more. I'm not pointing fingers at the corsair reps that are actually communicating with us. I just wanted to voice my two cents as I'm frustrated. I've given the corsair link system 9 months to win me over, but it's just too buggy. If someone out there can throw together some homebrewed software, please post it here. I've tried multiple OSs, compatibility settings, fresh installs, workarounds posted in the forums; nothing has shone me a product that is worthy of being released to the public. Sorry for the rant, I just needed to be heard and I want to promote this thread so that a solution can be found. Link to comment Share on other sites More sharing options...
Corsair Employee RAM GUY Posted September 9, 2013 Corsair Employee Share Posted September 9, 2013 zen1, Its no problem; for some it has not worked as well as it has for most. But we do understand your frustration and I do apologize. Thank you for taking the time to Voice your opinion. Link to comment Share on other sites More sharing options...
Thatualle1970 Posted September 10, 2013 Share Posted September 10, 2013 Hey all! Hey CFSworks; great job so far.... :cool: Some additional information to keep this thread moving in the positive direction. Corsair Link protocol overview As mentioned, the USB device uses a simple 64-byte HID "raw" report; the rawhid source code can be a useful starting point to establish low-level communication if a device isn't "just available" (e.g., on Windows). http://www.pjrc.com/teensy/rawhid.html The ReportID is always zero and is length-prefixed with a number of SMBus-like packets within the HID packet. Each packet has an index (which should be from 20~255), SMBus sub-packet type followed by any write data, if the sub-packet is writing data. Block types also have a length, and it appears the "quick" and "byte" modes are unsupported (only CmdByte, CmdWord and CmdBlock appear to be used). H80i/H100i Register set: [TABLE=head]__Number__|R/W|__Length__| Description 00 | R | 1 byte | Device ID - (H80 0x37, Cooling node 0x38, Lighting node 0x39, H100 0x3A, 80i 0x3B, 100i 0x3c -- this field as well as the version are common on all C-Link devices; but the rest aren't 01 | R | 2 bytes | Firmware Version in BCD (for example 1.0.5 is 0x1005, or 0x05, 0x10 in little endianess) 02 | R | 8 bytes | Product name, zero-terminated - only present on the H80i and H100i 03 | R | 1 | Status, 0 okay, 0xFF bad 04 | RW | 1 byte | Select current LED 05 | R | 1 byte | Number of LEDs 06 | RW | 1 byte | LED mode - 0x00 for static color, 0x40 for 2-color cycle, 0x80 for 4-color, 0xC0 for temperature mode; low nibble defines cycle speed or the temperature channel to use (0 internal sensor, 7 manual) 07 | R | 3 bytes | LED current color, RGB color of the selected LED 08 | RW | 2 bytes | In temperature controlled mode (0xC0) this defines the colour to use to with the below gradients 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 | RW | 2 bytes | Temperature limit (when the temperature goes over this, status is set to 0xff) 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; high bit is one when fan is detected, low bit is one when the fan is 4-pin, bits 6~4 define the temperautre channel to use in "curve" modes, 0 internal and 7 manual 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 | Maximum RPM recorded since power-on 18 | RW | 2 bytes | Fan under speed threshold 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. 1 Link to comment Share on other sites More sharing options...
Thatualle1970 Posted September 25, 2013 Share Posted September 25, 2013 For the people who are still interested... H80/H100/Cooling Node Register set: [TABLE=head]Register|Hex|Length|Read|Description Device ID|00|2|RO|H80 0x37; Cooling node 0x38; H100 0x3A Firmware Version|01|2|RO|#.#.## that is 0x1011 is version 1.0.17 System Status|02|2|RO|Status 0 okay 0xFF bad Status Led|04|2||Each bit = led; 0 = Extreme, 1 = Perf, 2 = Quiet, 3 = CLink Dude Temps|07|8|RW|Current temp for four channels (signed fixed point) RPM Current|0B|10|RO|Current fan speed RPM Max|10|10|RO|Measured fan maximum Input Temp|1A|10|RW|Temperature when in one of the curve modes Fan Control|20 30 40 50 60|2|RW|Bit 7 Fan detected; Bit 6~4 Temp Input; Bit 3~1 Fan Mode; Bit 0 Fan Pins (1 is 4-Pin) |(each channel)|||Temp Input: 0 Coolant Temp 1 Temp 0 2 Temp 1 3 Temp 2 4 MCU Temp 7 ManualFan ||||Mode: 0 Off 1 PWM 2 RPM 3 Set Point 4 Quiet 5 Performance 6 Extreme 7 User Curve Fan Target PWM|21 31 41 51 61|2|RW|Current fan speed as a percentage * 2.55 (e.g. 255 == 100%) Fan Target RPM|22 32 42 52 62|2|RW|Current RPM target Fan User Curve RPM|23 33 43 53 63|10|RW|User curve profile Fan User Curve Temp|28 38 48 58 68|10|RW|User curve profile [/TABLE] Note: Fields which only expect a single byte (e.g., PWM) must be zero-padded. LED Node Register set: [TABLE=head]Register|Hex|Length*|Read|Description Device ID|00|1|RO|0x3A for LED Node Firmware Version|01|2|RO|BCD 0x0910 is version 1.0.9 SystemStatus|03|1|RO|Good is 0; bad is 0xFF User Mode|04|2x1|RW|0x10 Pulse Mode; 0x01 through 0x0F Built-In Colours; 0x00 C-Link Mode RGB Out|06|2x3|RO|Current calculated LED output Temps|0C|2x2|RW|Input temperatures Cycle Mode|10 30|1|RW|Bit 6~7: 00 - Static; 01 - Two Cycle; 10 - Four Cycle; 11 - Temperature ||||Bit 0~2 Cycle Speed in powers of 2 x 1/8th Second Cold Warm Hot|11 31|3x5|RW|Temp (two bytes) and RGB (three bytes) for each threshold Cycle Colours|20 40|4x4|RW|RGB (with one byte padding) for each cycle step [/TABLE] Note: Lengths may be shown as number of sets x length of each set. For example, there are three sets (one for cold, warm and hot respectively) with five bytes of data for temperature settings. The hex address will be offset byt the BYTE position being written. LED Nodes, Cooling Nodes and the original H80 and H100 all have seuqential registers, meaning that writing more data than the first register will hold will "spill over" into the next register, allowing large block reads and writes. Link to comment Share on other sites More sharing options...
bobpombrio Posted October 2, 2013 Share Posted October 2, 2013 Can some one tell me the USB vendor ID and the USB product ID for the H100i? Like you I am not happy with the Link software and I'd like to write my own. I see all of this great work has been done that explains the protocol and I'd like to take advantage of it. Link to comment Share on other sites More sharing options...
Thatualle1970 Posted October 4, 2013 Share Posted October 4, 2013 Can some one tell me the USB vendor ID and the USB product ID for the H100i? Like you I am not happy with the Link software and I'd like to write my own. I see all of this great work has been done that explains the protocol and I'd like to take advantage of it. PID is always 0x1B1C VID is 0x0C04 for H80i and H100i VID is 0x0C02 for the USB Commander They use the same basic protocol, except the Commander's don't have the extra length byte at the very beginning. Here's what I have working so far (C# code, this depends on HidLibrary which you can get from NuGet): [b]public [/b]List<Int32> ListPorts() { [b]int [/b]packetIndex = 1; [color="Green"]// skip the report id field (always zero)[/color] [b]byte[/b][] packet = [b]new byte[/b][65]; [color="Green"]// size of the packet plus report id[/color] [b]if [/b](newProtocol) packet[packetIndex++] = 2; [color="Green"]// newProtocol = (pid == 0xC04)[/color] packet[packetIndex++] = ([b]byte[/b])index; [color="Green"]// write the index[/color] [b]if [/b](index < 255) index++; [b]else [/b]index = 20; [color="Green"]// and cycle it[/color] packet[packetIndex++] = 0x4F; [color="Green"]// command to get the port status[/color] [color="Green"]// perform the io[/color] device.Write(packet, 500); [b]var [/b]inpacket = device.Read(500); [color="Green"]// and collect our list of active channels[/color] List<Int32> ports = new List<Int32>(); [b]for [/b]([b]int [/b]portIndex = 0; portIndex < 8; portIndex++) { [color="Green"]// any port set to 0xFF is not present, anything else is 'live'[/color] [b]if [/b](inpacket.Data[3 + portIndex] != 0xFF) ports.Add(portIndex); } [b]return [/b]ports; }[color="Green"]// reportid, length, index, command, register, len // 0 1 2 3 4 5 <- block // 0 1 2 3 4 <- byte/word // ^-- omitted with old USB Commander (0xC02) // reportid, index, command/status, data... // 0 1 2 3 // ^-- omitted with USB Commander[/color] [b]public [/b][b]byte[/b][] ReadData([b]int [/b]reg, [b]int [/b]len = 1) { [b]int [/b]i = (newProtocol) ? 2 : 1; [color="Green"]// fill in the length when we're done[/color] [b]byte[/b][] packet = [b]new byte[/b][65]; [color="Green"]// our standard packet length[/color] [b]int [/b]command = (Port << 4) | 1; [color="Green"]// port fills the high nibble, 1 is 'read'[/color] [b]if [/b](len == 1) command |= 6; [color="Green"]// read byte[/color] [b]else if [/b](len == 2) command |= 8; [color="Green"]// read short[/color] [b]else [/b]command |= 10; [color="Green"]// read any length[/color] packet[i++] = ([b]byte[/b])index; [color="Green"]// obligatory index field[/color] [b]if [/b](index < 255) index++; [b]else [/b]index = 20; [color="Green"]// and cycle[/color] packet[i++] = ([b]byte[/b])command; [color="Green"]// command byte[/color] packet[i++] = ([b]byte[/b])reg; [color="Green"]// the register we'd like to read[/color] if (len > 2) packet[i++] = ([b]byte[/b])len; [color="Green"]// arbitrary length require the length[/color] [b]if [/b](newProtocol) packet[1] = ([b]byte[/b])(index - 1); [color="Green"]// add in our total length[/color] [color="Green"]// do the io[/color] device.Write(packet, 500); [b]var [/b]inpacket = device.Read(500); [b]if [/b](inpacket != null && inpacket.Status == HidDeviceData.ReadStatus.Success) { [color="Green"]// ignore bad packets (handle the null from the caller side)[/color] [b]if [/b](inpacket.Data[1] != packet[(newProtocol) ? 2 : 1]) [b]return [/b][b]null[/b]; [b]if [/b]((inpacket.Data[2] & 240) != 0) [b]return [/b][b]null[/b]; [color="Green"]// grab our returned data from within the packet[/color] [b]byte[/b][] data = [b]new byte[/b][len]; Buffer.BlockCopy(inpacket.Data, 3, data, 0, len); [b]return [/b]data; } [b]else [/b][b]return [/b][b]null[/b]; }[color="Green"]// reportid, length, index, command, register, len, data... // 0 1 2 3 4 5, 6... <- block // 0 1 2 3 4 5... <- byte/word // ^-- omitted with USB Commander (0xC02)[/color] [b]public [/b][b]bool [/b]WriteData([b]int [/b]reg, [b]byte[/b][] data) { [b]int [/b]i = (newProtocol) ? 2 : 1; [color="Green"]// fill in the length when we're done[/color] [b]byte[/b][] packet = [b]new byte[/b][65]; [color="Green"]// our standard packet length[/color] [b]int [/b]command = (Port << 4); [color="Green"]// port fills the high nibble, no low bit for write[/color] [b]if [/b](data.Length == 1) command |= 6; [color="Green"]// byte[/color] [b]else if [/b](data.Length == 2) command |= 8; [color="Green"]// short[/color] [b]else [/b]command |= 10; [color="Green"]// arbitrary length[/color] packet[i++] = ([b]byte[/b])index; [color="Green"]// index and cycle[/color] [b]if [/b](index < 255) index++; [b]else [/b]index = 20; packet[i++] = ([b]byte[/b])command; [color="Green"]// command field[/color] packet[i++] = ([b]byte[/b])reg; [color="Green"]// register we're writing too[/color] [b]if [/b](data.Length > 2) packet[i++] = ([b]byte[/b])(data.Length); [color="Green"]// length, if req'd[/color] Buffer.BlockCopy(data, 0, packet, i, data.Length); [color="Green"]// data we're writing[/color] i += data.Length; [color="Green"]// position in the packet[/color] [b]if [/b](newProtocol) packet[1] = (byte)(index - 1); [color="Green"]// prefix packet length[/color] device.Write(packet, 500); [color="Green"]// do the io[/color] [b]var [/b]inpacket = device.Read(500); [color="Green"]// return true if the write was successful[/color] [b]return [/b](inpacket != null && inpacket.Status == HidDeviceData.ReadStatus.Success); } From here you need to just enumerate everything and push that info up to some sort of UI. It's important that you not try to talk to the same device using multiple threads, that breaks things, so either stick to single threading, single threading per USB device, or use locks to ensure that the write-read sequence is always unbroken. Apologies for the messy code. The code is very "C" like and should be easy enough to port to Linux (unless mono can be made to work), but that's beyond my realm of expertise... Link to comment Share on other sites More sharing options...
bobpombrio Posted October 4, 2013 Share Posted October 4, 2013 This is extremely sweet. I'm doing a Windows implementation as well. In (wait for it) (waaiiiittttt) C# as well. :biggrin: Thanks!! Link to comment Share on other sites More sharing options...
Thatualle1970 Posted October 4, 2013 Share Posted October 4, 2013 This is extremely sweet. I'm doing a Windows implementation as well. In (wait for it) (waaiiiittttt) C# as well. :biggrin: Thanks!! You're welcome, and good luck! Regrading C#, if it had direct-to-x86 compilation it would be the perfect language to write in, but having to pass through the CLR all the time makes it imperfect for low latency communication. Ultimately, you'd be better off writing a C/C++ layer that handles the basic communication loop and pass that up to higher code. For example, in C# in Windows using HidLibrary, I get around 12ms per packet, whereas rewriting the low level code in ANSI C using direct Win32 calls and plugging it into LUA, I was able to easily hit 1ms per packet (the limit Windows/USB2.0 will allow). Link to comment Share on other sites More sharing options...
bobpombrio Posted October 5, 2013 Share Posted October 5, 2013 Your correct. I've done a fair amount of low level C and assembly over the years but I'm starting to play with C sharp. I haven't done much of anything yet with is thing, motivation seems to be a factor and getting a response from the 100i yet seems to be another. I recently did a large project for the army in c# on a hand-held unit that dealt with a couple low level devices so I should be able to figure this out. Unless you want some collaboration on yours.... :) Link to comment Share on other sites More sharing options...
dwmccauley Posted November 11, 2013 Share Posted November 11, 2013 Hi All, I modified the hidtest utility within the hidapi package (http://www.signal11.us/oss/hidapi/) to read and write the fan mode and RPM registers within the Corsair H80i (& H100i) coolers under Ubuntu 12.04. /******************************************************* Windows HID simplification Alan Ott Signal 11 Software 8/22/2009 Copyright 2009, All Rights Reserved. This contents of this file may be used by anyone for any reason without any conditions and may be used as a starting point for your own applications which use HIDAPI. ********************************************************/ #include <stdio.h> #include <wchar.h> #include <string.h> #include <stdlib.h> #include "hidapi.h" // Headers needed for sleeping. #ifdef _WIN32 #include <windows.h> #else #include <unistd.h> #endif int hid_read_wrapper (hid_device *handle, unsigned char *buf) { // Read requested state. hid_read() has been set to be // non-blocking by the call to hid_set_nonblocking() above. // This loop demonstrates the non-blocking nature of hid_read(). int res = 0; while (res == 0) { res = hid_read(handle, buf, sizeof(buf)); if (res == 0) // printf("waiting...\n"); if (res < 0) printf("Unable to read()\n"); #ifdef WIN32 Sleep(500); #else usleep(500*1000); #endif } //printf("Data read:\n "); //// Print out the returned buffer. //for (int i = 0; i < res; i++) // printf("%02hhx ", buf); //printf("\n"); //return res; } int main(int argc, char* argv[]) { int res; unsigned int commandId = 0x81; unsigned char buf[256]; #define MAX_STR 255 wchar_t wstr[MAX_STR]; hid_device *handle; int i; #ifdef WIN32 UNREFERENCED_PARAMETER(argc); UNREFERENCED_PARAMETER(argv); #endif struct hid_device_info *devs, *cur_dev; if (hid_init()) return -1; devs = hid_enumerate(0x0, 0x0); cur_dev = devs; while (cur_dev) { printf("Device Found\n type: %04hx %04hx\n path: %s\n serial_number: %ls", cur_dev->vendor_id, cur_dev->product_id, cur_dev->path, cur_dev->serial_number); printf("\n"); printf(" Manufacturer: %ls\n", cur_dev->manufacturer_string); printf(" Product: %ls\n", cur_dev->product_string); printf(" Release: %hx\n", cur_dev->release_number); printf(" Interface: %d\n", cur_dev->interface_number); printf("\n"); cur_dev = cur_dev->next; } hid_free_enumeration(devs); // Set up the command buffer. memset(buf,0x00,sizeof(buf)); buf[0] = 0x01; buf[1] = 0x81; // Open the device using the VID, PID, // and optionally the Serial number. // open Corsair H80i or H100i cooler handle = hid_open(0x1b1c, 0x0c04, NULL); if (!handle) { printf("Error: Unable to open Corsair H80i or H100i CPU Cooler\n"); return 1; } // Read the Manufacturer String wstr[0] = 0x0000; res = hid_get_manufacturer_string(handle, wstr, MAX_STR); if (res < 0) printf("Unable to read manufacturer string\n"); printf("Manufacturer String: %ls\n", wstr); // Read the Product String wstr[0] = 0x0000; res = hid_get_product_string(handle, wstr, MAX_STR); if (res < 0) printf("Unable to read product string\n"); printf("Product String: %ls\n", wstr); // Set the hid_read() function to be non-blocking. hid_set_nonblocking(handle, 1); memset(buf,0,sizeof(buf)); // Read Device ID: 0x3b = H80i. 0x3c = H100i buf[0] = 0x03; // Length buf[1] = commandId++; // Command ID buf[2] = 0x07; // Command Opcode buf[3] = 0x00; // Command data... buf[4] = 0x00; res = hid_write(handle, buf, 17); if (res < 0) { printf("Error: Unable to write() %ls\n", hid_error(handle)); } hid_read_wrapper(handle, buf); if (res < 0) { printf("Error: Unable to read() %ls\n", hid_error(handle)); } printf("Device ID: %02hhx\n", buf[2]); int deviceId = buf[2]; if ((deviceId != 0x3b) && (deviceId != 0x3c)) { printf("Device ID: %02hhx mismatch. Not Corsair H80i or H100i CPU Cooler\n", buf[2]); hid_close(handle); hid_exit(); } memset(buf,0x00,sizeof(buf)); // Read number-of-fans buf[0] = 0x03; // Length buf[1] = commandId++; // Command ID buf[2] = 0x07; // Command Opcode buf[3] = 0x11; // Command data... buf[4] = 0x00; res = hid_write(handle, buf, 5); if (res < 0) { printf("Error: Unable to write() %ls\n", hid_error(handle)); } hid_read_wrapper(handle, buf); if (res < 0) { printf("Error: Unable to read() %ls\n", hid_error(handle)); } printf("number-of-fans: %02hhx\n", buf[2]); memset(buf,0x00,sizeof(buf)); // Read number-of-LEDs buf[0] = 0x03; // Length buf[1] = commandId++; // Command ID buf[2] = 0x07; // Command Opcode buf[3] = 0x05; // Command data... buf[4] = 0x00; res = hid_write(handle, buf, 5); if (res < 0) { printf("Error: Unable to write() %ls\n", hid_error(handle)); } hid_read_wrapper(handle, buf); if (res < 0) { printf("Error: Unable to read() %ls\n", hid_error(handle)); } printf("number-of-LEDs: %02hhx\n", buf[2]); int fanMode = 0; for (int i = 0; i < 5; i++) { #ifdef WIN32 Sleep(500); #else usleep(500*1000); #endif memset(buf,0x00,sizeof(buf)); // Read fan Mode buf[0] = 0x07; // Length buf[1] = commandId++; // Command ID buf[2] = 0x06; // Command Opcode buf[3] = 0x10; // Command data... buf[4] = i; // select fan buf[5] = commandId++; // Command ID buf[6] = 0x07; // Command Opcode buf[7] = 0x12; // Command data... res = hid_write(handle, buf, 11); if (res < 0) { printf("Error: Unable to write() %ls\n", hid_error(handle)); } //printf("read fan Mode "); res = hid_read_wrapper(handle, buf); if (res < 0) { printf("Error: Unable to read() %ls\n", hid_error(handle)); } fanMode = buf[4]; memset(buf,0x00,sizeof(buf)); // Read fan RPM buf[0] = 0x07; // Length buf[1] = commandId++; // Command ID buf[2] = 0x06; // Command Opcode buf[3] = 0x10; // Command data... buf[4] = i; // select fan buf[5] = commandId++; // Command ID buf[6] = 0x09; // Command Opcode buf[7] = 0x16; // Command data... res = hid_write(handle, buf, 11); if (res < 0) { printf("Error: Unable to write() %ls\n", hid_error(handle)); } //printf("read fan RPM "); res = hid_read_wrapper(handle, buf); printf("Fan %d mode %02hhx RPM: %02hhx %02hhx\n", i, fanMode, buf[4], buf[5]); if (i < 5) { #ifdef WIN32 Sleep(500); #else usleep(500*1000); #endif memset(buf,0x00,sizeof(buf)); // setting fan mode to Default (0x06) or Performance (0x0c) fanMode = 0x0c; buf[0] = 0x0b; // Length buf[1] = commandId++; // Command ID buf[2] = 0x06; // Command Opcode buf[3] = 0x10; // Command data... buf[4] = i; // select fan buf[5] = commandId++; // Command ID buf[6] = 0x06; // Command Opcode buf[7] = 0x12; // Command data... buf[8] = fanMode; buf[9] = commandId++; // Command ID buf[10] = 0x07; // Command Opcode buf[11] = 0x12; // Command data... res = hid_write(handle, buf, 17); if (res < 0) { printf("Error: Unable to write() %ls\n", hid_error(handle)); } //printf("read fan mode to confirm "); res = hid_read_wrapper(handle, buf); if (res < 0) { printf("Error: Unable to read() %ls\n", hid_error(handle)); } printf("Fan %d mode is %02hhx should be: %02hhx\n", i, buf[6], fanMode); } } hid_close(handle); /* Free static HIDAPI objects. */ hid_exit(); #ifdef WIN32 system("pause"); #endif return 0; } 1 Link to comment Share on other sites More sharing options...
GrahamB Posted November 14, 2013 Share Posted November 14, 2013 There is a great difference between the description of a protocol, and software to implement it. From my POV Corsair should publish the protocol details, let people write their own code based on this, and maybe other hardware vendors will support it. Would do us all good including Corsair. 1 Link to comment Share on other sites More sharing options...
Thatualle1970 Posted November 30, 2013 Share Posted November 30, 2013 There is a great difference between the description of a protocol, and software to implement it. From my POV Corsair should publish the protocol details, let people write their own code based on this, and maybe other hardware vendors will support it. Would do us all good including Corsair. What part of the protocols are we missing? Confirmation from Corsair that we're right? They've already said they won't do that. Link to comment Share on other sites More sharing options...
GrahamB Posted November 30, 2013 Share Posted November 30, 2013 Yes, but there is a difference between a published protocol and a reverse engineered one. If published I would expect for example changes and additions to be published as well. But it seems that won't happen. Pity. They have a chance to create a protocol and communications for lighting, cooling etc. And if anyone connects non Corsair LEDs to their lighting node, so what. Corsair has decided not to sell them. Link to comment Share on other sites More sharing options...
Ubeogesh Posted December 1, 2013 Share Posted December 1, 2013 Wow guys, what you do here is awesome. I'm tired of the buggy and ugly Corsair Link. Every time I want to change my H100i settings or monitor power usage on my AX760i, I have to detach and reattach the USB connector. Moreover, the product is ugly as hell (why all hardware manufacturers make super ugly GUI in their software? Perhaps, the only exception is AMD, but recent Catalyst's got more ugly; I'd rather have no GUI at all and control everything through CLI and\or configuration files then seeing THIS) If I understand correctly, the CFSworks wants to make some Linux software for Corsair Link, but you guys Thatualle1970 and bobpombrio are doing something for Windows, right? Unfortunately I have no idea in developing drivers and only a very (very) little knowledge of c++ and WinAPI so can't help developing. If there is something else I can help otherwise (i.e. test something?), please PM me. I live in GMT +0300 and available (and hopefully will respond quickly) every weekend I've got AX760i and H100i devices (AX760i connected to H100i, and H100i goes to the USB; I can also connect AX760i to its USB dongle; if something working will come from this thread, I'll be eager to buy the Link device also...). I am also a very experienced Windows software user and know a lot of stuff about it. Link to comment Share on other sites More sharing options...
volmok Posted February 5, 2014 Share Posted February 5, 2014 Hi, I have switched one year ago to Linux and I found that it lacks the control over my cooler (H100i). Thanks dwmccauley's example I have created a working basic tool that allows the following: View fan and pump informationChange fan modeSet fan RPM (if fixed RPM mode is selected) The code is attached in the file and you can use the makefile in the Debug folder. I apologize in advance if I break any "Linux developing rules", but I am a .NET developer (yes I use Linux and develop on MS products) and I just got into C++. V. P.S. Let me know if you find bugs or issues with my code.OpenCorsairLink.zip Link to comment Share on other sites More sharing options...
Thatualle1970 Posted March 19, 2014 Share Posted March 19, 2014 (edited) Meh, to heck with it, I'll leave this up here as is. If anyone's interested in this: http://i.imgur.com/x7n6BP9.jpg You can download the source code from here and compile it with Visual C# Express: OpenHardwareMonitor.7z Edited March 19, 2014 by Thatualle1970 Missed one csproj file in the 7zip file, sorry. Link to comment Share on other sites More sharing options...
sa1 Posted March 21, 2014 Share Posted March 21, 2014 Hi, I have switched one year ago to Linux and I found that it lacks the control over my cooler (H100i). Thanks dwmccauley's example I have created a working basic tool that allows the following: View fan and pump informationChange fan modeSet fan RPM (if fixed RPM mode is selected) The code is attached in the file and you can use the makefile in the Debug folder. I apologize in advance if I break any "Linux developing rules", but I am a .NET developer (yes I use Linux and develop on MS products) and I just got into C++. V. P.S. Let me know if you find bugs or issues with my code. Thanks, this seems to work well. I hope that someone converts this into a kernel module. Link to comment Share on other sites More sharing options...
barryha Posted April 4, 2014 Share Posted April 4, 2014 (edited) Hi all This is my first post on this forum. I have been following this thread for sometime. I have a h80i and a Command module with a cooling node. I am a software engineer (I do operating systems). I have had the need for support for the hardware under Linux (on my personal home system). So I decided to take all the great information supplied by other users and develop a driver, which I did. Since I did this off of research and the hard work of the other users. I am giving this work back to you all. The attached tar-ball has a readme file that should help get things going. Remember this is not a polished thing. While it has been working well (for two weeks), it is new code. The install processes is ruff at best... Also I think that if there is enough interest, it should get cleaned up some and ultimately get put into appropriate community projects... At this point I am getting good use out of it and I hope others can also... :biggrin: Barrycorsairdriver.zip Edited April 4, 2014 by barryha Link to comment Share on other sites More sharing options...
fredo Posted April 7, 2014 Share Posted April 7, 2014 Hi, I have switched one year ago to Linux and I found that it lacks the control over my cooler (H100i). Thanks dwmccauley's example I have created a working basic tool that allows the following: View fan and pump informationChange fan modeSet fan RPM (if fixed RPM mode is selected) The code is attached in the file and you can use the makefile in the Debug folder. I apologize in advance if I break any "Linux developing rules", but I am a .NET developer (yes I use Linux and develop on MS products) and I just got into C++. V. P.S. Let me know if you find bugs or issues with my code. This worked for me with a little bit of hacking. There was a hard path to the source code I had to change and I had to change the library it uses from libhidapi-hidraw to libhidapi-libusb. This is probably because I'm using CentOS6, which has an older kernel. Also, for some reason the program segmentation faults when it finishes. Thanks for the code. Link to comment Share on other sites More sharing options...
fredo Posted April 10, 2014 Share Posted April 10, 2014 Hey CFSworks; great job so far.... :cool: Some additional information to keep this thread moving in the positive direction. Corsair Link protocol overview As mentioned, the USB device uses a simple 64-byte HID "raw" report; the rawhid source code can be a useful starting point to establish low-level communication if a device isn't "just available" (e.g., on Windows). http://www.pjrc.com/teensy/rawhid.html The ReportID is always zero and is length-prefixed with a number of SMBus-like packets within the HID packet. Each packet has an index (which should be from 20~255), SMBus sub-packet type followed by any write data, if the sub-packet is writing data. Block types also have a length, and it appears the "quick" and "byte" modes are unsupported (only CmdByte, CmdWord and CmdBlock appear to be used). H80i/H100i Register set: [TABLE=head]__Number__|R/W|__Length__| Description 00 | R | 1 byte | Device ID - (H80 0x37, Cooling node 0x38, Lighting node 0x39, H100 0x3A, 80i 0x3B, 100i 0x3c -- this field as well as the version are common on all C-Link devices; but the rest aren't 01 | R | 2 bytes | Firmware Version in BCD (for example 1.0.5 is 0x1005, or 0x05, 0x10 in little endianess) 02 | R | 8 bytes | Product name, zero-terminated - only present on the H80i and H100i 03 | R | 1 | Status, 0 okay, 0xFF bad 04 | RW | 1 byte | Select current LED 05 | R | 1 byte | Number of LEDs 06 | RW | 1 byte | LED mode - 0x00 for static color, 0x40 for 2-color cycle, 0x80 for 4-color, 0xC0 for temperature mode; low nibble defines cycle speed or the temperature channel to use (0 internal sensor, 7 manual) 07 | R | 3 bytes | LED current color, RGB color of the selected LED 08 | RW | 2 bytes | In temperature controlled mode (0xC0) this defines the colour to use to with the below gradients 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 | RW | 2 bytes | Temperature limit (when the temperature goes over this, status is set to 0xff) 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; high bit is one when fan is detected, low bit is one when the fan is 4-pin, bits 6~4 define the temperautre channel to use in "curve" modes, 0 internal and 7 manual 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 | Maximum RPM recorded since power-on 18 | RW | 2 bytes | Fan under speed threshold 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. I'm adding some LED control and status to the C version of OpenCorsairLink. Can you give any more information about how register 08 works? Does it map colours to temperature ranges? How is the data encoded in this register? Link to comment Share on other sites More sharing options...
Thatualle1970 Posted May 14, 2014 Share Posted May 14, 2014 I'm adding some LED control and status to the C version of OpenCorsairLink. Can you give any more information about how register 08 works? Does it map colours to temperature ranges? How is the data encoded in this register? Yes. In a nutshell, there are three colours, cool, warm and hot. The temperature thresholds are defined in 09 and are 8.8 signed fixed-point. The colours are simple RGB values for each point and reside in register 0A. To set the temperature colour points, you'd do something like: for(int i=0; i<3; i++) { temp[i * 2] = (unsigned char)(temp.value[i] * 256.0f); temp[i * 2 + 1] = (unsigned char)temp_value[i]; } When you write the temperature to 08 while under temperature controlled mode, the H80i will interpolate that temperature to within the cool-warm-hot range and determine the RGB value automatically. Link to comment Share on other sites More sharing options...
Recommended Posts