Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

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: 48e44ce0f881 ("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>

authored by

Hans de Goede and committed by
Jonathan Cameron
0f85406b 0f2aeee5

+14 -14
+14 -14
drivers/iio/inkern.c
··· 11 11 #include <linux/mutex.h> 12 12 #include <linux/property.h> 13 13 #include <linux/slab.h> 14 + #include <linux/units.h> 14 15 15 16 #include <linux/iio/iio.h> 16 17 #include <linux/iio/iio-opaque.h> ··· 605 604 { 606 605 int scale_type, scale_val, scale_val2; 607 606 int offset_type, offset_val, offset_val2; 608 - s64 raw64 = raw; 607 + s64 denominator, raw64 = raw; 609 608 610 609 offset_type = iio_channel_read(chan, &offset_val, &offset_val2, 611 610 IIO_CHAN_INFO_OFFSET); ··· 649 648 *processed = raw64 * scale_val * scale; 650 649 break; 651 650 case IIO_VAL_INT_PLUS_MICRO: 652 - if (scale_val2 < 0) 653 - *processed = -raw64 * scale_val * scale; 654 - else 655 - *processed = raw64 * scale_val * scale; 656 - *processed += div_s64(raw64 * (s64)scale_val2 * scale, 657 - 1000000LL); 658 - break; 659 651 case IIO_VAL_INT_PLUS_NANO: 660 - if (scale_val2 < 0) 661 - *processed = -raw64 * scale_val * scale; 662 - else 663 - *processed = raw64 * scale_val * scale; 664 - *processed += div_s64(raw64 * (s64)scale_val2 * scale, 665 - 1000000000LL); 652 + switch (scale_type) { 653 + case IIO_VAL_INT_PLUS_MICRO: 654 + denominator = MICRO; 655 + break; 656 + case IIO_VAL_INT_PLUS_NANO: 657 + denominator = NANO; 658 + break; 659 + } 660 + *processed = raw64 * scale * abs(scale_val); 661 + *processed += div_s64(raw64 * scale * abs(scale_val2), denominator); 662 + if (scale_val < 0 || scale_val2 < 0) 663 + *processed *= -1; 666 664 break; 667 665 case IIO_VAL_FRACTIONAL: 668 666 *processed = div_s64(raw64 * (s64)scale_val * scale,