1 // SPDX-License-Identifier: GPL-2.0-or-later 2 /* 3 * corsair-psu.c - Linux driver for Corsair power supplies with HID sensors interface 4 * Copyright (C) 2020 Wilken Gottwalt <wilken.gottwalt@posteo.net> 5 */ 6 7 #include <linux/completion.h> 8 #include <linux/debugfs.h> 9 #include <linux/errno.h> 10 #include <linux/hid.h> 11 #include <linux/hwmon.h> 12 #include <linux/hwmon-sysfs.h> 13 #include <linux/jiffies.h> 14 #include <linux/kernel.h> 15 #include <linux/module.h> 16 #include <linux/mutex.h> 17 #include <linux/slab.h> 18 #include <linux/types.h> 19 20 /* 21 * Corsair protocol for PSUs 22 * 23 * message size = 64 bytes (request and response, little endian) 24 * request: 25 * [length][command][param0][param1][paramX]... 26 * reply: 27 * [echo of length][echo of command][data0][data1][dataX]... 28 * 29 * - commands are byte sized opcodes 30 * - length is the sum of all bytes of the commands/params 31 * - the micro-controller of most of these PSUs support concatenation in the request and reply, 32 * but it is better to not rely on this (it is also hard to parse) 33 * - the driver uses raw events to be accessible from userspace (though this is not really 34 * supported, it is just there for convenience, may be removed in the future) 35 * - a reply always start with the length and command in the same order the request used it 36 * - length of the reply data is specific to the command used 37 * - some of the commands work on a rail and can be switched to a specific rail (0 = 12v, 38 * 1 = 5v, 2 = 3.3v) 39 * - the format of the init command 0xFE is swapped length/command bytes 40 * - parameter bytes amount and values are specific to the command (rail setting is the only 41 * for now that uses non-zero values) 42 * - there are much more commands, especially for configuring the device, but they are not 43 * supported because a wrong command/length can lockup the micro-controller 44 * - the driver supports debugfs for values not fitting into the hwmon class 45 * - not every device class (HXi, RMi or AXi) supports all commands 46 * - it is a pure sensors reading driver (will not support configuring) 47 */ 48 49 #define DRIVER_NAME "corsair-psu" 50 51 #define REPLY_SIZE 16 /* max length of a reply to a single command */ 52 #define CMD_BUFFER_SIZE 64 53 #define CMD_TIMEOUT_MS 250 54 #define SECONDS_PER_HOUR (60 * 60) 55 #define SECONDS_PER_DAY (SECONDS_PER_HOUR * 24) 56 57 #define PSU_CMD_SELECT_RAIL 0x00 /* expects length 2 */ 58 #define PSU_CMD_IN_VOLTS 0x88 /* the rest of the commands expect length 3 */ 59 #define PSU_CMD_IN_AMPS 0x89 60 #define PSU_CMD_RAIL_OUT_VOLTS 0x8B 61 #define PSU_CMD_RAIL_AMPS 0x8C 62 #define PSU_CMD_TEMP0 0x8D 63 #define PSU_CMD_TEMP1 0x8E 64 #define PSU_CMD_FAN 0x90 65 #define PSU_CMD_RAIL_WATTS 0x96 66 #define PSU_CMD_VEND_STR 0x99 67 #define PSU_CMD_PROD_STR 0x9A 68 #define PSU_CMD_TOTAL_WATTS 0xEE 69 #define PSU_CMD_TOTAL_UPTIME 0xD1 70 #define PSU_CMD_UPTIME 0xD2 71 #define PSU_CMD_INIT 0xFE 72 73 #define L_IN_VOLTS "v_in" 74 #define L_OUT_VOLTS_12V "v_out +12v" 75 #define L_OUT_VOLTS_5V "v_out +5v" 76 #define L_OUT_VOLTS_3_3V "v_out +3.3v" 77 #define L_IN_AMPS "curr in" 78 #define L_AMPS_12V "curr +12v" 79 #define L_AMPS_5V "curr +5v" 80 #define L_AMPS_3_3V "curr +3.3v" 81 #define L_FAN "psu fan" 82 #define L_TEMP0 "vrm temp" 83 #define L_TEMP1 "case temp" 84 #define L_WATTS "power total" 85 #define L_WATTS_12V "power +12v" 86 #define L_WATTS_5V "power +5v" 87 #define L_WATTS_3_3V "power +3.3v" 88 89 static const char *const label_watts[] = { 90 L_WATTS, 91 L_WATTS_12V, 92 L_WATTS_5V, 93 L_WATTS_3_3V 94 }; 95 96 static const char *const label_volts[] = { 97 L_IN_VOLTS, 98 L_OUT_VOLTS_12V, 99 L_OUT_VOLTS_5V, 100 L_OUT_VOLTS_3_3V 101 }; 102 103 static const char *const label_amps[] = { 104 L_IN_AMPS, 105 L_AMPS_12V, 106 L_AMPS_5V, 107 L_AMPS_3_3V 108 }; 109 110 struct corsairpsu_data { 111 struct hid_device *hdev; 112 struct device *hwmon_dev; 113 struct dentry *debugfs; 114 struct completion wait_completion; 115 struct mutex lock; /* for locking access to cmd_buffer */ 116 u8 *cmd_buffer; 117 char vendor[REPLY_SIZE]; 118 char product[REPLY_SIZE]; 119 }; 120 121 /* some values are SMBus LINEAR11 data which need a conversion */ 122 static int corsairpsu_linear11_to_int(const int val) 123 { 124 int exp = (val & 0xFFFF) >> 0x0B; 125 int mant = val & 0x7FF; 126 int i; 127 128 if (exp > 0x0F) 129 exp -= 0x20; 130 if (mant > 0x3FF) 131 mant -= 0x800; 132 if ((mant & 0x01) == 1) 133 ++mant; 134 if (exp < 0) { 135 for (i = 0; i < -exp; ++i) 136 mant /= 2; 137 } else { 138 for (i = 0; i < exp; ++i) 139 mant *= 2; 140 } 141 142 return mant; 143 } 144 145 static int corsairpsu_usb_cmd(struct corsairpsu_data *priv, u8 p0, u8 p1, u8 p2, void *data) 146 { 147 unsigned long time; 148 int ret; 149 150 memset(priv->cmd_buffer, 0, CMD_BUFFER_SIZE); 151 priv->cmd_buffer[0] = p0; 152 priv->cmd_buffer[1] = p1; 153 priv->cmd_buffer[2] = p2; 154 155 reinit_completion(&priv->wait_completion); 156 157 ret = hid_hw_output_report(priv->hdev, priv->cmd_buffer, CMD_BUFFER_SIZE); 158 if (ret < 0) 159 return ret; 160 161 time = wait_for_completion_timeout(&priv->wait_completion, 162 msecs_to_jiffies(CMD_TIMEOUT_MS)); 163 if (!time) 164 return -ETIMEDOUT; 165 166 /* 167 * at the start of the reply is an echo of the send command/length in the same order it 168 * was send, not every command is supported on every device class, if a command is not 169 * supported, the length value in the reply is okay, but the command value is set to 0 170 */ 171 if (p0 != priv->cmd_buffer[0] || p1 != priv->cmd_buffer[1]) 172 return -EOPNOTSUPP; 173 174 if (data) 175 memcpy(data, priv->cmd_buffer + 2, REPLY_SIZE); 176 177 return 0; 178 } 179 180 static int corsairpsu_init(struct corsairpsu_data *priv) 181 { 182 /* 183 * PSU_CMD_INIT uses swapped length/command and expects 2 parameter bytes, this command 184 * actually generates a reply, but we don't need it 185 */ 186 return corsairpsu_usb_cmd(priv, PSU_CMD_INIT, 3, 0, NULL); 187 } 188 189 static int corsairpsu_fwinfo(struct corsairpsu_data *priv) 190 { 191 int ret; 192 193 ret = corsairpsu_usb_cmd(priv, 3, PSU_CMD_VEND_STR, 0, priv->vendor); 194 if (ret < 0) 195 return ret; 196 197 ret = corsairpsu_usb_cmd(priv, 3, PSU_CMD_PROD_STR, 0, priv->product); 198 if (ret < 0) 199 return ret; 200 201 return 0; 202 } 203 204 static int corsairpsu_request(struct corsairpsu_data *priv, u8 cmd, u8 rail, void *data) 205 { 206 int ret; 207 208 mutex_lock(&priv->lock); 209 switch (cmd) { 210 case PSU_CMD_RAIL_OUT_VOLTS: 211 case PSU_CMD_RAIL_AMPS: 212 case PSU_CMD_RAIL_WATTS: 213 ret = corsairpsu_usb_cmd(priv, 2, PSU_CMD_SELECT_RAIL, rail, NULL); 214 if (ret < 0) 215 goto cmd_fail; 216 break; 217 default: 218 break; 219 } 220 221 ret = corsairpsu_usb_cmd(priv, 3, cmd, 0, data); 222 223 cmd_fail: 224 mutex_unlock(&priv->lock); 225 return ret; 226 } 227 228 static int corsairpsu_get_value(struct corsairpsu_data *priv, u8 cmd, u8 rail, long *val) 229 { 230 u8 data[REPLY_SIZE]; 231 long tmp; 232 int ret; 233 234 ret = corsairpsu_request(priv, cmd, rail, data); 235 if (ret < 0) 236 return ret; 237 238 /* 239 * the biggest value here comes from the uptime command and to exceed MAXINT total uptime 240 * needs to be about 68 years, the rest are u16 values and the biggest value coming out of 241 * the LINEAR11 conversion are the watts values which are about 1200 for the strongest psu 242 * supported (HX1200i) 243 */ 244 tmp = ((long)data[3] << 24) + (data[2] << 16) + (data[1] << 8) + data[0]; 245 switch (cmd) { 246 case PSU_CMD_IN_VOLTS: 247 case PSU_CMD_IN_AMPS: 248 case PSU_CMD_RAIL_OUT_VOLTS: 249 case PSU_CMD_RAIL_AMPS: 250 case PSU_CMD_TEMP0: 251 case PSU_CMD_TEMP1: 252 *val = corsairpsu_linear11_to_int(tmp & 0xFFFF) * 1000; 253 break; 254 case PSU_CMD_FAN: 255 /* 256 * this value is best guess, so the calculated value could be wrong, it is hard 257 * to ge the fan to spin in these semi-passive power supplies, which need a 258 * quite high load to do so 259 */ 260 *val = ((tmp & 0xFF) << 8) + ((tmp >> 8) & 0xFF); 261 break; 262 case PSU_CMD_RAIL_WATTS: 263 case PSU_CMD_TOTAL_WATTS: 264 *val = corsairpsu_linear11_to_int(tmp & 0xFFFF) * 1000000; 265 break; 266 case PSU_CMD_TOTAL_UPTIME: 267 case PSU_CMD_UPTIME: 268 *val = tmp; 269 break; 270 default: 271 ret = -EOPNOTSUPP; 272 break; 273 } 274 275 return ret; 276 } 277 278 static umode_t corsairpsu_hwmon_ops_is_visible(const void *data, enum hwmon_sensor_types type, 279 u32 attr, int channel) 280 { 281 if (type == hwmon_temp && (attr == hwmon_temp_input || attr == hwmon_temp_label)) 282 return 0444; 283 else if (type == hwmon_fan && (attr == hwmon_fan_input || attr == hwmon_fan_label)) 284 return 0444; 285 else if (type == hwmon_power && (attr == hwmon_power_input || attr == hwmon_power_label)) 286 return 0444; 287 else if (type == hwmon_in && (attr == hwmon_in_input || attr == hwmon_in_label)) 288 return 0444; 289 else if (type == hwmon_curr && (attr == hwmon_curr_input || attr == hwmon_curr_label)) 290 return 0444; 291 292 return 0; 293 } 294 295 static int corsairpsu_hwmon_ops_read(struct device *dev, enum hwmon_sensor_types type, u32 attr, 296 int channel, long *val) 297 { 298 struct corsairpsu_data *priv = dev_get_drvdata(dev); 299 int ret; 300 301 if (type == hwmon_temp && attr == hwmon_temp_input && channel < 2) { 302 ret = corsairpsu_get_value(priv, channel ? PSU_CMD_TEMP1 : PSU_CMD_TEMP0, channel, 303 val); 304 } else if (type == hwmon_fan && attr == hwmon_fan_input) { 305 ret = corsairpsu_get_value(priv, PSU_CMD_FAN, 0, val); 306 } else if (type == hwmon_power && attr == hwmon_power_input) { 307 switch (channel) { 308 case 0: 309 ret = corsairpsu_get_value(priv, PSU_CMD_TOTAL_WATTS, 0, val); 310 break; 311 case 1 ... 3: 312 ret = corsairpsu_get_value(priv, PSU_CMD_RAIL_WATTS, channel - 1, val); 313 break; 314 default: 315 return -EOPNOTSUPP; 316 } 317 } else if (type == hwmon_in && attr == hwmon_in_input) { 318 switch (channel) { 319 case 0: 320 ret = corsairpsu_get_value(priv, PSU_CMD_IN_VOLTS, 0, val); 321 break; 322 case 1 ... 3: 323 ret = corsairpsu_get_value(priv, PSU_CMD_RAIL_OUT_VOLTS, channel - 1, val); 324 break; 325 default: 326 return -EOPNOTSUPP; 327 } 328 } else if (type == hwmon_curr && attr == hwmon_curr_input) { 329 switch (channel) { 330 case 0: 331 ret = corsairpsu_get_value(priv, PSU_CMD_IN_AMPS, 0, val); 332 break; 333 case 1 ... 3: 334 ret = corsairpsu_get_value(priv, PSU_CMD_RAIL_AMPS, channel - 1, val); 335 break; 336 default: 337 return -EOPNOTSUPP; 338 } 339 } else { 340 return -EOPNOTSUPP; 341 } 342 343 if (ret < 0) 344 return ret; 345 346 return 0; 347 } 348 349 static int corsairpsu_hwmon_ops_read_string(struct device *dev, enum hwmon_sensor_types type, 350 u32 attr, int channel, const char **str) 351 { 352 if (type == hwmon_temp && attr == hwmon_temp_label) { 353 *str = channel ? L_TEMP1 : L_TEMP0; 354 return 0; 355 } else if (type == hwmon_fan && attr == hwmon_fan_label) { 356 *str = L_FAN; 357 return 0; 358 } else if (type == hwmon_power && attr == hwmon_power_label && channel < 4) { 359 *str = label_watts[channel]; 360 return 0; 361 } else if (type == hwmon_in && attr == hwmon_in_label && channel < 4) { 362 *str = label_volts[channel]; 363 return 0; 364 } else if (type == hwmon_curr && attr == hwmon_curr_label && channel < 4) { 365 *str = label_amps[channel]; 366 return 0; 367 } 368 369 return -EOPNOTSUPP; 370 } 371 372 static const struct hwmon_ops corsairpsu_hwmon_ops = { 373 .is_visible = corsairpsu_hwmon_ops_is_visible, 374 .read = corsairpsu_hwmon_ops_read, 375 .read_string = corsairpsu_hwmon_ops_read_string, 376 }; 377 378 static const struct hwmon_channel_info *corsairpsu_info[] = { 379 HWMON_CHANNEL_INFO(chip, 380 HWMON_C_REGISTER_TZ), 381 HWMON_CHANNEL_INFO(temp, 382 HWMON_T_INPUT | HWMON_T_LABEL, 383 HWMON_T_INPUT | HWMON_T_LABEL), 384 HWMON_CHANNEL_INFO(fan, 385 HWMON_F_INPUT | HWMON_F_LABEL), 386 HWMON_CHANNEL_INFO(power, 387 HWMON_P_INPUT | HWMON_P_LABEL, 388 HWMON_P_INPUT | HWMON_P_LABEL, 389 HWMON_P_INPUT | HWMON_P_LABEL, 390 HWMON_P_INPUT | HWMON_P_LABEL), 391 HWMON_CHANNEL_INFO(in, 392 HWMON_I_INPUT | HWMON_I_LABEL, 393 HWMON_I_INPUT | HWMON_I_LABEL, 394 HWMON_I_INPUT | HWMON_I_LABEL, 395 HWMON_I_INPUT | HWMON_I_LABEL), 396 HWMON_CHANNEL_INFO(curr, 397 HWMON_C_INPUT | HWMON_C_LABEL, 398 HWMON_C_INPUT | HWMON_C_LABEL, 399 HWMON_C_INPUT | HWMON_C_LABEL, 400 HWMON_C_INPUT | HWMON_C_LABEL), 401 NULL 402 }; 403 404 static const struct hwmon_chip_info corsairpsu_chip_info = { 405 .ops = &corsairpsu_hwmon_ops, 406 .info = corsairpsu_info, 407 }; 408 409 #ifdef CONFIG_DEBUG_FS 410 411 static void print_uptime(struct seq_file *seqf, u8 cmd) 412 { 413 struct corsairpsu_data *priv = seqf->private; 414 long val; 415 int ret; 416 417 ret = corsairpsu_get_value(priv, cmd, 0, &val); 418 if (ret < 0) { 419 seq_puts(seqf, "N/A\n"); 420 return; 421 } 422 423 if (val > SECONDS_PER_DAY) { 424 seq_printf(seqf, "%ld day(s), %02ld:%02ld:%02ld\n", val / SECONDS_PER_DAY, 425 val % SECONDS_PER_DAY / SECONDS_PER_HOUR, val % SECONDS_PER_HOUR / 60, 426 val % 60); 427 return; 428 } 429 430 seq_printf(seqf, "%02ld:%02ld:%02ld\n", val % SECONDS_PER_DAY / SECONDS_PER_HOUR, 431 val % SECONDS_PER_HOUR / 60, val % 60); 432 } 433 434 static int uptime_show(struct seq_file *seqf, void *unused) 435 { 436 print_uptime(seqf, PSU_CMD_UPTIME); 437 438 return 0; 439 } 440 DEFINE_SHOW_ATTRIBUTE(uptime); 441 442 static int uptime_total_show(struct seq_file *seqf, void *unused) 443 { 444 print_uptime(seqf, PSU_CMD_TOTAL_UPTIME); 445 446 return 0; 447 } 448 DEFINE_SHOW_ATTRIBUTE(uptime_total); 449 450 static int vendor_show(struct seq_file *seqf, void *unused) 451 { 452 struct corsairpsu_data *priv = seqf->private; 453 454 seq_printf(seqf, "%s\n", priv->vendor); 455 456 return 0; 457 } 458 DEFINE_SHOW_ATTRIBUTE(vendor); 459 460 static int product_show(struct seq_file *seqf, void *unused) 461 { 462 struct corsairpsu_data *priv = seqf->private; 463 464 seq_printf(seqf, "%s\n", priv->product); 465 466 return 0; 467 } 468 DEFINE_SHOW_ATTRIBUTE(product); 469 470 static void corsairpsu_debugfs_init(struct corsairpsu_data *priv) 471 { 472 char name[32]; 473 474 scnprintf(name, sizeof(name), "%s-%s", DRIVER_NAME, dev_name(&priv->hdev->dev)); 475 476 priv->debugfs = debugfs_create_dir(name, NULL); 477 debugfs_create_file("uptime", 0444, priv->debugfs, priv, &uptime_fops); 478 debugfs_create_file("uptime_total", 0444, priv->debugfs, priv, &uptime_total_fops); 479 debugfs_create_file("vendor", 0444, priv->debugfs, priv, &vendor_fops); 480 debugfs_create_file("product", 0444, priv->debugfs, priv, &product_fops); 481 } 482 483 #else 484 485 static void corsairpsu_debugfs_init(struct corsairpsu_data *priv) 486 { 487 } 488 489 #endif 490 491 static int corsairpsu_probe(struct hid_device *hdev, const struct hid_device_id *id) 492 { 493 struct corsairpsu_data *priv; 494 int ret; 495 496 priv = devm_kzalloc(&hdev->dev, sizeof(struct corsairpsu_data), GFP_KERNEL); 497 if (!priv) 498 return -ENOMEM; 499 500 priv->cmd_buffer = devm_kmalloc(&hdev->dev, CMD_BUFFER_SIZE, GFP_KERNEL); 501 if (!priv->cmd_buffer) 502 return -ENOMEM; 503 504 ret = hid_parse(hdev); 505 if (ret) 506 return ret; 507 508 ret = hid_hw_start(hdev, HID_CONNECT_HIDRAW); 509 if (ret) 510 return ret; 511 512 ret = hid_hw_open(hdev); 513 if (ret) 514 goto fail_and_stop; 515 516 priv->hdev = hdev; 517 hid_set_drvdata(hdev, priv); 518 mutex_init(&priv->lock); 519 init_completion(&priv->wait_completion); 520 521 hid_device_io_start(hdev); 522 523 ret = corsairpsu_init(priv); 524 if (ret < 0) { 525 dev_err(&hdev->dev, "unable to initialize device (%d)\n", ret); 526 goto fail_and_stop; 527 } 528 529 ret = corsairpsu_fwinfo(priv); 530 if (ret < 0) { 531 dev_err(&hdev->dev, "unable to query firmware (%d)\n", ret); 532 goto fail_and_stop; 533 } 534 535 priv->hwmon_dev = hwmon_device_register_with_info(&hdev->dev, "corsairpsu", priv, 536 &corsairpsu_chip_info, 0); 537 538 if (IS_ERR(priv->hwmon_dev)) { 539 ret = PTR_ERR(priv->hwmon_dev); 540 goto fail_and_close; 541 } 542 543 corsairpsu_debugfs_init(priv); 544 545 return 0; 546 547 fail_and_close: 548 hid_hw_close(hdev); 549 fail_and_stop: 550 hid_hw_stop(hdev); 551 return ret; 552 } 553 554 static void corsairpsu_remove(struct hid_device *hdev) 555 { 556 struct corsairpsu_data *priv = hid_get_drvdata(hdev); 557 558 debugfs_remove_recursive(priv->debugfs); 559 hwmon_device_unregister(priv->hwmon_dev); 560 hid_hw_close(hdev); 561 hid_hw_stop(hdev); 562 } 563 564 static int corsairpsu_raw_event(struct hid_device *hdev, struct hid_report *report, u8 *data, 565 int size) 566 { 567 struct corsairpsu_data *priv = hid_get_drvdata(hdev); 568 569 if (completion_done(&priv->wait_completion)) 570 return 0; 571 572 memcpy(priv->cmd_buffer, data, min(CMD_BUFFER_SIZE, size)); 573 complete(&priv->wait_completion); 574 575 return 0; 576 } 577 578 static const struct hid_device_id corsairpsu_idtable[] = { 579 { HID_USB_DEVICE(0x1b1c, 0x1c03) }, /* Corsair HX550i */ 580 { HID_USB_DEVICE(0x1b1c, 0x1c04) }, /* Corsair HX650i */ 581 { HID_USB_DEVICE(0x1b1c, 0x1c05) }, /* Corsair HX750i */ 582 { HID_USB_DEVICE(0x1b1c, 0x1c06) }, /* Corsair HX850i */ 583 { HID_USB_DEVICE(0x1b1c, 0x1c07) }, /* Corsair HX1000i */ 584 { HID_USB_DEVICE(0x1b1c, 0x1c08) }, /* Corsair HX1200i */ 585 { HID_USB_DEVICE(0x1b1c, 0x1c09) }, /* Corsair RM550i */ 586 { HID_USB_DEVICE(0x1b1c, 0x1c0a) }, /* Corsair RM650i */ 587 { HID_USB_DEVICE(0x1b1c, 0x1c0b) }, /* Corsair RM750i */ 588 { HID_USB_DEVICE(0x1b1c, 0x1c0c) }, /* Corsair RM850i */ 589 { HID_USB_DEVICE(0x1b1c, 0x1c0d) }, /* Corsair RM1000i */ 590 { }, 591 }; 592 MODULE_DEVICE_TABLE(hid, corsairpsu_idtable); 593 594 static struct hid_driver corsairpsu_driver = { 595 .name = DRIVER_NAME, 596 .id_table = corsairpsu_idtable, 597 .probe = corsairpsu_probe, 598 .remove = corsairpsu_remove, 599 .raw_event = corsairpsu_raw_event, 600 }; 601 module_hid_driver(corsairpsu_driver); 602 603 MODULE_LICENSE("GPL"); 604 MODULE_AUTHOR("Wilken Gottwalt <wilken.gottwalt@posteo.net>"); 605 MODULE_DESCRIPTION("Linux driver for Corsair power supplies with HID sensors interface"); 606