Commit 7bd10e0e authored by Takashi Sakamoto's avatar Takashi Sakamoto
Browse files

firewire: core: add enumerator of self ID sequences and its KUnit test

When the state of bus reset finishes, 1394 OHCI driver constructs self ID
sequences, then it calls fw_core_handle_bus_reset() in core function. The
core function enumerates the self ID sequences to build bus topology.

This commit adds a structure and some helper functions for the enumeration,
and adds a KUnit test suite to ensure its expected behaviour.

Link: https://lore.kernel.org/r/20240605235155.116468-2-o-takashi@sakamocchi.jp


Signed-off-by: default avatarTakashi Sakamoto <o-takashi@sakamocchi.jp>
parent 6ba59ff4
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -4,3 +4,4 @@ CONFIG_FIREWIRE=y
CONFIG_FIREWIRE_KUNIT_UAPI_TEST=y
CONFIG_FIREWIRE_KUNIT_DEVICE_ATTRIBUTE_TEST=y
CONFIG_FIREWIRE_KUNIT_PACKET_SERDES_TEST=y
CONFIG_FIREWIRE_KUNIT_SELF_ID_SEQUENCE_HELPER_TEST=y
+15 −0
Original line number Diff line number Diff line
@@ -66,6 +66,21 @@ config FIREWIRE_KUNIT_PACKET_SERDES_TEST
	  For more information on KUnit and unit tests in general, refer
	  to the KUnit documentation in Documentation/dev-tools/kunit/.

