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 *val = corsairpsu_linear11_to_int(tmp & 0xFFFF); 256 break; 257 case PSU_CMD_RAIL_WATTS: 258 case PSU_CMD_TOTAL_WATTS: 259 *val = corsairpsu_linear11_to_int(tmp & 0xFFFF) * 1000000; 260 break; 261 case PSU_CMD_TOTAL_UPTIME: 262 case PSU_CMD_UPTIME: 263 *val = tmp; 264 break; 265 default: 266 ret = -EOPNOTSUPP; 267 break; 268 } 269 270 return ret; 271 } 272 273 static umode_t corsairpsu_hwmon_ops_is_visible(const void *data, enum hwmon_sensor_types type, 274 u32 attr, int channel) 275 { 276 if (type == hwmon_temp && (attr == hwmon_temp_input || attr == hwmon_temp_label)) 277 return 0444; 278 else if (type == hwmon_fan && (attr == hwmon_fan_input || attr == hwmon_fan_label)) 279 return 0444; 280 else if (type == hwmon_power && (attr == hwmon_power_input || attr == hwmon_power_label)) 281 return 0444; 282 else if (type == hwmon_in && (attr == hwmon_in_input || attr == hwmon_in_label)) 283 return 0444; 284 else if (type == hwmon_curr && (attr == hwmon_curr_input || attr == hwmon_curr_label)) 285 return 0444; 286 287 return 0; 288 } 289 290 static int corsairpsu_hwmon_ops_read(struct device *dev, enum hwmon_sensor_types type, u32 attr, 291 int channel, long *val) 292 { 293 struct corsairpsu_data *priv = dev_get_drvdata(dev); 294 int ret; 295 296 if (type == hwmon_temp && attr == hwmon_temp_input && channel < 2) { 297 ret = corsairpsu_get_value(priv, channel ? PSU_CMD_TEMP1 : PSU_CMD_TEMP0, channel, 298 val); 299 } else if (type == hwmon_fan && attr == hwmon_fan_input) { 300 ret = corsairpsu_get_value(priv, PSU_CMD_FAN, 0, val); 301 } else if (type == hwmon_power && attr == hwmon_power_input) { 302 switch (channel) { 303 case 0: 304 ret = corsairpsu_get_value(priv, PSU_CMD_TOTAL_WATTS, 0, val); 305 break; 306 case 1 ... 3: 307 ret = corsairpsu_get_value(priv, PSU_CMD_RAIL_WATTS, channel - 1, val); 308 break; 309 default: 310 return -EOPNOTSUPP; 311 } 312 } else if (type == hwmon_in && attr == hwmon_in_input) { 313 switch (channel) { 314 case 0: 315 ret = corsairpsu_get_value(priv, PSU_CMD_IN_VOLTS, 0, val); 316 break; 317 case 1 ... 3: 318 ret = corsairpsu_get_value(priv, PSU_CMD_RAIL_OUT_VOLTS, channel - 1, val); 319 break; 320 default: 321 return -EOPNOTSUPP; 322 } 323 } else if (type == hwmon_curr && attr == hwmon_curr_input) { 324 switch (channel) { 325 case 0: 326 ret = corsairpsu_get_value(priv, PSU_CMD_IN_AMPS, 0, val); 327 break; 328 case 1 ... 3: 329 ret = corsairpsu_get_value(priv, PSU_CMD_RAIL_AMPS, channel - 1, val); 330 break; 331 default: 332 return -EOPNOTSUPP; 333 } 334 } else { 335 return -EOPNOTSUPP; 336 } 337 338 if (ret < 0) 339 return ret; 340 341 return 0; 342 } 343 344 static int corsairpsu_hwmon_ops_read_string(struct device *dev, enum hwmon_sensor_types type, 345 u32 attr, int channel, const char **str) 346 { 347 if (type == hwmon_temp && attr == hwmon_temp_label) { 348 *str = channel ? L_TEMP1 : L_TEMP0; 349 return 0; 350 } else if (type == hwmon_fan && attr == hwmon_fan_label) { 351 *str = L_FAN; 352 return 0; 353 } else if (type == hwmon_power && attr == hwmon_power_label && channel < 4) { 354 *str = label_watts[channel]; 355 return 0; 356 } else if (type == hwmon_in && attr == hwmon_in_label && channel < 4) { 357 *str = label_volts[channel]; 358 return 0; 359 } else if (type == hwmon_curr && attr == hwmon_curr_label && channel < 4) { 360 *str = label_amps[channel]; 361 return 0; 362 } 363 364 return -EOPNOTSUPP; 365 } 366 367 static const struct hwmon_ops corsairpsu_hwmon_ops = { 368 .is_visible = corsairpsu_hwmon_ops_is_visible, 369 .read = corsairpsu_hwmon_ops_read, 370 .read_string = corsairpsu_hwmon_ops_read_string, 371 }; 372 373 static const struct hwmon_channel_info *corsairpsu_info[] = { 374 HWMON_CHANNEL_INFO(chip, 375 HWMON_C_REGISTER_TZ), 376 HWMON_CHANNEL_INFO(temp, 377 HWMON_T_INPUT | HWMON_T_LABEL, 378 HWMON_T_INPUT | HWMON_T_LABEL), 379 HWMON_CHANNEL_INFO(fan, 380 HWMON_F_INPUT | HWMON_F_LABEL), 381 HWMON_CHANNEL_INFO(power, 382 HWMON_P_INPUT | HWMON_P_LABEL, 383 HWMON_P_INPUT | HWMON_P_LABEL, 384 HWMON_P_INPUT | HWMON_P_LABEL, 385 HWMON_P_INPUT | HWMON_P_LABEL), 386 HWMON_CHANNEL_INFO(in, 387 HWMON_I_INPUT | HWMON_I_LABEL, 388 HWMON_I_INPUT | HWMON_I_LABEL, 389 HWMON_I_INPUT | HWMON_I_LABEL, 390 HWMON_I_INPUT | HWMON_I_LABEL), 391 HWMON_CHANNEL_INFO(curr, 392 HWMON_C_INPUT | HWMON_C_LABEL, 393 HWMON_C_INPUT | HWMON_C_LABEL, 394 HWMON_C_INPUT | HWMON_C_LABEL, 395 HWMON_C_INPUT | HWMON_C_LABEL), 396 NULL 397 }; 398 399 static const struct hwmon_chip_info corsairpsu_chip_info = { 400 .ops = &corsairpsu_hwmon_ops, 401 .info = corsairpsu_info, 402 }; 403 404 #ifdef CONFIG_DEBUG_FS 405 406 static void print_uptime(struct seq_file *seqf, u8 cmd) 407 { 408 struct corsairpsu_data *priv = seqf->private; 409 long val; 410 int ret; 411 412 ret = corsairpsu_get_value(priv, cmd, 0, &val); 413 if (ret < 0) { 414 seq_puts(seqf, "N/A\n"); 415 return; 416 } 417 418 if (val > SECONDS_PER_DAY) { 419 seq_printf(seqf, "%ld day(s), %02ld:%02ld:%02ld\n", val / SECONDS_PER_DAY, 420 val % SECONDS_PER_DAY / SECONDS_PER_HOUR, val % SECONDS_PER_HOUR / 60, 421 val % 60); 422 return; 423 } 424 425 seq_printf(seqf, "%02ld:%02ld:%02ld\n", val % SECONDS_PER_DAY / SECONDS_PER_HOUR, 426 val % SECONDS_PER_HOUR / 60, val % 60); 427 } 428 429 static int uptime_show(struct seq_file *seqf, void *unused) 430 { 431 print_uptime(seqf, PSU_CMD_UPTIME); 432 433 return 0; 434 } 435 DEFINE_SHOW_ATTRIBUTE(uptime); 436 437 static int uptime_total_show(struct seq_file *seqf, void *unused) 438 { 439 print_uptime(seqf, PSU_CMD_TOTAL_UPTIME); 440 441 return 0; 442 } 443 DEFINE_SHOW_ATTRIBUTE(uptime_total); 444 445 static int vendor_show(struct seq_file *seqf, void *unused) 446 { 447 struct corsairpsu_data *priv = seqf->private; 448 449 seq_printf(seqf, "%s\n", priv->vendor); 450 451 return 0; 452 } 453 DEFINE_SHOW_ATTRIBUTE(vendor); 454 455 static int product_show(struct seq_file *seqf, void *unused) 456 { 457 struct corsairpsu_data *priv = seqf->private; 458 459 seq_printf(seqf, "%s\n", priv->product); 460 461 return 0; 462 } 463 DEFINE_SHOW_ATTRIBUTE(product); 464 465 static void corsairpsu_debugfs_init(struct corsairpsu_data *priv) 466 { 467 char name[32]; 468 469 scnprintf(name, sizeof(name), "%s-%s", DRIVER_NAME, dev_name(&priv->hdev->dev)); 470 471 priv->debugfs = debugfs_create_dir(name, NULL); 472 debugfs_create_file("uptime", 0444, priv->debugfs, priv, &uptime_fops); 473 debugfs_create_file("uptime_total", 0444, priv->debugfs, priv, &uptime_total_fops); 474 debugfs_create_file("vendor", 0444, priv->debugfs, priv, &vendor_fops); 475 debugfs_create_file("product", 0444, priv->debugfs, priv, &product_fops); 476 } 477 478 #else 479 480 static void corsairpsu_debugfs_init(struct corsairpsu_data *priv) 481 { 482 } 483 484 #endif 485 486 static int corsairpsu_probe(struct hid_device *hdev, const struct hid_device_id *id) 487 { 488 struct corsairpsu_data *priv; 489 int ret; 490 491 priv = devm_kzalloc(&hdev->dev, sizeof(struct corsairpsu_data), GFP_KERNEL); 492 if (!priv) 493 return -ENOMEM; 494 495 priv->cmd_buffer = devm_kmalloc(&hdev->dev, CMD_BUFFER_SIZE, GFP_KERNEL); 496 if (!priv->cmd_buffer) 497 return -ENOMEM; 498 499 ret = hid_parse(hdev); 500 if (ret) 501 return ret; 502 503 ret = hid_hw_start(hdev, HID_CONNECT_HIDRAW); 504 if (ret) 505 return ret; 506 507 ret = hid_hw_open(hdev); 508 if (ret) 509 goto fail_and_stop; 510 511 priv->hdev = hdev; 512 hid_set_drvdata(hdev, priv); 513 mutex_init(&priv->lock); 514 init_completion(&priv->wait_completion); 515 516 hid_device_io_start(hdev); 517 518 ret = corsairpsu_init(priv); 519 if (ret < 0) { 520 dev_err(&hdev->dev, "unable to initialize device (%d)\n", ret); 521 goto fail_and_stop; 522 } 523 524 ret = corsairpsu_fwinfo(priv); 525 if (ret < 0) { 526 dev_err(&hdev->dev, "unable to query firmware (%d)\n", ret); 527 goto fail_and_stop; 528 } 529 530 priv->hwmon_dev = hwmon_device_register_with_info(&hdev->dev, "corsairpsu", priv, 531 &corsairpsu_chip_info, 0); 532 533 if (IS_ERR(priv->hwmon_dev)) { 534 ret = PTR_ERR(priv->hwmon_dev); 535 goto fail_and_close; 536 } 537 538 corsairpsu_debugfs_init(priv); 539 540 return 0; 541 542 fail_and_close: 543 hid_hw_close(hdev); 544 fail_and_stop: 545 hid_hw_stop(hdev); 546 return ret; 547 } 548 549 static void corsairpsu_remove(struct hid_device *hdev) 550 { 551 struct corsairpsu_data *priv = hid_get_drvdata(hdev); 552 553 debugfs_remove_recursive(priv->debugfs); 554 hwmon_device_unregister(priv->hwmon_dev); 555 hid_hw_close(hdev); 556 hid_hw_stop(hdev); 557 } 558 559 static int corsairpsu_raw_event(struct hid_device *hdev, struct hid_report *report, u8 *data, 560 int size) 561 { 562 struct corsairpsu_data *priv = hid_get_drvdata(hdev); 563 564 if (completion_done(&priv->wait_completion)) 565 return 0; 566 567 memcpy(priv->cmd_buffer, data, min(CMD_BUFFER_SIZE, size)); 568 complete(&priv->wait_completion); 569 570 return 0; 571 } 572 573 static const struct hid_device_id corsairpsu_idtable[] = { 574 { HID_USB_DEVICE(0x1b1c, 0x1c03) }, /* Corsair HX550i */ 575 { HID_USB_DEVICE(0x1b1c, 0x1c04) }, /* Corsair HX650i */ 576 { HID_USB_DEVICE(0x1b1c, 0x1c05) }, /* Corsair HX750i */ 577 { HID_USB_DEVICE(0x1b1c, 0x1c06) }, /* Corsair HX850i */ 578 { HID_USB_DEVICE(0x1b1c, 0x1c07) }, /* Corsair HX1000i */ 579 { HID_USB_DEVICE(0x1b1c, 0x1c08) }, /* Corsair HX1200i */ 580 { HID_USB_DEVICE(0x1b1c, 0x1c09) }, /* Corsair RM550i */ 581 { HID_USB_DEVICE(0x1b1c, 0x1c0a) }, /* Corsair RM650i */ 582 { HID_USB_DEVICE(0x1b1c, 0x1c0b) }, /* Corsair RM750i */ 583 { HID_USB_DEVICE(0x1b1c, 0x1c0c) }, /* Corsair RM850i */ 584 { HID_USB_DEVICE(0x1b1c, 0x1c0d) }, /* Corsair RM1000i */ 585 { }, 586 }; 587 MODULE_DEVICE_TABLE(hid, corsairpsu_idtable); 588 589 static struct hid_driver corsairpsu_driver = { 590 .name = DRIVER_NAME, 591 .id_table = corsairpsu_idtable, 592 .probe = corsairpsu_probe, 593 .remove = corsairpsu_remove, 594 .raw_event = corsairpsu_raw_event, 595 }; 596 module_hid_driver(corsairpsu_driver); 597 598 MODULE_LICENSE("GPL"); 599 MODULE_AUTHOR("Wilken Gottwalt <wilken.gottwalt@posteo.net>"); 600 MODULE_DESCRIPTION("Linux driver for Corsair power supplies with HID sensors interface"); 601