]> pilppa.org Git - linux-2.6-omap-h63xx.git/blobdiff - drivers/firewire/fw-sbp2.c
firewire: Only use INIT_DELAYED_WORK for first initialization.
[linux-2.6-omap-h63xx.git] / drivers / firewire / fw-sbp2.c
index 0b0dcc760585be408a11e58a5ad78e13b8964706..994914f89b5553642e407cf7cf48b54fb071dde9 100644 (file)
@@ -1,7 +1,7 @@
 /*                                             -*- c-basic-offset: 8 -*-
- * fw-sbp2.c -- SBP2 driver (SCSI over IEEE1394)
+ * fw-spb2.c -- SBP2 driver (SCSI over IEEE1394)
  *
- * Copyright (C) 2005-2006  Kristian Hoegsberg <krh@bitplanet.net>
+ * Copyright (C) 2005-2007  Kristian Hoegsberg <krh@bitplanet.net>
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
  * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
  */
 
+/* The basic structure of this driver is based the old storage driver,
+ * drivers/ieee1394/sbp2.c, originally written by
+ *     James Goodwin <jamesg@filanet.com>
+ * with later contributions and ongoing maintenance from
+ *     Ben Collins <bcollins@debian.org>,
+ *     Stefan Richter <stefanr@s5r6.in-berlin.de>
+ * and many others.
+ */
+
 #include <linux/kernel.h>
 #include <linux/module.h>
 #include <linux/mod_devicetable.h>
 #include <linux/device.h>
 #include <linux/scatterlist.h>
 #include <linux/dma-mapping.h>
+#include <linux/timer.h>
 
 #include <scsi/scsi.h>
 #include <scsi/scsi_cmnd.h>
@@ -58,13 +68,17 @@ struct sbp2_device {
        int address_high;
        int generation;
 
-       struct work_struct work;
+       /* Timer for flushing ORBs. */
+       struct timer_list orb_timer;
+
+       int retries;
+       struct delayed_work work;
        struct Scsi_Host *scsi_host;
 };
 
 #define SBP2_MAX_SG_ELEMENT_LENGTH     0xf000
 #define SBP2_MAX_SECTORS               255     /* Max sectors supported */
-#define SBP2_MAX_CMDS                  8       /* This should be safe */
+#define SBP2_ORB_TIMEOUT               2000    /* Timeout in ms */
 
 #define SBP2_ORB_NULL                  0x80000000
 
@@ -328,8 +342,11 @@ sbp2_send_orb(struct sbp2_orb *orb, struct fw_unit *unit,
        list_add_tail(&orb->link, &sd->orb_list);
        spin_unlock_irqrestore(&device->card->lock, flags);
 
+       mod_timer(&sd->orb_timer,
+                 jiffies + DIV_ROUND_UP(SBP2_ORB_TIMEOUT * HZ, 1000));
+
        fw_send_request(device->card, &orb->t, TCODE_WRITE_BLOCK_REQUEST,
-                       node_id | LOCAL_BUS, generation,
+                       node_id, generation,
                        device->node->max_speed, offset,
                        &orb->pointer, sizeof orb->pointer,
                        complete_transaction, orb);
@@ -349,11 +366,21 @@ static void sbp2_cancel_orbs(struct fw_unit *unit)
        spin_unlock_irqrestore(&device->card->lock, flags);
 
        list_for_each_entry_safe(orb, next, &list, link) {
+               if (fw_cancel_transaction(device->card, &orb->t) == 0)
+                       continue;
+
                orb->rcode = RCODE_CANCELLED;
                orb->callback(orb, NULL);
        }
 }
 
