public inbox for linux-scsi@vger.kernel.org
 help / color / mirror / Atom feed
* [PATCH/RFC 1/2] 3ware 9000 SATA-RAID driver
@ 2004-05-22 18:19 Adam Radford
  2004-05-23 10:59 ` hch
  0 siblings, 1 reply; 4+ messages in thread
From: Adam Radford @ 2004-05-22 18:19 UTC (permalink / raw)
  To: linux-scsi

This email is a resend.  My previous patch never appeared on linux-scsi for some 
reason, possibly because it was a 143k attachment.

Andrew/James,

These 2 patches contain a new LLD SCSI driver to add support for the 3ware 9000 series
SATA-RAID controllers.  The driver is 100% GPL'd.

Please review for inclusion in 2.6.6-mm5.

Anybody who wishes to review the driver implementation and give feedback would be
very appreciated!

--
Adam Radford
Staff Software Engineer
AMCC

diff -Naur linux-2.6.6-mm4/drivers/pci/pci.ids linux-2.6.6-mm5/drivers/pci/pci.ids
--- linux-2.6.6-mm4/drivers/pci/pci.ids	2004-05-21 16:35:20.000000000 -0700
+++ linux-2.6.6-mm5/drivers/pci/pci.ids	2004-05-21 15:38:58.000000000 -0700
@@ -5751,7 +5751,7 @@
 13c1  3ware Inc
 	1000  3ware ATA-RAID
 	1001  3ware 7000-series ATA-RAID
-	1002  3ware ATA-RAID
+	1002  3ware 9000-series ATA-RAID
 13c2  Technotrend Systemtechnik GmbH
 13c3  Janz Computer AG
 13c4  Phase Metrics
diff -Naur linux-2.6.6-mm4/drivers/scsi/3w-9xxx.c linux-2.6.6-mm5/drivers/scsi/3w-9xxx.c
--- linux-2.6.6-mm4/drivers/scsi/3w-9xxx.c	1969-12-31 16:00:00.000000000 -0800
+++ linux-2.6.6-mm5/drivers/scsi/3w-9xxx.c	2004-05-21 15:14:48.000000000 -0700
@@ -0,0 +1,2129 @@
+/*
+   3w-9xxx.c -- 3ware 9000 Storage Controller device driver for Linux.
+
+   Written By: Adam Radford <linuxraid@amcc.com>
+
+   Copyright (C) 2004 Applied Micro Circuits Corporation.
+
+   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; version 2 of the License.
+
+   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.
+
+   NO WARRANTY
+   THE PROGRAM IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OR
+   CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT
+   LIMITATION, ANY WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT,
+   MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Each Recipient is
+   solely responsible for determining the appropriateness of using and
+   distributing the Program and assumes all risks associated with its
+   exercise of rights under this Agreement, including but not limited to
+   the risks and costs of program errors, damage to or loss of data,
+   programs or equipment, and unavailability or interruption of operations.
+
+   DISCLAIMER OF LIABILITY
+   NEITHER RECIPIENT NOR ANY CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY
+   DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+   DAMAGES (INCLUDING WITHOUT LIMITATION LOST PROFITS), HOWEVER CAUSED AND
+   ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
+   TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
+   USE OR DISTRIBUTION OF THE PROGRAM OR THE EXERCISE OF ANY RIGHTS GRANTED
+   HEREUNDER, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+   Bugs/Comments/Suggestions should be mailed to:
+   linuxraid@amcc.com
+
+   For more information, goto:
+   http://www.amcc.com
+
+   Note: This version of the driver does not contain a bundled firmware
+         image.
+
+   History
+   -------
+   2.26.00.005 - Initial release.
+   2.26.00.006 - Remove TW_WRITEL macro, replace with writeq().
+   2.26.00.007 - Skip lun and channel probes.
+   2.26.00.008 - Fix bug in attention/command interrupt handling.
+*/
+
+#include <linux/module.h>
+
+MODULE_AUTHOR ("AMCC");
+#ifdef CONFIG_SMP
+MODULE_DESCRIPTION ("3ware 9000 Storage Controller Linux Driver (SMP)");
+#else
+MODULE_DESCRIPTION ("3ware 9000 Storage Controller Linux Driver");
+#endif
+MODULE_LICENSE("GPL");
+
+#include <linux/kernel.h>
+#include <linux/pci.h>
+#include <linux/time.h>
+#include <linux/proc_fs.h>
+#include <linux/sched.h>
+#include <linux/ioport.h>
+#include <linux/blkdev.h>
+#include <linux/hdreg.h>
+#include <linux/string.h>
+#include <linux/delay.h>
+#include <linux/smp.h>
+#include <linux/reboot.h>
+#include <linux/spinlock.h>
+#include <linux/interrupt.h>
+#include <linux/moduleparam.h>
+
+#include <asm/errno.h>
+#include <asm/io.h>
+#include <asm/irq.h>
+#include <asm/uaccess.h>
+
+#include "scsi.h"
+#include "hosts.h"
+
+#include "3w-9xxx.h"
+
+/* Notifier block to get a notify on system shutdown/halt/reboot */
+static struct notifier_block twa_notifier = {
+	.notifier_call	= twa_halt,
+	.next		= NULL,
+	.priority	= 0
+};
+
+/* File operations struct for character device */
+static struct file_operations twa_fops = {
+	.owner		= THIS_MODULE,
+	.ioctl		= twa_chrdev_ioctl,
+	.open		= twa_chrdev_open,
+	.release	= twa_chrdev_release
+};
+
+/* Globals */
+char *twa_driver_version="2.26.00.008";
+TW_Device_Extension *twa_device_extension_list[TW_MAX_SLOT];
+unsigned int twa_device_extension_count = 0;
+static int twa_major = -1;
+extern struct timezone sys_tz;
+static int cmds_per_lun;
+
+/* Module parameters */
+module_param(cmds_per_lun, int, 0);
+MODULE_PARM_DESC(cmds_per_lun, "Maximum commands per LUN");
+
+/* Functions */
+
+/* This function will complete an aen request from the isr */
+static int twa_aen_complete(TW_Device_Extension *tw_dev, int request_id)
+{
+	TW_Command_Full *full_command_packet;
+	TW_Command *command_packet;
+	TW_Command_Apache_Header *header;
+	unsigned short aen;
+	int retval = 1;
+
+	header = (TW_Command_Apache_Header *)tw_dev->generic_buffer_virt[request_id];
+	tw_dev->posted_request_count--;
+	aen = header->status_block.error;
+	full_command_packet = (TW_Command_Full *)tw_dev->command_packet_virt[request_id];
+	command_packet = &full_command_packet->command.oldcommand;
+
+	/* First check for internal completion of set param for time sync */
+	if (command_packet->byte0_offset.opcode == TW_OP_SET_PARAM) {
+		/* Keep reading the queue in case there are more aen's */
+		if (twa_aen_read_queue(tw_dev, request_id))
+			goto out2;
+	        else {
+			retval = 0;
+			goto out;
+		}
+	}
+
+	switch (aen) {
+	case TW_AEN_QUEUE_EMPTY:
+		/* Quit reading the queue if this is the last one */
+		break;
+	case TW_AEN_SYNC_TIME_WITH_HOST:
+		twa_aen_sync_time(tw_dev, request_id);
+		retval = 0;
+		goto out;
+	default:
+		twa_aen_queue_event(tw_dev, header);
+
+		/* If there are more aen's, keep reading the queue */
+		if (twa_aen_read_queue(tw_dev, request_id))
+			goto out2;
+		else {
+			retval = 0;
+			goto out;
+		}
+	}
+	retval = 0;
+out2:
+	tw_dev->state[request_id] = TW_S_COMPLETED;
+	twa_free_request_id(tw_dev, request_id);
+	clear_bit(TW_IN_ATTENTION_LOOP, &tw_dev->flags);
+out:
+	return retval;
+} /* End twa_aen_complete() */
+
+/* This function will drain aen queue */
+static int twa_aen_drain_queue(TW_Device_Extension *tw_dev, int no_check_reset)
+{
+	int request_id = 0;
+	char cdb[TW_MAX_CDB_LEN];
+	TW_SG_Apache sglist[1];
+	int finished = 0, count = 0;
+	TW_Command_Full *full_command_packet;
+	TW_Command_Apache_Header *header;
+	unsigned short aen;
+	int first_reset = 0, queue = 0, retval = 1;
+
+	if (no_check_reset)
+		first_reset = 0;
+	else
+		first_reset = 1;
+
+	full_command_packet = (TW_Command_Full *)tw_dev->command_packet_virt[request_id];
+	memset(full_command_packet, 0, sizeof(TW_Command_Full));
+
+	/* Initialize cdb */
+	memset(&cdb, 0, TW_MAX_CDB_LEN);
+	cdb[0] = REQUEST_SENSE; /* opcode */
+	cdb[4] = TW_ALLOCATION_LENGTH; /* allocation length */
+
+	/* Initialize sglist */
+	memset(&sglist, 0, sizeof(TW_SG_Apache));
+	sglist[0].length = TW_SECTOR_SIZE;
+	sglist[0].address = tw_dev->generic_buffer_phys[request_id];
+
+	if (sglist[0].address & TW_ALIGNMENT_9000_SGL) {
+		TW_PRINTK(tw_dev->host, TW_DRIVER, 0x1, "Found unaligned address during AEN drain");
+		goto out;
+	}
+
+	/* Mark internal command */
+	tw_dev->srb[request_id] = NULL;
+
+	do {
+		/* Send command to the board */
+		if (twa_scsiop_execute_scsi(tw_dev, request_id, cdb, 1, sglist)) {
+			TW_PRINTK(tw_dev->host, TW_DRIVER, 0x2, "Error posting request sense");
+			goto out;
+		}
+
+		/* Now poll for completion */
+		if (twa_poll_response(tw_dev, request_id, 30)) {
+			TW_PRINTK(tw_dev->host, TW_DRIVER, 0x3, "No valid response while draining AEN queue");
+			tw_dev->posted_request_count--;
+			goto out;
+		}
+
+		tw_dev->posted_request_count--;
+		header = (TW_Command_Apache_Header *)tw_dev->generic_buffer_virt[request_id];
+		aen = header->status_block.error;
+		queue = 0;
+		count++;
+
+		switch (aen) {
+		case TW_AEN_QUEUE_EMPTY:
+			if (first_reset != 1)
+				goto out;
+			else
+				finished = 1;
+			break;
+		case TW_AEN_SOFT_RESET:
+			if (first_reset == 0)
+				first_reset = 1;
+			else
+				queue = 1;
+			break;
+		case TW_AEN_SYNC_TIME_WITH_HOST:
+			break;
+		default:
+			queue = 1;
+		}
+
+		/* Now queue an event info */
+		if (queue)
+			twa_aen_queue_event(tw_dev, header);
+	} while ((finished == 0) && (count < TW_MAX_AEN_DRAIN));
+
+	if (count == TW_MAX_AEN_DRAIN)
+		goto out;
+
+	retval = 0;
+out:
+	tw_dev->state[request_id] = TW_S_INITIAL;
+	return retval;
+} /* End twa_aen_drain_queue() */
+
+/* This function will queue an event */
+static void twa_aen_queue_event(TW_Device_Extension *tw_dev, TW_Command_Apache_Header *header)
+{
+	u32 local_time;
+	struct timeval time;
+	TW_Event *event;
+	unsigned short aen;
+	char host[16];
+
+	tw_dev->aen_count++;
+
+	/* Fill out event info */
+	event = tw_dev->event_queue[tw_dev->error_index];
+
+	/* Check for clobber */
+	host[0] = '\0';
+	if (tw_dev->host) {
+		sprintf(host, " scsi%d:", tw_dev->host->host_no);
+		if (event->retrieved == TW_AEN_NOT_RETRIEVED)
+			tw_dev->aen_clobber = 1;
+	}
+
+	aen = header->status_block.error;
+	memset(event, 0, sizeof(TW_Event));
+
+	event->severity = header->status_block.substatus_block.severity;
+	do_gettimeofday(&time);
+	local_time = (u32)(time.tv_sec - (sys_tz.tz_minuteswest * 60));
+	event->time_stamp_sec = local_time;
+	event->aen_code = aen;
+	event->retrieved = TW_AEN_NOT_RETRIEVED;
+	event->sequence_id = tw_dev->error_sequence_id;
+	tw_dev->error_sequence_id++;
+
+	header->err_specific_desc[sizeof(header->err_specific_desc) - 1] = '\0';
+	event->parameter_len = strlen(header->err_specific_desc);
+	memcpy(event->parameter_data, header->err_specific_desc, event->parameter_len);
+	if (event->severity != TW_AEN_SEVERITY_DEBUG)
+		printk(KERN_WARNING "3w-9xxx:%s AEN: %s (0x%02X:0x%04X): %s:%s.\n", host, twa_aen_severity_lookup(header->status_block.substatus_block.severity), TW_MESSAGE_SOURCE_CONTROLLER_EVENT, aen, twa_string_lookup(twa_aen_table, aen), header->err_specific_desc);
+	else
+		tw_dev->aen_count--;
+
+	if ((tw_dev->error_index + 1) == TW_Q_LENGTH)
+		tw_dev->event_queue_wrapped = 1;
+	tw_dev->error_index = (tw_dev->error_index + 1 ) % TW_Q_LENGTH;
+} /* End twa_aen_queue_event() */
+
+/* This function will read the aen queue from the isr */
+static int twa_aen_read_queue(TW_Device_Extension *tw_dev, int request_id)
+{
+	char cdb[TW_MAX_CDB_LEN];
+	TW_SG_Apache sglist[1];
+	TW_Command_Full *full_command_packet;
+	int retval = 1;
+
+	full_command_packet = (TW_Command_Full *)tw_dev->command_packet_virt[request_id];
+	memset(full_command_packet, 0, sizeof(TW_Command_Full));
+
+	/* Initialize cdb */
+	memset(&cdb, 0, TW_MAX_CDB_LEN);
+	cdb[0] = REQUEST_SENSE; /* opcode */
+	cdb[4] = TW_ALLOCATION_LENGTH; /* allocation length */
+
+	/* Initialize sglist */
+	memset(&sglist, 0, sizeof(TW_SG_Apache));
+	sglist[0].length = TW_SECTOR_SIZE;
+	sglist[0].address = tw_dev->generic_buffer_phys[request_id];
+
+	/* Mark internal command */
+	tw_dev->srb[request_id] = NULL;
+
+	/* Now post the command packet */
+	if (twa_scsiop_execute_scsi(tw_dev, request_id, cdb, 1, sglist)) {
+		TW_PRINTK(tw_dev->host, TW_DRIVER, 0x4, "Post failed while reading AEN queue");
+		goto out;
+	}
+	retval = 0;
+out:
+	return retval;
+} /* End twa_aen_read_queue() */
+
+/* This function will look up an AEN severity string */
+static char *twa_aen_severity_lookup(unsigned char severity_code)
+{
+	char *retval = NULL;
+
+	if ((severity_code < (unsigned char) TW_AEN_SEVERITY_ERROR) ||
+	    (severity_code > (unsigned char) TW_AEN_SEVERITY_DEBUG))
+		goto out;
+
+	retval = twa_aen_severity_table[severity_code];
+out:
+	return retval;
+} /* End twa_aen_severity_lookup() */
+
+/* This function will sync firmware time with the host time */
+static void twa_aen_sync_time(TW_Device_Extension *tw_dev, int request_id)
+{
+	u32 schedulertime;
+	struct timeval utc;
+	TW_Command_Full *full_command_packet;
+	TW_Command *command_packet;
+	TW_Param_Apache *param;
+	u32 local_time;
+
+	/* Fill out the command packet */
+	full_command_packet = (TW_Command_Full *)tw_dev->command_packet_virt[request_id];
+	memset(full_command_packet, 0, sizeof(TW_Command_Full));
+	command_packet = &full_command_packet->command.oldcommand;
+	command_packet->byte0_offset.opcode = TW_OP_SET_PARAM;
+	command_packet->byte0_offset.sgl_offset = 2;
+	command_packet->request_id = request_id;
+	command_packet->byte8_offset.param.sgl[0].address = tw_dev->generic_buffer_phys[request_id];
+	command_packet->byte8_offset.param.sgl[0].length = TW_SECTOR_SIZE;
+	command_packet->size = TW_COMMAND_SIZE;
+	command_packet->byte6_offset.parameter_count = 1;
+
+	/* Setup the param */
+	param = (TW_Param_Apache *)tw_dev->generic_buffer_virt[request_id];
+	memset(param, 0, TW_SECTOR_SIZE);
+	param->table_id = TW_TIMEKEEP_TABLE | 0x8000; /* Controller time keep table */
+	param->parameter_id = 0x3; /* SchedulerTime */
+	param->parameter_size_bytes = 4;
+
+	/* Convert system time in UTC to local time seconds since last 
+           Sunday 12:00AM */
+	do_gettimeofday(&utc);
+	local_time = (u32)(utc.tv_sec - (sys_tz.tz_minuteswest * 60));
+	schedulertime = local_time - (3 * 86400);
+	schedulertime = schedulertime % 604800;
+
+	memcpy(param->data, &schedulertime, sizeof(u32));
+
+	/* Mark internal command */
+	tw_dev->srb[request_id] = NULL;
+
+	/* Now post the command */
+	twa_post_command_packet(tw_dev, request_id);
+} /* End twa_aen_sync_time() */
+
+/* This function will allocate memory and check if it is correctly aligned */
+static int twa_allocate_memory(TW_Device_Extension *tw_dev, int size, int which)
+{
+	int i;
+	dma_addr_t dma_handle;
+	unsigned long *cpu_addr;
+	int retval = 1;
+
+	cpu_addr = pci_alloc_consistent(tw_dev->tw_pci_dev, size*TW_Q_LENGTH, &dma_handle);
+	if (!cpu_addr) {
+		TW_PRINTK(tw_dev->host, TW_DRIVER, 0x5, "Memory allocation failed");
+		goto out;
+	}
+
+	if ((unsigned long)cpu_addr % (TW_ALIGNMENT_9000)) {
+		TW_PRINTK(tw_dev->host, TW_DRIVER, 0x6, "Failed to allocate correctly aligned memory");
+		pci_free_consistent(tw_dev->tw_pci_dev, size*TW_Q_LENGTH, cpu_addr, dma_handle);
+		goto out;
+	}
+
+	memset(cpu_addr, 0, size*TW_Q_LENGTH);
+
+	for (i = 0; i < TW_Q_LENGTH; i++) {
+		switch(which) {
+		case 0:
+			tw_dev->command_packet_phys[i] = dma_handle+(i*size);
+			tw_dev->command_packet_virt[i] = (unsigned long *)((unsigned char *)cpu_addr + (i*size));
+			break;
+		case 1:
+			tw_dev->generic_buffer_phys[i] = dma_handle+(i*size);
+			tw_dev->generic_buffer_virt[i] = (unsigned long *)((unsigned char *)cpu_addr + (i*size));
+			break;
+		}
+	}
+	retval = 0;
+out:
+	return retval;
+} /* End twa_allocate_memory() */
+
+/* This function will check the status register for unexpected bits */
+static int twa_check_bits(u32 status_reg_value)
+{
+	int retval = 1;
+
+	if ((status_reg_value & TW_STATUS_EXPECTED_BITS) != TW_STATUS_EXPECTED_BITS)
+		goto out;
+	if ((status_reg_value & TW_STATUS_UNEXPECTED_BITS) != 0)
+		goto out;
+
+	retval = 0;
+out:
+	return retval;
+} /* End twa_check_bits() */
+
+/* This function will check the srl and decide if we are compatible  */
+static int twa_check_srl(TW_Device_Extension *tw_dev, int *flashed)
+{
+	int retval = 1;
+	unsigned short fw_on_ctlr_srl = 0, fw_on_ctlr_arch_id = 0;
+	unsigned short fw_on_ctlr_branch = 0, fw_on_ctlr_build = 0;
+	u32 init_connect_result = 0;
+
+	if (twa_initconnection(tw_dev, TW_INIT_MESSAGE_CREDITS, TW_EXTENDED_INIT_CONNECT, TW_CURRENT_FW_SRL, TW_9000_ARCH_ID, TW_CURRENT_FW_BRANCH, TW_CURRENT_FW_BUILD, &fw_on_ctlr_srl, &fw_on_ctlr_arch_id, &fw_on_ctlr_branch, &fw_on_ctlr_build, &init_connect_result)) {
+		TW_PRINTK(tw_dev->host, TW_DRIVER, 0x7, "Initconnection failed while checking SRL");
+		goto out;
+	}
+
+	tw_dev->working_srl = TW_CURRENT_FW_SRL;
+	tw_dev->working_branch = TW_CURRENT_FW_BRANCH;
+	tw_dev->working_build = TW_CURRENT_FW_BUILD;
+
+	/* Try base mode compatibility */
+	if (!(init_connect_result & TW_CTLR_FW_COMPATIBLE)) {
+		if (twa_initconnection(tw_dev, TW_INIT_MESSAGE_CREDITS, TW_EXTENDED_INIT_CONNECT, TW_BASE_FW_SRL, TW_9000_ARCH_ID, TW_BASE_FW_BRANCH, TW_BASE_FW_BUILD, &fw_on_ctlr_srl, &fw_on_ctlr_arch_id, &fw_on_ctlr_branch, &fw_on_ctlr_build, &init_connect_result)) {
+			TW_PRINTK(tw_dev->host, TW_DRIVER, 0xa, "Initconnection (base mode) failed while checking SRL");
+			goto out;
+		}
+		if (!(init_connect_result & TW_CTLR_FW_COMPATIBLE)) {
+			if (TW_CURRENT_FW_SRL > fw_on_ctlr_srl) {
+				TW_PRINTK(tw_dev->host, TW_DRIVER, 0x32, "Firmware and driver incompatibility: please upgrade firmware");
+			} else {
+				TW_PRINTK(tw_dev->host, TW_DRIVER, 0x33, "Firmware and driver incompatibility: please upgrade driver");
+			}
+			goto out;
+		}
+		tw_dev->working_srl = TW_BASE_FW_SRL;
+		tw_dev->working_branch = TW_BASE_FW_BRANCH;
+		tw_dev->working_build = TW_BASE_FW_BUILD;
+	}
+	retval = 0;
+out:
+	return retval;
+} /* End twa_check_srl() */
+
+/* This function handles ioctl for the character device */
+static int twa_chrdev_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg)
+{
+	long timeout;
+	unsigned long *cpu_addr, data_buffer_length_adjusted = 0, flags = 0;
+	dma_addr_t dma_handle;
+	int request_id = 0;
+	unsigned int sequence_id = 0;
+	unsigned char event_index, start_index;
+	TW_Ioctl_Driver_Command driver_command;
+	TW_Ioctl_Buf_Apache *tw_ioctl;
+	TW_Lock *tw_lock;
+	TW_Command_Full *full_command_packet;
+	TW_Compatibility_Info *tw_compat_info;
+	TW_Event *event;
+	struct timeval current_time;
+	u32 current_time_ms;
+	TW_Device_Extension *tw_dev = twa_device_extension_list[MINOR(inode->i_rdev)];
+	int retval = TW_IOCTL_ERROR_OS_EFAULT;
+
+	/* Only let one of these through at a time */
+	if (down_interruptible(&tw_dev->ioctl_sem)) {
+		retval = TW_IOCTL_ERROR_OS_EINTR;
+		goto out;
+	}
+
+	/* First copy down the driver command */
+	if (copy_from_user(&driver_command, (void *)arg, sizeof(TW_Ioctl_Driver_Command)))
+		goto out2;
+
+	/* Check data buffer size */
+	if (driver_command.buffer_length > TW_MAX_SECTORS * 512) {
+		retval = TW_IOCTL_ERROR_OS_EINVAL;
+		goto out2;
+	}
+
+	/* Hardware can only do multiple of 512 byte transfers */
+	data_buffer_length_adjusted = (driver_command.buffer_length + 511) & ~511;
+
+	/* Now allocate ioctl buf memory */
+	cpu_addr = pci_alloc_consistent(tw_dev->tw_pci_dev, data_buffer_length_adjusted+sizeof(TW_Ioctl_Buf_Apache) - 1, &dma_handle);
+	if (!cpu_addr) {
+		retval = TW_IOCTL_ERROR_OS_ENOMEM;
+		goto out2;
+	}
+
+	tw_ioctl = (TW_Ioctl_Buf_Apache *)cpu_addr;
+
+	/* Now copy down the entire ioctl */
+	if (copy_from_user(tw_ioctl, (void *)arg, driver_command.buffer_length + sizeof(TW_Ioctl_Buf_Apache) - 1))
+		goto out3;
+
+	/* See which ioctl we are doing */
+	switch (cmd) {
+	case TW_IOCTL_FIRMWARE_PASS_THROUGH:
+		spin_lock_irqsave(tw_dev->host->host_lock, flags);
+		twa_get_request_id(tw_dev, &request_id);
+
+		/* Flag internal command */
+		tw_dev->srb[request_id] = 0;
+
+		/* Flag chrdev ioctl */
+		tw_dev->chrdev_request_id = request_id;
+
+		full_command_packet = &tw_ioctl->firmware_command;
+
+		/* Load request id and sglist for both command types */
+		twa_load_sgl(full_command_packet, request_id, dma_handle, data_buffer_length_adjusted);
+
+		memcpy(tw_dev->command_packet_virt[request_id], &(tw_ioctl->firmware_command), sizeof(TW_Command_Full));
+
+		/* Now post the command packet to the controller */
+		twa_post_command_packet(tw_dev, request_id);
+		spin_unlock_irqrestore(tw_dev->host->host_lock, flags);
+
+		timeout = TW_IOCTL_CHRDEV_TIMEOUT*HZ;
+
+		/* Now wait for command to complete */
+		timeout = wait_event_interruptible_timeout(tw_dev->ioctl_wqueue, tw_dev->chrdev_request_id == TW_IOCTL_CHRDEV_FREE, timeout);
+
+		/* Check if we timed out, got a signal, or didn't get
+                   an interrupt */
+		if ((timeout <= 0) && (tw_dev->chrdev_request_id != TW_IOCTL_CHRDEV_FREE)) {
+			/* Now we need to reset the board */
+			if (timeout == TW_IOCTL_ERROR_OS_ERESTARTSYS) {
+				retval = timeout;
+			} else {
+				printk(KERN_WARNING "3w-9xxx: scsi%d: WARNING: (0x%02X:0x%04X): Character ioctl (0x%x) timed out, resetting card.\n", tw_dev->host->host_no, TW_DRIVER, 0xc, cmd);
+				retval = TW_IOCTL_ERROR_OS_EIO;
+			}
+			spin_lock_irqsave(tw_dev->host->host_lock, flags);
+			tw_dev->state[request_id] = TW_S_COMPLETED;
+			twa_free_request_id(tw_dev, request_id);
+			tw_dev->posted_request_count--;
+			twa_reset_device_extension(tw_dev);
+			spin_unlock_irqrestore(tw_dev->host->host_lock, flags);
+			goto out3;
+		}
+
+		/* Now copy in the command packet response */
+		memcpy(&(tw_ioctl->firmware_command), tw_dev->command_packet_virt[request_id], sizeof(TW_Command_Full));
+		
+		/* Now complete the io */
+		spin_lock_irqsave(tw_dev->host->host_lock, flags);
+		tw_dev->posted_request_count--;
+		tw_dev->state[request_id] = TW_S_COMPLETED;
+		twa_free_request_id(tw_dev, request_id);
+		spin_unlock_irqrestore(tw_dev->host->host_lock, flags);
+		break;
+	case TW_IOCTL_GET_COMPATIBILITY_INFO:
+		tw_ioctl->driver_command.status = 0;
+		/* Copy compatiblity struct into ioctl data buffer */
+		tw_compat_info = (TW_Compatibility_Info *)tw_ioctl->data_buffer;
+		strncpy(tw_compat_info->driver_version, twa_driver_version, strlen(twa_driver_version));
+		tw_compat_info->working_srl = tw_dev->working_srl;
+		tw_compat_info->working_branch = tw_dev->working_branch;
+		tw_compat_info->working_build = tw_dev->working_build;
+		break;
+	case TW_IOCTL_GET_LAST_EVENT:
+		if (tw_dev->event_queue_wrapped) {
+			if (tw_dev->aen_clobber) {
+				tw_ioctl->driver_command.status = TW_IOCTL_ERROR_STATUS_AEN_CLOBBER;
+				tw_dev->aen_clobber = 0;
+			} else
+				tw_ioctl->driver_command.status = 0;
+		} else {
+			if (!tw_dev->error_index) {
+				tw_ioctl->driver_command.status = TW_IOCTL_ERROR_STATUS_NO_MORE_EVENTS;
+				break;
+			}
+			tw_ioctl->driver_command.status = 0;
+		}
+		event_index = (tw_dev->error_index - 1 + TW_Q_LENGTH) % TW_Q_LENGTH;
+		memcpy(tw_ioctl->data_buffer, tw_dev->event_queue[event_index], sizeof(TW_Event));
+		tw_dev->event_queue[event_index]->retrieved = TW_AEN_RETRIEVED;
+		break;
+	case TW_IOCTL_GET_FIRST_EVENT:
+		if (tw_dev->event_queue_wrapped) {
+			if (tw_dev->aen_clobber) {
+				tw_ioctl->driver_command.status = TW_IOCTL_ERROR_STATUS_AEN_CLOBBER;
+				tw_dev->aen_clobber = 0;
+			} else 
+				tw_ioctl->driver_command.status = 0;
+			event_index = tw_dev->error_index;
+		} else {
+			if (!tw_dev->error_index) {
+				tw_ioctl->driver_command.status = TW_IOCTL_ERROR_STATUS_NO_MORE_EVENTS;
+				break;
+			}
+			tw_ioctl->driver_command.status = 0;
+			event_index = 0;
+		}
+		memcpy(tw_ioctl->data_buffer, tw_dev->event_queue[event_index], sizeof(TW_Event));
+		tw_dev->event_queue[event_index]->retrieved = TW_AEN_RETRIEVED;
+		break;
+	case TW_IOCTL_GET_NEXT_EVENT:
+		event = (TW_Event *)tw_ioctl->data_buffer;
+		sequence_id = event->sequence_id;
+		tw_ioctl->driver_command.status = 0;
+
+		if (tw_dev->event_queue_wrapped) {
+			if (tw_dev->aen_clobber) {
+				tw_ioctl->driver_command.status = TW_IOCTL_ERROR_STATUS_AEN_CLOBBER;
+				tw_dev->aen_clobber = 0;
+			}
+			start_index = tw_dev->error_index;
+		} else {
+			if (!tw_dev->error_index) {
+				tw_ioctl->driver_command.status = TW_IOCTL_ERROR_STATUS_NO_MORE_EVENTS;
+				break;
+			}
+			start_index = 0;
+		}
+		event_index = (start_index + sequence_id - tw_dev->event_queue[start_index]->sequence_id + 1) % TW_Q_LENGTH;
+
+		if (!(tw_dev->event_queue[event_index]->sequence_id > sequence_id)) {
+			if (tw_ioctl->driver_command.status == TW_IOCTL_ERROR_STATUS_AEN_CLOBBER)
+				tw_dev->aen_clobber = 1;
+			tw_ioctl->driver_command.status = TW_IOCTL_ERROR_STATUS_NO_MORE_EVENTS;
+			break;
+		}
+		memcpy(tw_ioctl->data_buffer, tw_dev->event_queue[event_index], sizeof(TW_Event));
+		tw_dev->event_queue[event_index]->retrieved = TW_AEN_RETRIEVED;
+		break;
+	case TW_IOCTL_GET_PREVIOUS_EVENT:
+		event = (TW_Event *)tw_ioctl->data_buffer;
+		sequence_id = event->sequence_id;
+		tw_ioctl->driver_command.status = 0;
+
+		if (tw_dev->event_queue_wrapped) {
+			if (tw_dev->aen_clobber) {
+				tw_ioctl->driver_command.status = TW_IOCTL_ERROR_STATUS_AEN_CLOBBER;
+				tw_dev->aen_clobber = 0;
+			}
+			start_index = tw_dev->error_index;
+		} else {
+			if (!tw_dev->error_index) {
+				tw_ioctl->driver_command.status = TW_IOCTL_ERROR_STATUS_NO_MORE_EVENTS;
+				break;
+			}
+			start_index = 0;
+		}
+		event_index = (start_index + sequence_id - tw_dev->event_queue[start_index]->sequence_id - 1) % TW_Q_LENGTH;
+
+		if (!(tw_dev->event_queue[event_index]->sequence_id < sequence_id)) {
+			if (tw_ioctl->driver_command.status == TW_IOCTL_ERROR_STATUS_AEN_CLOBBER)
+				tw_dev->aen_clobber = 1;
+			tw_ioctl->driver_command.status = TW_IOCTL_ERROR_STATUS_NO_MORE_EVENTS;
+			break;
+		}
+		memcpy(tw_ioctl->data_buffer, tw_dev->event_queue[event_index], sizeof(TW_Event));
+		tw_dev->event_queue[event_index]->retrieved = TW_AEN_RETRIEVED;
+		break;
+	case TW_IOCTL_GET_LOCK:
+		tw_lock = (TW_Lock *)tw_ioctl->data_buffer;
+		do_gettimeofday(&current_time);
+		current_time_ms = (current_time.tv_sec * 1000) + (current_time.tv_usec / 1000);
+
+		if ((tw_lock->force_flag == 1) || (tw_dev->ioctl_sem_lock == 0) || (current_time_ms >= tw_dev->ioctl_msec)) {
+			tw_dev->ioctl_sem_lock = 1;
+			tw_dev->ioctl_msec = current_time_ms + tw_lock->timeout_msec;
+			tw_ioctl->driver_command.status = 0;
+			tw_lock->time_remaining_msec = tw_lock->timeout_msec;
+		} else {
+			tw_ioctl->driver_command.status = TW_IOCTL_ERROR_STATUS_LOCKED;
+			tw_lock->time_remaining_msec = tw_dev->ioctl_msec - current_time_ms;
+		}
+		break;
+	case TW_IOCTL_RELEASE_LOCK:
+		if (tw_dev->ioctl_sem_lock == 1) {
+			tw_dev->ioctl_sem_lock = 0;
+			tw_ioctl->driver_command.status = 0;
+		} else {
+			tw_ioctl->driver_command.status = TW_IOCTL_ERROR_STATUS_NOT_LOCKED;
+		}
+		break;
+	default:
+		retval = TW_IOCTL_ERROR_OS_ENOTTY;
+		goto out3;
+	}
+
+	/* Now copy the entire response to userspace */
+	if (copy_to_user((void *)arg, tw_ioctl, sizeof(TW_Ioctl_Buf_Apache) + driver_command.buffer_length - 1) == 0)
+		retval = 0;
+out3:
+	/* Now free ioctl buf memory */
+	pci_free_consistent(tw_dev->tw_pci_dev, data_buffer_length_adjusted+sizeof(TW_Ioctl_Buf_Apache) - 1, cpu_addr, dma_handle);
+out2:
+	up(&tw_dev->ioctl_sem);
+out:
+	return retval;
+} /* End twa_chrdev_ioctl() */
+
+/* This function handles open for the character device */
+static int twa_chrdev_open(struct inode *inode, struct file *file)
+{
+	unsigned int minor_number;
+	int retval = TW_IOCTL_ERROR_OS_ENODEV;
+
+	minor_number = MINOR(inode->i_rdev);
+	if (minor_number >= twa_device_extension_count)
+		goto out;
+	retval = 0;
+out:
+	return retval;
+} /* End twa_chrdev_open() */
+
+/* This function handles close for the character device */
+static int twa_chrdev_release(struct inode *inode, struct file *file)
+{
+	return 0;
+} /* End twa_chrdev_release() */
+
+/* This function is called by twa_scsi_proc_info */
+static int twa_copy_info(TW_Info *info, char *fmt, ...)
+{
+	va_list args;
+	char buf[81];
+	int len;
+
+	va_start(args, fmt);
+	len = vsprintf(buf, fmt, args);
+	va_end(args);
+	twa_copy_mem_info(info, buf, len);
+
+	return len;
+} /* End twa_copy_info() */
+
+/* This function is called by twa_scsi_proc_info */
+static void twa_copy_mem_info(TW_Info *info, char *data, int len)
+{
+	if (info->position + len > info->length)
+		len = info->length - info->position;
+
+	if (info->position + len < info->offset) {
+		info->position += len;
+		return;
+	}
+	if (info->position < info->offset) {
+		data += (info->offset - info->position);
+		len  -= (info->offset - info->position);
+	}
+	if (len > 0) {
+		memcpy(info->buffer + info->position, data, len);
+		info->position += len;
+	}
+} /* End twa_copy_mem_info() */
+
+/* This function will print readable messages from status register errors */
+static int twa_decode_bits(TW_Device_Extension *tw_dev, u32 status_reg_value)
+{
+	int retval = 1;
+
+	/* Check for various error conditions and handle them appropriately */
+	if (status_reg_value & TW_STATUS_PCI_PARITY_ERROR) {
+		TW_PRINTK(tw_dev->host, TW_DRIVER, 0xc, "PCI Parity Error: clearing");
+		writel(TW_CONTROL_CLEAR_PARITY_ERROR, TW_CONTROL_REG_ADDR(tw_dev));
+	}
+
+	if (status_reg_value & TW_STATUS_PCI_ABORT) {
+		TW_PRINTK(tw_dev->host, TW_DRIVER, 0xd, "PCI Abort: clearing");
+		writel(TW_CONTROL_CLEAR_PCI_ABORT, TW_CONTROL_REG_ADDR(tw_dev));
+		pci_write_config_word(tw_dev->tw_pci_dev, PCI_STATUS, TW_PCI_CLEAR_PCI_ABORT);
+	}
+
+	if (status_reg_value & TW_STATUS_QUEUE_ERROR) {
+		TW_PRINTK(tw_dev->host, TW_DRIVER, 0xe, "Controller Queue Error: clearing");
+		writel(TW_CONTROL_CLEAR_QUEUE_ERROR, TW_CONTROL_REG_ADDR(tw_dev));
+	}
+
+	if (status_reg_value & TW_STATUS_SBUF_WRITE_ERROR) {
+		TW_PRINTK(tw_dev->host, TW_DRIVER, 0xf, "SBUF Write Error: clearing");
+		writel(TW_CONTROL_CLEAR_SBUF_WRITE_ERROR, TW_CONTROL_REG_ADDR(tw_dev));
+	}
+
+	if (status_reg_value & TW_STATUS_MICROCONTROLLER_ERROR) {
+		if (tw_dev->reset_print == 0) {
+			TW_PRINTK(tw_dev->host, TW_DRIVER, 0x10, "Microcontroller Error: clearing");
+			tw_dev->reset_print = 1;
+		}
+		goto out;
+	}
+	retval = 0;
+out:
+	return retval;
+} /* End twa_decode_bits() */
+
+/* This function will empty the response queue */
+static int twa_empty_response_queue(TW_Device_Extension *tw_dev)
+{
+	u32 status_reg_value, response_que_value;
+	int count = 0, retval = 1;
+
+	status_reg_value = readl(TW_STATUS_REG_ADDR(tw_dev));
+
+	while (((status_reg_value & TW_STATUS_RESPONSE_QUEUE_EMPTY) == 0) && (count < TW_MAX_RESPONSE_DRAIN)) {
+		response_que_value = readl(TW_RESPONSE_QUEUE_REG_ADDR(tw_dev));
+		status_reg_value = readl(TW_STATUS_REG_ADDR(tw_dev));
+		count++;
+	}
+	if (count == TW_MAX_RESPONSE_DRAIN)
+		goto out;
+
+	retval = 0;
+out:
+	return retval;
+} /* End twa_empty_response_queue() */
+
+/* This function passes sense keys from firmware to scsi layer */
+static int twa_fill_sense(TW_Device_Extension *tw_dev, int request_id, int copy_sense, int print_host)
+{
+	TW_Command_Full *full_command_packet;
+	unsigned short error;
+	int retval = 1;
+
+	full_command_packet = (TW_Command_Full *)tw_dev->command_packet_virt[request_id];
+	/* Don't print error for Logical unit not supported during rollcall */
+	error = full_command_packet->header.status_block.error;
+	if ((error != TW_ERROR_LOGICAL_UNIT_NOT_SUPPORTED) && (error != TW_ERROR_UNIT_OFFLINE)) {
+		if (print_host)
+			printk(KERN_WARNING "3w-9xxx: scsi%d: ERROR: (0x%02X:0x%04X): %s:%s.\n", tw_dev->host->host_no, TW_MESSAGE_SOURCE_CONTROLLER_ERROR, full_command_packet->header.status_block.error, twa_string_lookup(twa_error_table, full_command_packet->header.status_block.error), full_command_packet->header.err_specific_desc);
+		else
+			printk(KERN_WARNING "3w-9xxx: ERROR: (0x%02X:0x%04X): %s:%s.\n", TW_MESSAGE_SOURCE_CONTROLLER_ERROR, full_command_packet->header.status_block.error, twa_string_lookup(twa_error_table, full_command_packet->header.status_block.error), full_command_packet->header.err_specific_desc);
+	}
+
+	if (copy_sense) {
+		memcpy(tw_dev->srb[request_id]->sense_buffer, full_command_packet->header.sense_data, TW_SENSE_DATA_LENGTH);
+		tw_dev->srb[request_id]->result = (full_command_packet->command.newcommand.status << 1);
+		retval = TW_ISR_DONT_RESULT;
+		goto out;
+	}
+	retval = 0;
+out:
+	return retval;
+} /* End twa_fill_sense() */
+
+/* This function will free up device extension resources */
+static void twa_free_device_extension(TW_Device_Extension *tw_dev)
+{
+	if (tw_dev->command_packet_virt[0])
+		pci_free_consistent(tw_dev->tw_pci_dev, sizeof(TW_Command_Full)*TW_Q_LENGTH, tw_dev->command_packet_virt[0], tw_dev->command_packet_phys[0]);
+
+	if (tw_dev->generic_buffer_virt[0])
+		pci_free_consistent(tw_dev->tw_pci_dev, TW_SECTOR_SIZE*TW_Q_LENGTH, tw_dev->generic_buffer_virt[0], tw_dev->generic_buffer_phys[0]);
+
+	if (tw_dev->event_queue[0])
+		kfree(tw_dev->event_queue[0]);
+} /* End twa_free_device_extension() */
+
+/* This function will free a request id */
+static void twa_free_request_id(TW_Device_Extension *tw_dev, int request_id)
+{
+	tw_dev->free_queue[tw_dev->free_tail] = request_id;
+	tw_dev->state[request_id] = TW_S_FINISHED;
+	tw_dev->free_tail = (tw_dev->free_tail + 1) % TW_Q_LENGTH;
+} /* End twa_free_request_id() */
+
+/* This function will get parameter table entires from the firmware */
+static void *twa_get_param(TW_Device_Extension *tw_dev, int request_id, int table_id, int parameter_id, int parameter_size_bytes)
+{
+	TW_Command_Full *full_command_packet;
+	TW_Command *command_packet;
+	TW_Param_Apache *param;
+	unsigned long param_value;
+	void *retval = NULL;
+
+	/* Setup the command packet */
+	full_command_packet = (TW_Command_Full *)tw_dev->command_packet_virt[request_id];
+	memset(full_command_packet, 0, sizeof(TW_Command_Full));
+	command_packet = &full_command_packet->command.oldcommand;
+
+	command_packet->byte0_offset.opcode      = TW_OP_GET_PARAM;
+	command_packet->byte0_offset.sgl_offset  = 2;
+	command_packet->size              = TW_COMMAND_SIZE;
+	command_packet->request_id        = request_id;
+	command_packet->byte6_offset.block_count = 1;
+
+	/* Now setup the param */
+	param = (TW_Param_Apache *)tw_dev->generic_buffer_virt[request_id];
+	memset(param, 0, TW_SECTOR_SIZE);
+	param->table_id = table_id | 0x8000;
+	param->parameter_id = parameter_id;
+	param->parameter_size_bytes = parameter_size_bytes;
+	param_value = tw_dev->generic_buffer_phys[request_id];
+
+	command_packet->byte8_offset.param.sgl[0].address = param_value;
+	command_packet->byte8_offset.param.sgl[0].length = TW_SECTOR_SIZE;
+
+	/* Post the command packet to the board */
+	twa_post_command_packet(tw_dev, request_id);
+
+	/* Poll for completion */
+	if (twa_poll_response(tw_dev, request_id, 30))
+		TW_PRINTK(tw_dev->host, TW_DRIVER, 0x13, "No valid response during get param")
+	else
+		retval = (void *)&(param->data[0]);
+
+	tw_dev->posted_request_count--;
+	tw_dev->state[request_id] = TW_S_INITIAL;
+
+	return retval;
+} /* End twa_get_param() */
+
+/* This function will assign an available request id */
+static void twa_get_request_id(TW_Device_Extension *tw_dev, int *request_id)
+{
+	*request_id = tw_dev->free_queue[tw_dev->free_head];
+	tw_dev->free_head = (tw_dev->free_head + 1) % TW_Q_LENGTH;
+	tw_dev->state[*request_id] = TW_S_STARTED;
+} /* End twa_get_request_id() */
+
+/* Clean shutdown routine */
+static int twa_halt(struct notifier_block *nb, ulong event, void *buf)
+{
+	unsigned int i;
+
+	for (i = 0; i < twa_device_extension_count; i++) {
+		if (twa_device_extension_list[i]->online == 1) {
+			printk(KERN_WARNING "3w-9xxx: Shutting down card %d.\n", i);
+			twa_shutdown_device(twa_device_extension_list[i]);
+			twa_device_extension_list[i]->online = 0;
+		}
+	}
+	unregister_reboot_notifier(&twa_notifier);
+
+	return NOTIFY_OK;
+} /* End twa_halt() */
+
+/* This function will send an initconnection command to controller */
+static int twa_initconnection(TW_Device_Extension *tw_dev, int message_credits,
+ 			      u32 set_features, unsigned short current_fw_srl, 
+			      unsigned short current_fw_arch_id, 
+			      unsigned short current_fw_branch, 
+			      unsigned short current_fw_build, 
+			      unsigned short *fw_on_ctlr_srl, 
+			      unsigned short *fw_on_ctlr_arch_id, 
+			      unsigned short *fw_on_ctlr_branch, 
+			      unsigned short *fw_on_ctlr_build, 
+			      u32 *init_connect_result)
+{
+	TW_Command_Full *full_command_packet;
+	TW_Initconnect *tw_initconnect;
+	int request_id = 0, retval = 1;
+
+	/* Initialize InitConnection command packet */
+	full_command_packet = (TW_Command_Full *)tw_dev->command_packet_virt[request_id];
+	memset(full_command_packet, 0, sizeof(TW_Command_Full));
+	full_command_packet->header.header_desc.size_header = 128;
+	
+	tw_initconnect = (TW_Initconnect *)&full_command_packet->command.oldcommand;
+	tw_initconnect->opcode = TW_OP_INIT_CONNECTION;
+
+	tw_initconnect->request_id = request_id;
+	tw_initconnect->message_credits = message_credits;
+	tw_initconnect->features = set_features;
+#if BITS_PER_LONG > 32
+	/* Turn on 64-bit sgl support */
+	tw_initconnect->features |= 1;
+#endif
+
+	if (set_features & TW_EXTENDED_INIT_CONNECT)
+	{
+		tw_initconnect->size = TW_INIT_COMMAND_PACKET_SIZE_EXTENDED;
+		tw_initconnect->fw_srl = current_fw_srl;
+		tw_initconnect->fw_arch_id = current_fw_arch_id;
+		tw_initconnect->fw_branch = current_fw_branch;
+		tw_initconnect->fw_build = current_fw_build;
+	} else 
+		tw_initconnect->size = TW_INIT_COMMAND_PACKET_SIZE;
+
+	/* Send command packet to the board */
+	twa_post_command_packet(tw_dev, request_id);
+
+	/* Poll for completion */
+	if (twa_poll_response(tw_dev, request_id, 30)) {
+		TW_PRINTK(tw_dev->host, TW_DRIVER, 0x15, "No valid response during init connection");
+	} else {
+		if (set_features & TW_EXTENDED_INIT_CONNECT) {
+			*fw_on_ctlr_srl = tw_initconnect->fw_srl;
+			*fw_on_ctlr_arch_id = tw_initconnect->fw_arch_id;
+			*fw_on_ctlr_branch = tw_initconnect->fw_branch;
+			*fw_on_ctlr_build = tw_initconnect->fw_build;
+			*init_connect_result = tw_initconnect->result;
+		}
+		retval = 0;
+	}
+
+	tw_dev->posted_request_count--;
+	tw_dev->state[request_id] = TW_S_INITIAL;
+
+	return retval;
+} /* End twa_initconnection() */
+
+/* This function will initialize the fields of a device extension */
+static int twa_initialize_device_extension(TW_Device_Extension *tw_dev)
+{
+	int i, retval = 1;
+
+	/* Initialize command packet buffers */
+	if (twa_allocate_memory(tw_dev, sizeof(TW_Command_Full), 0)) {
+		TW_PRINTK(tw_dev->host, TW_DRIVER, 0x16, "Command packet memory allocation failed");
+		goto out;
+	}
+
+	/* Initialize generic buffer */
+	if (twa_allocate_memory(tw_dev, TW_SECTOR_SIZE, 1)) {
+		TW_PRINTK(tw_dev->host, TW_DRIVER, 0x17, "Generic memory allocation failed");
+		goto out;
+	}
+
+	/* Allocate event info space */
+	tw_dev->event_queue[0] = kmalloc(sizeof(TW_Event) * TW_Q_LENGTH, GFP_ATOMIC);
+	if (!tw_dev->event_queue[0]) {
+		TW_PRINTK(tw_dev->host, TW_DRIVER, 0x18, "Event info memory allocation failed");
+		goto out;
+	}
+
+	memset(tw_dev->event_queue[0], 0, sizeof(TW_Event) * TW_Q_LENGTH);
+
+	for (i = 0; i < TW_Q_LENGTH; i++) {
+		tw_dev->event_queue[i] = (TW_Event *)((unsigned char *)tw_dev->event_queue[0] + (i * sizeof(TW_Event)));
+		tw_dev->free_queue[i] = i;
+		tw_dev->state[i] = TW_S_INITIAL;
+	}
+
+	tw_dev->pending_head = TW_Q_START;
+	tw_dev->pending_tail = TW_Q_START;
+	tw_dev->free_head = TW_Q_START;
+	tw_dev->free_tail = TW_Q_START;
+	tw_dev->error_sequence_id = 1;
+	tw_dev->chrdev_request_id = TW_IOCTL_CHRDEV_FREE;
+	retval = 0;
+out:
+	return retval;
+} /* End twa_initialize_device_extension() */
+
+/* This function is the interrupt service routine */
+static irqreturn_t twa_interrupt(int irq, void *dev_instance, struct pt_regs *regs)
+{
+	int request_id, error = 0;
+	u32 status_reg_value;
+	TW_Response_Queue response_que;
+	unsigned long flags = 0;
+	TW_Command_Full *full_command_packet;
+	TW_Command *command_packet;
+	TW_Device_Extension *tw_dev = (TW_Device_Extension *)dev_instance;
+	int handled = 0;
+
+	/* See if we are already running on another processor */
+	if (test_and_set_bit(TW_IN_INTR, &tw_dev->flags))
+		return IRQ_NONE;
+
+	/* Get the per adapter lock */
+	spin_lock_irqsave(tw_dev->host->host_lock, flags);
+
+	/* See if the interrupt matches this instance */
+	if (tw_dev->tw_pci_dev->irq == (unsigned int)irq) {
+
+		handled = 1;
+
+		/* Read the registers */
+		status_reg_value = readl(TW_STATUS_REG_ADDR(tw_dev));
+
+		/* Check if this is our interrupt, otherwise bail */
+		if (!(status_reg_value & TW_STATUS_VALID_INTERRUPT))
+			goto twa_interrupt_bail;
+
+		/* Check controller for errors */
+		if (twa_check_bits(status_reg_value)) {
+			if (twa_decode_bits(tw_dev, status_reg_value)) {
+				TW_CLEAR_ALL_INTERRUPTS(tw_dev);
+				goto twa_interrupt_bail;
+			}
+		}
+
+		/* Handle host interrupt */
+		if (status_reg_value & TW_STATUS_HOST_INTERRUPT)
+			TW_CLEAR_HOST_INTERRUPT(tw_dev);
+
+		/* Handle attention interrupt */
+		if (status_reg_value & TW_STATUS_ATTENTION_INTERRUPT) {
+			TW_CLEAR_ATTENTION_INTERRUPT(tw_dev);
+			if (!(test_and_set_bit(TW_IN_ATTENTION_LOOP, &tw_dev->flags))) {
+				twa_get_request_id(tw_dev, &request_id);
+
+				error = twa_aen_read_queue(tw_dev, request_id);
+				if (error) {
+					tw_dev->state[request_id] = TW_S_COMPLETED;
+					twa_free_request_id(tw_dev, request_id);
+					clear_bit(TW_IN_ATTENTION_LOOP, &tw_dev->flags);
+				}
+			}
+		}
+
+		/* Handle command interrupt */
+		if (status_reg_value & TW_STATUS_COMMAND_INTERRUPT) {
+			TW_MASK_COMMAND_INTERRUPT(tw_dev);
+			/* Drain as many pending commands as we can */
+			while (tw_dev->pending_request_count > 0) {
+				request_id = tw_dev->pending_queue[tw_dev->pending_head];
+				if (tw_dev->state[request_id] != TW_S_PENDING) {
+					TW_PRINTK(tw_dev->host, TW_DRIVER, 0x19, "Found request id that wasn't pending");
+					TW_CLEAR_ALL_INTERRUPTS(tw_dev);
+					goto twa_interrupt_bail;
+				}
+				if (twa_post_command_packet(tw_dev, request_id)==0) {
+					tw_dev->pending_head = (tw_dev->pending_head + 1) % TW_Q_LENGTH;
+					tw_dev->pending_request_count--;
+				} else {
+					/* If we get here, we will continue re-posting on the next command interrupt */
+					break;
+				}
+			}
+		}
+
+		/* Handle response interrupt */
+		if (status_reg_value & TW_STATUS_RESPONSE_INTERRUPT) {
+
+			/* Drain the response queue from the board */
+			while ((status_reg_value & TW_STATUS_RESPONSE_QUEUE_EMPTY) == 0) {
+				/* Complete the response */
+				response_que.value = readl(TW_RESPONSE_QUEUE_REG_ADDR(tw_dev));
+				request_id = response_que.u.response_id;
+				full_command_packet = (TW_Command_Full *)tw_dev->command_packet_virt[request_id];
+				error = 0;
+				command_packet = &full_command_packet->command.oldcommand;
+				/* Check for command packet errors */
+				if (full_command_packet->command.newcommand.status != 0) {
+					if (tw_dev->srb[request_id] != 0) {
+						error = twa_fill_sense(tw_dev, request_id, 1, 1);
+					} else {
+						/* Skip ioctl error prints */
+						if (request_id != tw_dev->chrdev_request_id) {
+							error = twa_fill_sense(tw_dev, request_id, 0, 1);
+						}
+					}
+				}
+
+				/* Check for correct state */
+				if (tw_dev->state[request_id] != TW_S_POSTED) {
+					if (tw_dev->srb[request_id] != 0) {
+						TW_PRINTK(tw_dev->host, TW_DRIVER, 0x1a, "Received a request id that wasn't posted");
+					        TW_CLEAR_ALL_INTERRUPTS(tw_dev);
+						goto twa_interrupt_bail;
+					}
+				}
+
+				/* Check for internal command completion */
+				if (tw_dev->srb[request_id] == 0) {
+					if (request_id != tw_dev->chrdev_request_id) {
+						if (twa_aen_complete(tw_dev, request_id))
+							TW_PRINTK(tw_dev->host, TW_DRIVER, 0x1b, "Error completing AEN during attention interrupt");
+					} else {
+						tw_dev->chrdev_request_id = TW_IOCTL_CHRDEV_FREE;
+						wake_up(&tw_dev->ioctl_wqueue);
+					}
+				} else {
+					twa_scsiop_execute_scsi_complete(tw_dev, request_id);
+					/* If no error command was a success */
+					if (error == 0) {
+						tw_dev->srb[request_id]->result = (DID_OK << 16);
+					}
+
+					/* If error, command failed */
+					if (error == 1) {
+						/* Ask for a host reset */
+						tw_dev->srb[request_id]->result = (DID_OK << 16) | (CHECK_CONDITION << 1);
+					}
+
+					/* Now complete the io */
+					tw_dev->state[request_id] = TW_S_COMPLETED;
+					twa_free_request_id(tw_dev, request_id);
+					tw_dev->posted_request_count--;
+					tw_dev->srb[request_id]->scsi_done(tw_dev->srb[request_id]);
+					twa_unmap_scsi_data(tw_dev, request_id);
+				}
+
+				/* Check for valid status after each drain */
+				status_reg_value = readl(TW_STATUS_REG_ADDR(tw_dev));
+				if (twa_check_bits(status_reg_value)) {
+					if (twa_decode_bits(tw_dev, status_reg_value)) {
+						TW_CLEAR_ALL_INTERRUPTS(tw_dev);
+						goto twa_interrupt_bail;
+					}
+				}
+			}
+		}
+	}
+twa_interrupt_bail:
+	spin_unlock_irqrestore(tw_dev->host->host_lock, flags);
+	clear_bit(TW_IN_INTR, &tw_dev->flags);
+	return IRQ_RETVAL(handled);
+} /* End twa_interrupt() */
+
+/* This function will load the request id and various sgls for ioctls */
+static void twa_load_sgl(TW_Command_Full *full_command_packet, int request_id, dma_addr_t dma_handle, int length)
+{
+	TW_Command *oldcommand;
+	TW_Command_Apache *newcommand;
+	TW_SG_Entry *sgl;
+
+	if (full_command_packet->command.newcommand.command.opcode == TW_OP_EXECUTE_SCSI) {
+		newcommand = &full_command_packet->command.newcommand;
+		newcommand->request_id = request_id;
+		newcommand->sg_list[0].address = dma_handle + sizeof(TW_Ioctl_Buf_Apache) - 1;
+		newcommand->sg_list[0].length = length;
+	} else {
+		oldcommand = &full_command_packet->command.oldcommand;
+		oldcommand->request_id = request_id;
+
+		if (oldcommand->byte0_offset.sgl_offset) {
+			/* Load the sg list */
+			sgl = (TW_SG_Entry *)((u32 *)oldcommand+oldcommand->byte0_offset.sgl_offset);
+			sgl->address = dma_handle + sizeof(TW_Ioctl_Buf_Apache) - 1;
+			sgl->length = length;
+		}
+	}
+} /* End twa_load_sgl() */
+
+/* This function will perform a pci-dma mapping for a scatter gather list */
+static int twa_map_scsi_sg_data(TW_Device_Extension *tw_dev, int request_id)
+{
+	int use_sg;
+	Scsi_Cmnd *cmd = tw_dev->srb[request_id];
+	int dma_dir = scsi_to_pci_dma_dir(cmd->sc_data_direction);
+	struct pci_dev *pdev = tw_dev->tw_pci_dev;
+	int retval = 0;
+
+	if (cmd->use_sg == 0)
+		goto out;
+
+	use_sg = pci_map_sg(pdev, cmd->buffer, cmd->use_sg, dma_dir);
+
+	if (use_sg == 0) {
+		TW_PRINTK(tw_dev->host, TW_DRIVER, 0x1c, "Failed to map scatter gather list");
+		goto out;
+	}
+
+	cmd->SCp.phase = 2;
+	cmd->SCp.have_data_in = use_sg;
+	retval = use_sg;
+out:
+	return retval;
+} /* End twa_map_scsi_sg_data() */
+
+/* This function will perform a pci-dma map for a single buffer */
+static dma_addr_t twa_map_scsi_single_data(TW_Device_Extension *tw_dev, int request_id)
+{
+	dma_addr_t mapping;
+	Scsi_Cmnd *cmd = tw_dev->srb[request_id];
+	int dma_dir = scsi_to_pci_dma_dir(cmd->sc_data_direction);
+	struct pci_dev *pdev = tw_dev->tw_pci_dev;
+	int retval = 0;
+
+	if (cmd->request_bufflen == 0) {
+		retval = 0;
+		goto out;
+	}
+
+	mapping = pci_map_single(pdev, cmd->request_buffer, cmd->request_bufflen, dma_dir);
+
+	if (mapping == 0) {
+		TW_PRINTK(tw_dev->host, TW_DRIVER, 0x1d, "Failed to map page");
+		goto out;
+	}
+
+	cmd->SCp.phase = 1;
+	cmd->SCp.have_data_in = mapping;
+	retval = mapping;
+out:
+	return retval;
+} /* End twa_map_scsi_single_data() */
+
+/* This function will poll for a response interrupt of a request */
+static int twa_poll_response(TW_Device_Extension *tw_dev, int request_id, int seconds)
+{
+	int retval = 1, found = 0, response_request_id;
+	TW_Response_Queue response_queue;
+	TW_Command_Full *full_command_packet = (TW_Command_Full *)tw_dev->command_packet_virt[request_id];
+
+	if (twa_poll_status_gone(tw_dev, TW_STATUS_RESPONSE_QUEUE_EMPTY, seconds) == 0) {
+		response_queue.value = readl(TW_RESPONSE_QUEUE_REG_ADDR(tw_dev));
+		response_request_id = (unsigned char)response_queue.u.response_id;
+		if (request_id != response_request_id) {
+			TW_PRINTK(tw_dev->host, TW_DRIVER, 0x1e, "Found unexpected request id while polling for response");
+			goto out;
+		}
+		if (full_command_packet->command.newcommand.command.opcode == TW_OP_EXECUTE_SCSI) {
+			if (full_command_packet->command.newcommand.status != 0) {
+				/* bad response */
+				twa_fill_sense(tw_dev, request_id, 0, 0);
+				goto out;
+			}
+			found = 1;
+		} else {
+			if (full_command_packet->command.oldcommand.status != 0) {
+				/* bad response */
+				twa_fill_sense(tw_dev, request_id, 0, 0);
+				goto out;
+			}
+			found = 1;
+		}
+	}
+
+	if (found)
+		retval = 0;
+out:
+	return retval;
+} /* End twa_poll_response() */
+
+/* This function will poll the status register for a flag */
+static int twa_poll_status(TW_Device_Extension *tw_dev, u32 flag, int seconds)
+{
+	u32 status_reg_value; 
+	unsigned long before;
+	int retval = 1;
+
+	status_reg_value = readl(TW_STATUS_REG_ADDR(tw_dev));
+	before = jiffies;
+
+	if (twa_check_bits(status_reg_value))
+		twa_decode_bits(tw_dev, status_reg_value);
+
+	while ((status_reg_value & flag) != flag) {
+		status_reg_value = readl(TW_STATUS_REG_ADDR(tw_dev));
+
+		if (twa_check_bits(status_reg_value))
+			twa_decode_bits(tw_dev, status_reg_value);
+
+		if (time_after(jiffies, before + HZ * seconds))
+			goto out;
+
+		mdelay(5);
+	}
+	retval = 0;
+out:
+	return retval;
+} /* End twa_poll_status() */
+
+/* This function will poll the status register for disappearance of a flag */
+static int twa_poll_status_gone(TW_Device_Extension *tw_dev, u32 flag, int seconds)
+{
+	u32 status_reg_value;
+	unsigned long before;
+	int retval = 1;
+
+	status_reg_value = readl(TW_STATUS_REG_ADDR(tw_dev));
+	before = jiffies;
+
+	if (twa_check_bits(status_reg_value))
+		twa_decode_bits(tw_dev, status_reg_value);
+
+	while ((status_reg_value & flag) != 0) {
+		status_reg_value = readl(TW_STATUS_REG_ADDR(tw_dev));
+		if (twa_check_bits(status_reg_value))
+			twa_decode_bits(tw_dev, status_reg_value);
+
+		if (time_after(jiffies, before + HZ * seconds))
+			goto out;
+
+		mdelay(5);
+	}
+	retval = 0;
+out:
+	return retval;
+} /* End twa_poll_status_gone() */
+
+/* This function will attempt to post a command packet to the board */
+static int twa_post_command_packet(TW_Device_Extension *tw_dev, int request_id)
+{
+	u32 status_reg_value;
+	unsigned long command_que_value;
+	int retval = 1;
+
+	command_que_value = tw_dev->command_packet_phys[request_id];
+	status_reg_value = readl(TW_STATUS_REG_ADDR(tw_dev));
+
+	if (twa_check_bits(status_reg_value))
+		twa_decode_bits(tw_dev, status_reg_value);
+
+	if (((tw_dev->pending_request_count > 0) && (tw_dev->state[request_id] != TW_S_PENDING)) || (status_reg_value & TW_STATUS_COMMAND_QUEUE_FULL)) {
+		/* Couldn't post the command packet, so we do it later */
+		if (tw_dev->state[request_id] != TW_S_PENDING) {
+			tw_dev->state[request_id] = TW_S_PENDING;
+			tw_dev->pending_request_count++;
+			if (tw_dev->pending_request_count > tw_dev->max_pending_request_count) {
+				tw_dev->max_pending_request_count = tw_dev->pending_request_count;
+			}
+			tw_dev->pending_queue[tw_dev->pending_tail] = request_id;
+			tw_dev->pending_tail = (tw_dev->pending_tail + 1) % TW_Q_LENGTH;
+		}
+		TW_UNMASK_COMMAND_INTERRUPT(tw_dev);
+		goto out;
+	} else {
+		/* We successfully posted the command packet */
+#if BITS_PER_LONG > 32
+		writeq(TW_COMMAND_OFFSET + command_que_value, TW_COMMAND_QUEUE_REG_ADDR(tw_dev));
+#else
+		writel(TW_COMMAND_OFFSET + command_que_value, TW_COMMAND_QUEUE_REG_ADDR(tw_dev));
+#endif
+		tw_dev->state[request_id] = TW_S_POSTED;
+		tw_dev->posted_request_count++;
+		if (tw_dev->posted_request_count > tw_dev->max_posted_request_count) {
+			tw_dev->max_posted_request_count = tw_dev->posted_request_count;
+		}
+	}
+	retval = 0;
+out:
+	return retval;
+} /* End twa_post_command_packet() */
+
+/* This function will reset a device extension */
+static int twa_reset_device_extension(TW_Device_Extension *tw_dev)
+{
+	int i = 0;
+	int retval = 1;
+
+	/* Abort all requests that are in progress */
+	for (i = 0; i < TW_Q_LENGTH; i++) {
+		if ((tw_dev->state[i] != TW_S_FINISHED) &&
+		    (tw_dev->state[i] != TW_S_INITIAL) &&
+		    (tw_dev->state[i] != TW_S_COMPLETED)) {
+			if (tw_dev->srb[i]) {
+				tw_dev->srb[i]->result = (DID_RESET << 16);
+				tw_dev->srb[i]->scsi_done(tw_dev->srb[i]);
+				twa_unmap_scsi_data(tw_dev, i);
+			}
+		}
+	}
+
+	/* Reset queues and counts */
+	for (i = 0; i < TW_Q_LENGTH; i++) {
+		tw_dev->free_queue[i] = i;
+		tw_dev->state[i] = TW_S_INITIAL;
+	}
+	tw_dev->free_head = TW_Q_START;
+	tw_dev->free_tail = TW_Q_START;
+	tw_dev->posted_request_count = 0;
+	tw_dev->pending_request_count = 0;
+	tw_dev->pending_head = TW_Q_START;
+	tw_dev->pending_tail = TW_Q_START;
+	tw_dev->reset_print = 0;
+	tw_dev->chrdev_request_id = TW_IOCTL_CHRDEV_FREE;
+	tw_dev->flags = 0;
+
+	TW_DISABLE_INTERRUPTS(tw_dev);
+
+	if (twa_reset_sequence(tw_dev, 1))
+		goto out;
+
+        TW_ENABLE_AND_CLEAR_INTERRUPTS(tw_dev);
+
+	retval = 0;
+out:
+	return retval;
+} /* End twa_reset_device_extension() */
+
+/* This function will reset a controller */
+static int twa_reset_sequence(TW_Device_Extension *tw_dev, int soft_reset)
+{
+	int tries = 0, retval = 1, flashed = 0, do_soft_reset = soft_reset;
+
+	while (tries < TW_MAX_RESET_TRIES) {
+		if (do_soft_reset)
+			TW_SOFT_RESET(tw_dev);
+
+		/* Make sure controller is in a good state */
+		if (twa_poll_status(tw_dev, TW_STATUS_MICROCONTROLLER_READY | (do_soft_reset == 1 ? TW_STATUS_ATTENTION_INTERRUPT : 0), 30)) {
+			TW_PRINTK(tw_dev->host, TW_DRIVER, 0x1f, "Microcontroller not ready during reset sequence");
+			do_soft_reset = 1;
+			tries++;
+			continue;
+		}
+
+		/* Empty response queue */
+		if (twa_empty_response_queue(tw_dev)) {
+			TW_PRINTK(tw_dev->host, TW_DRIVER, 0x20, "Response queue empty failed during reset sequence");
+			do_soft_reset = 1;
+			tries++;
+			continue;
+		}
+
+		flashed = 0;
+
+		/* Check for compatibility/flash */
+		if (twa_check_srl(tw_dev, &flashed)) {
+			TW_PRINTK(tw_dev->host, TW_DRIVER, 0x21, "Compatibility check failed during reset sequence");
+			do_soft_reset = 1;
+			tries++;
+			continue;
+		} else {
+			if (flashed) {
+				tries++;
+				continue;
+			}
+		}
+
+		/* Drain the AEN queue */
+		if (twa_aen_drain_queue(tw_dev, soft_reset)) {
+			TW_PRINTK(tw_dev->host, TW_DRIVER, 0x22, "AEN drain failed during reset sequence");
+			do_soft_reset = 1;
+			tries++;
+			continue;
+		}
+
+		/* If we got here, controller is in a good state */
+		retval = 0;
+		goto out;
+	}
+out:
+	return retval;
+} /* End twa_reset_sequence() */
+
+/* This funciton returns unit geometry in cylinders/heads/sectors */
+static int twa_scsi_biosparam(struct scsi_device *sdev, struct block_device *bdev, sector_t capacity, int geom[])
+{
+	int heads, sectors, cylinders;
+	TW_Device_Extension *tw_dev;
+
+	tw_dev = (TW_Device_Extension *)sdev->host->hostdata;
+
+	heads = 64;
+	sectors = 32;
+	cylinders = (unsigned long)capacity / (heads * sectors);
+
+	if (capacity >= 0x200000) {
+		heads = 255;
+		sectors = 63;
+		cylinders = (unsigned long)capacity / (heads * sectors);
+	}
+
+	geom[0] = heads;
+	geom[1] = sectors;
+	geom[2] = cylinders;
+
+	return 0;
+} /* End twa_scsi_biosparam() */
+
+/* This function will find and initialize all cards */
+static int twa_scsi_detect(Scsi_Host_Template *tw_host)
+{
+	int numcards = 0, i;
+	struct Scsi_Host *host = NULL;
+	TW_Device_Extension *tw_dev, *tw_dev2;
+	struct pci_dev *tw_pci_dev=NULL;
+	u32 mem_addr, mem_len;
+	u16 device[TW_NUMDEVICES] = { TW_DEVICE_ID_9000 };
+
+	printk(KERN_WARNING "3ware 9000 Storage Controller device driver for Linux v%s.\n", twa_driver_version);
+
+	for (i = 0; i < TW_NUMDEVICES; i++) {
+		while ((tw_pci_dev = pci_find_device(TW_VENDOR_ID, device[i], tw_pci_dev))) {
+			if (pci_enable_device(tw_pci_dev))
+				continue;
+
+			if (pci_set_dma_mask(tw_pci_dev, TW_DMA_MASK)) {
+				TW_PRINTK(host, TW_DRIVER, 0x23, "Failed to set dma mask");
+				continue;
+			}
+
+			/* Prepare temporary device extension */
+			tw_dev=(TW_Device_Extension *)kmalloc(sizeof(TW_Device_Extension), GFP_ATOMIC);
+			if (!tw_dev) {
+				TW_PRINTK(host, TW_DRIVER, 0x24, "Failed to allocate memory for device extension");
+				continue;
+			}
+			memset(tw_dev, 0, sizeof(TW_Device_Extension));
+
+			/* Save pci_dev struct to device extension */
+			tw_dev->tw_pci_dev = tw_pci_dev;
+
+			if (twa_initialize_device_extension(tw_dev)) {
+				TW_PRINTK(tw_dev->host, TW_DRIVER, 0x25, "Failed to initialize device extension");
+				twa_free_device_extension(tw_dev);
+				kfree(tw_dev);
+				continue;
+			}
+
+			mem_addr = pci_resource_start(tw_pci_dev, 1);
+			mem_len = pci_resource_len(tw_pci_dev, 1);
+
+			/* Make sure that mem region isn't already taken */
+			if (check_mem_region(mem_addr, mem_len)) {
+				TW_PRINTK(tw_dev->host, TW_DRIVER, 0x26, "Failed to get mem region");
+				twa_free_device_extension(tw_dev);
+				kfree(tw_dev);
+				continue;
+			}
+
+			/* Reserve the mem address space */
+			request_mem_region(mem_addr, mem_len, TW_DEVICE_NAME);
+
+			/* Save base address */
+			tw_dev->base_addr = ioremap(mem_addr, PAGE_SIZE);
+
+			/* Disable interrupts on the card */
+			TW_DISABLE_INTERRUPTS(tw_dev);
+
+			/* Initialize the card */
+			if (twa_reset_sequence(tw_dev, 0)) {
+				release_mem_region(mem_addr, mem_len);
+				twa_free_device_extension(tw_dev);
+				kfree(tw_dev);
+				continue;
+			}
+
+			/* Set card status as online */
+			tw_dev->online = 1;
+
+			/* Register the card with the kernel SCSI layer */
+			host = scsi_register(tw_host, sizeof(TW_Device_Extension));
+			if (!host) {
+				TW_PRINTK(tw_dev->host, TW_DRIVER, 0x27, "scsi register failed");
+				release_mem_region(mem_addr, mem_len);
+				twa_free_device_extension(tw_dev);
+				kfree(tw_dev);
+				continue;
+			}
+
+			/* Set max target id's */
+			host->max_id = TW_MAX_UNITS;
+
+			/* Set max cdb size in bytes */
+			host->max_cmd_len = TW_MAX_CDB_LEN;
+
+			scsi_set_device(host, &tw_pci_dev->dev);
+
+			printk(KERN_WARNING "3w-9xxx: scsi%d: Found a 3ware 9000 Storage Controller at 0x%x, IRQ: %d.\n", host->host_no, mem_addr, tw_pci_dev->irq);
+			printk(KERN_WARNING "3w-9xxx: scsi%d: Firmware %s, BIOS %s, Ports: %d.\n", host->host_no, (char *)twa_get_param(tw_dev, 0, TW_VERSION_TABLE, TW_PARAM_FWVER, TW_PARAM_FWVER_LENGTH), (char *)twa_get_param(tw_dev, 1, TW_VERSION_TABLE, TW_PARAM_BIOSVER, TW_PARAM_BIOSVER_LENGTH), *(int *)twa_get_param(tw_dev, 2, TW_INFORMATION_TABLE, TW_PARAM_PORTCOUNT, TW_PARAM_PORTCOUNT_LENGTH));
+			if (host->hostdata) {
+				tw_dev2 = (TW_Device_Extension *)host->hostdata;
+				memcpy(tw_dev2, tw_dev, sizeof(TW_Device_Extension));
+				/* Need to init the sem/wqueue after the copy */
+				init_MUTEX(&tw_dev2->ioctl_sem);
+				init_waitqueue_head(&tw_dev2->ioctl_wqueue);
+
+				twa_device_extension_list[twa_device_extension_count] = tw_dev2;
+				numcards++;
+				twa_device_extension_count = numcards;
+				tw_dev2->host = host;
+			} else {
+				TW_PRINTK(tw_dev->host, TW_DRIVER, 0x28, "Bad scsi host data");
+				scsi_unregister(host);
+				release_mem_region(mem_addr, mem_len);
+				twa_free_device_extension(tw_dev);
+				kfree(tw_dev);
+				continue;
+			}
+
+			/* Now setup the interrupt handler */
+		        if (twa_setup_irq(tw_dev2)) {
+				scsi_unregister(host);
+				release_mem_region(mem_addr, mem_len);
+				twa_free_device_extension(tw_dev);
+				kfree(tw_dev);
+				numcards--;
+				continue;
+			}
+
+			/* Re-enable interrupts on the card */
+			TW_ENABLE_AND_CLEAR_INTERRUPTS(tw_dev2);
+
+			/* Free the temporary device extension */
+			if (tw_dev)
+				kfree(tw_dev);
+		}
+	}
+
+	if (numcards == 0) {
+		printk(KERN_WARNING "3w-9xxx: No cards successfully initialized.\n");
+	} else {
+		register_reboot_notifier(&twa_notifier);
+		if ((twa_major = register_chrdev (0, "twa", &twa_fops)) < 0)
+			TW_PRINTK(host, TW_DRIVER, 0x29, "Failed to register character device");
+	}
+	return numcards;
+} /* End twa_scsi_detect() */
+
+/* This is the new scsi eh abort function */
+static int twa_scsi_eh_abort(Scsi_Cmnd *SCpnt)
+{
+	int i;
+	TW_Device_Extension *tw_dev = NULL;
+	int retval = FAILED;
+
+	tw_dev = (TW_Device_Extension *)SCpnt->device->host->hostdata;
+
+	tw_dev->num_aborts++;
+
+	/* If we find any IO's in process, we have to reset the card */
+	for (i = 0; i < TW_Q_LENGTH; i++) {
+		if ((tw_dev->state[i] != TW_S_FINISHED) && (tw_dev->state[i] != TW_S_INITIAL)) {
+			printk(KERN_WARNING "3w-9xxx: scsi%d: WARNING: (0x%02X:0x%04X): Unit #%d: Command (0x%x) timed out, resetting card.\n", tw_dev->host->host_no, TW_DRIVER, 0x2c, SCpnt->device->id, SCpnt->cmnd[0]);
+			if (twa_reset_device_extension(tw_dev)) {
+				TW_PRINTK(tw_dev->host, TW_DRIVER, 0x2a, "Controller reset failed during scsi abort");
+				goto out;
+			}
+			break;
+		}
+	}
+	retval = SUCCESS;
+out:
+	return retval;
+} /* End twa_scsi_eh_abort() */
+
+/* This is the new scsi eh reset function */
+static int twa_scsi_eh_reset(Scsi_Cmnd *SCpnt)
+{
+	TW_Device_Extension *tw_dev = NULL;
+	int retval = FAILED;
+
+	tw_dev = (TW_Device_Extension *)SCpnt->device->host->hostdata;
+
+	tw_dev->num_resets++;
+
+	printk(KERN_WARNING "3w-9xxx: scsi%d: SCSI host reset started.\n", tw_dev->host->host_no);
+
+	/* Now reset the card and some of the device extension data */
+	if (twa_reset_device_extension(tw_dev)) {
+		TW_PRINTK(tw_dev->host, TW_DRIVER, 0x2b, "Controller reset failed during scsi host reset");
+		goto out;
+	}
+	printk(KERN_WARNING "3w-9xxx: scsi%d: SCSI host reset succeeded.\n", tw_dev->host->host_no);
+	retval = SUCCESS;
+out:
+	return retval;
+} /* End twa_scsi_eh_reset() */
+
+/* This function handles input and output from /proc/scsi/3w-9xxx/x */
+static int twa_scsi_proc_info(struct Scsi_Host *shost, char *buffer, char **start, off_t offset, int length, int inout)
+{
+	TW_Device_Extension *tw_dev = NULL;
+	TW_Info info;
+	unsigned int i;
+	int retval = -EINVAL;
+
+	/* Find the correct device extension */
+	for (i = 0; i < twa_device_extension_count; i++) 
+		if (twa_device_extension_list[i]->host->host_no == shost->host_no)
+			tw_dev = twa_device_extension_list[i];
+	if (!tw_dev) {
+		TW_PRINTK(tw_dev->host, TW_DRIVER, 0x2c, "No device extension for proc operation");
+		goto out;
+	}
+
+	info.buffer = buffer;
+	info.length = length;
+	info.offset = offset;
+	info.position = 0;
+
+	if (!inout) {
+		/* Read */
+		if (start) {
+			*start = buffer;
+		}
+		twa_copy_info(&info, "scsi%d: 3ware 9000 Storage Controller\n", shost->host_no);
+		twa_copy_info(&info, "Driver version: %s\n", twa_driver_version);
+		twa_copy_info(&info, "Current commands posted:       %4d\n", tw_dev->posted_request_count);
+		twa_copy_info(&info, "Max commands posted:           %4d\n", tw_dev->max_posted_request_count);
+		twa_copy_info(&info, "Current pending commands:      %4d\n", tw_dev->pending_request_count);
+		twa_copy_info(&info, "Max pending commands:          %4d\n", tw_dev->max_pending_request_count);
+		twa_copy_info(&info, "Last sgl length:               %4d\n", tw_dev->sgl_entries);
+		twa_copy_info(&info, "Max sgl length:                %4d\n", tw_dev->max_sgl_entries);
+		twa_copy_info(&info, "Last sector count:             %4d\n", tw_dev->sector_count);
+		twa_copy_info(&info, "Max sector count:              %4d\n", tw_dev->max_sector_count);
+		twa_copy_info(&info, "SCSI Host Resets:              %4d\n", tw_dev->num_resets);
+		twa_copy_info(&info, "SCSI Aborts/Timeouts:          %4d\n", tw_dev->num_aborts);
+		twa_copy_info(&info, "AEN's:                         %4d\n", tw_dev->aen_count);
+	}
+	if (info.position > info.offset) {
+		retval = info.position - info.offset;
+	} else {
+		retval = 0;
+	}
+out:
+	return retval;
+} /* End twa_scsi_proc_info() */
+
+/* This is the main scsi queue function to handle scsi opcodes */
+static int twa_scsi_queue(Scsi_Cmnd *SCpnt, void (*done)(Scsi_Cmnd *))
+{
+	int request_id;
+	TW_Device_Extension *tw_dev = (TW_Device_Extension *)SCpnt->device->host->hostdata;
+
+	/* Skip lun and channel probes */
+	if ((SCpnt->device->lun != 0) || (SCpnt->device->channel != 0)) {
+		SCpnt->result = (DID_BAD_TARGET << 16);
+		done(SCpnt);
+		goto out;
+	}
+
+	/* Save done function into Scsi_Cmnd struct */
+	SCpnt->scsi_done = done;
+		
+	/* Get a free request id */
+	twa_get_request_id(tw_dev, &request_id);
+
+	/* Save the scsi command for use by the ISR */
+	tw_dev->srb[request_id] = SCpnt;
+
+	/* Initialize phase to zero */
+	SCpnt->SCp.phase = 0;
+
+	if (twa_scsiop_execute_scsi(tw_dev, request_id, NULL, 0, NULL)) {
+		tw_dev->state[request_id] = TW_S_COMPLETED;
+		twa_free_request_id(tw_dev, request_id);
+		SCpnt->result = (DID_ERROR << 16);
+		done(SCpnt);
+	}
+out:
+	return 0;
+} /* End twa_scsi_queue() */
+
+/* This function will release the resources on an rmmod call */
+static int twa_scsi_release(struct Scsi_Host *tw_host)
+{
+	TW_Device_Extension *tw_dev = (TW_Device_Extension *)tw_host->hostdata;
+
+	twa_halt(0, 0, 0);
+
+	/* Free up the mem region */
+	release_mem_region(pci_resource_start(tw_dev->tw_pci_dev, 1), pci_resource_len(tw_dev->tw_pci_dev, 1));
+
+	/* Free up the IRQ */
+	free_irq(tw_dev->tw_pci_dev->irq, tw_dev);
+
+	/* Unregister character device */
+	if (twa_major >= 0) {
+		unregister_chrdev(twa_major, "twa");
+		twa_major = -1;
+	}
+
+	/* Free up device extension resources */
+	twa_free_device_extension(tw_dev);
+
+	/* Tell kernel scsi-layer we are gone */
+	scsi_unregister(tw_host);
+
+	return 0;
+} /* End twa_scsi_release() */
+
+/* This function hands scsi cdb's to the firmware */
+static int twa_scsiop_execute_scsi(TW_Device_Extension *tw_dev, int request_id, char *cdb, int use_sg, TW_SG_Apache *sglistarg)
+{
+	TW_Command_Full *full_command_packet;
+	TW_Command_Apache *command_packet;
+	u32 num_sectors = 0x0;
+	int i, sg_count;
+	Scsi_Cmnd *srb = NULL;
+	struct scatterlist *sglist = NULL;
+	u32 buffaddr = 0x0;
+	int retval = 1;
+
+	if (tw_dev->srb[request_id]) {
+		if (tw_dev->srb[request_id]->request_buffer) {
+			sglist = (struct scatterlist *)tw_dev->srb[request_id]->request_buffer;
+		}
+		srb = tw_dev->srb[request_id];
+	}
+
+	/* Initialize command packet */
+	full_command_packet = (TW_Command_Full *)tw_dev->command_packet_virt[request_id];
+	full_command_packet->header.header_desc.size_header = 128;
+	full_command_packet->header.status_block.error = 0;
+	full_command_packet->header.status_block.substatus_block.severity = 0;
+
+	command_packet = &full_command_packet->command.newcommand;
+	command_packet->status = 0;
+	command_packet->command.opcode = TW_OP_EXECUTE_SCSI;
+
+	/* We forced 16 byte cdb use earlier */
+	if (!cdb)
+		memcpy(command_packet->cdb, srb->cmnd, TW_MAX_CDB_LEN);
+	else
+		memcpy(command_packet->cdb, cdb, TW_MAX_CDB_LEN);
+
+	if (srb)
+		command_packet->unit = srb->device->id;
+	else
+		command_packet->unit = 0;
+
+	command_packet->request_id = request_id;
+	command_packet->sgl_offset = 16;
+
+	if (!sglistarg) {
+		/* Map sglist from scsi layer to cmd packet */
+		if (tw_dev->srb[request_id]->use_sg == 0) {
+			if (tw_dev->srb[request_id]->request_bufflen < TW_MIN_SGL_LENGTH) {
+				command_packet->sg_list[0].address = tw_dev->generic_buffer_phys[request_id];
+				command_packet->sg_list[0].length = TW_MIN_SGL_LENGTH;
+			} else {
+				buffaddr = twa_map_scsi_single_data(tw_dev, request_id);
+				if (buffaddr == 0)
+					goto out;
+
+				command_packet->sg_list[0].address = buffaddr;
+				command_packet->sg_list[0].length = tw_dev->srb[request_id]->request_bufflen;
+			}
+			command_packet->sgl_entries = 1;
+
+			if (command_packet->sg_list[0].address & TW_ALIGNMENT_9000_SGL) {
+				TW_PRINTK(tw_dev->host, TW_DRIVER, 0x2d, "Found unaligned address during execute scsi");
+				goto out;
+			}
+		}
+
+		if (tw_dev->srb[request_id]->use_sg > 0) {
+			sg_count = twa_map_scsi_sg_data(tw_dev, request_id);
+			if (sg_count == 0)
+				goto out;
+
+			for (i = 0; i < sg_count; i++) {
+				command_packet->sg_list[i].address = sg_dma_address(&sglist[i]);
+				command_packet->sg_list[i].length = sg_dma_len(&sglist[i]);
+				if (command_packet->sg_list[i].address & TW_ALIGNMENT_9000_SGL) {
+					TW_PRINTK(tw_dev->host, TW_DRIVER, 0x2e, "Found unaligned sgl address during execute scsi");
+					goto out;
+				}
+			}
+			command_packet->sgl_entries = tw_dev->srb[request_id]->use_sg;
+		}
+	} else {
+		/* Internal cdb post */
+		for (i = 0; i < use_sg; i++) {
+			command_packet->sg_list[i].address = sglistarg[i].address;
+			command_packet->sg_list[i].length = sglistarg[i].length;
+			if (command_packet->sg_list[i].address & TW_ALIGNMENT_9000_SGL) {
+				TW_PRINTK(tw_dev->host, TW_DRIVER, 0x2f, "Found unaligned sgl address during internal post");
+				goto out;
+			}
+		}
+		command_packet->sgl_entries = use_sg;
+	}
+
+	if (srb) {
+		if (srb->cmnd[0] == READ_6 || srb->cmnd[0] == WRITE_6)
+			num_sectors = (u32)srb->cmnd[4];
+
+		if (srb->cmnd[0] == READ_10 || srb->cmnd[0] == WRITE_10)
+			num_sectors = (u32)srb->cmnd[8] | ((u32)srb->cmnd[7] << 8);
+	}
+
+	/* Update sector statistic */
+	tw_dev->sector_count = num_sectors;
+	if (tw_dev->sector_count > tw_dev->max_sector_count)
+		tw_dev->max_sector_count = tw_dev->sector_count;
+
+	/* Update SG statistics */
+	if (srb) {
+		tw_dev->sgl_entries = tw_dev->srb[request_id]->use_sg;
+		if (tw_dev->sgl_entries > tw_dev->max_sgl_entries)
+			tw_dev->max_sgl_entries = tw_dev->sgl_entries;
+	}
+
+	/* Now post the command to the board */
+	twa_post_command_packet(tw_dev, request_id);
+	retval = 0;
+out:
+	return retval;
+} /* End twa_scsiop_execute_scsi() */
+
+/* This function completes an execute scsi operation */
+static void twa_scsiop_execute_scsi_complete(TW_Device_Extension *tw_dev, int request_id)
+{
+	/* Copy the response if too small */
+	if ((tw_dev->srb[request_id]->request_buffer) && (tw_dev->srb[request_id]->request_bufflen < TW_MIN_SGL_LENGTH)) {
+		memcpy(tw_dev->srb[request_id]->request_buffer, tw_dev->generic_buffer_virt[request_id], tw_dev->srb[request_id]->request_bufflen);
+	}
+} /* End twa_scsiop_execute_scsi_complete() */
+
+/* This function will setup the interrupt handler */
+static int twa_setup_irq(TW_Device_Extension *tw_dev)
+{
+	char *device = TW_DEVICE_NAME;
+	int retval = 1;
+
+	if (request_irq(tw_dev->tw_pci_dev->irq, twa_interrupt, SA_SHIRQ, device, tw_dev) < 0) {
+		TW_PRINTK(tw_dev->host, TW_DRIVER, 0x30, "Error requesting IRQ");
+		goto out;
+	}
+	retval = 0;
+out:
+	return retval;
+} /* End twa_setup_irq() */
+
+/* This function tells the controller to shut down */
+static void twa_shutdown_device(TW_Device_Extension *tw_dev)
+{
+	/* Disable interrupts */
+	TW_DISABLE_INTERRUPTS(tw_dev);
+
+	/* Tell the card we are shutting down */
+	if (twa_initconnection(tw_dev, 1, 0, 0, 0, 0, 0, NULL, NULL, NULL, NULL, NULL)) {
+		TW_PRINTK(tw_dev->host, TW_DRIVER, 0x31, "Connection shutdown failed");
+	} else {
+		printk(KERN_WARNING "3w-9xxx: Shutdown complete.\n");
+	}
+
+	/* Clear all interrupts just before exit */
+	TW_ENABLE_AND_CLEAR_INTERRUPTS(tw_dev);
+} /* End twa_shutdown_device() */
+
+/* This function will configure individual target parameters */
+static int twa_slave_configure(Scsi_Device *SDptr)
+{
+	int max_cmds;
+
+	if (cmds_per_lun) {
+		max_cmds = cmds_per_lun;
+		if (max_cmds > TW_MAX_CMDS_PER_LUN)
+			max_cmds = TW_MAX_CMDS_PER_LUN;
+	} else {
+		max_cmds = TW_MAX_CMDS_PER_LUN;
+	}
+	scsi_adjust_queue_depth(SDptr, MSG_ORDERED_TAG, max_cmds);
+
+	return 0;
+} /* End twa_slave_configure */
+
+/* This function will look up a string */
+static char *twa_string_lookup(twa_message_type *table, unsigned int code)
+{
+	int index;
+
+	for (index = 0; ((code != table[index].code) &&
+		      (table[index].text != (char *)0)); index++);
+	return(table[index].text);
+} /* End twa_string_lookup() */
+
+/* This function will perform a pci-dma unmap */
+static void twa_unmap_scsi_data(TW_Device_Extension *tw_dev, int request_id)
+{
+	Scsi_Cmnd *cmd = tw_dev->srb[request_id];
+	int dma_dir = scsi_to_pci_dma_dir(cmd->sc_data_direction);
+	struct pci_dev *pdev = tw_dev->tw_pci_dev;
+
+	switch(cmd->SCp.phase) {
+	case 1:
+		pci_unmap_single(pdev, cmd->SCp.have_data_in, cmd->request_bufflen, dma_dir);
+		break;
+	case 2:
+		pci_unmap_sg(pdev, cmd->request_buffer, cmd->use_sg, dma_dir);
+		break;
+	}
+} /* End twa_unmap_scsi_data() */
+
+/* Now get things going */
+static Scsi_Host_Template driver_template = {
+	.proc_name		= "3w-9xxx",
+	.proc_info		= twa_scsi_proc_info,
+	.name			= "3ware 9000 Storage Controller",
+	.detect			= twa_scsi_detect,
+	.release		= twa_scsi_release,
+	.queuecommand		= twa_scsi_queue,
+	.eh_abort_handler	= twa_scsi_eh_abort,
+	.eh_host_reset_handler	= twa_scsi_eh_reset,
+	.bios_param		= twa_scsi_biosparam,
+	.slave_configure	= twa_slave_configure,
+	.can_queue		= TW_Q_LENGTH-2,
+	.this_id		= -1,
+	.sg_tablesize		= TW_APACHE_MAX_SGL_LENGTH,
+	.max_sectors		= TW_MAX_SECTORS,
+	.cmd_per_lun		= TW_MAX_CMDS_PER_LUN,
+	.use_clustering		= ENABLE_CLUSTERING,
+	.emulated		= 1
+};
+#include "scsi_module.c"
+


^ permalink raw reply	[flat|nested] 4+ messages in thread

* Re: [PATCH/RFC 1/2] 3ware 9000 SATA-RAID driver
  2004-05-22 18:19 [PATCH/RFC 1/2] 3ware 9000 SATA-RAID driver Adam Radford
@ 2004-05-23 10:59 ` hch
  0 siblings, 0 replies; 4+ messages in thread
From: hch @ 2004-05-23 10:59 UTC (permalink / raw)
  To: Adam Radford; +Cc: linux-scsi

> diff -Naur linux-2.6.6-mm4/drivers/pci/pci.ids linux-2.6.6-mm5/drivers/pci/pci.ids
> --- linux-2.6.6-mm4/drivers/pci/pci.ids	2004-05-21 16:35:20.000000000 -0700
> +++ linux-2.6.6-mm5/drivers/pci/pci.ids	2004-05-21 15:38:58.000000000 -0700
> @@ -5751,7 +5751,7 @@
>  13c1  3ware Inc
>  	1000  3ware ATA-RAID
>  	1001  3ware 7000-series ATA-RAID
> -	1002  3ware ATA-RAID
> +	1002  3ware 9000-series ATA-RAID
>  13c2  Technotrend Systemtechnik GmbH
>  13c3  Janz Computer AG
>  13c4  Phase Metrics

I think this file is managed via pci-ids.sf.net, please submit there.

> +
> +#include <linux/module.h>
> +
> +MODULE_AUTHOR ("AMCC");
> +#ifdef CONFIG_SMP
> +MODULE_DESCRIPTION ("3ware 9000 Storage Controller Linux Driver (SMP)");
> +#else
> +MODULE_DESCRIPTION ("3ware 9000 Storage Controller Linux Driver");
> +#endif
> +MODULE_LICENSE("GPL");

