diff options
author | Stefan Richter <stefanr@s5r6.in-berlin.de> | 2010-05-30 21:43:52 +0400 |
---|---|---|
committer | Stefan Richter <stefanr@s5r6.in-berlin.de> | 2010-06-02 21:48:13 +0400 |
commit | 10389536742cefbedecb67a5b2906f155cf3a1c3 (patch) | |
tree | cf0dbc030578e267e7f9fe1349083c3af3090f19 | |
parent | 67a3e12b05e055c0415c556a315a3d3eb637e29e (diff) | |
download | linux-10389536742cefbedecb67a5b2906f155cf3a1c3.tar.xz |
firewire: core: check for 1394a compliant IRM, fix inaccessibility of Sony camcorder
Per IEEE 1394 clause 8.4.2.3, a contender for the IRM role shall check
whether the current IRM complies to 1394a-2000 or later. If not force a
compliant node (e.g. itself) to become IRM. This was implemented in the
older ieee1394 driver but not yet in firewire-core.
An older Sony camcorder (Sony DCR-TRV25) which implements 1394-1995 IRM
but neither 1394a-2000 IRM nor BM was now found to cause an
interoperability bug:
- Camcorder becomes root node when plugged in, hence gets IRM role.
- firewire-core successfully contends for BM role, proceeds to perform
gap count optimization and resets the bus.
- Sony camcorder ignores presence of a BM (against the spec, this is
a firmware bug), performs its idea of gap count optimization and
resets the bus.
- Preceding two steps are repeated endlessly, bus never settles,
regular I/O is practically impossible.
http://thread.gmane.org/gmane.linux.kernel.firewire.user/3913
This is an interoperability regression from the old to the new drivers.
Fix it indirectly by adding the 1394a IRM check. The spec suggests
three and a half methods to determine 1394a compliance of a remote IRM;
we choose the method of testing the Config_ROM.Bus_Info.generation
field. This is data that firewire-core should have readily available at
this point, i.e. does not require extra I/O.
Reported-by: Clemens Ladisch <clemens@ladisch.de> (missing 1394a check)
Reported-by: H. S. <hs.samix@gmail.com> (issue with Sony DCR-TRV25)
Tested-by: H. S. <hs.samix@gmail.com>
Cc: <stable@kernel.org> # .32.x and newer
Signed-off-by: Stefan Richter <stefanr@s5r6.in-berlin.de>
-rw-r--r-- | drivers/firewire/core-card.c | 24 |
1 files changed, 19 insertions, 5 deletions
diff --git a/drivers/firewire/core-card.c b/drivers/firewire/core-card.c index 9dcb30466ec0..371713ff0266 100644 --- a/drivers/firewire/core-card.c +++ b/drivers/firewire/core-card.c @@ -231,7 +231,7 @@ void fw_schedule_bm_work(struct fw_card *card, unsigned long delay) static void fw_card_bm_work(struct work_struct *work) { struct fw_card *card = container_of(work, struct fw_card, work.work); - struct fw_device *root_device; + struct fw_device *root_device, *irm_device; struct fw_node *root_node; unsigned long flags; int root_id, new_root_id, irm_id, local_id; @@ -239,6 +239,7 @@ static void fw_card_bm_work(struct work_struct *work) bool do_reset = false; bool root_device_is_running; bool root_device_is_cmc; + bool irm_is_1394_1995_only; spin_lock_irqsave(&card->lock, flags); @@ -248,12 +249,18 @@ static void fw_card_bm_work(struct work_struct *work) } generation = card->generation; + root_node = card->root_node; fw_node_get(root_node); root_device = root_node->data; root_device_is_running = root_device && atomic_read(&root_device->state) == FW_DEVICE_RUNNING; root_device_is_cmc = root_device && root_device->cmc; + + irm_device = card->irm_node->data; + irm_is_1394_1995_only = irm_device && irm_device->config_rom && + (irm_device->config_rom[2] & 0x000000f0) == 0; + root_id = root_node->node_id; irm_id = card->irm_node->node_id; local_id = card->local_node->node_id; @@ -276,8 +283,15 @@ static void fw_card_bm_work(struct work_struct *work) if (!card->irm_node->link_on) { new_root_id = local_id; - fw_notify("IRM has link off, making local node (%02x) root.\n", - new_root_id); + fw_notify("%s, making local node (%02x) root.\n", + "IRM has link off", new_root_id); + goto pick_me; + } + + if (irm_is_1394_1995_only) { + new_root_id = local_id; + fw_notify("%s, making local node (%02x) root.\n", + "IRM is not 1394a compliant", new_root_id); goto pick_me; } @@ -316,8 +330,8 @@ static void fw_card_bm_work(struct work_struct *work) * root, and thus, IRM. */ new_root_id = local_id; - fw_notify("BM lock failed, making local node (%02x) root.\n", - new_root_id); + fw_notify("%s, making local node (%02x) root.\n", + "BM lock failed", new_root_id); goto pick_me; } } else if (card->bm_generation != generation) { |