]> git.hungrycats.org Git - linux/commitdiff
[PATCH] 3ware driver update for 2.5.6-pre3
authorAdam Radford <adam@nmt.edu>
Tue, 5 Mar 2002 07:16:17 +0000 (23:16 -0800)
committerLinus Torvalds <torvalds@penguin.transmeta.com>
Tue, 5 Mar 2002 07:16:17 +0000 (23:16 -0800)
 - Fix bug in tw_aen_complete() where aen's could be lost.
   Fix tw_aen_drain_queue() to display useful info at init.
   Set tw_host->max_id for 12 port cards.
   Add ioctl support for raw command packet post from userspace
   with sglist fragments (parameter and io).
 - Fix read capacity to under report by 1 sector to fix get
   last sector ioctl.
 - Fix bug where more AEN codes weren't coming out during
   driver initialization.
   Improved handling of PCI aborts.
 - Fix bug in tw_findcards() where AEN code could be lost.
   Increase timeout in tw_aen_drain_queue() to 30 seconds.
 - Re-write raw command post with data ioctl method.
   Remove raid5 bounce buffers for raid5 for 6XXX for kernel 2.5
   Add tw_map/unmap_scsi_sg/single_data() for kernel 2.5
   Replace io_request_lock with host_lock for kernel 2.5
   Set max_cmd_len to 16 for 3dm for kernel 2.5
 - Set host->max_sectors back up to 256.
 - Modified pci parity error handling/clearing from config space
   during initialization.
 - Better handling of request sense opcode and sense information
   for failed commands.  Add tw_decode_sense().
   Replace all mdelay()'s with scsi_sleep().
 - Revert mdelay's and scsi_sleep's, this caused problems on
   some SMP systems.
 - Add pci_set_dma_mask(), rewrite kmalloc()/virt_to_bus() to
   pci_alloc/free_consistent().

drivers/scsi/3w-xxxx.c
drivers/scsi/3w-xxxx.h

index eaaedb98017023068f1042565eaebf7cc86425cd..4b02451d2877afe12c72cb3fd407ac2d14e9c57f 100644 (file)
@@ -6,7 +6,7 @@
                     Arnaldo Carvalho de Melo <acme@conectiva.com.br>
                      Brad Strand <linux@3ware.com>
 
-   Copyright (C) 1999-2001 3ware Inc.
+   Copyright (C) 1999-2002 3ware Inc.
 
    Kernel compatablity By:     Andre Hedrick <andre@suse.com>
    Non-Copyright (C) 2000      Andre Hedrick <andre@suse.com>
                  Add entire aen code string list.
    1.02.00.010 - Cleanup queueing code, fix jbod thoughput.
                  Fix get_param for specific units.
+   1.02.00.011 - Fix bug in tw_aen_complete() where aen's could be lost.
+                 Fix tw_aen_drain_queue() to display useful info at init.
+                 Set tw_host->max_id for 12 port cards.
+                 Add ioctl support for raw command packet post from userspace
+                 with sglist fragments (parameter and io).
+   1.02.00.012 - Fix read capacity to under report by 1 sector to fix get
+                 last sector ioctl.
+   1.02.00.013 - Fix bug where more AEN codes weren't coming out during
+                 driver initialization.
+                 Improved handling of PCI aborts.
+   1.02.00.014 - Fix bug in tw_findcards() where AEN code could be lost.
+                 Increase timeout in tw_aen_drain_queue() to 30 seconds.
+   1.02.00.015 - Re-write raw command post with data ioctl method.
+                 Remove raid5 bounce buffers for raid5 for 6XXX for kernel 2.5
+                 Add tw_map/unmap_scsi_sg/single_data() for kernel 2.5
+                 Replace io_request_lock with host_lock for kernel 2.5
+                 Set max_cmd_len to 16 for 3dm for kernel 2.5
+   1.02.00.016 - Set host->max_sectors back up to 256.
+   1.02.00.017 - Modified pci parity error handling/clearing from config space
+                 during initialization.
+   1.02.00.018 - Better handling of request sense opcode and sense information
+                 for failed commands.  Add tw_decode_sense().
+                 Replace all mdelay()'s with scsi_sleep().
+   1.02.00.019 - Revert mdelay's and scsi_sleep's, this caused problems on
+                 some SMP systems.
+   1.02.00.020 - Add pci_set_dma_mask(), rewrite kmalloc()/virt_to_bus() to
+                 pci_alloc/free_consistent().
 */
 
-#error Please convert me to Documentation/DMA-mapping.txt
-
 #include <linux/module.h>
 
 MODULE_AUTHOR ("3ware Inc.");
 MODULE_DESCRIPTION ("3ware Storage Controller Linux Driver");
 MODULE_LICENSE("GPL");
 
-
 #include <linux/kernel.h>
 #include <linux/pci.h>
 #include <linux/time.h>
@@ -148,14 +172,17 @@ static int tw_copy_info(TW_Info *info, char *fmt, ...);
 static void tw_copy_mem_info(TW_Info *info, char *data, int len);
 static void tw_interrupt(int irq, void *dev_instance, struct pt_regs *regs);
 static int tw_halt(struct notifier_block *nb, ulong event, void *buf);
+static int tw_map_scsi_sg_data(struct pci_dev *pdev, Scsi_Cmnd *cmd);
+static u32 tw_map_scsi_single_data(struct pci_dev *pdev, Scsi_Cmnd *cmd);
+static void tw_unmap_scsi_data(struct pci_dev *pdev, Scsi_Cmnd *cmd);
 
 /* Notifier block to get a notify on system shutdown/halt/reboot */
 static struct notifier_block tw_notifier = {
-  tw_halt, NULL, 0
+       tw_halt, NULL, 0
 };
 
 /* Globals */
-char *tw_driver_version="1.02.00.010";
+char *tw_driver_version="1.02.00.020";
 TW_Device_Extension *tw_device_extension_list[TW_MAX_SLOT];
 int tw_device_extension_count = 0;
 
@@ -166,6 +193,7 @@ int tw_aen_complete(TW_Device_Extension *tw_dev, int request_id)
 {
        TW_Param *param;
        unsigned short aen;
+       int error = 0;
 
        dprintk(KERN_WARNING "3w-xxxx: tw_aen_complete()\n");
        if (tw_dev->alignment_virtual_address[request_id] == NULL) {
@@ -184,12 +212,15 @@ int tw_aen_complete(TW_Device_Extension *tw_dev, int request_id)
                        if ((tw_aen_string[aen & 0xff][strlen(tw_aen_string[aen & 0xff])-1]) == '#') {
                                printk(KERN_WARNING "3w-xxxx: scsi%d: AEN: %s%d.\n", tw_dev->host->host_no, tw_aen_string[aen & 0xff], aen >> 8);
                        } else {
-                               printk(KERN_WARNING "3w-xxxx: scsi%d: AEN: %s.\n", tw_dev->host->host_no, tw_aen_string[aen & 0xff]);
+                               if (aen != 0x0) 
+                                       printk(KERN_WARNING "3w-xxxx: scsi%d: AEN: %s.\n", tw_dev->host->host_no, tw_aen_string[aen & 0xff]);
                        }
-               } else
+               } else {
                        printk(KERN_WARNING "3w-xxxx: scsi%d: Received AEN %d.\n", tw_dev->host->host_no, aen);
+               }
        }
-       tw_dev->aen_count++;
+       if (aen != 0x0) 
+               tw_dev->aen_count++;
 
        /* Now queue the code */
        tw_dev->aen_queue[tw_dev->aen_tail] = aen;
@@ -205,8 +236,18 @@ int tw_aen_complete(TW_Device_Extension *tw_dev, int request_id)
                        tw_dev->aen_head = tw_dev->aen_head + 1;
                }
        }
-       tw_dev->state[request_id] = TW_S_COMPLETED;
-       tw_state_request_finish(tw_dev, request_id);
+
+       if (aen != TW_AEN_QUEUE_EMPTY) {
+               error = tw_aen_read_queue(tw_dev, request_id);
+               if (error) {
+                       printk(KERN_WARNING "3w-xxxx: scsi%d: Error completing AEN.\n", tw_dev->host->host_no);
+                       tw_dev->state[request_id] = TW_S_COMPLETED;
+                       tw_state_request_finish(tw_dev, request_id);
+               }
+       } else {
+               tw_dev->state[request_id] = TW_S_COMPLETED;
+               tw_state_request_finish(tw_dev, request_id);
+       }
 
        return 0;
 } /* End tw_aen_complete() */
@@ -237,10 +278,11 @@ int tw_aen_drain_queue(TW_Device_Extension *tw_dev)
        status_reg_addr = tw_dev->registers.status_reg_addr;
        response_que_addr = tw_dev->registers.response_que_addr;
 
