summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--drivers/firewire/core-card.c21
-rw-r--r--drivers/firewire/core-device.c25
-rw-r--r--include/linux/firewire.h11
3 files changed, 41 insertions, 16 deletions
diff --git a/drivers/firewire/core-card.c b/drivers/firewire/core-card.c
index e5e0174a0335..6979d6a88ae2 100644
--- a/drivers/firewire/core-card.c
+++ b/drivers/firewire/core-card.c
@@ -86,8 +86,6 @@ static size_t config_rom_length = 1 + 4 + 1 + 1;
*/
#define DEFAULT_SPLIT_TIMEOUT (2 * 8000)
-#define CANON_OUI 0x000085
-
static void generate_config_rom(struct fw_card *card, __be32 *config_rom)
{
struct fw_descriptor *desc;
@@ -308,11 +306,9 @@ __must_hold(&card->lock)
cpu_to_be32(local_id),
};
bool grace = time_is_before_jiffies64(card->reset_jiffies + msecs_to_jiffies(125));
- bool irm_is_1394_1995_only = false;
- bool keep_this_irm = false;
struct fw_node *irm_node;
struct fw_device *irm_device;
- int irm_node_id;
+ int irm_node_id, irm_device_quirks = 0;
int rcode;
lockdep_assert_held(&card->lock);
@@ -328,15 +324,12 @@ __must_hold(&card->lock)
return BM_CONTENTION_OUTCOME_IRM_HAS_LINK_OFF;
}
+ // NOTE: It is likely that the quirk detection for IRM device has not done yet.
irm_device = fw_node_get_device(irm_node);
- if (irm_device && irm_device->config_rom) {
- irm_is_1394_1995_only = (irm_device->config_rom[2] & 0x000000f0) == 0;
-
- // Canon MV5i works unreliably if it is not root node.
- keep_this_irm = irm_device->config_rom[3] >> 8 == CANON_OUI;
- }
-
- if (irm_is_1394_1995_only && !keep_this_irm) {
+ if (irm_device)
+ irm_device_quirks = READ_ONCE(irm_device->quirks);
+ if ((irm_device_quirks & FW_DEVICE_QUIRK_IRM_IS_1394_1995_ONLY) &&
+ !(irm_device_quirks & FW_DEVICE_QUIRK_IRM_IGNORES_BUS_MANAGER)) {
fw_notice(card, "IRM is not 1394a compliant, making local node (%02x) root\n",
local_id);
return BM_CONTENTION_OUTCOME_IRM_COMPLIES_1394_1995_ONLY;
@@ -373,7 +366,7 @@ __must_hold(&card->lock)
return BM_CONTENTION_OUTCOME_IRM_HOLDS_LOCAL_NODE_AS_BM;
}
default:
- if (!keep_this_irm) {
+ if (!(irm_device_quirks & FW_DEVICE_QUIRK_IRM_IGNORES_BUS_MANAGER)) {
fw_notice(card, "BM lock failed (%s), making local node (%02x) root\n",
fw_rcode_string(rcode), local_id);
return BM_CONTENTION_OUTCOME_IRM_COMPLIES_1394_1995_ONLY;
diff --git a/drivers/firewire/core-device.c b/drivers/firewire/core-device.c
index 457a0da024a7..9bab2d594b89 100644
--- a/drivers/firewire/core-device.c
+++ b/drivers/firewire/core-device.c
@@ -542,6 +542,21 @@ static struct device_attribute fw_device_attributes[] = {
__ATTR_NULL,
};
+#define CANON_OUI 0x000085
+
+static int detect_quirks_by_bus_information_block(const u32 *bus_information_block)
+{
+ int quirks = 0;
+
+ if ((bus_information_block[2] & 0x000000f0) == 0)
+ quirks |= FW_DEVICE_QUIRK_IRM_IS_1394_1995_ONLY;
+
+ if ((bus_information_block[3] >> 8) == CANON_OUI)
+ quirks |= FW_DEVICE_QUIRK_IRM_IGNORES_BUS_MANAGER;
+
+ return quirks;
+}
+
static int read_rom(struct fw_device *device,
int generation, int index, u32 *data)
{
@@ -582,6 +597,7 @@ static int read_config_rom(struct fw_device *device, int generation)
u32 *rom, *stack;
u32 sp, key;
int i, end, length, ret;
+ int quirks;
rom = kmalloc(sizeof(*rom) * MAX_CONFIG_ROM_SIZE +
sizeof(*stack) * MAX_CONFIG_ROM_SIZE, GFP_KERNEL);
@@ -612,6 +628,11 @@ static int read_config_rom(struct fw_device *device, int generation)
}
}
+ quirks = detect_quirks_by_bus_information_block(rom);
+
+ // Just prevent from torn writing/reading.
+ WRITE_ONCE(device->quirks, quirks);
+
device->max_speed = device->node->max_speed;
/*
@@ -1122,10 +1143,10 @@ static void fw_device_init(struct work_struct *work)
device->workfn = fw_device_shutdown;
fw_schedule_device_work(device, SHUTDOWN_DELAY);
} else {
- fw_notice(card, "created device %s: GUID %08x%08x, S%d00\n",
+ fw_notice(card, "created device %s: GUID %08x%08x, S%d00, quirks %08x\n",
dev_name(&device->device),
device->config_rom[3], device->config_rom[4],
- 1 << device->max_speed);
+ 1 << device->max_speed, device->quirks);
device->config_rom_retries = 0;
set_broadcast_channel(device, device->generation);
diff --git a/include/linux/firewire.h b/include/linux/firewire.h
index 6d208769d456..161829cfcc00 100644
--- a/include/linux/firewire.h
+++ b/include/linux/firewire.h
@@ -170,6 +170,14 @@ struct fw_attribute_group {
struct attribute *attrs[13];
};
+enum fw_device_quirk {
+ // See afa1282a35d3 ("firewire: core: check for 1394a compliant IRM, fix inaccessibility of Sony camcorder").
+ FW_DEVICE_QUIRK_IRM_IS_1394_1995_ONLY = BIT(0),
+
+ // See a509e43ff338 ("firewire: core: fix unstable I/O with Canon camcorder").
+ FW_DEVICE_QUIRK_IRM_IGNORES_BUS_MANAGER = BIT(1),
+};
+
enum fw_device_state {
FW_DEVICE_INITIALIZING,
FW_DEVICE_RUNNING,
@@ -203,6 +211,9 @@ struct fw_device {
struct fw_card *card;
struct device device;
+ // A set of enum fw_device_quirk.
+ int quirks;
+
struct mutex client_list_mutex;
struct list_head client_list;