firewire: core: optimize propagation of BROADCAST_CHANNEL
[pandora-kernel.git] / drivers / firewire / fw-device.c
index c173be3..a47e212 100644 (file)
  * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
  */
 
-#include <linux/module.h>
-#include <linux/wait.h>
-#include <linux/errno.h>
-#include <linux/kthread.h>
-#include <linux/device.h>
+#include <linux/ctype.h>
 #include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/errno.h>
 #include <linux/idr.h>
-#include <linux/string.h>
+#include <linux/jiffies.h>
+#include <linux/kobject.h>
+#include <linux/list.h>
+#include <linux/mutex.h>
 #include <linux/rwsem.h>
 #include <linux/semaphore.h>
+#include <linux/spinlock.h>
+#include <linux/string.h>
+#include <linux/workqueue.h>
+
 #include <asm/system.h>
-#include <linux/ctype.h>
-#include "fw-transaction.h"
-#include "fw-topology.h"
+
 #include "fw-device.h"
+#include "fw-topology.h"
+#include "fw-transaction.h"
 
 void fw_csr_iterator_init(struct fw_csr_iterator *ci, u32 * p)
 {
@@ -131,8 +136,7 @@ static int get_modalias(struct fw_unit *unit, char *buffer, size_t buffer_size)
                        vendor, model, specifier_id, version);
 }
 
