aboutsummaryrefslogtreecommitdiff
path: root/drivers/firewire
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/firewire')
-rw-r--r--drivers/firewire/.kunitconfig1
-rw-r--r--drivers/firewire/Kconfig16
-rw-r--r--drivers/firewire/Makefile2
-rw-r--r--drivers/firewire/config-rom-generator-test.c407
-rw-r--r--drivers/firewire/config-rom-parser-test.c355
-rw-r--r--drivers/firewire/core-card.c124
-rw-r--r--drivers/firewire/core-cdev.c301
-rw-r--r--drivers/firewire/core-device.c12
-rw-r--r--drivers/firewire/core-iso.c32
-rw-r--r--drivers/firewire/core-topology.c3
-rw-r--r--drivers/firewire/core-transaction.c414
-rw-r--r--drivers/firewire/device-attribute-test.c4
-rw-r--r--drivers/firewire/ohci-serdes-test.c4
-rw-r--r--drivers/firewire/ohci.c312
-rw-r--r--drivers/firewire/ohci.h2
-rw-r--r--drivers/firewire/uapi-test.c20
16 files changed, 1518 insertions, 491 deletions
diff --git a/drivers/firewire/.kunitconfig b/drivers/firewire/.kunitconfig
index 7406acb00478..ee9129081cef 100644
--- a/drivers/firewire/.kunitconfig
+++ b/drivers/firewire/.kunitconfig
@@ -7,3 +7,4 @@ 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
+CONFIG_FIREWIRE_KUNIT_CONFIG_ROM_PARSER_AND_GENERATOR_TEST=y
diff --git a/drivers/firewire/Kconfig b/drivers/firewire/Kconfig
index b5abe00accc9..7e1d4b2c440b 100644
--- a/drivers/firewire/Kconfig
+++ b/drivers/firewire/Kconfig
@@ -96,6 +96,22 @@ config FIREWIRE_KUNIT_NODE_TREE_TEST
For more information on KUnit and unit tests in general, refer
to the KUnit documentation in Documentation/dev-tools/kunit/.
+config FIREWIRE_KUNIT_CONFIG_ROM_PARSER_AND_GENERATOR_TEST
+ tristate "KUnit tests for config ROM parser and generator" if !KUNIT_ALL_TESTS
+ depends on FIREWIRE && KUNIT
+ default KUNIT_ALL_TESTS
+ help
+ This builds the KUnit tests to check parser and generator for
+ configuration ROM content defined in IEEE 1212.
+
+ 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
diff --git a/drivers/firewire/Makefile b/drivers/firewire/Makefile
index 1ff550e93a8c..e27fca37b4b2 100644
--- a/drivers/firewire/Makefile
+++ b/drivers/firewire/Makefile
@@ -3,6 +3,8 @@
# Makefile for the Linux IEEE 1394 implementation
#
+CONTEXT_ANALYSIS_core-transaction.o := y
+
firewire-core-y += core-trace.o core-card.o core-cdev.o core-device.o \
core-iso.o core-topology.o core-transaction.o
firewire-ohci-y += ohci.o
diff --git a/drivers/firewire/config-rom-generator-test.c b/drivers/firewire/config-rom-generator-test.c
new file mode 100644
index 000000000000..dd2e71213209
--- /dev/null
+++ b/drivers/firewire/config-rom-generator-test.c
@@ -0,0 +1,407 @@
+// SPDX-License-Identifier: GPL-2.0-only
+//
+// config-rom-generator-test.c - An application of Kunit to test configuration ROM generator.
+//
+// Copyright (c) 2026 Takashi Sakamoto
+//
+// This file can not be built independently since it is intentionally included in core-card.c.
+
+#include <kunit/test.h>
+#include <kunit/device.h>
+
+static const __be32 config_rom_bare[] = {
+ cpu_to_be32(0x0404921b), // bus info
+ cpu_to_be32(0x31333934), // |
+ cpu_to_be32(0xf000b223), // |
+ cpu_to_be32(0x01234567), // |
+ cpu_to_be32(0x89abcdef), // v
+ cpu_to_be32(0x00051b70), // root directory
+ cpu_to_be32(0x0c0083c0), // |
+ cpu_to_be32(0x03001f11), // |
+ cpu_to_be32(0x81000003), // |
+ cpu_to_be32(0x17023901), // |
+ cpu_to_be32(0x81000008), // v
+ cpu_to_be32(0x00064cb7), // text descriptor leaf (from root)
+ cpu_to_be32(0x00000000), // |
+ cpu_to_be32(0x00000000), // |
+ cpu_to_be32(0x4c696e75), // |
+ cpu_to_be32(0x78204669), // |
+ cpu_to_be32(0x72657769), // |
+ cpu_to_be32(0x72650000), // v
+ cpu_to_be32(0x0003ff1c), // text descriptor leaf (from root)
+ cpu_to_be32(0x00000000), // |
+ cpu_to_be32(0x00000000), // |
+ cpu_to_be32(0x4a756a75), // v Juju is a code name at the development time of this stack.
+};
+
+// Following to Configuration ROM for AV/C Devices 1.0 (Dec. 2000. 1394 Trading Association,
+// Document 1999027).
+#define UNIT_SPEC_ID_1394TA 0x0000a02d
+#define UNIT_SW_VERSION_AVC 0x00010001
+#define UNIT_SW_VERSION_IIDC_0104 0x00000100
+
+static const u32 avc_unit_directory_and_leaf[] = {
+ 0x00040000, // Unit directory consists of below 4 quads.
+ (CSR_SPECIFIER_ID << 24) | UNIT_SPEC_ID_1394TA,
+ (CSR_VERSION << 24) | UNIT_SW_VERSION_AVC,
+ (CSR_MODEL << 24) | 0x00260827, // Today.
+ ((CSR_LEAF | CSR_DESCRIPTOR) << 24) | 0x00000001, // Point to next quadlet.
+ 0x00030000, // Text leaf consists of below 3 quads.
+ 0x00000000,
+ 0x00000000,
+ 0x50756900, // Pui is the name of a cat that the author takes care of.
+};
+
+static const __be32 config_rom_with_avc_unit[] = {
+ cpu_to_be32(0x0404c1e5), // bus info
+ cpu_to_be32(0x31333934), // |
+ cpu_to_be32(0xf000b233), // |
+ cpu_to_be32(0x01234567), // |
+ cpu_to_be32(0x89abcdef), // v
+ cpu_to_be32(0x0006a2d2), // root directory
+ cpu_to_be32(0x0c0083c0), // |
+ cpu_to_be32(0x03001f11), // |
+ cpu_to_be32(0x81000004), // |
+ cpu_to_be32(0x17023901), // |
+ cpu_to_be32(0x81000009), // |
+ cpu_to_be32(0xd100000c), // v
+ cpu_to_be32(0x00064cb7), // text descriptor leaf (from root)
+ cpu_to_be32(0x00000000), // |
+ cpu_to_be32(0x00000000), // |
+ cpu_to_be32(0x4c696e75), // |
+ cpu_to_be32(0x78204669), // |
+ cpu_to_be32(0x72657769), // |
+ cpu_to_be32(0x72650000), // v
+ cpu_to_be32(0x0003ff1c), // text descriptor leaf (from root)
+ cpu_to_be32(0x00000000), // |
+ cpu_to_be32(0x00000000), // |
+ cpu_to_be32(0x4a756a75), // v
+ cpu_to_be32(0x0004227b), // unit directory (from root)
+ cpu_to_be32(0x1200a02d), // |
+ cpu_to_be32(0x13010001), // |
+ cpu_to_be32(0x17260827), // |
+ cpu_to_be32(0x81000001), // v
+ cpu_to_be32(0x0003f771), // text descriptor leaf (from unit)
+ cpu_to_be32(0x00000000), // |
+ cpu_to_be32(0x00000000), // |
+ cpu_to_be32(0x50756900), // v
+};
+
+// Following to 1394-based Digital Camera Specification Version 1.04 (Aug. 1996. 1394 Trading
+// Association)
+#define IIDC_COMMAND_REGS_BASE 0x00
+#define IIDC_VENDOR_NAME_LEAF 0x01
+#define IIDC_MODEL_NAME_LEAF 0x02
+
+static const u32 iidc_unit_directories_and_leafs[] = {
+ 0x00030000,
+ (CSR_SPECIFIER_ID << 24) | UNIT_SPEC_ID_1394TA,
+ (CSR_VERSION << 24) | UNIT_SW_VERSION_IIDC_0104,
+ ((CSR_DIRECTORY | CSR_DEPENDENT_INFO) << 24) | 0x00000001,
+ 0x00030000,
+ ((CSR_OFFSET | IIDC_COMMAND_REGS_BASE) << 24) | 0x00012345,
+ ((CSR_LEAF | IIDC_VENDOR_NAME_LEAF) << 24) | 0x00000002,
+ ((CSR_LEAF | IIDC_MODEL_NAME_LEAF) << 24) | 0x00000008,
+ 0x00060000, // Text leaf consists of below 6 quads.
+ 0x00000000,
+ 0x00000000,
+ 0x4c696e75, // Use the same name in root directory.
+ 0x78204669,
+ 0x72657769,
+ 0x72650000,
+ 0x00040000, // Text leaf consists of below 4 quads.
+ 0x00000000,
+ 0x00000000,
+ 0x43686566, // Chef Cat is a nick name when inventing IEEE 1394 itself.
+ 0x20436174,
+};
+
+static const __be32 config_rom_with_iidc_unit[] = {
+ cpu_to_be32(0x04046a3e), // bus info
+ cpu_to_be32(0x31333934), // |
+ cpu_to_be32(0xf000b243), // |
+ cpu_to_be32(0x01234567), // |
+ cpu_to_be32(0x89abcdef), // v
+ cpu_to_be32(0x0006a2d2), // root directory
+ cpu_to_be32(0x0c0083c0), // |
+ cpu_to_be32(0x03001f11), // |
+ cpu_to_be32(0x81000004), // |
+ cpu_to_be32(0x17023901), // |
+ cpu_to_be32(0x81000009), // |
+ cpu_to_be32(0xd100000c), // v
+ cpu_to_be32(0x00064cb7), // text descriptor leaf (from root)
+ cpu_to_be32(0x00000000), // |
+ cpu_to_be32(0x00000000), // |
+ cpu_to_be32(0x4c696e75), // |
+ cpu_to_be32(0x78204669), // |
+ cpu_to_be32(0x72657769), // |
+ cpu_to_be32(0x72650000), // v
+ cpu_to_be32(0x0003ff1c), // text descriptor leaf (from root)
+ cpu_to_be32(0x00000000), // |
+ cpu_to_be32(0x00000000), // |
+ cpu_to_be32(0x4a756a75), // v
+ cpu_to_be32(0x0003d7fe), // unit directory (from root)
+ cpu_to_be32(0x1200a02d), // |
+ cpu_to_be32(0x13000100), // |
+ cpu_to_be32(0xd4000001), // v
+ cpu_to_be32(0x0003ea57), // dependent directory (from unit directory)
+ cpu_to_be32(0x40012345), // |
+ cpu_to_be32(0x81000002), // |
+ cpu_to_be32(0x82000008), // v
+ cpu_to_be32(0x00064cb7), // text descriptor leaf (from dependent directory)
+ cpu_to_be32(0x00000000), // |
+ cpu_to_be32(0x00000000), // |
+ cpu_to_be32(0x4c696e75), // |
+ cpu_to_be32(0x78204669), // |
+ cpu_to_be32(0x72657769), // |
+ cpu_to_be32(0x72650000), // v
+ cpu_to_be32(0x0004a3e9), // text descriptor leaf (from dependent directory)
+ cpu_to_be32(0x00000000), // |
+ cpu_to_be32(0x00000000), // |
+ cpu_to_be32(0x43686566), // |
+ cpu_to_be32(0x20436174), // v
+};
+
+static const __be32 config_rom_with_avc_and_iidc_units[] = {
+ cpu_to_be32(0x040439c0), // bus info
+ cpu_to_be32(0x31333934), // |
+ cpu_to_be32(0xf000b253), // |
+ cpu_to_be32(0x01234567), // |
+ cpu_to_be32(0x89abcdef), // v
+ cpu_to_be32(0x0007073e), // root directory
+ cpu_to_be32(0x0c0083c0), // |
+ cpu_to_be32(0x03001f11), // |
+ cpu_to_be32(0x81000005), // |
+ cpu_to_be32(0x17023901), // |
+ cpu_to_be32(0x8100000a), // |
+ cpu_to_be32(0xd100000d), // |
+ cpu_to_be32(0xd1000015), // v
+ cpu_to_be32(0x00064cb7), // text descriptor leaf (from root)
+ cpu_to_be32(0x00000000), // |
+ cpu_to_be32(0x00000000), // |
+ cpu_to_be32(0x4c696e75), // |
+ cpu_to_be32(0x78204669), // |
+ cpu_to_be32(0x72657769), // |
+ cpu_to_be32(0x72650000), // v
+ cpu_to_be32(0x0003ff1c), // text descriptor leaf (from root)
+ cpu_to_be32(0x00000000), // |
+ cpu_to_be32(0x00000000), // |
+ cpu_to_be32(0x4a756a75), // v
+ cpu_to_be32(0x0004227b), // unit directory (from root)
+ cpu_to_be32(0x1200a02d), // |
+ cpu_to_be32(0x13010001), // |
+ cpu_to_be32(0x17260827), // |
+ cpu_to_be32(0x81000001), // v
+ cpu_to_be32(0x0003f771), // text descriptor leaf (from unit)
+ cpu_to_be32(0x00000000), // |
+ cpu_to_be32(0x00000000), // |
+ cpu_to_be32(0x50756900), // v
+ cpu_to_be32(0x0003d7fe), // unit directory (from root)
+ cpu_to_be32(0x1200a02d), // |
+ cpu_to_be32(0x13000100), // |
+ cpu_to_be32(0xd4000001), // v
+ cpu_to_be32(0x0003ea57), // dependent directory (from unit directory)
+ cpu_to_be32(0x40012345), // |
+ cpu_to_be32(0x81000002), // |
+ cpu_to_be32(0x82000008), // v
+ cpu_to_be32(0x00064cb7), // text descriptor leaf (from dependent directory)
+ cpu_to_be32(0x00000000), // |
+ cpu_to_be32(0x00000000), // |
+ cpu_to_be32(0x4c696e75), // |
+ cpu_to_be32(0x78204669), // |
+ cpu_to_be32(0x72657769), // |
+ cpu_to_be32(0x72650000), // v
+ cpu_to_be32(0x0004a3e9), // text descriptor leaf (from dependent directory)
+ cpu_to_be32(0x00000000), // |
+ cpu_to_be32(0x00000000), // |
+ cpu_to_be32(0x43686566), // |
+ cpu_to_be32(0x20436174), // v
+};
+
+static const struct generator_test_case {
+ const char *const name;
+ int config_rom_generation;
+ const __be32 *const expected;
+ unsigned int quadlet_length;
+} generator_test_cases[] = {
+ {
+ .name = "bare",
+ .config_rom_generation = 0,
+ .expected = config_rom_bare,
+ .quadlet_length = ARRAY_SIZE(config_rom_bare),
+ },
+ {
+ .name = "with_avc_unit",
+ .config_rom_generation = 1,
+ .expected = config_rom_with_avc_unit,
+ .quadlet_length = ARRAY_SIZE(config_rom_with_avc_unit),
+ },
+ {
+ .name = "with_iidc_unit",
+ .config_rom_generation = 2,
+ .expected = config_rom_with_iidc_unit,
+ .quadlet_length = ARRAY_SIZE(config_rom_with_iidc_unit),
+ },
+ {
+ .name = "with_avc_and_iidc_unit",
+ .config_rom_generation = 3,
+ .expected = config_rom_with_avc_and_iidc_units,
+ .quadlet_length = ARRAY_SIZE(config_rom_with_avc_and_iidc_units),
+ },
+};
+
+// Define generator_test_gen_params.
+KUNIT_ARRAY_PARAM_DESC(generator_test, generator_test_cases, name);
+
+struct state_data {
+ struct fw_card card;
+ __be32 config_rom[(CSR_CONFIG_ROM_END - CSR_CONFIG_ROM) / sizeof(__be32)];
+};
+
+static void test_config_rom_generator(struct kunit *test)
+{
+ // Use kernel stack since they should be mutable for doubly linked-list.
+ struct fw_descriptor avc_unit_entry = {
+ .length = ARRAY_SIZE(avc_unit_directory_and_leaf),
+ .key = (CSR_DIRECTORY | CSR_UNIT) << 24,
+ .data = avc_unit_directory_and_leaf,
+ };
+ struct fw_descriptor iidc_unit_entry = {
+ .length = ARRAY_SIZE(iidc_unit_directories_and_leafs),
+ .key = (CSR_DIRECTORY | CSR_UNIT) << 24,
+ .data = iidc_unit_directories_and_leafs,
+ };
+ const struct generator_test_case *test_case = test->param_value;
+ struct state_data *state = test->priv;
+ struct fw_card *card = &state->card;
+ __be32 *config_rom = state->config_rom;
+
+ card->config_rom_generation = test_case->config_rom_generation;
+ card->link_speed = SCODE_800;
+ card->max_receive = 11;
+ card->guid = 0x0123456789abcdefULL;
+
+ if (test_case->expected == config_rom_with_avc_unit ||
+ test_case->expected == config_rom_with_avc_and_iidc_units)
+ KUNIT_EXPECT_EQ(test, fw_core_add_descriptor(&avc_unit_entry), 0);
+
+ if (test_case->expected == config_rom_with_iidc_unit ||
+ test_case->expected == config_rom_with_avc_and_iidc_units)
+ KUNIT_EXPECT_EQ(test, fw_core_add_descriptor(&iidc_unit_entry), 0);
+
+ scoped_guard(mutex, &card_mutex) {
+ generate_config_rom(card, config_rom);
+ KUNIT_EXPECT_EQ(test, config_rom_length, test_case->quadlet_length);
+ }
+
+ KUNIT_EXPECT_MEMEQ(test, config_rom, test_case->expected,
+ sizeof(*test_case->expected) * test_case->quadlet_length);
+
+ if (test_case->expected == config_rom_with_iidc_unit ||
+ test_case->expected == config_rom_with_avc_and_iidc_units)
+ fw_core_remove_descriptor(&iidc_unit_entry);
+
+ if (test_case->expected == config_rom_with_avc_unit ||
+ test_case->expected == config_rom_with_avc_and_iidc_units)
+ fw_core_remove_descriptor(&avc_unit_entry);
+}
+
+static void add_descriptor_with_invalid_length(struct kunit *test)
+{
+ // Use kernel stack since they should be mutable for doubly linked-list.
+ struct fw_descriptor entry_with_invalid_length = {
+ .length = 257,
+ };
+
+ KUNIT_EXPECT_EQ(test, fw_core_add_descriptor(&entry_with_invalid_length), -EINVAL);
+}
+
+static void add_descriptor_with_invalid_data(struct kunit *test)
+{
+ // Use vendor directory defined in Annex A of Configuration ROM for AV/C Devices 1.0 (Dec.
+ // 2000, 1394 Trading Association, TA Document 1999027).
+ static const u32 invalid_vendor_directory[] = {
+ 0x00020000,
+ (CSR_MODEL << 24) | 0x00009402,
+ ((CSR_LEAF | CSR_DESCRIPTOR) << 24) | 0x00000001,
+ 0xffff0000, // The length should be 6, invalid.
+ 0x00000000,
+ 0x00000000,
+ 0x436f6e63,
+ 0x6174204e,
+ 0x6f746174,
+ 0x696f6e00,
+ };
+ // Use kernel stack since they should be mutable for doubly linked-list.
+ struct fw_descriptor entry_with_invalid_data = {
+ .length = ARRAY_SIZE(invalid_vendor_directory),
+ .key = (CSR_DIRECTORY | CSR_VENDOR) << 24,
+ .data = invalid_vendor_directory,
+ };
+
+ KUNIT_EXPECT_EQ(test, fw_core_add_descriptor(&entry_with_invalid_data), -EINVAL);
+}
+
+static void add_descriptor_beyond_upper_limit(struct kunit *test)
+{
+ // Use kernel stack since they should be mutable for doubly linked-list.
+ struct fw_descriptor entry_beyond_upper_limit = {
+ .length = 255,
+ .key = (CSR_DIRECTORY | CSR_UNIT) << 24,
+ .data = NULL,
+ };
+ u32 *data;
+
+ data = kunit_kzalloc(test, entry_beyond_upper_limit.length, GFP_KERNEL);
+ KUNIT_ASSERT_NOT_NULL(test, data);
+
+ data[0] = (entry_beyond_upper_limit.length - 1) << 16;
+ entry_beyond_upper_limit.data = data;
+ KUNIT_EXPECT_EQ(test, fw_core_add_descriptor(&entry_beyond_upper_limit), -EBUSY);
+
+ kunit_kfree(test, data);
+}
+
+static const struct fw_card_driver dummy_card_driver;
+
+static int config_rom_generator_test_init(struct kunit *test)
+{
+ struct state_data *state = kunit_kzalloc(test, sizeof(*state), GFP_KERNEL);
+
+ KUNIT_ASSERT_NOT_NULL(test, state);
+
+ struct device *dev = kunit_device_register(test, "dummy-device");
+
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, dev);
+
+ fw_card_initialize(&state->card, &dummy_card_driver, dev);
+
+ test->priv = state;
+
+ return 0;
+}
+
+static void config_rom_generator_test_exit(struct kunit *test)
+{
+ struct state_data *state = test->priv;
+
+ kunit_device_unregister(test, state->card.device);
+ kunit_kfree(test, state);
+}
+
+
+static struct kunit_case config_rom_generator_test_cases[] = {
+ KUNIT_CASE_PARAM(test_config_rom_generator, generator_test_gen_params),
+ KUNIT_CASE(add_descriptor_with_invalid_length),
+ KUNIT_CASE(add_descriptor_with_invalid_data),
+ KUNIT_CASE(add_descriptor_beyond_upper_limit),
+ {}
+};
+
+static struct kunit_suite config_rom_generator_test_suite = {
+ .name = "firewire-config-rom-generator",
+ .init = config_rom_generator_test_init,
+ .exit = config_rom_generator_test_exit,
+ .test_cases = config_rom_generator_test_cases,
+};
+kunit_test_suite(config_rom_generator_test_suite);
diff --git a/drivers/firewire/config-rom-parser-test.c b/drivers/firewire/config-rom-parser-test.c
new file mode 100644
index 000000000000..45f1ead326c5
--- /dev/null
+++ b/drivers/firewire/config-rom-parser-test.c
@@ -0,0 +1,355 @@
+// SPDX-License-Identifier: GPL-2.0-only
+//
+// config-rom-parser-test.c - An application of Kunit to test configuration ROM parser.
+//
+// Copyright (c) 2026 Takashi Sakamoto
+//
+// This file can not be built independently since it is intentionally included in core-device.c.
+
+#include <kunit/test.h>
+#include <kunit/static_stub.h>
+#include <kunit/device.h>
+
+static const u32 sony_dcr_trv310k_config_rom[] = {
+ 0x0404e552,
+ 0x31333934,
+ 0xe0648100,
+ 0x00008500,
+ 0x005eb597,
+ 0x0007cdd0,
+ 0x03000085,
+ 0x8100000d,
+ 0x17000002,
+ 0x81000010,
+ 0x0c0083c0,
+ 0xd8000002,
+ 0xd1000003,
+ 0x0001ce96,
+ 0xd1000001,
+ 0x0004bbee,
+ 0x1200a02d,
+ 0x13010001,
+ 0x17000002,
+ 0x81000006,
+ 0x00046dc8,
+ 0x00000000,
+ 0x00000000,
+ 0x43616e6f,
+ 0x6e000000,
+ 0x000621ee,
+ 0x00000000,
+ 0x00000000,
+ 0x4d563569,
+ 0x204d4300,
+ 0x00000000,
+ 0x00000000,
+};
+
+static const u32 canon_mv5i_mc_config_rom[] = {
+ 0x0404e552,
+ 0x31333934,
+ 0xe0648100,
+ 0x00008500,
+ 0x005eb597,
+ 0x0007cdd0,
+ 0x03000085,
+ 0x8100000d,
+ 0x17000002,
+ 0x81000010,
+ 0x0c0083c0,
+ 0xd8000002,
+ 0xd1000003,
+ 0x0001ce96,
+ 0xd1000001,
+ 0x0004bbee,
+ 0x1200a02d,
+ 0x13010001,
+ 0x17000002,
+ 0x81000006,
+ 0x00046dc8,
+ 0x00000000,
+ 0x00000000,
+ 0x43616e6f,
+ 0x6e000000,
+ 0x000621ee,
+ 0x00000000,
+ 0x00000000,
+ 0x4d563569,
+ 0x204d4300,
+ 0x00000000,
+ 0x00000000,
+};
+
+static const u32 motu_audioexpress_config_rom[] = {
+ 0x0410a756,
+ 0x31333934,
+ 0x20ff7000,
+ 0x0001f200,
+ 0x000a8a7b,
+ 0x0004ef04,
+ 0x030001f2,
+ 0x0c0083c0,
+ 0xd1000002,
+ 0x8d000005,
+ 0x00031680,
+ 0x120001f2,
+ 0x13000033,
+ 0x17104800,
+ 0x00025ef3,
+ 0x0001f200,
+ 0x000a8a7b,
+};
+
+static const u32 tascam_fw1884_config_rom[] = {
+ 0x040f23c0,
+ 0x31333934,
+ 0x20ff7002,
+ 0x00022eff,
+ 0xfe800000,
+ 0x0004bccc,
+ 0x0300022e,
+ 0x0c0083c0,
+ 0x8d000006,
+ 0xd1000001,
+ 0x000347f5,
+ 0x1200022e,
+ 0x13800000,
+ 0xd4000004,
+ 0x000289aa,
+ 0x00022eff,
+ 0xfe800000,
+ 0x0002ae47,
+ 0x81000002,
+ 0x82000006,
+ 0x0004a79e,
+ 0x00000000,
+ 0x00000000,
+ 0x54415343,
+ 0x414d0000,
+ 0x00045443,
+ 0x00000000,
+ 0x00000000,
+ 0x46572d31,
+ 0x38383400,
+};
+
+static const struct parser_test_case {
+ const char *name;
+ const u32 *quadlets;
+ size_t quadlet_length;
+ int phy_speed_in_self_id;
+ int expected_speed;
+ int expected_quirk;
+ unsigned int expected_max_rec;
+ bool expected_cmc;
+ bool expected_irmc;
+} parser_test_cases[] = {
+ {
+ .name = "detect_irm_is_1394_1995_only_quirk",
+ .quadlets = sony_dcr_trv310k_config_rom,
+ .quadlet_length = ARRAY_SIZE(sony_dcr_trv310k_config_rom),
+ .phy_speed_in_self_id = SCODE_100,
+ .expected_speed = SCODE_100,
+ .expected_quirk = FW_DEVICE_QUIRK_IRM_IS_1394_1995_ONLY,
+ .expected_max_rec = 8,
+ .expected_cmc = true,
+ .expected_irmc = true,
+ },
+ {
+ .name = "detect_irm_ignores_bus_manager_quirk",
+ .quadlets = canon_mv5i_mc_config_rom,
+ .quadlet_length = ARRAY_SIZE(canon_mv5i_mc_config_rom),
+ .phy_speed_in_self_id = SCODE_100,
+ .expected_speed = SCODE_100,
+ .expected_quirk = FW_DEVICE_QUIRK_IRM_IGNORES_BUS_MANAGER,
+ .expected_max_rec = 8,
+ .expected_cmc = true,
+ .expected_irmc = true,
+ },
+ {
+ .name = "detect_ack_packet_with_invalid_pending_code_quirk",
+ .quadlets = motu_audioexpress_config_rom,
+ .quadlet_length = ARRAY_SIZE(motu_audioexpress_config_rom),
+ .phy_speed_in_self_id = SCODE_400,
+ .expected_speed = SCODE_400,
+ .expected_quirk = FW_DEVICE_QUIRK_ACK_PACKET_WITH_INVALID_PENDING_CODE,
+ .expected_max_rec = 7,
+ .expected_cmc = false,
+ .expected_irmc = false,
+ },
+ {
+ .name = "detect_unstable_at_s400_quirk",
+ .quadlets = tascam_fw1884_config_rom,
+ .quadlet_length = ARRAY_SIZE(tascam_fw1884_config_rom),
+ .phy_speed_in_self_id = SCODE_400,
+ .expected_speed = SCODE_200,
+ .expected_quirk = FW_DEVICE_QUIRK_UNSTABLE_AT_S400,
+ .expected_max_rec = 7,
+ .expected_cmc = false,
+ .expected_irmc = false,
+ },
+};
+
+// Define parser_test_gen_params.
+KUNIT_ARRAY_PARAM_DESC(parser_test, parser_test_cases, name);
+
+static int stub_run_transaction_regular(struct fw_card *card, int tcode, int destination_id,
+ int generation, int speed, unsigned long long offset,
+ void *payload, size_t length)
+{
+ struct kunit *test = kunit_get_current_test();
+ const struct parser_test_case *param = test->param_value;
+
+ KUNIT_ASSERT_GE(test, offset, CSR_REGISTER_BASE | CSR_CONFIG_ROM);
+ KUNIT_ASSERT_LT(test, offset, CSR_REGISTER_BASE | CSR_CONFIG_ROM_END);
+ KUNIT_ASSERT_NOT_NULL(test, payload);
+ KUNIT_ASSERT_EQ(test, length, 4);
+
+ unsigned int index = (offset - (CSR_REGISTER_BASE | CSR_CONFIG_ROM)) / sizeof(u32);
+ __be32 *quadlet = payload;
+
+ KUNIT_EXPECT_LE(test, speed, param->expected_speed);
+ KUNIT_EXPECT_LT(test, index, param->quadlet_length);
+
+ *quadlet = cpu_to_be32(param->quadlets[index]);
+
+ return RCODE_COMPLETE;
+}
+
+static void test_parser_with_regular_cases(struct kunit *test)
+{
+ struct fw_device *device = test->priv;
+ const struct parser_test_case *param = test->param_value;
+
+ kunit_activate_static_stub(test, fw_run_transaction, stub_run_transaction_regular);
+
+ device->card->link_speed = SCODE_BETA;
+ device->node->max_speed = param->phy_speed_in_self_id;
+
+ KUNIT_EXPECT_EQ(test, read_config_rom(device, 0), RCODE_COMPLETE);
+
+ KUNIT_EXPECT_EQ(test, device->config_rom_length, param->quadlet_length);
+ KUNIT_EXPECT_MEMEQ(test, device->config_rom, param->quadlets, param->quadlet_length);
+
+ KUNIT_EXPECT_TRUE(test, device->quirks & param->expected_quirk);
+ KUNIT_EXPECT_EQ(test, device->max_speed, param->expected_speed);
+ KUNIT_EXPECT_EQ(test, (unsigned int)device->max_rec, param->expected_max_rec);
+ KUNIT_EXPECT_EQ(test, (bool)device->cmc, param->expected_cmc);
+ KUNIT_EXPECT_EQ(test, (bool)device->irmc, param->expected_irmc);
+
+ kunit_deactivate_static_stub(test, fw_run_transaction);
+}
+
+static int stub_run_transaction_malformed(struct fw_card *card, int tcode, int destination_id,
+ int generation, int speed, unsigned long long offset,
+ void *payload, size_t length)
+{
+ static const u32 config_rom_first_part[] = {
+ 0x04000000,
+ 0x31333934,
+ 0x00008002,
+ 0x00000000,
+ 0x00000000,
+ 0x00030000,
+ (CSR_VENDOR << 24) | 0x00123456, // Regular entry.
+ ((CSR_LEAF | CSR_DESCRIPTOR) << 24) | 0x000000f9, // Beyond the upper limit.
+ ((CSR_DIRECTORY | CSR_UNIT) << 24) | 0x00000001,
+ 0xffff0000, // Over the upper limit.
+ };
+ struct kunit *test = kunit_get_current_test();
+
+ KUNIT_ASSERT_GE(test, offset, CSR_REGISTER_BASE | CSR_CONFIG_ROM);
+ KUNIT_ASSERT_LT(test, offset, CSR_REGISTER_BASE | CSR_CONFIG_ROM_END);
+ KUNIT_ASSERT_NOT_NULL(test, payload);
+ KUNIT_ASSERT_EQ(test, length, 4);
+
+ unsigned int index = (offset - (CSR_REGISTER_BASE | CSR_CONFIG_ROM)) / sizeof(u32);
+ __be32 *quadlet = payload;
+
+ if (index < ARRAY_SIZE(config_rom_first_part))
+ *quadlet = cpu_to_be32(config_rom_first_part[index]);
+ else
+ *quadlet = 0;
+
+ return RCODE_COMPLETE;
+}
+
+static void test_parser_with_overflowed_case(struct kunit *test)
+{
+ static const u32 corrected_config_rom[] = {
+ 0x04000000,
+ 0x31333934,
+ 0x00008002,
+ 0x00000000,
+ 0x00000000,
+ 0x00030000,
+ 0x03123456,
+ 0x00000000, // Sanitized.
+ 0xd1000001,
+ 0x00000000, // Sanitized.
+ };
+ struct fw_device *device = test->priv;
+
+ kunit_activate_static_stub(test, fw_run_transaction, stub_run_transaction_malformed);
+
+ device->card->link_speed = SCODE_BETA;
+
+ KUNIT_EXPECT_EQ(test, read_config_rom(device, 0), RCODE_COMPLETE);
+
+ KUNIT_EXPECT_EQ(test, device->config_rom_length, ARRAY_SIZE(corrected_config_rom));
+ KUNIT_EXPECT_MEMEQ(test, device->config_rom, corrected_config_rom,
+ sizeof(corrected_config_rom));
+
+ kunit_deactivate_static_stub(test, fw_run_transaction);
+}
+
+static const struct fw_card_driver dummy_card_driver;
+
+static int config_rom_parser_test_init(struct kunit *test)
+{
+ struct fw_device *device;
+ struct device *dev;
+
+ device = kunit_kzalloc(test, sizeof(*device), GFP_KERNEL);
+ KUNIT_ASSERT_NOT_NULL(test, device);
+
+ device->node = kunit_kzalloc(test, sizeof(*device->node), GFP_KERNEL);
+ KUNIT_ASSERT_NOT_NULL(test, device->node);
+ kref_init(&device->node->kref);
+
+ device->card = kunit_kzalloc(test, sizeof(*device->card), GFP_KERNEL);
+ KUNIT_ASSERT_NOT_NULL(test, device->card);
+
+ dev = kunit_device_register(test, "dummy-device");
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, dev);
+
+ fw_card_initialize(device->card, &dummy_card_driver, dev);
+
+ test->priv = device;
+
+ return 0;
+}
+
+static void config_rom_parser_test_exit(struct kunit *test)
+{
+ struct fw_device *device = test->priv;
+
+ kunit_device_unregister(test, device->card->device);
+ kunit_kfree(test, device->card);
+ kunit_kfree(test, device->node);
+ kunit_kfree(test, device);
+}
+
+static struct kunit_case config_rom_parser_test_cases[] = {
+ KUNIT_CASE_PARAM(test_parser_with_regular_cases, parser_test_gen_params),
+ KUNIT_CASE(test_parser_with_overflowed_case),
+ {}
+};
+
+static struct kunit_suite config_rom_parser_test_suite = {
+ .name = "firewire-config-rom-parser",
+ .init = config_rom_parser_test_init,
+ .exit = config_rom_parser_test_exit,
+ .test_cases = config_rom_parser_test_cases,
+};
+kunit_test_suite(config_rom_parser_test_suite);
diff --git a/drivers/firewire/core-card.c b/drivers/firewire/core-card.c
index 94992791f02e..b35e8901cc4b 100644
--- a/drivers/firewire/core-card.c
+++ b/drivers/firewire/core-card.c
@@ -153,8 +153,8 @@ static void update_config_roms(void)
list_for_each_entry (card, &card_list, link) {
generate_config_rom(card, tmp_config_rom);
- card->driver->set_config_rom(card, tmp_config_rom,
- config_rom_length);
+ smp_load_acquire(&card->driver)->set_config_rom(card, tmp_config_rom,
+ config_rom_length);
}
}
@@ -231,7 +231,7 @@ static int reset_bus(struct fw_card *card, bool short_reset)
trace_bus_reset_initiate(card->index, card->generation, short_reset);
- return card->driver->update_phy_reg(card, reg, 0, bit);
+ return smp_load_acquire(&card->driver)->update_phy_reg(card, reg, 0, bit);
}
void fw_schedule_bus_reset(struct fw_card *card, bool delayed, bool short_reset)
@@ -643,11 +643,12 @@ int fw_card_add(struct fw_card *card, u32 max_receive, u32 link_speed, u64 guid,
// * == WQ_FREEZABLE The target device would not be available when being freezed.
// * == WQ_HIGHPRI High priority to process semi-realtime timestamped data.
// * == WQ_SYSFS Parameters are available via sysfs.
- // * max_active == 4 A hardIRQ could notify events for a pair of requests and
- // response AR/AT contexts.
+ // * max_active == 4 + 2 A hardIRQ could notify events for a pair of requests and
+ // response AR/AT contexts. Additional 2 capacity are for the
+ // internal handling of local AT request and response packets.
async_wq = alloc_workqueue("firewire-async-card%u",
WQ_UNBOUND | WQ_MEM_RECLAIM | WQ_FREEZABLE | WQ_HIGHPRI | WQ_SYSFS,
- 4, card->index);
+ 6, card->index);
if (!async_wq)
return -ENOMEM;
@@ -659,7 +660,8 @@ int fw_card_add(struct fw_card *card, u32 max_receive, u32 link_speed, u64 guid,
scoped_guard(mutex, &card_mutex) {
generate_config_rom(card, tmp_config_rom);
- ret = card->driver->enable(card, tmp_config_rom, config_rom_length);
+ ret = smp_load_acquire(&card->driver)->enable(card, tmp_config_rom,
+ config_rom_length);
if (ret < 0) {
card->isoc_wq = NULL;
card->async_wq = NULL;
@@ -687,6 +689,15 @@ EXPORT_SYMBOL(fw_card_add);
* .set_config_rom() is never called since the card is taken out of card_list
* before switching to the dummy driver.
*/
+struct fw_card_driver_closer {
+ const struct fw_card_driver driver;
+
+ // For shutdown operations.
+ struct fw_card *card;
+ struct list_head packet_list;
+ spinlock_t packet_list_lock;
+ struct work_struct work;
+};
static int dummy_read_phy_reg(struct fw_card *card, int address)
{
@@ -699,14 +710,31 @@ static int dummy_update_phy_reg(struct fw_card *card, int address,
return -ENODEV;
}
-static void dummy_send_request(struct fw_card *card, struct fw_packet *packet)
+static void dummy_packet_callback_work(struct work_struct *work)
{
- packet->callback(packet, card, RCODE_CANCELLED);
+ struct fw_card_driver_closer *closer = container_of(work, typeof(*closer), work);
+ unsigned long flags;
+ struct fw_packet *p;
+
+ spin_lock_irqsave(&closer->packet_list_lock, flags);
+ while ((p = list_first_entry_or_null(&closer->packet_list, typeof(*p), link_for_local))) {
+ list_del(&p->link_for_local);
+ spin_unlock_irqrestore(&closer->packet_list_lock, flags);
+
+ p->callback(p, closer->card, RCODE_CANCELLED);
+
+ spin_lock_irqsave(&closer->packet_list_lock, flags);
+ }
+ spin_unlock_irqrestore(&closer->packet_list_lock, flags);
}
-static void dummy_send_response(struct fw_card *card, struct fw_packet *packet)
+static void dummy_schedule_packet_callback(struct fw_card *card, struct fw_packet *packet)
{
- packet->callback(packet, card, RCODE_CANCELLED);
+ struct fw_card_driver_closer *closer = container_of(card->driver, typeof(*closer), driver);
+
+ scoped_guard(spinlock_irqsave, &closer->packet_list_lock)
+ list_add_tail(&packet->link_for_local, &closer->packet_list);
+ queue_work(card->async_wq, &closer->work);
}
static int dummy_cancel_packet(struct fw_card *card, struct fw_packet *packet)
@@ -761,23 +789,6 @@ static int dummy_flush_iso_completions(struct fw_iso_context *ctx)
return -ENODEV;
}
-static const struct fw_card_driver dummy_driver_template = {
- .read_phy_reg = dummy_read_phy_reg,
- .update_phy_reg = dummy_update_phy_reg,
- .send_request = dummy_send_request,
- .send_response = dummy_send_response,
- .cancel_packet = dummy_cancel_packet,
- .enable_phys_dma = dummy_enable_phys_dma,
- .read_csr = dummy_read_csr,
- .write_csr = dummy_write_csr,
- .allocate_iso_context = dummy_allocate_iso_context,
- .start_iso = dummy_start_iso,
- .set_iso_channels = dummy_set_iso_channels,
- .queue_iso = dummy_queue_iso,
- .flush_queue_iso = dummy_flush_queue_iso,
- .flush_iso_completions = dummy_flush_iso_completions,
-};
-