Commit baf9d20b authored by Takashi Sakamoto's avatar Takashi Sakamoto
Browse files

firewire: core: add common inline functions to serialize/deserialize self ID packet

Within FireWire subsystem, the serializations and deserializations of phy
packet are implemented in several parts. They includes some redundancies.

This commit adds a series of helper functions for the serializations and
deserializations of self ID packet with a Kunit test suite.

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


Signed-off-by: default avatarTakashi Sakamoto <o-takashi@sakamocchi.jp>
parent a16931ac
Loading
Loading
Loading
Loading
+255 −0
Original line number Diff line number Diff line
@@ -10,6 +10,7 @@
#include <linux/firewire-constants.h>

#include "packet-header-definitions.h"
#include "phy-packet-definitions.h"

static void serialize_async_header_common(u32 header[ASYNC_HEADER_QUADLET_COUNT],
					  unsigned int dst_id, unsigned int tlabel,
@@ -187,6 +188,66 @@ static void deserialize_isoc_header(u32 header, unsigned int *data_length, unsig
	*sy = isoc_header_get_sy(header);
}

static void serialize_phy_packet_self_id_zero(u32 *quadlet, unsigned int packet_identifier,
					      unsigned int phy_id, bool extended,
					      bool link_is_active, unsigned int gap_count,
					      unsigned int scode, bool is_contender,
					      unsigned int power_class, bool is_initiated_reset,
					      bool has_more_packets)
{
	phy_packet_set_packet_identifier(quadlet, packet_identifier);
	phy_packet_self_id_set_phy_id(quadlet, phy_id);
	phy_packet_self_id_set_extended(quadlet, extended);
	phy_packet_self_id_zero_set_link_active(quadlet, link_is_active);
	phy_packet_self_id_zero_set_gap_count(quadlet, gap_count);
	phy_packet_self_id_zero_set_scode(quadlet, scode);
	phy_packet_self_id_zero_set_contender(quadlet, is_contender);
	phy_packet_self_id_zero_set_power_class(quadlet, power_class);
	phy_packet_self_id_zero_set_initiated_reset(quadlet, is_initiated_reset);
	phy_packet_self_id_set_more_packets(quadlet, has_more_packets);
}

static void deserialize_phy_packet_self_id_zero(u32 quadlet, unsigned int *packet_identifier,
						unsigned int *phy_id, bool *extended,
						bool *link_is_active, unsigned int *gap_count,
						unsigned int *scode, bool *is_contender,
						unsigned int *power_class,
						bool *is_initiated_reset, bool *has_more_packets)
{
	*packet_identifier = phy_packet_get_packet_identifier(quadlet);
	*phy_id = phy_packet_self_id_get_phy_id(quadlet);
	*extended = phy_packet_self_id_get_extended(quadlet);
	*link_is_active = phy_packet_self_id_zero_get_link_active(quadlet);
	*gap_count = phy_packet_self_id_zero_get_gap_count(quadlet);
	*scode = phy_packet_self_id_zero_get_scode(quadlet);
	*is_contender = phy_packet_self_id_zero_get_contender(quadlet);
	*power_class = phy_packet_self_id_zero_get_power_class(quadlet);
	*is_initiated_reset = phy_packet_self_id_zero_get_initiated_reset(quadlet);
	*has_more_packets = phy_packet_self_id_get_more_packets(quadlet);
}

static void serialize_phy_packet_self_id_extended(u32 *quadlet, unsigned int packet_identifier,
						  unsigned int phy_id, bool extended,
						  unsigned int sequence, bool has_more_packets)
{
	phy_packet_set_packet_identifier(quadlet, packet_identifier);
	phy_packet_self_id_set_phy_id(quadlet, phy_id);
	phy_packet_self_id_set_extended(quadlet, extended);
	phy_packet_self_id_extended_set_sequence(quadlet, sequence);
	phy_packet_self_id_set_more_packets(quadlet, has_more_packets);
}

static void deserialize_phy_packet_self_id_extended(u32 quadlet, unsigned int *packet_identifier,
						    unsigned int *phy_id, bool *extended,
						    unsigned int *sequence, bool *has_more_packets)
{
	*packet_identifier = phy_packet_get_packet_identifier(quadlet);
	*phy_id = phy_packet_self_id_get_phy_id(quadlet);
	*extended = phy_packet_self_id_get_extended(quadlet);
	*sequence = phy_packet_self_id_extended_get_sequence(quadlet);
	*has_more_packets = phy_packet_self_id_get_more_packets(quadlet);
}

static void test_async_header_write_quadlet_request(struct kunit *test)
{
	static const u32 expected[ASYNC_HEADER_QUADLET_COUNT] = {
@@ -559,6 +620,197 @@ static void test_isoc_header(struct kunit *test)
	KUNIT_EXPECT_EQ(test, header, expected);
}

static void test_phy_packet_self_id_zero_case0(struct kunit *test)
{
	// TSB41AB1/2 with 1 port.
	const u32 expected[] = {0x80458c80};
	u32 quadlets[] = {0};

	unsigned int packet_identifier;
	unsigned int phy_id;
	bool extended;
	bool link_is_active;
	unsigned int gap_count;
	unsigned int scode;
	bool is_contender;
	unsigned int power_class;
	enum phy_packet_self_id_port_status port_status[3];
	bool is_initiated_reset;
	bool has_more_packets;
	unsigned int port_index;

	deserialize_phy_packet_self_id_zero(expected[0], &packet_identifier, &phy_id, &extended,
					    &link_is_active, &gap_count, &scode, &is_contender,
					    &power_class, &is_initiated_reset, &has_more_packets);

	KUNIT_EXPECT_EQ(test, PHY_PACKET_PACKET_IDENTIFIER_SELF_ID, packet_identifier);
	KUNIT_EXPECT_EQ(test, 0, phy_id);
	KUNIT_EXPECT_FALSE(test, extended);
	KUNIT_EXPECT_TRUE(test, link_is_active);
	KUNIT_EXPECT_EQ(test, 0x05, gap_count);
	KUNIT_EXPECT_EQ(test, SCODE_400, scode);
	KUNIT_EXPECT_TRUE(test, is_contender);
	KUNIT_EXPECT_EQ(test, 0x4, power_class);
	KUNIT_EXPECT_FALSE(test, is_initiated_reset);
	KUNIT_EXPECT_FALSE(test, has_more_packets);

	serialize_phy_packet_self_id_zero(quadlets, packet_identifier, phy_id, extended,
					  link_is_active, gap_count, scode, is_contender,
					  power_class, is_initiated_reset, has_more_packets);

	for (port_index = 0; port_index < ARRAY_SIZE(port_status); ++port_index) {
		port_status[port_index] =
			self_id_sequence_get_port_status(expected, ARRAY_SIZE(expected), port_index);
	}

	KUNIT_EXPECT_EQ(test, PHY_PACKET_SELF_ID_PORT_STATUS_PARENT, port_status[0]);
	KUNIT_EXPECT_EQ(test, PHY_PACKET_SELF_ID_PORT_STATUS_NONE, port_status[1]);
	KUNIT_EXPECT_EQ(test, PHY_PACKET_SELF_ID_PORT_STATUS_NONE, port_status[2]);

	for (port_index = 0; port_index < ARRAY_SIZE(port_status); ++port_index) {
		self_id_sequence_set_port_status(quadlets, ARRAY_SIZE(quadlets), port_index,
						 port_status[port_index]);
	}

	KUNIT_EXPECT_MEMEQ(test, quadlets, expected, sizeof(expected));
}

static void test_phy_packet_self_id_zero_case1(struct kunit *test)
{
	// XIO2213 and TSB81BA3E with 3 ports.
	const u32 expected[] = {0x817fcc5e};
	u32 quadlets[] = {0};

	unsigned int packet_identifier;
	unsigned int phy_id;
	bool extended;
	bool link_is_active;
	unsigned int gap_count;
	unsigned int scode;
	bool is_contender;
	unsigned int power_class;
	enum phy_packet_self_id_port_status port_status[3];
	bool is_initiated_reset;
	bool has_more_packets;
	unsigned int port_index;

	deserialize_phy_packet_self_id_zero(expected[0], &packet_identifier, &phy_id, &extended,
					    &link_is_active, &gap_count, &scode, &is_contender,
					    &power_class, &is_initiated_reset, &has_more_packets);

	KUNIT_EXPECT_EQ(test, PHY_PACKET_PACKET_IDENTIFIER_SELF_ID, packet_identifier);
	KUNIT_EXPECT_EQ(test, 1, phy_id);
	KUNIT_EXPECT_FALSE(test, extended);
	KUNIT_EXPECT_TRUE(test, link_is_active);
	KUNIT_EXPECT_EQ(test, 0x3f, gap_count);
	KUNIT_EXPECT_EQ(test, SCODE_800, scode);
	KUNIT_EXPECT_TRUE(test, is_contender);
	KUNIT_EXPECT_EQ(test, 0x4, power_class);
	KUNIT_EXPECT_TRUE(test, is_initiated_reset);
	KUNIT_EXPECT_FALSE(test, has_more_packets);

	serialize_phy_packet_self_id_zero(quadlets, packet_identifier, phy_id, extended,
					  link_is_active, gap_count, scode, is_contender,
					  power_class, is_initiated_reset, has_more_packets);

	for (port_index = 0; port_index < ARRAY_SIZE(port_status); ++port_index) {
		port_status[port_index] =
			self_id_sequence_get_port_status(expected, ARRAY_SIZE(expected), port_index);
	}

	KUNIT_EXPECT_EQ(test, PHY_PACKET_SELF_ID_PORT_STATUS_NCONN, port_status[0]);
	KUNIT_EXPECT_EQ(test, PHY_PACKET_SELF_ID_PORT_STATUS_NCONN, port_status[1]);
	KUNIT_EXPECT_EQ(test, PHY_PACKET_SELF_ID_PORT_STATUS_CHILD, port_status[2]);

	for (port_index = 0; port_index < ARRAY_SIZE(port_status); ++port_index) {
		self_id_sequence_set_port_status(quadlets, ARRAY_SIZE(quadlets), port_index,
						 port_status[port_index]);
	}

	KUNIT_EXPECT_MEMEQ(test, quadlets, expected, sizeof(expected));
}

static void test_phy_packet_self_id_zero_and_one(struct kunit *test)
{
	// TSB41LV06A with 6 ports.
	const u32 expected[] = {
		0x803f8459,
		0x80815000,
	};
	u32 quadlets[] = {0, 0};

	unsigned int packet_identifier;
	unsigned int phy_id;
	bool extended;
	bool link_is_active;
	unsigned int gap_count;
	unsigned int scode;
	bool is_contender;
	unsigned int power_class;
	enum phy_packet_self_id_port_status port_status[11];
	bool is_initiated_reset;
	bool has_more_packets;

	unsigned int sequence;
	unsigned int port_index;

	deserialize_phy_packet_self_id_zero(expected[0], &packet_identifier, &phy_id, &extended,
					    &link_is_active, &gap_count, &scode, &is_contender,
					    &power_class, &is_initiated_reset, &has_more_packets);

	KUNIT_EXPECT_EQ(test, PHY_PACKET_PACKET_IDENTIFIER_SELF_ID, packet_identifier);
	KUNIT_EXPECT_EQ(test, 0, phy_id);
	KUNIT_EXPECT_FALSE(test, extended);
	KUNIT_EXPECT_FALSE(test, link_is_active);
	KUNIT_EXPECT_EQ(test, 0x3f, gap_count);
	KUNIT_EXPECT_EQ(test, SCODE_400, scode);
	KUNIT_EXPECT_FALSE(test, is_contender);
	KUNIT_EXPECT_EQ(test, 0x4, power_class);
	KUNIT_EXPECT_FALSE(test, is_initiated_reset);
	KUNIT_EXPECT_TRUE(test, has_more_packets);

	serialize_phy_packet_self_id_zero(quadlets, packet_identifier, phy_id, extended,
					  link_is_active, gap_count, scode, is_contender,
					  power_class, is_initiated_reset, has_more_packets);

	deserialize_phy_packet_self_id_extended(expected[1], &packet_identifier, &phy_id, &extended,
						&sequence, &has_more_packets);

	KUNIT_EXPECT_EQ(test, PHY_PACKET_PACKET_IDENTIFIER_SELF_ID, packet_identifier);
	KUNIT_EXPECT_EQ(test, 0, phy_id);
	KUNIT_EXPECT_TRUE(test, extended);
	KUNIT_EXPECT_EQ(test, 0, sequence);
	KUNIT_EXPECT_FALSE(test, has_more_packets);

	serialize_phy_packet_self_id_extended(&quadlets[1], packet_identifier, phy_id, extended,
					      sequence, has_more_packets);


	for (port_index = 0; port_index < ARRAY_SIZE(port_status); ++port_index) {
		port_status[port_index] =
			self_id_sequence_get_port_status(expected, ARRAY_SIZE(expected), port_index);
	}

	KUNIT_EXPECT_EQ(test, PHY_PACKET_SELF_ID_PORT_STATUS_NCONN, port_status[0]);
	KUNIT_EXPECT_EQ(test, PHY_PACKET_SELF_ID_PORT_STATUS_NCONN, port_status[1]);
	KUNIT_EXPECT_EQ(test, PHY_PACKET_SELF_ID_PORT_STATUS_PARENT, port_status[2]);
	KUNIT_EXPECT_EQ(test, PHY_PACKET_SELF_ID_PORT_STATUS_NCONN, port_status[3]);
	KUNIT_EXPECT_EQ(test, PHY_PACKET_SELF_ID_PORT_STATUS_NCONN, port_status[4]);
	KUNIT_EXPECT_EQ(test, PHY_PACKET_SELF_ID_PORT_STATUS_NCONN, port_status[5]);
	KUNIT_EXPECT_EQ(test, PHY_PACKET_SELF_ID_PORT_STATUS_NONE, port_status[6]);
	KUNIT_EXPECT_EQ(test, PHY_PACKET_SELF_ID_PORT_STATUS_NONE, port_status[7]);
	KUNIT_EXPECT_EQ(test, PHY_PACKET_SELF_ID_PORT_STATUS_NONE, port_status[8]);
	KUNIT_EXPECT_EQ(test, PHY_PACKET_SELF_ID_PORT_STATUS_NONE, port_status[9]);
	KUNIT_EXPECT_EQ(test, PHY_PACKET_SELF_ID_PORT_STATUS_NONE, port_status[10]);

	for (port_index = 0; port_index < ARRAY_SIZE(port_status); ++port_index) {
		self_id_sequence_set_port_status(quadlets, ARRAY_SIZE(quadlets), port_index,
						 port_status[port_index]);
	}

	KUNIT_EXPECT_MEMEQ(test, quadlets, expected, sizeof(expected));
}

static struct kunit_case packet_serdes_test_cases[] = {
	KUNIT_CASE(test_async_header_write_quadlet_request),
	KUNIT_CASE(test_async_header_write_block_request),
@@ -570,6 +822,9 @@ static struct kunit_case packet_serdes_test_cases[] = {
	KUNIT_CASE(test_async_header_lock_request),
	KUNIT_CASE(test_async_header_lock_response),
	KUNIT_CASE(test_isoc_header),
	KUNIT_CASE(test_phy_packet_self_id_zero_case0),
	KUNIT_CASE(test_phy_packet_self_id_zero_case1),
	KUNIT_CASE(test_phy_packet_self_id_zero_and_one),
	{}
};

+126 −0
Original line number Diff line number Diff line
@@ -7,11 +7,42 @@
#ifndef _FIREWIRE_PHY_PACKET_DEFINITIONS_H
#define _FIREWIRE_PHY_PACKET_DEFINITIONS_H

#define PACKET_IDENTIFIER_MASK				0xc0000000
#define PACKET_IDENTIFIER_SHIFT				30

static inline unsigned int phy_packet_get_packet_identifier(u32 quadlet)
{
	return (quadlet & PACKET_IDENTIFIER_MASK) >> PACKET_IDENTIFIER_SHIFT;
}

static inline void phy_packet_set_packet_identifier(u32 *quadlet, unsigned int packet_identifier)
{
	*quadlet &= ~PACKET_IDENTIFIER_MASK;
	*quadlet |= (packet_identifier << PACKET_IDENTIFIER_SHIFT) & PACKET_IDENTIFIER_MASK;
}

#define PHY_PACKET_PACKET_IDENTIFIER_SELF_ID		2

#define SELF_ID_PHY_ID_MASK				0x3f000000
#define SELF_ID_PHY_ID_SHIFT				24
#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_ZERO_LINK_ACTIVE_MASK			0x00400000
#define SELF_ID_ZERO_LINK_ACTIVE_SHIFT			22
#define SELF_ID_ZERO_GAP_COUNT_MASK			0x003f0000
#define SELF_ID_ZERO_GAP_COUNT_SHIFT			16
#define SELF_ID_ZERO_SCODE_MASK				0x0000c000
#define SELF_ID_ZERO_SCODE_SHIFT			14
#define SELF_ID_ZERO_CONTENDER_MASK			0x00000800
#define SELF_ID_ZERO_CONTENDER_SHIFT			11
#define SELF_ID_ZERO_POWER_CLASS_MASK			0x00000700
#define SELF_ID_ZERO_POWER_CLASS_SHIFT			8
#define SELF_ID_ZERO_INITIATED_RESET_MASK		0x00000002
#define SELF_ID_ZERO_INITIATED_RESET_SHIFT		1

#define SELF_ID_EXTENDED_SEQUENCE_MASK			0x00700000
#define SELF_ID_EXTENDED_SEQUENCE_SHIFT			20

@@ -19,21 +50,116 @@

#define SELF_ID_SEQUENCE_MAXIMUM_QUADLET_COUNT		4

static inline unsigned int phy_packet_self_id_get_phy_id(u32 quadlet)
{
	return (quadlet & SELF_ID_PHY_ID_MASK)  >> SELF_ID_PHY_ID_SHIFT;
}

static inline void phy_packet_self_id_set_phy_id(u32 *quadlet, unsigned int phy_id)
{
	*quadlet &= ~SELF_ID_PHY_ID_MASK;
	*quadlet |= (phy_id << SELF_ID_PHY_ID_SHIFT) & SELF_ID_PHY_ID_MASK;
}

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

static inline void phy_packet_self_id_set_extended(u32 *quadlet, bool extended)
{
	*quadlet &= ~SELF_ID_EXTENDED_MASK;
	*quadlet |= (extended << SELF_ID_EXTENDED_SHIFT) & SELF_ID_EXTENDED_MASK;
}

static inline bool phy_packet_self_id_zero_get_link_active(u32 quadlet)
{
	return (quadlet & SELF_ID_ZERO_LINK_ACTIVE_MASK) >> SELF_ID_ZERO_LINK_ACTIVE_SHIFT;
}

static inline void phy_packet_self_id_zero_set_link_active(u32 *quadlet, bool is_active)
{
	*quadlet &= ~SELF_ID_ZERO_LINK_ACTIVE_MASK;
	*quadlet |= (is_active << SELF_ID_ZERO_LINK_ACTIVE_SHIFT) & SELF_ID_ZERO_LINK_ACTIVE_MASK;
}

static inline unsigned int phy_packet_self_id_zero_get_gap_count(u32 quadlet)
{
	return (quadlet & SELF_ID_ZERO_GAP_COUNT_MASK) >> SELF_ID_ZERO_GAP_COUNT_SHIFT;
}

static inline void phy_packet_self_id_zero_set_gap_count(u32 *quadlet, unsigned int gap_count)
{
	*quadlet &= ~SELF_ID_ZERO_GAP_COUNT_MASK;
	*quadlet |= (gap_count << SELF_ID_ZERO_GAP_COUNT_SHIFT) & SELF_ID_ZERO_GAP_COUNT_MASK;
}

static inline unsigned int phy_packet_self_id_zero_get_scode(u32 quadlet)
{
	return (quadlet & SELF_ID_ZERO_SCODE_MASK) >> SELF_ID_ZERO_SCODE_SHIFT;
}

static inline void phy_packet_self_id_zero_set_scode(u32 *quadlet, unsigned int speed)
{
	*quadlet &= ~SELF_ID_ZERO_SCODE_MASK;
	*quadlet |= (speed << SELF_ID_ZERO_SCODE_SHIFT) & SELF_ID_ZERO_SCODE_MASK;
}

static inline bool phy_packet_self_id_zero_get_contender(u32 quadlet)
{
	return (quadlet & SELF_ID_ZERO_CONTENDER_MASK) >> SELF_ID_ZERO_CONTENDER_SHIFT;
}

static inline void phy_packet_self_id_zero_set_contender(u32 *quadlet, bool is_contender)
{
	*quadlet &= ~SELF_ID_ZERO_CONTENDER_MASK;
	*quadlet |= (is_contender << SELF_ID_ZERO_CONTENDER_SHIFT) & SELF_ID_ZERO_CONTENDER_MASK;
}

static inline unsigned int phy_packet_self_id_zero_get_power_class(u32 quadlet)
{
	return (quadlet & SELF_ID_ZERO_POWER_CLASS_MASK) >> SELF_ID_ZERO_POWER_CLASS_SHIFT;
}

static inline void phy_packet_self_id_zero_set_power_class(u32 *quadlet, unsigned int power_class)
{
	*quadlet &= ~SELF_ID_ZERO_POWER_CLASS_MASK;
	*quadlet |= (power_class << SELF_ID_ZERO_POWER_CLASS_SHIFT) & SELF_ID_ZERO_POWER_CLASS_MASK;
}

static inline bool phy_packet_self_id_zero_get_initiated_reset(u32 quadlet)
{
	return (quadlet & SELF_ID_ZERO_INITIATED_RESET_MASK) >> SELF_ID_ZERO_INITIATED_RESET_SHIFT;
}

static inline void phy_packet_self_id_zero_set_initiated_reset(u32 *quadlet, bool is_initiated_reset)
{
	*quadlet &= ~SELF_ID_ZERO_INITIATED_RESET_MASK;
	*quadlet |= (is_initiated_reset << SELF_ID_ZERO_INITIATED_RESET_SHIFT) & SELF_ID_ZERO_INITIATED_RESET_MASK;
}

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 void phy_packet_self_id_set_more_packets(u32 *quadlet, bool is_more_packets)
{
	*quadlet &= ~SELF_ID_MORE_PACKETS_MASK;
	*quadlet |= (is_more_packets << SELF_ID_MORE_PACKETS_SHIFT) & SELF_ID_MORE_PACKETS_MASK;
}

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;
}

static inline void phy_packet_self_id_extended_set_sequence(u32 *quadlet, unsigned int sequence)
{
	*quadlet &= ~SELF_ID_EXTENDED_SEQUENCE_MASK;
	*quadlet |= (sequence << SELF_ID_EXTENDED_SHIFT) & SELF_ID_EXTENDED_SEQUENCE_MASK;
}

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