find device when no args are given, accept RM850i
[corsairmi.git] / corsairmi.c
CommitLineData
472ae0c2 1/*
2 * minimal program to read out data from Corsair RMi series of PSUs
3 * tested on RM750i
4 *
5 * Copyright (c) notaz, 2016
6 *
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.
17 *
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.
28 */
29/*
30 * register list from SIV by Ray Hinchliffe
31 *
32 * left unimplemented:
33 * 3a fan mode
34 * 3b fan pwm
35 * 81 fan status
36 * f0 fan1 mode
37 *
38 * left unknown:
39 * 40: e6 d3 00 ... (15.6; const?)
40 * 44: 1a d2 00 ... ( 8.4; const?)
41 * 46: 2c f1 00 ... (75.0; const?)
42 * 4f: 46 00 ...
43 * 7a: 00 ...
44 * 7b: 00 ...
45 * 7d: 00 ...
46 * 7e: c0 00 ...
47 * c4: 01 00 ...
48 * d4: b9 bd eb fe 00 ... (32bit const?)
49 * d8: 02 00 ...
50 * d9: 00 ...
51 */
52
53#include <stdio.h>
54#include <stdlib.h>
55#include <string.h>
56#include <stdarg.h>
57#include <stdint.h>
58#include <math.h>
59#include <sys/types.h>
60#include <sys/stat.h>
61#include <fcntl.h>
62#include <sys/ioctl.h>
63#include <unistd.h>
64#include <linux/hidraw.h>
65
465ea922 66static const uint16_t products[] = {
67 0x1c0b, /* RM750i */
68 0x1c0c, /* RM850i */
69};
70
472ae0c2 71static void dump(const uint8_t *buf, size_t size)
72{
73 size_t i, j;
74
75 for (i = 0; i < size; i += 16) {
76 for (j = 0; j < 16; j++) {
77 if (i + j < size)
78 printf(" %02x", buf[i + j]);
79 else
80 fputs(" ", stdout);
81 }
82 fputs(" ", stdout);
83 for (j = 0; j < 16; j++) {
84 if (i + j < size) {
85 uint8_t c = buf[i + j];
86 printf("%c", 0x20 <= c && c <= 0x7f ? c : '.');
87 }
88 else
89 break;
90 }
91 puts("");
92 }
93}
94
95static void send_recv_cmd(int fd, uint8_t b0, uint8_t b1, uint8_t b2,
96 void *buf, size_t buf_size)
97{
98 uint8_t buf_w[65], buf_r[64];
99 ssize_t ret;
100
101 memset(buf_w, 0, sizeof(buf_w));
102 buf_w[1] = b0;
103 buf_w[2] = b1;
104 buf_w[3] = b2;
105 ret = write(fd, buf_w, sizeof(buf_w));
106 if (ret != sizeof(buf_w)) {
107 fprintf(stderr, "write %zd/%zd: ", ret, sizeof(buf_w));
108 perror(NULL);
109 exit(1);
110 }
111
112 ret = read(fd, buf_r, sizeof(buf_r));
113 if (ret != sizeof(buf_r)) {
114 fprintf(stderr, "read %zd/%zd: ", ret, sizeof(buf_r));
115 perror(NULL);
116 if (ret > 0)
117 dump(buf_r, ret);
118 exit(1);
119 }
120
121 if (buf_r[0] != b0 || buf_r[1] != b1) {
122 fprintf(stderr, "unexpected response %02x %02x "
123 "to cmd %02x %02x %02x\n",
124 buf_r[0], buf_r[1], b0, b1, b2);
125 dump(buf_r, sizeof(buf_r));
126 exit(1);
127 }
128
129 if (buf != NULL && buf_size > 0) {
130 if (buf_size > sizeof(buf_r) - 2)
131 buf_size = sizeof(buf_r) - 2;
132 memcpy(buf, buf_r + 2, buf_size);
133 }
134}
135
136static void read_reg(int fd, uint8_t reg, void *buf, size_t buf_size)
137{
138 send_recv_cmd(fd, 0x03, reg, 0x00, buf, buf_size);
139}
140
141static void read_reg16(int fd, uint8_t reg, uint16_t *v)
142{
143 send_recv_cmd(fd, 0x03, reg, 0x00, v, sizeof(*v));
144 // FIXME: big endian host
145}
146
147static void read_reg32(int fd, uint8_t reg, uint32_t *v)
148{
149 send_recv_cmd(fd, 0x03, reg, 0x00, v, sizeof(*v));
150 // FIXME: big endian host
151}
152
153static double mkv(uint16_t v16)
154{
155 int p = (int)(int16_t)v16 >> 11;
156 int v = ((int)v16 << 21) >> 21;
157
158 return (double)v * pow(2.0, p);
159}
160
161static void print_std_reg(int fd, uint8_t reg, const char *fmt, ...)
162{
163 size_t len = 0;
164 uint16_t val;
165 va_list ap;
166
167 read_reg16(fd, reg, &val);
168
169 va_start(ap, fmt);
170 len += vprintf(fmt, ap);
171 len += printf(": ");
172 va_end(ap);
173 for (; len < 16; len++)
174 fputs(" ", stdout);
175
176 printf("%5.1f\n", mkv(val));
177}
178
465ea922 179static int try_open_device(const char *name, int report_errors)
472ae0c2 180{
472ae0c2 181 struct hidraw_devinfo info;
465ea922 182 int found = 0;
183 int i, ret, fd;
184
185 fd = open(name, O_RDWR);
186 if (fd == -1) {
187 if (report_errors) {
188 fprintf(stderr, "open %s: ", name);
189 perror(NULL);
190 }
191 return -1;
192 }
193
194 memset(&info, 0, sizeof(info));
195 ret = ioctl(fd, HIDIOCGRAWINFO, &info);
196 if (ret != 0) {
197 perror("HIDIOCGRAWINFO");
198 goto out;
199 }
200
201 if (info.vendor != 0x1b1c)
202 goto out;
203
204 for (i = 0; i < sizeof(products) / sizeof(products[0]); i++) {
205 if (info.product == products[i]) {
206 found = 1;
207 break;
208 }
209 }
210
211out:
212 if (!found) {
213 if (report_errors)
214 fprintf(stderr, "unexpected device: %04hx:%04hx\n",
215 info.vendor, info.product);
216 close(fd);
217 fd = -1;
218 }
219 return fd;
220}
221
222int main(int argc, char *argv[])
223{
472ae0c2 224 char name[63];
225 uint32_t v32;
226 uint8_t osel;
465ea922 227 int i, fd;
472ae0c2 228
229 if (argc > 1) {
465ea922 230 if (argv[1][0] == '-' || argc != 2) {
472ae0c2 231 fprintf(stderr, "usage:\n");
465ea922 232 fprintf(stderr, "%s [/dev/hidrawN]\n", argv[0]);
472ae0c2 233 return 1;
234 }
465ea922 235 fd = try_open_device(argv[1], 1);
472ae0c2 236 }
465ea922 237 else {
238 for (i = 0; i < 16; i++) {
239 snprintf(name, sizeof(name), "/dev/hidraw%d", i);
240 fd = try_open_device(name, 0);
241 if (fd != -1)
242 break;
243 }
472ae0c2 244 }
245
465ea922 246 if (fd == -1)
472ae0c2 247 return 1;
472ae0c2 248
249 name[sizeof(name) - 1] = 0;
250 send_recv_cmd(fd, 0xfe, 0x03, 0x00, name, sizeof(name) - 1);
251 printf("name: '%s'\n", name);
252 read_reg(fd, 0x99, name, sizeof(name) - 1);
253 printf("vendor: '%s'\n", name);
254 read_reg(fd, 0x9a, name, sizeof(name) - 1);
255 printf("product: '%s'\n", name);
256
257 read_reg32(fd, 0xd1, &v32);
258 printf("powered: %u (%dd. %dh)\n",
259 v32, v32 / (24*60*60), v32 / (60*60) % 24);
260 read_reg32(fd, 0xd2, &v32);
261 printf("uptime: %u (%dd. %dh)\n",
262 v32, v32 / (24*60*60), v32 / (60*60) % 24);
263
264 print_std_reg(fd, 0x8d, "temp1");
265 print_std_reg(fd, 0x8e, "temp2");
266 print_std_reg(fd, 0x90, "fan rpm");
267 print_std_reg(fd, 0x88, "supply volts");
268 print_std_reg(fd, 0xee, "total watts");
269
270 for (osel = 0; osel < 3; osel++) {
271 // reg0 write (output select)
272 send_recv_cmd(fd, 0x02, 0x00, osel, NULL, 0);
273 print_std_reg(fd, 0x8b, "output%u volts", osel);
274 print_std_reg(fd, 0x8c, "output%u amps", osel);
275 print_std_reg(fd, 0x96, "output%u watts", osel);
276 }
277
278 send_recv_cmd(fd, 0x02, 0x00, 0x00, NULL, 0);
279
280 close(fd);
281 return 0;
282}