-       if (tw_poll_status(tw_dev, TW_STATUS_ATTENTION_INTERRUPT, 15)) {
+       if (tw_poll_status(tw_dev, TW_STATUS_ATTENTION_INTERRUPT, 30)) {
                dprintk(KERN_WARNING "3w-xxxx: tw_aen_drain_queue(): No attention interrupt for card %d.\n", tw_device_extension_count);
                return 1;
        }
+       tw_clear_attention_interrupt(tw_dev);
 
        /* Initialize command packet */
        if (tw_dev->command_packet_virtual_address[request_id] == NULL) {
@@ -288,7 +330,7 @@ int tw_aen_drain_queue(TW_Device_Extension *tw_dev)
        do {
                /* Post command packet */
                outl(command_que_value, command_que_addr);
-    
+
                /* Now poll for completion */
                for (i=0;i<imax;i++) {
                        mdelay(5);
@@ -311,8 +353,7 @@ int tw_aen_drain_queue(TW_Device_Extension *tw_dev)
                                if (command_packet->status != 0) {
                                        if (command_packet->flags != TW_AEN_TABLE_UNDEFINED) {
                                                /* Bad response */
-                                               dprintk(KERN_WARNING "3w-xxxx: tw_aen_drain_queue(): Bad response, status = 0x%x, flags = 0x%x.\n", command_packet->status, command_packet->flags);
-                                               tw_decode_error(tw_dev, command_packet->status, command_packet->flags, command_packet->byte3.unit);
+                                               tw_decode_sense(tw_dev, request_id, 0);
                                                return 1;
                                        } else {
                                                /* We know this is a 3w-1x00, and doesn't support aen's */
@@ -326,7 +367,7 @@ int tw_aen_drain_queue(TW_Device_Extension *tw_dev)
                                queue = 0;
                                switch (aen_code) {
                                        case TW_AEN_QUEUE_EMPTY:
-                                               dprintk(KERN_NOTICE "3w-xxxx: tw_aen_drain_queue(): Found TW_AEN_QUEUE_EMPTY.\n");
+                                               dprintk(KERN_WARNING "3w-xxxx: AEN: %s.\n", tw_aen_string[aen & 0xff]);
                                                if (first_reset != 1) {
                                                        continue;
                                                } else {
@@ -334,51 +375,28 @@ int tw_aen_drain_queue(TW_Device_Extension *tw_dev)
                                                }
                                                break;
                                        case TW_AEN_SOFT_RESET:
-                                               dprintk(KERN_NOTICE "3w-xxxx: tw_aen_drain_queue(): Found TW_AEN_SOFT_RESET.\n");
                                                if (first_reset == 0) {
                                                        first_reset = 1;
                                                } else {
+                                                       printk(KERN_WARNING "3w-xxxx: AEN: %s.\n", tw_aen_string[aen & 0xff]);
+                                                       tw_dev->aen_count++;
                                                        queue = 1;
                                                }
                                                break;
-                                       case TW_AEN_DEGRADED_MIRROR:
-                                               dprintk(KERN_NOTICE "3w-xxxx: tw_aen_drain_queue(): Found TW_AEN_DEGRADED_MIRROR.\n");
-                                               queue = 1;
-                                               break;
-                                       case TW_AEN_CONTROLLER_ERROR:
-                                               dprintk(KERN_NOTICE "3w-xxxx: tw_aen_drain_queue(): Found TW_AEN_CONTROLLER_ERROR.\n");
-                                               queue = 1;
-                                               break;
-                                       case TW_AEN_REBUILD_FAIL:
-                                               dprintk(KERN_NOTICE "3w-xxxx: tw_aen_drain_queue(): Found TW_AEN_REBUILD_FAIL.\n");
-                                               queue = 1;
-                                               break;
-                                       case TW_AEN_REBUILD_DONE:
-                                               dprintk(KERN_NOTICE "3w-xxxx: tw_aen_drain_queue(): Found TW_AEN_REBUILD_DONE.\n");
-                                               queue = 1;
-                                               break;
-                                       case TW_AEN_QUEUE_FULL:
-                                               dprintk(KERN_NOTICE "3w-xxxx: tw_aen_drain_queue(): Found TW_AEN_QUEUE_FULL.\n");
-                                               queue = 1;
-                                               break;
-                                       case TW_AEN_APORT_TIMEOUT:
-                                               printk(KERN_WARNING "3w-xxxx: Received drive timeout AEN on port %d, check drive and drive cables.\n", aen >> 8);
-                                               queue = 1;
-                                               break;
-                                       case TW_AEN_DRIVE_ERROR:
-                                               printk(KERN_WARNING "3w-xxxx: Received drive error AEN on port %d, check/replace cabling, or possible bad drive.\n", aen >> 8);
-                                               queue = 1;
-                                               break;
-                                       case TW_AEN_SMART_FAIL:
-                                               printk(KERN_WARNING "3w-xxxx: Received S.M.A.R.T. threshold AEN on port %d, check drive/cooling, or possible bad drive.\n", aen >> 8);
-                                               queue = 1;
-                                               break;
-                                       case TW_AEN_SBUF_FAIL:
-                                               printk(KERN_WARNING "3w-xxxx: Received SBUF integrity check failure AEN, reseat card or bad card.\n");
-                                               queue = 1;
-                                               break;
                                        default:
-                                               dprintk(KERN_WARNING "3w-xxxx: tw_aen_drain_queue(): Unknown AEN code 0x%x.\n", aen_code);
+                                               if (aen == 0x0ff) {
+                                                       printk(KERN_WARNING "3w-xxxx: AEN: AEN queue overflow.\n");
+                                               } else {
+                                                       if ((aen & 0x0ff) < TW_AEN_STRING_MAX) {
+                                                               if ((tw_aen_string[aen & 0xff][strlen(tw_aen_string[aen & 0xff])-1]) == '#') {
+                                                                       printk(KERN_WARNING "3w-xxxx: AEN: %s%d.\n", tw_aen_string[aen & 0xff], aen >> 8);
+                                                               } else {
+                                                                       printk(KERN_WARNING "3w-xxxx: AEN: %s.\n", tw_aen_string[aen & 0xff]);
+                                                               }
+                                                       } else
+                                                               printk(KERN_WARNING "3w-xxxx: Received AEN %d.\n", aen);
+                                               }
+                                               tw_dev->aen_count++;
                                                queue = 1;
                                }
 
@@ -488,40 +506,44 @@ int tw_aen_read_queue(TW_Device_Extension *tw_dev, int request_id)
        return 0;
 } /* End tw_aen_read_queue() */
 
-/* This function will allocate memory and check if it is 16 d-word aligned */
-int tw_allocate_memory(TW_Device_Extension *tw_dev, int request_id, int size, int which)
+/* This function will allocate memory */
+int tw_allocate_memory(TW_Device_Extension *tw_dev, int size, int which)
 {
-       u32 *virt_addr = kmalloc(size, GFP_ATOMIC);
+       int i;
+       dma_addr_t dma_handle;
+       u32 *cpu_addr = NULL;
 
        dprintk(KERN_NOTICE "3w-xxxx: tw_allocate_memory()\n");
 
-       if (!virt_addr) {
-               printk(KERN_WARNING "3w-xxxx: tw_allocate_memory(): kmalloc() failed.\n");
-               return 1;
-       }
+       for (i=0;i<TW_Q_LENGTH;i++) {
+               cpu_addr = pci_alloc_consistent(tw_dev->tw_pci_dev, size, &dma_handle);
+               if (cpu_addr == NULL) {
+                       printk(KERN_WARNING "3w-xxxx: pci_alloc_consistent() failed.\n");
+                       return 1;
+               }
 
-       if ((u32)virt_addr % TW_ALIGNMENT) {
-               kfree(virt_addr);
-               printk(KERN_WARNING "3w-xxxx: tw_allocate_memory(): Found unaligned address.\n");
-               return 1;
-       }
+               if ((u32)cpu_addr % (tw_dev->tw_pci_dev->device == TW_DEVICE_ID ? TW_ALIGNMENT_6000 : TW_ALIGNMENT_7000)) {
+                       printk(KERN_WARNING "3w-xxxx: Couldn't allocate correctly aligned memory.\n");
+                       return 1;
+               }
 
-       switch(which) {
-       case 0:
-               tw_dev->command_packet_virtual_address[request_id] = virt_addr;
-               tw_dev->command_packet_physical_address[request_id] = virt_to_bus(virt_addr);
-               break;
-       case 1:
-               tw_dev->alignment_virtual_address[request_id] = virt_addr;
-               tw_dev->alignment_physical_address[request_id] = virt_to_bus(virt_addr);
-               break;
-       case 2:
-               tw_dev->bounce_buffer[request_id] = virt_addr;
-               break;
-       default:
-               printk(KERN_WARNING "3w-xxxx: tw_allocate_memory(): case slip in tw_allocate_memory()\n");
-               return 1;
+               switch(which) {
+               case 0:
+                       tw_dev->command_packet_virtual_address[i] = cpu_addr;
+                       tw_dev->command_packet_physical_address[i] = dma_handle;
+                       memset(tw_dev->command_packet_virtual_address[i], 0, size);
+                       break;
+               case 1:
+                       tw_dev->alignment_virtual_address[i] = cpu_addr;
+                       tw_dev->alignment_physical_address[i] = dma_handle;
+                       memset(tw_dev->alignment_virtual_address[i], 0, size);
+                       break;
+               default:
+                       printk(KERN_WARNING "3w-xxxx: tw_allocate_memory(): case slip in tw_allocate_memory()\n");
+                       return 1;
+               }
        }
+
        return 0;
 } /* End tw_allocate_memory() */
 
@@ -548,8 +570,10 @@ int tw_check_errors(TW_Device_Extension *tw_dev)
        status_reg_addr = tw_dev->registers.status_reg_addr;
        status_reg_value = inl(status_reg_addr);
 
-       if (TW_STATUS_ERRORS(status_reg_value) || tw_check_bits(status_reg_value))
+       if (TW_STATUS_ERRORS(status_reg_value) || tw_check_bits(status_reg_value)) {
+               tw_decode_bits(tw_dev, status_reg_value);
                return 1;
+       }
 
        return 0;
 } /* End tw_check_errors() */
@@ -614,37 +638,62 @@ void tw_decode_bits(TW_Device_Extension *tw_dev, u32 status_reg_value)
        dprintk(KERN_WARNING "3w-xxxx: tw_decode_bits()\n");
        switch (status_reg_value & TW_STATUS_UNEXPECTED_BITS) {
        case TW_STATUS_PCI_PARITY_ERROR:
-               printk(KERN_WARNING "3w-xxxx: PCI Parity Error: Reseat card, move card, or buggy device on the bus.\n");
+               printk(KERN_WARNING "3w-xxxx: PCI Parity Error: clearing.\n");
                outl(TW_CONTROL_CLEAR_PARITY_ERROR, tw_dev->registers.control_reg_addr);
-               pci_write_config_word(tw_dev->tw_pci_dev, PCI_STATUS, TW_PCI_CLEAR_PARITY_ERRORS);
                break;
        case TW_STATUS_MICROCONTROLLER_ERROR:
                printk(KERN_WARNING "3w-xxxx: Microcontroller Error.\n");
                break;
+       case TW_STATUS_PCI_ABORT:
+               printk(KERN_WARNING "3w-xxxx: PCI Abort: clearing.\n");
+               outl(TW_CONTROL_CLEAR_PCI_ABORT, tw_dev->registers.control_reg_addr);
+               pci_write_config_word(tw_dev->tw_pci_dev, PCI_STATUS, TW_PCI_CLEAR_PCI_ABORT);
+               break;
   }
 } /* End tw_decode_bits() */
 
-/* This function will print readable messages from flags and status values */
-void tw_decode_error(TW_Device_Extension *tw_dev, unsigned char status, unsigned char flags, unsigned char unit)
+/* This function will return valid sense buffer information for failed cmds */
+void tw_decode_sense(TW_Device_Extension *tw_dev, int request_id, int fill_sense)
 {
-       dprintk(KERN_WARNING "3w-xxxx: tw_decode_error()\n");
-       switch (status) {
-       case 0xc7:
-               switch (flags) {
-               case 0x1b: 
-                       printk(KERN_WARNING "3w-xxxx: scsi%d: Drive timeout on unit %d, check drive and drive cables.\n", tw_dev->host->host_no, unit);
-                       break;
-               case 0x51:
-                       printk(KERN_WARNING "3w-xxxx: scsi%d: Unrecoverable drive error on unit %d, check/replace cabling, or possible bad drive.\n", tw_dev->host->host_no, unit);
-                       break;
-               default:
-                       printk(KERN_WARNING "3w-xxxx: scsi%d: Controller error: status = 0x%x, flags = 0x%x, unit #%d.\n", tw_dev->host->host_no, status, flags, unit);
+       int i, found=0;
+       TW_Command *command;
+
+        dprintk(KERN_WARNING "3w-xxxx: tw_decode_sense()\n");
+       command = (TW_Command *)tw_dev->command_packet_virtual_address[request_id];
+
+       printk(KERN_WARNING "3w-xxxx: scsi%d: Command failed: status = 0x%x, flags = 0x%x, unit #%d.\n", tw_dev->host->host_no, command->status, command->flags, command->byte3.unit);
+
+       /* Attempt to return intelligent sense information */
+       if (fill_sense) {
+               if ((command->status == 0xc7) || (command->status == 0xcb)) {
+                       for (i=0;i<(sizeof(tw_sense_table)/sizeof(tw_sense_table[0]));i++) {
+                               if (command->flags == tw_sense_table[i][0]) {
+                                       found=1;
+
+                                       /* Valid bit and 'current errors' */
+                                       tw_dev->srb[request_id]->sense_buffer[0] = (0x1 << 7 | 0x70);
+
+                                       /* Sense key */
+                                       tw_dev->srb[request_id]->sense_buffer[2] = tw_sense_table[i][1];
+
+                                       /* Additional sense length */
+                                       tw_dev->srb[request_id]->sense_buffer[7] = 0xa; /* 10 bytes */
+
+                                       /* Additional sense code */
+                                       tw_dev->srb[request_id]->sense_buffer[12] = tw_sense_table[i][2];
+
+                                       /* Additional sense code qualifier */
+                                       tw_dev->srb[request_id]->sense_buffer[13] = tw_sense_table[i][3];
+
+                                       tw_dev->srb[request_id]->result = (DID_OK << 16) | (CHECK_CONDITION << 1);
+                               }
+                       }
                }
-               break;
-       default:
-               printk(KERN_WARNING "3w-xxxx: scsi%d: Controller error: status = 0x%x, flags = 0x%x, unit #%d.\n", tw_dev->host->host_no, status, flags, unit);
+               /* If no table match, error so we get a reset */
+               if (found == 0)
+                       tw_dev->srb[request_id]->result = (DID_RESET << 16);
        }
-} /* End tw_decode_error() */
+} /* End tw_decode_sense() */
 
 /* This function will disable interrupts on the controller */  
 void tw_disable_interrupts(TW_Device_Extension *tw_dev) 
@@ -690,12 +739,23 @@ void tw_enable_interrupts(TW_Device_Extension *tw_dev)
 {
        u32 control_reg_value, control_reg_addr;
 
+       control_reg_addr = tw_dev->registers.control_reg_addr;
+       control_reg_value = (TW_CONTROL_ENABLE_INTERRUPTS |
+                            TW_CONTROL_UNMASK_RESPONSE_INTERRUPT);
+       outl(control_reg_value, control_reg_addr);
+} /* End tw_enable_interrupts() */
+
+/* This function will enable interrupts on the controller */
+void tw_enable_and_clear_interrupts(TW_Device_Extension *tw_dev)
+{
+       u32 control_reg_value, control_reg_addr;
+
        control_reg_addr = tw_dev->registers.control_reg_addr;
        control_reg_value = (TW_CONTROL_CLEAR_ATTENTION_INTERRUPT |
                             TW_CONTROL_UNMASK_RESPONSE_INTERRUPT |
                             TW_CONTROL_ENABLE_INTERRUPTS);
        outl(control_reg_value, control_reg_addr);
-} /* End tw_enable_interrupts() */
+} /* End tw_enable_and_clear_interrupts() */
 
 /* This function will find and initialize all cards */
 int tw_findcards(Scsi_Host_Template *tw_host) 
@@ -716,6 +776,13 @@ int tw_findcards(Scsi_Host_Template *tw_host)
                while ((tw_pci_dev = pci_find_device(TW_VENDOR_ID, device[i], tw_pci_dev))) {
                        if (pci_enable_device(tw_pci_dev))
                                continue;
+
+                       /* We only need 32-bit addressing for 5,6,7xxx cards */
+                       if (pci_set_dma_mask(tw_pci_dev, 0xffffffff)) {
+                               printk(KERN_WARNING "3w-xxxx: No suitable DMA available.\n");
+                               continue;
+                       }
+
                        /* Prepare temporary device extension */
                        tw_dev=(TW_Device_Extension *)kmalloc(sizeof(TW_Device_Extension), GFP_ATOMIC);
                        if (tw_dev == NULL) {
@@ -724,6 +791,9 @@ int tw_findcards(Scsi_Host_Template *tw_host)
                        }
                        memset(tw_dev, 0, sizeof(TW_Device_Extension));
 
+                       /* Save pci_dev struct to device extension */
+                       tw_dev->tw_pci_dev = tw_pci_dev;
+
                        error = tw_initialize_device_extension(tw_dev);
                        if (error) {
                                printk(KERN_WARNING "3w-xxxx: tw_findcards(): Couldn't initialize device extension for card %d.\n", numcards);
@@ -738,8 +808,6 @@ int tw_findcards(Scsi_Host_Template *tw_host)
                        tw_dev->registers.status_reg_addr = pci_resource_start(tw_pci_dev, 0) + 0x4;
                        tw_dev->registers.command_que_addr = pci_resource_start(tw_pci_dev, 0) + 0x8;
                        tw_dev->registers.response_que_addr = pci_resource_start(tw_pci_dev, 0) + 0xC;
-                       /* Save pci_dev struct to device extension */
-                       tw_dev->tw_pci_dev = tw_pci_dev;
 
                        /* Check for errors and clear them */
                        status_reg_value = inl(tw_dev->registers.status_reg_addr);
@@ -763,14 +831,14 @@ int tw_findcards(Scsi_Host_Template *tw_host)
                          
                                error = tw_aen_drain_queue(tw_dev);
                                if (error) {
-                                       printk(KERN_WARNING "3w-xxxx: tw_findcards(): No attention interrupt for card %d.\n", numcards);
+                                       printk(KERN_WARNING "3w-xxxx: AEN drain failed for card %d.\n", numcards);
                                        tries++;
                                        continue;
                                }
 
                                /* Check for controller errors */
                                if (tw_check_errors(tw_dev)) {
-                                       printk(KERN_WARNING "3w-xxxx: tw_findcards(): Controller errors found, soft resetting card %d.\n", numcards);
+                                       printk(KERN_WARNING "3w-xxxx: Controller errors found, retrying for card %d.\n", numcards);
                                        tries++;
                                        continue;
                                }
@@ -778,7 +846,7 @@ int tw_findcards(Scsi_Host_Template *tw_host)
                                /* Empty the response queue */
                                error = tw_empty_response_que(tw_dev);
                                if (error) {
-                                       printk(KERN_WARNING "3w-xxxx: tw_findcards(): Couldn't empty response queue for card %d.\n", numcards);
+                                       printk(KERN_WARNING "3w-xxxx: Couldn't empty response queue, retrying for card %d.\n", numcards);
                                        tries++;
                                        continue;
                                }
@@ -788,7 +856,7 @@ int tw_findcards(Scsi_Host_Template *tw_host)
                        }
 
                        if (tries >= TW_MAX_RESET_TRIES) {
-                               printk(KERN_WARNING "3w-xxxx: tw_findcards(): Controller error or no attention interrupt: giving up for card %d.\n", numcards);
+                               printk(KERN_WARNING "3w-xxxx: Controller errors, card not responding, check all cabling for card %d.\n", numcards);
                                tw_free_device_extension(tw_dev);
                                kfree(tw_dev);
                                continue;
@@ -818,7 +886,7 @@ int tw_findcards(Scsi_Host_Template *tw_host)
 
                        error = tw_initconnection(tw_dev, TW_INIT_MESSAGE_CREDITS);
                        if (error) {
-                               printk(KERN_WARNING "3w-xxxx: tw_findcards(): Couldn't initconnection for card %d.\n", numcards);
+                               printk(KERN_WARNING "3w-xxxx: Connection initialization failed for card %d.\n", numcards);
                                release_region((tw_dev->tw_pci_dev->resource[0].start), TW_IO_ADDRESS_RANGE);
                                tw_free_device_extension(tw_dev);
                                kfree(tw_dev);
@@ -827,29 +895,28 @@ int tw_findcards(Scsi_Host_Template *tw_host)
 
                        /* Calculate max cmds per lun, and setup queues */
                        if (tw_dev->num_units > 0) {
-                               if ((tw_dev->num_raid_five > 0) && (tw_dev->tw_pci_dev->device == TW_DEVICE_ID)) {
-                                       tw_host->cmd_per_lun = (TW_MAX_BOUNCEBUF-1)/tw_dev->num_units;
-                                       tw_dev->free_head = TW_Q_START;
-                                       tw_dev->free_tail = TW_MAX_BOUNCEBUF - 1;
-                                       tw_dev->free_wrap = TW_MAX_BOUNCEBUF - 1;
-                               } else {
-                                       tw_host->cmd_per_lun = (TW_Q_LENGTH-1)/tw_dev->num_units;
-                                       tw_dev->free_head = TW_Q_START;
-                                       tw_dev->free_tail = TW_Q_LENGTH - 1;
-                                       tw_dev->free_wrap = TW_Q_LENGTH - 1;
-                               }
+                               tw_host->cmd_per_lun = (TW_Q_LENGTH-1)/tw_dev->num_units;
+                               tw_dev->free_head = TW_Q_START;
+                               tw_dev->free_tail = TW_Q_LENGTH - 1;
+                               tw_dev->free_wrap = TW_Q_LENGTH - 1;
                        }
 
-               /* Register the card with the kernel SCSI layer */
+                       /* Register the card with the kernel SCSI layer */
                        host = scsi_register(tw_host, sizeof(TW_Device_Extension));
                        if (host == NULL) {
-                               printk(KERN_WARNING "3w-xxxx: tw_findcards(): scsi_register() failed for card %d.\n", numcards-1);
+                               printk(KERN_WARNING "3w-xxxx: tw_findcards(): scsi_register() failed for card %d.\n", numcards);
                                release_region((tw_dev->tw_pci_dev->resource[0].start), TW_IO_ADDRESS_RANGE);
                                tw_free_device_extension(tw_dev);
                                kfree(tw_dev);
                                continue;
                        }
 
+                       /* Set max target id's */
+                       host->max_id = TW_MAX_UNITS;
+
+                       /* Set max cdb size in bytes */
+                       host->max_cmd_len = 16;
+
                        /* Set max sectors per io */
 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,7)
                        host->max_sectors = TW_MAX_SECTORS;
@@ -874,7 +941,7 @@ int tw_findcards(Scsi_Host_Template *tw_host)
                                tw_device_extension_count = numcards;
                                tw_dev2->host = host;
                        } else { 
-                               printk(KERN_WARNING "3w-xxxx: tw_findcards(): Bad scsi host data for card %d.\n", numcards-1);
+                               printk(KERN_WARNING "3w-xxxx: tw_findcards(): Bad scsi host data for card %d.\n", numcards);
                                scsi_unregister(host);
                                release_region((tw_dev->tw_pci_dev->resource[0].start), TW_IO_ADDRESS_RANGE);
                                tw_free_device_extension(tw_dev);
@@ -924,15 +991,10 @@ void tw_free_device_extension(TW_Device_Extension *tw_dev)
        /* Free command packet and generic buffer memory */
        for (i=0;i<TW_Q_LENGTH;i++) {
                if (tw_dev->command_packet_virtual_address[i]) 
-                       kfree(tw_dev->command_packet_virtual_address[i]);
+                       pci_free_consistent(tw_dev->tw_pci_dev, sizeof(TW_Sector), tw_dev->command_packet_virtual_address[i], tw_dev->command_packet_physical_address[i]);
 
                if (tw_dev->alignment_virtual_address[i])
-                       kfree(tw_dev->alignment_virtual_address[i]);
-
-       }
-       for (i=0;i<TW_MAX_BOUNCEBUF;i++) {
-               if (tw_dev->bounce_buffer[i])
-                       kfree(tw_dev->bounce_buffer[i]);
+                       pci_free_consistent(tw_dev->tw_pci_dev, sizeof(TW_Sector), tw_dev->alignment_virtual_address[i], tw_dev->alignment_physical_address[i]);
        }
 } /* End tw_free_device_extension() */
 
@@ -1015,8 +1077,7 @@ int tw_initconnection(TW_Device_Extension *tw_dev, int message_credits)
                        }
                        if (command_packet->status != 0) {
                                /* bad response */
-                               dprintk(KERN_WARNING "3w-xxxx: tw_initconnection(): Bad response, status = 0x%x, flags = 0x%x.\n", command_packet->status, command_packet->flags);
-                               tw_decode_error(tw_dev, command_packet->status, command_packet->flags, command_packet->byte3.unit);
+                               tw_decode_sense(tw_dev, request_id, 0);
                                return 1;
                        }
                        break;  /* Response was okay, so we exit */
@@ -1028,57 +1089,33 @@ int tw_initconnection(TW_Device_Extension *tw_dev, int message_credits)
 /* This function will initialize the fields of a device extension */
 int tw_initialize_device_extension(TW_Device_Extension *tw_dev)
 {
-       int i, imax;
+       int i, error=0;
 
        dprintk(KERN_NOTICE "3w-xxxx: tw_initialize_device_extension()\n");
-       imax = TW_Q_LENGTH;
 
-       for (i=0; i<imax; i++) {
-               /* Initialize command packet buffers */
-               tw_allocate_memory(tw_dev, i, sizeof(TW_Sector), 0);
-               if (tw_dev->command_packet_virtual_address[i] == NULL) {
-                       printk(KERN_WARNING "3w-xxxx: tw_initialize_device_extension(): Bad command packet virtual address.\n");
-                       return 1;
-               }
-               memset(tw_dev->command_packet_virtual_address[i], 0, sizeof(TW_Sector));
-    
-               /* Initialize generic buffer */
-               tw_allocate_memory(tw_dev, i, sizeof(TW_Sector), 1);
-               if (tw_dev->alignment_virtual_address[i] == NULL) {
-                       printk(KERN_WARNING "3w-xxxx: tw_initialize_device_extension(): Bad alignment virtual address.\n");
-                       return 1;
-               }
-               memset(tw_dev->alignment_virtual_address[i], 0, sizeof(TW_Sector));
+       /* Initialize command packet buffers */
+       error = tw_allocate_memory(tw_dev, sizeof(TW_Command), 0);
+       if (error) {
+               printk(KERN_WARNING "3w-xxxx: Command packet memory allocation failed.\n");
+               return 1;
+       }
 
-               tw_dev->free_queue[i] = i;
-               tw_dev->state[i] = TW_S_INITIAL;
-               tw_dev->ioctl_size[i] = 0;
-               tw_dev->aen_queue[i] = 0;
+       /* Initialize generic buffer */
+       error = tw_allocate_memory(tw_dev, sizeof(TW_Sector), 1);
+       if (error) {
+               printk(KERN_WARNING "3w-xxxx: Generic memory allocation failed.\n");
+               return 1;
        }
 
-       for (i=0;i<TW_MAX_UNITS;i++) {
-               tw_dev->is_unit_present[i] = 0;
-               tw_dev->is_raid_five[i] = 0;
+       for (i=0;i<TW_Q_LENGTH;i++) {
+               tw_dev->free_queue[i] = i;
+               tw_dev->state[i] = TW_S_INITIAL;
        }
 
-       tw_dev->num_units = 0;
-       tw_dev->num_aborts = 0;
-       tw_dev->num_resets = 0;
-       tw_dev->posted_request_count = 0;
-       tw_dev->max_posted_request_count = 0;
-       tw_dev->max_sgl_entries = 0;
-       tw_dev->sgl_entries = 0;
-       tw_dev->host = NULL;
        tw_dev->pending_head = TW_Q_START;
        tw_dev->pending_tail = TW_Q_START;
-       tw_dev->aen_head = 0;
-       tw_dev->aen_tail = 0;
-       tw_dev->sector_count = 0;
-       tw_dev->max_sector_count = 0;
-       tw_dev->aen_count = 0;
-       tw_dev->num_raid_five = 0;
        spin_lock_init(&tw_dev->tw_lock);
-       tw_dev->flags = 0;
+
        return 0;
 } /* End tw_initialize_device_extension() */
 
@@ -1089,14 +1126,13 @@ int tw_initialize_units(TW_Device_Extension *tw_dev)
        unsigned char request_id = 0;
        TW_Command *command_packet;
        TW_Param *param;
-       int i, j, imax, num_units = 0, num_raid_five = 0;
+       int i, imax, num_units = 0;
        u32 status_reg_addr, status_reg_value;
        u32 command_que_addr, command_que_value;
        u32 response_que_addr;
        TW_Response_Queue response_queue;
        u32 param_value;
        unsigned char *is_unit_present;
-       unsigned char *raid_level;
 
        dprintk(KERN_NOTICE "3w-xxxx: tw_initialize_units()\n");
 
@@ -1168,8 +1204,7 @@ int tw_initialize_units(TW_Device_Extension *tw_dev)
                        }
                        if (command_packet->status != 0) {
                                /* bad response */
-                               dprintk(KERN_WARNING "3w-xxxx: tw_initialize_units(): Bad response, status = 0x%x, flags = 0x%x.\n", command_packet->status, command_packet->flags);
-                               tw_decode_error(tw_dev, command_packet->status, command_packet->flags, command_packet->byte3.unit);
+                               tw_decode_sense(tw_dev, request_id, 0);
                                return 1;
                        }
                        found = 1;
@@ -1205,109 +1240,6 @@ int tw_initialize_units(TW_Device_Extension *tw_dev)
                return 1;
        }
 
-       /* Find raid 5 arrays */
-       for (j=0;j<TW_MAX_UNITS;j++) {
-               if (tw_dev->is_unit_present[j] == 0)
-                       continue;
-               command_packet = (TW_Command *)tw_dev->command_packet_virtual_address[request_id];
-               if (command_packet == NULL) {
-                       printk(KERN_WARNING "3w-xxxx: tw_initialize_units(): Bad command packet virtual address.\n");
-                       return 1;
-               }
-               memset(command_packet, 0, sizeof(TW_Sector));
-               command_packet->byte0.opcode      = TW_OP_GET_PARAM;
-               command_packet->byte0.sgl_offset  = 2;
-               command_packet->size              = 4;
-               command_packet->request_id        = request_id;
-               command_packet->byte3.unit        = 0;
-               command_packet->byte3.host_id     = 0;
-               command_packet->status            = 0;
-               command_packet->flags             = 0;
-               command_packet->byte6.block_count = 1;
-
-               /* Now setup the param */
-               if (tw_dev->alignment_virtual_address[request_id] == NULL) {
-                       printk(KERN_WARNING "3w-xxxx: tw_initialize_units(): Bad alignment virtual address.\n");
-                       return 1;
-               }
-               param = (TW_Param *)tw_dev->alignment_virtual_address[request_id];
-               memset(param, 0, sizeof(TW_Sector));
-               param->table_id = 0x300+j; /* unit summary table */
-               param->parameter_id = 0x6; /* unit descriptor */
-               param->parameter_size_bytes = 0xc;
-               param_value = tw_dev->alignment_physical_address[request_id];
-               if (param_value == 0) {
-                       printk(KERN_WARNING "3w-xxxx: tw_initialize_units(): Bad alignment physical address.\n");
-                       return 1;
-               }
-
-               command_packet->byte8.param.sgl[0].address = param_value;
-               command_packet->byte8.param.sgl[0].length = sizeof(TW_Sector);
-
-               /* Post the command packet to the board */
-               command_que_value = tw_dev->command_packet_physical_address[request_id];
-               if (command_que_value == 0) {
-                       printk(KERN_WARNING "3w-xxxx: tw_initialize_units(): Bad command packet physical address.\n");
-                       return 1;
-               }
-               outl(command_que_value, command_que_addr);
-
-               /* Poll for completion */
-               imax = TW_POLL_MAX_RETRIES;
-               for(i=0; i<imax; i++) {
-                       mdelay(5);
-                       status_reg_value = inl(status_reg_addr);
-                       if (tw_check_bits(status_reg_value)) {
-                               dprintk(KERN_WARNING "3w-xxxx: tw_initialize_units(): Unexpected bits.\n");
-                               tw_decode_bits(tw_dev, status_reg_value);
-                               return 1;
-                       }
-                       if ((status_reg_value & TW_STATUS_RESPONSE_QUEUE_EMPTY) == 0) {
-                               response_queue.value = inl(response_que_addr);
-                               request_id = (unsigned char)response_queue.u.response_id;
-                               if (request_id != 0) {
-                                       /* unexpected request id */
-                                       printk(KERN_WARNING "3w-xxxx: tw_initialize_units(): Unexpected request id.\n");
-                                       return 1;
-                               }
-                               if (command_packet->status != 0) {
-                                       /* bad response */
-                                       dprintk(KERN_WARNING "3w-xxxx: tw_initialize_units(): Bad response, status = 0x%x, flags = 0x%x.\n", command_packet->status, command_packet->flags);
-                                       tw_decode_error(tw_dev, command_packet->status, command_packet->flags, command_packet->byte3.unit);
-                                       return 1;
-                               }
-                               found = 1;
-                               break;
-                       }
-               }
-               if (found == 0) {
-                       /* response never received */
-                       printk(KERN_WARNING "3w-xxxx: tw_initialize_units(): No response.\n");
-                       return 1;
-               }
-
-               param = (TW_Param *)tw_dev->alignment_virtual_address[request_id];
-               raid_level = (unsigned char *)&(param->data[1]);
-               if (*raid_level == 5) {
-                       dprintk(KERN_WARNING "3w-xxxx: Found unit %d to be a raid5 unit.\n", j);
-                       tw_dev->is_raid_five[j] = 1;
-                       num_raid_five++;
-               }
-       }
-       tw_dev->num_raid_five = num_raid_five;
-
-       /* Now allocate raid5 bounce buffers */
-       if ((num_raid_five != 0) && (tw_dev->tw_pci_dev->device == TW_DEVICE_ID)) {
-               for (i=0;i<TW_MAX_BOUNCEBUF;i++) {
-                       tw_allocate_memory(tw_dev, i, sizeof(TW_Sector)*TW_MAX_SECTORS, 2);
-                       if (tw_dev->bounce_buffer[i] == NULL) {
-                               printk(KERN_WARNING "3w-xxxx: Bounce buffer allocation failed.\n");
-                               return 1;
-                       }
-                       memset(tw_dev->bounce_buffer[i], 0, sizeof(TW_Sector)*TW_MAX_SECTORS);
-               }
-       }
-  
        return 0;
 } /* End tw_initialize_units() */
 
@@ -1332,13 +1264,18 @@ static void tw_interrupt(int irq, void *dev_instance, struct pt_regs *regs)
 
        if (tw_dev->tw_pci_dev->irq == irq) {
                spin_lock(&tw_dev->tw_lock);
-               dprintk(KERN_NOTICE "3w-xxxx: tw_interrupt()\n");
+               dprintk(KERN_WARNING "3w-xxxx: tw_interrupt()\n");
 
                /* Read the registers */
                status_reg_addr = tw_dev->registers.status_reg_addr;
                response_que_addr = tw_dev->registers.response_que_addr;
                status_reg_value = inl(status_reg_addr);
 
+               if (tw_check_bits(status_reg_value)) {
+                       dprintk(KERN_WARNING "3w-xxxx: tw_interrupt(): Unexpected bits.\n");
+                       tw_decode_bits(tw_dev, status_reg_value);
+               }
+
                /* Check which interrupt */
                if (status_reg_value & TW_STATUS_HOST_INTERRUPT)
                        do_host_interrupt=1;
@@ -1403,17 +1340,18 @@ static void tw_interrupt(int irq, void *dev_instance, struct pt_regs *regs)
                                command_packet = (TW_Command *)tw_dev->command_packet_virtual_address[request_id];
                                error = 0;
                                if (command_packet->status != 0) {
-                                       dprintk(KERN_WARNING "3w-xxxx: tw_interrupt(): Bad response, status = 0x%x, flags = 0x%x, unit = 0x%x.\n", command_packet->status, command_packet->flags, command_packet->byte3.unit);
-                                       tw_decode_error(tw_dev, command_packet->status, command_packet->flags, command_packet->byte3.unit);
-                                       error = 1;
+                                       /* Bad response */
+                                       if (tw_dev->srb[request_id] != 0)
+                                               tw_decode_sense(tw_dev, request_id, 1);
+                                       error = 3;
                                }
                                if (tw_dev->state[request_id] != TW_S_POSTED) {
                                        printk(KERN_WARNING "3w-xxxx: scsi%d: Received a request id (%d) (opcode = 0x%x) that wasn't posted.\n", tw_dev->host->host_no, request_id, command_packet->byte0.opcode);
                                        error = 1;
                                }
                                if (TW_STATUS_ERRORS(status_reg_value)) {
-                                 tw_decode_bits(tw_dev, status_reg_value);
-                                 error = 1;
+                                       tw_decode_bits(tw_dev, status_reg_value);
+                                       error = 1;
                                }
                                dprintk(KERN_NOTICE "3w-xxxx: tw_interrupt(): Response queue request id: %d.\n", request_id);
                                /* Check for internal command */
@@ -1428,7 +1366,7 @@ static void tw_interrupt(int irq, void *dev_instance, struct pt_regs *regs)
                                                dprintk(KERN_WARNING "3w-xxxx: tw_interrupt(): Unexpected bits.\n");
                                                tw_decode_bits(tw_dev, status_reg_value);
                                        }
-               } else {
+                               } else {
                                switch (tw_dev->srb[request_id]->cmnd[0]) {
                                        case READ_10:
                                        case READ_6:
@@ -1455,18 +1393,23 @@ static void tw_interrupt(int irq, void *dev_instance, struct pt_regs *regs)
                                                tw_dev->srb[request_id]->result = (DID_BAD_TARGET << 16);
                                                tw_dev->srb[request_id]->scsi_done(tw_dev->srb[request_id]);
                                        }
-                                       if (error) {
+                                       if (error == 1) {
                                                /* Tell scsi layer there was an error */
                                                dprintk(KERN_WARNING "3w-xxxx: tw_interrupt(): Scsi Error.\n");
                                                tw_dev->srb[request_id]->result = (DID_RESET << 16);
-                                       } else {
+                                       }
+                                       if (error == 0) {
                                                /* Tell scsi layer command was a success */
                                                tw_dev->srb[request_id]->result = (DID_OK << 16);
                                        }
-                                       tw_dev->state[request_id] = TW_S_COMPLETED;
-                                       tw_state_request_finish(tw_dev, request_id);
-                                       tw_dev->posted_request_count--;
-                                       tw_dev->srb[request_id]->scsi_done(tw_dev->srb[request_id]);
+                                       if (error != 2) {
+                                               tw_dev->state[request_id] = TW_S_COMPLETED;
+                                               tw_state_request_finish(tw_dev, request_id);
+                                               tw_dev->posted_request_count--;
+                                               tw_dev->srb[request_id]->scsi_done(tw_dev->srb[request_id]);
+
+                                               tw_unmap_scsi_data(tw_dev->tw_pci_dev, tw_dev->srb[request_id]);
+                                       }
                                        status_reg_value = inl(status_reg_addr);
                                        if (tw_check_bits(status_reg_value)) {
                                                dprintk(KERN_WARNING "3w-xxxx: tw_interrupt(): Unexpected bits.\n");
@@ -1479,19 +1422,22 @@ static void tw_interrupt(int irq, void *dev_instance, struct pt_regs *regs)
        }
        spin_unlock_irqrestore(tw_dev->host->host_lock, flags);
        clear_bit(TW_IN_INTR, &tw_dev->flags);
-}      /* End tw_interrupt() */
+} /* End tw_interrupt() */
 
 /* This function handles ioctls from userspace to the driver */
 int tw_ioctl(TW_Device_Extension *tw_dev, int request_id)
 {
        unsigned char opcode;
-       int bufflen;
+       int bufflen, error = 0;
        TW_Param *param;
-       TW_Command *command_packet;
+       TW_Command *command_packet, *command_save;
        u32 param_value;
        TW_Ioctl *ioctl = NULL;
        TW_Passthru *passthru = NULL;
-       int tw_aen_code;
+       int tw_aen_code, i, use_sg;
+       char *data_ptr;
+       int total_bytes = 0;
+       dma_addr_t dma_handle;
 
        ioctl = (TW_Ioctl *)tw_dev->srb[request_id]->request_buffer;
        if (ioctl == NULL) {
@@ -1598,7 +1544,7 @@ int tw_ioctl(TW_Device_Extension *tw_dev, int request_id)
                                printk(KERN_WARNING "3w-xxxx: tw_ioctl(): Passthru size (%ld) too big.\n", passthru->sg_list[0].length);
                                return 1;
                        }
-                       passthru->sg_list[0].address = virt_to_bus(tw_dev->alignment_virtual_address[request_id]);
+                       passthru->sg_list[0].address = tw_dev->alignment_physical_address[request_id];
                        tw_post_command_packet(tw_dev, request_id);
                        return 0;
                case TW_CMD_PACKET:
@@ -1612,6 +1558,161 @@ int tw_ioctl(TW_Device_Extension *tw_dev, int request_id)
                                printk(KERN_WARNING "3w-xxxx: tw_ioctl(): ioctl->data NULL.\n");
                                return 1;
                        }
+               case TW_CMD_PACKET_WITH_DATA:
+                       dprintk(KERN_WARNING "3w-xxxx: tw_ioctl(): caught TW_CMD_PACKET_WITH_DATA.\n");
+                       command_save = (TW_Command *)tw_dev->alignment_virtual_address[request_id];
+                       if (command_save == NULL) {
+                               printk(KERN_WARNING "3w-xxxx: scsi%d: tw_ioctl(): Bad alignment virtual address.\n", tw_dev->host->host_no);
+                               return 1;
+                       }
+                       if (ioctl->data != NULL) {
+                               /* Copy down the command packet */
+                               memcpy(command_packet, ioctl->data, sizeof(TW_Command));
+                               memcpy(command_save, ioctl->data, sizeof(TW_Command));
+                               command_packet->request_id = request_id;
+
+                               /* Now deal with the two possible sglists */
+                               if (command_packet->byte0.sgl_offset == 2) {
+                                       use_sg = command_packet->size - 3;
+                                       for (i=0;i<use_sg;i++)
+                                               total_bytes+=command_packet->byte8.param.sgl[i].length;
+                                       tw_dev->ioctl_data[request_id] = pci_alloc_consistent(tw_dev->tw_pci_dev, total_bytes, &dma_handle);
+
+                                       if (!tw_dev->ioctl_data[request_id]) {
+                                               printk(KERN_WARNING "3w-xxxx: scsi%d: tw_ioctl(): kmalloc failed for request_id %d.\n", tw_dev->host->host_no, request_id);
+                                               return 1;
+                                       }
+
+                                       /* Copy param sglist into the kernel */
+                                       data_ptr = tw_dev->ioctl_data[request_id];
+                                       for (i=0;i<use_sg;i++) {
+                                               if ((u32 *)command_packet->byte8.param.sgl[i].address != NULL) {
+                                                       error = copy_from_user(data_ptr, (u32 *)command_packet->byte8.param.sgl[i].address, command_packet->byte8.param.sgl[i].length);
+                                                       if (error) {
+                                                               printk(KERN_WARNING "3w-xxxx: scsi%d: Error copying param sglist from userspace.\n", tw_dev->host->host_no);
+                                                               goto tw_ioctl_bail;
+                                                       }
+                                               } else {
+                                                       printk(KERN_WARNING "3w-xxxx: scsi%d: tw_ioctl(): Bad param sgl address.\n", tw_dev->host->host_no);
+                                                       tw_dev->srb[request_id]->result = (DID_RESET << 16);
+                                                       goto tw_ioctl_bail;
+                                               }
+                                               data_ptr+=command_packet->byte8.param.sgl[i].length;
+                                       }
+                                       command_packet->size = 4;
+                                       command_packet->byte8.param.sgl[0].address = dma_handle;
+                                       command_packet->byte8.param.sgl[0].length = total_bytes;
+                               }
+                               if (command_packet->byte0.sgl_offset == 3) {
+                                       use_sg = command_packet->size - 4;
+                                       for (i=0;i<use_sg;i++)
+                                               total_bytes+=command_packet->byte8.io.sgl[i].length;
+                                       tw_dev->ioctl_data[request_id] = pci_alloc_consistent(tw_dev->tw_pci_dev, total_bytes, &dma_handle);
+
+                                       if (!tw_dev->ioctl_data[request_id]) {
+                                               printk(KERN_WARNING "3w-xxxx: scsi%d: tw_ioctl(): pci_alloc_consistent() failed for request_id %d.\n", tw_dev->host->host_no, request_id);
+                                               return 1;
+                                       }
+                                       if (command_packet->byte0.opcode == TW_OP_WRITE) {
+                                               /* Copy io sglist into the kernel */
+                                               data_ptr = tw_dev->ioctl_data[request_id];
+                                               for (i=0;i<use_sg;i++) {
+                                                       if ((u32 *)command_packet->byte8.io.sgl[i].address != NULL) {
+                                                               error = copy_from_user(data_ptr, (u32 *)command_packet->byte8.io.sgl[i].address, command_packet->byte8.io.sgl[i].length);
+                                                               if (error) {
+                                                                       printk(KERN_WARNING "3w-xxxx: scsi%d: Error copying io sglist from userspace.\n", tw_dev->host->host_no);
+                                                                       goto tw_ioctl_bail;
+                                                               }
+                                                       } else {
+                                                               printk(KERN_WARNING "3w-xxxx: scsi%d: tw_ioctl(): Bad io sgl address.\n", tw_dev->host->host_no);
+                                                               tw_dev->srb[request_id]->result = (DID_RESET << 16);
+                                                               goto tw_ioctl_bail;
+                                                       }
+                                                       data_ptr+=command_packet->byte8.io.sgl[i].length;
+                                               }
+                                       }
+                                       command_packet->size = 5;
+                                       command_packet->byte8.io.sgl[0].address = dma_handle;
+                                       command_packet->byte8.io.sgl[0].length = total_bytes;
+                               }
+
+                               spin_unlock_irq(tw_dev->host->host_lock);
+                               spin_unlock_irq(&tw_dev->tw_lock);
+
+                               /* Finally post the command packet */
+                               tw_post_command_packet(tw_dev, request_id);
+
+                               mdelay(TW_IOCTL_WAIT_TIME);
+                               spin_lock_irq(&tw_dev->tw_lock);
+                               spin_lock_irq(tw_dev->host->host_lock);
+
+                               if (signal_pending(current)) {
+                                       dprintk(KERN_WARNING "3w-xxxx: scsi%d: tw_ioctl(): Signal pending, aborting ioctl().\n", tw_dev->host->host_no);
+                                       tw_dev->srb[request_id]->result = (DID_OK << 16);
+                                       goto tw_ioctl_bail;
+                               }
+
+                               tw_dev->srb[request_id]->result = (DID_OK << 16);
+                               /* Now copy up the param or io sglist to userspace */
+                               if (command_packet->byte0.sgl_offset == 2) {
+                                       use_sg = command_save->size - 3;
+                                       data_ptr = phys_to_virt(command_packet->byte8.param.sgl[0].address);
+                                       for (i=0;i<use_sg;i++) {
+                                               if ((u32 *)command_save->byte8.param.sgl[i].address != NULL) {
+                                                       error = copy_to_user((u32 *)command_save->byte8.param.sgl[i].address, data_ptr, command_save->byte8.param.sgl[i].length);
+                                                       if (error) {
+                                                               printk(KERN_WARNING "3w-xxxx: scsi%d: Error copying param sglist to userspace.\n", tw_dev->host->host_no);
+                                                               goto tw_ioctl_bail;
+                                                       }
+                                                       dprintk(KERN_WARNING "3w-xxxx: scsi%d: Copied %ld bytes to pid %d.\n", tw_dev->host->host_no, command_save->byte8.param.sgl[i].length, current->pid);
+                                                       data_ptr+=command_save->byte8.param.sgl[i].length;
+                                               } else {
+                                                       printk(KERN_WARNING "3w-xxxx: scsi%d: tw_ioctl(): Bad param sgl address.\n", tw_dev->host->host_no);
+                                                       tw_dev->srb[request_id]->result = (DID_RESET << 16);
+                                                       goto tw_ioctl_bail;
+                                               }
+                                       }
+                               }
+                               if (command_packet->byte0.sgl_offset == 3) {
+                                       use_sg = command_save->size - 4;
+                                       if (command_packet->byte0.opcode == TW_OP_READ) {
+                                               data_ptr = phys_to_virt(command_packet->byte8.io.sgl[0].address);
+                                               for(i=0;i<use_sg;i++) {
+                                                       if ((u32 *)command_save->byte8.io.sgl[i].address != NULL) {
+                                                               error = copy_to_user((u32 *)command_save->byte8.io.sgl[i].address, data_ptr, command_save->byte8.io.sgl[i].length);
+                                                               if (error) {
+                                                                       printk(KERN_WARNING "3w-xxxx: scsi%d: Error copying io sglist to userspace.\n", tw_dev->host->host_no);
+                                                                       goto tw_ioctl_bail;
+                                                               }
+                                                               dprintk(KERN_WARNING "3w-xxxx: scsi%d: Copied %ld bytes to pid %d.\n", tw_dev->host->host_no, command_save->byte8.io.sgl[i].length, current->pid);
+                                                               data_ptr+=command_save->byte8.io.sgl[i].length;
+                                                       } else {
+                                                               printk(KERN_WARNING "3w-xxxx: scsi%d: tw_ioctl(): Bad io sgl address.\n", tw_dev->host->host_no);
+                                                               tw_dev->srb[request_id]->result = (DID_RESET << 16);
+                                                               goto tw_ioctl_bail;
+                                                       }
+                                               }
+                                       }
+                               }
+                               
+                       tw_ioctl_bail:
+
+                               /* Free up sglist memory */
+                               if (tw_dev->ioctl_data[request_id])
+                                       pci_free_consistent(tw_dev->tw_pci_dev, total_bytes, tw_dev->ioctl_data[request_id], dma_handle);
+                               else
+                                       printk(KERN_WARNING "3w-xxxx: scsi%d: tw_ioctl(): Error freeing ioctl data.\n", tw_dev->host->host_no);
+                               
+                               /* Now complete the io */
+                               tw_dev->state[request_id] = TW_S_COMPLETED;
+                               tw_state_request_finish(tw_dev, request_id);
+                               tw_dev->posted_request_count--;
+                               tw_dev->srb[request_id]->scsi_done(tw_dev->srb[request_id]);
+                               return 0;
+                       } else {
+                               printk(KERN_WARNING "3w-xxxx: tw_ioctl(): ioctl->data NULL.\n");
+                               return 1;
+                       }
                default:
                        printk(KERN_WARNING "3w-xxxx: Unknown ioctl 0x%x.\n", opcode);
                        tw_dev->state[request_id] = TW_S_COMPLETED;
@@ -1655,6 +1756,7 @@ int tw_ioctl_complete(TW_Device_Extension *tw_dev, int request_id)
        TW_Param *param;
        TW_Ioctl *ioctl = NULL;
        TW_Passthru *passthru = NULL;
+       TW_Command *command_packet;
 
        ioctl = (TW_Ioctl *)tw_dev->srb[request_id]->request_buffer;
        dprintk(KERN_NOTICE "3w-xxxx: tw_ioctl_complete()\n");
@@ -1663,6 +1765,13 @@ int tw_ioctl_complete(TW_Device_Extension *tw_dev, int request_id)
                printk(KERN_WARNING "3w-xxxx: tw_ioctl_complete(): Request buffer NULL.\n");
                return 1;
        }
+
+       command_packet = (TW_Command *)tw_dev->command_packet_virtual_address[request_id];
+       if (command_packet == NULL) {
+               printk(KERN_WARNING "3w-xxxx: scsi%d: tw_ioctl_complete(): Bad command packet virtual address.\n", tw_dev->host->host_no);
+               return 1;
+       }
+
        dprintk(KERN_NOTICE "3w-xxxx: tw_ioctl_complete(): Request_bufflen = %d\n", tw_dev->srb[request_id]->request_bufflen);
 
        ioctl = (TW_Ioctl *)buff;
@@ -1671,6 +1780,9 @@ int tw_ioctl_complete(TW_Device_Extension *tw_dev, int request_id)
                        passthru = (TW_Passthru *)ioctl->data;
                        memcpy(buff, tw_dev->alignment_virtual_address[request_id], passthru->sector_count * 512);
                        break;
+               case TW_CMD_PACKET_WITH_DATA:
+                       dprintk(KERN_WARNING "3w-xxxx: tw_ioctl_complete(): caught TW_CMD_PACKET_WITH_DATA.\n");
+                       return 2; /* Special case for isr to not complete io */
                default:
                        memset(buff, 0, tw_dev->srb[request_id]->request_bufflen);
                        param = (TW_Param *)tw_dev->alignment_virtual_address[request_id];
@@ -1684,6 +1796,40 @@ int tw_ioctl_complete(TW_Device_Extension *tw_dev, int request_id)
        return 0;
 } /* End tw_ioctl_complete() */
 
