From 472ae0c2a24c142d98afacbc9bfa52683a7b6be5 Mon Sep 17 00:00:00 2001 From: notaz Date: Mon, 18 Jan 2016 00:20:13 +0200 Subject: [PATCH 1/1] initial commit --- LICENSE | 23 +++++ Makefile | 11 +++ README.md | 12 +++ corsairmi.c | 245 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 291 insertions(+) create mode 100644 LICENSE create mode 100644 Makefile create mode 100644 README.md create mode 100644 corsairmi.c diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..d12828d --- /dev/null +++ b/LICENSE @@ -0,0 +1,23 @@ + * Copyright (c) notaz, 2016 + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the organization nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..6383151 --- /dev/null +++ b/Makefile @@ -0,0 +1,11 @@ +CFLAGS += -Wall -ggdb -O2 +LDLIBS += -lm + +TARGET = corsairmi + +all: $(TARGET) + +clean: + $(RM) $(TARGET) + +.PHONY: all clean diff --git a/README.md b/README.md new file mode 100644 index 0000000..932d636 --- /dev/null +++ b/README.md @@ -0,0 +1,12 @@ +corsaiRMi +========= +Minimal program for Linux to read monitoring information out of Corsair RMi series of PSUs. Uses Linux HIDRAW interface. +Tested on Corsair RM750i. + +Compiling +--------- +There are basically no dependencies, so running `make` should suffice. + +Usage +----- +`./corsairmi /dev/hidrawN` diff --git a/corsairmi.c b/corsairmi.c new file mode 100644 index 0000000..e09d80b --- /dev/null +++ b/corsairmi.c @@ -0,0 +1,245 @@ +/* + * minimal program to read out data from Corsair RMi series of PSUs + * tested on RM750i + * + * Copyright (c) notaz, 2016 + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the organization nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +/* + * register list from SIV by Ray Hinchliffe + * + * left unimplemented: + * 3a fan mode + * 3b fan pwm + * 81 fan status + * f0 fan1 mode + * + * left unknown: + * 40: e6 d3 00 ... (15.6; const?) + * 44: 1a d2 00 ... ( 8.4; const?) + * 46: 2c f1 00 ... (75.0; const?) + * 4f: 46 00 ... + * 7a: 00 ... + * 7b: 00 ... + * 7d: 00 ... + * 7e: c0 00 ... + * c4: 01 00 ... + * d4: b9 bd eb fe 00 ... (32bit const?) + * d8: 02 00 ... + * d9: 00 ... + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static void dump(const uint8_t *buf, size_t size) +{ + size_t i, j; + + for (i = 0; i < size; i += 16) { + for (j = 0; j < 16; j++) { + if (i + j < size) + printf(" %02x", buf[i + j]); + else + fputs(" ", stdout); + } + fputs(" ", stdout); + for (j = 0; j < 16; j++) { + if (i + j < size) { + uint8_t c = buf[i + j]; + printf("%c", 0x20 <= c && c <= 0x7f ? c : '.'); + } + else + break; + } + puts(""); + } +} + +static void send_recv_cmd(int fd, uint8_t b0, uint8_t b1, uint8_t b2, + void *buf, size_t buf_size) +{ + uint8_t buf_w[65], buf_r[64]; + ssize_t ret; + + memset(buf_w, 0, sizeof(buf_w)); + buf_w[1] = b0; + buf_w[2] = b1; + buf_w[3] = b2; + ret = write(fd, buf_w, sizeof(buf_w)); + if (ret != sizeof(buf_w)) { + fprintf(stderr, "write %zd/%zd: ", ret, sizeof(buf_w)); + perror(NULL); + exit(1); + } + + ret = read(fd, buf_r, sizeof(buf_r)); + if (ret != sizeof(buf_r)) { + fprintf(stderr, "read %zd/%zd: ", ret, sizeof(buf_r)); + perror(NULL); + if (ret > 0) + dump(buf_r, ret); + exit(1); + } + + if (buf_r[0] != b0 || buf_r[1] != b1) { + fprintf(stderr, "unexpected response %02x %02x " + "to cmd %02x %02x %02x\n", + buf_r[0], buf_r[1], b0, b1, b2); + dump(buf_r, sizeof(buf_r)); + exit(1); + } + + if (buf != NULL && buf_size > 0) { + if (buf_size > sizeof(buf_r) - 2) + buf_size = sizeof(buf_r) - 2; + memcpy(buf, buf_r + 2, buf_size); + } +} + +static void read_reg(int fd, uint8_t reg, void *buf, size_t buf_size) +{ + send_recv_cmd(fd, 0x03, reg, 0x00, buf, buf_size); +} + +static void read_reg16(int fd, uint8_t reg, uint16_t *v) +{ + send_recv_cmd(fd, 0x03, reg, 0x00, v, sizeof(*v)); + // FIXME: big endian host +} + +static void read_reg32(int fd, uint8_t reg, uint32_t *v) +{ + send_recv_cmd(fd, 0x03, reg, 0x00, v, sizeof(*v)); + // FIXME: big endian host +} + +static double mkv(uint16_t v16) +{ + int p = (int)(int16_t)v16 >> 11; + int v = ((int)v16 << 21) >> 21; + + return (double)v * pow(2.0, p); +} + +static void print_std_reg(int fd, uint8_t reg, const char *fmt, ...) +{ + size_t len = 0; + uint16_t val; + va_list ap; + + read_reg16(fd, reg, &val); + + va_start(ap, fmt); + len += vprintf(fmt, ap); + len += printf(": "); + va_end(ap); + for (; len < 16; len++) + fputs(" ", stdout); + + printf("%5.1f\n", mkv(val)); +} + +int main(int argc, char *argv[]) +{ + const char *device = "/dev/hidraw0"; + struct hidraw_devinfo info; + char name[63]; + uint32_t v32; + uint8_t osel; + int ret, fd; + + if (argc > 1) { + if (argv[1][0] == '-') { + fprintf(stderr, "usage:\n"); + fprintf(stderr, "%s /dev/hidrawN\n", argv[0]); + return 1; + } + device = argv[1]; + } + + fd = open(device, O_RDWR); + if (fd == -1) { + fprintf(stderr, "open %s: ", device); + perror(NULL); + return 1; + } + + memset(&info, 0, sizeof(info)); + ret = ioctl(fd, HIDIOCGRAWINFO, &info); + if (ret != 0) { + perror("HIDIOCGRAWINFO"); + return 1; + } + + if (info.vendor != 0x1b1c && info.product != 0x1c0b) { + fprintf(stderr, "unexpected device: %04hx:%04hx\n", + info.vendor, info.product); + return 1; + } + + name[sizeof(name) - 1] = 0; + send_recv_cmd(fd, 0xfe, 0x03, 0x00, name, sizeof(name) - 1); + printf("name: '%s'\n", name); + read_reg(fd, 0x99, name, sizeof(name) - 1); + printf("vendor: '%s'\n", name); + read_reg(fd, 0x9a, name, sizeof(name) - 1); + printf("product: '%s'\n", name); + + read_reg32(fd, 0xd1, &v32); + printf("powered: %u (%dd. %dh)\n", + v32, v32 / (24*60*60), v32 / (60*60) % 24); + read_reg32(fd, 0xd2, &v32); + printf("uptime: %u (%dd. %dh)\n", + v32, v32 / (24*60*60), v32 / (60*60) % 24); + + print_std_reg(fd, 0x8d, "temp1"); + print_std_reg(fd, 0x8e, "temp2"); + print_std_reg(fd, 0x90, "fan rpm"); + print_std_reg(fd, 0x88, "supply volts"); + print_std_reg(fd, 0xee, "total watts"); + + for (osel = 0; osel < 3; osel++) { + // reg0 write (output select) + send_recv_cmd(fd, 0x02, 0x00, osel, NULL, 0); + print_std_reg(fd, 0x8b, "output%u volts", osel); + print_std_reg(fd, 0x8c, "output%u amps", osel); + print_std_reg(fd, 0x96, "output%u watts", osel); + } + + send_recv_cmd(fd, 0x02, 0x00, 0x00, NULL, 0); + + close(fd); + return 0; +} -- 2.39.5