Commit 141d9c6e authored by Linus Torvalds's avatar Linus Torvalds

Merge tag 'firewire-updates-6.8' of...

Merge tag 'firewire-updates-6.8' of git://git.kernel.org/pub/scm/linux/kernel/git/ieee1394/linux1394

Pull firewire updates from Takashi Sakamoto:
 "This includes the series of changes related to device attributes, as
  well as slight code refactoring.

  Some old devices are recognized to have legacy layout of configuration
  ROM. They have an inconvenience that FireWire subsystem adds no
  attributes for vendor information to corresponding devices. The main
  purpose of this update is to rectify the inconvenience.

  We have a slight concern about regression. The update changes the
  value of modalias for the unit devices by populating its model field,
  which was previously left as zero in the case. I've assessed the
  potential impact of this change and anticipate it to have minimal
  concern for both the kernel and user lands. The change is enough
  acceptable"

* tag 'firewire-updates-6.8' of git://git.kernel.org/pub/scm/linux/kernel/git/ieee1394/linux1394:
  firewire: core: fill model field in modalias of unit device for legacy layout of configuration ROM
  firewire: core: detect model name for legacy layout of configuration ROM
  firewire: core: detect numeric model identifier for legacy layout of configuration ROM
  firewire: test: add test of device attributes for legacy AV/C device
  firewire: test: add test of device attributes for simple AV/C device
  firewire: test: add KUnit test for device attributes
  firewire: core: replace magic number with macro
  firewire: core: adds constant qualifier for local helper functions
  firewire: make fw_bus_type const
