summaryrefslogtreecommitdiff
path: root/drivers
diff options
context:
space:
mode:
authorAlan Cox <alan@lxorguk.ukuu.org.uk>2002-10-02 23:41:14 -0700
committerLinus Torvalds <torvalds@penguin.transmeta.com>2002-10-02 23:41:14 -0700
commit03f295368bcd594f502bc282f657a0d2d1346663 (patch)
tree3f73c94d8781f3bdc181818be407d5f2ba4f61f7 /drivers
parentc1a178bd20322fae7d3bdf4c2922998f6bb57efe (diff)
[PATCH] aacraid driver for 2.5
Forward port from 2.4
Diffstat (limited to 'drivers')
-rw-r--r--drivers/scsi/Config.in3
-rw-r--r--drivers/scsi/Makefile1
-rw-r--r--drivers/scsi/aacraid/Makefile10
-rw-r--r--drivers/scsi/aacraid/README42
-rw-r--r--drivers/scsi/aacraid/TODO4
-rw-r--r--drivers/scsi/aacraid/aachba.c1661
-rw-r--r--drivers/scsi/aacraid/aacraid.h1400
-rw-r--r--drivers/scsi/aacraid/commctrl.c432
-rw-r--r--drivers/scsi/aacraid/comminit.c341
-rw-r--r--drivers/scsi/aacraid/commsup.c948
-rw-r--r--drivers/scsi/aacraid/dpcsup.c201
-rw-r--r--drivers/scsi/aacraid/linit.c743
-rw-r--r--drivers/scsi/aacraid/rx.c409
-rw-r--r--drivers/scsi/aacraid/sa.c396
14 files changed, 6591 insertions, 0 deletions
diff --git a/drivers/scsi/Config.in b/drivers/scsi/Config.in
index 8d5e00d83cb7..e05d2905eece 100644
--- a/drivers/scsi/Config.in
+++ b/drivers/scsi/Config.in
@@ -51,6 +51,9 @@ fi
if [ "$CONFIG_EISA" = "y" ]; then
dep_tristate 'Adaptec AHA1740 support' CONFIG_SCSI_AHA1740 $CONFIG_SCSI
fi
+if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
+ dep_tristate 'Adaptec AACRAID support (EXPERIMENTAL)' CONFIG_SCSI_AACRAID $CONFIG_SCSI $CONFIG_PCI
+fi
source drivers/scsi/aic7xxx/Config.in
if [ "$CONFIG_SCSI_AIC7XXX" != "y" ]; then
dep_tristate 'Old Adaptec AIC7xxx support' CONFIG_SCSI_AIC7XXX_OLD $CONFIG_SCSI
diff --git a/drivers/scsi/Makefile b/drivers/scsi/Makefile
index d88c806426cb..f6cd4f29ce2d 100644
--- a/drivers/scsi/Makefile
+++ b/drivers/scsi/Makefile
@@ -58,6 +58,7 @@ obj-$(CONFIG_SCSI_AHA152X) += aha152x.o
obj-$(CONFIG_SCSI_AHA1542) += aha1542.o
obj-$(CONFIG_SCSI_AHA1740) += aha1740.o
obj-$(CONFIG_SCSI_AIC7XXX) += aic7xxx/
+obj-$(CONFIG_SCSI_AACRAID) += aacraid/
obj-$(CONFIG_SCSI_AIC7XXX_OLD) += aic7xxx_old.o
obj-$(CONFIG_SCSI_IPS) += ips.o
obj-$(CONFIG_SCSI_FD_MCS) += fd_mcs.o
diff --git a/drivers/scsi/aacraid/Makefile b/drivers/scsi/aacraid/Makefile
new file mode 100644
index 000000000000..1bea28d0ff6e
--- /dev/null
+++ b/drivers/scsi/aacraid/Makefile
@@ -0,0 +1,10 @@
+
+EXTRA_CFLAGS += -I$(TOPDIR)/drivers/scsi
+
+O_TARGET := aacraid.o
+obj-m := $(O_TARGET)
+
+obj-y := linit.o aachba.o commctrl.o comminit.o commsup.o \
+ dpcsup.o rx.o sa.o
+
+include $(TOPDIR)/Rules.make
diff --git a/drivers/scsi/aacraid/README b/drivers/scsi/aacraid/README
new file mode 100644
index 000000000000..9f73c6719b20
--- /dev/null
+++ b/drivers/scsi/aacraid/README
@@ -0,0 +1,42 @@
+AACRAID Driver for Linux (take two)
+
+Introduction
+-------------------------
+The aacraid driver adds support for Adaptec (http://www.adaptec.com)
+RAID controllers. This is a major rewrite from the original
+Adaptec supplied driver. It has signficantly cleaned up both the code
+and the running binary size (the module is less than half the size of
+the original).
+
+Supported Cards/Chipsets
+-------------------------
+ Dell Computer Corporation PERC 2 Quad Channel
+ Dell Computer Corporation PERC 2/Si
+ Dell Computer Corporation PERC 3/Si
+ Dell Computer Corporation PERC 3/Di
+ HP NetRAID-4M
+ ADAPTEC 2120S
+ ADAPTEC 2200S
+ ADAPTEC 5400S
+
+People
+-------------------------
+Alan Cox <alan@redhat.com>
+Christoph Hellwig <hch@infradead.org> (small cleanups/fixes)
+Matt Domsch <matt_domsch@dell.com> (revision ioctl, adapter messages)
+Deanna Bonds <deanna_bonds@adaptec.com> (non-DASD support, PAE fibs and 64 bit, added new adaptec controllers
+ added new ioctls, changed scsi interface to use new error handler,
+ increased the number of fibs and outstanding commands to a container)
+
+Original Driver
+-------------------------
+Adaptec Unix OEM Product Group
+
+Mailing List
+-------------------------
+None currently. Also note this is very different to Brian's original driver
+so don't expect him to support it.
+Adaptec does support this driver. Contact either tech support or deanna bonds.
+
+Original by Brian Boerner February 2001
+Rewritten by Alan Cox, November 2001
diff --git a/drivers/scsi/aacraid/TODO b/drivers/scsi/aacraid/TODO
new file mode 100644
index 000000000000..6f710224134c
--- /dev/null
+++ b/drivers/scsi/aacraid/TODO
@@ -0,0 +1,4 @@
+o Testing
+o More testing
+o Feature request: display the firmware/bios/etc revisions in the
+ /proc info
diff --git a/drivers/scsi/aacraid/aachba.c b/drivers/scsi/aacraid/aachba.c
new file mode 100644
index 000000000000..5afa780d5593
--- /dev/null
+++ b/drivers/scsi/aacraid/aachba.c
@@ -0,0 +1,1661 @@
+/*
+ * Adaptec AAC series RAID controller driver
+ * (c) Copyright 2001 Red Hat Inc. <alan@redhat.com>
+ *
+ * based on the old aacraid driver that is..
+ * Adaptec aacraid device driver for Linux.
+ * Copyright (c) 2000 Adaptec, Inc. (aacraid@adaptec.com)
+ *
+ * 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
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; see the file COPYING. If not, write to
+ * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+#include <linux/config.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/types.h>
+#include <linux/sched.h>
+#include <linux/pci.h>
+#include <linux/spinlock.h>
+#include <linux/slab.h>
+#include <linux/completion.h>
+#include <asm/semaphore.h>
+#include <asm/uaccess.h>
+#define MAJOR_NR SCSI_DISK0_MAJOR /* For DEVICE_NR() */
+#include <linux/blk.h>
+#include "scsi.h"
+#include "hosts.h"
+#include "sd.h"
+
+#include "aacraid.h"
+
+/* FIXME: We share this with sd.c - wants putting in one spot only */
+#define DEVICE_NR(device) (((major(device) & SD_MAJOR_MASK) << (8 - 4)) + (minor(device) >> 4))
+
+/* SCSI Commands */
+/* TODO: dmb - use the ones defined in include/scsi/scsi.h */
+
+#define SS_TEST 0x00 /* Test unit ready */
+#define SS_REZERO 0x01 /* Rezero unit */
+#define SS_REQSEN 0x03 /* Request Sense */
+#define SS_REASGN 0x07 /* Reassign blocks */
+#define SS_READ 0x08 /* Read 6 */
+#define SS_WRITE 0x0A /* Write 6 */
+#define SS_INQUIR 0x12 /* inquiry */
+#define SS_ST_SP 0x1B /* Start/Stop unit */
+#define SS_LOCK 0x1E /* prevent/allow medium removal */
+#define SS_RESERV 0x16 /* Reserve */
+#define SS_RELES 0x17 /* Release */
+#define SS_MODESEN 0x1A /* Mode Sense 6 */
+#define SS_RDCAP 0x25 /* Read Capacity */
+#define SM_READ 0x28 /* Read 10 */
+#define SM_WRITE 0x2A /* Write 10 */
+#define SS_SEEK 0x2B /* Seek */
+
+/* values for inqd_pdt: Peripheral device type in plain English */
+#define INQD_PDT_DA 0x00 /* Direct-access (DISK) device */
+#define INQD_PDT_PROC 0x03 /* Processor device */
+#define INQD_PDT_CHNGR 0x08 /* Changer (jukebox, scsi2) */
+#define INQD_PDT_COMM 0x09 /* Communication device (scsi2) */
+#define INQD_PDT_NOLUN2 0x1f /* Unknown Device (scsi2) */
+#define INQD_PDT_NOLUN 0x7f /* Logical Unit Not Present */
+
+#define INQD_PDT_DMASK 0x1F /* Peripheral Device Type Mask */
+#define INQD_PDT_QMASK 0xE0 /* Peripheral Device Qualifer Mask */
+
+#define TARGET_LUN_TO_CONTAINER(target, lun) (target)
+#define CONTAINER_TO_TARGET(cont) ((cont))
+#define CONTAINER_TO_LUN(cont) (0)
+
+#define MAX_FIB_DATA (sizeof(struct hw_fib) - sizeof(FIB_HEADER))
+
+#define MAX_DRIVER_SG_SEGMENT_COUNT 17
+
+/*
+ * Sense keys
+ */
+#define SENKEY_NO_SENSE 0x00
+#define SENKEY_UNDEFINED 0x01
+#define SENKEY_NOT_READY 0x02
+#define SENKEY_MEDIUM_ERR 0x03
+#define SENKEY_HW_ERR 0x04
+#define SENKEY_ILLEGAL 0x05
+#define SENKEY_ATTENTION 0x06
+#define SENKEY_PROTECTED 0x07
+#define SENKEY_BLANK 0x08
+#define SENKEY_V_UNIQUE 0x09
+#define SENKEY_CPY_ABORT 0x0A
+#define SENKEY_ABORT 0x0B
+#define SENKEY_EQUAL 0x0C
+#define SENKEY_VOL_OVERFLOW 0x0D
+#define SENKEY_MISCOMP 0x0E
+#define SENKEY_RESERVED 0x0F
+
+/*
+ * Sense codes
+ */
+
+#define SENCODE_NO_SENSE 0x00
+#define SENCODE_END_OF_DATA 0x00
+#define SENCODE_BECOMING_READY 0x04
+#define SENCODE_INIT_CMD_REQUIRED 0x04
+#define SENCODE_PARAM_LIST_LENGTH_ERROR 0x1A
+#define SENCODE_INVALID_COMMAND 0x20
+#define SENCODE_LBA_OUT_OF_RANGE 0x21
+#define SENCODE_INVALID_CDB_FIELD 0x24
+#define SENCODE_LUN_NOT_SUPPORTED 0x25
+#define SENCODE_INVALID_PARAM_FIELD 0x26
+#define SENCODE_PARAM_NOT_SUPPORTED 0x26
+#define SENCODE_PARAM_VALUE_INVALID 0x26
+#define SENCODE_RESET_OCCURRED 0x29
+#define SENCODE_LUN_NOT_SELF_CONFIGURED_YET 0x3E
+#define SENCODE_INQUIRY_DATA_CHANGED 0x3F
+#define SENCODE_SAVING_PARAMS_NOT_SUPPORTED 0x39
+#define SENCODE_DIAGNOSTIC_FAILURE 0x40
+#define SENCODE_INTERNAL_TARGET_FAILURE 0x44
+#define SENCODE_INVALID_MESSAGE_ERROR 0x49
+#define SENCODE_LUN_FAILED_SELF_CONFIG 0x4c
+#define SENCODE_OVERLAPPED_COMMAND 0x4E
+
+/*
+ * Additional sense codes
+ */
+
+#define ASENCODE_NO_SENSE 0x00
+#define ASENCODE_END_OF_DATA 0x05
+#define ASENCODE_BECOMING_READY 0x01
+#define ASENCODE_INIT_CMD_REQUIRED 0x02
+#define ASENCODE_PARAM_LIST_LENGTH_ERROR 0x00
+#define ASENCODE_INVALID_COMMAND 0x00
+#define ASENCODE_LBA_OUT_OF_RANGE 0x00
+#define ASENCODE_INVALID_CDB_FIELD 0x00
+#define ASENCODE_LUN_NOT_SUPPORTED 0x00
+#define ASENCODE_INVALID_PARAM_FIELD 0x00
+#define ASENCODE_PARAM_NOT_SUPPORTED 0x01
+#define ASENCODE_PARAM_VALUE_INVALID 0x02
+#define ASENCODE_RESET_OCCURRED 0x00
+#define ASENCODE_LUN_NOT_SELF_CONFIGURED_YET 0x00
+#define ASENCODE_INQUIRY_DATA_CHANGED 0x03
+#define ASENCODE_SAVING_PARAMS_NOT_SUPPORTED 0x00
+#define ASENCODE_DIAGNOSTIC_FAILURE 0x80
+#define ASENCODE_INTERNAL_TARGET_FAILURE 0x00
+#define ASENCODE_INVALID_MESSAGE_ERROR 0x00
+#define ASENCODE_LUN_FAILED_SELF_CONFIG 0x00
+#define ASENCODE_OVERLAPPED_COMMAND 0x00
+
+#define BYTE0(x) (unsigned char)(x)
+#define BYTE1(x) (unsigned char)((x) >> 8)
+#define BYTE2(x) (unsigned char)((x) >> 16)
+#define BYTE3(x) (unsigned char)((x) >> 24)
+
+/*------------------------------------------------------------------------------
+ * S T R U C T S / T Y P E D E F S
+ *----------------------------------------------------------------------------*/
+/* SCSI inquiry data */
+struct inquiry_data {
+ u8 inqd_pdt; /* Peripheral qualifier | Peripheral Device Type */
+ u8 inqd_dtq; /* RMB | Device Type Qualifier */
+ u8 inqd_ver; /* ISO version | ECMA version | ANSI-approved version */
+ u8 inqd_rdf; /* AENC | TrmIOP | Response data format */
+ u8 inqd_len; /* Additional length (n-4) */
+ u8 inqd_pad1[2]; /* Reserved - must be zero */
+ u8 inqd_pad2; /* RelAdr | WBus32 | WBus16 | Sync | Linked |Reserved| CmdQue | SftRe */
+ u8 inqd_vid[8]; /* Vendor ID */
+ u8 inqd_pid[16]; /* Product ID */
+ u8 inqd_prl[4]; /* Product Revision Level */
+};
+
+struct sense_data {
+ u8 error_code; /* 70h (current errors), 71h(deferred errors) */
+ u8 valid:1; /* A valid bit of one indicates that the information */
+ /* field contains valid information as defined in the
+ * SCSI-2 Standard.
+ */
+ u8 segment_number; /* Only used for COPY, COMPARE, or COPY AND VERIFY Commands */
+ u8 sense_key:4; /* Sense Key */
+ u8 reserved:1;
+ u8 ILI:1; /* Incorrect Length Indicator */
+ u8 EOM:1; /* End Of Medium - reserved for random access devices */
+ u8 filemark:1; /* Filemark - reserved for random access devices */
+
+ u8 information[4]; /* for direct-access devices, contains the unsigned
+ * logical block address or residue associated with
+ * the sense key
+ */
+ u8 add_sense_len; /* number of additional sense bytes to follow this field */
+ u8 cmnd_info[4]; /* not used */
+ u8 ASC; /* Additional Sense Code */
+ u8 ASCQ; /* Additional Sense Code Qualifier */
+ u8 FRUC; /* Field Replaceable Unit Code - not used */
+ u8 bit_ptr:3; /* indicates which byte of the CDB or parameter data
+ * was in error
+ */
+ u8 BPV:1; /* bit pointer valid (BPV): 1- indicates that
+ * the bit_ptr field has valid value
+ */
+ u8 reserved2:2;
+ u8 CD:1; /* command data bit: 1- illegal parameter in CDB.
+ * 0- illegal parameter in data.
+ */
+ u8 SKSV:1;
+ u8 field_ptr[2]; /* byte of the CDB or parameter data in error */
+};
+
+/*
+ * M O D U L E G L O B A L S
+ */
+
+static struct fsa_scsi_hba *fsa_dev[MAXIMUM_NUM_ADAPTERS]; /* SCSI Device Instance Pointers */
+static struct sense_data sense_data[MAXIMUM_NUM_CONTAINERS];
+static void get_sd_devname(int disknum, char *buffer);
+static unsigned long aac_build_sg(Scsi_Cmnd* scsicmd, struct sgmap* sgmap);
+static unsigned long aac_build_sg64(Scsi_Cmnd* scsicmd, struct sgmap64* psg);
+static int aac_send_srb_fib(Scsi_Cmnd* scsicmd);
+#ifdef AAC_DETAILED_STATUS_INFO
+static char *aac_get_status_string(u32 status);
+#endif
+
+/**
+ * aac_get_containers - list containers
+ * @common: adapter to probe
+ *
+ * Make a list of all containers on this controller
+ */
+int aac_get_containers(struct aac_dev *dev)
+{
+ struct fsa_scsi_hba *fsa_dev_ptr;
+ u32 index, status = 0;
+ struct aac_query_mount *dinfo;
+ struct aac_mount *dresp;
+ struct fib * fibptr;
+ unsigned instance;
+
+ fsa_dev_ptr = &(dev->fsa_dev);
+ instance = dev->scsi_host_ptr->unique_id;
+
+ if (!(fibptr = fib_alloc(dev)))
+ return -ENOMEM;
+
+ for (index = 0; index < MAXIMUM_NUM_CONTAINERS; index++) {
+ fib_init(fibptr);
+ dinfo = (struct aac_query_mount *) fib_data(fibptr);
+
+ dinfo->command = cpu_to_le32(VM_NameServe);
+ dinfo->count = cpu_to_le32(index);
+ dinfo->type = cpu_to_le32(FT_FILESYS);
+
+ status = fib_send(ContainerCommand,
+ fibptr,
+ sizeof (struct aac_query_mount),
+ FsaNormal,
+ 1, 1,
+ NULL, NULL);
+ if (status < 0 ) {
+ printk(KERN_WARNING "ProbeContainers: SendFIB failed.\n");
+ break;
+ }
+ dresp = (struct aac_mount *)fib_data(fibptr);
+
+ if ((le32_to_cpu(dresp->status) == ST_OK) &&
+ (le32_to_cpu(dresp->mnt[0].vol) != CT_NONE)) {
+ fsa_dev_ptr->valid[index] = 1;
+ fsa_dev_ptr->type[index] = le32_to_cpu(dresp->mnt[0].vol);
+ fsa_dev_ptr->size[index] = le32_to_cpu(dresp->mnt[0].capacity);
+ if (le32_to_cpu(dresp->mnt[0].state) & FSCS_READONLY)
+ fsa_dev_ptr->ro[index] = 1;
+ }
+ fib_complete(fibptr);
+ /*
+ * If there are no more containers, then stop asking.
+ */
+ if ((index + 1) >= le32_to_cpu(dresp->count))
+ break;
+ }
+ fib_free(fibptr);
+ fsa_dev[instance] = fsa_dev_ptr;
+ return status;
+}
+
+/**
+ * probe_container - query a logical volume
+ * @dev: device to query
+ * @cid: container identifier
+ *
+ * Queries the controller about the given volume. The volume information
+ * is updated in the struct fsa_scsi_hba structure rather than returned.
+ */
+
+static int probe_container(struct aac_dev *dev, int cid)
+{
+ struct fsa_scsi_hba *fsa_dev_ptr;
+ int status;
+ struct aac_query_mount *dinfo;
+ struct aac_mount *dresp;
+ struct fib * fibptr;
+ unsigned instance;
+
+ fsa_dev_ptr = &(dev->fsa_dev);
+ instance = dev->scsi_host_ptr->unique_id;
+
+ if (!(fibptr = fib_alloc(dev)))
+ return -ENOMEM;
+
+ fib_init(fibptr);
+
+ dinfo = (struct aac_query_mount *)fib_data(fibptr);
+
+ dinfo->command = cpu_to_le32(VM_NameServe);
+ dinfo->count = cpu_to_le32(cid);
+ dinfo->type = cpu_to_le32(FT_FILESYS);
+
+ status = fib_send(ContainerCommand,
+ fibptr,
+ sizeof(struct aac_query_mount),
+ FsaNormal,
+ 1, 1,
+ NULL, NULL);
+ if (status < 0) {
+ printk(KERN_WARNING "aacraid: probe_containers query failed.\n");
+ goto error;
+ }
+
+ dresp = (struct aac_mount *) fib_data(fibptr);
+
+ if ((le32_to_cpu(dresp->status) == ST_OK) &&
+ (le32_to_cpu(dresp->mnt[0].vol) != CT_NONE)) {
+ fsa_dev_ptr->valid[cid] = 1;
+ fsa_dev_ptr->type[cid] = le32_to_cpu(dresp->mnt[0].vol);
+ fsa_dev_ptr->size[cid] = le32_to_cpu(dresp->mnt[0].capacity);
+ if (le32_to_cpu(dresp->mnt[0].state) & FSCS_READONLY)
+ fsa_dev_ptr->ro[cid] = 1;
+ }
+
+error:
+ fib_complete(fibptr);
+ fib_free(fibptr);
+
+ return status;
+}
+
+/* Local Structure to set SCSI inquiry data strings */
+struct scsi_inq {
+ char vid[8]; /* Vendor ID */
+ char pid[16]; /* Product ID */
+ char prl[4]; /* Product Revision Level */
+};
+
+/**
+ * InqStrCopy - string merge
+ * @a: string to copy from
+ * @b: string to copy to
+ *
+ * Copy a String from one location to another
+ * without copying \0
+ */
+
+static void inqstrcpy(char *a, char *b)
+{
+
+ while(*a != (char)0)
+ *b++ = *a++;
+}
+
+static char *container_types[] = {
+ "None",
+ "Volume",
+ "Mirror",
+ "Stripe",
+ "RAID5",
+ "SSRW",
+ "SSRO",
+ "Morph",
+ "Legacy",
+ "RAID4",
+ "RAID10",
+ "RAID00",
+ "V-MIRRORS",
+ "PSEUDO R4",
+ "RAID50",
+ "Unknown"
+};
+
+
+
+/* Function: setinqstr
+ *
+ * Arguments: [1] pointer to void [1] int
+ *
+ * Purpose: Sets SCSI inquiry data strings for vendor, product
+ * and revision level. Allows strings to be set in platform dependant
+ * files instead of in OS dependant driver source.
+ */
+
+static void setinqstr(int devtype, void *data, int tindex)
+{
+ struct scsi_inq *str;
+ char *findit;
+ struct aac_driver_ident *mp;
+
+ mp = aac_get_driver_ident(devtype);
+
+ str = (struct scsi_inq *)(data); /* cast data to scsi inq block */
+
+ inqstrcpy (mp->vname, str->vid);
+ inqstrcpy (mp->model, str->pid); /* last six chars reserved for vol type */
+
+ findit = str->pid;
+
+ for ( ; *findit != ' '; findit++); /* walk till we find a space then incr by 1 */
+ findit++;
+
+ if (tindex < (sizeof(container_types)/sizeof(char *))){
+ inqstrcpy (container_types[tindex], findit);
+ }
+ inqstrcpy ("V1.0", str->prl);
+}
+
+void set_sense(u8 *sense_buf, u8 sense_key, u8 sense_code,
+ u8 a_sense_code, u8 incorrect_length,
+ u8 bit_pointer, u16 field_pointer,
+ u32 residue)
+{
+ sense_buf[0] = 0xF0; /* Sense data valid, err code 70h (current error) */
+ sense_buf[1] = 0; /* Segment number, always zero */
+
+ if (incorrect_length) {
+ sense_buf[2] = sense_key | 0x20; /* Set ILI bit | sense key */
+ sense_buf[3] = BYTE3(residue);
+ sense_buf[4] = BYTE2(residue);
+ sense_buf[5] = BYTE1(residue);
+ sense_buf[6] = BYTE0(residue);
+ } else
+ sense_buf[2] = sense_key; /* Sense key */
+
+ if (sense_key == SENKEY_ILLEGAL)
+ sense_buf[7] = 10; /* Additional sense length */
+ else
+ sense_buf[7] = 6; /* Additional sense length */
+
+ sense_buf[12] = sense_code; /* Additional sense code */
+ sense_buf[13] = a_sense_code; /* Additional sense code qualifier */
+ if (sense_key == SENKEY_ILLEGAL) {
+ sense_buf[15] = 0;
+
+ if (sense_code == SENCODE_INVALID_PARAM_FIELD)
+ sense_buf[15] = 0x80; /* Std sense key specific field */
+ /* Illegal parameter is in the parameter block */
+
+ if (sense_code == SENCODE_INVALID_CDB_FIELD)
+ sense_buf[15] = 0xc0; /* Std sense key specific field */
+ /* Illegal parameter is in the CDB block */
+ sense_buf[15] |= bit_pointer;
+ sense_buf[16] = field_pointer >> 8; /* MSB */
+ sense_buf[17] = field_pointer; /* LSB */
+ }
+}
+
+static void aac_io_done(Scsi_Cmnd * scsicmd)
+{
+ unsigned long cpu_flags;
+ spin_lock_irqsave(scsicmd->host->host_lock, cpu_flags);
+ scsicmd->scsi_done(scsicmd);
+ spin_unlock_irqrestore(scsicmd->host->host_lock, cpu_flags);
+}
+
+static void __aac_io_done(Scsi_Cmnd * scsicmd)
+{
+ scsicmd->scsi_done(scsicmd);
+}
+
+int aac_get_adapter_info(struct aac_dev* dev)
+{
+ struct fib* fibptr;
+ struct aac_adapter_info* info;
+ int rcode;
+ u32 tmp;
+ if (!(fibptr = fib_alloc(dev)))
+ return -ENOMEM;
+
+ fib_init(fibptr);
+ info = (struct aac_adapter_info*) fib_data(fibptr);
+
+ memset(info,0,sizeof(struct aac_adapter_info));
+
+ rcode = fib_send(RequestAdapterInfo,
+ fibptr,
+ sizeof(struct aac_adapter_info),
+ FsaNormal,
+ 1, 1,
+ NULL,
+ NULL);
+
+ memcpy(&dev->adapter_info, info, sizeof(struct aac_adapter_info));
+
+ tmp = dev->adapter_info.kernelrev;
+ printk(KERN_INFO "%s%d: kernel %d.%d.%d build %d\n",
+ dev->name, dev->id,
+ tmp>>24,(tmp>>16)&0xff,(tmp>>8)&0xff,
+ dev->adapter_info.kernelbuild);
+ tmp = dev->adapter_info.monitorrev;
+ printk(KERN_INFO "%s%d: monitor %d.%d.%d build %d\n",
+ dev->name, dev->id,
+ tmp>>24,(tmp>>16)&0xff,(tmp>>8)&0xff,
+ dev->adapter_info.monitorbuild);
+ tmp = dev->adapter_info.biosrev;
+ printk(KERN_INFO "%s%d: bios %d.%d.%d build %d\n",
+ dev->name, dev->id,
+ tmp>>24,(tmp>>16)&0xff,(tmp>>8)&0xff,
+ dev->adapter_info.biosbuild);
+ printk(KERN_INFO "%s%d: serial %x%x\n",
+ dev->name, dev->id,
+ dev->adapter_info.serial[0],
+ dev->adapter_info.serial[1]);
+ dev->pae_support = 0;
+ dev->nondasd_support = 0;
+ if( BITS_PER_LONG >= 64 &&
+ (dev->adapter_info.options & AAC_OPT_SGMAP_HOST64)){
+ printk(KERN_INFO "%s%d: 64 Bit PAE enabled\n", dev->name, dev->id);
+ dev->pae_support = 1;
+ }
+ /* TODO - dmb temporary until fw can set this bit */
+ dev->pae_support = (BITS_PER_LONG >= 64);
+ if(dev->pae_support != 0) {
+ printk(KERN_INFO "%s%d: 64 Bit PAE enabled\n", dev->name, dev->id);
+ }
+
+ if(dev->adapter_info.options & AAC_OPT_NONDASD){
+ dev->nondasd_support = 1;
+ }
+ return rcode;
+}
+
+
+static void read_callback(void *context, struct fib * fibptr)
+{
+ struct aac_dev *dev;
+ struct aac_read_reply *readreply;
+ Scsi_Cmnd *scsicmd;
+ u32 lba;
+ u32 cid;
+
+ scsicmd = (Scsi_Cmnd *) context;
+
+ dev = (struct aac_dev *)scsicmd->host->hostdata;
+ cid =TARGET_LUN_TO_CONTAINER(scsicmd->target, scsicmd->lun);
+
+ lba = ((scsicmd->cmnd[1] & 0x1F) << 16) | (scsicmd->cmnd[2] << 8) | scsicmd->cmnd[3];
+ dprintk((KERN_DEBUG "read_callback[cpu %d]: lba = %d, t = %ld.\n", smp_processor_id(), lba, jiffies));
+
+ if (fibptr == NULL)
+ BUG();
+
+ if(scsicmd->use_sg)
+ pci_unmap_sg(dev->pdev,
+ (struct scatterlist *)scsicmd->buffer,
+ scsicmd->use_sg,
+ scsi_to_pci_dma_dir(scsicmd->sc_data_direction));
+ else if(scsicmd->request_bufflen)
+ pci_unmap_single(dev->pdev, (dma_addr_t)(unsigned long)scsicmd->SCp.ptr,
+ scsicmd->request_bufflen,
+ scsi_to_pci_dma_dir(scsicmd->sc_data_direction));
+ readreply = (struct aac_read_reply *)fib_data(fibptr);
+ if (le32_to_cpu(readreply->status) == ST_OK)
+ scsicmd->result = DID_OK << 16 | COMMAND_COMPLETE << 8 | GOOD;
+ else {
+ printk(KERN_WARNING "read_callback: read failed, status = %d\n", readreply->status);
+ scsicmd->result = DID_OK << 16 | COMMAND_COMPLETE << 8 | CHECK_CONDITION;
+ set_sense((u8 *) &sense_data[cid],
+ SENKEY_HW_ERR,
+ SENCODE_INTERNAL_TARGET_FAILURE,
+ ASENCODE_INTERNAL_TARGET_FAILURE, 0, 0,
+ 0, 0);
+ }
+ fib_complete(fibptr);
+ fib_free(fibptr);
+
+ aac_io_done(scsicmd);
+}
+
+static void write_callback(void *context, struct fib * fibptr)
+{
+ struct aac_dev *dev;
+ struct aac_write_reply *writereply;
+ Scsi_Cmnd *scsicmd;
+ u32 lba;
+ u32 cid;
+
+ scsicmd = (Scsi_Cmnd *) context;
+ dev = (struct aac_dev *)scsicmd->host->hostdata;
+ cid = TARGET_LUN_TO_CONTAINER(scsicmd->target, scsicmd->lun);
+
+ lba = ((scsicmd->cmnd[1] & 0x1F) << 16) | (scsicmd->cmnd[2] << 8) | scsicmd->cmnd[3];
+ dprintk((KERN_DEBUG "write_callback[cpu %d]: lba = %d, t = %ld.\n", smp_processor_id(), lba, jiffies));
+ if (fibptr == NULL)
+ BUG();
+
+ if(scsicmd->use_sg)
+ pci_unmap_sg(dev->pdev,
+ (struct scatterlist *)scsicmd->buffer,
+ scsicmd->use_sg,
+ scsi_to_pci_dma_dir(scsicmd->sc_data_direction));
+ else if(scsicmd->request_bufflen)
+ pci_unmap_single(dev->pdev, (dma_addr_t)(unsigned long)scsicmd->SCp.ptr,
+ scsicmd->request_bufflen,
+ scsi_to_pci_dma_dir(scsicmd->sc_data_direction));
+
+ writereply = (struct aac_write_reply *) fib_data(fibptr);
+ if (le32_to_cpu(writereply->status) == ST_OK)
+ scsicmd->result = DID_OK << 16 | COMMAND_COMPLETE << 8 | GOOD;
+ else {
+ printk(KERN_WARNING "write_callback: write failed, status = %d\n", writereply->status);
+ scsicmd->result = DID_OK << 16 | COMMAND_COMPLETE << 8 | CHECK_CONDITION;
+ set_sense((u8 *) &sense_data[cid],
+ SENKEY_HW_ERR,
+ SENCODE_INTERNAL_TARGET_FAILURE,
+ ASENCODE_INTERNAL_TARGET_FAILURE, 0, 0,
+ 0, 0);
+ }
+
+ fib_complete(fibptr);
+ fib_free(fibptr);
+ aac_io_done(scsicmd);
+}
+
+int aac_read(Scsi_Cmnd * scsicmd, int cid)
+{
+ u32 lba;
+ u32 count;
+ int status;
+
+ u16 fibsize;
+ struct aac_dev *dev;
+ struct fib * cmd_fibcontext;
+
+ dev = (struct aac_dev *)scsicmd->host->hostdata;
+ /*
+ * Get block address and transfer length
+ */
+ if (scsicmd->cmnd[0] == SS_READ) /* 6 byte command */
+ {
+ dprintk((KERN_DEBUG "aachba: received a read(6) command on target %d.\n", cid));
+
+ lba = ((scsicmd->cmnd[1] & 0x1F) << 16) | (scsicmd->cmnd[2] << 8) | scsicmd->cmnd[3];
+ count = scsicmd->cmnd[4];
+
+ if (count == 0)
+ count = 256;
+ } else {
+ dprintk((KERN_DEBUG "aachba: received a read(10) command on target %d.\n", cid));
+
+ lba = (scsicmd->cmnd[2] << 24) | (scsicmd->cmnd[3] << 16) | (scsicmd->cmnd[4] << 8) | scsicmd->cmnd[5];
+ count = (scsicmd->cmnd[7] << 8) | scsicmd->cmnd[8];
+ }
+ dprintk((KERN_DEBUG "aac_read[cpu %d]: lba = %u, t = %ld.\n", smp_processor_id(), lba, jiffies));
+ /*
+ * Alocate and initialize a Fib
+ */
+ if (!(cmd_fibcontext = fib_alloc(dev))) {
+ scsicmd->result = DID_ERROR << 16;
+ aac_io_done(scsicmd);
+ return (-1);
+ }
+
+ fib_init(cmd_fibcontext);
+
+ if(dev->pae_support == 1){
+ struct aac_read64 *readcmd;
+ readcmd = (struct aac_read64 *) fib_data(cmd_fibcontext);
+ readcmd->command = cpu_to_le32(VM_CtHostRead64);
+ readcmd->cid = cpu_to_le16(cid);
+ readcmd->sector_count = cpu_to_le16(count);
+ readcmd->block = cpu_to_le32(lba);
+ readcmd->pad = cpu_to_le16(0);
+ readcmd->flags = cpu_to_le16(0);
+
+ aac_build_sg64(scsicmd, &readcmd->sg);
+ if(readcmd->sg.count > MAX_DRIVER_SG_SEGMENT_COUNT)
+ BUG();
+ fibsize = sizeof(struct aac_read64) + ((readcmd->sg.count - 1) * sizeof (struct sgentry64));
+ /*
+ * Now send the Fib to the adapter
+ */
+ status = fib_send(ContainerCommand64,
+ cmd_fibcontext,
+ fibsize,
+ FsaNormal,
+ 0, 1,
+ (fib_callback) read_callback,
+ (void *) scsicmd);
+ } else {
+ struct aac_read *readcmd;
+ readcmd = (struct aac_read *) fib_data(cmd_fibcontext);
+ readcmd->command = cpu_to_le32(VM_CtBlockRead);
+ readcmd->cid = cpu_to_le32(cid);
+ readcmd->block = cpu_to_le32(lba);
+ readcmd->count = cpu_to_le32(count * 512);
+
+ if (count * 512 > (64 * 1024))
+ BUG();
+
+ aac_build_sg(scsicmd, &readcmd->sg);
+ if(readcmd->sg.count > MAX_DRIVER_SG_SEGMENT_COUNT)
+ BUG();
+ fibsize = sizeof(struct aac_read) + ((readcmd->sg.count - 1) * sizeof (struct sgentry));
+ /*
+ * Now send the Fib to the adapter
+ */
+ status = fib_send(ContainerCommand,
+ cmd_fibcontext,
+ fibsize,
+ FsaNormal,
+ 0, 1,
+ (fib_callback) read_callback,
+ (void *) scsicmd);
+ }
+
+
+ /*
+ * Check that the command queued to the controller
+ */
+ if (status == -EINPROGRESS)
+ return 0;
+
+ printk(KERN_WARNING "aac_read: fib_send failed with status: %d.\n", status);
+ /*
+ * For some reason, the Fib didn't queue, return QUEUE_FULL
+ */
+ scsicmd->result = DID_OK << 16 | COMMAND_COMPLETE << 8 | QUEUE_FULL;
+ aac_io_done(scsicmd);
+ fib_complete(cmd_fibcontext);
+ fib_free(cmd_fibcontext);
+ return -1;
+}
+
+static int aac_write(Scsi_Cmnd * scsicmd, int cid)
+{
+ u32 lba;
+ u32 count;
+ int status;
+ u16 fibsize;
+ struct aac_dev *dev;
+ struct fib * cmd_fibcontext;
+
+ dev = (struct aac_dev *)scsicmd->host->hostdata;
+ /*
+ * Get block address and transfer length
+ */
+ if (scsicmd->cmnd[0] == SS_WRITE) /* 6 byte command */
+ {
+ lba = ((scsicmd->cmnd[1] & 0x1F) << 16) | (scsicmd->cmnd[2] << 8) | scsicmd->cmnd[3];
+ count = scsicmd->cmnd[4];
+ if (count == 0)
+ count = 256;
+ } else {
+ dprintk((KERN_DEBUG "aachba: received a write(10) command on target %d.\n", cid));
+ lba = (scsicmd->cmnd[2] << 24) | (scsicmd->cmnd[3] << 16) | (scsicmd->cmnd[4] << 8) | scsicmd->cmnd[5];
+ count = (scsicmd->cmnd[7] << 8) | scsicmd->cmnd[8];
+ }
+ dprintk((KERN_DEBUG "aac_write[cpu %d]: lba = %lu, t = %ld.\n", smp_processor_id(), lba, jiffies));
+ /*
+ * Allocate and initialize a Fib then setup a BlockWrite command
+ */
+ if (!(cmd_fibcontext = fib_alloc(dev))) {
+ scsicmd->result = DID_ERROR << 16;
+ aac_io_done(scsicmd);
+ return -1;
+ }
+ fib_init(cmd_fibcontext);
+
+ if(dev->pae_support == 1)
+ {
+ struct aac_write64 *writecmd;
+ writecmd = (struct aac_write64 *) fib_data(cmd_fibcontext);
+ writecmd->command = cpu_to_le32(VM_CtHostWrite64);
+ writecmd->cid = cpu_to_le16(cid);
+ writecmd->sector_count = cpu_to_le16(count);
+ writecmd->block = cpu_to_le32(lba);
+ writecmd->pad = cpu_to_le16(0);
+ writecmd->flags = cpu_to_le16(0);
+
+ aac_build_sg64(scsicmd, &writecmd->sg);
+ if(writecmd->sg.count > MAX_DRIVER_SG_SEGMENT_COUNT)
+ BUG();
+ fibsize = sizeof(struct aac_write64) + ((writecmd->sg.count - 1) * sizeof (struct sgentry64));
+ /*
+ * Now send the Fib to the adapter
+ */
+ status = fib_send(ContainerCommand64,
+ cmd_fibcontext,
+ fibsize,
+ FsaNormal,
+ 0, 1,
+ (fib_callback) write_callback,
+ (void *) scsicmd);
+ }
+ else
+ {
+ struct aac_write *writecmd;
+ writecmd = (struct aac_write *) fib_data(cmd_fibcontext);
+ writecmd->command = cpu_to_le32(VM_CtBlockWrite);
+ writecmd->cid = cpu_to_le32(cid);
+ writecmd->block = cpu_to_le32(lba);
+ writecmd->count = cpu_to_le32(count * 512);
+ writecmd->sg.count = cpu_to_le32(1);
+ /* ->stable is not used - it did mean which type of write */
+
+ if (count * 512 > (64 * 1024))
+ BUG();
+ aac_build_sg(scsicmd, &writecmd->sg);
+ if(writecmd->sg.count > MAX_DRIVER_SG_SEGMENT_COUNT)
+ BUG();
+ fibsize = sizeof(struct aac_write) + ((writecmd->sg.count - 1) * sizeof (struct sgentry));
+ /*
+ * Now send the Fib to the adapter
+ */
+ status = fib_send(ContainerCommand,
+ cmd_fibcontext,
+ fibsize,
+ FsaNormal,
+ 0, 1,
+ (fib_callback) write_callback,
+ (void *) scsicmd);
+ }
+
+ /*
+ * Check that the command queued to the controller
+ */
+ if (status == -EINPROGRESS)
+ return 0;
+
+ printk(KERN_WARNING "aac_write: fib_send failed with status: %d\n", status);
+ /*
+ * For some reason, the Fib didn't queue, return QUEUE_FULL
+ */
+ scsicmd->result = DID_OK << 16 | COMMAND_COMPLETE << 8 | QUEUE_FULL;
+ aac_io_done(scsicmd);
+
+ fib_complete(cmd_fibcontext);
+ fib_free(cmd_fibcontext);
+ return -1;
+}
+
+
+/**
+ * aac_scsi_cmd() - Process SCSI command
+ * @scsicmd: SCSI command block
+ * @wait: 1 if the user wants to await completion
+ *
+ * Emulate a SCSI command and queue the required request for the
+ * aacraid firmware.
+ */
+
+int aac_scsi_cmd(Scsi_Cmnd * scsicmd)
+{
+ u32 cid = 0;
+ struct fsa_scsi_hba *fsa_dev_ptr;
+ int cardtype;
+ int ret;
+ struct aac_dev *dev = (struct aac_dev *)scsicmd->host->hostdata;
+
+ cardtype = dev->cardtype;
+
+ fsa_dev_ptr = fsa_dev[scsicmd->host->unique_id];
+
+ /*
+ * If the bus, target or lun is out of range, return fail
+ * Test does not apply to ID 16, the pseudo id for the controller
+ * itself.
+ */
+ if (scsicmd->target != scsicmd->host->this_id) {
+ if ((scsicmd->channel == 0) ){
+ if( (scsicmd->target >= AAC_MAX_TARGET) || (scsicmd->lun != 0)){
+ scsicmd->result = DID_NO_CONNECT << 16;
+ __aac_io_done(scsicmd);
+ return 0;
+ }
+ cid = TARGET_LUN_TO_CONTAINER(scsicmd->target, scsicmd->lun);
+
+ /*
+ * If the target container doesn't exist, it may have
+ * been newly created
+ */
+ if (fsa_dev_ptr->valid[cid] == 0) {
+ switch (scsicmd->cmnd[0]) {
+ case SS_INQUIR:
+ case SS_RDCAP:
+ case SS_TEST:
+ spin_unlock_irq(scsicmd->host->host_lock);
+ probe_container(dev, cid);
+ spin_lock_irq(scsicmd->host->host_lock);
+ if (fsa_dev_ptr->valid[cid] == 0) {
+ scsicmd->result = DID_NO_CONNECT << 16;
+ __aac_io_done(scsicmd);
+ return 0;
+ }
+ default:
+ break;
+ }
+ }
+ /*
+ * If the target container still doesn't exist,
+ * return failure
+ */
+ if (fsa_dev_ptr->valid[cid] == 0) {
+ scsicmd->result = DID_BAD_TARGET << 16;
+ __aac_io_done(scsicmd);
+ return -1;
+ }
+ } else { /* check for physical non-dasd devices */
+ if(dev->nondasd_support == 1){
+ return aac_send_srb_fib(scsicmd);
+ } else {
+ scsicmd->result = DID_NO_CONNECT << 16;
+ __aac_io_done(scsicmd);
+ return 0;
+ }
+ }
+ }
+ /*
+ * else Command for the controller itself
+ */
+ else if ((scsicmd->cmnd[0] != SS_INQUIR) && /* only INQUIRY & TUR cmnd supported for controller */
+ (scsicmd->cmnd[0] != SS_TEST))
+ {
+ dprintk((KERN_WARNING "Only INQUIRY & TUR command supported for controller, rcvd = 0x%x.\n", scsicmd->cmnd[0]));
+ scsicmd->result = DID_OK << 16 | COMMAND_COMPLETE << 8 | CHECK_CONDITION;
+ set_sense((u8 *) &sense_data[cid],
+ SENKEY_ILLEGAL,
+ SENCODE_INVALID_COMMAND,
+ ASENCODE_INVALID_COMMAND, 0, 0, 0, 0);
+ __aac_io_done(scsicmd);
+ return -1;
+ }
+
+
+ /* Handle commands here that don't really require going out to the adapter */
+ switch (scsicmd->cmnd[0]) {
+ case SS_INQUIR:
+ {
+ struct inquiry_data *inq_data_ptr;
+
+ dprintk((KERN_DEBUG "INQUIRY command, ID: %d.\n", scsicmd->target));
+ inq_data_ptr = (struct inquiry_data *)scsicmd->request_buffer;
+ memset(inq_data_ptr, 0, sizeof (struct inquiry_data));
+
+ inq_data_ptr->inqd_ver = 2; /* claim compliance to SCSI-2 */
+ inq_data_ptr->inqd_dtq = 0x80; /* set RMB bit to one indicating that the medium is removable */
+ inq_data_ptr->inqd_rdf = 2; /* A response data format value of two indicates that the data shall be in the format specified in SCSI-2 */
+ inq_data_ptr->inqd_len = 31;
+ /*Format for "pad2" is RelAdr | WBus32 | WBus16 | Sync | Linked |Reserved| CmdQue | SftRe */
+ inq_data_ptr->inqd_pad2= 0x32 ; /*WBus16|Sync|CmdQue */
+ /*
+ * Set the Vendor, Product, and Revision Level
+ * see: <vendor>.c i.e. aac.c
+ */
+ setinqstr(cardtype, (void *) (inq_data_ptr->inqd_vid), fsa_dev_ptr->type[cid]);
+ if (scsicmd->target == scsicmd->host->this_id)
+ inq_data_ptr->inqd_pdt = INQD_PDT_PROC; /* Processor device */
+ else
+ inq_data_ptr->inqd_pdt = INQD_PDT_DA; /* Direct/random access device */
+ scsicmd->result = DID_OK << 16 | COMMAND_COMPLETE << 8 | GOOD;
+ __aac_io_done(scsicmd);
+ return 0;
+ }
+ case SS_RDCAP:
+ {
+ int capacity;
+ char *cp;
+
+ dprintk((KERN_DEBUG "READ CAPACITY command.\n"));
+ capacity = fsa_dev_ptr->size[cid] - 1;
+ cp = scsicmd->request_buffer;
+ cp[0] = (capacity >> 24) & 0xff;
+ cp[1] = (capacity >> 16) & 0xff;
+ cp[2] = (capacity >> 8) & 0xff;
+ cp[3] = (capacity >> 0) & 0xff;
+ cp[4] = 0;
+ cp[5] = 0;
+ cp[6] = 2;
+ cp[7] = 0;
+
+ scsicmd->result = DID_OK << 16 | COMMAND_COMPLETE << 8 | GOOD;
+ __aac_io_done(scsicmd);
+
+ return 0;
+ }
+
+ case SS_MODESEN:
+ {
+ char *mode_buf;
+
+ dprintk((KERN_DEBUG "MODE SENSE command.\n"));
+ mode_buf = scsicmd->request_buffer;
+ mode_buf[0] = 0; /* Mode data length (MSB) */
+ mode_buf[1] = 6; /* Mode data length (LSB) */
+ mode_buf[2] = 0; /* Medium type - default */
+ mode_buf[3] = 0; /* Device-specific param, bit 8: 0/1 = write enabled/protected */
+ mode_buf[4] = 0; /* reserved */
+ mode_buf[5] = 0; /* reserved */
+ mode_buf[6] = 0; /* Block descriptor length (MSB) */
+ mode_buf[7] = 0; /* Block descriptor length (LSB) */
+
+ scsicmd->result = DID_OK << 16 | COMMAND_COMPLETE << 8 | GOOD;
+ __aac_io_done(scsicmd);
+
+ return 0;
+ }
+ case SS_REQSEN:
+ dprintk((KERN_DEBUG "REQUEST SENSE command.\n"));
+ memcpy(scsicmd->sense_buffer, &sense_data[cid], sizeof (struct sense_data));
+ memset(&sense_data[cid], 0, sizeof (struct sense_data));
+ scsicmd->result = DID_OK << 16 | COMMAND_COMPLETE << 8 | GOOD;
+ __aac_io_done(scsicmd);
+ return (0);
+
+ case SS_LOCK:
+ dprintk((KERN_DEBUG "LOCK command.\n"));
+ if (scsicmd->cmnd[4])
+ fsa_dev_ptr->locked[cid] = 1;
+ else
+ fsa_dev_ptr->locked[cid] = 0;
+
+ scsicmd->result = DID_OK << 16 | COMMAND_COMPLETE << 8 | GOOD;
+ __aac_io_done(scsicmd);
+ return 0;
+ /*
+ * These commands are all No-Ops
+ */
+ case SS_TEST:
+ case SS_RESERV:
+ case SS_RELES:
+ case SS_REZERO:
+ case SS_REASGN:
+ case SS_SEEK:
+ case SS_ST_SP:
+ scsicmd->result = DID_OK << 16 | COMMAND_COMPLETE << 8 | GOOD;
+ __aac_io_done(scsicmd);
+ return (0);
+ }
+
+ switch (scsicmd->cmnd[0])
+ {
+ case SS_READ:
+ case SM_READ:
+ /*
+ * Hack to keep track of ordinal number of the device that
+ * corresponds to a container. Needed to convert
+ * containers to /dev/sd device names
+ */
+
+ spin_unlock_irq(scsicmd->host->host_lock);
+ fsa_dev_ptr->devno[cid] = DEVICE_NR(scsicmd->sc_request->sr_request->rq_dev);
+ ret = aac_read(scsicmd, cid);
+ spin_lock_irq(scsicmd->host->host_lock);
+ return ret;
+
+ case SS_WRITE:
+ case SM_WRITE:
+ spin_unlock_irq(scsicmd->host->host_lock);
+ ret = aac_write(scsicmd, cid);
+ spin_lock_irq(scsicmd->host->host_lock);
+ return ret;
+ default:
+ /*
+ * Unhandled commands
+ */
+ printk(KERN_WARNING "Unhandled SCSI Command: 0x%x.\n", scsicmd->cmnd[0]);
+ scsicmd->result = DID_OK << 16 | COMMAND_COMPLETE << 8 | CHECK_CONDITION;
+ set_sense((u8 *) &sense_data[cid],
+ SENKEY_ILLEGAL, SENCODE_INVALID_COMMAND,
+ ASENCODE_INVALID_COMMAND, 0, 0, 0, 0);
+ __aac_io_done(scsicmd);
+ return -1;
+ }
+}
+
+static int query_disk(struct aac_dev *dev, void *arg)
+{
+ struct aac_query_disk qd;
+ struct fsa_scsi_hba *fsa_dev_ptr;
+
+ fsa_dev_ptr = &(dev->fsa_dev);
+ if (copy_from_user(&qd, arg, sizeof (struct aac_query_disk)))
+ return -EFAULT;
+ if (qd.cnum == -1)
+ qd.cnum = TARGET_LUN_TO_CONTAINER(qd.target, qd.lun);
+ else if ((qd.bus == -1) && (qd.target == -1) && (qd.lun == -1))
+ {
+ if (qd.cnum < 0 || qd.cnum > MAXIMUM_NUM_CONTAINERS)
+ return -EINVAL;
+ qd.instance = dev->scsi_host_ptr->host_no;
+ qd.bus = 0;
+ qd.target = CONTAINER_TO_TARGET(qd.cnum);
+ qd.lun = CONTAINER_TO_LUN(qd.cnum);
+ }
+ else return -EINVAL;
+
+ qd.valid = fsa_dev_ptr->valid[qd.cnum];
+ qd.locked = fsa_dev_ptr->locked[qd.cnum];
+ qd.deleted = fsa_dev_ptr->deleted[qd.cnum];
+
+ if (fsa_dev_ptr->devno[qd.cnum] == -1)
+ qd.unmapped = 1;
+ else
+ qd.unmapped = 0;
+
+ get_sd_devname(fsa_dev_ptr->devno[qd.cnum], qd.name);
+
+ if (copy_to_user(arg, &qd, sizeof (struct aac_query_disk)))
+ return -EFAULT;
+ return 0;
+}
+
+static void get_sd_devname(int disknum, char *buffer)
+{
+ if (disknum < 0) {
+ sprintf(buffer, "%s", "");
+ return;
+ }
+
+ if (disknum < 26)
+ sprintf(buffer, "sd%c", 'a' + disknum);
+ else {
+ unsigned int min1;
+ unsigned int min2;
+ /*
+ * For larger numbers of disks, we need to go to a new
+ * naming scheme.
+ */
+ min1 = disknum / 26;
+ min2 = disknum % 26;
+ sprintf(buffer, "sd%c%c", 'a' + min1 - 1, 'a' + min2);
+ }
+}
+
+static int force_delete_disk(struct aac_dev *dev, void *arg)
+{
+ struct aac_delete_disk dd;
+ struct fsa_scsi_hba *fsa_dev_ptr;
+
+ fsa_dev_ptr = &(dev->fsa_dev);
+
+ if (copy_from_user(&dd, arg, sizeof (struct aac_delete_disk)))
+ return -EFAULT;
+
+ if (dd.cnum > MAXIMUM_NUM_CONTAINERS)
+ return -EINVAL;
+ /*
+ * Mark this container as being deleted.
+ */
+ fsa_dev_ptr->deleted[dd.cnum] = 1;
+ /*
+ * Mark the container as no longer valid
+ */
+ fsa_dev_ptr->valid[dd.cnum] = 0;
+ return 0;
+}
+
+static int delete_disk(struct aac_dev *dev, void *arg)
+{
+ struct aac_delete_disk dd;
+ struct fsa_scsi_hba *fsa_dev_ptr;
+
+ fsa_dev_ptr = &(dev->fsa_dev);
+
+ if (copy_from_user(&dd, arg, sizeof (struct aac_delete_disk)))
+ return -EFAULT;
+
+ if (dd.cnum > MAXIMUM_NUM_CONTAINERS)
+ return -EINVAL;
+ /*
+ * If the container is locked, it can not be deleted by the API.
+ */
+ if (fsa_dev_ptr->locked[dd.cnum])
+ return -EBUSY;
+ else {
+ /*
+ * Mark the container as no longer being valid.
+ */
+ fsa_dev_ptr->valid[dd.cnum] = 0;
+ fsa_dev_ptr->devno[dd.cnum] = -1;
+ return 0;
+ }
+}
+
+int aac_dev_ioctl(struct aac_dev *dev, int cmd, void *arg)
+{
+ switch (cmd) {
+ case FSACTL_QUERY_DISK:
+ return query_disk(dev, arg);
+ case FSACTL_DELETE_DISK:
+ return delete_disk(dev, arg);
+ case FSACTL_FORCE_DELETE_DISK:
+ return force_delete_disk(dev, arg);
+ case 2131:
+ return aac_get_containers(dev);
+ default:
+ return -ENOTTY;
+ }
+}
+
+/**
+ *
+ * aac_srb_callback
+ * @context: the context set in the fib - here it is scsi cmd
+ * @fibptr: pointer to the fib
+ *
+ * Handles the completion of a scsi command to a non dasd device
+ *
+ */
+
+static void aac_srb_callback(void *context, struct fib * fibptr)
+{
+ struct aac_dev *dev;
+ struct aac_srb_reply *srbreply;
+ Scsi_Cmnd *scsicmd;
+
+ scsicmd = (Scsi_Cmnd *) context;
+ dev = (struct aac_dev *)scsicmd->host->hostdata;
+
+ if (fibptr == NULL)
+ BUG();
+
+ srbreply = (struct aac_srb_reply *) fib_data(fibptr);
+
+ scsicmd->sense_buffer[0] = '\0'; // initialize sense valid flag to false
+ // calculate resid for sg
+ scsicmd->resid = scsicmd->request_bufflen - srbreply->data_xfer_length;
+
+ if(scsicmd->use_sg)
+ pci_unmap_sg(dev->pdev,
+ (struct scatterlist *)scsicmd->buffer,
+ scsicmd->use_sg,
+ scsi_to_pci_dma_dir(scsicmd->sc_data_direction));
+ else if(scsicmd->request_bufflen)
+ pci_unmap_single(dev->pdev, (ulong)scsicmd->SCp.ptr, scsicmd->request_bufflen,
+ scsi_to_pci_dma_dir(scsicmd->sc_data_direction));
+
+ /*
+ * First check the fib status
+ */
+
+ if (le32_to_cpu(srbreply->status) != ST_OK){
+ int len;
+ printk(KERN_WARNING "aac_srb_callback: srb failed, status = %d\n", le32_to_cpu(srbreply->status));
+ len = (srbreply->sense_data_size > sizeof(scsicmd->sense_buffer))?
+ sizeof(scsicmd->sense_buffer):srbreply->sense_data_size;
+ scsicmd->result = DID_ERROR << 16 | COMMAND_COMPLETE << 8 | CHECK_CONDITION;
+ memcpy(scsicmd->sense_buffer, srbreply->sense_data, len);
+ }
+
+ /*
+ * Next check the srb status
+ */
+ switch(le32_to_cpu(srbreply->srb_status)){
+ case SRB_STATUS_ERROR_RECOVERY:
+ case SRB_STATUS_PENDING:
+ case SRB_STATUS_SUCCESS:
+ if(scsicmd->cmnd[0] == INQUIRY ){
+ u8 b;
+ /* We can't expose disk devices because we can't tell whether they
+ * are the raw container drives or stand alone drives
+ */
+ b = *(u8*)scsicmd->buffer;
+ if( (b & 0x0f) == TYPE_DISK ){
+ scsicmd->result = DID_NO_CONNECT << 16 | COMMAND_COMPLETE << 8;
+ }
+ } else {
+ scsicmd->result = DID_OK << 16 | COMMAND_COMPLETE << 8;
+ }
+ break;
+ case SRB_STATUS_DATA_OVERRUN:
+ switch(scsicmd->cmnd[0]){
+ case READ_6:
+ case WRITE_6:
+ case READ_10:
+ case WRITE_10:
+ case READ_12:
+ case WRITE_12:
+ if(le32_to_cpu(srbreply->data_xfer_length) < scsicmd->underflow ) {
+ printk(KERN_WARNING"aacraid: SCSI CMD underflow\n");
+ } else {
+ printk(KERN_WARNING"aacraid: SCSI CMD Data Overrun\n");
+ }
+ scsicmd->result = DID_ERROR << 16 | COMMAND_COMPLETE << 8;
+ break;
+ default:
+ scsicmd->result = DID_OK << 16 | COMMAND_COMPLETE << 8;
+ break;
+ }
+ break;
+ case SRB_STATUS_ABORTED:
+ scsicmd->result = DID_ABORT << 16 | ABORT << 8;
+ break;
+ case SRB_STATUS_ABORT_FAILED:
+ // Not sure about this one - but assuming the hba was trying to abort for some reason
+ scsicmd->result = DID_ERROR << 16 | ABORT << 8;
+ break;
+ case SRB_STATUS_PARITY_ERROR:
+ scsicmd->result = DID_PARITY << 16 | MSG_PARITY_ERROR << 8;
+ break;
+ case SRB_STATUS_NO_DEVICE:
+ case SRB_STATUS_INVALID_PATH_ID:
+ case SRB_STATUS_INVALID_TARGET_ID:
+ case SRB_STATUS_INVALID_LUN:
+ case SRB_STATUS_SELECTION_TIMEOUT:
+ scsicmd->result = DID_NO_CONNECT << 16 | COMMAND_COMPLETE << 8;
+ break;
+
+ case SRB_STATUS_COMMAND_TIMEOUT:
+ case SRB_STATUS_TIMEOUT:
+ scsicmd->result = DID_TIME_OUT << 16 | COMMAND_COMPLETE << 8;
+ break;
+
+ case SRB_STATUS_BUSY:
+ scsicmd->result = DID_NO_CONNECT << 16 | COMMAND_COMPLETE << 8;
+ break;
+
+ case SRB_STATUS_BUS_RESET:
+ scsicmd->result = DID_RESET << 16 | COMMAND_COMPLETE << 8;
+ break;
+
+ case SRB_STATUS_MESSAGE_REJECTED:
+ scsicmd->result = DID_ERROR << 16 | MESSAGE_REJECT << 8;
+ break;
+ case SRB_STATUS_REQUEST_FLUSHED:
+ case SRB_STATUS_ERROR:
+ case SRB_STATUS_INVALID_REQUEST:
+ case SRB_STATUS_REQUEST_SENSE_FAILED:
+ case SRB_STATUS_NO_HBA:
+ case SRB_STATUS_UNEXPECTED_BUS_FREE:
+ case SRB_STATUS_PHASE_SEQUENCE_FAILURE:
+ case SRB_STATUS_BAD_SRB_BLOCK_LENGTH:
+ case SRB_STATUS_DELAYED_RETRY:
+ case SRB_STATUS_BAD_FUNCTION:
+ case SRB_STATUS_NOT_STARTED:
+ case SRB_STATUS_NOT_IN_USE:
+ case SRB_STATUS_FORCE_ABORT:
+ case SRB_STATUS_DOMAIN_VALIDATION_FAIL:
+ default:
+#ifdef AAC_DETAILED_STATUS_INFO
+ printk("aacraid: SRB ERROR (%s)\n",aac_get_status_string(le32_to_cpu(srbreply->srb_status)));
+#endif
+ scsicmd->result = DID_ERROR << 16 | COMMAND_COMPLETE << 8;
+ break;
+ }
+ if (le32_to_cpu(srbreply->scsi_status) == 0x02 ){ // Check Condition
+ int len;
+ len = (srbreply->sense_data_size > sizeof(scsicmd->sense_buffer))?
+ sizeof(scsicmd->sense_buffer):srbreply->sense_data_size;
+ printk(KERN_WARNING "aac_srb_callback: check condition, status = %d len=%d\n", le32_to_cpu(srbreply->status), len);
+ memcpy(scsicmd->sense_buffer, srbreply->sense_data, len);
+ }
+ /*
+ * OR in the scsi status (already shifted up a bit)
+ */
+ scsicmd->result |= le32_to_cpu(srbreply->scsi_status);
+
+ fib_complete(fibptr);
+ fib_free(fibptr);
+ aac_io_done(scsicmd);
+}
+
+/**
+ *
+ * aac_send_scb_fib
+ * @scsicmd: the scsi command block
+ *
+ * This routine will form a FIB and fill in the aac_srb from the
+ * scsicmd passed in.
+ */
+
+static int aac_send_srb_fib(Scsi_Cmnd* scsicmd)
+{
+ struct fib* cmd_fibcontext;
+ struct aac_dev* dev;
+ int status;
+ struct aac_srb *srbcmd;
+ u16 fibsize;
+ u32 flag;
+
+ if( scsicmd->target > 15 || scsicmd->lun > 7) {
+ scsicmd->result = DID_NO_CONNECT << 16;
+ __aac_io_done(scsicmd);
+ return 0;
+ }
+
+ dev = (struct aac_dev *)scsicmd->host->hostdata;
+ switch(scsicmd->sc_data_direction){
+ case SCSI_DATA_WRITE:
+ flag = SRB_DataOut;
+ break;
+ case SCSI_DATA_UNKNOWN:
+ flag = SRB_DataIn | SRB_DataOut;
+ break;
+ case SCSI_DATA_READ:
+ flag = SRB_DataIn;
+ break;
+ case SCSI_DATA_NONE:
+ default:
+ flag = SRB_NoDataXfer;
+ break;
+ }
+
+
+ /*
+ * Allocate and initialize a Fib then setup a BlockWrite command
+ */
+ if (!(cmd_fibcontext = fib_alloc(dev))) {
+ scsicmd->result = DID_ERROR << 16;
+ __aac_io_done(scsicmd);
+ return -1;
+ }
+ fib_init(cmd_fibcontext);
+
+ srbcmd = (struct aac_srb*) fib_data(cmd_fibcontext);
+ srbcmd->function = cpu_to_le32(SRBF_ExecuteScsi);
+ srbcmd->channel = cpu_to_le32(aac_logical_to_phys(scsicmd->channel));
+ srbcmd->target = cpu_to_le32(scsicmd->target);
+ srbcmd->lun = cpu_to_le32(scsicmd->lun);
+ srbcmd->flags = cpu_to_le32(flag);
+ srbcmd->timeout = cpu_to_le32(0); // timeout not used
+ srbcmd->retry_limit =cpu_to_le32(0); // Obsolete parameter
+ srbcmd->cdb_size = cpu_to_le32(scsicmd->cmd_len);
+
+ if( dev->pae_support ==1 ) {
+ aac_build_sg64(scsicmd, (struct sgmap64*) &srbcmd->sg);
+ srbcmd->count = cpu_to_le32(scsicmd->request_bufflen);
+
+ memset(srbcmd->cdb, 0, sizeof(srbcmd->cdb));
+ memcpy(srbcmd->cdb, scsicmd->cmnd, scsicmd->cmd_len);
+ /*
+ * Build Scatter/Gather list
+ */
+ fibsize = sizeof (struct aac_srb) + (((srbcmd->sg.count & 0xff) - 1) * sizeof (struct sgentry64));
+
+ /*
+ * Now send the Fib to the adapter
+ */
+ status = fib_send(ScsiPortCommand64, cmd_fibcontext, fibsize, FsaNormal, 0, 1,
+ (fib_callback) aac_srb_callback, (void *) scsicmd);
+ } else {
+ aac_build_sg(scsicmd, (struct sgmap*)&srbcmd->sg);
+ srbcmd->count = cpu_to_le32(scsicmd->request_bufflen);
+
+ memset(srbcmd->cdb, 0, sizeof(srbcmd->cdb));
+ memcpy(srbcmd->cdb, scsicmd->cmnd, scsicmd->cmd_len);
+ /*
+ * Build Scatter/Gather list
+ */
+ fibsize = sizeof (struct aac_srb) + (((srbcmd->sg.count & 0xff) - 1) * sizeof (struct sgentry));
+
+ /*
+ * Now send the Fib to the adapter
+ */
+ status = fib_send(ScsiPortCommand, cmd_fibcontext, fibsize, FsaNormal, 0, 1,
+ (fib_callback) aac_srb_callback, (void *) scsicmd);
+ }
+ /*
+ * Check that the command queued to the controller
+ */
+ if (status == -EINPROGRESS){
+ return 0;
+ }
+
+ printk(KERN_WARNING "aac_srb: fib_send failed with status: %d\n", status);
+ /*
+ * For some reason, the Fib didn't queue, return QUEUE_FULL
+ */
+ scsicmd->result = DID_OK << 16 | COMMAND_COMPLETE << 8 | QUEUE_FULL;
+ __aac_io_done(scsicmd);
+
+ fib_complete(cmd_fibcontext);
+ fib_free(cmd_fibcontext);
+
+ return -1;
+}
+
+static unsigned long aac_build_sg(Scsi_Cmnd* scsicmd, struct sgmap* psg)
+{
+ struct aac_dev *dev;
+ unsigned long byte_count = 0;
+
+ dev = (struct aac_dev *)scsicmd->host->hostdata;
+ // Get rid of old data
+ psg->count = cpu_to_le32(0);
+ psg->sg[0].addr = cpu_to_le32(NULL);
+ psg->sg[0].count = cpu_to_le32(0);
+ if (scsicmd->use_sg) {
+ struct scatterlist *sg;
+ int i;
+ int sg_count;
+ sg = (struct scatterlist *) scsicmd->request_buffer;
+
+ sg_count = pci_map_sg(dev->pdev, sg, scsicmd->use_sg,
+ scsi_to_pci_dma_dir(scsicmd->sc_data_direction));
+ psg->count = cpu_to_le32(sg_count);
+
+ byte_count = 0;
+
+ for (i = 0; i < sg_count; i++) {
+ psg->sg[i].addr = cpu_to_le32(sg_dma_address(sg));
+ psg->sg[i].count = cpu_to_le32(sg_dma_len(sg));
+ byte_count += sg_dma_len(sg);
+ sg++;
+ }
+ /* hba wants the size to be exact */
+ if(byte_count > scsicmd->request_bufflen){
+ psg->sg[i-1].count -= (byte_count - scsicmd->request_bufflen);
+ byte_count = scsicmd->request_bufflen;
+ }
+ /* Check for command underflow */
+ if(scsicmd->underflow && (byte_count < scsicmd->underflow)){
+ printk(KERN_WARNING"aacraid: cmd len %08lX cmd underflow %08X\n",
+ byte_count, scsicmd->underflow);
+ }
+ }
+ else if(scsicmd->request_bufflen) {
+ dma_addr_t addr;
+ addr = pci_map_single(dev->pdev,
+ scsicmd->request_buffer,
+ scsicmd->request_bufflen,
+ scsi_to_pci_dma_dir(scsicmd->sc_data_direction));
+ psg->count = cpu_to_le32(1);
+ psg->sg[0].addr = cpu_to_le32(addr);
+ psg->sg[0].count = cpu_to_le32(scsicmd->request_bufflen);
+ scsicmd->SCp.ptr = (void *)addr;
+ byte_count = scsicmd->request_bufflen;
+ }
+ return byte_count;
+}
+
+
+static unsigned long aac_build_sg64(Scsi_Cmnd* scsicmd, struct sgmap64* psg)
+{
+ struct aac_dev *dev;
+ unsigned long byte_count = 0;
+ u64 le_addr;
+
+ dev = (struct aac_dev *)scsicmd->host->hostdata;
+ // Get rid of old data
+ psg->count = cpu_to_le32(0);
+ psg->sg[0].addr[0] = cpu_to_le32(NULL);
+ psg->sg[0].addr[1] = cpu_to_le32(NULL);
+ psg->sg[0].count = cpu_to_le32(0);
+ if (scsicmd->use_sg) {
+ struct scatterlist *sg;
+ int i;
+ int sg_count;
+ sg = (struct scatterlist *) scsicmd->request_buffer;
+
+ sg_count = pci_map_sg(dev->pdev, sg, scsicmd->use_sg,
+ scsi_to_pci_dma_dir(scsicmd->sc_data_direction));
+ psg->count = cpu_to_le32(sg_count);
+
+ byte_count = 0;
+
+ for (i = 0; i < sg_count; i++) {
+ le_addr = cpu_to_le64(sg_dma_address(sg));
+ psg->sg[i].addr[1] = (u32)(le_addr>>32);
+ psg->sg[i].addr[0] = (u32)(le_addr & 0xffffffff);
+ psg->sg[i].count = cpu_to_le32(sg_dma_len(sg));
+ byte_count += sg_dma_len(sg);
+ sg++;
+ }
+ /* hba wants the size to be exact */
+ if(byte_count > scsicmd->request_bufflen){
+ psg->sg[i-1].count -= (byte_count - scsicmd->request_bufflen);
+ byte_count = scsicmd->request_bufflen;
+ }
+ /* Check for command underflow */
+ if(scsicmd->underflow && (byte_count < scsicmd->underflow)){
+ printk(KERN_WARNING"aacraid: cmd len %08lX cmd underflow %08X\n",
+ byte_count, scsicmd->underflow);
+ }
+ }
+ else if(scsicmd->request_bufflen) {
+ dma_addr_t addr;
+ addr = pci_map_single(dev->pdev,
+ scsicmd->request_buffer,
+ scsicmd->request_bufflen,
+ scsi_to_pci_dma_dir(scsicmd->sc_data_direction));
+ psg->count = cpu_to_le32(1);
+ le_addr = cpu_to_le64(addr);
+ psg->sg[0].addr[1] = (u32)(le_addr>>32);
+ psg->sg[0].addr[0] = (u32)(le_addr & 0xffffffff);
+ psg->sg[0].count = cpu_to_le32(scsicmd->request_bufflen);
+ scsicmd->SCp.ptr = (void *)addr;
+ byte_count = scsicmd->request_bufflen;
+ }
+ return byte_count;
+}
+
+#ifdef AAC_DETAILED_STATUS_INFO
+
+struct aac_srb_status_info {
+ u32 status;
+ char *str;
+};
+
+
+static struct aac_srb_status_info srb_status_info[] = {
+ { SRB_STATUS_PENDING, "Pending Status"},
+ { SRB_STATUS_SUCCESS, "Success"},
+ { SRB_STATUS_ABORTED, "Aborted Command"},
+ { SRB_STATUS_ABORT_FAILED, "Abort Failed"},
+ { SRB_STATUS_ERROR, "Error Event"},
+ { SRB_STATUS_BUSY, "Device Busy"},
+ { SRB_STATUS_INVALID_REQUEST, "Invalid Request"},
+ { SRB_STATUS_INVALID_PATH_ID, "Invalid Path ID"},
+ { SRB_STATUS_NO_DEVICE, "No Device"},
+ { SRB_STATUS_TIMEOUT, "Timeout"},
+ { SRB_STATUS_SELECTION_TIMEOUT, "Selection Timeout"},
+ { SRB_STATUS_COMMAND_TIMEOUT, "Command Timeout"},
+ { SRB_STATUS_MESSAGE_REJECTED, "Message Rejected"},
+ { SRB_STATUS_BUS_RESET, "Bus Reset"},
+ { SRB_STATUS_PARITY_ERROR, "Parity Error"},
+ { SRB_STATUS_REQUEST_SENSE_FAILED,"Request Sense Failed"},
+ { SRB_STATUS_NO_HBA, "No HBA"},
+ { SRB_STATUS_DATA_OVERRUN, "Data Overrun/Data Underrun"},
+ { SRB_STATUS_UNEXPECTED_BUS_FREE,"Unexpected Bus Free"},
+ { SRB_STATUS_PHASE_SEQUENCE_FAILURE,"Phase Error"},
+ { SRB_STATUS_BAD_SRB_BLOCK_LENGTH,"Bad Srb Block Length"},
+ { SRB_STATUS_REQUEST_FLUSHED, "Request Flushed"},
+ { SRB_STATUS_DELAYED_RETRY, "Delayed Retry"},
+ { SRB_STATUS_INVALID_LUN, "Invalid LUN"},
+ { SRB_STATUS_INVALID_TARGET_ID, "Invalid TARGET ID"},
+ { SRB_STATUS_BAD_FUNCTION, "Bad Function"},
+ { SRB_STATUS_ERROR_RECOVERY, "Error Recovery"},
+ { SRB_STATUS_NOT_STARTED, "Not Started"},
+ { SRB_STATUS_NOT_IN_USE, "Not In Use"},
+ { SRB_STATUS_FORCE_ABORT, "Force Abort"},
+ { SRB_STATUS_DOMAIN_VALIDATION_FAIL,"Domain Validation Failure"},
+ { 0xff, "Unknown Error"}
+};
+
+char *aac_get_status_string(u32 status)
+{
+ int i;
+
+ for(i=0; i < (sizeof(srb_status_info)/sizeof(struct aac_srb_status_info)); i++ ){
+ if(srb_status_info[i].status == status){
+ return srb_status_info[i].str;
+ }
+ }
+
+ return "Bad Status Code";
+}
+
+#endif
diff --git a/drivers/scsi/aacraid/aacraid.h b/drivers/scsi/aacraid/aacraid.h
new file mode 100644
index 000000000000..ff413451c25e
--- /dev/null
+++ b/drivers/scsi/aacraid/aacraid.h
@@ -0,0 +1,1400 @@
+#define dprintk(x)
+/*#define dprintk(x) printk x */
+
+/*------------------------------------------------------------------------------
+ * D E F I N E S
+ *----------------------------------------------------------------------------*/
+
+#define MAXIMUM_NUM_CONTAINERS 31
+#define MAXIMUM_NUM_ADAPTERS 8
+
+#define AAC_NUM_FIB 578
+#define AAC_NUM_IO_FIB 512
+
+#define AAC_MAX_TARGET (MAXIMUM_NUM_CONTAINERS+1)
+//#define AAC_MAX_TARGET (16)
+#define AAC_MAX_LUN (8)
+
+/*
+ * These macros convert from physical channels to virtual channels
+ */
+#define CONTAINER_CHANNEL (0)
+#define aac_phys_to_logical(x) (x+1)
+#define aac_logical_to_phys(x) (x?x-1:0)
+
+#define AAC_DETAILED_STATUS_INFO
+
+struct diskparm
+{
+ int heads;
+ int sectors;
+ int cylinders;
+};
+
+
+/*
+ * DON'T CHANGE THE ORDER, this is set by the firmware
+ */
+
+#define CT_NONE 0
+#define CT_VOLUME 1
+#define CT_MIRROR 2
+#define CT_STRIPE 3
+#define CT_RAID5 4
+#define CT_SSRW 5
+#define CT_SSRO 6
+#define CT_MORPH 7
+#define CT_PASSTHRU 8
+#define CT_RAID4 9
+#define CT_RAID10 10 /* stripe of mirror */
+#define CT_RAID00 11 /* stripe of stripe */
+#define CT_VOLUME_OF_MIRRORS 12 /* volume of mirror */
+#define CT_PSEUDO_RAID 13 /* really raid4 */
+#define CT_LAST_VOLUME_TYPE 14
+
+/*
+ * Types of objects addressable in some fashion by the client.
+ * This is a superset of those objects handled just by the filesystem
+ * and includes "raw" objects that an administrator would use to
+ * configure containers and filesystems.
+ */
+
+#define FT_REG 1 /* regular file */
+#define FT_DIR 2 /* directory */
+#define FT_BLK 3 /* "block" device - reserved */
+#define FT_CHR 4 /* "character special" device - reserved */
+#define FT_LNK 5 /* symbolic link */
+#define FT_SOCK 6 /* socket */
+#define FT_FIFO 7 /* fifo */
+#define FT_FILESYS 8 /* ADAPTEC's "FSA"(tm) filesystem */
+#define FT_DRIVE 9 /* physical disk - addressable in scsi by bus/target/lun */
+#define FT_SLICE 10 /* virtual disk - raw volume - slice */
+#define FT_PARTITION 11 /* FSA partition - carved out of a slice - building block for containers */
+#define FT_VOLUME 12 /* Container - Volume Set */
+#define FT_STRIPE 13 /* Container - Stripe Set */
+#define FT_MIRROR 14 /* Container - Mirror Set */
+#define FT_RAID5 15 /* Container - Raid 5 Set */
+#define FT_DATABASE 16 /* Storage object with "foreign" content manager */
+
+/*
+ * Host side memory scatter gather list
+ * Used by the adapter for read, write, and readdirplus operations
+ * We have seperate 32 and 64 bit version because even
+ * on 64 bit systems not all cards support the 64 bit version
+ */
+struct sgentry {
+ u32 addr; /* 32-bit address. */
+ u32 count; /* Length. */
+};
+
+struct sgentry64 {
+ u32 addr[2]; /* 64-bit addr. 2 pieces for data alignment */
+ u32 count; /* Length. */
+};
+
+/*
+ * SGMAP
+ *
+ * This is the SGMAP structure for all commands that use
+ * 32-bit addressing.
+ */
+
+struct sgmap {
+ u32 count;
+ struct sgentry sg[1];
+};
+
+struct sgmap64 {
+ u32 count;
+ struct sgentry64 sg[1];
+};
+
+struct creation_info
+{
+ u8 buildnum; /* e.g., 588 */
+ u8 usec; /* e.g., 588 */
+ u8 via; /* e.g., 1 = FSU,
+ * 2 = API
+ */
+ u8 year; /* e.g., 1997 = 97 */
+ u32 date; /*
+ * unsigned Month :4; // 1 - 12
+ * unsigned Day :6; // 1 - 32
+ * unsigned Hour :6; // 0 - 23
+ * unsigned Minute :6; // 0 - 60
+ * unsigned Second :6; // 0 - 60
+ */
+ u32 serial[2]; /* e.g., 0x1DEADB0BFAFAF001 */
+};
+
+
+/*
+ * Define all the constants needed for the communication interface
+ */
+
+/*
+ * Define how many queue entries each queue will have and the total
+ * number of entries for the entire communication interface. Also define
+ * how many queues we support.
+ *
+ * This has to match the controller
+ */
+
+#define NUMBER_OF_COMM_QUEUES 8 // 4 command; 4 response
+#define HOST_HIGH_CMD_ENTRIES 4
+#define HOST_NORM_CMD_ENTRIES 8
+#define ADAP_HIGH_CMD_ENTRIES 4
+#define ADAP_NORM_CMD_ENTRIES 512
+#define HOST_HIGH_RESP_ENTRIES 4
+#define HOST_NORM_RESP_ENTRIES 512
+#define ADAP_HIGH_RESP_ENTRIES 4
+#define ADAP_NORM_RESP_ENTRIES 8
+
+#define TOTAL_QUEUE_ENTRIES \
+ (HOST_NORM_CMD_ENTRIES + HOST_HIGH_CMD_ENTRIES + ADAP_NORM_CMD_ENTRIES + ADAP_HIGH_CMD_ENTRIES + \
+ HOST_NORM_RESP_ENTRIES + HOST_HIGH_RESP_ENTRIES + ADAP_NORM_RESP_ENTRIES + ADAP_HIGH_RESP_ENTRIES)
+
+
+/*
+ * Set the queues on a 16 byte alignment
+ */
+
+#define QUEUE_ALIGNMENT 16
+
+/*
+ * The queue headers define the Communication Region queues. These
+ * are physically contiguous and accessible by both the adapter and the
+ * host. Even though all queue headers are in the same contiguous block
+ * they will be represented as individual units in the data structures.
+ */
+
+struct aac_entry {
+ u32 size; /* Size in bytes of Fib which this QE points to */
+ u32 addr; /* Receiver address of the FIB */
+};
+
+/*
+ * The adapter assumes the ProducerIndex and ConsumerIndex are grouped
+ * adjacently and in that order.
+ */
+
+struct aac_qhdr {
+ u64 header_addr; /* Address to hand the adapter to access to this queue head */
+ u32 *producer; /* The producer index for this queue (host address) */
+ u32 *consumer; /* The consumer index for this queue (host address) */
+};
+
+/*
+ * Define all the events which the adapter would like to notify
+ * the host of.
+ */
+
+#define HostNormCmdQue 1 /* Change in host normal priority command queue */
+#define HostHighCmdQue 2 /* Change in host high priority command queue */
+#define HostNormRespQue 3 /* Change in host normal priority response queue */
+#define HostHighRespQue 4 /* Change in host high priority response queue */
+#define AdapNormRespNotFull 5
+#define AdapHighRespNotFull 6
+#define AdapNormCmdNotFull 7
+#define AdapHighCmdNotFull 8
+#define SynchCommandComplete 9
+#define AdapInternalError 0xfe /* The adapter detected an internal error shutting down */
+
+/*
+ * Define all the events the host wishes to notify the
+ * adapter of. The first four values much match the Qid the
+ * corresponding queue.
+ */
+
+#define AdapNormCmdQue 2
+#define AdapHighCmdQue 3
+#define AdapNormRespQue 6
+#define AdapHighRespQue 7
+#define HostShutdown 8
+#define HostPowerFail 9
+#define FatalCommError 10
+#define HostNormRespNotFull 11
+#define HostHighRespNotFull 12
+#define HostNormCmdNotFull 13
+#define HostHighCmdNotFull 14
+#define FastIo 15
+#define AdapPrintfDone 16
+
+/*
+ * Define all the queues that the adapter and host use to communicate
+ * Number them to match the physical queue layout.
+ */
+
+enum aac_queue_types {
+ HostNormCmdQueue = 0, /* Adapter to host normal priority command traffic */
+ HostHighCmdQueue, /* Adapter to host high priority command traffic */
+ AdapNormCmdQueue, /* Host to adapter normal priority command traffic */
+ AdapHighCmdQueue, /* Host to adapter high priority command traffic */
+ HostNormRespQueue, /* Adapter to host normal priority response traffic */
+ HostHighRespQueue, /* Adapter to host high priority response traffic */
+ AdapNormRespQueue, /* Host to adapter normal priority response traffic */
+ AdapHighRespQueue /* Host to adapter high priority response traffic */
+};
+
+/*
+ * Assign type values to the FSA communication data structures
+ */
+
+#define FIB_MAGIC 0x0001
+
+/*
+ * Define the priority levels the FSA communication routines support.
+ */
+
+#define FsaNormal 1
+#define FsaHigh 2
+
+/*
+ * Define the FIB. The FIB is the where all the requested data and
+ * command information are put to the application on the FSA adapter.
+ */
+
+struct aac_fibhdr {
+ u32 XferState; // Current transfer state for this CCB
+ u16 Command; // Routing information for the destination
+ u8 StructType; // Type FIB
+ u8 Flags; // Flags for FIB
+ u16 Size; // Size of this FIB in bytes
+ u16 SenderSize; // Size of the FIB in the sender (for response sizing)
+ u32 SenderFibAddress; // Host defined data in the FIB
+ u32 ReceiverFibAddress; // Logical address of this FIB for the adapter
+ u32 SenderData; // Place holder for the sender to store data
+ union {
+ struct {
+ u32 _ReceiverTimeStart; // Timestamp for receipt of fib
+ u32 _ReceiverTimeDone; // Timestamp for completion of fib
+ } _s;
+ struct list_head _FibLinks; // Used to link Adapter Initiated Fibs on the host
+ } _u;
+};
+
+#define FibLinks _u._FibLinks
+
+#define FIB_DATA_SIZE_IN_BYTES (512 - sizeof(struct aac_fibhdr))
+
+
+struct hw_fib {
+ struct aac_fibhdr header;
+ u8 data[FIB_DATA_SIZE_IN_BYTES]; // Command specific data
+};
+
+/*
+ * FIB commands
+ */
+
+#define TestCommandResponse 1
+#define TestAdapterCommand 2
+/*
+ * Lowlevel and comm commands
+ */
+#define LastTestCommand 100
+#define ReinitHostNormCommandQueue 101
+#define ReinitHostHighCommandQueue 102
+#define ReinitHostHighRespQueue 103
+#define ReinitHostNormRespQueue 104
+#define ReinitAdapNormCommandQueue 105
+#define ReinitAdapHighCommandQueue 107
+#define ReinitAdapHighRespQueue 108
+#define ReinitAdapNormRespQueue 109
+#define InterfaceShutdown 110
+#define DmaCommandFib 120
+#define StartProfile 121
+#define TermProfile 122
+#define SpeedTest 123
+#define TakeABreakPt 124
+#define RequestPerfData 125
+#define SetInterruptDefTimer 126
+#define SetInterruptDefCount 127
+#define GetInterruptDefStatus 128
+#define LastCommCommand 129
+/*
+ * Filesystem commands
+ */
+#define NuFileSystem 300
+#define UFS 301
+#define HostFileSystem 302
+#define LastFileSystemCommand 303
+/*
+ * Container Commands
+ */
+#define ContainerCommand 500
+#define ContainerCommand64 501
+/*
+ * Cluster Commands
+ */
+#define ClusterCommand 550
+/*
+ * Scsi Port commands (scsi passthrough)
+ */
+#define ScsiPortCommand 600
+#define ScsiPortCommand64 601
+/*
+ * Misc house keeping and generic adapter initiated commands
+ */
+#define AifRequest 700
+#define CheckRevision 701
+#define FsaHostShutdown 702
+#define RequestAdapterInfo 703
+#define IsAdapterPaused 704
+#define SendHostTime 705
+#define LastMiscCommand 706
+
+//
+// Commands that will target the failover level on the FSA adapter
+//
+
+enum fib_xfer_state {
+ HostOwned = (1<<0),
+ AdapterOwned = (1<<1),
+ FibInitialized = (1<<2),
+ FibEmpty = (1<<3),
+ AllocatedFromPool = (1<<4),
+ SentFromHost = (1<<5),
+ SentFromAdapter = (1<<6),
+ ResponseExpected = (1<<7),
+ NoResponseExpected = (1<<8),
+ AdapterProcessed = (1<<9),
+ HostProcessed = (1<<10),
+ HighPriority = (1<<11),
+ NormalPriority = (1<<12),
+ Async = (1<<13),
+ AsyncIo = (1<<13), // rpbfix: remove with new regime
+ PageFileIo = (1<<14), // rpbfix: remove with new regime
+ ShutdownRequest = (1<<15),
+ LazyWrite = (1<<16), // rpbfix: remove with new regime
+ AdapterMicroFib = (1<<17),
+ BIOSFibPath = (1<<18),
+ FastResponseCapable = (1<<19),
+ ApiFib = (1<<20) // Its an API Fib.
+};
+
+/*
+ * The following defines needs to be updated any time there is an
+ * incompatible change made to the aac_init structure.
+ */
+
+#define ADAPTER_INIT_STRUCT_REVISION 3
+
+struct aac_init
+{
+ u32 InitStructRevision;
+ u32 MiniPortRevision;
+ u32 fsrev;
+ u32 CommHeaderAddress;
+ u32 FastIoCommAreaAddress;
+ u32 AdapterFibsPhysicalAddress;
+ u32 AdapterFibsVirtualAddress;
+ u32 AdapterFibsSize;
+ u32 AdapterFibAlign;
+ u32 printfbuf;
+ u32 printfbufsiz;
+ u32 HostPhysMemPages; // number of 4k pages of host physical memory
+ u32 HostElapsedSeconds; // number of seconds since 1970.
+};
+
+enum aac_log_level {
+ LOG_INIT = 10,
+ LOG_INFORMATIONAL = 20,
+ LOG_WARNING = 30,
+ LOG_LOW_ERROR = 40,
+ LOG_MEDIUM_ERROR = 50,
+ LOG_HIGH_ERROR = 60,
+ LOG_PANIC = 70,
+ LOG_DEBUG = 80,
+ LOG_WINDBG_PRINT = 90
+};
+
+#define FSAFS_NTC_GET_ADAPTER_FIB_CONTEXT 0x030b
+#define FSAFS_NTC_FIB_CONTEXT 0x030c
+
+struct aac_dev;
+
+struct adapter_ops
+{
+ void (*adapter_interrupt)(struct aac_dev *dev);
+ void (*adapter_notify)(struct aac_dev *dev, u32 event);
+ void (*adapter_enable_int)(struct aac_dev *dev, u32 event);
+ void (*adapter_disable_int)(struct aac_dev *dev, u32 event);
+ int (*adapter_sync_cmd)(struct aac_dev *dev, u32 command, u32 p1, u32 *status);
+};
+
+/*
+ * Define which interrupt handler needs to be installed
+ */
+
+struct aac_driver_ident
+{
+ u16 vendor;
+ u16 device;
+ u16 subsystem_vendor;
+ u16 subsystem_device;
+ int (*init)(struct aac_dev *dev, unsigned long num);
+ char * name;
+ char * vname;
+ char * model;
+ u16 channels;
+};
+
+/*
+ * The adapter interface specs all queues to be located in the same
+ * physically contigous block. The host structure that defines the
+ * commuication queues will assume they are each a seperate physically
+ * contigous memory region that will support them all being one big
+ * contigous block.
+ * There is a command and response queue for each level and direction of
+ * commuication. These regions are accessed by both the host and adapter.
+ */
+
+struct aac_queue {
+ u64 logical; /* This is the address we give the adapter */
+ struct aac_entry *base; /* This is the system virtual address */
+ struct aac_qhdr headers; /* A pointer to the producer and consumer queue headers for this queue */
+ u32 entries; /* Number of queue entries on this queue */
+ wait_queue_head_t qfull; /* Event to wait on if the queue is full */
+ wait_queue_head_t cmdready; /* Indicates there is a Command ready from the adapter on this queue. */
+ /* This is only valid for adapter to host command queues. */
+ spinlock_t *lock; /* Spinlock for this queue must take this lock before accessing the lock */
+ spinlock_t lockdata; /* Actual lock (used only on one side of the lock) */
+ unsigned long SavedIrql; /* Previous IRQL when the spin lock is taken */
+ u32 padding; /* Padding - FIXME - can remove I believe */
+ struct list_head cmdq; /* A queue of FIBs which need to be prcessed by the FS thread. This is */
+ /* only valid for command queues which receive entries from the adapter. */
+ struct list_head pendingq; /* A queue of outstanding fib's to the adapter. */
+ unsigned long numpending; /* Number of entries on outstanding queue. */
+ struct aac_dev * dev; /* Back pointer to adapter structure */
+};
+
+/*
+ * Message queues. The order here is important, see also the
+ * queue type ordering
+ */
+
+struct aac_queue_block
+{
+ struct aac_queue queue[8];
+};
+
+/*
+ * SaP1 Message Unit Registers
+ */
+
+struct sa_drawbridge_CSR {
+ // Offset | Name
+ u32 reserved[10]; // 00h-27h | Reserved
+ u8 LUT_Offset; // 28h | Looup Table Offset
+ u8 reserved1[3]; // 29h-2bh | Reserved
+ u32 LUT_Data; // 2ch | Looup Table Data
+ u32 reserved2[26]; // 30h-97h | Reserved
+ u16 PRICLEARIRQ; // 98h | Primary Clear Irq
+ u16 SECCLEARIRQ; // 9ah | Secondary Clear Irq
+ u16 PRISETIRQ; // 9ch | Primary Set Irq
+ u16 SECSETIRQ; // 9eh | Secondary Set Irq
+ u16 PRICLEARIRQMASK; // a0h | Primary Clear Irq Mask
+ u16 SECCLEARIRQMASK; // a2h | Secondary Clear Irq Mask
+ u16 PRISETIRQMASK; // a4h | Primary Set Irq Mask
+ u16 SECSETIRQMASK; // a6h | Secondary Set Irq Mask
+ u32 MAILBOX0; // a8h | Scratchpad 0
+ u32 MAILBOX1; // ach | Scratchpad 1
+ u32 MAILBOX2; // b0h | Scratchpad 2
+ u32 MAILBOX3; // b4h | Scratchpad 3
+ u32 MAILBOX4; // b8h | Scratchpad 4
+ u32 MAILBOX5; // bch | Scratchpad 5
+ u32 MAILBOX6; // c0h | Scratchpad 6
+ u32 MAILBOX7; // c4h | Scratchpad 7
+
+ u32 ROM_Setup_Data; // c8h | Rom Setup and Data
+ u32 ROM_Control_Addr; // cch | Rom Control and Address
+
+ u32 reserved3[12]; // d0h-ffh | reserved
+ u32 LUT[64]; // 100h-1ffh| Lookup Table Entries
+
+ //
+ // TO DO
+ // need to add DMA, I2O, UART, etc registers form 80h to 364h
+ //
+
+};
+
+#define Mailbox0 SaDbCSR.MAILBOX0
+#define Mailbox1 SaDbCSR.MAILBOX1
+#define Mailbox2 SaDbCSR.MAILBOX2
+#define Mailbox3 SaDbCSR.MAILBOX3
+#define Mailbox4 SaDbCSR.MAILBOX4
+#define Mailbox5 SaDbCSR.MAILBOX5
+#define Mailbox7 SaDbCSR.MAILBOX7
+
+#define DoorbellReg_p SaDbCSR.PRISETIRQ
+#define DoorbellReg_s SaDbCSR.SECSETIRQ
+#define DoorbellClrReg_p SaDbCSR.PRICLEARIRQ
+
+
+#define DOORBELL_0 cpu_to_le16(0x0001)
+#define DOORBELL_1 cpu_to_le16(0x0002)
+#define DOORBELL_2 cpu_to_le16(0x0004)
+#define DOORBELL_3 cpu_to_le16(0x0008)
+#define DOORBELL_4 cpu_to_le16(0x0010)
+#define DOORBELL_5 cpu_to_le16(0x0020)
+#define DOORBELL_6 cpu_to_le16(0x0040)
+
+
+#define PrintfReady DOORBELL_5
+#define PrintfDone DOORBELL_5
+
+struct sa_registers {
+ struct sa_drawbridge_CSR SaDbCSR; /* 98h - c4h */
+};
+
+
+#define Sa_MINIPORT_REVISION 1
+
+#define sa_readw(AEP, CSR) readl(&((AEP)->regs.sa->CSR))
+#define sa_readl(AEP, CSR) readl(&((AEP)->regs.sa->CSR))
+#define sa_writew(AEP, CSR, value) writew(value, &((AEP)->regs.sa->CSR))
+#define sa_writel(AEP, CSR, value) writel(value, &((AEP)->regs.sa->CSR))
+
+/*
+ * Rx Message Unit Registers
+ */
+
+struct rx_mu_registers {
+ // Local | PCI* | Name
+ // | |
+ u32 ARSR; // 1300h | 00h | APIC Register Select Register
+ u32 reserved0; // 1304h | 04h | Reserved
+ u32 AWR; // 1308h | 08h | APIC Window Register
+ u32 reserved1; // 130Ch | 0Ch | Reserved
+ u32 IMRx[2]; // 1310h | 10h | Inbound Message Registers
+ u32 OMRx[2]; // 1318h | 18h | Outbound Message Registers
+ u32 IDR; // 1320h | 20h | Inbound Doorbell Register
+ u32 IISR; // 1324h | 24h | Inbound Interrupt Status Register
+ u32 IIMR; // 1328h | 28h | Inbound Interrupt Mask Register
+ u32 ODR; // 132Ch | 2Ch | Outbound Doorbell Register
+ u32 OISR; // 1330h | 30h | Outbound Interrupt Status Register
+ u32 OIMR; // 1334h | 34h | Outbound Interrupt Mask Register
+ // * Must access through ATU Inbound Translation Window
+};
+
+struct rx_inbound {
+ u32 Mailbox[8];
+};
+
+#define InboundMailbox0 IndexRegs.Mailbox[0]
+#define InboundMailbox1 IndexRegs.Mailbox[1]
+#define InboundMailbox2 IndexRegs.Mailbox[2]
+#define InboundMailbox3 IndexRegs.Mailbox[3]
+#define InboundMailbox4 IndexRegs.Mailbox[4]
+
+#define INBOUNDDOORBELL_0 cpu_to_le32(0x00000001)
+#define INBOUNDDOORBELL_1 cpu_to_le32(0x00000002)
+#define INBOUNDDOORBELL_2 cpu_to_le32(0x00000004)
+#define INBOUNDDOORBELL_3 cpu_to_le32(0x00000008)
+#define INBOUNDDOORBELL_4 cpu_to_le32(0x00000010)
+#define INBOUNDDOORBELL_5 cpu_to_le32(0x00000020)
+#define INBOUNDDOORBELL_6 cpu_to_le32(0x00000040)
+
+#define OUTBOUNDDOORBELL_0 cpu_to_le32(0x00000001)
+#define OUTBOUNDDOORBELL_1 cpu_to_le32(0x00000002)
+#define OUTBOUNDDOORBELL_2 cpu_to_le32(0x00000004)
+#define OUTBOUNDDOORBELL_3 cpu_to_le32(0x00000008)
+#define OUTBOUNDDOORBELL_4 cpu_to_le32(0x00000010)
+
+#define InboundDoorbellReg MUnit.IDR
+#define OutboundDoorbellReg MUnit.ODR
+
+struct rx_registers {
+ struct rx_mu_registers MUnit; // 1300h - 1334h
+ u32 reserved1[6]; // 1338h - 134ch
+ struct rx_inbound IndexRegs;
+};
+
+#define rx_readb(AEP, CSR) readb(&((AEP)->regs.rx->CSR))
+#define rx_readl(AEP, CSR) readl(&((AEP)->regs.rx->CSR))
+#define rx_writeb(AEP, CSR, value) writeb(value, &((AEP)->regs.rx->CSR))
+#define rx_writel(AEP, CSR, value) writel(value, &((AEP)->regs.rx->CSR))
+
+struct fib;
+
+typedef void (*fib_callback)(void *ctxt, struct fib *fibctx);
+
+struct aac_fib_context {
+ s16 type; // used for verification of structure
+ s16 size;
+ ulong jiffies; // used for cleanup - dmb changed to ulong
+ struct list_head next; // used to link context's into a linked list
+ struct semaphore wait_sem; // this is used to wait for the next fib to arrive.
+ int wait; // Set to true when thread is in WaitForSingleObject
+ unsigned long count; // total number of FIBs on FibList
+ struct list_head fibs;
+};
+
+struct fsa_scsi_hba {
+ u32 size[MAXIMUM_NUM_CONTAINERS];
+ u32 type[MAXIMUM_NUM_CONTAINERS];
+ u8 valid[MAXIMUM_NUM_CONTAINERS];
+ u8 ro[MAXIMUM_NUM_CONTAINERS];
+ u8 locked[MAXIMUM_NUM_CONTAINERS];
+ u8 deleted[MAXIMUM_NUM_CONTAINERS];
+ u32 devno[MAXIMUM_NUM_CONTAINERS];
+};
+
+struct fib {
+ void *next; /* this is used by the allocator */
+ s16 type;
+ s16 size;
+ /*
+ * The Adapter that this I/O is destined for.
+ */
+ struct aac_dev *dev;
+ u64 logicaladdr; /* 64 bit */
+ /*
+ * This is the event the sendfib routine will wait on if the
+ * caller did not pass one and this is synch io.
+ */
+ struct semaphore event_wait;
+ spinlock_t event_lock;
+
+ u32 done; /* gets set to 1 when fib is complete */
+ fib_callback callback;
+ void *callback_data;
+ u32 flags; // u32 dmb was ulong
+ /*
+ * The following is used to put this fib context onto the
+ * Outstanding I/O queue.
+ */
+ struct list_head queue;
+
+ void *data;
+ struct hw_fib *fib; /* Actual shared object */
+};
+
+/*
+ * Adapter Information Block
+ *
+ * This is returned by the RequestAdapterInfo block
+ */
+
+struct aac_adapter_info
+{
+ u32 platform;
+ u32 cpu;
+ u32 subcpu;
+ u32 clock;
+ u32 execmem;
+ u32 buffermem;
+ u32 totalmem;
+ u32 kernelrev;
+ u32 kernelbuild;
+ u32 monitorrev;
+ u32 monitorbuild;
+ u32 hwrev;
+ u32 hwbuild;
+ u32 biosrev;
+ u32 biosbuild;
+ u32 cluster;
+ u32 serial[2];
+ u32 battery;
+ u32 options;
+ u32 OEM;
+};
+
+/*
+ * Battery platforms
+ */
+#define AAC_BAT_REQ_PRESENT (1)
+#define AAC_BAT_REQ_NOTPRESENT (2)
+#define AAC_BAT_OPT_PRESENT (3)
+#define AAC_BAT_OPT_NOTPRESENT (4)
+#define AAC_BAT_NOT_SUPPORTED (5)
+/*
+ * cpu types
+ */
+#define AAC_CPU_SIMULATOR (1)
+#define AAC_CPU_I960 (2)
+#define AAC_CPU_STRONGARM (3)
+
+/*
+ * Supported Options
+ */
+#define AAC_OPT_SNAPSHOT cpu_to_le32(1)
+#define AAC_OPT_CLUSTERS cpu_to_le32(1<<1)
+#define AAC_OPT_WRITE_CACHE cpu_to_le32(1<<2)
+#define AAC_OPT_64BIT_DATA cpu_to_le32(1<<3)
+#define AAC_OPT_HOST_TIME_FIB cpu_to_le32(1<<4)
+#define AAC_OPT_RAID50 cpu_to_le32(1<<5)
+#define AAC_OPT_4GB_WINDOW cpu_to_le32(1<<6)
+#define AAC_OPT_SCSI_UPGRADEABLE cpu_to_le32(1<<7)
+#define AAC_OPT_SOFT_ERR_REPORT cpu_to_le32(1<<8)
+#define AAC_OPT_SUPPORTED_RECONDITION cpu_to_le32(1<<9)
+#define AAC_OPT_SGMAP_HOST64 cpu_to_le32(1<<10)
+#define AAC_OPT_ALARM cpu_to_le32(1<<11)
+#define AAC_OPT_NONDASD cpu_to_le32(1<<12)
+
+struct aac_dev
+{
+ struct aac_dev *next;
+ const char *name;
+ int id;
+
+ u16 irq_mask;
+ /*
+ * Map for 128 fib objects (64k)
+ */
+ dma_addr_t hw_fib_pa;
+ struct hw_fib *hw_fib_va;
+#if BITS_PER_LONG >= 64
+ ulong fib_base_va;
+#endif
+ /*
+ * Fib Headers
+ */
+ struct fib fibs[AAC_NUM_FIB];
+ struct fib *free_fib;
+ struct fib *timeout_fib;
+ spinlock_t fib_lock;
+
+ struct aac_queue_block *queues;
+ /*
+ * The user API will use an IOCTL to register itself to receive
+ * FIBs from the adapter. The following list is used to keep
+ * track of all the threads that have requested these FIBs. The
+ * mutex is used to synchronize access to all data associated
+ * with the adapter fibs.
+ */
+ struct list_head fib_list;
+
+ struct adapter_ops a_ops;
+ unsigned long fsrev; /* Main driver's revision number */
+
+ struct aac_init *init; /* Holds initialization info to communicate with adapter */
+ dma_addr_t init_pa; /* Holds physical address of the init struct */
+
+ struct pci_dev *pdev; /* Our PCI interface */
+ void * printfbuf; /* pointer to buffer used for printf's from the adapter */
+ void * comm_addr; /* Base address of Comm area */
+ dma_addr_t comm_phys; /* Physical Address of Comm area */
+ size_t comm_size;
+
+ struct Scsi_Host *scsi_host_ptr;
+ struct fsa_scsi_hba fsa_dev;
+ int thread_pid;
+ int cardtype;
+
+ /*
+ * The following is the device specific extension.
+ */
+ union
+ {
+ struct sa_registers *sa;
+ struct rx_registers *rx;
+ } regs;
+ /*
+ * The following is the number of the individual adapter
+ */
+ u32 devnum;
+ u32 aif_thread;
+ struct completion aif_completion;
+ struct aac_adapter_info adapter_info;
+ /* These are in adapter info but they are in the io flow so
+ * lets break them out so we don't have to do an AND to check them
+ */
+ u8 nondasd_support;
+ u8 pae_support;
+};
+
+#define AllocateAndMapFibSpace(dev, MapFibContext) \
+ dev->a_ops.AllocateAndMapFibSpace(dev, MapFibContext)
+
+#define UnmapAndFreeFibSpace(dev, MapFibContext) \
+ dev->a_ops.UnmapAndFreeFibSpace(dev, MapFibContext)
+
+#define aac_adapter_interrupt(dev) \
+ dev->a_ops.adapter_interrupt(dev)
+
+#define aac_adapter_notify(dev, event) \
+ dev->a_ops.adapter_notify(dev, event)
+
+#define aac_adapter_enable_int(dev, event) \
+ dev->a_ops.adapter_enable_int(dev, event)
+
+#define aac_adapter_disable_int(dev, event) \
+ dev->a_ops.adapter_disable_int(dev, event)
+
+
+
+#define FIB_CONTEXT_FLAG_TIMED_OUT (0x00000001)
+
+/*
+ * Define the command values
+ */
+
+#define Null 0
+#define GetAttributes 1
+#define SetAttributes 2
+#define Lookup 3
+#define ReadLink 4
+#define Read 5
+#define Write 6
+#define Create 7
+#define MakeDirectory 8
+#define SymbolicLink 9
+#define MakeNode 10
+#define Removex 11
+#define RemoveDirectoryx 12
+#define Rename 13
+#define Link 14
+#define ReadDirectory 15
+#define ReadDirectoryPlus 16
+#define FileSystemStatus 17
+#define FileSystemInfo 18
+#define PathConfigure 19
+#define Commit 20
+#define Mount 21
+#define UnMount 22
+#define Newfs 23
+#define FsCheck 24
+#define FsSync 25
+#define SimReadWrite 26
+#define SetFileSystemStatus 27
+#define BlockRead 28
+#define BlockWrite 29
+#define NvramIoctl 30
+#define FsSyncWait 31
+#define ClearArchiveBit 32
+#define SetAcl 33
+#define GetAcl 34
+#define AssignAcl 35
+#define FaultInsertion 36 /* Fault Insertion Command */
+#define CrazyCache 37 /* Crazycache */
+
+#define MAX_FSACOMMAND_NUM 38
+
+
+/*
+ * Define the status returns. These are very unixlike although
+ * most are not in fact used
+ */
+
+#define ST_OK 0
+#define ST_PERM 1
+#define ST_NOENT 2
+#define ST_IO 5
+#define ST_NXIO 6
+#define ST_E2BIG 7
+#define ST_ACCES 13
+#define ST_EXIST 17
+#define ST_XDEV 18
+#define ST_NODEV 19
+#define ST_NOTDIR 20
+#define ST_ISDIR 21
+#define ST_INVAL 22
+#define ST_FBIG 27
+#define ST_NOSPC 28
+#define ST_ROFS 30
+#define ST_MLINK 31
+#define ST_WOULDBLOCK 35
+#define ST_NAMETOOLONG 63
+#define ST_NOTEMPTY 66
+#define ST_DQUOT 69
+#define ST_STALE 70
+#define ST_REMOTE 71
+#define ST_BADHANDLE 10001
+#define ST_NOT_SYNC 10002
+#define ST_BAD_COOKIE 10003
+#define ST_NOTSUPP 10004
+#define ST_TOOSMALL 10005
+#define ST_SERVERFAULT 10006
+#define ST_BADTYPE 10007
+#define ST_JUKEBOX 10008
+#define ST_NOTMOUNTED 10009
+#define ST_MAINTMODE 10010
+#define ST_STALEACL 10011
+
+/*
+ * On writes how does the client want the data written.
+ */
+
+#define CACHE_CSTABLE 1
+#define CACHE_UNSTABLE 2
+
+/*
+ * Lets the client know at which level the data was commited on
+ * a write request
+ */
+
+#define CMFILE_SYNCH_NVRAM 1
+#define CMDATA_SYNCH_NVRAM 2
+#define CMFILE_SYNCH 3
+#define CMDATA_SYNCH 4
+#define CMUNSTABLE 5
+
+struct aac_read
+{
+ u32 command;
+ u32 cid;
+ u32 block;
+ u32 count;
+ struct sgmap sg; // Must be last in struct because it is variable
+};
+
+struct aac_read64
+{
+ u32 command;
+ u16 cid;
+ u16 sector_count;
+ u32 block;
+ u16 pad;
+ u16 flags;
+ struct sgmap64 sg; // Must be last in struct because it is variable
+};
+
+struct aac_read_reply
+{
+ u32 status;
+ u32 count;
+};
+
+struct aac_write
+{
+ u32 command;
+ u32 cid;
+ u32 block;
+ u32 count;
+ u32 stable; // Not used
+ struct sgmap sg; // Must be last in struct because it is variable
+};
+
+struct aac_write64
+{
+ u32 command;
+ u16 cid;
+ u16 sector_count;
+ u32 block;
+ u16 pad;
+ u16 flags;
+ struct sgmap64 sg; // Must be last in struct because it is variable
+};
+struct aac_write_reply
+{
+ u32 status;
+ u32 count;
+ u32 committed;
+};
+
+struct aac_srb
+{
+ u32 function;
+ u32 channel;
+ u32 target;
+ u32 lun;
+ u32 timeout;
+ u32 flags;
+ u32 count; // Data xfer size
+ u32 retry_limit;
+ u32 cdb_size;
+ u8 cdb[16];
+ struct sgmap sg;
+};
+
+
+
+#define AAC_SENSE_BUFFERSIZE 30
+
+struct aac_srb_reply
+{
+ u32 status;
+ u32 srb_status;
+ u32 scsi_status;
+ u32 data_xfer_length;
+ u32 sense_data_size;
+ u8 sense_data[AAC_SENSE_BUFFERSIZE]; // Can this be SCSI_SENSE_BUFFERSIZE
+};
+/*
+ * SRB Flags
+ */
+#define SRB_NoDataXfer 0x0000
+#define SRB_DisableDisconnect 0x0004
+#define SRB_DisableSynchTransfer 0x0008
+#define SRB_BypassFrozenQueue 0x0010
+#define SRB_DisableAutosense 0x0020
+#define SRB_DataIn 0x0040
+#define SRB_DataOut 0x0080
+
+/*
+ * SRB Functions - set in aac_srb->function
+ */
+#define SRBF_ExecuteScsi 0x0000
+#define SRBF_ClaimDevice 0x0001
+#define SRBF_IO_Control 0x0002
+#define SRBF_ReceiveEvent 0x0003
+#define SRBF_ReleaseQueue 0x0004
+#define SRBF_AttachDevice 0x0005
+#define SRBF_ReleaseDevice 0x0006
+#define SRBF_Shutdown 0x0007
+#define SRBF_Flush 0x0008
+#define SRBF_AbortCommand 0x0010
+#define SRBF_ReleaseRecovery 0x0011
+#define SRBF_ResetBus 0x0012
+#define SRBF_ResetDevice 0x0013
+#define SRBF_TerminateIO 0x0014
+#define SRBF_FlushQueue 0x0015
+#define SRBF_RemoveDevice 0x0016
+#define SRBF_DomainValidation 0x0017
+
+/*
+ * SRB SCSI Status - set in aac_srb->scsi_status
+ */
+#define SRB_STATUS_PENDING 0x00
+#define SRB_STATUS_SUCCESS 0x01
+#define SRB_STATUS_ABORTED 0x02
+#define SRB_STATUS_ABORT_FAILED 0x03
+#define SRB_STATUS_ERROR 0x04
+#define SRB_STATUS_BUSY 0x05
+#define SRB_STATUS_INVALID_REQUEST 0x06
+#define SRB_STATUS_INVALID_PATH_ID 0x07
+#define SRB_STATUS_NO_DEVICE 0x08
+#define SRB_STATUS_TIMEOUT 0x09
+#define SRB_STATUS_SELECTION_TIMEOUT 0x0A
+#define SRB_STATUS_COMMAND_TIMEOUT 0x0B
+#define SRB_STATUS_MESSAGE_REJECTED 0x0D
+#define SRB_STATUS_BUS_RESET 0x0E
+#define SRB_STATUS_PARITY_ERROR 0x0F
+#define SRB_STATUS_REQUEST_SENSE_FAILED 0x10
+#define SRB_STATUS_NO_HBA 0x11
+#define SRB_STATUS_DATA_OVERRUN 0x12
+#define SRB_STATUS_UNEXPECTED_BUS_FREE 0x13
+#define SRB_STATUS_PHASE_SEQUENCE_FAILURE 0x14
+#define SRB_STATUS_BAD_SRB_BLOCK_LENGTH 0x15
+#define SRB_STATUS_REQUEST_FLUSHED 0x16
+#define SRB_STATUS_DELAYED_RETRY 0x17
+#define SRB_STATUS_INVALID_LUN 0x20
+#define SRB_STATUS_INVALID_TARGET_ID 0x21
+#define SRB_STATUS_BAD_FUNCTION 0x22
+#define SRB_STATUS_ERROR_RECOVERY 0x23
+#define SRB_STATUS_NOT_STARTED 0x24
+#define SRB_STATUS_NOT_IN_USE 0x30
+#define SRB_STATUS_FORCE_ABORT 0x31
+#define SRB_STATUS_DOMAIN_VALIDATION_FAIL 0x32
+
+/*
+ * Object-Server / Volume-Manager Dispatch Classes
+ */
+
+#define VM_Null 0
+#define VM_NameServe 1
+#define VM_ContainerConfig 2
+#define VM_Ioctl 3
+#define VM_FilesystemIoctl 4
+#define VM_CloseAll 5
+#define VM_CtBlockRead 6
+#define VM_CtBlockWrite 7
+#define VM_SliceBlockRead 8 /* raw access to configured "storage objects" */
+#define VM_SliceBlockWrite 9
+#define VM_DriveBlockRead 10 /* raw access to physical devices */
+#define VM_DriveBlockWrite 11
+#define VM_EnclosureMgt 12 /* enclosure management */
+#define VM_Unused 13 /* used to be diskset management */
+#define VM_CtBlockVerify 14
+#define VM_CtPerf 15 /* performance test */
+#define VM_CtBlockRead64 16
+#define VM_CtBlockWrite64 17
+#define VM_CtBlockVerify64 18
+#define VM_CtHostRead64 19
+#define VM_CtHostWrite64 20
+
+#define MAX_VMCOMMAND_NUM 21 /* used for sizing stats array - leave last */
+
+/*
+ * Descriptive information (eg, vital stats)
+ * that a content manager might report. The
+ * FileArray filesystem component is one example
+ * of a content manager. Raw mode might be
+ * another.
+ */
+
+struct aac_fsinfo {
+ u32 fsTotalSize; /* Consumed by fs, incl. metadata */
+ u32 fsBlockSize;
+ u32 fsFragSize;
+ u32 fsMaxExtendSize;
+ u32 fsSpaceUnits;
+ u32 fsMaxNumFiles;
+ u32 fsNumFreeFiles;
+ u32 fsInodeDensity;
+}; /* valid iff ObjType == FT_FILESYS && !(ContentState & FSCS_NOTCLEAN) */
+
+union aac_contentinfo {
+ struct aac_fsinfo filesys; /* valid iff ObjType == FT_FILESYS && !(ContentState & FSCS_NOTCLEAN) */
+};
+
+/*
+ * Query for "mountable" objects, ie, objects that are typically
+ * associated with a drive letter on the client (host) side.
+ */
+
+struct aac_mntent {
+ u32 oid;
+ u8 name[16]; // if applicable
+ struct creation_info create_info; // if applicable
+ u32 capacity;
+ u32 vol; // substrate structure
+ u32 obj; // FT_FILESYS, FT_DATABASE, etc.
+ u32 state; // unready for mounting, readonly, etc.
+ union aac_contentinfo fileinfo; // Info specific to content manager (eg, filesystem)
+ u32 altoid; // != oid <==> snapshot or broken mirror exists
+};
+
+#define FSCS_READONLY 0x0002 /* possible result of broken mirror */
+
+struct aac_query_mount {
+ u32 command;
+ u32 type;
+ u32 count;
+};
+
+struct aac_mount {
+ u32 status;
+ u32 type; /* should be same as that requested */
+ u32 count;
+ struct aac_mntent mnt[1];
+};
+
+/*
+ * The following command is sent to shut down each container.
+ */
+
+struct aac_close {
+ u32 command;
+ u32 cid;
+};
+
+struct aac_query_disk
+{
+ s32 cnum;
+ s32 bus;
+ s32 target;
+ s32 lun;
+ u32 valid;
+ u32 locked;
+ u32 deleted;
+ s32 instance;
+ s8 name[10];
+ u32 unmapped;
+};
+
+struct aac_delete_disk {
+ u32 disknum;
+ u32 cnum;
+};
+
+struct fib_ioctl
+{
+ char *fibctx;
+ int wait;
+ char *fib;
+};
+
+struct revision
+{
+ u32 compat;
+ u32 version;
+ u32 build;
+};
+
+/*
+ * Ugly - non Linux like ioctl coding for back compat.
+ */
+
+#define CTL_CODE(function, method) ( \
+ (4<< 16) | ((function) << 2) | (method) \
+)
+
+/*
+ * Define the method codes for how buffers are passed for I/O and FS
+ * controls
+ */
+
+#define METHOD_BUFFERED 0
+#define METHOD_NEITHER 3
+
+/*
+ * Filesystem ioctls
+ */
+
+#define FSACTL_SENDFIB CTL_CODE(2050, METHOD_BUFFERED)
+#define FSACTL_SEND_RAW_SRB CTL_CODE(2067, METHOD_BUFFERED)
+#define FSACTL_DELETE_DISK 0x163
+#define FSACTL_QUERY_DISK 0x173
+#define FSACTL_OPEN_GET_ADAPTER_FIB CTL_CODE(2100, METHOD_BUFFERED)
+#define FSACTL_GET_NEXT_ADAPTER_FIB CTL_CODE(2101, METHOD_BUFFERED)
+#define FSACTL_CLOSE_GET_ADAPTER_FIB CTL_CODE(2102, METHOD_BUFFERED)
+#define FSACTL_MINIPORT_REV_CHECK CTL_CODE(2107, METHOD_BUFFERED)
+#define FSACTL_GET_PCI_INFO CTL_CODE(2119, METHOD_BUFFERED)
+#define FSACTL_FORCE_DELETE_DISK CTL_CODE(2120, METHOD_NEITHER)
+
+
+struct aac_common
+{
+ /*
+ * If this value is set to 1 then interrupt moderation will occur
+ * in the base commuication support.
+ */
+ u32 irq_mod;
+ u32 peak_fibs;
+ u32 zero_fibs;
+ u32 fib_timeouts;
+ /*
+ * Statistical counters in debug mode
+ */
+#ifdef DBG
+ u32 FibsSent;
+ u32 FibRecved;
+ u32 NoResponseSent;
+ u32 NoResponseRecved;
+ u32 AsyncSent;
+ u32 AsyncRecved;
+ u32 NormalSent;
+ u32 NormalRecved;
+#endif
+};
+
+extern struct aac_common aac_config;
+
+
+/*
+ * The following macro is used when sending and receiving FIBs. It is
+ * only used for debugging.
+ */
+
+#if DBG
+#define FIB_COUNTER_INCREMENT(counter) (counter)++
+#else
+#define FIB_COUNTER_INCREMENT(counter)
+#endif
+
+/*
+ * Adapter direct commands
+ * Monitor/Kernel API
+ */
+
+#define BREAKPOINT_REQUEST cpu_to_le32(0x00000004)
+#define INIT_STRUCT_BASE_ADDRESS cpu_to_le32(0x00000005)
+#define READ_PERMANENT_PARAMETERS cpu_to_le32(0x0000000a)
+#define WRITE_PERMANENT_PARAMETERS cpu_to_le32(0x0000000b)
+#define HOST_CRASHING cpu_to_le32(0x0000000d)
+#define SEND_SYNCHRONOUS_FIB cpu_to_le32(0x0000000c)
+#define GET_ADAPTER_PROPERTIES cpu_to_le32(0x00000019)
+#define RE_INIT_ADAPTER cpu_to_le32(0x000000ee)
+
+/*
+ * Adapter Status Register
+ *
+ * Phase Staus mailbox is 32bits:
+ * <31:16> = Phase Status
+ * <15:0> = Phase
+ *
+ * The adapter reports is present state through the phase. Only
+ * a single phase should be ever be set. Each phase can have multiple
+ * phase status bits to provide more detailed information about the
+ * state of the board. Care should be taken to ensure that any phase
+ * status bits that are set when changing the phase are also valid
+ * for the new phase or be cleared out. Adapter software (monitor,
+ * iflash, kernel) is responsible for properly maintining the phase
+ * status mailbox when it is running.
+ *
+ * MONKER_API Phases
+ *
+ * Phases are bit oriented. It is NOT valid to have multiple bits set
+ */
+
+#define SELF_TEST_FAILED cpu_to_le32(0x00000004)
+#define KERNEL_UP_AND_RUNNING cpu_to_le32(0x00000080)
+#define KERNEL_PANIC cpu_to_le32(0x00000100)
+
+/*
+ * Doorbell bit defines
+ */
+
+#define DoorBellPrintfDone cpu_to_le32(1<<5) // Host -> Adapter
+#define DoorBellAdapterNormCmdReady cpu_to_le32(1<<1) // Adapter -> Host
+#define DoorBellAdapterNormRespReady cpu_to_le32(1<<2) // Adapter -> Host
+#define DoorBellAdapterNormCmdNotFull cpu_to_le32(1<<3) // Adapter -> Host
+#define DoorBellAdapterNormRespNotFull cpu_to_le32(1<<4) // Adapter -> Host
+#define DoorBellPrintfReady cpu_to_le32(1<<5) // Adapter -> Host
+
+/*
+ * For FIB communication, we need all of the following things
+ * to send back to the user.
+ */
+
+#define AifCmdEventNotify 1 /* Notify of event */
+#define AifCmdJobProgress 2 /* Progress report */
+#define AifCmdAPIReport 3 /* Report from other user of API */
+#define AifCmdDriverNotify 4 /* Notify host driver of event */
+#define AifReqJobList 100 /* Gets back complete job list */
+#define AifReqJobsForCtr 101 /* Gets back jobs for specific container */
+#define AifReqJobsForScsi 102 /* Gets back jobs for specific SCSI device */
+#define AifReqJobReport 103 /* Gets back a specific job report or list of them */
+#define AifReqTerminateJob 104 /* Terminates job */
+#define AifReqSuspendJob 105 /* Suspends a job */
+#define AifReqResumeJob 106 /* Resumes a job */
+#define AifReqSendAPIReport 107 /* API generic report requests */
+#define AifReqAPIJobStart 108 /* Start a job from the API */
+#define AifReqAPIJobUpdate 109 /* Update a job report from the API */
+#define AifReqAPIJobFinish 110 /* Finish a job from the API */
+
+/*
+ * Adapter Initiated FIB command structures. Start with the adapter
+ * initiated FIBs that really come from the adapter, and get responded
+ * to by the host.
+ */
+
+struct aac_aifcmd {
+ u32 command; /* Tell host what type of notify this is */
+ u32 seqnum; /* To allow ordering of reports (if necessary) */
+ u8 data[1]; /* Undefined length (from kernel viewpoint) */
+};
+
+static inline u32 fib2addr(struct hw_fib *hw)
+{
+ return (u32)hw;
+}
+
+static inline struct hw_fib *addr2fib(u32 addr)
+{
+ return (struct hw_fib *)addr;
+}
+
+const char *aac_driverinfo(struct Scsi_Host *);
+struct fib *fib_alloc(struct aac_dev *dev);
+int fib_setup(struct aac_dev *dev);
+void fib_map_free(struct aac_dev *dev);
+void fib_free(struct fib * context);
+void fib_init(struct fib * context);
+void fib_dealloc(struct fib * context);
+void aac_printf(struct aac_dev *dev, u32 val);
+int fib_send(u16 command, struct fib * context, unsigned long size, int priority, int wait, int reply, fib_callback callback, void *ctxt);
+int aac_consumer_get(struct aac_dev * dev, struct aac_queue * q, struct aac_entry **entry);
+int aac_consumer_avail(struct aac_dev * dev, struct aac_queue * q);
+void aac_consumer_free(struct aac_dev * dev, struct aac_queue * q, u32 qnum);
+int fib_complete(struct fib * context);
+#define fib_data(fibctx) ((void *)(fibctx)->fib->data)
+int aac_detach(struct aac_dev *dev);
+struct aac_dev *aac_init_adapter(struct aac_dev *dev);
+int aac_get_containers(struct aac_dev *dev);
+int aac_scsi_cmd(Scsi_Cmnd *scsi_cmnd_ptr);
+int aac_dev_ioctl(struct aac_dev *dev, int cmd, void *arg);
+int aac_do_ioctl(struct aac_dev * dev, int cmd, void *arg);
+int aac_rx_init(struct aac_dev *dev, unsigned long devNumber);
+int aac_sa_init(struct aac_dev *dev, unsigned long devNumber);
+unsigned int aac_response_normal(struct aac_queue * q);
+unsigned int aac_command_normal(struct aac_queue * q);
+int aac_command_thread(struct aac_dev * dev);
+int aac_close_fib_context(struct aac_dev * dev, struct aac_fib_context *fibctx);
+int fib_adapter_complete(struct fib * fibptr, unsigned short size);
+struct aac_driver_ident* aac_get_driver_ident(int devtype);
+int aac_get_adapter_info(struct aac_dev* dev);
diff --git a/drivers/scsi/aacraid/commctrl.c b/drivers/scsi/aacraid/commctrl.c
new file mode 100644
index 000000000000..0acf5a86869f
--- /dev/null
+++ b/drivers/scsi/aacraid/commctrl.c
@@ -0,0 +1,432 @@
+/*
+ * Adaptec AAC series RAID controller driver
+ * (c) Copyright 2001 Red Hat Inc. <alan@redhat.com>
+ *
+ * based on the old aacraid driver that is..
+ * Adaptec aacraid device driver for Linux.
+ *
+ * Copyright (c) 2000 Adaptec, Inc. (aacraid@adaptec.com)
+ *
+ * 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
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; see the file COPYING. If not, write to
+ * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * Module Name:
+ * commctrl.c
+ *
+ * Abstract: Contains all routines for control of the AFA comm layer
+ *
+ */
+
+#include <linux/config.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/types.h>
+#include <linux/sched.h>
+#include <linux/pci.h>
+#include <linux/spinlock.h>
+#include <linux/slab.h>
+#include <linux/completion.h>
+#include <linux/blk.h>
+#include <asm/semaphore.h>
+#include <asm/uaccess.h>
+#include "scsi.h"
+#include "hosts.h"
+
+#include "aacraid.h"
+
+/**
+ * ioctl_send_fib - send a FIB from userspace
+ * @dev: adapter is being processed
+ * @arg: arguments to the ioctl call
+ *
+ * This routine sends a fib to the adapter on behalf of a user level
+ * program.
+ */
+
+static int ioctl_send_fib(struct aac_dev * dev, void *arg)
+{
+ struct hw_fib * kfib;
+ struct fib *fibptr;
+
+ fibptr = fib_alloc(dev);
+ if(fibptr == NULL)
+ return -ENOMEM;
+
+ kfib = fibptr->fib;
+ /*
+ * First copy in the header so that we can check the size field.
+ */
+ if (copy_from_user((void *)kfib, arg, sizeof(struct aac_fibhdr))) {
+ fib_free(fibptr);
+ return -EFAULT;
+ }
+ /*
+ * Since we copy based on the fib header size, make sure that we
+ * will not overrun the buffer when we copy the memory. Return
+ * an error if we would.
+ */
+ if(le32_to_cpu(kfib->header.Size) > sizeof(struct hw_fib) - sizeof(struct aac_fibhdr)) {
+ fib_free(fibptr);
+ return -EINVAL;
+ }
+
+ if (copy_from_user((void *) kfib, arg, le32_to_cpu(kfib->header.Size) + sizeof(struct aac_fibhdr))) {
+ fib_free(fibptr);
+ return -EFAULT;
+ }
+
+ if (kfib->header.Command == cpu_to_le32(TakeABreakPt)) {
+ aac_adapter_interrupt(dev);
+ /*
+ * Since we didn't really send a fib, zero out the state to allow
+ * cleanup code not to assert.
+ */
+ kfib->header.XferState = 0;
+ } else {
+ if (fib_send(kfib->header.Command, fibptr, le32_to_cpu(kfib->header.Size) , FsaNormal,
+ 1, 1, NULL, NULL) != 0)
+ {
+ fib_free(fibptr);
+ return -EINVAL;
+ }
+ if (fib_complete(fibptr) != 0) {
+ fib_free(fibptr);
+ return -EINVAL;
+ }
+ }
+ /*
+ * Make sure that the size returned by the adapter (which includes
+ * the header) is less than or equal to the size of a fib, so we
+ * don't corrupt application data. Then copy that size to the user
+ * buffer. (Don't try to add the header information again, since it
+ * was already included by the adapter.)
+ */
+
+ if (copy_to_user(arg, (void *)kfib, kfib->header.Size)) {
+ fib_free(fibptr);
+ return -EFAULT;
+ }
+ fib_free(fibptr);
+ return 0;
+}
+
+/**
+ * open_getadapter_fib - Get the next fib
+ *
+ * This routine will get the next Fib, if available, from the AdapterFibContext
+ * passed in from the user.
+ */
+
+static int open_getadapter_fib(struct aac_dev * dev, void *arg)
+{
+ struct aac_fib_context * fibctx;
+ int status;
+ unsigned long flags;
+
+ fibctx = kmalloc(sizeof(struct aac_fib_context), GFP_KERNEL);
+ if (fibctx == NULL) {
+ status = -ENOMEM;
+ } else {
+ fibctx->type = FSAFS_NTC_GET_ADAPTER_FIB_CONTEXT;
+ fibctx->size = sizeof(struct aac_fib_context);
+ /*
+ * Initialize the mutex used to wait for the next AIF.
+ */
+ init_MUTEX_LOCKED(&fibctx->wait_sem);
+ fibctx->wait = 0;
+ /*
+ * Initialize the fibs and set the count of fibs on
+ * the list to 0.
+ */
+ fibctx->count = 0;
+ INIT_LIST_HEAD(&fibctx->fibs);
+ fibctx->jiffies = jiffies/HZ;
+ /*
+ * Now add this context onto the adapter's
+ * AdapterFibContext list.
+ */
+ spin_lock_irqsave(&dev->fib_lock, flags);
+ list_add_tail(&fibctx->next, &dev->fib_list);
+ spin_unlock_irqrestore(&dev->fib_lock, flags);
+ if (copy_to_user(arg, &fibctx, sizeof(struct aac_fib_context *))) {
+ status = -EFAULT;
+ } else {
+ status = 0;
+ }
+ }
+ return status;
+}
+
+/**
+ * next_getadapter_fib - get the next fib
+ * @dev: adapter to use
+ * @arg: ioctl argument
+ *
+ * This routine will get the next Fib, if available, from the AdapterFibContext
+ * passed in from the user.
+ */
+
+static int next_getadapter_fib(struct aac_dev * dev, void *arg)
+{
+ struct fib_ioctl f;
+ struct aac_fib_context *fibctx, *aifcp;
+ struct hw_fib * fib;
+ int status;
+ struct list_head * entry;
+ int found;
+ unsigned long flags;
+
+ if(copy_from_user((void *)&f, arg, sizeof(struct fib_ioctl)))
+ return -EFAULT;
+ /*
+ * Extract the AdapterFibContext from the Input parameters.
+ */
+ fibctx = (struct aac_fib_context *) f.fibctx;
+
+ /*
+ * Verify that the HANDLE passed in was a valid AdapterFibContext
+ *
+ * Search the list of AdapterFibContext addresses on the adapter
+ * to be sure this is a valid address
+ */
+ found = 0;
+ entry = dev->fib_list.next;
+
+ while(entry != &dev->fib_list) {
+ aifcp = list_entry(entry, struct aac_fib_context, next);
+ if(fibctx == aifcp) { /* We found a winner */
+ found = 1;
+ break;
+ }
+ entry = entry->next;
+ }
+ if (found == 0)
+ return -EINVAL;
+
+ if((fibctx->type != FSAFS_NTC_GET_ADAPTER_FIB_CONTEXT) ||
+ (fibctx->size != sizeof(struct aac_fib_context)))
+ return -EINVAL;
+ status = 0;
+ spin_lock_irqsave(&dev->fib_lock, flags);
+ /*
+ * If there are no fibs to send back, then either wait or return
+ * -EAGAIN
+ */
+return_fib:
+ if (!list_empty(&fibctx->fibs)) {
+ struct list_head * entry;
+ /*
+ * Pull the next fib from the fibs
+ */
+ entry = fibctx->fibs.next;
+ list_del(entry);
+
+ fib = list_entry(entry, struct hw_fib, header.FibLinks);
+ fibctx->count--;
+ spin_unlock_irqrestore(&dev->fib_lock, flags);
+ if (copy_to_user(f.fib, fib, sizeof(struct hw_fib))) {
+ kfree(fib);
+ return -EFAULT;
+ }
+ /*
+ * Free the space occupied by this copy of the fib.
+ */
+ kfree(fib);
+ status = 0;
+ fibctx->jiffies = jiffies/HZ;
+ } else {
+ spin_unlock_irqrestore(&dev->fib_lock, flags);
+ if (f.wait) {
+ if(down_interruptible(&fibctx->wait_sem) < 0) {
+ status = -EINTR;
+ } else {
+ /* Lock again and retry */
+ spin_lock_irqsave(&dev->fib_lock, flags);
+ goto return_fib;
+ }
+ } else {
+ status = -EAGAIN;
+ }
+ }
+ return status;
+}
+
+int aac_close_fib_context(struct aac_dev * dev, struct aac_fib_context * fibctx)
+{
+ struct hw_fib *fib;
+
+ /*
+ * First free any FIBs that have not been consumed.
+ */
+ while (!list_empty(&fibctx->fibs)) {
+ struct list_head * entry;
+ /*
+ * Pull the next fib from the fibs
+ */
+ entry = fibctx->fibs.next;
+ list_del(entry);
+ fib = list_entry(entry, struct hw_fib, header.FibLinks);
+ fibctx->count--;
+ /*
+ * Free the space occupied by this copy of the fib.
+ */
+ kfree(fib);
+ }
+ /*
+ * Remove the Context from the AdapterFibContext List
+ */
+ list_del(&fibctx->next);
+ /*
+ * Invalidate context
+ */
+ fibctx->type = 0;
+ /*
+ * Free the space occupied by the Context
+ */
+ kfree(fibctx);
+ return 0;
+}
+
+/**
+ * close_getadapter_fib - close down user fib context
+ * @dev: adapter
+ * @arg: ioctl arguments
+ *
+ * This routine will close down the fibctx passed in from the user.
+ */
+
+static int close_getadapter_fib(struct aac_dev * dev, void *arg)
+{
+ struct aac_fib_context *fibctx, *aifcp;
+ int status;
+ unsigned long flags;
+ struct list_head * entry;
+ int found;
+
+ /*
+ * Extract the fibctx from the input parameters
+ */
+ fibctx = arg;
+
+ /*
+ * Verify that the HANDLE passed in was a valid AdapterFibContext
+ *
+ * Search the list of AdapterFibContext addresses on the adapter
+ * to be sure this is a valid address
+ */
+
+ found = 0;
+ entry = dev->fib_list.next;
+
+ while(entry != &dev->fib_list) {
+ aifcp = list_entry(entry, struct aac_fib_context, next);
+ if(fibctx == aifcp) { /* We found a winner */
+ found = 1;
+ break;
+ }
+ entry = entry->next;
+ }
+
+ if(found == 0)
+ return 0; /* Already gone */
+
+ if((fibctx->type != FSAFS_NTC_GET_ADAPTER_FIB_CONTEXT) ||
+ (fibctx->size != sizeof(struct aac_fib_context)))
+ return -EINVAL;
+ spin_lock_irqsave(&dev->fib_lock, flags);
+ status = aac_close_fib_context(dev, fibctx);
+ spin_unlock_irqrestore(&dev->fib_lock, flags);
+ return status;
+}
+
+/**
+ * check_revision - close down user fib context
+ * @dev: adapter
+ * @arg: ioctl arguments
+ *
+ * This routine returns the firmware version.
+ * Under Linux, there have been no version incompatibilities, so this is simple!
+ */
+
+static int check_revision(struct aac_dev *dev, void *arg)
+{
+ struct revision response;
+
+ response.compat = 1;
+ response.version = dev->adapter_info.kernelrev;
+ response.build = dev->adapter_info.kernelbuild;
+
+ if (copy_to_user(arg, &response, sizeof(response)))
+ return -EFAULT;
+ return 0;
+}
+
+
+struct aac_pci_info {
+ u32 bus;
+ u32 slot;
+};
+
+
+int aac_get_pci_info(struct aac_dev* dev, void* arg)
+{
+ struct aac_pci_info pci_info;
+
+ pci_info.bus = dev->pdev->bus->number;
+ pci_info.slot = PCI_SLOT(dev->pdev->devfn);
+
+ if(copy_to_user( arg, (void*)&pci_info, sizeof(struct aac_pci_info)))
+ return -EFAULT;
+ return 0;
+ }
+
+
+int aac_do_ioctl(struct aac_dev * dev, int cmd, void *arg)
+{
+ int status;
+
+ /*
+ * HBA gets first crack
+ */
+
+ status = aac_dev_ioctl(dev, cmd, arg);
+ if(status != -ENOTTY)
+ return status;
+
+ switch (cmd) {
+ case FSACTL_MINIPORT_REV_CHECK:
+ status = check_revision(dev, arg);
+ break;
+ case FSACTL_SENDFIB:
+ status = ioctl_send_fib(dev, arg);
+ break;
+ case FSACTL_OPEN_GET_ADAPTER_FIB:
+ status = open_getadapter_fib(dev, arg);
+ break;
+ case FSACTL_GET_NEXT_ADAPTER_FIB:
+ status = next_getadapter_fib(dev, arg);
+ break;
+ case FSACTL_CLOSE_GET_ADAPTER_FIB:
+ status = close_getadapter_fib(dev, arg);
+ break;
+ case FSACTL_GET_PCI_INFO:
+ status = aac_get_pci_info(dev,arg);
+ break;
+ default:
+ status = -ENOTTY;
+ break;
+ }
+ return status;
+}
+
diff --git a/drivers/scsi/aacraid/comminit.c b/drivers/scsi/aacraid/comminit.c
new file mode 100644
index 000000000000..88ca2f6fd58f
--- /dev/null
+++ b/drivers/scsi/aacraid/comminit.c
@@ -0,0 +1,341 @@
+/*
+ * Adaptec AAC series RAID controller driver
+ * (c) Copyright 2001 Red Hat Inc. <alan@redhat.com>
+ *
+ * based on the old aacraid driver that is..
+ * Adaptec aacraid device driver for Linux.
+ *
+ * Copyright (c) 2000 Adaptec, Inc. (aacraid@adaptec.com)
+ *
+ * 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
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; see the file COPYING. If not, write to
+ * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * Module Name:
+ * comminit.c
+ *
+ * Abstract: This supports the initialization of the host adapter commuication interface.
+ * This is a platform dependent module for the pci cyclone board.
+ *
+ */
+
+#include <linux/config.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/types.h>
+#include <linux/sched.h>
+#include <linux/pci.h>
+#include <linux/spinlock.h>
+#include <linux/slab.h>
+#include <linux/blk.h>
+#include <linux/completion.h>
+#include <asm/semaphore.h>
+#include "scsi.h"
+#include "hosts.h"
+
+#include "aacraid.h"
+
+struct aac_common aac_config;
+
+static struct aac_dev *devices;
+
+static int aac_alloc_comm(struct aac_dev *dev, void **commaddr, unsigned long commsize, unsigned long commalign)
+{
+ unsigned char *base;
+ unsigned long size, align;
+ unsigned long fibsize = 4096;
+ unsigned long printfbufsiz = 256;
+ struct aac_init *init;
+ dma_addr_t phys;
+
+ /* FIXME: Adaptec add 128 bytes to this value - WHY ?? */
+ size = fibsize + sizeof(struct aac_init) + commsize + commalign + printfbufsiz;
+
+ base = pci_alloc_consistent(dev->pdev, size, &phys);
+ if(base == NULL)
+ {
+ printk(KERN_ERR "aacraid: unable to create mapping.\n");
+ return 0;
+ }
+ dev->comm_addr = (void *)base;
+ dev->comm_phys = phys;
+ dev->comm_size = size;
+
+ dev->init = (struct aac_init *)(base + fibsize);
+ dev->init_pa = phys + fibsize;
+
+ /*
+ * Cache the upper bits of the virtual mapping for 64bit boxes
+ * FIXME: this crap should be rewritten
+ */
+#if BITS_PER_LONG >= 64
+ dev->fib_base_va = ((ulong)base & 0xffffffff00000000);
+#endif
+
+ init = dev->init;
+
+ init->InitStructRevision = cpu_to_le32(ADAPTER_INIT_STRUCT_REVISION);
+ init->MiniPortRevision = cpu_to_le32(Sa_MINIPORT_REVISION);
+ init->fsrev = cpu_to_le32(dev->fsrev);
+
+ /*
+ * Adapter Fibs are the first thing allocated so that they
+ * start page aligned
+ */
+ init->AdapterFibsVirtualAddress = cpu_to_le32((u32)base);
+ init->AdapterFibsPhysicalAddress = cpu_to_le32(phys);
+ init->AdapterFibsSize = cpu_to_le32(fibsize);
+ init->AdapterFibAlign = cpu_to_le32(sizeof(struct hw_fib));
+
+ /*
+ * Increment the base address by the amount already used
+ */
+ base = base + fibsize + sizeof(struct aac_init);
+ phys = phys + fibsize + sizeof(struct aac_init);
+ /*
+ * Align the beginning of Headers to commalign
+ */
+ align = (commalign - ((unsigned long)(base) & (commalign - 1)));
+ base = base + align;
+ phys = phys + align;
+ /*
+ * Fill in addresses of the Comm Area Headers and Queues
+ */
+ *commaddr = (unsigned long *)base;
+ init->CommHeaderAddress = cpu_to_le32(phys);
+ /*
+ * Increment the base address by the size of the CommArea
+ */
+ base = base + commsize;
+ phys = phys + commsize;
+ /*
+ * Place the Printf buffer area after the Fast I/O comm area.
+ */
+ dev->printfbuf = (void *)base;
+ init->printfbuf = cpu_to_le32(phys);
+ init->printfbufsiz = cpu_to_le32(printfbufsiz);
+ memset(base, 0, printfbufsiz);
+ return 1;
+}
+
+static void aac_queue_init(struct aac_dev * dev, struct aac_queue * q, u32 *mem, int qsize)
+{
+ q->numpending = 0;
+ q->dev = dev;
+ INIT_LIST_HEAD(&q->pendingq);
+ init_waitqueue_head(&q->cmdready);
+ INIT_LIST_HEAD(&q->cmdq);
+ init_waitqueue_head(&q->qfull);
+ spin_lock_init(&q->lockdata);
+ q->lock = &q->lockdata;
+ q->headers.producer = mem;
+ q->headers.consumer = mem+1;
+ *q->headers.producer = cpu_to_le32(qsize);
+ *q->headers.consumer = cpu_to_le32(qsize);
+ q->entries = qsize;
+}
+
+/**
+ * aac_send_shutdown - shutdown an adapter
+ * @dev: Adapter to shutdown
+ *
+ * This routine will send a VM_CloseAll (shutdown) request to the adapter.
+ */
+
+static int aac_send_shutdown(struct aac_dev * dev)
+{
+ struct fib * fibctx;
+ struct aac_close *cmd;
+ int status;
+
+ fibctx = fib_alloc(dev);
+ fib_init(fibctx);
+
+ cmd = (struct aac_close *) fib_data(fibctx);
+
+ cmd->command = cpu_to_le32(VM_CloseAll);
+ cmd->cid = cpu_to_le32(0xffffffff);
+
+ status = fib_send(ContainerCommand,
+ fibctx,
+ sizeof(struct aac_close),
+ FsaNormal,
+ 1, 1,
+ NULL, NULL);
+
+ if (status == 0)
+ fib_complete(fibctx);
+ fib_free(fibctx);
+ return status;
+}
+
+/**
+ * aac_detach - detach adapter
+ * @detach: adapter to disconnect
+ *
+ * Disconnect and shutdown an AAC based adapter, freeing resources
+ * as we go.
+ */
+
+int aac_detach(struct aac_dev *detach)
+{
+ struct aac_dev **dev = &devices;
+
+ while(*dev)
+ {
+ if(*dev == detach)
+ {
+ *dev = detach->next;
+ aac_send_shutdown(detach);
+ fib_map_free(detach);
+ pci_free_consistent(detach->pdev, detach->comm_size, detach->comm_addr, detach->comm_phys);
+ kfree(detach->queues);
+ return 1;
+ }
+ dev=&((*dev)->next);
+ }
+ BUG();
+ return 0;
+}
+
+/**
+ * aac_comm_init - Initialise FSA data structures
+ * @dev: Adapter to intialise
+ *
+ * Initializes the data structures that are required for the FSA commuication
+ * interface to operate.
+ * Returns
+ * 1 - if we were able to init the commuication interface.
+ * 0 - If there were errors initing. This is a fatal error.
+ */
+
+int aac_comm_init(struct aac_dev * dev)
+{
+ unsigned long hdrsize = (sizeof(u32) * NUMBER_OF_COMM_QUEUES) * 2;
+ unsigned long queuesize = sizeof(struct aac_entry) * TOTAL_QUEUE_ENTRIES;
+ u32 *headers;
+ struct aac_entry * queues;
+ unsigned long size;
+ struct aac_queue_block * comm = dev->queues;
+
+ /*
+ * Now allocate and initialize the zone structures used as our
+ * pool of FIB context records. The size of the zone is based
+ * on the system memory size. We also initialize the mutex used
+ * to protect the zone.
+ */
+ spin_lock_init(&dev->fib_lock);
+
+ /*
+ * Allocate the physically contigous space for the commuication
+ * queue headers.
+ */
+
+ size = hdrsize + queuesize;
+
+ if (!aac_alloc_comm(dev, (void * *)&headers, size, QUEUE_ALIGNMENT))
+ return -ENOMEM;
+
+ queues = (struct aac_entry *)((unsigned char *)headers + hdrsize);
+
+ /* Adapter to Host normal proirity Command queue */
+ comm->queue[HostNormCmdQueue].base = queues;
+ aac_queue_init(dev, &comm->queue[HostNormCmdQueue], headers, HOST_NORM_CMD_ENTRIES);
+ queues += HOST_NORM_CMD_ENTRIES;
+ headers += 2;
+
+ /* Adapter to Host high priority command queue */
+ comm->queue[HostHighCmdQueue].base = queues;
+ aac_queue_init(dev, &comm->queue[HostHighCmdQueue], headers, HOST_HIGH_CMD_ENTRIES);
+
+ queues += HOST_HIGH_CMD_ENTRIES;
+ headers +=2;
+
+ /* Host to adapter normal priority command queue */
+ comm->queue[AdapNormCmdQueue].base = queues;
+ aac_queue_init(dev, &comm->queue[AdapNormCmdQueue], headers, ADAP_NORM_CMD_ENTRIES);
+
+ queues += ADAP_NORM_CMD_ENTRIES;
+ headers += 2;
+
+ /* host to adapter high priority command queue */
+ comm->queue[AdapHighCmdQueue].base = queues;
+ aac_queue_init(dev, &comm->queue[AdapHighCmdQueue], headers, ADAP_HIGH_CMD_ENTRIES);
+
+ queues += ADAP_HIGH_CMD_ENTRIES;
+ headers += 2;
+
+ /* adapter to host normal priority response queue */
+ comm->queue[HostNormRespQueue].base = queues;
+ aac_queue_init(dev, &comm->queue[HostNormRespQueue], headers, HOST_NORM_RESP_ENTRIES);
+
+ queues += HOST_NORM_RESP_ENTRIES;
+ headers += 2;
+
+ /* adapter to host high priority response queue */
+ comm->queue[HostHighRespQueue].base = queues;
+ aac_queue_init(dev, &comm->queue[HostHighRespQueue], headers, HOST_HIGH_RESP_ENTRIES);
+
+ queues += HOST_HIGH_RESP_ENTRIES;
+ headers += 2;
+
+ /* host to adapter normal priority response queue */
+ comm->queue[AdapNormRespQueue].base = queues;
+ aac_queue_init(dev, &comm->queue[AdapNormRespQueue], headers, ADAP_NORM_RESP_ENTRIES);
+
+ queues += ADAP_NORM_RESP_ENTRIES;
+ headers += 2;
+
+ /* host to adapter high priority response queue */
+ comm->queue[AdapHighRespQueue].base = queues;
+ aac_queue_init(dev, &comm->queue[AdapHighRespQueue], headers, ADAP_HIGH_RESP_ENTRIES);
+
+ comm->queue[AdapNormCmdQueue].lock = comm->queue[HostNormRespQueue].lock;
+ comm->queue[AdapHighCmdQueue].lock = comm->queue[HostHighRespQueue].lock;
+ comm->queue[AdapNormRespQueue].lock = comm->queue[HostNormCmdQueue].lock;
+ comm->queue[AdapHighRespQueue].lock = comm->queue[HostHighCmdQueue].lock;
+
+ return 0;
+}
+
+struct aac_dev *aac_init_adapter(struct aac_dev *dev)
+{
+ /*
+ * Ok now init the communication subsystem
+ */
+ dev->queues = (struct aac_queue_block *) kmalloc(sizeof(struct aac_queue_block), GFP_KERNEL);
+ if (dev->queues == NULL) {
+ printk(KERN_ERR "Error could not allocate comm region.\n");
+ return NULL;
+ }
+ memset(dev->queues, 0, sizeof(struct aac_queue_block));
+
+ if (aac_comm_init(dev)<0)
+ return NULL;
+ /*
+ * Initialize the list of fibs
+ */
+ if(fib_setup(dev)<0)
+ return NULL;
+
+ INIT_LIST_HEAD(&dev->fib_list);
+ init_completion(&dev->aif_completion);
+ /*
+ * Add this adapter in to our dev List.
+ */
+ dev->next = devices;
+ devices = dev;
+ return dev;
+}
+
+
diff --git a/drivers/scsi/aacraid/commsup.c b/drivers/scsi/aacraid/commsup.c
new file mode 100644
index 000000000000..55c36b8bed90
--- /dev/null
+++ b/drivers/scsi/aacraid/commsup.c
@@ -0,0 +1,948 @@
+/*
+ * Adaptec AAC series RAID controller driver
+ * (c) Copyright 2001 Red Hat Inc. <alan@redhat.com>
+ *
+ * based on the old aacraid driver that is..
+
+ * Adaptec aacraid device driver for Linux.
+ *
+ * Copyright (c) 2000 Adaptec, Inc. (aacraid@adaptec.com)
+ *
+ * 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
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; see the file COPYING. If not, write to
+ * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * Module Name:
+ * commsup.c
+ *
+ * Abstract: Contain all routines that are required for FSA host/adapter
+ * commuication.
+ *
+ *
+ */
+
+#include <linux/config.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/types.h>
+#include <linux/sched.h>
+#include <linux/pci.h>
+#include <linux/spinlock.h>
+#include <linux/slab.h>
+#include <linux/completion.h>
+#include <asm/semaphore.h>
+#include <linux/blk.h>
+#include "scsi.h"
+#include "hosts.h"
+
+#include "aacraid.h"
+
+/**
+ * fib_map_alloc - allocate the fib objects
+ * @dev: Adapter to allocate for
+ *
+ * Allocate and map the shared PCI space for the FIB blocks used to
+ * talk to the Adaptec firmware.
+ */
+
+static int fib_map_alloc(struct aac_dev *dev)
+{
+ if((dev->hw_fib_va = pci_alloc_consistent(dev->pdev, sizeof(struct hw_fib) * AAC_NUM_FIB, &dev->hw_fib_pa))==NULL)
+ return -ENOMEM;
+ return 0;
+}
+
+/**
+ * fib_map_free - free the fib objects
+ * @dev: Adapter to free
+ *
+ * Free the PCI mappings and the memory allocated for FIB blocks
+ * on this adapter.
+ */
+
+void fib_map_free(struct aac_dev *dev)
+{
+ pci_free_consistent(dev->pdev, sizeof(struct hw_fib) * AAC_NUM_FIB, dev->hw_fib_va, dev->hw_fib_pa);
+}
+
+/**
+ * fib_setup - setup the fibs
+ * @dev: Adapter to set up
+ *
+ * Allocate the PCI space for the fibs, map it and then intialise the
+ * fib area, the unmapped fib data and also the free list
+ */
+
+int fib_setup(struct aac_dev * dev)
+{
+ struct fib *fibptr;
+ struct hw_fib *fib;
+ dma_addr_t fibpa;
+ int i;
+
+ if(fib_map_alloc(dev)<0)
+ return -ENOMEM;
+
+ fib = dev->hw_fib_va;
+ fibpa = dev->hw_fib_pa;
+ memset(fib, 0, sizeof(struct hw_fib) * AAC_NUM_FIB);
+ /*
+ * Initialise the fibs
+ */
+ for (i = 0, fibptr = &dev->fibs[i]; i < AAC_NUM_FIB; i++, fibptr++)
+ {
+ fibptr->dev = dev;
+ fibptr->fib = fib;
+ fibptr->data = (void *) fibptr->fib->data;
+ fibptr->next = fibptr+1; /* Forward chain the fibs */
+ init_MUTEX_LOCKED(&fibptr->event_wait);
+ spin_lock_init(&fibptr->event_lock);
+ fib->header.XferState = cpu_to_le32(0xffffffff);
+ fib->header.SenderSize = cpu_to_le16(sizeof(struct hw_fib));
+ fibptr->logicaladdr = (unsigned long) fibpa;
+ fib = (struct hw_fib *)((unsigned char *)fib + sizeof(struct hw_fib));
+ fibpa = fibpa + sizeof(struct hw_fib);
+ }
+ /*
+ * Add the fib chain to the free list
+ */
+ dev->fibs[AAC_NUM_FIB-1].next = NULL;
+ /*
+ * Enable this to debug out of queue space
+ */
+ dev->free_fib = &dev->fibs[0];
+ return 0;
+}
+
+/**
+ * fib_alloc - allocate a fib
+ * @dev: Adapter to allocate the fib for
+ *
+ * Allocate a fib from the adapter fib pool. If the pool is empty we
+ * wait for fibs to become free.
+ */
+
+struct fib * fib_alloc(struct aac_dev *dev)
+{
+ struct fib * fibptr;
+ unsigned long flags;
+
+ spin_lock_irqsave(&dev->fib_lock, flags);
+ fibptr = dev->free_fib;
+ if(!fibptr)
+ BUG();
+ dev->free_fib = fibptr->next;
+ spin_unlock_irqrestore(&dev->fib_lock, flags);
+ /*
+ * Set the proper node type code and node byte size
+ */
+ fibptr->type = FSAFS_NTC_FIB_CONTEXT;
+ fibptr->size = sizeof(struct fib);
+ /*
+ * Null out fields that depend on being zero at the start of
+ * each I/O
+ */
+ fibptr->fib->header.XferState = cpu_to_le32(0);
+ fibptr->callback = NULL;
+ fibptr->callback_data = NULL;
+
+ return fibptr;
+}
+
+/**
+ * fib_free - free a fib
+ * @fibptr: fib to free up
+ *
+ * Frees up a fib and places it on the appropriate queue
+ * (either free or timed out)
+ */
+
+void fib_free(struct fib * fibptr)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&fibptr->dev->fib_lock, flags);
+
+ if (fibptr->flags & FIB_CONTEXT_FLAG_TIMED_OUT) {
+ aac_config.fib_timeouts++;
+ fibptr->next = fibptr->dev->timeout_fib;
+ fibptr->dev->timeout_fib = fibptr;
+ } else {
+ if (fibptr->fib->header.XferState != 0) {
+ printk(KERN_WARNING "fib_free, XferState != 0, fibptr = 0x%p, XferState = 0x%x\n",
+ (void *)fibptr, fibptr->fib->header.XferState);
+ }
+ fibptr->next = fibptr->dev->free_fib;
+ fibptr->dev->free_fib = fibptr;
+ }
+ spin_unlock_irqrestore(&fibptr->dev->fib_lock, flags);
+}
+
+/**
+ * fib_init - initialise a fib
+ * @fibptr: The fib to initialize
+ *
+ * Set up the generic fib fields ready for use
+ */
+
+void fib_init(struct fib *fibptr)
+{
+ struct hw_fib *fib = fibptr->fib;
+
+ fib->header.StructType = FIB_MAGIC;
+ fib->header.Size = cpu_to_le16(sizeof(struct hw_fib));
+ fib->header.XferState = cpu_to_le32(HostOwned | FibInitialized | FibEmpty | FastResponseCapable);
+ fib->header.SenderFibAddress = cpu_to_le32(0);
+ fib->header.ReceiverFibAddress = cpu_to_le32(0);
+ fib->header.SenderSize = cpu_to_le16(sizeof(struct hw_fib));
+}
+
+/**
+ * fib_deallocate - deallocate a fib
+ * @fibptr: fib to deallocate
+ *
+ * Will deallocate and return to the free pool the FIB pointed to by the
+ * caller.
+ */
+
+void fib_dealloc(struct fib * fibptr)
+{
+ struct hw_fib *fib = fibptr->fib;
+ if(fib->header.StructType != FIB_MAGIC)
+ BUG();
+ fib->header.XferState = cpu_to_le32(0);
+}
+
+/*
+ * Commuication primitives define and support the queuing method we use to
+ * support host to adapter commuication. All queue accesses happen through
+ * these routines and are the only routines which have a knowledge of the
+ * how these queues are implemented.
+ */
+
+/**
+ * aac_get_entry - get a queue entry
+ * @dev: Adapter
+ * @qid: Queue Number
+ * @entry: Entry return
+ * @index: Index return
+ * @nonotify: notification control
+ *
+ * With a priority the routine returns a queue entry if the queue has free entries. If the queue
+ * is full(no free entries) than no entry is returned and the function returns 0 otherwise 1 is
+ * returned.
+ */
+
+static int aac_get_entry (struct aac_dev * dev, u32 qid, struct aac_entry **entry, u32 * index, unsigned long *nonotify)
+{
+ struct aac_queue * q;
+
+ /*
+ * All of the queues wrap when they reach the end, so we check
+ * to see if they have reached the end and if they have we just
+ * set the index back to zero. This is a wrap. You could or off
+ * the high bits in all updates but this is a bit faster I think.
+ */
+
+ q = &dev->queues->queue[qid];
+
+ *index = le32_to_cpu(*(q->headers.producer));
+ if (*index - 2 == le32_to_cpu(*(q->headers.consumer)))
+ *nonotify = 1;
+
+ if (qid == AdapHighCmdQueue) {
+ if (*index >= ADAP_HIGH_CMD_ENTRIES)
+ *index = 0;
+ } else if (qid == AdapNormCmdQueue) {
+ if (*index >= ADAP_NORM_CMD_ENTRIES)
+ *index = 0; /* Wrap to front of the Producer Queue. */
+ }
+ else if (qid == AdapHighRespQueue)
+ {
+ if (*index >= ADAP_HIGH_RESP_ENTRIES)
+ *index = 0;
+ }
+ else if (qid == AdapNormRespQueue)
+ {
+ if (*index >= ADAP_NORM_RESP_ENTRIES)
+ *index = 0; /* Wrap to front of the Producer Queue. */
+ }
+ else BUG();
+
+ if (*index + 1 == le32_to_cpu(*(q->headers.consumer))) { /* Queue is full */
+ printk(KERN_WARNING "Queue %d full, %ld outstanding.\n", qid, q->numpending);
+ return 0;
+ } else {
+ *entry = q->base + *index;
+ return 1;
+ }
+}
+
+/**
+ * aac_queue_get - get the next free QE
+ * @dev: Adapter
+ * @index: Returned index
+ * @priority: Priority of fib
+ * @fib: Fib to associate with the queue entry
+ * @wait: Wait if queue full
+ * @fibptr: Driver fib object to go with fib
+ * @nonotify: Don't notify the adapter
+ *
+ * Gets the next free QE off the requested priorty adapter command
+ * queue and associates the Fib with the QE. The QE represented by
+ * index is ready to insert on the queue when this routine returns
+ * success.
+ */
+
+static int aac_queue_get(struct aac_dev * dev, u32 * index, u32 qid, struct hw_fib * fib, int wait, struct fib * fibptr, unsigned long *nonotify)
+{
+ struct aac_entry * entry = NULL;
+ int map = 0;
+ struct aac_queue * q = &dev->queues->queue[qid];
+
+ spin_lock_irqsave(q->lock, q->SavedIrql);
+
+ if (qid == AdapHighCmdQueue || qid == AdapNormCmdQueue)
+ {
+ /* if no entries wait for some if caller wants to */
+ while (!aac_get_entry(dev, qid, &entry, index, nonotify))
+ {
+ printk(KERN_ERR "GetEntries failed\n");
+ }
+ /*
+ * Setup queue entry with a command, status and fib mapped
+ */
+ entry->size = cpu_to_le32(le16_to_cpu(fib->header.Size));
+ map = 1;
+ }
+ else if (qid == AdapHighRespQueue || qid == AdapNormRespQueue)
+ {
+ while(!aac_get_entry(dev, qid, &entry, index, nonotify))
+ {
+ /* if no entries wait for some if caller wants to */
+ }
+ /*
+ * Setup queue entry with command, status and fib mapped
+ */
+ entry->size = cpu_to_le32(le16_to_cpu(fib->header.Size));
+ entry->addr = cpu_to_le32(fib->header.SenderFibAddress); /* Restore adapters pointer to the FIB */
+ fib->header.ReceiverFibAddress = fib->header.SenderFibAddress; /* Let the adapter now where to find its data */
+ map = 0;
+ }
+ /*
+ * If MapFib is true than we need to map the Fib and put pointers
+ * in the queue entry.
+ */
+ if (map)
+ entry->addr = cpu_to_le32((unsigned long)(fibptr->logicaladdr));
+ return 0;
+}
+
+
+/**
+ * aac_insert_entry - insert a queue entry
+ * @dev: Adapter
+ * @index: Index of entry to insert
+ * @qid: Queue number
+ * @nonotify: Suppress adapter notification
+ *
+ * Gets the next free QE off the requested priorty adapter command
+ * queue and associates the Fib with the QE. The QE represented by
+ * index is ready to insert on the queue when this routine returns
+ * success.
+ */
+
+static int aac_insert_entry(struct aac_dev * dev, u32 index, u32 qid, unsigned long nonotify)
+{
+ struct aac_queue * q = &dev->queues->queue[qid];
+
+ if(q == NULL)
+ BUG();
+ *(q->headers.producer) = cpu_to_le32(index + 1);
+ spin_unlock_irqrestore(q->lock, q->SavedIrql);
+
+ if (qid == AdapHighCmdQueue ||
+ qid == AdapNormCmdQueue ||
+ qid == AdapHighRespQueue ||
+ qid == AdapNormRespQueue)
+ {
+ if (!nonotify)
+ aac_adapter_notify(dev, qid);
+ }
+ else
+ printk("Suprise insert!\n");
+ return 0;
+}
+
+/*
+ * Define the highest level of host to adapter communication routines.
+ * These routines will support host to adapter FS commuication. These
+ * routines have no knowledge of the commuication method used. This level
+ * sends and receives FIBs. This level has no knowledge of how these FIBs
+ * get passed back and forth.
+ */
+
+/**
+ * fib_send - send a fib to the adapter
+ * @command: Command to send
+ * @fibptr: The fib
+ * @size: Size of fib data area
+ * @priority: Priority of Fib
+ * @wait: Async/sync select
+ * @reply: True if a reply is wanted
+ * @callback: Called with reply
+ * @callback_data: Passed to callback
+ *
+ * Sends the requested FIB to the adapter and optionally will wait for a
+ * response FIB. If the caller does not wish to wait for a response than
+ * an event to wait on must be supplied. This event will be set when a
+ * response FIB is received from the adapter.
+ */
+
+int fib_send(u16 command, struct fib * fibptr, unsigned long size, int priority, int wait, int reply, fib_callback callback, void * callback_data)
+{
+ u32 index;
+ u32 qid;
+ struct aac_dev * dev = fibptr->dev;
+ unsigned long nointr = 0;
+ struct hw_fib * fib = fibptr->fib;
+ struct aac_queue * q;
+ unsigned long flags = 0;
+
+ if (!(le32_to_cpu(fib->header.XferState) & HostOwned))
+ return -EBUSY;
+ /*
+ * There are 5 cases with the wait and reponse requested flags.
+ * The only invalid cases are if the caller requests to wait and
+ * does not request a response and if the caller does not want a
+ * response and the Fibis not allocated from pool. If a response
+ * is not requesed the Fib will just be deallocaed by the DPC
+ * routine when the response comes back from the adapter. No
+ * further processing will be done besides deleting the Fib. We
+ * will have a debug mode where the adapter can notify the host
+ * it had a problem and the host can log that fact.
+ */
+ if (wait && !reply) {
+ return -EINVAL;
+ } else if (!wait && reply) {
+ fib->header.XferState |= cpu_to_le32(Async | ResponseExpected);
+ FIB_COUNTER_INCREMENT(aac_config.AsyncSent);
+ } else if (!wait && !reply) {
+ fib->header.XferState |= cpu_to_le32(NoResponseExpected);
+ FIB_COUNTER_INCREMENT(aac_config.NoResponseSent);
+ } else if (wait && reply) {
+ fib->header.XferState |= cpu_to_le32(ResponseExpected);
+ FIB_COUNTER_INCREMENT(aac_config.NormalSent);
+ }
+ /*
+ * Map the fib into 32bits by using the fib number
+ */
+ fib->header.SenderData = fibptr-&dev->fibs[0]; /* for callback */
+ /*
+ * Set FIB state to indicate where it came from and if we want a
+ * response from the adapter. Also load the command from the
+ * caller.
+ *
+ * Map the hw fib pointer as a 32bit value
+ */
+ fib->header.SenderFibAddress = fib2addr(fib);
+ fib->header.Command = cpu_to_le16(command);
+ fib->header.XferState |= cpu_to_le32(SentFromHost);
+ fibptr->fib->header.Flags = 0; /* Zero the flags field - its internal only... */
+ /*
+ * Set the size of the Fib we want to send to the adapter
+ */
+ fib->header.Size = cpu_to_le16(sizeof(struct aac_fibhdr) + size);
+ if (le16_to_cpu(fib->header.Size) > le16_to_cpu(fib->header.SenderSize)) {
+ return -EMSGSIZE;
+ }
+ /*
+ * Get a queue entry connect the FIB to it and send an notify
+ * the adapter a command is ready.
+ */
+ if (priority == FsaHigh) {
+ fib->header.XferState |= cpu_to_le32(HighPriority);
+ qid = AdapHighCmdQueue;
+ } else {
+ fib->header.XferState |= cpu_to_le32(NormalPriority);
+ qid = AdapNormCmdQueue;
+ }
+ q = &dev->queues->queue[qid];
+
+ if(wait)
+ spin_lock_irqsave(&fibptr->event_lock, flags);
+ if(aac_queue_get( dev, &index, qid, fib, 1, fibptr, &nointr)<0)
+ return -EWOULDBLOCK;
+ dprintk((KERN_DEBUG "fib_send: inserting a queue entry at index %d.\n",index));
+ dprintk((KERN_DEBUG "Fib contents:.\n"));
+ dprintk((KERN_DEBUG " Command = %d.\n", fib->header.Command));
+ dprintk((KERN_DEBUG " XferState = %x.\n", fib->header.XferState));
+ /*
+ * Fill in the Callback and CallbackContext if we are not
+ * going to wait.
+ */
+ if (!wait) {
+ fibptr->callback = callback;
+ fibptr->callback_data = callback_data;
+ }
+ FIB_COUNTER_INCREMENT(aac_config.FibsSent);
+ list_add_tail(&fibptr->queue, &q->pendingq);
+ q->numpending++;
+
+ fibptr->done = 0;
+
+ if(aac_insert_entry(dev, index, qid, (nointr & aac_config.irq_mod)) < 0)
+ return -EWOULDBLOCK;
+ /*
+ * If the caller wanted us to wait for response wait now.
+ */
+
+ if (wait) {
+ spin_unlock_irqrestore(&fibptr->event_lock, flags);
+ down(&fibptr->event_wait);
+ if(fibptr->done == 0)
+ BUG();
+
+ if((fibptr->flags & FIB_CONTEXT_FLAG_TIMED_OUT))
+ return -ETIMEDOUT;
+ else
+ return 0;
+ }
+ /*
+ * If the user does not want a response than return success otherwise
+ * return pending
+ */
+ if (reply)
+ return -EINPROGRESS;
+ else
+ return 0;
+}
+
+/**
+ * aac_consumer_get - get the top of the queue
+ * @dev: Adapter
+ * @q: Queue
+ * @entry: Return entry
+ *
+ * Will return a pointer to the entry on the top of the queue requested that
+ * we are a consumer of, and return the address of the queue entry. It does
+ * not change the state of the queue.
+ */
+
+int aac_consumer_get(struct aac_dev * dev, struct aac_queue * q, struct aac_entry **entry)
+{
+ u32 index;
+ int status;
+
+ if (*q->headers.producer == *q->headers.consumer) {
+ status = 0;
+ } else {
+ /*
+ * The consumer index must be wrapped if we have reached
+ * the end of the queue, else we just use the entry
+ * pointed to by the header index
+ */
+ if (le32_to_cpu(*q->headers.consumer) >= q->entries)
+ index = 0;
+ else
+ index = le32_to_cpu(*q->headers.consumer);
+ *entry = q->base + index;
+ status = 1;
+ }
+ return(status);
+}
+
+int aac_consumer_avail(struct aac_dev *dev, struct aac_queue * q)
+{
+ return (*q->headers.producer != *q->headers.consumer);
+}
+
+
+/**
+ * aac_consumer_free - free consumer entry
+ * @dev: Adapter
+ * @q: Queue
+ * @qid: Queue ident
+ *
+ * Frees up the current top of the queue we are a consumer of. If the
+ * queue was full notify the producer that the queue is no longer full.
+ */
+
+void aac_consumer_free(struct aac_dev * dev, struct aac_queue *q, u32 qid)
+{
+ int wasfull = 0;
+ u32 notify;
+
+ if (*q->headers.producer+1 == *q->headers.consumer)
+ wasfull = 1;
+
+ if (le32_to_cpu(*q->headers.consumer) >= q->entries)
+ *q->headers.consumer = cpu_to_le32(1);
+ else
+ *q->headers.consumer = cpu_to_le32(le32_to_cpu(*q->headers.consumer)+1);
+
+ if (wasfull) {
+ switch (qid) {
+
+ case HostNormCmdQueue:
+ notify = HostNormCmdNotFull;
+ break;
+ case HostHighCmdQueue:
+ notify = HostHighCmdNotFull;
+ break;
+ case HostNormRespQueue:
+ notify = HostNormRespNotFull;
+ break;
+ case HostHighRespQueue:
+ notify = HostHighRespNotFull;
+ break;
+ default:
+ BUG();
+ return;
+ }
+ aac_adapter_notify(dev, notify);
+ }
+}
+
+/**
+ * fib_adapter_complete - complete adapter issued fib
+ * @fibptr: fib to complete
+ * @size: size of fib
+ *
+ * Will do all necessary work to complete a FIB that was sent from
+ * the adapter.
+ */
+
+int fib_adapter_complete(struct fib * fibptr, unsigned short size)
+{
+ struct hw_fib * fib = fibptr->fib;
+ struct aac_dev * dev = fibptr->dev;
+ unsigned long nointr = 0;
+
+ if (le32_to_cpu(fib->header.XferState) == 0)
+ return 0;
+ /*
+ * If we plan to do anything check the structure type first.
+ */
+ if ( fib->header.StructType != FIB_MAGIC ) {
+ return -EINVAL;
+ }
+ /*
+ * This block handles the case where the adapter had sent us a
+ * command and we have finished processing the command. We
+ * call completeFib when we are done processing the command
+ * and want to send a response back to the adapter. This will
+ * send the completed cdb to the adapter.
+ */
+ if (fib->header.XferState & cpu_to_le32(SentFromAdapter)) {
+ fib->header.XferState |= cpu_to_le32(HostProcessed);
+ if (fib->header.XferState & cpu_to_le32(HighPriority)) {
+ u32 index;
+ if (size)
+ {
+ size += sizeof(struct aac_fibhdr);
+ if (size > le16_to_cpu(fib->header.SenderSize))
+ return -EMSGSIZE;
+ fib->header.Size = cpu_to_le16(size);
+ }
+ if(aac_queue_get(dev, &index, AdapHighRespQueue, fib, 1, NULL, &nointr) < 0) {
+ return -EWOULDBLOCK;
+ }
+ if (aac_insert_entry(dev, index, AdapHighRespQueue, (nointr & (int)aac_config.irq_mod)) != 0) {
+ }
+ }
+ else if (fib->header.XferState & NormalPriority)
+ {
+ u32 index;
+
+ if (size) {
+ size += sizeof(struct aac_fibhdr);
+ if (size > le16_to_cpu(fib->header.SenderSize))
+ return -EMSGSIZE;
+ fib->header.Size = cpu_to_le16(size);
+ }
+ if (aac_queue_get(dev, &index, AdapNormRespQueue, fib, 1, NULL, &nointr) < 0)
+ return -EWOULDBLOCK;
+ if (aac_insert_entry(dev, index, AdapNormRespQueue,
+ (nointr & (int)aac_config.irq_mod)) != 0)
+ {
+ }
+ }
+ }
+ else
+ {
+ printk(KERN_WARNING "fib_adapter_complete: Unknown xferstate detected.\n");
+ BUG();
+ }
+ return 0;
+}
+
+/**
+ * fib_complete - fib completion handler
+ * @fib: FIB to complete
+ *
+ * Will do all necessary work to complete a FIB.
+ */
+
+int fib_complete(struct fib * fibptr)
+{
+ struct hw_fib * fib = fibptr->fib;
+
+ /*
+ * Check for a fib which has already been completed
+ */
+
+ if (fib->header.XferState == cpu_to_le32(0))
+ return 0;
+ /*
+ * If we plan to do anything check the structure type first.
+ */
+
+ if (fib->header.StructType != FIB_MAGIC)
+ return -EINVAL;
+ /*
+ * This block completes a cdb which orginated on the host and we
+ * just need to deallocate the cdb or reinit it. At this point the
+ * command is complete that we had sent to the adapter and this
+ * cdb could be reused.
+ */
+ if((fib->header.XferState & cpu_to_le32(SentFromHost)) &&
+ (fib->header.XferState & cpu_to_le32(AdapterProcessed)))
+ {
+ fib_dealloc(fibptr);
+ }
+ else if(fib->header.XferState & cpu_to_le32(SentFromHost))
+ {
+ /*
+ * This handles the case when the host has aborted the I/O
+ * to the adapter because the adapter is not responding
+ */
+ fib_dealloc(fibptr);
+ } else if(fib->header.XferState & cpu_to_le32(HostOwned)) {
+ fib_dealloc(fibptr);
+ } else {
+ BUG();
+ }
+ return 0;
+}
+
+/**
+ * aac_printf - handle printf from firmware
+ * @dev: Adapter
+ * @val: Message info
+ *
+ * Print a message passed to us by the controller firmware on the
+ * Adaptec board
+ */
+
+void aac_printf(struct aac_dev *dev, u32 val)
+{
+ int length = val & 0xffff;
+ int level = (val >> 16) & 0xffff;
+ char *cp = dev->printfbuf;
+
+ /*
+ * The size of the printfbuf is set in port.c
+ * There is no variable or define for it
+ */
+ if (length > 255)
+ length = 255;
+ if (cp[length] != 0)
+ cp[length] = 0;
+ if (level == LOG_HIGH_ERROR)
+ printk(KERN_WARNING "aacraid:%s", cp);
+ else
+ printk(KERN_INFO "aacraid:%s", cp);
+ memset(cp, 0, 256);
+}
+
+
+/**
+ * aac_handle_aif - Handle a message from the firmware
+ * @dev: Which adapter this fib is from
+ * @fibptr: Pointer to fibptr from adapter
+ *
+ * This routine handles a driver notify fib from the adapter and
+ * dispatches it to the appropriate routine for handling.
+ */
+
+static void aac_handle_aif(struct aac_dev * dev, struct fib * fibptr)
+{
+ struct hw_fib * fib = fibptr->fib;
+ /*
+ * Set the status of this FIB to be Invalid parameter.
+ *
+ * *(u32 *)fib->data = ST_INVAL;
+ */
+ *(u32 *)fib->data = cpu_to_le32(ST_OK);
+ fib_adapter_complete(fibptr, sizeof(u32));
+}
+
+/**
+ * aac_command_thread - command processing thread
+ * @dev: Adapter to monitor
+ *
+ * Waits on the commandready event in it's queue. When the event gets set
+ * it will pull FIBs off it's queue. It will continue to pull FIBs off
+ * until the queue is empty. When the queue is empty it will wait for
+ * more FIBs.
+ */
+
+int aac_command_thread(struct aac_dev * dev)
+{
+ struct hw_fib *fib, *newfib;
+ struct fib fibptr; /* for error logging */
+ struct aac_queue_block *queues = dev->queues;
+ struct aac_fib_context *fibctx;
+ unsigned long flags;
+ DECLARE_WAITQUEUE(wait, current);
+
+ /*
+ * We can only have one thread per adapter for AIF's.
+ */
+ if (dev->aif_thread)
+ return -EINVAL;
+ /*
+ * Set up the name that will appear in 'ps'
+ * stored in task_struct.comm[16].
+ */
+ sprintf(current->comm, "aacraid");
+ daemonize();
+ /*
+ * Let the DPC know it has a place to send the AIF's to.
+ */
+ dev->aif_thread = 1;
+ memset(&fibptr, 0, sizeof(struct fib));
+ add_wait_queue(&queues->queue[HostNormCmdQueue].cmdready, &wait);
+ set_current_state(TASK_INTERRUPTIBLE);
+ while(1)
+ {
+ spin_lock_irqsave(queues->queue[HostNormCmdQueue].lock, flags);
+ while(!list_empty(&(queues->queue[HostNormCmdQueue].cmdq))) {
+ struct list_head *entry;
+ struct aac_aifcmd * aifcmd;
+
+ set_current_state(TASK_RUNNING);
+
+ entry = queues->queue[HostNormCmdQueue].cmdq.next;
+ list_del(entry);
+
+ spin_unlock_irqrestore(queues->queue[HostNormCmdQueue].lock, flags);
+ fib = list_entry(entry, struct hw_fib, header.FibLinks);
+ /*
+ * We will process the FIB here or pass it to a
+ * worker thread that is TBD. We Really can't
+ * do anything at this point since we don't have
+ * anything defined for this thread to do.
+ */
+ memset(&fibptr, 0, sizeof(struct fib));
+ fibptr.type = FSAFS_NTC_FIB_CONTEXT;
+ fibptr.size = sizeof( struct fib );
+ fibptr.fib = fib;
+ fibptr.data = fib->data;
+ fibptr.dev = dev;
+ /*
+ * We only handle AifRequest fibs from the adapter.
+ */
+ aifcmd = (struct aac_aifcmd *) fib->data;
+ if (aifcmd->command == le16_to_cpu(AifCmdDriverNotify)) {
+ aac_handle_aif(dev, &fibptr);
+ } else {
+ /* The u32 here is important and intended. We are using
+ 32bit wrapping time to fit the adapter field */
+
+ u32 time_now, time_last;
+ unsigned long flagv;
+
+ time_now = jiffies/HZ;
+
+ spin_lock_irqsave(&dev->fib_lock, flagv);
+ entry = dev->fib_list.next;
+ /*
+ * For each Context that is on the
+ * fibctxList, make a copy of the
+ * fib, and then set the event to wake up the
+ * thread that is waiting for it.
+ */
+ while (entry != &dev->fib_list) {
+ /*
+ * Extract the fibctx
+ */
+ fibctx = list_entry(entry, struct aac_fib_context, next);
+ /*
+ * Check if the queue is getting
+ * backlogged
+ */
+ if (fibctx->count > 20)
+ {
+ time_last = fibctx->jiffies;
+ /*
+ * Has it been > 2 minutes
+ * since the last read off
+ * the queue?
+ */
+ if ((time_now - time_last) > 120) {
+ entry = entry->next;
+ aac_close_fib_context(dev, fibctx);
+ continue;
+ }
+ }
+ /*
+ * Warning: no sleep allowed while
+ * holding spinlock
+ */
+ newfib = kmalloc(sizeof(struct hw_fib), GFP_ATOMIC);
+ if (newfib) {
+ /*
+ * Make the copy of the FIB
+ */
+ memcpy(newfib, fib, sizeof(struct hw_fib));
+ /*
+ * Put the FIB onto the
+ * fibctx's fibs
+ */
+ list_add_tail(&newfib->header.FibLinks, &fibctx->fibs);
+ fibctx->count++;
+ /*
+ * Set the event to wake up the
+ * thread that will waiting.
+ */
+ up(&fibctx->wait_sem);
+ } else {
+ printk(KERN_WARNING "aifd: didn't allocate NewFib.\n");
+ }
+ entry = entry->next;
+ }
+ /*
+ * Set the status of this FIB
+ */
+ *(u32 *)fib->data = cpu_to_le32(ST_OK);
+ fib_adapter_complete(&fibptr, sizeof(u32));
+ spin_unlock_irqrestore(&dev->fib_lock, flagv);
+ }
+ spin_lock_irqsave(queues->queue[HostNormCmdQueue].lock, flags);
+ }
+ /*
+ * There are no more AIF's
+ */
+ spin_unlock_irqrestore(queues->queue[HostNormCmdQueue].lock, flags);
+ schedule();
+
+ if(signal_pending(current))
+ break;
+ set_current_state(TASK_INTERRUPTIBLE);
+ }
+ remove_wait_queue(&queues->queue[HostNormCmdQueue].cmdready, &wait);
+ dev->aif_thread = 0;
+ complete_and_exit(&dev->aif_completion, 0);
+}
diff --git a/drivers/scsi/aacraid/dpcsup.c b/drivers/scsi/aacraid/dpcsup.c
new file mode 100644
index 000000000000..64a14f1a4545
--- /dev/null
+++ b/drivers/scsi/aacraid/dpcsup.c
@@ -0,0 +1,201 @@
+/*
+ * Adaptec AAC series RAID controller driver
+ * (c) Copyright 2001 Red Hat Inc. <alan@redhat.com>
+ *
+ * based on the old aacraid driver that is..
+ * Adaptec aacraid device driver for Linux.
+ *
+ * Copyright (c) 2000 Adaptec, Inc. (aacraid@adaptec.com)
+ *
+ * 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
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; see the file COPYING. If not, write to
+ * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * Module Name:
+ * dpcsup.c
+ *
+ * Abstract: All DPC processing routines for the cyclone board occur here.
+ *
+ *
+ */
+
+#include <linux/config.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/types.h>
+#include <linux/sched.h>
+#include <linux/pci.h>
+#include <linux/spinlock.h>
+#include <linux/slab.h>
+#include <linux/completion.h>
+#include <linux/blk.h>
+#include <asm/semaphore.h>
+#include "scsi.h"
+#include "hosts.h"
+
+#include "aacraid.h"
+
+/**
+ * aac_response_normal - Handle command replies
+ * @q: Queue to read from
+ *
+ * This DPC routine will be run when the adapter interrupts us to let us
+ * know there is a response on our normal priority queue. We will pull off
+ * all QE there are and wake up all the waiters before exiting. We will
+ * take a spinlock out on the queue before operating on it.
+ */
+
+unsigned int aac_response_normal(struct aac_queue * q)
+{
+ struct aac_dev * dev = q->dev;
+ struct aac_entry *entry;
+ struct hw_fib * hwfib;
+ struct fib * fib;
+ int consumed = 0;
+ unsigned long flags;
+
+ spin_lock_irqsave(q->lock, flags);
+
+ /*
+ * Keep pulling response QEs off the response queue and waking
+ * up the waiters until there are no more QEs. We then return
+ * back to the system. If no response was requesed we just
+ * deallocate the Fib here and continue.
+ */
+ while(aac_consumer_get(dev, q, &entry))
+ {
+ int fast;
+
+ fast = (int) (entry->addr & 0x01);
+ hwfib = addr2fib(entry->addr & ~0x01);
+ aac_consumer_free(dev, q, HostNormRespQueue);
+ fib = &dev->fibs[hwfib->header.SenderData];
+ /*
+ * Remove this fib from the Outstanding I/O queue.
+ * But only if it has not already been timed out.
+ *
+ * If the fib has been timed out already, then just
+ * continue. The caller has already been notified that
+ * the fib timed out.
+ */
+ if (!(fib->flags & FIB_CONTEXT_FLAG_TIMED_OUT)) {
+ list_del(&fib->queue);
+ dev->queues->queue[AdapNormCmdQueue].numpending--;
+ } else {
+ printk(KERN_WARNING "aacraid: FIB timeout (%x).\n", fib->flags);
+ continue;
+ }
+ spin_unlock_irqrestore(q->lock, flags);
+
+ if (fast) {
+ /*
+ * Doctor the fib
+ */
+ *(u32 *)hwfib->data = cpu_to_le32(ST_OK);
+ hwfib->header.XferState |= cpu_to_le32(AdapterProcessed);
+ }
+
+ FIB_COUNTER_INCREMENT(aac_config.FibRecved);
+
+ if (hwfib->header.Command == cpu_to_le16(NuFileSystem))
+ {
+ u32 *pstatus = (u32 *)hwfib->data;
+ if (*pstatus & cpu_to_le32(0xffff0000))
+ *pstatus = cpu_to_le32(ST_OK);
+ }
+ if (hwfib->header.XferState & cpu_to_le32(NoResponseExpected | Async))
+ {
+ if (hwfib->header.XferState & cpu_to_le32(NoResponseExpected))
+ FIB_COUNTER_INCREMENT(aac_config.NoResponseRecved);
+ else
+ FIB_COUNTER_INCREMENT(aac_config.AsyncRecved);
+ /*
+ * NOTE: we cannot touch the fib after this
+ * call, because it may have been deallocated.
+ */
+ fib->callback(fib->callback_data, fib);
+ } else {
+ unsigned long flagv;
+ spin_lock_irqsave(&fib->event_lock, flagv);
+ fib->done = 1;
+ up(&fib->event_wait);
+ spin_unlock_irqrestore(&fib->event_lock, flagv);
+ FIB_COUNTER_INCREMENT(aac_config.NormalRecved);
+ }
+ consumed++;
+ spin_lock_irqsave(q->lock, flags);
+ }
+
+ if (consumed > aac_config.peak_fibs)
+ aac_config.peak_fibs = consumed;
+ if (consumed == 0)
+ aac_config.zero_fibs++;
+
+ spin_unlock_irqrestore(q->lock, flags);
+ return 0;
+}
+
+
+/**
+ * aac_command_normal - handle commands
+ * @q: queue to process
+ *
+ * This DPC routine will be queued when the adapter interrupts us to
+ * let us know there is a command on our normal priority queue. We will
+ * pull off all QE there are and wake up all the waiters before exiting.
+ * We will take a spinlock out on the queue before operating on it.
+ */
+
+unsigned int aac_command_normal(struct aac_queue *q)
+{
+ struct aac_dev * dev = q->dev;
+ struct aac_entry *entry;
+ unsigned long flags;
+
+ spin_lock_irqsave(q->lock, flags);
+
+ /*
+ * Keep pulling response QEs off the response queue and waking
+ * up the waiters until there are no more QEs. We then return
+ * back to the system.
+ */
+ while(aac_consumer_get(dev, q, &entry))
+ {
+ struct hw_fib * fib;
+ fib = addr2fib(entry->addr);
+
+ if (dev->aif_thread) {
+ list_add_tail(&fib->header.FibLinks, &q->cmdq);
+ aac_consumer_free(dev, q, HostNormCmdQueue);
+ wake_up_interruptible(&q->cmdready);
+ } else {
+ struct fib fibctx;
+ aac_consumer_free(dev, q, HostNormCmdQueue);
+ spin_unlock_irqrestore(q->lock, flags);
+ memset(&fibctx, 0, sizeof(struct fib));
+ fibctx.type = FSAFS_NTC_FIB_CONTEXT;
+ fibctx.size = sizeof(struct fib);
+ fibctx.fib = fib;
+ fibctx.data = fib->data;
+ fibctx.dev = dev;
+ /*
+ * Set the status of this FIB
+ */
+ *(u32 *)fib->data = cpu_to_le32(ST_OK);
+ fib_adapter_complete(&fibctx, sizeof(u32));
+ spin_lock_irqsave(q->lock, flags);
+ }
+ }
+ spin_unlock_irqrestore(q->lock, flags);
+ return 0;
+}
diff --git a/drivers/scsi/aacraid/linit.c b/drivers/scsi/aacraid/linit.c
new file mode 100644
index 000000000000..797025c9bfc6
--- /dev/null
+++ b/drivers/scsi/aacraid/linit.c
@@ -0,0 +1,743 @@
+/*
+ * Adaptec AAC series RAID controller driver
+ * (c) Copyright 2001 Red Hat Inc. <alan@redhat.com>
+ *
+ * based on the old aacraid driver that is..
+ * Adaptec aacraid device driver for Linux.
+ *
+ * Copyright (c) 2000 Adaptec, Inc. (aacraid@adaptec.com)
+ *
+ * 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
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; see the file COPYING. If not, write to
+ * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * Module Name:
+ * linit.c
+ *
+ * Abstract: Linux Driver entry module for Adaptec RAID Array Controller
+ *
+ * Provides the following driver entry points:
+ * aac_detect()
+ * aac_release()
+ * aac_queuecommand()
+ * aac_resetcommand()
+ * aac_biosparm()
+ *
+ */
+
+#define AAC_DRIVER_VERSION "0.9.9ac6-TEST"
+#define AAC_DRIVER_BUILD_DATE __DATE__
+
+#include <linux/module.h>
+#include <linux/config.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/types.h>
+#include <linux/sched.h>
+#include <linux/pci.h>
+#include <linux/spinlock.h>
+#include <linux/slab.h>
+#include <linux/completion.h>
+#include <asm/semaphore.h>
+#include <linux/blk.h>
+#include "scsi.h"
+#include "hosts.h"
+
+#include <scsi/scsicam.h>
+
+#include "aacraid.h"
+#include "sd.h"
+
+#define AAC_DRIVERNAME "aacraid"
+
+MODULE_AUTHOR("Red Hat Inc and Adaptec");
+MODULE_DESCRIPTION("Supports Dell PERC2, 2/Si, 3/Si, 3/Di, Adaptec 2120S, 2200S, 5400S, and HP NetRAID-4M devices. http://domsch.com/linux/ or http://linux.adaptec.com");
+MODULE_LICENSE("GPL");
+MODULE_PARM(nondasd, "i");
+MODULE_PARM_DESC(nondasd, "Control scanning of hba for nondasd devices. 0=off, 1=on");
+
+static int nondasd=-1;
+
+struct aac_dev *aac_devices[MAXIMUM_NUM_ADAPTERS];
+
+static unsigned aac_count = 0;
+static int aac_cfg_major = -1;
+
+/*
+ * Because of the way Linux names scsi devices, the order in this table has
+ * become important. Check for on-board Raid first, add-in cards second.
+ *
+ * dmb - For now we add the number of channels to this structure.
+ * In the future we should add a fib that reports the number of channels
+ * for the card. At that time we can remove the channels from here
+ */
+
+static struct aac_driver_ident aac_drivers[] = {
+ { 0x1028, 0x0001, 0x1028, 0x0001, aac_rx_init, "percraid", "DELL ", "PERCRAID ", 2 }, /* PERC 2/Si */
+ { 0x1028, 0x0002, 0x1028, 0x0002, aac_rx_init, "percraid", "DELL ", "PERCRAID ", 2 }, /* PERC 3/Di */
+ { 0x1028, 0x0003, 0x1028, 0x0003, aac_rx_init, "percraid", "DELL ", "PERCRAID ", 2 }, /* PERC 3/Si */
+ { 0x1028, 0x0004, 0x1028, 0x00d0, aac_rx_init, "percraid", "DELL ", "PERCRAID ", 2 }, /* PERC 3/Si */
+ { 0x1028, 0x0002, 0x1028, 0x00d1, aac_rx_init, "percraid", "DELL ", "PERCRAID ", 2 }, /* PERC 3/Di */
+ { 0x1028, 0x0002, 0x1028, 0x00d9, aac_rx_init, "percraid", "DELL ", "PERCRAID ", 2 }, /* PERC 3/Di */
+ { 0x1028, 0x000a, 0x1028, 0x0106, aac_rx_init, "percraid", "DELL ", "PERCRAID ", 2 }, /* PERC 3/Di */
+ { 0x1028, 0x000a, 0x1028, 0x011b, aac_rx_init, "percraid", "DELL ", "PERCRAID ", 2 }, /* PERC 3/Di */
+ { 0x1028, 0x000a, 0x1028, 0x0121, aac_rx_init, "percraid", "DELL ", "PERCRAID ", 2 }, /* PERC 3/Di */
+ { 0x9005, 0x0283, 0x9005, 0x0283, aac_rx_init, "aacraid", "ADAPTEC ", "catapult ", 2 }, /* catapult*/
+ { 0x9005, 0x0284, 0x9005, 0x0284, aac_rx_init, "aacraid", "ADAPTEC ", "tomcat ", 2 }, /* tomcat*/
+ { 0x9005, 0x0285, 0x9005, 0x0286, aac_rx_init, "aacraid", "ADAPTEC ", "Adaptec 2120S ", 1 }, /* Adaptec 2120S (Crusader)*/
+ { 0x9005, 0x0285, 0x9005, 0x0285, aac_rx_init, "aacraid", "ADAPTEC ", "Adaptec 2200S ", 2 }, /* Adaptec 2200S (Vulcan)*/
+ { 0x9005, 0x0285, 0x9005, 0x0287, aac_rx_init, "aacraid", "ADAPTEC ", "Adaptec 2200S ", 2 }, /* Adaptec 2200S (Vulcan-2m)*/
+ { 0x1011, 0x0046, 0x9005, 0x0365, aac_sa_init, "aacraid", "ADAPTEC ", "Adaptec 5400S ", 4 }, /* Adaptec 5400S (Mustang)*/
+ { 0x1011, 0x0046, 0x9005, 0x0364, aac_sa_init, "aacraid", "ADAPTEC ", "AAC-364 ", 4 }, /* Adaptec 5400S (Mustang)*/
+ { 0x1011, 0x0046, 0x9005, 0x1364, aac_sa_init, "percraid", "DELL ", "PERCRAID ", 4 }, /* Dell PERC2 "Quad Channel" */
+ { 0x1011, 0x0046, 0x103c, 0x10c2, aac_sa_init, "hpnraid", "HP ", "NetRAID-4M ", 4 } /* HP NetRAID-4M */
+};
+
+#define NUM_AACTYPES (sizeof(aac_drivers) / sizeof(struct aac_driver_ident))
+static int num_aacdrivers = NUM_AACTYPES;
+
+static int aac_cfg_ioctl(struct inode * inode, struct file * file, unsigned int cmd, unsigned long arg);
+static int aac_cfg_open(struct inode * inode, struct file * file);
+static int aac_cfg_release(struct inode * inode,struct file * file);
+
+static struct file_operations aac_cfg_fops = {
+ owner: THIS_MODULE,
+ ioctl: aac_cfg_ioctl,
+ open: aac_cfg_open,
+ release: aac_cfg_release
+};
+
+static int aac_detect(Scsi_Host_Template *);
+static int aac_release(struct Scsi_Host *);
+static int aac_queuecommand(Scsi_Cmnd *, void (*CompletionRoutine)(Scsi_Cmnd *));
+static int aac_biosparm(Scsi_Disk *, struct block_device *, int *);
+static int aac_procinfo(char *, char **, off_t, int, int, int);
+static int aac_ioctl(Scsi_Device *, int, void *);
+static int aac_eh_abort(Scsi_Cmnd * cmd);
+static int aac_eh_device_reset(Scsi_Cmnd* cmd);
+static int aac_eh_bus_reset(Scsi_Cmnd* cmd);
+static int aac_eh_reset(Scsi_Cmnd* cmd);
+
+static void aac_queuedepth(struct Scsi_Host *, Scsi_Device *);
+
+/**
+ * aac_detect - Probe for aacraid cards
+ * @template: SCSI driver template
+ *
+ * Probe for AAC Host Adapters initialize, register, and report the
+ * configuration of each AAC Host Adapter found.
+ * Returns the number of adapters successfully initialized and
+ * registered.
+ * Initializes all data necessary for this particular SCSI driver.
+ * Notes:
+ * The detect routine must not call any of the mid level functions
+ * to queue commands because things are not guaranteed to be set
+ * up yet. The detect routine can send commands to the host adapter
+ * as long as the program control will not be passed to scsi.c in
+ * the processing of the command. Note especially that
+ * scsi_malloc/scsi_free must not be called.
+ *
+ */
+
+static int aac_detect(Scsi_Host_Template *template)
+{
+ int index;
+ int container;
+ u16 vendor_id, device_id;
+ struct Scsi_Host *host_ptr;
+ struct pci_dev *dev = NULL;
+ struct aac_dev *aac;
+ struct fsa_scsi_hba *fsa_dev_ptr;
+ char *name = NULL;
+
+ printk(KERN_INFO "Red Hat/Adaptec aacraid driver, %s\n", AAC_DRIVER_BUILD_DATE);
+
+ /* setting up the proc directory structure */
+ template->proc_name = "aacraid";
+
+ for( index = 0; index != num_aacdrivers; index++ )
+ {
+ device_id = aac_drivers[index].device;
+ vendor_id = aac_drivers[index].vendor;
+ name = aac_drivers[index].name;
+ dprintk((KERN_DEBUG "Checking %s %x/%x/%x/%x.\n",
+ name, vendor_id, device_id,
+ aac_drivers[index].subsystem_vendor,
+ aac_drivers[index].subsystem_device));
+
+ dev = NULL;
+ while((dev = pci_find_device(vendor_id, device_id, dev))) {
+ if (pci_enable_device(dev))
+ continue;
+ pci_set_master(dev);
+ pci_set_dma_mask(dev, 0xFFFFFFFFULL);
+
+ if((dev->subsystem_vendor != aac_drivers[index].subsystem_vendor) ||
+ (dev->subsystem_device != aac_drivers[index].subsystem_device))
+ continue;
+
+ dprintk((KERN_DEBUG "%s device detected.\n", name));
+ dprintk((KERN_DEBUG "%x/%x/%x/%x.\n", vendor_id, device_id,
+ aac_drivers[index].subsystem_vendor, aac_drivers[index].subsystem_device));
+ /* Increment the host adapter count */
+ aac_count++;
+ /*
+ * scsi_register() allocates memory for a Scsi_Hosts structure and
+ * links it into the linked list of host adapters. This linked list
+ * contains the data for all possible <supported> scsi hosts.
+ * This is similar to the Scsi_Host_Template, except that we have
+ * one entry for each actual physical host adapter on the system,
+ * stored as a linked list. If there are two AAC boards, then we
+ * will need to make two Scsi_Host entries, but there will be only
+ * one Scsi_Host_Template entry. The second argument to scsi_register()
+ * specifies the size of the extra memory we want to hold any device
+ * specific information.
+ */
+ host_ptr = scsi_register( template, sizeof(struct aac_dev) );
+ /*
+ * These three parameters can be used to allow for wide SCSI
+ * and for host adapters that support multiple buses.
+ */
+ host_ptr->max_id = 17;
+ host_ptr->max_lun = 8;
+ host_ptr->max_channel = 1;
+ host_ptr->irq = dev->irq; /* Adapter IRQ number */
+ /* host_ptr->base = ( char * )(dev->resource[0].start & ~0xff); */
+ host_ptr->base = dev->resource[0].start;
+ scsi_set_pci_device(host_ptr, dev);
+ dprintk((KERN_DEBUG "Device base address = 0x%lx [0x%lx].\n", host_ptr->base, dev->resource[0].start));
+ dprintk((KERN_DEBUG "Device irq = 0x%x.\n", dev->irq));
+ /*
+ * The unique_id field is a unique identifier that must
+ * be assigned so that we have some way of identifying
+ * each host adapter properly and uniquely. For hosts
+ * that do not support more than one card in the
+ * system, this does not need to be set. It is
+ * initialized to zero in scsi_register(). This is the
+ * value returned as aac->id.
+ */
+ host_ptr->unique_id = aac_count - 1;
+ /*
+ * This function is called after the device list has
+ * been built to find the tagged queueing depth
+ * supported for each device.
+ */
+ host_ptr->select_queue_depths = aac_queuedepth;
+ aac = (struct aac_dev *)host_ptr->hostdata;
+ /* attach a pointer back to Scsi_Host */
+ aac->scsi_host_ptr = host_ptr;
+ aac->pdev = dev;
+ aac->cardtype = index;
+ aac->name = aac->scsi_host_ptr->hostt->name;
+ aac->id = aac->scsi_host_ptr->unique_id;
+ /* Initialize the ordinal number of the device to -1 */
+ fsa_dev_ptr = &(aac->fsa_dev);
+ for( container = 0; container < MAXIMUM_NUM_CONTAINERS; container++ )
+ fsa_dev_ptr->devno[container] = -1;
+
+ dprintk((KERN_DEBUG "Initializing Hardware...\n"));
+ if((*aac_drivers[index].init)(aac , host_ptr->unique_id) != 0)
+ {
+ /* device initialization failed */
+ printk(KERN_WARNING "aacraid: device initialization failed.\n");
+ scsi_unregister(host_ptr);
+ aac_count--;
+ continue;
+ }
+ dprintk((KERN_DEBUG "%s:%d device initialization successful.\n", name, host_ptr->unique_id));
+ aac_get_adapter_info(aac);
+ if(nondasd != -1)
+ {
+ /* someone told us how to set this on the cmdline */
+ aac->nondasd_support = (nondasd!=0);
+ }
+ if(aac->nondasd_support != 0){
+ printk(KERN_INFO "%s%d: Non-DASD support enabled\n", aac->name, aac->id);
+ }
+ dprintk((KERN_DEBUG "%s:%d options flag %04x.\n",name, host_ptr->unique_id,aac->adapter_info.options));
+ if(aac->nondasd_support == 1)
+ {
+ /*
+ * max channel will be the physical channels plus 1 virtual channel
+ * all containers are on the virtual channel 0
+ * physical channels are address by their actual physical number+1
+ */
+ host_ptr->max_channel = aac_drivers[index].channels+1;
+ } else {
+ host_ptr->max_channel = 1;
+ }
+ dprintk((KERN_DEBUG "Device has %d logical channels\n", host_ptr->max_channel));
+ aac_get_containers(aac);
+ aac_devices[aac_count-1] = aac;
+
+ /*
+ * dmb - we may need to move these 3 parms somewhere else once
+ * we get a fib that can report the actual numbers
+ */
+ host_ptr->max_id = AAC_MAX_TARGET;
+ host_ptr->max_lun = AAC_MAX_LUN;
+
+ /*
+ * If we are PAE capable then our future DMA mappings
+ * (for read/write commands) are 64bit clean and don't
+ * need bouncing. This assumes we do no other 32bit only
+ * allocations (eg fib table expands) after this point.
+ */
+
+ if(aac->pae_support)
+ pci_set_dma_mask(dev, 0xFFFFFFFFFFFFFFFFUL);
+ }
+ }
+
+ if( aac_count ){
+ if((aac_cfg_major = register_chrdev( 0, "aac", &aac_cfg_fops))<0)
+ printk(KERN_WARNING "aacraid: unable to register \"aac\" device.\n");
+ }
+
+ template->present = aac_count; /* # of cards of this type found */
+ return aac_count;
+}
+
+/**
+ * aac_release - release SCSI host resources
+ * @host_ptr: SCSI host to clean up
+ *
+ * Release all resources previously acquired to support a specific Host
+ * Adapter and unregister the AAC Host Adapter.
+ *
+ * BUGS: Does not wait for the thread it kills to die.
+ */
+
+static int aac_release(struct Scsi_Host *host_ptr)
+{
+ struct aac_dev *dev;
+ dprintk((KERN_DEBUG "aac_release.\n"));
+ dev = (struct aac_dev *)host_ptr->hostdata;
+ /*
+ * kill any threads we started
+ */
+ kill_proc(dev->thread_pid, SIGKILL, 0);
+ wait_for_completion(&dev->aif_completion);
+ /*
+ * Call the comm layer to detach from this adapter
+ */
+ aac_detach(dev);
+ /* Check free orderings... */
+ /* remove interrupt binding */
+ free_irq(host_ptr->irq, dev);
+ iounmap((void * )dev->regs.sa);
+ /* unregister adapter */
+ scsi_unregister(host_ptr);
+ /*
+ * FIXME: This assumes no hot plugging is going on...
+ */
+ if( aac_cfg_major >= 0 )
+ {
+ unregister_chrdev(aac_cfg_major, "aac");
+ aac_cfg_major = -1;
+ }
+ return 0;
+}
+
+/**
+ * aac_queuecommand - queue a SCSI command
+ * @scsi_cmnd_ptr: SCSI command to queue
+ * @CompletionRoutine: Function to call on command completion
+ *
+ * Queues a command for execution by the associated Host Adapter.
+ */
+
+static int aac_queuecommand(Scsi_Cmnd *scsi_cmnd_ptr, void (*complete)(Scsi_Cmnd *))
+{
+ int ret;
+
+ scsi_cmnd_ptr->scsi_done = complete;
+ /*
+ * aac_scsi_cmd() handles command processing, setting the
+ * result code and calling completion routine.
+ */
+ if((ret = aac_scsi_cmd(scsi_cmnd_ptr)) != 0)
+ dprintk((KERN_DEBUG "aac_scsi_cmd failed.\n"));
+ return ret;
+}
+
+/**
+ * aac_driverinfo - Returns the host adapter name
+ * @host_ptr: Scsi host to report on
+ *
+ * Returns a static string describing the device in question
+ */
+
+const char *aac_driverinfo(struct Scsi_Host *host_ptr)
+{
+ struct aac_dev *dev = (struct aac_dev *)host_ptr->hostdata;
+ return aac_drivers[dev->cardtype].name;
+}
+
+/**
+ * aac_get_driver_ident
+ * @devtype: index into lookup table
+ *
+ * Returns a pointer to the entry in the driver lookup table.
+ */
+struct aac_driver_ident* aac_get_driver_ident(int devtype)
+{
+ return &aac_drivers[devtype];
+}
+
+/**
+ * aac_biosparm - return BIOS parameters for disk
+ * @disk: SCSI disk object to process
+ * @device: Disk in question
+ * @geom: geometry block to fill in
+ *
+ * Return the Heads/Sectors/Cylinders BIOS Disk Parameters for Disk.
+ * The default disk geometry is 64 heads, 32 sectors, and the appropriate
+ * number of cylinders so as not to exceed drive capacity. In order for
+ * disks equal to or larger than 1 GB to be addressable by the BIOS
+ * without exceeding the BIOS limitation of 1024 cylinders, Extended
+ * Translation should be enabled. With Extended Translation enabled,
+ * drives between 1 GB inclusive and 2 GB exclusive are given a disk
+ * geometry of 128 heads and 32 sectors, and drives above 2 GB inclusive
+ * are given a disk geometry of 255 heads and 63 sectors. However, if
+ * the BIOS detects that the Extended Translation setting does not match
+ * the geometry in the partition table, then the translation inferred
+ * from the partition table will be used by the BIOS, and a warning may
+ * be displayed.
+ */
+
+static int aac_biosparm(Scsi_Disk *disk, struct block_device *bdev, int *geom)
+{
+ struct diskparm *param = (struct diskparm *)geom;
+ u8 *buf;
+
+ dprintk((KERN_DEBUG "aac_biosparm.\n"));
+
+ /*
+ * Assuming extended translation is enabled - #REVISIT#
+ */
+ if( disk->capacity >= 2 * 1024 * 1024 ) /* 1 GB in 512 byte sectors */
+ {
+ if( disk->capacity >= 4 * 1024 * 1024 ) /* 2 GB in 512 byte sectors */
+ {
+ param->heads = 255;
+ param->sectors = 63;
+ }
+ else
+ {
+ param->heads = 128;
+ param->sectors = 32;
+ }
+ }
+ else
+ {
+ param->heads = 64;
+ param->sectors = 32;
+ }
+
+ param->cylinders = disk->capacity/(param->heads * param->sectors);
+
+ /*
+ * Read the partition table block
+ */
+
+ buf = scsi_bios_ptable(bdev);
+
+ /*
+ * If the boot sector partition table is valid, search for a partition
+ * table entry whose end_head matches one of the standard geometry
+ * translations ( 64/32, 128/32, 255/63 ).
+ */
+
+ if(*(unsigned short *)(buf + 0x40) == cpu_to_le16(0xaa55))
+ {
+ struct partition *first = (struct partition *)buf;
+ struct partition *entry = first;
+ int saved_cylinders = param->cylinders;
+ int num;
+ unsigned char end_head, end_sec;
+
+ for(num = 0; num < 4; num++)
+ {
+ end_head = entry->end_head;
+ end_sec = entry->end_sector & 0x3f;
+
+ if(end_head == 63)
+ {
+ param->heads = 64;
+ param->sectors = 32;
+ break;
+ }
+ else if(end_head == 127)
+ {
+ param->heads = 128;
+ param->sectors = 32;
+ break;
+ }
+ else if(end_head == 254)
+ {
+ param->heads = 255;
+ param->sectors = 63;
+ break;
+ }
+ entry++;
+ }
+
+ if(num == 4)
+ {
+ end_head = first->end_head;
+ end_sec = first->end_sector & 0x3f;
+ }
+
+ param->cylinders = disk->capacity / (param->heads * param->sectors);
+
+ if(num < 4 && end_sec == param->sectors)
+ {
+ if(param->cylinders != saved_cylinders)
+ dprintk((KERN_DEBUG "Adopting geometry: heads=%d, sectors=%d from partition table %d.\n",
+ param->heads, param->sectors, num));
+ }
+ else if(end_head > 0 || end_sec > 0)
+ {
+ dprintk((KERN_DEBUG "Strange geometry: heads=%d, sectors=%d in partition table %d.\n",
+ end_head + 1, end_sec, num));
+ dprintk((KERN_DEBUG "Using geometry: heads=%d, sectors=%d.\n",
+ param->heads, param->sectors));
+ }
+ }
+ kfree(buf);
+ return 0;
+}
+
+/**
+ * aac_queuedepth - compute queue depths
+ * @host: SCSI host in question
+ * @dev: SCSI device we are considering
+ *
+ * Selects queue depths for each target device based on the host adapter's
+ * total capacity and the queue depth supported by the target device.
+ * A queue depth of one automatically disables tagged queueing.
+ */
+
+static void aac_queuedepth(struct Scsi_Host * host, Scsi_Device * dev )
+{
+ Scsi_Device * dptr;
+
+ dprintk((KERN_DEBUG "aac_queuedepth.\n"));
+ dprintk((KERN_DEBUG "Device # Q Depth Online\n"));
+ dprintk((KERN_DEBUG "---------------------------\n"));
+ for(dptr = dev; dptr != NULL; dptr = dptr->next)
+ {
+ if(dptr->host == host)
+ {
+ dptr->queue_depth = 10;
+ dprintk((KERN_DEBUG " %2d %d %d\n",
+ dptr->id, dptr->queue_depth, dptr->online));
+ }
+ }
+}
+
+
+/**
+ * aac_eh_abort - Abort command if possible.
+ * @cmd: SCSI command block to abort
+ *
+ * Called when the midlayer wishes to abort a command. We don't support
+ * this facility, and our firmware looks after life for us. We just
+ * report this as failing
+ */
+
+static int aac_eh_abort(Scsi_Cmnd *cmd)
+{
+ return FAILED;
+}
+
+/**
+ * aac_eh_device_reset - Reset command handling
+ * @cmd: SCSI command block causing the reset
+ *
+ * Issue a reset of a SCSI device. We are ourselves not truely a SCSI
+ * controller and our firmware will do the work for us anyway. Thus this
+ * is a no-op. We just return FAILED.
+ */
+
+static int aac_eh_device_reset(Scsi_Cmnd *cmd)
+{
+ return FAILED;
+}
+
+/**
+ * aac_eh_bus_reset - Reset command handling
+ * @scsi_cmd: SCSI command block causing the reset
+ *
+ * Issue a reset of a SCSI bus. We are ourselves not truely a SCSI
+ * controller and our firmware will do the work for us anyway. Thus this
+ * is a no-op. We just return FAILED.
+ */
+
+static int aac_eh_bus_reset(Scsi_Cmnd* cmd)
+{
+ return FAILED;
+}
+
+/**
+ * aac_eh_hba_reset - Reset command handling
+ * @scsi_cmd: SCSI command block causing the reset
+ *
+ * Issue a reset of a SCSI host. If things get this bad then arguably we should
+ * go take a look at what the host adapter is doing and see if something really
+ * broke (as can occur at least on my Dell QC card if a drive keeps failing spinup)
+ */
+
+static int aac_eh_reset(Scsi_Cmnd* cmd)
+{
+ printk(KERN_ERR "aacraid: Host adapter reset request. SCSI hang ?\n");
+ return FAILED;
+}
+
+/**
+ * aac_ioctl - Handle SCSI ioctls
+ * @scsi_dev_ptr: scsi device to operate upon
+ * @cmd: ioctl command to use issue
+ * @arg: ioctl data pointer
+ *
+ * Issue an ioctl on an aacraid device. Returns a standard unix error code or
+ * zero for success
+ */
+
+static int aac_ioctl(Scsi_Device * scsi_dev_ptr, int cmd, void * arg)
+{
+ struct aac_dev *dev;
+ dprintk((KERN_DEBUG "aac_ioctl.\n"));
+ dev = (struct aac_dev *)scsi_dev_ptr->host->hostdata;
+ return aac_do_ioctl(dev, cmd, arg);
+}
+
+/**
+ * aac_cfg_open - open a configuration file
+ * @inode: inode being opened
+ * @file: file handle attached
+ *
+ * Called when the configuration device is opened. Does the needed
+ * set up on the handle and then returns
+ *
+ * Bugs: This needs extending to check a given adapter is present
+ * so we can support hot plugging, and to ref count adapters.
+ */
+
+static int aac_cfg_open(struct inode * inode, struct file * file )
+{
+ unsigned minor_number = minor(inode->i_rdev);
+ if(minor_number >= aac_count)
+ return -ENODEV;
+ return 0;
+}
+
+/**
+ * aac_cfg_release - close down an AAC config device
+ * @inode: inode of configuration file
+ * @file: file handle of configuration file
+ *
+ * Called when the last close of the configuration file handle
+ * is performed.
+ */
+
+static int aac_cfg_release(struct inode * inode, struct file * file )
+{
+ return 0;
+}
+
+/**
+ * aac_cfg_ioctl - AAC configuration request
+ * @inode: inode of device
+ * @file: file handle
+ * @cmd: ioctl command code
+ * @arg: argument
+ *
+ * Handles a configuration ioctl. Currently this involves wrapping it
+ * up and feeding it into the nasty windowsalike glue layer.
+ *
+ * Bugs: Needs locking against parallel ioctls lower down
+ * Bugs: Needs to handle hot plugging
+ */
+
+static int aac_cfg_ioctl(struct inode * inode, struct file * file, unsigned int cmd, unsigned long arg )
+{
+ struct aac_dev *dev = aac_devices[minor(inode->i_rdev)];
+ return aac_do_ioctl(dev, cmd, (void *)arg);
+}
+
+/*
+ * To use the low level SCSI driver support using the linux kernel loadable
+ * module interface we should initialize the global variable driver_interface
+ * (datatype Scsi_Host_Template) and then include the file scsi_module.c.
+ */
+
+static Scsi_Host_Template driver_template = {
+ module: THIS_MODULE,
+ name: "AAC",
+ proc_info: aac_procinfo,
+ detect: aac_detect,
+ release: aac_release,
+ info: aac_driverinfo,
+ ioctl: aac_ioctl,
+ queuecommand: aac_queuecommand,
+ bios_param: aac_biosparm,
+ can_queue: AAC_NUM_IO_FIB,
+ this_id: 16,
+ sg_tablesize: 16,
+ max_sectors: 128,
+ cmd_per_lun: AAC_NUM_IO_FIB,
+ eh_abort_handler: aac_eh_abort,
+ eh_device_reset_handler:aac_eh_device_reset,
+ eh_bus_reset_handler: aac_eh_bus_reset,
+ eh_host_reset_handler: aac_eh_reset,
+
+ use_clustering: ENABLE_CLUSTERING,
+};
+
+#include "scsi_module.c"
+
+/**
+ * aac_procinfo - Implement /proc/scsi/<drivername>/<n>
+ * @proc_buffer: memory buffer for I/O
+ * @start_ptr: pointer to first valid data
+ * @offset: offset into file
+ * @bytes_available: space left
+ * @host_no: scsi host ident
+ * @write: direction of I/O
+ *
+ * Used to export driver statistics and other infos to the world outside
+ * the kernel using the proc file system. Also provides an interface to
+ * feed the driver with information.
+ *
+ * For reads
+ * - if offset > 0 return 0
+ * - if offset == 0 write data to proc_buffer and set the start_ptr to
+ * beginning of proc_buffer, return the number of characters written.
+ * For writes
+ * - writes currently not supported, return 0
+ *
+ * Bugs: Only offset zero is handled
+ */
+
+static int aac_procinfo(char *proc_buffer, char **start_ptr,off_t offset,
+ int bytes_available, int host_no, int write)
+{
+ if(write || offset > 0)
+ return 0;
+ *start_ptr = proc_buffer;
+ return sprintf(proc_buffer, "%s %d\n", "Raid Controller, scsi hba number", host_no);
+}
+
+EXPORT_NO_SYMBOLS;
diff --git a/drivers/scsi/aacraid/rx.c b/drivers/scsi/aacraid/rx.c
new file mode 100644
index 000000000000..bc47374ef991
--- /dev/null
+++ b/drivers/scsi/aacraid/rx.c
@@ -0,0 +1,409 @@
+/*
+ * Adaptec AAC series RAID controller driver
+ * (c) Copyright 2001 Red Hat Inc. <alan@redhat.com>
+ *
+ * based on the old aacraid driver that is..
+ * Adaptec aacraid device driver for Linux.
+ *
+ * Copyright (c) 2000 Adaptec, Inc. (aacraid@adaptec.com)
+ *
+ * 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
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; see the file COPYING. If not, write to
+ * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * Module Name:
+ * rx.c
+ *
+ * Abstract: Hardware miniport for Drawbridge specific hardware functions.
+ *
+ */
+
+#include <linux/config.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/types.h>
+#include <linux/sched.h>
+#include <linux/pci.h>
+#include <linux/spinlock.h>
+#include <linux/slab.h>
+#include <linux/blk.h>
+#include <linux/delay.h>
+#include <linux/completion.h>
+#include <asm/semaphore.h>
+#include "scsi.h"
+#include "hosts.h"
+
+#include "aacraid.h"
+
+static void aac_rx_intr(int irq, void *dev_id, struct pt_regs *regs)
+{
+ struct aac_dev *dev = dev_id;
+ unsigned long bellbits;
+ u8 intstat, mask;
+ intstat = rx_readb(dev, MUnit.OISR);
+ /*
+ * Read mask and invert because drawbridge is reversed.
+ * This allows us to only service interrupts that have
+ * been enabled.
+ */
+ mask = ~(rx_readb(dev, MUnit.OIMR));
+ /* Check to see if this is our interrupt. If it isn't just return */
+ if (intstat & mask)
+ {
+ bellbits = rx_readl(dev, OutboundDoorbellReg);
+ if (bellbits & DoorBellPrintfReady) {
+ aac_printf(dev, le32_to_cpu(rx_readl (dev, IndexRegs.Mailbox[5])));
+ rx_writel(dev, MUnit.ODR,DoorBellPrintfReady);
+ rx_writel(dev, InboundDoorbellReg,DoorBellPrintfDone);
+ }
+ else if (bellbits & DoorBellAdapterNormCmdReady) {
+ aac_command_normal(&dev->queues->queue[HostNormCmdQueue]);
+ rx_writel(dev, MUnit.ODR, DoorBellAdapterNormCmdReady);
+ }
+ else if (bellbits & DoorBellAdapterNormRespReady) {
+ aac_response_normal(&dev->queues->queue[HostNormRespQueue]);
+ rx_writel(dev, MUnit.ODR,DoorBellAdapterNormRespReady);
+ }
+ else if (bellbits & DoorBellAdapterNormCmdNotFull) {
+ rx_writel(dev, MUnit.ODR, DoorBellAdapterNormCmdNotFull);
+ }
+ else if (bellbits & DoorBellAdapterNormRespNotFull) {
+ rx_writel(dev, MUnit.ODR, DoorBellAdapterNormCmdNotFull);
+ rx_writel(dev, MUnit.ODR, DoorBellAdapterNormRespNotFull);
+ }
+ }
+}
+
+/**
+ * aac_rx_enable_interrupt - Enable event reporting
+ * @dev: Adapter
+ * @event: Event to enable
+ *
+ * Enable event reporting from the i960 for a given event.
+ */
+
+static void aac_rx_enable_interrupt(struct aac_dev * dev, u32 event)
+{
+ switch (event) {
+
+ case HostNormCmdQue:
+ dev->irq_mask &= ~(OUTBOUNDDOORBELL_1);
+ break;
+
+ case HostNormRespQue:
+ dev->irq_mask &= ~(OUTBOUNDDOORBELL_2);
+ break;
+
+ case AdapNormCmdNotFull:
+ dev->irq_mask &= ~(OUTBOUNDDOORBELL_3);
+ break;
+
+ case AdapNormRespNotFull:
+ dev->irq_mask &= ~(OUTBOUNDDOORBELL_4);
+ break;
+ }
+}
+
+/**
+ * aac_rx_disable_interrupt - Disable event reporting
+ * @dev: Adapter
+ * @event: Event to enable
+ *
+ * Disable event reporting from the i960 for a given event.
+ */
+
+static void aac_rx_disable_interrupt(struct aac_dev *dev, u32 event)
+{
+ switch (event) {
+
+ case HostNormCmdQue:
+ dev->irq_mask |= (OUTBOUNDDOORBELL_1);
+ break;
+
+ case HostNormRespQue:
+ dev->irq_mask |= (OUTBOUNDDOORBELL_2);
+ break;
+
+ case AdapNormCmdNotFull:
+ dev->irq_mask |= (OUTBOUNDDOORBELL_3);
+ break;
+
+ case AdapNormRespNotFull:
+ dev->irq_mask |= (OUTBOUNDDOORBELL_4);
+ break;
+ }
+}
+
+/**
+ * rx_sync_cmd - send a command and wait
+ * @dev: Adapter
+ * @command: Command to execute
+ * @p1: first parameter
+ * @ret: adapter status
+ *
+ * This routine will send a synchronous comamnd to the adapter and wait
+ * for its completion.
+ */
+
+static int rx_sync_cmd(struct aac_dev *dev, u32 command, u32 p1, u32 *status)
+{
+ unsigned long start;
+ int ok;
+ /*
+ * Write the command into Mailbox 0
+ */
+ rx_writel(dev, InboundMailbox0, cpu_to_le32(command));
+ /*
+ * Write the parameters into Mailboxes 1 - 4
+ */
+ rx_writel(dev, InboundMailbox1, cpu_to_le32(p1));
+ rx_writel(dev, InboundMailbox2, 0);
+ rx_writel(dev, InboundMailbox3, 0);
+ rx_writel(dev, InboundMailbox4, 0);
+ /*
+ * Clear the synch command doorbell to start on a clean slate.
+ */
+ rx_writel(dev, OutboundDoorbellReg, OUTBOUNDDOORBELL_0);
+ /*
+ * Disable doorbell interrupts
+ */
+ rx_writeb(dev, MUnit.OIMR, rx_readb(dev, MUnit.OIMR) | 0x04);
+ /*
+ * Force the completion of the mask register write before issuing
+ * the interrupt.
+ */
+ rx_readb (dev, MUnit.OIMR);
+ /*
+ * Signal that there is a new synch command
+ */
+ rx_writel(dev, InboundDoorbellReg, INBOUNDDOORBELL_0);
+
+ ok = 0;
+ start = jiffies;
+
+ /*
+ * Wait up to 30 seconds
+ */
+ while (time_before(jiffies, start+30*HZ))
+ {
+ udelay(5); /* Delay 5 microseconds to let Mon960 get info. */
+ /*
+ * Mon960 will set doorbell0 bit when it has completed the command.
+ */
+ if (rx_readl(dev, OutboundDoorbellReg) & OUTBOUNDDOORBELL_0) {
+ /*
+ * Clear the doorbell.
+ */
+ rx_writel(dev, OutboundDoorbellReg, OUTBOUNDDOORBELL_0);
+ ok = 1;
+ break;
+ }
+ /*
+ * Yield the processor in case we are slow
+ */
+ set_current_state(TASK_UNINTERRUPTIBLE);
+ schedule_timeout(1);
+ }
+ if (ok != 1) {
+ /*
+ * Restore interrupt mask even though we timed out
+ */
+ rx_writeb(dev, MUnit.OIMR, rx_readl(dev, MUnit.OIMR) & 0xfb);
+ return -ETIMEDOUT;
+ }
+ /*
+ * Pull the synch status from Mailbox 0.
+ */
+ *status = le32_to_cpu(rx_readl(dev, IndexRegs.Mailbox[0]));
+ /*
+ * Clear the synch command doorbell.
+ */
+ rx_writel(dev, OutboundDoorbellReg, OUTBOUNDDOORBELL_0);
+ /*
+ * Restore interrupt mask
+ */
+ rx_writeb(dev, MUnit.OIMR, rx_readl(dev, MUnit.OIMR) & 0xfb);
+ return 0;
+
+}
+
+/**
+ * aac_rx_interrupt_adapter - interrupt adapter
+ * @dev: Adapter
+ *
+ * Send an interrupt to the i960 and breakpoint it.
+ */
+
+static void aac_rx_interrupt_adapter(struct aac_dev *dev)
+{
+ u32 ret;
+ rx_sync_cmd(dev, BREAKPOINT_REQUEST, 0, &ret);
+}
+
+/**
+ * aac_rx_notify_adapter - send an event to the adapter
+ * @dev: Adapter
+ * @event: Event to send
+ *
+ * Notify the i960 that something it probably cares about has
+ * happened.
+ */
+
+static void aac_rx_notify_adapter(struct aac_dev *dev, u32 event)
+{
+ switch (event) {
+
+ case AdapNormCmdQue:
+ rx_writel(dev, MUnit.IDR,INBOUNDDOORBELL_1);
+ break;
+ case HostNormRespNotFull:
+ rx_writel(dev, MUnit.IDR,INBOUNDDOORBELL_4);
+ break;
+ case AdapNormRespQue:
+ rx_writel(dev, MUnit.IDR,INBOUNDDOORBELL_2);
+ break;
+ case HostNormCmdNotFull:
+ rx_writel(dev, MUnit.IDR,INBOUNDDOORBELL_3);
+ break;
+ case HostShutdown:
+// rx_sync_cmd(dev, HOST_CRASHING, 0, 0, 0, 0, &ret);
+ break;
+ case FastIo:
+ rx_writel(dev, MUnit.IDR,INBOUNDDOORBELL_6);
+ break;
+ case AdapPrintfDone:
+ rx_writel(dev, MUnit.IDR,INBOUNDDOORBELL_5);
+ break;
+ default:
+ BUG();
+ break;
+ }
+}
+
+/**
+ * aac_rx_start_adapter - activate adapter
+ * @dev: Adapter
+ *
+ * Start up processing on an i960 based AAC adapter
+ */
+
+static void aac_rx_start_adapter(struct aac_dev *dev)
+{
+ u32 status;
+ struct aac_init *init;
+
+ init = dev->init;
+ init->HostElapsedSeconds = cpu_to_le32(jiffies/HZ);
+ /*
+ * Tell the adapter we are back and up and running so it will scan
+ * its command queues and enable our interrupts
+ */
+ dev->irq_mask = (DoorBellPrintfReady | OUTBOUNDDOORBELL_1 | OUTBOUNDDOORBELL_2 | OUTBOUNDDOORBELL_3 | OUTBOUNDDOORBELL_4);
+ /*
+ * First clear out all interrupts. Then enable the one's that we
+ * can handle.
+ */
+ rx_writeb(dev, MUnit.OIMR, 0xff);
+ rx_writel(dev, MUnit.ODR, 0xffffffff);
+// rx_writeb(dev, MUnit.OIMR, ~(u8)OUTBOUND_DOORBELL_INTERRUPT_MASK);
+ rx_writeb(dev, MUnit.OIMR, 0xfb);
+
+ // We can only use a 32 bit address here
+ rx_sync_cmd(dev, INIT_STRUCT_BASE_ADDRESS, (u32)(ulong)dev->init_pa, &status);
+}
+
+/**
+ * aac_rx_init - initialize an i960 based AAC card
+ * @dev: device to configure
+ * @devnum: adapter number
+ *
+ * Allocate and set up resources for the i960 based AAC variants. The
+ * device_interface in the commregion will be allocated and linked
+ * to the comm region.
+ */
+
+int aac_rx_init(struct aac_dev *dev, unsigned long num)
+{
+ unsigned long start;
+ unsigned long status;
+ int instance;
+ const char * name;
+
+ dev->devnum = num;
+ instance = dev->id;
+ name = dev->name;
+
+ /*
+ * Map in the registers from the adapter.
+ */
+ if((dev->regs.rx = (struct rx_registers *)ioremap((unsigned long)dev->scsi_host_ptr->base, 8192))==NULL)
+ {
+ printk(KERN_WARNING "aacraid: unable to map i960.\n" );
+ return -1;
+ }
+ /*
+ * Check to see if the board failed any self tests.
+ */
+ if (rx_readl(dev, IndexRegs.Mailbox[7]) & SELF_TEST_FAILED) {
+ printk(KERN_ERR "%s%d: adapter self-test failed.\n", dev->name, instance);
+ return -1;
+ }
+ /*
+ * Check to see if the board panic'd while booting.
+ */
+ if (rx_readl(dev, IndexRegs.Mailbox[7]) & KERNEL_PANIC) {
+ printk(KERN_ERR "%s%d: adapter kernel panic'd.\n", dev->name, instance);
+ return -1;
+ }
+ start = jiffies;
+ /*
+ * Wait for the adapter to be up and running. Wait up to 3 minutes
+ */
+ while (!(rx_readl(dev, IndexRegs.Mailbox[7]) & KERNEL_UP_AND_RUNNING))
+ {
+ if(time_after(jiffies, start+180*HZ))
+ {
+ status = rx_readl(dev, IndexRegs.Mailbox[7]) >> 16;
+ printk(KERN_ERR "%s%d: adapter kernel failed to start, init status = %ld.\n", dev->name, instance, status);
+ return -1;
+ }
+ set_current_state(TASK_UNINTERRUPTIBLE);
+ schedule_timeout(1);
+ }
+ if (request_irq(dev->scsi_host_ptr->irq, aac_rx_intr, SA_SHIRQ|SA_INTERRUPT, "aacraid", (void *)dev)<0)
+ {
+ printk(KERN_ERR "%s%d: Interrupt unavailable.\n", name, instance);
+ return -1;
+ }
+ /*
+ * Fill in the function dispatch table.
+ */
+ dev->a_ops.adapter_interrupt = aac_rx_interrupt_adapter;
+ dev->a_ops.adapter_enable_int = aac_rx_enable_interrupt;
+ dev->a_ops.adapter_disable_int = aac_rx_disable_interrupt;
+ dev->a_ops.adapter_notify = aac_rx_notify_adapter;
+ dev->a_ops.adapter_sync_cmd = rx_sync_cmd;
+
+ if (aac_init_adapter(dev) == NULL)
+ return -1;
+ /*
+ * Start any kernel threads needed
+ */
+ dev->thread_pid = kernel_thread((int (*)(void *))aac_command_thread, dev, 0);
+ /*
+ * Tell the adapter that all is configured, and it can start
+ * accepting requests
+ */
+ aac_rx_start_adapter(dev);
+ return 0;
+}
diff --git a/drivers/scsi/aacraid/sa.c b/drivers/scsi/aacraid/sa.c
new file mode 100644
index 000000000000..86e9f3cfd27d
--- /dev/null
+++ b/drivers/scsi/aacraid/sa.c
@@ -0,0 +1,396 @@
+/*
+ * Adaptec AAC series RAID controller driver
+ * (c) Copyright 2001 Red Hat Inc. <alan@redhat.com>
+ *
+ * based on the old aacraid driver that is..
+ * Adaptec aacraid device driver for Linux.
+ *
+ * Copyright (c) 2000 Adaptec, Inc. (aacraid@adaptec.com)
+ *
+ * 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
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; see the file COPYING. If not, write to
+ * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * Module Name:
+ * sa.c
+ *
+ * Abstract: Drawbridge specific support functions
+ *
+ */
+
+#include <linux/config.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/types.h>
+#include <linux/sched.h>
+#include <linux/pci.h>
+#include <linux/spinlock.h>
+#include <linux/slab.h>
+#include <linux/blk.h>
+#include <linux/delay.h>
+#include <linux/completion.h>
+#include <asm/semaphore.h>
+#include "scsi.h"
+#include "hosts.h"
+
+#include "aacraid.h"
+
+static void aac_sa_intr(int irq, void *dev_id, struct pt_regs *regs)
+{
+ struct aac_dev *dev = dev_id;
+ unsigned short intstat, mask;
+
+ intstat = sa_readw(dev, DoorbellReg_p);
+ /*
+ * Read mask and invert because drawbridge is reversed.
+ * This allows us to only service interrupts that have been enabled.
+ */
+ mask = ~(sa_readw(dev, SaDbCSR.PRISETIRQMASK));
+
+ /* Check to see if this is our interrupt. If it isn't just return */
+
+ if (intstat & mask) {
+ if (intstat & PrintfReady) {
+ aac_printf(dev, le32_to_cpu(sa_readl(dev, Mailbox5)));
+ sa_writew(dev, DoorbellClrReg_p, PrintfReady); /* clear PrintfReady */
+ sa_writew(dev, DoorbellReg_s, PrintfDone);
+ } else if (intstat & DOORBELL_1) { // dev -> Host Normal Command Ready
+ aac_command_normal(&dev->queues->queue[HostNormCmdQueue]);
+ sa_writew(dev, DoorbellClrReg_p, DOORBELL_1);
+ } else if (intstat & DOORBELL_2) { // dev -> Host Normal Response Ready
+ aac_response_normal(&dev->queues->queue[HostNormRespQueue]);
+ sa_writew(dev, DoorbellClrReg_p, DOORBELL_2);
+ } else if (intstat & DOORBELL_3) { // dev -> Host Normal Command Not Full
+ sa_writew(dev, DoorbellClrReg_p, DOORBELL_3);
+ } else if (intstat & DOORBELL_4) { // dev -> Host Normal Response Not Full
+ sa_writew(dev, DoorbellClrReg_p, DOORBELL_4);
+ }
+ }
+}
+
+/**
+ * aac_sa_enable_interrupt - enable an interrupt event
+ * @dev: Which adapter to enable.
+ * @event: Which adapter event.
+ *
+ * This routine will enable the corresponding adapter event to cause an interrupt on
+ * the host.
+ */
+
+void aac_sa_enable_interrupt(struct aac_dev *dev, u32 event)
+{
+ switch (event) {
+
+ case HostNormCmdQue:
+ sa_writew(dev, SaDbCSR.PRICLEARIRQMASK, DOORBELL_1);
+ break;
+
+ case HostNormRespQue:
+ sa_writew(dev, SaDbCSR.PRICLEARIRQMASK, DOORBELL_2);
+ break;
+
+ case AdapNormCmdNotFull:
+ sa_writew(dev, SaDbCSR.PRICLEARIRQMASK, DOORBELL_3);
+ break;
+
+ case AdapNormRespNotFull:
+ sa_writew(dev, SaDbCSR.PRICLEARIRQMASK, DOORBELL_4);
+ break;
+ }
+}
+
+/**
+ * aac_sa_disable_interrupt - disable an interrupt event
+ * @dev: Which adapter to enable.
+ * @event: Which adapter event.
+ *
+ * This routine will enable the corresponding adapter event to cause an interrupt on
+ * the host.
+ */
+
+void aac_sa_disable_interrupt (struct aac_dev *dev, u32 event)
+{
+ switch (event) {
+
+ case HostNormCmdQue:
+ sa_writew(dev, SaDbCSR.PRISETIRQMASK, DOORBELL_1);
+ break;
+
+ case HostNormRespQue:
+ sa_writew(dev, SaDbCSR.PRISETIRQMASK, DOORBELL_2);
+ break;
+
+ case AdapNormCmdNotFull:
+ sa_writew(dev, SaDbCSR.PRISETIRQMASK, DOORBELL_3);
+ break;
+
+ case AdapNormRespNotFull:
+ sa_writew(dev, SaDbCSR.PRISETIRQMASK, DOORBELL_4);
+ break;
+ }
+}
+
+/**
+ * aac_sa_notify_adapter - handle adapter notification
+ * @dev: Adapter that notification is for
+ * @event: Event to notidy
+ *
+ * Notify the adapter of an event
+ */
+
+void aac_sa_notify_adapter(struct aac_dev *dev, u32 event)
+{
+ switch (event) {
+
+ case AdapNormCmdQue:
+ sa_writew(dev, DoorbellReg_s,DOORBELL_1);
+ break;
+ case HostNormRespNotFull:
+ sa_writew(dev, DoorbellReg_s,DOORBELL_4);
+ break;
+ case AdapNormRespQue:
+ sa_writew(dev, DoorbellReg_s,DOORBELL_2);
+ break;
+ case HostNormCmdNotFull:
+ sa_writew(dev, DoorbellReg_s,DOORBELL_3);
+ break;
+ case HostShutdown:
+ //sa_sync_cmd(dev, HOST_CRASHING, 0, &ret);
+ break;
+ case FastIo:
+ sa_writew(dev, DoorbellReg_s,DOORBELL_6);
+ break;
+ case AdapPrintfDone:
+ sa_writew(dev, DoorbellReg_s,DOORBELL_5);
+ break;
+ default:
+ BUG();
+ break;
+ }
+}
+
+
+/**
+ * sa_sync_cmd - send a command and wait
+ * @dev: Adapter
+ * @command: Command to execute
+ * @p1: first parameter
+ * @ret: adapter status
+ *
+ * This routine will send a synchronous comamnd to the adapter and wait
+ * for its completion.
+ */
+
+static int sa_sync_cmd(struct aac_dev *dev, u32 command, u32 p1, u32 *ret)
+{
+ unsigned long start;
+ int ok;
+ /*
+ * Write the Command into Mailbox 0
+ */
+ sa_writel(dev, Mailbox0, cpu_to_le32(command));
+ /*
+ * Write the parameters into Mailboxes 1 - 4
+ */
+ sa_writel(dev, Mailbox1, cpu_to_le32(p1));
+ sa_writel(dev, Mailbox2, 0);
+ sa_writel(dev, Mailbox3, 0);
+ sa_writel(dev, Mailbox4, 0);
+ /*
+ * Clear the synch command doorbell to start on a clean slate.
+ */
+ sa_writew(dev, DoorbellClrReg_p, DOORBELL_0);
+ /*
+ * Signal that there is a new synch command
+ */
+ sa_writew(dev, DoorbellReg_s, DOORBELL_0);
+
+ ok = 0;
+ start = jiffies;
+
+ while(time_before(jiffies, start+30*HZ))
+ {
+ /*
+ * Delay 5uS so that the monitor gets access
+ */
+ udelay(5);
+ /*
+ * Mon110 will set doorbell0 bit when it has
+ * completed the command.
+ */
+ if(sa_readw(dev, DoorbellReg_p) & DOORBELL_0) {
+ ok = 1;
+ break;
+ }
+ set_current_state(TASK_UNINTERRUPTIBLE);
+ schedule_timeout(1);
+ }
+
+ if (ok != 1)
+ return -ETIMEDOUT;
+ /*
+ * Clear the synch command doorbell.
+ */
+ sa_writew(dev, DoorbellClrReg_p, DOORBELL_0);
+ /*
+ * Pull the synch status from Mailbox 0.
+ */
+ *ret = le32_to_cpu(sa_readl(dev, Mailbox0));
+ return 0;
+}
+
+/**
+ * aac_sa_interrupt_adapter - interrupt an adapter
+ * @dev: Which adapter to enable.
+ *
+ * Breakpoint an adapter.
+ */
+
+static void aac_sa_interrupt_adapter (struct aac_dev *dev)
+{
+ u32 ret;
+ sa_sync_cmd(dev, BREAKPOINT_REQUEST, 0, &ret);
+}
+
+/**
+ * aac_sa_start_adapter - activate adapter
+ * @dev: Adapter
+ *
+ * Start up processing on an ARM based AAC adapter
+ */
+
+static void aac_sa_start_adapter(struct aac_dev *dev)
+{
+ u32 ret;
+ struct aac_init *init;
+ /*
+ * Fill in the remaining pieces of the init.
+ */
+ init = dev->init;
+ init->HostElapsedSeconds = cpu_to_le32(jiffies/HZ);
+
+ dprintk(("INIT\n"));
+ /*
+ * Tell the adapter we are back and up and running so it will scan its command
+ * queues and enable our interrupts
+ */
+ dev->irq_mask = (PrintfReady | DOORBELL_1 | DOORBELL_2 | DOORBELL_3 | DOORBELL_4);
+ /*
+ * First clear out all interrupts. Then enable the one's that
+ * we can handle.
+ */
+ dprintk(("MASK\n"));
+ sa_writew(dev, SaDbCSR.PRISETIRQMASK, cpu_to_le16(0xffff));
+ sa_writew(dev, SaDbCSR.PRICLEARIRQMASK, (PrintfReady | DOORBELL_1 | DOORBELL_2 | DOORBELL_3 | DOORBELL_4));
+ dprintk(("SYNCCMD\n"));
+ /* We can only use a 32 bit address here */
+ sa_sync_cmd(dev, INIT_STRUCT_BASE_ADDRESS, (u32)(ulong)dev->init_pa, &ret);
+}
+
+/**
+ * aac_sa_init - initialize an ARM based AAC card
+ * @dev: device to configure
+ * @devnum: adapter number
+ *
+ * Allocate and set up resources for the ARM based AAC variants. The
+ * device_interface in the commregion will be allocated and linked
+ * to the comm region.
+ */
+
+int aac_sa_init(struct aac_dev *dev, unsigned long devnum)
+{
+ unsigned long start;
+ unsigned long status;
+ int instance;
+ const char *name;
+
+ dev->devnum = devnum;
+
+ dprintk(("PREINST\n"));
+ instance = dev->id;
+ name = dev->name;
+
+ /*
+ * Map in the registers from the adapter.
+ */
+ dprintk(("PREMAP\n"));
+
+ if((dev->regs.sa = (struct sa_registers *)ioremap((unsigned long)dev->scsi_host_ptr->base, 8192))==NULL)
+ {
+ printk(KERN_WARNING "aacraid: unable to map ARM.\n" );
+ return -1;
+ }
+ /*
+ * Check to see if the board failed any self tests.
+ */
+ if (sa_readl(dev, Mailbox7) & SELF_TEST_FAILED) {
+ printk(KERN_WARNING "%s%d: adapter self-test failed.\n", name, instance);
+ return -1;
+ }
+ /*
+ * Check to see if the board panic'd while booting.
+ */
+ if (sa_readl(dev, Mailbox7) & KERNEL_PANIC) {
+ printk(KERN_WARNING "%s%d: adapter kernel panic'd.\n", name, instance);
+ return -1;
+ }
+ start = jiffies;
+ /*
+ * Wait for the adapter to be up and running. Wait up to 3 minutes.
+ */
+ while (!(sa_readl(dev, Mailbox7) & KERNEL_UP_AND_RUNNING)) {
+ if (time_after(start+180*HZ, jiffies)) {
+ status = sa_readl(dev, Mailbox7) >> 16;
+ printk(KERN_WARNING "%s%d: adapter kernel failed to start, init status = %d.\n", name, instance, le32_to_cpu(status));
+ return -1;
+ }
+ set_current_state(TASK_UNINTERRUPTIBLE);
+ schedule_timeout(1);
+ }
+
+ dprintk(("ATIRQ\n"));
+ if (request_irq(dev->scsi_host_ptr->irq, aac_sa_intr, SA_SHIRQ|SA_INTERRUPT, "aacraid", (void *)dev ) < 0) {
+ printk(KERN_WARNING "%s%d: Interrupt unavailable.\n", name, instance);
+ return -1;
+ }
+
+ /*
+ * Fill in the function dispatch table.
+ */
+
+ dev->a_ops.adapter_interrupt = aac_sa_interrupt_adapter;
+ dev->a_ops.adapter_enable_int = aac_sa_enable_interrupt;
+ dev->a_ops.adapter_disable_int = aac_sa_disable_interrupt;
+ dev->a_ops.adapter_notify = aac_sa_notify_adapter;
+ dev->a_ops.adapter_sync_cmd = sa_sync_cmd;
+
+ dprintk(("FUNCDONE\n"));
+
+ if(aac_init_adapter(dev) == NULL)
+ return -1;
+
+ dprintk(("NEWADAPTDONE\n"));
+ /*
+ * Start any kernel threads needed
+ */
+ dev->thread_pid = kernel_thread((int (*)(void *))aac_command_thread, dev, 0);
+ /*
+ * Tell the adapter that all is configure, and it can start
+ * accepting requests
+ */
+ dprintk(("STARTING\n"));
+ aac_sa_start_adapter(dev);
+ dprintk(("STARTED\n"));
+ return 0;
+}
+