+static int tw_map_scsi_sg_data(struct pci_dev *pdev, Scsi_Cmnd *cmd)
+{
+       int use_sg;
+       int dma_dir = scsi_to_pci_dma_dir(cmd->sc_data_direction);
+
+       dprintk(KERN_WARNING "3w-xxxx: tw_map_scsi_sg_data()\n");
+       
+       if (cmd->use_sg == 0)
+               return 0;
+
+       use_sg = pci_map_sg(pdev, cmd->buffer, cmd->use_sg, dma_dir);
+       cmd->SCp.phase = 2;
+       cmd->SCp.have_data_in = use_sg;
+       
+       return use_sg;
+} /* End tw_map_scsi_sg_data() */
+
+static u32 tw_map_scsi_single_data(struct pci_dev *pdev, Scsi_Cmnd *cmd)
+{
+       dma_addr_t mapping;
+       int dma_dir = scsi_to_pci_dma_dir(cmd->sc_data_direction);
+
+       dprintk(KERN_WARNING "3w-xxxx: tw_map_scsi_single_data()\n");
+
+       if (cmd->request_bufflen == 0)
+               return 0;
+
+       mapping = pci_map_single(pdev, cmd->request_buffer, cmd->request_bufflen, dma_dir);
+       cmd->SCp.phase = 2;
+       cmd->SCp.have_data_in = mapping;
+
+       return mapping;
+} /* End tw_map_scsi_single_data() */
+
 /* This function will mask the command interrupt */
 void tw_mask_command_interrupt(TW_Device_Extension *tw_dev)
 {
@@ -1704,14 +1850,25 @@ int tw_poll_status(TW_Device_Extension *tw_dev, u32 flag, int seconds)
        do_gettimeofday(&before);
        status_reg_value = inl(status_reg_addr);
 
+       if (tw_check_bits(status_reg_value)) {
+               dprintk(KERN_WARNING "3w-xxxx: tw_poll_status(): Unexpected bits.\n");
+               tw_decode_bits(tw_dev, status_reg_value);
+       }
+               
        while ((status_reg_value & flag) != flag) {
                status_reg_value = inl(status_reg_addr);
+
+               if (tw_check_bits(status_reg_value)) {
+                       dprintk(KERN_WARNING "3w-xxxx: tw_poll_status(): Unexpected bits.\n");
+                       tw_decode_bits(tw_dev, status_reg_value);
+               }
+
                do_gettimeofday(&timeout);
                if (before.tv_sec + seconds < timeout.tv_sec) { 
                        dprintk(KERN_WARNING "3w-xxxx: tw_poll_status(): Flag 0x%x not found.\n", flag);
                        return 1;
                }
-               mdelay(1);
+               mdelay(5);
        }
        return 0;
 } /* End tw_poll_status() */
