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