// SPDX-License-Identifier: GPL-2.0-or-later /* * corsair-cpro.c - Linux driver for Corsair Commander Pro * Copyright (C) 2020 Marius Zachmann <mail@mariuszachmann.de> * * This driver uses hid reports to communicate with the device to allow hidraw userspace drivers * still being used. The device does not use report ids. When using hidraw and this driver * simultaniously, reports could be switched.
*/
#define CTL_GET_FW_VER 0x02 /* returns the firmware version in bytes 1-3 */ #define CTL_GET_BL_VER 0x06 /* returns the bootloader version in bytes 1-2 */ #define CTL_GET_TMP_CNCT 0x10 /* * returns in bytes 1-4 for each temp sensor: * 0 not connected * 1 connected
*/ #define CTL_GET_TMP 0x11 /* * send: byte 1 is channel, rest zero * rcv: returns temp for channel in centi-degree celsius * in bytes 1 and 2 * returns 0x11 in byte 0 if no sensor is connected
*/ #define CTL_GET_VOLT 0x12 /* * send: byte 1 is rail number: 0 = 12v, 1 = 5v, 2 = 3.3v * rcv: returns millivolt in bytes 1,2 * returns error 0x10 if request is invalid
*/ #define CTL_GET_FAN_CNCT 0x20 /* * returns in bytes 1-6 for each fan: * 0 not connected * 1 3pin * 2 4pin
*/ #define CTL_GET_FAN_RPM 0x21 /* * send: byte 1 is channel, rest zero * rcv: returns rpm in bytes 1,2
*/ #define CTL_GET_FAN_PWM 0x22 /* * send: byte 1 is channel, rest zero * rcv: returns pwm in byte 1 if it was set * returns error 0x12 if fan is controlled via * fan_target or fan curve
*/ #define CTL_SET_FAN_FPWM 0x23 /* * set fixed pwm * send: byte 1 is fan number * send: byte 2 is percentage from 0 - 100
*/ #define CTL_SET_FAN_TARGET 0x24 /* * set target rpm * send: byte 1 is fan number * send: byte 2-3 is target * device accepts all values from 0x00 - 0xFFFF
*/
#define NUM_FANS 6 #define NUM_TEMP_SENSORS 4
struct ccp_device { struct hid_device *hdev; struct device *hwmon_dev; struct dentry *debugfs; /* For reinitializing the completion below */
spinlock_t wait_input_report_lock; struct completion wait_input_report; struct mutex mutex; /* whenever buffer is used, lock before send_usb_cmd */
u8 *cmd_buffer;
u8 *buffer; int buffer_recv_size; /* number of received bytes in buffer */ int target[6];
DECLARE_BITMAP(temp_cnct, NUM_TEMP_SENSORS);
DECLARE_BITMAP(fan_cnct, NUM_FANS); char fan_label[6][LABEL_LENGTH];
u8 firmware_ver[3];
u8 bootloader_ver[2];
};
/* converts response error in buffer to errno */ staticint ccp_get_errno(struct ccp_device *ccp)
{ switch (ccp->buffer[0]) { case 0x00: /* success */ return 0; case 0x01: /* called invalid command */ return -EOPNOTSUPP; case 0x10: /* called GET_VOLT / GET_TMP with invalid arguments */ return -EINVAL; case 0x11: /* requested temps of disconnected sensors */ case 0x12: /* requested pwm of not pwm controlled channels */ return -ENODATA; default:
hid_dbg(ccp->hdev, "unknown device response error: %d", ccp->buffer[0]); return -EIO;
}
}
/* send command, check for error in response, response in ccp->buffer */ staticint send_usb_cmd(struct ccp_device *ccp, u8 command, u8 byte1, u8 byte2, u8 byte3)
{ unsignedlong t; int ret;
/* * Disable raw event parsing for a moment to safely reinitialize the * completion. Reinit is done because hidraw could have triggered * the raw event parsing and marked the ccp->wait_input_report * completion as done.
*/
spin_lock_bh(&ccp->wait_input_report_lock);
reinit_completion(&ccp->wait_input_report);
spin_unlock_bh(&ccp->wait_input_report_lock);
ret = hid_hw_output_report(ccp->hdev, ccp->cmd_buffer, OUT_BUFFER_SIZE); if (ret < 0) return ret;
t = wait_for_completion_timeout(&ccp->wait_input_report, msecs_to_jiffies(REQ_TIMEOUT)); if (!t) return -ETIMEDOUT;
if (ccp->buffer_recv_size != IN_BUFFER_SIZE) return -EPROTO;
/* only copy buffer when requested */
spin_lock(&ccp->wait_input_report_lock); if (!completion_done(&ccp->wait_input_report)) {
memcpy(ccp->buffer, data, min(IN_BUFFER_SIZE, size));
ccp->buffer_recv_size = size;
complete_all(&ccp->wait_input_report);
}
spin_unlock(&ccp->wait_input_report_lock);
return 0;
}
/* requests and returns single data values depending on channel */ staticint get_data(struct ccp_device *ccp, int command, int channel, bool two_byte_data)
{ int ret;
mutex_lock(&ccp->mutex);
ret = send_usb_cmd(ccp, command, channel, 0, 0); if (ret) goto out_unlock;
ret = ccp->buffer[1]; if (two_byte_data)
ret = (ret << 8) + ccp->buffer[2];
switch (type) { case hwmon_temp: if (!test_bit(channel, ccp->temp_cnct)) break;
switch (attr) { case hwmon_temp_input: return 0444; case hwmon_temp_label: return 0444; default: break;
} break; case hwmon_fan: if (!test_bit(channel, ccp->fan_cnct)) break;
switch (attr) { case hwmon_fan_input: return 0444; case hwmon_fan_label: return 0444; case hwmon_fan_target: return 0644; default: break;
} break; case hwmon_pwm: if (!test_bit(channel, ccp->fan_cnct)) break;
switch (attr) { case hwmon_pwm_input: return 0644; default: break;
} break; case hwmon_in: switch (attr) { case hwmon_in_input: return 0444; default: break;
} break; default: break;
}
/* * When compiling this driver as built-in, hwmon initcalls will get called before the * hid driver and this driver would fail to register. late_initcall solves this.
*/
late_initcall(ccp_init);
module_exit(ccp_exit);
Messung V0.5
¤ Dauer der Verarbeitung: 0.1 Sekunden
(vorverarbeitet)
¤
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.