mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2026-09-18 22:09:30 +02:00
Merge tag 'firewire-updates-7.3' of git://git.kernel.org/pub/scm/linux/kernel/git/ieee1394/linux1394
Pull firewire updates from Takashi Sakamoto:
"Error handling, a potential bug fix, and KUnit tests:
- Handle failures when generating the contents of the configuration
ROM with parameters supplied by in-kernel implementations such as
unit drivers (Sreeraj S Kurup)
- Fix potential memory leak when an invalid self-ID sequence causes
an error while building the internal node tree (Abdun Nihaal)
KUnit tests have been added to trigger this case"
* tag 'firewire-updates-7.3' of git://git.kernel.org/pub/scm/linux/kernel/git/ieee1394/linux1394:
firewire: core: fix memory leak in error path of build_tree()
firewire: core: validate parent port count before allocating nodes in build_tree()
firewire: core: consolidate port counting in build_tree()
firewire: core: add KUnit tests for failure of tree building
firewire: core: add KUnit tests for successful tree building
firewire: core: add KUnit test skeleton for node tree
firewire: core: validate sub-block lengths in fw_core_add_descriptor()
firewire: core: validate overall descriptor length in fw_core_add_descriptor()
This commit is contained in:
@@ -6,3 +6,4 @@ CONFIG_FIREWIRE_KUNIT_DEVICE_ATTRIBUTE_TEST=y
|
||||
CONFIG_FIREWIRE_KUNIT_PACKET_SERDES_TEST=y
|
||||
CONFIG_FIREWIRE_KUNIT_SELF_ID_SEQUENCE_HELPER_TEST=y
|
||||
CONFIG_FIREWIRE_KUNIT_OHCI_SERDES_TEST=y
|
||||
CONFIG_FIREWIRE_KUNIT_NODE_TREE_TEST=y
|
||||
|
||||
@@ -81,6 +81,21 @@ config FIREWIRE_KUNIT_SELF_ID_SEQUENCE_HELPER_TEST
|
||||
For more information on KUnit and unit tests in general, refer
|
||||
to the KUnit documentation in Documentation/dev-tools/kunit/.
|
||||
|
||||
config FIREWIRE_KUNIT_NODE_TREE_TEST
|
||||
tristate "KUnit tests for node tree" if !KUNIT_ALL_TESTS
|
||||
depends on FIREWIRE && KUNIT
|
||||
default KUNIT_ALL_TESTS
|
||||
help
|
||||
This builds the KUnit tests for node tree.
|
||||
|
||||
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
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
#include <linux/list.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/minmax.h>
|
||||
#include <linux/spinlock.h>
|
||||
#include <linux/workqueue.h>
|
||||
|
||||
@@ -167,15 +168,30 @@ int fw_core_add_descriptor(struct fw_descriptor *desc)
|
||||
{
|
||||
size_t i;
|
||||
|
||||
/*
|
||||
* Check descriptor is valid; the length of all blocks in the
|
||||
* descriptor has to add up to exactly the length of the
|
||||
* block.
|
||||
*/
|
||||
i = 0;
|
||||
while (i < desc->length)
|
||||
i += (desc->data[i] >> 16) + 1;
|
||||
/* Reject empty descriptors or those exceeding max Config ROM size (256 quadlets) */
|
||||
if (!in_range(desc->length, 1, 256))
|
||||
return -EINVAL;
|
||||
|
||||
i = 0;
|
||||
/*
|
||||
* Validate internal block structures within the descriptor. Each sub-block
|
||||
* encodes its length in the top 16 bits of its header quadlet.
|
||||
*/
|
||||
while (i < desc->length) {
|
||||
u16 block_len = desc->data[i] >> 16;
|
||||
|
||||
/*
|
||||
* Guard against corrupted descriptors where an individual block length
|
||||
* claims to extend past the allocated end of desc->data, avoiding
|
||||
* out-of-bounds reads.
|
||||
*/
|
||||
if (block_len >= desc->length - i)
|
||||
return -EINVAL;
|
||||
|
||||
i += block_len + 1;
|
||||
}
|
||||
|
||||
/* The sum of sub-block lengths must match total descriptor length */
|
||||
if (i != desc->length)
|
||||
return -EINVAL;
|
||||
|
||||
|
||||
@@ -88,6 +88,17 @@ static inline struct fw_node *fw_node(struct list_head *l)
|
||||
return list_entry(l, struct fw_node, link);
|
||||
}
|
||||
|
||||
typedef void (*fw_node_callback_t)(struct fw_card *card, struct fw_node *node,
|
||||
struct fw_node *parent);
|
||||
|
||||
static void for_each_fw_node(struct fw_card *card, struct fw_node *root,
|
||||
fw_node_callback_t callback);
|
||||
|
||||
static void free_fw_node(struct fw_card *card, struct fw_node *node, struct fw_node *parent)
|
||||
{
|
||||
kfree(node);
|
||||
}
|
||||
|
||||
/*
|
||||
* This function builds the tree representation of the topology given
|
||||
* by the self IDs from the latest bus reset. During the construction
|
||||
@@ -119,8 +130,8 @@ static struct fw_node *build_tree(struct fw_card *card, const u32 *sid, int self
|
||||
|
||||
while (enumerator.quadlet_count > 0) {
|
||||
unsigned int child_port_count = 0;
|
||||
unsigned int parent_port_count = 0;
|
||||
unsigned int total_port_count = 0;
|
||||
unsigned int parent_count = 0;
|
||||
unsigned int quadlet_count;
|
||||
const u32 *self_id_sequence;
|
||||
unsigned int port_capacity;
|
||||
@@ -134,7 +145,7 @@ static struct fw_node *build_tree(struct fw_card *card, const u32 *sid, int self
|
||||
if (PTR_ERR(self_id_sequence) != -ENODATA) {
|
||||
fw_err(card, "inconsistent extended self IDs: %ld\n",
|
||||
PTR_ERR(self_id_sequence));
|
||||
return NULL;
|
||||
goto error;
|
||||
}
|
||||
break;
|
||||
}
|
||||
@@ -148,26 +159,38 @@ static struct fw_node *build_tree(struct fw_card *card, const u32 *sid, int self
|
||||
switch (port_status) {
|
||||
case PHY_PACKET_SELF_ID_PORT_STATUS_CHILD:
|
||||
++child_port_count;
|
||||
fallthrough;
|
||||
break;
|
||||
case PHY_PACKET_SELF_ID_PORT_STATUS_PARENT:
|
||||
++parent_port_count;
|
||||
break;
|
||||
case PHY_PACKET_SELF_ID_PORT_STATUS_NCONN:
|
||||
++total_port_count;
|
||||
fallthrough;
|
||||
break;
|
||||
case PHY_PACKET_SELF_ID_PORT_STATUS_NONE:
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
total_port_count += child_port_count + parent_port_count;
|
||||
|
||||
// Check that the node reports exactly one parent port, except for the root, which
|
||||
// of course should have no parents.
|
||||
if ((enumerator.quadlet_count == 0 && parent_port_count != 0) ||
|
||||
(enumerator.quadlet_count > 0 && parent_port_count != 1)) {
|
||||
fw_err(card, "parent port inconsistency for node %d: parent_count=%d\n",
|
||||
phy_id, parent_port_count);
|
||||
goto error;
|
||||
}
|
||||
|
||||
if (phy_id != phy_packet_self_id_get_phy_id(self_id_sequence[0])) {
|
||||
fw_err(card, "PHY ID mismatch in self ID: %d != %d\n",
|
||||
phy_id, phy_packet_self_id_get_phy_id(self_id_sequence[0]));
|
||||
return NULL;
|
||||
goto error;
|
||||
}
|
||||
|
||||
if (child_port_count > stack_depth) {
|
||||
fw_err(card, "topology stack underflow\n");
|
||||
return NULL;
|
||||
goto error;
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -185,7 +208,7 @@ static struct fw_node *build_tree(struct fw_card *card, const u32 *sid, int self
|
||||
node = fw_node_create(self_id_sequence[0], total_port_count, card->color);
|
||||
if (node == NULL) {
|
||||
fw_err(card, "out of memory while building topology\n");
|
||||
return NULL;
|
||||
goto error;
|
||||
}
|
||||
|
||||
if (phy_id == (card->node_id & 0x3f))
|
||||
@@ -203,7 +226,6 @@ static struct fw_node *build_tree(struct fw_card *card, const u32 *sid, int self
|
||||
// we temporarily abuse node->color for remembering the entry in
|
||||
// the node->ports array where the parent node should be. Later,
|
||||
// when we handle the parent node, we fix up the reference.
|
||||
++parent_count;
|
||||
node->color = port_index;
|
||||
break;
|
||||
|
||||
@@ -221,21 +243,12 @@ static struct fw_node *build_tree(struct fw_card *card, const u32 *sid, int self
|
||||
}
|
||||
}
|
||||
|
||||
// Check that the node reports exactly one parent port, except for the root, which
|
||||
// of course should have no parents.
|
||||
if ((enumerator.quadlet_count == 0 && parent_count != 0) ||
|
||||
(enumerator.quadlet_count > 0 && parent_count != 1)) {
|
||||
fw_err(card, "parent port inconsistency for node %d: "
|
||||
"parent_count=%d\n", phy_id, parent_count);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Pop the child nodes off the stack and push the new node. */
|
||||
__list_del(h->prev, &stack);
|
||||
list_add_tail(&node->link, &stack);
|
||||
stack_depth += 1 - child_port_count;
|
||||
|
||||
if (node->phy_speed == SCODE_BETA && parent_count + child_port_count > 1)
|
||||
if (node->phy_speed == SCODE_BETA && parent_port_count + child_port_count > 1)
|
||||
beta_repeaters_present = true;
|
||||
|
||||
// If PHYs report different gap counts, set an invalid count which will force a gap
|
||||
@@ -254,12 +267,13 @@ static struct fw_node *build_tree(struct fw_card *card, const u32 *sid, int self
|
||||
card->beta_repeaters_present = beta_repeaters_present;
|
||||
|
||||
return local_node;
|
||||
error:
|
||||
++card->color;
|
||||
list_for_each_entry_safe(node, child, &stack, link)
|
||||
for_each_fw_node(card, node, free_fw_node);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
typedef void (*fw_node_callback_t)(struct fw_card * card,
|
||||
struct fw_node * node,
|
||||
struct fw_node * parent);
|
||||
|
||||
static void for_each_fw_node(struct fw_card *card, struct fw_node *root,
|
||||
fw_node_callback_t callback)
|
||||
{
|
||||
@@ -507,3 +521,7 @@ void fw_core_handle_bus_reset(struct fw_card *card, int node_id, int generation,
|
||||
}
|
||||
}
|
||||
EXPORT_SYMBOL(fw_core_handle_bus_reset);
|
||||
|
||||
#ifdef CONFIG_FIREWIRE_KUNIT_NODE_TREE_TEST
|
||||
#include "node-tree-test.c"
|
||||
#endif
|
||||
|
||||
@@ -0,0 +1,607 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
//
|
||||
// node-tree-test.c - An application of Kunit to test node tree.
|
||||
//
|
||||
// Copyright (c) 2026 Takashi Sakamoto
|
||||
//
|
||||
// This file can not be built independently since it is intentionally included in core-topology.c.
|
||||
|
||||
#include <kunit/test.h>
|
||||
#include <kunit/test-bug.h>
|
||||
#include <kunit/device.h>
|
||||
|
||||
struct private_data {
|
||||
struct fw_card *card;
|
||||
unsigned int release_count;
|
||||
};
|
||||
|
||||
static int node_tree_test_init(struct kunit *test)
|
||||
{
|
||||
struct private_data *data;
|
||||
|
||||
data = kunit_kzalloc(test, sizeof(*data), GFP_KERNEL);
|
||||
KUNIT_ASSERT_NOT_NULL(test, data);
|
||||
|
||||
data->card = kunit_kzalloc(test, sizeof(struct fw_card), GFP_KERNEL);
|
||||
KUNIT_ASSERT_NOT_NULL(test, data->card);
|
||||
|
||||
data->card->device = kunit_device_register(test, "dummy-device");
|
||||
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, data->card->device);
|
||||
|
||||
test->priv = data;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void node_tree_test_exit(struct kunit *test)
|
||||
{
|
||||
struct private_data *data = test->priv;
|
||||
|
||||
kunit_device_unregister(test, data->card->device);
|
||||
kunit_kfree(test, data->card);
|
||||
kunit_kfree(test, data);
|
||||
}
|
||||
|
||||
static void release_fw_node(struct fw_card *card, struct fw_node *node, struct fw_node *parent)
|
||||
{
|
||||
struct private_data *data = kunit_get_current_test()->priv;
|
||||
|
||||
fw_node_put(node);
|
||||
++data->release_count;
|
||||
}
|
||||
|
||||
static void node_tree_test_two_nodes(struct kunit *test)
|
||||
{
|
||||
// root
|
||||
// ++============++
|
||||
// || phy 1 ||
|
||||
// || P0 P1 P2 ||
|
||||
// ++===|==|==|==++
|
||||
// |
|
||||
// +-----+
|
||||
// |
|
||||
// ++===|==x==x==++
|
||||
// || P0 P1 P2 ||
|
||||
// || phy 0 ||
|
||||
// ++============++
|
||||
//
|
||||
static const u32 self_id_sequence[] = {
|
||||
0x80000080,
|
||||
0x8100005e,
|
||||
};
|
||||
struct private_data *data = test->priv;
|
||||
struct fw_card *card = data->card;
|
||||
|
||||
card->node_id = LOCAL_BUS | 0x01;
|
||||
|
||||
card->local_node = build_tree(card, self_id_sequence, ARRAY_SIZE(self_id_sequence), 123);
|
||||
KUNIT_EXPECT_NOT_NULL(test, card->local_node);
|
||||
KUNIT_EXPECT_PTR_EQ(test, card->local_node, card->root_node);
|
||||
|
||||
struct fw_node *node = card->root_node;
|
||||
KUNIT_EXPECT_EQ(test, node->node_id, LOCAL_BUS | 0x01);
|
||||
KUNIT_EXPECT_EQ(test, node->port_count, 3);
|
||||
KUNIT_EXPECT_NULL(test, node->ports[0]);
|
||||
KUNIT_EXPECT_NULL(test, node->ports[1]);
|
||||
KUNIT_EXPECT_NOT_NULL(test, node->ports[2]);
|
||||
|
||||
struct fw_node *parent = node;
|
||||
node = parent->ports[2];
|
||||
KUNIT_EXPECT_EQ(test, node->node_id, LOCAL_BUS | 0x00);
|
||||
KUNIT_EXPECT_EQ(test, node->port_count, 1);
|
||||
KUNIT_EXPECT_PTR_EQ(test, node->ports[0], parent);
|
||||
|
||||
++card->color;
|
||||
for_each_fw_node(card, card->root_node, release_fw_node);
|
||||
KUNIT_EXPECT_EQ(test, data->release_count, 2);
|
||||
}
|
||||
|
||||
static void node_tree_test_two_nodes_1394a(struct kunit *test)
|
||||
{
|
||||
// root
|
||||
// ++===============++
|
||||
// || phy 0 ||
|
||||
// || P0 P1 P2 P3 ||
|
||||
// ++===|==|==|==|==++
|
||||
// |
|
||||
// +--+
|
||||
// |
|
||||
// ++===|==|==|==|==|==++
|
||||
// || P0 P1 P2 P3 P4 ||
|
||||
// || phy 1 ||
|
||||
// ++==================++
|
||||
//
|
||||
// NOTE: Just for Self-ID Packets Zero and One.
|
||||
static const u32 self_id_sequence[] = {
|
||||
0x80000065, 0x80814000,
|
||||
0x8100005d, 0x81810000,
|
||||
};
|
||||
struct private_data *data = test->priv;
|
||||
struct fw_card *card = data->card;
|
||||
|
||||
card->node_id = LOCAL_BUS | 0x01;
|
||||
|
||||
card->local_node = build_tree(card, self_id_sequence, ARRAY_SIZE(self_id_sequence), 123);
|
||||
KUNIT_EXPECT_NOT_NULL(test, card->local_node);
|
||||
KUNIT_EXPECT_PTR_EQ(test, card->local_node, card->root_node);
|
||||
|
||||
struct fw_node *node = card->root_node;
|
||||
KUNIT_EXPECT_EQ(test, node->node_id, LOCAL_BUS | 0x01);
|
||||
KUNIT_EXPECT_EQ(test, node->port_count, 4);
|
||||
KUNIT_EXPECT_NULL(test, node->ports[0]);
|
||||
KUNIT_EXPECT_NULL(test, node->ports[1]);
|
||||
KUNIT_EXPECT_NOT_NULL(test, node->ports[2]);
|
||||
KUNIT_EXPECT_NULL(test, node->ports[3]);
|
||||
|
||||
struct fw_node *parent = node;
|
||||
node = parent->ports[2];
|
||||
KUNIT_EXPECT_EQ(test, node->node_id, LOCAL_BUS | 0x00);
|
||||
KUNIT_EXPECT_EQ(test, node->port_count, 5);
|
||||
KUNIT_EXPECT_NULL(test, node->ports[0]);
|
||||
KUNIT_EXPECT_PTR_EQ(test, node->ports[1], parent);
|
||||
KUNIT_EXPECT_NULL(test, node->ports[2]);
|
||||
KUNIT_EXPECT_NULL(test, node->ports[3]);
|
||||
KUNIT_EXPECT_NULL(test, node->ports[4]);
|
||||
|
||||
++card->color;
|
||||
for_each_fw_node(card, card->root_node, release_fw_node);
|
||||
KUNIT_EXPECT_EQ(test, data->release_count, 2);
|
||||
}
|
||||
|
||||
static void node_tree_test_three_nodes_case0(struct kunit *test)
|
||||
{
|
||||
// root
|
||||
// ++============++
|
||||
// || phy 2 ||
|
||||
// || P0 P1 P2 ||
|
||||
// ++===|==|==|==++
|
||||
// | |
|
||||
// +--+ +-----------------+
|
||||
// | |
|
||||
// ++===|==|==x==++ ++===|==|==|==++
|
||||
// || P0 P1 P2 || || P0 P1 P2 ||
|
||||
// || phy 0 || || phy 1 ||
|
||||
// ++============++ ++============++
|
||||
//
|
||||
static const u32 self_id_sequence[] = {
|
||||
0x80000060,
|
||||
0x81000058,
|
||||
0x820000dc,
|
||||
};
|
||||
struct private_data *data = test->priv;
|
||||
struct fw_card *card = data->card;
|
||||
|
||||
card->node_id = LOCAL_BUS | 0x02;
|
||||
|
||||
card->local_node = build_tree(card, self_id_sequence, ARRAY_SIZE(self_id_sequence), 123);
|
||||
KUNIT_EXPECT_NOT_NULL(test, card->local_node);
|
||||
KUNIT_EXPECT_PTR_EQ(test, card->local_node, card->root_node);
|
||||
|
||||
struct fw_node *node = card->root_node;
|
||||
KUNIT_EXPECT_EQ(test, node->node_id, LOCAL_BUS | 0x02);
|
||||
KUNIT_EXPECT_EQ(test, node->port_count, 3);
|
||||
KUNIT_EXPECT_NOT_NULL(test, node->ports[0]);
|
||||
KUNIT_EXPECT_NULL(test, node->ports[1]);
|
||||
KUNIT_EXPECT_NOT_NULL(test, node->ports[2]);
|
||||
|
||||
struct fw_node *parent = node;
|
||||
node = parent->ports[0];
|
||||
KUNIT_EXPECT_EQ(test, node->node_id, LOCAL_BUS | 0x00);
|
||||
KUNIT_EXPECT_EQ(test, node->port_count, 2);
|
||||
KUNIT_EXPECT_NULL(test, node->ports[0]);
|
||||
KUNIT_EXPECT_PTR_EQ(test, node->ports[1], parent);
|
||||
|
||||
node = parent->ports[2];
|
||||
KUNIT_EXPECT_EQ(test, node->node_id, LOCAL_BUS | 0x01);
|
||||
KUNIT_EXPECT_EQ(test, node->port_count, 3);
|
||||
KUNIT_EXPECT_NULL(test, node->ports[0]);
|
||||
KUNIT_EXPECT_NULL(test, node->ports[1]);
|
||||
KUNIT_EXPECT_PTR_EQ(test, node->ports[2], parent);
|
||||
|
||||
++card->color;
|
||||
for_each_fw_node(card, card->root_node, release_fw_node);
|
||||
KUNIT_EXPECT_EQ(test, data->release_count, 3);
|
||||
}
|
||||
|
||||
static void node_tree_test_three_nodes_case1(struct kunit *test)
|
||||
{
|
||||
// root
|
||||
// ++============++
|
||||
// || phy 2 ||
|
||||
// || P0 P1 P2 ||
|
||||
// ++===|==|==x==++
|
||||
// |
|
||||
// | +-----------+
|
||||
// | | |
|
||||
// ++===|==|==|==++ ++===|==x==x==++
|
||||
// || P0 P1 P2 || || P0 P1 P2 ||
|
||||
// || phy 1 || || phy 0 ||
|
||||
// ++============++ ++============++
|
||||
//
|
||||
static const u32 self_id_sequence[] = {
|
||||
0x80000080,
|
||||
0x8100006c,
|
||||
0x82000070,
|
||||
};
|
||||
struct private_data *data = test->priv;
|
||||
struct fw_card *card = data->card;
|
||||
|
||||
card->node_id = LOCAL_BUS | 0x02;
|
||||
|
||||
card->local_node = build_tree(card, self_id_sequence, ARRAY_SIZE(self_id_sequence), 123);
|
||||
KUNIT_EXPECT_NOT_NULL(test, card->local_node);
|
||||
KUNIT_EXPECT_PTR_EQ(test, card->local_node, card->root_node);
|
||||
|
||||
struct fw_node *node = card->root_node;
|
||||
KUNIT_EXPECT_EQ(test, node->node_id, LOCAL_BUS | 0x02);
|
||||
KUNIT_EXPECT_EQ(test, node->port_count, 2);
|
||||
KUNIT_EXPECT_NULL(test, node->ports[0]);
|
||||
KUNIT_EXPECT_NOT_NULL(test, node->ports[1]);
|
||||
|
||||
struct fw_node *parent = node;
|
||||
node = parent->ports[1];
|
||||
KUNIT_EXPECT_EQ(test, node->node_id, LOCAL_BUS | 0x01);
|
||||
KUNIT_EXPECT_EQ(test, node->port_count, 3);
|
||||
KUNIT_EXPECT_NULL(test, node->ports[0]);
|
||||
KUNIT_EXPECT_PTR_EQ(test, node->ports[1], parent);
|
||||
KUNIT_EXPECT_NOT_NULL(test, node->ports[2]);
|
||||
|
||||
parent = node;
|
||||
node = parent->ports[2];
|
||||
KUNIT_EXPECT_EQ(test, node->node_id, LOCAL_BUS | 0x00);
|
||||
KUNIT_EXPECT_EQ(test, node->port_count, 1);
|
||||
KUNIT_EXPECT_PTR_EQ(test, node->ports[0], parent);
|
||||
|
||||
++card->color;
|
||||
for_each_fw_node(card, card->root_node, release_fw_node);
|
||||
KUNIT_EXPECT_EQ(test, data->release_count, 3);
|
||||
}
|
||||
|
||||
static void node_tree_test_four_nodes_case0(struct kunit *test)
|
||||
{
|
||||
// root
|
||||
// ++============++
|
||||
// || phy 3 ||
|
||||
// || P0 P1 P2 ||
|
||||
// ++===|==|==|==++
|
||||
// |
|
||||
// | +-----------+ +--------------+
|
||||
// | | | | |
|
||||
// ++===|==|==|==++ ++===|==|==x==++ ++===|==x==x==++
|
||||
// || P0 P1 P2 || || P0 P1 P2 || || P0 P1 P2 ||
|
||||
// || phy 2 || || phy 1 || || phy 0 ||
|
||||
// ++============++ ++============++ ++============++
|
||||
//
|
||||
static const u32 self_id_sequence[] = {
|
||||
0x80000080,
|
||||
0x810000b0,
|
||||
0x8200006c,
|
||||
0x83000074,
|
||||
};
|
||||
struct private_data *data = test->priv;
|
||||
struct fw_card *card = data->card;
|
||||
|
||||
card->node_id = LOCAL_BUS | 0x03;
|
||||
|
||||
card->local_node = build_tree(card, self_id_sequence, ARRAY_SIZE(self_id_sequence), 123);
|
||||
KUNIT_EXPECT_NOT_NULL(test, card->local_node);
|
||||
KUNIT_EXPECT_PTR_EQ(test, card->local_node, card->root_node);
|
||||
|
||||
struct fw_node *node = card->root_node;
|
||||
KUNIT_EXPECT_EQ(test, node->node_id, LOCAL_BUS | 0x03);
|
||||
KUNIT_EXPECT_EQ(test, node->port_count, 3);
|
||||
KUNIT_EXPECT_NULL(test, node->ports[0]);
|
||||
KUNIT_EXPECT_NOT_NULL(test, node->ports[1]);
|
||||
KUNIT_EXPECT_NULL(test, node->ports[2]);
|
||||
|
||||
struct fw_node *parent = node;
|
||||
node = parent->ports[1];
|
||||
KUNIT_EXPECT_EQ(test, node->node_id, LOCAL_BUS | 0x02);
|
||||
KUNIT_EXPECT_EQ(test, node->port_count, 3);
|
||||
KUNIT_EXPECT_NULL(test, node->ports[0]);
|
||||
KUNIT_EXPECT_PTR_EQ(test, node->ports[1], parent);
|
||||
KUNIT_EXPECT_NOT_NULL(test, node->ports[2]);
|
||||
|
||||
parent = node;
|
||||
node = parent->ports[2];
|
||||
KUNIT_EXPECT_EQ(test, node->node_id, LOCAL_BUS | 0x01);
|
||||
KUNIT_EXPECT_EQ(test, node->port_count, 2);
|
||||
KUNIT_EXPECT_PTR_EQ(test, node->ports[0], parent);
|
||||
KUNIT_EXPECT_NOT_NULL(test, node->ports[1]);
|
||||
|
||||
parent = node;
|
||||
node = parent->ports[1];
|
||||
KUNIT_EXPECT_EQ(test, node->node_id, LOCAL_BUS | 0x00);
|
||||
KUNIT_EXPECT_EQ(test, node->port_count, 1);
|
||||
KUNIT_EXPECT_PTR_EQ(test, node->ports[0], parent);
|
||||
|
||||
++card->color;
|
||||
for_each_fw_node(card, card->root_node, release_fw_node);
|
||||
KUNIT_EXPECT_EQ(test, data->release_count, 4);
|
||||
}
|
||||
|
||||
static void node_tree_test_four_nodes_case1(struct kunit *test)
|
||||
{
|
||||
// root
|
||||
// ++============++
|
||||
// || phy 3 ||
|
||||
// || P0 P1 P2 ||
|
||||
// ++===|==|==x==++
|
||||
// |
|
||||
// | +--------------------------------+
|
||||
// | | +-----------+ |
|
||||
// ++===|==|==|==++ ++===|==x==x==++ ++===|==|==|==++
|
||||
// || P0 P1 P2 || || P0 P1 P2 || || P0 P1 P2 ||
|
||||
// || phy 2 || || phy 1 || || phy 0 ||
|
||||
// ++============++ ++============++ ++============++
|
||||
//
|
||||
static const u32 self_id_sequence[] = {
|
||||
0x80000094,
|
||||
0x81000080,
|
||||
0x820000bc,
|
||||
0x830000d0,
|
||||
};
|
||||
struct private_data *data = test->priv;
|
||||
struct fw_card *card = data->card;
|
||||
|
||||
card->node_id = LOCAL_BUS | 0x03;
|
||||
|
||||
card->local_node = build_tree(card, self_id_sequence, ARRAY_SIZE(self_id_sequence), 123);
|
||||
KUNIT_EXPECT_NOT_NULL(test, card->local_node);
|
||||
KUNIT_EXPECT_PTR_EQ(test, card->local_node, card->root_node);
|
||||
|
||||
struct fw_node *node = card->root_node;
|
||||
KUNIT_EXPECT_EQ(test, node->node_id, LOCAL_BUS | 0x03);
|
||||
KUNIT_EXPECT_EQ(test, node->port_count, 2);
|
||||
KUNIT_EXPECT_NOT_NULL(test, node->ports[0]);
|
||||
KUNIT_EXPECT_NULL(test, node->ports[1]);
|
||||
|
||||
struct fw_node *parent = node;
|
||||
node = parent->ports[0];
|
||||
KUNIT_EXPECT_EQ(test, node->node_id, LOCAL_BUS | 0x02);
|
||||
KUNIT_EXPECT_EQ(test, node->port_count, 3);
|
||||
KUNIT_EXPECT_PTR_EQ(test, node->ports[0], parent);
|
||||
KUNIT_EXPECT_NOT_NULL(test, node->ports[1]);
|
||||
KUNIT_EXPECT_NOT_NULL(test, node->ports[2]);
|
||||
|
||||
parent = node;
|
||||
node = parent->ports[2];
|
||||
KUNIT_EXPECT_EQ(test, node->node_id, LOCAL_BUS | 0x01);
|
||||
KUNIT_EXPECT_EQ(test, node->port_count, 1);
|
||||
KUNIT_EXPECT_PTR_EQ(test, node->ports[0], parent);
|
||||
|
||||
node = parent->ports[1];
|
||||
KUNIT_EXPECT_EQ(test, node->node_id, LOCAL_BUS | 0x00);
|
||||
KUNIT_EXPECT_EQ(test, node->port_count, 3);
|
||||
KUNIT_EXPECT_PTR_EQ(test, node->ports[0], parent);
|
||||
KUNIT_EXPECT_NULL(test, node->ports[1]);
|
||||
KUNIT_EXPECT_NULL(test, node->ports[2]);
|
||||
|
||||
++card->color;
|
||||
for_each_fw_node(card, card->root_node, release_fw_node);
|
||||
KUNIT_EXPECT_EQ(test, data->release_count, 4);
|
||||
}
|
||||
|
||||
static void node_tree_test_four_nodes_case2(struct kunit *test)
|
||||
{
|
||||
// root
|
||||
// ++============++
|
||||
// || phy 3 ||
|
||||
// || P0 P1 P2 ||
|
||||
// ++===|==|==|==++
|
||||
// | |
|
||||
// | +-----------------------------+
|
||||
// | +--------------+ |
|
||||
// ++===|==|==x==++ ++===|==|==|==++ ++===|==x==x==++
|
||||
// || P0 P1 P2 || || P0 P1 P2 || || P0 P1 P2 ||
|
||||
// || phy 1 || || phy 0 || || phy 2 ||
|
||||
// ++============++ ++============++ ++============++
|
||||
//
|
||||
static const u32 self_id_sequence[] = {
|
||||
0x80000094,
|
||||
0x810000b0,
|
||||
0x82000080,
|
||||
0x830000dc,
|
||||
};
|
||||
struct private_data *data = test->priv;
|
||||
struct fw_card *card = data->card;
|
||||
|
||||
card->node_id = LOCAL_BUS | 0x03;
|
||||
|
||||
card->local_node = build_tree(card, self_id_sequence, ARRAY_SIZE(self_id_sequence), 123);
|
||||
KUNIT_EXPECT_NOT_NULL(test, card->local_node);
|
||||
KUNIT_EXPECT_PTR_EQ(test, card->local_node, card->root_node);
|
||||
|
||||
struct fw_node *node = card->root_node;
|
||||
KUNIT_EXPECT_EQ(test, node->node_id, LOCAL_BUS | 0x03);
|
||||
KUNIT_EXPECT_EQ(test, node->port_count, 3);
|
||||
KUNIT_EXPECT_NOT_NULL(test, node->ports[0]);
|
||||
KUNIT_EXPECT_NULL(test, node->ports[1]);
|
||||
KUNIT_EXPECT_NOT_NULL(test, node->ports[2]);
|
||||
|
||||
struct fw_node *parent = node;
|
||||
node = parent->ports[2];
|
||||
KUNIT_EXPECT_EQ(test, node->node_id, LOCAL_BUS | 0x02);
|
||||
KUNIT_EXPECT_EQ(test, node->port_count, 1);
|
||||
KUNIT_EXPECT_PTR_EQ(test, node->ports[0], parent);
|
||||
|
||||
node = parent->ports[0];
|
||||
KUNIT_EXPECT_EQ(test, node->node_id, LOCAL_BUS | 0x01);
|
||||
KUNIT_EXPECT_EQ(test, node->port_count, 2);
|
||||
KUNIT_EXPECT_PTR_EQ(test, node->ports[0], parent);
|
||||
KUNIT_EXPECT_NOT_NULL(test, node->ports[1]);
|
||||
|
||||
parent = node;
|
||||
node = parent->ports[1];
|
||||
KUNIT_EXPECT_EQ(test, node->node_id, LOCAL_BUS | 0x00);
|
||||
KUNIT_EXPECT_EQ(test, node->port_count, 3);
|
||||
KUNIT_EXPECT_PTR_EQ(test, node->ports[0], parent);
|
||||
KUNIT_EXPECT_NULL(test, node->ports[1]);
|
||||
KUNIT_EXPECT_NULL(test, node->ports[2]);
|
||||
|
||||
++card->color;
|
||||
for_each_fw_node(card, card->root_node, release_fw_node);
|
||||
KUNIT_EXPECT_EQ(test, data->release_count, 4);
|
||||
}
|
||||
|
||||
static void node_tree_test_four_nodes_case3(struct kunit *test)
|
||||
{
|
||||
// root
|
||||
// ++============++
|
||||
// || phy 3 ||
|
||||
// || P0 P1 P2 ||
|
||||
// ++===|==|==|==++
|
||||
// | | +--------------------------------+
|
||||
// | +--------------------+ |
|
||||
// | | |
|
||||
// ++===|==|==x==++ ++===|==|==|==++ ++===|==|==x==++
|
||||
// || P0 P1 P2 || || P0 P1 P2 || || P0 P1 P2 ||
|
||||
// || phy 0 || || phy 1 || || phy 2 ||
|
||||
// ++============++ ++============++ ++============++
|
||||
//
|
||||
static const u32 self_id_sequence[] = {
|
||||
0x80000090,
|
||||
0x81000058,
|
||||
0x82000060,
|
||||
0x830000fc,
|
||||
};
|
||||
struct private_data *data = test->priv;
|
||||
struct fw_card *card = data->card;
|
||||
|
||||
card->node_id = LOCAL_BUS | 0x03;
|
||||
|
||||
card->local_node = build_tree(card, self_id_sequence, ARRAY_SIZE(self_id_sequence), 123);
|
||||
KUNIT_EXPECT_NOT_NULL(test, card->local_node);
|
||||
KUNIT_EXPECT_PTR_EQ(test, card->local_node, card->root_node);
|
||||
|
||||
struct fw_node *node = card->root_node;
|
||||
KUNIT_EXPECT_EQ(test, node->node_id, LOCAL_BUS | 0x03);
|
||||
KUNIT_EXPECT_EQ(test, node->port_count, 3);
|
||||
KUNIT_EXPECT_NOT_NULL(test, node->ports[0]);
|
||||
KUNIT_EXPECT_NOT_NULL(test, node->ports[1]);
|
||||
KUNIT_EXPECT_NOT_NULL(test, node->ports[2]);
|
||||
|
||||
struct fw_node *parent = node;
|
||||
node = parent->ports[2];
|
||||
KUNIT_EXPECT_EQ(test, node->node_id, LOCAL_BUS | 0x02);
|
||||
KUNIT_EXPECT_EQ(test, node->port_count, 2);
|
||||
KUNIT_EXPECT_NULL(test, node->ports[0]);
|
||||
KUNIT_EXPECT_PTR_EQ(test, node->ports[1], parent);
|
||||
|
||||
node = parent->ports[1];
|
||||
KUNIT_EXPECT_EQ(test, node->node_id, LOCAL_BUS | 0x01);
|
||||
KUNIT_EXPECT_EQ(test, node->port_count, 3);
|
||||
KUNIT_EXPECT_NULL(test, node->ports[0]);
|
||||
KUNIT_EXPECT_NULL(test, node->ports[1]);
|
||||
KUNIT_EXPECT_PTR_EQ(test, node->ports[2], parent);
|
||||
|
||||
node = parent->ports[0];
|
||||
KUNIT_EXPECT_EQ(test, node->node_id, LOCAL_BUS | 0x00);
|
||||
KUNIT_EXPECT_EQ(test, node->port_count, 2);
|
||||
KUNIT_EXPECT_PTR_EQ(test, node->ports[0], parent);
|
||||
KUNIT_EXPECT_NULL(test, node->ports[1]);
|
||||
|
||||
++card->color;
|
||||
for_each_fw_node(card, card->root_node, release_fw_node);
|
||||
KUNIT_EXPECT_EQ(test, data->release_count, 4);
|
||||
}
|
||||
|
||||
static void node_tree_test_invalid_extended_self_id_sequence(struct kunit *test)
|
||||
{
|
||||
// Use the same node tree as node_tree_test_four_nodes_case1, except for the invalid
|
||||
// content of self ID packet for the phy 3.
|
||||
static const u32 self_id_sequence[] = {
|
||||
0x80000094,
|
||||
0x81000080,
|
||||
0x820000bc,
|
||||
0x830000d1, // Invalid.
|
||||
};
|
||||
struct private_data *data = test->priv;
|
||||
struct fw_card *card = data->card;
|
||||
|
||||
card->node_id = LOCAL_BUS | 0x03;
|
||||
|
||||
card->local_node = build_tree(card, self_id_sequence, ARRAY_SIZE(self_id_sequence), 123);
|
||||
KUNIT_EXPECT_NULL(test, card->local_node);
|
||||
}
|
||||
|
||||
static void node_tree_test_invalid_phy_id(struct kunit *test)
|
||||
{
|
||||
// Use the same node tree as node_tree_test_four_nodes_case1, except for the invalid
|
||||
// phy ID for phy 3.
|
||||
static const u32 self_id_sequence[] = {
|
||||
0x80000094,
|
||||
0x81000080,
|
||||
0x820000bc,
|
||||
0x8f0000d0, // Invalid.
|
||||
};
|
||||
struct private_data *data = test->priv;
|
||||
struct fw_card *card = data->card;
|
||||
|
||||
card->node_id = LOCAL_BUS | 0x03;
|
||||
|
||||
card->local_node = build_tree(card, self_id_sequence, ARRAY_SIZE(self_id_sequence), 123);
|
||||
KUNIT_EXPECT_NULL(test, card->local_node);
|
||||
}
|
||||
|
||||
static void node_tree_test_invalid_child_port_count(struct kunit *test)
|
||||
{
|
||||
// Use the same node tree as node_tree_test_four_nodes_case1, except for the invalid
|
||||
// count of child ports for phy 3.
|
||||
static const u32 self_id_sequence[] = {
|
||||
0x80000094,
|
||||
0x81000080,
|
||||
0x820000bc,
|
||||
0x830000fc, // Invalid.
|
||||
};
|
||||
struct private_data *data = test->priv;
|
||||
struct fw_card *card = data->card;
|
||||
|
||||
card->node_id = LOCAL_BUS | 0x03;
|
||||
|
||||
card->local_node = build_tree(card, self_id_sequence, ARRAY_SIZE(self_id_sequence), 123);
|
||||
KUNIT_EXPECT_NULL(test, card->local_node);
|
||||
}
|
||||
|
||||
static void node_tree_test_invalid_parent_port_count(struct kunit *test)
|
||||
{
|
||||
// Use the same node tree as node_tree_test_four_nodes_case1, except for the invalid
|
||||
// count of parent ports for phy 3.
|
||||
static const u32 self_id_sequence[] = {
|
||||
0x80000094,
|
||||
0x81000080,
|
||||
0x820000bc,
|
||||
0x830000e8, // Invalid.
|
||||
};
|
||||
struct private_data *data = test->priv;
|
||||
struct fw_card *card = data->card;
|
||||
|
||||
card->node_id = LOCAL_BUS | 0x03;
|
||||
|
||||
card->local_node = build_tree(card, self_id_sequence, ARRAY_SIZE(self_id_sequence), 123);
|
||||
KUNIT_EXPECT_NULL(test, card->local_node);
|
||||
}
|
||||
|
||||
static struct kunit_case node_tree_test_cases[] = {
|
||||
KUNIT_CASE(node_tree_test_two_nodes),
|
||||
KUNIT_CASE(node_tree_test_two_nodes_1394a),
|
||||
KUNIT_CASE(node_tree_test_three_nodes_case0),
|
||||
KUNIT_CASE(node_tree_test_three_nodes_case1),
|
||||
KUNIT_CASE(node_tree_test_four_nodes_case0),
|
||||
KUNIT_CASE(node_tree_test_four_nodes_case1),
|
||||
KUNIT_CASE(node_tree_test_four_nodes_case2),
|
||||
KUNIT_CASE(node_tree_test_four_nodes_case3),
|
||||
KUNIT_CASE(node_tree_test_invalid_extended_self_id_sequence),
|
||||
KUNIT_CASE(node_tree_test_invalid_phy_id),
|
||||
KUNIT_CASE(node_tree_test_invalid_child_port_count),
|
||||
KUNIT_CASE(node_tree_test_invalid_parent_port_count),
|
||||
{}
|
||||
};
|
||||
|
||||
static struct kunit_suite node_tree_test_suite = {
|
||||
.name = "firewire-node-tree",
|
||||
.init = node_tree_test_init,
|
||||
.exit = node_tree_test_exit,
|
||||
.test_cases = node_tree_test_cases,
|
||||
};
|
||||
kunit_test_suite(node_tree_test_suite);
|
||||
Reference in New Issue
Block a user