-static int
-fw_unit_uevent(struct device *dev, struct kobj_uevent_env *env)
+static int fw_unit_uevent(struct device *dev, struct kobj_uevent_env *env)
 {
        struct fw_unit *unit = fw_unit(dev);
        char modalias[64];
@@ -151,26 +155,6 @@ struct bus_type fw_bus_type = {
 };
 EXPORT_SYMBOL(fw_bus_type);
 
-static void fw_device_release(struct device *dev)
-{
-       struct fw_device *device = fw_device(dev);
-       struct fw_card *card = device->card;
-       unsigned long flags;
-
-       /*
-        * Take the card lock so we don't set this to NULL while a
-        * FW_NODE_UPDATED callback is being handled.
-        */
-       spin_lock_irqsave(&card->lock, flags);
-       device->node->data = NULL;
-       spin_unlock_irqrestore(&card->lock, flags);
-
-       fw_node_put(device->node);
-       kfree(device->config_rom);
-       kfree(device);
-       fw_card_put(card);
-}
-
 int fw_device_enable_phys_dma(struct fw_device *device)
 {
        int generation = device->generation;
@@ -189,8 +173,8 @@ struct config_rom_attribute {
        u32 key;
 };
 
-static ssize_t
-show_immediate(struct device *dev, struct device_attribute *dattr, char *buf)
+static ssize_t show_immediate(struct device *dev,
+                             struct device_attribute *dattr, char *buf)
 {
        struct config_rom_attribute *attr =
                container_of(dattr, struct config_rom_attribute, attr);
@@ -221,8 +205,8 @@ show_immediate(struct device *dev, struct device_attribute *dattr, char *buf)
 #define IMMEDIATE_ATTR(name, key)                              \
        { __ATTR(name, S_IRUGO, show_immediate, NULL), key }
 
-static ssize_t
-show_text_leaf(struct device *dev, struct device_attribute *dattr, char *buf)
+static ssize_t show_text_leaf(struct device *dev,
+                             struct device_attribute *dattr, char *buf)
 {
        struct config_rom_attribute *attr =
                container_of(dattr, struct config_rom_attribute, attr);
@@ -291,10 +275,9 @@ static struct config_rom_attribute config_rom_attributes[] = {
        TEXT_LEAF_ATTR(hardware_version_name, CSR_HARDWARE_VERSION),
 };
 
-static void
-init_fw_attribute_group(struct device *dev,
-                       struct device_attribute *attrs,
-                       struct fw_attribute_group *group)
+static void init_fw_attribute_group(struct device *dev,
+                                   struct device_attribute *attrs,
+                                   struct fw_attribute_group *group)
 {
        struct device_attribute *attr;
        int i, j;
@@ -317,9 +300,8 @@ init_fw_attribute_group(struct device *dev,
        dev->groups = group->groups;
 }
 
-static ssize_t
-modalias_show(struct device *dev,
-             struct device_attribute *attr, char *buf)
+static ssize_t modalias_show(struct device *dev,
+                            struct device_attribute *attr, char *buf)
 {
        struct fw_unit *unit = fw_unit(dev);
        int length;
@@ -330,9 +312,8 @@ modalias_show(struct device *dev,
        return length + 1;
 }
 
-static ssize_t
-rom_index_show(struct device *dev,
-              struct device_attribute *attr, char *buf)
+static ssize_t rom_index_show(struct device *dev,
+                             struct device_attribute *attr, char *buf)
 {
        struct fw_device *device = fw_device(dev->parent);
        struct fw_unit *unit = fw_unit(dev);
@@ -347,8 +328,8 @@ static struct device_attribute fw_unit_attributes[] = {
        __ATTR_NULL,
 };
 
-static ssize_t
-config_rom_show(struct device *dev, struct device_attribute *attr, char *buf)
+static ssize_t config_rom_show(struct device *dev,
+                              struct device_attribute *attr, char *buf)
 {
        struct fw_device *device = fw_device(dev);
        size_t length;
@@ -361,8 +342,8 @@ config_rom_show(struct device *dev, struct device_attribute *attr, char *buf)
        return length;
 }
 
-static ssize_t
-guid_show(struct device *dev, struct device_attribute *attr, char *buf)
+static ssize_t guid_show(struct device *dev,
+                        struct device_attribute *attr, char *buf)
 {
        struct fw_device *device = fw_device(dev);
        int ret;
@@ -381,8 +362,8 @@ static struct device_attribute fw_device_attributes[] = {
        __ATTR_NULL,
 };
 
-static int
-read_rom(struct fw_device *device, int generation, int index, u32 *data)
+static int read_rom(struct fw_device *device,
+                   int generation, int index, u32 *data)
 {
        int rcode;
 
@@ -537,7 +518,7 @@ static int read_bus_info_block(struct fw_device *device, int generation)
 
        kfree(old_rom);
        ret = 0;
-       device->cmc = rom[2] & 1 << 30;
+       device->cmc = rom[2] >> 30 & 1;
  out:
        kfree(rom);
 
@@ -633,12 +614,39 @@ struct fw_device *fw_device_get_by_devt(dev_t devt)
        return device;
 }
 
+/*
+ * These defines control the retry behavior for reading the config
+ * rom.  It shouldn't be necessary to tweak these; if the device
+ * doesn't respond to a config rom read within 10 seconds, it's not
+ * going to respond at all.  As for the initial delay, a lot of
+ * devices will be able to respond within half a second after bus
+ * reset.  On the other hand, it's not really worth being more
+ * aggressive than that, since it scales pretty well; if 10 devices
+ * are plugged in, they're all getting read within one second.
+ */
+
+#define MAX_RETRIES    10
+#define RETRY_DELAY    (3 * HZ)
+#define INITIAL_DELAY  (HZ / 2)
+#define SHUTDOWN_DELAY (2 * HZ)
+
 static void fw_device_shutdown(struct work_struct *work)
 {
        struct fw_device *device =
                container_of(work, struct fw_device, work.work);
        int minor = MINOR(device->device.devt);
 
+       if (time_is_after_jiffies(device->card->reset_jiffies + SHUTDOWN_DELAY)
+           && !list_empty(&device->card->link)) {
+               schedule_delayed_work(&device->work, SHUTDOWN_DELAY);
+               return;
+       }
+
+       if (atomic_cmpxchg(&device->state,
+                          FW_DEVICE_GONE,
+                          FW_DEVICE_SHUTDOWN) != FW_DEVICE_GONE)
+               return;
+
        fw_device_cdev_remove(device);
        device_for_each_child(&device->device, NULL, shutdown_unit);
        device_unregister(&device->device);
@@ -646,33 +654,152 @@ static void fw_device_shutdown(struct work_struct *work)
        down_write(&fw_device_rwsem);
        idr_remove(&fw_device_idr, minor);
        up_write(&fw_device_rwsem);
+
        fw_device_put(device);
 }
 
+static void fw_device_release(struct device *dev)
+{
+       struct fw_device *device = fw_device(dev);
+       struct fw_card *card = device->card;
+       unsigned long flags;
+
+       /*
+        * Take the card lock so we don't set this to NULL while a
+        * FW_NODE_UPDATED callback is being handled or while the
+        * bus manager work looks at this node.
+        */
+       spin_lock_irqsave(&card->lock, flags);
+       device->node->data = NULL;
+       spin_unlock_irqrestore(&card->lock, flags);
+
+       fw_node_put(device->node);
+       kfree(device->config_rom);
+       kfree(device);
+       fw_card_put(card);
+}
+
 static struct device_type fw_device_type = {
-       .release        = fw_device_release,
+       .release = fw_device_release,
 };
 
+static int update_unit(struct device *dev, void *data)
+{
+       struct fw_unit *unit = fw_unit(dev);
+       struct fw_driver *driver = (struct fw_driver *)dev->driver;
+
+       if (is_fw_unit(dev) && driver != NULL && driver->update != NULL) {
+               down(&dev->sem);
+               driver->update(unit);
+               up(&dev->sem);
+       }
+
+       return 0;
+}
+
+static void fw_device_update(struct work_struct *work)
+{
+       struct fw_device *device =
+               container_of(work, struct fw_device, work.work);
+
+       fw_device_cdev_update(device);
+       device_for_each_child(&device->device, NULL, update_unit);
+}
+
 /*
- * These defines control the retry behavior for reading the config
- * rom.  It shouldn't be necessary to tweak these; if the device
- * doesn't respond to a config rom read within 10 seconds, it's not
- * going to respond at all.  As for the initial delay, a lot of
- * devices will be able to respond within half a second after bus
- * reset.  On the other hand, it's not really worth being more
- * aggressive than that, since it scales pretty well; if 10 devices
- * are plugged in, they're all getting read within one second.
+ * If a device was pending for deletion because its node went away but its
+ * bus info block and root directory header matches that of a newly discovered
+ * device, revive the existing fw_device.
+ * The newly allocated fw_device becomes obsolete instead.
  */
+static int lookup_existing_device(struct device *dev, void *data)
+{
+       struct fw_device *old = fw_device(dev);
+       struct fw_device *new = data;
+       struct fw_card *card = new->card;
+       int match = 0;
+
+       down_read(&fw_device_rwsem); /* serialize config_rom access */
+       spin_lock_irq(&card->lock);  /* serialize node access */
+
+       if (memcmp(old->config_rom, new->config_rom, 6 * 4) == 0 &&
+           atomic_cmpxchg(&old->state,
+                          FW_DEVICE_GONE,
+                          FW_DEVICE_RUNNING) == FW_DEVICE_GONE) {
+               struct fw_node *current_node = new->node;
+               struct fw_node *obsolete_node = old->node;
+
+               new->node = obsolete_node;
+               new->node->data = new;
+               old->node = current_node;
+               old->node->data = old;
+
+               old->max_speed = new->max_speed;
+               old->node_id = current_node->node_id;
+               smp_wmb();  /* update node_id before generation */
+               old->generation = card->generation;
+               old->config_rom_retries = 0;
+               fw_notify("rediscovered device %s\n", dev_name(dev));
 
-#define MAX_RETRIES    10
-#define RETRY_DELAY    (3 * HZ)
-#define INITIAL_DELAY  (HZ / 2)
+               PREPARE_DELAYED_WORK(&old->work, fw_device_update);
+               schedule_delayed_work(&old->work, 0);
+
+               if (current_node == card->root_node)
+                       fw_schedule_bm_work(card, 0);
+
+               match = 1;
+       }
+
+       spin_unlock_irq(&card->lock);
+       up_read(&fw_device_rwsem);
+
+       return match;
+}
+
+enum { BC_UNKNOWN = 0, BC_UNIMPLEMENTED, BC_IMPLEMENTED, };
+
+void fw_device_set_broadcast_channel(struct fw_device *device, int generation)
+{
+       struct fw_card *card = device->card;
+       __be32 data;
+       int rcode;
+
+       if (!card->broadcast_channel_allocated)
+               return;
+
+       if (device->bc_implemented == BC_UNKNOWN) {
+               rcode = fw_run_transaction(card, TCODE_READ_QUADLET_REQUEST,
+                               device->node_id, generation, device->max_speed,
+                               CSR_REGISTER_BASE + CSR_BROADCAST_CHANNEL,
+                               &data, 4);
+               switch (rcode) {
+               case RCODE_COMPLETE:
+                       if (data & cpu_to_be32(1 << 31)) {
+                               device->bc_implemented = BC_IMPLEMENTED;
+                               break;
+                       }
+                       /* else fall through to case address error */
+               case RCODE_ADDRESS_ERROR:
+                       device->bc_implemented = BC_UNIMPLEMENTED;
+               }
+       }
+
+       if (device->bc_implemented == BC_IMPLEMENTED) {
+               data = cpu_to_be32(BROADCAST_CHANNEL_INITIAL |
+                                  BROADCAST_CHANNEL_VALID);
+               fw_run_transaction(card, TCODE_WRITE_QUADLET_REQUEST,
+                               device->node_id, generation, device->max_speed,
+                               CSR_REGISTER_BASE + CSR_BROADCAST_CHANNEL,
+                               &data, 4);
+       }
+}
 
 static void fw_device_init(struct work_struct *work)
 {
        struct fw_device *device =
                container_of(work, struct fw_device, work.work);
-       int minor, err;
+       struct device *revived_dev;
+       int minor, ret;
 
        /*
         * All failure paths here set node->data to NULL, so that we
@@ -695,15 +822,25 @@ static void fw_device_init(struct work_struct *work)
                return;
        }
 
-       err = -ENOMEM;
+       revived_dev = device_find_child(device->card->device,
+                                       device, lookup_existing_device);
+       if (revived_dev) {
+               put_device(revived_dev);
+               fw_device_release(&device->device);
+
+               return;
+       }
+
+       device_initialize(&device->device);
 
        fw_device_get(device);
        down_write(&fw_device_rwsem);
-       if (idr_pre_get(&fw_device_idr, GFP_KERNEL))
-               err = idr_get_new(&fw_device_idr, device, &minor);
+       ret = idr_pre_get(&fw_device_idr, GFP_KERNEL) ?
+             idr_get_new(&fw_device_idr, device, &minor) :
+             -ENOMEM;
        up_write(&fw_device_rwsem);
 
-       if (err < 0)
+       if (ret < 0)
                goto error;
 
        device->device.bus = &fw_bus_type;
@@ -732,9 +869,10 @@ static void fw_device_init(struct work_struct *work)
         * fw_node_event().
         */
        if (atomic_cmpxchg(&device->state,
-                   FW_DEVICE_INITIALIZING,
-                   FW_DEVICE_RUNNING) == FW_DEVICE_SHUTDOWN) {
-               fw_device_shutdown(work);
+                          FW_DEVICE_INITIALIZING,
+                          FW_DEVICE_RUNNING) == FW_DEVICE_GONE) {
+               PREPARE_DELAYED_WORK(&device->work, fw_device_shutdown);
+               schedule_delayed_work(&device->work, SHUTDOWN_DELAY);
        } else {
                if (device->config_rom_retries)
                        fw_notify("created device %s: GUID %08x%08x, S%d00, "
@@ -749,6 +887,8 @@ static void fw_device_init(struct work_struct *work)
                                  device->config_rom[3], device->config_rom[4],
                                  1 << device->max_speed);
                device->config_rom_retries = 0;
+
+               fw_device_set_broadcast_channel(device, device->generation);
        }
 
        /*
@@ -772,29 +912,6 @@ static void fw_device_init(struct work_struct *work)
        put_device(&device->device);    /* our reference */
 }
 
-static int update_unit(struct device *dev, void *data)
-{
-       struct fw_unit *unit = fw_unit(dev);
-       struct fw_driver *driver = (struct fw_driver *)dev->driver;
-
-       if (is_fw_unit(dev) && driver != NULL && driver->update != NULL) {
-               down(&dev->sem);
-               driver->update(unit);
-               up(&dev->sem);
-       }
-
-       return 0;
-}
-
-static void fw_device_update(struct work_struct *work)
-{
-       struct fw_device *device =
-               container_of(work, struct fw_device, work.work);
-
-       fw_device_cdev_update(device);
-       device_for_each_child(&device->device, NULL, update_unit);
-}
-
 enum {
        REREAD_BIB_ERROR,
        REREAD_BIB_GONE,
@@ -815,7 +932,7 @@ static int reread_bus_info_block(struct fw_device *device, int generation)
                if (i == 0 && q == 0)
                        return REREAD_BIB_GONE;
 
-               if (i > device->config_rom_length || q != device->config_rom[i])
+               if (q != device->config_rom[i])
                        return REREAD_BIB_CHANGED;
        }
 
@@ -845,8 +962,8 @@ static void fw_device_refresh(struct work_struct *work)
 
        case REREAD_BIB_UNCHANGED:
                if (atomic_cmpxchg(&device->state,
-                           FW_DEVICE_INITIALIZING,
-                           FW_DEVICE_RUNNING) == FW_DEVICE_SHUTDOWN)
+                                  FW_DEVICE_INITIALIZING,
+                                  FW_DEVICE_RUNNING) == FW_DEVICE_GONE)
                        goto gone;
 
                fw_device_update(work);
@@ -877,8 +994,8 @@ static void fw_device_refresh(struct work_struct *work)
        create_units(device);
 
        if (atomic_cmpxchg(&device->state,
-                   FW_DEVICE_INITIALIZING,
-                   FW_DEVICE_RUNNING) == FW_DEVICE_SHUTDOWN)
+                          FW_DEVICE_INITIALIZING,
+                          FW_DEVICE_RUNNING) == FW_DEVICE_GONE)
                goto gone;
 
        fw_notify("refreshed device %s\n", dev_name(&device->device));
@@ -888,8 +1005,9 @@ static void fw_device_refresh(struct work_struct *work)
  give_up:
        fw_notify("giving up on refresh of device %s\n", dev_name(&device->device));
  gone:
-       atomic_set(&device->state, FW_DEVICE_SHUTDOWN);
-       fw_device_shutdown(work);
+       atomic_set(&device->state, FW_DEVICE_GONE);
+       PREPARE_DELAYED_WORK(&device->work, fw_device_shutdown);
+       schedule_delayed_work(&device->work, SHUTDOWN_DELAY);
  out:
        if (node_id == card->root_node->node_id)
                fw_schedule_bm_work(card, 0);
@@ -911,18 +1029,20 @@ void fw_node_event(struct fw_card *card, struct fw_node *node, int event)
 
                /*
                 * Do minimal intialization of the device here, the
-                * rest will happen in fw_device_init().  We need the
-                * card and node so we can read the config rom and we
-                * need to do device_initialize() now so
-                * device_for_each_child() in FW_NODE_UPDATED is
-                * doesn't freak out.
+                * rest will happen in fw_device_init().
+                *
+                * Attention:  A lot of things, even fw_device_get(),
+                * cannot be done before fw_device_init() finished!
+                * You can basically just check device->state and
+                * schedule work until then, but only while holding
+                * card->lock.
                 */
-               device_initialize(&device->device);
                atomic_set(&device->state, FW_DEVICE_INITIALIZING);
                device->card = fw_card_get(card);
                device->node = fw_node_get(node);
                device->node_id = node->node_id;
                device->generation = card->generation;
+               mutex_init(&device->client_list_mutex);
                INIT_LIST_HEAD(&device->client_list);
 
                /*
@@ -992,9 +1112,10 @@ void fw_node_event(struct fw_card *card, struct fw_node *node, int event)
                 */
                device = node->data;
                if (atomic_xchg(&device->state,
-                               FW_DEVICE_SHUTDOWN) == FW_DEVICE_RUNNING) {
+                               FW_DEVICE_GONE) == FW_DEVICE_RUNNING) {
                        PREPARE_DELAYED_WORK(&device->work, fw_device_shutdown);
-                       schedule_delayed_work(&device->work, 0);
+                       schedule_delayed_work(&device->work,
+                               list_empty(&card->link) ? 0 : SHUTDOWN_DELAY);
                }
                break;
        }