From: Jonathan Cameron <jic23@kernel.org>
To: Jinseob Kim <kimjinseob88@gmail.com>
Cc: linux-iio@vger.kernel.org, dlechner@baylibre.com,
nuno.sa@analog.com, andy@kernel.org, robh@kernel.org,
krzk+dt@kernel.org, conor+dt@kernel.org,
neil.armstrong@linaro.org, andersson@kernel.org,
prabhakar.mahadev-lad.rj@bp.renesas.com,
devicetree@vger.kernel.org, linux-kernel@vger.kernel.org,
corbet@lwn.net, skhan@linuxfoundation.org, rdunlap@infradead.org,
linux-doc@vger.kernel.org
Subject: Re: [PATCH v11 7/8] iio: osf: add core KUnit tests
Date: Sun, 18 Oct 2026 00:58:05 +0100 [thread overview]
Message-ID: <20261018005805.7db7ca9f@jic23-hlaptop> (raw)
In-Reply-To: <20261011231340.42885-8-kimjinseob88@gmail.com>
On Mon, 12 Oct 2026 08:13:39 +0900
Jinseob Kim <kimjinseob88@gmail.com> wrote:
> Exercise capability discovery, complete repeated inventories, sample-cache
> admission, exact lengths, reserved wire bytes and session faults. Protect
> producer lifetime with deferred cleanup and close the session mutex
> lifecycle before reinitialization.
>
> Assisted-by: LLM
> Signed-off-by: Jinseob Kim <kimjinseob88@gmail.com>
https://sashiko.dev/#/patchset/20261011231340.42885-1-kimjinseob88%40gmail.com
The comment about maybe allowing building when osf is a module
looks superficially valid. Some of the others are about
outdated code, but please take a look.
Thanks,
Jonathan
> ---
> drivers/iio/sensorhubs/osf/Kconfig | 12 +
> drivers/iio/sensorhubs/osf/Makefile | 1 +
> drivers/iio/sensorhubs/osf/osf_core_test.c | 1056 ++++++++++++++++++++
> 3 files changed, 1069 insertions(+)
> create mode 100644 drivers/iio/sensorhubs/osf/osf_core_test.c
>
> diff --git a/drivers/iio/sensorhubs/osf/Kconfig b/drivers/iio/sensorhubs/osf/Kconfig
> index f955a1f2993d244369943efbd853b9d2c6d47139..316f33be1327ca85eb18920b8d610a3e983439d2 100644
> --- a/drivers/iio/sensorhubs/osf/Kconfig
> +++ b/drivers/iio/sensorhubs/osf/Kconfig
> @@ -13,3 +13,15 @@ config OPEN_SENSOR_FUSION
> registers IIO devices for supported capability entries. It exposes
> accelerometer, gyroscope, magnetometer, and temperature samples
> through IIO direct reads and software buffers.
> +
> +config OPEN_SENSOR_FUSION_KUNIT_TEST
> + bool "KUnit tests for Open Sensor Fusion" if !KUNIT_ALL_TESTS
> + depends on OPEN_SENSOR_FUSION=y && KUNIT=y
> + default KUNIT_ALL_TESTS
> + help
> + Build focused unit tests for the Open Sensor Fusion core sample
> + acceptance, direct-read cache, and capability/session handling. The tests
> + exercise accepted, ignored, and rejected sample handling without
> + exposing additional production interfaces.
> +
> + If unsure, say N.
> diff --git a/drivers/iio/sensorhubs/osf/Makefile b/drivers/iio/sensorhubs/osf/Makefile
> index b4e03b80cfa4cca6a67e6cd8228a4776a38dcb72..c2f5ba065a62fef2a4897034239b405ccdd69250 100644
> --- a/drivers/iio/sensorhubs/osf/Makefile
> +++ b/drivers/iio/sensorhubs/osf/Makefile
> @@ -4,3 +4,4 @@ obj-$(CONFIG_OPEN_SENSOR_FUSION) += open-sensor-fusion.o
>
> open-sensor-fusion-y := osf_core.o osf_iio.o osf_protocol.o osf_serdev.o \
> osf_stream.o
> +open-sensor-fusion-$(CONFIG_OPEN_SENSOR_FUSION_KUNIT_TEST) += osf_core_test.o
> diff --git a/drivers/iio/sensorhubs/osf/osf_core_test.c b/drivers/iio/sensorhubs/osf/osf_core_test.c
> new file mode 100644
> index 0000000000000000000000000000000000000000..fabf19018b200359bcfe5a910e6190daae3d22f2
> --- /dev/null
> +++ b/drivers/iio/sensorhubs/osf/osf_core_test.c
> @@ -0,0 +1,1056 @@
> +// SPDX-License-Identifier: GPL-2.0-only
> +
> +#include <kunit/device.h>
> +#include <kunit/test.h>
> +
> +#include <linux/bitmap.h>
> +#include <linux/crc32.h>
> +#include <linux/completion.h>
> +#include <linux/delay.h>
> +#include <linux/err.h>
> +#include <linux/errno.h>
> +#include <linux/iio/buffer.h>
> +#include <linux/iio/buffer_impl.h>
> +#include <linux/iio/iio.h>
> +#include <linux/module.h>
> +#include <linux/kthread.h>
> +#include <linux/string.h>
> +#include <linux/types.h>
> +#include <linux/unaligned.h>
> +
> +#include "osf_core.h"
> +#include "osf_protocol.h"
> +#include "osf_stream.h"
> +
> +#define OSF_TEST_SENSOR_INDEX 0
> +#define OSF_TEST_UNSUPPORTED_INDEX 7
> +#define OSF_TEST_SCALE_NANO 1000000
> +#define OSF_TEST_FLOOD_COUNT (OSF_MAX_CAPABILITIES + 4)
> +#define OSF_TEST_MAX_PAYLOAD_LEN \
> + (OSF_SENSOR_SAMPLE_BASE_LEN + OSF_MAX_SAMPLE_CHANNELS * sizeof(__le32))
> +#define OSF_TEST_MAX_FRAME_LEN \
> + (OSF_FRAME_HEADER_LEN + OSF_CAP_REPORT_BASE_LEN + \
> + 3 * OSF_CAP_SENSOR_ENTRY_LEN + OSF_FRAME_CRC_LEN)
> +
> +struct osf_test_context {
> + struct osf_device osf;
> + struct device *dev;
> + struct iio_dev *indio_dev;
> + struct iio_buffer *buffer;
> + bool buffer_enabled;
> +};
> +
> +struct osf_test_scan_3axis {
> + s32 values[3];
> +};
> +
> +static size_t osf_test_build_frame(u8 *buf, size_t buf_size,
> + u16 message_type, const u8 *payload,
> + u32 payload_len, u64 sequence)
> +{
> + size_t frame_len = OSF_FRAME_HEADER_LEN + payload_len +
> + OSF_FRAME_CRC_LEN;
> + u32 crc;
> +
> + if (frame_len > buf_size)
> + return 0;
> +
> + memset(buf, 0, frame_len);
> + put_unaligned_le32(OSF_FRAME_MAGIC, buf);
> + buf[4] = OSF_PROTOCOL_MAJOR;
> + buf[5] = OSF_PROTOCOL_MINOR;
> + put_unaligned_le16(OSF_FRAME_HEADER_LEN, buf + 6);
> + put_unaligned_le16(message_type, buf + 8);
> + put_unaligned_le32(payload_len, buf + 10);
> + put_unaligned_le64(sequence, buf + 14);
> + put_unaligned_le64(sequence * 1000, buf + 22);
> + memcpy(buf + OSF_FRAME_HEADER_LEN, payload, payload_len);
> +
> + crc = crc32_le(~0U, buf, OSF_FRAME_HEADER_LEN + payload_len) ^ ~0U;
> + put_unaligned_le32(crc, buf + OSF_FRAME_HEADER_LEN + payload_len);
> +
> + return frame_len;
> +}
> +
> +static size_t osf_test_build_capability_frame(u8 *buf, size_t buf_size,
> + u64 sequence)
> +{
> + u8 payload[OSF_CAP_REPORT_BASE_LEN +
> + 2 * OSF_CAP_SENSOR_ENTRY_LEN] = { };
> + u8 *supported = payload + OSF_CAP_REPORT_BASE_LEN;
> + u8 *unsupported = supported + OSF_CAP_SENSOR_ENTRY_LEN;
> +
> + put_unaligned_le16(2, payload);
> + put_unaligned_le16(OSF_SENSOR_ACCELEROMETER, supported);
> + put_unaligned_le16(OSF_TEST_SENSOR_INDEX, supported + 2);
> + put_unaligned_le16(3, supported + 4);
> + put_unaligned_le16(OSF_SAMPLE_FORMAT_S32, supported + 6);
> + put_unaligned_le32(OSF_TEST_SCALE_NANO, supported + 8);
> +
> + put_unaligned_le16(OSF_SENSOR_BAROMETER, unsupported);
> + put_unaligned_le16(OSF_TEST_UNSUPPORTED_INDEX, unsupported + 2);
> + put_unaligned_le16(1, unsupported + 4);
> + put_unaligned_le16(OSF_SAMPLE_FORMAT_S32, unsupported + 6);
> + put_unaligned_le32(OSF_TEST_SCALE_NANO, unsupported + 8);
> +
> + return osf_test_build_frame(buf, buf_size, OSF_MSG_CAPABILITY_REPORT,
> + payload, sizeof(payload), sequence);
> +}
> +
> +static size_t osf_test_build_sample_frame(u8 *buf, size_t buf_size,
> + u16 sensor_type, u16 sensor_index,
> + u16 channel_count, u16 sample_format,
> + const s32 *values, u64 sequence)
> +{
> + u8 payload[OSF_TEST_MAX_PAYLOAD_LEN] = { };
> + u32 payload_len;
> +
> + if (!channel_count || channel_count > OSF_MAX_SAMPLE_CHANNELS)
> + return 0;
> +
> + put_unaligned_le16(sensor_type, payload);
> + put_unaligned_le16(sensor_index, payload + 2);
> + put_unaligned_le16(channel_count, payload + 4);
> + put_unaligned_le16(sample_format, payload + 6);
> + put_unaligned_le32(OSF_TEST_SCALE_NANO, payload + 8);
> +
> + for (u16 i = 0; i < channel_count; i++)
> + put_unaligned_le32(values[i],
> + payload + OSF_SENSOR_SAMPLE_BASE_LEN +
> + i * sizeof(__le32));
> +
> + payload_len = OSF_SENSOR_SAMPLE_BASE_LEN +
> + channel_count * sizeof(__le32);
> +
> + return osf_test_build_frame(buf, buf_size, OSF_MSG_SENSOR_SAMPLE,
> + payload, payload_len, sequence);
> +}
> +
> +static size_t osf_test_build_truncated_sample_frame(u8 *buf, size_t buf_size,
> + u64 sequence)
> +{
> + u8 payload[OSF_SENSOR_SAMPLE_BASE_LEN + sizeof(__le32)] = { };
> +
> + put_unaligned_le16(OSF_SENSOR_ACCELEROMETER, payload);
> + put_unaligned_le16(OSF_TEST_SENSOR_INDEX, payload + 2);
> + put_unaligned_le16(3, payload + 4);
> + put_unaligned_le16(OSF_SAMPLE_FORMAT_S32, payload + 6);
> + put_unaligned_le32(OSF_TEST_SCALE_NANO, payload + 8);
> + put_unaligned_le32(42, payload + OSF_SENSOR_SAMPLE_BASE_LEN);
> +
> + return osf_test_build_frame(buf, buf_size, OSF_MSG_SENSOR_SAMPLE,
> + payload, sizeof(payload), sequence);
> +}
> +
> +/* Keep frame-construction scratch out of the caller's KASAN stack frame. */
> +static noinline int osf_test_submit_sample(struct osf_test_context *ctx,
> + u16 sensor_type, u16 sensor_index,
> + u16 channel_count, u16 sample_format,
> + const s32 *values, u64 sequence)
> +{
> + u8 frame[OSF_TEST_MAX_FRAME_LEN];
> + size_t frame_len;
> +
> + frame_len = osf_test_build_sample_frame(frame, sizeof(frame),
> + sensor_type, sensor_index,
> + channel_count, sample_format,
> + values, sequence);
> + if (!frame_len)
> + return -EINVAL;
> +
> + return osf_core_receive_frame(&ctx->osf, frame, frame_len);
> +}
> +
> +static int osf_test_read_raw(struct osf_test_context *ctx,
> + unsigned int channel, int *value)
> +{
> + int value2 = 0;
> +
> + return ctx->indio_dev->info->read_raw(ctx->indio_dev,
> + &ctx->indio_dev->channels[channel],
> + value, &value2,
> + IIO_CHAN_INFO_RAW);
> +}
> +
> +static int osf_test_remove_scan(struct osf_test_context *ctx,
> + struct osf_test_scan_3axis *scan)
> +{
> + return iio_pop_from_buffer(ctx->buffer, scan);
> +}
> +
> +static int osf_test_enable_buffer(struct osf_test_context *ctx)
> +{
> + unsigned long *scan_mask;
> + int ret;
> +
> + scan_mask = (unsigned long *)ctx->buffer->scan_mask;
> + for (unsigned int i = 0; i < 3; i++)
> + bitmap_set(scan_mask, ctx->indio_dev->channels[i].scan_index, 1);
> + ret = iio_update_buffers(ctx->indio_dev, ctx->buffer, NULL);
> + if (!ret)
> + ctx->buffer_enabled = true;
> +
> + return ret;
> +}
> +
> +static void osf_test_cleanup(void *data)
> +{
> + struct osf_test_context *ctx = data;
> +
> + if (ctx->buffer_enabled) {
> + iio_update_buffers(ctx->indio_dev, NULL, ctx->buffer);
> + ctx->buffer_enabled = false;
> + }
> +
> + osf_core_unregister_iio(&ctx->osf);
> + mutex_destroy(&ctx->osf.latest_lock);
> +}
> +
> +static int osf_test_init(struct kunit *test)
> +{
> + struct osf_test_context *ctx;
> + u8 frame[OSF_TEST_MAX_FRAME_LEN];
> + size_t frame_len;
> + int ret;
> +
> + ctx = kunit_kzalloc(test, sizeof(*ctx), GFP_KERNEL);
> + if (!ctx)
> + return -ENOMEM;
> +
> + test->priv = ctx;
> + ctx->dev = kunit_device_register(test, "osf-core");
> + if (IS_ERR(ctx->dev))
> + return PTR_ERR(ctx->dev);
> + if (!ctx->dev)
> + return -ENOMEM;
> +
> + osf_core_init(&ctx->osf, ctx->dev);
> + osf_core_start(&ctx->osf);
> + ret = kunit_add_action_or_reset(test, osf_test_cleanup, ctx);
> + if (ret)
> + return ret;
> +
> + frame_len = osf_test_build_capability_frame(frame, sizeof(frame), 1);
> + if (!frame_len)
> + return -EINVAL;
> +
> + ret = osf_core_receive_frame(&ctx->osf, frame, frame_len);
> + if (ret != OSF_STREAM_FRAME_HANDLED)
> + return ret < 0 ? ret : -EINVAL;
> + if (ctx->osf.iio_dev_count != 1 ||
> + ctx->osf.capability_cache.capability_count != 1)
> + return -EINVAL;
> +
> + ctx->indio_dev = ctx->osf.iio_devs[0].indio_dev;
> + ctx->buffer = ctx->indio_dev->buffer;
> + if (!ctx->buffer)
> + return -EINVAL;
> +
> + return 0;
> +}
> +
> +static void osf_test_expect_direct_sample(struct kunit *test,
> + struct osf_test_context *ctx,
> + const s32 *expected)
> +{
> + int value;
> + int ret;
> +
> + for (unsigned int i = 0; i < 3; i++) {
> + value = 0;
> + ret = osf_test_read_raw(ctx, i, &value);
> + KUNIT_EXPECT_EQ(test, ret, IIO_VAL_INT);
> + KUNIT_EXPECT_EQ(test, value, expected[i]);
> + }
> +}
> +
> +static void
> +osf_rejected_channel_count_preserves_latest_test(struct kunit *test)
> +{
> + struct osf_test_context *ctx = test->priv;
> + const s32 baseline_values[] = { 11, 22, 33 };
> + const s32 rejected_values[] = { -999 };
> + struct osf_sensor_sample decoded_sample;
> + struct osf_latest_sample baseline;
> + struct osf_test_scan_3axis scan;
> + struct osf_frame decoded_frame;
> + u8 rejected_frame[OSF_TEST_MAX_FRAME_LEN];
> + size_t decoded_len;
> + size_t frame_len;
> + u64 baseline_sequence;
> + int ret;
> +
> + KUNIT_ASSERT_EQ(test, osf_test_enable_buffer(ctx), 0);
> + KUNIT_ASSERT_TRUE(test, iio_buffer_enabled(ctx->indio_dev));
> +
> + ret = osf_test_submit_sample(ctx, OSF_SENSOR_ACCELEROMETER,
> + OSF_TEST_SENSOR_INDEX, 3,
> + OSF_SAMPLE_FORMAT_S32,
> + baseline_values, 2);
> + KUNIT_ASSERT_EQ(test, ret, OSF_STREAM_FRAME_HANDLED);
> + KUNIT_ASSERT_EQ(test, ctx->osf.latest_sample_count, 1U);
> + osf_test_expect_direct_sample(test, ctx, baseline_values);
> +
> + KUNIT_ASSERT_EQ(test, osf_test_remove_scan(ctx, &scan), 0);
> +
> + baseline = ctx->osf.latest_samples[0];
> + baseline_sequence = ctx->osf.last_sequence;
> + frame_len = osf_test_build_sample_frame(rejected_frame,
> + sizeof(rejected_frame),
> + OSF_SENSOR_ACCELEROMETER,
> + OSF_TEST_SENSOR_INDEX, 1,
> + OSF_SAMPLE_FORMAT_S32,
> + rejected_values, 3);
> + KUNIT_ASSERT_NE(test, frame_len, (size_t)0);
> + KUNIT_ASSERT_EQ(test,
> + osf_protocol_decode_frame(rejected_frame, frame_len,
> + &decoded_frame, &decoded_len), 0);
> + KUNIT_ASSERT_EQ(test, decoded_len, frame_len);
> + KUNIT_ASSERT_EQ(test,
> + osf_protocol_decode_sensor_sample(&decoded_frame,
> + &decoded_sample), 0);
> + KUNIT_ASSERT_EQ(test, decoded_sample.channel_count, (u16)1);
> + ret = osf_core_receive_frame(&ctx->osf, rejected_frame, frame_len);
> + KUNIT_ASSERT_EQ(test, ret, OSF_STREAM_FRAME_REJECTED);
> + KUNIT_EXPECT_EQ(test, ctx->osf.latest_sample_count, 1U);
> + KUNIT_EXPECT_MEMEQ(test, &ctx->osf.latest_samples[0], &baseline,
> + sizeof(baseline));
> + KUNIT_EXPECT_EQ(test, ctx->osf.last_sequence, baseline_sequence);
> + osf_test_expect_direct_sample(test, ctx, baseline_values);
> + KUNIT_EXPECT_EQ(test, osf_test_remove_scan(ctx, &scan), -EBUSY);
> +}
> +
> +static void
> +osf_valid_sample_updates_direct_and_buffer_test(struct kunit *test)
> +{
> + struct osf_test_context *ctx = test->priv;
> + const s32 baseline_values[] = { 1, 2, 3 };
> + const s32 rejected_values[] = { -1 };
> + const s32 valid_values[] = { 101, -202, 303 };
> + struct osf_test_scan_3axis scan = { };
> + int ret;
> +
> + KUNIT_ASSERT_EQ(test, osf_test_enable_buffer(ctx), 0);
> +
> + ret = osf_test_submit_sample(ctx, OSF_SENSOR_ACCELEROMETER,
> + OSF_TEST_SENSOR_INDEX, 3,
> + OSF_SAMPLE_FORMAT_S32,
> + baseline_values, 2);
> + KUNIT_ASSERT_EQ(test, ret, OSF_STREAM_FRAME_HANDLED);
> + KUNIT_ASSERT_EQ(test, osf_test_remove_scan(ctx, &scan), 0);
> +
> + ret = osf_test_submit_sample(ctx, OSF_SENSOR_ACCELEROMETER,
> + OSF_TEST_SENSOR_INDEX, 1,
> + OSF_SAMPLE_FORMAT_S32,
> + rejected_values, 3);
> + KUNIT_ASSERT_EQ(test, ret, OSF_STREAM_FRAME_REJECTED);
> + KUNIT_ASSERT_EQ(test, osf_test_remove_scan(ctx, &scan), -EBUSY);
> +
> + ret = osf_test_submit_sample(ctx, OSF_SENSOR_ACCELEROMETER,
> + OSF_TEST_SENSOR_INDEX, 3,
> + OSF_SAMPLE_FORMAT_S32,
> + valid_values, 4);
> + KUNIT_ASSERT_EQ(test, ret, OSF_STREAM_FRAME_HANDLED);
> + osf_test_expect_direct_sample(test, ctx, valid_values);
> + KUNIT_ASSERT_EQ(test, ctx->indio_dev->scan_bytes, (int)sizeof(scan));
> + KUNIT_ASSERT_EQ(test, osf_test_remove_scan(ctx, &scan), 0);
> + KUNIT_EXPECT_EQ(test, scan.values[0], valid_values[0]);
> + KUNIT_EXPECT_EQ(test, scan.values[1], valid_values[1]);
> + KUNIT_EXPECT_EQ(test, scan.values[2], valid_values[2]);
> + KUNIT_EXPECT_EQ(test, osf_test_remove_scan(ctx, &scan), -EBUSY);
> +}
> +
> +static void osf_unregistered_sample_is_ignored_test(struct kunit *test)
> +{
> + struct osf_test_context *ctx = test->priv;
> + const s32 baseline_values[] = { 10, 20, 30 };
> + const s32 ignored_values[] = { -10, -20, -30 };
> + struct osf_latest_sample baseline;
> + struct osf_test_scan_3axis scan;
> + s32 value;
> + int ret;
> +
> + KUNIT_ASSERT_EQ(test, osf_test_enable_buffer(ctx), 0);
> + ret = osf_test_submit_sample(ctx, OSF_SENSOR_ACCELEROMETER,
> + OSF_TEST_SENSOR_INDEX, 3,
> + OSF_SAMPLE_FORMAT_S32,
> + baseline_values, 2);
> + KUNIT_ASSERT_EQ(test, ret, OSF_STREAM_FRAME_HANDLED);
> + KUNIT_ASSERT_EQ(test, osf_test_remove_scan(ctx, &scan), 0);
> + baseline = ctx->osf.latest_samples[0];
> +
> + ret = osf_test_submit_sample(ctx, OSF_SENSOR_ACCELEROMETER,
> + OSF_TEST_SENSOR_INDEX + 1, 3,
> + OSF_SAMPLE_FORMAT_S32,
> + ignored_values, 3);
> + KUNIT_ASSERT_EQ(test, ret, OSF_STREAM_FRAME_IGNORED);
> + KUNIT_EXPECT_EQ(test, ctx->osf.iio_dev_count, 1U);
> + KUNIT_EXPECT_EQ(test, ctx->osf.latest_sample_count, 1U);
> + KUNIT_EXPECT_MEMEQ(test, &ctx->osf.latest_samples[0], &baseline,
> + sizeof(baseline));
> + osf_test_expect_direct_sample(test, ctx, baseline_values);
> + ret = osf_core_read_latest_sample(&ctx->osf,
> + OSF_SENSOR_ACCELEROMETER,
> + OSF_TEST_SENSOR_INDEX + 1,
> + 0, &value);
> + KUNIT_EXPECT_EQ(test, ret, -ENODATA);
> + KUNIT_EXPECT_EQ(test, osf_test_remove_scan(ctx, &scan), -EBUSY);
> +}
> +
> +static void
> +osf_unaccepted_samples_do_not_exhaust_cache_test(struct kunit *test)
> +{
> + struct osf_test_context *ctx = test->priv;
> + const s32 three_values[] = { 7, 8, 9 };
> + const s32 one_value[] = { 42 };
> + struct osf_latest_sample *empty_cache;
> + struct osf_sensor_sample decoded_sample;
> + struct osf_frame decoded_frame;
> + u8 malformed_frame[OSF_TEST_MAX_FRAME_LEN];
> + size_t decoded_len;
> + size_t frame_len;
> + int value;
> + int ret;
> +
> + KUNIT_ASSERT_EQ(test, ctx->osf.latest_sample_count, 0U);
> + empty_cache = kunit_kzalloc(test, sizeof(ctx->osf.latest_samples), GFP_KERNEL);
> + KUNIT_ASSERT_NOT_NULL(test, empty_cache);
> + memcpy(empty_cache, ctx->osf.latest_samples, sizeof(ctx->osf.latest_samples));
> +
> + for (unsigned int i = 0; i < OSF_TEST_FLOOD_COUNT; i++) {
> + ret = osf_test_submit_sample(ctx, OSF_SENSOR_ACCELEROMETER,
> + OSF_TEST_SENSOR_INDEX + i + 1,
> + 3, OSF_SAMPLE_FORMAT_S32,
> + three_values, 100 + i);
> + KUNIT_ASSERT_EQ(test, ret, OSF_STREAM_FRAME_IGNORED);
> + }
> + KUNIT_ASSERT_EQ(test, ctx->osf.latest_sample_count, 0U);
> +
> + for (unsigned int i = 0; i < OSF_TEST_FLOOD_COUNT; i++) {
> + ret = osf_test_submit_sample(ctx, OSF_SENSOR_BAROMETER,
> + OSF_TEST_UNSUPPORTED_INDEX, 1,
> + OSF_SAMPLE_FORMAT_S32,
> + one_value, 200 + i);
> + KUNIT_ASSERT_EQ(test, ret, OSF_STREAM_FRAME_IGNORED);
> + }
> + KUNIT_ASSERT_EQ(test, ctx->osf.latest_sample_count, 0U);
> +
> + for (unsigned int i = 0; i < OSF_TEST_FLOOD_COUNT; i++) {
> + ret = osf_test_submit_sample(ctx, OSF_SENSOR_ACCELEROMETER,
> + OSF_TEST_SENSOR_INDEX, 1,
> + OSF_SAMPLE_FORMAT_S32,
> + one_value, 300 + i);
> + KUNIT_ASSERT_EQ(test, ret, OSF_STREAM_FRAME_REJECTED);
> + }
> + KUNIT_ASSERT_EQ(test, ctx->osf.latest_sample_count, 0U);
> +
> + frame_len = osf_test_build_truncated_sample_frame(malformed_frame,
> + sizeof(malformed_frame), 400);
> + KUNIT_ASSERT_NE(test, frame_len, (size_t)0);
> + KUNIT_ASSERT_EQ(test,
> + osf_protocol_decode_frame(malformed_frame, frame_len,
> + &decoded_frame, &decoded_len), 0);
> + KUNIT_ASSERT_EQ(test, decoded_len, frame_len);
> + KUNIT_ASSERT_EQ(test,
> + osf_protocol_decode_sensor_sample(&decoded_frame,
> + &decoded_sample),
> + -EMSGSIZE);
> +
> + for (unsigned int i = 0; i < OSF_TEST_FLOOD_COUNT; i++) {
> + ret = osf_core_receive_frame(&ctx->osf, malformed_frame,
> + frame_len);
> + KUNIT_ASSERT_EQ(test, ret, OSF_STREAM_FRAME_REJECTED);
> + }
> + KUNIT_ASSERT_EQ(test, ctx->osf.latest_sample_count, 0U);
> +
> + KUNIT_EXPECT_MEMEQ(test, ctx->osf.latest_samples, empty_cache,
> + sizeof(ctx->osf.latest_samples));
> + KUNIT_EXPECT_EQ(test, ctx->osf.last_sequence, (u64)1);
> +
> + ret = osf_test_submit_sample(ctx, OSF_SENSOR_ACCELEROMETER,
> + OSF_TEST_SENSOR_INDEX, 3,
> + OSF_SAMPLE_FORMAT_S32,
> + three_values, 500);
> + KUNIT_ASSERT_EQ(test, ret, OSF_STREAM_FRAME_HANDLED);
> + KUNIT_ASSERT_EQ(test, ctx->osf.latest_sample_count, 1U);
> +
> + for (unsigned int i = 0; i < 3; i++) {
> + value = 0;
> + ret = osf_test_read_raw(ctx, i, &value);
> + KUNIT_ASSERT_EQ(test, ret, IIO_VAL_INT);
> + KUNIT_EXPECT_EQ(test, value, three_values[i]);
> + }
> +}
> +
> +static void osf_test_unregister_pending(void *data)
> +{
> + osf_core_unregister_iio(data);
> +}
> +
> +static void osf_early_capability_is_owned_test(struct kunit *test)
> +{
> + struct osf_test_context *ctx = test->priv;
> + struct osf_device *pending;
> + struct iio_dev *indio_dev;
> + u8 frame[OSF_TEST_MAX_FRAME_LEN];
> + size_t len;
> + int scale, nano, ret;
> +
> + pending = kunit_kzalloc(test, sizeof(*pending), GFP_KERNEL);
> + KUNIT_ASSERT_NOT_NULL(test, pending);
> + osf_core_init(pending, ctx->dev);
> + ret = kunit_add_action_or_reset(test, osf_test_unregister_pending, pending);
> + KUNIT_ASSERT_EQ(test, ret, 0);
> + len = osf_test_build_capability_frame(frame, sizeof(frame), 10);
> + KUNIT_ASSERT_NE(test, len, (size_t)0);
> + KUNIT_EXPECT_EQ(test, osf_core_receive_frame(pending, frame, len),
> + OSF_STREAM_FRAME_HANDLED);
> + KUNIT_EXPECT_EQ(test, pending->iio_dev_count, 0U);
> + KUNIT_ASSERT_TRUE(test, pending->capability_cache.valid);
> + KUNIT_EXPECT_EQ(test, pending->capability_cache.capability_count, (u16)1);
> +
> + /* The stream may reuse its storage before probe opens the registration gate. */
> + memset(frame, 0xa5, sizeof(frame));
> + osf_core_start(pending);
> + KUNIT_ASSERT_EQ(test, pending->iio_dev_count, 1U);
> + indio_dev = pending->iio_devs[0].indio_dev;
> + ret = indio_dev->info->read_raw(indio_dev, &indio_dev->channels[0],
> + &scale, &nano, IIO_CHAN_INFO_SCALE);
> + KUNIT_EXPECT_EQ(test, ret, IIO_VAL_INT_PLUS_NANO);
> + KUNIT_EXPECT_EQ(test, scale, 0);
> + KUNIT_EXPECT_EQ(test, nano, OSF_TEST_SCALE_NANO);
> + osf_core_start(pending);
> + KUNIT_EXPECT_EQ(test, pending->iio_dev_count, 1U);
> + len = osf_test_build_capability_frame(frame, sizeof(frame), 11);
> + KUNIT_EXPECT_EQ(test, osf_core_receive_frame(pending, frame, len),
> + OSF_STREAM_FRAME_IGNORED);
> + KUNIT_EXPECT_EQ(test, pending->iio_dev_count, 1U);
> + KUNIT_EXPECT_EQ(test, pending->latest_sample_count, 0U);
> +}
> +
> +static void osf_early_duplicate_capability_is_rejected_test(struct kunit *test)
> +{
> + struct osf_test_context *ctx = test->priv;
> + struct osf_device *pending;
> + u8 payload[OSF_CAP_REPORT_BASE_LEN + 2 * OSF_CAP_SENSOR_ENTRY_LEN] = { };
> + u8 frame[OSF_TEST_MAX_FRAME_LEN];
> + u8 *entry = payload + OSF_CAP_REPORT_BASE_LEN;
> + size_t len;
> + int ret;
> +
> + pending = kunit_kzalloc(test, sizeof(*pending), GFP_KERNEL);
> + KUNIT_ASSERT_NOT_NULL(test, pending);
> + osf_core_init(pending, ctx->dev);
> + ret = kunit_add_action_or_reset(test, osf_test_unregister_pending, pending);
> + KUNIT_ASSERT_EQ(test, ret, 0);
> + put_unaligned_le16(2, payload);
> + put_unaligned_le16(OSF_SENSOR_ACCELEROMETER, entry);
> + put_unaligned_le16(3, entry + 4);
> + put_unaligned_le16(OSF_SAMPLE_FORMAT_S32, entry + 6);
> + put_unaligned_le32(OSF_TEST_SCALE_NANO, entry + 8);
> + memcpy(entry + OSF_CAP_SENSOR_ENTRY_LEN, entry, OSF_CAP_SENSOR_ENTRY_LEN);
> + len = osf_test_build_frame(frame, sizeof(frame), OSF_MSG_CAPABILITY_REPORT,
> + payload, sizeof(payload), 20);
> + KUNIT_EXPECT_EQ(test, osf_core_receive_frame(pending, frame, len),
> + OSF_STREAM_FRAME_REJECTED);
> + KUNIT_EXPECT_FALSE(test, pending->capability_cache.valid);
> + KUNIT_EXPECT_EQ(test, pending->iio_dev_count, 0U);
> +
> + /* A failed early report must not prevent a subsequent valid report. */
> + len = osf_test_build_capability_frame(frame, sizeof(frame), 21);
> + KUNIT_EXPECT_EQ(test, osf_core_receive_frame(pending, frame, len),
> + OSF_STREAM_FRAME_HANDLED);
> + osf_core_start(pending);
> + KUNIT_EXPECT_EQ(test, pending->iio_dev_count, 1U);
> +}
> +
> +static const struct osf_capability_entry osf_test_accel = {
> + .sensor_type = OSF_SENSOR_ACCELEROMETER,
> + .channel_count = 3,
> + .sample_format = OSF_SAMPLE_FORMAT_S32,
> + .scale_nano = OSF_TEST_SCALE_NANO,
> +};
> +
> +static void osf_test_crc(u8 *frame, size_t len)
> +{
> + put_unaligned_le32(crc32_le(~0U, frame, len - OSF_FRAME_CRC_LEN) ^ ~0U,
> + frame + len - OSF_FRAME_CRC_LEN);
> +}
> +
> +static size_t osf_test_report(u8 *frame,
> + const struct osf_capability_entry *entries, u16 count)
> +{
> + u8 payload[OSF_CAP_REPORT_BASE_LEN + 3 * OSF_CAP_SENSOR_ENTRY_LEN] = { };
> +
> + put_unaligned_le16(count, payload);
> + for (u16 i = 0; i < count; i++) {
> + u8 *p = payload + OSF_CAP_REPORT_BASE_LEN + i * OSF_CAP_SENSOR_ENTRY_LEN;
> +
> + put_unaligned_le16(entries[i].sensor_type, p);
> + put_unaligned_le16(entries[i].sensor_index, p + 2);
> + put_unaligned_le16(entries[i].channel_count, p + 4);
> + put_unaligned_le16(entries[i].sample_format, p + 6);
> + put_unaligned_le32(entries[i].scale_nano, p + 8);
> + put_unaligned_le32(entries[i].flags, p + 12);
> + }
> + return osf_test_build_frame(frame, OSF_TEST_MAX_FRAME_LEN,
> + OSF_MSG_CAPABILITY_REPORT, payload,
> + OSF_CAP_REPORT_BASE_LEN + count * OSF_CAP_SENSOR_ENTRY_LEN,
> + 10);
> +}
> +
> +static int osf_test_submit_report(struct osf_test_context *ctx,
> + const struct osf_capability_entry *entries, u16 count)
> +{
> + u8 frame[OSF_TEST_MAX_FRAME_LEN];
> + size_t len = osf_test_report(frame, entries, count);
> +
> + return osf_core_receive_frame(&ctx->osf, frame, len);
> +}
> +
> +static void osf_test_new_session(struct osf_test_context *ctx)
> +{
> + osf_test_cleanup(ctx);
> + osf_core_init(&ctx->osf, ctx->dev);
> + osf_core_start(&ctx->osf);
> + ctx->indio_dev = NULL;
> + ctx->buffer = NULL;
> +}
> +
> +static void osf_test_bind_accel(struct kunit *test, struct osf_test_context *ctx)
> +{
> + KUNIT_ASSERT_EQ(test, osf_test_submit_report(ctx, &osf_test_accel, 1),
> + OSF_STREAM_FRAME_HANDLED);
> + KUNIT_ASSERT_EQ(test, ctx->osf.iio_dev_count, 1U);
> + ctx->indio_dev = ctx->osf.iio_devs[0].indio_dev;
> + ctx->buffer = ctx->indio_dev->buffer;
> +}
> +
> +static void osf_zero_scale_is_atomic_test(struct kunit *test)
> +{
> + struct osf_test_context *ctx = test->priv;
> + struct osf_capability_entry entries[] = { osf_test_accel, osf_test_accel };
> +
> + entries[1].sensor_index = 1;
> + entries[1].scale_nano = 0;
> + /* Even a repeat with a valid prefix must be rejected without a fault. */
> + KUNIT_EXPECT_EQ(test, osf_test_submit_report(ctx, entries, 2),
> + OSF_STREAM_FRAME_REJECTED);
> + KUNIT_EXPECT_FALSE(test, ctx->osf.session_fault);
> + KUNIT_EXPECT_EQ(test, ctx->osf.iio_dev_count, 1U);
> + osf_test_new_session(ctx);
> + KUNIT_EXPECT_EQ(test, osf_test_submit_report(ctx, entries, 2),
> + OSF_STREAM_FRAME_REJECTED);
> + KUNIT_EXPECT_EQ(test, ctx->osf.iio_dev_count, 0U);
> + KUNIT_EXPECT_FALSE(test, ctx->osf.capability_cache.valid);
> + osf_test_bind_accel(test, ctx);
> +}
> +
> +static int osf_test_stream_frame(void *context, const u8 *buf, size_t len)
> +{
> + return osf_core_receive_frame(context, buf, len);
> +}
> +
> +static void osf_scale_mismatch_preserves_and_recovers_test(struct kunit *test)
> +{
> + struct osf_test_context *ctx = test->priv;
> + const s32 values[] = { OSF_FRAME_MAGIC, -22, 33 };
> + const s32 next[] = { 101, 202, 303 };
> + struct osf_latest_sample baseline;
> + struct osf_test_scan_3axis scan;
> + struct osf_stream *stream;
> + u8 frame[OSF_TEST_MAX_FRAME_LEN];
> + size_t len;
> +
> + stream = kunit_kzalloc(test, sizeof(*stream), GFP_KERNEL);
> + KUNIT_ASSERT_NOT_NULL(test, stream);
> + osf_stream_init(stream, osf_test_stream_frame, &ctx->osf);
> + KUNIT_ASSERT_EQ(test, osf_test_enable_buffer(ctx), 0);
> + KUNIT_ASSERT_EQ(test, osf_test_submit_sample(ctx, OSF_SENSOR_ACCELEROMETER,
> + 0, 3, OSF_SAMPLE_FORMAT_S32, values, 2),
> + OSF_STREAM_FRAME_HANDLED);
> + KUNIT_ASSERT_EQ(test, osf_test_remove_scan(ctx, &scan), 0);
> + baseline = ctx->osf.latest_samples[0];
> + len = osf_test_build_sample_frame(frame, sizeof(frame), OSF_SENSOR_ACCELEROMETER,
> + 0, 3, OSF_SAMPLE_FORMAT_S32, values, 3);
> + put_unaligned_le32(OSF_TEST_SCALE_NANO + 1, frame + OSF_FRAME_HEADER_LEN + 8);
> + osf_test_crc(frame, len);
> + KUNIT_EXPECT_EQ(test, osf_stream_receive_bytes(stream, frame, len), 0);
> + KUNIT_EXPECT_EQ(test, stream->stats.rejected_frames, 1ULL);
> + KUNIT_EXPECT_EQ(test, stream->stats.validated_frames, 1ULL);
> + KUNIT_EXPECT_EQ(test, stream->stats.bad_magic_resyncs, 0ULL);
> + KUNIT_EXPECT_MEMEQ(test, &ctx->osf.latest_samples[0], &baseline, sizeof(baseline));
> + KUNIT_EXPECT_FALSE(test, ctx->osf.session_fault);
> + osf_test_expect_direct_sample(test, ctx, values);
> + KUNIT_EXPECT_EQ(test, osf_test_remove_scan(ctx, &scan), -EBUSY);
> + KUNIT_EXPECT_EQ(test, osf_test_submit_sample(ctx, OSF_SENSOR_ACCELEROMETER,
> + 0, 3, OSF_SAMPLE_FORMAT_S32, next, 4),
> + OSF_STREAM_FRAME_HANDLED);
> + osf_test_expect_direct_sample(test, ctx, next);
> + KUNIT_ASSERT_EQ(test, osf_test_remove_scan(ctx, &scan), 0);
> + KUNIT_EXPECT_MEMEQ(test, scan.values, next, sizeof(next));
> +}
> +
> +static void osf_test_initial_recovery(struct kunit *test, bool unsupported)
> +{
> + struct osf_test_context *ctx = test->priv;
> + struct osf_capability_entry entry = osf_test_accel;
> +
> + osf_test_new_session(ctx);
> + entry.sensor_type = 0xffff;
> + KUNIT_EXPECT_EQ(test, osf_test_submit_report(ctx, &entry, unsupported ? 1 : 0),
> + OSF_STREAM_FRAME_IGNORED);
> + KUNIT_EXPECT_FALSE(test, ctx->osf.capability_cache.valid);
> + KUNIT_EXPECT_EQ(test, ctx->osf.iio_dev_count, 0U);
> + osf_test_bind_accel(test, ctx);
> + KUNIT_EXPECT_FALSE(test, ctx->osf.session_fault);
> +}
> +
> +static void osf_initial_empty_recovers_test(struct kunit *test)
> +{
> + osf_test_initial_recovery(test, false);
> +}
> +
> +static void osf_initial_unsupported_recovers_test(struct kunit *test)
> +{
> + osf_test_initial_recovery(test, true);
> +}
> +
> +static void osf_equivalent_reports_test(struct kunit *test)
> +{
> + struct osf_test_context *ctx = test->priv;
> + struct osf_capability_entry entries[] = { osf_test_accel, osf_test_accel,
> + osf_test_accel };
> + struct osf_capability_entry swap;
> + struct iio_dev *first;
> +
> + osf_test_new_session(ctx);
> + entries[1].sensor_type = OSF_SENSOR_GYROSCOPE;
> + KUNIT_ASSERT_EQ(test, osf_test_submit_report(ctx, entries, 2), OSF_STREAM_FRAME_HANDLED);
> + first = ctx->osf.iio_devs[0].indio_dev;
> + KUNIT_EXPECT_EQ(test, osf_test_submit_report(ctx, entries, 2), OSF_STREAM_FRAME_IGNORED);
> + swap = entries[0];
> + entries[0] = entries[1];
> + entries[1] = swap;
> + KUNIT_EXPECT_EQ(test, osf_test_submit_report(ctx, entries, 2), OSF_STREAM_FRAME_IGNORED);
> + entries[0].flags = OSF_CAPABILITY_FLAGS_MASK;
> + entries[2].sensor_type = 0xffff;
> + KUNIT_EXPECT_EQ(test, osf_test_submit_report(ctx, entries, 3), OSF_STREAM_FRAME_IGNORED);
> + /* Unknown flags make the added key unsupported. */
> + entries[2] = osf_test_accel;
> + entries[2].sensor_index = 1;
> + entries[2].flags = BIT(7);
> + KUNIT_EXPECT_EQ(test, osf_test_submit_report(ctx, entries, 3), OSF_STREAM_FRAME_IGNORED);
> + KUNIT_EXPECT_EQ(test, ctx->osf.iio_dev_count, 2U);
> + KUNIT_EXPECT_PTR_EQ(test, ctx->osf.iio_devs[0].indio_dev, first);
> + KUNIT_EXPECT_FALSE(test, ctx->osf.session_fault);
> +}
> +
> +static void osf_changed_reports_fault_test(struct kunit *test)
> +{
> + struct osf_test_context *ctx = test->priv;
> + const s32 v[] = { 11, 22, 33 };
> + struct osf_test_scan_3axis scan;
> +
> + for (unsigned int change = 0; change < 8; change++) {
> + struct osf_capability_entry entries[] = { osf_test_accel, osf_test_accel };
> + struct osf_latest_sample baseline;
> + struct iio_dev *child;
> + u16 count = 1;
> + int value = 0;
> +
> + osf_test_new_session(ctx);
> + osf_test_bind_accel(test, ctx);
> + child = ctx->indio_dev;
> + KUNIT_ASSERT_EQ(test, osf_test_enable_buffer(ctx), 0);
> + KUNIT_ASSERT_EQ(test, osf_test_submit_sample(ctx, OSF_SENSOR_ACCELEROMETER,
> + 0, 3, OSF_SAMPLE_FORMAT_S32, v, 2),
> + OSF_STREAM_FRAME_HANDLED);
> + KUNIT_ASSERT_EQ(test, osf_test_remove_scan(ctx, &scan), 0);
> + baseline = ctx->osf.latest_samples[0];
> + switch (change) {
> + case 0:
> + entries[0].scale_nano++;
> + break;
> + case 1:
> + entries[0].channel_count = 1;
> + break;
> + case 2:
> + entries[0].sample_format = 2;
> + break;
> + case 3:
> + entries[0].sensor_index = 1;
> + break;
> + case 4:
> + count = 0;
> + break;
> + case 5:
> + entries[0].sensor_type = 0xffff;
> + break;
> + case 6:
> + entries[1].sensor_index = 1;
> + count = 2;
> + break;
> + case 7:
> + entries[0].flags = BIT(7);
> + break;
> + }
> + kunit_info(test, "meaning change=%u\n", change);
> + KUNIT_EXPECT_EQ(test, osf_test_submit_report(ctx, entries, count),
> + OSF_STREAM_FRAME_REJECTED);
> + KUNIT_EXPECT_TRUE(test, ctx->osf.session_fault);
> + KUNIT_EXPECT_EQ(test, ctx->osf.iio_dev_count, 1U);
> + KUNIT_EXPECT_PTR_EQ(test, ctx->osf.iio_devs[0].indio_dev, child);
> + KUNIT_EXPECT_EQ(test, osf_test_submit_sample(ctx, OSF_SENSOR_ACCELEROMETER,
> + 0, 3, OSF_SAMPLE_FORMAT_S32, v, 3),
> + OSF_STREAM_FRAME_REJECTED);
> + KUNIT_EXPECT_MEMEQ(test, &ctx->osf.latest_samples[0], &baseline, sizeof(baseline));
> + KUNIT_EXPECT_EQ(test, osf_test_remove_scan(ctx, &scan), -EBUSY);
> + KUNIT_EXPECT_EQ(test, osf_test_read_raw(ctx, 0, &value), -EPROTO);
> + /* Equivalent reports do not repair a faulted host session. */
> + KUNIT_EXPECT_EQ(test, osf_test_submit_report(ctx, &osf_test_accel, 1),
> + OSF_STREAM_FRAME_IGNORED);
> + KUNIT_EXPECT_EQ(test, osf_test_read_raw(ctx, 0, &value), -EPROTO);
> + }
> +}
> +
> +static void osf_rebind_and_timestamp_restart_test(struct kunit *test)
> +{
> + struct osf_test_context *ctx = test->priv;
> + struct osf_capability_entry entry = osf_test_accel;
> + const s32 values[] = { 101, -202, 303 };
> + u8 frame[OSF_TEST_MAX_FRAME_LEN];
> + size_t len;
> +
> + entry.scale_nano++;
> + KUNIT_ASSERT_EQ(test, osf_test_submit_report(ctx, &entry, 1), OSF_STREAM_FRAME_REJECTED);
> + KUNIT_ASSERT_TRUE(test, ctx->osf.session_fault);
> + osf_test_new_session(ctx);
> + KUNIT_ASSERT_EQ(test, osf_test_submit_report(ctx, &entry, 1), OSF_STREAM_FRAME_HANDLED);
> + ctx->indio_dev = ctx->osf.iio_devs[0].indio_dev;
> + ctx->buffer = ctx->indio_dev->buffer;
> + KUNIT_EXPECT_FALSE(test, ctx->osf.session_fault);
> + KUNIT_EXPECT_EQ(test, ctx->osf.latest_sample_count, 0U);
> + for (unsigned int i = 0; i < 2; i++) {
> + len = osf_test_build_sample_frame(frame, sizeof(frame), OSF_SENSOR_ACCELEROMETER,
> + 0, 3, OSF_SAMPLE_FORMAT_S32, values, i ? 0 : 100);
> + put_unaligned_le32(entry.scale_nano, frame + OSF_FRAME_HEADER_LEN + 8);
> + osf_test_crc(frame, len);
> + KUNIT_EXPECT_EQ(test, osf_core_receive_frame(&ctx->osf, frame, len),
> + OSF_STREAM_FRAME_HANDLED);
> + KUNIT_EXPECT_EQ(test, osf_test_submit_report(ctx, &entry, 1),
> + OSF_STREAM_FRAME_IGNORED);
> + }
> + osf_test_expect_direct_sample(test, ctx, values);
> + KUNIT_EXPECT_FALSE(test, ctx->osf.session_fault);
> +}
> +
> +static void osf_reserved_padding_is_ignored_test(struct kunit *test)
> +{
> + struct osf_test_context *ctx = test->priv;
> + struct osf_capability_entry entry = osf_test_accel;
> + const s32 values[] = { 11, 22, 33 };
> + u8 status[OSF_DEVICE_STATUS_LEN] = { };
> + u8 frame[OSF_TEST_MAX_FRAME_LEN];
> + size_t len;
> +
> + osf_test_new_session(ctx);
> + len = osf_test_report(frame, &entry, 1);
> + put_unaligned_le32(0xdeadbeef, frame + 34);
> + put_unaligned_le16(0x1234, frame + OSF_FRAME_HEADER_LEN + 2);
> + put_unaligned_le32(0xa5a5a5a5,
> + frame + OSF_FRAME_HEADER_LEN + OSF_CAP_REPORT_BASE_LEN + 16);
> + osf_test_crc(frame, len);
> + KUNIT_ASSERT_EQ(test, osf_core_receive_frame(&ctx->osf, frame, len),
> + OSF_STREAM_FRAME_HANDLED);
> + KUNIT_EXPECT_EQ(test, osf_core_receive_frame(&ctx->osf, frame, len),
> + OSF_STREAM_FRAME_IGNORED);
> + KUNIT_ASSERT_EQ(test, ctx->osf.iio_dev_count, 1U);
> + ctx->indio_dev = ctx->osf.iio_devs[0].indio_dev;
> + ctx->buffer = ctx->indio_dev->buffer;
> + len = osf_test_build_sample_frame(frame, sizeof(frame), OSF_SENSOR_ACCELEROMETER,
> + 0, 3, OSF_SAMPLE_FORMAT_S32, values, 2);
> + put_unaligned_le32(0xaabbccdd, frame + OSF_FRAME_HEADER_LEN + 12);
> + osf_test_crc(frame, len);
> + KUNIT_EXPECT_EQ(test, osf_core_receive_frame(&ctx->osf, frame, len),
> + OSF_STREAM_FRAME_HANDLED);
> + osf_test_expect_direct_sample(test, ctx, values);
> + put_unaligned_le32(0x12345678, status + 16);
> + len = osf_test_build_frame(frame, sizeof(frame), OSF_MSG_DEVICE_STATUS,
> + status, sizeof(status), 3);
> + KUNIT_EXPECT_EQ(test, osf_core_receive_frame(&ctx->osf, frame, len),
> + OSF_STREAM_FRAME_HANDLED);
> + KUNIT_EXPECT_TRUE(test, ctx->osf.status_cache.valid);
> +}
> +
> +static void osf_bad_crc_bounds_and_extensions_test(struct kunit *test)
> +{
> + struct osf_test_context *ctx = test->priv;
> + const s32 values[] = { 11, 22, 33 };
> + struct osf_latest_sample baseline;
> + u8 frame[OSF_TEST_MAX_FRAME_LEN];
> + u8 payload[OSF_TEST_MAX_PAYLOAD_LEN + 1] = { };
> + size_t len;
> +
> + KUNIT_ASSERT_EQ(test, osf_test_submit_sample(ctx, OSF_SENSOR_ACCELEROMETER,
> + 0, 3, OSF_SAMPLE_FORMAT_S32, values, 2),
> + OSF_STREAM_FRAME_HANDLED);
> + baseline = ctx->osf.latest_samples[0];
> + len = osf_test_report(frame, &osf_test_accel, 1);
> + frame[34] = 1;
> + KUNIT_EXPECT_EQ(test, osf_core_receive_frame(&ctx->osf, frame, len), -EBADMSG);
> + osf_test_crc(frame, len);
> + put_unaligned_le32(U32_MAX, frame + 10);
> + KUNIT_EXPECT_EQ(test, osf_core_receive_frame(&ctx->osf, frame, len), -EMSGSIZE);
> + len = osf_test_report(frame, &osf_test_accel, 1);
> + put_unaligned_le16(2, frame + OSF_FRAME_HEADER_LEN);
> + osf_test_crc(frame, len);
> + KUNIT_EXPECT_EQ(test, osf_core_receive_frame(&ctx->osf, frame, len),
> + OSF_STREAM_FRAME_REJECTED);
> + /* A CRC-valid trailing byte is not an extension of any fixed baseline ID. */
> + for (u16 type = OSF_MSG_SENSOR_SAMPLE; type <= OSF_MSG_CAPABILITY_REPORT; type++) {
> + u32 size;
> +
> + memset(payload, 0, sizeof(payload));
> + if (type == OSF_MSG_SENSOR_SAMPLE) {
> + put_unaligned_le16(OSF_SENSOR_ACCELEROMETER, payload);
> + put_unaligned_le16(3, payload + 4);
> + put_unaligned_le16(OSF_SAMPLE_FORMAT_S32, payload + 6);
> + put_unaligned_le32(OSF_TEST_SCALE_NANO, payload + 8);
> + size = OSF_TEST_MAX_PAYLOAD_LEN;
> + } else if (type == OSF_MSG_DEVICE_STATUS) {
> + size = OSF_DEVICE_STATUS_LEN;
> + } else {
> + size = OSF_CAP_REPORT_BASE_LEN;
> + }
> + len = osf_test_build_frame(frame, sizeof(frame), type, payload, size + 1, 3);
> + KUNIT_EXPECT_EQ(test, osf_core_receive_frame(&ctx->osf, frame, len),
> + OSF_STREAM_FRAME_REJECTED);
> + }
> + KUNIT_EXPECT_FALSE(test, ctx->osf.session_fault);
> + KUNIT_EXPECT_MEMEQ(test, &ctx->osf.latest_samples[0], &baseline, sizeof(baseline));
> +}
> +
> +struct osf_session_race {
> + struct osf_test_context *ctx;
> + /* Model the transport serialization separately from IIO configuration. */
> + struct mutex rx_lock;
> + struct completion started;
> + struct completion fault_observed;
> + atomic_t faulted;
> + atomic_t rejected;
> + atomic_t errors;
> +};
> +
> +static int osf_session_producer(void *data)
> +{
> + struct osf_session_race *race = data;
> + const s32 values[] = { 101, 202, 303 };
> + u64 sequence = 100;
> + int ret, raw;
> +
> + complete(&race->started);
> + while (!kthread_should_stop()) {
> + mutex_lock(&race->rx_lock);
> + ret = osf_test_submit_sample(race->ctx, OSF_SENSOR_ACCELEROMETER,
> + 0, 3, OSF_SAMPLE_FORMAT_S32, values, sequence++);
> + if (atomic_read(&race->faulted)) {
> + if (ret == OSF_STREAM_FRAME_REJECTED)
> + atomic_inc(&race->rejected);
> + else
> + atomic_inc(&race->errors);
> + complete(&race->fault_observed);
> + }
> + mutex_unlock(&race->rx_lock);
> + ret = osf_test_read_raw(race->ctx, 0, &raw);
> + if (ret != IIO_VAL_INT && ret != -EPROTO && ret != -ENODATA)
> + atomic_inc(&race->errors);
> + cond_resched();
> + }
> + return 0;
> +}
> +
> +KUNIT_DEFINE_ACTION_WRAPPER(osf_test_stop_kthread, kthread_stop,
> + struct task_struct *);
> +
> +static void osf_session_fault_buffer_race_test(struct kunit *test)
> +{
> + struct osf_test_context *ctx = test->priv;
> + struct osf_capability_entry entry = osf_test_accel;
> + struct osf_latest_sample baseline;
> + struct osf_test_scan_3axis scan;
> + struct osf_session_race *race;
> + struct task_struct *producer;
> + int ret, raw;
> +
> + race = kunit_kzalloc(test, sizeof(*race), GFP_KERNEL);
> + KUNIT_ASSERT_NOT_NULL(test, race);
> + race->ctx = ctx;
> + mutex_init(&race->rx_lock);
> + init_completion(&race->started);
> + init_completion(&race->fault_observed);
> + KUNIT_ASSERT_EQ(test, osf_test_enable_buffer(ctx), 0);
> + producer = kthread_run(osf_session_producer, race, "osf-session-test");
> + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, producer);
> + ret = kunit_add_action_or_reset(test, osf_test_stop_kthread, producer);
> + KUNIT_ASSERT_EQ(test, ret, 0);
> + KUNIT_EXPECT_NE(test, wait_for_completion_timeout(&race->started, HZ), 0UL);
> + /* Exercise buffer reconfiguration against latest_lock -> IIO mlock. */
> + for (unsigned int i = 0; i < 100; i++) {
> + KUNIT_EXPECT_EQ(test, iio_update_buffers(ctx->indio_dev, NULL, ctx->buffer), 0);
> + KUNIT_EXPECT_EQ(test, iio_update_buffers(ctx->indio_dev, ctx->buffer, NULL), 0);
> + cond_resched();
> + }
> + mutex_lock(&race->rx_lock);
> + entry.scale_nano++;
> + ret = osf_test_submit_report(ctx, &entry, 1);
> + KUNIT_EXPECT_EQ(test, ret, OSF_STREAM_FRAME_REJECTED);
> + atomic_set(&race->faulted, 1);
> + baseline = ctx->osf.latest_samples[0];
> + while (!osf_test_remove_scan(ctx, &scan))
> + ;
> + mutex_unlock(&race->rx_lock);
> + KUNIT_EXPECT_NE(test, wait_for_completion_timeout(&race->fault_observed,
> + 5 * HZ), 0UL);
> + kunit_release_action(test, osf_test_stop_kthread, producer);
> + KUNIT_EXPECT_GT(test, atomic_read(&race->rejected), 0);
> + KUNIT_EXPECT_EQ(test, atomic_read(&race->errors), 0);
> + KUNIT_EXPECT_MEMEQ(test, &ctx->osf.latest_samples[0], &baseline, sizeof(baseline));
> + KUNIT_EXPECT_EQ(test, osf_test_remove_scan(ctx, &scan), -EBUSY);
> + KUNIT_EXPECT_EQ(test, osf_test_read_raw(ctx, 0, &raw), -EPROTO);
> + /* Cleanup follows the real producer-stop -> child-unregister ordering. */
> + osf_test_new_session(ctx);
> + osf_test_bind_accel(test, ctx);
> + KUNIT_EXPECT_FALSE(test, ctx->osf.session_fault);
> +}
> +
> +static struct kunit_case osf_core_test_cases[] = {
> + KUNIT_CASE(osf_zero_scale_is_atomic_test),
> + KUNIT_CASE(osf_scale_mismatch_preserves_and_recovers_test),
> + KUNIT_CASE(osf_initial_empty_recovers_test),
> + KUNIT_CASE(osf_initial_unsupported_recovers_test),
> + KUNIT_CASE(osf_equivalent_reports_test),
> + KUNIT_CASE(osf_changed_reports_fault_test),
> + KUNIT_CASE(osf_rebind_and_timestamp_restart_test),
> + KUNIT_CASE(osf_reserved_padding_is_ignored_test),
> + KUNIT_CASE(osf_bad_crc_bounds_and_extensions_test),
> + KUNIT_CASE(osf_session_fault_buffer_race_test),
> +
> + KUNIT_CASE(osf_early_capability_is_owned_test),
> + KUNIT_CASE(osf_early_duplicate_capability_is_rejected_test),
> + KUNIT_CASE(osf_rejected_channel_count_preserves_latest_test),
> + KUNIT_CASE(osf_valid_sample_updates_direct_and_buffer_test),
> + KUNIT_CASE(osf_unregistered_sample_is_ignored_test),
> + KUNIT_CASE(osf_unaccepted_samples_do_not_exhaust_cache_test),
> + { }
> +};
> +
> +static struct kunit_suite osf_core_test_suite = {
> + .name = "osf-core",
> + .init = osf_test_init,
> + .test_cases = osf_core_test_cases,
> +};
> +
> +kunit_test_suite(osf_core_test_suite);
> +
> +MODULE_LICENSE("GPL");
>
next prev parent reply other threads:[~2026-10-17 23:58 UTC|newest]
Thread overview: 14+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-10-11 23:13 [PATCH v11 0/8] iio: add Open Sensor Fusion UART support Jinseob Kim
2026-10-11 23:13 ` [PATCH v11 1/8] dt-bindings: iio: add Open Sensor Fusion device Jinseob Kim
2026-10-12 19:10 ` Conor Dooley
2026-10-11 23:13 ` [PATCH v11 2/8] Documentation: iio: add Open Sensor Fusion driver overview Jinseob Kim
2026-10-11 23:13 ` [PATCH v11 3/8] iio: osf: add protocol decoding Jinseob Kim
2026-10-11 23:13 ` [PATCH v11 4/8] iio: osf: add validated stream parser Jinseob Kim
2026-10-11 23:13 ` [PATCH v11 5/8] iio: osf: add UART transport and core receive path Jinseob Kim
2026-10-11 23:13 ` [PATCH v11 6/8] iio: osf: add IIO devices from capability reports Jinseob Kim
2026-10-11 23:13 ` [PATCH v11 7/8] iio: osf: add core KUnit tests Jinseob Kim
2026-10-17 23:58 ` Jonathan Cameron [this message]
2026-10-11 23:13 ` [PATCH v11 8/8] iio: osf: add IIO " Jinseob Kim
2026-10-18 0:02 ` [PATCH v11 0/8] iio: add Open Sensor Fusion UART support Jonathan Cameron
2026-10-18 9:46 ` Kim Jinseob
2026-10-18 18:04 ` Jonathan Cameron
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=20261018005805.7db7ca9f@jic23-hlaptop \
--to=jic23@kernel.org \
--cc=andersson@kernel.org \
--cc=andy@kernel.org \
--cc=conor+dt@kernel.org \
--cc=corbet@lwn.net \
--cc=devicetree@vger.kernel.org \
--cc=dlechner@baylibre.com \
--cc=kimjinseob88@gmail.com \
--cc=krzk+dt@kernel.org \
--cc=linux-doc@vger.kernel.org \
--cc=linux-iio@vger.kernel.org \
--cc=linux-kernel@vger.kernel.org \
--cc=neil.armstrong@linaro.org \
--cc=nuno.sa@analog.com \
--cc=prabhakar.mahadev-lad.rj@bp.renesas.com \
--cc=rdunlap@infradead.org \
--cc=robh@kernel.org \
--cc=skhan@linuxfoundation.org \
/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 a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox
all inboxes | Powered by JetHome®