+static void orb_timer_callback(unsigned long data)
+{
+       struct sbp2_device *sd = (struct sbp2_device *)data;
+
+       sbp2_cancel_orbs(sd->unit);
+}
+
 static void
 complete_management_orb(struct sbp2_orb *base_orb, struct sbp2_status *status)
 {
@@ -372,7 +399,6 @@ sbp2_send_management_orb(struct fw_unit *unit, int node_id, int generation,
        struct fw_device *device = fw_device(unit->device.parent);
        struct sbp2_device *sd = unit->device.driver_data;
        struct sbp2_management_orb *orb;
-       unsigned long timeout;
        int retval = -ENOMEM;
 
        orb = kzalloc(sizeof *orb, GFP_ATOMIC);
@@ -385,13 +411,13 @@ sbp2_send_management_orb(struct fw_unit *unit, int node_id, int generation,
        orb->base.request_bus =
                dma_map_single(device->card->device, &orb->request,
                               sizeof orb->request, DMA_TO_DEVICE);
-       if (orb->base.request_bus == 0)
+       if (dma_mapping_error(orb->base.request_bus))
                goto out;
 
        orb->response_bus =
                dma_map_single(device->card->device, &orb->response,
                               sizeof orb->response, DMA_FROM_DEVICE);
-       if (orb->response_bus == 0)
+       if (dma_mapping_error(orb->response_bus))
                goto out;
 
        orb->request.response.high    = 0;
@@ -424,7 +450,7 @@ sbp2_send_management_orb(struct fw_unit *unit, int node_id, int generation,
        sbp2_send_orb(&orb->base, unit,
                      node_id, generation, sd->management_agent_address);
 
-       timeout = wait_for_completion_timeout(&orb->done, 10 * HZ);
+       wait_for_completion(&orb->done);
 
        /* FIXME: Handle bus reset race here. */
 
@@ -435,7 +461,7 @@ sbp2_send_management_orb(struct fw_unit *unit, int node_id, int generation,
                goto out;
        }
 
-       if (timeout == 0) {
+       if (orb->base.rcode == RCODE_CANCELLED) {
                fw_error("orb reply timed out, rcode=0x%02x\n",
                         orb->base.rcode);
                goto out;
@@ -486,7 +512,7 @@ static int sbp2_agent_reset(struct fw_unit *unit)
                return -ENOMEM;
 
        fw_send_request(device->card, t, TCODE_WRITE_QUADLET_REQUEST,
-                       sd->node_id | LOCAL_BUS, sd->generation, SCODE_400,
+                       sd->node_id, sd->generation, SCODE_400,
                        sd->command_block_agent_address + SBP2_AGENT_RESET,
                        &zero, sizeof zero, complete_agent_reset_write, t);
 
@@ -495,6 +521,75 @@ static int sbp2_agent_reset(struct fw_unit *unit)
 
 static int add_scsi_devices(struct fw_unit *unit);
 static void remove_scsi_devices(struct fw_unit *unit);
+static void sbp2_reconnect(struct work_struct *work);
+
+static void sbp2_login(struct work_struct *work)
+{
+       struct sbp2_device *sd =
+               container_of(work, struct sbp2_device, work.work);
+       struct fw_unit *unit = sd->unit;
+       struct fw_device *device = fw_device(unit->device.parent);
+       struct sbp2_login_response response;
+       int generation, node_id, local_node_id, lun, retval;
+
+       /* FIXME: Make this work for multi-lun devices. */
+       lun = 0;
+
+       generation    = device->card->generation;
+       node_id       = device->node->node_id;
+       local_node_id = device->card->local_node->node_id;
+
+       if (sbp2_send_management_orb(unit, node_id, generation,
+                                    SBP2_LOGIN_REQUEST, lun, &response) < 0) {
+               if (sd->retries++ < 5) {
+                       fw_error("login attempt %d for %s failed, "
+                                "rescheduling\n",
+                                sd->retries, unit->device.bus_id);
+                       schedule_delayed_work(&sd->work, DIV_ROUND_UP(HZ, 5));
+               } else {
+                       fw_error("failed to login to %s\n",
+                                unit->device.bus_id);
+                       remove_scsi_devices(unit);
+               }
+               return;
+       }
+
+       sd->generation   = generation;
+       sd->node_id      = node_id;
+       sd->address_high = local_node_id << 16;
+
+       /* Get command block agent offset and login id. */
+       sd->command_block_agent_address =
+               ((u64) response.command_block_agent.high << 32) |
+               response.command_block_agent.low;
+       sd->login_id = login_response_get_login_id(response);
+
+       fw_notify("logged in to sbp2 unit %s\n", unit->device.bus_id);
+       fw_notify(" - management_agent_address: 0x%012llx\n",
+                 (unsigned long long) sd->management_agent_address);
+       fw_notify(" - command_block_agent_address: 0x%012llx\n",
+                 (unsigned long long) sd->command_block_agent_address);
+       fw_notify(" - status write address: 0x%012llx\n",
+                 (unsigned long long) sd->address_handler.offset);
+
+#if 0
+       /* FIXME: The linux1394 sbp2 does this last step. */
+       sbp2_set_busy_timeout(scsi_id);
+#endif
+
+       PREPARE_DELAYED_WORK(&sd->work, sbp2_reconnect);
+       sbp2_agent_reset(unit);
+
+       retval = add_scsi_devices(unit);
+       if (retval < 0) {
+               sbp2_send_management_orb(unit, sd->node_id, sd->generation,
+                                        SBP2_LOGOUT_REQUEST, sd->login_id,
+                                        NULL);
+               /* Set this back to sbp2_login so we fall back and
+                * retry login on bus reset. */
+               PREPARE_DELAYED_WORK(&sd->work, sbp2_login);
+       }
+}
 
 static int sbp2_probe(struct device *dev)
 {
@@ -502,9 +597,7 @@ static int sbp2_probe(struct device *dev)
        struct fw_device *device = fw_device(unit->device.parent);
        struct sbp2_device *sd;
        struct fw_csr_iterator ci;
-       int i, key, value, lun, retval;
-       int node_id, generation, local_node_id;
-       struct sbp2_login_response response;
+       int i, key, value;
        u32 model, firmware_revision;
 
        sd = kzalloc(sizeof *sd, GFP_KERNEL);
@@ -514,6 +607,7 @@ static int sbp2_probe(struct device *dev)
        unit->device.driver_data = sd;
        sd->unit = unit;
        INIT_LIST_HEAD(&sd->orb_list);
+       setup_timer(&sd->orb_timer, orb_timer_callback, (unsigned long)sd);
 
        sd->address_handler.length = 0x100;
        sd->address_handler.address_callback = sbp2_status_write;
@@ -569,56 +663,10 @@ static int sbp2_probe(struct device *dev)
                          unit->device.bus_id,
                          sd->workarounds, firmware_revision, model);
 
-       /* FIXME: Make this work for multi-lun devices. */
-       lun = 0;
-
-       generation    = device->card->generation;
-       node_id       = device->node->node_id;
-       local_node_id = device->card->local_node->node_id;
-
-       /* FIXME: We should probably do this from a keventd callback
-        * and handle retries by rescheduling the work. */
-       if (sbp2_send_management_orb(unit, node_id, generation,
-                                    SBP2_LOGIN_REQUEST, lun, &response) < 0) {
-               fw_core_remove_address_handler(&sd->address_handler);
-               kfree(sd);
-               return -EBUSY;
-       }
-
-       sd->generation   = generation;
-       sd->node_id      = node_id;
-       sd->address_high = (LOCAL_BUS | local_node_id) << 16;
-
-       /* Get command block agent offset and login id. */
-       sd->command_block_agent_address =
-               ((u64) response.command_block_agent.high << 32) |
-               response.command_block_agent.low;
-       sd->login_id = login_response_get_login_id(response);
-
-       fw_notify("logged in to sbp2 unit %s\n", unit->device.bus_id);
-       fw_notify(" - management_agent_address: 0x%012llx\n",
-                 (unsigned long long) sd->management_agent_address);
-       fw_notify(" - command_block_agent_address: 0x%012llx\n",
-                 (unsigned long long) sd->command_block_agent_address);
-       fw_notify(" - status write address: 0x%012llx\n",
-                 (unsigned long long) sd->address_handler.offset);
-
-#if 0
-       /* FIXME: The linux1394 sbp2 does this last step. */
-       sbp2_set_busy_timeout(scsi_id);
-#endif
-
-       sbp2_agent_reset(unit);
-
-       retval = add_scsi_devices(unit);
-       if (retval < 0) {
-               sbp2_send_management_orb(unit, sd->node_id, sd->generation,
-                                        SBP2_LOGOUT_REQUEST, sd->login_id,
-                                        NULL);
-               fw_core_remove_address_handler(&sd->address_handler);
-               kfree(sd);
-               return retval;
-       }
+       /* We schedule work to do the login so we can easily
+        * reschedule retries. */
+       INIT_DELAYED_WORK(&sd->work, sbp2_login);
+       schedule_delayed_work(&sd->work, 0);
 
        return 0;
 }
@@ -632,6 +680,7 @@ static int sbp2_remove(struct device *dev)
                                 SBP2_LOGOUT_REQUEST, sd->login_id, NULL);
 
        remove_scsi_devices(unit);
+       del_timer_sync(&sd->orb_timer);
 
        fw_core_remove_address_handler(&sd->address_handler);
        kfree(sd);
@@ -643,28 +692,41 @@ static int sbp2_remove(struct device *dev)
 
 static void sbp2_reconnect(struct work_struct *work)
 {
-       struct sbp2_device *sd = container_of(work, struct sbp2_device, work);
+       struct sbp2_device *sd =
+               container_of(work, struct sbp2_device, work.work);
        struct fw_unit *unit = sd->unit;
        struct fw_device *device = fw_device(unit->device.parent);
        int generation, node_id, local_node_id;
 
-       fw_notify("in sbp2_reconnect, reconnecting to unit %s\n",
-                 unit->device.bus_id);
-
        generation    = device->card->generation;
        node_id       = device->node->node_id;
        local_node_id = device->card->local_node->node_id;
 
-       sbp2_send_management_orb(unit, node_id, generation,
-                                SBP2_RECONNECT_REQUEST, sd->login_id, NULL);
-
-       /* FIXME: handle reconnect failures. */
-
-       sbp2_cancel_orbs(unit);
+       if (sbp2_send_management_orb(unit, node_id, generation,
+                                    SBP2_RECONNECT_REQUEST,
+                                    sd->login_id, NULL) < 0) {
+               if (sd->retries++ < 5) {
+                       fw_error("reconnect attempt %d for %s failed, "
+                                "rescheduling\n",
+                                sd->retries, unit->device.bus_id);
+               } else {
+                       fw_error("failed to reconnect to %s\n",
+                                unit->device.bus_id);
+                       /* Fall back and try to log in again. */
+                       sd->retries = 0;
+                       PREPARE_DELAYED_WORK(&sd->work, sbp2_login);
+               }
+               schedule_delayed_work(&sd->work, DIV_ROUND_UP(HZ, 5));
+               return;
+       }
 
        sd->generation   = generation;
        sd->node_id      = node_id;
-       sd->address_high = (LOCAL_BUS | local_node_id) << 16;
+       sd->address_high = local_node_id << 16;
+
+       fw_notify("reconnected to unit %s\n", unit->device.bus_id);
+       sbp2_agent_reset(unit);
+       sbp2_cancel_orbs(unit);
 }
 
 static void sbp2_update(struct fw_unit *unit)
@@ -672,10 +734,9 @@ static void sbp2_update(struct fw_unit *unit)
        struct fw_device *device = fw_device(unit->device.parent);
        struct sbp2_device *sd = unit->device.driver_data;
 
+       sd->retries = 0;
        fw_device_enable_phys_dma(device);
-
-       INIT_WORK(&sd->work, sbp2_reconnect);
-       schedule_work(&sd->work);
+       schedule_delayed_work(&sd->work, 0);
 }
 
 #define SBP2_UNIT_SPEC_ID_ENTRY        0x0000609e
@@ -778,7 +839,7 @@ complete_command_orb(struct sbp2_orb *base_orb, struct sbp2_status *status)
                 * or when sending the write (less likely). */
                fw_notify("no command orb status, rcode=%d\n",
                          orb->base.rcode);
-               result = DID_ERROR;
+               result = DID_BUS_BUSY;
        }
 
        dma_unmap_single(device->card->device, orb->base.request_bus,
@@ -902,22 +963,20 @@ static int sbp2_scsi_queuecommand(struct scsi_cmnd *cmd, scsi_done_fn_t done)
         * transfer direction not handled. */
        if (cmd->sc_data_direction == DMA_BIDIRECTIONAL) {
                fw_error("Cannot handle DMA_BIDIRECTIONAL - rejecting command");
-               cmd->result = DID_ERROR << 16;
-               done(cmd);
-               return 0;
+               goto fail_alloc;
        }
 
        orb = kzalloc(sizeof *orb, GFP_ATOMIC);
        if (orb == NULL) {
                fw_notify("failed to alloc orb\n");
-               cmd->result = DID_NO_CONNECT << 16;
-               done(cmd);
-               return 0;
+               goto fail_alloc;
        }
 
        orb->base.request_bus =
                dma_map_single(device->card->device, &orb->request,
                               sizeof orb->request, DMA_TO_DEVICE);
+       if (dma_mapping_error(orb->base.request_bus))
+               goto fail_mapping;
 
        orb->unit = unit;
        orb->done = done;
@@ -948,9 +1007,7 @@ static int sbp2_scsi_queuecommand(struct scsi_cmnd *cmd, scsi_done_fn_t done)
                 * could we get the scsi or blk layer to do that by
                 * reporting our max supported block size? */
                fw_error("command > 64k\n");
-               cmd->result = DID_ERROR << 16;
-               done(cmd);
-               return 0;
+               goto fail_bufflen;
        } else if (cmd->request_bufflen > 0) {
                sbp2_command_orb_map_buffer(orb);
        }
@@ -967,6 +1024,28 @@ static int sbp2_scsi_queuecommand(struct scsi_cmnd *cmd, scsi_done_fn_t done)
                      sd->command_block_agent_address + SBP2_ORB_POINTER);
 
        return 0;
+
+ fail_bufflen:
+       dma_unmap_single(device->card->device, orb->base.request_bus,
+                        sizeof orb->request, DMA_TO_DEVICE);
+ fail_mapping:
+       kfree(orb);
+ fail_alloc:
+       cmd->result = DID_ERROR << 16;
+       done(cmd);
+       return 0;
+}
+
+static int sbp2_scsi_slave_alloc(struct scsi_device *sdev)
+{
+       struct fw_unit *unit = (struct fw_unit *)sdev->host->hostdata[0];
+       struct sbp2_device *sd = unit->device.driver_data;
+
+       sdev->allow_restart = 1;
+
+       if (sd->workarounds & SBP2_WORKAROUND_INQUIRY_36)
+               sdev->inquiry_len = 36;
+       return 0;
 }
 
 static int sbp2_scsi_slave_configure(struct scsi_device *sdev)
