Skip to content

Commit

Permalink
hwmon: (corsair-cpro) add reading pwm values
Browse files Browse the repository at this point in the history
This adds the possibility for reading pwm values.
These can not be read if the device is controlled via
fan_target or a fan curve and will return an error in
this case. Since an error is expected, this adds some
rudimentary error handling.

Changes:
- add CTL_GET_FAN_PWM and use it via get_data
- pwm returns -ENODATA if the device returns error 0x12
- fan_target now returns -ENODATA when the driver is
  started or a pwm value is set.
- add ccp_get_errno to determine errno from device error.
- get_data now has a parameter to determine whether
  to read one or two bytes of data.
- update documentation
- fix missing surname in MAINTAINERS

Signed-off-by: Marius Zachmann <mail@mariuszachmann.de>
Signed-off-by: Guenter Roeck <linux@roeck-us.net>
  • Loading branch information
Marius Zachmann authored and Guenter Roeck committed Jul 24, 2020
1 parent e492217 commit fa4dac3
Show file tree
Hide file tree
Showing 3 changed files with 48 additions and 25 deletions.
7 changes: 3 additions & 4 deletions Documentation/hwmon/corsair-cpro.rst
Original file line number Diff line number Diff line change
Expand Up @@ -35,8 +35,7 @@ fan[1-6]_input Connected fan rpm.
fan[1-6]_label Shows fan type as detected by the device.
fan[1-6]_target Sets fan speed target rpm.
When reading, it reports the last value if it was set by the driver.
Otherwise returns 0.
pwm[1-6] Sets the fan speed. Values from 0-255.
When reading, it reports the last value if it was set by the driver.
Otherwise returns 0.
Otherwise returns an error.
pwm[1-6] Sets the fan speed. Values from 0-255. Can only be read if pwm
was set directly.
======================= =====================================================================
2 changes: 1 addition & 1 deletion MAINTAINERS
Original file line number Diff line number Diff line change
Expand Up @@ -4402,7 +4402,7 @@ F: Documentation/hwmon/coretemp.rst
F: drivers/hwmon/coretemp.c

CORSAIR-CPRO HARDWARE MONITOR DRIVER
M: Marius <mail@mariuszachmann.de>
M: Marius Zachmann <mail@mariuszachmann.de>
L: linux-hwmon@vger.kernel.org
S: Maintained
F: drivers/hwmon/corsair-cpro.c
Expand Down
64 changes: 44 additions & 20 deletions drivers/hwmon/corsair-cpro.c
Original file line number Diff line number Diff line change
Expand Up @@ -36,11 +36,12 @@
* send: byte 1 is channel, rest zero
* rcv: returns temp for channel in centi-degree celsius
* in bytes 1 and 2
* returns 17 in byte 0 if no sensor is connected
* 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:
Expand All @@ -52,6 +53,12 @@
* 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
Expand All @@ -73,13 +80,31 @@ struct ccp_device {
struct completion wait_input_report;
struct mutex mutex; /* whenever buffer is used, lock before send_usb_cmd */
u8 *buffer;
int pwm[6];
int target[6];
DECLARE_BITMAP(temp_cnct, NUM_TEMP_SENSORS);
DECLARE_BITMAP(fan_cnct, NUM_FANS);
char fan_label[6][LABEL_LENGTH];
};