config FIREWIRE_KUNIT_SELF_ID_SEQUENCE_HELPER_TEST
	tristate "KUnit tests for helpers of self ID sequence" if !KUNIT_ALL_TESTS
	depends on FIREWIRE && KUNIT
	default KUNIT_ALL_TESTS
	help
	  This builds the KUnit tests for helpers of self ID sequence.

	  KUnit tests run during boot and output the results to the debug
	  log in TAP format (https://testanything.org/). Only useful for
	  kernel devs running KUnit test harness and are not for inclusion
	  into a production build.

	  For more information on KUnit and unit tests in general, refer
	  to the KUnit documentation in Documentation/dev-tools/kunit/.

config FIREWIRE_OHCI
	tristate "OHCI-1394 controllers"
	depends on PCI && FIREWIRE && MMU
+1 −0
Original line number Diff line number Diff line
@@ -18,3 +18,4 @@ obj-$(CONFIG_PROVIDE_OHCI1394_DMA_INIT) += init_ohci1394_dma.o

obj-$(CONFIG_FIREWIRE_KUNIT_UAPI_TEST) += uapi-test.o
obj-$(CONFIG_FIREWIRE_KUNIT_PACKET_SERDES_TEST) += packet-serdes-test.o
obj-$(CONFIG_FIREWIRE_KUNIT_SELF_ID_SEQUENCE_HELPER_TEST) += self-id-sequence-helper-test.o
+78 −0
Original line number Diff line number Diff line
// SPDX-License-Identifier: GPL-2.0-or-later
//
// phy-packet-definitions.h - The definitions of phy packet for IEEE 1394.
//
// Copyright (c) 2024 Takashi Sakamoto

#ifndef _FIREWIRE_PHY_PACKET_DEFINITIONS_H
#define _FIREWIRE_PHY_PACKET_DEFINITIONS_H

#define SELF_ID_EXTENDED_MASK				0x00800000
#define SELF_ID_EXTENDED_SHIFT				23
#define SELF_ID_MORE_PACKETS_MASK			0x00000001
#define SELF_ID_MORE_PACKETS_SHIFT			0

#define SELF_ID_EXTENDED_SEQUENCE_MASK			0x00700000
#define SELF_ID_EXTENDED_SEQUENCE_SHIFT			20

#define SELF_ID_SEQUENCE_MAXIMUM_QUADLET_COUNT		4

static inline bool phy_packet_self_id_get_extended(u32 quadlet)
{
	return (quadlet & SELF_ID_EXTENDED_MASK) >> SELF_ID_EXTENDED_SHIFT;
}

static inline bool phy_packet_self_id_get_more_packets(u32 quadlet)
{
	return (quadlet & SELF_ID_MORE_PACKETS_MASK) >> SELF_ID_MORE_PACKETS_SHIFT;
}

static inline unsigned int phy_packet_self_id_extended_get_sequence(u32 quadlet)
{
	return (quadlet & SELF_ID_EXTENDED_SEQUENCE_MASK) >> SELF_ID_EXTENDED_SEQUENCE_SHIFT;
}

struct self_id_sequence_enumerator {
	const u32 *cursor;
	unsigned int quadlet_count;
};

static inline const u32 *self_id_sequence_enumerator_next(
		struct self_id_sequence_enumerator *enumerator, unsigned int *quadlet_count)
{
	const u32 *self_id_sequence, *cursor;
	u32 quadlet;
	unsigned int count;
	unsigned int sequence;

	if (enumerator->cursor == NULL || enumerator->quadlet_count == 0)
		return ERR_PTR(-ENODATA);
	cursor = enumerator->cursor;
	count = 1;

	quadlet = *cursor;
	sequence = 0;
	while (phy_packet_self_id_get_more_packets(quadlet)) {
		if (count >= enumerator->quadlet_count ||
		    count >= SELF_ID_SEQUENCE_MAXIMUM_QUADLET_COUNT)
			return ERR_PTR(-EPROTO);
		++cursor;
		++count;
		quadlet = *cursor;

		if (!phy_packet_self_id_get_extended(quadlet) ||
		    sequence != phy_packet_self_id_extended_get_sequence(quadlet))
			return ERR_PTR(-EPROTO);
		++sequence;
	}

	*quadlet_count = count;
	self_id_sequence = enumerator->cursor;

	enumerator->cursor += count;
	enumerator->quadlet_count -= count;

	return self_id_sequence;
}

#endif // _FIREWIRE_PHY_PACKET_DEFINITIONS_H
+79 −0
Original line number Diff line number Diff line
// SPDX-License-Identifier: GPL-2.0-or-later
//
// self-id-sequence-helper-test.c - An application of Kunit to test helpers of self ID sequence.
//
// Copyright (c) 2024 Takashi Sakamoto

#include <kunit/test.h>

#include "phy-packet-definitions.h"

static void test_self_id_sequence_enumerator_valid(struct kunit *test)
{
	static const u32 valid_sequences[] = {
		0x00000000,
		0x00000001, 0x00800000,
		0x00000001, 0x00800001, 0x00900000,
		0x00000000,
	};
	struct self_id_sequence_enumerator enumerator;
	const u32 *entry;
	unsigned int quadlet_count;

	enumerator.cursor = valid_sequences;
	enumerator.quadlet_count = ARRAY_SIZE(valid_sequences);

	entry = self_id_sequence_enumerator_next(&enumerator, &quadlet_count);
	KUNIT_EXPECT_PTR_EQ(test, entry, &valid_sequences[0]);
	KUNIT_EXPECT_EQ(test, quadlet_count, 1);
	KUNIT_EXPECT_EQ(test, enumerator.quadlet_count, 6);

	entry = self_id_sequence_enumerator_next(&enumerator, &quadlet_count);
	KUNIT_EXPECT_PTR_EQ(test, entry, &valid_sequences[1]);
	KUNIT_EXPECT_EQ(test, quadlet_count, 2);
	KUNIT_EXPECT_EQ(test, enumerator.quadlet_count, 4);

	entry = self_id_sequence_enumerator_next(&enumerator, &quadlet_count);
	KUNIT_EXPECT_PTR_EQ(test, entry, &valid_sequences[3]);
	KUNIT_EXPECT_EQ(test, quadlet_count, 3);
	KUNIT_EXPECT_EQ(test, enumerator.quadlet_count, 1);

	entry = self_id_sequence_enumerator_next(&enumerator, &quadlet_count);
	KUNIT_EXPECT_PTR_EQ(test, entry, &valid_sequences[6]);
	KUNIT_EXPECT_EQ(test, quadlet_count, 1);
	KUNIT_EXPECT_EQ(test, enumerator.quadlet_count, 0);

	entry = self_id_sequence_enumerator_next(&enumerator, &quadlet_count);
	KUNIT_EXPECT_EQ(test, PTR_ERR(entry), -ENODATA);
}

static void test_self_id_sequence_enumerator_invalid(struct kunit *test)
{
	static const u32 invalid_sequences[] = {
		0x00000001,
	};
	struct self_id_sequence_enumerator enumerator;
	const u32 *entry;
	unsigned int count;

	enumerator.cursor = invalid_sequences;
	enumerator.quadlet_count = ARRAY_SIZE(invalid_sequences);

	entry = self_id_sequence_enumerator_next(&enumerator, &count);
	KUNIT_EXPECT_EQ(test, PTR_ERR(entry), -EPROTO);
}

static struct kunit_case self_id_sequence_helper_test_cases[] = {
	KUNIT_CASE(test_self_id_sequence_enumerator_valid),
	KUNIT_CASE(test_self_id_sequence_enumerator_invalid),
	{}
};

static struct kunit_suite self_id_sequence_helper_test_suite = {
	.name = "self-id-sequence-helper",
	.test_cases = self_id_sequence_helper_test_cases,
};
kunit_test_suite(self_id_sequence_helper_test_suite);

MODULE_DESCRIPTION("Unit test suite for helpers of self ID sequence");
MODULE_LICENSE("GPL");