@@ -1787,6 +1944,7 @@ int tw_reset_device_extension(TW_Device_Extension *tw_dev)
                                srb = tw_dev->srb[i];
                                srb->result = (DID_RESET << 16);
                                tw_dev->srb[i]->scsi_done(tw_dev->srb[i]);
+                               tw_unmap_scsi_data(tw_dev->tw_pci_dev, tw_dev->srb[i]);
                        }
                }
        }
@@ -1821,7 +1979,7 @@ int tw_reset_sequence(TW_Device_Extension *tw_dev)
 
                error = tw_aen_drain_queue(tw_dev);
                if (error) {
-                       printk(KERN_WARNING "3w-xxxx: scsi%d: Card not responding, retrying.\n", tw_dev->host->host_no);
+                       printk(KERN_WARNING "3w-xxxx: scsi%d: AEN drain failed, retrying.\n", tw_dev->host->host_no);
                        tries++;
                        continue;
                }
@@ -1857,7 +2015,7 @@ int tw_reset_sequence(TW_Device_Extension *tw_dev)
        }
 
        /* Re-enable interrupts */
-       tw_enable_interrupts(tw_dev);
+       tw_enable_and_clear_interrupts(tw_dev);
 
        return 0;
 } /* End tw_reset_sequence() */
@@ -2381,6 +2539,9 @@ int tw_scsiop_read_capacity_complete(TW_Device_Extension *tw_dev, int request_id
        capacity = (param_data[3] << 24) | (param_data[2] << 16) | 
                   (param_data[1] << 8) | param_data[0];
 
+       /* Subtract one sector to fix get last sector ioctl */
+       capacity -= 1;
+
        dprintk(KERN_NOTICE "3w-xxxx: tw_scsiop_read_capacity_complete(): Capacity = 0x%x.\n", capacity);
 
        /* Number of LBA's */
@@ -2403,8 +2564,8 @@ int tw_scsiop_read_write(TW_Device_Extension *tw_dev, int request_id)
 {
        TW_Command *command_packet;
        u32 command_que_addr, command_que_value = 0;
-       u32 lba = 0x0, num_sectors = 0x0;
-       int i, count = 0;
+       u32 lba = 0x0, num_sectors = 0x0, buffaddr = 0x0;
+       int i, use_sg;
        Scsi_Cmnd *srb;
        struct scatterlist *sglist;
 
@@ -2461,45 +2622,25 @@ int tw_scsiop_read_write(TW_Device_Extension *tw_dev, int request_id)
        command_packet->byte8.io.lba = lba;
        command_packet->byte6.block_count = num_sectors;
 
-       if ((tw_dev->is_raid_five[tw_dev->srb[request_id]->target] == 0) || (srb->cmnd[0] == READ_6) || (srb->cmnd[0] == READ_10) || (tw_dev->tw_pci_dev->device == TW_DEVICE_ID2)) {
-               /* Do this if there are no sg list entries */
-               if (tw_dev->srb[request_id]->use_sg == 0) {    
-                       dprintk(KERN_NOTICE "3w-xxxx: tw_scsiop_read_write(): SG = 0\n");
-                       command_packet->byte8.io.sgl[0].address = virt_to_bus(tw_dev->srb[request_id]->request_buffer);
-                       command_packet->byte8.io.sgl[0].length = tw_dev->srb[request_id]->request_bufflen;
-               }
-
-               /* Do this if we have multiple sg list entries */
-               if (tw_dev->srb[request_id]->use_sg > 0) {
-                       for (i=0;i<tw_dev->srb[request_id]->use_sg; i++) {
-                               command_packet->byte8.io.sgl[i].address = virt_to_bus(sglist[i].address);
-                               command_packet->byte8.io.sgl[i].length = sglist[i].length;
-                               command_packet->size+=2;
-                       }
-                       if (tw_dev->srb[request_id]->use_sg >= 1)
-                               command_packet->size-=2;
+       /* Do this if there are no sg list entries */
+       if (tw_dev->srb[request_id]->use_sg == 0) {    
+               dprintk(KERN_NOTICE "3w-xxxx: tw_scsiop_read_write(): SG = 0\n");
+               buffaddr = tw_map_scsi_single_data(tw_dev->tw_pci_dev, tw_dev->srb[request_id]);
+               command_packet->byte8.io.sgl[0].address = buffaddr;
+               command_packet->byte8.io.sgl[0].length = tw_dev->srb[request_id]->request_bufflen;
+       }
+
+       /* Do this if we have multiple sg list entries */
+       if (tw_dev->srb[request_id]->use_sg > 0) {
+               use_sg = tw_map_scsi_sg_data(tw_dev->tw_pci_dev, tw_dev->srb[request_id]);;
+               for (i=0;i<use_sg; i++) {
+                       command_packet->byte8.io.sgl[i].address = sg_dma_address(&sglist[i]);
+                       command_packet->byte8.io.sgl[i].length = sg_dma_len(&sglist[i]);
+                       command_packet->size+=2;
                }
-       } else {
-                /* Do this if there are no sg list entries for raid 5 */
-                if (tw_dev->srb[request_id]->use_sg == 0) {
-                       dprintk(KERN_WARNING "doing raid 5 write use_sg = 0, bounce_buffer[%d] = 0x%p\n", request_id, tw_dev->bounce_buffer[request_id]);
-                       memcpy(tw_dev->bounce_buffer[request_id], tw_dev->srb[request_id]->request_buffer, tw_dev->srb[request_id]->request_bufflen);
-                       command_packet->byte8.io.sgl[0].address = virt_to_bus(tw_dev->bounce_buffer[request_id]);
-                       command_packet->byte8.io.sgl[0].length = tw_dev->srb[request_id]->request_bufflen;
-                }
-
-                /* Do this if we have multiple sg list entries for raid 5 */
-                if (tw_dev->srb[request_id]->use_sg > 0) {
-                        dprintk(KERN_WARNING "doing raid 5 write use_sg = %d, sglist[0].length = %d\n", tw_dev->srb[request_id]->use_sg, sglist[0].length);
-                        for (i=0;i<tw_dev->srb[request_id]->use_sg; i++) {
-                                memcpy((char *)(tw_dev->bounce_buffer[request_id])+count, sglist[i].address, sglist[i].length);
-                               count+=sglist[i].length;
-                        }
-                        command_packet->byte8.io.sgl[0].address = virt_to_bus(tw_dev->bounce_buffer[request_id]);
-                        command_packet->byte8.io.sgl[0].length = count;
-                        command_packet->size = 5; /* single sgl */
-                }
-        }
+               if (tw_dev->srb[request_id]->use_sg >= 1)
+                       command_packet->size-=2;
+       }
 
        /* Update SG statistics */
        tw_dev->sgl_entries = tw_dev->srb[request_id]->use_sg;
@@ -2521,18 +2662,18 @@ int tw_scsiop_read_write(TW_Device_Extension *tw_dev, int request_id)
 /* This function will handle the request sense scsi command */
 int tw_scsiop_request_sense(TW_Device_Extension *tw_dev, int request_id)
 {
-       dprintk(KERN_NOTICE "3w-xxxx: tw_scsiop_request_sense()\n");
-  
-       /* For now we just zero the sense buffer */
-       memset(tw_dev->srb[request_id]->request_buffer, 0, tw_dev->srb[request_id]->request_bufflen);
-       tw_dev->state[request_id] = TW_S_COMPLETED;
-       tw_state_request_finish(tw_dev, request_id);
-  
-       /* If we got a request_sense, we probably want a reset, return error */
-       tw_dev->srb[request_id]->result = (DID_ERROR << 16);
-       tw_dev->srb[request_id]->scsi_done(tw_dev->srb[request_id]);
-  
-       return 0;
+       dprintk(KERN_NOTICE "3w-xxxx: tw_scsiop_request_sense()\n");
+
+       /* For now we just zero the request buffer */
+       memset(tw_dev->srb[request_id]->request_buffer, 0, tw_dev->srb[request_id]->request_bufflen);
+       tw_dev->state[request_id] = TW_S_COMPLETED;
+       tw_state_request_finish(tw_dev, request_id);
+
+       /* If we got a request_sense, we probably want a reset, return error */
+       tw_dev->srb[request_id]->result = (DID_ERROR << 16);
+       tw_dev->srb[request_id]->scsi_done(tw_dev->srb[request_id]);
+
+       return 0;
 } /* End tw_scsiop_request_sense() */
 
 /* This function will handle test unit ready scsi command */
@@ -2626,8 +2767,7 @@ int tw_setfeature(TW_Device_Extension *tw_dev, int parm, int param_size,
                        }
                        if (command_packet->status != 0) {
                                /* bad response */
-                               dprintk(KERN_WARNING "3w-xxxx: tw_setfeature(): Bad response, status = 0x%x, flags = 0x%x.\n", command_packet->status, command_packet->flags);
-                               tw_decode_error(tw_dev, command_packet->status, command_packet->flags, command_packet->byte3.unit);
+                               tw_decode_sense(tw_dev, request_id, 0);
                                return 1;
                        }
                        break; /* Response was okay, so we exit */
@@ -2671,7 +2811,7 @@ int tw_shutdown_device(TW_Device_Extension *tw_dev)
        }
 
        /* Re-enable interrupts */
-       tw_enable_interrupts(tw_dev);
+       tw_enable_and_clear_interrupts(tw_dev);
 
        return 0;
 } /* End tw_shutdown_device() */
@@ -2719,7 +2859,7 @@ int tw_state_request_start(TW_Device_Extension *tw_dev, int *request_id)
        int id = 0;
 
        dprintk(KERN_NOTICE "3w-xxxx: tw_state_request_start()\n");
-
+       
        /* Obtain next free request_id */
        do {
                if (tw_dev->free_head == tw_dev->free_wrap) {
@@ -2738,6 +2878,19 @@ int tw_state_request_start(TW_Device_Extension *tw_dev, int *request_id)
        return 0;
 } /* End tw_state_request_start() */
 
+static void tw_unmap_scsi_data(struct pci_dev *pdev, Scsi_Cmnd *cmd)
+{
+       int dma_dir = scsi_to_pci_dma_dir(cmd->sc_data_direction);
+
+       dprintk(KERN_WARNING "3w-xxxx: tw_unmap_scsi_data()\n");
+
+       if (cmd->use_sg) {
+               pci_unmap_sg(pdev, cmd->request_buffer, cmd->use_sg, dma_dir);
+       } else {
+               pci_unmap_single(pdev, cmd->SCp.have_data_in, cmd->request_bufflen, dma_dir);
+       }
+} /* End tw_unmap_scsi_data() */
+
 /* This function will unmask the command interrupt on the controller */
 void tw_unmask_command_interrupt(TW_Device_Extension *tw_dev)
 {
@@ -2749,7 +2902,6 @@ void tw_unmask_command_interrupt(TW_Device_Extension *tw_dev)
 } /* End tw_unmask_command_interrupt() */
 
 /* Now get things going */
-
 static Scsi_Host_Template driver_template = TWXXXX;
 #include "scsi_module.c"
 
index 0302b83ae6c32ff09fd0bfadb3fa9e3f300c5ed4..5ab618c7168bef0bfe7ec7ddbf66da5fa0ed6c98 100644 (file)
@@ -6,7 +6,7 @@
                     Arnaldo Carvalho de Melo <acme@conectiva.com.br>
                      Brad Strand <linux@3ware.com>
 
-   Copyright (C) 1999-2001 3ware Inc.
+   Copyright (C) 1999-2002 3ware Inc.
 
    Kernel compatablity By:     Andre Hedrick <andre@suse.com>
    Non-Copyright (C) 2000      Andre Hedrick <andre@suse.com>
@@ -62,7 +62,7 @@
 static char *tw_aen_string[] = {
        "AEN queue empty",                      // 0x000
        "Soft reset occurred",                  // 0x001
-       "Mirorr degraded: Unit #",              // 0x002
+       "Unit degraded: Unit #",                // 0x002
        "Controller error",                     // 0x003 
        "Rebuild failed: Unit #",               // 0x004
        "Rebuild complete: Unit #",             // 0x005
@@ -90,10 +90,36 @@ static char *tw_aen_string[] = {
        "DCB unsupported version: Port #",      // 0x028
        "Verify started: Unit #",               // 0x029
        "Verify failed: Port #",                // 0x02A
-       "Verify complete: Unit #"               // 0x02B
+       "Verify complete: Unit #",              // 0x02B
+       "Overwrote bad sector during rebuild: Port #",  //0x2C
+       "Encountered bad sector during rebuild: Port #" //0x2D
 };
 
-#define TW_AEN_STRING_MAX                      0x02C
+#define TW_AEN_STRING_MAX                      0x02E
+
+/*
+   Sense key lookup table
+   Format: ESDC/flags,SenseKey,AdditionalSenseCode,AdditionalSenseCodeQualifier
+*/
+static unsigned char tw_sense_table[][4] =
+{
+  /* Codes for newer firmware */
+                            // ATA Error                    SCSI Error
+  {0x01, 0x03, 0x13, 0x00}, // Address mark not found       Address mark not found for data field
+  {0x04, 0x0b, 0x00, 0x00}, // Aborted command              Aborted command
+  {0x10, 0x0b, 0x14, 0x00}, // ID not found                 Recorded entity not found
+  {0x40, 0x03, 0x11, 0x00}, // Uncorrectable ECC error      Unrecovered read error
+  {0x61, 0x04, 0x00, 0x00}, // Device fault                 Hardware error
+  {0x84, 0x0b, 0x47, 0x00}, // Data CRC error               SCSI parity error
+  {0xd0, 0x0b, 0x00, 0x00}, // Device busy                  Aborted command
+  {0xd1, 0x0b, 0x00, 0x00}, // Device busy                  Aborted command
+
+  /* Codes for older firmware */
+                            // 3ware Error                  SCSI Error
+  {0x09, 0x0b, 0x00, 0x00}, // Unrecovered disk error       Aborted command
+  {0x37, 0x0b, 0x04, 0x00}, // Unit offline                 Logical unit not ready
+  {0x51, 0x0b, 0x00, 0x00}  // Unspecified                  Aborted command
+};
 
 /* Control register bit definitions */
 #define TW_CONTROL_CLEAR_HOST_INTERRUPT               0x00080000
@@ -108,6 +134,7 @@ static char *tw_aen_string[] = {
 #define TW_CONTROL_DISABLE_INTERRUPTS         0x00000040
 #define TW_CONTROL_ISSUE_HOST_INTERRUPT               0x00000020
 #define TW_CONTROL_CLEAR_PARITY_ERROR          0x00800000
+#define TW_CONTROL_CLEAR_PCI_ABORT             0x00100000
 
 /* Status register bit definitions */
 #define TW_STATUS_MAJOR_VERSION_MASK          0xF0000000
@@ -140,6 +167,7 @@ static char *tw_aen_string[] = {
 #define TW_DEVICE_ID2 (0x1001)  /* 7000 series controller */
 #define TW_NUMDEVICES 2
 #define TW_PCI_CLEAR_PARITY_ERRORS 0xc100
+#define TW_PCI_CLEAR_PCI_ABORT     0x2000
 
 /* Command packet opcodes */
 #define TW_OP_NOP            0x0
@@ -153,6 +181,7 @@ static char *tw_aen_string[] = {
 #define TW_OP_AEN_LISTEN      0x1c
 #define TW_CMD_PACKET         0x1d
 #define TW_ATA_PASSTHRU       0x1e
+#define TW_CMD_PACKET_WITH_DATA 0x1f
 
 /* Asynchronous Event Notification (AEN) Codes */
 #define TW_AEN_QUEUE_EMPTY       0x0000
@@ -169,7 +198,8 @@ static char *tw_aen_string[] = {
 #define TW_AEN_SBUF_FAIL         0x0024
 
 /* Misc defines */
-#define TW_ALIGNMENT                         0x200 /* 16 D-WORDS */
+#define TW_ALIGNMENT_6000                    64 /* 64 bytes */
+#define TW_ALIGNMENT_7000                     4  /* 4 bytes */
 #define TW_MAX_UNITS                         16
 #define TW_COMMAND_ALIGNMENT_MASK            0x1ff
 #define TW_INIT_MESSAGE_CREDITS                      0x100
@@ -179,7 +209,6 @@ static char *tw_aen_string[] = {
 #define TW_ATA_PASS_SGL_MAX                   60
 #define TW_MAX_PASSTHRU_BYTES                 4096
 #define TW_Q_LENGTH                          256
-#define TW_MAX_BOUNCEBUF                      16
 #define TW_Q_START                           0
 #define TW_MAX_SLOT                          32
 #define TW_MAX_PCI_BUSES                     255
@@ -191,12 +220,9 @@ static char *tw_aen_string[] = {
 #define TW_MAX_AEN_TRIES                      100
 #define TW_UNIT_ONLINE                        1
 #define TW_IN_INTR                            1
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,4,7)
 #define TW_MAX_SECTORS                        256
-#else
-#define TW_MAX_SECTORS                        128
-#endif 
 #define TW_AEN_WAIT_TIME                      1000
+#define TW_IOCTL_WAIT_TIME                    (1 * HZ) /* 1 second */
 
 /* Macros */
 #define TW_STATUS_ERRORS(x) \
@@ -262,7 +288,6 @@ typedef struct TW_Command {
 } TW_Command;
 
 typedef struct TAG_TW_Ioctl {
-       int buffer;
        unsigned char opcode;
        unsigned short table_id;
        unsigned char parameter_id;
@@ -345,11 +370,8 @@ typedef struct TAG_TW_Device_Extension {
        TW_Registers            registers;
        u32                     *alignment_virtual_address[TW_Q_LENGTH];
        u32                     alignment_physical_address[TW_Q_LENGTH];
-       u32                     *bounce_buffer[TW_Q_LENGTH];
        int                     is_unit_present[TW_MAX_UNITS];
-       int                     is_raid_five[TW_MAX_UNITS];
        int                     num_units;
-       int                     num_raid_five;
        u32                     *command_packet_virtual_address[TW_Q_LENGTH];
        u32                     command_packet_physical_address[TW_Q_LENGTH];
        struct pci_dev          *tw_pci_dev;
@@ -381,22 +403,24 @@ typedef struct TAG_TW_Device_Extension {
        unsigned char           aen_head;
        unsigned char           aen_tail;
        long                    flags; /* long req'd for set_bit --RR */
+       char                    *ioctl_data[TW_Q_LENGTH];
 } TW_Device_Extension;
 
 /* Function prototypes */
 int tw_aen_complete(TW_Device_Extension *tw_dev, int request_id);
 int tw_aen_drain_queue(TW_Device_Extension *tw_dev);
 int tw_aen_read_queue(TW_Device_Extension *tw_dev, int request_id);
-int tw_allocate_memory(TW_Device_Extension *tw_dev, int request_id, int size, int which);
+int tw_allocate_memory(TW_Device_Extension *tw_dev, int size, int which);
 int tw_check_bits(u32 status_reg_value);
 int tw_check_errors(TW_Device_Extension *tw_dev);
 void tw_clear_attention_interrupt(TW_Device_Extension *tw_dev);
 void tw_clear_host_interrupt(TW_Device_Extension *tw_dev);
 void tw_decode_bits(TW_Device_Extension *tw_dev, u32 status_reg_value);
-void tw_decode_error(TW_Device_Extension *tw_dev, unsigned char status, unsigned char flags, unsigned char unit);
+void tw_decode_sense(TW_Device_Extension *tw_dev, int request_id, int fill_sense);
 void tw_disable_interrupts(TW_Device_Extension *tw_dev);
 int tw_empty_response_que(TW_Device_Extension *tw_dev);
 void tw_enable_interrupts(TW_Device_Extension *tw_dev);
+void tw_enable_and_clear_interrupts(TW_Device_Extension *tw_dev);
 int tw_findcards(Scsi_Host_Template *tw_host);
 void tw_free_device_extension(TW_Device_Extension *tw_dev);
 int tw_initconnection(TW_Device_Extension *tw_dev, int message_credits);