@@ -974,6 +1053,10 @@ static int sbp2_scsi_slave_configure(struct scsi_device *sdev)
        struct fw_unit *unit = (struct fw_unit *)sdev->host->hostdata[0];
        struct sbp2_device *sd = unit->device.driver_data;
 
+       sdev->use_10_for_rw = 1;
+
+       if (sdev->type == TYPE_ROM)
+               sdev->use_10_for_ms = 1;
        if (sdev->type == TYPE_DISK &&
            sd->workarounds & SBP2_WORKAROUND_MODE_SENSE_8)
                sdev->skip_ms_page_8 = 1;
@@ -1005,13 +1088,14 @@ static struct scsi_host_template scsi_driver_template = {
        .name                   = "SBP-2 IEEE-1394",
        .proc_name              = (char *)sbp2_driver_name,
        .queuecommand           = sbp2_scsi_queuecommand,
+       .slave_alloc            = sbp2_scsi_slave_alloc,
        .slave_configure        = sbp2_scsi_slave_configure,
        .eh_abort_handler       = sbp2_scsi_abort,
        .this_id                = -1,
        .sg_tablesize           = SG_ALL,
        .use_clustering         = ENABLE_CLUSTERING,
-       .cmd_per_lun            = 1,    /* SBP2_MAX_CMDS, */
-       .can_queue              = 1,            /* SBP2_MAX_CMDS, */
+       .cmd_per_lun            = 1,
+       .can_queue              = 1,
 };
 
 static int add_scsi_devices(struct fw_unit *unit)
@@ -1019,6 +1103,9 @@ static int add_scsi_devices(struct fw_unit *unit)
        struct sbp2_device *sd = unit->device.driver_data;
        int retval, lun;
 
+       if (sd->scsi_host != NULL)
+               return 0;
+
        sd->scsi_host = scsi_host_alloc(&scsi_driver_template,
                                        sizeof(unsigned long));
        if (sd->scsi_host == NULL) {
@@ -1051,8 +1138,11 @@ static void remove_scsi_devices(struct fw_unit *unit)
 {
        struct sbp2_device *sd = unit->device.driver_data;
 
-       scsi_remove_host(sd->scsi_host);
-       scsi_host_put(sd->scsi_host);
+       if (sd->scsi_host != NULL) {
+               scsi_remove_host(sd->scsi_host);
+               scsi_host_put(sd->scsi_host);
+       }
+       sd->scsi_host = NULL;
 }
 
 MODULE_AUTHOR("Kristian Hoegsberg <krh@bitplanet.net>");