mirror of
https://github.com/torvalds/linux.git
synced 2026-09-22 12:44:03 +02:00
hwmon: (corsair-psu) Fix linear11 calculation
In corsairpsu_linear11_to_int(), the mantissa is extracted using bitwise
operations and cast to s16 before being shifted left:
static int corsairpsu_linear11_to_int(const u16 val, const int scale)
{
...
const int mant = (((s16)(val & 0x7ff)) << 5) >> 5;
...
}
Due to C integer promotion rules, the masked value (which is always
positive) is promoted to a 32-bit integer before the left shift. As a
result, the sign bit is never extended to bit 31 of the promoted integer.
When the device hardware reports a negative temperature in Linear11 format
(such as an ambient temperature probe reporting sub-zero), the negative
mantissa is parsed incorrectly as a massive positive value. For example,
-1 becomes 2047, which scales to 2047 degrees Celsius.
Fix the problem by type casting the result of the left shift operation
to s16.
Another problem is left-shifting of negative values. In C, the result of
left-shifting negative values is undefined. Use a multiplication instead
to avoid the problem.
Also use a local s64 variable to store temporary results, change
the return value type from int to long, and clamp the final value
to LONG_MIN and LONG_MAX to avoid under- and overflow issues while
retaining as much information as possible.
Reported-by: Sashiko <sashiko-bot@kernel.org>
Cc: Wilken Gottwalt <wilken.gottwalt@posteo.net>
Signed-off-by: Guenter Roeck <linux@roeck-us.net>
Tested-by: Wilken Gottwalt <wilken.gottwalt@posteo.net>
Link: https://lore.kernel.org/r/20260804034811.2385506-1-linux@roeck-us.net
Signed-off-by: Guenter Roeck <linux@roeck-us.net>
This commit is contained in:
parent
2da6050809
commit
c6c4234928
|
|
@ -137,13 +137,18 @@ struct corsairpsu_data {
|
|||
};
|
||||
|
||||
/* some values are SMBus LINEAR11 data which need a conversion */
|
||||
static int corsairpsu_linear11_to_int(const u16 val, const int scale)
|
||||
static long corsairpsu_linear11_to_long(const u16 val, const int scale)
|
||||
{
|
||||
const int exp = ((s16)val) >> 11;
|
||||
const int mant = (((s16)(val & 0x7ff)) << 5) >> 5;
|
||||
const int result = mant * scale;
|
||||
const int mant = ((s16)((val & 0x7ff) << 5)) >> 5;
|
||||
s64 result = mant * scale;
|
||||
|
||||
return (exp >= 0) ? (result << exp) : (result >> -exp);
|
||||
if (exp >= 0)
|
||||
result *= (int)(1UL << exp);
|
||||
else
|
||||
result >>= -exp;
|
||||
|
||||
return clamp(result, LONG_MIN, LONG_MAX);
|
||||
}
|
||||
|
||||
/* the micro-controller uses percentage values to control pwm */
|
||||
|
|
@ -263,13 +268,13 @@ static int corsairpsu_get_value(struct corsairpsu_data *priv, u8 cmd, u8 rail, l
|
|||
case PSU_CMD_RAIL_AMPS:
|
||||
case PSU_CMD_TEMP0:
|
||||
case PSU_CMD_TEMP1:
|
||||
*val = corsairpsu_linear11_to_int(tmp & 0xFFFF, 1000);
|
||||
*val = corsairpsu_linear11_to_long(tmp & 0xFFFF, 1000);
|
||||
break;
|
||||
case PSU_CMD_FAN:
|
||||
*val = corsairpsu_linear11_to_int(tmp & 0xFFFF, 1);
|
||||
*val = corsairpsu_linear11_to_long(tmp & 0xFFFF, 1);
|
||||
break;
|
||||
case PSU_CMD_FAN_PWM_ENABLE:
|
||||
*val = corsairpsu_linear11_to_int(tmp & 0xFFFF, 1);
|
||||
*val = corsairpsu_linear11_to_long(tmp & 0xFFFF, 1);
|
||||
/*
|
||||
* 0 = automatic mode, means the micro-controller controls the fan using a plan
|
||||
* which can be modified, but changing this plan is not supported by this
|
||||
|
|
@ -283,12 +288,12 @@ static int corsairpsu_get_value(struct corsairpsu_data *priv, u8 cmd, u8 rail, l
|
|||
*val = 2;
|
||||
break;
|
||||
case PSU_CMD_FAN_PWM:
|
||||
*val = corsairpsu_linear11_to_int(tmp & 0xFFFF, 1);
|
||||
*val = corsairpsu_linear11_to_long(tmp & 0xFFFF, 1);
|
||||
*val = corsairpsu_dutycycle_to_pwm(*val);
|
||||
break;
|
||||
case PSU_CMD_RAIL_WATTS:
|
||||
case PSU_CMD_TOTAL_WATTS:
|
||||
*val = corsairpsu_linear11_to_int(tmp & 0xFFFF, 1000000);
|
||||
*val = corsairpsu_linear11_to_long(tmp & 0xFFFF, 1000000);
|
||||
break;
|
||||
case PSU_CMD_TOTAL_UPTIME:
|
||||
case PSU_CMD_UPTIME:
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user