summaryrefslogtreecommitdiffstats
path: root/drivers/firewire/fw-card.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/firewire/fw-card.c')
-rw-r--r--drivers/firewire/fw-card.c56
1 files changed, 56 insertions, 0 deletions
diff --git a/drivers/firewire/fw-card.c b/drivers/firewire/fw-card.c
index d8abd70ce84..79773907e10 100644
--- a/drivers/firewire/fw-card.c
+++ b/drivers/firewire/fw-card.c
@@ -24,6 +24,7 @@
#include <linux/device.h>
#include "fw-transaction.h"
#include "fw-topology.h"
+#include "fw-device.h"
/* The lib/crc16.c implementation uses the standard (0x8005)
* polynomial, but we need the ITU-T (or CCITT) polynomial (0x1021).
@@ -186,6 +187,59 @@ fw_core_remove_descriptor (struct fw_descriptor *desc)
EXPORT_SYMBOL(fw_core_remove_descriptor);
static void
+fw_card_irm_work(struct work_struct *work)
+{
+ struct fw_card *card =
+ container_of(work, struct fw_card, work.work);
+ struct fw_device *root;
+ unsigned long flags;
+ int new_irm_id, generation;
+
+ /* FIXME: This simple bus management unconditionally picks a
+ * cycle master if the current root can't do it. We need to
+ * not do this if there is a bus manager already. Also, some
+ * hubs set the contender bit, which is bogus, so we should
+ * probably do a little sanity check on the IRM (like, read
+ * the bandwidth register) if it's not us. */
+
+ spin_lock_irqsave(&card->lock, flags);
+
+ generation = card->generation;
+ root = card->root_node->data;
+
+ if (root == NULL)
+ /* Either link_on is false, or we failed to read the
+ * config rom. In either case, pick another root. */
+ new_irm_id = card->local_node->node_id;
+ else if (root->state != FW_DEVICE_RUNNING)
+ /* If we haven't probed this device yet, bail out now
+ * and let's try again once that's done. */
+ new_irm_id = -1;
+ else if (root->config_rom[2] & bib_cmc)
+ /* FIXME: I suppose we should set the cmstr bit in the
+ * STATE_CLEAR register of this node, as described in
+ * 1394-1995, 8.4.2.6. Also, send out a force root
+ * packet for this node. */
+ new_irm_id = -1;
+ else
+ /* Current root has an active link layer and we
+ * successfully read the config rom, but it's not
+ * cycle master capable. */
+ new_irm_id = card->local_node->node_id;
+
+ if (card->irm_retries++ > 5)
+ new_irm_id = -1;
+
+ spin_unlock_irqrestore(&card->lock, flags);
+
+ if (new_irm_id > 0) {
+ fw_notify("Trying to become root (card %d)\n", card->index);
+ fw_send_force_root(card, new_irm_id, generation);
+ fw_core_initiate_bus_reset(card, 1);
+ }
+}
+
+static void
release_card(struct device *device)
{
struct fw_card *card =
@@ -222,6 +276,8 @@ fw_card_initialize(struct fw_card *card, struct fw_card_driver *driver,
card->local_node = NULL;
+ INIT_DELAYED_WORK(&card->work, fw_card_irm_work);
+
card->card_device.bus = &fw_bus_type;
card->card_device.release = release_card;
card->card_device.parent = card->device;