mirror of
https://github.com/raspberrypi/linux.git
synced 2025-12-06 10:00:17 +00:00
iio: consumers: Fix handling of negative channel scale in iio_convert_raw_to_processed()
There is an issue with the handling of negative channel scales
in iio_convert_raw_to_processed_unlocked() when the channel-scale
is of the IIO_VAL_INT_PLUS_[MICRO|NANO] type:
Things work for channel-scale values > -1.0 and < 0.0 because of
the use of signed values in:
*processed += div_s64(raw64 * (s64)scale_val2 * scale, 1000000LL);
Things will break however for scale values < -1.0. Lets for example say
that raw = 2, (caller-provided)scale = 10 and (channel)scale_val = -1.5.
The result should then be 2 * 10 * -1.5 = -30.
channel-scale = -1.5 means scale_val = -1 and scale_val2 = 500000,
now lets see what gets stored in processed:
1. *processed = raw64 * scale_val * scale;
2. *processed += raw64 * scale_val2 * scale / 1000000LL;
1. Sets processed to 2 * -1 * 10 = -20
2. Adds 2 * 500000 * 10 / 1000000 = 10 to processed
And the end result is processed = -20 + 10 = -10, which is not correct.
Fix this by always using the abs value of both scale_val and scale_val2
and if either is negative multiply the end-result by -1.
Note there seems to be an unwritten rule about negative
IIO_VAL_INT_PLUS_[MICRO|NANO] values that:
i. values > -1.0 and < 0.0 are written as val=0 val2=-xxx
ii. values <= -1.0 are written as val=-xxx val2=xxx
But iio_format_value() will also correctly display a third option:
iii. values <= -1.0 written as val=-xxx val2=-xxx
Since iio_format_value() uses abs(val) when val2 < 0.
This fix also makes iio_convert_raw_to_processed() properly handle
channel-scales using this third option.
Fixes: 48e44ce0f8 ("iio:inkern: Add function to read the processed value")
Cc: Matteo Martelli <matteomartelli3@gmail.com>
Reviewed-by: Andy Shevchenko <andy@kernel.org>
Signed-off-by: Hans de Goede <hansg@kernel.org>
Link: https://patch.msgid.link/20250831104825.15097-2-hansg@kernel.org
Signed-off-by: Jonathan Cameron <Jonathan.Cameron@huawei.com>
This commit is contained in:
committed by
Jonathan Cameron
parent
0f2aeee584
commit
0f85406bf8
@@ -11,6 +11,7 @@
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/property.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/units.h>
|
||||
|
||||
#include <linux/iio/iio.h>
|
||||
#include <linux/iio/iio-opaque.h>
|
||||
@@ -604,7 +605,7 @@ static int iio_convert_raw_to_processed_unlocked(struct iio_channel *chan,
|
||||
{
|
||||
int scale_type, scale_val, scale_val2;
|
||||
int offset_type, offset_val, offset_val2;
|
||||
s64 raw64 = raw;
|
||||
s64 denominator, raw64 = raw;
|
||||
|
||||
offset_type = iio_channel_read(chan, &offset_val, &offset_val2,
|
||||
IIO_CHAN_INFO_OFFSET);
|
||||
@@ -648,20 +649,19 @@ static int iio_convert_raw_to_processed_unlocked(struct iio_channel *chan,
|
||||
*processed = raw64 * scale_val * scale;
|
||||
break;
|
||||
case IIO_VAL_INT_PLUS_MICRO:
|
||||
if (scale_val2 < 0)
|
||||
*processed = -raw64 * scale_val * scale;
|
||||
else
|
||||
*processed = raw64 * scale_val * scale;
|
||||
*processed += div_s64(raw64 * (s64)scale_val2 * scale,
|
||||
1000000LL);
|
||||
case IIO_VAL_INT_PLUS_NANO:
|
||||
switch (scale_type) {
|
||||
case IIO_VAL_INT_PLUS_MICRO:
|
||||
denominator = MICRO;
|
||||
break;
|
||||
case IIO_VAL_INT_PLUS_NANO:
|
||||
if (scale_val2 < 0)
|
||||
*processed = -raw64 * scale_val * scale;
|
||||
else
|
||||
*processed = raw64 * scale_val * scale;
|
||||
*processed += div_s64(raw64 * (s64)scale_val2 * scale,
|
||||
1000000000LL);
|
||||
denominator = NANO;
|
||||
break;
|
||||
}
|
||||
*processed = raw64 * scale * abs(scale_val);
|
||||
*processed += div_s64(raw64 * scale * abs(scale_val2), denominator);
|
||||
if (scale_val < 0 || scale_val2 < 0)
|
||||
*processed *= -1;
|
||||
break;
|
||||
case IIO_VAL_FRACTIONAL:
|
||||
*processed = div_s64(raw64 * (s64)scale_val * scale,
|
||||
|
||||
Reference in New Issue
Block a user