Commit 582b70ec authored by Dave Jones's avatar Dave Jones Committed by Linus Torvalds

[PATCH] 3ware vendor update

parent cd39bceb
...@@ -6,7 +6,7 @@ ...@@ -6,7 +6,7 @@
Arnaldo Carvalho de Melo <acme@conectiva.com.br> Arnaldo Carvalho de Melo <acme@conectiva.com.br>
Brad Strand <linux@3ware.com> Brad Strand <linux@3ware.com>
Copyright (C) 1999-2002 3ware Inc. Copyright (C) 1999-2003 3ware Inc.
Kernel compatiblity By: Andre Hedrick <andre@suse.com> Kernel compatiblity By: Andre Hedrick <andre@suse.com>
Non-Copyright (C) 2000 Andre Hedrick <andre@suse.com> Non-Copyright (C) 2000 Andre Hedrick <andre@suse.com>
...@@ -164,6 +164,11 @@ ...@@ -164,6 +164,11 @@
Add support for mode sense opcode. Add support for mode sense opcode.
Add support for cache mode page. Add support for cache mode page.
Add support for synchronize cache opcode. Add support for synchronize cache opcode.
1.02.00.032 - Fix small multicard rollcall bug.
Make driver stay loaded with no units for hot add/swap.
Add support for "twe" character device for ioctls.
Clean up request_id queueing code.
Fix tw_scsi_queue() spinlocks.
*/ */
#include <linux/module.h> #include <linux/module.h>
...@@ -203,6 +208,9 @@ MODULE_LICENSE("GPL"); ...@@ -203,6 +208,9 @@ MODULE_LICENSE("GPL");
#include "3w-xxxx.h" #include "3w-xxxx.h"
static int tw_chrdev_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg);
static int tw_chrdev_open(struct inode *inode, struct file *file);
static int tw_chrdev_release(struct inode *inode, struct file *file);
static int tw_copy_info(TW_Info *info, char *fmt, ...); 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_copy_mem_info(TW_Info *info, char *data, int len);
static void tw_interrupt(int irq, void *dev_instance, struct pt_regs *regs); static void tw_interrupt(int irq, void *dev_instance, struct pt_regs *regs);
...@@ -216,10 +224,19 @@ static struct notifier_block tw_notifier = { ...@@ -216,10 +224,19 @@ static struct notifier_block tw_notifier = {
tw_halt, NULL, 0 tw_halt, NULL, 0
}; };
/* File operations struct for character device */
static struct file_operations tw_fops = {
owner: THIS_MODULE,
ioctl: tw_chrdev_ioctl,
open: tw_chrdev_open,
release: tw_chrdev_release
};
/* Globals */ /* Globals */
char *tw_driver_version="1.02.00.031"; char *tw_driver_version="1.02.00.032";
TW_Device_Extension *tw_device_extension_list[TW_MAX_SLOT]; TW_Device_Extension *tw_device_extension_list[TW_MAX_SLOT];
int tw_device_extension_count = 0; int tw_device_extension_count = 0;
static int twe_major = -1;
/* Functions */ /* Functions */
...@@ -599,6 +616,209 @@ int tw_check_errors(TW_Device_Extension *tw_dev) ...@@ -599,6 +616,209 @@ int tw_check_errors(TW_Device_Extension *tw_dev)
return 0; return 0;
} /* End tw_check_errors() */ } /* End tw_check_errors() */
/* This function handles ioctl for the character device */
static int tw_chrdev_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg)
{
int error, request_id;
dma_addr_t dma_handle;
unsigned short tw_aen_code;
struct timeval before, timeout;
unsigned long flags = 0x0;
unsigned int data_buffer_length = 0;
unsigned long data_buffer_length_adjusted = 0;
unsigned long *cpu_addr;
TW_New_Ioctl *tw_ioctl;
TW_Passthru *passthru;
TW_Device_Extension *tw_dev = tw_device_extension_list[minor(inode->i_rdev)];
dprintk(KERN_WARNING "3w-xxxx: tw_chrdev_ioctl()\n");
/* Only let one of these through at a time */
if (test_and_set_bit(TW_IN_CHRDEV_IOCTL, &tw_dev->flags)) {
return -EBUSY;
}
/* First copy down the buffer length */
error = copy_from_user(&data_buffer_length, (void *)arg, sizeof(unsigned int));
if (error) {
printk(KERN_WARNING "3w-xxxx: tw_chrdev_ioctl(): Error copying buffer length from userspace.\n");
clear_bit(TW_IN_CHRDEV_IOCTL, &tw_dev->flags);
return -EFAULT;
}
/* Check size */
if (data_buffer_length > TW_MAX_SECTORS * 512) {
printk(KERN_WARNING "3w-xxxx: tw_chrdev_ioctl(): Invalid buffer size (%d).\n", data_buffer_length);
clear_bit(TW_IN_CHRDEV_IOCTL, &tw_dev->flags);
return -EFAULT;
}
/* Hardware can only do multiple of 512 byte transfers */
if (data_buffer_length % 512)
data_buffer_length_adjusted = data_buffer_length + 512 - (data_buffer_length % 512);
else
data_buffer_length_adjusted = data_buffer_length;
/* Now allocate ioctl buf memory */
cpu_addr = pci_alloc_consistent(tw_dev->tw_pci_dev, data_buffer_length_adjusted+sizeof(TW_New_Ioctl) - 1, &dma_handle);
if (cpu_addr == NULL) {
printk(KERN_WARNING "3w-xxxx: tw_chrdev_ioctl(): Error allocating memory.\n");
clear_bit(TW_IN_CHRDEV_IOCTL, &tw_dev->flags);
return -ENOMEM;
}
tw_ioctl = (TW_New_Ioctl *)cpu_addr;
/* Now copy down the entire ioctl */
error = copy_from_user(tw_ioctl, (void *)arg, data_buffer_length + sizeof(TW_New_Ioctl) - 1);
if (error) {
printk(KERN_WARNING "3w-xxxx: tw_chrdev_ioctl(): Error copying data from userspace.\n");
pci_free_consistent(tw_dev->tw_pci_dev, data_buffer_length_adjusted+sizeof(TW_New_Ioctl) - 1, cpu_addr, dma_handle);
clear_bit(TW_IN_CHRDEV_IOCTL, &tw_dev->flags);
return -EFAULT;
}
passthru = (TW_Passthru *)&tw_ioctl->firmware_command;
/* See which ioctl we are doing */
switch (cmd) {
case TW_OP_NOP:
dprintk(KERN_WARNING "3w-xxxx: tw_chrdev_ioctl(): caught TW_OP_NOP.\n");
break;
case TW_OP_AEN_LISTEN:
dprintk(KERN_WARNING "3w-xxxx: tw_chrdev_ioctl(): caught TW_AEN_LISTEN.\n");
memset(tw_ioctl->data_buffer, 0, tw_ioctl->data_buffer_length);
if (tw_dev->aen_head == tw_dev->aen_tail) {
tw_aen_code = TW_AEN_QUEUE_EMPTY;
} else {
tw_aen_code = tw_dev->aen_queue[tw_dev->aen_head];
if (tw_dev->aen_head == TW_Q_LENGTH - 1) {
tw_dev->aen_head = TW_Q_START;
} else {
tw_dev->aen_head = tw_dev->aen_head + 1;
}
}
memcpy(tw_ioctl->data_buffer, &tw_aen_code, sizeof(tw_aen_code));
break;
case TW_CMD_PACKET_WITH_DATA:
dprintk(KERN_WARNING "3w-xxxx: tw_chrdev_ioctl(): caught TW_CMD_PACKET_WITH_DATA.\n");
spin_lock_irqsave(&tw_dev->tw_lock, flags);
tw_state_request_start(tw_dev, &request_id);
/* Flag internal command */
tw_dev->srb[request_id] = 0;
/* Flag chrdev ioctl */
tw_dev->chrdev_request_id = request_id;
tw_ioctl->firmware_command.request_id = request_id;
/* Load the sg list */
switch (tw_ioctl->firmware_command.byte0.sgl_offset) {
case 2:
tw_ioctl->firmware_command.byte8.param.sgl[0].address = dma_handle + sizeof(TW_New_Ioctl) - 1;
tw_ioctl->firmware_command.byte8.param.sgl[0].length = data_buffer_length_adjusted;
break;
case 3:
tw_ioctl->firmware_command.byte8.io.sgl[0].address = dma_handle + sizeof(TW_New_Ioctl) - 1;
tw_ioctl->firmware_command.byte8.io.sgl[0].length = data_buffer_length_adjusted;
break;
case 5:
passthru->sg_list[0].address = dma_handle + sizeof(TW_New_Ioctl) - 1;
passthru->sg_list[0].length = data_buffer_length_adjusted;
break;
}
memcpy(tw_dev->command_packet_virtual_address[request_id], &(tw_ioctl->firmware_command), sizeof(TW_Command));
/* Now post the command packet to the controller */
tw_post_command_packet(tw_dev, request_id);
spin_unlock_irqrestore(&tw_dev->tw_lock, flags);
/* Now wait for the command to complete */
do_gettimeofday(&before);
tw_ioctl_chrdev_retry:
if (tw_dev->chrdev_request_id != TW_IOCTL_CHRDEV_FREE) {
interruptible_sleep_on_timeout(&tw_dev->ioctl_wqueue, 1);
do_gettimeofday(&timeout);
if (before.tv_sec + TW_IOCTL_CHRDEV_TIMEOUT < timeout.tv_sec) {
/* Now we need to reset the board */
printk(KERN_WARNING "3w-xxxx: scsi%d: Character ioctl (0x%x) timed out, resetting card.\n", tw_dev->host->host_no, cmd);
spin_lock_irqsave(&tw_dev->tw_lock, flags);
tw_dev->state[request_id] = TW_S_COMPLETED;
tw_state_request_finish(tw_dev, request_id);
pci_free_consistent(tw_dev->tw_pci_dev, data_buffer_length_adjusted+sizeof(TW_New_Ioctl) - 1, cpu_addr, dma_handle);
tw_dev->posted_request_count--;
if (tw_reset_device_extension(tw_dev)) {
printk(KERN_WARNING "3w-xxxx: tw_chrdev_ioctl(): Reset failed for card %d.\n", tw_dev->host->host_no);
}
spin_unlock_irqrestore(&tw_dev->tw_lock, flags);
clear_bit(TW_IN_CHRDEV_IOCTL, &tw_dev->flags);
if (signal_pending(current))
return -EINTR;
else
return -EIO;
} else {
goto tw_ioctl_chrdev_retry;
}
}
/* Now copy in the command packet response */
memcpy(&(tw_ioctl->firmware_command), tw_dev->command_packet_virtual_address[request_id], sizeof(TW_Command));
/* Now complete the io */
spin_lock_irqsave(&tw_dev->tw_lock, flags);
tw_dev->posted_request_count--;
tw_dev->state[request_id] = TW_S_COMPLETED;
tw_state_request_finish(tw_dev, request_id);
spin_unlock_irqrestore(&tw_dev->tw_lock, flags);
break;
default:
printk(KERN_WARNING "3w-xxxx: Unknown chrdev ioctl 0x%x.\n", cmd);
}
/* Now copy the response to userspace */
error = copy_to_user((void *)arg, tw_ioctl, sizeof(TW_New_Ioctl) + tw_ioctl->data_buffer_length - 1);
if (error) {
printk(KERN_WARNING "3w-xxxx: tw_chrdev_ioctl(): Error copying data to userspace.\n");
pci_free_consistent(tw_dev->tw_pci_dev, data_buffer_length_adjusted+sizeof(TW_New_Ioctl) - 1, cpu_addr, dma_handle);
clear_bit(TW_IN_CHRDEV_IOCTL, &tw_dev->flags);
return -EFAULT;
}
/* Now free ioctl buf memory */
pci_free_consistent(tw_dev->tw_pci_dev, data_buffer_length_adjusted+sizeof(TW_New_Ioctl) - 1, cpu_addr, dma_handle);
clear_bit(TW_IN_CHRDEV_IOCTL, &tw_dev->flags);
return 0;
} /* End tw_chrdev_ioctl() */
/* This function handles open for the character device */
static int tw_chrdev_open(struct inode *inode, struct file *file)
{
unsigned int minor_number;
dprintk(KERN_WARNING "3w-xxxx: tw_ioctl_open()\n");
minor_number = minor(inode->i_rdev);
if (minor_number >= tw_device_extension_count)
return -ENODEV;
return 0;
} /* End tw_chrdev_open() */
/* This function handles close for the character device */
static int tw_chrdev_release(struct inode *inode, struct file *file)
{
dprintk(KERN_WARNING "3w-xxxx: tw_ioctl_release()\n");
return 0;
} /* End tw_chrdev_release() */
/* This function will clear all interrupts on the controller */ /* This function will clear all interrupts on the controller */
void tw_clear_all_interrupts(TW_Device_Extension *tw_dev) void tw_clear_all_interrupts(TW_Device_Extension *tw_dev)
{ {
...@@ -867,7 +1087,9 @@ int tw_findcards(Scsi_Host_Template *tw_host) ...@@ -867,7 +1087,9 @@ int tw_findcards(Scsi_Host_Template *tw_host)
/* Disable interrupts on the card */ /* Disable interrupts on the card */
tw_disable_interrupts(tw_dev); tw_disable_interrupts(tw_dev);
tries = 0;
while (tries < TW_MAX_RESET_TRIES) { while (tries < TW_MAX_RESET_TRIES) {
/* Do soft reset */ /* Do soft reset */
tw_soft_reset(tw_dev); tw_soft_reset(tw_dev);
...@@ -897,8 +1119,8 @@ int tw_findcards(Scsi_Host_Template *tw_host) ...@@ -897,8 +1119,8 @@ int tw_findcards(Scsi_Host_Template *tw_host)
continue; continue;
} }
/* Make sure that io region isn't already taken */ /* Reserve the io address space */
if (check_region((tw_dev->tw_pci_dev->resource[0].start), TW_IO_ADDRESS_RANGE)) { if (!request_region((tw_dev->tw_pci_dev->resource[0].start), TW_IO_ADDRESS_RANGE, TW_DEVICE_NAME)) {
printk(KERN_WARNING "3w-xxxx: tw_findcards(): Couldn't get io range 0x%lx-0x%lx for card %d.\n", printk(KERN_WARNING "3w-xxxx: tw_findcards(): Couldn't get io range 0x%lx-0x%lx for card %d.\n",
(tw_dev->tw_pci_dev->resource[0].start), (tw_dev->tw_pci_dev->resource[0].start),
(tw_dev->tw_pci_dev->resource[0].start) + (tw_dev->tw_pci_dev->resource[0].start) +
...@@ -907,16 +1129,10 @@ int tw_findcards(Scsi_Host_Template *tw_host) ...@@ -907,16 +1129,10 @@ int tw_findcards(Scsi_Host_Template *tw_host)
kfree(tw_dev); kfree(tw_dev);
continue; continue;
} }
/* Reserve the io address space */
request_region((tw_dev->tw_pci_dev->resource[0].start), TW_IO_ADDRESS_RANGE, TW_DEVICE_NAME);
error = tw_initialize_units(tw_dev); error = tw_initialize_units(tw_dev);
if (error) { if (error) {
printk(KERN_WARNING "3w-xxxx: No valid units for for card %d.\n", j); printk(KERN_WARNING "3w-xxxx: No valid units for for card %d.\n", j);
release_region((tw_dev->tw_pci_dev->resource[0].start), TW_IO_ADDRESS_RANGE);
tw_free_device_extension(tw_dev);
kfree(tw_dev);
continue;
} }
error = tw_initconnection(tw_dev, TW_INIT_MESSAGE_CREDITS); error = tw_initconnection(tw_dev, TW_INIT_MESSAGE_CREDITS);
...@@ -935,7 +1151,7 @@ int tw_findcards(Scsi_Host_Template *tw_host) ...@@ -935,7 +1151,7 @@ int tw_findcards(Scsi_Host_Template *tw_host)
if (tw_dev->num_units > 0) { if (tw_dev->num_units > 0) {
/* Use SHT cmd_per_lun here */ /* Use SHT cmd_per_lun here */
tw_dev->free_head = TW_Q_START; tw_dev->free_head = TW_Q_START;
tw_dev->free_tail = TW_Q_LENGTH - 1; tw_dev->free_tail = TW_Q_START;
tw_dev->free_wrap = TW_Q_LENGTH - 1; tw_dev->free_wrap = TW_Q_LENGTH - 1;
} }
...@@ -992,13 +1208,7 @@ int tw_findcards(Scsi_Host_Template *tw_host) ...@@ -992,13 +1208,7 @@ int tw_findcards(Scsi_Host_Template *tw_host)
/* Tell the firmware we support shutdown notification*/ /* Tell the firmware we support shutdown notification*/
error = tw_setfeature(tw_dev2, 2, 1, &c); error = tw_setfeature(tw_dev2, 2, 1, &c);
if (error) { if (error) {
printk(KERN_WARNING "3w-xxxx: tw_setfeature(): Error setting features for card %d.\n", j); printk(KERN_WARNING "3w-xxxx: Unable to set features for card %d, old firmware or card.\n", j);
scsi_unregister(host);
release_region((tw_dev->tw_pci_dev->resource[0].start), TW_IO_ADDRESS_RANGE);
tw_free_device_extension(tw_dev);
kfree(tw_dev);
numcards--;
continue;
} }
/* Now setup the interrupt handler */ /* Now setup the interrupt handler */
...@@ -1023,10 +1233,14 @@ int tw_findcards(Scsi_Host_Template *tw_host) ...@@ -1023,10 +1233,14 @@ int tw_findcards(Scsi_Host_Template *tw_host)
} }
} }
if (numcards == 0) if (numcards == 0) {
printk(KERN_WARNING "3w-xxxx: No cards with valid units found.\n"); printk(KERN_WARNING "3w-xxxx: No cards found.\n");
else } else {
register_reboot_notifier(&tw_notifier); register_reboot_notifier(&tw_notifier);
if ((twe_major = register_chrdev (0, "twe", &tw_fops)) < 0) {
printk(KERN_WARNING "3w-xxxx: Unable to register \"twe\" character device, error = %d.\n", twe_major);
}
}
return numcards; return numcards;
} /* End tw_findcards() */ } /* End tw_findcards() */
...@@ -1153,6 +1367,8 @@ int tw_initialize_device_extension(TW_Device_Extension *tw_dev) ...@@ -1153,6 +1367,8 @@ int tw_initialize_device_extension(TW_Device_Extension *tw_dev)
tw_dev->pending_head = TW_Q_START; tw_dev->pending_head = TW_Q_START;
tw_dev->pending_tail = TW_Q_START; tw_dev->pending_tail = TW_Q_START;
spin_lock_init(&tw_dev->tw_lock); spin_lock_init(&tw_dev->tw_lock);
tw_dev->chrdev_request_id = TW_IOCTL_CHRDEV_FREE;
init_waitqueue_head(&tw_dev->ioctl_wqueue);
return 0; return 0;
} /* End tw_initialize_device_extension() */ } /* End tw_initialize_device_extension() */
...@@ -1395,9 +1611,14 @@ static void tw_interrupt(int irq, void *dev_instance, struct pt_regs *regs) ...@@ -1395,9 +1611,14 @@ static void tw_interrupt(int irq, void *dev_instance, struct pt_regs *regs)
/* Check for internal command completion */ /* Check for internal command completion */
if (tw_dev->srb[request_id] == 0) { if (tw_dev->srb[request_id] == 0) {
dprintk(KERN_WARNING "3w-xxxx: tw_interrupt(): Found internally posted command.\n"); dprintk(KERN_WARNING "3w-xxxx: tw_interrupt(): Found internally posted command.\n");
retval = tw_aen_complete(tw_dev, request_id); /* Check for chrdev ioctl completion */
if (retval) { if (request_id != tw_dev->chrdev_request_id) {
printk(KERN_WARNING "3w-xxxx: scsi%d: Error completing aen.\n", tw_dev->host->host_no); retval = tw_aen_complete(tw_dev, request_id);
if (retval) {
printk(KERN_WARNING "3w-xxxx: scsi%d: Error completing aen.\n", tw_dev->host->host_no);
}
} else {
tw_dev->chrdev_request_id = TW_IOCTL_CHRDEV_FREE;
} }
} else { } else {
switch (tw_dev->srb[request_id]->cmnd[0]) { switch (tw_dev->srb[request_id]->cmnd[0]) {
...@@ -1409,6 +1630,10 @@ static void tw_interrupt(int irq, void *dev_instance, struct pt_regs *regs) ...@@ -1409,6 +1630,10 @@ static void tw_interrupt(int irq, void *dev_instance, struct pt_regs *regs)
case WRITE_6: case WRITE_6:
dprintk(KERN_NOTICE "3w-xxxx: tw_interrupt(): caught WRITE_10/WRITE_6\n"); dprintk(KERN_NOTICE "3w-xxxx: tw_interrupt(): caught WRITE_10/WRITE_6\n");
break; break;
case TEST_UNIT_READY:
dprintk(KERN_NOTICE "3w-xxxx: tw_interrupt(): caught TEST_UNIT_READY\n");
error = tw_scsiop_test_unit_ready_complete(tw_dev, request_id);
break;
case INQUIRY: case INQUIRY:
dprintk(KERN_NOTICE "3w-xxxx: tw_interrupt(): caught INQUIRY\n"); dprintk(KERN_NOTICE "3w-xxxx: tw_interrupt(): caught INQUIRY\n");
error = tw_scsiop_inquiry_complete(tw_dev, request_id); error = tw_scsiop_inquiry_complete(tw_dev, request_id);
...@@ -2070,12 +2295,13 @@ int tw_reset_device_extension(TW_Device_Extension *tw_dev) ...@@ -2070,12 +2295,13 @@ int tw_reset_device_extension(TW_Device_Extension *tw_dev)
tw_dev->state[i] = TW_S_INITIAL; tw_dev->state[i] = TW_S_INITIAL;
} }
tw_dev->free_head = TW_Q_START; tw_dev->free_head = TW_Q_START;
tw_dev->free_tail = TW_Q_LENGTH - 1; tw_dev->free_tail = TW_Q_START;
tw_dev->posted_request_count = 0; tw_dev->posted_request_count = 0;
tw_dev->pending_request_count = 0; tw_dev->pending_request_count = 0;
tw_dev->pending_head = TW_Q_START; tw_dev->pending_head = TW_Q_START;
tw_dev->pending_tail = TW_Q_START; tw_dev->pending_tail = TW_Q_START;
tw_dev->reset_print = 0; tw_dev->reset_print = 0;
tw_dev->chrdev_request_id = TW_IOCTL_CHRDEV_FREE;
return 0; return 0;
} /* End tw_reset_device_extension() */ } /* End tw_reset_device_extension() */
...@@ -2357,7 +2583,6 @@ int tw_scsi_queue(Scsi_Cmnd *SCpnt, void (*done)(Scsi_Cmnd *)) ...@@ -2357,7 +2583,6 @@ int tw_scsi_queue(Scsi_Cmnd *SCpnt, void (*done)(Scsi_Cmnd *))
unsigned char *command = SCpnt->cmnd; unsigned char *command = SCpnt->cmnd;
int request_id = 0; int request_id = 0;
int error = 0; int error = 0;
unsigned long flags = 0;
TW_Device_Extension *tw_dev = (TW_Device_Extension *)SCpnt->device->host->hostdata; TW_Device_Extension *tw_dev = (TW_Device_Extension *)SCpnt->device->host->hostdata;
if (tw_dev == NULL) { if (tw_dev == NULL) {
...@@ -2367,14 +2592,14 @@ int tw_scsi_queue(Scsi_Cmnd *SCpnt, void (*done)(Scsi_Cmnd *)) ...@@ -2367,14 +2592,14 @@ int tw_scsi_queue(Scsi_Cmnd *SCpnt, void (*done)(Scsi_Cmnd *))
return 0; return 0;
} }
spin_lock_irqsave(&tw_dev->tw_lock, flags); spin_lock(&tw_dev->tw_lock);
dprintk(KERN_NOTICE "3w-xxxx: tw_scsi_queue()\n"); dprintk(KERN_NOTICE "3w-xxxx: tw_scsi_queue()\n");
/* Skip scsi command if it isn't for us */ /* Skip scsi command if it isn't for us */
if ((tw_dev->is_unit_present[SCpnt->device->id] == FALSE) || (SCpnt->device->lun != 0)) { if ((SCpnt->device->channel != 0) || (SCpnt->device->lun != 0)) {
SCpnt->result = (DID_BAD_TARGET << 16); SCpnt->result = (DID_BAD_TARGET << 16);
done(SCpnt); done(SCpnt);
spin_unlock_irqrestore(&tw_dev->tw_lock, flags); spin_unlock(&tw_dev->tw_lock);
return 0; return 0;
} }
...@@ -2387,6 +2612,9 @@ int tw_scsi_queue(Scsi_Cmnd *SCpnt, void (*done)(Scsi_Cmnd *)) ...@@ -2387,6 +2612,9 @@ int tw_scsi_queue(Scsi_Cmnd *SCpnt, void (*done)(Scsi_Cmnd *))
/* Save the scsi command for use by the ISR */ /* Save the scsi command for use by the ISR */
tw_dev->srb[request_id] = SCpnt; tw_dev->srb[request_id] = SCpnt;
/* Initialize phase to zero */
SCpnt->SCp.phase = 0;
switch (*command) { switch (*command) {
case READ_10: case READ_10:
case READ_6: case READ_6:
...@@ -2436,7 +2664,7 @@ int tw_scsi_queue(Scsi_Cmnd *SCpnt, void (*done)(Scsi_Cmnd *)) ...@@ -2436,7 +2664,7 @@ int tw_scsi_queue(Scsi_Cmnd *SCpnt, void (*done)(Scsi_Cmnd *))
SCpnt->result = (DID_ERROR << 16); SCpnt->result = (DID_ERROR << 16);
done(SCpnt); done(SCpnt);
} }
spin_unlock_irqrestore(&tw_dev->tw_lock, flags); spin_unlock(&tw_dev->tw_lock);
return 0; return 0;
} /* End tw_scsi_queue() */ } /* End tw_scsi_queue() */
...@@ -2460,6 +2688,12 @@ int tw_scsi_release(struct Scsi_Host *tw_host) ...@@ -2460,6 +2688,12 @@ int tw_scsi_release(struct Scsi_Host *tw_host)
/* Free up the IRQ */ /* Free up the IRQ */
free_irq(tw_dev->tw_pci_dev->irq, tw_dev); free_irq(tw_dev->tw_pci_dev->irq, tw_dev);
/* Unregister character device */
if (twe_major >= 0) {
unregister_chrdev(twe_major, "twe");
twe_major = -1;
}
/* Free up device extension resources */ /* Free up device extension resources */
tw_free_device_extension(tw_dev); tw_free_device_extension(tw_dev);
...@@ -2533,7 +2767,6 @@ int tw_scsiop_inquiry_complete(TW_Device_Extension *tw_dev, int request_id) ...@@ -2533,7 +2767,6 @@ int tw_scsiop_inquiry_complete(TW_Device_Extension *tw_dev, int request_id)
{ {
unsigned char *is_unit_present; unsigned char *is_unit_present;
unsigned char *request_buffer; unsigned char *request_buffer;
int i;
TW_Param *param; TW_Param *param;
dprintk(KERN_NOTICE "3w-xxxx: tw_scsiop_inquiry_complete()\n"); dprintk(KERN_NOTICE "3w-xxxx: tw_scsiop_inquiry_complete()\n");
...@@ -2545,12 +2778,12 @@ int tw_scsiop_inquiry_complete(TW_Device_Extension *tw_dev, int request_id) ...@@ -2545,12 +2778,12 @@ int tw_scsiop_inquiry_complete(TW_Device_Extension *tw_dev, int request_id)
} }
request_buffer = tw_dev->srb[request_id]->request_buffer; request_buffer = tw_dev->srb[request_id]->request_buffer;
memset(request_buffer, 0, tw_dev->srb[request_id]->request_bufflen); memset(request_buffer, 0, tw_dev->srb[request_id]->request_bufflen);
request_buffer[0] = TYPE_DISK; /* Peripheral device type */ request_buffer[0] = TYPE_DISK; /* Peripheral device type */
request_buffer[1] = 0; /* Device type modifier */ request_buffer[1] = 0; /* Device type modifier */
request_buffer[2] = 0; /* No ansi/iso compliance */ request_buffer[2] = 0; /* No ansi/iso compliance */
request_buffer[4] = 31; /* Additional length */ request_buffer[4] = 31; /* Additional length */
memcpy(&request_buffer[8], "3ware ", 8); /* Vendor ID */ memcpy(&request_buffer[8], "3ware ", 8); /* Vendor ID */
memcpy(&request_buffer[16], "3w-xxxx ", 16); /* Product ID */ sprintf(&request_buffer[16], "Logical Disk %-2d ", tw_dev->srb[request_id]->device->id);
memcpy(&request_buffer[32], tw_driver_version, 3); memcpy(&request_buffer[32], tw_driver_version, 3);
param = (TW_Param *)tw_dev->alignment_virtual_address[request_id]; param = (TW_Param *)tw_dev->alignment_virtual_address[request_id];
...@@ -2560,15 +2793,12 @@ int tw_scsiop_inquiry_complete(TW_Device_Extension *tw_dev, int request_id) ...@@ -2560,15 +2793,12 @@ int tw_scsiop_inquiry_complete(TW_Device_Extension *tw_dev, int request_id)
} }
is_unit_present = &(param->data[0]); is_unit_present = &(param->data[0]);
for (i=0 ; i<TW_MAX_UNITS; i++) { if (is_unit_present[tw_dev->srb[request_id]->device->id] & TW_UNIT_ONLINE) {
if (is_unit_present[i] == 0) { tw_dev->is_unit_present[tw_dev->srb[request_id]->device->id] = TRUE;
tw_dev->is_unit_present[i] = FALSE; } else {
} else { tw_dev->is_unit_present[tw_dev->srb[request_id]->device->id] = FALSE;
if (is_unit_present[i] & TW_UNIT_ONLINE) { tw_dev->srb[request_id]->result = (DID_BAD_TARGET << 16);
tw_dev->is_unit_present[i] = TRUE; return TW_ISR_DONT_RESULT;
dprintk(KERN_NOTICE "3w-xxxx: tw_scsiop_inquiry_complete: Unit %d found.\n", i);
}
}
} }
return 0; return 0;
...@@ -2946,17 +3176,88 @@ int tw_scsiop_synchronize_cache(TW_Device_Extension *tw_dev, int request_id) ...@@ -2946,17 +3176,88 @@ int tw_scsiop_synchronize_cache(TW_Device_Extension *tw_dev, int request_id)
/* This function will handle test unit ready scsi command */ /* This function will handle test unit ready scsi command */
int tw_scsiop_test_unit_ready(TW_Device_Extension *tw_dev, int request_id) int tw_scsiop_test_unit_ready(TW_Device_Extension *tw_dev, int request_id)
{ {
TW_Param *param;
TW_Command *command_packet;
unsigned long command_que_value;
u32 command_que_addr;
unsigned long param_value;
dprintk(KERN_NOTICE "3w-xxxx: tw_scsiop_test_unit_ready()\n"); dprintk(KERN_NOTICE "3w-xxxx: tw_scsiop_test_unit_ready()\n");
/* Tell the scsi layer were done */ /* Initialize command packet */
tw_dev->state[request_id] = TW_S_COMPLETED; command_que_addr = tw_dev->registers.command_que_addr;
tw_state_request_finish(tw_dev, request_id); command_packet = (TW_Command *)tw_dev->command_packet_virtual_address[request_id];
tw_dev->srb[request_id]->result = (DID_OK << 16); if (command_packet == NULL) {
tw_dev->srb[request_id]->scsi_done(tw_dev->srb[request_id]); printk(KERN_WARNING "3w-xxxx: tw_scsiop_test_unit_ready(): 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.parameter_count = 1;
/* Now setup the param */
if (tw_dev->alignment_virtual_address[request_id] == NULL) {
printk(KERN_WARNING "3w-xxxx: tw_scsiop_test_unit_ready(): 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 = 3; /* unit summary table */
param->parameter_id = 3; /* unitsstatus parameter */
param->parameter_size_bytes = TW_MAX_UNITS;
param_value = tw_dev->alignment_physical_address[request_id];
if (param_value == 0) {
printk(KERN_WARNING "3w-xxxx: tw_scsiop_test_unit_ready(): 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);
command_que_value = tw_dev->command_packet_physical_address[request_id];
if (command_que_value == 0) {
printk(KERN_WARNING "3w-xxxx: tw_scsiop_test_unit_ready(): Bad command packet physical address.\n");
return 1;
}
/* Now try to post the command packet */
tw_post_command_packet(tw_dev, request_id);
return 0; return 0;
} /* End tw_scsiop_test_unit_ready() */ } /* End tw_scsiop_test_unit_ready() */
/* This function is called by the isr to complete a testunitready command */
int tw_scsiop_test_unit_ready_complete(TW_Device_Extension *tw_dev, int request_id)
{
unsigned char *is_unit_present;
TW_Param *param;
dprintk(KERN_WARNING "3w-xxxx: tw_scsiop_test_unit_ready_complete()\n");
param = (TW_Param *)tw_dev->alignment_virtual_address[request_id];
if (param == NULL) {
printk(KERN_WARNING "3w-xxxx: tw_scsiop_test_unit_ready_complete(): Bad alignment virtual address.\n");
return 1;
}
is_unit_present = &(param->data[0]);
if (is_unit_present[tw_dev->srb[request_id]->device->id] & TW_UNIT_ONLINE) {
tw_dev->is_unit_present[tw_dev->srb[request_id]->device->id] = TRUE;
} else {
tw_dev->is_unit_present[tw_dev->srb[request_id]->device->id] = FALSE;
tw_dev->srb[request_id]->result = (DID_BAD_TARGET << 16);
return TW_ISR_DONT_RESULT;
}
return 0;
} /* End tw_scsiop_test_unit_ready_complete() */
/* Set a value in the features table */ /* Set a value in the features table */
int tw_setfeature(TW_Device_Extension *tw_dev, int parm, int param_size, int tw_setfeature(TW_Device_Extension *tw_dev, int parm, int param_size,
unsigned char *val) unsigned char *val)
...@@ -3091,17 +3392,13 @@ int tw_state_request_finish(TW_Device_Extension *tw_dev, int request_id) ...@@ -3091,17 +3392,13 @@ int tw_state_request_finish(TW_Device_Extension *tw_dev, int request_id)
{ {
dprintk(KERN_NOTICE "3w-xxxx: tw_state_request_finish()\n"); dprintk(KERN_NOTICE "3w-xxxx: tw_state_request_finish()\n");
do {
if (tw_dev->free_tail == tw_dev->free_wrap) {
tw_dev->free_tail = TW_Q_START;
} else {
tw_dev->free_tail = tw_dev->free_tail + 1;
}
} while ((tw_dev->state[tw_dev->free_queue[tw_dev->free_tail]] != TW_S_COMPLETED));
tw_dev->free_queue[tw_dev->free_tail] = request_id; tw_dev->free_queue[tw_dev->free_tail] = request_id;
tw_dev->state[request_id] = TW_S_FINISHED; tw_dev->state[request_id] = TW_S_FINISHED;
if (tw_dev->free_tail == tw_dev->free_wrap)
tw_dev->free_tail = TW_Q_START;
else
tw_dev->free_tail++;
dprintk(KERN_NOTICE "3w-xxxx: tw_state_request_finish(): Freeing request_id %d\n", request_id); dprintk(KERN_NOTICE "3w-xxxx: tw_state_request_finish(): Freeing request_id %d\n", request_id);
return 0; return 0;
...@@ -3115,20 +3412,16 @@ int tw_state_request_start(TW_Device_Extension *tw_dev, int *request_id) ...@@ -3115,20 +3412,16 @@ int tw_state_request_start(TW_Device_Extension *tw_dev, int *request_id)
dprintk(KERN_NOTICE "3w-xxxx: tw_state_request_start()\n"); dprintk(KERN_NOTICE "3w-xxxx: tw_state_request_start()\n");
/* Obtain next free request_id */ /* Obtain next free request_id */
do {
if (tw_dev->free_head == tw_dev->free_wrap) {
tw_dev->free_head = TW_Q_START;
} else {
tw_dev->free_head = tw_dev->free_head + 1;
}
} while (tw_dev->state[tw_dev->free_queue[tw_dev->free_head]] & TW_START_MASK);
id = tw_dev->free_queue[tw_dev->free_head]; id = tw_dev->free_queue[tw_dev->free_head];
if (tw_dev->free_head == tw_dev->free_wrap)
tw_dev->free_head = TW_Q_START;
else
tw_dev->free_head++;
dprintk(KERN_NOTICE "3w-xxxx: tw_state_request_start(): id = %d.\n", id);
*request_id = id; *request_id = id;
tw_dev->state[id] = TW_S_STARTED; tw_dev->state[id] = TW_S_STARTED;
dprintk(KERN_NOTICE "3w-xxxx: tw_state_request_start(): id = %d.\n", id);
return 0; return 0;
} /* End tw_state_request_start() */ } /* End tw_state_request_start() */
......
...@@ -6,7 +6,7 @@ ...@@ -6,7 +6,7 @@
Arnaldo Carvalho de Melo <acme@conectiva.com.br> Arnaldo Carvalho de Melo <acme@conectiva.com.br>
Brad Strand <linux@3ware.com> Brad Strand <linux@3ware.com>
Copyright (C) 1999-2002 3ware Inc. Copyright (C) 1999-2003 3ware Inc.
Kernel compatiblity By: Andre Hedrick <andre@suse.com> Kernel compatiblity By: Andre Hedrick <andre@suse.com>
Non-Copyright (C) 2000 Andre Hedrick <andre@suse.com> Non-Copyright (C) 2000 Andre Hedrick <andre@suse.com>
...@@ -113,11 +113,11 @@ static unsigned char tw_sense_table[][4] = ...@@ -113,11 +113,11 @@ static unsigned char tw_sense_table[][4] =
{0x84, 0x0b, 0x47, 0x00}, // Data CRC error SCSI parity error {0x84, 0x0b, 0x47, 0x00}, // Data CRC error SCSI parity error
{0xd0, 0x0b, 0x00, 0x00}, // Device busy Aborted command {0xd0, 0x0b, 0x00, 0x00}, // Device busy Aborted command
{0xd1, 0x0b, 0x00, 0x00}, // Device busy Aborted command {0xd1, 0x0b, 0x00, 0x00}, // Device busy Aborted command
{0x37, 0x02, 0x04, 0x00}, // Unit offline Not ready
/* Codes for older firmware */ /* Codes for older firmware */
// 3ware Error SCSI Error // 3ware Error SCSI Error
{0x09, 0x0b, 0x00, 0x00}, // Unrecovered disk error Aborted command {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 {0x51, 0x0b, 0x00, 0x00} // Unspecified Aborted command
}; };
...@@ -219,18 +219,23 @@ static unsigned char tw_sense_table[][4] = ...@@ -219,18 +219,23 @@ static unsigned char tw_sense_table[][4] =
#define TW_MAX_PCI_BUSES 255 #define TW_MAX_PCI_BUSES 255
#define TW_MAX_RESET_TRIES 3 #define TW_MAX_RESET_TRIES 3
#define TW_UNIT_INFORMATION_TABLE_BASE 0x300 #define TW_UNIT_INFORMATION_TABLE_BASE 0x300
#define TW_MAX_CMDS_PER_LUN 255 #define TW_MAX_CMDS_PER_LUN 254 /* 254 for io, 1 for
chrdev ioctl, one for
internal aen post */
#define TW_BLOCK_SIZE 0x200 /* 512-byte blocks */ #define TW_BLOCK_SIZE 0x200 /* 512-byte blocks */
#define TW_IOCTL 0x80 #define TW_IOCTL 0x80
#define TW_UNIT_ONLINE 1 #define TW_UNIT_ONLINE 1
#define TW_IN_INTR 1 #define TW_IN_INTR 1
#define TW_IN_IOCTL 2 #define TW_IN_IOCTL 2
#define TW_IN_CHRDEV_IOCTL 3
#define TW_MAX_SECTORS 256 #define TW_MAX_SECTORS 256
#define TW_AEN_WAIT_TIME 1000 #define TW_AEN_WAIT_TIME 1000
#define TW_IOCTL_WAIT_TIME (1 * HZ) /* 1 second */ #define TW_IOCTL_WAIT_TIME (1 * HZ) /* 1 second */
#define TW_ISR_DONT_COMPLETE 2 #define TW_ISR_DONT_COMPLETE 2
#define TW_ISR_DONT_RESULT 3 #define TW_ISR_DONT_RESULT 3
#define TW_IOCTL_TIMEOUT 25 /* 25 seconds */ #define TW_IOCTL_TIMEOUT 25 /* 25 seconds */
#define TW_IOCTL_CHRDEV_TIMEOUT 25 /* 25 seconds */
#define TW_IOCTL_CHRDEV_FREE -1
/* Macros */ /* Macros */
#define TW_STATUS_ERRORS(x) \ #define TW_STATUS_ERRORS(x) \
...@@ -246,6 +251,8 @@ static unsigned char tw_sense_table[][4] = ...@@ -246,6 +251,8 @@ static unsigned char tw_sense_table[][4] =
#define dprintk(msg...) do { } while(0) #define dprintk(msg...) do { } while(0)
#endif #endif
#pragma pack(1)
/* Scatter Gather List Entry */ /* Scatter Gather List Entry */
typedef struct TAG_TW_SG_Entry { typedef struct TAG_TW_SG_Entry {
u32 address; u32 address;
...@@ -295,6 +302,8 @@ typedef struct TW_Command { ...@@ -295,6 +302,8 @@ typedef struct TW_Command {
} byte8; } byte8;
} TW_Command; } TW_Command;
#pragma pack()
typedef struct TAG_TW_Ioctl { typedef struct TAG_TW_Ioctl {
unsigned char opcode; unsigned char opcode;
unsigned short table_id; unsigned short table_id;
...@@ -304,6 +313,16 @@ typedef struct TAG_TW_Ioctl { ...@@ -304,6 +313,16 @@ typedef struct TAG_TW_Ioctl {
unsigned char data[1]; unsigned char data[1];
} TW_Ioctl; } TW_Ioctl;
#pragma pack(1)
/* Structure for new chardev ioctls */
typedef struct TAG_TW_New_Ioctl {
unsigned int data_buffer_length;
unsigned char padding [508];
TW_Command firmware_command;
char data_buffer[1];
} TW_New_Ioctl;
/* GetParam descriptor */ /* GetParam descriptor */
typedef struct { typedef struct {
unsigned short table_id; unsigned short table_id;
...@@ -414,8 +433,12 @@ typedef struct TAG_TW_Device_Extension { ...@@ -414,8 +433,12 @@ typedef struct TAG_TW_Device_Extension {
unsigned long *ioctl_data[TW_Q_LENGTH]; unsigned long *ioctl_data[TW_Q_LENGTH];
int reset_print; int reset_print;
char online; char online;
volatile int chrdev_request_id;
wait_queue_head_t ioctl_wqueue;
} TW_Device_Extension; } TW_Device_Extension;
#pragma pack()
/* Function prototypes */ /* Function prototypes */
int tw_aen_complete(TW_Device_Extension *tw_dev, int request_id); 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_drain_queue(TW_Device_Extension *tw_dev);
...@@ -463,6 +486,7 @@ int tw_scsiop_read_write(TW_Device_Extension *tw_dev, int request_id); ...@@ -463,6 +486,7 @@ int tw_scsiop_read_write(TW_Device_Extension *tw_dev, int request_id);
int tw_scsiop_request_sense(TW_Device_Extension *tw_dev, int request_id); int tw_scsiop_request_sense(TW_Device_Extension *tw_dev, int request_id);
int tw_scsiop_synchronize_cache(TW_Device_Extension *tw_dev, int request_id); int tw_scsiop_synchronize_cache(TW_Device_Extension *tw_dev, int request_id);
int tw_scsiop_test_unit_ready(TW_Device_Extension *tw_dev, int request_id); int tw_scsiop_test_unit_ready(TW_Device_Extension *tw_dev, int request_id);
int tw_scsiop_test_unit_ready_complete(TW_Device_Extension *tw_dev, int request_id);
int tw_setfeature(TW_Device_Extension *tw_dev, int parm, int param_size, int tw_setfeature(TW_Device_Extension *tw_dev, int parm, int param_size,
unsigned char *val); unsigned char *val);
int tw_setup_irq(TW_Device_Extension *tw_dev); int tw_setup_irq(TW_Device_Extension *tw_dev);
...@@ -483,9 +507,10 @@ void tw_unmask_command_interrupt(TW_Device_Extension *tw_dev); ...@@ -483,9 +507,10 @@ void tw_unmask_command_interrupt(TW_Device_Extension *tw_dev);
.eh_abort_handler = tw_scsi_eh_abort, \ .eh_abort_handler = tw_scsi_eh_abort, \
.eh_host_reset_handler = tw_scsi_eh_reset, \ .eh_host_reset_handler = tw_scsi_eh_reset, \
.bios_param = tw_scsi_biosparam, \ .bios_param = tw_scsi_biosparam, \
.can_queue = TW_Q_LENGTH-1, \ .can_queue = TW_Q_LENGTH-2, \
.this_id = -1, \ .this_id = -1, \
.sg_tablesize = TW_MAX_SGL_LENGTH, \ .sg_tablesize = TW_MAX_SGL_LENGTH, \
.max_sectors = TW_MAX_SECTORS, \
.cmd_per_lun = TW_MAX_CMDS_PER_LUN, \ .cmd_per_lun = TW_MAX_CMDS_PER_LUN, \
.present = 0, \ .present = 0, \
.unchecked_isa_dma = 0, \ .unchecked_isa_dma = 0, \
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment