FILE
corsair-cpro.c
File preview with syntax highlighting.
linux-6.19.14/drivers/hwmon/corsair-cpro.c
1// SPDX-License-Identifier: GPL-2.0-or-later 2/* 3 * corsair-cpro.c - Linux driver for Corsair Commander Pro 4 * Copyright (C) 2020 Marius Zachmann <mail@mariuszachmann.de> 5 * 6 * This driver uses hid reports to communicate with the device to allow hidraw userspace drivers 7 * still being used. The device does not use report ids. When using hidraw and this driver 8 * simultaniously, reports could be switched. 9 */ 10 11#include <linux/bitops.h> 12#include <linux/completion.h> 13#include <linux/debugfs.h> 14#include <linux/hid.h> 15#include <linux/hwmon.h> 16#include <linux/kernel.h> 17#include <linux/module.h> 18#include <linux/mutex.h> 19#include <linux/seq_file.h> 20#include <linux/slab.h> 21#include <linux/spinlock.h> 22#include <linux/types.h> 23 24#define USB_VENDOR_ID_CORSAIR 0x1b1c 25#define USB_PRODUCT_ID_CORSAIR_COMMANDERPRO 0x0c10 26#define USB_PRODUCT_ID_CORSAIR_1000D 0x1d00 27 28#define OUT_BUFFER_SIZE 63 29#define IN_BUFFER_SIZE 16 30#define LABEL_LENGTH 11 31#define REQ_TIMEOUT 300 32 33#define CTL_GET_FW_VER 0x02 /* returns the firmware version in bytes 1-3 */ 34#define CTL_GET_BL_VER 0x06 /* returns the bootloader version in bytes 1-2 */ 35#define CTL_GET_TMP_CNCT 0x10 /* 36 * returns in bytes 1-4 for each temp sensor: 37 * 0 not connected 38 * 1 connected 39 */ 40#define CTL_GET_TMP 0x11 /* 41 * send: byte 1 is channel, rest zero 42 * rcv: returns temp for channel in centi-degree celsius 43 * in bytes 1 and 2 as a two's complement value 44 * returns 0x11 in byte 0 if no sensor is connected 45 */ 46#define CTL_GET_VOLT 0x12 /* 47 * send: byte 1 is rail number: 0 = 12v, 1 = 5v, 2 = 3.3v 48 * rcv: returns millivolt in bytes 1,2 49 * returns error 0x10 if request is invalid 50 */ 51#define CTL_GET_FAN_CNCT 0x20 /* 52 * returns in bytes 1-6 for each fan: 53 * 0 not connected 54 * 1 3pin 55 * 2 4pin 56 */ 57#define CTL_GET_FAN_RPM 0x21 /* 58 * send: byte 1 is channel, rest zero 59 * rcv: returns rpm in bytes 1,2 60 */ 61#define CTL_GET_FAN_PWM 0x22 /* 62 * send: byte 1 is channel, rest zero 63 * rcv: returns pwm in byte 1 if it was set 64 * returns error 0x12 if fan is controlled via 65 * fan_target or fan curve 66 */ 67#define CTL_SET_FAN_FPWM 0x23 /* 68 * set fixed pwm 69 * send: byte 1 is fan number 70 * send: byte 2 is percentage from 0 - 100 71 */ 72#define CTL_SET_FAN_TARGET 0x24 /* 73 * set target rpm 74 * send: byte 1 is fan number 75 * send: byte 2-3 is target 76 * device accepts all values from 0x00 - 0xFFFF 77 */ 78 79#define NUM_FANS 6 80#define NUM_TEMP_SENSORS 4 81 82struct ccp_device { 83 struct hid_device *hdev; 84 struct device *hwmon_dev; 85 struct dentry *debugfs; 86 /* For reinitializing the completion below */ 87 spinlock_t wait_input_report_lock; 88 struct completion wait_input_report; 89 struct mutex mutex; /* whenever buffer is used, lock before send_usb_cmd */ 90 u8 *cmd_buffer; 91 u8 *buffer; 92 int buffer_recv_size; /* number of received bytes in buffer */ 93 int target[NUM_FANS]; 94 DECLARE_BITMAP(temp_cnct, NUM_TEMP_SENSORS); 95 DECLARE_BITMAP(fan_cnct, NUM_FANS); 96 char fan_label[NUM_FANS][LABEL_LENGTH]; 97 u8 firmware_ver[3]; 98 u8 bootloader_ver[2]; 99}; 100 101/* converts response error in buffer to errno */ 102static int ccp_get_errno(struct ccp_device *ccp) 103{ 104 switch (ccp->buffer[0]) { 105 case 0x00: /* success */ 106 return 0; 107 case 0x01: /* called invalid command */ 108 return -EOPNOTSUPP; 109 case 0x10: /* called GET_VOLT / GET_TMP with invalid arguments */ 110 return -EINVAL; 111 case 0x11: /* requested temps of disconnected sensors */ 112 case 0x12: /* requested pwm of not pwm controlled channels */ 113 return -ENODATA; 114 default: 115 hid_dbg(ccp->hdev, "unknown device response error: %d", ccp->buffer[0]); 116 return -EIO; 117 } 118} 119 120/* send command, check for error in response, response in ccp->buffer */ 121static int send_usb_cmd(struct ccp_device *ccp, u8 command, u8 byte1, u8 byte2, u8 byte3) 122{ 123 unsigned long t; 124 int ret; 125 126 memset(ccp->cmd_buffer, 0x00, OUT_BUFFER_SIZE); 127 ccp->cmd_buffer[0] = command; 128 ccp->cmd_buffer[1] = byte1; 129 ccp->cmd_buffer[2] = byte2; 130 ccp->cmd_buffer[3] = byte3; 131 132 /* 133 * Disable raw event parsing for a moment to safely reinitialize the 134 * completion. Reinit is done because hidraw could have triggered 135 * the raw event parsing and marked the ccp->wait_input_report 136 * completion as done. 137 */ 138 spin_lock_bh(&ccp->wait_input_report_lock); 139 reinit_completion(&ccp->wait_input_report); 140 spin_unlock_bh(&ccp->wait_input_report_lock); 141 142 ret = hid_hw_output_report(ccp->hdev, ccp->cmd_buffer, OUT_BUFFER_SIZE); 143 if (ret < 0) 144 return ret; 145 146 t = wait_for_completion_timeout(&ccp->wait_input_report, msecs_to_jiffies(REQ_TIMEOUT)); 147 if (!t) 148 return -ETIMEDOUT; 149 150 if (ccp->buffer_recv_size != IN_BUFFER_SIZE) 151 return -EPROTO; 152 153 return ccp_get_errno(ccp); 154} 155 156static int ccp_raw_event(struct hid_device *hdev, struct hid_report *report, u8 *data, int size) 157{ 158 struct ccp_device *ccp = hid_get_drvdata(hdev); 159 160 /* only copy buffer when requested */ 161 spin_lock(&ccp->wait_input_report_lock); 162 if (!completion_done(&ccp->wait_input_report)) { 163 memcpy(ccp->buffer, data, min(IN_BUFFER_SIZE, size)); 164 ccp->buffer_recv_size = size; 165 complete_all(&ccp->wait_input_report); 166 } 167 spin_unlock(&ccp->wait_input_report_lock); 168 169 return 0; 170} 171 172/* requests and returns single data values depending on channel */ 173static int get_data(struct ccp_device *ccp, int command, int channel, bool two_byte_data) 174{ 175 int ret; 176 177 mutex_lock(&ccp->mutex); 178 179 ret = send_usb_cmd(ccp, command, channel, 0, 0); 180 if (ret) 181 goto out_unlock; 182 183 ret = ccp->buffer[1]; 184 if (two_byte_data) 185 ret = (ret << 8) + ccp->buffer[2]; 186 187out_unlock: 188 mutex_unlock(&ccp->mutex); 189 return ret; 190} 191 192static int set_pwm(struct ccp_device *ccp, int channel, long val) 193{ 194 int ret; 195 196 if (val < 0 || val > 255) 197 return -EINVAL; 198 199 /* The Corsair Commander Pro uses values from 0-100 */ 200 val = DIV_ROUND_CLOSEST(val * 100, 255); 201 202 mutex_lock(&ccp->mutex); 203 204 ret = send_usb_cmd(ccp, CTL_SET_FAN_FPWM, channel, val, 0); 205 if (!ret) 206 ccp->target[channel] = -ENODATA; 207 208 mutex_unlock(&ccp->mutex); 209 return ret; 210} 211 212static int set_target(struct ccp_device *ccp, int channel, long val) 213{ 214 int ret; 215 216 val = clamp_val(val, 0, 0xFFFF); 217 ccp->target[channel] = val; 218 219 mutex_lock(&ccp->mutex); 220 ret = send_usb_cmd(ccp, CTL_SET_FAN_TARGET, channel, val >> 8, val); 221 222 mutex_unlock(&ccp->mutex); 223 return ret; 224} 225 226static int ccp_read_string(struct device *dev, enum hwmon_sensor_types type, 227 u32 attr, int channel, const char **str) 228{ 229 struct ccp_device *ccp = dev_get_drvdata(dev); 230 231 switch (type) { 232 case hwmon_fan: 233 switch (attr) { 234 case hwmon_fan_label: 235 *str = ccp->fan_label[channel]; 236 return 0; 237 default: 238 break; 239 } 240 break; 241 default: 242 break; 243 } 244 245 return -EOPNOTSUPP; 246} 247 248static int ccp_read(struct device *dev, enum hwmon_sensor_types type, 249 u32 attr, int channel, long *val) 250{ 251 struct ccp_device *ccp = dev_get_drvdata(dev); 252 int ret; 253 254 switch (type) { 255 case hwmon_temp: 256 switch (attr) { 257 case hwmon_temp_input: 258 ret = get_data(ccp, CTL_GET_TMP, channel, true); 259 if (ret < 0) 260 return ret; 261 *val = (s16)ret * 10; 262 return 0; 263 default: 264 break; 265 } 266 break; 267 case hwmon_fan: 268 switch (attr) { 269 case hwmon_fan_input: 270 ret = get_data(ccp, CTL_GET_FAN_RPM, channel, true); 271 if (ret < 0) 272 return ret; 273 *val = ret; 274 return 0; 275 case hwmon_fan_target: 276 /* how to read target values from the device is unknown */ 277 /* driver returns last set value or 0 */ 278 if (ccp->target[channel] < 0) 279 return -ENODATA; 280 *val = ccp->target[channel]; 281 return 0; 282 default: 283 break; 284 } 285 break; 286 case hwmon_pwm: 287 switch (attr) { 288 case hwmon_pwm_input: 289 ret = get_data(ccp, CTL_GET_FAN_PWM, channel, false); 290 if (ret < 0) 291 return ret; 292 *val = DIV_ROUND_CLOSEST(ret * 255, 100); 293 return 0; 294 default: 295 break; 296 } 297 break; 298 case hwmon_in: 299 switch (attr) { 300 case hwmon_in_input: 301 ret = get_data(ccp, CTL_GET_VOLT, channel, true); 302 if (ret < 0) 303 return ret; 304 *val = ret; 305 return 0; 306 default: 307 break; 308 } 309 break; 310 default: 311 break; 312 } 313 314 return -EOPNOTSUPP; 315}; 316 317static int ccp_write(struct device *dev, enum hwmon_sensor_types type, 318 u32 attr, int channel, long val) 319{ 320 struct ccp_device *ccp = dev_get_drvdata(dev); 321 322 switch (type) { 323 case hwmon_pwm: 324 switch (attr) { 325 case hwmon_pwm_input: 326 return set_pwm(ccp, channel, val); 327 default: 328 break; 329 } 330 break; 331 case hwmon_fan: 332 switch (attr) { 333 case hwmon_fan_target: 334 return set_target(ccp, channel, val); 335 default: 336 break; 337 } 338 break; 339 default: 340 break; 341 } 342 343 return -EOPNOTSUPP; 344}; 345 346static umode_t ccp_is_visible(const void *data, enum hwmon_sensor_types type, 347 u32 attr, int channel) 348{ 349 const struct ccp_device *ccp = data; 350 351 switch (type) { 352 case hwmon_temp: 353 if (!test_bit(channel, ccp->temp_cnct)) 354 break; 355 356 switch (attr) { 357 case hwmon_temp_input: 358 return 0444; 359 case hwmon_temp_label: 360 return 0444; 361 default: 362 break; 363 } 364 break; 365 case hwmon_fan: 366 if (!test_bit(channel, ccp->fan_cnct)) 367 break; 368 369 switch (attr) { 370 case hwmon_fan_input: 371 return 0444; 372 case hwmon_fan_label: 373 return 0444; 374 case hwmon_fan_target: 375 return 0644; 376 default: 377 break; 378 } 379 break; 380 case hwmon_pwm: 381 if (!test_bit(channel, ccp->fan_cnct)) 382 break; 383 384 switch (attr) { 385 case hwmon_pwm_input: 386 return 0644; 387 default: 388 break; 389 } 390 break; 391 case hwmon_in: 392 switch (attr) { 393 case hwmon_in_input: 394 return 0444; 395 default: 396 break; 397 } 398 break; 399 default: 400 break; 401 } 402 403 return 0; 404}; 405 406static const struct hwmon_ops ccp_hwmon_ops = { 407 .is_visible = ccp_is_visible, 408 .read = ccp_read, 409 .read_string = ccp_read_string, 410 .write = ccp_write, 411}; 412 413static const struct hwmon_channel_info * const ccp_info[] = { 414 HWMON_CHANNEL_INFO(chip, 415 HWMON_C_REGISTER_TZ), 416 HWMON_CHANNEL_INFO(temp, 417 HWMON_T_INPUT, 418 HWMON_T_INPUT, 419 HWMON_T_INPUT, 420 HWMON_T_INPUT 421 ), 422 HWMON_CHANNEL_INFO(fan, 423 HWMON_F_INPUT | HWMON_F_LABEL | HWMON_F_TARGET, 424 HWMON_F_INPUT | HWMON_F_LABEL | HWMON_F_TARGET, 425 HWMON_F_INPUT | HWMON_F_LABEL | HWMON_F_TARGET, 426 HWMON_F_INPUT | HWMON_F_LABEL | HWMON_F_TARGET, 427 HWMON_F_INPUT | HWMON_F_LABEL | HWMON_F_TARGET, 428 HWMON_F_INPUT | HWMON_F_LABEL | HWMON_F_TARGET 429 ), 430 HWMON_CHANNEL_INFO(pwm, 431 HWMON_PWM_INPUT, 432 HWMON_PWM_INPUT, 433 HWMON_PWM_INPUT, 434 HWMON_PWM_INPUT, 435 HWMON_PWM_INPUT, 436 HWMON_PWM_INPUT 437 ), 438 HWMON_CHANNEL_INFO(in, 439 HWMON_I_INPUT, 440 HWMON_I_INPUT, 441 HWMON_I_INPUT 442 ), 443 NULL 444}; 445 446static const struct hwmon_chip_info ccp_chip_info = { 447 .ops = &ccp_hwmon_ops, 448 .info = ccp_info, 449}; 450 451/* read fan connection status and set labels */ 452static int get_fan_cnct(struct ccp_device *ccp) 453{ 454 int channel; 455 int mode; 456 int ret; 457 458 ret = send_usb_cmd(ccp, CTL_GET_FAN_CNCT, 0, 0, 0); 459 if (ret) 460 return ret; 461 462 for (channel = 0; channel < NUM_FANS; channel++) { 463 mode = ccp->buffer[channel + 1]; 464 if (mode == 0) 465 continue; 466 467 set_bit(channel, ccp->fan_cnct); 468 ccp->target[channel] = -ENODATA; 469 470 switch (mode) { 471 case 1: 472 scnprintf(ccp->fan_label[channel], LABEL_LENGTH, 473 "fan%d 3pin", channel + 1); 474 break; 475 case 2: 476 scnprintf(ccp->fan_label[channel], LABEL_LENGTH, 477 "fan%d 4pin", channel + 1); 478 break; 479 default: 480 scnprintf(ccp->fan_label[channel], LABEL_LENGTH, 481 "fan%d other", channel + 1); 482 break; 483 } 484 } 485 486 return 0; 487} 488 489/* read temp sensor connection status */ 490static int get_temp_cnct(struct ccp_device *ccp) 491{ 492 int channel; 493 int mode; 494 int ret; 495 496 ret = send_usb_cmd(ccp, CTL_GET_TMP_CNCT, 0, 0, 0); 497 if (ret) 498 return ret; 499 500 for (channel = 0; channel < NUM_TEMP_SENSORS; channel++) { 501 mode = ccp->buffer[channel + 1]; 502 if (mode == 0) 503 continue; 504 505 set_bit(channel, ccp->temp_cnct); 506 } 507 508 return 0; 509} 510 511/* read firmware version */ 512static int get_fw_version(struct ccp_device *ccp) 513{ 514 int ret; 515 516 ret = send_usb_cmd(ccp, CTL_GET_FW_VER, 0, 0, 0); 517 if (ret) { 518 hid_notice(ccp->hdev, "Failed to read firmware version.\n"); 519 return ret; 520 } 521 ccp->firmware_ver[0] = ccp->buffer[1]; 522 ccp->firmware_ver[1] = ccp->buffer[2]; 523 ccp->firmware_ver[2] = ccp->buffer[3]; 524 525 return 0; 526} 527 528/* read bootloader version */ 529static int get_bl_version(struct ccp_device *ccp) 530{ 531 int ret; 532 533 ret = send_usb_cmd(ccp, CTL_GET_BL_VER, 0, 0, 0); 534 if (ret) { 535 hid_notice(ccp->hdev, "Failed to read bootloader version.\n"); 536 return ret; 537 } 538 ccp->bootloader_ver[0] = ccp->buffer[1]; 539 ccp->bootloader_ver[1] = ccp->buffer[2]; 540 541 return 0; 542} 543 544static int firmware_show(struct seq_file *seqf, void *unused) 545{ 546 struct ccp_device *ccp = seqf->private; 547 548 seq_printf(seqf, "%d.%d.%d\n", 549 ccp->firmware_ver[0], 550 ccp->firmware_ver[1], 551 ccp->firmware_ver[2]); 552 553 return 0; 554} 555DEFINE_SHOW_ATTRIBUTE(firmware); 556 557static int bootloader_show(struct seq_file *seqf, void *unused) 558{ 559 struct ccp_device *ccp = seqf->private; 560 561 seq_printf(seqf, "%d.%d\n", 562 ccp->bootloader_ver[0], 563 ccp->bootloader_ver[1]); 564 565 return 0; 566} 567DEFINE_SHOW_ATTRIBUTE(bootloader); 568 569static void ccp_debugfs_init(struct ccp_device *ccp) 570{ 571 char name[32]; 572 int ret; 573 574 scnprintf(name, sizeof(name), "corsaircpro-%s", dev_name(&ccp->hdev->dev)); 575 ccp->debugfs = debugfs_create_dir(name, NULL); 576 577 ret = get_fw_version(ccp); 578 if (!ret) 579 debugfs_create_file("firmware_version", 0444, 580 ccp->debugfs, ccp, &firmware_fops); 581 582 ret = get_bl_version(ccp); 583 if (!ret) 584 debugfs_create_file("bootloader_version", 0444, 585 ccp->debugfs, ccp, &bootloader_fops); 586} 587 588static int ccp_probe(struct hid_device *hdev, const struct hid_device_id *id) 589{ 590 struct ccp_device *ccp; 591 int ret; 592 593 ccp = devm_kzalloc(&hdev->dev, sizeof(*ccp), GFP_KERNEL); 594 if (!ccp) 595 return -ENOMEM; 596 597 ccp->cmd_buffer = devm_kmalloc(&hdev->dev, OUT_BUFFER_SIZE, GFP_KERNEL); 598 if (!ccp->cmd_buffer) 599 return -ENOMEM; 600 601 ccp->buffer = devm_kmalloc(&hdev->dev, IN_BUFFER_SIZE, GFP_KERNEL); 602 if (!ccp->buffer) 603 return -ENOMEM; 604 605 ret = hid_parse(hdev); 606 if (ret) 607 return ret; 608 609 ret = hid_hw_start(hdev, HID_CONNECT_HIDRAW); 610 if (ret) 611 return ret; 612 613 ret = hid_hw_open(hdev); 614 if (ret) 615 goto out_hw_stop; 616 617 ccp->hdev = hdev; 618 hid_set_drvdata(hdev, ccp); 619 620 mutex_init(&ccp->mutex); 621 spin_lock_init(&ccp->wait_input_report_lock); 622 init_completion(&ccp->wait_input_report); 623 624 hid_device_io_start(hdev); 625 626 /* temp and fan connection status only updates when device is powered on */ 627 ret = get_temp_cnct(ccp); 628 if (ret) 629 goto out_hw_close; 630 631 ret = get_fan_cnct(ccp); 632 if (ret) 633 goto out_hw_close; 634 635 ccp_debugfs_init(ccp); 636 637 ccp->hwmon_dev = hwmon_device_register_with_info(&hdev->dev, "corsaircpro", 638 ccp, &ccp_chip_info, NULL); 639 if (IS_ERR(ccp->hwmon_dev)) { 640 ret = PTR_ERR(ccp->hwmon_dev); 641 goto out_hw_close; 642 } 643 644 return 0; 645 646out_hw_close: 647 hid_hw_close(hdev); 648out_hw_stop: 649 hid_hw_stop(hdev); 650 return ret; 651} 652 653static void ccp_remove(struct hid_device *hdev) 654{ 655 struct ccp_device *ccp = hid_get_drvdata(hdev); 656 657 debugfs_remove_recursive(ccp->debugfs); 658 hwmon_device_unregister(ccp->hwmon_dev); 659 hid_hw_close(hdev); 660 hid_hw_stop(hdev); 661} 662 663static const struct hid_device_id ccp_devices[] = { 664 { HID_USB_DEVICE(USB_VENDOR_ID_CORSAIR, USB_PRODUCT_ID_CORSAIR_COMMANDERPRO) }, 665 { HID_USB_DEVICE(USB_VENDOR_ID_CORSAIR, USB_PRODUCT_ID_CORSAIR_1000D) }, 666 { } 667}; 668 669static struct hid_driver ccp_driver = { 670 .name = "corsair-cpro", 671 .id_table = ccp_devices, 672 .probe = ccp_probe, 673 .remove = ccp_remove, 674 .raw_event = ccp_raw_event, 675}; 676 677MODULE_DEVICE_TABLE(hid, ccp_devices); 678MODULE_DESCRIPTION("Corsair Commander Pro controller driver"); 679MODULE_LICENSE("GPL"); 680 681static int __init ccp_init(void) 682{ 683 return hid_register_driver(&ccp_driver); 684} 685 686static void __exit ccp_exit(void) 687{ 688 hid_unregister_driver(&ccp_driver); 689} 690 691/* 692 * When compiling this driver as built-in, hwmon initcalls will get called before the 693 * hid driver and this driver would fail to register. late_initcall solves this. 694 */ 695late_initcall(ccp_init); 696module_exit(ccp_exit);
~/
Read-only file preview.