parents 645f910f 986c20bb
......@@ -2,3 +2,4 @@ CONFIG_KUNIT=y
CONFIG_PCI=y
CONFIG_FIREWIRE=y
CONFIG_FIREWIRE_KUNIT_UAPI_TEST=y
CONFIG_FIREWIRE_KUNIT_DEVICE_ATTRIBUTE_TEST=y
......@@ -34,6 +34,22 @@ config FIREWIRE_KUNIT_UAPI_TEST
For more information on KUnit and unit tests in general, refer
to the KUnit documentation in Documentation/dev-tools/kunit/.
config FIREWIRE_KUNIT_DEVICE_ATTRIBUTE_TEST
tristate "KUnit tests for device attributes" if !KUNIT_ALL_TESTS
depends on FIREWIRE && KUNIT
default KUNIT_ALL_TESTS
help
This builds the KUnit tests for device attribute for node and
unit.
KUnit tests run during boot and output the results to the debug
log in TAP format (https://testanything.org/). Only useful for
kernel devs running KUnit test harness and are not for inclusion
into a production build.
For more information on KUnit and unit tests in general, refer
to the KUnit documentation in Documentation/dev-tools/kunit/.
config FIREWIRE_OHCI
tristate "OHCI-1394 controllers"
depends on PCI && FIREWIRE && MMU
......
......@@ -31,6 +31,8 @@
#include "core.h"
#define ROOT_DIR_OFFSET 5
void fw_csr_iterator_init(struct fw_csr_iterator *ci, const u32 *p)
{
ci->p = p + 1;
......@@ -47,6 +49,22 @@ int fw_csr_iterator_next(struct fw_csr_iterator *ci, int *key, int *value)
}
EXPORT_SYMBOL(fw_csr_iterator_next);
static const u32 *search_directory(const u32 *directory, int search_key)
{
struct fw_csr_iterator ci;
int key, value;
search_key |= CSR_DIRECTORY;
fw_csr_iterator_init(&ci, directory);
while (fw_csr_iterator_next(&ci, &key, &value)) {
if (key == search_key)
return ci.p - 1 + value;
}
return NULL;
}
static const u32 *search_leaf(const u32 *directory, int search_key)
{
struct fw_csr_iterator ci;
......@@ -135,8 +153,25 @@ static void get_ids(const u32 *directory, int *id)
static void get_modalias_ids(const struct fw_unit *unit, int *id)
{
get_ids(&fw_parent_device(unit)->config_rom[5], id);
get_ids(unit->directory, id);
const u32 *root_directory = &fw_parent_device(unit)->config_rom[ROOT_DIR_OFFSET];
const u32 *directories[] = {NULL, NULL, NULL};
const u32 *vendor_directory;
int i;
directories[0] = root_directory;
// Legacy layout of configuration ROM described in Annex 1 of 'Configuration ROM for AV/C
// Devices 1.0 (December 12, 2000, 1394 Trading Association, TA Document 1999027)'.
vendor_directory = search_directory(root_directory, CSR_VENDOR);
if (!vendor_directory) {
directories[1] = unit->directory;
} else {
directories[1] = vendor_directory;
directories[2] = unit->directory;
}
for (i = 0; i < ARRAY_SIZE(directories) && !!directories[i]; ++i)
get_ids(directories[i], id);
}
static bool match_ids(const struct ieee1394_device_id *id_table, int *id)
......@@ -171,7 +206,7 @@ static const struct ieee1394_device_id *unit_match(struct device *dev,
return NULL;
}
static bool is_fw_unit(struct device *dev);
static bool is_fw_unit(const struct device *dev);
static int fw_unit_match(struct device *dev, struct device_driver *drv)
{
......@@ -219,7 +254,7 @@ static int fw_unit_uevent(const struct device *dev, struct kobj_uevent_env *env)
return 0;
}
struct bus_type fw_bus_type = {
const struct bus_type fw_bus_type = {
.name = "firewire",
.match = fw_unit_match,
.probe = fw_unit_probe,
......@@ -251,27 +286,44 @@ static ssize_t show_immediate(struct device *dev,
struct config_rom_attribute *attr =
container_of(dattr, struct config_rom_attribute, attr);
struct fw_csr_iterator ci;
const u32 *dir;
int key, value, ret = -ENOENT;
const u32 *directories[] = {NULL, NULL};
int i, value = -1;
down_read(&fw_device_rwsem);
if (is_fw_unit(dev))
dir = fw_unit(dev)->directory;
else
dir = fw_device(dev)->config_rom + 5;
if (is_fw_unit(dev)) {
directories[0] = fw_unit(dev)->directory;
} else {
const u32 *root_directory = fw_device(dev)->config_rom + ROOT_DIR_OFFSET;
const u32 *vendor_directory = search_directory(root_directory, CSR_VENDOR);
if (!vendor_directory) {
directories[0] = root_directory;
} else {
// Legacy layout of configuration ROM described in Annex 1 of
// 'Configuration ROM for AV/C Devices 1.0 (December 12, 2000, 1394 Trading
// Association, TA Document 1999027)'.
directories[0] = vendor_directory;
directories[1] = root_directory;
}
}
fw_csr_iterator_init(&ci, dir);
while (fw_csr_iterator_next(&ci, &key, &value))
if (attr->key == key) {
ret = snprintf(buf, buf ? PAGE_SIZE : 0,
"0x%06x\n", value);
break;
for (i = 0; i < ARRAY_SIZE(directories) && !!directories[i]; ++i) {
int key, val;
fw_csr_iterator_init(&ci, directories[i]);
while (fw_csr_iterator_next(&ci, &key, &val)) {
if (attr->key == key)
value = val;
}
}
up_read(&fw_device_rwsem);
return ret;
if (value < 0)
return -ENOENT;
return snprintf(buf, buf ? PAGE_SIZE : 0, "0x%06x\n", value);
}
#define IMMEDIATE_ATTR(name, key) \
......@@ -282,17 +334,29 @@ static ssize_t show_text_leaf(struct device *dev,
{
struct config_rom_attribute *attr =
container_of(dattr, struct config_rom_attribute, attr);
const u32 *dir;
const u32 *directories[] = {NULL, NULL};
size_t bufsize;
char dummy_buf[2];
int ret;
int i, ret = -ENOENT;
down_read(&fw_device_rwsem);
if (is_fw_unit(dev))
dir = fw_unit(dev)->directory;
else
dir = fw_device(dev)->config_rom + 5;
if (is_fw_unit(dev)) {
directories[0] = fw_unit(dev)->directory;
} else {
const u32 *root_directory = fw_device(dev)->config_rom + ROOT_DIR_OFFSET;
const u32 *vendor_directory = search_directory(root_directory, CSR_VENDOR);
if (!vendor_directory) {
directories[0] = root_directory;
} else {
// Legacy layout of configuration ROM described in Annex 1 of
// 'Configuration ROM for AV/C Devices 1.0 (December 12, 2000, 1394
// Trading Association, TA Document 1999027)'.
directories[0] = root_directory;
directories[1] = vendor_directory;
}
}
if (buf) {
bufsize = PAGE_SIZE - 1;
......@@ -301,7 +365,12 @@ static ssize_t show_text_leaf(struct device *dev,
bufsize = 1;
}
ret = fw_csr_string(dir, attr->key, buf, bufsize);
for (i = 0; i < ARRAY_SIZE(directories) && !!directories[i]; ++i) {
int result = fw_csr_string(directories[i], attr->key, buf, bufsize);
// Detected.
if (result >= 0)
ret = result;
}
if (ret >= 0) {
/* Strip trailing whitespace and add newline. */
......@@ -446,7 +515,7 @@ static ssize_t units_show(struct device *dev,
int key, value, i = 0;
down_read(&fw_device_rwsem);
fw_csr_iterator_init(&ci, &device->config_rom[5]);
fw_csr_iterator_init(&ci, &device->config_rom[ROOT_DIR_OFFSET]);
while (fw_csr_iterator_next(&ci, &key, &value)) {
if (key != (CSR_UNIT | CSR_DIRECTORY))
continue;
......@@ -679,7 +748,7 @@ static struct device_type fw_unit_type = {
.release = fw_unit_release,
};
static bool is_fw_unit(struct device *dev)
static bool is_fw_unit(const struct device *dev)
{
return dev->type == &fw_unit_type;
}
......@@ -691,7 +760,7 @@ static void create_units(struct fw_device *device)
int key, value, i;
i = 0;
fw_csr_iterator_init(&ci, &device->config_rom[5]);
fw_csr_iterator_init(&ci, &device->config_rom[ROOT_DIR_OFFSET]);
while (fw_csr_iterator_next(&ci, &key, &value)) {
if (key != (CSR_UNIT | CSR_DIRECTORY))
continue;
......@@ -835,7 +904,7 @@ static struct device_type fw_device_type = {
.release = fw_device_release,
};
static bool is_fw_device(struct device *dev)
static bool is_fw_device(const struct device *dev)
{
return dev->type == &fw_device_type;
}
......@@ -1308,3 +1377,7 @@ void fw_node_event(struct fw_card *card, struct fw_node *node, int event)
break;
}
}
#ifdef CONFIG_FIREWIRE_KUNIT_DEVICE_ATTRIBUTE_TEST
#include "device-attribute-test.c"
#endif
// SPDX-License-Identifier: GPL-2.0-only
//
// device-attribute-test.c - An application of Kunit to test implementation for device attributes.
//
// Copyright (c) 2023 Takashi Sakamoto
//
// This file can not be built independently since it is intentionally included in core-device.c.
#include <kunit/test.h>
// Configuration ROM for AV/C Devices 1.0 (Dec. 12, 2000, 1394 Trading Association)
// Annex C:Configuration ROM example(informative)
// C.1 Simple AV/C device
//
// Copied from the documentation.
static const u32 simple_avc_config_rom[] = {
0x0404eabf,
0x31333934,
0xe0646102,
0xffffffff,
0xffffffff,
0x00063287, // root directory.
0x03ffffff,
0x8100000a,
0x17ffffff,
0x8100000e,
0x0c0083c0,
0xd1000001,
0x0004442d, // unit 0 directory.
0x1200a02d,
0x13010001,
0x17ffffff,
0x81000007,
0x0005c915, // leaf for textual descriptor.
0x00000000,
0x00000000,
0x56656e64,
0x6f72204e,
0x616d6500,
0x00057f16, // leaf for textual descriptor.
0x00000000,
0x00000000,
0x4d6f6465,
0x6c204e61,
0x6d650000,
};
// Ibid.
// Annex A:Consideration for configuration ROM reader design (informative)
// A.1 Vendor directory
//
// Written by hand.
static const u32 legacy_avc_config_rom[] = {
0x04199fe7,
0x31333934,
0xe0644000,
0x00112233,
0x44556677,
0x0005dace, // root directory.
0x03012345,
0x0c0083c0,
0x8d000009,
0xd1000002,
0xc3000004,
0x0002e107, // unit 0 directory.
0x12abcdef,
0x13543210,
0x0002cb73, // vendor directory.
0x17fedcba,
0x81000004,
0x00026dc1, // leaf for EUI-64.
0x00112233,
0x44556677,
0x00050e84, // leaf for textual descriptor.
0x00000000,
0x00000000,
0x41424344,
0x45464748,
0x494a0000,
};
static void device_attr_simple_avc(struct kunit *test)
{
static const struct fw_device node = {
.device = {
.type = &fw_device_type,
},
.config_rom = simple_avc_config_rom,
.config_rom_length = sizeof(simple_avc_config_rom),
};
static const struct fw_unit unit0 = {
.device = {
.type = &fw_unit_type,
.parent = (struct device *)&node.device,
},
.directory = &simple_avc_config_rom[12],
};
struct device *node_dev = (struct device *)&node.device;
struct device *unit0_dev = (struct device *)&unit0.device;
static const int unit0_expected_ids[] = {0x00ffffff, 0x00ffffff, 0x0000a02d, 0x00010001};
char *buf = kunit_kzalloc(test, PAGE_SIZE, GFP_KERNEL);
int ids[4] = {0, 0, 0, 0};
// Ensure associations for node and unit devices.
KUNIT_ASSERT_TRUE(test, is_fw_device(node_dev));
KUNIT_ASSERT_FALSE(test, is_fw_unit(node_dev));
KUNIT_ASSERT_PTR_EQ(test, fw_device(node_dev), &node);
KUNIT_ASSERT_FALSE(test, is_fw_device(unit0_dev));
KUNIT_ASSERT_TRUE(test, is_fw_unit(unit0_dev));
KUNIT_ASSERT_PTR_EQ(test, fw_parent_device((&unit0)), &node);
KUNIT_ASSERT_PTR_EQ(test, fw_unit(unit0_dev), &unit0);
// For entries in root directory.
// Vendor immediate entry is found.
KUNIT_EXPECT_GT(test, show_immediate(node_dev, &config_rom_attributes[0].attr, buf), 0);
KUNIT_EXPECT_STREQ(test, buf, "0xffffff\n");
// Model immediate entry is found.
KUNIT_EXPECT_GT(test, show_immediate(node_dev, &config_rom_attributes[4].attr, buf), 0);
KUNIT_EXPECT_STREQ(test, buf, "0xffffff\n");
// Descriptor leaf entry for vendor is found.
KUNIT_EXPECT_GT(test, show_text_leaf(node_dev, &config_rom_attributes[5].attr, buf), 0);
KUNIT_EXPECT_STREQ(test, buf, "Vendor Name\n");
// Descriptor leaf entry for model is found.
KUNIT_EXPECT_GT(test, show_text_leaf(node_dev, &config_rom_attributes[6].attr, buf), 0);
KUNIT_EXPECT_STREQ(test, buf, "Model Name\n");
// For entries in unit 0 directory.
// Vendor immediate entry is not found.
KUNIT_EXPECT_LT(test, show_immediate(unit0_dev, &config_rom_attributes[0].attr, buf), 0);
// Model immediate entry is found.
KUNIT_EXPECT_GT(test, show_immediate(unit0_dev, &config_rom_attributes[4].attr, buf), 0);
KUNIT_EXPECT_STREQ(test, buf, "0xffffff\n");
// Descriptor leaf entry for vendor is not found.
KUNIT_EXPECT_LT(test, show_text_leaf(unit0_dev, &config_rom_attributes[5].attr, buf), 0);
// Descriptor leaf entry for model is found.
KUNIT_EXPECT_GT(test, show_text_leaf(unit0_dev, &config_rom_attributes[6].attr, buf), 0);
KUNIT_EXPECT_STREQ(test, buf, "Model Name\n");
// Specifier_ID immediate entry is found.
KUNIT_EXPECT_GT(test, show_immediate(unit0_dev, &config_rom_attributes[2].attr, buf), 0);
KUNIT_EXPECT_STREQ(test, buf, "0x00a02d\n");
// Version immediate entry is found.
KUNIT_EXPECT_GT(test, show_immediate(unit0_dev, &config_rom_attributes[3].attr, buf), 0);
KUNIT_EXPECT_STREQ(test, buf, "0x010001\n");
kunit_kfree(test, buf);
get_modalias_ids(&unit0, ids);
KUNIT_EXPECT_MEMEQ(test, ids, unit0_expected_ids, sizeof(ids));
}
static void device_attr_legacy_avc(struct kunit *test)
{
static const struct fw_device node = {
.device = {
.type = &fw_device_type,
},
.config_rom = legacy_avc_config_rom,
.config_rom_length = sizeof(legacy_avc_config_rom),
};
static const struct fw_unit unit0 = {
.device = {
.type = &fw_unit_type,
.parent = (struct device *)&node.device,
},
.directory = &legacy_avc_config_rom[11],
};
struct device *node_dev = (struct device *)&node.device;
struct device *unit0_dev = (struct device *)&unit0.device;
static const int unit0_expected_ids[] = {0x00012345, 0x00fedcba, 0x00abcdef, 0x00543210};
char *buf = kunit_kzalloc(test, PAGE_SIZE, GFP_KERNEL);
int ids[4] = {0, 0, 0, 0};
// Ensure associations for node and unit devices.
KUNIT_ASSERT_TRUE(test, is_fw_device(node_dev));
KUNIT_ASSERT_FALSE(test, is_fw_unit(node_dev));
KUNIT_ASSERT_PTR_EQ(test, fw_device((node_dev)), &node);
KUNIT_ASSERT_FALSE(test, is_fw_device(unit0_dev));
KUNIT_ASSERT_TRUE(test, is_fw_unit(unit0_dev));
KUNIT_ASSERT_PTR_EQ(test, fw_parent_device((&unit0)), &node);
KUNIT_ASSERT_PTR_EQ(test, fw_unit(unit0_dev), &unit0);
// For entries in root directory.
// Vendor immediate entry is found.
KUNIT_EXPECT_GT(test, show_immediate(node_dev, &config_rom_attributes[0].attr, buf), 0);
KUNIT_EXPECT_STREQ(test, buf, "0x012345\n");
// Model immediate entry is found.
KUNIT_EXPECT_GT(test, show_immediate(node_dev, &config_rom_attributes[4].attr, buf), 0);
KUNIT_EXPECT_STREQ(test, buf, "0xfedcba\n");
// Descriptor leaf entry for vendor is not found.
KUNIT_EXPECT_LT(test, show_text_leaf(node_dev, &config_rom_attributes[5].attr, buf), 0);
// Descriptor leaf entry for model is found.
KUNIT_EXPECT_GT(test, show_text_leaf(node_dev, &config_rom_attributes[6].attr, buf), 0);
KUNIT_EXPECT_STREQ(test, buf, "ABCDEFGHIJ\n");
// For entries in unit 0 directory.
// Vendor immediate entry is not found.
KUNIT_EXPECT_LT(test, show_immediate(unit0_dev, &config_rom_attributes[0].attr, buf), 0);
// Model immediate entry is not found.
KUNIT_EXPECT_LT(test, show_immediate(unit0_dev, &config_rom_attributes[4].attr, buf), 0);
// Descriptor leaf entry for vendor is not found.
KUNIT_EXPECT_LT(test, show_text_leaf(unit0_dev, &config_rom_attributes[5].attr, buf), 0);
// Descriptor leaf entry for model is not found.
KUNIT_EXPECT_LT(test, show_text_leaf(unit0_dev, &config_rom_attributes[6].attr, buf), 0);
// Specifier_ID immediate entry is found.
KUNIT_EXPECT_GT(test, show_immediate(unit0_dev, &config_rom_attributes[2].attr, buf), 0);
KUNIT_EXPECT_STREQ(test, buf, "0xabcdef\n");
// Version immediate entry is found.
KUNIT_EXPECT_GT(test, show_immediate(unit0_dev, &config_rom_attributes[3].attr, buf), 0);
KUNIT_EXPECT_STREQ(test, buf, "0x543210\n");
kunit_kfree(test, buf);
get_modalias_ids(&unit0, ids);
KUNIT_EXPECT_MEMEQ(test, ids, unit0_expected_ids, sizeof(ids));
}
static struct kunit_case device_attr_test_cases[] = {
KUNIT_CASE(device_attr_simple_avc),
KUNIT_CASE(device_attr_legacy_avc),
{}
};
static struct kunit_suite device_attr_test_suite = {
.name = "firewire-device-attribute",
.test_cases = device_attr_test_cases,
};
kunit_test_suite(device_attr_test_suite);
......@@ -75,7 +75,7 @@ void fw_csr_iterator_init(struct fw_csr_iterator *ci, const u32 *p);
int fw_csr_iterator_next(struct fw_csr_iterator *ci, int *key, int *value);
int fw_csr_string(const u32 *directory, int key, char *buf, size_t size);
extern struct bus_type fw_bus_type;
extern const struct bus_type fw_bus_type;
struct fw_card_driver;
struct fw_node;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment