public inbox for linux-scsi@vger.kernel.org
 help / color / mirror / Atom feed
* [PATCH/RFC 1/2] 3ware 9000 SATA-RAID driver v2.26.00.009
@ 2004-05-26 20:46 Adam Radford
  2004-05-26 21:42 ` Jeff Garzik
  0 siblings, 1 reply; 5+ messages in thread
From: Adam Radford @ 2004-05-26 20:46 UTC (permalink / raw)
  To: akpm, james.bottomley, hch; +Cc: linux-scsi

Andrew/James,

Here is an updated version v2.26.00.009 of the 3ware 9000 SATA-RAID driver.

Changes in this release:
   - Convert driver to pci_driver format.
   - Remove proc interface, add sysfs attributes.
   - Return SCSI_MLQUEUE_HOST_BUSY when card status is busy.

Future versions may include:
   - PAE fix
   - 80 column wrap fixes
   - Remove more prototypes by re-ordering functions.
   - sysfs queue depth adjustment instead of module_param

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

--
Adam Radford
Staff Software Engineer
AMCC

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-26 13:17:24.000000000 -0700
@@ -0,0 +1,2144 @@
+/*
+   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.
+   2.26.00.009 - Convert driver to pci_driver format.
+                 Remove proc interface, add sysfs attributes.
+                 Return SCSI_MLQUEUE_HOST_BUSY when card status is busy.
+*/
+
+#include <linux/module.h>
+#include <linux/reboot.h>
+#include <linux/spinlock.h>
+#include <linux/interrupt.h>
+#include <linux/moduleparam.h>
+#include <linux/errno.h>
+#include <linux/types.h>
+#include <linux/delay.h>
+#include <linux/pci.h>
+#include <linux/time.h>
+#include <asm/io.h>
+#include <asm/irq.h>
+#include <asm/uaccess.h>
+#include <scsi/scsi.h>
+#include <scsi/scsi_host.h>
+#include <scsi/scsi_tcq.h>
+#include <scsi/scsi_cmnd.h>
+#include "3w-9xxx.h"
+
+/* Globals */
+char *twa_driver_version="2.26.00.009";
+TW_Device_Extension *twa_device_extension_list[TW_MAX_SLOT];
+static unsigned int twa_device_extension_count;
+static int twa_major = -1;
+extern struct timezone sys_tz;
+static int cmds_per_lun;
+
+/* Module parameters */
+MODULE_AUTHOR ("AMCC");
+MODULE_DESCRIPTION ("3ware 9000 Storage Controller Linux Driver");
+MODULE_LICENSE("GPL");
+module_param(cmds_per_lun, int, 0);
+MODULE_PARM_DESC(cmds_per_lun, "Maximum commands per LUN");
+
+/* Function prototypes */
+static int twa_aen_complete(TW_Device_Extension *tw_dev, int request_id);
+static int twa_aen_drain_queue(TW_Device_Extension *tw_dev, int check_reset);
+static void twa_aen_queue_event(TW_Device_Extension *tw_dev, TW_Command_Apache_Header *header);
+static int twa_aen_read_queue(TW_Device_Extension *tw_dev, int request_id);
+static char *twa_aen_severity_lookup(unsigned char severity_code);
+static void twa_aen_sync_time(TW_Device_Extension *tw_dev, int request_id);
+static int twa_allocate_memory(TW_Device_Extension *tw_dev, int size, int which);
+static int twa_check_bits(u32 status_reg_value);
+static int twa_check_srl(TW_Device_Extension *tw_dev, int *flashed);
+static int twa_chrdev_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg);
+static int twa_chrdev_open(struct inode *inode, struct file *file);
+static int twa_decode_bits(TW_Device_Extension *tw_dev, u32 status_reg_value);
+static int twa_empty_response_queue(TW_Device_Extension *tw_dev);
+static int twa_fill_sense(TW_Device_Extension *tw_dev, int request_id, int copy_sense, int print_host);
+static void twa_free_device_extension(TW_Device_Extension *tw_dev);
+static void twa_free_request_id(TW_Device_Extension *tw_dev,int request_id);
+static void *twa_get_param(TW_Device_Extension *tw_dev, int request_id, int table_id, int parameter_id, int parameter_size_bytes);
+static void twa_get_request_id(TW_Device_Extension *tw_dev, int *request_id);
+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);
+static int twa_initialize_device_extension(TW_Device_Extension *tw_dev);
+static irqreturn_t twa_interrupt(int irq, void *dev_instance, struct pt_regs *regs);
+static void twa_load_sgl(TW_Command_Full *full_command_packet, int request_id, dma_addr_t dma_handle, int length);
+static int twa_map_scsi_sg_data(TW_Device_Extension *tw_dev, int request_id);
+static dma_addr_t twa_map_scsi_single_data(TW_Device_Extension *tw_dev, int request_id);
+static int twa_poll_response(TW_Device_Extension *tw_dev, int request_id, int seconds);
+static int twa_poll_status(TW_Device_Extension *tw_dev, u32 flag, int seconds);
+static int twa_poll_status_gone(TW_Device_Extension *tw_dev, u32 flag, int seconds);
+static int twa_post_command_packet(TW_Device_Extension *tw_dev, int request_id, char internal);
+static int twa_reset_device_extension(TW_Device_Extension *tw_dev);
+static int twa_reset_sequence(TW_Device_Extension *tw_dev, int soft_reset);
+static int twa_scsi_biosparam(struct scsi_device *sdev, struct block_device *bdev, sector_t capacity, int geom[]);
+static int __devinit twa_probe(struct pci_dev *pdev, const struct pci_device_id *dev_id);
+static int twa_scsi_eh_abort(struct scsi_cmnd *SCpnt);
+static int twa_scsi_eh_reset(struct scsi_cmnd *SCpnt);
+static int twa_scsi_queue(struct scsi_cmnd *cmd, void (*done) (struct scsi_cmnd *));
+static int twa_scsiop_execute_scsi(TW_Device_Extension *tw_dev, int request_id, char *cdb, int use_sg, TW_SG_Apache *sglistarg);
+static void twa_scsiop_execute_scsi_complete(TW_Device_Extension *tw_dev, int request_id);
+static int twa_setup_irq(TW_Device_Extension *tw_dev);
+static int twa_slave_configure(struct scsi_device *SDptr);
+static char *twa_string_lookup(twa_message_type *table, unsigned int aen_code);
+static void twa_unmap_scsi_data(TW_Device_Extension *tw_dev, int request_id);
+
+/* Functions */
+
+/* Show some statistics about the card */
+static ssize_t twa_show_stats(struct class_device *class_dev, char *buf)
+{
+	struct Scsi_Host *host = class_to_shost(class_dev);
+	TW_Device_Extension *tw_dev = (TW_Device_Extension *)host->hostdata;
+	unsigned long flags = 0;
+	ssize_t len;
+
+	spin_lock_irqsave(tw_dev->host->host_lock, flags);
+	len = snprintf(buf, PAGE_SIZE, "Driver version: %s\n"
+		       "Current commands posted:   %4d\n"
+		       "Max commands posted:       %4d\n"
+		       "Current pending commands:  %4d\n"
+		       "Max pending commands:      %4d\n"
+		       "Last sgl length:           %4d\n"
+		       "Max sgl length:            %4d\n"
+		       "Last sector count:         %4d\n"
+		       "Max sector count:          %4d\n"
+		       "SCSI Host Resets:          %4d\n"
+		       "SCSI Aborts/Timeouts:      %4d\n"
+		       "AEN's:                     %4d\n", 
+		       twa_driver_version,
+		       tw_dev->posted_request_count,
+		       tw_dev->max_posted_request_count,
+		       tw_dev->pending_request_count,
+		       tw_dev->max_pending_request_count,
+		       tw_dev->sgl_entries,
+		       tw_dev->max_sgl_entries,
+		       tw_dev->sector_count,
+		       tw_dev->max_sector_count,
+		       tw_dev->num_resets,
+		       tw_dev->num_aborts,
+		       tw_dev->aen_count);
+	spin_unlock_irqrestore(tw_dev->host->host_lock, flags);
+	return len;
+} /* End twa_show_stats() */
+
+/* Create sysfs 'stats' entry */
+static struct class_device_attribute twa_host_stats_attr = {
+	.attr = {
+		.name = 	"stats",
+		.mode =		S_IRUGO,
+	},
+	.show = twa_show_stats
+};
+
+/* Host attributes initializer */
+static struct class_device_attribute *twa_host_attrs[] = {
+	&twa_host_stats_attr,
+	NULL,
+};
+
+/* File operations struct for character device */
+static struct file_operations twa_fops = {
+	.owner		= THIS_MODULE,
+	.ioctl		= twa_chrdev_ioctl,
+	.open		= twa_chrdev_open,
+	.release	= NULL
+};
+
+/* 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, 1);
+} /* 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[iminor(inode)];
+	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, 1);
+		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 = iminor(inode);
+	if (minor_number >= twa_device_extension_count)
+		goto out;
+	retval = 0;
+out:
+	return retval;
+} /* End twa_chrdev_open() */
+
+/* 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, 1);
+
+	/* 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() */
+
+/* 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, 1);
+
+	/* 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;
+
+	init_MUTEX(&tw_dev->ioctl_sem);
+	init_waitqueue_head(&tw_dev->ioctl_wqueue);
+
+	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, 1)==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;
+	struct scsi_cmnd *cmd = tw_dev->srb[request_id];
+	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_BIDIRECTIONAL);
+
+	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;
+	struct scsi_cmnd *cmd = tw_dev->srb[request_id];
+	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_BIDIRECTIONAL);
+
+	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, char internal)
+{
+	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)) {
+
+		/* Only pend internal driver commands */
+		if (!internal) {
+			retval = SCSI_MLQUEUE_HOST_BUSY;
+			goto out;
+		}
+
+		/* 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;
+
+	if (capacity >= 0x200000) {
+		heads = 255;
+		sectors = 63;
+		cylinders = sector_div(capacity, heads * sectors);
+	} else {
+		heads = 64;
+		sectors = 32;
+		cylinders = sector_div(capacity, heads * sectors);
+	}
+
+	geom[0] = heads;
+	geom[1] = sectors;
+	geom[2] = cylinders;
+
+	return 0;
+} /* End twa_scsi_biosparam() */
+
+/* This is the new scsi eh abort function */
+static int twa_scsi_eh_abort(struct 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(struct 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 is the main scsi queue function to handle scsi opcodes */
+static int twa_scsi_queue(struct scsi_cmnd *SCpnt, void (*done)(struct scsi_cmnd *))
+{
+	int request_id, retval;
+	TW_Device_Extension *tw_dev = (TW_Device_Extension *)SCpnt->device->host->hostdata;
+
+	/* 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;
+
+	retval = twa_scsiop_execute_scsi(tw_dev, request_id, NULL, 0, NULL);
+	switch (retval) {
+	case SCSI_MLQUEUE_HOST_BUSY:
+		twa_free_request_id(tw_dev, request_id);
+		break;
+	case 1:
+		tw_dev->state[request_id] = TW_S_COMPLETED;
+		twa_free_request_id(tw_dev, request_id);
+		SCpnt->result = (DID_ERROR << 16);
+		done(SCpnt);
+	}
+
+	return retval;
+} /* End twa_scsi_queue() */
+
+/* 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;
+	struct 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 */
+	if (srb) {
+		retval = twa_post_command_packet(tw_dev, request_id, 0);
+	} else {
+		twa_post_command_packet(tw_dev, request_id, 1);
+		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(TW_Device_Extension *tw_dev)
+{
+	/* Disable interrupts */
+	TW_DISABLE_INTERRUPTS(tw_dev);
+
+	printk(KERN_WARNING "3w-9xxx: Shutting down host %d.\n", tw_dev->host->host_no);
+
+	/* 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() */
+
+/* Wrapper for __twa_shutdown */
+static void twa_shutdown(struct device *dev)
+{
+	struct Scsi_Host *host = pci_get_drvdata(to_pci_dev(dev));
+	TW_Device_Extension *tw_dev = (TW_Device_Extension *)host->hostdata;
+
+	__twa_shutdown(tw_dev);
+} /* End twa_shutdown() */
+
+/* This function will configure individual target parameters */
+static int twa_slave_configure(struct 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)
+{
+	struct scsi_cmnd *cmd = tw_dev->srb[request_id];
+	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_BIDIRECTIONAL);
+		break;
+	case 2:
+		pci_unmap_sg(pdev, cmd->request_buffer, cmd->use_sg, DMA_BIDIRECTIONAL);
+		break;
+	}
+} /* End twa_unmap_scsi_data() */
+
+/* Get informationa bout the card/driver */
+static const char *twa_info(struct Scsi_Host *host)
+{
+	static char buffer[512];
+
+	sprintf(buffer, "3ware 9000 Storage Controller");
+
+	return buffer;
+} /* End twa_info() */
+
+/* scsi_host_template initializer */
+static struct scsi_host_template driver_template = {
+	.module			= THIS_MODULE,
+	.name			= "3ware 9000 Storage Controller",
+	.info			= twa_info,
+	.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,
+	.shost_attrs		= twa_host_attrs,
+	.emulated		= 1
+};
+
+/* This function will probe and initialize a card */
+static int __devinit twa_probe(struct pci_dev *pdev, const struct pci_device_id *dev_id)
+{
+	struct Scsi_Host *host = NULL;
+	TW_Device_Extension *tw_dev;
+	u32 mem_addr, mem_len;
+	int retval = -ENODEV;
+
+	if (pci_enable_device(pdev)) {
+		TW_PRINTK(host, TW_DRIVER, 0x32, "Failed to enable pci device");
+		goto out;
+	}
+
+	pci_set_master(pdev);
+
+	if (pci_set_dma_mask(pdev, TW_DMA_MASK)) {
+		TW_PRINTK(host, TW_DRIVER, 0x23, "Failed to set dma mask");
+		goto out;
+	}
+
+	host = scsi_host_alloc(&driver_template, sizeof(TW_Device_Extension));
+	if (!host) {
+		TW_PRINTK(host, TW_DRIVER, 0x24, "Failed to allocate memory for device extension");
+		retval = -ENOMEM;
+		goto out;
+	}
+	tw_dev = (TW_Device_Extension *)host->hostdata;
+
+	memset(tw_dev, 0, sizeof(TW_Device_Extension));
+
+	/* Save values to device extension */
+	tw_dev->host = host;
+	tw_dev->tw_pci_dev = pdev;
+
+	if (twa_initialize_device_extension(tw_dev)) {
+		TW_PRINTK(tw_dev->host, TW_DRIVER, 0x25, "Failed to initialize device extension");
+		goto out_free_device_extension;
+	}
+
+	mem_addr = pci_resource_start(pdev, 1);
+	mem_len = pci_resource_len(pdev, 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");
+		goto out_free_device_extension;
+	}
+
+	/* 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))
+		goto out_release_mem_region;
+
+	/* Set host specific parameters */
+	host->max_id = TW_MAX_UNITS;
+	host->max_cmd_len = TW_MAX_CDB_LEN;
+	host->max_lun = 0;
+	host->max_channel = 0;
+
+	/* Register the card with the kernel SCSI layer */
+	retval = scsi_add_host(host, &pdev->dev);
+	if (retval) {
+		TW_PRINTK(tw_dev->host, TW_DRIVER, 0x27, "scsi add host failed");
+		goto out_release_mem_region;
+	}
+
+	pci_set_drvdata(pdev, host);
+
+	printk(KERN_WARNING "3w-9xxx: scsi%d: Found a 3ware 9000 Storage Controller at 0x%x, IRQ: %d.\n", host->host_no, mem_addr, pdev->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));
+
+	/* Now setup the interrupt handler */
+	if (twa_setup_irq(tw_dev))
+		goto out_remove_host;
+
+	twa_device_extension_list[twa_device_extension_count] = tw_dev;
+	twa_device_extension_count++;
+
+	/* Re-enable interrupts on the card */
+	TW_ENABLE_AND_CLEAR_INTERRUPTS(tw_dev);
+
+	/* Finally, scan the host */
+	scsi_scan_host(host);
+
+	if (twa_major == -1) {
+		if ((twa_major = register_chrdev (0, "twa", &twa_fops)) < 0)
+			TW_PRINTK(host, TW_DRIVER, 0x29, "Failed to register character device");
+	}
+	return 0;
+
+out_remove_host:
+	scsi_remove_host(host);
+out_release_mem_region:
+	release_mem_region(mem_addr, mem_len);
+out_free_device_extension:
+	twa_free_device_extension(tw_dev);
+	scsi_host_put(host);
+out:
+	return retval;
+} /* End twa_probe() */
+
+/* This function is called to remove a device */
+static void twa_remove(struct pci_dev *pdev)
+{
+	struct Scsi_Host *host = pci_get_drvdata(pdev);
+	TW_Device_Extension *tw_dev = (TW_Device_Extension *)host->hostdata;
+
+	scsi_remove_host(tw_dev->host);
+
+	__twa_shutdown(tw_dev);
+
+	/* Free up the IRQ */
+	free_irq(tw_dev->tw_pci_dev->irq, tw_dev);
+
+	/* 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 device extension resources */
+	twa_free_device_extension(tw_dev);
+
+	scsi_host_put(tw_dev->host);
+	pci_disable_device(pdev);
+	twa_device_extension_count--;
+} /* End twa_remove() */
+
+/* PCI Devices supported by this driver */
+static struct pci_device_id twa_pci_tbl[] __devinitdata = {
+	{ PCI_VENDOR_ID_3WARE, PCI_DEVICE_ID_3WARE_9000,
+	  PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+	{ }
+};
+MODULE_DEVICE_TABLE(pci, twa_pci_tbl);
+
+/* pci_driver initializer */
+static struct pci_driver twa_driver = {
+	.name		= "3w-9xxx",
+	.id_table	= twa_pci_tbl,
+	.probe		= twa_probe,
+	.remove		= twa_remove,
+	.driver		= {
+		.shutdown = twa_shutdown
+	}
+};
+
+/* This function is called on driver initialization */
+static int __init twa_init(void)
+{
+	printk(KERN_WARNING "3ware 9000 Storage Controller device driver for Linux v%s.\n", twa_driver_version);
+
+	pci_register_driver(&twa_driver);
+
+	return 0;
+} /* End twa_init() */
+
+/* This function is called on driver exit */
+static void __exit twa_exit(void)
+{
+	/* Unregister character device */
+	if (twa_major >= 0) {
+		unregister_chrdev(twa_major, "twa");
+		twa_major = -1;
+	}
+
+	pci_unregister_driver(&twa_driver);
+} /* End twa_exit() */
+
+module_init(twa_init);
+module_exit(twa_exit);
+


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

* Re: [PATCH/RFC 1/2] 3ware 9000 SATA-RAID driver v2.26.00.009
  2004-05-26 20:46 Adam Radford
@ 2004-05-26 21:42 ` Jeff Garzik
  0 siblings, 0 replies; 5+ messages in thread
From: Jeff Garzik @ 2004-05-26 21:42 UTC (permalink / raw)
  To: Adam Radford; +Cc: akpm, james.bottomley, hch, linux-scsi

Adam Radford wrote:


Overall:  your driver duplicates some SCSI infrastructure for sending 
SCSI commands, and waiting for their replies.



> Changes in this release:
>    - Convert driver to pci_driver format.

yay!


>    - Return SCSI_MLQUEUE_HOST_BUSY when card status is busy.

Does the hardware have some sort of metric, like "queued commands < 32", 
to better determine when card is busy?

Normally, you want to set up the system such that SCSI stops calling 
your ->queuecommand() hook when there is no more room.

SCSI layer provides device-wide and host-wide queueing limits.


> Future versions may include:
>    - PAE fix

What does this involve?


>    - 80 column wrap fixes
>    - Remove more prototypes by re-ordering functions.

Bah, who cares...  Maintainer's choice, IMO.



> 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-26 13:17:24.000000000 -0700
> @@ -0,0 +1,2144 @@
> +/*
> +   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.
> +   2.26.00.009 - Convert driver to pci_driver format.
> +                 Remove proc interface, add sysfs attributes.
> +                 Return SCSI_MLQUEUE_HOST_BUSY when card status is busy.
> +*/
> +
> +#include <linux/module.h>
> +#include <linux/reboot.h>
> +#include <linux/spinlock.h>
> +#include <linux/interrupt.h>
> +#include <linux/moduleparam.h>
> +#include <linux/errno.h>
> +#include <linux/types.h>
> +#include <linux/delay.h>
> +#include <linux/pci.h>
> +#include <linux/time.h>
> +#include <asm/io.h>
> +#include <asm/irq.h>
> +#include <asm/uaccess.h>
> +#include <scsi/scsi.h>
> +#include <scsi/scsi_host.h>
> +#include <scsi/scsi_tcq.h>
> +#include <scsi/scsi_cmnd.h>
> +#include "3w-9xxx.h"
> +
> +/* Globals */
> +char *twa_driver_version="2.26.00.009";

