/*****************************************************************************/ /* ips.c -- driver for the IBM ServeRAID controller */ /* */ /* Written By: Keith Mitchell, IBM Corporation */ /* */ /* Copyright (C) 1999 IBM 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; either version 2 of the License, or */ /* (at your option) any later version. */ /* */ /* 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: */ /* ipslinux@us.ibm.com */ /* */ /*****************************************************************************/ /*****************************************************************************/ /* Change Log */ /* */ /* 0.99.02 - Breakup commands that are bigger than 8 * the stripe size */ /* 0.99.03 - Make interrupt routine handle all completed request on the */ /* adapter not just the first one */ /* - Make sure passthru commands get woken up if we run out of */ /* SCBs */ /* - Send all of the commands on the queue at once rather than */ /* one at a time since the card will support it. */ /* 0.99.04 - Fix race condition in the passthru mechanism -- this required */ /* the interface to the utilities to change */ /* - Fix error recovery code */ /* 0.99.05 - Fix an oops when we get certain passthru commands */ /* 1.00.00 - Initial Public Release */ /* Functionally equivalent to 0.99.05 */ /* 3.60.00 - Bump max commands to 128 for use with ServeRAID firmware 3.60 */ /* - Change version to 3.60 to coincide with ServeRAID release */ /* numbering. */ /* 3.60.01 - Remove bogus error check in passthru routine */ /* 3.60.02 - Make DCDB direction based on lookup table */ /* - Only allow one DCDB command to a SCSI ID at a time */ /* 4.00.00 - Add support for ServeRAID 4 */ /* 4.00.01 - Add support for First Failure Data Capture */ /* 4.00.02 - Fix problem with PT DCDB with no buffer */ /* 4.00.03 - Add alternative passthru interface */ /* - Add ability to flash ServeRAID BIOS */ /* 4.00.04 - Rename structures/constants to be prefixed with IPS_ */ /* 4.00.05 - Remove wish_block from init routine */ /* - Use linux/spinlock.h instead of asm/spinlock.h for kernels */ /* 2.3.18 and later */ /* - Sync with other changes from the 2.3 kernels */ /* 4.00.06 - Fix timeout with initial FFDC command */ /* 4.00.06a - Port to 2.4 (trivial) -- Christoph Hellwig */ /* 4.10.00 - Add support for ServeRAID 4M/4L */ /* 4.10.13 - Fix for dynamic unload and proc file system */ /* 4.20.03 - Rename version to coincide with new release schedules */ /* Performance fixes */ /* Fix truncation of /proc files with cat */ /* Merge in changes through kernel 2.4.0test1ac21 */ /* 4.20.13 - Fix some failure cases / reset code */ /* - Hook into the reboot_notifier to flush the controller cache */ /* 4.50.01 - Fix problem when there is a whole in logical drive numbering */ /* 4.70.09 - Use a Common ( Large Buffer ) for Flashing from the JCRM CD */ /* - Add IPSSEND Flash Support */ /* - Set Sense Data for Unknown SCSI Command */ /* - Use Slot Number from NVRAM Page 5 */ /* - Restore caller's DCDB Structure */ /* 4.70.12 - Corrective actions for bad controller ( during initialization )*/ /* 4.70.13 - Don't Send CDB's if we already know the device is not present */ /* - Don't release HA Lock in ips_next() until SC taken off queue */ /* - Unregister SCSI device in ips_release() */ /* 4.70.15 - Fix Breakup for very large ( non-SG ) requests in ips_done() */ /* 4.71.00 - Change all memory allocations to not use GFP_DMA flag */ /* Code Clean-Up for 2.4.x kernel */ /* 4.72.00 - Allow for a Scatter-Gather Element to exceed MAX_XFER Size */ /* */ /*****************************************************************************/ /* * Conditional Compilation directives for this driver: * * IPS_DEBUG - Turn on debugging info * * * Parameters: * * debug: - Set debug level to * NOTE: only works when IPS_DEBUG compile directive * is used. * * 1 - Normal debug messages * 2 - Verbose debug messages * 11 - Method trace (non interrupt) * 12 - Method trace (includes interrupt) * * noreset - Don't reset the controller * nocmdline - Turn off passthru support * noi2o - Don't use I2O Queues (ServeRAID 4 only) * nommap - Don't use memory mapped I/O * ioctlsize - Initial size of the IOCTL buffer */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifndef NO_IPS_CMDLINE #include #endif #include "sd.h" #include "scsi.h" #include "hosts.h" #include "ips.h" #include #include #include #if LINUX_VERSION_CODE >= LinuxVersionCode(2,3,18) #include #else #include #endif #if LINUX_VERSION_CODE >= LinuxVersionCode(2,3,13) #include #endif #include #ifdef MODULE static char *ips = NULL; MODULE_PARM(ips, "s"); #endif /* * DRIVER_VER */ #define IPS_VERSION_HIGH "$$MAJORVERSION$$.$$MINORVERSION$$" #define IPS_VERSION_LOW ".$$BUILDNUMBER$$ " #if LINUX_VERSION_CODE < LinuxVersionCode(2,3,27) struct proc_dir_entry proc_scsi_ips = { 0, 3, "ips", S_IFDIR | S_IRUGO | S_IXUGO, 2 }; #endif #if !defined(__i386__) #error "This driver has only been tested on the x86 platform" #endif #if LINUX_VERSION_CODE < LinuxVersionCode(2,2,0) #error "This driver only works with kernel 2.2.0 and later" #endif #if !defined(NO_IPS_CMDLINE) && ((SG_BIG_BUFF < 8192) || !defined(SG_BIG_BUFF)) #error "To use the command-line interface you need to define SG_BIG_BUFF" #endif #ifdef IPS_DEBUG #define METHOD_TRACE(s, i) if (ips_debug >= (i+10)) printk(KERN_NOTICE s "\n"); #define DEBUG(i, s) if (ips_debug >= i) printk(KERN_NOTICE s "\n"); #define DEBUG_VAR(i, s, v...) if (ips_debug >= i) printk(KERN_NOTICE s "\n", v); #else #define METHOD_TRACE(s, i) #define DEBUG(i, s) #define DEBUG_VAR(i, s, v...) #endif /* * global variables */ static const char * ips_name = "ips"; static struct Scsi_Host * ips_sh[IPS_MAX_ADAPTERS]; /* Array of host controller structures */ static ips_ha_t * ips_ha[IPS_MAX_ADAPTERS]; /* Array of HA structures */ static unsigned int ips_next_controller = 0; static unsigned int ips_num_controllers = 0; static unsigned int ips_released_controllers = 0; static int ips_cmd_timeout = 60; static int ips_reset_timeout = 60 * 5; static int ips_force_memio = 1; /* Always use Memory Mapped I/O */ static int ips_force_i2o = 1; /* Always use I2O command delivery */ static int ips_resetcontroller = 1; /* Reset the controller */ static int ips_cmdline = 1; /* Support for passthru */ static int ips_ioctlsize = IPS_IOCTL_SIZE; /* Size of the ioctl buffer */ static int ips_cd_boot = 0; /* Booting from ServeRAID Manager CD */ static char *ips_FlashData = NULL; /* CD Boot - Flash Data Buffer */ static int ips_FlashDataInUse = 0; /* CD Boot - Flash Data In Use Flag */ #ifdef IPS_DEBUG static int ips_debug = 0; /* Debug mode */ #endif /* * Necessary forward function protoypes */ static int ips_halt(struct notifier_block *nb, ulong event, void *buf); #define MAX_ADAPTER_NAME 11 static char ips_adapter_name[][30] = { "ServeRAID", "ServeRAID II", "ServeRAID on motherboard", "ServeRAID on motherboard", "ServeRAID 3H", "ServeRAID 3L", "ServeRAID 4H", "ServeRAID 4M", "ServeRAID 4L", "ServeRAID 4Mx", "ServeRAID 4Lx" }; static struct notifier_block ips_notifier = { ips_halt, NULL, 0 }; /* * Direction table */ static char ips_command_direction[] = { IPS_DATA_NONE, IPS_DATA_NONE, IPS_DATA_IN, IPS_DATA_IN, IPS_DATA_OUT, IPS_DATA_IN, IPS_DATA_IN, IPS_DATA_OUT, IPS_DATA_IN, IPS_DATA_UNK, IPS_DATA_OUT, IPS_DATA_OUT, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_IN, IPS_DATA_NONE, IPS_DATA_IN, IPS_DATA_IN, IPS_DATA_OUT, IPS_DATA_IN, IPS_DATA_OUT, IPS_DATA_NONE, IPS_DATA_NONE, IPS_DATA_OUT, IPS_DATA_NONE, IPS_DATA_IN, IPS_DATA_NONE, IPS_DATA_IN, IPS_DATA_OUT, IPS_DATA_NONE, IPS_DATA_UNK, IPS_DATA_IN, IPS_DATA_UNK, IPS_DATA_IN, IPS_DATA_UNK, IPS_DATA_OUT, IPS_DATA_IN, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_IN, IPS_DATA_IN, IPS_DATA_OUT, IPS_DATA_NONE, IPS_DATA_UNK, IPS_DATA_IN, IPS_DATA_OUT, IPS_DATA_OUT, IPS_DATA_OUT, IPS_DATA_OUT, IPS_DATA_OUT, IPS_DATA_NONE, IPS_DATA_IN, IPS_DATA_NONE, IPS_DATA_NONE, IPS_DATA_IN, IPS_DATA_OUT, IPS_DATA_OUT, IPS_DATA_OUT, IPS_DATA_OUT, IPS_DATA_IN, IPS_DATA_OUT, IPS_DATA_IN, IPS_DATA_OUT, IPS_DATA_OUT, IPS_DATA_OUT, IPS_DATA_IN, IPS_DATA_IN, IPS_DATA_IN, IPS_DATA_NONE, IPS_DATA_UNK, IPS_DATA_NONE, IPS_DATA_NONE, IPS_DATA_NONE, IPS_DATA_UNK, IPS_DATA_NONE, IPS_DATA_OUT, IPS_DATA_IN, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_OUT, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_IN, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_NONE, IPS_DATA_NONE, IPS_DATA_UNK, IPS_DATA_IN, IPS_DATA_NONE, IPS_DATA_OUT, IPS_DATA_UNK, IPS_DATA_NONE, IPS_DATA_UNK, IPS_DATA_OUT, IPS_DATA_OUT, IPS_DATA_OUT, IPS_DATA_OUT, IPS_DATA_OUT, IPS_DATA_NONE, IPS_DATA_UNK, IPS_DATA_IN, IPS_DATA_OUT, IPS_DATA_IN, IPS_DATA_IN, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_OUT, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK }; /* * Function prototypes */ int ips_detect(Scsi_Host_Template *); int ips_release(struct Scsi_Host *); int ips_eh_abort(Scsi_Cmnd *); int ips_eh_reset(Scsi_Cmnd *); int ips_queue(Scsi_Cmnd *, void (*) (Scsi_Cmnd *)); int ips_biosparam(Disk *, kdev_t, int *); const char * ips_info(struct Scsi_Host *); void do_ipsintr(int, void *, struct pt_regs *); static int ips_hainit(ips_ha_t *); static int ips_map_status(ips_ha_t *, ips_scb_t *, ips_stat_t *); static int ips_send(ips_ha_t *, ips_scb_t *, ips_scb_callback); static int ips_send_wait(ips_ha_t *, ips_scb_t *, int, int); static int ips_send_cmd(ips_ha_t *, ips_scb_t *); static int ips_online(ips_ha_t *, ips_scb_t *); static int ips_inquiry(ips_ha_t *, ips_scb_t *); static int ips_rdcap(ips_ha_t *, ips_scb_t *); static int ips_msense(ips_ha_t *, ips_scb_t *); static int ips_reqsen(ips_ha_t *, ips_scb_t *); static int ips_allocatescbs(ips_ha_t *); static int ips_reset_copperhead(ips_ha_t *); static int ips_reset_copperhead_memio(ips_ha_t *); static int ips_reset_morpheus(ips_ha_t *); static int ips_issue_copperhead(ips_ha_t *, ips_scb_t *); static int ips_issue_copperhead_memio(ips_ha_t *, ips_scb_t *); static int ips_issue_i2o(ips_ha_t *, ips_scb_t *); static int ips_issue_i2o_memio(ips_ha_t *, ips_scb_t *); static int ips_isintr_copperhead(ips_ha_t *); static int ips_isintr_copperhead_memio(ips_ha_t *); static int ips_isintr_morpheus(ips_ha_t *); static int ips_wait(ips_ha_t *, int, int); static int ips_write_driver_status(ips_ha_t *, int); static int ips_read_adapter_status(ips_ha_t *, int); static int ips_read_subsystem_parameters(ips_ha_t *, int); static int ips_read_config(ips_ha_t *, int); static int ips_clear_adapter(ips_ha_t *, int); static int ips_readwrite_page5(ips_ha_t *, int, int); static int ips_init_copperhead(ips_ha_t *); static int ips_init_copperhead_memio(ips_ha_t *); static int ips_init_morpheus(ips_ha_t *); static int ips_isinit_copperhead(ips_ha_t *); static int ips_isinit_copperhead_memio(ips_ha_t *); static int ips_isinit_morpheus(ips_ha_t *); static u32 ips_statupd_copperhead(ips_ha_t *); static u32 ips_statupd_copperhead_memio(ips_ha_t *); static u32 ips_statupd_morpheus(ips_ha_t *); static void ips_flash_bios_section(void *); static void ips_flash_bios_segment(void *); static void ips_scheduled_flash_bios(void *); static void ips_create_nvrampage5(ips_ha_t *, IPS_NVRAM_P5 *); static void ips_get_bios_version(ips_ha_t *, int); static void ips_identify_controller(ips_ha_t *); static void ips_select_queue_depth(struct Scsi_Host *, Scsi_Device *); static void ips_chkstatus(ips_ha_t *, IPS_STATUS *); static void ips_enable_int_copperhead(ips_ha_t *); static void ips_enable_int_copperhead_memio(ips_ha_t *); static void ips_enable_int_morpheus(ips_ha_t *); static void ips_intr_copperhead(ips_ha_t *); static void ips_intr_morpheus(ips_ha_t *); static void ips_next(ips_ha_t *, int); static void ipsintr_blocking(ips_ha_t *, struct ips_scb *); static void ipsintr_done(ips_ha_t *, struct ips_scb *); static void ips_done(ips_ha_t *, ips_scb_t *); static void ips_free(ips_ha_t *); static void ips_init_scb(ips_ha_t *, ips_scb_t *); static void ips_freescb(ips_ha_t *, ips_scb_t *); static void ips_statinit(ips_ha_t *); static void ips_statinit_memio(ips_ha_t *); static void ips_fix_ffdc_time(ips_ha_t *, ips_scb_t *, time_t); static void ips_ffdc_reset(ips_ha_t *, int); static void ips_ffdc_time(ips_ha_t *, int); static ips_scb_t * ips_getscb(ips_ha_t *); static inline void ips_putq_scb_head(ips_scb_queue_t *, ips_scb_t *); static inline void ips_putq_scb_tail(ips_scb_queue_t *, ips_scb_t *); static inline ips_scb_t * ips_removeq_scb_head(ips_scb_queue_t *); static inline ips_scb_t * ips_removeq_scb(ips_scb_queue_t *, ips_scb_t *); static inline void ips_putq_wait_head(ips_wait_queue_t *, Scsi_Cmnd *); static inline void ips_putq_wait_tail(ips_wait_queue_t *, Scsi_Cmnd *); static inline Scsi_Cmnd * ips_removeq_wait_head(ips_wait_queue_t *); static inline Scsi_Cmnd * ips_removeq_wait(ips_wait_queue_t *, Scsi_Cmnd *); static inline void ips_putq_copp_head(ips_copp_queue_t *, ips_copp_wait_item_t *); static inline void ips_putq_copp_tail(ips_copp_queue_t *, ips_copp_wait_item_t *); static inline ips_copp_wait_item_t * ips_removeq_copp(ips_copp_queue_t *, ips_copp_wait_item_t *); static inline ips_copp_wait_item_t * ips_removeq_copp_head(ips_copp_queue_t *); static int ips_erase_bios(ips_ha_t *); static int ips_program_bios(ips_ha_t *, char *, u32, u32); static int ips_verify_bios(ips_ha_t *, char *, u32, u32); static int ips_erase_bios_memio(ips_ha_t *); static int ips_program_bios_memio(ips_ha_t *, char *, u32, u32); static int ips_verify_bios_memio(ips_ha_t *, char *, u32, u32); #ifndef NO_IPS_CMDLINE static int ips_is_passthru(Scsi_Cmnd *); static int ips_make_passthru(ips_ha_t *, Scsi_Cmnd *, ips_scb_t *, int); static int ips_usrcmd(ips_ha_t *, ips_passthru_t *, ips_scb_t *); static int ips_newusrcmd(ips_ha_t *, ips_passthru_t *, ips_scb_t *); static void ips_cleanup_passthru(ips_ha_t *, ips_scb_t *); #endif int ips_proc_info(char *, char **, off_t, int, int, int); static int ips_host_info(ips_ha_t *, char *, off_t, int); static void copy_mem_info(IPS_INFOSTR *, char *, int); static int copy_info(IPS_INFOSTR *, char *, ...); /*--------------------------------------------------------------------------*/ /* Exported Functions */ /*--------------------------------------------------------------------------*/ /****************************************************************************/ /* */ /* Routine Name: ips_setup */ /* */ /* Routine Description: */ /* */ /* setup parameters to the driver */ /* */ /****************************************************************************/ #if LINUX_VERSION_CODE >= LinuxVersionCode(2,3,13) static int ips_setup(char *ips_str) { #else void ips_setup(char *ips_str, int *dummy) { #endif int i; char *p; char *key; char *value; char tokens[3] = {',', '.', 0}; IPS_OPTION options[] = { {"noreset", &ips_resetcontroller, 0}, #ifdef IPS_DEBUG {"debug", &ips_debug, 1}, #endif {"noi2o", &ips_force_i2o, 0}, {"nommap", &ips_force_memio, 0}, {"nocmdline", &ips_cmdline, 0}, {"ioctlsize", &ips_ioctlsize, IPS_IOCTL_SIZE}, {"cdboot", &ips_cd_boot, 0}, }; METHOD_TRACE("ips_setup", 1); for (key = strtok(ips_str, tokens); key; key = strtok(NULL, tokens)) { p = key; /* Search for value */ while ((p) && (*p != ':')) p++; if (p) { *p = '\0'; value = p+1; } else value = NULL; /* * We now have key/value pairs. * Update the variables */ for (i = 0; i < (sizeof(options) / sizeof(options[0])); i++) { if (strnicmp(key, options[i].option_name, strlen(ips_str)) == 0) { if (value) *options[i].option_flag = simple_strtoul(value, NULL, 0); else *options[i].option_flag = options[i].option_value; break; } } } #if LINUX_VERSION_CODE >= LinuxVersionCode(2,3,13) return (1); #endif } #if LINUX_VERSION_CODE >= LinuxVersionCode(2,3,13) __setup("ips=", ips_setup); #endif /****************************************************************************/ /* */ /* Routine Name: ips_detect */ /* */ /* Routine Description: */ /* */ /* Detect and initialize the driver */ /* */ /* NOTE: this routine is called under the io_request_lock spinlock */ /* */ /****************************************************************************/ int ips_detect(Scsi_Host_Template *SHT) { struct Scsi_Host *sh; ips_ha_t *ha; u32 io_addr; u32 mem_addr; u32 io_len; u32 mem_len; u16 planer; u8 revision_id; u8 bus; u8 func; u8 irq; u16 deviceID[2]; u16 subdevice_id; int i; int j; u32 count; char *ioremap_ptr; char *mem_ptr; struct pci_dev *dev[2]; struct pci_dev *morpheus = NULL; struct pci_dev *trombone = NULL; #if LINUX_VERSION_CODE < LinuxVersionCode(2,3,14) u32 currbar; u32 maskbar; u8 barnum; #endif METHOD_TRACE("ips_detect", 1); #ifdef MODULE if (ips) #if LINUX_VERSION_CODE >= LinuxVersionCode(2,3,13) ips_setup(ips); #else ips_setup(ips, NULL); #endif #endif /* If Booting from the ServeRAID Manager CD, Allocate a large Flash */ /* Buffer ( so we won't need to allocate one for each adapter ). */ if ( ips_cd_boot ) { ips_FlashData = ( char * ) __get_free_pages( GFP_KERNEL, 7 ); if (ips_FlashData == NULL) { /* The validity of this pointer is checked in ips_make_passthru() before it is used */ printk( KERN_WARNING "ERROR: Can't Allocate Large Buffer for Flashing\n" ); } } SHT->proc_info = ips_proc_info; #if LINUX_VERSION_CODE < LinuxVersionCode(2,3,27) SHT->proc_dir = &proc_scsi_ips; #else SHT->proc_name = "ips"; #endif #if defined(CONFIG_PCI) /* initalize number of controllers */ ips_num_controllers = 0; ips_next_controller = 0; ips_released_controllers = 0; if (!pci_present()) return (0); morpheus = pci_find_device(IPS_VENDORID, IPS_DEVICEID_MORPHEUS, morpheus); trombone = pci_find_device(IPS_VENDORID, IPS_DEVICEID_COPPERHEAD, trombone); /* determine which controller to probe first */ if (!morpheus) { /* we only have trombone */ dev[0] = trombone; dev[1] = NULL; deviceID[0] = IPS_DEVICEID_COPPERHEAD; } else if (!trombone) { /* we only have morpheus */ dev[0] = morpheus; dev[1] = NULL; deviceID[0] = IPS_DEVICEID_MORPHEUS; } else { /* we have both in the system */ if (trombone->bus < morpheus->bus) { dev[0] = trombone; dev[1] = morpheus; deviceID[0] = IPS_DEVICEID_COPPERHEAD; deviceID[1] = IPS_DEVICEID_MORPHEUS; } else if (trombone->bus > morpheus->bus) { dev[0] = morpheus; dev[1] = trombone; deviceID[0] = IPS_DEVICEID_MORPHEUS; deviceID[1] = IPS_DEVICEID_COPPERHEAD; } else { /* further detection required */ if (trombone->devfn < morpheus->devfn) { dev[0] = trombone; dev[1] = morpheus; deviceID[0] = IPS_DEVICEID_COPPERHEAD; deviceID[1] = IPS_DEVICEID_MORPHEUS; } else { dev[0] = morpheus; dev[1] = trombone; deviceID[0] = IPS_DEVICEID_MORPHEUS; deviceID[1] = IPS_DEVICEID_COPPERHEAD; } } } /* Now scan the controllers */ for (i = 0; i < 2; i++) { if (!dev[i]) break; do { if (ips_next_controller >= IPS_MAX_ADAPTERS) break; #if LINUX_VERSION_CODE >= LinuxVersionCode(2,4,0) if (pci_enable_device(dev[i])) break; #endif /* stuff that we get in dev */ irq = dev[i]->irq; bus = dev[i]->bus->number; func = dev[i]->devfn; /* Init MEM/IO addresses to 0 */ mem_addr = 0; io_addr = 0; mem_len = 0; io_len = 0; for (j = 0; j < 2; j++) { #if LINUX_VERSION_CODE >= LinuxVersionCode(2,4,0) if (!pci_resource_start(dev[i], j)) break; if (pci_resource_flags(dev[i], j) & IORESOURCE_IO) { io_addr = pci_resource_start(dev[i], j); io_len = pci_resource_len(dev[i], j); } else { mem_addr = pci_resource_start(dev[i], j); mem_len = pci_resource_len(dev[i], j); } #elif LINUX_VERSION_CODE >= LinuxVersionCode(2,3,14) if (!dev[i]->resource[j].start) break; if ((dev[i]->resource[j].start & PCI_BASE_ADDRESS_SPACE) == PCI_BASE_ADDRESS_SPACE_IO) { io_addr = dev[i]->resource[j].start; io_len = dev[i]->resource[j].end - dev[i]->resource[j].start + 1; } else { mem_addr = dev[i]->resource[j].start; mem_len = dev[i]->resource[j].end - dev[i]->resource[j].start + 1; } #else if (!dev[i]->base_address[j]) break; if ((dev[i]->base_address[j] & PCI_BASE_ADDRESS_SPACE) == PCI_BASE_ADDRESS_SPACE_IO) { barnum = PCI_BASE_ADDRESS_0 + (j * 4); io_addr = dev[i]->base_address[j] & PCI_BASE_ADDRESS_IO_MASK; /* Get Size */ pci_read_config_dword(dev[i], barnum, &currbar); pci_write_config_dword(dev[i], barnum, ~0); pci_read_config_dword(dev[i], barnum, &maskbar); pci_write_config_dword(dev[i], barnum, currbar); io_len = ~(maskbar & PCI_BASE_ADDRESS_IO_MASK) + 1; } else { barnum = PCI_BASE_ADDRESS_0 + (j * 4); mem_addr = dev[i]->base_address[j] & PCI_BASE_ADDRESS_MEM_MASK; /* Get Size */ pci_read_config_dword(dev[i], barnum, &currbar); pci_write_config_dword(dev[i], barnum, ~0); pci_read_config_dword(dev[i], barnum, &maskbar); pci_write_config_dword(dev[i], barnum, currbar); mem_len = ~(maskbar & PCI_BASE_ADDRESS_MEM_MASK) + 1; } #endif } /* setup memory mapped area (if applicable) */ if (mem_addr) { u32 base; u32 offs; DEBUG_VAR(1, "(%s%d) detect, Memory region %x, size: %d", ips_name, ips_next_controller, mem_addr, mem_len); #if LINUX_VERSION_CODE >= LinuxVersionCode(2,3,17) if (check_mem_region(mem_addr, mem_len)) { /* Couldn't allocate io space */ printk(KERN_WARNING "(%s%d) couldn't allocate IO space %x len %d.\n", ips_name, ips_next_controller, io_addr, io_len); ips_next_controller++; continue; } request_mem_region(mem_addr, mem_len, "ips"); #endif base = mem_addr & PAGE_MASK; offs = mem_addr - base; ioremap_ptr = ioremap(base, PAGE_SIZE); mem_ptr = ioremap_ptr + offs; } else { ioremap_ptr = NULL; mem_ptr = NULL; } /* setup I/O mapped area (if applicable) */ if (io_addr) { DEBUG_VAR(1, "(%s%d) detect, IO region %x, size: %d", ips_name, ips_next_controller, io_addr, io_len); if (check_region(io_addr, io_len)) { /* Couldn't allocate io space */ printk(KERN_WARNING "(%s%d) couldn't allocate IO space %x len %d.\n", ips_name, ips_next_controller, io_addr, io_len); ips_next_controller++; continue; } request_region(io_addr, io_len, "ips"); } /* get planer status */ if (pci_read_config_word(dev[i], 0x04, &planer)) { printk(KERN_WARNING "(%s%d) can't get planer status.\n", ips_name, ips_next_controller); ips_next_controller++; continue; } /* check to see if an onboard planer controller is disabled */ if (!(planer & 0x000C)) { DEBUG_VAR(1, "(%s%d) detect, Onboard ServeRAID disabled by BIOS", ips_name, ips_next_controller); ips_next_controller++; continue; } DEBUG_VAR(1, "(%s%d) detect bus %d, func %x, irq %d, io %x, mem: %x, ptr: %x", ips_name, ips_next_controller, bus, func, irq, io_addr, mem_addr, (u32) mem_ptr); /* get the revision ID */ if (pci_read_config_byte(dev[i], 0x08, &revision_id)) { printk(KERN_WARNING "(%s%d) can't get revision id.\n", ips_name, ips_next_controller); ips_next_controller++; continue; } #if LINUX_VERSION_CODE < LinuxVersionCode(2,3,15) /* get the subdevice id */ if (pci_read_config_word(dev[i], 0x2e, &subdevice_id)) { printk(KERN_WARNING "(%s%d) can't get subdevice id.\n", ips_name, ips_next_controller); ips_next_controller++; continue; } #else subdevice_id = dev[i]->subsystem_device; #endif /* found a controller */ sh = scsi_register(SHT, sizeof(ips_ha_t)); if (sh == NULL) { printk(KERN_WARNING "(%s%d) Unable to register controller with SCSI subsystem - skipping controller\n", ips_name, ips_next_controller); ips_next_controller++; continue; } ha = IPS_HA(sh); memset(ha, 0, sizeof(ips_ha_t)); /* Initialize spin lock */ spin_lock_init(&ha->scb_lock); spin_lock_init(&ha->copp_lock); spin_lock_init(&ha->ips_lock); spin_lock_init(&ha->copp_waitlist.lock); spin_lock_init(&ha->scb_waitlist.lock); spin_lock_init(&ha->scb_activelist.lock); ips_sh[ips_next_controller] = sh; ips_ha[ips_next_controller] = ha; ips_num_controllers++; ha->active = 1; ha->enq = kmalloc(sizeof(IPS_ENQ), GFP_ATOMIC); if (!ha->enq) { printk(KERN_WARNING "(%s%d) Unable to allocate host inquiry structure - skipping contoller\n", ips_name, ips_next_controller); ha->active = 0; ips_free(ha); scsi_unregister(sh); ips_ha[ips_next_controller] = 0; ips_sh[ips_next_controller] = 0; ips_next_controller++; ips_num_controllers--; continue; } ha->adapt = kmalloc(sizeof(IPS_ADAPTER), GFP_ATOMIC); if (!ha->adapt) { printk(KERN_WARNING "(%s%d) Unable to allocate host adapt structure - skipping controller\n", ips_name, ips_next_controller); ha->active = 0; ips_free(ha); scsi_unregister(sh); ips_ha[ips_next_controller] = 0; ips_sh[ips_next_controller] = 0; ips_next_controller++; ips_num_controllers--; continue; } ha->conf = kmalloc(sizeof(IPS_CONF), GFP_ATOMIC); if (!ha->conf) { printk(KERN_WARNING "(%s%d) Unable to allocate host conf structure - skipping controller\n", ips_name, ips_next_controller); ha->active = 0; ips_free(ha); scsi_unregister(sh); ips_ha[ips_next_controller] = 0; ips_sh[ips_next_controller] = 0; ips_next_controller++; ips_num_controllers--; continue; } ha->nvram = kmalloc(sizeof(IPS_NVRAM_P5), GFP_ATOMIC); if (!ha->nvram) { printk(KERN_WARNING "(%s%d) Unable to allocate host nvram structure - skipping controller\n", ips_name, ips_next_controller); ha->active = 0; ips_free(ha); scsi_unregister(sh); ips_ha[ips_next_controller] = 0; ips_sh[ips_next_controller] = 0; ips_next_controller++; ips_num_controllers--; continue; } ha->subsys = kmalloc(sizeof(IPS_SUBSYS), GFP_ATOMIC); if (!ha->subsys) { printk(KERN_WARNING "(%s%d) Unable to allocate host subsystem structure - skipping controller\n", ips_name, ips_next_controller); ha->active = 0; ips_free(ha); scsi_unregister(sh); ips_ha[ips_next_controller] = 0; ips_sh[ips_next_controller] = 0; ips_next_controller++; ips_num_controllers--; continue; } ha->dummy = kmalloc(sizeof(IPS_IO_CMD), GFP_ATOMIC); if (!ha->dummy) { printk(KERN_WARNING "(%s%d) Unable to allocate host dummy structure - skipping controller\n", ips_name, ips_next_controller); ha->active = 0; ips_free(ha); scsi_unregister(sh); ips_ha[ips_next_controller] = 0; ips_sh[ips_next_controller] = 0; ips_next_controller++; ips_num_controllers--; continue; } for (count = PAGE_SIZE, ha->ioctl_order = 0; count < ips_ioctlsize; ha->ioctl_order++, count <<= 1); ha->ioctl_data = (char *) __get_free_pages(GFP_KERNEL, ha->ioctl_order); ha->ioctl_datasize = count; if (!ha->ioctl_data) { printk(KERN_WARNING "(%s%d) Unable to allocate ioctl data\n", ips_name, ips_next_controller); ha->ioctl_data = NULL; ha->ioctl_order = 0; ha->ioctl_datasize = 0; } /* Store away needed values for later use */ sh->io_port = io_addr; sh->n_io_port = io_addr ? 255 : 0; sh->unique_id = (io_addr) ? io_addr : mem_addr; sh->irq = irq; sh->select_queue_depths = ips_select_queue_depth; sh->sg_tablesize = sh->hostt->sg_tablesize; sh->can_queue = sh->hostt->can_queue; sh->cmd_per_lun = sh->hostt->cmd_per_lun; sh->unchecked_isa_dma = sh->hostt->unchecked_isa_dma; sh->use_clustering = sh->hostt->use_clustering; /***** Implement the following if it gets into a future kernel #if LINUX_VERSION_CODE >= LinuxVersionCode(2,4,4) sh->max_sectors = 128; #endif ******/ #if LINUX_VERSION_CODE < LinuxVersionCode(2,3,32) sh->wish_block = FALSE; #endif /* Store info in HA structure */ ha->irq = irq; ha->io_addr = io_addr; ha->io_len = io_len; ha->mem_addr = mem_addr; ha->mem_len = mem_len; ha->mem_ptr = mem_ptr; ha->ioremap_ptr = ioremap_ptr; ha->host_num = ips_next_controller; ha->revision_id = revision_id; ha->slot_num = PCI_SLOT(dev[i]->devfn); ha->device_id = deviceID[i]; ha->subdevice_id = subdevice_id; ha->pcidev = dev[i]; /* * Setup Functions */ if (IPS_IS_MORPHEUS(ha)) { /* morpheus */ ha->func.isintr = ips_isintr_morpheus; ha->func.isinit = ips_isinit_morpheus; ha->func.issue = ips_issue_i2o_memio; ha->func.init = ips_init_morpheus; ha->func.statupd = ips_statupd_morpheus; ha->func.reset = ips_reset_morpheus; ha->func.intr = ips_intr_morpheus; ha->func.enableint = ips_enable_int_morpheus; } else if (IPS_USE_MEMIO(ha)) { /* copperhead w/MEMIO */ ha->func.isintr = ips_isintr_copperhead_memio; ha->func.isinit = ips_isinit_copperhead_memio; ha->func.init = ips_init_copperhead_memio; ha->func.statupd = ips_statupd_copperhead_memio; ha->func.statinit = ips_statinit_memio; ha->func.reset = ips_reset_copperhead_memio; ha->func.intr = ips_intr_copperhead; ha->func.erasebios = ips_erase_bios_memio; ha->func.programbios = ips_program_bios_memio; ha->func.verifybios = ips_verify_bios_memio; ha->func.enableint = ips_enable_int_copperhead_memio; if (IPS_USE_I2O_DELIVER(ha)) ha->func.issue = ips_issue_i2o_memio; else ha->func.issue = ips_issue_copperhead_memio; } else { /* copperhead */ ha->func.isintr = ips_isintr_copperhead; ha->func.isinit = ips_isinit_copperhead; ha->func.init = ips_init_copperhead; ha->func.statupd = ips_statupd_copperhead; ha->func.statinit = ips_statinit; ha->func.reset = ips_reset_copperhead; ha->func.intr = ips_intr_copperhead; ha->func.erasebios = ips_erase_bios; ha->func.programbios = ips_program_bios; ha->func.verifybios = ips_verify_bios; ha->func.enableint = ips_enable_int_copperhead; if (IPS_USE_I2O_DELIVER(ha)) ha->func.issue = ips_issue_i2o; else ha->func.issue = ips_issue_copperhead; } /* * Initialize the card if it isn't already */ if (!(*ha->func.isinit)(ha)) { if (!(*ha->func.init)(ha)) { /* * Initialization failed */ printk(KERN_WARNING "(%s%d) unable to initialize controller - skipping controller\n", ips_name, ips_next_controller); ha->active = 0; ips_free(ha); scsi_unregister(sh); ips_ha[ips_next_controller] = 0; ips_sh[ips_next_controller] = 0; ips_next_controller++; ips_num_controllers--; continue; } } /* install the interrupt handler */ if (request_irq(irq, do_ipsintr, SA_SHIRQ, ips_name, ha)) { printk(KERN_WARNING "(%s%d) unable to install interrupt handler - skipping controller\n", ips_name, ips_next_controller); ha->active = 0; ips_free(ha); scsi_unregister(sh); ips_ha[ips_next_controller] = 0; ips_sh[ips_next_controller] = 0; ips_next_controller++; ips_num_controllers--; continue; } /* * Allocate a temporary SCB for initialization */ ha->scbs = (ips_scb_t *) kmalloc(sizeof(ips_scb_t), GFP_ATOMIC); if (!ha->scbs) { /* couldn't allocate a temp SCB */ printk(KERN_WARNING "(%s%d) unable to allocate CCBs - skipping contoller\n", ips_name, ips_next_controller); ha->active = 0; ips_free(ha); scsi_unregister(sh); ips_ha[ips_next_controller] = 0; ips_sh[ips_next_controller] = 0; free_irq(ha->irq, ha); ips_next_controller++; ips_num_controllers--; continue; } memset(ha->scbs, 0, sizeof(ips_scb_t)); ha->scbs->sg_list = (IPS_SG_LIST *) kmalloc(sizeof(IPS_SG_LIST) * IPS_MAX_SG, GFP_ATOMIC); if (!ha->scbs->sg_list) { /* couldn't allocate a temp SCB S/G list */ printk(KERN_WARNING "(%s%d) unable to allocate CCBs - skipping contoller\n", ips_name, ips_next_controller); ha->active = 0; ips_free(ha); scsi_unregister(sh); ips_ha[ips_next_controller] = 0; ips_sh[ips_next_controller] = 0; free_irq(ha->irq, ha); ips_next_controller++; ips_num_controllers--; continue; } ha->max_cmds = 1; ips_next_controller++; } while ((dev[i] = pci_find_device(IPS_VENDORID, deviceID[i], dev[i]))); } /* * Do Phase 2 Initialization * Controller init */ for (i = 0; i < ips_next_controller; i++) { if (ips_ha[i] == 0) { printk(KERN_WARNING "(%s%d) ignoring bad controller\n", ips_name, i); continue; } ha = ips_ha[i]; sh = ips_sh[i]; if (!ha->active) { scsi_unregister(sh); ips_ha[i] = NULL; ips_sh[i] = NULL; continue; } if (!ips_hainit(ha)) { printk(KERN_WARNING "(%s%d) unable to initialize controller - skipping\n", ips_name, i); ha->active = 0; ips_free(ha); free_irq(ha->irq, ha); scsi_unregister(sh); ips_ha[i] = NULL; ips_sh[i] = NULL; ips_num_controllers--; continue; } /* * Free the temporary SCB */ kfree(ha->scbs->sg_list); kfree(ha->scbs); ha->scbs = NULL; /* allocate CCBs */ if (!ips_allocatescbs(ha)) { printk(KERN_WARNING "(%s%d) unable to allocate CCBs - skipping contoller\n", ips_name, i); ha->active = 0; ips_free(ha); free_irq(ha->irq, ha); scsi_unregister(sh); ips_ha[i] = NULL; ips_sh[i] = NULL; ips_num_controllers--; continue; } /* finish setting values */ sh->max_id = ha->ntargets; sh->max_lun = ha->nlun; sh->max_channel = ha->nbus - 1; sh->can_queue = ha->max_cmds-1; } if (ips_num_controllers > 0) register_reboot_notifier(&ips_notifier); return (ips_num_controllers); #else /* No PCI -- No ServeRAID */ return (0); #endif /* CONFIG_PCI */ } /****************************************************************************/ /* */ /* Routine Name: ips_release */ /* */ /* Routine Description: */ /* */ /* Remove a driver */ /* */ /****************************************************************************/ int ips_release(struct Scsi_Host *sh) { ips_scb_t *scb; ips_ha_t *ha; int i; METHOD_TRACE("ips_release", 1); for (i = 0; i < IPS_MAX_ADAPTERS && ips_sh[i] != sh; i++); if (i == IPS_MAX_ADAPTERS) panic("(%s) release, invalid Scsi_Host pointer.\n", ips_name); ha = IPS_HA(sh); if (!ha) return (FALSE); /* flush the cache on the controller */ scb = &ha->scbs[ha->max_cmds-1]; ips_init_scb(ha, scb); scb->timeout = ips_cmd_timeout; scb->cdb[0] = IPS_CMD_FLUSH; scb->cmd.flush_cache.op_code = IPS_CMD_FLUSH; scb->cmd.flush_cache.command_id = IPS_COMMAND_ID(ha, scb); scb->cmd.flush_cache.state = IPS_NORM_STATE; scb->cmd.flush_cache.reserved = 0; scb->cmd.flush_cache.reserved2 = 0; scb->cmd.flush_cache.reserved3 = 0; scb->cmd.flush_cache.reserved4 = 0; printk("(%s%d) Flushing Cache.\n", ips_name, ha->host_num); /* send command */ if (ips_send_wait(ha, scb, ips_cmd_timeout, IPS_INTR_ON) == IPS_FAILURE) printk("(%s%d) Incomplete Flush.\n", ips_name, ha->host_num); printk("(%s%d) Flushing Complete.\n", ips_name, ha->host_num); ips_sh[i] = NULL; ips_ha[i] = NULL; /* free extra memory */ ips_free(ha); /* Free I/O Region */ if (ha->io_addr) release_region(ha->io_addr, ha->io_len); #if LINUX_VERSION_CODE >= LinuxVersionCode(2,3,17) if (ha->mem_addr) release_mem_region(ha->mem_addr, ha->mem_len); #endif /* free IRQ */ free_irq(ha->irq, ha); scsi_unregister(sh); ips_released_controllers++; if (ips_num_controllers == ips_released_controllers) unregister_reboot_notifier(&ips_notifier); return (FALSE); } /****************************************************************************/ /* */ /* Routine Name: ips_halt */ /* */ /* Routine Description: */ /* */ /* Perform cleanup when the system reboots */ /* */ /****************************************************************************/ static int ips_halt(struct notifier_block *nb, ulong event, void *buf) { ips_scb_t *scb; ips_ha_t *ha; int i; if ((event != SYS_RESTART) && (event != SYS_HALT) && (event != SYS_POWER_OFF)) return (NOTIFY_DONE); for (i = 0; i < ips_next_controller; i++) { ha = (ips_ha_t *) ips_ha[i]; if (!ha) continue; if (!ha->active) continue; /* flush the cache on the controller */ scb = &ha->scbs[ha->max_cmds-1]; ips_init_scb(ha, scb); scb->timeout = ips_cmd_timeout; scb->cdb[0] = IPS_CMD_FLUSH; scb->cmd.flush_cache.op_code = IPS_CMD_FLUSH; scb->cmd.flush_cache.command_id = IPS_COMMAND_ID(ha, scb); scb->cmd.flush_cache.state = IPS_NORM_STATE; scb->cmd.flush_cache.reserved = 0; scb->cmd.flush_cache.reserved2 = 0; scb->cmd.flush_cache.reserved3 = 0; scb->cmd.flush_cache.reserved4 = 0; printk("(%s%d) Flushing Cache.\n", ips_name, ha->host_num); /* send command */ if (ips_send_wait(ha, scb, ips_cmd_timeout, IPS_INTR_ON) == IPS_FAILURE) printk("(%s%d) Incomplete Flush.\n", ips_name, ha->host_num); else printk("(%s%d) Flushing Complete.\n", ips_name, ha->host_num); } unregister_reboot_notifier(&ips_notifier); return (NOTIFY_OK); } /****************************************************************************/ /* */ /* Routine Name: ips_eh_abort */ /* */ /* Routine Description: */ /* */ /* Abort a command (using the new error code stuff) */ /* */ /****************************************************************************/ int ips_eh_abort(Scsi_Cmnd *SC) { ips_ha_t *ha; ips_copp_wait_item_t *item; METHOD_TRACE("ips_eh_abort", 1); if (!SC) return (FAILED); ha = (ips_ha_t *) SC->host->hostdata; if (!ha) return (FAILED); if (!ha->active) return (FAILED); if (SC->serial_number != SC->serial_number_at_timeout) { /* HMM, looks like a bogus command */ DEBUG(1, "Abort called with bogus scsi command"); return (FAILED); } if (test_and_set_bit(IPS_IN_ABORT, &ha->flags)) return (FAILED); /* See if the command is on the copp queue */ IPS_QUEUE_LOCK(&ha->copp_waitlist); item = ha->copp_waitlist.head; while ((item) && (item->scsi_cmd != SC)) item = item->next; IPS_QUEUE_UNLOCK(&ha->copp_waitlist); if (item) { /* Found it */ ips_removeq_copp(&ha->copp_waitlist, item); clear_bit(IPS_IN_ABORT, &ha->flags); return (SUCCESS); } /* See if the command is on the wait queue */ if (ips_removeq_wait(&ha->scb_waitlist, SC)) { /* command not sent yet */ clear_bit(IPS_IN_ABORT, &ha->flags); return (SUCCESS); } else { /* command must have already been sent */ clear_bit(IPS_IN_ABORT, &ha->flags); return (FAILED); } } /****************************************************************************/ /* */ /* Routine Name: ips_eh_reset */ /* */ /* Routine Description: */ /* */ /* Reset the controller (with new eh error code) */ /* */ /* NOTE: this routine is called under the io_request_lock spinlock */ /* */ /****************************************************************************/ int ips_eh_reset(Scsi_Cmnd *SC) { int ret; int i; u32 cpu_flags; ips_ha_t *ha; ips_scb_t *scb; ips_copp_wait_item_t *item; METHOD_TRACE("ips_eh_reset", 1); #ifdef NO_IPS_RESET return (FAILED); #else if (!SC) { DEBUG(1, "Reset called with NULL scsi command"); return (FAILED); } ha = (ips_ha_t *) SC->host->hostdata; if (!ha) { DEBUG(1, "Reset called with NULL ha struct"); return (FAILED); } if (!ha->active) return (FAILED); if (test_and_set_bit(IPS_IN_RESET, &ha->flags)) return (FAILED); /* See if the command is on the copp queue */ IPS_QUEUE_LOCK(&ha->copp_waitlist); item = ha->copp_waitlist.head; while ((item) && (item->scsi_cmd != SC)) item = item->next; IPS_QUEUE_UNLOCK(&ha->copp_waitlist); if (item) { /* Found it */ ips_removeq_copp(&ha->copp_waitlist, item); clear_bit(IPS_IN_RESET, &ha->flags); return (SUCCESS); } /* See if the command is on the wait queue */ if (ips_removeq_wait(&ha->scb_waitlist, SC)) { /* command not sent yet */ clear_bit(IPS_IN_RESET, &ha->flags); return (SUCCESS); } /* * command must have already been sent * reset the controller */ printk(KERN_NOTICE "(%s%d) Resetting controller.\n", ips_name, ha->host_num); ret = (*ha->func.reset)(ha); if (!ret) { Scsi_Cmnd *scsi_cmd; printk(KERN_NOTICE "(%s%d) Controller reset failed - controller now offline.\n", ips_name, ha->host_num); /* Now fail all of the active commands */ DEBUG_VAR(1, "(%s%d) Failing active commands", ips_name, ha->host_num); while ((scb = ips_removeq_scb_head(&ha->scb_activelist))) { scb->scsi_cmd->result = DID_ERROR << 16; scb->scsi_cmd->scsi_done(scb->scsi_cmd); ips_freescb(ha, scb); } /* Now fail all of the pending commands */ DEBUG_VAR(1, "(%s%d) Failing pending commands", ips_name, ha->host_num); while ((scsi_cmd = ips_removeq_wait_head(&ha->scb_waitlist))) { scsi_cmd->result = DID_ERROR; scsi_cmd->scsi_done(scsi_cmd); } ha->active = FALSE; clear_bit(IPS_IN_RESET, &ha->flags); return (FAILED); } if (!ips_clear_adapter(ha, IPS_INTR_IORL)) { Scsi_Cmnd *scsi_cmd; printk(KERN_NOTICE "(%s%d) Controller reset failed - controller now offline.\n", ips_name, ha->host_num); /* Now fail all of the active commands */ DEBUG_VAR(1, "(%s%d) Failing active commands", ips_name, ha->host_num); while ((scb = ips_removeq_scb_head(&ha->scb_activelist))) { scb->scsi_cmd->result = DID_ERROR << 16; scb->scsi_cmd->scsi_done(scb->scsi_cmd); ips_freescb(ha, scb); } /* Now fail all of the pending commands */ DEBUG_VAR(1, "(%s%d) Failing pending commands", ips_name, ha->host_num); while ((scsi_cmd = ips_removeq_wait_head(&ha->scb_waitlist))) { scsi_cmd->result = DID_ERROR << 16; scsi_cmd->scsi_done(scsi_cmd); } ha->active = FALSE; clear_bit(IPS_IN_RESET, &ha->flags); return (FAILED); } /* FFDC */ if (ha->subsys->param[3] & 0x300000) { struct timeval tv; do_gettimeofday(&tv); IPS_HA_LOCK(cpu_flags); ha->last_ffdc = tv.tv_sec; ha->reset_count++; IPS_HA_UNLOCK(cpu_flags); ips_ffdc_reset(ha, IPS_INTR_IORL); } /* Now fail all of the active commands */ DEBUG_VAR(1, "(%s%d) Failing active commands", ips_name, ha->host_num); while ((scb = ips_removeq_scb_head(&ha->scb_activelist))) { scb->scsi_cmd->result = (DID_RESET << 16) | (SUGGEST_RETRY << 24); scb->scsi_cmd->scsi_done(scb->scsi_cmd); ips_freescb(ha, scb); } /* Reset DCDB active command bits */ for (i = 1; i < ha->nbus; i++) ha->dcdb_active[i-1] = 0; /* Reset the number of active IOCTLs */ IPS_HA_LOCK(cpu_flags); ha->num_ioctl = 0; IPS_HA_UNLOCK(cpu_flags); clear_bit(IPS_IN_RESET, &ha->flags); if (!test_bit(IPS_IN_INTR, &ha->flags)) { /* * Only execute the next command when * we are not being called from the * interrupt handler. The interrupt * handler wants to do this and since * interrupts are turned off here.... */ ips_next(ha, IPS_INTR_IORL); } return (SUCCESS); #endif /* NO_IPS_RESET */ } /****************************************************************************/ /* */ /* Routine Name: ips_queue */ /* */ /* Routine Description: */ /* */ /* Send a command to the controller */ /* */ /* NOTE: */ /* Linux obtains io_request_lock before calling this function */ /* */ /****************************************************************************/ int ips_queue(Scsi_Cmnd *SC, void (*done) (Scsi_Cmnd *)) { ips_ha_t *ha; u32 cpu_flags; DECLARE_MUTEX_LOCKED(sem); METHOD_TRACE("ips_queue", 1); ha = (ips_ha_t *) SC->host->hostdata; if (!ha) return (1); if (!ha->active) return (DID_ERROR); #ifndef NO_IPS_CMDLINE if (ips_is_passthru(SC)) { IPS_QUEUE_LOCK(&ha->copp_waitlist); if (ha->copp_waitlist.count == IPS_MAX_IOCTL_QUEUE) { IPS_QUEUE_UNLOCK(&ha->copp_waitlist); SC->result = DID_BUS_BUSY << 16; done(SC); return (0); } else { IPS_QUEUE_UNLOCK(&ha->copp_waitlist); } } else { #endif IPS_QUEUE_LOCK(&ha->scb_waitlist); if (ha->scb_waitlist.count == IPS_MAX_QUEUE) { IPS_QUEUE_UNLOCK(&ha->scb_waitlist); SC->result = DID_BUS_BUSY << 16; done(SC); return (0); } else { IPS_QUEUE_UNLOCK(&ha->scb_waitlist); } #ifndef NO_IPS_CMDLINE } #endif SC->scsi_done = done; DEBUG_VAR(2, "(%s%d): ips_queue: cmd 0x%X (%d %d %d)", ips_name, ha->host_num, SC->cmnd[0], SC->channel, SC->target, SC->lun); /* Check for command to initiator IDs */ if ((SC->channel > 0) && (SC->target == ha->ha_id[SC->channel])) { SC->result = DID_NO_CONNECT << 16; done(SC); return (0); } #ifndef NO_IPS_CMDLINE if (ips_is_passthru(SC)) { ips_copp_wait_item_t *scratch; /* allocate space for the scribble */ scratch = kmalloc(sizeof(ips_copp_wait_item_t), GFP_ATOMIC); if (!scratch) { SC->result = DID_ERROR << 16; done(SC); return (0); } scratch->scsi_cmd = SC; scratch->sem = &sem; scratch->next = NULL; ips_putq_copp_tail(&ha->copp_waitlist, scratch); } else #endif ips_putq_wait_tail(&ha->scb_waitlist, SC); IPS_HA_LOCK(cpu_flags); if ((!test_bit(IPS_IN_INTR, &ha->flags)) && (!test_bit(IPS_IN_ABORT, &ha->flags)) && (!test_bit(IPS_IN_RESET, &ha->flags))) { IPS_HA_UNLOCK(cpu_flags); ips_next(ha, IPS_INTR_IORL); } else { IPS_HA_UNLOCK(cpu_flags); } /* * If this request was a new style IOCTL wait * for it to finish. * * NOTE: we relinquished the lock above so this should * not cause contention problems */ if (ips_is_passthru(SC) && SC->cmnd[0] == IPS_IOCTL_NEW_COMMAND) { char *user_area; char *kern_area; u32 datasize; /* free io_request_lock */ spin_unlock_irq(&io_request_lock); /* wait for the command to finish */ down(&sem); /* reobtain the lock */ spin_lock_irq(&io_request_lock); /* command finished -- copy back */ user_area = *((char **) &SC->cmnd[4]); kern_area = ha->ioctl_data; datasize = *((u32 *) &SC->cmnd[8]); if (datasize) { if (copy_to_user(user_area, kern_area, datasize) > 0) { DEBUG_VAR(1, "(%s%d) passthru failed - unable to copy out user data", ips_name, ha->host_num); SC->result = DID_ERROR << 16; SC->scsi_done(SC); } else { SC->scsi_done(SC); } } else { SC->scsi_done(SC); } } /* If We were using the CD Boot Flash Buffer, Restore the Old Values */ if ( ips_FlashData == ha->ioctl_data ) { ha->ioctl_data = ha->save_ioctl_data; ha->ioctl_order = ha->save_ioctl_order; ha->ioctl_datasize = ha->save_ioctl_datasize; ips_FlashDataInUse = 0; } return (0); } /****************************************************************************/ /* */ /* Routine Name: ips_biosparam */ /* */ /* Routine Description: */ /* */ /* Set bios geometry for the controller */ /* */ /****************************************************************************/ int ips_biosparam(Disk *disk, kdev_t dev, int geom[]) { ips_ha_t *ha; int heads; int sectors; int cylinders; METHOD_TRACE("ips_biosparam", 1); ha = (ips_ha_t *) disk->device->host->hostdata; if (!ha) /* ?!?! host adater info invalid */ return (0); if (!ha->active) return (0); if (!ips_read_adapter_status(ha, IPS_INTR_ON)) /* ?!?! Enquiry command failed */ return (0); if ((disk->capacity > 0x400000) && ((ha->enq->ucMiscFlag & 0x8) == 0)) { heads = IPS_NORM_HEADS; sectors = IPS_NORM_SECTORS; } else { heads = IPS_COMP_HEADS; sectors = IPS_COMP_SECTORS; } cylinders = disk->capacity / (heads * sectors); DEBUG_VAR(2, "Geometry: heads: %d, sectors: %d, cylinders: %d", heads, sectors, cylinders); geom[0] = heads; geom[1] = sectors; geom[2] = cylinders; return (0); } /****************************************************************************/ /* */ /* Routine Name: ips_select_queue_depth */ /* */ /* Routine Description: */ /* */ /* Select queue depths for the devices on the contoller */ /* */ /****************************************************************************/ static void ips_select_queue_depth(struct Scsi_Host *host, Scsi_Device *scsi_devs) { Scsi_Device *device; ips_ha_t *ha; int count = 0; ha = IPS_HA(host); for (device = scsi_devs; device; device = device->next) { if (device->host == host) { if ((device->channel == 0) && (device->type == 0)) count++; } } for (device = scsi_devs; device; device = device->next) { if (device->host == host) { if ((device->channel == 0) && (device->type == 0)) device->queue_depth = ha->max_cmds / count - 1; else device->queue_depth = 2; if (device->queue_depth < 2) device->queue_depth = 2; } } } /****************************************************************************/ /* */ /* Routine Name: do_ipsintr */ /* */ /* Routine Description: */ /* */ /* Wrapper for the interrupt handler */ /* */ /****************************************************************************/ void do_ipsintr(int irq, void *dev_id, struct pt_regs *regs) { ips_ha_t *ha; u32 cpu_flags; METHOD_TRACE("do_ipsintr", 2); ha = (ips_ha_t *) dev_id; spin_lock_irqsave(&io_request_lock, cpu_flags); if (test_and_set_bit(IPS_IN_INTR, &ha->flags)) { spin_unlock_irqrestore(&io_request_lock, cpu_flags); return ; } if (!ha) { clear_bit(IPS_IN_INTR, &ha->flags); spin_unlock_irqrestore(&io_request_lock, cpu_flags); return; } if (!ha->active) { clear_bit(IPS_IN_INTR, &ha->flags); spin_unlock_irqrestore(&io_request_lock, cpu_flags); return; } (*ha->func.intr)(ha); clear_bit(IPS_IN_INTR, &ha->flags); spin_unlock_irqrestore(&io_request_lock, cpu_flags); /* start the next command */ ips_next(ha, IPS_INTR_ON); } /****************************************************************************/ /* */ /* Routine Name: ips_intr_copperhead */ /* */ /* Routine Description: */ /* */ /* Polling interrupt handler */ /* */ /* ASSUMES interrupts are disabled */ /* */ /****************************************************************************/ void ips_intr_copperhead(ips_ha_t *ha) { ips_stat_t *sp; ips_scb_t *scb; IPS_STATUS cstatus; int intrstatus; u32 cpu_flags; METHOD_TRACE("ips_intr", 2); if (!ha) return; if (!ha->active) return; IPS_HA_LOCK(cpu_flags); intrstatus = (*ha->func.isintr)(ha); if (!intrstatus) { /* * Unexpected/Shared interrupt */ IPS_HA_UNLOCK(cpu_flags); return; } while (TRUE) { sp = &ha->sp; intrstatus = (*ha->func.isintr)(ha); if (!intrstatus) break; else cstatus.value = (*ha->func.statupd)(ha); if (cstatus.fields.command_id > (IPS_MAX_CMDS - 1)) { printk(KERN_WARNING "(%s%d) Spurious interrupt; no ccb.\n", ips_name, ha->host_num); continue; } ips_chkstatus(ha, &cstatus); scb = (ips_scb_t *) sp->scb_addr; /* * use the callback function to finish things up * NOTE: interrupts are OFF for this */ IPS_HA_UNLOCK(cpu_flags); (*scb->callback) (ha, scb); IPS_HA_LOCK(cpu_flags); } /* end while */ IPS_HA_UNLOCK(cpu_flags); } /****************************************************************************/ /* */ /* Routine Name: ips_intr_morpheus */ /* */ /* Routine Description: */ /* */ /* Polling interrupt handler */ /* */ /* ASSUMES interrupts are disabled */ /* */ /****************************************************************************/ void ips_intr_morpheus(ips_ha_t *ha) { ips_stat_t *sp; ips_scb_t *scb; IPS_STATUS cstatus; int intrstatus; u32 cpu_flags; METHOD_TRACE("ips_intr_morpheus", 2); if (!ha) return; if (!ha->active) return; IPS_HA_LOCK(cpu_flags); intrstatus = (*ha->func.isintr)(ha); if (!intrstatus) { /* * Unexpected/Shared interrupt */ IPS_HA_UNLOCK(cpu_flags); return; } while (TRUE) { sp = &ha->sp; intrstatus = (*ha->func.isintr)(ha); if (!intrstatus) break; else cstatus.value = (*ha->func.statupd)(ha); if (cstatus.value == 0xffffffff) /* No more to process */ break; if (cstatus.fields.command_id > (IPS_MAX_CMDS - 1)) { printk(KERN_WARNING "(%s%d) Spurious interrupt; no ccb.\n", ips_name, ha->host_num); continue; } ips_chkstatus(ha, &cstatus); scb = (ips_scb_t *) sp->scb_addr; /* * use the callback function to finish things up * NOTE: interrupts are OFF for this */ IPS_HA_UNLOCK(cpu_flags); (*scb->callback) (ha, scb); IPS_HA_LOCK(cpu_flags); } /* end while */ IPS_HA_UNLOCK(cpu_flags); } /****************************************************************************/ /* */ /* Routine Name: ips_info */ /* */ /* Routine Description: */ /* */ /* Return info about the driver */ /* */ /****************************************************************************/ const char * ips_info(struct Scsi_Host *SH) { static char buffer[256]; char *bp; ips_ha_t *ha; METHOD_TRACE("ips_info", 1); ha = IPS_HA(SH); if (!ha) return (NULL); bp = &buffer[0]; memset(bp, 0, sizeof(buffer)); strcpy(bp, "IBM PCI ServeRAID "); strcat(bp, IPS_VERSION_HIGH); strcat(bp, IPS_VERSION_LOW); if (ha->ad_type > 0 && ha->ad_type <= MAX_ADAPTER_NAME) { strcat(bp, " <"); strcat(bp, ips_adapter_name[ha->ad_type-1]); strcat(bp, ">"); } return (bp); } /****************************************************************************/ /* */ /* Routine Name: ips_proc_info */ /* */ /* Routine Description: */ /* */ /* The passthru interface for the driver */ /* */ /****************************************************************************/ int ips_proc_info(char *buffer, char **start, off_t offset, int length, int hostno, int func) { int i; int ret; ips_ha_t *ha = NULL; METHOD_TRACE("ips_proc_info", 1); /* Find our host structure */ for (i = 0; i < ips_next_controller; i++) { if (ips_sh[i]) { if (ips_sh[i]->host_no == hostno) { ha = (ips_ha_t *) ips_sh[i]->hostdata; break; } } } if (!ha) return (-EINVAL); if (func) { /* write */ return (0); } else { /* read */ if (start) *start = buffer; ret = ips_host_info(ha, buffer, offset, length); return (ret); } } /*--------------------------------------------------------------------------*/ /* Helper Functions */ /*--------------------------------------------------------------------------*/ #ifndef NO_IPS_CMDLINE /****************************************************************************/ /* */ /* Routine Name: ips_is_passthru */ /* */ /* Routine Description: */ /* */ /* Determine if the specified SCSI command is really a passthru command */ /* */ /****************************************************************************/ static int ips_is_passthru(Scsi_Cmnd *SC) { METHOD_TRACE("ips_is_passthru", 1); if (!SC) return (0); if (((SC->cmnd[0] == IPS_IOCTL_COMMAND) || (SC->cmnd[0] == IPS_IOCTL_NEW_COMMAND)) && (SC->channel == 0) && (SC->target == IPS_ADAPTER_ID) && (SC->lun == 0) && (SC->request_bufflen) && (!SC->use_sg) && (((char *) SC->request_buffer)[0] == 'C') && (((char *) SC->request_buffer)[1] == 'O') && (((char *) SC->request_buffer)[2] == 'P') && (((char *) SC->request_buffer)[3] == 'P')) { return (1); } else { return (0); } } /****************************************************************************/ /* */ /* Routine Name: ips_make_passthru */ /* */ /* Routine Description: */ /* */ /* Make a passthru command out of the info in the Scsi block */ /* */ /****************************************************************************/ static int ips_make_passthru(ips_ha_t *ha, Scsi_Cmnd *SC, ips_scb_t *scb, int intr) { IPS_NVRAM_P5 nvram; ips_passthru_t *pt; METHOD_TRACE("ips_make_passthru", 1); if (!SC->request_bufflen || !SC->request_buffer) { /* no data */ DEBUG_VAR(1, "(%s%d) No passthru structure", ips_name, ha->host_num); return (IPS_FAILURE); } if (SC->request_bufflen < sizeof(ips_passthru_t)) { /* wrong size */ DEBUG_VAR(1, "(%s%d) Passthru structure wrong size", ips_name, ha->host_num); return (IPS_FAILURE); } if ((((char *) SC->request_buffer)[0] != 'C') || (((char *) SC->request_buffer)[1] != 'O') || (((char *) SC->request_buffer)[2] != 'P') || (((char *) SC->request_buffer)[3] != 'P')) { /* signature doesn't match */ DEBUG_VAR(1, "(%s%d) Wrong signature on passthru structure.", ips_name, ha->host_num); return (IPS_FAILURE); } pt = (ips_passthru_t *) SC->request_buffer; /* * Some notes about the passthru interface used * * IF the scsi op_code == 0x0d then we assume * that the data came along with/goes with the * packet we received from the sg driver. In this * case the CmdBSize field of the pt structure is * used for the size of the buffer. * * IF the scsi op_code == 0x81 then we assume that * we will need our own buffer and we will copy the * data to/from the user buffer passed in the scsi * command. The data address resides at offset 4 * in the scsi command. The length of the data resides * at offset 8 in the scsi command. */ switch (pt->CoppCmd) { case IPS_NUMCTRLS: memcpy(SC->request_buffer + sizeof(ips_passthru_t), &ips_num_controllers, sizeof(int)); SC->result = DID_OK << 16; return (IPS_SUCCESS_IMM); case IPS_CTRLINFO: memcpy(SC->request_buffer + sizeof(ips_passthru_t), ha, sizeof(ips_ha_t)); SC->result = DID_OK << 16; return (IPS_SUCCESS_IMM); case IPS_COPPUSRCMD: case IPS_COPPIOCCMD: if (SC->cmnd[0] == IPS_IOCTL_COMMAND) { if (SC->request_bufflen < (sizeof(ips_passthru_t) + pt->CmdBSize)) { /* wrong size */ DEBUG_VAR(1, "(%s%d) Passthru structure wrong size", ips_name, ha->host_num); return (IPS_FAILURE); } if ((pt->CoppCP.cmd.nvram.op_code == IPS_CMD_RW_NVRAM_PAGE) && (pt->CoppCP.cmd.nvram.page == 5) && (pt->CoppCP.cmd.nvram.write == 0)) { if (pt->CmdBSize < sizeof(IPS_NVRAM_P5)) { SC->result = DID_ERROR << 16; return (IPS_FAILURE); } ips_get_bios_version(ha, IPS_INTR_IORL); ips_create_nvrampage5(ha, &nvram); /* Copy the result back */ memcpy(SC->request_buffer + sizeof(ips_passthru_t), &nvram, sizeof(IPS_NVRAM_P5)); SC->result = DID_OK << 16; pt->BasicStatus = 0x00; pt->ExtendedStatus = 0x00; return (IPS_SUCCESS_IMM); } if (ips_usrcmd(ha, pt, scb)) return (IPS_SUCCESS); else return (IPS_FAILURE); } else if (SC->cmnd[0] == IPS_IOCTL_NEW_COMMAND) { char *user_area; char *kern_area; u32 datasize; if (SC->request_bufflen < (sizeof(ips_passthru_t))) { /* wrong size */ DEBUG_VAR(1, "(%s%d) Passthru structure wrong size", ips_name, ha->host_num); SC->result = DID_ERROR << 16; return (IPS_FAILURE); } /* IF it's OK to Use the "CD BOOT" Flash Buffer, then you can */ /* avoid allocating a huge buffer per adapter ( which can fail ). */ if ( (ips_FlashData) && (pt->CmdBSize == IPS_IMAGE_SIZE) && (ips_FlashDataInUse == 0) ) {