the ifdef SMP is bogus, also please move the MODULE_ macros below all
includes.

> +
> +#include <linux/kernel.h>
> +#include <linux/pci.h>
> +#include <linux/time.h>
> +#include <linux/proc_fs.h>
> +#include <linux/sched.h>
> +#include <linux/ioport.h>
> +#include <linux/blkdev.h>
> +#include <linux/hdreg.h>
> +#include <linux/string.h>
> +#include <linux/delay.h>
> +#include <linux/smp.h>

shouldn't be needed with a modern driver.

> +#include <linux/reboot.h>
> +#include <linux/spinlock.h>
> +#include <linux/interrupt.h>
> +#include <linux/moduleparam.h>
> +
> +#include <asm/errno.h>

never use asm/errno.h but always linux/errno.h

> +#include "scsi.h"
> +#include "hosts.h"

please don't include these anymore, use the headers from include/scsi/*.h,
this also involves some reshuffling like getting rid of the obsolete Scsi_Foo
typedefs and using the DMA_* constants instead of SCSI_DATA_*

> +
> +#include "3w-9xxx.h"
> +
> +/* Notifier block to get a notify on system shutdown/halt/reboot */
> +static struct notifier_block twa_notifier = {
> +	.notifier_call	= twa_halt,
> +	.next		= NULL,
> +	.priority	= 0
> +};