/* converts response error in buffer to errno */
static int 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 */
static int send_usb_cmd(struct ccp_device *ccp, u8 command, u8 byte1, u8 byte2, u8 byte3)
{
Expand All @@ -102,13 +127,7 @@ static int send_usb_cmd(struct ccp_device *ccp, u8 command, u8 byte1, u8 byte2,
if (!t)
return -ETIMEDOUT;

/* first byte of response is error code */
if (ccp->buffer[0] != 0x00) {
hid_dbg(ccp->hdev, "device response error: %d", ccp->buffer[0]);
return -EIO;
}

return 0;
return ccp_get_errno(ccp);
}

static int ccp_raw_event(struct hid_device *hdev, struct hid_report *report, u8 *data, int size)
Expand All @@ -126,7 +145,7 @@ static int ccp_raw_event(struct hid_device *hdev, struct hid_report *report, u8
}

/* requests and returns single data values depending on channel */
static int get_data(struct ccp_device *ccp, int command, int channel)
static int get_data(struct ccp_device *ccp, int command, int channel, bool two_byte_data)
{
int ret;

Expand All @@ -136,7 +155,9 @@ static int get_data(struct ccp_device *ccp, int command, int channel)
if (ret)
goto out_unlock;

ret = (ccp->buffer[1] << 8) + ccp->buffer[2];
ret = ccp->buffer[1];
if (two_byte_data)
ret = (ret << 8) + ccp->buffer[2];

out_unlock:
mutex_unlock(&ccp->mutex);
Expand All @@ -150,14 +171,14 @@ static int set_pwm(struct ccp_device *ccp, int channel, long val)
if (val < 0 || val > 255)
return -EINVAL;

ccp->pwm[channel] = val;

/* The Corsair Commander Pro uses values from 0-100 */
val = DIV_ROUND_CLOSEST(val * 100, 255);

mutex_lock(&ccp->mutex);

ret = send_usb_cmd(ccp, CTL_SET_FAN_FPWM, channel, val, 0);
if (!ret)
ccp->target[channel] = -ENODATA;

mutex_unlock(&ccp->mutex);
return ret;
Expand All @@ -171,7 +192,6 @@ static int set_target(struct ccp_device *ccp, int channel, long val)
ccp->target[channel] = val;

mutex_lock(&ccp->mutex);

ret = send_usb_cmd(ccp, CTL_SET_FAN_TARGET, channel, val >> 8, val);

mutex_unlock(&ccp->mutex);
Expand Down Expand Up @@ -210,7 +230,7 @@ static int ccp_read(struct device *dev, enum hwmon_sensor_types type,
case hwmon_temp:
switch (attr) {
case hwmon_temp_input:
ret = get_data(ccp, CTL_GET_TMP, channel);
ret = get_data(ccp, CTL_GET_TMP, channel, true);
if (ret < 0)
return ret;
*val = ret * 10;
Expand All @@ -222,14 +242,16 @@ static int ccp_read(struct device *dev, enum hwmon_sensor_types type,
case hwmon_fan:
switch (attr) {
case hwmon_fan_input:
ret = get_data(ccp, CTL_GET_FAN_RPM, channel);
ret = get_data(ccp, CTL_GET_FAN_RPM, channel, true);
if (ret < 0)
return ret;
*val = ret;
return 0;
case hwmon_fan_target:
/* how to read target values from the device is unknown */
/* driver returns last set value or 0 */
if (ccp->target[channel] < 0)
return -ENODATA;
*val = ccp->target[channel];
return 0;
default:
Expand All @@ -239,9 +261,10 @@ static int ccp_read(struct device *dev, enum hwmon_sensor_types type,
case hwmon_pwm:
switch (attr) {
case hwmon_pwm_input:
/* how to read pwm values from the device is currently unknown */
/* driver returns last set value or 0 */
*val = ccp->pwm[channel];
ret = get_data(ccp, CTL_GET_FAN_PWM, channel, false);
if (ret < 0)
return ret;
*val = DIV_ROUND_CLOSEST(ret * 255, 100);
return 0;
default:
break;
Expand All @@ -250,7 +273,7 @@ static int ccp_read(struct device *dev, enum hwmon_sensor_types type,
case hwmon_in:
switch (attr) {
case hwmon_in_input:
ret = get_data(ccp, CTL_GET_VOLT, channel);
ret = get_data(ccp, CTL_GET_VOLT, channel, true);
if (ret < 0)
return ret;
*val = ret;
Expand Down Expand Up @@ -416,6 +439,7 @@ static int get_fan_cnct(struct ccp_device *ccp)
continue;

set_bit(channel, ccp->fan_cnct);
ccp->target[channel] = -ENODATA;

switch (mode) {
case 1:
Expand Down

0 comments on commit fa4dac3

Please sign in to comment.