mark 'static', possibly 'const' too.


> +TW_Device_Extension *twa_device_extension_list[TW_MAX_SLOT];

static


> +static unsigned int twa_device_extension_count;
> +static int twa_major = -1;
> +extern struct timezone sys_tz;
> +static int cmds_per_lun;
> +
> +/* Module parameters */
> +MODULE_AUTHOR ("AMCC");
> +MODULE_DESCRIPTION ("3ware 9000 Storage Controller Linux Driver");
> +MODULE_LICENSE("GPL");
> +module_param(cmds_per_lun, int, 0);
> +MODULE_PARM_DESC(cmds_per_lun, "Maximum commands per LUN");
> +
> +/* Function prototypes */
> +static int twa_aen_complete(TW_Device_Extension *tw_dev, int request_id);
> +static int twa_aen_drain_queue(TW_Device_Extension *tw_dev, int check_reset);
> +static void twa_aen_queue_event(TW_Device_Extension *tw_dev, TW_Command_Apache_Header *header);
> +static int twa_aen_read_queue(TW_Device_Extension *tw_dev, int request_id);
> +static char *twa_aen_severity_lookup(unsigned char severity_code);
> +static void twa_aen_sync_time(TW_Device_Extension *tw_dev, int request_id);
> +static int twa_allocate_memory(TW_Device_Extension *tw_dev, int size, int which);
> +static int twa_check_bits(u32 status_reg_value);
> +static int twa_check_srl(TW_Device_Extension *tw_dev, int *flashed);
> +static int twa_chrdev_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg);
> +static int twa_chrdev_open(struct inode *inode, struct file *file);
> +static int twa_decode_bits(TW_Device_Extension *tw_dev, u32 status_reg_value);
> +static int twa_empty_response_queue(TW_Device_Extension *tw_dev);
> +static int twa_fill_sense(TW_Device_Extension *tw_dev, int request_id, int copy_sense, int print_host);
> +static void twa_free_device_extension(TW_Device_Extension *tw_dev);
> +static void twa_free_request_id(TW_Device_Extension *tw_dev,int request_id);
> +static void *twa_get_param(TW_Device_Extension *tw_dev, int request_id, int table_id, int parameter_id, int parameter_size_bytes);
> +static void twa_get_request_id(TW_Device_Extension *tw_dev, int *request_id);
> +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);
> +static int twa_initialize_device_extension(TW_Device_Extension *tw_dev);
> +static irqreturn_t twa_interrupt(int irq, void *dev_instance, struct pt_regs *regs);
> +static void twa_load_sgl(TW_Command_Full *full_command_packet, int request_id, dma_addr_t dma_handle, int length);
> +static int twa_map_scsi_sg_data(TW_Device_Extension *tw_dev, int request_id);
> +static dma_addr_t twa_map_scsi_single_data(TW_Device_Extension *tw_dev, int request_id);
> +static int twa_poll_response(TW_Device_Extension *tw_dev, int request_id, int seconds);
> +static int twa_poll_status(TW_Device_Extension *tw_dev, u32 flag, int seconds);
> +static int twa_poll_status_gone(TW_Device_Extension *tw_dev, u32 flag, int seconds);
> +static int twa_post_command_packet(TW_Device_Extension *tw_dev, int request_id, char internal);
> +static int twa_reset_device_extension(TW_Device_Extension *tw_dev);
> +static int twa_reset_sequence(TW_Device_Extension *tw_dev, int soft_reset);
> +static int twa_scsi_biosparam(struct scsi_device *sdev, struct block_device *bdev, sector_t capacity, int geom[]);
> +static int __devinit twa_probe(struct pci_dev *pdev, const struct pci_device_id *dev_id);
> +static int twa_scsi_eh_abort(struct scsi_cmnd *SCpnt);
> +static int twa_scsi_eh_reset(struct scsi_cmnd *SCpnt);
> +static int twa_scsi_queue(struct scsi_cmnd *cmd, void (*done) (struct scsi_cmnd *));
> +static int twa_scsiop_execute_scsi(TW_Device_Extension *tw_dev, int request_id, char *cdb, int use_sg, TW_SG_Apache *sglistarg);
> +static void twa_scsiop_execute_scsi_complete(TW_Device_Extension *tw_dev, int request_id);
> +static int twa_setup_irq(TW_Device_Extension *tw_dev);
> +static int twa_slave_configure(struct scsi_device *SDptr);
> +static char *twa_string_lookup(twa_message_type *table, unsigned int aen_code);
> +static void twa_unmap_scsi_data(TW_Device_Extension *tw_dev, int request_id);
> +
> +/* Functions */
> +
> +/* Show some statistics about the card */
> +static ssize_t twa_show_stats(struct class_device *class_dev, char *buf)
> +{
> +	struct Scsi_Host *host = class_to_shost(class_dev);
> +	TW_Device_Extension *tw_dev = (TW_Device_Extension *)host->hostdata;
> +	unsigned long flags = 0;
> +	ssize_t len;
> +
> +	spin_lock_irqsave(tw_dev->host->host_lock, flags);
> +	len = snprintf(buf, PAGE_SIZE, "Driver version: %s\n"
> +		       "Current commands posted:   %4d\n"
> +		       "Max commands posted:       %4d\n"
> +		       "Current pending commands:  %4d\n"
> +		       "Max pending commands:      %4d\n"
> +		       "Last sgl length:           %4d\n"
> +		       "Max sgl length:            %4d\n"
> +		       "Last sector count:         %4d\n"
> +		       "Max sector count:          %4d\n"
> +		       "SCSI Host Resets:          %4d\n"
> +		       "SCSI Aborts/Timeouts:      %4d\n"
> +		       "AEN's:                     %4d\n", 
> +		       twa_driver_version,
> +		       tw_dev->posted_request_count,
> +		       tw_dev->max_posted_request_count,
> +		       tw_dev->pending_request_count,
> +		       tw_dev->max_pending_request_count,
> +		       tw_dev->sgl_entries,
> +		       tw_dev->max_sgl_entries,
> +		       tw_dev->sector_count,
> +		       tw_dev->max_sector_count,
> +		       tw_dev->num_resets,
> +		       tw_dev->num_aborts,
> +		       tw_dev->aen_count);
> +	spin_unlock_irqrestore(tw_dev->host->host_lock, flags);
> +	return len;
> +} /* End twa_show_stats() */

surely sysfs allows use of seq_file API???


> +/* File operations struct for character device */
> +static struct file_operations twa_fops = {
> +	.owner		= THIS_MODULE,
> +	.ioctl		= twa_chrdev_ioctl,
> +	.open		= twa_chrdev_open,
> +	.release	= NULL
> +};
> +
> +/* 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;

kill unneeded casts


> +	/* 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);

are we really calling do_gettimeofday() on every command sent to 
hardware?  Can this be avoided?


> +	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, 1);
> +} /* 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[iminor(inode)];
> +	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;

why not return -EINVAL and require userland to align?


> +	/* 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;

are you really copying all the user data buffers here?

That will be godawful slow.

I would suggest building and sending a struct request instead.


> +	/* 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, 1);
> +		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);

surely there is a better way to do this... semaphore?

down() here, and up() in irq handler or timer.


> +		/* 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 = iminor(inode);
> +	if (minor_number >= twa_device_extension_count)
> +		goto out;
> +	retval = 0;
> +out:
> +	return retval;
> +} /* End twa_chrdev_open() */
> +
> +/* 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, 1);
> +
> +	/* 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]);

just use standard SCSI code to issue a SCSI command, and wait for a 
response.


> +	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() */
> +
> +/* 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, 1);
> +
> +	/* 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() */

ditto


> +/* 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;
> +	}

allocate with GFP_KERNEL, GFP_ATOMIC is not necessary, and it depletes 
the emergency pools



> +	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;
> +
> +	init_MUTEX(&tw_dev->ioctl_sem);
> +	init_waitqueue_head(&tw_dev->ioctl_wqueue);
> +
> +	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;

completely bogus test.  remove.  remove TW_IN_INTR too.


> +	/* Get the per adapter lock */
> +	spin_lock_irqsave(tw_dev->host->host_lock, flags);

spin_lock(), not spin_lock_irqsave()