please usethe shutdown method in the pci_driver instead, see the megaraid
driver for an example.

> +/* Globals */
> +char *twa_driver_version="2.26.00.008";
> +TW_Device_Extension *twa_device_extension_list[TW_MAX_SLOT];

after the changes I'll suggest below this will only be used in the ioctl
code anymore, and by doing the lookup in ->open there and stuffing a pointer
into file->private_data you'll only need this in a slow-path, so a linked
list will be enough.

> +unsigned int twa_device_extension_count = 0;

No need to initialize to 0, .bss will do it for you.

> +static int twa_major = -1;
> +extern struct timezone sys_tz;
> +static int cmds_per_lun;

please mark all symbols in the driver static.

> +/* This function handles open for the character device */
> +static int twa_chrdev_open(struct inode *inode, struct file *file)
> +{
> +	unsigned int minor_number;
> +	int retval = TW_IOCTL_ERROR_OS_ENODEV;
> +
> +	minor_number = MINOR(inode->i_rdev);

2.6 has a nice iminor() helper for this.

> +/* This function handles close for the character device */
> +static int twa_chrdev_release(struct inode *inode, struct file *file)
> +{
> +	return 0;
> +} /* End twa_chrdev_release() */

no need to implement ->release at all if it doesn't do anything.

> +#if BITS_PER_LONG > 32
> +	/* Turn on 64-bit sgl support */
> +	tw_initconnect->features |= 1;
> +#endif

Don't you also need this on 32bit arches with >32bit physical
addressing, e.g. x86 with PAE?

> +/* This funciton returns unit geometry in cylinders/heads/sectors */
> +static int twa_scsi_biosparam(struct scsi_device *sdev, struct block_device *bdev, sector_t capacity, int geom[])
> +{
> +	int heads, sectors, cylinders;
> +	TW_Device_Extension *tw_dev;
> +
> +	tw_dev = (TW_Device_Extension *)sdev->host->hostdata;
> +
> +	heads = 64;
> +	sectors = 32;
> +	cylinders = (unsigned long)capacity / (heads * sectors);

with CONFIG_LBD set capacity will be a 64bit type on 32bit arches,
so you must use sector_div for it.

> +/* This function will find and initialize all cards */
> +static int twa_scsi_detect(Scsi_Host_Template *tw_host)
> +{
> +	int numcards = 0, i;
> +	struct Scsi_Host *host = NULL;
> +	TW_Device_Extension *tw_dev, *tw_dev2;
> +	struct pci_dev *tw_pci_dev=NULL;
> +	u32 mem_addr, mem_len;
> +	u16 device[TW_NUMDEVICES] = { TW_DEVICE_ID_9000 };
> +
> +	printk(KERN_WARNING "3ware 9000 Storage Controller device driver for Linux v%s.\n", twa_driver_version);
> +
> +	for (i = 0; i < TW_NUMDEVICES; i++) {
> +		while ((tw_pci_dev = pci_find_device(TW_VENDOR_ID, device[i], tw_pci_dev))) {

Please redo the probing useing a struct pci_driver and appropinquate
callbacks.  See e.g. megaraid or qla1280 for details.

> +/* This function handles input and output from /proc/scsi/3w-9xxx/x */
> +static int twa_scsi_proc_info(struct Scsi_Host *shost, char *buffer, char **start, off_t offset, int length, int inout)
> +{
> +	TW_Device_Extension *tw_dev = NULL;
> +	TW_Info info;
> +	unsigned int i;
> +	int retval = -EINVAL;
> +
> +	/* Find the correct device extension */
> +	for (i = 0; i < twa_device_extension_count; i++) 
> +		if (twa_device_extension_list[i]->host->host_no == shost->host_no)
> +			tw_dev = twa_device_extension_list[i];

You can simply use shost->private_data to find your device extension
these days.  But we don't want ->proc_info in new drivers anyway, please
use sysfs attributes instead, see 53c700 or the qla2xxx driver for
examples.

> +/* This is the main scsi queue function to handle scsi opcodes */
> +static int twa_scsi_queue(Scsi_Cmnd *SCpnt, void (*done)(Scsi_Cmnd *))
> +{
> +	int request_id;
> +	TW_Device_Extension *tw_dev = (TW_Device_Extension *)SCpnt->device->host->hostdata;
> +
> +	/* Skip lun and channel probes */
> +	if ((SCpnt->device->lun != 0) || (SCpnt->device->channel != 0)) {
> +		SCpnt->result = (DID_BAD_TARGET << 16);
> +		done(SCpnt);
> +		goto out;
> +	}

If you set max_lun and max_channel right you should need this change.

> +	/* Save done function into Scsi_Cmnd struct */
> +	SCpnt->scsi_done = done;
> +		
> +	/* Get a free request id */
> +	twa_get_request_id(tw_dev, &request_id);
> +
> +	/* Save the scsi command for use by the ISR */
> +	tw_dev->srb[request_id] = SCpnt;
> +
> +	/* Initialize phase to zero */
> +	SCpnt->SCp.phase = 0;
> +
> +	if (twa_scsiop_execute_scsi(tw_dev, request_id, NULL, 0, NULL)) {
> +		tw_dev->state[request_id] = TW_S_COMPLETED;
> +		twa_free_request_id(tw_dev, request_id);
> +		SCpnt->result = (DID_ERROR << 16);
> +		done(SCpnt);

If twa_scsiop_execute_scsi returns a non-fatal error and the request
should be retired you should reurn one of the MLQUEUE*BUSY values
instead.



In addition to these code comments please make sure all lines are
wrapper after 80 characters.

^ permalink raw reply	[flat|nested] 4+ messages in thread

* RE: [PATCH/RFC 1/2] 3ware 9000 SATA-RAID driver
@ 2004-05-25 17:46 Adam Radford
  2004-05-25 19:19 ` Christoph Hellwig
  0 siblings, 1 reply; 4+ messages in thread
From: Adam Radford @ 2004-05-25 17:46 UTC (permalink / raw)
  To: hch; +Cc: linux-scsi

Hch,

Thanks for the feedback.  I have already made most of the changes, including
converting the driver to pci_driver format.  I will re-post the next version
soon.  The only thing I cannot do is mark 'extern struct timezone sys_tz' as 
static.

Thanks,

--
Adam Radford
Staff Software Engineer
AMCC

-----Original Message-----
From: linux-scsi-owner@vger.kernel.org
[mailto:linux-scsi-owner@vger.kernel.org]On Behalf Of hch@lst.de
Sent: Sunday, May 23, 2004 4:00 AM
To: Adam Radford
Cc: linux-scsi@vger.kernel.org
Subject: Re: [PATCH/RFC 1/2] 3ware 9000 SATA-RAID driver


> diff -Naur linux-2.6.6-mm4/drivers/pci/pci.ids linux-2.6.6-mm5/drivers/pci/pci.ids
> --- linux-2.6.6-mm4/drivers/pci/pci.ids	2004-05-21 16:35:20.000000000 -0700
> +++ linux-2.6.6-mm5/drivers/pci/pci.ids	2004-05-21 15:38:58.000000000 -0700
> @@ -5751,7 +5751,7 @@
>  13c1  3ware Inc
>  	1000  3ware ATA-RAID
>  	1001  3ware 7000-series ATA-RAID
> -	1002  3ware ATA-RAID
> +	1002  3ware 9000-series ATA-RAID
>  13c2  Technotrend Systemtechnik GmbH
>  13c3  Janz Computer AG
>  13c4  Phase Metrics

I think this file is managed via pci-ids.sf.net, please submit there.

> +
> +#include <linux/module.h>
> +
> +MODULE_AUTHOR ("AMCC");
> +#ifdef CONFIG_SMP
> +MODULE_DESCRIPTION ("3ware 9000 Storage Controller Linux Driver (SMP)");
> +#else
> +MODULE_DESCRIPTION ("3ware 9000 Storage Controller Linux Driver");
> +#endif
> +MODULE_LICENSE("GPL");

the ifdef SMP is bogus, also please move the MODULE_ macros below all
includes.

> +
> +#include <linux/kernel.h>
> +#include <linux/pci.h>
> +#include <linux/time.h>
> +#include <linux/proc_fs.h>
> +#include <linux/sched.h>
> +#include <linux/ioport.h>
> +#include <linux/blkdev.h>
> +#include <linux/hdreg.h>
> +#include <linux/string.h>
> +#include <linux/delay.h>
> +#include <linux/smp.h>

shouldn't be needed with a modern driver.

> +#include <linux/reboot.h>
> +#include <linux/spinlock.h>
> +#include <linux/interrupt.h>
> +#include <linux/moduleparam.h>
> +
> +#include <asm/errno.h>

never use asm/errno.h but always linux/errno.h

> +#include "scsi.h"
> +#include "hosts.h"

please don't include these anymore, use the headers from include/scsi/*.h,
this also involves some reshuffling like getting rid of the obsolete Scsi_Foo
typedefs and using the DMA_* constants instead of SCSI_DATA_*

> +
> +#include "3w-9xxx.h"
> +
> +/* Notifier block to get a notify on system shutdown/halt/reboot */
> +static struct notifier_block twa_notifier = {
> +	.notifier_call	= twa_halt,
> +	.next		= NULL,
> +	.priority	= 0
> +};

please usethe shutdown method in the pci_driver instead, see the megaraid
driver for an example.

> +/* Globals */
> +char *twa_driver_version="2.26.00.008";
> +TW_Device_Extension *twa_device_extension_list[TW_MAX_SLOT];

after the changes I'll suggest below this will only be used in the ioctl
code anymore, and by doing the lookup in ->open there and stuffing a pointer
into file->private_data you'll only need this in a slow-path, so a linked
list will be enough.

> +unsigned int twa_device_extension_count = 0;

No need to initialize to 0, .bss will do it for you.

> +static int twa_major = -1;
> +extern struct timezone sys_tz;
> +static int cmds_per_lun;

please mark all symbols in the driver static.

> +/* This function handles open for the character device */
> +static int twa_chrdev_open(struct inode *inode, struct file *file)
> +{
> +	unsigned int minor_number;
> +	int retval = TW_IOCTL_ERROR_OS_ENODEV;
> +
> +	minor_number = MINOR(inode->i_rdev);

2.6 has a nice iminor() helper for this.

> +/* This function handles close for the character device */
> +static int twa_chrdev_release(struct inode *inode, struct file *file)
> +{
> +	return 0;
> +} /* End twa_chrdev_release() */

no need to implement ->release at all if it doesn't do anything.

> +#if BITS_PER_LONG > 32
> +	/* Turn on 64-bit sgl support */
> +	tw_initconnect->features |= 1;
> +#endif

Don't you also need this on 32bit arches with >32bit physical
addressing, e.g. x86 with PAE?

> +/* This funciton returns unit geometry in cylinders/heads/sectors */
> +static int twa_scsi_biosparam(struct scsi_device *sdev, struct block_device *bdev, sector_t capacity, int geom[])
> +{
> +	int heads, sectors, cylinders;
> +	TW_Device_Extension *tw_dev;
> +
> +	tw_dev = (TW_Device_Extension *)sdev->host->hostdata;
> +
> +	heads = 64;
> +	sectors = 32;
> +	cylinders = (unsigned long)capacity / (heads * sectors);

with CONFIG_LBD set capacity will be a 64bit type on 32bit arches,
so you must use sector_div for it.

> +/* This function will find and initialize all cards */
> +static int twa_scsi_detect(Scsi_Host_Template *tw_host)
> +{
> +	int numcards = 0, i;
> +	struct Scsi_Host *host = NULL;
> +	TW_Device_Extension *tw_dev, *tw_dev2;
> +	struct pci_dev *tw_pci_dev=NULL;
> +	u32 mem_addr, mem_len;
> +	u16 device[TW_NUMDEVICES] = { TW_DEVICE_ID_9000 };
> +
> +	printk(KERN_WARNING "3ware 9000 Storage Controller device driver for Linux v%s.\n", twa_driver_version);
> +
> +	for (i = 0; i < TW_NUMDEVICES; i++) {
> +		while ((tw_pci_dev = pci_find_device(TW_VENDOR_ID, device[i], tw_pci_dev))) {

Please redo the probing useing a struct pci_driver and appropinquate
callbacks.  See e.g. megaraid or qla1280 for details.

> +/* This function handles input and output from /proc/scsi/3w-9xxx/x */
> +static int twa_scsi_proc_info(struct Scsi_Host *shost, char *buffer, char **start, off_t offset, int length, int inout)
> +{
> +	TW_Device_Extension *tw_dev = NULL;
> +	TW_Info info;
> +	unsigned int i;
> +	int retval = -EINVAL;
> +
> +	/* Find the correct device extension */
> +	for (i = 0; i < twa_device_extension_count; i++)
> +		if (twa_device_extension_list[i]->host->host_no == shost->host_no)
> +			tw_dev = twa_device_extension_list[i];

You can simply use shost->private_data to find your device extension
these days.  But we don't want ->proc_info in new drivers anyway, please
use sysfs attributes instead, see 53c700 or the qla2xxx driver for
examples.

> +/* This is the main scsi queue function to handle scsi opcodes */
> +static int twa_scsi_queue(Scsi_Cmnd *SCpnt, void (*done)(Scsi_Cmnd *))
> +{
> +	int request_id;
> +	TW_Device_Extension *tw_dev = (TW_Device_Extension *)SCpnt->device->host->hostdata;
> +
> +	/* Skip lun and channel probes */
> +	if ((SCpnt->device->lun != 0) || (SCpnt->device->channel != 0)) {
> +		SCpnt->result = (DID_BAD_TARGET << 16);
> +		done(SCpnt);
> +		goto out;
> +	}

If you set max_lun and max_channel right you should need this change.

> +	/* Save done function into Scsi_Cmnd struct */
> +	SCpnt->scsi_done = done;
> +
> +	/* Get a free request id */
> +	twa_get_request_id(tw_dev, &request_id);
> +
> +	/* Save the scsi command for use by the ISR */
> +	tw_dev->srb[request_id] = SCpnt;
> +
> +	/* Initialize phase to zero */
> +	SCpnt->SCp.phase = 0;
> +
> +	if (twa_scsiop_execute_scsi(tw_dev, request_id, NULL, 0, NULL)) {
> +		tw_dev->state[request_id] = TW_S_COMPLETED;
> +		twa_free_request_id(tw_dev, request_id);
> +		SCpnt->result = (DID_ERROR << 16);
> +		done(SCpnt);

If twa_scsiop_execute_scsi returns a non-fatal error and the request
should be retired you should reurn one of the MLQUEUE*BUSY values
instead.



In addition to these code comments please make sure all lines are
wrapper after 80 characters.
-
To unsubscribe from this list: send the line "unsubscribe linux-scsi" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html


^ permalink raw reply	[flat|nested] 4+ messages in thread

* Re: [PATCH/RFC 1/2] 3ware 9000 SATA-RAID driver
  2004-05-25 17:46 Adam Radford
@ 2004-05-25 19:19 ` Christoph Hellwig
  0 siblings, 0 replies; 4+ messages in thread
From: Christoph Hellwig @ 2004-05-25 19:19 UTC (permalink / raw)
  To: Adam Radford; +Cc: linux-scsi

On Tue, May 25, 2004 at 10:46:37AM -0700, Adam Radford wrote:
> Hch,
> 
> Thanks for the feedback.  I have already made most of the changes, including
> converting the driver to pci_driver format.  I will re-post the next version
> soon.  The only thing I cannot do is mark 'extern struct timezone sys_tz' as 
> static.

Hmm, I wonder why you need that.  But I'm too lazy to look at the old
patch again and will check when you post the next revision.


^ permalink raw reply	[flat|nested] 4+ messages in thread

end of thread, other threads:[~2004-05-25 19:19 UTC | newest]

Thread overview: 4+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2004-05-22 18:19 [PATCH/RFC 1/2] 3ware 9000 SATA-RAID driver Adam Radford
2004-05-23 10:59 ` hch
  -- strict thread matches above, loose matches on Subject: below --
2004-05-25 17:46 Adam Radford
2004-05-25 19:19 ` Christoph Hellwig

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox