From: Rodrigo Alencar via B4 Relay <devnull+rodrigo.alencar.analog.com@kernel.org>
To: linux-kernel@vger.kernel.org, linux-iio@vger.kernel.org
Cc: Andrew Morton <akpm@linux-foundation.org>,
Jonathan Cameron <jic23@kernel.org>,
David Lechner <dlechner@baylibre.com>,
Andy Shevchenko <andy@kernel.org>,
Rodrigo Alencar <rodrigo.alencar@analog.com>
Subject: [PATCH RFC v2 3/4] iio: core: use kstrtodec64() to parse fixed-point values
Date: Tue, 13 Oct 2026 10:36:46 -0300 [thread overview]
Message-ID: <20261013-e-notation-v2-3-d4adb5e193fc@analog.com> (raw)
In-Reply-To: <20261013-e-notation-v2-0-d4adb5e193fc@analog.com>
From: Rodrigo Alencar <rodrigo.alencar@analog.com>
Replace the open-coded __iio_str_to_fixpoint() parser with kstrtodec64(),
so sysfs writes of fixed-point values also accept E notation.
The fractional part only keeps the sign when the integer part is zero,
as before. The dB suffix for scale attributes is now handled by a separate
iio_str_to_fixpoint_units() helper. It strips an optional trailing newline
and the "dB" or " dB" suffix before parsing.
When there is no fractional part (fract_mult == 0, or dec_scale == 0 for
64-bit values), try base-autodetecting kstrtoll() first so hexadecimal
and octal input keeps its meaning ("010" is still 8). Fall back to
kstrtodec64() on -EINVAL to accept E notation.
As a side effect, inputs that used to be rejected for integer-only
attributes are now accepted. An invalid octal number such as "08" is
parsed as decimal 8, and a fractional value such as "1.5" is truncated
to 1, matching how kstrtodec64() drops digits beyond the requested
scale.
Signed-off-by: Rodrigo Alencar <rodrigo.alencar@analog.com>
---
drivers/iio/industrialio-core.c | 159 ++++++++++++++++++++--------------------
1 file changed, 81 insertions(+), 78 deletions(-)
diff --git a/drivers/iio/industrialio-core.c b/drivers/iio/industrialio-core.c
index d73f2541375f..616bfea711e0 100644
--- a/drivers/iio/industrialio-core.c
+++ b/drivers/iio/industrialio-core.c
@@ -994,77 +994,6 @@ static ssize_t iio_read_channel_info_avail(struct device *dev,
}
}
-/**
- * __iio_str_to_fixpoint() - Parse a fixed-point number from a string
- * @str: The string to parse
- * @fract_mult: Multiplier for the first decimal place, should be a power of 10
- * @integer: The integer part of the number
- * @fract: The fractional part of the number
- * @scale_db: True if this should parse as dB
- *
- * Returns:
- * 0 on success, or a negative error code if the string could not be parsed.
- */
-static int __iio_str_to_fixpoint(const char *str, int fract_mult,
- int *integer, int *fract, bool scale_db)
-{
- int i = 0, f = 0;
- bool integer_part = true, negative = false;
-
- if (fract_mult == 0) {
- *fract = 0;
-
- return kstrtoint(str, 0, integer);
- }
-
- if (str[0] == '-') {
- negative = true;
- str++;
- } else if (str[0] == '+') {
- str++;
- }
-
- while (*str) {
- if ('0' <= *str && *str <= '9') {
- if (integer_part) {
- i = i * 10 + *str - '0';
- } else {
- f += fract_mult * (*str - '0');
- fract_mult /= 10;
- }
- } else if (*str == '\n') {
- if (*(str + 1) == '\0')
- break;
- return -EINVAL;
- } else if (!strncmp(str, " dB", sizeof(" dB") - 1) && scale_db) {
- /* Ignore the dB suffix */
- str += sizeof(" dB") - 1;
- continue;
- } else if (!strncmp(str, "dB", sizeof("dB") - 1) && scale_db) {
- /* Ignore the dB suffix */
- str += sizeof("dB") - 1;
- continue;
- } else if (*str == '.' && integer_part) {
- integer_part = false;
- } else {
- return -EINVAL;
- }
- str++;
- }
-
- if (negative) {
- if (i)
- i = -i;
- else
- f = -f;
- }
-
- *integer = i;
- *fract = f;
-
- return 0;
-}
-
/**
* iio_str_to_fixpoint() - Parse a fixed-point number from a string
* @str: The string to parse
@@ -1075,13 +1004,79 @@ static int __iio_str_to_fixpoint(const char *str, int fract_mult,
* Returns:
* 0 on success, or a negative error code if the string could not be parsed.
*/
-int iio_str_to_fixpoint(const char *str, int fract_mult,
- int *integer, int *fract)
+int iio_str_to_fixpoint(const char *str, int fract_mult, int *integer,
+ int *fract)
{
- return __iio_str_to_fixpoint(str, fract_mult, integer, fract, false);
+ /* fract_mult = 10^n, so ffs(10^n) = ffs(2^n * 5^n) = n + 1 */
+ unsigned int scale = ffs(fract_mult);
+ s64 dec64;
+ int ret;
+
+ if (fract_mult) {
+ ret = kstrtodec64(str, scale, &dec64);
+ } else {
+ /* keep hex/octal support for integers */
+ ret = kstrtoll(str, 0, &dec64);
+ if (ret == -EINVAL)
+ ret = kstrtodec64(str, scale, &dec64);
+ }
+ if (ret)
+ return ret;
+
+ if (fract_mult > 0)
+ dec64 = div_s64_rem(dec64, fract_mult * 10, fract);
+ else
+ *fract = 0;
+
+ if (dec64 > INT_MAX || dec64 < INT_MIN)
+ return -ERANGE;
+
+ *integer = (int)dec64;
+ /* only carry the sign in the fractional part if the integer is zero */
+ if (*integer)
+ *fract = abs(*fract);
+
+ return 0;
}
EXPORT_SYMBOL_GPL(iio_str_to_fixpoint);
+/**
+ * iio_str_to_fixpoint_units() - Parse a fixed-point number with units from a string
+ * @str: The string to parse
+ * @units: The units to expect at the end of the string
+ * @fract_mult: Multiplier for the first decimal place, should be a power of 10
+ * @integer: The integer part of the number
+ * @fract: The fractional part of the number
+ *
+ * Returns:
+ * 0 on success, or a negative error code if the string could not be parsed.
+ */
+static int iio_str_to_fixpoint_units(const char *str, const char *units,
+ int fract_mult, int *integer, int *fract)
+{
+ size_t units_len = strlen(units);
+ size_t num_len = strlen(str);
+ const char *dec_str = str;
+ char buf[64];
+
+ if (num_len && str[num_len - 1] == '\n')
+ num_len--;
+
+ if (num_len > units_len &&
+ !strncmp(str + num_len - units_len, units, units_len)) {
+ num_len -= units_len;
+ if (str[num_len - 1] == ' ')
+ num_len--;
+ if (num_len >= sizeof(buf))
+ return -EINVAL;
+ memcpy(buf, str, num_len);
+ buf[num_len] = '\0';
+ dec_str = buf;
+ }
+
+ return iio_str_to_fixpoint(dec_str, fract_mult, integer, fract);
+}
+
static ssize_t iio_write_channel_info(struct device *dev,
struct device_attribute *attr,
const char *buf,
@@ -1143,17 +1138,25 @@ static ssize_t iio_write_channel_info(struct device *dev,
return -EINVAL;
integer = ch;
} else if (is_64bit) {
- if (dec_scale)
+ if (dec_scale) {
ret = kstrtodec64(buf, dec_scale, &integer64);
- else
+ } else {
+ /* keep hex/octal support for integers */
ret = kstrtoll(buf, 0, &integer64);
+ if (ret == -EINVAL)
+ ret = kstrtodec64(buf, dec_scale, &integer64);
+ }
if (ret)
return ret;
iio_val_s64_decompose(integer64, &integer, &fract);
} else {
- ret = __iio_str_to_fixpoint(buf, fract_mult, &integer, &fract,
- scale_db);
+ if (scale_db)
+ ret = iio_str_to_fixpoint_units(buf, "dB", fract_mult,
+ &integer, &fract);
+ else
+ ret = iio_str_to_fixpoint(buf, fract_mult, &integer,
+ &fract);
if (ret)
return ret;
}
--
2.43.0
WARNING: multiple messages have this Message-ID (diff)
From: Rodrigo Alencar <rodrigo.alencar@analog.com>
To: linux-kernel@vger.kernel.org, linux-iio@vger.kernel.org
Cc: Andrew Morton <akpm@linux-foundation.org>,
Jonathan Cameron <jic23@kernel.org>,
David Lechner <dlechner@baylibre.com>,
Andy Shevchenko <andy@kernel.org>,
Rodrigo Alencar <rodrigo.alencar@analog.com>
Subject: [PATCH RFC v2 3/4] iio: core: use kstrtodec64() to parse fixed-point values
Date: Tue, 13 Oct 2026 10:36:46 -0300 [thread overview]
Message-ID: <20261013-e-notation-v2-3-d4adb5e193fc@analog.com> (raw)
In-Reply-To: <20261013-e-notation-v2-0-d4adb5e193fc@analog.com>
Replace the open-coded __iio_str_to_fixpoint() parser with kstrtodec64(),
so sysfs writes of fixed-point values also accept E notation.
The fractional part only keeps the sign when the integer part is zero,
as before. The dB suffix for scale attributes is now handled by a separate
iio_str_to_fixpoint_units() helper. It strips an optional trailing newline
and the "dB" or " dB" suffix before parsing.
When there is no fractional part (fract_mult == 0, or dec_scale == 0 for
64-bit values), try base-autodetecting kstrtoll() first so hexadecimal
and octal input keeps its meaning ("010" is still 8). Fall back to
kstrtodec64() on -EINVAL to accept E notation.
As a side effect, inputs that used to be rejected for integer-only
attributes are now accepted. An invalid octal number such as "08" is
parsed as decimal 8, and a fractional value such as "1.5" is truncated
to 1, matching how kstrtodec64() drops digits beyond the requested
scale.
Signed-off-by: Rodrigo Alencar <rodrigo.alencar@analog.com>
---
drivers/iio/industrialio-core.c | 159 ++++++++++++++++++++--------------------
1 file changed, 81 insertions(+), 78 deletions(-)
diff --git a/drivers/iio/industrialio-core.c b/drivers/iio/industrialio-core.c
index d73f2541375f..616bfea711e0 100644
--- a/drivers/iio/industrialio-core.c
+++ b/drivers/iio/industrialio-core.c
@@ -994,77 +994,6 @@ static ssize_t iio_read_channel_info_avail(struct device *dev,
}
}
-/**
- * __iio_str_to_fixpoint() - Parse a fixed-point number from a string
- * @str: The string to parse
- * @fract_mult: Multiplier for the first decimal place, should be a power of 10
- * @integer: The integer part of the number
- * @fract: The fractional part of the number
- * @scale_db: True if this should parse as dB
- *
- * Returns:
- * 0 on success, or a negative error code if the string could not be parsed.
- */
-static int __iio_str_to_fixpoint(const char *str, int fract_mult,
- int *integer, int *fract, bool scale_db)
-{
- int i = 0, f = 0;
- bool integer_part = true, negative = false;
-
- if (fract_mult == 0) {
- *fract = 0;
-
- return kstrtoint(str, 0, integer);
- }
-
- if (str[0] == '-') {
- negative = true;
- str++;
- } else if (str[0] == '+') {
- str++;
- }
-
- while (*str) {
- if ('0' <= *str && *str <= '9') {
- if (integer_part) {
- i = i * 10 + *str - '0';
- } else {
- f += fract_mult * (*str - '0');
- fract_mult /= 10;
- }
- } else if (*str == '\n') {
- if (*(str + 1) == '\0')
- break;
- return -EINVAL;
- } else if (!strncmp(str, " dB", sizeof(" dB") - 1) && scale_db) {
- /* Ignore the dB suffix */
- str += sizeof(" dB") - 1;
- continue;
- } else if (!strncmp(str, "dB", sizeof("dB") - 1) && scale_db) {
- /* Ignore the dB suffix */
- str += sizeof("dB") - 1;
- continue;
- } else if (*str == '.' && integer_part) {
- integer_part = false;
- } else {
- return -EINVAL;
- }
- str++;
- }
-
- if (negative) {
- if (i)
- i = -i;
- else
- f = -f;
- }
-
- *integer = i;
- *fract = f;
-
- return 0;
-}
-
/**
* iio_str_to_fixpoint() - Parse a fixed-point number from a string
* @str: The string to parse
@@ -1075,13 +1004,79 @@ static int __iio_str_to_fixpoint(const char *str, int fract_mult,
* Returns:
* 0 on success, or a negative error code if the string could not be parsed.
*/
-int iio_str_to_fixpoint(const char *str, int fract_mult,
- int *integer, int *fract)
+int iio_str_to_fixpoint(const char *str, int fract_mult, int *integer,
+ int *fract)
{
- return __iio_str_to_fixpoint(str, fract_mult, integer, fract, false);
+ /* fract_mult = 10^n, so ffs(10^n) = ffs(2^n * 5^n) = n + 1 */
+ unsigned int scale = ffs(fract_mult);
+ s64 dec64;
+ int ret;
+
+ if (fract_mult) {
+ ret = kstrtodec64(str, scale, &dec64);
+ } else {
+ /* keep hex/octal support for integers */
+ ret = kstrtoll(str, 0, &dec64);
+ if (ret == -EINVAL)
+ ret = kstrtodec64(str, scale, &dec64);
+ }
+ if (ret)
+ return ret;
+
+ if (fract_mult > 0)
+ dec64 = div_s64_rem(dec64, fract_mult * 10, fract);
+ else
+ *fract = 0;
+
+ if (dec64 > INT_MAX || dec64 < INT_MIN)
+ return -ERANGE;
+
+ *integer = (int)dec64;
+ /* only carry the sign in the fractional part if the integer is zero */
+ if (*integer)
+ *fract = abs(*fract);
+
+ return 0;
}
EXPORT_SYMBOL_GPL(iio_str_to_fixpoint);
+/**
+ * iio_str_to_fixpoint_units() - Parse a fixed-point number with units from a string
+ * @str: The string to parse
+ * @units: The units to expect at the end of the string
+ * @fract_mult: Multiplier for the first decimal place, should be a power of 10
+ * @integer: The integer part of the number
+ * @fract: The fractional part of the number
+ *
+ * Returns:
+ * 0 on success, or a negative error code if the string could not be parsed.
+ */
+static int iio_str_to_fixpoint_units(const char *str, const char *units,
+ int fract_mult, int *integer, int *fract)
+{
+ size_t units_len = strlen(units);
+ size_t num_len = strlen(str);
+ const char *dec_str = str;
+ char buf[64];
+
+ if (num_len && str[num_len - 1] == '\n')
+ num_len--;
+
+ if (num_len > units_len &&
+ !strncmp(str + num_len - units_len, units, units_len)) {
+ num_len -= units_len;
+ if (str[num_len - 1] == ' ')
+ num_len--;
+ if (num_len >= sizeof(buf))
+ return -EINVAL;
+ memcpy(buf, str, num_len);
+ buf[num_len] = '\0';
+ dec_str = buf;
+ }
+
+ return iio_str_to_fixpoint(dec_str, fract_mult, integer, fract);
+}
+
static ssize_t iio_write_channel_info(struct device *dev,
struct device_attribute *attr,
const char *buf,
@@ -1143,17 +1138,25 @@ static ssize_t iio_write_channel_info(struct device *dev,
return -EINVAL;
integer = ch;
} else if (is_64bit) {
- if (dec_scale)
+ if (dec_scale) {
ret = kstrtodec64(buf, dec_scale, &integer64);
- else
+ } else {
+ /* keep hex/octal support for integers */
ret = kstrtoll(buf, 0, &integer64);
+ if (ret == -EINVAL)
+ ret = kstrtodec64(buf, dec_scale, &integer64);
+ }
if (ret)
return ret;
iio_val_s64_decompose(integer64, &integer, &fract);
} else {
- ret = __iio_str_to_fixpoint(buf, fract_mult, &integer, &fract,
- scale_db);
+ if (scale_db)
+ ret = iio_str_to_fixpoint_units(buf, "dB", fract_mult,
+ &integer, &fract);
+ else
+ ret = iio_str_to_fixpoint(buf, fract_mult, &integer,
+ &fract);
if (ret)
return ret;
}
--
2.43.0
next prev parent reply other threads:[~2026-10-13 13:36 UTC|newest]
Thread overview: 15+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-10-13 13:36 [PATCH RFC v2 0/4] Add E notation support to kstrtodec64() and IIO sysfs writes Rodrigo Alencar via B4 Relay
2026-10-13 13:36 ` Rodrigo Alencar
2026-10-13 13:36 ` [PATCH RFC v2 1/4] lib: kstrtox: support E notation in kstrtoudec64() and kstrtodec64() Rodrigo Alencar via B4 Relay
2026-10-13 13:36 ` Rodrigo Alencar
2026-10-13 18:48 ` Andy Shevchenko
2026-10-13 13:36 ` [PATCH RFC v2 2/4] lib: test-kstrtox: add E notation tests for kstrto[u]dec64() Rodrigo Alencar via B4 Relay
2026-10-13 13:36 ` Rodrigo Alencar
2026-10-13 18:49 ` Andy Shevchenko
2026-10-13 13:36 ` Rodrigo Alencar via B4 Relay [this message]
2026-10-13 13:36 ` [PATCH RFC v2 3/4] iio: core: use kstrtodec64() to parse fixed-point values Rodrigo Alencar
2026-10-13 19:08 ` Andy Shevchenko
2026-10-14 10:46 ` Rodrigo Alencar
2026-10-13 13:36 ` [PATCH RFC v2 4/4] iio: test: add kunit tests for channel info writes Rodrigo Alencar via B4 Relay
2026-10-13 13:36 ` Rodrigo Alencar
2026-10-13 19:10 ` Andy Shevchenko
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20261013-e-notation-v2-3-d4adb5e193fc@analog.com \
--to=devnull+rodrigo.alencar.analog.com@kernel.org \
--cc=akpm@linux-foundation.org \
--cc=andy@kernel.org \
--cc=dlechner@baylibre.com \
--cc=jic23@kernel.org \
--cc=linux-iio@vger.kernel.org \
--cc=linux-kernel@vger.kernel.org \
--cc=rodrigo.alencar@analog.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.