diff options
Diffstat (limited to 'drivers/firewire')
| -rw-r--r-- | drivers/firewire/.kunitconfig | 1 | ||||
| -rw-r--r-- | drivers/firewire/Kconfig | 16 | ||||
| -rw-r--r-- | drivers/firewire/Makefile | 2 | ||||
| -rw-r--r-- | drivers/firewire/config-rom-generator-test.c | 407 | ||||
| -rw-r--r-- | drivers/firewire/config-rom-parser-test.c | 355 | ||||
| -rw-r--r-- | drivers/firewire/core-card.c | 124 | ||||
| -rw-r--r-- | drivers/firewire/core-cdev.c | 301 | ||||
| -rw-r--r-- | drivers/firewire/core-device.c | 12 | ||||
| -rw-r--r-- | drivers/firewire/core-iso.c | 32 | ||||
| -rw-r--r-- | drivers/firewire/core-topology.c | 3 | ||||
| -rw-r--r-- | drivers/firewire/core-transaction.c | 414 | ||||
| -rw-r--r-- | drivers/firewire/device-attribute-test.c | 4 | ||||
| -rw-r--r-- | drivers/firewire/ohci-serdes-test.c | 4 | ||||
| -rw-r--r-- | drivers/firewire/ohci.c | 312 | ||||
| -rw-r--r-- | drivers/firewire/ohci.h | 2 | ||||
| -rw-r--r-- | drivers/firewire/uapi-test.c | 20 |
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, -}; - |
