3ware 9000 SATA-RAID driver v2.26.00.009
PATCH 1/2 3ware 9000 SATA-RAID driver v2.26.00.009
Andrew/James,
Here is an updated version v2.26.00.009 of the 3ware 9000 SATA-RAID driver.
Changes in this release:
- Convert driver to pci_driver format.
- Remove proc interface, add sysfs attributes.
- Return SCSI_MLQUEUE_HOST_BUSY when card status is busy.
Future versions may include:
- PAE fix
- 80 column wrap fixes
- Remove more prototypes by re-ordering functions.
- sysfs queue depth adjustment instead of module_param
Anybody who wishes to review this updated driver implementation and give feedback would be very appreciated!
--
Adam Radford
Staff Software Engineer
AMCC
drivers/scsi/3w-9xxx.c +2144 -0
@@ -0,0 +1,2144 @@ +/* +3w-9xxx.c -- 3ware 9000 Storage Controller device driver for Linux. + +Written By: Adam Radford <[email protected]> + +Copyright (C) 2004 Applied Micro Circuits Corporation. + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; version 2 of the License. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +NO WARRANTY +THE PROGRAM IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OR +CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT +LIMITATION, ANY WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT, +MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Each Recipient is +solely responsible for determining the appropriateness of using and +distributing the Program and assumes all risks associated with its +exercise of rights under this Agreement, including but not limited to +the risks and costs of program errors, damage to or loss of data, +programs or equipment, and unavailability or interruption of operations. + +DISCLAIMER OF LIABILITY +NEITHER RECIPIENT NOR ANY CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING WITHOUT LIMITATION LOST PROFITS), HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR +TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +USE OR DISTRIBUTION OF THE PROGRAM OR THE EXERCISE OF ANY RIGHTS GRANTED +HEREUNDER, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +Bugs/Comments/Suggestions should be mailed to: +[email protected] + +For more information, goto: +http://www.amcc.com + +Note: This version of the driver does not contain a bundled firmware +image. + +History +------- +2.26.00.005 - Initial release. +2.26.00.006 - Remove TW_WRITEL macro, replace with writeq(). +2.26.00.007 - Skip lun and channel probes. +2.26.00.008 - Fix bug in attention/command interrupt handling. +2.26.00.009 - Convert driver to pci_driver format. +Remove proc interface, add sysfs attributes. +Return SCSI_MLQUEUE_HOST_BUSY when card status is busy. +*/ + +#include <linux/module.h> +#include <linux/reboot.h> +#include <linux/spinlock.h> +#include <linux/interrupt.h> +#include <linux/moduleparam.h> +#include <linux/errno.h> +#include <linux/types.h> +#include <linux/delay.h> +#include <linux/pci.h> +#include <linux/time.h> +#include <asm/io.h> +#include <asm/irq.h> +#include <asm/uaccess.h> +#include <scsi/scsi.h> +#include <scsi/scsi_host.h> +#include <scsi/scsi_tcq.h> +#include <scsi/scsi_cmnd.h> +#include "3w-9xxx.h" + +/* Globals */ +char *twa_driver_version="2.26.00.009" ; +TW_Device_Extension *twa_device_extension_list[TW_MAX_SLOT ]; +static unsigned int twa_device_extension_count; +static int twa_major =-1 ; +extern struct timezone sys_tz; +static int cmds_per_lun; + +/* Module parameters */ +MODULE_AUTHOR ("AMCC" ); +MODULE_DESCRIPTION ("3ware 9000 Storage Controller Linux Driver" ); +MODULE_LICENSE ("GPL" ); +module_param (cmds_per_lun, int,0 ); +MODULE_PARM_DESC (cmds_per_lun,"Maximum commands per LUN" ); + +/* Function prototypes */ +static int twa_aen_complete (TW_Device_Extension *tw_dev,int request_id); +static int twa_aen_drain_queue (TW_Device_Extension *tw_dev,int check_reset); +static void twa_aen_queue_event (TW_Device_Extension *tw_dev,TW_Command_Apache_Header *header); +static int twa_aen_read_queue (TW_Device_Extension *tw_dev,int request_id); +static char *twa_aen_severity_lookup (unsigned char severity_code); +static void twa_aen_sync_time (TW_Device_Extension *tw_dev,int request_id); +static int twa_allocate_memory (TW_Device_Extension *tw_dev,int size,int which); +static int twa_check_bits (u32 status_reg_value); +static int twa_check_srl (TW_Device_Extension *tw_dev,int *flashed); +static int twa_chrdev_ioctl (struct inode *inode,struct file *file,unsigned int cmd,unsigned long arg); +static int twa_chrdev_open (struct inode *inode,struct file *file); +static int twa_decode_bits (TW_Device_Extension *tw_dev,u32 status_reg_value); +static int twa_empty_response_queue (TW_Device_Extension *tw_dev); +static int twa_fill_sense (TW_Device_Extension *tw_dev,int request_id,int copy_sense,int print_host); +static void twa_free_device_extension (TW_Device_Extension *tw_dev); +static void twa_free_request_id (TW_Device_Extension *tw_dev,int request_id); +static void *twa_get_param (TW_Device_Extension *tw_dev,int request_id,int table_id,int parameter_id,int parameter_size_bytes); +static void twa_get_request_id (TW_Device_Extension *tw_dev,int *request_id); +static int twa_initconnection (TW_Device_Extension *tw_dev,int message_credits, +u32 set_features,unsigned short current_fw_srl, +unsigned short current_fw_arch_id, +unsigned short current_fw_branch, +unsigned short current_fw_build, +unsigned short *fw_on_ctlr_srl, +unsigned short *fw_on_ctlr_arch_id, +unsigned short *fw_on_ctlr_branch, +unsigned short *fw_on_ctlr_build, +u32 *init_connect_result); +static int twa_initialize_device_extension (TW_Device_Extension *tw_dev); +static irqreturn_t twa_interrupt (int irq,void *dev_instance,struct pt_regs *regs); +static void twa_load_sgl (TW_Command_Full *full_command_packet,int request_id,dma_addr_t dma_handle,int length); +static int twa_map_scsi_sg_data (TW_Device_Extension *tw_dev,int request_id); +static dma_addr_t twa_map_scsi_single_data (TW_Device_Extension *tw_dev,int request_id); +static int twa_poll_response (TW_Device_Extension *tw_dev,int request_id,int seconds); +static int twa_poll_status (TW_Device_Extension *tw_dev,u32 flag,int seconds); +static int twa_poll_status_gone (TW_Device_Extension *tw_dev,u32 flag,int seconds); +static int twa_post_command_packet (TW_Device_Extension *tw_dev,int request_id,char internal); +static int twa_reset_device_extension (TW_Device_Extension *tw_dev); +static int twa_reset_sequence (TW_Device_Extension *tw_dev,int soft_reset); +static int twa_scsi_biosparam (struct scsi_device *sdev,struct block_device *bdev,sector_t capacity,int geom[]); +static int __devinit twa_probe (struct pci_dev *pdev,const struct pci_device_id *dev_id); +static int twa_scsi_eh_abort (struct scsi_cmnd *SCpnt); +static int twa_scsi_eh_reset (struct scsi_cmnd *SCpnt); +static int twa_scsi_queue (struct scsi_cmnd *cmd,void (*done) (struct scsi_cmnd *)); +static int twa_scsiop_execute_scsi (TW_Device_Extension *tw_dev,int request_id,char *cdb,int use_sg,TW_SG_Apache *sglistarg); +static void twa_scsiop_execute_scsi_complete (TW_Device_Extension *tw_dev,int request_id); +static int twa_setup_irq (TW_Device_Extension *tw_dev); +static int twa_slave_configure (struct scsi_device *SDptr); +static char *twa_string_lookup (twa_message_type *table,unsigned int aen_code); +static void twa_unmap_scsi_data (TW_Device_Extension *tw_dev,int request_id); + +/* Functions */ + +/* Show some statistics about the card */ +static ssize_t twa_show_stats (struct class_device *class_dev,char *buf) +{ +struct Scsi_Host *host =class_to_shost (class_dev); +TW_Device_Extension *tw_dev = (TW_Device_Extension *)host->hostdata; +unsigned long flags =0 ; +ssize_t len; + +spin_lock_irqsave (tw_dev->host->host_lock, flags); + len =snprintf (buf,PAGE_SIZE ,"Driver version: %s\n" +"Current commands posted: %4d\n" +"Max commands posted: %4d\n" +"Current pending commands: %4d\n" +"Max pending commands: %4d\n" +"Last sgl length: %4d\n" +"Max sgl length: %4d\n" +"Last sector count: %4d\n" +"Max sector count: %4d\n" +"SCSI Host Resets: %4d\n" +"SCSI Aborts/Timeouts: %4d\n" +"AEN's: %4d\n" , + twa_driver_version, + tw_dev->posted_request_count, + tw_dev->max_posted_request_count, + tw_dev->pending_request_count, + tw_dev->max_pending_request_count, + tw_dev->sgl_entries, + tw_dev->max_sgl_entries, + tw_dev->sector_count, + tw_dev->max_sector_count, + tw_dev->num_resets, + tw_dev->num_aborts, + tw_dev->aen_count); +spin_unlock_irqrestore (tw_dev->host->host_lock, flags); +return len; +}/* End twa_show_stats() */ + +/* Create sysfs 'stats' entry */ +static struct class_device_attribute twa_host_stats_attr = { + .attr = { + .name ="stats" , + .mode =S_IRUGO , + }, + .show = twa_show_stats +}; + +/* Host attributes initializer */ +static struct class_device_attribute *twa_host_attrs[] = { + &twa_host_stats_attr, +NULL , +}; + +/* File operations struct for character device */ +static struct file_operations twa_fops = { + .owner =THIS_MODULE , + .ioctl = twa_chrdev_ioctl, + .open = twa_chrdev_open, + .release =NULL +}; + +/* This function will complete an aen request from the isr */ +static int twa_aen_complete (TW_Device_Extension *tw_dev,int request_id) +{ +TW_Command_Full *full_command_packet; +TW_Command *command_packet; +TW_Command_Apache_Header *header; +unsigned short aen; +int retval =1 ; + + header = (TW_Command_Apache_Header *)tw_dev->generic_buffer_virt[request_id]; + tw_dev->posted_request_count--; + aen = header->status_block.error; + full_command_packet = (TW_Command_Full *)tw_dev->command_packet_virt[request_id]; + command_packet = &full_command_packet->command.oldcommand; + +/* First check for internal completion of set param for time sync */ +if (command_packet->byte0_offset.opcode ==TW_OP_SET_PARAM ) { +/* Keep reading the queue in case there are more aen's */ +if (twa_aen_read_queue (tw_dev, request_id)) + goto out2; +else { + retval =0 ; + goto out; + } + } + +switch (aen) { +case TW_AEN_QUEUE_EMPTY : +/* Quit reading the queue if this is the last one */ +break ; +case TW_AEN_SYNC_TIME_WITH_HOST : +twa_aen_sync_time (tw_dev, request_id); + retval =0 ; + goto out; +default : +twa_aen_queue_event (tw_dev, header); + +/* If there are more aen's, keep reading the queue */ +if (twa_aen_read_queue (tw_dev, request_id)) + goto out2; +else { + retval =0 ; + goto out; + } + } + retval =0 ; +out2: + tw_dev->state[request_id] =TW_S_COMPLETED ; +twa_free_request_id (tw_dev, request_id); +clear_bit (TW_IN_ATTENTION_LOOP , &tw_dev->flags); +out: +return retval; +}/* End twa_aen_complete() */ + +/* This function will drain aen queue */ +static int twa_aen_drain_queue (TW_Device_Extension *tw_dev,int no_check_reset) +{ +int request_id =0 ; +char cdb[TW_MAX_CDB_LEN ]; +TW_SG_Apache sglist[1 ]; +int finished =0 , count =0 ; +TW_Command_Full *full_command_packet; +TW_Command_Apache_Header *header; +unsigned short aen; +int first_reset =0 , queue =0 , retval =1 ; + +if (no_check_reset) + first_reset =0 ; +else + first_reset =1 ; + + full_command_packet = (TW_Command_Full *)tw_dev->command_packet_virt[request_id]; +memset (full_command_packet,0 ,sizeof (TW_Command_Full)); + +/* Initialize cdb */ +memset (&cdb,0 ,TW_MAX_CDB_LEN ); + cdb[0 ] =REQUEST_SENSE ;/* opcode */ + cdb[4 ] =TW_ALLOCATION_LENGTH ;/* allocation length */ + +/* Initialize sglist */ +memset (&sglist,0 ,sizeof (TW_SG_Apache)); + sglist[0 ].length =TW_SECTOR_SIZE ; + sglist[0 ].address = tw_dev->generic_buffer_phys[request_id]; + +if (sglist[0 ].address &TW_ALIGNMENT_9000_SGL ) { +TW_PRINTK (tw_dev->host,TW_DRIVER ,0x1 ,"Found unaligned address during AEN drain" ); + goto out; + } + +/* Mark internal command */ + tw_dev->srb[request_id] =NULL ; + +do { +/* Send command to the board */ +if (twa_scsiop_execute_scsi (tw_dev, request_id, cdb,1 , sglist)) { +TW_PRINTK (tw_dev->host,TW_DRIVER ,0x2 ,"Error posting request sense" ); + goto out; + } + +/* Now poll for completion */ +if (twa_poll_response (tw_dev, request_id,30 )) { +TW_PRINTK (tw_dev->host,TW_DRIVER ,0x3 ,"No valid response while draining AEN queue" ); + tw_dev->posted_request_count--; + goto out; + } + + tw_dev->posted_request_count--; + header = (TW_Command_Apache_Header *)tw_dev->generic_buffer_virt[request_id]; + aen = header->status_block.error; + queue =0 ; + count++; + +switch (aen) { +case TW_AEN_QUEUE_EMPTY : +if (first_reset !=1 ) + goto out; +else + finished =1 ; +break ; +case TW_AEN_SOFT_RESET : +if (first_reset ==0 ) + first_reset =1 ; +else + queue =1 ; +break ; +case TW_AEN_SYNC_TIME_WITH_HOST : +break ; +default : + queue =1 ; + } + +/* Now queue an event info */ +if (queue) +twa_aen_queue_event (tw_dev, header); + }while ((finished ==0 ) && (count <TW_MAX_AEN_DRAIN )); + +if (count ==TW_MAX_AEN_DRAIN ) + goto out; + + retval =0 ; +out: + tw_dev->state[request_id] =TW_S_INITIAL ; +return retval; +}/* End twa_aen_drain_queue() */ + +/* This function will queue an event */ +static void twa_aen_queue_event (TW_Device_Extension *tw_dev,TW_Command_Apache_Header *header) +{ +u32 local_time; +struct timeval time; +TW_Event *event; +unsigned short aen; +char host[16 ]; + + tw_dev->aen_count++; + +/* Fill out event info */ + event = tw_dev->event_queue[tw_dev->error_index]; + +/* Check for clobber */ + host[0 ] ='\0' ; +if (tw_dev->host) { +sprintf (host," scsi%d:" , tw_dev->host->host_no); +if (event->retrieved ==TW_AEN_NOT_RETRIEVED ) + tw_dev->aen_clobber =1 ; + } + + aen = header->status_block.error; +memset (event,0 ,sizeof (TW_Event)); + + event->severity = header->status_block.substatus_block.severity; +do_gettimeofday (&time); + local_time = (u32)(time.tv_sec - (sys_tz.tz_minuteswest *60 )); + event->time_stamp_sec = local_time; + event->aen_code = aen; + event->retrieved =TW_AEN_NOT_RETRIEVED ; + event->sequence_id = tw_dev->error_sequence_id; + tw_dev->error_sequence_id++; + + header->err_specific_desc[sizeof (header->err_specific_desc) -1 ] ='\0' ; + event->parameter_len =strlen (header->err_specific_desc); +memcpy (event->parameter_data, header->err_specific_desc, event->parameter_len); +if (event->severity !=TW_AEN_SEVERITY_DEBUG ) +printk (KERN_WARNING "3w-9xxx:%s AEN: %s (0x%02X:0x%04X): %s:%s.\n" , host,twa_aen_severity_lookup (header->status_block.substatus_block.severity),TW_MESSAGE_SOURCE_CONTROLLER_EVENT , aen,twa_string_lookup (twa_aen_table, aen), header->err_specific_desc); +else + tw_dev->aen_count--; + +if ((tw_dev->error_index +1 ) ==TW_Q_LENGTH ) + tw_dev->event_queue_wrapped =1 ; + tw_dev->error_index = (tw_dev->error_index +1 ) %TW_Q_LENGTH ; +}/* End twa_aen_queue_event() */ + +/* This function will read the aen queue from the isr */ +static int twa_aen_read_queue (TW_Device_Extension *tw_dev,int request_id) +{ +char cdb[TW_MAX_CDB_LEN ]; +TW_SG_Apache sglist[1 ]; +TW_Command_Full *full_command_packet; +int retval =1 ; + + full_command_packet = (TW_Command_Full *)tw_dev->command_packet_virt[request_id]; +memset (full_command_packet,0 ,sizeof (TW_Command_Full)); + +/* Initialize cdb */ +memset (&cdb,0 ,TW_MAX_CDB_LEN ); + cdb[0 ] =REQUEST_SENSE ;/* opcode */ + cdb[4 ] =TW_ALLOCATION_LENGTH ;/* allocation length */ + +/* Initialize sglist */ +memset (&sglist,0 ,sizeof (TW_SG_Apache)); + sglist[0 ].length =TW_SECTOR_SIZE ; + sglist[0 ].address = tw_dev->generic_buffer_phys[request_id]; + +/* Mark internal command */ + tw_dev->srb[request_id] =NULL ; + +/* Now post the command packet */ +if (twa_scsiop_execute_scsi (tw_dev, request_id, cdb,1 , sglist)) { +TW_PRINTK (tw_dev->host,TW_DRIVER ,0x4 ,"Post failed while reading AEN queue" ); + goto out; + } + retval =0 ; +out: +return retval; +}/* End twa_aen_read_queue() */ + +/* This function will look up an AEN severity string */ +static char *twa_aen_severity_lookup (unsigned char severity_code) +{ +char *retval =NULL ; + +if ((severity_code < (unsigned char )TW_AEN_SEVERITY_ERROR ) || + (severity_code > (unsigned char )TW_AEN_SEVERITY_DEBUG )) + goto out; + + retval = twa_aen_severity_table[severity_code]; +out: +return retval; +}/* End twa_aen_severity_lookup() */ + +/* This function will sync firmware time with the host time */ +static void twa_aen_sync_time (TW_Device_Extension *tw_dev,int request_id) +{ +u32 schedulertime; +struct timeval utc; +TW_Command_Full *full_command_packet; +TW_Command *command_packet; +TW_Param_Apache *param; +u32 local_time; + +/* Fill out the command packet */ + full_command_packet = (TW_Command_Full *)tw_dev->command_packet_virt[request_id]; +memset (full_command_packet,0 ,sizeof (TW_Command_Full)); + command_packet = &full_command_packet->command.oldcommand; + command_packet->byte0_offset.opcode =TW_OP_SET_PARAM ; + command_packet->byte0_offset.sgl_offset =2 ; + command_packet->request_id = request_id; + command_packet->byte8_offset.param.sgl[0 ].address = tw_dev->generic_buffer_phys[request_id]; + command_packet->byte8_offset.param.sgl[0 ].length =TW_SECTOR_SIZE ; + command_packet->size =TW_COMMAND_SIZE ; + command_packet->byte6_offset.parameter_count =1 ; + +/* Setup the param */ + param = (TW_Param_Apache *)tw_dev->generic_buffer_virt[request_id]; +memset (param,0 ,TW_SECTOR_SIZE ); + param->table_id =TW_TIMEKEEP_TABLE |0x8000 ;/* Controller time keep table */ + param->parameter_id =0x3 ;/* SchedulerTime */ + param->parameter_size_bytes =4 ; + +/* Convert system time in UTC to local time seconds since last +Sunday 12:00AM */ +do_gettimeofday (&utc); + local_time = (u32)(utc.tv_sec - (sys_tz.tz_minuteswest *60 )); + schedulertime = local_time - (3 *86400 ); + schedulertime = schedulertime %604800 ; + +memcpy (param->data, &schedulertime,sizeof (u32)); + +/* Mark internal command */ + tw_dev->srb[request_id] =NULL ; + +/* Now post the command */ +twa_post_command_packet (tw_dev, request_id,1 ); +}/* End twa_aen_sync_time() */ + +/* This function will allocate memory and check if it is correctly aligned */ +static int twa_allocate_memory (TW_Device_Extension *tw_dev,int size,int which) +{ +int i; +dma_addr_t dma_handle; +unsigned long *cpu_addr; +int retval =1 ; + + cpu_addr =pci_alloc_consistent (tw_dev->tw_pci_dev, size*TW_Q_LENGTH , &dma_handle); +if (!cpu_addr) { +TW_PRINTK (tw_dev->host,TW_DRIVER ,0x5 ,"Memory allocation failed" ); + goto out; + } + +if ((unsigned long )cpu_addr % (TW_ALIGNMENT_9000 )) { +TW_PRINTK (tw_dev->host,TW_DRIVER ,0x6 ,"Failed to allocate correctly aligned memory" ); +pci_free_consistent (tw_dev->tw_pci_dev, size*TW_Q_LENGTH , cpu_addr, dma_handle); + goto out; + } + +memset (cpu_addr,0 , size*TW_Q_LENGTH ); + +for (i =0 ; i <TW_Q_LENGTH ; i++) { +switch (which) { +case 0 : + tw_dev->command_packet_phys[i] = dma_handle+(i*size); + tw_dev->command_packet_virt[i] = (unsigned long *)((unsigned char *)cpu_addr + (i*size)); +break ; +case 1 : + tw_dev->generic_buffer_phys[i] = dma_handle+(i*size); + tw_dev->generic_buffer_virt[i] = (unsigned long *)((unsigned char *)cpu_addr + (i*size)); +break ; + } + } + retval =0 ; +out: +return retval; +}/* End twa_allocate_memory() */ + +/* This function will check the status register for unexpected bits */ +static int twa_check_bits (u32 status_reg_value) +{ +int retval =1 ; + +if ((status_reg_value &TW_STATUS_EXPECTED_BITS ) !=TW_STATUS_EXPECTED_BITS ) + goto out; +if ((status_reg_value &TW_STATUS_UNEXPECTED_BITS ) !=0 ) + goto out; + + retval =0 ; +out: +return retval; +}/* End twa_check_bits() */ + +/* This function will check the srl and decide if we are compatible */ +static int twa_check_srl (TW_Device_Extension *tw_dev,int *flashed) +{ +int retval =1 ; +unsigned short fw_on_ctlr_srl =0 , fw_on_ctlr_arch_id =0 ; +unsigned short fw_on_ctlr_branch =0 , fw_on_ctlr_build =0 ; +u32 init_connect_result =0 ; + +if (twa_initconnection (tw_dev,TW_INIT_MESSAGE_CREDITS ,TW_EXTENDED_INIT_CONNECT ,TW_CURRENT_FW_SRL ,TW_9000_ARCH_ID ,TW_CURRENT_FW_BRANCH ,TW_CURRENT_FW_BUILD , &fw_on_ctlr_srl, &fw_on_ctlr_arch_id, &fw_on_ctlr_branch, &fw_on_ctlr_build, &init_connect_result)) { +TW_PRINTK (tw_dev->host,TW_DRIVER ,0x7 ,"Initconnection failed while checking SRL" ); + goto out; + } + + tw_dev->working_srl =TW_CURRENT_FW_SRL ; + tw_dev->working_branch =TW_CURRENT_FW_BRANCH ; + tw_dev->working_build =TW_CURRENT_FW_BUILD ; + +/* Try base mode compatibility */ +if (!(init_connect_result &TW_CTLR_FW_COMPATIBLE )) { +if (twa_initconnection (tw_dev,TW_INIT_MESSAGE_CREDITS ,TW_EXTENDED_INIT_CONNECT ,TW_BASE_FW_SRL ,TW_9000_ARCH_ID ,TW_BASE_FW_BRANCH ,TW_BASE_FW_BUILD , &fw_on_ctlr_srl, &fw_on_ctlr_arch_id, &fw_on_ctlr_branch, &fw_on_ctlr_build, &init_connect_result)) { +TW_PRINTK (tw_dev->host,TW_DRIVER ,0xa ,"Initconnection (base mode) failed while checking SRL" ); + goto out; + } +if (!(init_connect_result &TW_CTLR_FW_COMPATIBLE )) { +if (TW_CURRENT_FW_SRL > fw_on_ctlr_srl) { +TW_PRINTK (tw_dev->host,TW_DRIVER ,0x32 ,"Firmware and driver incompatibility: please upgrade firmware" ); + }else { +TW_PRINTK (tw_dev->host,TW_DRIVER ,0x33 ,"Firmware and driver incompatibility: please upgrade driver" ); + } + goto out; + } + tw_dev->working_srl =TW_BASE_FW_SRL ; + tw_dev->working_branch =TW_BASE_FW_BRANCH ; + tw_dev->working_build =TW_BASE_FW_BUILD ; + } + retval =0 ; +out: +return retval; +}/* End twa_check_srl() */ + +/* This function handles ioctl for the character device */ +static int twa_chrdev_ioctl (struct inode *inode,struct file *file,unsigned int cmd,unsigned long arg) +{ +long timeout; +unsigned long *cpu_addr, data_buffer_length_adjusted =0 , flags =0 ; +dma_addr_t dma_handle; +int request_id =0 ; +unsigned int sequence_id =0 ; +unsigned char event_index, start_index; +TW_Ioctl_Driver_Command driver_command; +TW_Ioctl_Buf_Apache *tw_ioctl; +TW_Lock *tw_lock; +TW_Command_Full *full_command_packet; +TW_Compatibility_Info *tw_compat_info; +TW_Event *event; +struct timeval current_time; +u32 current_time_ms; +TW_Device_Extension *tw_dev = twa_device_extension_list[iminor (inode)]; +int retval =TW_IOCTL_ERROR_OS_EFAULT ; + +/* Only let one of these through at a time */ +if (down_interruptible (&tw_dev->ioctl_sem)) { + retval =TW_IOCTL_ERROR_OS_EINTR ; + goto out; + } + +/* First copy down the driver command */ +if (copy_from_user (&driver_command, (void *)arg,sizeof (TW_Ioctl_Driver_Command))) + goto out2; + +/* Check data buffer size */ +if (driver_command.buffer_length >TW_MAX_SECTORS *512 ) { + retval =TW_IOCTL_ERROR_OS_EINVAL ; + goto out2; + } + +/* Hardware can only do multiple of 512 byte transfers */ + data_buffer_length_adjusted = (driver_command.buffer_length +511 ) & ~511 ; + +/* Now allocate ioctl buf memory */ + cpu_addr =pci_alloc_consistent (tw_dev->tw_pci_dev, data_buffer_length_adjusted+sizeof (TW_Ioctl_Buf_Apache) -1 , &dma_handle); +if (!cpu_addr) { + retval =TW_IOCTL_ERROR_OS_ENOMEM ; + goto out2; + } + + tw_ioctl = (TW_Ioctl_Buf_Apache *)cpu_addr; + +/* Now copy down the entire ioctl */ +if (copy_from_user (tw_ioctl, (void *)arg, driver_command.buffer_length +sizeof (TW_Ioctl_Buf_Apache) -1 )) + goto out3; + +/* See which ioctl we are doing */ +switch (cmd) { +case TW_IOCTL_FIRMWARE_PASS_THROUGH : +spin_lock_irqsave (tw_dev->host->host_lock, flags); +twa_get_request_id (tw_dev, &request_id); + +/* Flag internal command */ + tw_dev->srb[request_id] =0 ; + +/* Flag chrdev ioctl */ + tw_dev->chrdev_request_id = request_id; + + full_command_packet = &tw_ioctl->firmware_command; + +/* Load request id and sglist for both command types */ +twa_load_sgl (full_command_packet, request_id, dma_handle, data_buffer_length_adjusted); + +memcpy (tw_dev->command_packet_virt[request_id], &(tw_ioctl->firmware_command),sizeof (TW_Command_Full)); + +/* Now post the command packet to the controller */ +twa_post_command_packet (tw_dev, request_id,1 ); +spin_unlock_irqrestore (tw_dev->host->host_lock, flags); + + timeout =TW_IOCTL_CHRDEV_TIMEOUT *HZ ; + +/* Now wait for command to complete */ + timeout =wait_event_interruptible_timeout (tw_dev->ioctl_wqueue, tw_dev->chrdev_request_id ==TW_IOCTL_CHRDEV_FREE , timeout); + +/* Check if we timed out, got a signal, or didn't get +an interrupt */ +if ((timeout <=0 ) && (tw_dev->chrdev_request_id !=TW_IOCTL_CHRDEV_FREE )) { +/* Now we need to reset the board */ +if (timeout ==TW_IOCTL_ERROR_OS_ERESTARTSYS ) { + retval = timeout; + }else { +printk (KERN_WARNING "3w-9xxx: scsi%d: WARNING: (0x%02X:0x%04X): Character ioctl (0x%x) timed out, resetting card.\n" , tw_dev->host->host_no,TW_DRIVER ,0xc , cmd); + retval =TW_IOCTL_ERROR_OS_EIO ; + } +spin_lock_irqsave (tw_dev->host->host_lock, flags); + tw_dev->state[request_id] =TW_S_COMPLETED ; +twa_free_request_id (tw_dev, request_id); + tw_dev->posted_request_count--; +twa_reset_device_extension (tw_dev); +spin_unlock_irqrestore (tw_dev->host->host_lock, flags); + goto out3; + } + +/* Now copy in the command packet response */ +memcpy (&(tw_ioctl->firmware_command), tw_dev->command_packet_virt[request_id],sizeof (TW_Command_Full)); + +/* Now complete the io */ +spin_lock_irqsave (tw_dev->host->host_lock, flags); + tw_dev->posted_request_count--; + tw_dev->state[request_id] =TW_S_COMPLETED ; +twa_free_request_id (tw_dev, request_id); +spin_unlock_irqrestore (tw_dev->host->host_lock, flags); +break ; +case TW_IOCTL_GET_COMPATIBILITY_INFO : + tw_ioctl->driver_command.status =0 ; +/* Copy compatiblity struct into ioctl data buffer */ + tw_compat_info = (TW_Compatibility_Info *)tw_ioctl->data_buffer; +strncpy (tw_compat_info->driver_version, twa_driver_version,strlen (twa_driver_version)); + tw_compat_info->working_srl = tw_dev->working_srl; + tw_compat_info->working_branch = tw_dev->working_branch; + tw_compat_info->working_build = tw_dev->working_build; +break ; +case TW_IOCTL_GET_LAST_EVENT : +if (tw_dev->event_queue_wrapped) { +if (tw_dev->aen_clobber) { + tw_ioctl->driver_command.status =TW_IOCTL_ERROR_STATUS_AEN_CLOBBER ; + tw_dev->aen_clobber =0 ; + }else + tw_ioctl->driver_command.status =0 ; + }else { +if (!tw_dev->error_index) { + tw_ioctl->driver_command.status =TW_IOCTL_ERROR_STATUS_NO_MORE_EVENTS ; +break ; + } + tw_ioctl->driver_command.status =0 ; + } + event_index = (tw_dev->error_index -1 +TW_Q_LENGTH ) %TW_Q_LENGTH ; +memcpy (tw_ioctl->data_buffer, tw_dev->event_queue[event_index],sizeof (TW_Event)); + tw_dev->event_queue[event_index]->retrieved =TW_AEN_RETRIEVED ; +break ; +case TW_IOCTL_GET_FIRST_EVENT : +if (tw_dev->event_queue_wrapped) { +if (tw_dev->aen_clobber) { + tw_ioctl->driver_command.status =TW_IOCTL_ERROR_STATUS_AEN_CLOBBER ; + tw_dev->aen_clobber =0 ; + }else + tw_ioctl->driver_command.status =0 ; + event_index = tw_dev->error_index; + }else { +if (!tw_dev->error_index) { + tw_ioctl->driver_command.status =TW_IOCTL_ERROR_STATUS_NO_MORE_EVENTS ; +break ; + } + tw_ioctl->driver_command.status =0 ; + event_index =0 ; + } +memcpy (tw_ioctl->data_buffer, tw_dev->event_queue[event_index],sizeof (TW_Event)); + tw_dev->event_queue[event_index]->retrieved =TW_AEN_RETRIEVED ; +break ; +case TW_IOCTL_GET_NEXT_EVENT : + event = (TW_Event *)tw_ioctl->data_buffer; + sequence_id = event->sequence_id; + tw_ioctl->driver_command.status =0 ; + +if (tw_dev->event_queue_wrapped) { +if (tw_dev->aen_clobber) { + tw_ioctl->driver_command.status =TW_IOCTL_ERROR_STATUS_AEN_CLOBBER ; + tw_dev->aen_clobber =0 ; + } + start_index = tw_dev->error_index; + }else { +if (!tw_dev->error_index) { + tw_ioctl->driver_command.status =TW_IOCTL_ERROR_STATUS_NO_MORE_EVENTS ; +break ; + } + start_index =0 ; + } + event_index = (start_index + sequence_id - tw_dev->event_queue[start_index]->sequence_id +1 ) %TW_Q_LENGTH ; + +if (!(tw_dev->event_queue[event_index]->sequence_id > sequence_id)) { +if (tw_ioctl->driver_command.status ==TW_IOCTL_ERROR_STATUS_AEN_CLOBBER ) + tw_dev->aen_clobber =1 ; + tw_ioctl->driver_command.status =TW_IOCTL_ERROR_STATUS_NO_MORE_EVENTS ; +break ; + } +memcpy (tw_ioctl->data_buffer, tw_dev->event_queue[event_index],sizeof (TW_Event)); + tw_dev->event_queue[event_index]->retrieved =TW_AEN_RETRIEVED ; +break ; +case TW_IOCTL_GET_PREVIOUS_EVENT : + event = (TW_Event *)tw_ioctl->data_buffer; + sequence_id = event->sequence_id; + tw_ioctl->driver_command.status =0 ; + +if (tw_dev->event_queue_wrapped) { +if (tw_dev->aen_clobber) { + tw_ioctl->driver_command.status =TW_IOCTL_ERROR_STATUS_AEN_CLOBBER ; + tw_dev->aen_clobber =0 ; + } + start_index = tw_dev->error_index; + }else { +if (!tw_dev->error_index) { + tw_ioctl->driver_command.status =TW_IOCTL_ERROR_STATUS_NO_MORE_EVENTS ; +break ; + } + start_index =0 ; + } + event_index = (start_index + sequence_id - tw_dev->event_queue[start_index]->sequence_id -1 ) %TW_Q_LENGTH ; + +if (!(tw_dev->event_queue[event_index]->sequence_id < sequence_id)) { +if (tw_ioctl->driver_command.status ==TW_IOCTL_ERROR_STATUS_AEN_CLOBBER ) + tw_dev->aen_clobber =1 ; + tw_ioctl->driver_command.status =TW_IOCTL_ERROR_STATUS_NO_MORE_EVENTS ; +break ; + } +memcpy (tw_ioctl->data_buffer, tw_dev->event_queue[event_index],sizeof (TW_Event)); + tw_dev->event_queue[event_index]->retrieved =TW_AEN_RETRIEVED ; +break ; +case TW_IOCTL_GET_LOCK : + tw_lock = (TW_Lock *)tw_ioctl->data_buffer; +do_gettimeofday (¤t_time); + current_time_ms = (current_time.tv_sec *1000 ) + (current_time.tv_usec /1000 ); + +if ((tw_lock->force_flag ==1 ) || (tw_dev->ioctl_sem_lock ==0 ) || (current_time_ms >= tw_dev->ioctl_msec)) { + tw_dev->ioctl_sem_lock =1 ; + tw_dev->ioctl_msec = current_time_ms + tw_lock->timeout_msec; + tw_ioctl->driver_command.status =0 ; + tw_lock->time_remaining_msec = tw_lock->timeout_msec; + }else { + tw_ioctl->driver_command.status =TW_IOCTL_ERROR_STATUS_LOCKED ; + tw_lock->time_remaining_msec = tw_dev->ioctl_msec - current_time_ms; + } +break ; +case TW_IOCTL_RELEASE_LOCK : +if (tw_dev->ioctl_sem_lock ==1 ) { + tw_dev->ioctl_sem_lock =0 ; + tw_ioctl->driver_command.status =0 ; + }else { + tw_ioctl->driver_command.status =TW_IOCTL_ERROR_STATUS_NOT_LOCKED ; + } +break ; +default : + retval =TW_IOCTL_ERROR_OS_ENOTTY ; + goto out3; + } + +/* Now copy the entire response to userspace */ +if (copy_to_user ((void *)arg, tw_ioctl,sizeof (TW_Ioctl_Buf_Apache) + driver_command.buffer_length -1 ) ==0 ) + retval =0 ; +out3: +/* Now free ioctl buf memory */ +pci_free_consistent (tw_dev->tw_pci_dev, data_buffer_length_adjusted+sizeof (TW_Ioctl_Buf_Apache) -1 , cpu_addr, dma_handle); +out2: +up (&tw_dev->ioctl_sem); +out: +return retval; +}/* End twa_chrdev_ioctl() */ + +/* This function handles open for the character device */ +static int twa_chrdev_open (struct inode *inode,struct file *file) +{ +unsigned int minor_number; +int retval =TW_IOCTL_ERROR_OS_ENODEV ; + + minor_number =iminor (inode); +if (minor_number >= twa_device_extension_count) + goto out; + retval =0 ; +out: +return retval; +}/* End twa_chrdev_open() */ + +/* This function will print readable messages from status register errors */ +static int twa_decode_bits (TW_Device_Extension *tw_dev,u32 status_reg_value) +{ +int retval =1 ; + +/* Check for various error conditions and handle them appropriately */ +if (status_reg_value &TW_STATUS_PCI_PARITY_ERROR ) { +TW_PRINTK (tw_dev->host,TW_DRIVER ,0xc ,"PCI Parity Error: clearing" ); +writel (TW_CONTROL_CLEAR_PARITY_ERROR ,TW_CONTROL_REG_ADDR (tw_dev)); + } + +if (status_reg_value &TW_STATUS_PCI_ABORT ) { +TW_PRINTK (tw_dev->host,TW_DRIVER ,0xd ,"PCI Abort: clearing" ); +writel (TW_CONTROL_CLEAR_PCI_ABORT ,TW_CONTROL_REG_ADDR (tw_dev)); +pci_write_config_word (tw_dev->tw_pci_dev,PCI_STATUS ,TW_PCI_CLEAR_PCI_ABORT ); + } + +if (status_reg_value &TW_STATUS_QUEUE_ERROR ) { +TW_PRINTK (tw_dev->host,TW_DRIVER ,0xe ,"Controller Queue Error: clearing" ); +writel (TW_CONTROL_CLEAR_QUEUE_ERROR ,TW_CONTROL_REG_ADDR (tw_dev)); + } + +if (status_reg_value &TW_STATUS_SBUF_WRITE_ERROR ) { +TW_PRINTK (tw_dev->host,TW_DRIVER ,0xf ,"SBUF Write Error: clearing" ); +writel (TW_CONTROL_CLEAR_SBUF_WRITE_ERROR ,TW_CONTROL_REG_ADDR (tw_dev)); + } + +if (status_reg_value &TW_STATUS_MICROCONTROLLER_ERROR ) { +if (tw_dev->reset_print ==0 ) { +TW_PRINTK (tw_dev->host,TW_DRIVER ,0x10 ,"Microcontroller Error: clearing" ); + tw_dev->reset_print =1 ; + } + goto out; + } + retval =0 ; +out: +return retval; +}/* End twa_decode_bits() */ + +/* This function will empty the response queue */ +static int twa_empty_response_queue (TW_Device_Extension *tw_dev) +{ +u32 status_reg_value, response_que_value; +int count =0 , retval =1 ; + + status_reg_value =readl (TW_STATUS_REG_ADDR (tw_dev)); + +while (((status_reg_value &TW_STATUS_RESPONSE_QUEUE_EMPTY ) ==0 ) && (count <TW_MAX_RESPONSE_DRAIN )) { + response_que_value =readl (TW_RESPONSE_QUEUE_REG_ADDR (tw_dev)); + status_reg_value =readl (TW_STATUS_REG_ADDR (tw_dev)); + count++; + } +if (count ==TW_MAX_RESPONSE_DRAIN ) + goto out; + + retval =0 ; +out: +return retval; +}/* End twa_empty_response_queue() */ + +/* This function passes sense keys from firmware to scsi layer */ +static int twa_fill_sense (TW_Device_Extension *tw_dev,int request_id,int copy_sense,int print_host) +{ +TW_Command_Full *full_command_packet; +unsigned short error; +int retval =1 ; + + full_command_packet = (TW_Command_Full *)tw_dev->command_packet_virt[request_id]; +/* Don't print error for Logical unit not supported during rollcall */ + error = full_command_packet->header.status_block.error; +if ((error !=TW_ERROR_LOGICAL_UNIT_NOT_SUPPORTED ) && (error !=TW_ERROR_UNIT_OFFLINE )) { +if (print_host) +printk (KERN_WARNING "3w-9xxx: scsi%d: ERROR: (0x%02X:0x%04X): %s:%s.\n" , tw_dev->host->host_no,TW_MESSAGE_SOURCE_CONTROLLER_ERROR , full_command_packet->header.status_block.error,twa_string_lookup (twa_error_table, full_command_packet->header.status_block.error), full_command_packet->header.err_specific_desc); +else +printk (KERN_WARNING "3w-9xxx: ERROR: (0x%02X:0x%04X): %s:%s.\n" ,TW_MESSAGE_SOURCE_CONTROLLER_ERROR , full_command_packet->header.status_block.error,twa_string_lookup (twa_error_table, full_command_packet->header.status_block.error), full_command_packet->header.err_specific_desc); + } + +if (copy_sense) { +memcpy (tw_dev->srb[request_id]->sense_buffer, full_command_packet->header.sense_data,TW_SENSE_DATA_LENGTH ); + tw_dev->srb[request_id]->result = (full_command_packet->command.newcommand.status <<1 ); + retval =TW_ISR_DONT_RESULT ; + goto out; + } + retval =0 ; +out: +return retval; +}/* End twa_fill_sense() */ + +/* This function will free up device extension resources */ +static void twa_free_device_extension (TW_Device_Extension *tw_dev) +{ +if (tw_dev->command_packet_virt[0 ]) +pci_free_consistent (tw_dev->tw_pci_dev,sizeof (TW_Command_Full)*TW_Q_LENGTH , tw_dev->command_packet_virt[0 ], tw_dev->command_packet_phys[0 ]); + +if (tw_dev->generic_buffer_virt[0 ]) +pci_free_consistent (tw_dev->tw_pci_dev,TW_SECTOR_SIZE *TW_Q_LENGTH , tw_dev->generic_buffer_virt[0 ], tw_dev->generic_buffer_phys[0 ]); + +if (tw_dev->event_queue[0 ]) +kfree (tw_dev->event_queue[0 ]); +}/* End twa_free_device_extension() */ + +/* This function will free a request id */ +static void twa_free_request_id (TW_Device_Extension *tw_dev,int request_id) +{ + tw_dev->free_queue[tw_dev->free_tail] = request_id; + tw_dev->state[request_id] =TW_S_FINISHED ; + tw_dev->free_tail = (tw_dev->free_tail +1 ) %TW_Q_LENGTH ; +}/* End twa_free_request_id() */ + +/* This function will get parameter table entires from the firmware */ +static void *twa_get_param (TW_Device_Extension *tw_dev,int request_id,int table_id,int parameter_id,int parameter_size_bytes) +{ +TW_Command_Full *full_command_packet; +TW_Command *command_packet; +TW_Param_Apache *param; +unsigned long param_value; +void *retval =NULL ; + +/* Setup the command packet */ + full_command_packet = (TW_Command_Full *)tw_dev->command_packet_virt[request_id]; +memset (full_command_packet,0 ,sizeof (TW_Command_Full)); + command_packet = &full_command_packet->command.oldcommand; + + command_packet->byte0_offset.opcode =TW_OP_GET_PARAM ; + command_packet->byte0_offset.sgl_offset =2 ; + command_packet->size =TW_COMMAND_SIZE ; + command_packet->request_id = request_id; + command_packet->byte6_offset.block_count =1 ; + +/* Now setup the param */ + param = (TW_Param_Apache *)tw_dev->generic_buffer_virt[request_id]; +memset (param,0 ,TW_SECTOR_SIZE ); + param->table_id = table_id |0x8000 ; + param->parameter_id = parameter_id; + param->parameter_size_bytes = parameter_size_bytes; + param_value = tw_dev->generic_buffer_phys[request_id]; + + command_packet->byte8_offset.param.sgl[0 ].address = param_value; + command_packet->byte8_offset.param.sgl[0 ].length =TW_SECTOR_SIZE ; + +/* Post the command packet to the board */ +twa_post_command_packet (tw_dev, request_id,1 ); + +/* Poll for completion */ +if (twa_poll_response (tw_dev, request_id,30 )) + TW_PRINTK(tw_dev->host, TW_DRIVER, 0x13, "No valid response during get param") + else + retval = (void *)&(param->data[0 ]); + + tw_dev->posted_request_count--; + tw_dev->state[request_id] =TW_S_INITIAL ; + +return retval; +}/* End twa_get_param() */ + +/* This function will assign an available request id */ +static void twa_get_request_id (TW_Device_Extension *tw_dev,int *request_id) +{ + *request_id = tw_dev->free_queue[tw_dev->free_head]; + tw_dev->free_head = (tw_dev->free_head +1 ) %TW_Q_LENGTH ; + tw_dev->state[*request_id] =TW_S_STARTED ; +}/* End twa_get_request_id() */ + +/* This function will send an initconnection command to controller */ +static int twa_initconnection (TW_Device_Extension *tw_dev,int message_credits, +u32 set_features,unsigned short current_fw_srl, +unsigned short current_fw_arch_id, +unsigned short current_fw_branch, +unsigned short current_fw_build, +unsigned short *fw_on_ctlr_srl, +unsigned short *fw_on_ctlr_arch_id, +unsigned short *fw_on_ctlr_branch, +unsigned short *fw_on_ctlr_build, +u32 *init_connect_result) +{ +TW_Command_Full *full_command_packet; +TW_Initconnect *tw_initconnect; +int request_id =0 , retval =1 ; + +/* Initialize InitConnection command packet */ + full_command_packet = (TW_Command_Full *)tw_dev->command_packet_virt[request_id]; +memset (full_command_packet,0 ,sizeof (TW_Command_Full)); + full_command_packet->header.header_desc.size_header =128 ; + + tw_initconnect = (TW_Initconnect *)&full_command_packet->command.oldcommand; + tw_initconnect->opcode =TW_OP_INIT_CONNECTION ; + + tw_initconnect->request_id = request_id; + tw_initconnect->message_credits = message_credits; + tw_initconnect->features = set_features; +#if BITS_PER_LONG >32 +/* Turn on 64-bit sgl support */ + tw_initconnect->features |=1 ; +#endif + +if (set_features &TW_EXTENDED_INIT_CONNECT ) { + tw_initconnect->size =TW_INIT_COMMAND_PACKET_SIZE_EXTENDED ; + tw_initconnect->fw_srl = current_fw_srl; + tw_initconnect->fw_arch_id = current_fw_arch_id; + tw_initconnect->fw_branch = current_fw_branch; + tw_initconnect->fw_build = current_fw_build; + }else + tw_initconnect->size =TW_INIT_COMMAND_PACKET_SIZE ; + +/* Send command packet to the board */ +twa_post_command_packet (tw_dev, request_id,1 ); + +/* Poll for completion */ +if (twa_poll_response (tw_dev, request_id,30 )) { +TW_PRINTK (tw_dev->host,TW_DRIVER ,0x15 ,"No valid response during init connection" ); + }else { +if (set_features &TW_EXTENDED_INIT_CONNECT ) { + *fw_on_ctlr_srl = tw_initconnect->fw_srl; + *fw_on_ctlr_arch_id = tw_initconnect->fw_arch_id; + *fw_on_ctlr_branch = tw_initconnect->fw_branch; + *fw_on_ctlr_build = tw_initconnect->fw_build; + *init_connect_result = tw_initconnect->result; + } + retval =0 ; + } + + tw_dev->posted_request_count--; + tw_dev->state[request_id] =TW_S_INITIAL ; + +return retval; +}/* End twa_initconnection() */ + +/* This function will initialize the fields of a device extension */ +static int twa_initialize_device_extension (TW_Device_Extension *tw_dev) +{ +int i, retval =1 ; + +/* Initialize command packet buffers */ +if (twa_allocate_memory (tw_dev,sizeof (TW_Command_Full),0 )) { +TW_PRINTK (tw_dev->host,TW_DRIVER ,0x16 ,"Command packet memory allocation failed" ); + goto out; + } + +/* Initialize generic buffer */ +if (twa_allocate_memory (tw_dev,TW_SECTOR_SIZE ,1 )) { +TW_PRINTK (tw_dev->host,TW_DRIVER ,0x17 ,"Generic memory allocation failed" ); + goto out; + } + +/* Allocate event info space */ + tw_dev->event_queue[0 ] =kmalloc (sizeof (TW_Event) *TW_Q_LENGTH ,GFP_ATOMIC ); +if (!tw_dev->event_queue[0 ]) { +TW_PRINTK (tw_dev->host,TW_DRIVER ,0x18 ,"Event info memory allocation failed" ); + goto out; + } + +memset (tw_dev->event_queue[0 ],0 ,sizeof (TW_Event) *TW_Q_LENGTH ); + +for (i =0 ; i <TW_Q_LENGTH ; i++) { + tw_dev->event_queue[i] = (TW_Event *)((unsigned char *)tw_dev->event_queue[0 ] + (i *sizeof (TW_Event))); + tw_dev->free_queue[i] = i; + tw_dev->state[i] =TW_S_INITIAL ; + } + + tw_dev->pending_head =TW_Q_START ; + tw_dev->pending_tail =TW_Q_START ; + tw_dev->free_head =TW_Q_START ; + tw_dev->free_tail =TW_Q_START ; + tw_dev->error_sequence_id =1 ; + tw_dev->chrdev_request_id =TW_IOCTL_CHRDEV_FREE ; + +init_MUTEX (&tw_dev->ioctl_sem); +init_waitqueue_head (&tw_dev->ioctl_wqueue); + + retval =0 ; +out: +return retval; +}/* End twa_initialize_device_extension() */ + +/* This function is the interrupt service routine */ +static irqreturn_t twa_interrupt (int irq,void *dev_instance,struct pt_regs *regs) +{ +int request_id, error =0 ; +u32 status_reg_value; +TW_Response_Queue response_que; +unsigned long flags =0 ; +TW_Command_Full *full_command_packet; +TW_Command *command_packet; +TW_Device_Extension *tw_dev = (TW_Device_Extension *)dev_instance; +int handled =0 ; + +/* See if we are already running on another processor */ +if (test_and_set_bit (TW_IN_INTR , &tw_dev->flags)) +return IRQ_NONE ; + +/* Get the per adapter lock */ +spin_lock_irqsave (tw_dev->host->host_lock, flags); + +/* See if the interrupt matches this instance */ +if (tw_dev->tw_pci_dev->irq == (unsigned int )irq) { + + handled =1 ; + +/* Read the registers */ + status_reg_value =readl (TW_STATUS_REG_ADDR (tw_dev)); + +/* Check if this is our interrupt, otherwise bail */ +if (!(status_reg_value &TW_STATUS_VALID_INTERRUPT )) + goto twa_interrupt_bail; + +/* Check controller for errors */ +if (twa_check_bits (status_reg_value)) { +if (twa_decode_bits (tw_dev, status_reg_value)) { +TW_CLEAR_ALL_INTERRUPTS (tw_dev); + goto twa_interrupt_bail; + } + } + +/* Handle host interrupt */ +if (status_reg_value &TW_STATUS_HOST_INTERRUPT ) +TW_CLEAR_HOST_INTERRUPT (tw_dev); + +/* Handle attention interrupt */ +if (status_reg_value &TW_STATUS_ATTENTION_INTERRUPT ) { +TW_CLEAR_ATTENTION_INTERRUPT (tw_dev); +if (!(test_and_set_bit (TW_IN_ATTENTION_LOOP , &tw_dev->flags))) { +twa_get_request_id (tw_dev, &request_id); + + error =twa_aen_read_queue (tw_dev, request_id); +if (error) { + tw_dev->state[request_id] =TW_S_COMPLETED ; +twa_free_request_id (tw_dev, request_id); +clear_bit (TW_IN_ATTENTION_LOOP , &tw_dev->flags); + } + } + } + +/* Handle command interrupt */ +if (status_reg_value &TW_STATUS_COMMAND_INTERRUPT ) { +TW_MASK_COMMAND_INTERRUPT (tw_dev); +/* Drain as many pending commands as we can */ +while (tw_dev->pending_request_count >0 ) { + request_id = tw_dev->pending_queue[tw_dev->pending_head]; +if (tw_dev->state[request_id] !=TW_S_PENDING ) { +TW_PRINTK (tw_dev->host,TW_DRIVER ,0x19 ,"Found request id that wasn't pending" ); +TW_CLEAR_ALL_INTERRUPTS (tw_dev); + goto twa_interrupt_bail; + } +if (twa_post_command_packet (tw_dev, request_id,1 )==0 ) { + tw_dev->pending_head = (tw_dev->pending_head +1 ) %TW_Q_LENGTH ; + tw_dev->pending_request_count--; + }else { +/* If we get here, we will continue re-posting on the next command interrupt */ +break ; + } + } + } + +/* Handle response interrupt */ +if (status_reg_value &TW_STATUS_RESPONSE_INTERRUPT ) { + +/* Drain the response queue from the board */ +while ((status_reg_value &TW_STATUS_RESPONSE_QUEUE_EMPTY ) ==0 ) { +/* Complete the response */ + response_que.value =readl (TW_RESPONSE_QUEUE_REG_ADDR (tw_dev)); + request_id = response_que.u.response_id; + full_command_packet = (TW_Command_Full *)tw_dev->command_packet_virt[request_id]; + error =0 ; + command_packet = &full_command_packet->command.oldcommand; +/* Check for command packet errors */ +if (full_command_packet->command.newcommand.status !=0 ) { +if (tw_dev->srb[request_id] !=0 ) { + error =twa_fill_sense (tw_dev, request_id,1 ,1 ); + }else { +/* Skip ioctl error prints */ +if (request_id != tw_dev->chrdev_request_id) { + error =twa_fill_sense (tw_dev, request_id,0 ,1 ); + } + } + } + +/* Check for correct state */ +if (tw_dev->state[request_id] !=TW_S_POSTED ) { +if (tw_dev->srb[request_id] !=0 ) { +TW_PRINTK (tw_dev->host,TW_DRIVER ,0x1a ,"Received a request id that wasn't posted" ); +TW_CLEAR_ALL_INTERRUPTS (tw_dev); + goto twa_interrupt_bail; + } + } + +/* Check for internal command completion */ +if (tw_dev->srb[request_id] ==0 ) { +if (request_id != tw_dev->chrdev_request_id) { +if (twa_aen_complete (tw_dev, request_id)) +TW_PRINTK (tw_dev->host,TW_DRIVER ,0x1b ,"Error completing AEN during attention interrupt" ); + }else { + tw_dev->chrdev_request_id =TW_IOCTL_CHRDEV_FREE ; +wake_up (&tw_dev->ioctl_wqueue); + } + }else { +twa_scsiop_execute_scsi_complete (tw_dev, request_id); +/* If no error command was a success */ +if (error ==0 ) { + tw_dev->srb[request_id]->result = (DID_OK <<16 ); + } + +/* If error, command failed */ +if (error ==1 ) { +/* Ask for a host reset */ + tw_dev->srb[request_id]->result = (DID_OK <<16 ) | (CHECK_CONDITION <<1 ); + } + +/* Now complete the io */ + tw_dev->state[request_id] =TW_S_COMPLETED ; +twa_free_request_id (tw_dev, request_id); + tw_dev->posted_request_count--; + tw_dev->srb[request_id]->scsi_done (tw_dev->srb[request_id]); +twa_unmap_scsi_data (tw_dev, request_id); + } + +/* Check for valid status after each drain */ + status_reg_value =readl (TW_STATUS_REG_ADDR (tw_dev)); +if (twa_check_bits (status_reg_value)) { +if (twa_decode_bits (tw_dev, status_reg_value)) { +TW_CLEAR_ALL_INTERRUPTS (tw_dev); + goto twa_interrupt_bail; + } + } + } + } + } +twa_interrupt_bail: +spin_unlock_irqrestore (tw_dev->host->host_lock, flags); +clear_bit (TW_IN_INTR , &tw_dev->flags); +return IRQ_RETVAL (handled); +}/* End twa_interrupt() */ + +/* This function will load the request id and various sgls for ioctls */ +static void twa_load_sgl (TW_Command_Full *full_command_packet,int request_id,dma_addr_t dma_handle,int length) +{ +TW_Command *oldcommand; +TW_Command_Apache *newcommand; +TW_SG_Entry *sgl; + +if (full_command_packet->command.newcommand.command.opcode ==TW_OP_EXECUTE_SCSI ) { + newcommand = &full_command_packet->command.newcommand; + newcommand->request_id = request_id; + newcommand->sg_list[0 ].address = dma_handle +sizeof (TW_Ioctl_Buf_Apache) -1 ; + newcommand->sg_list[0 ].length = length; + }else { + oldcommand = &full_command_packet->command.oldcommand; + oldcommand->request_id = request_id; + +if (oldcommand->byte0_offset.sgl_offset) { +/* Load the sg list */ + sgl = (TW_SG_Entry *)((u32 *)oldcommand+oldcommand->byte0_offset.sgl_offset); + sgl->address = dma_handle +sizeof (TW_Ioctl_Buf_Apache) -1 ; + sgl->length = length; + } + } +}/* End twa_load_sgl() */ + +/* This function will perform a pci-dma mapping for a scatter gather list */ +static int twa_map_scsi_sg_data (TW_Device_Extension *tw_dev,int request_id) +{ +int use_sg; +struct scsi_cmnd *cmd = tw_dev->srb[request_id]; +struct pci_dev *pdev = tw_dev->tw_pci_dev; +int retval =0 ; + +if (cmd->use_sg ==0 ) + goto out; + + use_sg =pci_map_sg (pdev, cmd->buffer, cmd->use_sg,DMA_BIDIRECTIONAL ); + +if (use_sg ==0 ) { +TW_PRINTK (tw_dev->host,TW_DRIVER ,0x1c ,"Failed to map scatter gather list" ); + goto out; + } + + cmd->SCp.phase =2 ; + cmd->SCp.have_data_in = use_sg; + retval = use_sg; +out: +return retval; +}/* End twa_map_scsi_sg_data() */ + +/* This function will perform a pci-dma map for a single buffer */ +static dma_addr_t twa_map_scsi_single_data (TW_Device_Extension *tw_dev,int request_id) +{ +dma_addr_t mapping; +struct scsi_cmnd *cmd = tw_dev->srb[request_id]; +struct pci_dev *pdev = tw_dev->tw_pci_dev; +int retval =0 ; + +if (cmd->request_bufflen ==0 ) { + retval =0 ; + goto out; + } + + mapping =pci_map_single (pdev, cmd->request_buffer, cmd->request_bufflen,DMA_BIDIRECTIONAL ); + +if (mapping ==0 ) { +TW_PRINTK (tw_dev->host,TW_DRIVER ,0x1d ,"Failed to map page" ); + goto out; + } + + cmd->SCp.phase =1 ; + cmd->SCp.have_data_in = mapping; + retval = mapping; +out: +return retval; +}/* End twa_map_scsi_single_data() */ + +/* This function will poll for a response interrupt of a request */ +static int twa_poll_response (TW_Device_Extension *tw_dev,int request_id,int seconds) +{ +int retval =1 , found =0 , response_request_id; +TW_Response_Queue response_queue; +TW_Command_Full *full_command_packet = (TW_Command_Full *)tw_dev->command_packet_virt[request_id]; + +if (twa_poll_status_gone (tw_dev,TW_STATUS_RESPONSE_QUEUE_EMPTY , seconds) ==0 ) { + response_queue.value =readl (TW_RESPONSE_QUEUE_REG_ADDR (tw_dev)); + response_request_id = (unsigned char )response_queue.u.response_id; +if (request_id != response_request_id) { +TW_PRINTK (tw_dev->host,TW_DRIVER ,0x1e ,"Found unexpected request id while polling for response" ); + goto out; + } +if (full_command_packet->command.newcommand.command.opcode ==TW_OP_EXECUTE_SCSI ) { +if (full_command_packet->command.newcommand.status !=0 ) { +/* bad response */ +twa_fill_sense (tw_dev, request_id,0 ,0 ); + goto out; + } + found =1 ; + }else { +if (full_command_packet->command.oldcommand.status !=0 ) { +/* bad response */ +twa_fill_sense (tw_dev, request_id,0 ,0 ); + goto out; + } + found =1 ; + } + } + +if (found) + retval =0 ; +out: +return retval; +}/* End twa_poll_response() */ + +/* This function will poll the status register for a flag */ +static int twa_poll_status (TW_Device_Extension *tw_dev,u32 flag,int seconds) +{ +u32 status_reg_value; +unsigned long before; +int retval =1 ; + + status_reg_value =readl (TW_STATUS_REG_ADDR (tw_dev)); + before = jiffies; + +if (twa_check_bits (status_reg_value)) +twa_decode_bits (tw_dev, status_reg_value); + +while ((status_reg_value & flag) != flag) { + status_reg_value =readl (TW_STATUS_REG_ADDR (tw_dev)); + +if (twa_check_bits (status_reg_value)) +twa_decode_bits (tw_dev, status_reg_value); + +if (time_after (jiffies, before +HZ * seconds)) + goto out; + +mdelay (5 ); + } + retval =0 ; +out: +return retval; +}/* End twa_poll_status() */ + +/* This function will poll the status register for disappearance of a flag */ +static int twa_poll_status_gone (TW_Device_Extension *tw_dev,u32 flag,int seconds) +{ +u32 status_reg_value; +unsigned long before; +int retval =1 ; + + status_reg_value =readl (TW_STATUS_REG_ADDR (tw_dev)); + before = jiffies; + +if (twa_check_bits (status_reg_value)) +twa_decode_bits (tw_dev, status_reg_value); + +while ((status_reg_value & flag) !=0 ) { + status_reg_value =readl (TW_STATUS_REG_ADDR (tw_dev)); +if (twa_check_bits (status_reg_value)) +twa_decode_bits (tw_dev, status_reg_value); + +if (time_after (jiffies, before +HZ * seconds)) + goto out; + +mdelay (5 ); + } + retval =0 ; +out: +return retval; +}/* End twa_poll_status_gone() */ + +/* This function will attempt to post a command packet to the board */ +static int twa_post_command_packet (TW_Device_Extension *tw_dev,int request_id,char internal) +{ +u32 status_reg_value; +unsigned long command_que_value; +int retval =1 ; + + command_que_value = tw_dev->command_packet_phys[request_id]; + status_reg_value =readl (TW_STATUS_REG_ADDR (tw_dev)); + +if (twa_check_bits (status_reg_value)) +twa_decode_bits (tw_dev, status_reg_value); + +if (((tw_dev->pending_request_count >0 ) && (tw_dev->state[request_id] !=TW_S_PENDING )) || (status_reg_value &TW_STATUS_COMMAND_QUEUE_FULL )) { + +/* Only pend internal driver commands */ +if (!internal) { + retval =SCSI_MLQUEUE_HOST_BUSY ; + goto out; + } + +/* Couldn't post the command packet, so we do it later */ +if (tw_dev->state[request_id] !=TW_S_PENDING ) { + tw_dev->state[request_id] =TW_S_PENDING ; + tw_dev->pending_request_count++; +if (tw_dev->pending_request_count > tw_dev->max_pending_request_count) { + tw_dev->max_pending_request_count = tw_dev->pending_request_count; + } + tw_dev->pending_queue[tw_dev->pending_tail] = request_id; + tw_dev->pending_tail = (tw_dev->pending_tail +1 ) %TW_Q_LENGTH ; + } +TW_UNMASK_COMMAND_INTERRUPT (tw_dev); + goto out; + }else { +/* We successfully posted the command packet */ +#if BITS_PER_LONG >32 +writeq (TW_COMMAND_OFFSET + command_que_value,TW_COMMAND_QUEUE_REG_ADDR (tw_dev)); +#else +writel (TW_COMMAND_OFFSET + command_que_value,TW_COMMAND_QUEUE_REG_ADDR (tw_dev)); +#endif + tw_dev->state[request_id] =TW_S_POSTED ; + tw_dev->posted_request_count++; +if (tw_dev->posted_request_count > tw_dev->max_posted_request_count) { + tw_dev->max_posted_request_count = tw_dev->posted_request_count; + } + } + retval =0 ; +out: +return retval; +}/* End twa_post_command_packet() */ + +/* This function will reset a device extension */ +static int twa_reset_device_extension (TW_Device_Extension *tw_dev) +{ +int i =0 ; +int retval =1 ; + +/* Abort all requests that are in progress */ +for (i =0 ; i <TW_Q_LENGTH ; i++) { +if ((tw_dev->state[i] !=TW_S_FINISHED ) && + (tw_dev->state[i] !=TW_S_INITIAL ) && + (tw_dev->state[i] !=TW_S_COMPLETED )) { +if (tw_dev->srb[i]) { + tw_dev->srb[i]->result = (DID_RESET <<16 ); + tw_dev->srb[i]->scsi_done (tw_dev->srb[i]); +twa_unmap_scsi_data (tw_dev, i); + } + } + } + +/* Reset queues and counts */ +for (i =0 ; i <TW_Q_LENGTH ; i++) { + tw_dev->free_queue[i] = i; + tw_dev->state[i] =TW_S_INITIAL ; + } + tw_dev->free_head =TW_Q_START ; + tw_dev->free_tail =TW_Q_START ; + tw_dev->posted_request_count =0 ; + tw_dev->pending_request_count =0 ; + tw_dev->pending_head =TW_Q_START ; + tw_dev->pending_tail =TW_Q_START ; + tw_dev->reset_print =0 ; + tw_dev->chrdev_request_id =TW_IOCTL_CHRDEV_FREE ; + tw_dev->flags =0 ; + +TW_DISABLE_INTERRUPTS (tw_dev); + +if (twa_reset_sequence (tw_dev,1 )) + goto out; + +TW_ENABLE_AND_CLEAR_INTERRUPTS (tw_dev); + + retval =0 ; +out: +return retval; +}/* End twa_reset_device_extension() */ + +/* This function will reset a controller */ +static int twa_reset_sequence (TW_Device_Extension *tw_dev,int soft_reset) +{ +int tries =0 , retval =1 , flashed =0 , do_soft_reset = soft_reset; + +while (tries <TW_MAX_RESET_TRIES ) { +if (do_soft_reset) +TW_SOFT_RESET (tw_dev); + +/* Make sure controller is in a good state */ +if (twa_poll_status (tw_dev,TW_STATUS_MICROCONTROLLER_READY | (do_soft_reset ==1 ?TW_STATUS_ATTENTION_INTERRUPT :0 ),30 )) { +TW_PRINTK (tw_dev->host,TW_DRIVER ,0x1f ,"Microcontroller not ready during reset sequence" ); + do_soft_reset =1 ; + tries++; +continue ; + } + +/* Empty response queue */ +if (twa_empty_response_queue (tw_dev)) { +TW_PRINTK (tw_dev->host,TW_DRIVER ,0x20 ,"Response queue empty failed during reset sequence" ); + do_soft_reset =1 ; + tries++; +continue ; + } + + flashed =0 ; + +/* Check for compatibility/flash */ +if (twa_check_srl (tw_dev, &flashed)) { +TW_PRINTK (tw_dev->host,TW_DRIVER ,0x21 ,"Compatibility check failed during reset sequence" ); + do_soft_reset =1 ; + tries++; +continue ; + }else { +if (flashed) { + tries++; +continue ; + } + } + +/* Drain the AEN queue */ +if (twa_aen_drain_queue (tw_dev, soft_reset)) { +TW_PRINTK (tw_dev->host,TW_DRIVER ,0x22 ,"AEN drain failed during reset sequence" ); + do_soft_reset =1 ; + tries++; +continue ; + } + +/* If we got here, controller is in a good state */ + retval =0 ; + goto out; + } +out: +return retval; +}/* End twa_reset_sequence() */ + +/* This funciton returns unit geometry in cylinders/heads/sectors */ +static int twa_scsi_biosparam (struct scsi_device *sdev,struct block_device *bdev,sector_t capacity,int geom[]) +{ +int heads, sectors, cylinders; +TW_Device_Extension *tw_dev; + + tw_dev = (TW_Device_Extension *)sdev->host->hostdata; + +if (capacity >=0x200000 ) { + heads =255 ; + sectors =63 ; + cylinders =sector_div (capacity, heads * sectors); + }else { + heads =64 ; + sectors =32 ; + cylinders =sector_div (capacity, heads * sectors); + } + + geom[0 ] = heads; + geom[1 ] = sectors; + geom[2 ] = cylinders; + +return 0 ; +}/* End twa_scsi_biosparam() */ + +/* This is the new scsi eh abort function */ +static int twa_scsi_eh_abort (struct scsi_cmnd *SCpnt) +{ +int i; +TW_Device_Extension *tw_dev =NULL ; +int retval =FAILED ; + + tw_dev = (TW_Device_Extension *)SCpnt->device->host->hostdata; + + tw_dev->num_aborts++; + +/* If we find any IO's in process, we have to reset the card */ +for (i =0 ; i <TW_Q_LENGTH ; i++) { +if ((tw_dev->state[i] !=TW_S_FINISHED ) && (tw_dev->state[i] !=TW_S_INITIAL )) { +printk (KERN_WARNING "3w-9xxx: scsi%d: WARNING: (0x%02X:0x%04X): Unit #%d: Command (0x%x) timed out, resetting card.\n" , tw_dev->host->host_no,TW_DRIVER ,0x2c , SCpnt->device->id, SCpnt->cmnd[0 ]); +if (twa_reset_device_extension (tw_dev)) { +TW_PRINTK (tw_dev->host,TW_DRIVER ,0x2a ,"Controller reset failed during scsi abort" ); + goto out; + } +break ; + } + } + retval =SUCCESS ; +out: +return retval; +}/* End twa_scsi_eh_abort() */ + +/* This is the new scsi eh reset function */ +static int twa_scsi_eh_reset (struct scsi_cmnd *SCpnt) +{ +TW_Device_Extension *tw_dev =NULL ; +int retval =FAILED ; + + tw_dev = (TW_Device_Extension *)SCpnt->device->host->hostdata; + + tw_dev->num_resets++; + +printk (KERN_WARNING "3w-9xxx: scsi%d: SCSI host reset started.\n" , tw_dev->host->host_no); + +/* Now reset the card and some of the device extension data */ +if (twa_reset_device_extension (tw_dev)) { +TW_PRINTK (tw_dev->host,TW_DRIVER ,0x2b ,"Controller reset failed during scsi host reset" ); + goto out; + } +printk (KERN_WARNING "3w-9xxx: scsi%d: SCSI host reset succeeded.\n" , tw_dev->host->host_no); + retval =SUCCESS ; +out: +return retval; +}/* End twa_scsi_eh_reset() */ + +/* This is the main scsi queue function to handle scsi opcodes */ +static int twa_scsi_queue (struct scsi_cmnd *SCpnt,void (*done)(struct scsi_cmnd *)) +{ +int request_id, retval; +TW_Device_Extension *tw_dev = (TW_Device_Extension *)SCpnt->device->host->hostdata; + +/* Save done function into scsi_cmnd struct */ + SCpnt->scsi_done = done; + +/* Get a free request id */ +twa_get_request_id (tw_dev, &request_id); + +/* Save the scsi command for use by the ISR */ + tw_dev->srb[request_id] = SCpnt; + +/* Initialize phase to zero */ + SCpnt->SCp.phase =0 ; + + retval =twa_scsiop_execute_scsi (tw_dev, request_id,NULL ,0 ,NULL ); +switch (retval) { +case SCSI_MLQUEUE_HOST_BUSY : +twa_free_request_id (tw_dev, request_id); +break ; +case 1 : + tw_dev->state[request_id] =TW_S_COMPLETED ; +twa_free_request_id (tw_dev, request_id); + SCpnt->result = (DID_ERROR <<16 ); +done (SCpnt); + } + +return retval; +}/* End twa_scsi_queue() */ + +/* This function hands scsi cdb's to the firmware */ +static int twa_scsiop_execute_scsi (TW_Device_Extension *tw_dev,int request_id,char *cdb,int use_sg,TW_SG_Apache *sglistarg) +{ +TW_Command_Full *full_command_packet; +TW_Command_Apache *command_packet; +u32 num_sectors =0x0 ; +int i, sg_count; +struct scsi_cmnd *srb =NULL ; +struct scatterlist *sglist =NULL ; +u32 buffaddr =0x0 ; +int retval =1 ; + +if (tw_dev->srb[request_id]) { +if (tw_dev->srb[request_id]->request_buffer) { + sglist = (struct scatterlist *)tw_dev->srb[request_id]->request_buffer; + } + srb = tw_dev->srb[request_id]; + } + +/* Initialize command packet */ + full_command_packet = (TW_Command_Full *)tw_dev->command_packet_virt[request_id]; + full_command_packet->header.header_desc.size_header =128 ; + full_command_packet->header.status_block.error =0 ; + full_command_packet->header.status_block.substatus_block.severity =0 ; + + command_packet = &full_command_packet->command.newcommand; + command_packet->status =0 ; + command_packet->command.opcode =TW_OP_EXECUTE_SCSI ; + +/* We forced 16 byte cdb use earlier */ +if (!cdb) +memcpy (command_packet->cdb, srb->cmnd,TW_MAX_CDB_LEN ); +else +memcpy (command_packet->cdb, cdb,TW_MAX_CDB_LEN ); + +if (srb) + command_packet->unit = srb->device->id; +else + command_packet->unit =0 ; + + command_packet->request_id = request_id; + command_packet->sgl_offset =16 ; + +if (!sglistarg) { +/* Map sglist from scsi layer to cmd packet */ +if (tw_dev->srb[request_id]->use_sg ==0 ) { +if (tw_dev->srb[request_id]->request_bufflen <TW_MIN_SGL_LENGTH ) { + command_packet->sg_list[0 ].address = tw_dev->generic_buffer_phys[request_id]; + command_packet->sg_list[0 ].length =TW_MIN_SGL_LENGTH ; + }else { + buffaddr =twa_map_scsi_single_data (tw_dev, request_id); +if (buffaddr ==0 ) + goto out; + + command_packet->sg_list[0 ].address = buffaddr; + command_packet->sg_list[0 ].length = tw_dev->srb[request_id]->request_bufflen; + } + command_packet->sgl_entries =1 ; + +if (command_packet->sg_list[0 ].address &TW_ALIGNMENT_9000_SGL ) { +TW_PRINTK (tw_dev->host,TW_DRIVER ,0x2d ,"Found unaligned address during execute scsi" ); + goto out; + } + } + +if (tw_dev->srb[request_id]->use_sg >0 ) { + sg_count =twa_map_scsi_sg_data (tw_dev, request_id); +if (sg_count ==0 ) + goto out; + +for (i =0 ; i < sg_count; i++) { + command_packet->sg_list[i].address =sg_dma_address (&sglist[i]); + command_packet->sg_list[i].length =sg_dma_len (&sglist[i]); +if (command_packet->sg_list[i].address &TW_ALIGNMENT_9000_SGL ) { +TW_PRINTK (tw_dev->host,TW_DRIVER ,0x2e ,"Found unaligned sgl address during execute scsi" ); + goto out; + } + } + command_packet->sgl_entries = tw_dev->srb[request_id]->use_sg; + } + }else { +/* Internal cdb post */ +for (i =0 ; i < use_sg; i++) { + command_packet->sg_list[i].address = sglistarg[i].address; + command_packet->sg_list[i].length = sglistarg[i].length; +if (command_packet->sg_list[i].address &TW_ALIGNMENT_9000_SGL ) { +TW_PRINTK (tw_dev->host,TW_DRIVER ,0x2f ,"Found unaligned sgl address during internal post" ); + goto out; + } + } + command_packet->sgl_entries = use_sg; + } + +if (srb) { +if (srb->cmnd[0 ] ==READ_6 || srb->cmnd[0 ] ==WRITE_6 ) + num_sectors = (u32 )srb->cmnd[4 ]; + +if (srb->cmnd[0 ] ==READ_10 || srb->cmnd[0 ] ==WRITE_10 ) + num_sectors = (u32 )srb->cmnd[8 ] | ((u32 )srb->cmnd[7 ] <<8 ); + } + +/* Update sector statistic */ + tw_dev->sector_count = num_sectors; +if (tw_dev->sector_count > tw_dev->max_sector_count) + tw_dev->max_sector_count = tw_dev->sector_count; + +/* Update SG statistics */ +if (srb) { + tw_dev->sgl_entries = tw_dev->srb[request_id]->use_sg; +if (tw_dev->sgl_entries > tw_dev->max_sgl_entries) + tw_dev->max_sgl_entries = tw_dev->sgl_entries; + } + +/* Now post the command to the board */ +if (srb) { + retval =twa_post_command_packet (tw_dev, request_id,0 ); + }else { +twa_post_command_packet (tw_dev, request_id,1 ); + retval =0 ; + } +out: +return retval; +}/* End twa_scsiop_execute_scsi() */ + +/* This function completes an execute scsi operation */ +static void twa_scsiop_execute_scsi_complete (TW_Device_Extension *tw_dev,int request_id) +{ +/* Copy the response if too small */ +if ((tw_dev->srb[request_id]->request_buffer) && (tw_dev->srb[request_id]->request_bufflen <TW_MIN_SGL_LENGTH )) { +memcpy (tw_dev->srb[request_id]->request_buffer, tw_dev->generic_buffer_virt[request_id], tw_dev->srb[request_id]->request_bufflen); + } +}/* End twa_scsiop_execute_scsi_complete() */ + +/* This function will setup the interrupt handler */ +static int twa_setup_irq (TW_Device_Extension *tw_dev) +{ +char *device =TW_DEVICE_NAME ; +int retval =1 ; + +if (request_irq (tw_dev->tw_pci_dev->irq, twa_interrupt,SA_SHIRQ , device, tw_dev) <0 ) { +TW_PRINTK (tw_dev->host,TW_DRIVER ,0x30 ,"Error requesting IRQ" ); + goto out; + } + retval =0 ; +out: +return retval; +}/* End twa_setup_irq() */ + +/* This function tells the controller to shut down */ +static void __twa_shutdown (TW_Device_Extension *tw_dev) +{ +/* Disable interrupts */ +TW_DISABLE_INTERRUPTS (tw_dev); + +printk (KERN_WARNING "3w-9xxx: Shutting down host %d.\n" , tw_dev->host->host_no); + +/* Tell the card we are shutting down */ +if (twa_initconnection (tw_dev,1 ,0 ,0 ,0 ,0 ,0 ,NULL ,NULL ,NULL ,NULL ,NULL )) { +TW_PRINTK (tw_dev->host,TW_DRIVER ,0x31 ,"Connection shutdown failed" ); + }else { +printk (KERN_WARNING "3w-9xxx: Shutdown complete.\n" ); + } + +/* Clear all interrupts just before exit */ +TW_ENABLE_AND_CLEAR_INTERRUPTS (tw_dev); +}/* End __twa_shutdown() */ + +/* Wrapper for __twa_shutdown */ +static void twa_shutdown (struct device *dev) +{ +struct Scsi_Host *host =pci_get_drvdata (to_pci_dev (dev)); +TW_Device_Extension *tw_dev = (TW_Device_Extension *)host->hostdata; + +__twa_shutdown (tw_dev); +}/* End twa_shutdown() */ + +/* This function will configure individual target parameters */ +static int twa_slave_configure (struct scsi_device *SDptr) +{ +int max_cmds; + +if (cmds_per_lun) { + max_cmds = cmds_per_lun; +if (max_cmds >TW_MAX_CMDS_PER_LUN ) + max_cmds =TW_MAX_CMDS_PER_LUN ; + }else { + max_cmds =TW_MAX_CMDS_PER_LUN ; + } +scsi_adjust_queue_depth (SDptr,MSG_ORDERED_TAG , max_cmds); + +return 0 ; +}/* End twa_slave_configure */ + +/* This function will look up a string */ +static char *twa_string_lookup (twa_message_type *table,unsigned int code) +{ +int index; + +for (index =0 ; ((code != table[index].code) && + (table[index].text != (char *)0 )); index++); +return (table[index].text); +}/* End twa_string_lookup() */ + +/* This function will perform a pci-dma unmap */ +static void twa_unmap_scsi_data (TW_Device_Extension *tw_dev,int request_id) +{ +struct scsi_cmnd *cmd = tw_dev->srb[request_id]; +struct pci_dev *pdev = tw_dev->tw_pci_dev; + +switch (cmd->SCp.phase) { +case 1 : +pci_unmap_single (pdev, cmd->SCp.have_data_in, cmd->request_bufflen,DMA_BIDIRECTIONAL ); +break ; +case 2 : +pci_unmap_sg (pdev, cmd->request_buffer, cmd->use_sg,DMA_BIDIRECTIONAL ); +break ; + } +}/* End twa_unmap_scsi_data() */ + +/* Get informationa bout the card/driver */ +static const char *twa_info (struct Scsi_Host *host) +{ +static char buffer[512 ]; + +sprintf (buffer,"3ware 9000 Storage Controller" ); + +return buffer; +}/* End twa_info() */ + +/* scsi_host_template initializer */ +static struct scsi_host_template driver_template = { + .module =THIS_MODULE , + .name ="3ware 9000 Storage Controller" , + .info = twa_info, + .queuecommand = twa_scsi_queue, + .eh_abort_handler = twa_scsi_eh_abort, + .eh_host_reset_handler = twa_scsi_eh_reset, + .bios_param = twa_scsi_biosparam, + .slave_configure = twa_slave_configure, + .can_queue =TW_Q_LENGTH -2 , + .this_id =-1 , + .sg_tablesize =TW_APACHE_MAX_SGL_LENGTH , + .max_sectors =TW_MAX_SECTORS , + .cmd_per_lun =TW_MAX_CMDS_PER_LUN , + .use_clustering =ENABLE_CLUSTERING , + .shost_attrs = twa_host_attrs, + .emulated =1 +}; + +/* This function will probe and initialize a card */ +static int __devinit twa_probe (struct pci_dev *pdev,const struct pci_device_id *dev_id) +{ +struct Scsi_Host *host =NULL ; +TW_Device_Extension *tw_dev; +u32 mem_addr, mem_len; +int retval = -ENODEV ; + +if (pci_enable_device (pdev)) { +TW_PRINTK (host,TW_DRIVER ,0x32 ,"Failed to enable pci device" ); + goto out; + } + +pci_set_master (pdev); + +if (pci_set_dma_mask (pdev,TW_DMA_MASK )) { +TW_PRINTK (host,TW_DRIVER ,0x23 ,"Failed to set dma mask" ); + goto out; + } + + host =scsi_host_alloc (&driver_template,sizeof (TW_Device_Extension)); +if (!host) { +TW_PRINTK (host,TW_DRIVER ,0x24 ,"Failed to allocate memory for device extension" ); + retval = -ENOMEM ; + goto out; + } + tw_dev = (TW_Device_Extension *)host->hostdata; + +memset (tw_dev,0 ,sizeof (TW_Device_Extension)); + +/* Save values to device extension */ + tw_dev->host = host; + tw_dev->tw_pci_dev = pdev; + +if (twa_initialize_device_extension (tw_dev)) { +TW_PRINTK (tw_dev->host,TW_DRIVER ,0x25 ,"Failed to initialize device extension" ); + goto out_free_device_extension; + } + + mem_addr =pci_resource_start (pdev,1 ); + mem_len =pci_resource_len (pdev,1 ); + +/* Make sure that mem region isn't already taken */ +if (check_mem_region (mem_addr, mem_len)) { +TW_PRINTK (tw_dev->host,TW_DRIVER ,0x26 ,"Failed to get mem region" ); + goto out_free_device_extension; + } + +/* Reserve the mem address space */ +request_mem_region (mem_addr, mem_len,TW_DEVICE_NAME ); + +/* Save base address */ + tw_dev->base_addr =ioremap (mem_addr,PAGE_SIZE ); + +/* Disable interrupts on the card */ +TW_DISABLE_INTERRUPTS (tw_dev); + +/* Initialize the card */ +if (twa_reset_sequence (tw_dev,0 )) + goto out_release_mem_region; + +/* Set host specific parameters */ + host->max_id =TW_MAX_UNITS ; + host->max_cmd_len =TW_MAX_CDB_LEN ; + host->max_lun =0 ; + host->max_channel =0 ; + +/* Register the card with the kernel SCSI layer */ + retval =scsi_add_host (host, &pdev->dev); +if (retval) { +TW_PRINTK (tw_dev->host,TW_DRIVER ,0x27 ,"scsi add host failed" ); + goto out_release_mem_region; + } + +pci_set_drvdata (pdev, host); + +printk (KERN_WARNING "3w-9xxx: scsi%d: Found a 3ware 9000 Storage Controller at 0x%x, IRQ: %d.\n" , host->host_no, mem_addr, pdev->irq); +printk (KERN_WARNING "3w-9xxx: scsi%d: Firmware %s, BIOS %s, Ports: %d.\n" , host->host_no, (char *)twa_get_param (tw_dev,0 ,TW_VERSION_TABLE ,TW_PARAM_FWVER ,TW_PARAM_FWVER_LENGTH ), (char *)twa_get_param (tw_dev,1 ,TW_VERSION_TABLE ,TW_PARAM_BIOSVER ,TW_PARAM_BIOSVER_LENGTH ), *(int *)twa_get_param (tw_dev,2 ,TW_INFORMATION_TABLE ,TW_PARAM_PORTCOUNT ,TW_PARAM_PORTCOUNT_LENGTH )); + +/* Now setup the interrupt handler */ +if (twa_setup_irq (tw_dev)) + goto out_remove_host; + + twa_device_extension_list[twa_device_extension_count] = tw_dev; + twa_device_extension_count++; + +/* Re-enable interrupts on the card */ +TW_ENABLE_AND_CLEAR_INTERRUPTS (tw_dev); + +/* Finally, scan the host */ +scsi_scan_host (host); + +if (twa_major ==-1 ) { +if ((twa_major =register_chrdev (0 ,"twa" , &twa_fops)) <0 ) +TW_PRINTK (host,TW_DRIVER ,0x29 ,"Failed to register character device" ); + } +return 0 ; + +out_remove_host: +scsi_remove_host (host); +out_release_mem_region: +release_mem_region (mem_addr, mem_len); +out_free_device_extension: +twa_free_device_extension (tw_dev); +scsi_host_put (host); +out: +return retval; +}/* End twa_probe() */ + +/* This function is called to remove a device */ +static void twa_remove (struct pci_dev *pdev) +{ +struct Scsi_Host *host =pci_get_drvdata (pdev); +TW_Device_Extension *tw_dev = (TW_Device_Extension *)host->hostdata; + +scsi_remove_host (tw_dev->host); + +__twa_shutdown (tw_dev); + +/* Free up the IRQ */ +free_irq (tw_dev->tw_pci_dev->irq, tw_dev); + +/* Free up the mem region */ +release_mem_region (pci_resource_start (tw_dev->tw_pci_dev,1 ),pci_resource_len (tw_dev->tw_pci_dev,1 )); + +/* Free up device extension resources */ +twa_free_device_extension (tw_dev); + +scsi_host_put (tw_dev->host); +pci_disable_device (pdev); + twa_device_extension_count--; +}/* End twa_remove() */ + +/* PCI Devices supported by this driver */ +static struct pci_device_id twa_pci_tbl[]__devinitdata = { + {PCI_VENDOR_ID_3WARE ,PCI_DEVICE_ID_3WARE_9000 , +PCI_ANY_ID ,PCI_ANY_ID ,0 ,0 ,0 }, + { } +}; +MODULE_DEVICE_TABLE (pci, twa_pci_tbl); + +/* pci_driver initializer */ +static struct pci_driver twa_driver = { + .name ="3w-9xxx" , + .id_table = twa_pci_tbl, + .probe = twa_probe, + .remove = twa_remove, + .driver = { + .shutdown = twa_shutdown + } +}; + +/* This function is called on driver initialization */ +static int __init twa_init (void ) +{ +printk (KERN_WARNING "3ware 9000 Storage Controller device driver for Linux v%s.\n" , twa_driver_version); + +pci_register_driver (&twa_driver); + +return 0 ; +}/* End twa_init() */ + +/* This function is called on driver exit */ +static void __exit twa_exit(void) +{ +/* Unregister character device */ +if (twa_major >=0 ) { +unregister_chrdev (twa_major,"twa" ); + twa_major =-1 ; + } + +pci_unregister_driver (&twa_driver); +}/* End twa_exit() */ + +module_init (twa_init); +module_exit (twa_exit); +
Adam Radford wrote:
Overall: your driver duplicates some SCSI infrastructure for sending SCSI commands, and waiting for their replies.
Quoted 2 lines
Changes in this release:
- Convert driver topci_driverformat.
yay!
Quoted 1 line
- Return
SCSI_MLQUEUE_HOST_BUSYwhen card status is busy.
Does the hardware have some sort of metric, like "queued commands < 32", to better determine when card is busy?
Normally, you want to set up the system such that SCSI stops calling your ->queuecommand() hook when there is no more room.
SCSI layer provides device-wide and host-wide queueing limits.
Quoted 2 lines
Future versions may include:
- PAE fix
What does this involve?
Quoted 2 lines
- 80 column wrap fixes
- Remove more prototypes by re-ordering functions.
Bah, who cares... Maintainer's choice, IMO.
Quoted 75 lines
diff -Naur
linux-2.6.6-mm4/drivers/scsi/3w-9xxx.clinux-2.6.6-mm5/drivers/scsi/3w-9xxx.cdrivers/scsi/3w-9xxx.c +71
-0@@ -0,0 +1,2144 @@ +/* +3w-9xxx.c -- 3ware 9000 Storage Controller device driver for Linux. + +Written By: Adam Radford <[email protected]> + +Copyright (C) 2004 Applied Micro Circuits Corporation. + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; version 2 of the License. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +NO WARRANTY +THE PROGRAM IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OR +CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT +LIMITATION, ANY WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT, +MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Each Recipient is +solely responsible for determining the appropriateness of using and +distributing the Program and assumes all risks associated with its +exercise of rights under this Agreement, including but not limited to +the risks and costs of program errors, damage to or loss of data, +programs or equipment, and unavailability or interruption of operations. + +DISCLAIMER OF LIABILITY +NEITHER RECIPIENT NOR ANY CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING WITHOUT LIMITATION LOST PROFITS), HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR +TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +USE OR DISTRIBUTION OF THE PROGRAM OR THE EXERCISE OF ANY RIGHTS GRANTED +HEREUNDER, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +Bugs/Comments/Suggestions should be mailed to: +[email protected] + +For more information, goto: +http://www.amcc.com + +Note: This version of the driver does not contain a bundled firmware +image. + +History +------- +2.26.00.005 - Initial release. +2.26.00.006 - Remove TW_WRITEL macro, replace with writeq(). +2.26.00.007 - Skip lun and channel probes. +2.26.00.008 - Fix bug in attention/command interrupt handling. +2.26.00.009 - Convert driver to pci_driver format. +Remove proc interface, add sysfs attributes. +Return SCSI_MLQUEUE_HOST_BUSY when card status is busy. +*/ + +#include <linux/module.h> +#include <linux/reboot.h> +#include <linux/spinlock.h> +#include <linux/interrupt.h> +#include <linux/moduleparam.h> +#include <linux/errno.h> +#include <linux/types.h> +#include <linux/delay.h> +#include <linux/pci.h> +#include <linux/time.h> +#include <asm/io.h>
drivers/scsi/3w-9xxx.c line 81
Quoted 10 lines
drivers/scsi/3w-9xxx.c +10
-0+#include <asm/irq.h> +#include <asm/uaccess.h> +#include <scsi/scsi.h> +#include <scsi/scsi_host.h> +#include <scsi/scsi_tcq.h> +#include <scsi/scsi_cmnd.h> +#include "3w-9xxx.h" + +/* Globals */ +char *twa_driver_version="2.26.00.009" ;
mark 'static', possibly 'const' too.
drivers/scsi/3w-9xxx.c line 82
Quoted 1 line
drivers/scsi/3w-9xxx.c +1
-0+TW_Device_Extension *twa_device_extension_list[TW_MAX_SLOT ];
static
Quoted 92 lines
drivers/scsi/3w-9xxx.c +92
-0+static unsigned int twa_device_extension_count; +static int twa_major =-1 ; +extern struct timezone sys_tz; +static int cmds_per_lun; + +/* Module parameters */ +MODULE_AUTHOR ("AMCC" ); +MODULE_DESCRIPTION ("3ware 9000 Storage Controller Linux Driver" ); +MODULE_LICENSE ("GPL" ); +module_param (cmds_per_lun, int,0 ); +MODULE_PARM_DESC (cmds_per_lun,"Maximum commands per LUN" ); + +/* Function prototypes */ +static int twa_aen_complete (TW_Device_Extension *tw_dev,int request_id); +static int twa_aen_drain_queue (TW_Device_Extension *tw_dev,int check_reset); +static void twa_aen_queue_event (TW_Device_Extension *tw_dev,TW_Command_Apache_Header *header); +static int twa_aen_read_queue (TW_Device_Extension *tw_dev,int request_id); +static char *twa_aen_severity_lookup (unsigned char severity_code); +static void twa_aen_sync_time (TW_Device_Extension *tw_dev,int request_id); +static int twa_allocate_memory (TW_Device_Extension *tw_dev,int size,int which); +static int twa_check_bits (u32 status_reg_value); +static int twa_check_srl (TW_Device_Extension *tw_dev,int *flashed); +static int twa_chrdev_ioctl (struct inode *inode,struct file *file,unsigned int cmd,unsigned long arg); +static int twa_chrdev_open (struct inode *inode,struct file *file); +static int twa_decode_bits (TW_Device_Extension *tw_dev,u32 status_reg_value); +static int twa_empty_response_queue (TW_Device_Extension *tw_dev); +static int twa_fill_sense (TW_Device_Extension *tw_dev,int request_id,int copy_sense,int print_host); +static void twa_free_device_extension (TW_Device_Extension *tw_dev); +static void twa_free_request_id (TW_Device_Extension *tw_dev,int request_id); +static void *twa_get_param (TW_Device_Extension *tw_dev,int request_id,int table_id,int parameter_id,int parameter_size_bytes); +static void twa_get_request_id (TW_Device_Extension *tw_dev,int *request_id); +static int twa_initconnection (TW_Device_Extension *tw_dev,int message_credits, +u32 set_features,unsigned short current_fw_srl, +unsigned short current_fw_arch_id, +unsigned short current_fw_branch, +unsigned short current_fw_build, +unsigned short *fw_on_ctlr_srl, +unsigned short *fw_on_ctlr_arch_id, +unsigned short *fw_on_ctlr_branch, +unsigned short *fw_on_ctlr_build, +u32 *init_connect_result); +static int twa_initialize_device_extension (TW_Device_Extension *tw_dev); +static irqreturn_t twa_interrupt (int irq,void *dev_instance,struct pt_regs *regs); +static void twa_load_sgl (TW_Command_Full *full_command_packet,int request_id,dma_addr_t dma_handle,int length); +static int twa_map_scsi_sg_data (TW_Device_Extension *tw_dev,int request_id); +static dma_addr_t twa_map_scsi_single_data (TW_Device_Extension *tw_dev,int request_id); +static int twa_poll_response (TW_Device_Extension *tw_dev,int request_id,int seconds); +static int twa_poll_status (TW_Device_Extension *tw_dev,u32 flag,int seconds); +static int twa_poll_status_gone (TW_Device_Extension *tw_dev,u32 flag,int seconds); +static int twa_post_command_packet (TW_Device_Extension *tw_dev,int request_id,char internal); +static int twa_reset_device_extension (TW_Device_Extension *tw_dev); +static int twa_reset_sequence (TW_Device_Extension *tw_dev,int soft_reset); +static int twa_scsi_biosparam (struct scsi_device *sdev,struct block_device *bdev,sector_t capacity,int geom[]); +static int __devinit twa_probe (struct pci_dev *pdev,const struct pci_device_id *dev_id); +static int twa_scsi_eh_abort (struct scsi_cmnd *SCpnt); +static int twa_scsi_eh_reset (struct scsi_cmnd *SCpnt); +static int twa_scsi_queue (struct scsi_cmnd *cmd,void (*done) (struct scsi_cmnd *)); +static int twa_scsiop_execute_scsi (TW_Device_Extension *tw_dev,int request_id,char *cdb,int use_sg,TW_SG_Apache *sglistarg); +static void twa_scsiop_execute_scsi_complete (TW_Device_Extension *tw_dev,int request_id); +static int twa_setup_irq (TW_Device_Extension *tw_dev); +static int twa_slave_configure (struct scsi_device *SDptr); +static char *twa_string_lookup (twa_message_type *table,unsigned int aen_code); +static void twa_unmap_scsi_data (TW_Device_Extension *tw_dev,int request_id); + +/* Functions */ + +/* Show some statistics about the card */ +static ssize_t twa_show_stats (struct class_device *class_dev,char *buf) +{ +struct Scsi_Host *host =class_to_shost (class_dev); +TW_Device_Extension *tw_dev = (TW_Device_Extension *)host->hostdata; +unsigned long flags =0 ; +ssize_t len; + +spin_lock_irqsave (tw_dev->host->host_lock, flags); + len = snprintf(buf, PAGE_SIZE, "Driver version: %s\n" + "Current commands posted: %4d\n" + "Max commands posted: %4d\n" + "Current pending commands: %4d\n" + "Max pending commands: %4d\n" + "Last sgl length: %4d\n" + "Max sgl length: %4d\n" + "Last sector count: %4d\n" + "Max sector count: %4d\n" + "SCSI Host Resets: %4d\n" + "SCSI Aborts/Timeouts: %4d\n" + "AEN's: %4d\n", + twa_driver_version, + tw_dev->posted_request_count, + tw_dev->max_posted_request_count, + tw_dev->pending_request_count, + tw_dev->max_pending_request_count,
drivers/scsi/3w-9xxx.c line 184
Quoted 10 lines
drivers/scsi/3w-9xxx.c +10
-0+ tw_dev->sgl_entries, + tw_dev->max_sgl_entries, + tw_dev->sector_count, + tw_dev->max_sector_count, + tw_dev->num_resets, + tw_dev->num_aborts, + tw_dev->aen_count); +spin_unlock_irqrestore (tw_dev->host->host_lock, flags); +return len; +}/* End twa_show_stats() */
surely sysfs allows use of seq_file API???
Quoted 12 lines
drivers/scsi/3w-9xxx.c +12
-0+/* File operations struct for character device */ +static struct file_operations twa_fops = { + .owner =THIS_MODULE , + .ioctl = twa_chrdev_ioctl, + .open = twa_chrdev_open, + .release =NULL +}; + +/* This function will complete an aen request from the isr */ +static int twa_aen_complete (TW_Device_Extension *tw_dev,int request_id) +{ +TW_Command_Full *full_command_packet;
drivers/scsi/3w-9xxx.c line 222
Quoted 10 lines
drivers/scsi/3w-9xxx.c +10
-0+TW_Command *command_packet; +TW_Command_Apache_Header *header; +unsigned short aen; +int retval =1 ; + + header = (TW_Command_Apache_Header *)tw_dev->generic_buffer_virt[request_id]; + tw_dev->posted_request_count--; + aen = header->status_block.error; + full_command_packet = (TW_Command_Full *)tw_dev->command_packet_virt[request_id]; + command_packet = &full_command_packet->command.oldcommand;
kill unneeded casts
Quoted 147 lines
drivers/scsi/3w-9xxx.c +147
-0+/* First check for internal completion of set param for time sync */ +if (command_packet->byte0_offset.opcode ==TW_OP_SET_PARAM ) { +/* Keep reading the queue in case there are more aen's */ +if (twa_aen_read_queue (tw_dev, request_id)) + goto out2; +else { + retval =0 ; + goto out; + } + } + +switch (aen) { +case TW_AEN_QUEUE_EMPTY : +/* Quit reading the queue if this is the last one */ +break ; +case TW_AEN_SYNC_TIME_WITH_HOST : +twa_aen_sync_time (tw_dev, request_id); + retval =0 ; + goto out; +default : +twa_aen_queue_event (tw_dev, header); + +/* If there are more aen's, keep reading the queue */ +if (twa_aen_read_queue (tw_dev, request_id)) + goto out2; +else { + retval =0 ; + goto out; + } + } + retval =0 ; +out2: + tw_dev->state[request_id] =TW_S_COMPLETED ; +twa_free_request_id (tw_dev, request_id); +clear_bit (TW_IN_ATTENTION_LOOP , &tw_dev->flags); +out: +return retval; +}/* End twa_aen_complete() */ + +/* This function will drain aen queue */ +static int twa_aen_drain_queue (TW_Device_Extension *tw_dev,int no_check_reset) +{ +int request_id =0 ; +char cdb[TW_MAX_CDB_LEN ]; +TW_SG_Apache sglist[1 ]; +int finished =0 , count =0 ; +TW_Command_Full *full_command_packet; +TW_Command_Apache_Header *header; +unsigned short aen; +int first_reset =0 , queue =0 , retval =1 ; + +if (no_check_reset) + first_reset =0 ; +else + first_reset =1 ; + + full_command_packet = (TW_Command_Full *)tw_dev->command_packet_virt[request_id]; +memset (full_command_packet,0 ,sizeof (TW_Command_Full)); + +/* Initialize cdb */ +memset (&cdb,0 ,TW_MAX_CDB_LEN ); + cdb[0 ] =REQUEST_SENSE ;/* opcode */ + cdb[4 ] =TW_ALLOCATION_LENGTH ;/* allocation length */ + +/* Initialize sglist */ +memset (&sglist,0 ,sizeof (TW_SG_Apache)); + sglist[0 ].length =TW_SECTOR_SIZE ; + sglist[0 ].address = tw_dev->generic_buffer_phys[request_id]; + +if (sglist[0 ].address &TW_ALIGNMENT_9000_SGL ) { +TW_PRINTK (tw_dev->host,TW_DRIVER ,0x1 ,"Found unaligned address during AEN drain" ); + goto out; + } + +/* Mark internal command */ + tw_dev->srb[request_id] =NULL ; + +do { +/* Send command to the board */ +if (twa_scsiop_execute_scsi (tw_dev, request_id, cdb,1 , sglist)) { +TW_PRINTK (tw_dev->host,TW_DRIVER ,0x2 ,"Error posting request sense" ); + goto out; + } + +/* Now poll for completion */ +if (twa_poll_response (tw_dev, request_id,30 )) { +TW_PRINTK (tw_dev->host,TW_DRIVER ,0x3 ,"No valid response while draining AEN queue" ); + tw_dev->posted_request_count--; + goto out; + } + + tw_dev->posted_request_count--; + header = (TW_Command_Apache_Header *)tw_dev->generic_buffer_virt[request_id]; + aen = header->status_block.error; + queue =0 ; + count++; + +switch (aen) { +case TW_AEN_QUEUE_EMPTY : +if (first_reset !=1 ) + goto out; +else + finished =1 ; +break ; +case TW_AEN_SOFT_RESET : +if (first_reset ==0 ) + first_reset =1 ; +else + queue =1 ; +break ; +case TW_AEN_SYNC_TIME_WITH_HOST : +break ; +default : + queue =1 ; + } + +/* Now queue an event info */ +if (queue) +twa_aen_queue_event (tw_dev, header); + }while ((finished ==0 ) && (count <TW_MAX_AEN_DRAIN )); + +if (count ==TW_MAX_AEN_DRAIN ) + goto out; + + retval =0 ; +out: + tw_dev->state[request_id] =TW_S_INITIAL ; +return retval; +}/* End twa_aen_drain_queue() */ + +/* This function will queue an event */ +static void twa_aen_queue_event (TW_Device_Extension *tw_dev,TW_Command_Apache_Header *header) +{ +u32 local_time; +struct timeval time; +TW_Event *event; +unsigned short aen; +char host[16 ]; + + tw_dev->aen_count++; + +/* Fill out event info */ + event = tw_dev->event_queue[tw_dev->error_index]; + +/* Check for clobber */ + host[0 ] ='\0' ; +if (tw_dev->host) {
drivers/scsi/3w-9xxx.c line 380
Quoted 10 lines
drivers/scsi/3w-9xxx.c +10
-0+sprintf (host," scsi%d:" , tw_dev->host->host_no); +if (event->retrieved ==TW_AEN_NOT_RETRIEVED ) + tw_dev->aen_clobber =1 ; + } + + aen = header->status_block.error; +memset (event,0 ,sizeof (TW_Event)); + + event->severity = header->status_block.substatus_block.severity; +do_gettimeofday (&time);
are we really calling do_gettimeofday() on every command sent to hardware? Can this be avoided?
Quoted 235 lines
drivers/scsi/3w-9xxx.c +235
-0+ local_time = (u32)(time.tv_sec - (sys_tz.tz_minuteswest *60 )); + event->time_stamp_sec = local_time; + event->aen_code = aen; + event->retrieved =TW_AEN_NOT_RETRIEVED ; + event->sequence_id = tw_dev->error_sequence_id; + tw_dev->error_sequence_id++; + + header->err_specific_desc[sizeof (header->err_specific_desc) -1 ] ='\0' ; + event->parameter_len =strlen (header->err_specific_desc); +memcpy (event->parameter_data, header->err_specific_desc, event->parameter_len); +if (event->severity !=TW_AEN_SEVERITY_DEBUG ) +printk (KERN_WARNING "3w-9xxx:%s AEN: %s (0x%02X:0x%04X): %s:%s.\n" , host,twa_aen_severity_lookup (header->status_block.substatus_block.severity),TW_MESSAGE_SOURCE_CONTROLLER_EVENT , aen,twa_string_lookup (twa_aen_table, aen), header->err_specific_desc); +else + tw_dev->aen_count--; + +if ((tw_dev->error_index +1 ) ==TW_Q_LENGTH ) + tw_dev->event_queue_wrapped =1 ; + tw_dev->error_index = (tw_dev->error_index +1 ) %TW_Q_LENGTH ; +}/* End twa_aen_queue_event() */ + +/* This function will read the aen queue from the isr */ +static int twa_aen_read_queue (TW_Device_Extension *tw_dev,int request_id) +{ +char cdb[TW_MAX_CDB_LEN ]; +TW_SG_Apache sglist[1 ]; +TW_Command_Full *full_command_packet; +int retval =1 ; + + full_command_packet = (TW_Command_Full *)tw_dev->command_packet_virt[request_id]; +memset (full_command_packet,0 ,sizeof (TW_Command_Full)); + +/* Initialize cdb */ +memset (&cdb,0 ,TW_MAX_CDB_LEN ); + cdb[0 ] =REQUEST_SENSE ;/* opcode */ + cdb[4 ] =TW_ALLOCATION_LENGTH ;/* allocation length */ + +/* Initialize sglist */ +memset (&sglist,0 ,sizeof (TW_SG_Apache)); + sglist[0 ].length =TW_SECTOR_SIZE ; + sglist[0 ].address = tw_dev->generic_buffer_phys[request_id]; + +/* Mark internal command */ + tw_dev->srb[request_id] =NULL ; + +/* Now post the command packet */ +if (twa_scsiop_execute_scsi (tw_dev, request_id, cdb,1 , sglist)) { +TW_PRINTK (tw_dev->host,TW_DRIVER ,0x4 ,"Post failed while reading AEN queue" ); + goto out; + } + retval =0 ; +out: +return retval; +}/* End twa_aen_read_queue() */ + +/* This function will look up an AEN severity string */ +static char *twa_aen_severity_lookup (unsigned char severity_code) +{ +char *retval =NULL ; + +if ((severity_code < (unsigned char )TW_AEN_SEVERITY_ERROR ) || + (severity_code > (unsigned char )TW_AEN_SEVERITY_DEBUG )) + goto out; + + retval = twa_aen_severity_table[severity_code]; +out: +return retval; +}/* End twa_aen_severity_lookup() */ + +/* This function will sync firmware time with the host time */ +static void twa_aen_sync_time (TW_Device_Extension *tw_dev,int request_id) +{ +u32 schedulertime; +struct timeval utc; +TW_Command_Full *full_command_packet; +TW_Command *command_packet; +TW_Param_Apache *param; +u32 local_time; + +/* Fill out the command packet */ + full_command_packet = (TW_Command_Full *)tw_dev->command_packet_virt[request_id]; +memset (full_command_packet,0 ,sizeof (TW_Command_Full)); + command_packet = &full_command_packet->command.oldcommand; + command_packet->byte0_offset.opcode =TW_OP_SET_PARAM ; + command_packet->byte0_offset.sgl_offset =2 ; + command_packet->request_id = request_id; + command_packet->byte8_offset.param.sgl[0 ].address = tw_dev->generic_buffer_phys[request_id]; + command_packet->byte8_offset.param.sgl[0 ].length =TW_SECTOR_SIZE ; + command_packet->size =TW_COMMAND_SIZE ; + command_packet->byte6_offset.parameter_count =1 ; + +/* Setup the param */ + param = (TW_Param_Apache *)tw_dev->generic_buffer_virt[request_id]; +memset (param,0 ,TW_SECTOR_SIZE ); + param->table_id =TW_TIMEKEEP_TABLE |0x8000 ;/* Controller time keep table */ + param->parameter_id =0x3 ;/* SchedulerTime */ + param->parameter_size_bytes =4 ; + +/* Convert system time in UTC to local time seconds since last +Sunday 12:00AM */ +do_gettimeofday (&utc); + local_time = (u32)(utc.tv_sec - (sys_tz.tz_minuteswest *60 )); + schedulertime = local_time - (3 *86400 ); + schedulertime = schedulertime %604800 ; + +memcpy (param->data, &schedulertime,sizeof (u32)); + +/* Mark internal command */ + tw_dev->srb[request_id] =NULL ; + +/* Now post the command */ +twa_post_command_packet (tw_dev, request_id,1 ); +}/* End twa_aen_sync_time() */ + +/* This function will allocate memory and check if it is correctly aligned */ +static int twa_allocate_memory (TW_Device_Extension *tw_dev,int size,int which) +{ +int i; +dma_addr_t dma_handle; +unsigned long *cpu_addr; +int retval =1 ; + + cpu_addr =pci_alloc_consistent (tw_dev->tw_pci_dev, size*TW_Q_LENGTH , &dma_handle); +if (!cpu_addr) { +TW_PRINTK (tw_dev->host,TW_DRIVER ,0x5 ,"Memory allocation failed" ); + goto out; + } + +if ((unsigned long )cpu_addr % (TW_ALIGNMENT_9000 )) { +TW_PRINTK (tw_dev->host,TW_DRIVER ,0x6 ,"Failed to allocate correctly aligned memory" ); +pci_free_consistent (tw_dev->tw_pci_dev, size*TW_Q_LENGTH , cpu_addr, dma_handle); + goto out; + } + +memset (cpu_addr,0 , size*TW_Q_LENGTH ); + +for (i =0 ; i <TW_Q_LENGTH ; i++) { +switch (which) { +case 0 : + tw_dev->command_packet_phys[i] = dma_handle+(i*size); + tw_dev->command_packet_virt[i] = (unsigned long *)((unsigned char *)cpu_addr + (i*size)); +break ; +case 1 : + tw_dev->generic_buffer_phys[i] = dma_handle+(i*size); + tw_dev->generic_buffer_virt[i] = (unsigned long *)((unsigned char *)cpu_addr + (i*size)); +break ; + } + } + retval =0 ; +out: +return retval; +}/* End twa_allocate_memory() */ + +/* This function will check the status register for unexpected bits */ +static int twa_check_bits (u32 status_reg_value) +{ +int retval =1 ; + +if ((status_reg_value &TW_STATUS_EXPECTED_BITS ) !=TW_STATUS_EXPECTED_BITS ) + goto out; +if ((status_reg_value &TW_STATUS_UNEXPECTED_BITS ) !=0 ) + goto out; + + retval =0 ; +out: +return retval; +}/* End twa_check_bits() */ + +/* This function will check the srl and decide if we are compatible */ +static int twa_check_srl (TW_Device_Extension *tw_dev,int *flashed) +{ +int retval =1 ; +unsigned short fw_on_ctlr_srl =0 , fw_on_ctlr_arch_id =0 ; +unsigned short fw_on_ctlr_branch =0 , fw_on_ctlr_build =0 ; +u32 init_connect_result =0 ; + +if (twa_initconnection (tw_dev,TW_INIT_MESSAGE_CREDITS ,TW_EXTENDED_INIT_CONNECT ,TW_CURRENT_FW_SRL ,TW_9000_ARCH_ID ,TW_CURRENT_FW_BRANCH ,TW_CURRENT_FW_BUILD , &fw_on_ctlr_srl, &fw_on_ctlr_arch_id, &fw_on_ctlr_branch, &fw_on_ctlr_build, &init_connect_result)) { +TW_PRINTK (tw_dev->host,TW_DRIVER ,0x7 ,"Initconnection failed while checking SRL" ); + goto out; + } + + tw_dev->working_srl =TW_CURRENT_FW_SRL ; + tw_dev->working_branch =TW_CURRENT_FW_BRANCH ; + tw_dev->working_build =TW_CURRENT_FW_BUILD ; + +/* Try base mode compatibility */ +if (!(init_connect_result &TW_CTLR_FW_COMPATIBLE )) { +if (twa_initconnection (tw_dev,TW_INIT_MESSAGE_CREDITS ,TW_EXTENDED_INIT_CONNECT ,TW_BASE_FW_SRL ,TW_9000_ARCH_ID ,TW_BASE_FW_BRANCH ,TW_BASE_FW_BUILD , &fw_on_ctlr_srl, &fw_on_ctlr_arch_id, &fw_on_ctlr_branch, &fw_on_ctlr_build, &init_connect_result)) { +TW_PRINTK (tw_dev->host,TW_DRIVER ,0xa ,"Initconnection (base mode) failed while checking SRL" ); + goto out; + } +if (!(init_connect_result &TW_CTLR_FW_COMPATIBLE )) { +if (TW_CURRENT_FW_SRL > fw_on_ctlr_srl) { +TW_PRINTK (tw_dev->host,TW_DRIVER ,0x32 ,"Firmware and driver incompatibility: please upgrade firmware" ); + }else { +TW_PRINTK (tw_dev->host,TW_DRIVER ,0x33 ,"Firmware and driver incompatibility: please upgrade driver" ); + } + goto out; + } + tw_dev->working_srl =TW_BASE_FW_SRL ; + tw_dev->working_branch =TW_BASE_FW_BRANCH ; + tw_dev->working_build =TW_BASE_FW_BUILD ; + } + retval =0 ; +out: +return retval; +}/* End twa_check_srl() */ + +/* This function handles ioctl for the character device */ +static int twa_chrdev_ioctl (struct inode *inode,struct file *file,unsigned int cmd,unsigned long arg) +{ +long timeout; +unsigned long *cpu_addr, data_buffer_length_adjusted =0 , flags =0 ; +dma_addr_t dma_handle; +int request_id =0 ; +unsigned int sequence_id =0 ; +unsigned char event_index, start_index; +TW_Ioctl_Driver_Command driver_command; +TW_Ioctl_Buf_Apache *tw_ioctl; +TW_Lock *tw_lock; +TW_Command_Full *full_command_packet; +TW_Compatibility_Info *tw_compat_info; +TW_Event *event; +struct timeval current_time; +u32 current_time_ms; +TW_Device_Extension *tw_dev = twa_device_extension_list[iminor (inode)]; +int retval =TW_IOCTL_ERROR_OS_EFAULT ; + +/* Only let one of these through at a time */ +if (down_interruptible (&tw_dev->ioctl_sem)) { + retval =TW_IOCTL_ERROR_OS_EINTR ; + goto out; + } + +/* First copy down the driver command */ +if (copy_from_user (&driver_command, (void *)arg,sizeof (TW_Ioctl_Driver_Command)))
drivers/scsi/3w-9xxx.c line 625
Quoted 10 lines
drivers/scsi/3w-9xxx.c +10
-0+ goto out2; + +/* Check data buffer size */ +if (driver_command.buffer_length >TW_MAX_SECTORS *512 ) { + retval =TW_IOCTL_ERROR_OS_EINVAL ; + goto out2; + } + +/* Hardware can only do multiple of 512 byte transfers */ + data_buffer_length_adjusted = (driver_command.buffer_length +511 ) & ~511 ;
why not return -EINVAL and require userland to align?
drivers/scsi/3w-9xxx.c line 638
Quoted 12 lines
drivers/scsi/3w-9xxx.c +12
-0+/* Now allocate ioctl buf memory */ + cpu_addr =pci_alloc_consistent (tw_dev->tw_pci_dev, data_buffer_length_adjusted+sizeof (TW_Ioctl_Buf_Apache) -1 , &dma_handle); +if (!cpu_addr) { + retval =TW_IOCTL_ERROR_OS_ENOMEM ; + goto out2; + } + + tw_ioctl = (TW_Ioctl_Buf_Apache *)cpu_addr; + +/* Now copy down the entire ioctl */ +if (copy_from_user (tw_ioctl, (void *)arg, driver_command.buffer_length +sizeof (TW_Ioctl_Buf_Apache) -1 )) + goto out3;
are you really copying all the user data buffers here?
That will be godawful slow.
I would suggest building and sending a struct request instead.
Quoted 17 lines
drivers/scsi/3w-9xxx.c +17
-0+/* See which ioctl we are doing */ +switch (cmd) { +case TW_IOCTL_FIRMWARE_PASS_THROUGH : +spin_lock_irqsave (tw_dev->host->host_lock, flags); +twa_get_request_id (tw_dev, &request_id); + +/* Flag internal command */ + tw_dev->srb[request_id] =0 ; + +/* Flag chrdev ioctl */ + tw_dev->chrdev_request_id = request_id; + + full_command_packet = &tw_ioctl->firmware_command; + +/* Load request id and sglist for both command types */ +twa_load_sgl (full_command_packet, request_id, dma_handle, data_buffer_length_adjusted); +
drivers/scsi/3w-9xxx.c line 666
Quoted 10 lines
drivers/scsi/3w-9xxx.c +10
-0+memcpy (tw_dev->command_packet_virt[request_id], &(tw_ioctl->firmware_command),sizeof (TW_Command_Full)); + +/* Now post the command packet to the controller */ +twa_post_command_packet (tw_dev, request_id,1 ); +spin_unlock_irqrestore (tw_dev->host->host_lock, flags); + + timeout =TW_IOCTL_CHRDEV_TIMEOUT *HZ ; + +/* Now wait for command to complete */ + timeout =wait_event_interruptible_timeout (tw_dev->ioctl_wqueue, tw_dev->chrdev_request_id ==TW_IOCTL_CHRDEV_FREE , timeout);
surely there is a better way to do this... semaphore?
down() here, and up() in irq handler or timer.
Quoted 325 lines
drivers/scsi/3w-9xxx.c +325
-0+/* Check if we timed out, got a signal, or didn't get +an interrupt */ +if ((timeout <=0 ) && (tw_dev->chrdev_request_id !=TW_IOCTL_CHRDEV_FREE )) { +/* Now we need to reset the board */ +if (timeout ==TW_IOCTL_ERROR_OS_ERESTARTSYS ) { + retval = timeout; + }else { +printk (KERN_WARNING "3w-9xxx: scsi%d: WARNING: (0x%02X:0x%04X): Character ioctl (0x%x) timed out, resetting card.\n" , tw_dev->host->host_no,TW_DRIVER ,0xc , cmd); + retval =TW_IOCTL_ERROR_OS_EIO ; + } +spin_lock_irqsave (tw_dev->host->host_lock, flags); + tw_dev->state[request_id] =TW_S_COMPLETED ; +twa_free_request_id (tw_dev, request_id); + tw_dev->posted_request_count--; +twa_reset_device_extension (tw_dev); +spin_unlock_irqrestore (tw_dev->host->host_lock, flags); + goto out3; + } + +/* Now copy in the command packet response */ +memcpy (&(tw_ioctl->firmware_command), tw_dev->command_packet_virt[request_id],sizeof (TW_Command_Full)); + +/* Now complete the io */ +spin_lock_irqsave (tw_dev->host->host_lock, flags); + tw_dev->posted_request_count--; + tw_dev->state[request_id] =TW_S_COMPLETED ; +twa_free_request_id (tw_dev, request_id); +spin_unlock_irqrestore (tw_dev->host->host_lock, flags); +break ; +case TW_IOCTL_GET_COMPATIBILITY_INFO : + tw_ioctl->driver_command.status =0 ; +/* Copy compatiblity struct into ioctl data buffer */ + tw_compat_info = (TW_Compatibility_Info *)tw_ioctl->data_buffer; +strncpy (tw_compat_info->driver_version, twa_driver_version,strlen (twa_driver_version)); + tw_compat_info->working_srl = tw_dev->working_srl; + tw_compat_info->working_branch = tw_dev->working_branch; + tw_compat_info->working_build = tw_dev->working_build; +break ; +case TW_IOCTL_GET_LAST_EVENT : +if (tw_dev->event_queue_wrapped) { +if (tw_dev->aen_clobber) { + tw_ioctl->driver_command.status =TW_IOCTL_ERROR_STATUS_AEN_CLOBBER ; + tw_dev->aen_clobber =0 ; + }else + tw_ioctl->driver_command.status =0 ; + }else { +if (!tw_dev->error_index) { + tw_ioctl->driver_command.status =TW_IOCTL_ERROR_STATUS_NO_MORE_EVENTS ; +break ; + } + tw_ioctl->driver_command.status =0 ; + } + event_index = (tw_dev->error_index -1 +TW_Q_LENGTH ) %TW_Q_LENGTH ; +memcpy (tw_ioctl->data_buffer, tw_dev->event_queue[event_index],sizeof (TW_Event)); + tw_dev->event_queue[event_index]->retrieved =TW_AEN_RETRIEVED ; +break ; +case TW_IOCTL_GET_FIRST_EVENT : +if (tw_dev->event_queue_wrapped) { +if (tw_dev->aen_clobber) { + tw_ioctl->driver_command.status =TW_IOCTL_ERROR_STATUS_AEN_CLOBBER ; + tw_dev->aen_clobber =0 ; + }else + tw_ioctl->driver_command.status =0 ; + event_index = tw_dev->error_index; + }else { +if (!tw_dev->error_index) { + tw_ioctl->driver_command.status =TW_IOCTL_ERROR_STATUS_NO_MORE_EVENTS ; +break ; + } + tw_ioctl->driver_command.status =0 ; + event_index =0 ; + } +memcpy (tw_ioctl->data_buffer, tw_dev->event_queue[event_index],sizeof (TW_Event)); + tw_dev->event_queue[event_index]->retrieved =TW_AEN_RETRIEVED ; +break ; +case TW_IOCTL_GET_NEXT_EVENT : + event = (TW_Event *)tw_ioctl->data_buffer; + sequence_id = event->sequence_id; + tw_ioctl->driver_command.status =0 ; + +if (tw_dev->event_queue_wrapped) { +if (tw_dev->aen_clobber) { + tw_ioctl->driver_command.status =TW_IOCTL_ERROR_STATUS_AEN_CLOBBER ; + tw_dev->aen_clobber =0 ; + } + start_index = tw_dev->error_index; + }else { +if (!tw_dev->error_index) { + tw_ioctl->driver_command.status =TW_IOCTL_ERROR_STATUS_NO_MORE_EVENTS ; +break ; + } + start_index =0 ; + } + event_index = (start_index + sequence_id - tw_dev->event_queue[start_index]->sequence_id +1 ) %TW_Q_LENGTH ; + +if (!(tw_dev->event_queue[event_index]->sequence_id > sequence_id)) { +if (tw_ioctl->driver_command.status ==TW_IOCTL_ERROR_STATUS_AEN_CLOBBER ) + tw_dev->aen_clobber =1 ; + tw_ioctl->driver_command.status =TW_IOCTL_ERROR_STATUS_NO_MORE_EVENTS ; +break ; + } +memcpy (tw_ioctl->data_buffer, tw_dev->event_queue[event_index],sizeof (TW_Event)); + tw_dev->event_queue[event_index]->retrieved =TW_AEN_RETRIEVED ; +break ; +case TW_IOCTL_GET_PREVIOUS_EVENT : + event = (TW_Event *)tw_ioctl->data_buffer; + sequence_id = event->sequence_id; + tw_ioctl->driver_command.status =0 ; + +if (tw_dev->event_queue_wrapped) { +if (tw_dev->aen_clobber) { + tw_ioctl->driver_command.status =TW_IOCTL_ERROR_STATUS_AEN_CLOBBER ; + tw_dev->aen_clobber =0 ; + } + start_index = tw_dev->error_index; + }else { +if (!tw_dev->error_index) { + tw_ioctl->driver_command.status =TW_IOCTL_ERROR_STATUS_NO_MORE_EVENTS ; +break ; + } + start_index =0 ; + } + event_index = (start_index + sequence_id - tw_dev->event_queue[start_index]->sequence_id -1 ) %TW_Q_LENGTH ; + +if (!(tw_dev->event_queue[event_index]->sequence_id < sequence_id)) { +if (tw_ioctl->driver_command.status ==TW_IOCTL_ERROR_STATUS_AEN_CLOBBER ) + tw_dev->aen_clobber =1 ; + tw_ioctl->driver_command.status =TW_IOCTL_ERROR_STATUS_NO_MORE_EVENTS ; +break ; + } +memcpy (tw_ioctl->data_buffer, tw_dev->event_queue[event_index],sizeof (TW_Event)); + tw_dev->event_queue[event_index]->retrieved =TW_AEN_RETRIEVED ; +break ; +case TW_IOCTL_GET_LOCK : + tw_lock = (TW_Lock *)tw_ioctl->data_buffer; +do_gettimeofday (¤t_time); + current_time_ms = (current_time.tv_sec *1000 ) + (current_time.tv_usec /1000 ); + +if ((tw_lock->force_flag ==1 ) || (tw_dev->ioctl_sem_lock ==0 ) || (current_time_ms >= tw_dev->ioctl_msec)) { + tw_dev->ioctl_sem_lock =1 ; + tw_dev->ioctl_msec = current_time_ms + tw_lock->timeout_msec; + tw_ioctl->driver_command.status =0 ; + tw_lock->time_remaining_msec = tw_lock->timeout_msec; + }else { + tw_ioctl->driver_command.status =TW_IOCTL_ERROR_STATUS_LOCKED ; + tw_lock->time_remaining_msec = tw_dev->ioctl_msec - current_time_ms; + } +break ; +case TW_IOCTL_RELEASE_LOCK : +if (tw_dev->ioctl_sem_lock ==1 ) { + tw_dev->ioctl_sem_lock =0 ; + tw_ioctl->driver_command.status =0 ; + }else { + tw_ioctl->driver_command.status =TW_IOCTL_ERROR_STATUS_NOT_LOCKED ; + } +break ; +default : + retval =TW_IOCTL_ERROR_OS_ENOTTY ; + goto out3; + } + +/* Now copy the entire response to userspace */ +if (copy_to_user ((void *)arg, tw_ioctl,sizeof (TW_Ioctl_Buf_Apache) + driver_command.buffer_length -1 ) ==0 ) + retval =0 ; +out3: +/* Now free ioctl buf memory */ +pci_free_consistent (tw_dev->tw_pci_dev, data_buffer_length_adjusted+sizeof (TW_Ioctl_Buf_Apache) -1 , cpu_addr, dma_handle); +out2: +up (&tw_dev->ioctl_sem); +out: +return retval; +}/* End twa_chrdev_ioctl() */ + +/* This function handles open for the character device */ +static int twa_chrdev_open (struct inode *inode,struct file *file) +{ +unsigned int minor_number; +int retval =TW_IOCTL_ERROR_OS_ENODEV ; + + minor_number =iminor (inode); +if (minor_number >= twa_device_extension_count) + goto out; + retval =0 ; +out: +return retval; +}/* End twa_chrdev_open() */ + +/* This function will print readable messages from status register errors */ +static int twa_decode_bits (TW_Device_Extension *tw_dev,u32 status_reg_value) +{ +int retval =1 ; + +/* Check for various error conditions and handle them appropriately */ +if (status_reg_value &TW_STATUS_PCI_PARITY_ERROR ) { +TW_PRINTK (tw_dev->host,TW_DRIVER ,0xc ,"PCI Parity Error: clearing" ); +writel (TW_CONTROL_CLEAR_PARITY_ERROR ,TW_CONTROL_REG_ADDR (tw_dev)); + } + +if (status_reg_value &TW_STATUS_PCI_ABORT ) { +TW_PRINTK (tw_dev->host,TW_DRIVER ,0xd ,"PCI Abort: clearing" ); +writel (TW_CONTROL_CLEAR_PCI_ABORT ,TW_CONTROL_REG_ADDR (tw_dev)); +pci_write_config_word (tw_dev->tw_pci_dev,PCI_STATUS ,TW_PCI_CLEAR_PCI_ABORT ); + } + +if (status_reg_value &TW_STATUS_QUEUE_ERROR ) { +TW_PRINTK (tw_dev->host,TW_DRIVER ,0xe ,"Controller Queue Error: clearing" ); +writel (TW_CONTROL_CLEAR_QUEUE_ERROR ,TW_CONTROL_REG_ADDR (tw_dev)); + } + +if (status_reg_value &TW_STATUS_SBUF_WRITE_ERROR ) { +TW_PRINTK (tw_dev->host,TW_DRIVER ,0xf ,"SBUF Write Error: clearing" ); +writel (TW_CONTROL_CLEAR_SBUF_WRITE_ERROR ,TW_CONTROL_REG_ADDR (tw_dev)); + } + +if (status_reg_value &TW_STATUS_MICROCONTROLLER_ERROR ) { +if (tw_dev->reset_print ==0 ) { +TW_PRINTK (tw_dev->host,TW_DRIVER ,0x10 ,"Microcontroller Error: clearing" ); + tw_dev->reset_print =1 ; + } + goto out; + } + retval =0 ; +out: +return retval; +}/* End twa_decode_bits() */ + +/* This function will empty the response queue */ +static int twa_empty_response_queue (TW_Device_Extension *tw_dev) +{ +u32 status_reg_value, response_que_value; +int count =0 , retval =1 ; + + status_reg_value =readl (TW_STATUS_REG_ADDR (tw_dev)); + +while (((status_reg_value &TW_STATUS_RESPONSE_QUEUE_EMPTY ) ==0 ) && (count <TW_MAX_RESPONSE_DRAIN )) { + response_que_value =readl (TW_RESPONSE_QUEUE_REG_ADDR (tw_dev)); + status_reg_value =readl (TW_STATUS_REG_ADDR (tw_dev)); + count++; + } +if (count ==TW_MAX_RESPONSE_DRAIN ) + goto out; + + retval =0 ; +out: +return retval; +}/* End twa_empty_response_queue() */ + +/* This function passes sense keys from firmware to scsi layer */ +static int twa_fill_sense (TW_Device_Extension *tw_dev,int request_id,int copy_sense,int print_host) +{ +TW_Command_Full *full_command_packet; +unsigned short error; +int retval =1 ; + + full_command_packet = (TW_Command_Full *)tw_dev->command_packet_virt[request_id]; +/* Don't print error for Logical unit not supported during rollcall */ + error = full_command_packet->header.status_block.error; +if ((error !=TW_ERROR_LOGICAL_UNIT_NOT_SUPPORTED ) && (error !=TW_ERROR_UNIT_OFFLINE )) { +if (print_host) +printk (KERN_WARNING "3w-9xxx: scsi%d: ERROR: (0x%02X:0x%04X): %s:%s.\n" , tw_dev->host->host_no,TW_MESSAGE_SOURCE_CONTROLLER_ERROR , full_command_packet->header.status_block.error,twa_string_lookup (twa_error_table, full_command_packet->header.status_block.error), full_command_packet->header.err_specific_desc); +else +printk (KERN_WARNING "3w-9xxx: ERROR: (0x%02X:0x%04X): %s:%s.\n" ,TW_MESSAGE_SOURCE_CONTROLLER_ERROR , full_command_packet->header.status_block.error,twa_string_lookup (twa_error_table, full_command_packet->header.status_block.error), full_command_packet->header.err_specific_desc); + } + +if (copy_sense) { +memcpy (tw_dev->srb[request_id]->sense_buffer, full_command_packet->header.sense_data,TW_SENSE_DATA_LENGTH ); + tw_dev->srb[request_id]->result = (full_command_packet->command.newcommand.status <<1 ); + retval =TW_ISR_DONT_RESULT ; + goto out; + } + retval =0 ; +out: +return retval; +}/* End twa_fill_sense() */ + +/* This function will free up device extension resources */ +static void twa_free_device_extension (TW_Device_Extension *tw_dev) +{ +if (tw_dev->command_packet_virt[0 ]) +pci_free_consistent (tw_dev->tw_pci_dev,sizeof (TW_Command_Full)*TW_Q_LENGTH , tw_dev->command_packet_virt[0 ], tw_dev->command_packet_phys[0 ]); + +if (tw_dev->generic_buffer_virt[0 ]) +pci_free_consistent (tw_dev->tw_pci_dev,TW_SECTOR_SIZE *TW_Q_LENGTH , tw_dev->generic_buffer_virt[0 ], tw_dev->generic_buffer_phys[0 ]); + +if (tw_dev->event_queue[0 ]) +kfree (tw_dev->event_queue[0 ]); +}/* End twa_free_device_extension() */ + +/* This function will free a request id */ +static void twa_free_request_id (TW_Device_Extension *tw_dev,int request_id) +{ + tw_dev->free_queue[tw_dev->free_tail] = request_id; + tw_dev->state[request_id] =TW_S_FINISHED ; + tw_dev->free_tail = (tw_dev->free_tail +1 ) %TW_Q_LENGTH ; +}/* End twa_free_request_id() */ + +/* This function will get parameter table entires from the firmware */ +static void *twa_get_param (TW_Device_Extension *tw_dev,int request_id,int table_id,int parameter_id,int parameter_size_bytes) +{ +TW_Command_Full *full_command_packet; +TW_Command *command_packet; +TW_Param_Apache *param; +unsigned long param_value; +void *retval =NULL ; + +/* Setup the command packet */ + full_command_packet = (TW_Command_Full *)tw_dev->command_packet_virt[request_id]; +memset (full_command_packet,0 ,sizeof (TW_Command_Full)); + command_packet = &full_command_packet->command.oldcommand; + + command_packet->byte0_offset.opcode =TW_OP_GET_PARAM ; + command_packet->byte0_offset.sgl_offset =2 ; + command_packet->size =TW_COMMAND_SIZE ; + command_packet->request_id = request_id; + command_packet->byte6_offset.block_count =1 ; + +/* Now setup the param */ + param = (TW_Param_Apache *)tw_dev->generic_buffer_virt[request_id]; +memset (param,0 ,TW_SECTOR_SIZE ); + param->table_id = table_id |0x8000 ; + param->parameter_id = parameter_id; + param->parameter_size_bytes = parameter_size_bytes; + param_value = tw_dev->generic_buffer_phys[request_id]; + + command_packet->byte8_offset.param.sgl[0 ].address = param_value;
drivers/scsi/3w-9xxx.c line 1002
Quoted 10 lines
drivers/scsi/3w-9xxx.c +10
-0+ command_packet->byte8_offset.param.sgl[0 ].length =TW_SECTOR_SIZE ; + +/* Post the command packet to the board */ +twa_post_command_packet (tw_dev, request_id,1 ); + +/* Poll for completion */ +if (twa_poll_response (tw_dev, request_id,30 )) + TW_PRINTK(tw_dev->host, TW_DRIVER, 0x13, "No valid response during get param") + else + retval = (void *)&(param->data[0 ]);
just use standard SCSI code to issue a SCSI command, and wait for a response.
Quoted 67 lines
drivers/scsi/3w-9xxx.c +67
-0+ tw_dev->posted_request_count--; + tw_dev->state[request_id] =TW_S_INITIAL ; + +return retval; +}/* End twa_get_param() */ + +/* This function will assign an available request id */ +static void twa_get_request_id (TW_Device_Extension *tw_dev,int *request_id) +{ + *request_id = tw_dev->free_queue[tw_dev->free_head]; + tw_dev->free_head = (tw_dev->free_head +1 ) %TW_Q_LENGTH ; + tw_dev->state[*request_id] =TW_S_STARTED ; +}/* End twa_get_request_id() */ + +/* This function will send an initconnection command to controller */ +static int twa_initconnection (TW_Device_Extension *tw_dev,int message_credits, +u32 set_features,unsigned short current_fw_srl, +unsigned short current_fw_arch_id, +unsigned short current_fw_branch, +unsigned short current_fw_build, +unsigned short *fw_on_ctlr_srl, +unsigned short *fw_on_ctlr_arch_id, +unsigned short *fw_on_ctlr_branch, +unsigned short *fw_on_ctlr_build, +u32 *init_connect_result) +{ +TW_Command_Full *full_command_packet; +TW_Initconnect *tw_initconnect; +int request_id =0 , retval =1 ; + +/* Initialize InitConnection command packet */ + full_command_packet = (TW_Command_Full *)tw_dev->command_packet_virt[request_id]; +memset (full_command_packet,0 ,sizeof (TW_Command_Full)); + full_command_packet->header.header_desc.size_header =128 ; + + tw_initconnect = (TW_Initconnect *)&full_command_packet->command.oldcommand; + tw_initconnect->opcode =TW_OP_INIT_CONNECTION ; + + tw_initconnect->request_id = request_id; + tw_initconnect->message_credits = message_credits; + tw_initconnect->features = set_features; +#if BITS_PER_LONG >32 +/* Turn on 64-bit sgl support */ + tw_initconnect->features |=1 ; +#endif + +if (set_features &TW_EXTENDED_INIT_CONNECT ) { + tw_initconnect->size =TW_INIT_COMMAND_PACKET_SIZE_EXTENDED ; + tw_initconnect->fw_srl = current_fw_srl; + tw_initconnect->fw_arch_id = current_fw_arch_id; + tw_initconnect->fw_branch = current_fw_branch; + tw_initconnect->fw_build = current_fw_build; + }else + tw_initconnect->size =TW_INIT_COMMAND_PACKET_SIZE ; + +/* Send command packet to the board */ +twa_post_command_packet (tw_dev, request_id,1 ); + +/* Poll for completion */ +if (twa_poll_response (tw_dev, request_id,30 )) { +TW_PRINTK (tw_dev->host,TW_DRIVER ,0x15 ,"No valid response during init connection" ); + }else { +if (set_features &TW_EXTENDED_INIT_CONNECT ) { + *fw_on_ctlr_srl = tw_initconnect->fw_srl; + *fw_on_ctlr_arch_id = tw_initconnect->fw_arch_id; + *fw_on_ctlr_branch = tw_initconnect->fw_branch; + *fw_on_ctlr_build = tw_initconnect->fw_build;
drivers/scsi/3w-9xxx.c line 1080
Quoted 10 lines
drivers/scsi/3w-9xxx.c +10
-0+ *init_connect_result = tw_initconnect->result; + } + retval =0 ; + } + + tw_dev->posted_request_count--; + tw_dev->state[request_id] =TW_S_INITIAL ; + +return retval; +}/* End twa_initconnection() */
ditto
Quoted 13 lines
drivers/scsi/3w-9xxx.c +13
-0+/* This function will initialize the fields of a device extension */ +static int twa_initialize_device_extension (TW_Device_Extension *tw_dev) +{ +int i, retval =1 ; + +/* Initialize command packet buffers */ +if (twa_allocate_memory (tw_dev,sizeof (TW_Command_Full),0 )) { +TW_PRINTK (tw_dev->host,TW_DRIVER ,0x16 ,"Command packet memory allocation failed" ); + goto out; + } + +/* Initialize generic buffer */ +if (twa_allocate_memory (tw_dev,TW_SECTOR_SIZE ,1 )) {
drivers/scsi/3w-9xxx.c line 1104
Quoted 10 lines
drivers/scsi/3w-9xxx.c +10
-0+TW_PRINTK (tw_dev->host,TW_DRIVER ,0x17 ,"Generic memory allocation failed" ); + goto out; + } + +/* Allocate event info space */ + tw_dev->event_queue[0 ] =kmalloc (sizeof (TW_Event) *TW_Q_LENGTH ,GFP_ATOMIC ); +if (!tw_dev->event_queue[0 ]) { +TW_PRINTK (tw_dev->host,TW_DRIVER ,0x18 ,"Event info memory allocation failed" ); + goto out; + }
allocate with GFP_KERNEL, GFP_ATOMIC is not necessary, and it depletes the emergency pools
Quoted 28 lines
drivers/scsi/3w-9xxx.c +28
-0+memset (tw_dev->event_queue[0 ],0 ,sizeof (TW_Event) *TW_Q_LENGTH ); + +for (i =0 ; i <TW_Q_LENGTH ; i++) { + tw_dev->event_queue[i] = (TW_Event *)((unsigned char *)tw_dev->event_queue[0 ] + (i *sizeof (TW_Event))); + tw_dev->free_queue[i] = i; + tw_dev->state[i] =TW_S_INITIAL ; + } + + tw_dev->pending_head =TW_Q_START ; + tw_dev->pending_tail =TW_Q_START ; + tw_dev->free_head =TW_Q_START ; + tw_dev->free_tail =TW_Q_START ; + tw_dev->error_sequence_id =1 ; + tw_dev->chrdev_request_id =TW_IOCTL_CHRDEV_FREE ; + +init_MUTEX (&tw_dev->ioctl_sem); +init_waitqueue_head (&tw_dev->ioctl_wqueue); + + retval =0 ; +out: +return retval; +}/* End twa_initialize_device_extension() */ + +/* This function is the interrupt service routine */ +static irqreturn_t twa_interrupt (int irq,void *dev_instance,struct pt_regs *regs) +{ +int request_id, error =0 ; +u32 status_reg_value;
drivers/scsi/3w-9xxx.c line 1143
Quoted 10 lines
drivers/scsi/3w-9xxx.c +10
-0+TW_Response_Queue response_que; +unsigned long flags =0 ; +TW_Command_Full *full_command_packet; +TW_Command *command_packet; +TW_Device_Extension *tw_dev = (TW_Device_Extension *)dev_instance; +int handled =0 ; + +/* See if we are already running on another processor */ +if (test_and_set_bit (TW_IN_INTR , &tw_dev->flags)) +return IRQ_NONE ;
completely bogus test. remove. remove TW_IN_INTR too.
drivers/scsi/3w-9xxx.c line 1146
Quoted 2 lines
drivers/scsi/3w-9xxx.c +2
-0+/* Get the per adapter lock */ +spin_lock_irqsave (tw_dev->host->host_lock, flags);
spin_lock(), not spin_lock_irqsave()
drivers/scsi/3w-9xxx.c line 1158
Quoted 11 lines
drivers/scsi/3w-9xxx.c +11
-0+/* See if the interrupt matches this instance */ +if (tw_dev->tw_pci_dev->irq == (unsigned int )irq) { + + handled =1 ; + +/* Read the registers */ + status_reg_value =readl (TW_STATUS_REG_ADDR (tw_dev)); + +/* Check if this is our interrupt, otherwise bail */ +if (!(status_reg_value &TW_STATUS_VALID_INTERRUPT )) + goto twa_interrupt_bail;
also check for 0xffffffff return value, indicating hardware removed or hardware fault.
Quoted 161 lines
drivers/scsi/3w-9xxx.c +161
-0+/* Check controller for errors */ +if (twa_check_bits (status_reg_value)) { +if (twa_decode_bits (tw_dev, status_reg_value)) { +TW_CLEAR_ALL_INTERRUPTS (tw_dev); + goto twa_interrupt_bail; + } + } + +/* Handle host interrupt */ +if (status_reg_value &TW_STATUS_HOST_INTERRUPT ) +TW_CLEAR_HOST_INTERRUPT (tw_dev); + +/* Handle attention interrupt */ +if (status_reg_value &TW_STATUS_ATTENTION_INTERRUPT ) { +TW_CLEAR_ATTENTION_INTERRUPT (tw_dev); +if (!(test_and_set_bit (TW_IN_ATTENTION_LOOP , &tw_dev->flags))) { +twa_get_request_id (tw_dev, &request_id); + + error =twa_aen_read_queue (tw_dev, request_id); +if (error) { + tw_dev->state[request_id] =TW_S_COMPLETED ; +twa_free_request_id (tw_dev, request_id); +clear_bit (TW_IN_ATTENTION_LOOP , &tw_dev->flags); + } + } + } + +/* Handle command interrupt */ +if (status_reg_value &TW_STATUS_COMMAND_INTERRUPT ) { +TW_MASK_COMMAND_INTERRUPT (tw_dev); +/* Drain as many pending commands as we can */ +while (tw_dev->pending_request_count >0 ) { + request_id = tw_dev->pending_queue[tw_dev->pending_head]; +if (tw_dev->state[request_id] !=TW_S_PENDING ) { +TW_PRINTK (tw_dev->host,TW_DRIVER ,0x19 ,"Found request id that wasn't pending" ); +TW_CLEAR_ALL_INTERRUPTS (tw_dev); + goto twa_interrupt_bail; + } +if (twa_post_command_packet (tw_dev, request_id,1 )==0 ) { + tw_dev->pending_head = (tw_dev->pending_head +1 ) %TW_Q_LENGTH ; + tw_dev->pending_request_count--; + }else { +/* If we get here, we will continue re-posting on the next command interrupt */ +break ; + } + } + } + +/* Handle response interrupt */ +if (status_reg_value &TW_STATUS_RESPONSE_INTERRUPT ) { + +/* Drain the response queue from the board */ +while ((status_reg_value &TW_STATUS_RESPONSE_QUEUE_EMPTY ) ==0 ) { +/* Complete the response */ + response_que.value =readl (TW_RESPONSE_QUEUE_REG_ADDR (tw_dev)); + request_id = response_que.u.response_id; + full_command_packet = (TW_Command_Full *)tw_dev->command_packet_virt[request_id]; + error =0 ; + command_packet = &full_command_packet->command.oldcommand; +/* Check for command packet errors */ +if (full_command_packet->command.newcommand.status !=0 ) { +if (tw_dev->srb[request_id] !=0 ) { + error =twa_fill_sense (tw_dev, request_id,1 ,1 ); + }else { +/* Skip ioctl error prints */ +if (request_id != tw_dev->chrdev_request_id) { + error =twa_fill_sense (tw_dev, request_id,0 ,1 ); + } + } + } + +/* Check for correct state */ +if (tw_dev->state[request_id] !=TW_S_POSTED ) { +if (tw_dev->srb[request_id] !=0 ) { +TW_PRINTK (tw_dev->host,TW_DRIVER ,0x1a ,"Received a request id that wasn't posted" ); +TW_CLEAR_ALL_INTERRUPTS (tw_dev); + goto twa_interrupt_bail; + } + } + +/* Check for internal command completion */ +if (tw_dev->srb[request_id] ==0 ) { +if (request_id != tw_dev->chrdev_request_id) { +if (twa_aen_complete (tw_dev, request_id)) +TW_PRINTK (tw_dev->host,TW_DRIVER ,0x1b ,"Error completing AEN during attention interrupt" ); + }else { + tw_dev->chrdev_request_id =TW_IOCTL_CHRDEV_FREE ; +wake_up (&tw_dev->ioctl_wqueue); + } + }else { +twa_scsiop_execute_scsi_complete (tw_dev, request_id); +/* If no error command was a success */ +if (error ==0 ) { + tw_dev->srb[request_id]->result = (DID_OK <<16 ); + } + +/* If error, command failed */ +if (error ==1 ) { +/* Ask for a host reset */ + tw_dev->srb[request_id]->result = (DID_OK <<16 ) | (CHECK_CONDITION <<1 ); + } + +/* Now complete the io */ + tw_dev->state[request_id] =TW_S_COMPLETED ; +twa_free_request_id (tw_dev, request_id); + tw_dev->posted_request_count--; + tw_dev->srb[request_id]->scsi_done (tw_dev->srb[request_id]); +twa_unmap_scsi_data (tw_dev, request_id); + } + +/* Check for valid status after each drain */ + status_reg_value =readl (TW_STATUS_REG_ADDR (tw_dev)); +if (twa_check_bits (status_reg_value)) { +if (twa_decode_bits (tw_dev, status_reg_value)) { +TW_CLEAR_ALL_INTERRUPTS (tw_dev); + goto twa_interrupt_bail; + } + } + } + } + } +twa_interrupt_bail: +spin_unlock_irqrestore (tw_dev->host->host_lock, flags); +clear_bit (TW_IN_INTR , &tw_dev->flags); +return IRQ_RETVAL (handled); +}/* End twa_interrupt() */ + +/* This function will load the request id and various sgls for ioctls */ +static void twa_load_sgl (TW_Command_Full *full_command_packet,int request_id,dma_addr_t dma_handle,int length) +{ +TW_Command *oldcommand; +TW_Command_Apache *newcommand; +TW_SG_Entry *sgl; + +if (full_command_packet->command.newcommand.command.opcode ==TW_OP_EXECUTE_SCSI ) { + newcommand = &full_command_packet->command.newcommand; + newcommand->request_id = request_id; + newcommand->sg_list[0 ].address = dma_handle +sizeof (TW_Ioctl_Buf_Apache) -1 ; + newcommand->sg_list[0 ].length = length; + }else { + oldcommand = &full_command_packet->command.oldcommand; + oldcommand->request_id = request_id; + +if (oldcommand->byte0_offset.sgl_offset) { +/* Load the sg list */ + sgl = (TW_SG_Entry *)((u32 *)oldcommand+oldcommand->byte0_offset.sgl_offset); + sgl->address = dma_handle +sizeof (TW_Ioctl_Buf_Apache) -1 ; + sgl->length = length; + } + } +}/* End twa_load_sgl() */ + +/* This function will perform a pci-dma mapping for a scatter gather list */ +static int twa_map_scsi_sg_data (TW_Device_Extension *tw_dev,int request_id) +{ +int use_sg; +struct scsi_cmnd *cmd = tw_dev->srb[request_id]; +struct pci_dev *pdev = tw_dev->tw_pci_dev; +int retval =0 ; + +if (cmd->use_sg ==0 )
drivers/scsi/3w-9xxx.c line 1330
Quoted 10 lines
drivers/scsi/3w-9xxx.c +10
-0+ goto out; + + use_sg =pci_map_sg (pdev, cmd->buffer, cmd->use_sg,DMA_BIDIRECTIONAL ); + +if (use_sg ==0 ) { +TW_PRINTK (tw_dev->host,TW_DRIVER ,0x1c ,"Failed to map scatter gather list" ); + goto out; + } + + cmd->SCp.phase =2 ;
Don't use magic numbers for phases, use a descriptive constant
Quoted 84 lines
drivers/scsi/3w-9xxx.c +84
-0+ cmd->SCp.have_data_in = use_sg; + retval = use_sg; +out: +return retval; +}/* End twa_map_scsi_sg_data() */ + +/* This function will perform a pci-dma map for a single buffer */ +static dma_addr_t twa_map_scsi_single_data (TW_Device_Extension *tw_dev,int request_id) +{ +dma_addr_t mapping; +struct scsi_cmnd *cmd = tw_dev->srb[request_id]; +struct pci_dev *pdev = tw_dev->tw_pci_dev; +int retval =0 ; + +if (cmd->request_bufflen ==0 ) { + retval =0 ; + goto out; + } + + mapping =pci_map_single (pdev, cmd->request_buffer, cmd->request_bufflen,DMA_BIDIRECTIONAL ); + +if (mapping ==0 ) { +TW_PRINTK (tw_dev->host,TW_DRIVER ,0x1d ,"Failed to map page" ); + goto out; + } + + cmd->SCp.phase =1 ; + cmd->SCp.have_data_in = mapping; + retval = mapping; +out: +return retval; +}/* End twa_map_scsi_single_data() */ + +/* This function will poll for a response interrupt of a request */ +static int twa_poll_response (TW_Device_Extension *tw_dev,int request_id,int seconds) +{ +int retval =1 , found =0 , response_request_id; +TW_Response_Queue response_queue; +TW_Command_Full *full_command_packet = (TW_Command_Full *)tw_dev->command_packet_virt[request_id]; + +if (twa_poll_status_gone (tw_dev,TW_STATUS_RESPONSE_QUEUE_EMPTY , seconds) ==0 ) { + response_queue.value =readl (TW_RESPONSE_QUEUE_REG_ADDR (tw_dev)); + response_request_id = (unsigned char )response_queue.u.response_id; +if (request_id != response_request_id) { +TW_PRINTK (tw_dev->host,TW_DRIVER ,0x1e ,"Found unexpected request id while polling for response" ); + goto out; + } +if (full_command_packet->command.newcommand.command.opcode ==TW_OP_EXECUTE_SCSI ) { +if (full_command_packet->command.newcommand.status !=0 ) { +/* bad response */ +twa_fill_sense (tw_dev, request_id,0 ,0 ); + goto out; + } + found =1 ; + }else { +if (full_command_packet->command.oldcommand.status !=0 ) { +/* bad response */ +twa_fill_sense (tw_dev, request_id,0 ,0 ); + goto out; + } + found =1 ; + } + } + +if (found) + retval =0 ; +out: +return retval; +}/* End twa_poll_response() */ + +/* This function will poll the status register for a flag */ +static int twa_poll_status (TW_Device_Extension *tw_dev,u32 flag,int seconds) +{ +u32 status_reg_value; +unsigned long before; +int retval =1 ; + + status_reg_value =readl (TW_STATUS_REG_ADDR (tw_dev)); + before = jiffies; + +if (twa_check_bits (status_reg_value)) +twa_decode_bits (tw_dev, status_reg_value); + +while ((status_reg_value & flag) != flag) {
drivers/scsi/3w-9xxx.c line 1424
Quoted 10 lines
drivers/scsi/3w-9xxx.c +10
-0+ status_reg_value =readl (TW_STATUS_REG_ADDR (tw_dev)); + +if (twa_check_bits (status_reg_value)) +twa_decode_bits (tw_dev, status_reg_value); + +if (time_after (jiffies, before +HZ * seconds)) + goto out; + +mdelay (5 ); + }
bzzzz. this delay loop is _completely_ unacceptable. It can hang the machine for far too long. Use a timer, or schedule_timeout() in process context.
Quoted 20 lines
drivers/scsi/3w-9xxx.c +20
-0+ retval =0 ; +out: +return retval; +}/* End twa_poll_status() */ + +/* This function will poll the status register for disappearance of a flag */ +static int twa_poll_status_gone (TW_Device_Extension *tw_dev,u32 flag,int seconds) +{ +u32 status_reg_value; +unsigned long before; +int retval =1 ; + + status_reg_value =readl (TW_STATUS_REG_ADDR (tw_dev)); + before = jiffies; + +if (twa_check_bits (status_reg_value)) +twa_decode_bits (tw_dev, status_reg_value); + +while ((status_reg_value & flag) !=0 ) { + status_reg_value =readl (TW_STATUS_REG_ADDR (tw_dev));
drivers/scsi/3w-9xxx.c line 1454
Quoted 10 lines
drivers/scsi/3w-9xxx.c +10
-0+if (twa_check_bits (status_reg_value)) +twa_decode_bits (tw_dev, status_reg_value); + +if (time_after (jiffies, before +HZ * seconds)) + goto out; + +mdelay (5 ); + } + retval =0 ; +out:
another bogus delay loop
Quoted 30 lines
drivers/scsi/3w-9xxx.c +30
-0+/* This function will attempt to post a command packet to the board */ +static int twa_post_command_packet (TW_Device_Extension *tw_dev,int request_id,char internal) +{ +u32 status_reg_value; +unsigned long command_que_value; +int retval =1 ; + + command_que_value = tw_dev->command_packet_phys[request_id]; + status_reg_value =readl (TW_STATUS_REG_ADDR (tw_dev)); + +if (twa_check_bits (status_reg_value)) +twa_decode_bits (tw_dev, status_reg_value); + +if (((tw_dev->pending_request_count >0 ) && (tw_dev->state[request_id] !=TW_S_PENDING )) || (status_reg_value &TW_STATUS_COMMAND_QUEUE_FULL )) { + +/* Only pend internal driver commands */ +if (!internal) { + retval =SCSI_MLQUEUE_HOST_BUSY ; + goto out; + } + +/* Couldn't post the command packet, so we do it later */ +if (tw_dev->state[request_id] !=TW_S_PENDING ) { + tw_dev->state[request_id] =TW_S_PENDING ; + tw_dev->pending_request_count++; +if (tw_dev->pending_request_count > tw_dev->max_pending_request_count) { + tw_dev->max_pending_request_count = tw_dev->pending_request_count; + } + tw_dev->pending_queue[tw_dev->pending_tail] = request_id; + tw_dev->pending_tail = (tw_dev->pending_tail +1 ) %TW_Q_LENGTH ;
drivers/scsi/3w-9xxx.c line 1497
Quoted 10 lines
drivers/scsi/3w-9xxx.c +10
-0+ } +TW_UNMASK_COMMAND_INTERRUPT (tw_dev); + goto out; + }else { +/* We successfully posted the command packet */ +#if BITS_PER_LONG >32 +writeq (TW_COMMAND_OFFSET + command_que_value,TW_COMMAND_QUEUE_REG_ADDR (tw_dev)); +#else +writel (TW_COMMAND_OFFSET + command_que_value,TW_COMMAND_QUEUE_REG_ADDR (tw_dev)); +#endif
PCI posting?
Quoted 192 lines
drivers/scsi/3w-9xxx.c +192
-0+ tw_dev->state[request_id] =TW_S_POSTED ; + tw_dev->posted_request_count++; +if (tw_dev->posted_request_count > tw_dev->max_posted_request_count) { + tw_dev->max_posted_request_count = tw_dev->posted_request_count; + } + } + retval =0 ; +out: +return retval; +}/* End twa_post_command_packet() */ + +/* This function will reset a device extension */ +static int twa_reset_device_extension (TW_Device_Extension *tw_dev) +{ +int i =0 ; +int retval =1 ; + +/* Abort all requests that are in progress */ +for (i =0 ; i <TW_Q_LENGTH ; i++) { +if ((tw_dev->state[i] !=TW_S_FINISHED ) && + (tw_dev->state[i] !=TW_S_INITIAL ) && + (tw_dev->state[i] !=TW_S_COMPLETED )) { +if (tw_dev->srb[i]) { + tw_dev->srb[i]->result = (DID_RESET <<16 ); + tw_dev->srb[i]->scsi_done (tw_dev->srb[i]); +twa_unmap_scsi_data (tw_dev, i); + } + } + } + +/* Reset queues and counts */ +for (i =0 ; i <TW_Q_LENGTH ; i++) { + tw_dev->free_queue[i] = i; + tw_dev->state[i] =TW_S_INITIAL ; + } + tw_dev->free_head =TW_Q_START ; + tw_dev->free_tail =TW_Q_START ; + tw_dev->posted_request_count =0 ; + tw_dev->pending_request_count =0 ; + tw_dev->pending_head =TW_Q_START ; + tw_dev->pending_tail =TW_Q_START ; + tw_dev->reset_print =0 ; + tw_dev->chrdev_request_id =TW_IOCTL_CHRDEV_FREE ; + tw_dev->flags =0 ; + +TW_DISABLE_INTERRUPTS (tw_dev); + +if (twa_reset_sequence (tw_dev,1 )) + goto out; + +TW_ENABLE_AND_CLEAR_INTERRUPTS (tw_dev); + + retval =0 ; +out: +return retval; +}/* End twa_reset_device_extension() */ + +/* This function will reset a controller */ +static int twa_reset_sequence (TW_Device_Extension *tw_dev,int soft_reset) +{ +int tries =0 , retval =1 , flashed =0 , do_soft_reset = soft_reset; + +while (tries <TW_MAX_RESET_TRIES ) { +if (do_soft_reset) +TW_SOFT_RESET (tw_dev); + +/* Make sure controller is in a good state */ +if (twa_poll_status (tw_dev,TW_STATUS_MICROCONTROLLER_READY | (do_soft_reset ==1 ?TW_STATUS_ATTENTION_INTERRUPT :0 ),30 )) { +TW_PRINTK (tw_dev->host,TW_DRIVER ,0x1f ,"Microcontroller not ready during reset sequence" ); + do_soft_reset =1 ; + tries++; +continue ; + } + +/* Empty response queue */ +if (twa_empty_response_queue (tw_dev)) { +TW_PRINTK (tw_dev->host,TW_DRIVER ,0x20 ,"Response queue empty failed during reset sequence" ); + do_soft_reset =1 ; + tries++; +continue ; + } + + flashed =0 ; + +/* Check for compatibility/flash */ +if (twa_check_srl (tw_dev, &flashed)) { +TW_PRINTK (tw_dev->host,TW_DRIVER ,0x21 ,"Compatibility check failed during reset sequence" ); + do_soft_reset =1 ; + tries++; +continue ; + }else { +if (flashed) { + tries++; +continue ; + } + } + +/* Drain the AEN queue */ +if (twa_aen_drain_queue (tw_dev, soft_reset)) { +TW_PRINTK (tw_dev->host,TW_DRIVER ,0x22 ,"AEN drain failed during reset sequence" ); + do_soft_reset =1 ; + tries++; +continue ; + } + +/* If we got here, controller is in a good state */ + retval =0 ; + goto out; + } +out: +return retval; +}/* End twa_reset_sequence() */ + +/* This funciton returns unit geometry in cylinders/heads/sectors */ +static int twa_scsi_biosparam (struct scsi_device *sdev,struct block_device *bdev,sector_t capacity,int geom[]) +{ +int heads, sectors, cylinders; +TW_Device_Extension *tw_dev; + + tw_dev = (TW_Device_Extension *)sdev->host->hostdata; + +if (capacity >=0x200000 ) { + heads =255 ; + sectors =63 ; + cylinders =sector_div (capacity, heads * sectors); + }else { + heads =64 ; + sectors =32 ; + cylinders =sector_div (capacity, heads * sectors); + } + + geom[0 ] = heads; + geom[1 ] = sectors; + geom[2 ] = cylinders; + +return 0 ; +}/* End twa_scsi_biosparam() */ + +/* This is the new scsi eh abort function */ +static int twa_scsi_eh_abort (struct scsi_cmnd *SCpnt) +{ +int i; +TW_Device_Extension *tw_dev =NULL ; +int retval =FAILED ; + + tw_dev = (TW_Device_Extension *)SCpnt->device->host->hostdata; + + tw_dev->num_aborts++; + +/* If we find any IO's in process, we have to reset the card */ +for (i =0 ; i <TW_Q_LENGTH ; i++) { +if ((tw_dev->state[i] !=TW_S_FINISHED ) && (tw_dev->state[i] !=TW_S_INITIAL )) { +printk (KERN_WARNING "3w-9xxx: scsi%d: WARNING: (0x%02X:0x%04X): Unit #%d: Command (0x%x) timed out, resetting card.\n" , tw_dev->host->host_no,TW_DRIVER ,0x2c , SCpnt->device->id, SCpnt->cmnd[0 ]); +if (twa_reset_device_extension (tw_dev)) { +TW_PRINTK (tw_dev->host,TW_DRIVER ,0x2a ,"Controller reset failed during scsi abort" ); + goto out; + } +break ; + } + } + retval =SUCCESS ; +out: +return retval; +}/* End twa_scsi_eh_abort() */ + +/* This is the new scsi eh reset function */ +static int twa_scsi_eh_reset (struct scsi_cmnd *SCpnt) +{ +TW_Device_Extension *tw_dev =NULL ; +int retval =FAILED ; + + tw_dev = (TW_Device_Extension *)SCpnt->device->host->hostdata; + + tw_dev->num_resets++; + +printk (KERN_WARNING "3w-9xxx: scsi%d: SCSI host reset started.\n" , tw_dev->host->host_no); + +/* Now reset the card and some of the device extension data */ +if (twa_reset_device_extension (tw_dev)) { +TW_PRINTK (tw_dev->host,TW_DRIVER ,0x2b ,"Controller reset failed during scsi host reset" ); + goto out; + } +printk (KERN_WARNING "3w-9xxx: scsi%d: SCSI host reset succeeded.\n" , tw_dev->host->host_no); + retval =SUCCESS ; +out: +return retval; +}/* End twa_scsi_eh_reset() */ + +/* This is the main scsi queue function to handle scsi opcodes */ +static int twa_scsi_queue (struct scsi_cmnd *SCpnt,void (*done)(struct scsi_cmnd *)) +{ +int request_id, retval;
drivers/scsi/3w-9xxx.c line 1699
Quoted 10 lines
drivers/scsi/3w-9xxx.c +10
-0+TW_Device_Extension *tw_dev = (TW_Device_Extension *)SCpnt->device->host->hostdata; + +/* Save done function into scsi_cmnd struct */ + SCpnt->scsi_done = done; + +/* Get a free request id */ +twa_get_request_id (tw_dev, &request_id); + +/* Save the scsi command for use by the ISR */ + tw_dev->srb[request_id] = SCpnt;
you need to save the 'done' hook too
Quoted 140 lines
drivers/scsi/3w-9xxx.c +140
-0+/* Initialize phase to zero */ + SCpnt->SCp.phase =0 ; + + retval =twa_scsiop_execute_scsi (tw_dev, request_id,NULL ,0 ,NULL ); +switch (retval) { +case SCSI_MLQUEUE_HOST_BUSY : +twa_free_request_id (tw_dev, request_id); +break ; +case 1 : + tw_dev->state[request_id] =TW_S_COMPLETED ; +twa_free_request_id (tw_dev, request_id); + SCpnt->result = (DID_ERROR <<16 ); +done (SCpnt); + } + +return retval; +}/* End twa_scsi_queue() */ + +/* This function hands scsi cdb's to the firmware */ +static int twa_scsiop_execute_scsi (TW_Device_Extension *tw_dev,int request_id,char *cdb,int use_sg,TW_SG_Apache *sglistarg) +{ +TW_Command_Full *full_command_packet; +TW_Command_Apache *command_packet; +u32 num_sectors =0x0 ; +int i, sg_count; +struct scsi_cmnd *srb =NULL ; +struct scatterlist *sglist =NULL ; +u32 buffaddr =0x0 ; +int retval =1 ; + +if (tw_dev->srb[request_id]) { +if (tw_dev->srb[request_id]->request_buffer) { + sglist = (struct scatterlist *)tw_dev->srb[request_id]->request_buffer; + } + srb = tw_dev->srb[request_id]; + } + +/* Initialize command packet */ + full_command_packet = (TW_Command_Full *)tw_dev->command_packet_virt[request_id]; + full_command_packet->header.header_desc.size_header =128 ; + full_command_packet->header.status_block.error =0 ; + full_command_packet->header.status_block.substatus_block.severity =0 ; + + command_packet = &full_command_packet->command.newcommand; + command_packet->status =0 ; + command_packet->command.opcode =TW_OP_EXECUTE_SCSI ; + +/* We forced 16 byte cdb use earlier */ +if (!cdb) +memcpy (command_packet->cdb, srb->cmnd,TW_MAX_CDB_LEN ); +else +memcpy (command_packet->cdb, cdb,TW_MAX_CDB_LEN ); + +if (srb) + command_packet->unit = srb->device->id; +else + command_packet->unit =0 ; + + command_packet->request_id = request_id; + command_packet->sgl_offset =16 ; + +if (!sglistarg) { +/* Map sglist from scsi layer to cmd packet */ +if (tw_dev->srb[request_id]->use_sg ==0 ) { +if (tw_dev->srb[request_id]->request_bufflen <TW_MIN_SGL_LENGTH ) { + command_packet->sg_list[0 ].address = tw_dev->generic_buffer_phys[request_id]; + command_packet->sg_list[0 ].length =TW_MIN_SGL_LENGTH ; + }else { + buffaddr =twa_map_scsi_single_data (tw_dev, request_id); +if (buffaddr ==0 ) + goto out; + + command_packet->sg_list[0 ].address = buffaddr; + command_packet->sg_list[0 ].length = tw_dev->srb[request_id]->request_bufflen; + } + command_packet->sgl_entries =1 ; + +if (command_packet->sg_list[0 ].address &TW_ALIGNMENT_9000_SGL ) { +TW_PRINTK (tw_dev->host,TW_DRIVER ,0x2d ,"Found unaligned address during execute scsi" ); + goto out; + } + } + +if (tw_dev->srb[request_id]->use_sg >0 ) { + sg_count =twa_map_scsi_sg_data (tw_dev, request_id); +if (sg_count ==0 ) + goto out; + +for (i =0 ; i < sg_count; i++) { + command_packet->sg_list[i].address =sg_dma_address (&sglist[i]); + command_packet->sg_list[i].length =sg_dma_len (&sglist[i]); +if (command_packet->sg_list[i].address &TW_ALIGNMENT_9000_SGL ) { +TW_PRINTK (tw_dev->host,TW_DRIVER ,0x2e ,"Found unaligned sgl address during execute scsi" ); + goto out; + } + } + command_packet->sgl_entries = tw_dev->srb[request_id]->use_sg; + } + }else { +/* Internal cdb post */ +for (i =0 ; i < use_sg; i++) { + command_packet->sg_list[i].address = sglistarg[i].address; + command_packet->sg_list[i].length = sglistarg[i].length; +if (command_packet->sg_list[i].address &TW_ALIGNMENT_9000_SGL ) { +TW_PRINTK (tw_dev->host,TW_DRIVER ,0x2f ,"Found unaligned sgl address during internal post" ); + goto out; + } + } + command_packet->sgl_entries = use_sg; + } + +if (srb) { +if (srb->cmnd[0 ] ==READ_6 || srb->cmnd[0 ] ==WRITE_6 ) + num_sectors = (u32 )srb->cmnd[4 ]; + +if (srb->cmnd[0 ] ==READ_10 || srb->cmnd[0 ] ==WRITE_10 ) + num_sectors = (u32 )srb->cmnd[8 ] | ((u32 )srb->cmnd[7 ] <<8 ); + } + +/* Update sector statistic */ + tw_dev->sector_count = num_sectors; +if (tw_dev->sector_count > tw_dev->max_sector_count) + tw_dev->max_sector_count = tw_dev->sector_count; + +/* Update SG statistics */ +if (srb) { + tw_dev->sgl_entries = tw_dev->srb[request_id]->use_sg; +if (tw_dev->sgl_entries > tw_dev->max_sgl_entries) + tw_dev->max_sgl_entries = tw_dev->sgl_entries; + } + +/* Now post the command to the board */ +if (srb) { + retval =twa_post_command_packet (tw_dev, request_id,0 ); + }else { +twa_post_command_packet (tw_dev, request_id,1 ); + retval =0 ; + } +out: +return retval;
drivers/scsi/3w-9xxx.c line 1850
Quoted 10 lines
drivers/scsi/3w-9xxx.c +10
-0+}/* End twa_scsiop_execute_scsi() */ + +/* This function completes an execute scsi operation */ +static void twa_scsiop_execute_scsi_complete (TW_Device_Extension *tw_dev,int request_id) +{ +/* Copy the response if too small */ +if ((tw_dev->srb[request_id]->request_buffer) && (tw_dev->srb[request_id]->request_bufflen <TW_MIN_SGL_LENGTH )) { +memcpy (tw_dev->srb[request_id]->request_buffer, tw_dev->generic_buffer_virt[request_id], tw_dev->srb[request_id]->request_bufflen); + } +}/* End twa_scsiop_execute_scsi_complete() */
this badly needs line wrapping
Greater-than-80 columns is ok, but completely invisible code off the right of the screen is annoying to review.
Quoted 5 lines
drivers/scsi/3w-9xxx.c +5
-0+ +/* This function will setup the interrupt handler */ +static int twa_setup_irq (TW_Device_Extension *tw_dev) +{ +char *device =TW_DEVICE_NAME ;
drivers/scsi/3w-9xxx.c line 1865
Quoted 10 lines
drivers/scsi/3w-9xxx.c +10
-0+int retval =1 ; + +if (request_irq (tw_dev->tw_pci_dev->irq, twa_interrupt,SA_SHIRQ , device, tw_dev) <0 ) { +TW_PRINTK (tw_dev->host,TW_DRIVER ,0x30 ,"Error requesting IRQ" ); + goto out; + } + retval =0 ; +out: +return retval; +}/* End twa_setup_irq() */
delete, and call directly
drivers/scsi/3w-9xxx.c line 1876
Quoted 10 lines
drivers/scsi/3w-9xxx.c +10
-0+/* This function tells the controller to shut down */ +static void __twa_shutdown (TW_Device_Extension *tw_dev) +{ +/* Disable interrupts */ +TW_DISABLE_INTERRUPTS (tw_dev); + +printk (KERN_WARNING "3w-9xxx: Shutting down host %d.\n" , tw_dev->host->host_no); + +/* Tell the card we are shutting down */ +if (twa_initconnection (tw_dev,1 ,0 ,0 ,0 ,0 ,0 ,NULL ,NULL ,NULL ,NULL ,NULL )) {
That's way too many arguments to the function. Create a "struct twa_initconnection_info" or similar...
Quoted 60 lines
drivers/scsi/3w-9xxx.c +60
-0+TW_PRINTK (tw_dev->host,TW_DRIVER ,0x31 ,"Connection shutdown failed" ); + }else { +printk (KERN_WARNING "3w-9xxx: Shutdown complete.\n" ); + } + +/* Clear all interrupts just before exit */ +TW_ENABLE_AND_CLEAR_INTERRUPTS (tw_dev); +}/* End __twa_shutdown() */ + +/* Wrapper for __twa_shutdown */ +static void twa_shutdown (struct device *dev) +{ +struct Scsi_Host *host =pci_get_drvdata (to_pci_dev (dev)); +TW_Device_Extension *tw_dev = (TW_Device_Extension *)host->hostdata; + +__twa_shutdown (tw_dev); +}/* End twa_shutdown() */ + +/* This function will configure individual target parameters */ +static int twa_slave_configure (struct scsi_device *SDptr) +{ +int max_cmds; + +if (cmds_per_lun) { + max_cmds = cmds_per_lun; +if (max_cmds >TW_MAX_CMDS_PER_LUN ) + max_cmds =TW_MAX_CMDS_PER_LUN ; + }else { + max_cmds =TW_MAX_CMDS_PER_LUN ; + } +scsi_adjust_queue_depth (SDptr,MSG_ORDERED_TAG , max_cmds); + +return 0 ; +}/* End twa_slave_configure */ + +/* This function will look up a string */ +static char *twa_string_lookup (twa_message_type *table,unsigned int code) +{ +int index; + +for (index =0 ; ((code != table[index].code) && + (table[index].text != (char *)0 )); index++); +return (table[index].text); +}/* End twa_string_lookup() */ + +/* This function will perform a pci-dma unmap */ +static void twa_unmap_scsi_data (TW_Device_Extension *tw_dev,int request_id) +{ +struct scsi_cmnd *cmd = tw_dev->srb[request_id]; +struct pci_dev *pdev = tw_dev->tw_pci_dev; + +switch (cmd->SCp.phase) { +case 1 : +pci_unmap_single (pdev, cmd->SCp.have_data_in, cmd->request_bufflen,DMA_BIDIRECTIONAL ); +break ; +case 2 : +pci_unmap_sg (pdev, cmd->request_buffer, cmd->use_sg,DMA_BIDIRECTIONAL ); +break ; + } +}/* End twa_unmap_scsi_data() */
drivers/scsi/3w-9xxx.c line 1946
Quoted 10 lines
drivers/scsi/3w-9xxx.c +10
-0+ +/* Get informationa bout the card/driver */ +static const char *twa_info (struct Scsi_Host *host) +{ +static char buffer[512 ]; + +sprintf (buffer,"3ware 9000 Storage Controller" ); + +return buffer; +}/* End twa_info() */
I thought the ->info hook was deprecated?
Quoted 22 lines
drivers/scsi/3w-9xxx.c +22
-0+/* scsi_host_template initializer */ +static struct scsi_host_template driver_template = { + .module =THIS_MODULE , + .name ="3ware 9000 Storage Controller" , + .info = twa_info, + .queuecommand = twa_scsi_queue, + .eh_abort_handler = twa_scsi_eh_abort, + .eh_host_reset_handler = twa_scsi_eh_reset, + .bios_param = twa_scsi_biosparam, + .slave_configure = twa_slave_configure, + .can_queue =TW_Q_LENGTH -2 , + .this_id =-1 , + .sg_tablesize =TW_APACHE_MAX_SGL_LENGTH , + .max_sectors =TW_MAX_SECTORS , + .cmd_per_lun =TW_MAX_CMDS_PER_LUN , + .use_clustering =ENABLE_CLUSTERING , + .shost_attrs = twa_host_attrs, + .emulated =1 +}; + +/* This function will probe and initialize a card */ +static int __devinit twa_probe (struct pci_dev *pdev,const struct pci_device_id *dev_id)
drivers/scsi/3w-9xxx.c line 1979
Quoted 10 lines
drivers/scsi/3w-9xxx.c +10
-0+{ +struct Scsi_Host *host =NULL ; +TW_Device_Extension *tw_dev; +u32 mem_addr, mem_len; +int retval = -ENODEV ; + +if (pci_enable_device (pdev)) { +TW_PRINTK (host,TW_DRIVER ,0x32 ,"Failed to enable pci device" ); + goto out; + }
propagate pci_enable_device return value to caller
drivers/scsi/3w-9xxx.c line 1986
Quoted 6 lines
drivers/scsi/3w-9xxx.c +6
-0+pci_set_master (pdev); + +if (pci_set_dma_mask (pdev,TW_DMA_MASK )) { +TW_PRINTK (host,TW_DRIVER ,0x23 ,"Failed to set dma mask" ); + goto out; + }
propagate pci_set_dma_mask return value to caller
Quoted 20 lines
drivers/scsi/3w-9xxx.c +20
-0+ host =scsi_host_alloc (&driver_template,sizeof (TW_Device_Extension)); +if (!host) { +TW_PRINTK (host,TW_DRIVER ,0x24 ,"Failed to allocate memory for device extension" ); + retval = -ENOMEM ; + goto out; + } + tw_dev = (TW_Device_Extension *)host->hostdata; + +memset (tw_dev,0 ,sizeof (TW_Device_Extension)); + +/* Save values to device extension */ + tw_dev->host = host; + tw_dev->tw_pci_dev = pdev; + +if (twa_initialize_device_extension (tw_dev)) { +TW_PRINTK (tw_dev->host,TW_DRIVER ,0x25 ,"Failed to initialize device extension" ); + goto out_free_device_extension; + } + + mem_addr =pci_resource_start (pdev,1 );
drivers/scsi/3w-9xxx.c line 2017
Quoted 10 lines
drivers/scsi/3w-9xxx.c +10
-0+ mem_len =pci_resource_len (pdev,1 ); + +/* Make sure that mem region isn't already taken */ +if (check_mem_region (mem_addr, mem_len)) { +TW_PRINTK (tw_dev->host,TW_DRIVER ,0x26 ,"Failed to get mem region" ); + goto out_free_device_extension; + } + +/* Reserve the mem address space */ +request_mem_region (mem_addr, mem_len,TW_DEVICE_NAME );
bogus. First, check_mem_region is racy.
Moreover, you should be using pci_request_regions() and pci_release_regions(), not request_mem_region.
drivers/scsi/3w-9xxx.c line 2020
Quoted 2 lines
drivers/scsi/3w-9xxx.c +2
-0+/* Save base address */ + tw_dev->base_addr =ioremap (mem_addr,PAGE_SIZE );
check for NULL return
drivers/scsi/3w-9xxx.c line 2023
Quoted 2 lines
drivers/scsi/3w-9xxx.c +2
-0+/* Disable interrupts on the card */ +TW_DISABLE_INTERRUPTS (tw_dev);
don't you think you enable busmastering _after_ you do this?
drivers/scsi/3w-9xxx.c line 2033
Quoted 9 lines
drivers/scsi/3w-9xxx.c +9
-0+/* Initialize the card */ +if (twa_reset_sequence (tw_dev,0 )) + goto out_release_mem_region; + +/* Set host specific parameters */ + host->max_id =TW_MAX_UNITS ; + host->max_cmd_len =TW_MAX_CDB_LEN ; + host->max_lun =0 ; + host->max_channel =0 ;
max_lun and max_channel should be greater than zero, right?
Quoted 25 lines
drivers/scsi/3w-9xxx.c +25
-0+/* Register the card with the kernel SCSI layer */ + retval =scsi_add_host (host, &pdev->dev); +if (retval) { +TW_PRINTK (tw_dev->host,TW_DRIVER ,0x27 ,"scsi add host failed" ); + goto out_release_mem_region; + } + +pci_set_drvdata (pdev, host); + +printk (KERN_WARNING "3w-9xxx: scsi%d: Found a 3ware 9000 Storage Controller at 0x%x, IRQ: %d.\n" , host->host_no, mem_addr, pdev->irq); +printk (KERN_WARNING "3w-9xxx: scsi%d: Firmware %s, BIOS %s, Ports: %d.\n" , host->host_no, (char *)twa_get_param (tw_dev,0 ,TW_VERSION_TABLE ,TW_PARAM_FWVER ,TW_PARAM_FWVER_LENGTH ), (char *)twa_get_param (tw_dev,1 ,TW_VERSION_TABLE ,TW_PARAM_BIOSVER ,TW_PARAM_BIOSVER_LENGTH ), *(int *)twa_get_param (tw_dev,2 ,TW_INFORMATION_TABLE ,TW_PARAM_PORTCOUNT ,TW_PARAM_PORTCOUNT_LENGTH )); + +/* Now setup the interrupt handler */ +if (twa_setup_irq (tw_dev)) + goto out_remove_host; + + twa_device_extension_list[twa_device_extension_count] = tw_dev; + twa_device_extension_count++; + +/* Re-enable interrupts on the card */ +TW_ENABLE_AND_CLEAR_INTERRUPTS (tw_dev); + +/* Finally, scan the host */ +scsi_scan_host (host); +
drivers/scsi/3w-9xxx.c line 2069
Quoted 10 lines
drivers/scsi/3w-9xxx.c +10
-0+if (twa_major ==-1 ) { +if ((twa_major =register_chrdev (0 ,"twa" , &twa_fops)) <0 ) +TW_PRINTK (host,TW_DRIVER ,0x29 ,"Failed to register character device" ); + } +return 0 ; + +out_remove_host: +scsi_remove_host (host); +out_release_mem_region: +release_mem_region (mem_addr, mem_len);
pci_release_regions
drivers/scsi/3w-9xxx.c line 2073
Quoted 4 lines
drivers/scsi/3w-9xxx.c +4
-0+out_free_device_extension: +twa_free_device_extension (tw_dev); +scsi_host_put (host); +out:
missing pci_disable_device, complementary to pci_enable_device
Quoted 15 lines
drivers/scsi/3w-9xxx.c +15
-0+return retval; +}/* End twa_probe() */ + +/* This function is called to remove a device */ +static void twa_remove (struct pci_dev *pdev) +{ +struct Scsi_Host *host =pci_get_drvdata (pdev); +TW_Device_Extension *tw_dev = (TW_Device_Extension *)host->hostdata; + +scsi_remove_host (tw_dev->host); + +__twa_shutdown (tw_dev); + +/* Free up the IRQ */ +free_irq (tw_dev->tw_pci_dev->irq, tw_dev);
drivers/scsi/3w-9xxx.c line 2098
Quoted 10 lines
drivers/scsi/3w-9xxx.c +10
-0+ +/* Free up the mem region */ +release_mem_region (pci_resource_start (tw_dev->tw_pci_dev,1 ),pci_resource_len (tw_dev->tw_pci_dev,1 )); + +/* Free up device extension resources */ +twa_free_device_extension (tw_dev); + +scsi_host_put (tw_dev->host); +pci_disable_device (pdev); + twa_device_extension_count--;
BUG -- if you register the chrdev in PCI ->probe, you must unregister it here.
Quoted 20 lines
drivers/scsi/3w-9xxx.c +20
-0+}/* End twa_remove() */ + +/* PCI Devices supported by this driver */ +static struct pci_device_id twa_pci_tbl[]__devinitdata = { + {PCI_VENDOR_ID_3WARE ,PCI_DEVICE_ID_3WARE_9000 , +PCI_ANY_ID ,PCI_ANY_ID ,0 ,0 ,0 }, + { } +}; +MODULE_DEVICE_TABLE (pci, twa_pci_tbl); + +/* pci_driver initializer */ +static struct pci_driver twa_driver = { + .name ="3w-9xxx" , + .id_table = twa_pci_tbl, + .probe = twa_probe, + .remove = twa_remove, + .driver = { + .shutdown = twa_shutdown + } +};
drivers/scsi/3w-9xxx.c line 2128
Quoted 10 lines
drivers/scsi/3w-9xxx.c +10
-0+ +/* This function is called on driver initialization */ +static int __init twa_init (void ) +{ +printk (KERN_WARNING "3ware 9000 Storage Controller device driver for Linux v%s.\n" , twa_driver_version); + +pci_register_driver (&twa_driver); + +return 0 ; +}/* End twa_init() */
wrong. do
return pci_module_init(&twa_driver)
drivers/scsi/3w-9xxx.c line 2137
Quoted 8 lines
drivers/scsi/3w-9xxx.c +8
-0+/* This function is called on driver exit */ +static void __exit twa_exit(void) +{ +/* Unregister character device */ +if (twa_major >=0 ) { +unregister_chrdev (twa_major,"twa" ); + twa_major =-1 ; + }
see above
Quoted 5 lines
drivers/scsi/3w-9xxx.c +5
-0+pci_unregister_driver (&twa_driver); +}/* End twa_exit() */ + +module_init (twa_init); +module_exit (twa_exit);