2 * minimal program to read out data from Corsair RMi series of PSUs
5 * Copyright (c) notaz, 2016
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions are met:
9 * * Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * * Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 * * Neither the name of the organization nor the
15 * names of its contributors may be used to endorse or promote products
16 * derived from this software without specific prior written permission.
18 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
19 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21 * ARE DISCLAIMED. IN NO EVENT SHALL COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
22 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
24 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
25 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
26 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
27 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30 * register list from SIV by Ray Hinchliffe
39 * 40: e6 d3 00 ... (15.6; const?)
40 * 44: 1a d2 00 ... ( 8.4; const?)
41 * 46: 2c f1 00 ... (75.0; const?)
48 * d4: b9 bd eb fe 00 ... (32bit const?)
59 #include <sys/types.h>
62 #include <sys/ioctl.h>
65 #include <linux/hidraw.h>
67 static const uint16_t products[] = {
72 static void dump(const uint8_t *buf, size_t size)
76 for (i = 0; i < size; i += 16) {
77 for (j = 0; j < 16; j++) {
79 printf(" %02x", buf[i + j]);
84 for (j = 0; j < 16; j++) {
86 uint8_t c = buf[i + j];
87 printf("%c", 0x20 <= c && c <= 0x7f ? c : '.');
96 static void send_recv_cmd(int fd, uint8_t b0, uint8_t b1, uint8_t b2,
97 void *buf, size_t buf_size)
99 uint8_t buf_w[65], buf_r[64];
102 memset(buf_w, 0, sizeof(buf_w));
106 ret = write(fd, buf_w, sizeof(buf_w));
107 if (ret != sizeof(buf_w)) {
108 fprintf(stderr, "write %zd/%zd: ", ret, sizeof(buf_w));
113 ret = read(fd, buf_r, sizeof(buf_r));
114 if (ret != sizeof(buf_r)) {
115 fprintf(stderr, "read %zd/%zd: ", ret, sizeof(buf_r));
122 if (buf_r[0] != b0 || buf_r[1] != b1) {
123 fprintf(stderr, "unexpected response %02x %02x "
124 "to cmd %02x %02x %02x\n",
125 buf_r[0], buf_r[1], b0, b1, b2);
126 dump(buf_r, sizeof(buf_r));
130 if (buf != NULL && buf_size > 0) {
131 if (buf_size > sizeof(buf_r) - 2)
132 buf_size = sizeof(buf_r) - 2;
133 memcpy(buf, buf_r + 2, buf_size);
137 static void read_reg(int fd, uint8_t reg, void *buf, size_t buf_size)
139 send_recv_cmd(fd, 0x03, reg, 0x00, buf, buf_size);
142 static void read_reg16(int fd, uint8_t reg, uint16_t *v)
144 send_recv_cmd(fd, 0x03, reg, 0x00, v, sizeof(*v));
145 // FIXME: big endian host
148 static void read_reg32(int fd, uint8_t reg, uint32_t *v)
150 send_recv_cmd(fd, 0x03, reg, 0x00, v, sizeof(*v));
151 // FIXME: big endian host
154 static double mkv(uint16_t v16)
156 int p = (int)(int16_t)v16 >> 11;
157 int v = ((int)v16 << 21) >> 21;
159 return (double)v * pow(2.0, p);
162 static void print_std_reg(int fd, uint8_t reg, const char *fmt, ...)
168 read_reg16(fd, reg, &val);
171 len += vprintf(fmt, ap);
174 for (; len < 16; len++)
177 printf("%5.1f\n", mkv(val));
180 static int try_open_device(const char *name, int report_errors)
182 struct hidraw_devinfo info;
186 fd = open(name, O_RDWR);
189 fprintf(stderr, "open %s: ", name);
195 memset(&info, 0, sizeof(info));
196 ret = ioctl(fd, HIDIOCGRAWINFO, &info);
198 perror("HIDIOCGRAWINFO");
202 if (info.vendor != 0x1b1c)
205 for (i = 0; i < sizeof(products) / sizeof(products[0]); i++) {
206 if (info.product == products[i]) {
215 fprintf(stderr, "unexpected device: %04hx:%04hx\n",
216 info.vendor, info.product);
223 int main(int argc, char *argv[])
232 if (argv[1][0] == '-' || argc != 2) {
233 fprintf(stderr, "usage:\n");
234 fprintf(stderr, "%s [/dev/hidrawN]\n", argv[0]);
237 fd = try_open_device(argv[1], 1);
240 for (i = 0; i < 16; i++) {
241 snprintf(name, sizeof(name), "/dev/hidraw%d", i);
242 fd = try_open_device(name, 0);
249 fprintf(stderr, "No compatible devices found.\n");
251 fprintf(stderr, "At least one device "
252 "could not be checked because "
253 "of lack of permissions for "
261 name[sizeof(name) - 1] = 0;
262 send_recv_cmd(fd, 0xfe, 0x03, 0x00, name, sizeof(name) - 1);
263 printf("name: '%s'\n", name);
264 read_reg(fd, 0x99, name, sizeof(name) - 1);
265 printf("vendor: '%s'\n", name);
266 read_reg(fd, 0x9a, name, sizeof(name) - 1);
267 printf("product: '%s'\n", name);
269 read_reg32(fd, 0xd1, &v32);
270 printf("powered: %u (%dd. %dh)\n",
271 v32, v32 / (24*60*60), v32 / (60*60) % 24);
272 read_reg32(fd, 0xd2, &v32);
273 printf("uptime: %u (%dd. %dh)\n",
274 v32, v32 / (24*60*60), v32 / (60*60) % 24);
276 print_std_reg(fd, 0x8d, "temp1");
277 print_std_reg(fd, 0x8e, "temp2");
278 print_std_reg(fd, 0x90, "fan rpm");
279 print_std_reg(fd, 0x88, "supply volts");
280 print_std_reg(fd, 0xee, "total watts");
282 for (osel = 0; osel < 3; osel++) {
283 // reg0 write (output select)
284 send_recv_cmd(fd, 0x02, 0x00, osel, NULL, 0);
285 print_std_reg(fd, 0x8b, "output%u volts", osel);
286 print_std_reg(fd, 0x8c, "output%u amps", osel);
287 print_std_reg(fd, 0x96, "output%u watts", osel);
290 send_recv_cmd(fd, 0x02, 0x00, 0x00, NULL, 0);