> +	/* 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;

also check for 0xffffffff return value, indicating hardware removed or 
hardware fault.


> +		/* 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, 1)==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;
> +	struct scsi_cmnd *cmd = tw_dev->srb[request_id];
> +	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_BIDIRECTIONAL);
> +
> +	if (use_sg == 0) {
> +		TW_PRINTK(tw_dev->host, TW_DRIVER, 0x1c, "Failed to map scatter gather list");
> +		goto out;
> +	}
> +
> +	cmd->SCp.phase = 2;

Don't use magic numbers for phases, use a descriptive constant



> +	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;
> +	struct scsi_cmnd *cmd = tw_dev->srb[request_id];
> +	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_BIDIRECTIONAL);
> +
> +	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);
> +	}

bzzzz.  this delay loop is _completely_ unacceptable.  It can hang the 
machine for far too long.  Use a timer, or schedule_timeout() in process 
context.



> +	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:

another bogus delay loop



> +/* 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, char internal)
> +{
> +	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)) {
> +
> +		/* Only pend internal driver commands */
> +		if (!internal) {
> +			retval = SCSI_MLQUEUE_HOST_BUSY;
> +			goto out;
> +		}
> +
> +		/* 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

PCI posting?


> +		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;
> +
> +	if (capacity >= 0x200000) {
> +		heads = 255;
> +		sectors = 63;
> +		cylinders = sector_div(capacity, heads * sectors);
> +	} else {
> +		heads = 64;
> +		sectors = 32;
> +		cylinders = sector_div(capacity, heads * sectors);
> +	}
> +
> +	geom[0] = heads;
> +	geom[1] = sectors;
> +	geom[2] = cylinders;
> +
> +	return 0;
> +} /* End twa_scsi_biosparam() */
> +
> +/* This is the new scsi eh abort function */
> +static int twa_scsi_eh_abort(struct 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(struct 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 is the main scsi queue function to handle scsi opcodes */
> +static int twa_scsi_queue(struct scsi_cmnd *SCpnt, void (*done)(struct scsi_cmnd *))
> +{
> +	int request_id, retval;
> +	TW_Device_Extension *tw_dev = (TW_Device_Extension *)SCpnt->device->host->hostdata;
> +
> +	/* 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;

you need to save the 'done' hook too


> +	/* Initialize phase to zero */
> +	SCpnt->SCp.phase = 0;
> +
> +	retval = twa_scsiop_execute_scsi(tw_dev, request_id, NULL, 0, NULL);
> +	switch (retval) {
> +	case SCSI_MLQUEUE_HOST_BUSY:
> +		twa_free_request_id(tw_dev, request_id);
> +		break;
> +	case 1:
> +		tw_dev->state[request_id] = TW_S_COMPLETED;
> +		twa_free_request_id(tw_dev, request_id);
> +		SCpnt->result = (DID_ERROR << 16);
> +		done(SCpnt);
> +	}
> +
> +	return retval;
> +} /* End twa_scsi_queue() */
> +
> +/* 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;
> +	struct 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 */
> +	if (srb) {
> +		retval = twa_post_command_packet(tw_dev, request_id, 0);
> +	} else {
> +		twa_post_command_packet(tw_dev, request_id, 1);
> +		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 badly needs line wrapping

Greater-than-80 columns is ok, but completely invisible code off the 
right of the screen is annoying to review.


> +
> +/* 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() */

delete, and call directly


> +/* This function tells the controller to shut down */
> +static void __twa_shutdown(TW_Device_Extension *tw_dev)
> +{
> +	/* Disable interrupts */
> +	TW_DISABLE_INTERRUPTS(tw_dev);
> +
> +	printk(KERN_WARNING "3w-9xxx: Shutting down host %d.\n", tw_dev->host->host_no);
> +
> +	/* Tell the card we are shutting down */
> +	if (twa_initconnection(tw_dev, 1, 0, 0, 0, 0, 0, NULL, NULL, NULL, NULL, NULL)) {

That's way too many arguments to the function.  Create a "struct 
twa_initconnection_info" or similar...


> +		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() */
> +
> +/* Wrapper for __twa_shutdown */
> +static void twa_shutdown(struct device *dev)
> +{
> +	struct Scsi_Host *host = pci_get_drvdata(to_pci_dev(dev));
> +	TW_Device_Extension *tw_dev = (TW_Device_Extension *)host->hostdata;
> +
> +	__twa_shutdown(tw_dev);
> +} /* End twa_shutdown() */
> +
> +/* This function will configure individual target parameters */
> +static int twa_slave_configure(struct 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)
> +{
> +	struct scsi_cmnd *cmd = tw_dev->srb[request_id];
> +	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_BIDIRECTIONAL);
> +		break;
> +	case 2:
> +		pci_unmap_sg(pdev, cmd->request_buffer, cmd->use_sg, DMA_BIDIRECTIONAL);
> +		break;
> +	}
> +} /* End twa_unmap_scsi_data() */
> +
> +/* Get informationa bout the card/driver */
> +static const char *twa_info(struct Scsi_Host *host)
> +{
> +	static char buffer[512];
> +
> +	sprintf(buffer, "3ware 9000 Storage Controller");
> +
> +	return buffer;
> +} /* End twa_info() */

I thought the ->info hook was deprecated?


> +/* scsi_host_template initializer */
> +static struct scsi_host_template driver_template = {
> +	.module			= THIS_MODULE,
> +	.name			= "3ware 9000 Storage Controller",
> +	.info			= twa_info,
> +	.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,
> +	.shost_attrs		= twa_host_attrs,
> +	.emulated		= 1
> +};
> +
> +/* This function will probe and initialize a card */
> +static int __devinit twa_probe(struct pci_dev *pdev, const struct pci_device_id *dev_id)
> +{
> +	struct Scsi_Host *host = NULL;
> +	TW_Device_Extension *tw_dev;
> +	u32 mem_addr, mem_len;
> +	int retval = -ENODEV;
> +
> +	if (pci_enable_device(pdev)) {
> +		TW_PRINTK(host, TW_DRIVER, 0x32, "Failed to enable pci device");
> +		goto out;
> +	}

propagate pci_enable_device return value to caller


> +	pci_set_master(pdev);
> +
> +	if (pci_set_dma_mask(pdev, TW_DMA_MASK)) {
> +		TW_PRINTK(host, TW_DRIVER, 0x23, "Failed to set dma mask");
> +		goto out;
> +	}

propagate pci_set_dma_mask return value to caller


> +	host = scsi_host_alloc(&driver_template, sizeof(TW_Device_Extension));
> +	if (!host) {
> +		TW_PRINTK(host, TW_DRIVER, 0x24, "Failed to allocate memory for device extension");
> +		retval = -ENOMEM;
> +		goto out;
> +	}
> +	tw_dev = (TW_Device_Extension *)host->hostdata;
> +
> +	memset(tw_dev, 0, sizeof(TW_Device_Extension));
> +
> +	/* Save values to device extension */
> +	tw_dev->host = host;
> +	tw_dev->tw_pci_dev = pdev;
> +
> +	if (twa_initialize_device_extension(tw_dev)) {
> +		TW_PRINTK(tw_dev->host, TW_DRIVER, 0x25, "Failed to initialize device extension");
> +		goto out_free_device_extension;
> +	}
> +
> +	mem_addr = pci_resource_start(pdev, 1);
> +	mem_len = pci_resource_len(pdev, 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");
> +		goto out_free_device_extension;
> +	}
> +
> +	/* Reserve the mem address space */
> +	request_mem_region(mem_addr, mem_len, TW_DEVICE_NAME);

bogus.  First, check_mem_region is racy.

Moreover, you should be using pci_request_regions() and 
pci_release_regions(), not request_mem_region.


> +	/* Save base address */
> +	tw_dev->base_addr = ioremap(mem_addr, PAGE_SIZE);

check for NULL return


> +	/* Disable interrupts on the card */
> +	TW_DISABLE_INTERRUPTS(tw_dev);

don't you think you enable busmastering _after_ you do this?


> +	/* Initialize the card */
> +	if (twa_reset_sequence(tw_dev, 0))
> +		goto out_release_mem_region;
> +
> +	/* Set host specific parameters */
> +	host->max_id = TW_MAX_UNITS;
> +	host->max_cmd_len = TW_MAX_CDB_LEN;
> +	host->max_lun = 0;
> +	host->max_channel = 0;

max_lun and max_channel should be greater than zero, right?


> +	/* Register the card with the kernel SCSI layer */
> +	retval = scsi_add_host(host, &pdev->dev);
> +	if (retval) {
> +		TW_PRINTK(tw_dev->host, TW_DRIVER, 0x27, "scsi add host failed");
> +		goto out_release_mem_region;
> +	}
> +
> +	pci_set_drvdata(pdev, host);
> +
> +	printk(KERN_WARNING "3w-9xxx: scsi%d: Found a 3ware 9000 Storage Controller at 0x%x, IRQ: %d.\n", host->host_no, mem_addr, pdev->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));
> +
> +	/* Now setup the interrupt handler */
> +	if (twa_setup_irq(tw_dev))
> +		goto out_remove_host;
> +
> +	twa_device_extension_list[twa_device_extension_count] = tw_dev;
> +	twa_device_extension_count++;
> +
> +	/* Re-enable interrupts on the card */
> +	TW_ENABLE_AND_CLEAR_INTERRUPTS(tw_dev);
> +
> +	/* Finally, scan the host */
> +	scsi_scan_host(host);
> +
> +	if (twa_major == -1) {
> +		if ((twa_major = register_chrdev (0, "twa", &twa_fops)) < 0)
> +			TW_PRINTK(host, TW_DRIVER, 0x29, "Failed to register character device");
> +	}
> +	return 0;
> +
> +out_remove_host:
> +	scsi_remove_host(host);
> +out_release_mem_region:
> +	release_mem_region(mem_addr, mem_len);

pci_release_regions


> +out_free_device_extension:
> +	twa_free_device_extension(tw_dev);
> +	scsi_host_put(host);
> +out:

missing pci_disable_device, complementary to pci_enable_device


> +	return retval;
> +} /* End twa_probe() */
> +
> +/* This function is called to remove a device */
> +static void twa_remove(struct pci_dev *pdev)
> +{
> +	struct Scsi_Host *host = pci_get_drvdata(pdev);
> +	TW_Device_Extension *tw_dev = (TW_Device_Extension *)host->hostdata;
> +
> +	scsi_remove_host(tw_dev->host);
> +
> +	__twa_shutdown(tw_dev);
> +
> +	/* Free up the IRQ */
> +	free_irq(tw_dev->tw_pci_dev->irq, tw_dev);
> +
> +	/* 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 device extension resources */
> +	twa_free_device_extension(tw_dev);
> +
> +	scsi_host_put(tw_dev->host);
> +	pci_disable_device(pdev);
> +	twa_device_extension_count--;

BUG -- if you register the chrdev in PCI ->probe, you must unregister it 
here.



> +} /* End twa_remove() */
> +
> +/* PCI Devices supported by this driver */
> +static struct pci_device_id twa_pci_tbl[] __devinitdata = {
> +	{ PCI_VENDOR_ID_3WARE, PCI_DEVICE_ID_3WARE_9000,
> +	  PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
> +	{ }
> +};
> +MODULE_DEVICE_TABLE(pci, twa_pci_tbl);
> +
> +/* pci_driver initializer */
> +static struct pci_driver twa_driver = {
> +	.name		= "3w-9xxx",
> +	.id_table	= twa_pci_tbl,
> +	.probe		= twa_probe,
> +	.remove		= twa_remove,
> +	.driver		= {
> +		.shutdown = twa_shutdown
> +	}
> +};
> +
> +/* This function is called on driver initialization */
> +static int __init twa_init(void)
> +{
> +	printk(KERN_WARNING "3ware 9000 Storage Controller device driver for Linux v%s.\n", twa_driver_version);
> +
> +	pci_register_driver(&twa_driver);
> +
> +	return 0;
> +} /* End twa_init() */

wrong.  do

	return pci_module_init(&twa_driver)


> +/* This function is called on driver exit */
> +static void __exit twa_exit(void)
> +{
> +	/* Unregister character device */
> +	if (twa_major >= 0) {
> +		unregister_chrdev(twa_major, "twa");
> +		twa_major = -1;
> +	}

see above


> +	pci_unregister_driver(&twa_driver);
> +} /* End twa_exit() */
> +
> +module_init(twa_init);
> +module_exit(twa_exit);


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

* RE: [PATCH/RFC 1/2] 3ware 9000 SATA-RAID driver v2.26.00.009
@ 2004-05-26 21:59 Adam Radford
  2004-05-27 17:20 ` Jeff Garzik
  0 siblings, 1 reply; 5+ messages in thread
From: Adam Radford @ 2004-05-26 21:59 UTC (permalink / raw)
  To: Jeff Garzik; +Cc: akpm, james.bottomley, hch, linux-scsi

Jeff,

Thanks for the useful feedback.

- The card can take up to 256 io's at a time, but 2 are marked off
  for internal use.  There are no 'per device' limits.  The firmware queue
  depth is 256 total for all devices.
- PAE fix involves removing #ifdef BITS_PER_LONG's, and using sizeof(dma_addr_t)
  or some such mechanism to tell the firmware to use 64-bit sgl mode even when
  sizeof(long) = 4 bytes.
- We are not calling do_gettimeofday() on every command sent to hardware.  It is
  only sent when firmware requests an AEN_SYNC_TIME, which happens at startup, and
  probably once a day thereafter.  This is so we can have scheduled events in the
  firmware, such as rebuild events, scrub events, etc, _without_ an annoying daemon
  running.  There is clock drift so we periodically sync the time.
- The ioctl routine _does_ copy down data buffers.  This isn't in the main IO path.
- twa_poll_status_gone() is called to prevent return from a function until the
  hardware is ready.  How else do you propose delaying when the scsi midlayer called
  your entrypoint with spin_lock_irqsave(host->host_lock, flags) ?

I will fix some of the issues you mention and re-send.  Thanks!

--
Adam Radford
Staff Software Engineer
AMCC


-----Original Message-----
From: Jeff Garzik [mailto:jgarzik@pobox.com]
Sent: Wednesday, May 26, 2004 2:42 PM
To: Adam Radford
Cc: akpm@osdl.org; james.bottomley@steeleye.com; hch@lst.de;
linux-scsi@vger.kernel.org
Subject: Re: [PATCH/RFC 1/2] 3ware 9000 SATA-RAID driver v2.26.00.009


Adam Radford wrote:


Overall:  your driver duplicates some SCSI infrastructure for sending
SCSI commands, and waiting for their replies.



> Changes in this release:
>    - Convert driver to pci_driver format.

yay!


>    - Return SCSI_MLQUEUE_HOST_BUSY when card status is busy.

Does the hardware have some sort of metric, like "queued commands < 32",
to better determine when card is busy?

Normally, you want to set up the system such that SCSI stops calling
your ->queuecommand() hook when there is no more room.

SCSI layer provides device-wide and host-wide queueing limits.


> Future versions may include:
>    - PAE fix

What does this involve?


>    - 80 column wrap fixes
>    - Remove more prototypes by re-ordering functions.

Bah, who cares...  Maintainer's choice, IMO.



> 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-26 13:17:24.000000000 -0700
> @@ -0,0 +1,2144 @@
> +/*
> +   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.
> +   2.26.00.009 - Convert driver to pci_driver format.
> +                 Remove proc interface, add sysfs attributes.
> +                 Return SCSI_MLQUEUE_HOST_BUSY when card status is busy.
> +*/
> +
> +#include <linux/module.h>
> +#include <linux/reboot.h>
> +#include <linux/spinlock.h>
> +#include <linux/interrupt.h>
> +#include <linux/moduleparam.h>
> +#include <linux/errno.h>
> +#include <linux/types.h>
> +#include <linux/delay.h>
> +#include <linux/pci.h>
> +#include <linux/time.h>
> +#include <asm/io.h>
> +#include <asm/irq.h>
> +#include <asm/uaccess.h>
> +#include <scsi/scsi.h>
> +#include <scsi/scsi_host.h>
> +#include <scsi/scsi_tcq.h>
> +#include <scsi/scsi_cmnd.h>
> +#include "3w-9xxx.h"
> +
> +/* Globals */
> +char *twa_driver_version="2.26.00.009";

mark 'static', possibly 'const' too.


> +TW_Device_Extension *twa_device_extension_list[TW_MAX_SLOT];

static


> +static unsigned int twa_device_extension_count;
> +static int twa_major = -1;
> +extern struct timezone sys_tz;
> +static int cmds_per_lun;
> +
> +/* Module parameters */
> +MODULE_AUTHOR ("AMCC");
> +MODULE_DESCRIPTION ("3ware 9000 Storage Controller Linux Driver");
> +MODULE_LICENSE("GPL");
> +module_param(cmds_per_lun, int, 0);
> +MODULE_PARM_DESC(cmds_per_lun, "Maximum commands per LUN");
> +
> +/* Function prototypes */
> +static int twa_aen_complete(TW_Device_Extension *tw_dev, int request_id);
> +static int twa_aen_drain_queue(TW_Device_Extension *tw_dev, int check_reset);
> +static void twa_aen_queue_event(TW_Device_Extension *tw_dev, TW_Command_Apache_Header *header);
> +static int twa_aen_read_queue(TW_Device_Extension *tw_dev, int request_id);
> +static char *twa_aen_severity_lookup(unsigned char severity_code);
> +static void twa_aen_sync_time(TW_Device_Extension *tw_dev, int request_id);
> +static int twa_allocate_memory(TW_Device_Extension *tw_dev, int size, int which);
> +static int twa_check_bits(u32 status_reg_value);
> +static int twa_check_srl(TW_Device_Extension *tw_dev, int *flashed);
> +static int twa_chrdev_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg);
> +static int twa_chrdev_open(struct inode *inode, struct file *file);
> +static int twa_decode_bits(TW_Device_Extension *tw_dev, u32 status_reg_value);
> +static int twa_empty_response_queue(TW_Device_Extension *tw_dev);
> +static int twa_fill_sense(TW_Device_Extension *tw_dev, int request_id, int copy_sense, int print_host);
> +static void twa_free_device_extension(TW_Device_Extension *tw_dev);
> +static void twa_free_request_id(TW_Device_Extension *tw_dev,int request_id);
> +static void *twa_get_param(TW_Device_Extension *tw_dev, int request_id, int table_id, int parameter_id, int parameter_size_bytes);
> +static void twa_get_request_id(TW_Device_Extension *tw_dev, int *request_id);
> +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);
> +static int twa_initialize_device_extension(TW_Device_Extension *tw_dev);
> +static irqreturn_t twa_interrupt(int irq, void *dev_instance, struct pt_regs *regs);
> +static void twa_load_sgl(TW_Command_Full *full_command_packet, int request_id, dma_addr_t dma_handle, int length);
> +static int twa_map_scsi_sg_data(TW_Device_Extension *tw_dev, int request_id);
> +static dma_addr_t twa_map_scsi_single_data(TW_Device_Extension *tw_dev, int request_id);
> +static int twa_poll_response(TW_Device_Extension *tw_dev, int request_id, int seconds);
> +static int twa_poll_status(TW_Device_Extension *tw_dev, u32 flag, int seconds);
> +static int twa_poll_status_gone(TW_Device_Extension *tw_dev, u32 flag, int seconds);
> +static int twa_post_command_packet(TW_Device_Extension *tw_dev, int request_id, char internal);
> +static int twa_reset_device_extension(TW_Device_Extension *tw_dev);
> +static int twa_reset_sequence(TW_Device_Extension *tw_dev, int soft_reset);
> +static int twa_scsi_biosparam(struct scsi_device *sdev, struct block_device *bdev, sector_t capacity, int geom[]);
> +static int __devinit twa_probe(struct pci_dev *pdev, const struct pci_device_id *dev_id);
> +static int twa_scsi_eh_abort(struct scsi_cmnd *SCpnt);
> +static int twa_scsi_eh_reset(struct scsi_cmnd *SCpnt);
> +static int twa_scsi_queue(struct scsi_cmnd *cmd, void (*done) (struct scsi_cmnd *));
> +static int twa_scsiop_execute_scsi(TW_Device_Extension *tw_dev, int request_id, char *cdb, int use_sg, TW_SG_Apache *sglistarg);
> +static void twa_scsiop_execute_scsi_complete(TW_Device_Extension *tw_dev, int request_id);
> +static int twa_setup_irq(TW_Device_Extension *tw_dev);
> +static int twa_slave_configure(struct scsi_device *SDptr);
> +static char *twa_string_lookup(twa_message_type *table, unsigned int aen_code);
> +static void twa_unmap_scsi_data(TW_Device_Extension *tw_dev, int request_id);
> +
> +/* Functions */
> +
> +/* Show some statistics about the card */
> +static ssize_t twa_show_stats(struct class_device *class_dev, char *buf)
> +{
> +	struct Scsi_Host *host = class_to_shost(class_dev);
> +	TW_Device_Extension *tw_dev = (TW_Device_Extension *)host->hostdata;
> +	unsigned long flags = 0;
> +	ssize_t len;
> +
> +	spin_lock_irqsave(tw_dev->host->host_lock, flags);
> +	len = snprintf(buf, PAGE_SIZE, "Driver version: %s\n"
> +		       "Current commands posted:   %4d\n"
> +		       "Max commands posted:       %4d\n"
> +		       "Current pending commands:  %4d\n"
> +		       "Max pending commands:      %4d\n"
> +		       "Last sgl length:           %4d\n"
> +		       "Max sgl length:            %4d\n"
> +		       "Last sector count:         %4d\n"
> +		       "Max sector count:          %4d\n"
> +		       "SCSI Host Resets:          %4d\n"
> +		       "SCSI Aborts/Timeouts:      %4d\n"
> +		       "AEN's:                     %4d\n",
> +		       twa_driver_version,
> +		       tw_dev->posted_request_count,
> +		       tw_dev->max_posted_request_count,
> +		       tw_dev->pending_request_count,
> +		       tw_dev->max_pending_request_count,
> +		       tw_dev->sgl_entries,
> +		       tw_dev->max_sgl_entries,
> +		       tw_dev->sector_count,
> +		       tw_dev->max_sector_count,
> +		       tw_dev->num_resets,
> +		       tw_dev->num_aborts,
> +		       tw_dev->aen_count);
> +	spin_unlock_irqrestore(tw_dev->host->host_lock, flags);
> +	return len;
> +} /* End twa_show_stats() */

surely sysfs allows use of seq_file API???


> +/* File operations struct for character device */
> +static struct file_operations twa_fops = {
> +	.owner		= THIS_MODULE,
> +	.ioctl		= twa_chrdev_ioctl,
> +	.open		= twa_chrdev_open,
> +	.release	= NULL
> +};
> +
> +/* 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;

kill unneeded casts


> +	/* 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);

are we really calling do_gettimeofday() on every command sent to
hardware?  Can this be avoided?


> +	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, 1);
> +} /* 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[iminor(inode)];
> +	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;

why not return -EINVAL and require userland to align?


> +	/* 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;

are you really copying all the user data buffers here?

That will be godawful slow.

I would suggest building and sending a struct request instead.


> +	/* 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, 1);
> +		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);

surely there is a better way to do this... semaphore?

down() here, and up() in irq handler or timer.


> +		/* 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 = iminor(inode);
> +	if (minor_number >= twa_device_extension_count)
> +		goto out;
> +	retval = 0;
> +out:
> +	return retval;
> +} /* End twa_chrdev_open() */
> +
> +/* 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, 1);
> +
> +	/* 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]);

just use standard SCSI code to issue a SCSI command, and wait for a
response.


> +	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() */
> +
> +/* 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, 1);
> +
> +	/* 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() */

ditto


> +/* 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;
> +	}

allocate with GFP_KERNEL, GFP_ATOMIC is not necessary, and it depletes
the emergency pools



> +	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;
> +
> +	init_MUTEX(&tw_dev->ioctl_sem);
> +	init_waitqueue_head(&tw_dev->ioctl_wqueue);
> +
> +	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;

completely bogus test.  remove.  remove TW_IN_INTR too.


> +	/* Get the per adapter lock */
> +	spin_lock_irqsave(tw_dev->host->host_lock, flags);

spin_lock(), not spin_lock_irqsave()


> +	/* 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;

also check for 0xffffffff return value, indicating hardware removed or
hardware fault.


> +		/* 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, 1)==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;
> +	struct scsi_cmnd *cmd = tw_dev->srb[request_id];
> +	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_BIDIRECTIONAL);
> +
> +	if (use_sg == 0) {
> +		TW_PRINTK(tw_dev->host, TW_DRIVER, 0x1c, "Failed to map scatter gather list");
> +		goto out;
> +	}
> +
> +	cmd->SCp.phase = 2;

Don't use magic numbers for phases, use a descriptive constant



> +	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;
> +	struct scsi_cmnd *cmd = tw_dev->srb[request_id];
> +	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_BIDIRECTIONAL);
> +
> +	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);
> +	}

bzzzz.  this delay loop is _completely_ unacceptable.  It can hang the
machine for far too long.  Use a timer, or schedule_timeout() in process
context.



> +	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:

another bogus delay loop



> +/* 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, char internal)
> +{
> +	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)) {
> +
> +		/* Only pend internal driver commands */
> +		if (!internal) {
> +			retval = SCSI_MLQUEUE_HOST_BUSY;
> +			goto out;
> +		}
> +
> +		/* 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

PCI posting?


> +		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;
> +
> +	if (capacity >= 0x200000) {
> +		heads = 255;
> +		sectors = 63;
> +		cylinders = sector_div(capacity, heads * sectors);
> +	} else {
> +		heads = 64;
> +		sectors = 32;
> +		cylinders = sector_div(capacity, heads * sectors);
> +	}
> +
> +	geom[0] = heads;
> +	geom[1] = sectors;
> +	geom[2] = cylinders;
> +
> +	return 0;
> +} /* End twa_scsi_biosparam() */
> +
> +/* This is the new scsi eh abort function */
> +static int twa_scsi_eh_abort(struct 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(struct 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 is the main scsi queue function to handle scsi opcodes */
> +static int twa_scsi_queue(struct scsi_cmnd *SCpnt, void (*done)(struct scsi_cmnd *))
> +{
> +	int request_id, retval;
> +	TW_Device_Extension *tw_dev = (TW_Device_Extension *)SCpnt->device->host->hostdata;
> +
> +	/* 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;

you need to save the 'done' hook too


> +	/* Initialize phase to zero */
> +	SCpnt->SCp.phase = 0;
> +
> +	retval = twa_scsiop_execute_scsi(tw_dev, request_id, NULL, 0, NULL);
> +	switch (retval) {
> +	case SCSI_MLQUEUE_HOST_BUSY:
> +		twa_free_request_id(tw_dev, request_id);
> +		break;
> +	case 1:
> +		tw_dev->state[request_id] = TW_S_COMPLETED;
> +		twa_free_request_id(tw_dev, request_id);
> +		SCpnt->result = (DID_ERROR << 16);
> +		done(SCpnt);
> +	}
> +
> +	return retval;
> +} /* End twa_scsi_queue() */
> +
> +/* 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;
> +	struct 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 */
> +	if (srb) {
> +		retval = twa_post_command_packet(tw_dev, request_id, 0);
> +	} else {
> +		twa_post_command_packet(tw_dev, request_id, 1);
> +		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 badly needs line wrapping

Greater-than-80 columns is ok, but completely invisible code off the
right of the screen is annoying to review.


> +
> +/* 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() */

delete, and call directly


> +/* This function tells the controller to shut down */
> +static void __twa_shutdown(TW_Device_Extension *tw_dev)
> +{
> +	/* Disable interrupts */
> +	TW_DISABLE_INTERRUPTS(tw_dev);
> +
> +	printk(KERN_WARNING "3w-9xxx: Shutting down host %d.\n", tw_dev->host->host_no);
> +
> +	/* Tell the card we are shutting down */
> +	if (twa_initconnection(tw_dev, 1, 0, 0, 0, 0, 0, NULL, NULL, NULL, NULL, NULL)) {

That's way too many arguments to the function.  Create a "struct
twa_initconnection_info" or similar...


> +		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() */
> +
> +/* Wrapper for __twa_shutdown */
> +static void twa_shutdown(struct device *dev)
> +{
> +	struct Scsi_Host *host = pci_get_drvdata(to_pci_dev(dev));
> +	TW_Device_Extension *tw_dev = (TW_Device_Extension *)host->hostdata;
> +
> +	__twa_shutdown(tw_dev);
> +} /* End twa_shutdown() */
> +
> +/* This function will configure individual target parameters */
> +static int twa_slave_configure(struct 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)
> +{
> +	struct scsi_cmnd *cmd = tw_dev->srb[request_id];
> +	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_BIDIRECTIONAL);
> +		break;
> +	case 2:
> +		pci_unmap_sg(pdev, cmd->request_buffer, cmd->use_sg, DMA_BIDIRECTIONAL);
> +		break;
> +	}
> +} /* End twa_unmap_scsi_data() */
> +
> +/* Get informationa bout the card/driver */
> +static const char *twa_info(struct Scsi_Host *host)
> +{
> +	static char buffer[512];
> +
> +	sprintf(buffer, "3ware 9000 Storage Controller");
> +
> +	return buffer;
> +} /* End twa_info() */

I thought the ->info hook was deprecated?


> +/* scsi_host_template initializer */
> +static struct scsi_host_template driver_template = {
> +	.module			= THIS_MODULE,
> +	.name			= "3ware 9000 Storage Controller",
> +	.info			= twa_info,
> +	.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,
> +	.shost_attrs		= twa_host_attrs,
> +	.emulated		= 1
> +};
> +
> +/* This function will probe and initialize a card */
> +static int __devinit twa_probe(struct pci_dev *pdev, const struct pci_device_id *dev_id)
> +{
> +	struct Scsi_Host *host = NULL;
> +	TW_Device_Extension *tw_dev;
> +	u32 mem_addr, mem_len;
> +	int retval = -ENODEV;
> +
> +	if (pci_enable_device(pdev)) {
> +		TW_PRINTK(host, TW_DRIVER, 0x32, "Failed to enable pci device");
> +		goto out;
> +	}

propagate pci_enable_device return value to caller


> +	pci_set_master(pdev);
> +
> +	if (pci_set_dma_mask(pdev, TW_DMA_MASK)) {
> +		TW_PRINTK(host, TW_DRIVER, 0x23, "Failed to set dma mask");
> +		goto out;
> +	}

propagate pci_set_dma_mask return value to caller


> +	host = scsi_host_alloc(&driver_template, sizeof(TW_Device_Extension));
> +	if (!host) {
> +		TW_PRINTK(host, TW_DRIVER, 0x24, "Failed to allocate memory for device extension");
> +		retval = -ENOMEM;
> +		goto out;
> +	}
> +	tw_dev = (TW_Device_Extension *)host->hostdata;
> +
> +	memset(tw_dev, 0, sizeof(TW_Device_Extension));
> +
> +	/* Save values to device extension */
> +	tw_dev->host = host;
> +	tw_dev->tw_pci_dev = pdev;
> +
> +	if (twa_initialize_device_extension(tw_dev)) {
> +		TW_PRINTK(tw_dev->host, TW_DRIVER, 0x25, "Failed to initialize device extension");
> +		goto out_free_device_extension;
> +	}
> +
> +	mem_addr = pci_resource_start(pdev, 1);
> +	mem_len = pci_resource_len(pdev, 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");
> +		goto out_free_device_extension;
> +	}
> +
> +	/* Reserve the mem address space */
> +	request_mem_region(mem_addr, mem_len, TW_DEVICE_NAME);

bogus.  First, check_mem_region is racy.

Moreover, you should be using pci_request_regions() and
pci_release_regions(), not request_mem_region.


> +	/* Save base address */
> +	tw_dev->base_addr = ioremap(mem_addr, PAGE_SIZE);

check for NULL return


> +	/* Disable interrupts on the card */
> +	TW_DISABLE_INTERRUPTS(tw_dev);

don't you think you enable busmastering _after_ you do this?


> +	/* Initialize the card */
> +	if (twa_reset_sequence(tw_dev, 0))
> +		goto out_release_mem_region;
> +
> +	/* Set host specific parameters */
> +	host->max_id = TW_MAX_UNITS;
> +	host->max_cmd_len = TW_MAX_CDB_LEN;
> +	host->max_lun = 0;
> +	host->max_channel = 0;

max_lun and max_channel should be greater than zero, right?


> +	/* Register the card with the kernel SCSI layer */
> +	retval = scsi_add_host(host, &pdev->dev);
> +	if (retval) {
> +		TW_PRINTK(tw_dev->host, TW_DRIVER, 0x27, "scsi add host failed");
> +		goto out_release_mem_region;
> +	}
> +
> +	pci_set_drvdata(pdev, host);
> +
> +	printk(KERN_WARNING "3w-9xxx: scsi%d: Found a 3ware 9000 Storage Controller at 0x%x, IRQ: %d.\n", host->host_no, mem_addr, pdev->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));
> +
> +	/* Now setup the interrupt handler */
> +	if (twa_setup_irq(tw_dev))
> +		goto out_remove_host;
> +
> +	twa_device_extension_list[twa_device_extension_count] = tw_dev;
> +	twa_device_extension_count++;
> +
> +	/* Re-enable interrupts on the card */
> +	TW_ENABLE_AND_CLEAR_INTERRUPTS(tw_dev);
> +
> +	/* Finally, scan the host */
> +	scsi_scan_host(host);
> +
> +	if (twa_major == -1) {
> +		if ((twa_major = register_chrdev (0, "twa", &twa_fops)) < 0)
> +			TW_PRINTK(host, TW_DRIVER, 0x29, "Failed to register character device");
> +	}
> +	return 0;
> +
> +out_remove_host:
> +	scsi_remove_host(host);
> +out_release_mem_region:
> +	release_mem_region(mem_addr, mem_len);

pci_release_regions


> +out_free_device_extension:
> +	twa_free_device_extension(tw_dev);
> +	scsi_host_put(host);
> +out:

missing pci_disable_device, complementary to pci_enable_device


> +	return retval;
> +} /* End twa_probe() */
> +
> +/* This function is called to remove a device */
> +static void twa_remove(struct pci_dev *pdev)
> +{
> +	struct Scsi_Host *host = pci_get_drvdata(pdev);
> +	TW_Device_Extension *tw_dev = (TW_Device_Extension *)host->hostdata;
> +
> +	scsi_remove_host(tw_dev->host);
> +
> +	__twa_shutdown(tw_dev);
> +
> +	/* Free up the IRQ */
> +	free_irq(tw_dev->tw_pci_dev->irq, tw_dev);
> +
> +	/* 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 device extension resources */
> +	twa_free_device_extension(tw_dev);
> +
> +	scsi_host_put(tw_dev->host);
> +	pci_disable_device(pdev);
> +	twa_device_extension_count--;

BUG -- if you register the chrdev in PCI ->probe, you must unregister it
here.



> +} /* End twa_remove() */
> +
> +/* PCI Devices supported by this driver */
> +static struct pci_device_id twa_pci_tbl[] __devinitdata = {
> +	{ PCI_VENDOR_ID_3WARE, PCI_DEVICE_ID_3WARE_9000,
> +	  PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
> +	{ }
> +};
> +MODULE_DEVICE_TABLE(pci, twa_pci_tbl);
> +
> +/* pci_driver initializer */
> +static struct pci_driver twa_driver = {
> +	.name		= "3w-9xxx",
> +	.id_table	= twa_pci_tbl,
> +	.probe		= twa_probe,
> +	.remove		= twa_remove,
> +	.driver		= {
> +		.shutdown = twa_shutdown
> +	}
> +};
> +
> +/* This function is called on driver initialization */
> +static int __init twa_init(void)
> +{
> +	printk(KERN_WARNING "3ware 9000 Storage Controller device driver for Linux v%s.\n", twa_driver_version);
> +
> +	pci_register_driver(&twa_driver);
> +
> +	return 0;
> +} /* End twa_init() */

wrong.  do

	return pci_module_init(&twa_driver)


> +/* This function is called on driver exit */
> +static void __exit twa_exit(void)
> +{
> +	/* Unregister character device */
> +	if (twa_major >= 0) {
> +		unregister_chrdev(twa_major, "twa");
> +		twa_major = -1;
> +	}

see above


> +	pci_unregister_driver(&twa_driver);
> +} /* End twa_exit() */
> +
> +module_init(twa_init);
> +module_exit(twa_exit);



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

* Re: [PATCH/RFC 1/2] 3ware 9000 SATA-RAID driver v2.26.00.009
  2004-05-26 21:59 [PATCH/RFC 1/2] 3ware 9000 SATA-RAID driver v2.26.00.009 Adam Radford
@ 2004-05-27 17:20 ` Jeff Garzik
  2004-05-28  4:32   ` Matthew Wilcox
  0 siblings, 1 reply; 5+ messages in thread
From: Jeff Garzik @ 2004-05-27 17:20 UTC (permalink / raw)
  To: Adam Radford; +Cc: akpm, james.bottomley, hch, linux-scsi

Adam Radford wrote:
> Jeff,
> 
> Thanks for the useful feedback.
> 
> - The card can take up to 256 io's at a time, but 2 are marked off
>   for internal use.  There are no 'per device' limits.  The firmware queue
>   depth is 256 total for all devices.

Ok.

I would therefore recommend not setting device queue depth in 
->slave_configure(), but rather setting the Scsi_Host_Template's 
->can_queue to 256.

This more accurately reflects the hardware.


> - We are not calling do_gettimeofday() on every command sent to hardware.  It is
>   only sent when firmware requests an AEN_SYNC_TIME, which happens at startup, and
>   probably once a day thereafter.  This is so we can have scheduled events in the
>   firmware, such as rebuild events, scrub events, etc, _without_ an annoying daemon
>   running.  There is clock drift so we periodically sync the time.

I see the AEN_SYNC_TIME usage of do_gettimeofday(), but there are other 
uses of this function as well.


> - The ioctl routine _does_ copy down data buffers.  This isn't in the main IO path.

Still, you shouldn't need to copy all those buffers, just make sure they 
are present and mapped, then pass them to hardware.


> - twa_poll_status_gone() is called to prevent return from a function until the
>   hardware is ready.  How else do you propose delaying when the scsi midlayer called
>   your entrypoint with spin_lock_irqsave(host->host_lock, flags) ?

It's a problem with your model.  The ->queuecommand should 
asynchronously submit commands to hardware, up to the hardware limit.

twa_poll_status_done() AFAICS is being used during initialization and 
such, where ideally you should have code that issues a SCSI command, 
then calls scsi_wait_req or similar to wait for the command's completion.


> I will fix some of the issues you mention and re-send.  Thanks!

Thanks!

	Jeff




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

* Re: [PATCH/RFC 1/2] 3ware 9000 SATA-RAID driver v2.26.00.009
  2004-05-27 17:20 ` Jeff Garzik
@ 2004-05-28  4:32   ` Matthew Wilcox
  0 siblings, 0 replies; 5+ messages in thread
From: Matthew Wilcox @ 2004-05-28  4:32 UTC (permalink / raw)
  To: Jeff Garzik; +Cc: Adam Radford, akpm, james.bottomley, hch, linux-scsi

On Thu, May 27, 2004 at 01:20:27PM -0400, Jeff Garzik wrote:
> Adam Radford wrote:
> >- The card can take up to 256 io's at a time, but 2 are marked off
> >  for internal use.  There are no 'per device' limits.  The firmware queue
> >  depth is 256 total for all devices.
> 
> I would therefore recommend not setting device queue depth in 
> ->slave_configure(), but rather setting the Scsi_Host_Template's 
> ->can_queue to 256.
> 
> This more accurately reflects the hardware.

254, surely?  The midlayer can't be expected to know about the 2 reserved
ones ;-)

-- 
"Next the statesmen will invent cheap lies, putting the blame upon 
the nation that is attacked, and every man will be glad of those
conscience-soothing falsities, and will diligently study them, and refuse
to examine any refutations of them; and thus he will by and by convince 
himself that the war is just, and will thank God for the better sleep 
he enjoys after this process of grotesque self-deception." -- Mark Twain

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

end of thread, other threads:[~2004-05-28  4:32 UTC | newest]

Thread overview: 5+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2004-05-26 21:59 [PATCH/RFC 1/2] 3ware 9000 SATA-RAID driver v2.26.00.009 Adam Radford
2004-05-27 17:20 ` Jeff Garzik
2004-05-28  4:32   ` Matthew Wilcox
  -- strict thread matches above, loose matches on Subject: below --
2004-05-26 20:46 Adam Radford
2004-05-26 21:42 ` Jeff Garzik

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