/* -*- 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>
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
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);
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)
{
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);
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;
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. */
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;
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);
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)
{
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);
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;
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;
}
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);
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)
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
* 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,
* 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;
* 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);
}
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)
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;
.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)
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) {
{
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>");