From mboxrd@z Thu Jan 1 00:00:00 1970 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-alma10-1.taild15c8.ts.net [100.103.45.18]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 8B3EF39B4A5; Sat, 17 Oct 2026 23:58:13 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=100.103.45.18 ARC-Seal:i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1792281496; cv=none; b=oawCRPEkGiNEIlgxh6SIceThaXVtOHn6PEREDiVS/5+H8gx3iav0ign49Tvb7FLzPrpPNTdsnW9Ih+nm5MqagDvoo8UClL6Jg4YlwRKDUQ1AKHsHfRKwwDcf+4EY+m3atnZ1/CJAdN1EDmgEb0DNGSGGbCug/wLJSugyzS/DJcw= ARC-Message-Signature:i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1792281496; c=relaxed/simple; bh=iXrIBoyjAnKtH4CeN+xWav4HcbFyrTl+DvREdNiLcoI=; h=Date:From:To:Cc:Subject:Message-ID:In-Reply-To:References: MIME-Version:Content-Type; b=GDIOvH9nPFLPYrbeXfhZKJukfdP4ow72Ht3gO2kmMVRVQhqeubw/6oERSv+MSXioDcVlEQh0ju13+LvvkAkGUlMyKoVX/1ZMaAo6fD9hK1WIKR4I+gVqGD23Al9+Dy4ApbN5uyt2rhq2zTwdU2WKpOXDk8v7RwiJ8g00cQ83HZY= ARC-Authentication-Results:i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=ZvS/VP+k; arc=none smtp.client-ip=100.103.45.18 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="ZvS/VP+k" Received: by smtp.kernel.org (Postfix) with ESMTPSA id B330E1F000C4; Sat, 17 Oct 2026 23:58:10 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=kernel.org; s=k20260515; t=1792281493; bh=M35iTUKv5lE8YHvVX4hQFjjL0HEvGbq/HgjGpyetJwg=; h=Date:From:To:Cc:Subject:In-Reply-To:References; b=ZvS/VP+koZ6MoX+FObgPbiRiSbmhyMT1FIt4KKllv1Jrc9+DPwN9byLkWSE0PKOjw +BOORy0aBmOPlRhdMwhfv14Xj6PnjMr9P5/7l9oHkfd3/1sfN5MqM+LzVq7x5LvopK JsjN/O+LZddB8U3oMmF2VDBg7V2MGoDJo2dkIk9A1LLrkl0WNsgn9KFzBU9Q/pPAwn bW+wZ4XdE8xOi8r/eciM3dzc7NaYRUWgZjnd5fv5FMx8AZp/7J2hP69blWKsDbbvJ0 myTTJrCWFKktkz+OgbU96RGopUjp1kw4vgTouHHkm5qS9/yz9y355V7uJOdYLkDQ7f f79U74tXW3/AA== Date: Sun, 18 Oct 2026 00:58:05 +0100 From: Jonathan Cameron To: Jinseob Kim 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 Message-ID: <20261018005805.7db7ca9f@jic23-hlaptop> In-Reply-To: <20261011231340.42885-8-kimjinseob88@gmail.com> References: <20261011231340.42885-1-kimjinseob88@gmail.com> <20261011231340.42885-8-kimjinseob88@gmail.com> X-Mailer: Claws Mail 4.4.0 (GTK 3.24.52; x86_64-pc-linux-gnu) Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset=US-ASCII Content-Transfer-Encoding: 7bit On Mon, 12 Oct 2026 08:13:39 +0900 Jinseob Kim 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 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 > +#include > + > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > + > +#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"); >