extern int blk_dev_init(void);
-#ifdef CONFIG_FUSION
-extern int fusion_init(void);
-#endif
extern int soc_probe(void);
extern int atmdev_init(void);
extern int i2o_init(void);
#ifdef CONFIG_I2O
i2o_init();
#endif
-#ifdef CONFIG_FUSION
- fusion_init();
-#endif
#ifdef CONFIG_FC4_SOC
/* This has to be done before scsi_dev_init */
soc_probe();
if [ "$CONFIG_FUSION" = "y" -o "$CONFIG_FUSION" = "m" ]; then
- if [ "$CONFIG_FUSION" = "y" ]; then
- comment "(ability to boot linux kernel from Fusion device is ENABLED!)"
+ if [ "$CONFIG_BLK_DEV_SD" = "y" -a "$CONFIG_FUSION" = "y" ]; then
+ define_bool CONFIG_FUSION_BOOT y
else
- comment "(ability to boot linux kernel from Fusion device is DISABLED!)"
+ define_bool CONFIG_FUSION_BOOT n
fi
- # Modular only
- dep_tristate " Enhanced SCSI error reporting" CONFIG_FUSION_ISENSE $CONFIG_FUSION m
- dep_tristate " Fusion MPT misc device (ioctl) driver" CONFIG_FUSION_CTL $CONFIG_FUSION m
+ if [ "$CONFIG_MODULES" = "y" ]; then
+ # How can we force these options to module or nothing?
+ dep_tristate " Enhanced SCSI error reporting" CONFIG_FUSION_ISENSE $CONFIG_FUSION m
+ dep_tristate " Fusion MPT misc device (ioctl) driver" CONFIG_FUSION_CTL $CONFIG_FUSION m
+ fi
dep_tristate " Fusion MPT LAN driver" CONFIG_FUSION_LAN $CONFIG_FUSION $CONFIG_NET
if [ "$CONFIG_FUSION_LAN" != "n" ]; then
define_bool CONFIG_NET_FC y
fi
+else
+
+ define_bool CONFIG_FUSION_BOOT n
+ # These <should> be define_tristate, but we leave them define_bool
+ # for backward compatibility with pre-linux-2.2.15 kernels.
+ # (Bugzilla:fibrebugs, #384)
+ define_bool CONFIG_FUSION_ISENSE n
+ define_bool CONFIG_FUSION_CTL n
+ define_bool CONFIG_FUSION_LAN n
+
fi
endmenu
* Error Report logging output. This module implements SCSI-3
* Opcode lookup and a sorted table of SCSI-3 ASC/ASCQ strings.
*
- * Copyright (c) 1991-2001 Steven J. Ralston
+ * Copyright (c) 1991-2002 Steven J. Ralston
* Written By: Steven J. Ralston
* (yes I wrote some of the orig. code back in 1991!)
- * (mailto:Steve.Ralston@lsil.com)
+ * (mailto:sjralston1@netscape.net)
+ * (mailto:Pam.Delaney@lsil.com)
*
- * $Id: isense.c,v 1.28.14.1 2001/08/24 20:07:04 sralston Exp $
+ * $Id: isense.c,v 1.33 2002/02/27 18:44:19 sralston Exp $
*/
/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
/*
*/
/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
-#include <linux/module.h>
+#include <linux/version.h>
#include <linux/kernel.h>
+#include <linux/module.h>
#include <linux/errno.h>
#include <linux/init.h>
-#include <linux/version.h>
+#include <asm/io.h>
+#if defined (__sparc__)
+#include <linux/timer.h>
+#endif
/* Hmmm, avoid undefined spinlock_t on lk-2.2.14-5.0 */
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,3,0)
#endif
#define MODULEAUTHOR "Steven J. Ralston"
-#define COPYRIGHT "Copyright (c) 2001 " MODULEAUTHOR
+#define COPYRIGHT "Copyright (c) 2001-2002 " MODULEAUTHOR
#include "mptbase.h"
#include "isense.h"
#define my_VERSION MPT_LINUX_VERSION_COMMON
#define MYNAM "isense"
+EXPORT_NO_SYMBOLS;
MODULE_AUTHOR(MODULEAUTHOR);
MODULE_DESCRIPTION(my_NAME);
-MODULE_LICENSE("GPL");
/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
int __init isense_init(void)
/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
+#ifndef rwlock_init
+#define rwlock_init(x) do { *(x) = RW_LOCK_UNLOCKED; } while(0)
+#endif
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0)
+#define SET_NICE(current,x) do {(current)->nice = (x);} while (0)
+#else
+#define SET_NICE(current,x)
+#endif
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,4,0)
+#define pci_enable_device(pdev) (0)
+#define SCSI_DATA_UNKNOWN 0
+#define SCSI_DATA_WRITE 1
+#define SCSI_DATA_READ 2
+#define SCSI_DATA_NONE 3
+#endif
+
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,4,4)
+#define pci_set_dma_mask(pdev, mask) (0)
+#define scsi_set_pci_device(sh, pdev) (0)
+#endif
+
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,3,0)
# if LINUX_VERSION_CODE < KERNEL_VERSION(2,2,18)
typedef unsigned int dma_addr_t;
extern inline __cleanup_module_func_t __cleanup_module_inline(void) \
{ return x; }
-#else
+#else
#define module_init(x) __initcall(x);
#define module_exit(x) __exitcall(x);
#endif
/* } block snipped from lk-2.2.18/include/linux/init.h */
+/* This block snipped from lk-2.2.18/include/linux/sched.h { */
+/*
+ * Used prior to schedule_timeout calls..
+ */
+#define __set_current_state(state_value) do { current->state = state_value; } while (0)
+#ifdef __SMP__
+#define set_current_state(state_value) do { __set_current_state(state_value); mb(); } while (0)
+#else
+#define set_current_state(state_value) __set_current_state(state_value)
+#endif
+/* } block snipped from lk-2.2.18/include/linux/sched.h */
+
+/* procfs compat stuff... */
+#define proc_mkdir(x,y) create_proc_entry(x, S_IFDIR, y)
+
+/* MUTEX compat stuff... */
+#define DECLARE_MUTEX(name) struct semaphore name=MUTEX
+#define DECLARE_MUTEX_LOCKED(name) struct semaphore name=MUTEX_LOCKED
+#define init_MUTEX(x) *(x)=MUTEX
+#define init_MUTEX_LOCKED(x) *(x)=MUTEX_LOCKED
+
/* Wait queues. */
#define DECLARE_WAIT_QUEUE_HEAD(name) \
struct wait_queue * (name) = NULL
#endif /* LINUX_VERSION_CODE < KERNEL_VERSION(2,2,18) */
+/*
+ * Inclined to use:
+ * #if LINUX_VERSION_CODE < KERNEL_VERSION(2,4,10)
+ * here, but MODULE_LICENSE defined in 2.4.9-6 and 2.4.9-13
+ * breaks the rule:-(
+ */
+#ifndef MODULE_LICENSE
+#define MODULE_LICENSE(license)
+#endif
+
+
/* PCI/driver subsystem { */
#ifndef pci_for_each_dev
#define pci_for_each_dev(dev) for((dev)=pci_devices; (dev)!=NULL; (dev)=(dev)->next)
#endif /* } ifndef pci_for_each_dev */
-/* procfs compat stuff... */
-#ifdef CONFIG_PROC_FS
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,3,28)
-#define CREATE_PROCDIR_ENTRY(x,y) create_proc_entry(x, S_IFDIR, y)
-/* This is a macro so we don't need to pull all the procfs
- * headers into this file. -DaveM
- */
-#define create_proc_read_entry(name, mode, base, __read_proc, __data) \
-({ struct proc_dir_entry *__res=create_proc_entry(name,mode,base); \
- if (__res) { \
- __res->read_proc=(__read_proc); \
- __res->data=(__data); \
- } \
- __res; \
-})
-#else
-#define CREATE_PROCDIR_ENTRY(x,y) proc_mkdir(x, y)
-#endif
-#endif
-
/* Compatability for the 2.3.x PCI DMA API. */
#ifndef PCI_DMA_BIDIRECTIONAL
/*{-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
/*}-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
#endif /* PCI_DMA_BIDIRECTIONAL */
+/*
+ * With the new command queuing code in the SCSI mid-layer we no longer have
+ * to hold the io_request_lock spin lock when calling the scsi_done routine.
+ * For now we only do this with the 2.5.1 kernel or newer.
+ */
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,5,1)
+ #define MPT_HOST_LOCK(flags)
+ #define MPT_HOST_UNLOCK(flags)
+#else
+ #define MPT_HOST_LOCK(flags) \
+ spin_lock_irqsave(&io_request_lock, flags)
+ #define MPT_HOST_UNLOCK(flags) \
+ spin_unlock_irqrestore(&io_request_lock, flags)
+#endif
+
+/*
+ * We use our new error handling code if the kernel version is 2.5.1 or newer.
+ */
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,5,1)
+ #define MPT_SCSI_USE_NEW_EH
+#endif
+
/*}-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
#endif /* _LINUX_COMPAT_H */
* in the IOCLogInfo field of a MPI Default Reply Message.
*
* CREATION DATE: 6/02/2000
- * ID: $Id: fc_log.h,v 4.5 2001/06/07 19:18:00 sschremm Exp $
+ * ID: $Id: fc_log.h,v 4.6 2001/07/26 14:41:33 sschremm Exp $
*/
MPI_IOCLOGINFO_FC_TARGET_MRSP_KILLED_BY_LIP = 0x2100000a, /* Manual Response not sent due to a LIP */
MPI_IOCLOGINFO_FC_TARGET_NO_CLASS_3 = 0x2100000b, /* not sent because remote node does not support Class 3 */
MPI_IOCLOGINFO_FC_TARGET_LOGIN_NOT_VALID = 0x2100000c, /* not sent because login to remote node not validated */
- MPI_IOCLOGINFO_FC_TARGET_FROM_OUTBOUND = 0x2100000e, /* cleared from the outbound after a logout */
+ MPI_IOCLOGINFO_FC_TARGET_FROM_OUTBOUND = 0x2100000e, /* cleared from the outbound queue after a logout */
MPI_IOCLOGINFO_FC_TARGET_WAITING_FOR_DATA_IN = 0x2100000f, /* cleared waiting for data after a logout */
MPI_IOCLOGINFO_FC_LAN_BASE = 0x22000000,
* Title: MPI Message independent structures and definitions
* Creation Date: July 27, 2000
*
- * MPI Version: 01.01.07
+ * MPI Version: 01.02.03
*
* Version History
* ---------------
* Added function codes for RAID.
* 04-09-01 01.01.07 Added alternate define for MPI_DOORBELL_ACTIVE,
* MPI_DOORBELL_USED, to better match the spec.
+ * 08-08-01 01.02.01 Original release for v1.2 work.
+ * Changed MPI_VERSION_MINOR from 0x01 to 0x02.
+ * Added define MPI_FUNCTION_TOOLBOX.
+ * 09-28-01 01.02.02 New function code MPI_SCSI_ENCLOSURE_PROCESSOR.
+ * 11-01-01 01.02.03 Changed name to MPI_FUNCTION_SCSI_ENCLOSURE_PROCESSOR.
* --------------------------------------------------------------------------
*/
*****************************************************************************/
#define MPI_VERSION_MAJOR (0x01)
-#define MPI_VERSION_MINOR (0x01)
+#define MPI_VERSION_MINOR (0x02)
#define MPI_VERSION ((MPI_VERSION_MAJOR << 8) | MPI_VERSION_MINOR)
/* Note: The major versions of 0xe0 through 0xff are reserved */
#define MPI_FUNCTION_FC_COMMON_TRANSPORT_SEND (0x13)
#define MPI_FUNCTION_FC_PRIMITIVE_SEND (0x14)
-#define MPI_FUNCTION_RAID_VOLUME (0x15)
+#define MPI_FUNCTION_RAID_ACTION (0x15)
#define MPI_FUNCTION_RAID_SCSI_IO_PASSTHROUGH (0x16)
+#define MPI_FUNCTION_TOOLBOX (0x17)
+
+#define MPI_FUNCTION_SCSI_ENCLOSURE_PROCESSOR (0x18)
+
#define MPI_FUNCTION_LAN_SEND (0x20)
#define MPI_FUNCTION_LAN_RECEIVE (0x21)
#define MPI_FUNCTION_LAN_RESET (0x22)
* Title: MPI Config message, structures, and Pages
* Creation Date: July 27, 2000
*
- * MPI Version: 01.01.11
+ * MPI Version: 01.02.05
*
* Version History
* ---------------
* Added IO Unit Page 3.
* Modified defines for Scsi Port Page 2.
* Modified RAID Volume Pages.
+ * 08-08-01 01.02.01 Original release for v1.2 work.
+ * Added SepID and SepBus to RVP2 IMPhysicalDisk struct.
+ * Added defines for the SEP bits in RVP2 VolumeSettings.
+ * Modified the DeviceSettings field in RVP2 to use the
+ * proper structure.
+ * Added defines for SES, SAF-TE, and cross channel for
+ * IOCPage2 CapabilitiesFlags.
+ * Removed define for MPI_IOUNITPAGE2_FLAGS_RAID_DISABLE.
+ * Removed define for
+ * MPI_SCSIPORTPAGE2_PORT_FLAGS_PARITY_ENABLE.
+ * Added define for MPI_CONFIG_PAGEATTR_RO_PERSISTENT.
+ * 08-29-01 01.02.02 Fixed value for MPI_MANUFACTPAGE_DEVID_53C1035.
+ * Added defines for MPI_FCPORTPAGE1_FLAGS_HARD_ALPA_ONLY
+ * and MPI_FCPORTPAGE1_FLAGS_IMMEDIATE_ERROR_REPLY.
+ * Removed MPI_SCSIPORTPAGE0_CAP_PACING_TRANSFERS,
+ * MPI_SCSIDEVPAGE0_NP_PACING_TRANSFERS, and
+ * MPI_SCSIDEVPAGE1_RP_PACING_TRANSFERS, and
+ * MPI_SCSIDEVPAGE1_CONF_PPR_ALLOWED.
+ * Added defines for MPI_SCSIDEVPAGE1_CONF_WDTR_DISALLOWED
+ * and MPI_SCSIDEVPAGE1_CONF_SDTR_DISALLOWED.
+ * Added OnBusTimerValue to CONFIG_PAGE_SCSI_PORT_1.
+ * Added rejected bits to SCSI Device Page 0 Information.
+ * Increased size of ALPA array in FC Port Page 2 by one
+ * and removed a one byte reserved field.
+ * 09-28-01 01.02.03 Swapped NegWireSpeedLow and NegWireSpeedLow in
+ * CONFIG_PAGE_LAN_1 to match preferred 64-bit ordering.
+ * Added structures for Manufacturing Page 4, IO Unit
+ * Page 3, IOC Page 3, IOC Page 4, RAID Volume Page 0, and
+ * RAID PhysDisk Page 0.
+ * 10-04-01 01.02.04 Added define for MPI_CONFIG_PAGETYPE_RAID_PHYSDISK.
+ * Modified some of the new defines to make them 32
+ * character unique.
+ * Modified how variable length pages (arrays) are defined.
+ * Added generic defines for hot spare pools and RAID
+ * volume types.
+ * 11-01-01 01.02.05 Added define for MPI_IOUNITPAGE1_DISABLE_IR.
* --------------------------------------------------------------------------
*/
fCONFIG_PAGE_HEADER_UNION, MPI_POINTER PTR_CONFIG_PAGE_HEADER_UNION;
-/****************************************************************************/
-/* PageType field values */
-/****************************************************************************/
+/****************************************************************************
+* PageType field values
+****************************************************************************/
#define MPI_CONFIG_PAGEATTR_READ_ONLY (0x00)
#define MPI_CONFIG_PAGEATTR_CHANGEABLE (0x10)
#define MPI_CONFIG_PAGEATTR_PERSISTENT (0x20)
+#define MPI_CONFIG_PAGEATTR_RO_PERSISTENT (0x30)
#define MPI_CONFIG_PAGEATTR_MASK (0xF0)
#define MPI_CONFIG_PAGETYPE_IO_UNIT (0x00)
#define MPI_CONFIG_PAGETYPE_LAN (0x07)
#define MPI_CONFIG_PAGETYPE_RAID_VOLUME (0x08)
#define MPI_CONFIG_PAGETYPE_MANUFACTURING (0x09)
+#define MPI_CONFIG_PAGETYPE_RAID_PHYSDISK (0x0A)
#define MPI_CONFIG_PAGETYPE_MASK (0x0F)
#define MPI_CONFIG_TYPENUM_MASK (0x0FFF)
/****************************************************************************
- * PageAddres field values
- ****************************************************************************/
+* PageAddress field values
+****************************************************************************/
#define MPI_SCSI_PORT_PGAD_PORT_MASK (0x000000FF)
-#define MPI_SCSI_DEVICE_FORM_MASK (0xF0000000)
-#define MPI_SCSI_DEVICE_FORM_TARGETID (0x00000000)
-#define MPI_SCSI_DEVICE_FORM_RAID_PHYS_DEV_NUM (0x10000000)
#define MPI_SCSI_DEVICE_TARGET_ID_MASK (0x000000FF)
#define MPI_SCSI_DEVICE_TARGET_ID_SHIFT (0)
#define MPI_SCSI_DEVICE_BUS_MASK (0x0000FF00)
#define MPI_SCSI_DEVICE_BUS_SHIFT (8)
-#define MPI_SCSI_DEVICE_VOLUME_TARG_ID_MASK (0x000000FF)
-#define MPI_SCSI_DEVICE_VOLUME_TARG_ID_SHIFT (0)
-#define MPI_SCSI_DEVICE_VOLUME_BUS_MASK (0x0000FF00)
-#define MPI_SCSI_DEVICE_VOLUME_BUS_SHIFT (8)
-#define MPI_SCSI_DEVICE_PHYS_DISK_NUM_MASK (0x00FF0000)
-#define MPI_SCSI_DEVICE_PHYS_DISK_NUM_SHIFT (16)
#define MPI_FC_PORT_PGAD_PORT_MASK (0xF0000000)
#define MPI_FC_PORT_PGAD_PORT_SHIFT (28)
#define MPI_FC_DEVICE_PGAD_BT_TID_MASK (0x000000FF)
#define MPI_FC_DEVICE_PGAD_BT_TID_SHIFT (0)
+#define MPI_PHYSDISK_PGAD_PHYSDISKNUM_MASK (0x000000FF)
+#define MPI_PHYSDISK_PGAD_PHYSDISKNUM_SHIFT (0)
+
+
-/****************************************************************************/
-/* Config Request Message */
-/****************************************************************************/
+/****************************************************************************
+* Config Request Message
+****************************************************************************/
typedef struct _MSG_CONFIG
{
U8 Action; /* 00h */
U8 MsgFlags; /* 07h */
U32 MsgContext; /* 08h */
U8 Reserved2[8]; /* 0Ch */
- fCONFIG_PAGE_HEADER Header; /* 14h */
+ fCONFIG_PAGE_HEADER Header; /* 14h */
U32 PageAddress; /* 18h */
SGE_IO_UNION PageBufferSGE; /* 1Ch */
} MSG_CONFIG, MPI_POINTER PTR_MSG_CONFIG,
Config_t, MPI_POINTER pConfig_t;
-/****************************************************************************/
-/* Action field values */
-/****************************************************************************/
+/****************************************************************************
+* Action field values
+****************************************************************************/
#define MPI_CONFIG_ACTION_PAGE_HEADER (0x00)
#define MPI_CONFIG_ACTION_PAGE_READ_CURRENT (0x01)
#define MPI_CONFIG_ACTION_PAGE_WRITE_CURRENT (0x02)
U8 Reserved2[2]; /* 0Ch */
U16 IOCStatus; /* 0Eh */
U32 IOCLogInfo; /* 10h */
- fCONFIG_PAGE_HEADER Header; /* 14h */
+ fCONFIG_PAGE_HEADER Header; /* 14h */
} MSG_CONFIG_REPLY, MPI_POINTER PTR_MSG_CONFIG_REPLY,
ConfigReply_t, MPI_POINTER pConfigReply_t;
*
*****************************************************************************/
-/****************************************************************************/
-/* Manufacturing Config pages */
-/****************************************************************************/
+/****************************************************************************
+* Manufacturing Config pages
+****************************************************************************/
#define MPI_MANUFACTPAGE_DEVICEID_FC909 (0x0621)
#define MPI_MANUFACTPAGE_DEVICEID_FC919 (0x0624)
#define MPI_MANUFACTPAGE_DEVICEID_FC929 (0x0622)
+#define MPI_MANUFACTPAGE_DEVICEID_FC919X (0x0628)
+#define MPI_MANUFACTPAGE_DEVICEID_FC929X (0x0626)
#define MPI_MANUFACTPAGE_DEVID_53C1030 (0x0030)
#define MPI_MANUFACTPAGE_DEVID_53C1030ZC (0x0031)
-#define MPI_MANUFACTPAGE_DEVID_53C1035 (0x0035)
+#define MPI_MANUFACTPAGE_DEVID_1030_53C1035 (0x0032)
+#define MPI_MANUFACTPAGE_DEVID_1030ZC_53C1035 (0x0033)
+#define MPI_MANUFACTPAGE_DEVID_53C1035 (0x0040)
+#define MPI_MANUFACTPAGE_DEVID_53C1035ZC (0x0041)
typedef struct _CONFIG_PAGE_MANUFACTURING_0
{
- fCONFIG_PAGE_HEADER Header; /* 00h */
+ fCONFIG_PAGE_HEADER Header; /* 00h */
U8 ChipName[16]; /* 04h */
U8 ChipRevision[8]; /* 14h */
U8 BoardName[16]; /* 1Ch */
typedef struct _CONFIG_PAGE_MANUFACTURING_1
{
- fCONFIG_PAGE_HEADER Header; /* 00h */
+ fCONFIG_PAGE_HEADER Header; /* 00h */
U8 VPD[256]; /* 04h */
} fCONFIG_PAGE_MANUFACTURING_1, MPI_POINTER PTR_CONFIG_PAGE_MANUFACTURING_1,
ManufacturingPage1_t, MPI_POINTER pManufacturingPage1_t;
MpiChipRevisionId_t, MPI_POINTER pMpiChipRevisionId_t;
+/*
+ * Host code (drivers, BIOS, utilities, etc.) should leave this define set to
+ * one and check Header.PageLength at runtime.
+ */
+#ifndef MPI_MAN_PAGE_2_HW_SETTINGS_WORDS
+#define MPI_MAN_PAGE_2_HW_SETTINGS_WORDS (1)
+#endif
+
typedef struct _CONFIG_PAGE_MANUFACTURING_2
{
- fCONFIG_PAGE_HEADER Header; /* 00h */
- MPI_CHIP_REVISION_ID ChipId; /* 04h */
- U32 HwSettings[1]; /* 08h */
+ fCONFIG_PAGE_HEADER Header; /* 00h */
+ MPI_CHIP_REVISION_ID ChipId; /* 04h */
+ U32 HwSettings[MPI_MAN_PAGE_2_HW_SETTINGS_WORDS];/* 08h */
} fCONFIG_PAGE_MANUFACTURING_2, MPI_POINTER PTR_CONFIG_PAGE_MANUFACTURING_2,
ManufacturingPage2_t, MPI_POINTER pManufacturingPage2_t;
#define MPI_MANUFACTURING2_PAGEVERSION (0x00)
+/*
+ * Host code (drivers, BIOS, utilities, etc.) should leave this define set to
+ * one and check Header.PageLength at runtime.
+ */
+#ifndef MPI_MAN_PAGE_3_INFO_WORDS
+#define MPI_MAN_PAGE_3_INFO_WORDS (1)
+#endif
+
typedef struct _CONFIG_PAGE_MANUFACTURING_3
{
- fCONFIG_PAGE_HEADER Header; /* 00h */
- MPI_CHIP_REVISION_ID ChipId; /* 04h */
- U32 Info[1]; /* 08h */
+ fCONFIG_PAGE_HEADER Header; /* 00h */
+ MPI_CHIP_REVISION_ID ChipId; /* 04h */
+ U32 Info[MPI_MAN_PAGE_3_INFO_WORDS];/* 08h */
} fCONFIG_PAGE_MANUFACTURING_3, MPI_POINTER PTR_CONFIG_PAGE_MANUFACTURING_3,
ManufacturingPage3_t, MPI_POINTER pManufacturingPage3_t;
#define MPI_MANUFACTURING3_PAGEVERSION (0x00)
-/****************************************************************************/
-/* IO Unit Config Pages */
-/****************************************************************************/
+typedef struct _CONFIG_PAGE_MANUFACTURING_4
+{
+ fCONFIG_PAGE_HEADER Header; /* 00h */
+ U32 Reserved1; /* 04h */
+ U8 InfoOffset0; /* 08h */
+ U8 InfoSize0; /* 09h */
+ U8 InfoOffset1; /* 0Ah */
+ U8 InfoSize1; /* 0Bh */
+ U8 InquirySize; /* 0Ch */
+ U8 Reserved2; /* 0Dh */
+ U16 Reserved3; /* 0Eh */
+ U8 InquiryData[56]; /* 10h */
+ U32 ISVolumeSettings; /* 48h */
+ U32 IMEVolumeSettings; /* 4Ch */
+ U32 IMVolumeSettings; /* 50h */
+} fCONFIG_PAGE_MANUFACTURING_4, MPI_POINTER PTR_CONFIG_PAGE_MANUFACTURING_4,
+ ManufacturingPage4_t, MPI_POINTER pManufacturingPage4_t;
+
+#define MPI_MANUFACTURING4_PAGEVERSION (0x00)
+
+
+/****************************************************************************
+* IO Unit Config Pages
+****************************************************************************/
typedef struct _CONFIG_PAGE_IO_UNIT_0
{
- fCONFIG_PAGE_HEADER Header; /* 00h */
+ fCONFIG_PAGE_HEADER Header; /* 00h */
U64 UniqueValue; /* 04h */
} fCONFIG_PAGE_IO_UNIT_0, MPI_POINTER PTR_CONFIG_PAGE_IO_UNIT_0,
IOUnitPage0_t, MPI_POINTER pIOUnitPage0_t;
typedef struct _CONFIG_PAGE_IO_UNIT_1
{
- fCONFIG_PAGE_HEADER Header; /* 00h */
+ fCONFIG_PAGE_HEADER Header; /* 00h */
U32 Flags; /* 04h */
} fCONFIG_PAGE_IO_UNIT_1, MPI_POINTER PTR_CONFIG_PAGE_IO_UNIT_1,
IOUnitPage1_t, MPI_POINTER pIOUnitPage1_t;
#define MPI_IOUNITPAGE1_PAGEVERSION (0x00)
+/* IO Unit Page 1 Flags defines */
+
#define MPI_IOUNITPAGE1_MULTI_FUNCTION (0x00000000)
#define MPI_IOUNITPAGE1_SINGLE_FUNCTION (0x00000001)
#define MPI_IOUNITPAGE1_MULTI_PATHING (0x00000002)
#define MPI_IOUNITPAGE1_SINGLE_PATHING (0x00000000)
-
+#define MPI_IOUNITPAGE1_DISABLE_IR (0x00000040)
#define MPI_IOUNITPAGE1_FORCE_32 (0x00000080)
typedef struct _CONFIG_PAGE_IO_UNIT_2
{
- fCONFIG_PAGE_HEADER Header; /* 00h */
+ fCONFIG_PAGE_HEADER Header; /* 00h */
U32 Flags; /* 04h */
U32 BiosVersion; /* 08h */
MPI_ADAPTER_INFO AdapterOrder[4]; /* 0Ch */
#define MPI_IOUNITPAGE2_PAGEVERSION (0x00)
-#define MPI_IOUNITPAGE2_FLAGS_RAID_DISABLE (0x00000001)
#define MPI_IOUNITPAGE2_FLAGS_PAUSE_ON_ERROR (0x00000002)
#define MPI_IOUNITPAGE2_FLAGS_VERBOSE_ENABLE (0x00000004)
#define MPI_IOUNITPAGE2_FLAGS_COLOR_VIDEO_DISABLE (0x00000008)
#define MPI_IOUNITPAGE2_FLAGS_DONT_HOOK_INT_40 (0x00000010)
+/*
+ * Host code (drivers, BIOS, utilities, etc.) should leave this define set to
+ * one and check Header.PageLength at runtime.
+ */
+#ifndef MPI_IO_UNIT_PAGE_3_GPIO_VAL_MAX
+#define MPI_IO_UNIT_PAGE_3_GPIO_VAL_MAX (1)
+#endif
+
typedef struct _CONFIG_PAGE_IO_UNIT_3
{
- fCONFIG_PAGE_HEADER Header; /* 00h */
- U32 VolumeSettings; /* 04h */
- U8 InfoOffset0; /* 08h */
- U8 InfoSize0; /* 09h */
- U8 InfoOffset1; /* 0Ah */
- U8 InfoSize1; /* 0Bh */
- U8 InquirySize; /* 0Ch */
- U8 Reserved; /* 0Dh */
- U16 Reserved2; /* 0Eh */
- U8 InquiryData[56]; /* 10h */
+ fCONFIG_PAGE_HEADER Header; /* 00h */
+ U8 GPIOCount; /* 04h */
+ U8 Reserved1; /* 05h */
+ U16 Reserved2; /* 06h */
+ U16 GPIOVal[MPI_IO_UNIT_PAGE_3_GPIO_VAL_MAX]; /* 08h */
} fCONFIG_PAGE_IO_UNIT_3, MPI_POINTER PTR_CONFIG_PAGE_IO_UNIT_3,
IOUnitPage3_t, MPI_POINTER pIOUnitPage3_t;
-#define MPI_IOUNITPAGE3_PAGEVERSION (0x00)
+#define MPI_IOUNITPAGE3_PAGEVERSION (0x01)
+#define MPI_IOUNITPAGE3_GPIO_FUNCTION_MASK (0xFC)
+#define MPI_IOUNITPAGE3_GPIO_FUNCTION_SHIFT (2)
+#define MPI_IOUNITPAGE3_GPIO_SETTING_OFF (0x00)
+#define MPI_IOUNITPAGE3_GPIO_SETTING_ON (0x01)
-/****************************************************************************/
-/* IOC Config Pages */
-/****************************************************************************/
+
+/****************************************************************************
+* IOC Config Pages
+****************************************************************************/
typedef struct _CONFIG_PAGE_IOC_0
{
- fCONFIG_PAGE_HEADER Header; /* 00h */
+ fCONFIG_PAGE_HEADER Header; /* 00h */
U32 TotalNVStore; /* 04h */
U32 FreeNVStore; /* 08h */
U16 VendorID; /* 0Ch */
typedef struct _CONFIG_PAGE_IOC_1
{
- fCONFIG_PAGE_HEADER Header; /* 00h */
+ fCONFIG_PAGE_HEADER Header; /* 00h */
U32 Flags; /* 04h */
U32 CoalescingTimeout; /* 08h */
U8 CoalescingDepth; /* 0Ch */
typedef struct _CONFIG_PAGE_IOC_2_RAID_VOL
{
- U8 VolumeTargetID; /* 00h */
- U8 VolumeBus; /* 01h */
- U16 Reserved; /* 02h */
- U8 VolumeVersionMinor; /* 04h */
- U8 VolumeVersionMajor; /* 05h */
- U8 VolumeRaidType; /* 06h */
- U8 Reserved1; /* 07h */
+ U8 VolumeID; /* 00h */
+ U8 VolumeBus; /* 01h */
+ U8 VolumeIOC; /* 02h */
+ U8 VolumePageNumber; /* 03h */
+ U8 VolumeType; /* 04h */
+ U8 Reserved2; /* 05h */
+ U16 Reserved3; /* 06h */
} fCONFIG_PAGE_IOC_2_RAID_VOL, MPI_POINTER PTR_CONFIG_PAGE_IOC_2_RAID_VOL,
ConfigPageIoc2RaidVol_t, MPI_POINTER pConfigPageIoc2RaidVol_t;
+/*
+ * Host code (drivers, BIOS, utilities, etc.) should leave this define set to
+ * one and check Header.PageLength at runtime.
+ */
+#ifndef MPI_IOC_PAGE_2_RAID_VOLUME_MAX
+#define MPI_IOC_PAGE_2_RAID_VOLUME_MAX (1)
+#endif
+
typedef struct _CONFIG_PAGE_IOC_2
{
- fCONFIG_PAGE_HEADER Header; /* 00h */
- U32 CapabilitiesFlags; /* 04h */
- U8 NumActiveVolumes; /* 08h */
- U8 MaxVolumes; /* 09h */
- U16 Reserved; /* 0Ah */
- fCONFIG_PAGE_IOC_2_RAID_VOL RaidVolume[1]; /* 0Ch */
+ fCONFIG_PAGE_HEADER Header; /* 00h */
+ U32 CapabilitiesFlags; /* 04h */
+ U8 NumActiveVolumes; /* 08h */
+ U8 MaxVolumes; /* 09h */
+ U8 NumActivePhysDisks; /* 0Ah */
+ U8 MaxPhysDisks; /* 0Bh */
+ fCONFIG_PAGE_IOC_2_RAID_VOL RaidVolume[MPI_IOC_PAGE_2_RAID_VOLUME_MAX];/* 0Ch */
} fCONFIG_PAGE_IOC_2, MPI_POINTER PTR_CONFIG_PAGE_IOC_2,
IOCPage2_t, MPI_POINTER pIOCPage2_t;
-#define MPI_IOCPAGE2_PAGEVERSION (0x00)
+#define MPI_IOCPAGE2_PAGEVERSION (0x01)
/* IOC Page 2 Capabilities flags */
-#define MPI_IOCPAGE2_CAP_FLAGS_RAID_0_SUPPORT (0x00000001)
-#define MPI_IOCPAGE2_CAP_FLAGS_RAID_1_SUPPORT (0x00000002)
-#define MPI_IOCPAGE2_CAP_FLAGS_LSI_MIRROR_SUPPORT (0x00000004)
-#define MPI_IOCPAGE2_CAP_FLAGS_RAID_5_SUPPORT (0x00000008)
-#define MPI_IOCPAGE2_CAP_FLAGS_RAID_10_SUPPORT (0x00000010)
+#define MPI_IOCPAGE2_CAP_FLAGS_IS_SUPPORT (0x00000001)
+#define MPI_IOCPAGE2_CAP_FLAGS_IME_SUPPORT (0x00000002)
+#define MPI_IOCPAGE2_CAP_FLAGS_IM_SUPPORT (0x00000004)
+#define MPI_IOCPAGE2_CAP_FLAGS_SES_SUPPORT (0x20000000)
+#define MPI_IOCPAGE2_CAP_FLAGS_SAFTE_SUPPORT (0x40000000)
+#define MPI_IOCPAGE2_CAP_FLAGS_CROSS_CHANNEL_SUPPORT (0x80000000)
+
+/* IOC Page 2 Volume RAID Type values, also used in RAID Volume pages */
+
+#define MPI_RAID_VOL_TYPE_IS (0x00)
+#define MPI_RAID_VOL_TYPE_IME (0x01)
+#define MPI_RAID_VOL_TYPE_IM (0x02)
+
+
+typedef struct _IOC_3_PHYS_DISK
+{
+ U8 PhysDiskID; /* 00h */
+ U8 PhysDiskBus; /* 01h */
+ U8 PhysDiskIOC; /* 02h */
+ U8 PhysDiskNum; /* 03h */
+} IOC_3_PHYS_DISK, MPI_POINTER PTR_IOC_3_PHYS_DISK,
+ Ioc3PhysDisk_t, MPI_POINTER pIoc3PhysDisk_t;
+
+/*
+ * Host code (drivers, BIOS, utilities, etc.) should leave this define set to
+ * one and check Header.PageLength at runtime.
+ */
+#ifndef MPI_IOC_PAGE_3_PHYSDISK_MAX
+#define MPI_IOC_PAGE_3_PHYSDISK_MAX (1)
+#endif
+
+typedef struct _CONFIG_PAGE_IOC_3
+{
+ fCONFIG_PAGE_HEADER Header; /* 00h */
+ U8 NumPhysDisks; /* 04h */
+ U8 Reserved1; /* 05h */
+ U16 Reserved2; /* 06h */
+ IOC_3_PHYS_DISK PhysDisk[MPI_IOC_PAGE_3_PHYSDISK_MAX]; /* 08h */
+} fCONFIG_PAGE_IOC_3, MPI_POINTER PTR_CONFIG_PAGE_IOC_3,
+ IOCPage3_t, MPI_POINTER pIOCPage3_t;
+
+#define MPI_IOCPAGE3_PAGEVERSION (0x00)
-/* IOC Page 2 Volume RAID Type values */
-#define MPI_IOCPAGE2_VOL_TYPE_RAID_0 (0x00)
-#define MPI_IOCPAGE2_VOL_TYPE_RAID_1 (0x01)
-#define MPI_IOCPAGE2_VOL_TYPE_LSI_MIRROR (0x02)
-#define MPI_IOCPAGE2_VOL_TYPE_RAID_5 (0x05)
-#define MPI_IOCPAGE2_VOL_TYPE_RAID_10 (0x0A)
+typedef struct _IOC_4_SEP
+{
+ U8 SEPTargetID; /* 00h */
+ U8 SEPBus; /* 01h */
+ U16 Reserved; /* 02h */
+} IOC_4_SEP, MPI_POINTER PTR_IOC_4_SEP,
+ Ioc4Sep_t, MPI_POINTER pIoc4Sep_t;
+/*
+ * Host code (drivers, BIOS, utilities, etc.) should leave this define set to
+ * one and check Header.PageLength at runtime.
+ */
+#ifndef MPI_IOC_PAGE_4_SEP_MAX
+#define MPI_IOC_PAGE_4_SEP_MAX (1)
+#endif
-/****************************************************************************/
-/* SCSI Port Config Pages */
-/****************************************************************************/
+typedef struct _CONFIG_PAGE_IOC_4
+{
+ fCONFIG_PAGE_HEADER Header; /* 00h */
+ U8 ActiveSEP; /* 04h */
+ U8 MaxSEP; /* 05h */
+ U16 Reserved1; /* 06h */
+ IOC_4_SEP SEP[MPI_IOC_PAGE_4_SEP_MAX]; /* 08h */
+} fCONFIG_PAGE_IOC_4, MPI_POINTER PTR_CONFIG_PAGE_IOC_4,
+ IOCPage4_t, MPI_POINTER pIOCPage4_t;
+
+#define MPI_IOCPAGE4_PAGEVERSION (0x00)
+
+
+/****************************************************************************
+* SCSI Port Config Pages
+****************************************************************************/
typedef struct _CONFIG_PAGE_SCSI_PORT_0
{
- fCONFIG_PAGE_HEADER Header; /* 00h */
+ fCONFIG_PAGE_HEADER Header; /* 00h */
U32 Capabilities; /* 04h */
U32 PhysicalInterface; /* 08h */
} fCONFIG_PAGE_SCSI_PORT_0, MPI_POINTER PTR_CONFIG_PAGE_SCSI_PORT_0,
#define MPI_SCSIPORTPAGE0_CAP_IU (0x00000001)
#define MPI_SCSIPORTPAGE0_CAP_DT (0x00000002)
#define MPI_SCSIPORTPAGE0_CAP_QAS (0x00000004)
-#define MPI_SCSIPORTPAGE0_CAP_PACING_TRANSFERS (0x00000008)
#define MPI_SCSIPORTPAGE0_CAP_MIN_SYNC_PERIOD_MASK (0x0000FF00)
#define MPI_SCSIPORTPAGE0_CAP_MAX_SYNC_OFFSET_MASK (0x00FF0000)
#define MPI_SCSIPORTPAGE0_CAP_WIDE (0x20000000)
typedef struct _CONFIG_PAGE_SCSI_PORT_1
{
- fCONFIG_PAGE_HEADER Header; /* 00h */
+ fCONFIG_PAGE_HEADER Header; /* 00h */
U32 Configuration; /* 04h */
+ U32 OnBusTimerValue; /* 08h */
} fCONFIG_PAGE_SCSI_PORT_1, MPI_POINTER PTR_CONFIG_PAGE_SCSI_PORT_1,
SCSIPortPage1_t, MPI_POINTER pSCSIPortPage1_t;
-#define MPI_SCSIPORTPAGE1_PAGEVERSION (0x01)
+#define MPI_SCSIPORTPAGE1_PAGEVERSION (0x02)
#define MPI_SCSIPORTPAGE1_CFG_PORT_SCSI_ID_MASK (0x000000FF)
#define MPI_SCSIPORTPAGE1_CFG_PORT_RESPONSE_ID_MASK (0xFFFF0000)
typedef struct _CONFIG_PAGE_SCSI_PORT_2
{
- fCONFIG_PAGE_HEADER Header; /* 00h */
+ fCONFIG_PAGE_HEADER Header; /* 00h */
U32 PortFlags; /* 04h */
U32 PortSettings; /* 08h */
MPI_DEVICE_INFO DeviceSettings[16]; /* 0Ch */
#define MPI_SCSIPORTPAGE2_PAGEVERSION (0x01)
#define MPI_SCSIPORTPAGE2_PORT_FLAGS_SCAN_HIGH_TO_LOW (0x00000001)
-#define MPI_SCSIPORTPAGE2_PORT_FLAGS_PARITY_ENABLE (0x00000002)
#define MPI_SCSIPORTPAGE2_PORT_FLAGS_AVOID_SCSI_RESET (0x00000004)
#define MPI_SCSIPORTPAGE2_PORT_FLAGS_ALTERNATE_CHS (0x00000008)
#define MPI_SCSIPORTPAGE2_PORT_FLAGS_TERMINATION_DISABLE (0x00000010)
#define MPI_SCSIPORTPAGE2_DEVICE_BOOT_CHOICE (0x0020)
-/****************************************************************************/
-/* SCSI Target Device Config Pages */
-/****************************************************************************/
+/****************************************************************************
+* SCSI Target Device Config Pages
+****************************************************************************/
typedef struct _CONFIG_PAGE_SCSI_DEVICE_0
{
- fCONFIG_PAGE_HEADER Header; /* 00h */
+ fCONFIG_PAGE_HEADER Header; /* 00h */
U32 NegotiatedParameters; /* 04h */
U32 Information; /* 08h */
} fCONFIG_PAGE_SCSI_DEVICE_0, MPI_POINTER PTR_CONFIG_PAGE_SCSI_DEVICE_0,
SCSIDevicePage0_t, MPI_POINTER pSCSIDevicePage0_t;
-#define MPI_SCSIDEVPAGE0_PAGEVERSION (0x01)
+#define MPI_SCSIDEVPAGE0_PAGEVERSION (0x02)
#define MPI_SCSIDEVPAGE0_NP_IU (0x00000001)
#define MPI_SCSIDEVPAGE0_NP_DT (0x00000002)
#define MPI_SCSIDEVPAGE0_NP_QAS (0x00000004)
-#define MPI_SCSIDEVPAGE0_NP_PACING_TRANSFERS (0x00000008)
#define MPI_SCSIDEVPAGE0_NP_NEG_SYNC_PERIOD_MASK (0x0000FF00)
#define MPI_SCSIDEVPAGE0_NP_NEG_SYNC_OFFSET_MASK (0x00FF0000)
#define MPI_SCSIDEVPAGE0_NP_WIDE (0x20000000)
#define MPI_SCSIDEVPAGE0_NP_AIP (0x80000000)
#define MPI_SCSIDEVPAGE0_INFO_PARAMS_NEGOTIATED (0x00000001)
+#define MPI_SCSIDEVPAGE0_INFO_SDTR_REJECTED (0x00000002)
+#define MPI_SCSIDEVPAGE0_INFO_WDTR_REJECTED (0x00000004)
+#define MPI_SCSIDEVPAGE0_INFO_PPR_REJECTED (0x00000008)
typedef struct _CONFIG_PAGE_SCSI_DEVICE_1
{
- fCONFIG_PAGE_HEADER Header; /* 00h */
+ fCONFIG_PAGE_HEADER Header; /* 00h */
U32 RequestedParameters; /* 04h */
U32 Reserved; /* 08h */
U32 Configuration; /* 0Ch */
} fCONFIG_PAGE_SCSI_DEVICE_1, MPI_POINTER PTR_CONFIG_PAGE_SCSI_DEVICE_1,
SCSIDevicePage1_t, MPI_POINTER pSCSIDevicePage1_t;
-#define MPI_SCSIDEVPAGE1_PAGEVERSION (0x02)
+#define MPI_SCSIDEVPAGE1_PAGEVERSION (0x03)
#define MPI_SCSIDEVPAGE1_RP_IU (0x00000001)
#define MPI_SCSIDEVPAGE1_RP_DT (0x00000002)
#define MPI_SCSIDEVPAGE1_RP_QAS (0x00000004)
-#define MPI_SCSIDEVPAGE1_RP_PACING_TRANSFERS (0x00000008)
#define MPI_SCSIDEVPAGE1_RP_MIN_SYNC_PERIOD_MASK (0x0000FF00)
#define MPI_SCSIDEVPAGE1_RP_MAX_SYNC_OFFSET_MASK (0x00FF0000)
#define MPI_SCSIDEVPAGE1_RP_WIDE (0x20000000)
#define MPI_SCSIDEVPAGE1_DV_LVD_DRIVE_STRENGTH_MASK (0x00000003)
#define MPI_SCSIDEVPAGE1_DV_SE_SLEW_RATE_MASK (0x00000300)
-#define MPI_SCSIDEVPAGE1_CONF_PPR_ALLOWED (0x00000001)
+#define MPI_SCSIDEVPAGE1_CONF_WDTR_DISALLOWED (0x00000002)
+#define MPI_SCSIDEVPAGE1_CONF_SDTR_DISALLOWED (0x00000004)
typedef struct _CONFIG_PAGE_SCSI_DEVICE_2
{
- fCONFIG_PAGE_HEADER Header; /* 00h */
+ fCONFIG_PAGE_HEADER Header; /* 00h */
U32 DomainValidation; /* 04h */
U32 ParityPipeSelect; /* 08h */
U32 DataPipeSelect; /* 0Ch */
#define MPI_SCSIDEVPAGE2_DPS_BIT_15_PL_SELECT_MASK (0xC0000000)
-/****************************************************************************/
-/* FC Port Config Pages */
-/****************************************************************************/
+/****************************************************************************
+* FC Port Config Pages
+****************************************************************************/
typedef struct _CONFIG_PAGE_FC_PORT_0
{
- fCONFIG_PAGE_HEADER Header; /* 00h */
+ fCONFIG_PAGE_HEADER Header; /* 00h */
U32 Flags; /* 04h */
U8 MPIPortNumber; /* 08h */
U8 LinkType; /* 09h */
typedef struct _CONFIG_PAGE_FC_PORT_1
{
- fCONFIG_PAGE_HEADER Header; /* 00h */
+ fCONFIG_PAGE_HEADER Header; /* 00h */
U32 Flags; /* 04h */
U64 NoSEEPROMWWNN; /* 08h */
U64 NoSEEPROMWWPN; /* 10h */
} fCONFIG_PAGE_FC_PORT_1, MPI_POINTER PTR_CONFIG_PAGE_FC_PORT_1,
FCPortPage1_t, MPI_POINTER pFCPortPage1_t;
-#define MPI_FCPORTPAGE1_PAGEVERSION (0x01)
+#define MPI_FCPORTPAGE1_PAGEVERSION (0x02)
+#define MPI_FCPORTPAGE1_FLAGS_EXT_FCP_STATUS_EN (0x08000000)
+#define MPI_FCPORTPAGE1_FLAGS_IMMEDIATE_ERROR_REPLY (0x04000000)
#define MPI_FCPORTPAGE1_FLAGS_SORT_BY_DID (0x00000001)
#define MPI_FCPORTPAGE1_FLAGS_SORT_BY_WWN (0x00000000)
#define MPI_FCPORTPAGE1_LCONFIG_SPEED_10GIG (0x03)
#define MPI_FCPORTPAGE1_LCONFIG_SPEED_AUTO (0x0F)
-#define MPI_FCPORTPAGE1_TOPOLGY_MASK (0x0F)
-#define MPI_FCPORTPAGE1_TOPOLGY_NLPORT (0x01)
-#define MPI_FCPORTPAGE1_TOPOLGY_NPORT (0x02)
-#define MPI_FCPORTPAGE1_TOPOLGY_AUTO (0x0F)
+#define MPI_FCPORTPAGE1_TOPOLOGY_MASK (0x0F)
+#define MPI_FCPORTPAGE1_TOPOLOGY_NLPORT (0x01)
+#define MPI_FCPORTPAGE1_TOPOLOGY_NPORT (0x02)
+#define MPI_FCPORTPAGE1_TOPOLOGY_AUTO (0x0F)
typedef struct _CONFIG_PAGE_FC_PORT_2
{
- fCONFIG_PAGE_HEADER Header; /* 00h */
+ fCONFIG_PAGE_HEADER Header; /* 00h */
U8 NumberActive; /* 04h */
- U8 ALPA[126]; /* 05h */
- U8 Reserved; /* 83h */
+ U8 ALPA[127]; /* 05h */
} fCONFIG_PAGE_FC_PORT_2, MPI_POINTER PTR_CONFIG_PAGE_FC_PORT_2,
FCPortPage2_t, MPI_POINTER pFCPortPage2_t;
-#define MPI_FCPORTPAGE2_PAGEVERSION (0x00)
+#define MPI_FCPORTPAGE2_PAGEVERSION (0x01)
typedef struct _WWN_FORMAT
#define MPI_PERSISTENT_FLAGS_BOOT_DEVICE (0x0008)
#define MPI_PERSISTENT_FLAGS_BY_DID (0x0080)
+/*
+ * Host code (drivers, BIOS, utilities, etc.) should leave this define set to
+ * one and check Header.PageLength at runtime.
+ */
+#ifndef MPI_FC_PORT_PAGE_3_ENTRY_MAX
+#define MPI_FC_PORT_PAGE_3_ENTRY_MAX (1)
+#endif
+
typedef struct _CONFIG_PAGE_FC_PORT_3
{
- fCONFIG_PAGE_HEADER Header; /* 00h */
- FC_PORT_PERSISTENT Entry[1]; /* 04h */
+ fCONFIG_PAGE_HEADER Header; /* 00h */
+ FC_PORT_PERSISTENT Entry[MPI_FC_PORT_PAGE_3_ENTRY_MAX]; /* 04h */
} fCONFIG_PAGE_FC_PORT_3, MPI_POINTER PTR_CONFIG_PAGE_FC_PORT_3,
FCPortPage3_t, MPI_POINTER pFCPortPage3_t;
typedef struct _CONFIG_PAGE_FC_PORT_4
{
- fCONFIG_PAGE_HEADER Header; /* 00h */
+ fCONFIG_PAGE_HEADER Header; /* 00h */
U32 PortFlags; /* 04h */
U32 PortSettings; /* 08h */
} fCONFIG_PAGE_FC_PORT_4, MPI_POINTER PTR_CONFIG_PAGE_FC_PORT_4,
U16 Reserved; /* 02h */
U64 AliasWWNN; /* 04h */
U64 AliasWWPN; /* 0Ch */
-} fCONFIG_PAGE_FC_PORT_5_ALIAS_INFO, MPI_POINTER PTR_CONFIG_PAGE_FC_PORT_5_ALIAS_INFO,
+} fCONFIG_PAGE_FC_PORT_5_ALIAS_INFO,
+ MPI_POINTER PTR_CONFIG_PAGE_FC_PORT_5_ALIAS_INFO,
FcPortPage5AliasInfo_t, MPI_POINTER pFcPortPage5AliasInfo_t;
+/*
+ * Host code (drivers, BIOS, utilities, etc.) should leave this define set to
+ * one and check Header.PageLength at runtime.
+ */
+#ifndef MPI_FC_PORT_PAGE_5_ALIAS_MAX
+#define MPI_FC_PORT_PAGE_5_ALIAS_MAX (1)
+#endif
+
typedef struct _CONFIG_PAGE_FC_PORT_5
{
- fCONFIG_PAGE_HEADER Header; /* 00h */
- fCONFIG_PAGE_FC_PORT_5_ALIAS_INFO AliasInfo[1]; /* 04h */
+ fCONFIG_PAGE_HEADER Header; /* 00h */
+ fCONFIG_PAGE_FC_PORT_5_ALIAS_INFO AliasInfo[MPI_FC_PORT_PAGE_5_ALIAS_MAX];/* 04h */
} fCONFIG_PAGE_FC_PORT_5, MPI_POINTER PTR_CONFIG_PAGE_FC_PORT_5,
FCPortPage5_t, MPI_POINTER pFCPortPage5_t;
typedef struct _CONFIG_PAGE_FC_PORT_6
{
- fCONFIG_PAGE_HEADER Header; /* 00h */
+ fCONFIG_PAGE_HEADER Header; /* 00h */
U32 Reserved; /* 04h */
U64 TimeSinceReset; /* 08h */
U64 TxFrames; /* 10h */
typedef struct _CONFIG_PAGE_FC_PORT_7
{
- fCONFIG_PAGE_HEADER Header; /* 00h */
+ fCONFIG_PAGE_HEADER Header; /* 00h */
U32 Reserved; /* 04h */
U8 PortSymbolicName[256]; /* 08h */
} fCONFIG_PAGE_FC_PORT_7, MPI_POINTER PTR_CONFIG_PAGE_FC_PORT_7,
typedef struct _CONFIG_PAGE_FC_PORT_8
{
- fCONFIG_PAGE_HEADER Header; /* 00h */
+ fCONFIG_PAGE_HEADER Header; /* 00h */
U32 BitVector[8]; /* 04h */
} fCONFIG_PAGE_FC_PORT_8, MPI_POINTER PTR_CONFIG_PAGE_FC_PORT_8,
FCPortPage8_t, MPI_POINTER pFCPortPage8_t;
typedef struct _CONFIG_PAGE_FC_PORT_9
{
- fCONFIG_PAGE_HEADER Header; /* 00h */
+ fCONFIG_PAGE_HEADER Header; /* 00h */
U32 Reserved; /* 04h */
U64 GlobalWWPN; /* 08h */
U64 GlobalWWNN; /* 10h */
#define MPI_FCPORTPAGE9_PAGEVERSION (0x00)
-/****************************************************************************/
-/* FC Device Config Pages */
-/****************************************************************************/
+/****************************************************************************
+* FC Device Config Pages
+****************************************************************************/
typedef struct _CONFIG_PAGE_FC_DEVICE_0
{
- fCONFIG_PAGE_HEADER Header; /* 00h */
+ fCONFIG_PAGE_HEADER Header; /* 00h */
U64 WWNN; /* 04h */
U64 WWPN; /* 0Ch */
U32 PortIdentifier; /* 14h */
#define MPI_FC_DEVICE_PAGE0_PROT_FCP_TARGET (0x02)
#define MPI_FC_DEVICE_PAGE0_PROT_FCP_INITIATOR (0x04)
-#define MPI_FC_DEVICE_PAGE0_PGAD_PORT_MASK (MPI_FC_DEVICE_PGAD_PORT_MASK)
-#define MPI_FC_DEVICE_PAGE0_PGAD_FORM_MASK (MPI_FC_DEVICE_PGAD_FORM_MASK)
-#define MPI_FC_DEVICE_PAGE0_PGAD_FORM_NEXT_DID (MPI_FC_DEVICE_PGAD_FORM_NEXT_DID)
-#define MPI_FC_DEVICE_PAGE0_PGAD_FORM_BUS_TID (MPI_FC_DEVICE_PGAD_FORM_BUS_TID)
-#define MPI_FC_DEVICE_PAGE0_PGAD_DID_MASK (MPI_FC_DEVICE_PGAD_ND_DID_MASK)
-#define MPI_FC_DEVICE_PAGE0_PGAD_BUS_MASK (MPI_FC_DEVICE_PGAD_BT_BUS_MASK)
-#define MPI_FC_DEVICE_PAGE0_PGAD_BUS_SHIFT (MPI_FC_DEVICE_PGAD_BT_BUS_SHIFT)
-#define MPI_FC_DEVICE_PAGE0_PGAD_TID_MASK (MPI_FC_DEVICE_PGAD_BT_TID_MASK)
+#define MPI_FC_DEVICE_PAGE0_PGAD_PORT_MASK (MPI_FC_DEVICE_PGAD_PORT_MASK)
+#define MPI_FC_DEVICE_PAGE0_PGAD_FORM_MASK (MPI_FC_DEVICE_PGAD_FORM_MASK)
+#define MPI_FC_DEVICE_PAGE0_PGAD_FORM_NEXT_DID (MPI_FC_DEVICE_PGAD_FORM_NEXT_DID)
+#define MPI_FC_DEVICE_PAGE0_PGAD_FORM_BUS_TID (MPI_FC_DEVICE_PGAD_FORM_BUS_TID)
+#define MPI_FC_DEVICE_PAGE0_PGAD_DID_MASK (MPI_FC_DEVICE_PGAD_ND_DID_MASK)
+#define MPI_FC_DEVICE_PAGE0_PGAD_BUS_MASK (MPI_FC_DEVICE_PGAD_BT_BUS_MASK)
+#define MPI_FC_DEVICE_PAGE0_PGAD_BUS_SHIFT (MPI_FC_DEVICE_PGAD_BT_BUS_SHIFT)
+#define MPI_FC_DEVICE_PAGE0_PGAD_TID_MASK (MPI_FC_DEVICE_PGAD_BT_TID_MASK)
-/****************************************************************************/
-/* RAID Volume Config Pages */
-/****************************************************************************/
+/****************************************************************************
+* RAID Volume Config Pages
+****************************************************************************/
-typedef struct _RAIDVOL2_IM_PHYS_ID
+typedef struct _RAID_VOL0_PHYS_DISK
{
- U8 TargetID; /* 00h */
- U8 Bus; /* 01h */
- U8 IocNumber; /* 02h */
- U8 PhysDiskNumber; /* 03h */
- U8 Reserved[8]; /* 04h */
- U8 PhysicalDiskIdentifier[16]; /* 0Ch */
- U8 VendorId[8]; /* 1Ch */
- U8 ProductId[16]; /* 24h */
- U8 ProductRevLevel[4]; /* 34h */
- U32 Reserved1; /* 38h */
- U8 Info[32]; /* 3Ch */
-} RAIDVOL2_IM_PHYS_ID, MPI_POINTER PTR_RAIDVOL2_IM_PHYS_ID,
- RaidVol2ImPhysicalID_t, MPI_POINTER pRaidVol2ImPhysicalID_t;
-
-typedef struct _RAIDVOL2_IM_DISK_INFO
+ U16 Reserved; /* 00h */
+ U8 PhysDiskMap; /* 02h */
+ U8 PhysDiskNum; /* 03h */
+} RAID_VOL0_PHYS_DISK, MPI_POINTER PTR_RAID_VOL0_PHYS_DISK,
+ RaidVol0PhysDisk_t, MPI_POINTER pRaidVol0PhysDisk_t;
+
+#define MPI_RAIDVOL0_PHYSDISK_PRIMARY (0x01)
+#define MPI_RAIDVOL0_PHYSDISK_SECONDARY (0x02)
+
+typedef struct _RAID_VOL0_STATUS
{
- U32 DiskStatus; /* 00h */
- U32 DeviceSettings; /* 04h */
- U16 ErrorCount; /* 08h */
- U16 Reserved; /* 0Ah */
- U8 ErrorCdbByte; /* 0Ch */
- U8 ErrorSenseKey; /* 0Dh */
- U8 ErrorASC; /* 0Eh */
- U8 ErrorASCQ; /* 0Fh */
- U16 SmartCount; /* 10h */
- U8 SmartASC; /* 12h */
- U8 SmartASCQ; /* 13h */
-} RAIDVOL2_IM_DISK_INFO, MPI_POINTER PTR_RAIDVOL2_IM_DISK_INFO,
- RaidVol2ImDiskInfo_t, MPI_POINTER pRaidVol2ImDiskInfo_t;
+ U8 Flags; /* 00h */
+ U8 State; /* 01h */
+ U16 Reserved; /* 02h */
+} RAID_VOL0_STATUS, MPI_POINTER PTR_RAID_VOL0_STATUS,
+ RaidVol0Status_t, MPI_POINTER pRaidVol0Status_t;
-/* RAID Volume 2 IM Physical Disk DiskStatus flags */
+/* RAID Volume Page 0 VolumeStatus defines */
+
+#define MPI_RAIDVOL0_STATUS_FLAG_ENABLED (0x01)
+#define MPI_RAIDVOL0_STATUS_FLAG_QUIESCED (0x02)
+#define MPI_RAIDVOL0_STATUS_FLAG_RESYNC_IN_PROGRESS (0x04)
-#define MPI_RVP2_PHYS_DISK_PRIMARY (0x00000001)
-#define MPI_RVP2_PHYS_DISK_SECONDARY (0x00000002)
-#define MPI_RVP2_PHYS_DISK_HOT_SPARE (0x00000004)
-#define MPI_RVP2_PHYS_DISK_OUT_OF_SYNC (0x00000008)
-#define MPI_RVP2_PHYS_DISK_STATUS_MASK (0x00000F00)
-#define MPI_RVP2_PHYS_DISK_STATUS_ONLINE (0x00000000)
-#define MPI_RVP2_PHYS_DISK_STATUS_MISSING (0x00000100)
-#define MPI_RVP2_PHYS_DISK_STATUS_NOT_COMPATIBLE (0x00000200)
-#define MPI_RVP2_PHYS_DISK_STATUS_FAILED (0x00000300)
-#define MPI_RVP2_PHYS_DISK_STATUS_INITIALIZING (0x00000400)
-#define MPI_RVP2_PHYS_DISK_STATUS_OFFLINE_REQUESTED (0x00000500)
-#define MPI_RVP2_PHYS_DISK_STATUS_OTHER_OFFLINE (0x00000F00)
-
-
-typedef struct _RAIDVOL2_IM_PHYSICAL_DISK
+#define MPI_RAIDVOL0_STATUS_STATE_OPTIMAL (0x00)
+#define MPI_RAIDVOL0_STATUS_STATE_DEGRADED (0x01)
+#define MPI_RAIDVOL0_STATUS_STATE_FAILED (0x02)
+
+typedef struct _RAID_VOL0_SETTINGS
{
- RAIDVOL2_IM_PHYS_ID Id; /* 00h */
- RAIDVOL2_IM_DISK_INFO Info; /* 5Ch */
-} RAIDVOL2_IM_PHYSICAL_DISK, MPI_POINTER PTR_RAIDVOL2_IM_PHYSICAL_DISK,
- RaidVol2ImPhysicalDisk_t, MPI_POINTER pRaidVol2ImPhysicalDisk_t;
+ U16 Settings; /* 00h */
+ U8 HotSparePool; /* 01h */ /* MPI_RAID_HOT_SPARE_POOL_ */
+ U8 Reserved; /* 02h */
+} RAID_VOL0_SETTINGS, MPI_POINTER PTR_RAID_VOL0_SETTINGS,
+ RaidVol0Settings, MPI_POINTER pRaidVol0Settings;
+
+/* RAID Volume Page 0 VolumeSettings defines */
+
+#define MPI_RAIDVOL0_SETTING_WRITE_CACHING_ENABLE (0x0001)
+#define MPI_RAIDVOL0_SETTING_OFFLINE_ON_SMART (0x0002)
+#define MPI_RAIDVOL0_SETTING_AUTO_CONFIGURE (0x0004)
+#define MPI_RAIDVOL0_SETTING_PRIORITY_RESYNC (0x0008)
+#define MPI_RAIDVOL0_SETTING_USE_PRODUCT_ID_SUFFIX (0x0010)
+#define MPI_RAIDVOL0_SETTING_USE_DEFAULTS (0x8000)
+
+/* RAID Volume Page 0 HotSparePool defines, also used in RAID Physical Disk */
+#define MPI_RAID_HOT_SPARE_POOL_0 (0x01)
+#define MPI_RAID_HOT_SPARE_POOL_1 (0x02)
+#define MPI_RAID_HOT_SPARE_POOL_2 (0x04)
+#define MPI_RAID_HOT_SPARE_POOL_3 (0x08)
+#define MPI_RAID_HOT_SPARE_POOL_4 (0x10)
+#define MPI_RAID_HOT_SPARE_POOL_5 (0x20)
+#define MPI_RAID_HOT_SPARE_POOL_6 (0x40)
+#define MPI_RAID_HOT_SPARE_POOL_7 (0x80)
-#define MPI_RAIDVOLPAGE2_MAX_DISKS (3)
+/*
+ * Host code (drivers, BIOS, utilities, etc.) should leave this define set to
+ * one and check Header.PageLength at runtime.
+ */
+#ifndef MPI_RAID_VOL_PAGE_0_PHYSDISK_MAX
+#define MPI_RAID_VOL_PAGE_0_PHYSDISK_MAX (1)
+#endif
-typedef struct _CONFIG_PAGE_RAID_VOL_2
+typedef struct _CONFIG_PAGE_RAID_VOL_0
{
- fCONFIG_PAGE_HEADER Header; /* 00h */
- U32 VolumeStatus; /* 04h */
- U32 VolumeSettings; /* 08h */
- U32 Reserved; /* 0Ch */
- U64 MaxLba; /* 10h */
- U32 BlockSize; /* 18h */
- U8 Reserved1; /* 1Ch */
- U8 NumPhysicalDisks; /* 1Dh */
- U16 Reserved2; /* 1Eh */
- RAIDVOL2_IM_PHYSICAL_DISK IMPhysicalDisk[MPI_RAIDVOLPAGE2_MAX_DISKS];
-} fCONFIG_PAGE_RAID_VOL_2, MPI_POINTER PTR_CONFIG_PAGE_RAID_VOL_2,
- RaidVolumePage2_t, MPI_POINTER pRaidVolumePage2_t;
+ fCONFIG_PAGE_HEADER Header; /* 00h */
+ U8 VolumeID; /* 04h */
+ U8 VolumeBus; /* 05h */
+ U8 VolumeIOC; /* 06h */
+ U8 VolumeType; /* 07h */ /* MPI_RAID_VOL_TYPE_ */
+ RAID_VOL0_STATUS VolumeStatus; /* 08h */
+ RAID_VOL0_SETTINGS VolumeSettings; /* 0Ch */
+ U32 MaxLBA; /* 10h */
+ U32 Reserved1; /* 14h */
+ U32 StripeSize; /* 18h */
+ U32 Reserved2; /* 1Ch */
+ U32 Reserved3; /* 20h */
+ U8 NumPhysDisks; /* 24h */
+ U8 Reserved4; /* 25h */
+ U16 Reserved5; /* 26h */
+ RAID_VOL0_PHYS_DISK PhysDisk[MPI_RAID_VOL_PAGE_0_PHYSDISK_MAX];/* 28h */
+} fCONFIG_PAGE_RAID_VOL_0, MPI_POINTER PTR_CONFIG_PAGE_RAID_VOL_0,
+ RaidVolumePage0_t, MPI_POINTER pRaidVolumePage0_t;
+
+#define MPI_RAIDVOLPAGE0_PAGEVERSION (0x00)
-#define MPI_RAIDVOLPAGE2_PAGEVERSION (0x00)
-/* RAID Volume Page 2 VolumeStatus defines */
+/****************************************************************************
+* RAID Physical Disk Config Pages
+****************************************************************************/
-#define MPI_RAIDVOLPAGE2_STATUS_ENABLED (0x00000001)
-#define MPI_RAIDVOLPAGE2_STATUS_QUIESCED (0x00000002)
-#define MPI_RAIDVOLPAGE2_STATUS_RESYNC_IN_PROGRESS (0x00000004)
-#define MPI_RAIDVOLPAGE2_STATUS_DEGRADED (0x00000008)
+typedef struct _RAID_PHYS_DISK0_ERROR_DATA
+{
+ U8 ErrorCdbByte; /* 00h */
+ U8 ErrorSenseKey; /* 01h */
+ U16 Reserved; /* 02h */
+ U16 ErrorCount; /* 04h */
+ U8 ErrorASC; /* 06h */
+ U8 ErrorASCQ; /* 07h */
+ U16 SmartCount; /* 08h */
+ U8 SmartASC; /* 0Ah */
+ U8 SmartASCQ; /* 0Bh */
+} RAID_PHYS_DISK0_ERROR_DATA, MPI_POINTER PTR_RAID_PHYS_DISK0_ERROR_DATA,
+ RaidPhysDisk0ErrorData_t, MPI_POINTER pRaidPhysDisk0ErrorData_t;
+
+typedef struct _RAID_PHYS_DISK_INQUIRY_DATA
+{
+ U8 VendorID[8]; /* 00h */
+ U8 ProductID[16]; /* 08h */
+ U8 ProductRevLevel[4]; /* 18h */
+ U8 Info[32]; /* 1Ch */
+} RAID_PHYS_DISK0_INQUIRY_DATA, MPI_POINTER PTR_RAID_PHYS_DISK0_INQUIRY_DATA,
+ RaidPhysDisk0InquiryData, MPI_POINTER pRaidPhysDisk0InquiryData;
+
+typedef struct _RAID_PHYS_DISK0_SETTINGS
+{
+ U8 SepID; /* 00h */
+ U8 SepBus; /* 01h */
+ U8 HotSparePool; /* 02h */ /* MPI_RAID_HOT_SPARE_POOL_ */
+ U8 PhysDiskSettings; /* 03h */
+} RAID_PHYS_DISK0_SETTINGS, MPI_POINTER PTR_RAID_PHYS_DISK0_SETTINGS,
+ RaidPhysDiskSettings_t, MPI_POINTER pRaidPhysDiskSettings_t;
+
+typedef struct _RAID_PHYS_DISK0_STATUS
+{
+ U8 Flags; /* 00h */
+ U8 State; /* 01h */
+ U16 Reserved; /* 02h */
+} RAID_PHYS_DISK0_STATUS, MPI_POINTER PTR_RAID_PHYS_DISK0_STATUS,
+ RaidPhysDiskStatus_t, MPI_POINTER pRaidPhysDiskStatus_t;
-/* RAID Volume Page 2 VolumeSettings defines */
+/* RAID Volume 2 IM Physical Disk DiskStatus flags */
+
+#define MPI_PHYSDISK0_STATUS_FLAG_OUT_OF_SYNC (0x01)
+#define MPI_PHYSDISK0_STATUS_FLAG_QUIESCED (0x02)
-#define MPI_RAIDVOLPAGE2_SETTING_WRITE_CACHING_ENABLE (0x00000001)
-#define MPI_RAIDVOLPAGE2_SETTING_OFFLINE_ON_SMART (0x00000002)
-#define MPI_RAIDVOLPAGE2_SETTING_AUTO_CONFIGURE (0x00000004)
-#define MPI_RAIDVOLPAGE2_SETTING_USE_DEFAULTS (0x80000000)
+#define MPI_PHYSDISK0_STATUS_ONLINE (0x00)
+#define MPI_PHYSDISK0_STATUS_MISSING (0x01)
+#define MPI_PHYSDISK0_STATUS_NOT_COMPATIBLE (0x02)
+#define MPI_PHYSDISK0_STATUS_FAILED (0x03)
+#define MPI_PHYSDISK0_STATUS_INITIALIZING (0x04)
+#define MPI_PHYSDISK0_STATUS_OFFLINE_REQUESTED (0x05)
+#define MPI_PHYSDISK0_STATUS_FAILED_REQUESTED (0x06)
+#define MPI_PHYSDISK0_STATUS_OTHER_OFFLINE (0xFF)
+typedef struct _CONFIG_PAGE_RAID_PHYS_DISK_0
+{
+ fCONFIG_PAGE_HEADER Header; /* 00h */
+ U8 PhysDiskID; /* 04h */
+ U8 PhysDiskBus; /* 05h */
+ U8 PhysDiskIOC; /* 06h */
+ U8 PhysDiskNum; /* 07h */
+ RAID_PHYS_DISK0_SETTINGS PhysDiskSettings; /* 08h */
+ U32 Reserved1; /* 0Ch */
+ U32 Reserved2; /* 10h */
+ U32 Reserved3; /* 14h */
+ U8 DiskIdentifier[16]; /* 18h */
+ RAID_PHYS_DISK0_INQUIRY_DATA InquiryData; /* 28h */
+ RAID_PHYS_DISK0_STATUS PhysDiskStatus; /* 64h */
+ U32 MaxLBA; /* 68h */
+ RAID_PHYS_DISK0_ERROR_DATA ErrorData; /* 6Ch */
+} fCONFIG_PAGE_RAID_PHYS_DISK_0, MPI_POINTER PTR_CONFIG_PAGE_RAID_PHYS_DISK_0,
+ RaidPhysDiskPage0_t, MPI_POINTER pRaidPhysDiskPage0_t;
+
+#define MPI_RAIDPHYSDISKPAGE0_PAGEVERSION (0x00)
-/****************************************************************************/
-/* LAN Config Pages */
-/****************************************************************************/
+
+/****************************************************************************
+* LAN Config Pages
+****************************************************************************/
typedef struct _CONFIG_PAGE_LAN_0
{
U32 MaxWireSpeedHigh; /* 1Ch */
U32 BucketsRemaining; /* 20h */
U32 MaxReplySize; /* 24h */
- U32 NegWireSpeedHigh; /* 28h */
- U32 NegWireSpeedLow; /* 2Ch */
+ U32 NegWireSpeedLow; /* 28h */
+ U32 NegWireSpeedHigh; /* 2Ch */
} fCONFIG_PAGE_LAN_1, MPI_POINTER PTR_CONFIG_PAGE_LAN_1,
LANPage1_t, MPI_POINTER pLANPage1_t;
* Title: MPI Fibre Channel messages and structures
* Creation Date: June 12, 2000
*
- * MPI Version: 01.01.07
+ * MPI Version: 01.02.02
*
* Version History
* ---------------
* Added MPI_FC_PRIM_SEND_FLAGS_RESET_LINK define.
* Added structure offset comments.
* 04-09-01 01.01.07 Added RspLength field to MSG_LINK_SERVICE_RSP_REQUEST.
+ * 08-08-01 01.02.01 Original release for v1.2 work.
+ * 09-28-01 01.02.02 Change name of reserved field in
+ * MSG_LINK_SERVICE_RSP_REPLY.
* --------------------------------------------------------------------------
*/
U8 MsgLength; /* 02h */
U8 Function; /* 03h */
U16 Reserved1; /* 04h */
- U8 Reserved2; /* 06h */
+ U8 Reserved_0100_InitiatorIndex; /* 06h */ /* obsolete InitiatorIndex */
U8 MsgFlags; /* 07h */
U32 MsgContext; /* 08h */
U16 Reserved3; /* 0Ch */
* Title: MPI initiator mode messages and structures
* Creation Date: June 8, 2000
*
- * MPI Version: 01.01.05
+ * MPI Version: 01.02.04
*
* Version History
* ---------------
* 02-20-01 01.01.03 Started using MPI_POINTER.
* 03-27-01 01.01.04 Added structure offset comments.
* 04-10-01 01.01.05 Added new MsgFlag for MSG_SCSI_TASK_MGMT.
+ * 08-08-01 01.02.01 Original release for v1.2 work.
+ * 08-29-01 01.02.02 Added MPI_SCSITASKMGMT_TASKTYPE_LOGICAL_UNIT_RESET.
+ * Added MPI_SCSI_STATE_QUEUE_TAG_REJECTED for
+ * MSG_SCSI_IO_REPLY.
+ * 09-28-01 01.02.03 Added structures and defines for SCSI Enclosure
+ * Processor messages.
+ * 10-04-01 01.02.04 Added defines for SEP request Action field.
* --------------------------------------------------------------------------
*/
#define MPI_SCSI_STATE_NO_SCSI_STATUS (0x04)
#define MPI_SCSI_STATE_TERMINATED (0x08)
#define MPI_SCSI_STATE_RESPONSE_INFO_VALID (0x10)
+#define MPI_SCSI_STATE_QUEUE_TAG_REJECTED (0x20)
/* SCSIIO Reply ResponseInfo values */
/* (FCP-1 RSP_CODE values and SPI-3 Packetized Failure codes) */
#define MPI_SCSITASKMGMT_TASKTYPE_ABRT_TASK_SET (0x02)
#define MPI_SCSITASKMGMT_TASKTYPE_TARGET_RESET (0x03)
#define MPI_SCSITASKMGMT_TASKTYPE_RESET_BUS (0x04)
+#define MPI_SCSITASKMGMT_TASKTYPE_LOGICAL_UNIT_RESET (0x05)
/* MsgFlags bits */
#define MPI_SCSITASKMGMT_MSGFLAGS_TARGET_RESET_OPTION (0x00)
} MSG_SCSI_TASK_MGMT_REPLY, MPI_POINTER PTR_MSG_SCSI_TASK_MGMT_REPLY,
SCSITaskMgmtReply_t, MPI_POINTER pSCSITaskMgmtReply_t;
+
+/****************************************************************************/
+/* SCSI Enclosure Processor messages */
+/****************************************************************************/
+
+typedef struct _MSG_SEP_REQUEST
+{
+ U8 TargetID; /* 00h */
+ U8 Bus; /* 01h */
+ U8 ChainOffset; /* 02h */
+ U8 Function; /* 03h */
+ U8 Action; /* 04h */
+ U8 Reserved1; /* 05h */
+ U8 Reserved2; /* 06h */
+ U8 MsgFlags; /* 07h */
+ U32 MsgContext; /* 08h */
+ U32 SlotStatus; /* 0Ch */
+} MSG_SEP_REQUEST, MPI_POINTER PTR_MSG_SEP_REQUEST,
+ SEPRequest_t, MPI_POINTER pSEPRequest_t;
+
+/* Action defines */
+#define MPI_SEP_REQ_ACTION_WRITE_STATUS (0x00)
+#define MPI_SEP_REQ_ACTION_READ_STATUS (0x01)
+
+/* SlotStatus bits for MSG_SEP_REQUEST */
+#define MPI_SEP_REQ_SLOTSTATUS_NO_ERROR (0x00000001)
+#define MPI_SEP_REQ_SLOTSTATUS_DEV_FAULTY (0x00000002)
+#define MPI_SEP_REQ_SLOTSTATUS_DEV_REBUILDING (0x00000004)
+#define MPI_SEP_REQ_SLOTSTATUS_IN_FAILED_ARRAY (0x00000008)
+#define MPI_SEP_REQ_SLOTSTATUS_IN_CRITICAL_ARRAY (0x00000010)
+#define MPI_SEP_REQ_SLOTSTATUS_PARITY_CHECK (0x00000020)
+#define MPI_SEP_REQ_SLOTSTATUS_PREDICTED_FAULT (0x00000040)
+#define MPI_SEP_REQ_SLOTSTATUS_UNCONFIGURED (0x00000080)
+#define MPI_SEP_REQ_SLOTSTATUS_HOT_SPARE (0x00000100)
+#define MPI_SEP_REQ_SLOTSTATUS_REBUILD_STOPPED (0x00000200)
+#define MPI_SEP_REQ_SLOTSTATUS_IDENTIFY_REQUEST (0x00020000)
+#define MPI_SEP_REQ_SLOTSTATUS_REQUEST_REMOVE (0x00040000)
+#define MPI_SEP_REQ_SLOTSTATUS_REQUEST_INSERT (0x00080000)
+#define MPI_SEP_REQ_SLOTSTATUS_DO_NOT_MOVE (0x00400000)
+#define MPI_SEP_REQ_SLOTSTATUS_B_ENABLE_BYPASS (0x04000000)
+#define MPI_SEP_REQ_SLOTSTATUS_A_ENABLE_BYPASS (0x08000000)
+#define MPI_SEP_REQ_SLOTSTATUS_DEV_OFF (0x10000000)
+#define MPI_SEP_REQ_SLOTSTATUS_SWAP_RESET (0x80000000)
+
+
+typedef struct _MSG_SEP_REPLY
+{
+ U8 TargetID; /* 00h */
+ U8 Bus; /* 01h */
+ U8 MsgLength; /* 02h */
+ U8 Function; /* 03h */
+ U8 Action; /* 04h */
+ U8 Reserved1; /* 05h */
+ U8 Reserved2; /* 06h */
+ U8 MsgFlags; /* 07h */
+ U32 MsgContext; /* 08h */
+ U16 Reserved3; /* 0Ch */
+ U16 IOCStatus; /* 0Eh */
+ U32 IOCLogInfo; /* 10h */
+ U32 SlotStatus; /* 14h */
+} MSG_SEP_REPLY, MPI_POINTER PTR_MSG_SEP_REPLY,
+ SEPReply_t, MPI_POINTER pSEPReply_t;
+
+/* SlotStatus bits for MSG_SEP_REPLY */
+#define MPI_SEP_REPLY_SLOTSTATUS_NO_ERROR (0x00000001)
+#define MPI_SEP_REPLY_SLOTSTATUS_DEV_FAULTY (0x00000002)
+#define MPI_SEP_REPLY_SLOTSTATUS_DEV_REBUILDING (0x00000004)
+#define MPI_SEP_REPLY_SLOTSTATUS_IN_FAILED_ARRAY (0x00000008)
+#define MPI_SEP_REPLY_SLOTSTATUS_IN_CRITICAL_ARRAY (0x00000010)
+#define MPI_SEP_REPLY_SLOTSTATUS_PARITY_CHECK (0x00000020)
+#define MPI_SEP_REPLY_SLOTSTATUS_PREDICTED_FAULT (0x00000040)
+#define MPI_SEP_REPLY_SLOTSTATUS_UNCONFIGURED (0x00000080)
+#define MPI_SEP_REPLY_SLOTSTATUS_HOT_SPARE (0x00000100)
+#define MPI_SEP_REPLY_SLOTSTATUS_REBUILD_STOPPED (0x00000200)
+#define MPI_SEP_REPLY_SLOTSTATUS_REPORT (0x00010000)
+#define MPI_SEP_REPLY_SLOTSTATUS_IDENTIFY_REQUEST (0x00020000)
+#define MPI_SEP_REPLY_SLOTSTATUS_REMOVE_READY (0x00040000)
+#define MPI_SEP_REPLY_SLOTSTATUS_INSERT_READY (0x00080000)
+#define MPI_SEP_REPLY_SLOTSTATUS_DO_NOT_REMOVE (0x00400000)
+#define MPI_SEP_REPLY_SLOTSTATUS_B_BYPASS_ENABLED (0x01000000)
+#define MPI_SEP_REPLY_SLOTSTATUS_A_BYPASS_ENABLED (0x02000000)
+#define MPI_SEP_REPLY_SLOTSTATUS_B_ENABLE_BYPASS (0x04000000)
+#define MPI_SEP_REPLY_SLOTSTATUS_A_ENABLE_BYPASS (0x08000000)
+#define MPI_SEP_REPLY_SLOTSTATUS_DEV_OFF (0x10000000)
+#define MPI_SEP_REPLY_SLOTSTATUS_FAULT_SENSED (0x40000000)
+#define MPI_SEP_REPLY_SLOTSTATUS_SWAPPED (0x80000000)
+
#endif
* Title: MPI IOC, Port, Event, FW Download, and FW Upload messages
* Creation Date: August 11, 2000
*
- * MPI Version: 01.01.07
+ * MPI Version: 01.02.04
*
* Version History
* ---------------
* 03-27-01 01.01.06 Added defines for ProductId field of MPI_FW_HEADER.
* Added structure offset comments.
* 04-09-01 01.01.07 Added structure EVENT_DATA_EVENT_CHANGE.
+ * 08-08-01 01.02.01 Original release for v1.2 work.
+ * New format for FWVersion and ProductId in
+ * MSG_IOC_FACTS_REPLY and MPI_FW_HEADER.
+ * 08-31-01 01.02.02 Addded event MPI_EVENT_SCSI_DEVICE_STATUS_CHANGE and
+ * related structure and defines.
+ * Added event MPI_EVENT_ON_BUS_TIMER_EXPIRED.
+ * Added MPI_IOCINIT_FLAGS_DISCARD_FW_IMAGE.
+ * Replaced a reserved field in MSG_IOC_FACTS_REPLY with
+ * IOCExceptions and changed DataImageSize to reserved.
+ * Added MPI_FW_DOWNLOAD_ITYPE_NVSTORE_DATA and
+ * MPI_FW_UPLOAD_ITYPE_NVDATA.
+ * 09-28-01 01.02.03 Modified Event Data for Integrated RAID.
+ * 11-01-01 01.02.04 Added defines for MPI_EXT_IMAGE_HEADER ImageType field.
* --------------------------------------------------------------------------
*/
} MSG_IOC_INIT, MPI_POINTER PTR_MSG_IOC_INIT,
IOCInit_t, MPI_POINTER pIOCInit_t;
+/* WhoInit values */
+#define MPI_WHOINIT_NO_ONE (0x00)
+#define MPI_WHOINIT_SYSTEM_BIOS (0x01)
+#define MPI_WHOINIT_ROM_BIOS (0x02)
+#define MPI_WHOINIT_PCI_PEER (0x03)
+#define MPI_WHOINIT_HOST_DRIVER (0x04)
+#define MPI_WHOINIT_MANUFACTURER (0x05)
+
+/* Flags values */
+#define MPI_IOCINIT_FLAGS_DISCARD_FW_IMAGE (0x01)
+
typedef struct _MSG_IOC_INIT_REPLY
{
U8 WhoInit; /* 00h */
} MSG_IOC_INIT_REPLY, MPI_POINTER PTR_MSG_IOC_INIT_REPLY,
IOCInitReply_t, MPI_POINTER pIOCInitReply_t;
-/* WhoInit values */
-
-#define MPI_WHOINIT_NO_ONE (0x00)
-#define MPI_WHOINIT_SYSTEM_BIOS (0x01)
-#define MPI_WHOINIT_ROM_BIOS (0x02)
-#define MPI_WHOINIT_PCI_PEER (0x03)
-#define MPI_WHOINIT_HOST_DRIVER (0x04)
-#define MPI_WHOINIT_MANUFACTURER (0x05)
/****************************************************************************/
} MSG_IOC_FACTS, MPI_POINTER PTR_IOC_FACTS,
IOCFacts_t, MPI_POINTER pIOCFacts_t;
-/* IOC Facts Reply */
+typedef struct _MPI_FW_VERSION_STRUCT
+{
+ U8 Dev; /* 00h */
+ U8 Unit; /* 01h */
+ U8 Minor; /* 02h */
+ U8 Major; /* 03h */
+} MPI_FW_VERSION_STRUCT;
+
+typedef union _MPI_FW_VERSION
+{
+ MPI_FW_VERSION_STRUCT Struct;
+ U32 Word;
+} MPI_FW_VERSION;
+/* IOC Facts Reply */
typedef struct _MSG_IOC_FACTS_REPLY
{
U16 MsgVersion; /* 00h */
U8 IOCNumber; /* 06h */
U8 MsgFlags; /* 07h */
U32 MsgContext; /* 08h */
- U16 Reserved2; /* 0Ch */
+ U16 IOCExceptions; /* 0Ch */
U16 IOCStatus; /* 0Eh */
U32 IOCLogInfo; /* 10h */
U8 MaxChainDepth; /* 14h */
U8 Flags; /* 17h */
U16 ReplyQueueDepth; /* 18h */
U16 RequestFrameSize; /* 1Ah */
- U16 FWVersion; /* 1Ch */
+ U16 Reserved_0101_FWVersion; /* 1Ch */ /* obsolete 16-bit FWVersion */
U16 ProductID; /* 1Eh */
U32 CurrentHostMfaHighAddr; /* 20h */
U16 GlobalCredits; /* 24h */
U8 MaxDevices; /* 2Eh */
U8 MaxBuses; /* 2Fh */
U32 FWImageSize; /* 30h */
- U32 DataImageSize; /* 34h */
+ U32 Reserved4; /* 34h */
+ MPI_FW_VERSION FWVersion; /* 38h */
} MSG_IOC_FACTS_REPLY, MPI_POINTER PTR_MSG_IOC_FACTS_REPLY,
IOCFactsReply_t, MPI_POINTER pIOCFactsReply_t;
-#define MPI_IOCFACTS_MSGVERSION_MAJOR_MASK (0xFF00)
-#define MPI_IOCFACTS_MSGVERSION_MINOR_MASK (0x00FF)
+#define MPI_IOCFACTS_MSGVERSION_MAJOR_MASK (0xFF00)
+#define MPI_IOCFACTS_MSGVERSION_MINOR_MASK (0x00FF)
-#define MPI_IOCFACTS_FLAGS_FW_DOWNLOAD_BOOT (0x01)
-#define MPI_IOCFACTS_FLAGS_DATA_IMAGE_UPLOAD (0x02)
+#define MPI_IOCFACTS_EXCEPT_CONFIG_CHECKSUM_FAIL (0x0001)
-#define MPI_IOCFACTS_EVENTSTATE_DISABLED (0x00)
-#define MPI_IOCFACTS_EVENTSTATE_ENABLED (0x01)
+#define MPI_IOCFACTS_FLAGS_FW_DOWNLOAD_BOOT (0x01)
+
+#define MPI_IOCFACTS_EVENTSTATE_DISABLED (0x00)
+#define MPI_IOCFACTS_EVENTSTATE_ENABLED (0x01)
} MSG_EVENT_ACK_REPLY, MPI_POINTER PTR_MSG_EVENT_ACK_REPLY,
EventAckReply_t, MPI_POINTER pEventAckReply_t;
-
/* Switch */
#define MPI_EVENT_NOTIFICATION_SWITCH_OFF (0x00)
#define MPI_EVENT_LOOP_STATE_CHANGE (0x00000008)
#define MPI_EVENT_LOGOUT (0x00000009)
#define MPI_EVENT_EVENT_CHANGE (0x0000000A)
-#define MPI_EVENT_RAID_STATUS_CHANGE (0x0000000B)
+#define MPI_EVENT_INTEGRATED_RAID (0x0000000B)
+#define MPI_EVENT_SCSI_DEVICE_STATUS_CHANGE (0x0000000C)
+#define MPI_EVENT_ON_BUS_TIMER_EXPIRED (0x0000000D)
/* AckRequired field values */
} EVENT_DATA_SCSI, MPI_POINTER PTR_EVENT_DATA_SCSI,
EventDataScsi_t, MPI_POINTER pEventDataScsi_t;
+/* SCSI Device Status Change Event data */
+
+typedef struct _EVENT_DATA_SCSI_DEVICE_STATUS_CHANGE
+{
+ U8 TargetID; /* 00h */
+ U8 Bus; /* 01h */
+ U8 ReasonCode; /* 02h */
+ U8 LUN; /* 03h */
+ U8 ASC; /* 04h */
+ U8 ASCQ; /* 05h */
+ U16 Reserved; /* 06h */
+} EVENT_DATA_SCSI_DEVICE_STATUS_CHANGE,
+ MPI_POINTER PTR_EVENT_DATA_SCSI_DEVICE_STATUS_CHANGE,
+ MpiEventDataScsiDeviceStatusChange_t,
+ MPI_POINTER pMpiEventDataScsiDeviceStatusChange_t;
+
+/* MPI SCSI Device Status Change Event data ReasonCode values */
+#define MPI_EVENT_SCSI_DEV_STAT_RC_ADDED (0x03)
+#define MPI_EVENT_SCSI_DEV_STAT_RC_NOT_RESPONDING (0x04)
+#define MPI_EVENT_SCSI_DEV_STAT_RC_SMART_DATA (0x05)
+
/* MPI Link Status Change Event data */
typedef struct _EVENT_DATA_LINK_STATUS
} EVENT_DATA_LOGOUT, MPI_POINTER PTR_EVENT_DATA_LOGOUT,
EventDataLogout_t, MPI_POINTER pEventDataLogout_t;
-/* MPI RAID Status Change Event data */
+/* MPI Integrated RAID Event data */
-typedef struct _EVENT_DATA_RAID_STATUS_CHANGE
+typedef struct _EVENT_DATA_RAID
{
- U8 VolumeTargetID; /* 00h */
+ U8 VolumeID; /* 00h */
U8 VolumeBus; /* 01h */
U8 ReasonCode; /* 02h */
U8 PhysDiskNum; /* 03h */
U8 ASC; /* 04h */
U8 ASCQ; /* 05h */
U16 Reserved; /* 06h */
-} EVENT_DATA_RAID_STATUS_CHANGE, MPI_POINTER PTR_EVENT_DATA_RAID_STATUS_CHANGE,
- MpiEventDataRaidStatusChange_t, MPI_POINTER pMpiEventDataRaidStatusChange_t;
-
-
-/* MPI RAID Status Change Event data ReasonCode values */
-
-#define MPI_EVENT_RAID_DATA_RC_VOLUME_OPTIMAL (0x00)
-#define MPI_EVENT_RAID_DATA_RC_VOLUME_DEGRADED (0x01)
-#define MPI_EVENT_RAID_DATA_RC_STARTED_RESYNC (0x02)
-#define MPI_EVENT_RAID_DATA_RC_DISK_ADDED (0x03)
-#define MPI_EVENT_RAID_DATA_RC_DISK_NOT_RESPONDING (0x04)
-#define MPI_EVENT_RAID_DATA_RC_SMART_DATA (0x05)
+ U32 SettingsStatus; /* 08h */
+} EVENT_DATA_RAID, MPI_POINTER PTR_EVENT_DATA_RAID,
+ MpiEventDataRaid_t, MPI_POINTER pMpiEventDataRaid_t;
+
+/* MPI Integrated RAID Event data ReasonCode values */
+#define MPI_EVENT_RAID_RC_VOLUME_CREATED (0x00)
+#define MPI_EVENT_RAID_RC_VOLUME_DELETED (0x01)
+#define MPI_EVENT_RAID_RC_VOLUME_SETTINGS_CHANGED (0x02)
+#define MPI_EVENT_RAID_RC_VOLUME_STATUS_CHANGED (0x03)
+#define MPI_EVENT_RAID_RC_VOLUME_PHYSDISK_CHANGED (0x04)
+#define MPI_EVENT_RAID_RC_PHYSDISK_CREATED (0x05)
+#define MPI_EVENT_RAID_RC_PHYSDISK_DELETED (0x06)
+#define MPI_EVENT_RAID_RC_PHYSDISK_SETTINGS_CHANGED (0x07)
+#define MPI_EVENT_RAID_RC_PHYSDISK_STATUS_CHANGED (0x08)
+#define MPI_EVENT_RAID_RC_DOMAIN_VAL_NEEDED (0x09)
+#define MPI_EVENT_RAID_RC_SMART_DATA (0x0A)
+#define MPI_EVENT_RAID_RC_REPLACE_ACTION_STARTED (0x0B)
/*****************************************************************************
#define MPI_FW_DOWNLOAD_ITYPE_RESERVED (0x00)
#define MPI_FW_DOWNLOAD_ITYPE_FW (0x01)
#define MPI_FW_DOWNLOAD_ITYPE_BIOS (0x02)
+#define MPI_FW_DOWNLOAD_ITYPE_NVDATA (0x03)
typedef struct _FWDownloadTCSGE
U8 ContextSize; /* 01h */
U8 DetailsLength; /* 02h */
U8 Flags; /* 03h */
- U32 Reserved1; /* 04h */
+ U32 Reserved_0100_Checksum; /* 04h */ /* obsolete Checksum */
U32 ImageOffset; /* 08h */
U32 ImageSize; /* 0Ch */
} FW_DOWNLOAD_TCSGE, MPI_POINTER PTR_FW_DOWNLOAD_TCSGE,
#define MPI_FW_UPLOAD_ITYPE_FW_IOC_MEM (0x00)
#define MPI_FW_UPLOAD_ITYPE_FW_FLASH (0x01)
#define MPI_FW_UPLOAD_ITYPE_BIOS_FLASH (0x02)
-#define MPI_FW_UPLOAD_ITYPE_DATA_IOC_MEM (0x03)
+#define MPI_FW_UPLOAD_ITYPE_NVDATA (0x03)
typedef struct _FWUploadTCSGE
{
U32 Checksum; /* 1Ch */
U16 VendorId; /* 20h */
U16 ProductId; /* 22h */
- U16 FwVersion; /* 24h */
- U16 Reserved1; /* 26h */
+ MPI_FW_VERSION FWVersion; /* 24h */
U32 SeqCodeVersion; /* 28h */
U32 ImageSize; /* 2Ch */
- U32 Reserved2; /* 30h */
+ U32 NextImageHeaderOffset; /* 30h */
U32 LoadStartAddress; /* 34h */
U32 IopResetVectorValue; /* 38h */
U32 IopResetRegAddr; /* 3Ch */
#define MPI_FW_HEADER_WHAT_SIGNATURE (0x29232840)
/* defines for using the ProductId field */
-#define MPI_FW_HEADER_PID_TYPE_MASK (0xF000)
-#define MPI_FW_HEADER_PID_TYPE_SCSI (0x0000)
-#define MPI_FW_HEADER_PID_TYPE_FC (0x1000)
-
-#define MPI_FW_HEADER_PID_FW_VENDOR_MASK (0x0F00)
-#define MPI_FW_HEADER_PID_FW_VENDOR_LSI (0x0000)
-
-#define MPI_FW_HEADER_PID_FAMILY_MASK (0x000F)
-#define MPI_FW_HEADER_PID_FAMILY_1030_SCSI (0x0000)
-#define MPI_FW_HEADER_PID_FAMILY_909_FC (0x0000)
-#define MPI_FW_HEADER_PID_FAMILY_919_FC (0x0001)
-#define MPI_FW_HEADER_PID_FAMILY_919X_FC (0x0002)
-
-
-typedef struct _MPI_DATA_HEADER
+#define MPI_FW_HEADER_PID_TYPE_MASK (0xF000)
+#define MPI_FW_HEADER_PID_TYPE_SCSI (0x0000)
+#define MPI_FW_HEADER_PID_TYPE_FC (0x1000)
+
+#define MPI_FW_HEADER_PID_PROD_MASK (0x0F00)
+#define MPI_FW_HEADER_PID_PROD_INITIATOR_SCSI (0x0100)
+#define MPI_FW_HEADER_PID_PROD_TARGET_INITIATOR_SCSI (0x0200)
+#define MPI_FW_HEADER_PID_PROD_TARGET_SCSI (0x0300)
+#define MPI_FW_HEADER_PID_PROD_IM_SCSI (0x0400)
+#define MPI_FW_HEADER_PID_PROD_IS_SCSI (0x0500)
+#define MPI_FW_HEADER_PID_PROD_CTX_SCSI (0x0600)
+
+#define MPI_FW_HEADER_PID_FAMILY_MASK (0x00FF)
+#define MPI_FW_HEADER_PID_FAMILY_1030A0_SCSI (0x0001)
+#define MPI_FW_HEADER_PID_FAMILY_1030B0_SCSI (0x0002)
+#define MPI_FW_HEADER_PID_FAMILY_1030B1_SCSI (0x0003)
+#define MPI_FW_HEADER_PID_FAMILY_1030C0_SCSI (0x0004)
+#define MPI_FW_HEADER_PID_FAMILY_1020A0_SCSI (0x0005)
+#define MPI_FW_HEADER_PID_FAMILY_1020B0_SCSI (0x0006)
+#define MPI_FW_HEADER_PID_FAMILY_1020B1_SCSI (0x0007)
+#define MPI_FW_HEADER_PID_FAMILY_1020C0_SCSI (0x0008)
+#define MPI_FW_HEADER_PID_FAMILY_1035A0_SCSI (0x0009)
+#define MPI_FW_HEADER_PID_FAMILY_1035B0_SCSI (0x000A)
+#define MPI_FW_HEADER_PID_FAMILY_909_FC (0x0000)
+#define MPI_FW_HEADER_PID_FAMILY_919_FC (0x0001)
+#define MPI_FW_HEADER_PID_FAMILY_919X_FC (0x0002)
+
+typedef struct _MPI_EXT_IMAGE_HEADER
{
- U32 Signature; /* 00h */
- U16 FunctionNumber; /* 04h */
- U16 Length; /* 06h */
- U32 Checksum; /* 08h */
- U32 LoadStartAddress; /* 0Ch */
-} MPI_DATA_HEADER, MPI_POINTER PTR_MPI_DATA_HEADER,
- MpiDataHeader_t, MPI_POINTER pMpiDataHeader_t;
-
-#define MPI_DATA_HEADER_SIGNATURE (0x43504147)
+ U8 ImageType; /* 00h */
+ U8 Reserved; /* 01h */
+ U16 Reserved1; /* 02h */
+ U32 Checksum; /* 04h */
+ U32 ImageSize; /* 08h */
+ U32 NextImageHeaderOffset; /* 0Ch */
+ U32 LoadStartAddress; /* 10h */
+ U32 Reserved2; /* 14h */
+} MPI_EXT_IMAGE_HEADER, MPI_POINTER PTR_MPI_EXT_IMAGE_HEADER,
+ MpiExtImageHeader_t, MPI_POINTER pMpiExtImageHeader_t;
+
+/* defines for the ImageType field */
+#define MPI_EXT_IMAGE_TYPE_UNSPECIFIED (0x00)
+#define MPI_EXT_IMAGE_TYPE_FW (0x01)
+#define MPI_EXT_IMAGE_TYPE_NVDATA (0x03)
#endif
* Title: MPI LAN messages and structures
* Creation Date: June 30, 2000
*
- * MPI Version: 01.01.03
+ * MPI Version: 01.02.01
*
* Version History
* ---------------
* 11-02-00 01.01.01 Original release for post 1.0 work
* 02-20-01 01.01.02 Started using MPI_POINTER.
* 03-27-01 01.01.03 Added structure offset comments.
+ * 08-08-01 01.02.01 Original release for v1.2 work.
* --------------------------------------------------------------------------
*/
--- /dev/null
+/*
+ * Copyright (c) 2001 LSI Logic Corporation.
+ *
+ *
+ * Name: MPI_RAID.H
+ * Title: MPI RAID message and structures
+ * Creation Date: February 27, 2001
+ *
+ * MPI Version: 01.02.04
+ *
+ * Version History
+ * ---------------
+ *
+ * Date Version Description
+ * -------- -------- ------------------------------------------------------
+ * 02-27-01 01.01.01 Original release for this file.
+ * 03-27-01 01.01.02 Added structure offset comments.
+ * 08-08-01 01.02.01 Original release for v1.2 work.
+ * 09-28-01 01.02.02 Major rework for MPI v1.2 Integrated RAID changes.
+ * 10-04-01 01.02.03 Added ActionData defines for
+ * MPI_RAID_ACTION_DELETE_VOLUME action.
+ * 11-01-01 01.02.04 Added define for MPI_RAID_ACTION_ADATA_DO_NOT_SYNC.
+ * --------------------------------------------------------------------------
+ */
+
+#ifndef MPI_RAID_H
+#define MPI_RAID_H
+
+
+/******************************************************************************
+*
+* R A I D M e s s a g e s
+*
+*******************************************************************************/
+
+
+/****************************************************************************/
+/* RAID Volume Request */
+/****************************************************************************/
+
+typedef struct _MSG_RAID_ACTION
+{
+ U8 Action; /* 00h */
+ U8 Reserved1; /* 01h */
+ U8 ChainOffset; /* 02h */
+ U8 Function; /* 03h */
+ U8 VolumeID; /* 04h */
+ U8 VolumeBus; /* 05h */
+ U8 PhysDiskNum; /* 06h */
+ U8 MsgFlags; /* 07h */
+ U32 MsgContext; /* 08h */
+ U32 Reserved2; /* 0Ch */
+ U32 ActionDataWord; /* 10h */
+ SGE_SIMPLE_UNION ActionDataSGE; /* 14h */
+} MSG_RAID_ACTION_REQUEST, MPI_POINTER PTR_MSG_RAID_ACTION_REQUEST,
+ MpiRaidActionRequest_t , MPI_POINTER pMpiRaidActionRequest_t;
+
+
+/* RAID Action request Action values */
+
+#define MPI_RAID_ACTION_STATUS (0x00)
+#define MPI_RAID_ACTION_INDICATOR_STRUCT (0x01)
+#define MPI_RAID_ACTION_CREATE_VOLUME (0x02)
+#define MPI_RAID_ACTION_DELETE_VOLUME (0x03)
+#define MPI_RAID_ACTION_DISABLE_VOLUME (0x04)
+#define MPI_RAID_ACTION_ENABLE_VOLUME (0x05)
+#define MPI_RAID_ACTION_QUIESCE_PHYS_IO (0x06)
+#define MPI_RAID_ACTION_ENABLE_PHYS_IO (0x07)
+#define MPI_RAID_ACTION_CHANGE_VOLUME_SETTINGS (0x08)
+#define MPI_RAID_ACTION_PHYSDISK_OFFLINE (0x0A)
+#define MPI_RAID_ACTION_PHYSDISK_ONLINE (0x0B)
+#define MPI_RAID_ACTION_CHANGE_PHYSDISK_SETTINGS (0x0C)
+#define MPI_RAID_ACTION_CREATE_PHYSDISK (0x0D)
+#define MPI_RAID_ACTION_DELETE_PHYSDISK (0x0E)
+#define MPI_RAID_ACTION_FAIL_PHYSDISK (0x0F)
+#define MPI_RAID_ACTION_REPLACE_PHYSDISK (0x10)
+
+/* ActionDataWord defines for use with MPI_RAID_ACTION_CREATE_VOLUME action */
+#define MPI_RAID_ACTION_ADATA_DO_NOT_SYNC (0x00000001)
+
+/* ActionDataWord defines for use with MPI_RAID_ACTION_DELETE_VOLUME action */
+#define MPI_RAID_ACTION_ADATA_KEEP_PHYS_DISKS (0x00000000)
+#define MPI_RAID_ACTION_ADATA_DEL_PHYS_DISKS (0x00000001)
+
+
+/* RAID Action reply message */
+
+typedef struct _MSG_RAID_ACTION_REPLY
+{
+ U8 Action; /* 00h */
+ U8 Reserved; /* 01h */
+ U8 MsgLength; /* 02h */
+ U8 Function; /* 03h */
+ U8 VolumeID; /* 04h */
+ U8 VolumeBus; /* 05h */
+ U8 PhysDiskNum; /* 06h */
+ U8 MsgFlags; /* 07h */
+ U32 MsgContext; /* 08h */
+ U16 ActionStatus; /* 0Ch */
+ U16 IOCStatus; /* 0Eh */
+ U32 IOCLogInfo; /* 10h */
+ U32 VolumeStatus; /* 14h */
+ U32 ActionData; /* 18h */
+} MSG_RAID_ACTION_REPLY, MPI_POINTER PTR_MSG_RAID_ACTION_REPLY,
+ MpiRaidActionReply_t, MPI_POINTER pMpiRaidActionReply_t;
+
+
+/* RAID Volume reply ActionStatus values */
+
+#define MPI_RAID_ACTION_ASTATUS_SUCCESS (0x0000)
+#define MPI_RAID_ACTION_ASTATUS_INVALID_ACTION (0x0001)
+#define MPI_RAID_ACTION_ASTATUS_FAILURE (0x0002)
+#define MPI_RAID_ACTION_ASTATUS_IN_PROGRESS (0x0003)
+
+
+/* RAID Volume reply RAID Volume Indicator structure */
+
+typedef struct _MPI_RAID_VOL_INDICATOR
+{
+ U64 TotalBlocks; /* 00h */
+ U64 BlocksRemaining; /* 08h */
+} MPI_RAID_VOL_INDICATOR, MPI_POINTER PTR_MPI_RAID_VOL_INDICATOR,
+ MpiRaidVolIndicator_t, MPI_POINTER pMpiRaidVolIndicator_t;
+
+
+/****************************************************************************/
+/* SCSI IO RAID Passthrough Request */
+/****************************************************************************/
+
+typedef struct _MSG_SCSI_IO_RAID_PT_REQUEST
+{
+ U8 PhysDiskNum; /* 00h */
+ U8 Reserved1; /* 01h */
+ U8 ChainOffset; /* 02h */
+ U8 Function; /* 03h */
+ U8 CDBLength; /* 04h */
+ U8 SenseBufferLength; /* 05h */
+ U8 Reserved2; /* 06h */
+ U8 MsgFlags; /* 07h */
+ U32 MsgContext; /* 08h */
+ U8 LUN[8]; /* 0Ch */
+ U32 Control; /* 14h */
+ U8 CDB[16]; /* 18h */
+ U32 DataLength; /* 28h */
+ U32 SenseBufferLowAddr; /* 2Ch */
+ SGE_IO_UNION SGL; /* 30h */
+} MSG_SCSI_IO_RAID_PT_REQUEST, MPI_POINTER PTR_MSG_SCSI_IO_RAID_PT_REQUEST,
+ SCSIIORaidPassthroughRequest_t, MPI_POINTER pSCSIIORaidPassthroughRequest_t;
+
+
+/* SCSI IO RAID Passthrough reply structure */
+
+typedef struct _MSG_SCSI_IO_RAID_PT_REPLY
+{
+ U8 PhysDiskNum; /* 00h */
+ U8 Reserved1; /* 01h */
+ U8 MsgLength; /* 02h */
+ U8 Function; /* 03h */
+ U8 CDBLength; /* 04h */
+ U8 SenseBufferLength; /* 05h */
+ U8 Reserved2; /* 06h */
+ U8 MsgFlags; /* 07h */
+ U32 MsgContext; /* 08h */
+ U8 SCSIStatus; /* 0Ch */
+ U8 SCSIState; /* 0Dh */
+ U16 IOCStatus; /* 0Eh */
+ U32 IOCLogInfo; /* 10h */
+ U32 TransferCount; /* 14h */
+ U32 SenseCount; /* 18h */
+ U32 ResponseInfo; /* 1Ch */
+} MSG_SCSI_IO_RAID_PT_REPLY, MPI_POINTER PTR_MSG_SCSI_IO_RAID_PT_REPLY,
+ SCSIIORaidPassthroughReply_t, MPI_POINTER pSCSIIORaidPassthroughReply_t;
+
+
+#endif
+
+
+
* Title: MPI Target mode messages and structures
* Creation Date: June 22, 2000
*
- * MPI Version: 01.01.04
+ * MPI Version: 01.02.04
*
* Version History
* ---------------
* Added structures for MPI_TARGET_SCSI_SPI_CMD_BUFFER and
* MPI_TARGET_FCP_CMD_BUFFER.
* 03-27-01 01.01.04 Added structure offset comments.
+ * 08-08-01 01.02.01 Original release for v1.2 work.
+ * 09-28-01 01.02.02 Added structure for MPI_TARGET_SCSI_SPI_STATUS_IU.
+ * Added PriorityReason field to some replies and
+ * defined more PriorityReason codes.
+ * Added some defines for to support previous version
+ * of MPI.
+ * 10-04-01 01.02.03 Added PriorityReason to MSG_TARGET_ERROR_REPLY.
+ * 11-01-01 01.02.04 Added define for TARGET_STATUS_SEND_FLAGS_HIGH_PRIORITY.
* --------------------------------------------------------------------------
*/
#define CMD_BUFFER_POST_FLAGS_64_BIT_ADDR (0x80)
#define CMD_BUFFER_POST_IO_INDEX_MASK (0x00003FFF)
+#define CMD_BUFFER_POST_IO_INDEX_MASK_0100 (0x000003FF) /* obsolete */
typedef struct _MSG_TARGET_CMD_BUFFER_POST_REPLY
} MSG_TARGET_CMD_BUFFER_POST_REPLY, MPI_POINTER PTR_MSG_TARGET_CMD_BUFFER_POST_REPLY,
TargetCmdBufferPostReply_t, MPI_POINTER pTargetCmdBufferPostReply_t;
-
+/* the following structure is obsolete as of MPI v1.2 */
typedef struct _MSG_PRIORITY_CMD_RECEIVED_REPLY
{
U16 Reserved; /* 00h */
#define PRIORITY_REASON_NO_DISCONNECT (0x00)
#define PRIORITY_REASON_SCSI_TASK_MANAGEMENT (0x01)
+#define PRIORITY_REASON_CMD_PARITY_ERR (0x02)
+#define PRIORITY_REASON_MSG_OUT_PARITY_ERR (0x03)
+#define PRIORITY_REASON_LQ_CRC_ERR (0x04)
+#define PRIORITY_REASON_CMD_CRC_ERR (0x05)
+#define PRIORITY_REASON_PROTOCOL_ERR (0x06)
+#define PRIORITY_REASON_DATA_OUT_PARITY_ERR (0x07)
+#define PRIORITY_REASON_DATA_OUT_CRC_ERR (0x08)
#define PRIORITY_REASON_UNKNOWN (0xFF)
U8 Reserved2; /* 06h */
U8 MsgFlags; /* 07h */
U32 MsgContext; /* 08h */
- U16 Reserved3; /* 0Ch */
+ U8 PriorityReason; /* 0Ch */
+ U8 Reserved3; /* 0Dh */
U16 IOCStatus; /* 0Eh */
U32 IOCLogInfo; /* 10h */
U32 ReplyWord; /* 14h */
U8 Reserved2; /* 06h */
U8 MsgFlags; /* 07h */
U32 MsgContext; /* 08h */
- U16 Reserved3; /* 0Ch */
+ U8 PriorityReason; /* 0Ch */
+ U8 Reserved3; /* 0Dh */
U16 IOCStatus; /* 0Eh */
U32 IOCLogInfo; /* 10h */
U32 ReplyWord; /* 14h */
TargetStatusSendRequest_t, MPI_POINTER pTargetStatusSendRequest_t;
#define TARGET_STATUS_SEND_FLAGS_AUTO_GOOD_STATUS (0x01)
+#define TARGET_STATUS_SEND_FLAGS_HIGH_PRIORITY (0x04)
#define TARGET_STATUS_SEND_FLAGS_REPOST_CMD_BUFFER (0x80)
+typedef struct _MPI_TARGET_FCP_RSP_BUFFER
+{
+ U8 Reserved0[8]; /* 00h */
+ U8 FcpStatus; /* 08h */
+ U8 FcpFlags; /* 09h */
+ U8 Reserved1[2]; /* 0Ah */
+ U32 FcpResid; /* 0Ch */
+ U32 FcpSenseLength; /* 10h */
+ U32 FcpResponseLength; /* 14h */
+ U8 FcpResponseData[8]; /* 18h */
+ U8 FcpSenseData[32]; /* Pad to 64 bytes */ /* 20h */
+} MPI_TARGET_FCP_RSP_BUFFER, MPI_POINTER PTR_MPI_TARGET_FCP_RSP_BUFFER,
+ MpiTargetFcpRspBuffer, MPI_POINTER pMpiTargetFcpRspBuffer;
+
+typedef struct _MPI_TARGET_SCSI_SPI_STATUS_IU
+{
+ U8 Reserved0; /* 00h */
+ U8 Reserved1; /* 01h */
+ U8 Valid; /* 02h */
+ U8 Status; /* 03h */
+ U32 SenseDataListLength; /* 04h */
+ U32 PktFailuresListLength; /* 08h */
+ U8 SenseData[52]; /* Pad the IU to 64 bytes */ /* 0Ch */
+} MPI_TARGET_SCSI_SPI_STATUS_IU, MPI_POINTER PTR_MPI_TARGET_SCSI_SPI_STATUS_IU,
+ TargetScsiSpiStatusIU_t, MPI_POINTER pTargetScsiSpiStatusIU_t;
/****************************************************************************/
/* Target Mode Abort Request */
(((p) << TARGET_MODE_REPLY_PORT_SHIFT) & \
TARGET_MODE_REPLY_PORT_MASK))
+/* the following obsolete values are for MPI v1.0 support */
+#define TARGET_MODE_REPLY_0100_MASK_HOST_INDEX (0x000003FF)
+#define TARGET_MODE_REPLY_0100_SHIFT_HOST_INDEX (0)
+#define TARGET_MODE_REPLY_0100_MASK_IOC_INDEX (0x001FF800)
+#define TARGET_MODE_REPLY_0100_SHIFT_IOC_INDEX (11)
+#define TARGET_MODE_REPLY_0100_PORT_MASK (0x00400000)
+#define TARGET_MODE_REPLY_0100_PORT_SHIFT (22)
+#define TARGET_MODE_REPLY_0100_MASK_INITIATOR_INDEX (0x1F800000)
+#define TARGET_MODE_REPLY_0100_SHIFT_INITIATOR_INDEX (23)
+
+#define GET_HOST_INDEX_0100(x) (((x) & TARGET_MODE_REPLY_0100_MASK_HOST_INDEX) \
+ >> TARGET_MODE_REPLY_0100_SHIFT_HOST_INDEX)
+
+#define SET_HOST_INDEX_0100(t, hi) \
+ ((t) = ((t) & ~TARGET_MODE_REPLY_0100_MASK_HOST_INDEX) | \
+ (((hi) << TARGET_MODE_REPLY_0100_SHIFT_HOST_INDEX) & \
+ TARGET_MODE_REPLY_0100_MASK_HOST_INDEX))
+
+#define GET_IOC_INDEX_0100(x) (((x) & TARGET_MODE_REPLY_0100_MASK_IOC_INDEX) \
+ >> TARGET_MODE_REPLY_0100_SHIFT_IOC_INDEX)
+
+#define SET_IOC_INDEX_0100(t, ii) \
+ ((t) = ((t) & ~TARGET_MODE_REPLY_0100_MASK_IOC_INDEX) | \
+ (((ii) << TARGET_MODE_REPLY_0100_SHIFT_IOC_INDEX) & \
+ TARGET_MODE_REPLY_0100_MASK_IOC_INDEX))
+
+#define GET_INITIATOR_INDEX_0100(x) \
+ (((x) & TARGET_MODE_REPLY_0100_MASK_INITIATOR_INDEX) \
+ >> TARGET_MODE_REPLY_0100_SHIFT_INITIATOR_INDEX)
+
+#define SET_INITIATOR_INDEX_0100(t, ii) \
+ ((t) = ((t) & ~TARGET_MODE_REPLY_0100_MASK_INITIATOR_INDEX) | \
+ (((ii) << TARGET_MODE_REPLY_0100_SHIFT_INITIATOR_INDEX) & \
+ TARGET_MODE_REPLY_0100_MASK_INITIATOR_INDEX))
+
#endif
* Title: MPI Basic type definitions
* Creation Date: June 6, 2000
*
- * MPI Version: 01.01.02
+ * MPI Version: 01.02.01
*
* Version History
* ---------------
* 06-06-00 01.00.01 Update version number for 1.0 release.
* 11-02-00 01.01.01 Original release for post 1.0 work
* 02-20-01 01.01.02 Added define and ifdef for MPI_POINTER.
+ * 08-08-01 01.02.01 Original release for v1.2 work.
* --------------------------------------------------------------------------
*/
* And to Roger Hickerson (LSI Logic) for tirelessly supporting
* this driver project.
*
+ * A special thanks to Pamela Delaney (LSI Logic) for tons of work
+ * and countless enhancements while adding support for the 1030
+ * chip family. Pam has been instrumental in the development of
+ * of the 2.xx.xx series fusion drivers, and her contributions are
+ * far too numerous to hope to list in one place.
+ *
* All manner of help from Stephen Shirron (LSI Logic):
* low-level FC analysis, debug + various fixes in FCxx firmware,
* initial port to alpha platform, various driver code optimizations,
* for gobs of hard work fixing and optimizing LAN code.
* THANK YOU!
*
- * Copyright (c) 1999-2001 LSI Logic Corporation
+ * Copyright (c) 1999-2002 LSI Logic Corporation
* Originally By: Steven J. Ralston
- * (mailto:Steve.Ralston@lsil.com)
+ * (mailto:sjralston1@netscape.net)
+ * (mailto:Pam.Delaney@lsil.com)
*
- * $Id: mptbase.c,v 1.53.4.3 2001/09/18 03:54:54 sralston Exp $
+ * $Id: mptbase.c,v 1.119 2002/06/20 13:28:15 pdelaney Exp $
*/
/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
/*
#include <linux/kdev_t.h>
#include <linux/blkdev.h>
#include <linux/delay.h>
-#include <linux/proc_fs.h>
+#include <linux/interrupt.h> /* needed for in_interrupt() proto */
#include <asm/io.h>
#ifdef CONFIG_MTRR
#include <asm/mtrr.h>
#endif
+#ifdef __sparc__
+#include <asm/irq.h> /* needed for __irq_itoa() proto */
+#endif
#include "mptbase.h"
MODULE_DESCRIPTION(my_NAME);
MODULE_LICENSE("GPL");
-
/*
* cmd line parameters
*/
MODULE_PARM(PortIo, "0-1i");
MODULE_PARM_DESC(PortIo, "[0]=Use mmap, 1=Use port io");
-MODULE_PARM(HardReset, "0-1i");
-MODULE_PARM_DESC(HardReset, "0=Disable HardReset, [1]=Enable HardReset");
static int PortIo = 0;
-static int HardReset = 1;
+
+#ifdef MFCNT
+static int mfcounter = 0;
+#define PRINT_MF_COUNT 20000
+#endif
/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
/*
* Public data...
*/
-int mpt_lan_index = 0;
-int mpt_stm_index = 0;
+int mpt_lan_index = -1;
+int mpt_stm_index = -1;
+
+struct proc_dir_entry *mpt_proc_root_dir;
+
+DmpServices_t *DmpService;
+
+void *mpt_v_ASCQ_TablePtr;
+const char **mpt_ScsiOpcodesPtr;
+int mpt_ASCQ_TableSz;
-void *mpt_v_ASCQ_TablePtr = NULL;
-const char **mpt_ScsiOpcodesPtr = NULL;
-int mpt_ASCQ_TableSz = 0;
#define WHOINIT_UNKNOWN 0xAA
* Private data...
*/
/* Adapter lookup table */
-static MPT_ADAPTER *mpt_adapters[MPT_MAX_ADAPTERS] = {0};
+ MPT_ADAPTER *mpt_adapters[MPT_MAX_ADAPTERS];
static MPT_ADAPTER_TRACKER MptAdapters;
/* Callback lookup table */
static MPT_CALLBACK MptCallbacks[MPT_MAX_PROTOCOL_DRIVERS];
/* Protocol driver class lookup table */
-static int MptDriverClass[MPT_MAX_PROTOCOL_DRIVERS];
+static int MptDriverClass[MPT_MAX_PROTOCOL_DRIVERS];
/* Event handler lookup table */
static MPT_EVHANDLER MptEvHandlers[MPT_MAX_PROTOCOL_DRIVERS];
/* Reset handler lookup table */
static int FusionInitCalled = 0;
static int mpt_base_index = -1;
+static int last_drv_idx = -1;
+static int isense_idx = -1;
+
+static DECLARE_WAIT_QUEUE_HEAD(mpt_waitq);
/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
/*
static void mpt_interrupt(int irq, void *bus_id, struct pt_regs *r);
static int mpt_base_reply(MPT_ADAPTER *ioc, MPT_FRAME_HDR *req, MPT_FRAME_HDR *reply);
-static int mpt_do_ioc_recovery(MPT_ADAPTER *ioc, u32 reason);
-static int mpt_adapter_install(struct pci_dev *pdev);
-static void mpt_detect_929_bound_ports(MPT_ADAPTER *this, struct pci_dev *pdev);
+static int mpt_do_ioc_recovery(MPT_ADAPTER *ioc, u32 reason, int sleepFlag);
+static int mpt_adapter_install(struct pci_dev *pdev);
+static void mpt_detect_bound_ports(MPT_ADAPTER *this, struct pci_dev *pdev);
static void mpt_adapter_disable(MPT_ADAPTER *ioc, int freeup);
static void mpt_adapter_dispose(MPT_ADAPTER *ioc);
static void MptDisplayIocCapabilities(MPT_ADAPTER *ioc);
-static int MakeIocReady(MPT_ADAPTER *ioc, int force);
-static u32 GetIocState(MPT_ADAPTER *ioc, int cooked);
-static int GetIocFacts(MPT_ADAPTER *ioc);
-static int GetPortFacts(MPT_ADAPTER *ioc, int portnum);
-static int SendIocInit(MPT_ADAPTER *ioc);
-static int SendPortEnable(MPT_ADAPTER *ioc, int portnum);
-static int mpt_fc9x9_reset(MPT_ADAPTER *ioc, int ignore);
-static int KickStart(MPT_ADAPTER *ioc, int ignore);
-static int SendIocReset(MPT_ADAPTER *ioc, u8 reset_type);
+static int MakeIocReady(MPT_ADAPTER *ioc, int force, int sleepFlag);
+//static u32 mpt_GetIocState(MPT_ADAPTER *ioc, int cooked);
+static int GetIocFacts(MPT_ADAPTER *ioc, int sleepFlag, int reason);
+static int GetPortFacts(MPT_ADAPTER *ioc, int portnum, int sleepFlag);
+static int SendIocInit(MPT_ADAPTER *ioc, int sleepFlag);
+static int SendPortEnable(MPT_ADAPTER *ioc, int portnum, int sleepFlag);
+static int mpt_do_upload(MPT_ADAPTER *ioc, int sleepFlag);
+static int mpt_downloadboot(MPT_ADAPTER *ioc, int sleepFlag);
+static int mpt_diag_reset(MPT_ADAPTER *ioc, int ignore, int sleepFlag);
+static int KickStart(MPT_ADAPTER *ioc, int ignore, int sleepFlag);
+static int SendIocReset(MPT_ADAPTER *ioc, u8 reset_type, int sleepFlag);
static int PrimeIocFifos(MPT_ADAPTER *ioc);
-static int HandShakeReqAndReply(MPT_ADAPTER *ioc, int reqBytes, u32 *req, int replyBytes, u16 *u16reply, int maxwait);
-static int WaitForDoorbellAck(MPT_ADAPTER *ioc, int howlong);
-static int WaitForDoorbellInt(MPT_ADAPTER *ioc, int howlong);
-static int WaitForDoorbellReply(MPT_ADAPTER *ioc, int howlong);
+static int WaitForDoorbellAck(MPT_ADAPTER *ioc, int howlong, int sleepFlag);
+static int WaitForDoorbellInt(MPT_ADAPTER *ioc, int howlong, int sleepFlag);
+static int WaitForDoorbellReply(MPT_ADAPTER *ioc, int howlong, int sleepFlag);
static int GetLanConfigPages(MPT_ADAPTER *ioc);
+static int GetFcPortPage0(MPT_ADAPTER *ioc, int portnum);
+static int GetIoUnitPage2(MPT_ADAPTER *ioc);
+static int mpt_GetScsiPortSettings(MPT_ADAPTER *ioc, int portnum);
+static int mpt_readScsiDevicePageHeaders(MPT_ADAPTER *ioc, int portnum);
+static int mpt_findImVolumes(MPT_ADAPTER *ioc);
+static void mpt_timer_expired(unsigned long data);
static int SendEventNotification(MPT_ADAPTER *ioc, u8 EvSwitch);
static int SendEventAck(MPT_ADAPTER *ioc, EventNotificationReply_t *evnp);
-static int procmpt_create(void);
#ifdef CONFIG_PROC_FS
+static int procmpt_create(void);
static int procmpt_destroy(void);
+static int procmpt_summary_read(char *buf, char **start, off_t offset,
+ int request, int *eof, void *data);
+static int procmpt_version_read(char *buf, char **start, off_t offset,
+ int request, int *eof, void *data);
+static int procmpt_iocinfo_read(char *buf, char **start, off_t offset,
+ int request, int *eof, void *data);
#endif
-static int procmpt_read_summary(char *page, char **start, off_t off, int count, int *eof, void *data);
-static int procmpt_read_dbg(char *page, char **start, off_t off, int count, int *eof, void *data);
-/*static int procmpt_info(char *buf, char **start, off_t offset, int len);*/
+static void mpt_get_fw_exp_ver(char *buf, MPT_ADAPTER *ioc);
+//int mpt_HardResetHandler(MPT_ADAPTER *ioc, int sleepFlag);
static int ProcessEventNotification(MPT_ADAPTER *ioc, EventNotificationReply_t *evReply, int *evHandlers);
static void mpt_fc_log_info(MPT_ADAPTER *ioc, u32 log_info);
static void mpt_sp_log_info(MPT_ADAPTER *ioc, u32 log_info);
-static struct proc_dir_entry *procmpt_root_dir = NULL;
-
int fusion_init(void);
static void fusion_exit(void);
+/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
+/*
+ * more Private data...
+ */
+#ifdef CONFIG_PROC_FS
+struct _mpt_proc_list {
+ const char *name;
+ int (*f)(char *, char **, off_t, int, int *, void *);
+} mpt_proc_list[] = {
+ { "summary", procmpt_summary_read},
+ { "version", procmpt_version_read},
+};
+#define MPT_PROC_ENTRIES (sizeof(mpt_proc_list)/sizeof(mpt_proc_list[0]))
+
+struct _mpt_ioc_proc_list {
+ const char *name;
+ int (*f)(char *, char **, off_t, int, int *, void *);
+} mpt_ioc_proc_list[] = {
+ { "info", procmpt_iocinfo_read},
+ { "summary", procmpt_summary_read},
+};
+#define MPT_IOC_PROC_ENTRIES (sizeof(mpt_ioc_proc_list)/sizeof(mpt_ioc_proc_list[0]))
+
+#endif
+
/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
/* 20000207 -sralston
* GRRRRR... IOSpace (port i/o) register access (for the 909) is back!
writel(v, a);
}
+static inline void CHIPREG_PIO_WRITE32(volatile u32 *a, u32 v)
+{
+ outl(v, (unsigned long)a);
+}
+
+static inline u32 CHIPREG_PIO_READ32(volatile u32 *a)
+{
+ return inl((unsigned long)a);
+}
/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
-/**
+/*
* mpt_interrupt - MPT adapter (IOC) specific interrupt handler.
* @irq: irq number (not used)
* @bus_id: bus identifier cookie == pointer to MPT_ADAPTER structure
MPT_FRAME_HDR *mf;
MPT_FRAME_HDR *mr;
u32 pa;
- u32 *m;
- int req_idx;
+ int req_idx = -1;
int cb_idx;
int type;
int freeme;
ioc = bus_id;
+ /*
+ * Verify ioc pointer is ok
+ */
+ {
+ MPT_ADAPTER *iocCmp;
+ iocCmp = mpt_adapter_find_first();
+ while ((ioc != iocCmp) && iocCmp)
+ iocCmp = mpt_adapter_find_next(iocCmp);
+
+ if (!iocCmp) {
+ printk(KERN_WARNING "mpt_interrupt: Invalid ioc!\n");
+ return;
+ }
+ }
+
/*
* Drain the reply FIFO!
*
* Check for non-TURBO reply!
*/
if (pa & MPI_ADDRESS_REPLY_A_BIT) {
- dma_addr_t reply_dma_addr;
+ u32 reply_dma_low;
u16 ioc_stat;
/* non-TURBO reply! Hmmm, something may be up...
* Newest turbo reply mechanism; get address
* via left shift 1 (get rid of MPI_ADDRESS_REPLY_A_BIT)!
*/
- reply_dma_addr = (pa = (pa << 1));
- /* Map DMA address of reply header to cpu address. */
- m = (u32 *) ((u8 *)ioc->reply_frames +
- (reply_dma_addr - ioc->reply_frames_dma));
+ /* Map DMA address of reply header to cpu address.
+ * pa is 32 bits - but the dma address may be 32 or 64 bits
+ * get offset based only only the low addresses
+ */
+ reply_dma_low = (pa = (pa << 1));
+ mr = (MPT_FRAME_HDR *)((u8 *)ioc->reply_frames +
+ (reply_dma_low - ioc->reply_frames_low_dma));
- mr = (MPT_FRAME_HDR *) m;
req_idx = le16_to_cpu(mr->u.frame.hwhdr.msgctxu.fld.req_idx);
cb_idx = mr->u.frame.hwhdr.msgctxu.fld.cb_idx;
mf = MPT_INDEX_2_MFPTR(ioc, req_idx);
- dprintk((KERN_INFO MYNAM ": %s: Got non-TURBO reply=%p\n",
+ dprintk((MYIOC_s_INFO_FMT "Got non-TURBO reply=%p\n",
ioc->name, mr));
DBG_DUMP_REPLY_FRAME(mr)
* Check/log IOC log info
*/
ioc_stat = le16_to_cpu(mr->u.reply.IOCStatus);
- if (ioc_stat & MPI_IOCSTATUS_FLAG_LOG_INFO_AVAILABLE) {
+ if (ioc_stat & MPI_IOCSTATUS_FLAG_LOG_INFO_AVAILABLE) {
u32 log_info = le32_to_cpu(mr->u.reply.IOCLogInfo);
if ((int)ioc->chip_type <= (int)FC929)
mpt_fc_log_info(ioc, log_info);
/*
* Process turbo (context) reply...
*/
- dirqprintk((KERN_INFO MYNAM ": %s: Got TURBO reply(=%08x)\n", ioc->name, pa));
+ dirqprintk((MYIOC_s_INFO_FMT "Got TURBO reply(=%08x)\n", ioc->name, pa));
type = (pa >> MPI_CONTEXT_REPLY_TYPE_SHIFT);
if (type == MPI_CONTEXT_REPLY_TYPE_SCSI_TARGET) {
cb_idx = mpt_stm_index;
pa = 0; /* No reply flush! */
}
+ if ((int)ioc->chip_type > (int)FC929) {
+ /* Verify mf, mf are reasonable.
+ */
+ if ((mf) && ((mf >= MPT_INDEX_2_MFPTR(ioc, ioc->req_depth))
+ || (mf < ioc->req_frames)) ) {
+ printk(MYIOC_s_WARN_FMT
+ "mpt_interrupt: Invalid mf (%p) req_idx (%d)!\n", ioc->name, (void *)mf, req_idx);
+ cb_idx = 0;
+ pa = 0;
+ freeme = 0;
+ }
+ if ((pa) && (mr) && ((mr >= MPT_INDEX_2_RFPTR(ioc, ioc->req_depth))
+ || (mr < ioc->reply_frames)) ) {
+ printk(MYIOC_s_WARN_FMT
+ "mpt_interrupt: Invalid rf (%p)!\n", ioc->name, (void *)mr);
+ cb_idx = 0;
+ pa = 0;
+ freeme = 0;
+ }
+ if (cb_idx > (MPT_MAX_PROTOCOL_DRIVERS-1)) {
+ printk(MYIOC_s_WARN_FMT
+ "mpt_interrupt: Invalid cb_idx (%d)!\n", ioc->name, cb_idx);
+ cb_idx = 0;
+ pa = 0;
+ freeme = 0;
+ }
+ }
+
/* Check for (valid) IO callback! */
if (cb_idx) {
/* Do the callback! */
/* Put Request back on FreeQ! */
spin_lock_irqsave(&ioc->FreeQlock, flags);
Q_ADD_TAIL(&ioc->FreeQ, &mf->u.frame.linkage, MPT_FRAME_HDR);
+#ifdef MFCNT
+ ioc->mfcnt--;
+#endif
spin_unlock_irqrestore(&ioc->FreeQlock, flags);
}
count++;
- dirqprintk((KERN_INFO MYNAM ": %s: ISR processed frame #%d\n", ioc->name, count));
+ dirqprintk((MYIOC_s_INFO_FMT "ISR processed frame #%d\n", ioc->name, count));
mb();
if (count >= MPT_MAX_REPLIES_PER_ISR) {
- dirqprintk((KERN_INFO MYNAM ": %s: ISR processed %d replies.",
+ dirqprintk((MYIOC_s_INFO_FMT "ISR processed %d replies.",
ioc->name, count));
dirqprintk((" Giving this ISR a break!\n"));
return;
int freereq = 1;
u8 func;
- dprintk((KERN_INFO MYNAM ": %s: mpt_base_reply() called\n", ioc->name));
+ dprintk((MYIOC_s_INFO_FMT "mpt_base_reply() called\n", ioc->name));
if ((mf == NULL) ||
(mf >= MPT_INDEX_2_MFPTR(ioc, ioc->req_depth))) {
- printk(KERN_ERR MYNAM ": %s: ERROR - NULL or BAD request frame ptr! (=%p)\n",
- ioc->name, mf);
+ printk(MYIOC_s_ERR_FMT "NULL or BAD request frame ptr! (=%p)\n",
+ ioc->name, (void *)mf);
return 1;
}
if (reply == NULL) {
- dprintk((KERN_ERR MYNAM ": %s: ERROR - Unexpected NULL Event (turbo?) reply!\n",
+ dprintk((MYIOC_s_ERR_FMT "Unexpected NULL Event (turbo?) reply!\n",
ioc->name));
return 1;
}
}
func = reply->u.hdr.Function;
- dprintk((KERN_INFO MYNAM ": %s: mpt_base_reply, Function=%02Xh\n",
+ dprintk((MYIOC_s_INFO_FMT "mpt_base_reply, Function=%02Xh\n",
ioc->name, func));
if (func == MPI_FUNCTION_EVENT_NOTIFICATION) {
results = ProcessEventNotification(ioc, pEvReply, &evHandlers);
if (results != evHandlers) {
/* CHECKME! Any special handling needed here? */
- dprintk((KERN_WARNING MYNAM ": %s: Hmmm... Called %d event handlers, sum results = %d\n",
+ dprintk((MYIOC_s_WARN_FMT "Called %d event handlers, sum results = %d\n",
ioc->name, evHandlers, results));
}
/*
- * Hmmm... It seems that EventNotificationReply is an exception
- * to the rule of one reply per request.
+ * Hmmm... It seems that EventNotificationReply is an exception
+ * to the rule of one reply per request.
*/
if (pEvReply->MsgFlags & MPI_MSGFLAGS_CONTINUATION_REPLY)
freereq = 0;
+
#ifdef CONFIG_PROC_FS
// LogEvent(ioc, pEvReply);
#endif
+
} else if (func == MPI_FUNCTION_EVENT_ACK) {
- dprintk((KERN_INFO MYNAM ": %s: mpt_base_reply, EventAck reply received\n",
+ dprintk((MYIOC_s_INFO_FMT "mpt_base_reply, EventAck reply received\n",
ioc->name));
+ } else if (func == MPI_FUNCTION_CONFIG) {
+ CONFIGPARMS *pCfg;
+ unsigned long flags;
+
+ dprintk((MYIOC_s_INFO_FMT "config_complete (mf=%p,mr=%p)\n",
+ ioc->name, mf, reply));
+
+ pCfg = * ((CONFIGPARMS **)((u8 *) mf + ioc->req_sz - sizeof(void *)));
+
+ if (pCfg) {
+ /* disable timer and remove from linked list */
+ del_timer(&pCfg->timer);
+
+ spin_lock_irqsave(&ioc->FreeQlock, flags);
+ Q_DEL_ITEM(&pCfg->linkage);
+ spin_unlock_irqrestore(&ioc->FreeQlock, flags);
+
+ /*
+ * If IOC Status is SUCCESS, save the header
+ * and set the status code to GOOD.
+ */
+ pCfg->status = MPT_CONFIG_ERROR;
+ if (reply) {
+ ConfigReply_t *pReply = (ConfigReply_t *)reply;
+ u16 status;
+
+ status = le16_to_cpu(pReply->IOCStatus) & MPI_IOCSTATUS_MASK;
+ dprintk((KERN_NOTICE " IOCStatus=%04xh, IOCLogInfo=%08xh\n",
+ status, le32_to_cpu(pReply->IOCLogInfo)));
+
+ pCfg->status = status;
+ if (status == MPI_IOCSTATUS_SUCCESS) {
+ pCfg->hdr->PageVersion = pReply->Header.PageVersion;
+ pCfg->hdr->PageLength = pReply->Header.PageLength;
+ pCfg->hdr->PageNumber = pReply->Header.PageNumber;
+ pCfg->hdr->PageType = pReply->Header.PageType;
+ }
+ }
+
+ /*
+ * Wake up the original calling thread
+ */
+ pCfg->wait_done = 1;
+ wake_up(&mpt_waitq);
+ }
} else {
- printk(KERN_ERR MYNAM ": %s: ERROR - Unexpected msg function (=%02Xh) reply received!\n",
+ printk(MYIOC_s_ERR_FMT "Unexpected msg function (=%02Xh) reply received!\n",
ioc->name, func);
}
/*
- * Conditionally tell caller to free the original
- * EventNotification/EventAck/unexpected request frame!
+ * Conditionally tell caller to free the original
+ * EventNotification/EventAck/unexpected request frame!
*/
return freereq;
}
* protocol-specific driver must do this before it will be able to
* use any IOC resources, such as obtaining request frames.
*
- * NOTES: The SCSI protocol driver currently calls this routine twice
- * in order to register separate callbacks; one for "normal" SCSI IO
- * and another for MptScsiTaskMgmt requests.
+ * NOTES: The SCSI protocol driver currently calls this routine thrice
+ * in order to register separate callbacks; one for "normal" SCSI IO;
+ * one for MptScsiTaskMgmt requests; one for Scan/DV requests.
*
* Returns a positive integer valued "handle" in the
- * range (and S.O.D. order) {7,6,...,1} if successful.
+ * range (and S.O.D. order) {N,...,7,6,5,...,1} if successful.
* Any non-positive return value (including zero!) should be considered
* an error by the caller.
*/
int
mpt_register(MPT_CALLBACK cbfunc, MPT_DRIVER_CLASS dclass)
{
- int r = -1;
int i;
+ last_drv_idx = -1;
+
#ifndef MODULE
/*
* Handle possibility of the mptscsih_detect() routine getting
#endif
/*
- * Search for empty callback slot in this order: {7,6,...,1}
+ * Search for empty callback slot in this order: {N,...,7,6,5,...,1}
* (slot/handle 0 is reserved!)
*/
for (i = MPT_MAX_PROTOCOL_DRIVERS-1; i; i--) {
MptCallbacks[i] = cbfunc;
MptDriverClass[i] = dclass;
MptEvHandlers[i] = NULL;
- r = i;
+ last_drv_idx = i;
if (cbfunc != mpt_base_reply) {
MOD_INC_USE_COUNT;
}
}
}
- return r;
+ return last_drv_idx;
}
/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
MptCallbacks[cb_idx] = NULL;
MptDriverClass[cb_idx] = MPTUNKNOWN_DRIVER;
MptEvHandlers[cb_idx] = NULL;
+
+ last_drv_idx++;
+ if (isense_idx != -1 && isense_idx <= cb_idx)
+ isense_idx++;
+
if (cb_idx != mpt_base_index) {
MOD_DEC_USE_COUNT;
}
* @handle: Handle of registered MPT protocol driver
* @iocid: IOC unique identifier (integer)
*
- * Returns pointer to a MPT request frame or %NULL if none are available.
+ * Returns pointer to a MPT request frame or %NULL if none are available
+ * or IOC is not active.
*/
MPT_FRAME_HDR*
mpt_get_msg_frame(int handle, int iocid)
/* validate handle and ioc identifier */
iocp = mpt_adapters[iocid];
+
+#ifdef MFCNT
+ if (!iocp->active)
+ printk(KERN_WARNING "IOC Not Active! mpt_get_msg_frame returning NULL!\n");
+#endif
+
+ /* If interrupts are not attached, do not return a request frame */
+ if (!iocp->active)
+ return NULL;
+
spin_lock_irqsave(&iocp->FreeQlock, flags);
if (! Q_IS_EMPTY(&iocp->FreeQ)) {
int req_offset;
mf->u.frame.hwhdr.msgctxu.fld.req_idx =
cpu_to_le16(req_offset / iocp->req_sz);
mf->u.frame.hwhdr.msgctxu.fld.rsvd = 0;
+#ifdef MFCNT
+ iocp->mfcnt++;
+#endif
}
spin_unlock_irqrestore(&iocp->FreeQlock, flags);
+
+#ifdef MFCNT
+ if (mf == NULL)
+ printk(KERN_WARNING "IOC Active. No free Msg Frames! Count 0x%x Max 0x%x\n", iocp->mfcnt, iocp->req_depth);
+ mfcounter++;
+ if (mfcounter == PRINT_MF_COUNT)
+ printk(KERN_INFO "MF Count 0x%x Max 0x%x \n", iocp->mfcnt, iocp->req_depth);
+#endif
+
dmfprintk((KERN_INFO MYNAM ": %s: mpt_get_msg_frame(%d,%d), got mf=%p\n",
iocp->name, handle, iocid, mf));
return mf;
iocp = mpt_adapters[iocid];
if (iocp != NULL) {
- dma_addr_t mf_dma_addr;
+ u32 mf_dma_addr;
int req_offset;
/* ensure values are reset properly! */
#ifdef MPT_DEBUG_MSG_FRAME
{
u32 *m = mf->u.frame.hwhdr.__hdr;
- int i, n;
+ int ii, n;
printk(KERN_INFO MYNAM ": %s: About to Put msg frame @ %p:\n" KERN_INFO " ",
iocp->name, m);
n = iocp->req_sz/4 - 1;
while (m[n] == 0)
n--;
- for (i=0; i<=n; i++) {
- if (i && ((i%8)==0))
+ for (ii=0; ii<=n; ii++) {
+ if (ii && ((ii%8)==0))
printk("\n" KERN_INFO " ");
- printk(" %08x", le32_to_cpu(m[i]));
+ printk(" %08x", le32_to_cpu(m[ii]));
}
printk("\n");
}
#endif
- mf_dma_addr = iocp->req_frames_dma + req_offset;
+ mf_dma_addr = iocp->req_frames_low_dma + req_offset;
CHIPREG_WRITE32(&iocp->chip->RequestFifo, mf_dma_addr);
}
}
/* Put Request back on FreeQ! */
spin_lock_irqsave(&iocp->FreeQlock, flags);
Q_ADD_TAIL(&iocp->FreeQ, &mf->u.frame.linkage, MPT_FRAME_HDR);
+#ifdef MFCNT
+ iocp->mfcnt--;
+#endif
spin_unlock_irqrestore(&iocp->FreeQlock, flags);
}
}
+/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
+/**
+ * mpt_add_sge - Place a simple SGE at address pAddr.
+ * @pAddr: virtual address for SGE
+ * @flagslength: SGE flags and data transfer length
+ * @dma_addr: Physical address
+ *
+ * This routine places a MPT request frame back on the MPT adapter's
+ * FreeQ.
+ */
+void
+mpt_add_sge(char *pAddr, u32 flagslength, dma_addr_t dma_addr)
+{
+ if (sizeof(dma_addr_t) == sizeof(u64)) {
+ SGESimple64_t *pSge = (SGESimple64_t *) pAddr;
+ u32 tmp = dma_addr & 0xFFFFFFFF;
+
+ pSge->FlagsLength = cpu_to_le32(flagslength);
+ pSge->Address.Low = cpu_to_le32(tmp);
+ tmp = (u32) ((u64)dma_addr >> 32);
+ pSge->Address.High = cpu_to_le32(tmp);
+
+ } else {
+ SGESimple32_t *pSge = (SGESimple32_t *) pAddr;
+ pSge->FlagsLength = cpu_to_le32(flagslength);
+ pSge->Address = cpu_to_le32(dma_addr);
+ }
+}
+
+/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
+/**
+ * mpt_add_chain - Place a chain SGE at address pAddr.
+ * @pAddr: virtual address for SGE
+ * @next: nextChainOffset value (u32's)
+ * @length: length of next SGL segment
+ * @dma_addr: Physical address
+ *
+ * This routine places a MPT request frame back on the MPT adapter's
+ * FreeQ.
+ */
+void
+mpt_add_chain(char *pAddr, u8 next, u16 length, dma_addr_t dma_addr)
+{
+ if (sizeof(dma_addr_t) == sizeof(u64)) {
+ SGEChain64_t *pChain = (SGEChain64_t *) pAddr;
+ u32 tmp = dma_addr & 0xFFFFFFFF;
+
+ pChain->Length = cpu_to_le16(length);
+ pChain->Flags = MPI_SGE_FLAGS_CHAIN_ELEMENT | mpt_addr_size();
+
+ pChain->NextChainOffset = next;
+
+ pChain->Address.Low = cpu_to_le32(tmp);
+ tmp = (u32) ((u64)dma_addr >> 32);
+ pChain->Address.High = cpu_to_le32(tmp);
+ } else {
+ SGEChain32_t *pChain = (SGEChain32_t *) pAddr;
+ pChain->Length = cpu_to_le16(length);
+ pChain->Flags = MPI_SGE_FLAGS_CHAIN_ELEMENT | mpt_addr_size();
+ pChain->NextChainOffset = next;
+ pChain->Address = cpu_to_le32(dma_addr);
+ }
+}
+
/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
/**
* mpt_send_handshake_request - Send MPT request via doorbell
* @iocid: IOC unique identifier (integer)
* @reqBytes: Size of the request in bytes
* @req: Pointer to MPT request frame
+ * @sleepFlag: Use schedule if CAN_SLEEP else use udelay.
*
- * This routine is used exclusively by mptscsih to send MptScsiTaskMgmt
+ * This routine is used exclusively to send MptScsiTaskMgmt
* requests since they are required to be sent via doorbell handshake.
*
* NOTE: It is the callers responsibility to byte-swap fields in the
* Returns 0 for success, non-zero for failure.
*/
int
-mpt_send_handshake_request(int handle, int iocid, int reqBytes, u32 *req)
+mpt_send_handshake_request(int handle, int iocid, int reqBytes, u32 *req, int sleepFlag)
{
MPT_ADAPTER *iocp;
int r = 0;
iocp = mpt_adapters[iocid];
if (iocp != NULL) {
- u8 *req_as_bytes;
- u32 ioc_raw_state;
- int i;
+ u8 *req_as_bytes;
+ int ii;
- /* YIKES! We already know something is amiss.
- * Do upfront check on IOC state.
+ /* State is known to be good upon entering
+ * this function so issue the bus reset
+ * request.
*/
- ioc_raw_state = GetIocState(iocp, 0);
- if ((ioc_raw_state & MPI_DOORBELL_ACTIVE) ||
- ((ioc_raw_state & MPI_IOC_STATE_MASK) != MPI_IOC_STATE_OPERATIONAL)) {
- printk(KERN_WARNING MYNAM ": %s: Bad IOC state (%08x) WARNING!\n",
- iocp->name, ioc_raw_state);
- if ((r = mpt_do_ioc_recovery(iocp, MPT_HOSTEVENT_IOC_RECOVER)) != 0) {
- printk(KERN_WARNING MYNAM ": WARNING - (%d) Cannot recover %s\n",
- r, iocp->name);
- return r;
- }
- }
/*
* Emulate what mpt_put_msg_frame() does /wrt to sanity
* setting cb_idx/req_idx. But ONLY if this request
* is in proper (pre-alloc'd) request buffer range...
*/
- i = MFPTR_2_MPT_INDEX(iocp,(MPT_FRAME_HDR*)req);
- if (reqBytes >= 12 && i >= 0 && i < iocp->req_depth) {
+ ii = MFPTR_2_MPT_INDEX(iocp,(MPT_FRAME_HDR*)req);
+ if (reqBytes >= 12 && ii >= 0 && ii < iocp->req_depth) {
MPT_FRAME_HDR *mf = (MPT_FRAME_HDR*)req;
- mf->u.frame.hwhdr.msgctxu.fld.req_idx = cpu_to_le16(i);
+ mf->u.frame.hwhdr.msgctxu.fld.req_idx = cpu_to_le16(ii);
mf->u.frame.hwhdr.msgctxu.fld.cb_idx = handle;
}
((reqBytes/4)<<MPI_DOORBELL_ADD_DWORDS_SHIFT)));
/* Wait for IOC doorbell int */
- if ((i = WaitForDoorbellInt(iocp, 2)) < 0) {
- return i;
+ if ((ii = WaitForDoorbellInt(iocp, 2, sleepFlag)) < 0) {
+ return ii;
}
+ /* Read doorbell and check for active bit */
+ if (!(CHIPREG_READ32(&iocp->chip->Doorbell) & MPI_DOORBELL_ACTIVE))
+ return -5;
+
dhsprintk((KERN_INFO MYNAM ": %s: mpt_send_handshake_request start, WaitCnt=%d\n",
- iocp->name, i));
+ iocp->name, ii));
CHIPREG_WRITE32(&iocp->chip->IntStatus, 0);
- if ((r = WaitForDoorbellAck(iocp, 1)) < 0) {
+ if ((r = WaitForDoorbellAck(iocp, 1, sleepFlag)) < 0) {
return -2;
}
/* Send request via doorbell handshake */
req_as_bytes = (u8 *) req;
- for (i = 0; i < reqBytes/4; i++) {
+ for (ii = 0; ii < reqBytes/4; ii++) {
u32 word;
- word = ((req_as_bytes[(i*4) + 0] << 0) |
- (req_as_bytes[(i*4) + 1] << 8) |
- (req_as_bytes[(i*4) + 2] << 16) |
- (req_as_bytes[(i*4) + 3] << 24));
+ word = ((req_as_bytes[(ii*4) + 0] << 0) |
+ (req_as_bytes[(ii*4) + 1] << 8) |
+ (req_as_bytes[(ii*4) + 2] << 16) |
+ (req_as_bytes[(ii*4) + 3] << 24));
CHIPREG_WRITE32(&iocp->chip->Doorbell, word);
- if ((r = WaitForDoorbellAck(iocp, 1)) < 0) {
+ if ((r = WaitForDoorbellAck(iocp, 1, sleepFlag)) < 0) {
r = -3;
break;
}
}
- if ((r = WaitForDoorbellInt(iocp, 2)) >= 0)
+ if (r >= 0 && WaitForDoorbellInt(iocp, 10, sleepFlag) >= 0)
r = 0;
else
r = -4;
/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
/**
- * mpt_adapter_find_next - Find next MPT adapter pointer.
- * @prev: Pointer to previous MPT adapter
+ * mpt_adapter_find_next - Find next MPT adapter pointer.
+ * @prev: Pointer to previous MPT adapter
*
* Returns next MPT adapter pointer or %NULL if there are no more.
*/
}
/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
-/**
+/*
* mpt_pci_scan - Scan PCI devices for MPT adapters.
*
* Returns count of MPT adapters found, keying off of PCI vendor and
* device_id's.
*/
-int __init
+static int __init
mpt_pci_scan(void)
{
struct pci_dev *pdev;
dprintk((KERN_INFO MYNAM ": Checking for MPT adapters...\n"));
/*
- * NOTE: The 929 (I believe) will appear as 2 separate PCI devices,
+ * NOTE: The 929, 929X and 1030 will appear as 2 separate PCI devices,
* one for each channel.
*/
pci_for_each_dev(pdev) {
if ((pdev->device != MPI_MANUFACTPAGE_DEVICEID_FC909) &&
(pdev->device != MPI_MANUFACTPAGE_DEVICEID_FC929) &&
(pdev->device != MPI_MANUFACTPAGE_DEVICEID_FC919) &&
+ (pdev->device != MPI_MANUFACTPAGE_DEVICEID_FC929X) &&
+ (pdev->device != MPI_MANUFACTPAGE_DEVICEID_FC919X) &&
+ (pdev->device != MPI_MANUFACTPAGE_DEVID_53C1030) &&
#if 0
/* FIXME! C103x family */
- (pdev->device != MPI_MANUFACTPAGE_DEVID_53C1030) &&
(pdev->device != MPI_MANUFACTPAGE_DEVID_53C1030_ZC) &&
(pdev->device != MPI_MANUFACTPAGE_DEVID_53C1035) &&
#endif
}
/* GRRRRR
- * 929 dual function devices may be presented in Func 1,0 order,
+ * dual function devices (929, 929X, 1030) may be presented in Func 1,0 order,
* but we'd really really rather have them in Func 0,1 order.
* Do some kind of look ahead here...
*/
pdev2 = pci_peek_next_dev(pdev);
if (pdev2 && (pdev2->vendor == 0x1000) &&
(PCI_SLOT(pdev2->devfn) == PCI_SLOT(pdev->devfn)) &&
- (pdev2->device == MPI_MANUFACTPAGE_DEVICEID_FC929) &&
+ (pdev2->device == pdev->device) &&
(pdev2->bus->number == pdev->bus->number) &&
!(pdev2->devfn & 1)) {
dprintk((KERN_INFO MYNAM ": MPT adapter found: PCI bus/dfn=%02x/%02xh, class=%08x, id=%xh\n",
- pdev2->bus->number, pdev2->devfn, pdev2->class, pdev2->device));
+ pdev2->bus->number, pdev2->devfn, pdev2->class, pdev2->device));
found++;
if ((r = mpt_adapter_install(pdev2)) == 0)
count++;
}
#ifdef CONFIG_PROC_FS
- if (procmpt_create() != 0)
- printk(KERN_WARNING MYNAM ": WARNING! - %s creation failed!\n",
- MPT_PROCFS_MPTBASEDIR);
+ (void) procmpt_create();
#endif
return count;
}
/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
-/**
+/*
* mpt_adapter_install - Install a PCI intelligent MPT adapter.
* @pdev: Pointer to pci_dev structure
*
mpt_adapter_install(struct pci_dev *pdev)
{
MPT_ADAPTER *ioc;
- char *myname;
u8 *mem;
unsigned long mem_phys;
unsigned long port;
u32 msize;
u32 psize;
- int i;
+ int ii;
int r = -ENODEV;
- int len;
+ u64 mask = 0xffffffffffffffff;
- ioc = kmalloc(sizeof(MPT_ADAPTER), GFP_KERNEL);
+ if (pci_enable_device(pdev))
+ return r;
+
+ if (!pci_set_dma_mask(pdev, mask)) {
+ dprintk((KERN_INFO MYNAM
+ ": 64 BIT PCI BUS DMA ADDRESSING SUPPORTED\n"));
+ } else if (pci_set_dma_mask(pdev, (u64) 0xffffffff)) {
+ printk(KERN_WARNING MYNAM ": 32 BIT PCI BUS DMA ADDRESSING NOT SUPPORTED\n");
+ return r;
+ }
+
+ ioc = kmalloc(sizeof(MPT_ADAPTER), GFP_ATOMIC);
if (ioc == NULL) {
printk(KERN_ERR MYNAM ": ERROR - Insufficient memory to add adapter!\n");
return -ENOMEM;
}
memset(ioc, 0, sizeof(*ioc));
- ioc->req_sz = MPT_REQ_SIZE; /* avoid div by zero! */
ioc->alloc_total = sizeof(MPT_ADAPTER);
+ ioc->req_sz = MPT_DEFAULT_FRAME_SIZE; /* avoid div by zero! */
+ ioc->reply_sz = ioc->req_sz;
ioc->pcidev = pdev;
+ ioc->diagPending = 0;
+ spin_lock_init(&ioc->diagLock);
+
+ /* Initialize the event logging.
+ */
+ ioc->eventTypes = 0; /* None */
+ ioc->eventContext = 0;
+ ioc->eventLogSize = 0;
+ ioc->events = NULL;
+
+#ifdef MFCNT
+ ioc->mfcnt = 0;
+#endif
+
+ ioc->cached_fw = NULL;
+
+ /* Initilize SCSI Config Data structure
+ */
+ memset(&ioc->spi_data, 0, sizeof(ScsiCfgData));
+
+ /* Initialize the running configQ head.
+ */
+ Q_INIT(&ioc->configQ, Q_ITEM);
/* Find lookup slot. */
- for (i=0; i < MPT_MAX_ADAPTERS; i++) {
- if (mpt_adapters[i] == NULL) {
- ioc->id = i; /* Assign adapter unique id (lookup) */
+ for (ii=0; ii < MPT_MAX_ADAPTERS; ii++) {
+ if (mpt_adapters[ii] == NULL) {
+ ioc->id = ii; /* Assign adapter unique id (lookup) */
break;
}
}
- if (i == MPT_MAX_ADAPTERS) {
- printk(KERN_ERR MYNAM ": ERROR - mpt_adapters[%d] table overflow!\n", i);
+ if (ii == MPT_MAX_ADAPTERS) {
+ printk(KERN_ERR MYNAM ": ERROR - mpt_adapters[%d] table overflow!\n", ii);
kfree(ioc);
return -ENFILE;
}
mem_phys = msize = 0;
port = psize = 0;
- for (i=0; i < DEVICE_COUNT_RESOURCE; i++) {
- if (pdev->PCI_BASEADDR_FLAGS(i) & PCI_BASE_ADDRESS_SPACE_IO) {
+ for (ii=0; ii < DEVICE_COUNT_RESOURCE; ii++) {
+ if (pdev->PCI_BASEADDR_FLAGS(ii) & PCI_BASE_ADDRESS_SPACE_IO) {
/* Get I/O space! */
- port = pdev->PCI_BASEADDR_START(i);
- psize = PCI_BASEADDR_SIZE(pdev,i);
+ port = pdev->PCI_BASEADDR_START(ii);
+ psize = PCI_BASEADDR_SIZE(pdev,ii);
} else {
/* Get memmap */
- mem_phys = pdev->PCI_BASEADDR_START(i);
- msize = PCI_BASEADDR_SIZE(pdev,i);
+ mem_phys = pdev->PCI_BASEADDR_START(ii);
+ msize = PCI_BASEADDR_SIZE(pdev,ii);
break;
}
}
ioc->mem_size = msize;
- if (i == DEVICE_COUNT_RESOURCE) {
+ if (ii == DEVICE_COUNT_RESOURCE) {
printk(KERN_ERR MYNAM ": ERROR - MPT adapter has no memory regions defined!\n");
kfree(ioc);
return -EINVAL;
}
dprintk((KERN_INFO MYNAM ": mem = %p, mem_phys = %lx\n", mem, mem_phys));
+ dprintk((KERN_INFO MYNAM ": facts @ %p, pfacts[0] @ %p\n",
+ &ioc->facts, &ioc->pfacts[0]));
if (PortIo) {
u8 *pmem = (u8*)port;
ioc->mem_phys = port;
ioc->chip = (SYSIF_REGS*)mem;
}
+ /* Save Port IO values incase we need to do downloadboot */
+ {
+ u8 *pmem = (u8*)port;
+ ioc->pio_mem_phys = port;
+ ioc->pio_chip = (SYSIF_REGS*)pmem;
+ }
+
ioc->chip_type = FCUNK;
if (pdev->device == MPI_MANUFACTPAGE_DEVICEID_FC909) {
ioc->chip_type = FC909;
ioc->chip_type = FC919;
ioc->prod_name = "LSIFC919";
}
-#if 0
- else if (pdev->device == MPI_MANUFACTPAGE_DEVICEID_53C1030) {
+ else if (pdev->device == MPI_MANUFACTPAGE_DEVICEID_FC929X) {
+ ioc->chip_type = FC929X;
+ ioc->prod_name = "LSIFC929X";
+ }
+ else if (pdev->device == MPI_MANUFACTPAGE_DEVICEID_FC919X) {
+ ioc->chip_type = FC919X;
+ ioc->prod_name = "LSIFC919X";
+ }
+ else if (pdev->device == MPI_MANUFACTPAGE_DEVID_53C1030) {
ioc->chip_type = C1030;
ioc->prod_name = "LSI53C1030";
+ {
+ /* 1030 Chip Fix. Disable Split transactions
+ * for PCIX. Set bits 4 - 6 to zero.
+ */
+ u16 pcixcmd = 0;
+ pci_read_config_word(pdev, 0x6a, &pcixcmd);
+ pcixcmd &= 0xFF8F;
+ pci_write_config_word(pdev, 0x6a, pcixcmd);
+ }
}
-#endif
- myname = "iocN";
- len = strlen(myname);
- memcpy(ioc->name, myname, len+1);
- ioc->name[len-1] = '0' + ioc->id;
+ sprintf(ioc->name, "ioc%d", ioc->id);
Q_INIT(&ioc->FreeQ, MPT_FRAME_HDR);
spin_lock_init(&ioc->FreeQlock);
r = request_irq(pdev->irq, mpt_interrupt, SA_SHIRQ, ioc->name, ioc);
if (r < 0) {
- printk(KERN_ERR MYNAM ": %s: ERROR - Unable to allocate interrupt %d!\n",
+#ifndef __sparc__
+ printk(MYIOC_s_ERR_FMT "Unable to allocate interrupt %d!\n",
ioc->name, pdev->irq);
+#else
+ printk(MYIOC_s_ERR_FMT "Unable to allocate interrupt %s!\n",
+ ioc->name, __irq_itoa(pdev->irq));
+#endif
iounmap(mem);
kfree(ioc);
return -EBUSY;
pci_set_master(pdev); /* ?? */
+#ifndef __sparc__
dprintk((KERN_INFO MYNAM ": %s installed at interrupt %d\n", ioc->name, pdev->irq));
+#else
+ dprintk((KERN_INFO MYNAM ": %s installed at interrupt %s\n", ioc->name, __irq_itoa(pdev->irq)));
+#endif
}
/* tack onto tail of our MPT adapter list */
mpt_adapters[ioc->id] = ioc;
/* NEW! 20010220 -sralston
- * Check for "929 bound ports" to reduce redundant resets.
+ * Check for "bound ports" (929, 929X, 1030) to reduce redundant resets.
*/
- if (ioc->chip_type == FC929)
- mpt_detect_929_bound_ports(ioc, pdev);
+ if ((ioc->chip_type == FC929) || (ioc->chip_type == C1030) || (ioc->chip_type == FC929X))
+ mpt_detect_bound_ports(ioc, pdev);
- if ((r = mpt_do_ioc_recovery(ioc, MPT_HOSTEVENT_IOC_BRINGUP)) != 0) {
+ if ((r = mpt_do_ioc_recovery(ioc, MPT_HOSTEVENT_IOC_BRINGUP, CAN_SLEEP)) != 0) {
printk(KERN_WARNING MYNAM ": WARNING - %s did not initialize properly! (%d)\n",
ioc->name, r);
}
}
/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
-/**
+/*
* mpt_do_ioc_recovery - Initialize or recover MPT adapter.
* @ioc: Pointer to MPT adapter structure
* @reason: Event word / reason
+ * @sleepFlag: Use schedule if CAN_SLEEP else use udelay.
*
* This routine performs all the steps necessary to bring the IOC
* to a OPERATIONAL state.
* This routine also pre-fetches the LAN MAC address of a Fibre Channel
* MPT adapter.
*
- * Returns 0 for success.
+ * Returns:
+ * 0 for success
+ * -1 if failed to get board READY
+ * -2 if READY but IOCFacts Failed
+ * -3 if READY but PrimeIOCFifos Failed
+ * -4 if READY but IOCInit Failed
*/
static int
-mpt_do_ioc_recovery(MPT_ADAPTER *ioc, u32 reason)
+mpt_do_ioc_recovery(MPT_ADAPTER *ioc, u32 reason, int sleepFlag)
{
int hard_reset_done = 0;
int alt_ioc_ready = 0;
int hard;
int r;
- int i;
+ int ii;
int handlers;
printk(KERN_INFO MYNAM ": Initiating %s %s\n",
ioc->active = 0;
/* NOTE: Access to IOC's request FreeQ is now blocked! */
-// FIXME? Cleanup all IOC requests here! (or below?)
-// But watch out for event associated request?
+ if (ioc->alt_ioc) {
+ /* Disable alt-IOC's reply interrupts for a bit ... */
+ CHIPREG_WRITE32(&ioc->alt_ioc->chip->IntMask, 0xFFFFFFFF);
+ ioc->alt_ioc->active = 0;
+ /* NOTE: Access to alt-IOC's request FreeQ is now blocked! */
+ }
- hard = HardReset;
- if (ioc->alt_ioc && (reason == MPT_HOSTEVENT_IOC_BRINGUP))
+ hard = 1;
+ if (reason == MPT_HOSTEVENT_IOC_BRINGUP)
hard = 0;
- if ((hard_reset_done = MakeIocReady(ioc, hard)) < 0) {
+ if ((hard_reset_done = MakeIocReady(ioc, hard, sleepFlag)) < 0) {
printk(KERN_WARNING MYNAM ": %s NOT READY WARNING!\n",
ioc->name);
return -1;
}
-// NEW!
-#if 0 // Kiss-of-death!?!
- if (ioc->alt_ioc) {
-// Grrr... Hold off any alt-IOC interrupts (and events) while
-// handshaking to <this> IOC, needed because?
- /* Disable alt-IOC's reply interrupts for a bit ... */
- alt_ioc_intmask = CHIPREG_READ32(&ioc->alt_ioc->chip->IntMask);
- CHIPREG_WRITE32(&ioc->alt_ioc->chip->IntMask, 0xFFFFFFFF);
- ioc->alt_ioc->active = 0;
- /* NOTE: Access to alt-IOC's request FreeQ is now blocked! */
- }
-#endif
-
+ /* hard_reset_done = 0 if a soft reset was performed
+ * and 1 if a hard reset was performed.
+ */
if (hard_reset_done && ioc->alt_ioc) {
- if ((r = MakeIocReady(ioc->alt_ioc, 0)) == 0)
+ if ((r = MakeIocReady(ioc->alt_ioc, 0, sleepFlag)) == 0)
alt_ioc_ready = 1;
else
- printk(KERN_WARNING MYNAM ": alt-%s: (%d) Not ready WARNING!\n",
+ printk(KERN_WARNING MYNAM
+ ": alt-%s: (%d) Not ready WARNING!\n",
ioc->alt_ioc->name, r);
}
+ /* Get IOC facts! */
+ if ((r = GetIocFacts(ioc, sleepFlag, reason)) != 0)
+ return -2;
if (reason == MPT_HOSTEVENT_IOC_BRINGUP) {
- /* Get IOC facts! */
- if ((r = GetIocFacts(ioc)) != 0)
- return -2;
MptDisplayIocCapabilities(ioc);
}
- /*
- * Call each currently registered protocol IOC reset handler
- * with pre-reset indication.
- * NOTE: If we're doing _IOC_BRINGUP, there can be no
- * MptResetHandlers[] registered yet.
- */
- if (hard_reset_done) {
- r = handlers = 0;
- for (i=MPT_MAX_PROTOCOL_DRIVERS-1; i; i--) {
- if (MptResetHandlers[i]) {
- dprintk((KERN_INFO MYNAM ": %s: Calling IOC pre_reset handler #%d\n",
- ioc->name, i));
- r += (*(MptResetHandlers[i]))(ioc, MPT_IOC_PRE_RESET);
- handlers++;
-
- if (alt_ioc_ready) {
- dprintk((KERN_INFO MYNAM ": %s: Calling alt-IOC pre_reset handler #%d\n",
- ioc->alt_ioc->name, i));
- r += (*(MptResetHandlers[i]))(ioc->alt_ioc, MPT_IOC_PRE_RESET);
- handlers++;
- }
- }
- }
- /* FIXME? Examine results here? */
- }
-
- // May need to check/upload firmware & data here!
-
- if ((r = SendIocInit(ioc)) != 0)
- return -3;
-// NEW!
if (alt_ioc_ready) {
- if ((r = SendIocInit(ioc->alt_ioc)) != 0) {
- alt_ioc_ready = 0;
- printk(KERN_WARNING MYNAM ": alt-%s: (%d) init failure WARNING!\n",
- ioc->alt_ioc->name, r);
- }
- }
-
- /*
- * Call each currently registered protocol IOC reset handler
- * with post-reset indication.
- * NOTE: If we're doing _IOC_BRINGUP, there can be no
- * MptResetHandlers[] registered yet.
- */
- if (hard_reset_done) {
- r = handlers = 0;
- for (i=MPT_MAX_PROTOCOL_DRIVERS-1; i; i--) {
- if (MptResetHandlers[i]) {
- dprintk((KERN_INFO MYNAM ": %s: Calling IOC post_reset handler #%d\n",
- ioc->name, i));
- r += (*(MptResetHandlers[i]))(ioc, MPT_IOC_POST_RESET);
- handlers++;
-
- if (alt_ioc_ready) {
- dprintk((KERN_INFO MYNAM ": %s: Calling alt-IOC post_reset handler #%d\n",
- ioc->alt_ioc->name, i));
- r += (*(MptResetHandlers[i]))(ioc->alt_ioc, MPT_IOC_POST_RESET);
- handlers++;
- }
- }
+ if ((r = GetIocFacts(ioc->alt_ioc, sleepFlag, reason)) != 0)
+ return -2;
+ if (reason == MPT_HOSTEVENT_IOC_BRINGUP) {
+ MptDisplayIocCapabilities(ioc->alt_ioc);
}
- /* FIXME? Examine results here? */
}
/*
* Prime reply & request queues!
- * (mucho alloc's)
+ * (mucho alloc's) Must be done prior to
+ * init as upper addresses are needed for init.
*/
if ((r = PrimeIocFifos(ioc)) != 0)
+ return -3;
+
+ // May need to check/upload firmware & data here!
+ if ((r = SendIocInit(ioc, sleepFlag)) != 0)
return -4;
// NEW!
if (alt_ioc_ready && ((r = PrimeIocFifos(ioc->alt_ioc)) != 0)) {
printk(KERN_WARNING MYNAM ": alt-%s: (%d) FIFO mgmt alloc WARNING!\n",
ioc->alt_ioc->name, r);
+ alt_ioc_ready = 0;
}
-// FIXME! Cleanup all IOC (and alt-IOC?) requests here!
+ if (alt_ioc_ready) {
+ if ((r = SendIocInit(ioc->alt_ioc, sleepFlag)) != 0) {
+ alt_ioc_ready = 0;
+ printk(KERN_WARNING MYNAM
+ ": alt-%s: (%d) init failure WARNING!\n",
+ ioc->alt_ioc->name, r);
+ }
+ }
- if ((ioc->pfacts[0].ProtocolFlags & MPI_PORTFACTS_PROTOCOL_LAN) &&
- (ioc->lan_cnfg_page0.Header.PageLength == 0)) {
- /*
- * Pre-fetch the ports LAN MAC address!
- * (LANPage1_t stuff)
- */
- (void) GetLanConfigPages(ioc);
-#ifdef MPT_DEBUG
- {
- u8 *a = (u8*)&ioc->lan_cnfg_page1.HardwareAddressLow;
- dprintk((KERN_INFO MYNAM ": %s: LanAddr = %02X:%02X:%02X:%02X:%02X:%02X\n",
- ioc->name, a[5], a[4], a[3], a[2], a[1], a[0] ));
+ if (reason == MPT_HOSTEVENT_IOC_BRINGUP){
+ if (ioc->upload_fw) {
+ dprintk((MYIOC_s_INFO_FMT
+ "firmware upload required!\n", ioc->name));
+
+ r = mpt_do_upload(ioc, sleepFlag);
+ if (r != 0)
+ printk(KERN_WARNING MYNAM ": firmware upload failure!\n");
+ /* Handle the alt IOC too */
+ if ((alt_ioc_ready) && (ioc->alt_ioc->upload_fw)){
+ r = mpt_do_upload(ioc->alt_ioc, sleepFlag);
+ if (r != 0)
+ printk(KERN_WARNING MYNAM ": firmware upload failure!\n");
+ }
}
-#endif
}
+
/* Enable! (reply interrupt) */
CHIPREG_WRITE32(&ioc->chip->IntMask, ~(MPI_HIM_RIM));
ioc->active = 1;
-// NEW!
-#if 0 // Kiss-of-death!?!
- if (alt_ioc_ready && (r==0)) {
+ if (ioc->alt_ioc) {
/* (re)Enable alt-IOC! (reply interrupt) */
dprintk((KERN_INFO MYNAM ": alt-%s reply irq re-enabled\n",
ioc->alt_ioc->name));
CHIPREG_WRITE32(&ioc->alt_ioc->chip->IntMask, ~(MPI_HIM_RIM));
ioc->alt_ioc->active = 1;
}
-#endif
/* NEW! 20010120 -sralston
* Enable MPT base driver management of EventNotification
*/
if (!ioc->facts.EventState)
(void) SendEventNotification(ioc, 1); /* 1=Enable EventNotification */
-// NEW!
-// FIXME!?!
-// if (ioc->alt_ioc && alt_ioc_ready && !ioc->alt_ioc->facts.EventState) {
-// (void) SendEventNotification(ioc->alt_ioc, 1); /* 1=Enable EventNotification */
-// }
- return 0;
-}
+ if (ioc->alt_ioc && alt_ioc_ready && !ioc->alt_ioc->facts.EventState)
+ (void) SendEventNotification(ioc->alt_ioc, 1); /* 1=Enable EventNotification */
-/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
-/*
- * mpt_detect_929_bound_ports - Search for PCI bus/dev_function
- * which matches PCI bus/dev_function (+/-1) for newly discovered 929.
- * @ioc: Pointer to MPT adapter structure
- * @pdev: Pointer to (struct pci_dev) structure
- *
- * If match on PCI dev_function +/-1 is found, bind the two MPT adapters
- * using alt_ioc pointer fields in their %MPT_ADAPTER structures.
- */
-static void
-mpt_detect_929_bound_ports(MPT_ADAPTER *ioc, struct pci_dev *pdev)
-{
- MPT_ADAPTER *ioc_srch = mpt_adapter_find_first();
- unsigned int match_lo, match_hi;
+ /* (Bugzilla:fibrebugs, #513)
+ * Bug fix (part 2)! 20010905 -sralston
+ * Add additional "reason" check before call to GetLanConfigPages
+ * (combined with GetIoUnitPage2 call). This prevents a somewhat
+ * recursive scenario; GetLanConfigPages times out, timer expired
+ * routine calls HardResetHandler, which calls into here again,
+ * and we try GetLanConfigPages again...
+ */
+ if (reason == MPT_HOSTEVENT_IOC_BRINGUP) {
+ if ((int)ioc->chip_type <= (int)FC929) {
+ /*
+ * Pre-fetch FC port WWN and stuff...
+ * (FCPortPage0_t stuff)
+ */
+ for (ii=0; ii < ioc->facts.NumberOfPorts; ii++) {
+ (void) GetFcPortPage0(ioc, ii);
+ }
- match_lo = pdev->devfn-1;
- match_hi = pdev->devfn+1;
- dprintk((KERN_INFO MYNAM ": %s: PCI bus/devfn=%x/%x, searching for devfn match on %x or %x\n",
- ioc->name, pdev->bus->number, pdev->devfn, match_lo, match_hi));
+ if ((ioc->pfacts[0].ProtocolFlags & MPI_PORTFACTS_PROTOCOL_LAN) &&
+ (ioc->lan_cnfg_page0.Header.PageLength == 0)) {
+ /*
+ * Pre-fetch the ports LAN MAC address!
+ * (LANPage1_t stuff)
+ */
+ (void) GetLanConfigPages(ioc);
+#ifdef MPT_DEBUG
+ {
+ u8 *a = (u8*)&ioc->lan_cnfg_page1.HardwareAddressLow;
+ dprintk((MYIOC_s_INFO_FMT "LanAddr = %02X:%02X:%02X:%02X:%02X:%02X\n",
+ ioc->name, a[5], a[4], a[3], a[2], a[1], a[0] ));
+ }
+#endif
+ }
+ } else {
+ /* Get NVRAM and adapter maximums from SPP 0 and 2
+ */
+ mpt_GetScsiPortSettings(ioc, 0);
- while (ioc_srch != NULL) {
- struct pci_dev *_pcidev = ioc_srch->pcidev;
+ /* Get version and length of SDP 1
+ */
+ mpt_readScsiDevicePageHeaders(ioc, 0);
- if ( (_pcidev->device == MPI_MANUFACTPAGE_DEVICEID_FC929) &&
- (_pcidev->bus->number == pdev->bus->number) &&
- (_pcidev->devfn == match_lo || _pcidev->devfn == match_hi) ) {
- /* Paranoia checks */
- if (ioc->alt_ioc != NULL) {
- printk(KERN_WARNING MYNAM ": Oops, already bound (%s <==> %s)!\n",
- ioc->name, ioc->alt_ioc->name);
+ /* Find IM volumes
+ */
+ if (ioc->facts.MsgVersion >= 0x0102)
+ mpt_findImVolumes(ioc);
+ }
+
+ GetIoUnitPage2(ioc);
+ }
+
+ /*
+ * Call each currently registered protocol IOC reset handler
+ * with post-reset indication.
+ * NOTE: If we're doing _IOC_BRINGUP, there can be no
+ * MptResetHandlers[] registered yet.
+ */
+ if (hard_reset_done) {
+ r = handlers = 0;
+ for (ii=MPT_MAX_PROTOCOL_DRIVERS-1; ii; ii--) {
+ if (MptResetHandlers[ii]) {
+ dprintk((MYIOC_s_INFO_FMT "Calling IOC post_reset handler #%d\n",
+ ioc->name, ii));
+ r += (*(MptResetHandlers[ii]))(ioc, MPT_IOC_POST_RESET);
+ handlers++;
+
+ if (alt_ioc_ready) {
+ dprintk((MYIOC_s_INFO_FMT "Calling alt-%s post_reset handler #%d\n",
+ ioc->name, ioc->alt_ioc->name, ii));
+ r += (*(MptResetHandlers[ii]))(ioc->alt_ioc, MPT_IOC_POST_RESET);
+ handlers++;
+ }
+ }
+ }
+ /* FIXME? Examine results here? */
+ }
+
+ return 0;
+}
+
+/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
+/*
+ * mpt_detect_bound_ports - Search for PCI bus/dev_function
+ * which matches PCI bus/dev_function (+/-1) for newly discovered 929,
+ * 929X or 1030.
+ * @ioc: Pointer to MPT adapter structure
+ * @pdev: Pointer to (struct pci_dev) structure
+ *
+ * If match on PCI dev_function +/-1 is found, bind the two MPT adapters
+ * using alt_ioc pointer fields in their %MPT_ADAPTER structures.
+ */
+static void
+mpt_detect_bound_ports(MPT_ADAPTER *ioc, struct pci_dev *pdev)
+{
+ MPT_ADAPTER *ioc_srch = mpt_adapter_find_first();
+ unsigned int match_lo, match_hi;
+
+ match_lo = pdev->devfn-1;
+ match_hi = pdev->devfn+1;
+ dprintk((MYIOC_s_INFO_FMT "PCI bus/devfn=%x/%x, searching for devfn match on %x or %x\n",
+ ioc->name, pdev->bus->number, pdev->devfn, match_lo, match_hi));
+
+ while (ioc_srch != NULL) {
+ struct pci_dev *_pcidev = ioc_srch->pcidev;
+
+ if ((_pcidev->device == pdev->device) &&
+ (_pcidev->bus->number == pdev->bus->number) &&
+ (_pcidev->devfn == match_lo || _pcidev->devfn == match_hi) ) {
+ /* Paranoia checks */
+ if (ioc->alt_ioc != NULL) {
+ printk(KERN_WARNING MYNAM ": Oops, already bound (%s <==> %s)!\n",
+ ioc->name, ioc->alt_ioc->name);
break;
} else if (ioc_srch->alt_ioc != NULL) {
printk(KERN_WARNING MYNAM ": Oops, already bound (%s <==> %s)!\n",
ioc->name, ioc_srch->name));
ioc_srch->alt_ioc = ioc;
ioc->alt_ioc = ioc_srch;
- ioc->sod_reset = ioc->alt_ioc->sod_reset;
- ioc->last_kickstart = ioc->alt_ioc->last_kickstart;
break;
}
ioc_srch = mpt_adapter_find_next(ioc_srch);
u32 state;
/* Disable the FW */
- state = GetIocState(this, 1);
+ state = mpt_GetIocState(this, 1);
if (state == MPI_IOC_STATE_OPERATIONAL) {
- if (SendIocReset(this, MPI_FUNCTION_IOC_MESSAGE_UNIT_RESET) != 0)
- (void) KickStart(this, 1);
+ if (SendIocReset(this, MPI_FUNCTION_IOC_MESSAGE_UNIT_RESET, NO_SLEEP) != 0)
+ (void) KickStart(this, 1, NO_SLEEP);
}
/* Disable adapter interrupts! */
}
if (freeup && this->sense_buf_pool != NULL) {
- sz = (this->req_depth * 256);
+ sz = (this->req_depth * MPT_SENSE_BUFFER_ALLOC);
pci_free_consistent(this->pcidev, sz,
this->sense_buf_pool, this->sense_buf_pool_dma);
this->sense_buf_pool = NULL;
this->alloc_total -= sz;
}
+
+ if (freeup && this->events != NULL){
+ sz = MPTCTL_EVENT_LOG_SIZE * sizeof(MPT_IOCTL_EVENTS);
+ kfree(this->events);
+ this->events = NULL;
+ this->alloc_total -= sz;
+ }
+
+ if (freeup && this->cached_fw != NULL) {
+ int ii = 0;
+
+ while ((ii < this->num_fw_frags) && (this->cached_fw[ii]!= NULL)) {
+ sz = this->cached_fw[ii]->size;
+ pci_free_consistent(this->pcidev, sz,
+ this->cached_fw[ii]->fw, this->cached_fw[ii]->fw_dma);
+ this->cached_fw[ii]->fw = NULL;
+ this->alloc_total -= sz;
+
+ kfree(this->cached_fw[ii]);
+ this->cached_fw[ii] = NULL;
+ this->alloc_total -= sizeof(fw_image_t);
+
+ ii++;
+ }
+
+ kfree(this->cached_fw);
+ this->cached_fw = NULL;
+ sz = this->num_fw_frags * sizeof(void *);
+ this->alloc_total -= sz;
+ }
+
+ if (freeup && this->spi_data.nvram != NULL) {
+ kfree(this->spi_data.nvram);
+ this->spi_data.nvram = NULL;
+ }
+
+ if (freeup && this->spi_data.pIocPg3 != NULL) {
+ kfree(this->spi_data.pIocPg3);
+ this->spi_data.pIocPg3 = NULL;
+ }
}
}
/*
* MakeIocReady - Get IOC to a READY state, using KickStart if needed.
* @ioc: Pointer to MPT_ADAPTER structure
- * @kick: Force hard KickStart of IOC
+ * @force: Force hard KickStart of IOC
+ * @sleepFlag: Specifies whether the process can sleep
*
- * Returns 0 for already-READY, 1 for hard reset success,
- * else negative for failure.
+ * Returns:
+ * 1 - DIAG reset and READY
+ * 0 - READY initially OR soft reset and READY
+ * -1 - Any failure on KickStart
+ * -2 - Msg Unit Reset Failed
+ * -3 - IO Unit Reset Failed
+ * -4 - IOC owned by a PEER
*/
static int
-MakeIocReady(MPT_ADAPTER *ioc, int force)
+MakeIocReady(MPT_ADAPTER *ioc, int force, int sleepFlag)
{
u32 ioc_state;
int statefault = 0;
- int cntdn;
+ int cntdn;
int hard_reset_done = 0;
int r;
- int i;
+ int ii;
+ int whoinit;
/* Get current [raw] IOC state */
- ioc_state = GetIocState(ioc, 0);
+ ioc_state = mpt_GetIocState(ioc, 0);
dhsprintk((KERN_INFO MYNAM "::MakeIocReady, %s [raw] state=%08x\n", ioc->name, ioc_state));
/*
*/
if (ioc_state & MPI_DOORBELL_ACTIVE) {
statefault = 1;
- printk(KERN_WARNING MYNAM ": %s: Uh-oh, unexpected doorbell active!\n",
+ printk(MYIOC_s_WARN_FMT "Unexpected doorbell active!\n",
ioc->name);
}
*/
if ((ioc_state & MPI_IOC_STATE_MASK) == MPI_IOC_STATE_FAULT) {
statefault = 2;
- printk(KERN_WARNING MYNAM ": %s: Uh-oh, IOC is in FAULT state!!!\n",
+ printk(MYIOC_s_WARN_FMT "IOC is in FAULT state!!!\n",
ioc->name);
printk(KERN_WARNING " FAULT code = %04xh\n",
ioc_state & MPI_DOORBELL_DATA_MASK);
* Hmmm... Did it get left operational?
*/
if ((ioc_state & MPI_IOC_STATE_MASK) == MPI_IOC_STATE_OPERATIONAL) {
- statefault = 3;
- dprintk((KERN_WARNING MYNAM ": %s: Hmmm... IOC operational unexpected\n",
+ dprintk((MYIOC_s_WARN_FMT "IOC operational unexpected\n",
ioc->name));
+
+ /* Check WhoInit.
+ * If PCI Peer, exit.
+ * Else, if no fault conditions are present, issue a MessageUnitReset
+ * Else, fall through to KickStart case
+ */
+ whoinit = (ioc_state & MPI_DOORBELL_WHO_INIT_MASK) >> MPI_DOORBELL_WHO_INIT_SHIFT;
+ dprintk((KERN_WARNING MYNAM
+ ": whoinit 0x%x\n statefault %d force %d\n",
+ whoinit, statefault, force));
+ if (whoinit == MPI_WHOINIT_PCI_PEER)
+ return -4;
+ else {
+ if ((statefault == 0 ) && (force == 0)) {
+ if ((r = SendIocReset(ioc, MPI_FUNCTION_IOC_MESSAGE_UNIT_RESET, sleepFlag)) == 0)
+ return 0;
+ }
+ statefault = 3;
+ }
}
- hard_reset_done = KickStart(ioc, statefault||force);
+ hard_reset_done = KickStart(ioc, statefault||force, sleepFlag);
if (hard_reset_done < 0)
return -1;
/*
* Loop here waiting for IOC to come READY.
*/
- i = 0;
+ ii = 0;
cntdn = HZ * 15;
- while ((ioc_state = GetIocState(ioc, 1)) != MPI_IOC_STATE_READY) {
+ if (sleepFlag != CAN_SLEEP)
+ cntdn *= 10; /* 1500 iterations @ 1msec per */
+
+ while ((ioc_state = mpt_GetIocState(ioc, 1)) != MPI_IOC_STATE_READY) {
if (ioc_state == MPI_IOC_STATE_OPERATIONAL) {
/*
* BIOS or previous driver load left IOC in OP state.
* Reset messaging FIFOs.
*/
- if ((r = SendIocReset(ioc, MPI_FUNCTION_IOC_MESSAGE_UNIT_RESET)) != 0) {
- printk(KERN_ERR MYNAM ": %s: ERROR - IOC msg unit reset failed!\n", ioc->name);
+ if ((r = SendIocReset(ioc, MPI_FUNCTION_IOC_MESSAGE_UNIT_RESET, sleepFlag)) != 0) {
+ printk(MYIOC_s_ERR_FMT "IOC msg unit reset failed!\n", ioc->name);
return -2;
}
} else if (ioc_state == MPI_IOC_STATE_RESET) {
* Something is wrong. Try to get IOC back
* to a known state.
*/
- if ((r = SendIocReset(ioc, MPI_FUNCTION_IO_UNIT_RESET)) != 0) {
- printk(KERN_ERR MYNAM ": %s: ERROR - IO unit reset failed!\n", ioc->name);
+ if ((r = SendIocReset(ioc, MPI_FUNCTION_IO_UNIT_RESET, sleepFlag)) != 0) {
+ printk(MYIOC_s_ERR_FMT "IO unit reset failed!\n", ioc->name);
return -3;
}
}
- i++; cntdn--;
+ ii++; cntdn--;
if (!cntdn) {
- printk(KERN_ERR MYNAM ": %s: ERROR - Wait IOC_READY state timeout(%d)!\n",
- ioc->name, (i+5)/HZ);
+ printk(MYIOC_s_ERR_FMT "Wait IOC_READY state timeout(%d)!\n",
+ ioc->name, (ii+5)/HZ);
return -ETIME;
}
- current->state = TASK_INTERRUPTIBLE;
- schedule_timeout(1);
+ if (sleepFlag == CAN_SLEEP) {
+ set_current_state(TASK_INTERRUPTIBLE);
+ schedule_timeout(1);
+ } else {
+ mdelay (1); /* 1 msec delay */
+ }
+
}
if (statefault < 3) {
- printk(KERN_WARNING MYNAM ": %s: Whew! Recovered from %s\n",
+ printk(MYIOC_s_INFO_FMT "Recovered from %s\n",
ioc->name,
statefault==1 ? "stuck handshake" : "IOC FAULT");
}
/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
/*
- * GetIocState - Get the current state of a MPT adapter.
+ * mpt_GetIocState - Get the current state of a MPT adapter.
* @ioc: Pointer to MPT_ADAPTER structure
* @cooked: Request raw or cooked IOC state
*
* Returns all IOC Doorbell register bits if cooked==0, else just the
* Doorbell bits in MPI_IOC_STATE_MASK.
*/
-static u32
-GetIocState(MPT_ADAPTER *ioc, int cooked)
+u32
+mpt_GetIocState(MPT_ADAPTER *ioc, int cooked)
{
u32 s, sc;
/* Get! */
s = CHIPREG_READ32(&ioc->chip->Doorbell);
- dprintk((KERN_INFO MYNAM ": %s: raw state = %08x\n", ioc->name, s));
+// dprintk((MYIOC_s_INFO_FMT "raw state = %08x\n", ioc->name, s));
sc = s & MPI_IOC_STATE_MASK;
/* Save! */
/*
* GetIocFacts - Send IOCFacts request to MPT adapter.
* @ioc: Pointer to MPT_ADAPTER structure
+ * @sleepFlag: Specifies whether the process can sleep
+ * @reason: If recovery, only update facts.
*
* Returns 0 for success, non-zero for failure.
*/
static int
-GetIocFacts(MPT_ADAPTER *ioc)
+GetIocFacts(MPT_ADAPTER *ioc, int sleepFlag, int reason)
{
IOCFacts_t get_facts;
IOCFactsReply_t *facts;
get_facts.Function = MPI_FUNCTION_IOC_FACTS;
/* Assert: All other get_facts fields are zero! */
- dprintk((KERN_INFO MYNAM ": %s: Sending get IocFacts request\n", ioc->name));
+ dprintk((MYIOC_s_INFO_FMT "Sending get IocFacts request\n", ioc->name));
/* No non-zero fields in the get_facts request are greater than
* 1 byte in size, so we can just fire it off as is.
*/
- r = HandShakeReqAndReply(ioc,
- req_sz, (u32*)&get_facts,
- reply_sz, (u16*)facts, 3);
+ r = mpt_handshake_req_reply_wait(ioc, req_sz, (u32*)&get_facts,
+ reply_sz, (u16*)facts, 3 /*seconds*/, sleepFlag);
if (r != 0)
return r;
*/
/* Did we get a valid reply? */
if (facts->MsgLength > offsetof(IOCFactsReply_t, RequestFrameSize)/sizeof(u32)) {
- /*
- * If not been here, done that, save off first WhoInit value
- */
- if (ioc->FirstWhoInit == WHOINIT_UNKNOWN)
- ioc->FirstWhoInit = facts->WhoInit;
+ if (reason == MPT_HOSTEVENT_IOC_BRINGUP) {
+ /*
+ * If not been here, done that, save off first WhoInit value
+ */
+ if (ioc->FirstWhoInit == WHOINIT_UNKNOWN)
+ ioc->FirstWhoInit = facts->WhoInit;
+ }
facts->MsgVersion = le16_to_cpu(facts->MsgVersion);
facts->MsgContext = le32_to_cpu(facts->MsgContext);
+ facts->IOCExceptions = le16_to_cpu(facts->IOCExceptions);
facts->IOCStatus = le16_to_cpu(facts->IOCStatus);
facts->IOCLogInfo = le32_to_cpu(facts->IOCLogInfo);
status = facts->IOCStatus & MPI_IOCSTATUS_MASK;
facts->ReplyQueueDepth = le16_to_cpu(facts->ReplyQueueDepth);
facts->RequestFrameSize = le16_to_cpu(facts->RequestFrameSize);
- facts->FWVersion = le16_to_cpu(facts->FWVersion);
+
+ /*
+ * FC f/w version changed between 1.1 and 1.2
+ * Old: u16{Major(4),Minor(4),SubMinor(8)}
+ * New: u32{Major(8),Minor(8),Unit(8),Dev(8)}
+ */
+ if (facts->MsgVersion < 0x0102) {
+ /*
+ * Handle old FC f/w style, convert to new...
+ */
+ u16 oldv = le16_to_cpu(facts->Reserved_0101_FWVersion);
+ facts->FWVersion.Word =
+ ((oldv<<12) & 0xFF000000) |
+ ((oldv<<8) & 0x000FFF00);
+ } else
+ facts->FWVersion.Word = le32_to_cpu(facts->FWVersion.Word);
+
facts->ProductID = le16_to_cpu(facts->ProductID);
facts->CurrentHostMfaHighAddr =
le32_to_cpu(facts->CurrentHostMfaHighAddr);
* Older MPI-1.00.xx struct had 13 dwords, and enlarged
* to 14 in MPI-1.01.0x.
*/
- if (facts->MsgLength >= sizeof(IOCFactsReply_t)/sizeof(u32) && facts->MsgVersion > 0x0100) {
+ if (facts->MsgLength >= (offsetof(IOCFactsReply_t,FWImageSize) + 7)/4 &&
+ facts->MsgVersion > 0x0100) {
facts->FWImageSize = le32_to_cpu(facts->FWImageSize);
- facts->DataImageSize = le32_to_cpu(facts->DataImageSize);
}
- if (facts->RequestFrameSize) {
- /*
- * Set values for this IOC's REQUEST queue size & depth...
- */
- ioc->req_sz = MIN(MPT_REQ_SIZE, facts->RequestFrameSize * 4);
-
- /*
- * Set values for this IOC's REPLY queue size & depth...
- *
- * BUG? FIX? 20000516 -nromer & sralston
- * GRRR... The following did not translate well from MPI v0.09:
- * ioc->reply_sz = MIN(MPT_REPLY_SIZE, facts->ReplySize * 4);
- * to 0.10:
- * ioc->reply_sz = MIN(MPT_REPLY_SIZE, facts->BlockSize * 4);
- * Was trying to minimally optimize to smallest possible reply size
- * (and greatly reduce kmalloc size). But LAN may need larger reply?
- *
- * So for now, just set reply size to request size. FIXME?
- */
- ioc->reply_sz = ioc->req_sz;
- } else {
+ if (!facts->RequestFrameSize) {
/* Something is wrong! */
- printk(KERN_ERR MYNAM ": %s: ERROR - IOC reported invalid 0 request size!\n",
+ printk(MYIOC_s_ERR_FMT "IOC reported invalid 0 request size!\n",
ioc->name);
- ioc->req_sz = MPT_REQ_SIZE;
- ioc->reply_sz = MPT_REPLY_SIZE;
return -55;
}
- ioc->req_depth = MIN(MPT_REQ_DEPTH, facts->GlobalCredits);
- ioc->reply_depth = MIN(MPT_REPLY_DEPTH, facts->ReplyQueueDepth);
- dprintk((KERN_INFO MYNAM ": %s: reply_sz=%3d, reply_depth=%4d\n",
+ if (reason == MPT_HOSTEVENT_IOC_BRINGUP) {
+ /*
+ * Set values for this IOC's request & reply frame sizes,
+ * and request & reply queue depths...
+ */
+ ioc->req_sz = MIN(MPT_DEFAULT_FRAME_SIZE, facts->RequestFrameSize * 4);
+ ioc->req_depth = MIN(MPT_MAX_REQ_DEPTH, facts->GlobalCredits);
+ ioc->reply_sz = ioc->req_sz;
+ ioc->reply_depth = MIN(MPT_DEFAULT_REPLY_DEPTH, facts->ReplyQueueDepth);
+
+ /* 1030 - should we use a smaller DEFAULT_REPLY_DEPTH?
+ * FIX
+ */
+ dprintk((MYIOC_s_INFO_FMT "reply_sz=%3d, reply_depth=%4d\n",
ioc->name, ioc->reply_sz, ioc->reply_depth));
- dprintk((KERN_INFO MYNAM ": %s: req_sz =%3d, req_depth =%4d\n",
+ dprintk((MYIOC_s_INFO_FMT "req_sz =%3d, req_depth =%4d\n",
ioc->name, ioc->req_sz, ioc->req_depth));
- /* Get port facts! */
- if ( (r = GetPortFacts(ioc, 0)) != 0 )
- return r;
+ /* Get port facts! */
+ if ( (r = GetPortFacts(ioc, 0, sleepFlag)) != 0 )
+ return r;
+ }
} else {
- printk(KERN_ERR MYNAM ": %s: ERROR - Invalid IOC facts reply!\n",
+ printk(MYIOC_s_ERR_FMT "Invalid IOC facts reply!\n",
ioc->name);
return -66;
}
* GetPortFacts - Send PortFacts request to MPT adapter.
* @ioc: Pointer to MPT_ADAPTER structure
* @portnum: Port number
+ * @sleepFlag: Specifies whether the process can sleep
*
* Returns 0 for success, non-zero for failure.
*/
static int
-GetPortFacts(MPT_ADAPTER *ioc, int portnum)
+GetPortFacts(MPT_ADAPTER *ioc, int portnum, int sleepFlag)
{
PortFacts_t get_pfacts;
PortFactsReply_t *pfacts;
- int i;
+ int ii;
int req_sz;
int reply_sz;
get_pfacts.PortNumber = portnum;
/* Assert: All other get_pfacts fields are zero! */
- dprintk((KERN_INFO MYNAM ": %s: Sending get PortFacts(%d) request\n",
+ dprintk((MYIOC_s_INFO_FMT "Sending get PortFacts(%d) request\n",
ioc->name, portnum));
/* No non-zero fields in the get_pfacts request are greater than
* 1 byte in size, so we can just fire it off as is.
*/
- i = HandShakeReqAndReply(ioc, req_sz, (u32*)&get_pfacts,
- reply_sz, (u16*)pfacts, 3);
- if (i != 0)
- return i;
+ ii = mpt_handshake_req_reply_wait(ioc, req_sz, (u32*)&get_pfacts,
+ reply_sz, (u16*)pfacts, 3 /*seconds*/, sleepFlag);
+ if (ii != 0)
+ return ii;
/* Did we get a valid reply? */
/*
* SendIocInit - Send IOCInit request to MPT adapter.
* @ioc: Pointer to MPT_ADAPTER structure
+ * @sleepFlag: Specifies whether the process can sleep
*
* Send IOCInit followed by PortEnable to bring IOC to OPERATIONAL state.
*
* Returns 0 for success, non-zero for failure.
*/
static int
-SendIocInit(MPT_ADAPTER *ioc)
+SendIocInit(MPT_ADAPTER *ioc, int sleepFlag)
{
IOCInit_t ioc_init;
MPIDefaultReply_t init_reply;
ioc_init.Function = MPI_FUNCTION_IOC_INIT;
/* ioc_init.Flags = 0; */
- /*ioc_init.MaxDevices = 16;*/
- ioc_init.MaxDevices = 255;
-/* ioc_init.MaxBuses = 16; */
- ioc_init.MaxBuses = 1;
+ /* If we are in a recovery mode and we uploaded the FW image,
+ * then this pointer is not NULL. Skip the upload a second time.
+ * Set this flag if cached_fw set for either IOC.
+ */
+ ioc->upload_fw = 0;
+ ioc_init.Flags = 0;
+ if (ioc->facts.Flags & MPI_IOCFACTS_FLAGS_FW_DOWNLOAD_BOOT) {
+ if ((ioc->cached_fw) || (ioc->alt_ioc && ioc->alt_ioc->cached_fw))
+ ioc_init.Flags = MPI_IOCINIT_FLAGS_DISCARD_FW_IMAGE;
+ else
+ ioc->upload_fw = 1;
+ }
+
+ if ((int)ioc->chip_type <= (int)FC929) {
+ ioc_init.MaxDevices = MPT_MAX_FC_DEVICES;
+ } else {
+ ioc_init.MaxDevices = MPT_MAX_SCSI_DEVICES;
+ }
+ ioc_init.MaxBuses = MPT_MAX_BUS;
/* ioc_init.MsgFlags = 0; */
/* ioc_init.MsgContext = cpu_to_le32(0x00000000); */
ioc_init.ReplyFrameSize = cpu_to_le16(ioc->reply_sz); /* in BYTES */
- ioc_init.HostMfaHighAddr = cpu_to_le32(0); /* Say we 32-bit! for now */
- dprintk((KERN_INFO MYNAM ": %s: Sending IOCInit (req @ %p)\n", ioc->name, &ioc_init));
+ if (sizeof(dma_addr_t) == sizeof(u64)) {
+ /* Save the upper 32-bits of the request
+ * (reply) and sense buffers.
+ */
+ ioc_init.HostMfaHighAddr = cpu_to_le32((u32)((u64)ioc->req_frames_dma >> 32));
+ ioc_init.SenseBufferHighAddr = cpu_to_le32((u32)((u64)ioc->sense_buf_pool_dma >> 32));
+ } else {
+ /* Force 32-bit addressing */
+ ioc_init.HostMfaHighAddr = cpu_to_le32(0);
+ ioc_init.SenseBufferHighAddr = cpu_to_le32(0);
+ }
+
+ dprintk((MYIOC_s_INFO_FMT "Sending IOCInit (req @ %p)\n",
+ ioc->name, &ioc_init));
- r = HandShakeReqAndReply(ioc, sizeof(IOCInit_t), (u32*)&ioc_init,
- sizeof(MPIDefaultReply_t), (u16*)&init_reply, 10);
+ r = mpt_handshake_req_reply_wait(ioc, sizeof(IOCInit_t), (u32*)&ioc_init,
+ sizeof(MPIDefaultReply_t), (u16*)&init_reply, 10 /*seconds*/, sleepFlag);
if (r != 0)
return r;
* since we don't even look at it's contents.
*/
- if ((r = SendPortEnable(ioc, 0)) != 0)
+ if ((r = SendPortEnable(ioc, 0, sleepFlag)) != 0)
return r;
/* YIKES! SUPER IMPORTANT!!!
*/
count = 0;
cntdn = HZ * 60; /* chg'd from 30 to 60 seconds */
- state = GetIocState(ioc, 1);
+ if (sleepFlag != CAN_SLEEP)
+ cntdn *= 10; /* scale for 1msec delays */
+ state = mpt_GetIocState(ioc, 1);
while (state != MPI_IOC_STATE_OPERATIONAL && --cntdn) {
- current->state = TASK_INTERRUPTIBLE;
- schedule_timeout(1);
+ if (sleepFlag == CAN_SLEEP) {
+ set_current_state(TASK_INTERRUPTIBLE);
+ schedule_timeout(1);
+ } else {
+ mdelay(1);
+ }
if (!cntdn) {
- printk(KERN_ERR MYNAM ": %s: ERROR - Wait IOC_OP state timeout(%d)!\n",
+ printk(MYIOC_s_ERR_FMT "Wait IOC_OP state timeout(%d)!\n",
ioc->name, (count+5)/HZ);
return -9;
}
- state = GetIocState(ioc, 1);
+ state = mpt_GetIocState(ioc, 1);
count++;
}
- dhsprintk((KERN_INFO MYNAM ": %s: INFO - Wait IOC_OPERATIONAL state (cnt=%d)\n",
+ dhsprintk((MYIOC_s_INFO_FMT "INFO - Wait IOC_OPERATIONAL state (cnt=%d)\n",
ioc->name, count));
return r;
* SendPortEnable - Send PortEnable request to MPT adapter port.
* @ioc: Pointer to MPT_ADAPTER structure
* @portnum: Port number to enable
+ * @sleepFlag: Specifies whether the process can sleep
*
* Send PortEnable to bring IOC to OPERATIONAL state.
*
* Returns 0 for success, non-zero for failure.
*/
static int
-SendPortEnable(MPT_ADAPTER *ioc, int portnum)
+SendPortEnable(MPT_ADAPTER *ioc, int portnum, int sleepFlag)
{
PortEnable_t port_enable;
MPIDefaultReply_t reply_buf;
- int i;
+ int ii;
int req_sz;
int reply_sz;
/* port_enable.MsgFlags = 0; */
/* port_enable.MsgContext = 0; */
- dprintk((KERN_INFO MYNAM ": %s: Sending Port(%d)Enable (req @ %p)\n",
+ dprintk((MYIOC_s_INFO_FMT "Sending Port(%d)Enable (req @ %p)\n",
ioc->name, portnum, &port_enable));
- i = HandShakeReqAndReply(ioc, req_sz, (u32*)&port_enable,
- reply_sz, (u16*)&reply_buf, 65);
- if (i != 0)
- return i;
+ /* RAID FW may take a long time to enable
+ */
+ if ((int)ioc->chip_type <= (int)FC929) {
+ ii = mpt_handshake_req_reply_wait(ioc, req_sz, (u32*)&port_enable,
+ reply_sz, (u16*)&reply_buf, 65 /*seconds*/, sleepFlag);
+ } else {
+ ii = mpt_handshake_req_reply_wait(ioc, req_sz, (u32*)&port_enable,
+ reply_sz, (u16*)&reply_buf, 300 /*seconds*/, sleepFlag);
+ }
+
+ if (ii != 0)
+ return ii;
/* We do not even look at the reply, so we need not
* swap the multi-byte fields.
return 0;
}
+/*
+ * Inputs: size - total FW bytes
+ * Outputs: frags - number of fragments needed
+ * Return NULL if failed.
+ */
+void *
+mpt_alloc_fw_memory(MPT_ADAPTER *ioc, int size, int *frags, int *alloc_sz)
+{
+ fw_image_t **cached_fw = NULL;
+ u8 *mem = NULL;
+ dma_addr_t fw_dma;
+ int alloc_total = 0;
+ int bytes_left, bytes, num_frags;
+ int sz, ii;
+
+ /* cached_fw
+ */
+ sz = ioc->num_fw_frags * sizeof(void *);
+ mem = kmalloc(sz, GFP_ATOMIC);
+ if (mem == NULL)
+ return NULL;
+
+ memset(mem, 0, sz);
+ cached_fw = (fw_image_t **)mem;
+ alloc_total += sz;
+
+ /* malloc fragment memory
+ * fw_image_t struct and dma for fw data
+ */
+ bytes_left = size;
+ ii = 0;
+ num_frags = 0;
+ bytes = bytes_left;
+ while((bytes_left) && (num_frags < ioc->num_fw_frags)) {
+ if (cached_fw[ii] == NULL) {
+ mem = kmalloc(sizeof(fw_image_t), GFP_ATOMIC);
+ if (mem == NULL)
+ break;
+
+ memset(mem, 0, sizeof(fw_image_t));
+ cached_fw[ii] = (fw_image_t *)mem;
+ alloc_total += sizeof(fw_image_t);
+ }
+
+ mem = pci_alloc_consistent(ioc->pcidev, bytes, &fw_dma);
+ if (mem == NULL) {
+ if (bytes > 0x10000)
+ bytes = 0x10000;
+ else if (bytes > 0x8000)
+ bytes = 0x8000;
+ else if (bytes > 0x4000)
+ bytes = 0x4000;
+ else if (bytes > 0x2000)
+ bytes = 0x2000;
+ else if (bytes > 0x1000)
+ bytes = 0x1000;
+ else
+ break;
+
+ continue;
+ }
+
+ cached_fw[ii]->fw = mem;
+ cached_fw[ii]->fw_dma = fw_dma;
+ cached_fw[ii]->size = bytes;
+ memset(mem, 0, bytes);
+ alloc_total += bytes;
+
+ bytes_left -= bytes;
+
+ num_frags++;
+ ii++;
+ }
+
+ if (bytes_left ) {
+ /* Major Failure.
+ */
+ mpt_free_fw_memory(ioc, cached_fw);
+ return NULL;
+ }
+
+ *frags = num_frags;
+ *alloc_sz = alloc_total;
+
+ return (void *) cached_fw;
+}
+
+/*
+ * If alt_img is NULL, delete from ioc structure.
+ * Else, delete a secondary image in same format.
+ */
+void
+mpt_free_fw_memory(MPT_ADAPTER *ioc, fw_image_t **alt_img)
+{
+ fw_image_t **cached_fw;
+ int ii;
+ int sz;
+ int alloc_freed = 0;
+
+ if (alt_img != NULL)
+ cached_fw = alt_img;
+ else
+ cached_fw = ioc->cached_fw;
+
+ if (cached_fw == NULL)
+ return;
+
+ ii = 0;
+ while ((ii < ioc->num_fw_frags) && (cached_fw[ii]!= NULL)) {
+ sz = cached_fw[ii]->size;
+ if (sz > 0) {
+ pci_free_consistent(ioc->pcidev, sz,
+ cached_fw[ii]->fw, cached_fw[ii]->fw_dma);
+ }
+ cached_fw[ii]->fw = NULL;
+ alloc_freed += sz;
+
+ kfree(cached_fw[ii]);
+ cached_fw[ii] = NULL;
+ alloc_freed += sizeof(fw_image_t);
+
+ ii++;
+ }
+
+ kfree(cached_fw);
+ cached_fw = NULL;
+ sz = ioc->num_fw_frags * sizeof(void *);
+ alloc_freed += sz;
+
+ if (alt_img == NULL)
+ ioc->alloc_total -= alloc_freed;
+
+ return;
+}
+
+
/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
/*
- * KickStart - Perform hard reset of MPT adapter.
+ * mpt_do_upload - Construct and Send FWUpload request to MPT adapter port.
* @ioc: Pointer to MPT_ADAPTER structure
- * @force: Force hard reset
+ * @sleepFlag: Specifies whether the process can sleep
*
- * This routine places MPT adapter in diagnostic mode via the
- * WriteSequence register, and then performs a hard reset of adapter
- * via the Diagnostic register.
+ * Returns 0 for success, >0 for handshake failure
+ * <0 for fw upload failure.
*
- * Returns 0 for soft reset success, 1 for hard reset success,
- * else a negative value for failure.
+ * Remark: If bound IOC and a successful FWUpload was performed
+ * on the bound IOC, the second image is discarded
+ * and memory is free'd. Both channels must upload to prevent
+ * IOC from running in degraded mode.
*/
static int
-KickStart(MPT_ADAPTER *ioc, int force)
+mpt_do_upload(MPT_ADAPTER *ioc, int sleepFlag)
{
- int hard_reset_done = 0;
- u32 ioc_state;
- int cnt = 0;
+ u8 request[ioc->req_sz];
+ u8 reply[sizeof(FWUploadReply_t)];
+ FWUpload_t *prequest;
+ FWUploadReply_t *preply;
+ FWUploadTCSGE_t *ptcsge = NULL;
+ int sgeoffset;
+ int ii, sz, reply_sz;
+ int cmdStatus, freeMem = 0;
+ int num_frags, alloc_sz;
+
+ /* If the image size is 0 or if the pointer is
+ * not NULL (error), we are done.
+ */
+ if (((sz = ioc->facts.FWImageSize) == 0) || ioc->cached_fw)
+ return 0;
- dprintk((KERN_WARNING MYNAM ": KickStarting %s!\n", ioc->name));
+ ioc->num_fw_frags = ioc->req_sz - sizeof(FWUpload_t) + sizeof(dma_addr_t) + sizeof(u32) -1;
+ ioc->num_fw_frags /= sizeof(dma_addr_t) + sizeof(u32);
- hard_reset_done = mpt_fc9x9_reset(ioc, force);
-#if 0
- if (ioc->chip_type == FC909 || ioc->chip-type == FC919) {
- hard_reset_done = mpt_fc9x9_reset(ioc, force);
- } else if (ioc->chip_type == FC929) {
- unsigned long delta;
-
- delta = jiffies - ioc->last_kickstart;
- dprintk((KERN_INFO MYNAM ": %s: 929 KickStart, last=%ld, delta = %ld\n",
- ioc->name, ioc->last_kickstart, delta));
- if ((ioc->sod_reset == 0) || (delta >= 10*HZ))
- hard_reset_done = mpt_fc9x9_reset(ioc, ignore);
- else {
- dprintk((KERN_INFO MYNAM ": %s: Skipping KickStart (delta=%ld)!\n",
- ioc->name, delta));
- return 0;
- }
- /* TODO! Add C1030!
- } else if (ioc->chip_type == C1030) {
- */
- } else {
- printk(KERN_ERR MYNAM ": %s: ERROR - Bad chip_type (0x%x)\n",
- ioc->name, ioc->chip_type);
- return -5;
+ ioc->cached_fw = (fw_image_t **) mpt_alloc_fw_memory(ioc,
+ ioc->facts.FWImageSize, &num_frags, &alloc_sz);
+
+ if (ioc->cached_fw == NULL) {
+ /* Major Failure.
+ */
+ mpt_free_fw_memory(ioc, NULL);
+ ioc->cached_fw = NULL;
+
+ return -ENOMEM;
}
-#endif
+ ioc->alloc_total += alloc_sz;
- if (hard_reset_done < 0)
- return hard_reset_done;
+ dprintk((KERN_INFO MYNAM ": FW Image @ %p, sz=%d bytes\n",
+ (void *)(ulong)ioc->cached_fw, ioc->facts.FWImageSize));
- dprintk((KERN_INFO MYNAM ": %s: Diagnostic reset successful\n",
- ioc->name));
+ prequest = (FWUpload_t *)&request;
+ preply = (FWUploadReply_t *)&reply;
- for (cnt=0; cnt<HZ*20; cnt++) {
- if ((ioc_state = GetIocState(ioc, 1)) == MPI_IOC_STATE_READY) {
- dprintk((KERN_INFO MYNAM ": %s: KickStart successful! (cnt=%d)\n",
- ioc->name, cnt));
- return hard_reset_done;
+ /* Destination... */
+ memset(prequest, 0, ioc->req_sz);
+
+ reply_sz = sizeof(reply);
+ memset(preply, 0, reply_sz);
+
+ prequest->ImageType = MPI_FW_UPLOAD_ITYPE_FW_IOC_MEM;
+ prequest->Function = MPI_FUNCTION_FW_UPLOAD;
+ prequest->MsgContext = 0; /* anything */
+
+ ptcsge = (FWUploadTCSGE_t *) &prequest->SGL;
+ ptcsge->Reserved = 0;
+ ptcsge->ContextSize = 0;
+ ptcsge->DetailsLength = 12;
+ ptcsge->Flags = MPI_SGE_FLAGS_TRANSACTION_ELEMENT;
+ ptcsge->Reserved1 = 0;
+ ptcsge->ImageOffset = 0;
+ ptcsge->ImageSize = cpu_to_le32(sz);
+
+ sgeoffset = sizeof(FWUpload_t) - sizeof(SGE_MPI_UNION) + sizeof(FWUploadTCSGE_t);
+
+ for (ii = 0; ii < (num_frags-1); ii++) {
+ mpt_add_sge(&request[sgeoffset], MPT_SGE_FLAGS_SIMPLE_ELEMENT |
+ MPT_SGE_FLAGS_ADDRESSING | MPT_TRANSFER_IOC_TO_HOST |
+ (u32) ioc->cached_fw[ii]->size, ioc->cached_fw[ii]->fw_dma);
+
+ sgeoffset += sizeof(u32) + sizeof(dma_addr_t);
+ }
+
+ mpt_add_sge(&request[sgeoffset],
+ MPT_SGE_FLAGS_SSIMPLE_READ |(u32) ioc->cached_fw[ii]->size,
+ ioc->cached_fw[ii]->fw_dma);
+
+ sgeoffset += sizeof(u32) + sizeof(dma_addr_t);
+
+ dprintk((MYIOC_s_INFO_FMT "Sending FW Upload (req @ %p) size %d \n",
+ ioc->name, prequest, sgeoffset));
+
+ ii = mpt_handshake_req_reply_wait(ioc, sgeoffset, (u32*)prequest,
+ reply_sz, (u16*)preply, 65 /*seconds*/, sleepFlag);
+
+ cmdStatus = -EFAULT;
+ if (ii == 0) {
+ /* Handshake transfer was complete and successful.
+ * Check the Reply Frame.
+ */
+ int status, transfer_sz;
+ status = le16_to_cpu(preply->IOCStatus);
+ if (status == MPI_IOCSTATUS_SUCCESS) {
+ transfer_sz = le32_to_cpu(preply->ActualImageSize);
+ if (transfer_sz == sz)
+ cmdStatus = 0;
}
- /* udelay(10000) ? */
- current->state = TASK_INTERRUPTIBLE;
- schedule_timeout(1);
}
+ dprintk((MYIOC_s_INFO_FMT ": do_upload status %d \n",
+ ioc->name, cmdStatus));
- printk(KERN_ERR MYNAM ": %s: ERROR - Failed to come READY after reset!\n",
- ioc->name);
- return -1;
+ /* Check to see if we have a copy of this image in
+ * host memory already.
+ */
+ if (cmdStatus == 0) {
+ ioc->upload_fw = 0;
+ if (ioc->alt_ioc && ioc->alt_ioc->cached_fw)
+ freeMem = 1;
+ }
+
+ /* We already have a copy of this image or
+ * we had some type of an error - either the handshake
+ * failed (i != 0) or the command did not complete successfully.
+ */
+ if (cmdStatus || freeMem) {
+
+ dprintk((MYIOC_s_INFO_FMT ": do_upload freeing %s image \n",
+ ioc->name, cmdStatus ? "incomplete" : "duplicate"));
+ mpt_free_fw_memory(ioc, NULL);
+ ioc->cached_fw = NULL;
+ }
+
+ return cmdStatus;
}
/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
/*
- * mpt_fc9x9_reset - Perform hard reset of FC9x9 adapter.
+ * mpt_downloadboot - DownloadBoot code
* @ioc: Pointer to MPT_ADAPTER structure
+ * @flag: Specify which part of IOC memory is to be uploaded.
+ * @sleepFlag: Specifies whether the process can sleep
*
- * This routine places FC9x9 adapter in diagnostic mode via the
- * WriteSequence register, and then performs a hard reset of adapter
- * via the Diagnostic register.
+ * FwDownloadBoot requires Programmed IO access.
*
- * Returns 0 for success, non-zero for failure.
+ * Returns 0 for success
+ * -1 FW Image size is 0
+ * -2 No valid cached_fw Pointer
+ * <0 for fw upload failure.
*/
static int
-mpt_fc9x9_reset(MPT_ADAPTER *ioc, int ignore)
+mpt_downloadboot(MPT_ADAPTER *ioc, int sleepFlag)
{
- u32 diag0val;
- int hard_reset_done = 0;
-
- /* Use "Diagnostic reset" method! (only thing available!) */
-
- diag0val = CHIPREG_READ32(&ioc->chip->Diagnostic);
+ MpiFwHeader_t *FwHdr = NULL;
+ MpiExtImageHeader_t *ExtHdr;
+ fw_image_t **pCached = NULL;
+ int fw_sz;
+ u32 diag0val;
#ifdef MPT_DEBUG
-{
- u32 diag1val = 0;
+ u32 diag1val = 0;
+#endif
+ int count = 0;
+ u32 *ptru32 = NULL;
+ u32 diagRwData;
+ u32 nextImage;
+ u32 ext_offset;
+ u32 load_addr;
+ int max_idx, fw_idx, ext_idx;
+ int left_u32s;
+
+ dprintk((MYIOC_s_INFO_FMT "DbGb0: downloadboot entered.\n",
+ ioc->name));
+#ifdef MPT_DEBUG
+ diag0val = CHIPREG_READ32(&ioc->chip->Diagnostic);
if (ioc->alt_ioc)
diag1val = CHIPREG_READ32(&ioc->alt_ioc->chip->Diagnostic);
- dprintk((KERN_INFO MYNAM ": %s: DBG1: diag0=%08x, diag1=%08x\n",
- ioc->name, diag0val, diag1val));
-}
+ dprintk((MYIOC_s_INFO_FMT "DbGb1: diag0=%08x, diag1=%08x\n",
+ ioc->name, diag0val, diag1val));
#endif
- if (diag0val & MPI_DIAG_DRWE) {
- dprintk((KERN_INFO MYNAM ": %s: DiagWriteEn bit already set\n",
- ioc->name));
- } else {
- /* Write magic sequence to WriteSequence register */
+
+ dprintk((MYIOC_s_INFO_FMT "fw size 0x%x, ioc FW Ptr %p\n",
+ ioc->name, ioc->facts.FWImageSize, ioc->cached_fw));
+ if (ioc->alt_ioc)
+ dprintk((MYIOC_s_INFO_FMT "alt ioc FW Ptr %p\n",
+ ioc->name, ioc->alt_ioc->cached_fw));
+
+ /* Get dma_addr and data transfer size.
+ */
+ if ((fw_sz = ioc->facts.FWImageSize) == 0)
+ return -1;
+
+ /* Get the DMA from ioc or ioc->alt_ioc */
+ if (ioc->cached_fw == NULL)
+ pCached = (fw_image_t **)ioc->cached_fw;
+ else if (ioc->alt_ioc && ioc->alt_ioc->cached_fw)
+ pCached = (fw_image_t **)ioc->alt_ioc->cached_fw;
+
+ dprintk((MYIOC_s_INFO_FMT "DbGb2: FW Image @ %p\n",
+ ioc->name, FwHdr));
+ if (!pCached)
+ return -2;
+
+ /* Write magic sequence to WriteSequence register
+ * until enter diagnostic mode
+ */
+ diag0val = CHIPREG_READ32(&ioc->chip->Diagnostic);
+ while ((diag0val & MPI_DIAG_DRWE) == 0) {
CHIPREG_WRITE32(&ioc->chip->WriteSequence, MPI_WRSEQ_1ST_KEY_VALUE);
CHIPREG_WRITE32(&ioc->chip->WriteSequence, MPI_WRSEQ_2ND_KEY_VALUE);
CHIPREG_WRITE32(&ioc->chip->WriteSequence, MPI_WRSEQ_3RD_KEY_VALUE);
CHIPREG_WRITE32(&ioc->chip->WriteSequence, MPI_WRSEQ_4TH_KEY_VALUE);
CHIPREG_WRITE32(&ioc->chip->WriteSequence, MPI_WRSEQ_5TH_KEY_VALUE);
- dprintk((KERN_INFO MYNAM ": %s: Wrote magic DiagWriteEn sequence [spot#1]\n",
- ioc->name));
+
+ /* wait 100 msec */
+ if (sleepFlag == CAN_SLEEP) {
+ set_current_state(TASK_INTERRUPTIBLE);
+ schedule_timeout(100 * HZ / 1000);
+ } else {
+ mdelay (100);
+ }
+
+ count++;
+ if (count > 20) {
+ printk(MYIOC_s_ERR_FMT "Enable Diagnostic mode FAILED! (%02xh)\n",
+ ioc->name, diag0val);
+ return -EFAULT;
+
+ }
+
+ diag0val = CHIPREG_READ32(&ioc->chip->Diagnostic);
+#ifdef MPT_DEBUG
+ if (ioc->alt_ioc)
+ diag1val = CHIPREG_READ32(&ioc->alt_ioc->chip->Diagnostic);
+ dprintk((MYIOC_s_INFO_FMT "DbGb3: diag0=%08x, diag1=%08x\n",
+ ioc->name, diag0val, diag1val));
+#endif
+ dprintk((MYIOC_s_INFO_FMT "Wrote magic DiagWriteEn sequence (%x)\n",
+ ioc->name, diag0val));
}
- diag0val = CHIPREG_READ32(&ioc->chip->Diagnostic);
+ /* Set the DiagRwEn and Disable ARM bits */
+ diag0val |= (MPI_DIAG_RW_ENABLE | MPI_DIAG_DISABLE_ARM);
+ CHIPREG_WRITE32(&ioc->chip->Diagnostic, diag0val);
+
#ifdef MPT_DEBUG
-{
- u32 diag1val = 0;
if (ioc->alt_ioc)
diag1val = CHIPREG_READ32(&ioc->alt_ioc->chip->Diagnostic);
- dprintk((KERN_INFO MYNAM ": %s: DbG2: diag0=%08x, diag1=%08x\n",
+ dprintk((MYIOC_s_INFO_FMT "DbGb3: diag0=%08x, diag1=%08x\n",
ioc->name, diag0val, diag1val));
+#endif
+
+ /* Write the LoadStartAddress to the DiagRw Address Register
+ * using Programmed IO
+ */
+ CHIPREG_PIO_WRITE32(&ioc->pio_chip->DiagRwAddress, FwHdr->LoadStartAddress);
+ dprintk((MYIOC_s_INFO_FMT "LoadStart addr written 0x%x \n",
+ ioc->name, FwHdr->LoadStartAddress));
+#if 1
+
+ /* max_idx = 1 + maximum valid buffer index
+ */
+ max_idx = 0;
+ while (pCached[max_idx])
+ max_idx++;
+
+ fw_idx = 0;
+ FwHdr = (MpiFwHeader_t *) pCached[fw_idx]->fw;
+ ptru32 = (u32 *) FwHdr;
+ count = (FwHdr->ImageSize + 3)/4;
+ nextImage = FwHdr->NextImageHeaderOffset;
+
+ dprintk((MYIOC_s_INFO_FMT "Write FW Image: 0x%x u32's @ %p\n",
+ ioc->name, count, ptru32));
+ left_u32s = pCached[fw_idx]->size/4;
+ while (count--) {
+ if (left_u32s == 0) {
+ fw_idx++;
+ if (fw_idx >= max_idx) {
+ /* FIXME
+ ERROR CASE
+ */
+ ;
+ }
+ ptru32 = (u32 *) pCached[fw_idx]->fw;
+ left_u32s = pCached[fw_idx]->size / 4;
+ }
+ left_u32s--;
+ CHIPREG_PIO_WRITE32(&ioc->pio_chip->DiagRwData, *ptru32);
+ ptru32++;
+ }
+
+ /* left_u32s, fw_idx and ptru32 are all valid
+ */
+ while (nextImage) {
+ ext_idx = 0;
+ ext_offset = nextImage;
+ while (ext_offset > pCached[ext_idx]->size) {
+ ext_idx++;
+ if (ext_idx >= max_idx) {
+ /* FIXME
+ ERROR CASE
+ */
+ ;
+ }
+ ext_offset -= pCached[ext_idx]->size;
+ }
+ ptru32 = (u32 *) ((char *)pCached[ext_idx]->fw + ext_offset);
+ left_u32s = pCached[ext_idx]->size - ext_offset;
+
+ if ((left_u32s * 4) >= sizeof(MpiExtImageHeader_t)) {
+ ExtHdr = (MpiExtImageHeader_t *) ptru32;
+ count = (ExtHdr->ImageSize + 3 )/4;
+ nextImage = ExtHdr->NextImageHeaderOffset;
+ load_addr = ExtHdr->LoadStartAddress;
+ } else {
+ u32 * ptmp = (u32 *)pCached[ext_idx+1]->fw;
+
+ switch (left_u32s) {
+ case 5:
+ count = *(ptru32 + 2);
+ nextImage = *(ptru32 + 3);
+ load_addr = *(ptru32 + 4);
+ break;
+ case 4:
+ count = *(ptru32 + 2);
+ nextImage = *(ptru32 + 3);
+ load_addr = *ptmp;
+ break;
+ case 3:
+ count = *(ptru32 + 2);
+ nextImage = *ptmp;
+ load_addr = *(ptmp + 1);
+ break;
+ case 2:
+ count = *ptmp;
+ nextImage = *(ptmp + 1);
+ load_addr = *(ptmp + 2);
+ break;
+
+ case 1:
+ count = *(ptmp + 1);
+ nextImage = *(ptmp + 2);
+ load_addr = *(ptmp + 3);
+ break;
+
+ default:
+ count = 0;
+ nextImage = 0;
+ load_addr = 0;
+ /* FIXME
+ ERROR CASE
+ */
+ ;
+
+ }
+ count = (count +3)/4;
+ }
+
+ dprintk((MYIOC_s_INFO_FMT "Write Ext Image: 0x%x u32's @ %p\n",
+ ioc->name, count, ptru32));
+ CHIPREG_PIO_WRITE32(&ioc->pio_chip->DiagRwAddress, load_addr);
+
+ while (count--) {
+ if (left_u32s == 0) {
+ fw_idx++;
+ if (fw_idx >= max_idx) {
+ /* FIXME
+ ERROR CASE
+ */
+ ;
+ }
+ ptru32 = (u32 *) pCached[fw_idx]->fw;
+ left_u32s = pCached[fw_idx]->size / 4;
+ }
+ left_u32s--;
+ CHIPREG_PIO_WRITE32(&ioc->pio_chip->DiagRwData, *ptru32);
+ ptru32++;
+ }
+ }
+
+#else
+ while (nextImage) {
+
+ /* Set the pointer to the extended image
+ */
+ ExtHdr = (MpiExtImageHeader_t *) ((char *) FwHdr + nextImage);
+
+ CHIPREG_PIO_WRITE32(&ioc->pio_chip->DiagRwAddress, ExtHdr->LoadStartAddress);
+
+ count = (ExtHdr->ImageSize + 3 )/4;
+
+ ptru32 = (u32 *) ExtHdr;
+ dprintk((MYIOC_s_INFO_FMT "Write Ext Image: 0x%x u32's @ %p\n",
+ ioc->name, count, ptru32));
+ while (count-- ) {
+ CHIPREG_PIO_WRITE32(&ioc->pio_chip->DiagRwData, *ptru32);
+ ptru32++;
+ }
+ nextImage = ExtHdr->NextImageHeaderOffset;
+ }
+#endif
+
+
+ /* Write the IopResetVectorRegAddr */
+ dprintk((MYIOC_s_INFO_FMT "Write IopResetVector Addr! \n", ioc->name));
+ CHIPREG_PIO_WRITE32(&ioc->pio_chip->DiagRwAddress, FwHdr->IopResetRegAddr);
+
+ /* Write the IopResetVectorValue */
+ dprintk((MYIOC_s_INFO_FMT "Write IopResetVector Value! \n", ioc->name));
+ CHIPREG_PIO_WRITE32(&ioc->pio_chip->DiagRwData, FwHdr->IopResetVectorValue);
+
+ /* Clear the internal flash bad bit - autoincrementing register,
+ * so must do two writes.
+ */
+ CHIPREG_PIO_WRITE32(&ioc->pio_chip->DiagRwAddress, 0x3F000000);
+ diagRwData = CHIPREG_PIO_READ32(&ioc->pio_chip->DiagRwData);
+ diagRwData |= 0x4000000;
+ CHIPREG_PIO_WRITE32(&ioc->pio_chip->DiagRwAddress, 0x3F000000);
+ CHIPREG_PIO_WRITE32(&ioc->pio_chip->DiagRwData, diagRwData);
+
+ /* clear the RW enable and DISARM bits */
+ diag0val = CHIPREG_READ32(&ioc->chip->Diagnostic);
+ diag0val &= ~(MPI_DIAG_DISABLE_ARM | MPI_DIAG_RW_ENABLE | MPI_DIAG_FLASH_BAD_SIG);
+ CHIPREG_WRITE32(&ioc->chip->Diagnostic, diag0val);
+
+ /* Write 0xFF to reset the sequencer */
+ CHIPREG_WRITE32(&ioc->chip->WriteSequence, 0xFF);
+
+ return 0;
}
+
+/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
+/*
+ * KickStart - Perform hard reset of MPT adapter.
+ * @ioc: Pointer to MPT_ADAPTER structure
+ * @force: Force hard reset
+ * @sleepFlag: Specifies whether the process can sleep
+ *
+ * This routine places MPT adapter in diagnostic mode via the
+ * WriteSequence register, and then performs a hard reset of adapter
+ * via the Diagnostic register.
+ *
+ * Inputs: sleepflag - CAN_SLEEP (non-interrupt thread)
+ * or NO_SLEEP (interrupt thread, use mdelay)
+ * force - 1 if doorbell active, board fault state
+ * board operational, IOC_RECOVERY or
+ * IOC_BRINGUP and there is an alt_ioc.
+ * 0 else
+ *
+ * Returns:
+ * 1 - hard reset, READY
+ * 0 - no reset due to History bit, READY
+ * -1 - no reset due to History bit but not READY
+ * OR reset but failed to come READY
+ * -2 - no reset, could not enter DIAG mode
+ * -3 - reset but bad FW bit
+ */
+static int
+KickStart(MPT_ADAPTER *ioc, int force, int sleepFlag)
+{
+ int hard_reset_done = 0;
+ u32 ioc_state;
+ int cnt = 0;
+
+ dprintk((KERN_WARNING MYNAM ": KickStarting %s!\n", ioc->name));
+
+ hard_reset_done = mpt_diag_reset(ioc, force, sleepFlag);
+ if (hard_reset_done < 0)
+ return hard_reset_done;
+
+ dprintk((MYIOC_s_INFO_FMT "Diagnostic reset successful!\n",
+ ioc->name));
+
+ for (cnt=0; cnt<HZ*20; cnt++) {
+ if ((ioc_state = mpt_GetIocState(ioc, 1)) == MPI_IOC_STATE_READY) {
+ dprintk((MYIOC_s_INFO_FMT "KickStart successful! (cnt=%d)\n",
+ ioc->name, cnt));
+ return hard_reset_done;
+ }
+ if (sleepFlag == CAN_SLEEP) {
+ set_current_state(TASK_INTERRUPTIBLE);
+ schedule_timeout(1);
+ } else {
+ mdelay (10);
+ }
+ }
+
+ printk(MYIOC_s_ERR_FMT "Failed to come READY after reset!\n",
+ ioc->name);
+ return -1;
+}
+
+/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
+/*
+ * mpt_diag_reset - Perform hard reset of the adapter.
+ * @ioc: Pointer to MPT_ADAPTER structure
+ * @ignore: Set if to honor and clear to ignore
+ * the reset history bit
+ * @sleepflag: CAN_SLEEP if called in a non-interrupt thread,
+ * else set to NO_SLEEP (use mdelay instead)
+ *
+ * This routine places the adapter in diagnostic mode via the
+ * WriteSequence register and then performs a hard reset of adapter
+ * via the Diagnostic register. Adapter should be in ready state
+ * upon successful completion.
+ *
+ * Returns: 1 hard reset successful
+ * 0 no reset performed because reset history bit set
+ * -2 enabling diagnostic mode failed
+ * -3 diagnostic reset failed
+ */
+static int
+mpt_diag_reset(MPT_ADAPTER *ioc, int ignore, int sleepFlag)
+{
+ u32 diag0val;
+ u32 doorbell;
+ int hard_reset_done = 0;
+ int count = 0;
+#ifdef MPT_DEBUG
+ u32 diag1val = 0;
#endif
- if (!ignore && (diag0val & MPI_DIAG_RESET_HISTORY)) {
- dprintk((KERN_INFO MYNAM ": %s: Skipping due to ResetHistory bit set!\n",
- ioc->name));
- } else {
+
+ /* Clear any existing interrupts */
+ CHIPREG_WRITE32(&ioc->chip->IntStatus, 0);
+
+ /* Use "Diagnostic reset" method! (only thing available!) */
+ diag0val = CHIPREG_READ32(&ioc->chip->Diagnostic);
+
+#ifdef MPT_DEBUG
+ if (ioc->alt_ioc)
+ diag1val = CHIPREG_READ32(&ioc->alt_ioc->chip->Diagnostic);
+ dprintk((MYIOC_s_INFO_FMT "DbG1: diag0=%08x, diag1=%08x\n",
+ ioc->name, diag0val, diag1val));
+#endif
+
+ /* Do the reset if we are told to ignore the reset history
+ * or if the reset history is 0
+ */
+ if (ignore || !(diag0val & MPI_DIAG_RESET_HISTORY)) {
+ while ((diag0val & MPI_DIAG_DRWE) == 0) {
+ /* Write magic sequence to WriteSequence register
+ * Loop until in diagnostic mode
+ */
+ CHIPREG_WRITE32(&ioc->chip->WriteSequence, MPI_WRSEQ_1ST_KEY_VALUE);
+ CHIPREG_WRITE32(&ioc->chip->WriteSequence, MPI_WRSEQ_2ND_KEY_VALUE);
+ CHIPREG_WRITE32(&ioc->chip->WriteSequence, MPI_WRSEQ_3RD_KEY_VALUE);
+ CHIPREG_WRITE32(&ioc->chip->WriteSequence, MPI_WRSEQ_4TH_KEY_VALUE);
+ CHIPREG_WRITE32(&ioc->chip->WriteSequence, MPI_WRSEQ_5TH_KEY_VALUE);
+
+ /* wait 100 msec */
+ if (sleepFlag == CAN_SLEEP) {
+ set_current_state(TASK_INTERRUPTIBLE);
+ schedule_timeout(100 * HZ / 1000);
+ } else {
+ mdelay (100);
+ }
+
+ count++;
+ if (count > 20) {
+ printk(MYIOC_s_ERR_FMT "Enable Diagnostic mode FAILED! (%02xh)\n",
+ ioc->name, diag0val);
+ return -2;
+
+ }
+
+ diag0val = CHIPREG_READ32(&ioc->chip->Diagnostic);
+
+ dprintk((MYIOC_s_INFO_FMT "Wrote magic DiagWriteEn sequence (%x)\n",
+ ioc->name, diag0val));
+ }
+
+#ifdef MPT_DEBUG
+ if (ioc->alt_ioc)
+ diag1val = CHIPREG_READ32(&ioc->alt_ioc->chip->Diagnostic);
+ dprintk((MYIOC_s_INFO_FMT "DbG2: diag0=%08x, diag1=%08x\n",
+ ioc->name, diag0val, diag1val));
+#endif
+ /* Write the PreventIocBoot bit */
+ if (ioc->facts.Flags & MPI_IOCFACTS_FLAGS_FW_DOWNLOAD_BOOT) {
+ diag0val |= MPI_DIAG_PREVENT_IOC_BOOT;
+ CHIPREG_WRITE32(&ioc->chip->Diagnostic, diag0val);
+ }
+
+ /*
+ * Disable the ARM (Bug fix)
+ *
+ */
+ CHIPREG_WRITE32(&ioc->chip->Diagnostic, diag0val | MPI_DIAG_DISABLE_ARM);
+ mdelay (1);
+
/*
* Now hit the reset bit in the Diagnostic register
- * (THE BIG HAMMER!)
+ * (THE BIG HAMMER!) (Clears DRWE bit).
*/
- CHIPREG_WRITE32(&ioc->chip->Diagnostic, MPI_DIAG_RESET_ADAPTER);
+ CHIPREG_WRITE32(&ioc->chip->Diagnostic, diag0val | MPI_DIAG_RESET_ADAPTER);
hard_reset_done = 1;
- dprintk((KERN_INFO MYNAM ": %s: Diagnostic reset performed\n",
+ dprintk((MYIOC_s_INFO_FMT "Diagnostic reset performed\n",
ioc->name));
- /* want udelay(100) */
- current->state = TASK_INTERRUPTIBLE;
- schedule_timeout(1);
+ /*
+ * Call each currently registered protocol IOC reset handler
+ * with pre-reset indication.
+ * NOTE: If we're doing _IOC_BRINGUP, there can be no
+ * MptResetHandlers[] registered yet.
+ */
+ {
+ int ii;
+ int r = 0;
+
+ for (ii=MPT_MAX_PROTOCOL_DRIVERS-1; ii; ii--) {
+ if (MptResetHandlers[ii]) {
+ dprintk((MYIOC_s_INFO_FMT "Calling IOC pre_reset handler #%d\n",
+ ioc->name, ii));
+ r += (*(MptResetHandlers[ii]))(ioc, MPT_IOC_PRE_RESET);
+ if (ioc->alt_ioc) {
+ dprintk((MYIOC_s_INFO_FMT "Calling alt-%s pre_reset handler #%d\n",
+ ioc->name, ioc->alt_ioc->name, ii));
+ r += (*(MptResetHandlers[ii]))(ioc->alt_ioc, MPT_IOC_PRE_RESET);
+ }
+ }
+ }
+ /* FIXME? Examine results here? */
+ }
+
+ if (ioc->facts.Flags & MPI_IOCFACTS_FLAGS_FW_DOWNLOAD_BOOT) {
+ /* If the DownloadBoot operation fails, the
+ * IOC will be left unusable. This is a fatal error
+ * case. _diag_reset will return < 0
+ */
+ for (count = 0; count < 30; count ++) {
+ diag0val = CHIPREG_READ32(&ioc->chip->Diagnostic);
+#ifdef MPT_DEBUG
+ if (ioc->alt_ioc)
+ diag1val = CHIPREG_READ32(&ioc->alt_ioc->chip->Diagnostic);
+ dprintk((MYIOC_s_INFO_FMT
+ "DbG2b: diag0=%08x, diag1=%08x\n",
+ ioc->name, diag0val, diag1val));
+#endif
+ if (!(diag0val & MPI_DIAG_RESET_ADAPTER)) {
+ break;
+ }
+
+ /* wait 1 sec */
+ if (sleepFlag == CAN_SLEEP) {
+ set_current_state(TASK_INTERRUPTIBLE);
+ schedule_timeout(HZ);
+ } else {
+ mdelay (1000);
+ }
+ }
+ if ((count = mpt_downloadboot(ioc, sleepFlag)) < 0) {
+ printk(KERN_WARNING MYNAM
+ ": firmware downloadboot failure (%d)!\n", count);
+ }
+
+ } else {
+ /* Wait for FW to reload and for board
+ * to go to the READY state.
+ * Maximum wait is 30 seconds.
+ * If fail, no error will check again
+ * with calling program.
+ */
+ for (count = 0; count < 30; count ++) {
+ doorbell = CHIPREG_READ32(&ioc->chip->Doorbell);
+ doorbell &= MPI_IOC_STATE_MASK;
+
+ if (doorbell == MPI_IOC_STATE_READY) {
+ break;
+ }
+
+ /* wait 1 sec */
+ if (sleepFlag == CAN_SLEEP) {
+ set_current_state(TASK_INTERRUPTIBLE);
+ schedule_timeout(HZ);
+ } else {
+ mdelay (1000);
+ }
+ }
+ }
+ }
+
+ diag0val = CHIPREG_READ32(&ioc->chip->Diagnostic);
+#ifdef MPT_DEBUG
+ if (ioc->alt_ioc)
+ diag1val = CHIPREG_READ32(&ioc->alt_ioc->chip->Diagnostic);
+ dprintk((MYIOC_s_INFO_FMT "DbG3: diag0=%08x, diag1=%08x\n",
+ ioc->name, diag0val, diag1val));
+#endif
- /* Write magic sequence to WriteSequence register */
+ /* Clear RESET_HISTORY bit! Place board in the
+ * diagnostic mode to update the diag register.
+ */
+ diag0val = CHIPREG_READ32(&ioc->chip->Diagnostic);
+ count = 0;
+ while ((diag0val & MPI_DIAG_DRWE) == 0) {
+ /* Write magic sequence to WriteSequence register
+ * Loop until in diagnostic mode
+ */
CHIPREG_WRITE32(&ioc->chip->WriteSequence, MPI_WRSEQ_1ST_KEY_VALUE);
CHIPREG_WRITE32(&ioc->chip->WriteSequence, MPI_WRSEQ_2ND_KEY_VALUE);
CHIPREG_WRITE32(&ioc->chip->WriteSequence, MPI_WRSEQ_3RD_KEY_VALUE);
CHIPREG_WRITE32(&ioc->chip->WriteSequence, MPI_WRSEQ_4TH_KEY_VALUE);
CHIPREG_WRITE32(&ioc->chip->WriteSequence, MPI_WRSEQ_5TH_KEY_VALUE);
- dprintk((KERN_INFO MYNAM ": %s: Wrote magic DiagWriteEn sequence [spot#2]\n",
- ioc->name));
- }
- /* Clear RESET_HISTORY bit! */
- CHIPREG_WRITE32(&ioc->chip->Diagnostic, 0x0);
+ /* wait 100 msec */
+ if (sleepFlag == CAN_SLEEP) {
+ set_current_state(TASK_INTERRUPTIBLE);
+ schedule_timeout(100 * HZ / 1000);
+ } else {
+ mdelay (100);
+ }
+ count++;
+ if (count > 20) {
+ printk(MYIOC_s_ERR_FMT "Enable Diagnostic mode FAILED! (%02xh)\n",
+ ioc->name, diag0val);
+ break;
+ }
+ diag0val = CHIPREG_READ32(&ioc->chip->Diagnostic);
+ }
+ diag0val &= ~MPI_DIAG_RESET_HISTORY;
+ CHIPREG_WRITE32(&ioc->chip->Diagnostic, diag0val);
diag0val = CHIPREG_READ32(&ioc->chip->Diagnostic);
-#ifdef MPT_DEBUG
-{
- u32 diag1val = 0;
- if (ioc->alt_ioc)
- diag1val = CHIPREG_READ32(&ioc->alt_ioc->chip->Diagnostic);
- dprintk((KERN_INFO MYNAM ": %s: DbG3: diag0=%08x, diag1=%08x\n",
- ioc->name, diag0val, diag1val));
-}
-#endif
if (diag0val & MPI_DIAG_RESET_HISTORY) {
- printk(KERN_WARNING MYNAM ": %s: WARNING - ResetHistory bit failed to clear!\n",
+ printk(MYIOC_s_WARN_FMT "ResetHistory bit failed to clear!\n",
ioc->name);
}
+ /* Disable Diagnostic Mode
+ */
+ CHIPREG_WRITE32(&ioc->chip->WriteSequence, 0xFFFFFFFF);
+
+ /* Check FW reload status flags.
+ */
diag0val = CHIPREG_READ32(&ioc->chip->Diagnostic);
+ if (diag0val & (MPI_DIAG_FLASH_BAD_SIG | MPI_DIAG_RESET_ADAPTER | MPI_DIAG_DISABLE_ARM)) {
+ printk(MYIOC_s_ERR_FMT "Diagnostic reset FAILED! (%02xh)\n",
+ ioc->name, diag0val);
+ return -3;
+ }
+
#ifdef MPT_DEBUG
-{
- u32 diag1val = 0;
if (ioc->alt_ioc)
diag1val = CHIPREG_READ32(&ioc->alt_ioc->chip->Diagnostic);
- dprintk((KERN_INFO MYNAM ": %s: DbG4: diag0=%08x, diag1=%08x\n",
+ dprintk((MYIOC_s_INFO_FMT "DbG4: diag0=%08x, diag1=%08x\n",
ioc->name, diag0val, diag1val));
-}
#endif
- if (diag0val & (MPI_DIAG_FLASH_BAD_SIG | MPI_DIAG_RESET_ADAPTER | MPI_DIAG_DISABLE_ARM)) {
- printk(KERN_ERR MYNAM ": %s: ERROR - Diagnostic reset FAILED! (%02xh)\n",
- ioc->name, diag0val);
- return -3;
- }
/*
* Reset flag that says we've enabled event notification
*/
ioc->facts.EventState = 0;
- /* NEW! 20010220 -sralston
- * Try to avoid redundant resets of the 929.
- */
- ioc->sod_reset++;
- ioc->last_kickstart = jiffies;
- if (ioc->alt_ioc) {
- ioc->alt_ioc->sod_reset = ioc->sod_reset;
- ioc->alt_ioc->last_kickstart = ioc->last_kickstart;
- }
+ if (ioc->alt_ioc)
+ ioc->alt_ioc->facts.EventState = 0;
return hard_reset_done;
}
* Returns 0 for success, non-zero for failure.
*/
static int
-SendIocReset(MPT_ADAPTER *ioc, u8 reset_type)
+SendIocReset(MPT_ADAPTER *ioc, u8 reset_type, int sleepFlag)
{
int r;
+ u32 state;
+ int cntdn, count;
dprintk((KERN_WARNING MYNAM ": %s: Sending IOC reset(0x%02x)!\n",
ioc->name, reset_type));
CHIPREG_WRITE32(&ioc->chip->Doorbell, reset_type<<MPI_DOORBELL_FUNCTION_SHIFT);
- if ((r = WaitForDoorbellAck(ioc, 2)) < 0)
+ if ((r = WaitForDoorbellAck(ioc, 2, sleepFlag)) < 0)
return r;
+ /* FW ACK'd request, wait for READY state
+ */
+ cntdn = HZ * 15;
+ count = 0;
+ if (sleepFlag != CAN_SLEEP)
+ cntdn *= 10; /* 1500 iterations @ 1msec per */
+
+ while ((state = mpt_GetIocState(ioc, 1)) != MPI_IOC_STATE_READY) {
+ cntdn--;
+ count++;
+ if (!cntdn) {
+ if (sleepFlag != CAN_SLEEP)
+ count *= 10;
+
+ printk(KERN_ERR MYNAM ": %s: ERROR - Wait IOC_READY state timeout(%d)!\n",
+ ioc->name, (count+5)/HZ);
+ return -ETIME;
+ }
+
+ if (sleepFlag == CAN_SLEEP) {
+ set_current_state(TASK_INTERRUPTIBLE);
+ schedule_timeout(1);
+ } else {
+ mdelay (1); /* 1 msec delay */
+ }
+ }
+
/* TODO!
* Cleanup all event stuff for this IOC; re-issue EventNotification
* request if needed.
* @ioc: Pointer to MPT_ADAPTER structure
*
* This routine allocates memory for the MPT reply and request frame
- * pools, and primes the IOC reply FIFO with reply frames.
+ * pools (if necessary), and primes the IOC reply FIFO with
+ * reply frames.
*
* Returns 0 for success, non-zero for failure.
*/
{
MPT_FRAME_HDR *mf;
unsigned long b;
+ unsigned long flags;
dma_addr_t aligned_mem_dma;
u8 *mem, *aligned_mem;
int i, sz;
memset(mem, 0, sz);
ioc->alloc_total += sz;
ioc->reply_alloc = mem;
- dprintk((KERN_INFO MYNAM ": %s.reply_alloc @ %p[%08x], sz=%d bytes\n",
- ioc->name, mem, ioc->reply_alloc_dma, sz));
+ dprintk((KERN_INFO MYNAM ": %s.reply_alloc @ %p[%p], sz=%d bytes\n",
+ ioc->name, mem, (void *)(ulong)ioc->reply_alloc_dma, sz));
b = (unsigned long) mem;
b = (b + (0x80UL - 1UL)) & ~(0x80UL - 1UL); /* round up to 128-byte boundary */
ioc->reply_frames = (MPT_FRAME_HDR *) aligned_mem;
ioc->reply_frames_dma =
(ioc->reply_alloc_dma + (aligned_mem - mem));
- aligned_mem_dma = ioc->reply_frames_dma;
- dprintk((KERN_INFO MYNAM ": %s.reply_frames @ %p[%08x]\n",
- ioc->name, aligned_mem, aligned_mem_dma));
- for (i = 0; i < ioc->reply_depth; i++) {
- /* Write each address to the IOC! */
- CHIPREG_WRITE32(&ioc->chip->ReplyFifo, aligned_mem_dma);
- aligned_mem_dma += ioc->reply_sz;
- }
+ ioc->reply_frames_low_dma = (u32) (ioc->reply_frames_dma & 0xFFFFFFFF);
+ }
+
+ /* Post Reply frames to FIFO
+ */
+ aligned_mem_dma = ioc->reply_frames_dma;
+ dprintk((KERN_INFO MYNAM ": %s.reply_frames @ %p[%p]\n",
+ ioc->name, ioc->reply_frames, (void *)(ulong)aligned_mem_dma));
+
+ for (i = 0; i < ioc->reply_depth; i++) {
+ /* Write each address to the IOC! */
+ CHIPREG_WRITE32(&ioc->chip->ReplyFifo, aligned_mem_dma);
+ aligned_mem_dma += ioc->reply_sz;
}
memset(mem, 0, sz);
ioc->alloc_total += sz;
ioc->req_alloc = mem;
- dprintk((KERN_INFO MYNAM ": %s.req_alloc @ %p[%08x], sz=%d bytes\n",
- ioc->name, mem, ioc->req_alloc_dma, sz));
+ dprintk((KERN_INFO MYNAM ": %s.req_alloc @ %p[%p], sz=%d bytes\n",
+ ioc->name, mem, (void *)(ulong)ioc->req_alloc_dma, sz));
b = (unsigned long) mem;
b = (b + (0x80UL - 1UL)) & ~(0x80UL - 1UL); /* round up to 128-byte boundary */
ioc->req_frames = (MPT_FRAME_HDR *) aligned_mem;
ioc->req_frames_dma =
(ioc->req_alloc_dma + (aligned_mem - mem));
- aligned_mem_dma = ioc->req_frames_dma;
-
- dprintk((KERN_INFO MYNAM ": %s.req_frames @ %p[%08x]\n",
- ioc->name, aligned_mem, aligned_mem_dma));
- for (i = 0; i < ioc->req_depth; i++) {
- mf = (MPT_FRAME_HDR *) aligned_mem;
+ ioc->req_frames_low_dma = (u32) (ioc->req_frames_dma & 0xFFFFFFFF);
- /* Queue REQUESTs *internally*! */
- Q_ADD_TAIL(&ioc->FreeQ.head, &mf->u.frame.linkage, MPT_FRAME_HDR);
- aligned_mem += ioc->req_sz;
+ if (sizeof(dma_addr_t) == sizeof(u64)) {
+ /* Check: upper 32-bits of the request and reply frame
+ * physical addresses must be the same.
+ */
+ if (((u64)ioc->req_frames_dma >> 32) != ((u64)ioc->reply_frames_dma >> 32)){
+ goto out_fail;
+ }
}
#if defined(CONFIG_MTRR) && 0
ioc->mtrr_reg = mtrr_add(ioc->req_alloc_dma,
sz,
MTRR_TYPE_WRCOMB, 1);
- dprintk((KERN_INFO MYNAM ": %s: MTRR region registered (base:size=%08x:%x)\n",
- ioc->name, ioc->req_alloc_dma,
- sz ));
+ dprintk((MYIOC_s_INFO_FMT "MTRR region registered (base:size=%08x:%x)\n",
+ ioc->name, ioc->req_alloc_dma, sz));
#endif
+ }
+
+ /* Initialize Request frames linked list
+ */
+ aligned_mem_dma = ioc->req_frames_dma;
+ aligned_mem = (u8 *) ioc->req_frames;
+ dprintk((KERN_INFO MYNAM ": %s.req_frames @ %p[%p]\n",
+ ioc->name, aligned_mem, (void *)(ulong)aligned_mem_dma));
+
+ spin_lock_irqsave(&ioc->FreeQlock, flags);
+ Q_INIT(&ioc->FreeQ, MPT_FRAME_HDR);
+ for (i = 0; i < ioc->req_depth; i++) {
+ mf = (MPT_FRAME_HDR *) aligned_mem;
+ /* Queue REQUESTs *internally*! */
+ Q_ADD_TAIL(&ioc->FreeQ.head, &mf->u.frame.linkage, MPT_FRAME_HDR);
+ aligned_mem += ioc->req_sz;
}
+ spin_unlock_irqrestore(&ioc->FreeQlock, flags);
+
if (ioc->sense_buf_pool == NULL) {
- sz = (ioc->req_depth * 256);
+ sz = (ioc->req_depth * MPT_SENSE_BUFFER_ALLOC);
ioc->sense_buf_pool =
pci_alloc_consistent(ioc->pcidev, sz, &ioc->sense_buf_pool_dma);
if (ioc->sense_buf_pool == NULL)
goto out_fail;
+ ioc->sense_buf_low_dma = (u32) (ioc->sense_buf_pool_dma & 0xFFFFFFFF);
ioc->alloc_total += sz;
}
#if defined(CONFIG_MTRR) && 0
if (ioc->mtrr_reg > 0) {
mtrr_del(ioc->mtrr_reg, 0, 0);
- dprintk((KERN_INFO MYNAM ": %s: MTRR region de-registered\n",
+ dprintk((MYIOC_s_INFO_FMT "MTRR region de-registered\n",
ioc->name));
}
#endif
ioc->alloc_total -= sz;
}
if (ioc->sense_buf_pool != NULL) {
- sz = (ioc->req_depth * 256);
+ sz = (ioc->req_depth * MPT_SENSE_BUFFER_ALLOC);
pci_free_consistent(ioc->pcidev,
sz,
ioc->sense_buf_pool, ioc->sense_buf_pool_dma);
}
/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
-/*
- * HandShakeReqAndReply - Send MPT request to and receive reply from
+/**
+ * mpt_handshake_req_reply_wait - Send MPT request to and receive reply from
* IOC via doorbell handshake method.
* @ioc: Pointer to MPT_ADAPTER structure
* @reqBytes: Size of the request in bytes
* @replyBytes: Expected size of the reply in bytes
* @u16reply: Pointer to area where reply should be written
* @maxwait: Max wait time for a reply (in seconds)
+ * @sleepFlag: Specifies whether the process can sleep
*
* NOTES: It is the callers responsibility to byte-swap fields in the
* request which are greater than 1 byte in size. It is also the
*
* Returns 0 for success, non-zero for failure.
*/
-static int
-HandShakeReqAndReply(MPT_ADAPTER *ioc, int reqBytes, u32 *req, int replyBytes, u16 *u16reply, int maxwait)
+int
+mpt_handshake_req_reply_wait(MPT_ADAPTER *ioc, int reqBytes, u32 *req,
+ int replyBytes, u16 *u16reply, int maxwait, int sleepFlag)
{
MPIDefaultReply_t *mptReply;
int failcnt = 0;
/*
* Wait for IOC's doorbell handshake int
*/
- if ((t = WaitForDoorbellInt(ioc, 2)) < 0)
+ if ((t = WaitForDoorbellInt(ioc, 5, sleepFlag)) < 0)
failcnt++;
- dhsprintk((KERN_INFO MYNAM ": %s: HandShake request start, WaitCnt=%d%s\n",
+ dhsprintk((MYIOC_s_INFO_FMT "HandShake request start, WaitCnt=%d%s\n",
ioc->name, t, failcnt ? " - MISSING DOORBELL HANDSHAKE!" : ""));
+ /* Read doorbell and check for active bit */
+ if (!(CHIPREG_READ32(&ioc->chip->Doorbell) & MPI_DOORBELL_ACTIVE))
+ return -1;
+
/*
* Clear doorbell int (WRITE 0 to IntStatus reg),
* then wait for IOC to ACKnowledge that it's ready for
* our handshake request.
*/
CHIPREG_WRITE32(&ioc->chip->IntStatus, 0);
- if (!failcnt && (t = WaitForDoorbellAck(ioc, 2)) < 0)
+ if (!failcnt && (t = WaitForDoorbellAck(ioc, 2, sleepFlag)) < 0)
failcnt++;
if (!failcnt) {
- int i;
+ int ii;
u8 *req_as_bytes = (u8 *) req;
/*
* Stuff request words via doorbell handshake,
* with ACK from IOC for each.
*/
- for (i = 0; !failcnt && i < reqBytes/4; i++) {
- u32 word = ((req_as_bytes[(i*4) + 0] << 0) |
- (req_as_bytes[(i*4) + 1] << 8) |
- (req_as_bytes[(i*4) + 2] << 16) |
- (req_as_bytes[(i*4) + 3] << 24));
+ for (ii = 0; !failcnt && ii < reqBytes/4; ii++) {
+ u32 word = ((req_as_bytes[(ii*4) + 0] << 0) |
+ (req_as_bytes[(ii*4) + 1] << 8) |
+ (req_as_bytes[(ii*4) + 2] << 16) |
+ (req_as_bytes[(ii*4) + 3] << 24));
CHIPREG_WRITE32(&ioc->chip->Doorbell, word);
- if ((t = WaitForDoorbellAck(ioc, 2)) < 0)
+ if ((t = WaitForDoorbellAck(ioc, 2, sleepFlag)) < 0)
failcnt++;
}
dmfprintk((KERN_INFO MYNAM ": Handshake request frame (@%p) header\n", req));
DBG_DUMP_REQUEST_FRAME_HDR(req)
- dhsprintk((KERN_INFO MYNAM ": %s: HandShake request post done, WaitCnt=%d%s\n",
+ dhsprintk((MYIOC_s_INFO_FMT "HandShake request post done, WaitCnt=%d%s\n",
ioc->name, t, failcnt ? " - MISSING DOORBELL ACK!" : ""));
/*
* Wait for completion of doorbell handshake reply from the IOC
*/
- if (!failcnt && (t = WaitForDoorbellReply(ioc, maxwait)) < 0)
+ if (!failcnt && (t = WaitForDoorbellReply(ioc, maxwait, sleepFlag)) < 0)
failcnt++;
/*
* Copy out the cached reply...
*/
- for(i=0; i < MIN(replyBytes/2,mptReply->MsgLength*2); i++)
- u16reply[i] = ioc->hs_reply[i];
+ for (ii=0; ii < MIN(replyBytes/2,mptReply->MsgLength*2); ii++)
+ u16reply[ii] = ioc->hs_reply[ii];
} else {
return -99;
}
* in it's IntStatus register.
* @ioc: Pointer to MPT_ADAPTER structure
* @howlong: How long to wait (in seconds)
+ * @sleepFlag: Specifies whether the process can sleep
*
* This routine waits (up to ~2 seconds max) for IOC doorbell
* handshake ACKnowledge.
* Returns a negative value on failure, else wait loop count.
*/
static int
-WaitForDoorbellAck(MPT_ADAPTER *ioc, int howlong)
+WaitForDoorbellAck(MPT_ADAPTER *ioc, int howlong, int sleepFlag)
{
int cntdn = HZ * howlong;
int count = 0;
u32 intstat;
- while (--cntdn) {
- intstat = CHIPREG_READ32(&ioc->chip->IntStatus);
- if (! (intstat & MPI_HIS_IOP_DOORBELL_STATUS))
- break;
- current->state = TASK_INTERRUPTIBLE;
- schedule_timeout(1);
- count++;
+ if (sleepFlag == CAN_SLEEP) {
+ while (--cntdn) {
+ intstat = CHIPREG_READ32(&ioc->chip->IntStatus);
+ if (! (intstat & MPI_HIS_IOP_DOORBELL_STATUS))
+ break;
+ set_current_state(TASK_INTERRUPTIBLE);
+ schedule_timeout(1);
+ count++;
+ }
+ } else {
+ cntdn *= 10; /* convert to msec */
+ while (--cntdn) {
+ intstat = CHIPREG_READ32(&ioc->chip->IntStatus);
+ if (! (intstat & MPI_HIS_IOP_DOORBELL_STATUS))
+ break;
+ mdelay (1);
+ count++;
+ }
+ count /= 10;
}
if (cntdn) {
- dhsprintk((KERN_INFO MYNAM ": %s: WaitForDoorbell ACK (cnt=%d)\n",
+ dhsprintk((MYIOC_s_INFO_FMT "WaitForDoorbell ACK (cnt=%d)\n",
ioc->name, count));
return count;
}
- printk(KERN_ERR MYNAM ": %s: ERROR - Doorbell ACK timeout(%d)!\n",
+ printk(MYIOC_s_ERR_FMT "Doorbell ACK timeout(%d)!\n",
ioc->name, (count+5)/HZ);
return -1;
}
* in it's IntStatus register.
* @ioc: Pointer to MPT_ADAPTER structure
* @howlong: How long to wait (in seconds)
+ * @sleepFlag: Specifies whether the process can sleep
*
* This routine waits (up to ~2 seconds max) for IOC doorbell interrupt.
*
* Returns a negative value on failure, else wait loop count.
*/
static int
-WaitForDoorbellInt(MPT_ADAPTER *ioc, int howlong)
+WaitForDoorbellInt(MPT_ADAPTER *ioc, int howlong, int sleepFlag)
{
int cntdn = HZ * howlong;
int count = 0;
u32 intstat;
- while (--cntdn) {
- intstat = CHIPREG_READ32(&ioc->chip->IntStatus);
- if (intstat & MPI_HIS_DOORBELL_INTERRUPT)
- break;
- current->state = TASK_INTERRUPTIBLE;
- schedule_timeout(1);
- count++;
+ if (sleepFlag == CAN_SLEEP) {
+ while (--cntdn) {
+ intstat = CHIPREG_READ32(&ioc->chip->IntStatus);
+ if (intstat & MPI_HIS_DOORBELL_INTERRUPT)
+ break;
+ set_current_state(TASK_INTERRUPTIBLE);
+ schedule_timeout(1);
+ count++;
+ }
+ } else {
+ cntdn *= 10; /* convert to msec */
+ while (--cntdn) {
+ intstat = CHIPREG_READ32(&ioc->chip->IntStatus);
+ if (intstat & MPI_HIS_DOORBELL_INTERRUPT)
+ break;
+ mdelay(1);
+ count++;
+ }
+ count /= 10;
}
if (cntdn) {
- dhsprintk((KERN_INFO MYNAM ": %s: WaitForDoorbell INT (cnt=%d)\n",
+ dhsprintk((MYIOC_s_INFO_FMT "WaitForDoorbell INT (cnt=%d)\n",
ioc->name, count));
return count;
}
- printk(KERN_ERR MYNAM ": %s: ERROR - Doorbell INT timeout(%d)!\n",
+ printk(MYIOC_s_ERR_FMT "Doorbell INT timeout(%d)!\n",
ioc->name, (count+5)/HZ);
return -1;
}
* WaitForDoorbellReply - Wait for and capture a IOC handshake reply.
* @ioc: Pointer to MPT_ADAPTER structure
* @howlong: How long to wait (in seconds)
+ * @sleepFlag: Specifies whether the process can sleep
*
* This routine polls the IOC for a handshake reply, 16 bits at a time.
* Reply is cached to IOC private area large enough to hold a maximum
* Returns a negative value on failure, else size of reply in WORDS.
*/
static int
-WaitForDoorbellReply(MPT_ADAPTER *ioc, int howlong)
+WaitForDoorbellReply(MPT_ADAPTER *ioc, int howlong, int sleepFlag)
{
int u16cnt = 0;
int failcnt = 0;
int t;
u16 *hs_reply = ioc->hs_reply;
- volatile MPIDefaultReply_t *mptReply = (MPIDefaultReply_t *) ioc->hs_reply;
+ volatile MPIDefaultReply_t *mptReply = (MPIDefaultReply_t *) ioc->hs_reply;
u16 hword;
hs_reply[0] = hs_reply[1] = hs_reply[7] = 0;
* Get first two u16's so we can look at IOC's intended reply MsgLength
*/
u16cnt=0;
- if ((t = WaitForDoorbellInt(ioc, howlong)) < 0) {
+ if ((t = WaitForDoorbellInt(ioc, howlong, sleepFlag)) < 0) {
failcnt++;
} else {
hs_reply[u16cnt++] = le16_to_cpu(CHIPREG_READ32(&ioc->chip->Doorbell) & 0x0000FFFF);
CHIPREG_WRITE32(&ioc->chip->IntStatus, 0);
- if ((t = WaitForDoorbellInt(ioc, 2)) < 0)
+ if ((t = WaitForDoorbellInt(ioc, 2, sleepFlag)) < 0)
failcnt++;
else {
hs_reply[u16cnt++] = le16_to_cpu(CHIPREG_READ32(&ioc->chip->Doorbell) & 0x0000FFFF);
}
}
- dhsprintk((KERN_INFO MYNAM ": %s: First handshake reply word=%08x%s\n",
+ dhsprintk((MYIOC_s_INFO_FMT "First handshake reply word=%08x%s\n",
ioc->name, le32_to_cpu(*(u32 *)hs_reply),
failcnt ? " - MISSING DOORBELL HANDSHAKE!" : ""));
* reply 16 bits at a time.
*/
for (u16cnt=2; !failcnt && u16cnt < (2 * mptReply->MsgLength); u16cnt++) {
- if ((t = WaitForDoorbellInt(ioc, 2)) < 0)
+ if ((t = WaitForDoorbellInt(ioc, 2, sleepFlag)) < 0)
failcnt++;
hword = le16_to_cpu(CHIPREG_READ32(&ioc->chip->Doorbell) & 0x0000FFFF);
/* don't overflow our IOC hs_reply[] buffer! */
CHIPREG_WRITE32(&ioc->chip->IntStatus, 0);
}
- if (!failcnt && (t = WaitForDoorbellInt(ioc, 2)) < 0)
+ if (!failcnt && (t = WaitForDoorbellInt(ioc, 2, sleepFlag)) < 0)
failcnt++;
CHIPREG_WRITE32(&ioc->chip->IntStatus, 0);
if (failcnt) {
- printk(KERN_ERR MYNAM ": %s: ERROR - Handshake reply failure!\n",
+ printk(MYIOC_s_ERR_FMT "Handshake reply failure!\n",
ioc->name);
return -failcnt;
}
}
#endif
- dmfprintk((KERN_INFO MYNAM ": %s: Got Handshake reply:\n", ioc->name));
+ dmfprintk((MYIOC_s_INFO_FMT "Got Handshake reply:\n", ioc->name));
DBG_DUMP_REPLY_FRAME(mptReply)
- dhsprintk((KERN_INFO MYNAM ": %s: WaitForDoorbell REPLY (sz=%d)\n",
+ dhsprintk((MYIOC_s_INFO_FMT "WaitForDoorbell REPLY (sz=%d)\n",
ioc->name, u16cnt/2));
return u16cnt/2;
}
* GetLanConfigPages - Fetch LANConfig pages.
* @ioc: Pointer to MPT_ADAPTER structure
*
- * Returns 0 for success, non-zero for failure.
+ * Return: 0 for success
+ * -ENOMEM if no memory available
+ * -EPERM if not allowed due to ISR context
+ * -EAGAIN if no msg frames currently available
+ * -EFAULT for non-successful reply or no reply (timeout)
+ */
+static int
+GetLanConfigPages(MPT_ADAPTER *ioc)
+{
+ ConfigPageHeader_t hdr;
+ CONFIGPARMS cfg;
+ LANPage0_t *ppage0_alloc;
+ dma_addr_t page0_dma;
+ LANPage1_t *ppage1_alloc;
+ dma_addr_t page1_dma;
+ int rc = 0;
+ int data_sz;
+ int copy_sz;
+
+ /* Get LAN Page 0 header */
+ hdr.PageVersion = 0;
+ hdr.PageLength = 0;
+ hdr.PageNumber = 0;
+ hdr.PageType = MPI_CONFIG_PAGETYPE_LAN;
+ cfg.hdr = &hdr;
+ cfg.physAddr = -1;
+ cfg.action = MPI_CONFIG_ACTION_PAGE_HEADER;
+ cfg.dir = 0;
+ cfg.pageAddr = 0;
+ cfg.timeout = 0;
+
+ if ((rc = mpt_config(ioc, &cfg)) != 0)
+ return rc;
+
+ if (hdr.PageLength > 0) {
+ data_sz = hdr.PageLength * 4;
+ ppage0_alloc = (LANPage0_t *) pci_alloc_consistent(ioc->pcidev, data_sz, &page0_dma);
+ rc = -ENOMEM;
+ if (ppage0_alloc) {
+ memset((u8 *)ppage0_alloc, 0, data_sz);
+ cfg.physAddr = page0_dma;
+ cfg.action = MPI_CONFIG_ACTION_PAGE_READ_CURRENT;
+
+ if ((rc = mpt_config(ioc, &cfg)) == 0) {
+ /* save the data */
+ copy_sz = MIN(sizeof(LANPage0_t), data_sz);
+ memcpy(&ioc->lan_cnfg_page0, ppage0_alloc, copy_sz);
+
+ }
+
+ pci_free_consistent(ioc->pcidev, data_sz, (u8 *) ppage0_alloc, page0_dma);
+
+ /* FIXME!
+ * Normalize endianness of structure data,
+ * by byte-swapping all > 1 byte fields!
+ */
+
+ }
+
+ if (rc)
+ return rc;
+ }
+
+ /* Get LAN Page 1 header */
+ hdr.PageVersion = 0;
+ hdr.PageLength = 0;
+ hdr.PageNumber = 1;
+ hdr.PageType = MPI_CONFIG_PAGETYPE_LAN;
+ cfg.hdr = &hdr;
+ cfg.physAddr = -1;
+ cfg.action = MPI_CONFIG_ACTION_PAGE_HEADER;
+ cfg.dir = 0;
+ cfg.pageAddr = 0;
+
+ if ((rc = mpt_config(ioc, &cfg)) != 0)
+ return rc;
+
+ if (hdr.PageLength == 0)
+ return 0;
+
+ data_sz = hdr.PageLength * 4;
+ rc = -ENOMEM;
+ ppage1_alloc = (LANPage1_t *) pci_alloc_consistent(ioc->pcidev, data_sz, &page1_dma);
+ if (ppage1_alloc) {
+ memset((u8 *)ppage1_alloc, 0, data_sz);
+ cfg.physAddr = page1_dma;
+ cfg.action = MPI_CONFIG_ACTION_PAGE_READ_CURRENT;
+
+ if ((rc = mpt_config(ioc, &cfg)) == 0) {
+ /* save the data */
+ copy_sz = MIN(sizeof(LANPage1_t), data_sz);
+ memcpy(&ioc->lan_cnfg_page1, ppage1_alloc, copy_sz);
+ }
+
+ pci_free_consistent(ioc->pcidev, data_sz, (u8 *) ppage1_alloc, page1_dma);
+
+ /* FIXME!
+ * Normalize endianness of structure data,
+ * by byte-swapping all > 1 byte fields!
+ */
+
+ }
+
+ return rc;
+}
+
+/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
+/*
+ * GetFcPortPage0 - Fetch FCPort config Page0.
+ * @ioc: Pointer to MPT_ADAPTER structure
+ * @portnum: IOC Port number
+ *
+ * Return: 0 for success
+ * -ENOMEM if no memory available
+ * -EPERM if not allowed due to ISR context
+ * -EAGAIN if no msg frames currently available
+ * -EFAULT for non-successful reply or no reply (timeout)
+ */
+static int
+GetFcPortPage0(MPT_ADAPTER *ioc, int portnum)
+{
+ ConfigPageHeader_t hdr;
+ CONFIGPARMS cfg;
+ FCPortPage0_t *ppage0_alloc;
+ FCPortPage0_t *pp0dest;
+ dma_addr_t page0_dma;
+ int data_sz;
+ int copy_sz;
+ int rc;
+
+ /* Get FCPort Page 0 header */
+ hdr.PageVersion = 0;
+ hdr.PageLength = 0;
+ hdr.PageNumber = 0;
+ hdr.PageType = MPI_CONFIG_PAGETYPE_FC_PORT;
+ cfg.hdr = &hdr;
+ cfg.physAddr = -1;
+ cfg.action = MPI_CONFIG_ACTION_PAGE_HEADER;
+ cfg.dir = 0;
+ cfg.pageAddr = portnum;
+ cfg.timeout = 0;
+
+ if ((rc = mpt_config(ioc, &cfg)) != 0)
+ return rc;
+
+ if (hdr.PageLength == 0)
+ return 0;
+
+ data_sz = hdr.PageLength * 4;
+ rc = -ENOMEM;
+ ppage0_alloc = (FCPortPage0_t *) pci_alloc_consistent(ioc->pcidev, data_sz, &page0_dma);
+ if (ppage0_alloc) {
+ memset((u8 *)ppage0_alloc, 0, data_sz);
+ cfg.physAddr = page0_dma;
+ cfg.action = MPI_CONFIG_ACTION_PAGE_READ_CURRENT;
+
+ if ((rc = mpt_config(ioc, &cfg)) == 0) {
+ /* save the data */
+ pp0dest = &ioc->fc_port_page0[portnum];
+ copy_sz = MIN(sizeof(FCPortPage0_t), data_sz);
+ memcpy(pp0dest, ppage0_alloc, copy_sz);
+
+ /*
+ * Normalize endianness of structure data,
+ * by byte-swapping all > 1 byte fields!
+ */
+ pp0dest->Flags = le32_to_cpu(pp0dest->Flags);
+ pp0dest->PortIdentifier = le32_to_cpu(pp0dest->PortIdentifier);
+ pp0dest->WWNN.Low = le32_to_cpu(pp0dest->WWNN.Low);
+ pp0dest->WWNN.High = le32_to_cpu(pp0dest->WWNN.High);
+ pp0dest->WWPN.Low = le32_to_cpu(pp0dest->WWPN.Low);
+ pp0dest->WWPN.High = le32_to_cpu(pp0dest->WWPN.High);
+ pp0dest->SupportedServiceClass = le32_to_cpu(pp0dest->SupportedServiceClass);
+ pp0dest->SupportedSpeeds = le32_to_cpu(pp0dest->SupportedSpeeds);
+ pp0dest->CurrentSpeed = le32_to_cpu(pp0dest->CurrentSpeed);
+ pp0dest->MaxFrameSize = le32_to_cpu(pp0dest->MaxFrameSize);
+ pp0dest->FabricWWNN.Low = le32_to_cpu(pp0dest->FabricWWNN.Low);
+ pp0dest->FabricWWNN.High = le32_to_cpu(pp0dest->FabricWWNN.High);
+ pp0dest->FabricWWPN.Low = le32_to_cpu(pp0dest->FabricWWPN.Low);
+ pp0dest->FabricWWPN.High = le32_to_cpu(pp0dest->FabricWWPN.High);
+ pp0dest->DiscoveredPortsCount = le32_to_cpu(pp0dest->DiscoveredPortsCount);
+ pp0dest->MaxInitiators = le32_to_cpu(pp0dest->MaxInitiators);
+
+ }
+
+ pci_free_consistent(ioc->pcidev, data_sz, (u8 *) ppage0_alloc, page0_dma);
+ }
+
+ return rc;
+}
+
+/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
+/*
+ * GetIoUnitPage2 - Retrieve BIOS version and boot order information.
+ * @ioc: Pointer to MPT_ADAPTER structure
+ *
+ * Returns: 0 for success
+ * -ENOMEM if no memory available
+ * -EPERM if not allowed due to ISR context
+ * -EAGAIN if no msg frames currently available
+ * -EFAULT for non-successful reply or no reply (timeout)
+ */
+static int
+GetIoUnitPage2(MPT_ADAPTER *ioc)
+{
+ ConfigPageHeader_t hdr;
+ CONFIGPARMS cfg;
+ IOUnitPage2_t *ppage_alloc;
+ dma_addr_t page_dma;
+ int data_sz;
+ int rc;
+
+ /* Get the page header */
+ hdr.PageVersion = 0;
+ hdr.PageLength = 0;
+ hdr.PageNumber = 2;
+ hdr.PageType = MPI_CONFIG_PAGETYPE_IO_UNIT;
+ cfg.hdr = &hdr;
+ cfg.physAddr = -1;
+ cfg.action = MPI_CONFIG_ACTION_PAGE_HEADER;
+ cfg.dir = 0;
+ cfg.pageAddr = 0;
+ cfg.timeout = 0;
+
+ if ((rc = mpt_config(ioc, &cfg)) != 0)
+ return rc;
+
+ if (hdr.PageLength == 0)
+ return 0;
+
+ /* Read the config page */
+ data_sz = hdr.PageLength * 4;
+ rc = -ENOMEM;
+ ppage_alloc = (IOUnitPage2_t *) pci_alloc_consistent(ioc->pcidev, data_sz, &page_dma);
+ if (ppage_alloc) {
+ memset((u8 *)ppage_alloc, 0, data_sz);
+ cfg.physAddr = page_dma;
+ cfg.action = MPI_CONFIG_ACTION_PAGE_READ_CURRENT;
+
+ /* If Good, save data */
+ if ((rc = mpt_config(ioc, &cfg)) == 0)
+ ioc->biosVersion = le32_to_cpu(ppage_alloc->BiosVersion);
+
+ pci_free_consistent(ioc->pcidev, data_sz, (u8 *) ppage_alloc, page_dma);
+ }
+
+ return rc;
+}
+
+/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
+/* mpt_GetScsiPortSettings - read SCSI Port Page 0 and 2
+ * @ioc: Pointer to a Adapter Strucutre
+ * @portnum: IOC port number
+ *
+ * Return: -EFAULT if read of config page header fails
+ * or if no nvram
+ * If read of SCSI Port Page 0 fails,
+ * NVRAM = MPT_HOST_NVRAM_INVALID (0xFFFFFFFF)
+ * Adapter settings: async, narrow
+ * Return 1
+ * If read of SCSI Port Page 2 fails,
+ * Adapter settings valid
+ * NVRAM = MPT_HOST_NVRAM_INVALID (0xFFFFFFFF)
+ * Return 1
+ * Else
+ * Both valid
+ * Return 0
+ * CHECK - what type of locking mechanisms should be used????
+ */
+static int
+mpt_GetScsiPortSettings(MPT_ADAPTER *ioc, int portnum)
+{
+ u8 *pbuf = NULL;
+ dma_addr_t buf_dma;
+ CONFIGPARMS cfg;
+ ConfigPageHeader_t header;
+ int ii;
+ int data, rc = 0;
+
+ /* Allocate memory
+ */
+ if (!ioc->spi_data.nvram) {
+ int sz;
+ u8 *mem;
+ sz = MPT_MAX_SCSI_DEVICES * sizeof(int);
+ mem = kmalloc(sz, GFP_ATOMIC);
+ if (mem == NULL)
+ return -EFAULT;
+
+ ioc->spi_data.nvram = (int *) mem;
+
+ dprintk((MYIOC_s_INFO_FMT "SCSI device NVRAM settings @ %p, sz=%d\n",
+ ioc->name, ioc->spi_data.nvram, sz));
+ }
+
+ /* Invalidate NVRAM information
+ */
+ for (ii=0; ii < MPT_MAX_SCSI_DEVICES; ii++) {
+ ioc->spi_data.nvram[ii] = MPT_HOST_NVRAM_INVALID;
+ }
+
+ /* Read SPP0 header, allocate memory, then read page.
+ */
+ header.PageVersion = 0;
+ header.PageLength = 0;
+ header.PageNumber = 0;
+ header.PageType = MPI_CONFIG_PAGETYPE_SCSI_PORT;
+ cfg.hdr = &header;
+ cfg.physAddr = -1;
+ cfg.pageAddr = portnum;
+ cfg.action = MPI_CONFIG_ACTION_PAGE_HEADER;
+ cfg.dir = 0;
+ cfg.timeout = 0; /* use default */
+ if (mpt_config(ioc, &cfg) != 0)
+ return -EFAULT;
+
+ if (header.PageLength > 0) {
+ pbuf = pci_alloc_consistent(ioc->pcidev, header.PageLength * 4, &buf_dma);
+ if (pbuf) {
+ cfg.action = MPI_CONFIG_ACTION_PAGE_READ_CURRENT;
+ cfg.physAddr = buf_dma;
+ if (mpt_config(ioc, &cfg) != 0) {
+ ioc->spi_data.maxBusWidth = MPT_NARROW;
+ ioc->spi_data.maxSyncOffset = 0;
+ ioc->spi_data.minSyncFactor = MPT_ASYNC;
+ ioc->spi_data.busType = MPT_HOST_BUS_UNKNOWN;
+ rc = 1;
+ } else {
+ /* Save the Port Page 0 data
+ */
+ SCSIPortPage0_t *pPP0 = (SCSIPortPage0_t *) pbuf;
+ pPP0->Capabilities = le32_to_cpu(pPP0->Capabilities);
+ pPP0->PhysicalInterface = le32_to_cpu(pPP0->PhysicalInterface);
+
+ ioc->spi_data.maxBusWidth = pPP0->Capabilities & MPI_SCSIPORTPAGE0_CAP_WIDE ? 1 : 0;
+ data = pPP0->Capabilities & MPI_SCSIPORTPAGE0_CAP_MAX_SYNC_OFFSET_MASK;
+ if (data) {
+ ioc->spi_data.maxSyncOffset = (u8) (data >> 16);
+ data = pPP0->Capabilities & MPI_SCSIPORTPAGE0_CAP_MIN_SYNC_PERIOD_MASK;
+ ioc->spi_data.minSyncFactor = (u8) (data >> 8);
+ } else {
+ ioc->spi_data.maxSyncOffset = 0;
+ ioc->spi_data.minSyncFactor = MPT_ASYNC;
+ }
+
+ ioc->spi_data.busType = pPP0->PhysicalInterface & MPI_SCSIPORTPAGE0_PHY_SIGNAL_TYPE_MASK;
+
+ /* Update the minSyncFactor based on bus type.
+ */
+ if ((ioc->spi_data.busType == MPI_SCSIPORTPAGE0_PHY_SIGNAL_HVD) ||
+ (ioc->spi_data.busType == MPI_SCSIPORTPAGE0_PHY_SIGNAL_SE)) {
+
+ if (ioc->spi_data.minSyncFactor < MPT_ULTRA)
+ ioc->spi_data.minSyncFactor = MPT_ULTRA;
+ }
+ }
+ if (pbuf) {
+ pci_free_consistent(ioc->pcidev, header.PageLength * 4, pbuf, buf_dma);
+ pbuf = NULL;
+ }
+ }
+ }
+
+ /* SCSI Port Page 2 - Read the header then the page.
+ */
+ header.PageVersion = 0;
+ header.PageLength = 0;
+ header.PageNumber = 2;
+ header.PageType = MPI_CONFIG_PAGETYPE_SCSI_PORT;
+ cfg.hdr = &header;
+ cfg.physAddr = -1;
+ cfg.pageAddr = portnum;
+ cfg.action = MPI_CONFIG_ACTION_PAGE_HEADER;
+ cfg.dir = 0;
+ if (mpt_config(ioc, &cfg) != 0)
+ return -EFAULT;
+
+ if (header.PageLength > 0) {
+ /* Allocate memory and read SCSI Port Page 2
+ */
+ pbuf = pci_alloc_consistent(ioc->pcidev, header.PageLength * 4, &buf_dma);
+ if (pbuf) {
+ cfg.action = MPI_CONFIG_ACTION_PAGE_READ_NVRAM;
+ cfg.physAddr = buf_dma;
+ if (mpt_config(ioc, &cfg) != 0) {
+ /* Nvram data is left with INVALID mark
+ */
+ rc = 1;
+ } else {
+ SCSIPortPage2_t *pPP2 = (SCSIPortPage2_t *) pbuf;
+ MpiDeviceInfo_t *pdevice = NULL;
+
+ /* Save the Port Page 2 data
+ * (reformat into a 32bit quantity)
+ */
+ for (ii=0; ii < MPT_MAX_SCSI_DEVICES; ii++) {
+ pdevice = &pPP2->DeviceSettings[ii];
+ data = (le16_to_cpu(pdevice->DeviceFlags) << 16) |
+ (pdevice->SyncFactor << 8) | pdevice->Timeout;
+ ioc->spi_data.nvram[ii] = data;
+ }
+ }
+
+ pci_free_consistent(ioc->pcidev, header.PageLength * 4, pbuf, buf_dma);
+ pbuf = NULL;
+ }
+ }
+
+ /* Update Adapter limits with those from NVRAM
+ * Comment: Don't need to do this. Target performance
+ * parameters will never exceed the adapters limits.
+ */
+
+ return rc;
+}
+
+/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
+/* mpt_readScsiDevicePageHeaders - save version and length of SDP1
+ * @ioc: Pointer to a Adapter Strucutre
+ * @portnum: IOC port number
+ *
+ * Return: -EFAULT if read of config page header fails
+ * or 0 if success.
+ */
+static int
+mpt_readScsiDevicePageHeaders(MPT_ADAPTER *ioc, int portnum)
+{
+ CONFIGPARMS cfg;
+ ConfigPageHeader_t header;
+
+ /* Read the SCSI Device Page 1 header
+ */
+ header.PageVersion = 0;
+ header.PageLength = 0;
+ header.PageNumber = 1;
+ header.PageType = MPI_CONFIG_PAGETYPE_SCSI_DEVICE;
+ cfg.hdr = &header;
+ cfg.physAddr = -1;
+ cfg.pageAddr = portnum;
+ cfg.action = MPI_CONFIG_ACTION_PAGE_HEADER;
+ cfg.dir = 0;
+ cfg.timeout = 0;
+ if (mpt_config(ioc, &cfg) != 0)
+ return -EFAULT;
+
+ ioc->spi_data.sdp1version = cfg.hdr->PageVersion;
+ ioc->spi_data.sdp1length = cfg.hdr->PageLength;
+
+ header.PageVersion = 0;
+ header.PageLength = 0;
+ header.PageNumber = 0;
+ header.PageType = MPI_CONFIG_PAGETYPE_SCSI_DEVICE;
+ if (mpt_config(ioc, &cfg) != 0)
+ return -EFAULT;
+
+ ioc->spi_data.sdp0version = cfg.hdr->PageVersion;
+ ioc->spi_data.sdp0length = cfg.hdr->PageLength;
+
+ return 0;
+}
+
+/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
+/**
+ * mpt_findImVolumes - Identify IDs of hidden disks and RAID Volumes
+ * @ioc: Pointer to a Adapter Strucutre
+ * @portnum: IOC port number
+ *
+ * Return:
+ * 0 on success
+ * -EFAULT if read of config page header fails or data pointer not NULL
+ * -ENOMEM if pci_alloc failed
*/
static int
-GetLanConfigPages(MPT_ADAPTER *ioc)
+mpt_findImVolumes(MPT_ADAPTER *ioc)
{
- Config_t config_req;
- ConfigReply_t config_reply;
- LANPage0_t *page0;
- dma_addr_t page0_dma;
- LANPage1_t *page1;
- dma_addr_t page1_dma;
- int i;
- int req_sz;
- int reply_sz;
- int data_sz;
+ IOCPage2_t *pIoc2 = NULL;
+ IOCPage3_t *pIoc3 = NULL;
+ ConfigPageIoc2RaidVol_t *pIocRv = NULL;
+ u8 *mem;
+ dma_addr_t ioc2_dma;
+ dma_addr_t ioc3_dma;
+ CONFIGPARMS cfg;
+ ConfigPageHeader_t header;
+ int jj;
+ int rc = 0;
+ int iocpage2sz;
+ int iocpage3sz = 0;
+ u8 nVols, nPhys;
+ u8 vid, vbus, vioc;
+
+ if (ioc->spi_data.pIocPg3)
+ return -EFAULT;
+
+ /* Read IOCP2 header then the page.
+ */
+ header.PageVersion = 0;
+ header.PageLength = 0;
+ header.PageNumber = 2;
+ header.PageType = MPI_CONFIG_PAGETYPE_IOC;
+ cfg.hdr = &header;
+ cfg.physAddr = -1;
+ cfg.pageAddr = 0;
+ cfg.action = MPI_CONFIG_ACTION_PAGE_HEADER;
+ cfg.dir = 0;
+ cfg.timeout = 0;
+ if (mpt_config(ioc, &cfg) != 0)
+ return -EFAULT;
+
+ if (header.PageLength == 0)
+ return -EFAULT;
+
+ iocpage2sz = header.PageLength * 4;
+ pIoc2 = pci_alloc_consistent(ioc->pcidev, iocpage2sz, &ioc2_dma);
+ if (!pIoc2)
+ return -ENOMEM;
-/* LANPage0 */
- /* Immediate destination (reply area)... */
- reply_sz = sizeof(config_reply);
- memset(&config_reply, 0, reply_sz);
-
- /* Ultimate destination... */
- page0 = &ioc->lan_cnfg_page0;
- data_sz = sizeof(*page0);
- memset(page0, 0, data_sz);
-
- /* Request area (config_req on the stack right now!) */
- req_sz = sizeof(config_req);
- memset(&config_req, 0, req_sz);
- config_req.Function = MPI_FUNCTION_CONFIG;
- config_req.Action = MPI_CONFIG_ACTION_PAGE_READ_CURRENT;
- /* config_req.Header.PageVersion = 0; */
- /* config_req.Header.PageLength = 0; */
- config_req.Header.PageNumber = 0;
- config_req.Header.PageType = MPI_CONFIG_PAGETYPE_LAN;
- /* config_req.PageAddress = 0; */
- config_req.PageBufferSGE.u.Simple.FlagsLength = cpu_to_le32(
- ((MPI_SGE_FLAGS_LAST_ELEMENT |
- MPI_SGE_FLAGS_END_OF_BUFFER |
- MPI_SGE_FLAGS_END_OF_LIST |
- MPI_SGE_FLAGS_SIMPLE_ELEMENT |
- MPI_SGE_FLAGS_SYSTEM_ADDRESS |
- MPI_SGE_FLAGS_32_BIT_ADDRESSING |
- MPI_SGE_FLAGS_32_BIT_CONTEXT) << MPI_SGE_FLAGS_SHIFT) |
- (u32)data_sz
- );
- page0_dma = pci_map_single(ioc->pcidev, page0, data_sz, PCI_DMA_FROMDEVICE);
- config_req.PageBufferSGE.u.Simple.u.Address32 = cpu_to_le32(page0_dma);
-
- dprintk((KERN_INFO MYNAM ": %s: Sending Config request LAN_PAGE_0\n",
- ioc->name));
+ cfg.action = MPI_CONFIG_ACTION_PAGE_READ_CURRENT;
+ cfg.physAddr = ioc2_dma;
+ if (mpt_config(ioc, &cfg) != 0)
+ goto done_and_free;
- i = HandShakeReqAndReply(ioc, req_sz, (u32*)&config_req,
- reply_sz, (u16*)&config_reply, 3);
- pci_unmap_single(ioc->pcidev, page0_dma, data_sz, PCI_DMA_FROMDEVICE);
- if (i != 0)
- return i;
+ /* Identify RAID Volume Id's */
+ nVols = pIoc2->NumActiveVolumes;
+ if ( nVols == 0) {
+ /* No RAID Volumes. Done.
+ */
+ } else {
+ /* At least 1 RAID Volume
+ */
+ pIocRv = pIoc2->RaidVolume;
+ ioc->spi_data.isRaid = 0;
+ for (jj = 0; jj < nVols; jj++, pIocRv++) {
+ vid = pIocRv->VolumeID;
+ vbus = pIocRv->VolumeBus;
+ vioc = pIocRv->VolumeIOC;
+
+ /* find the match
+ */
+ if (vbus == 0) {
+ ioc->spi_data.isRaid |= (1 << vid);
+ } else {
+ /* Error! Always bus 0
+ */
+ }
+ }
+ }
- /* Now byte swap the necessary LANPage0 fields */
-
-/* LANPage1 */
- /* Immediate destination (reply area)... */
- reply_sz = sizeof(config_reply);
- memset(&config_reply, 0, reply_sz);
-
- /* Ultimate destination... */
- page1 = &ioc->lan_cnfg_page1;
- data_sz = sizeof(*page1);
- memset(page1, 0, data_sz);
-
- /* Request area (config_req on the stack right now!) */
- req_sz = sizeof(config_req);
- memset(&config_req, 0, req_sz);
- config_req.Function = MPI_FUNCTION_CONFIG;
- config_req.Action = MPI_CONFIG_ACTION_PAGE_READ_CURRENT;
- /* config_req.Header.PageVersion = 0; */
- /* config_req.Header.PageLength = 0; */
- config_req.Header.PageNumber = 1;
- config_req.Header.PageType = MPI_CONFIG_PAGETYPE_LAN;
- /* config_req.PageAddress = 0; */
- config_req.PageBufferSGE.u.Simple.FlagsLength = cpu_to_le32(
- ((MPI_SGE_FLAGS_LAST_ELEMENT |
- MPI_SGE_FLAGS_END_OF_BUFFER |
- MPI_SGE_FLAGS_END_OF_LIST |
- MPI_SGE_FLAGS_SIMPLE_ELEMENT |
- MPI_SGE_FLAGS_SYSTEM_ADDRESS |
- MPI_SGE_FLAGS_32_BIT_ADDRESSING |
- MPI_SGE_FLAGS_32_BIT_CONTEXT) << MPI_SGE_FLAGS_SHIFT) |
- (u32)data_sz
- );
- page1_dma = pci_map_single(ioc->pcidev, page1, data_sz, PCI_DMA_FROMDEVICE);
- config_req.PageBufferSGE.u.Simple.u.Address32 = cpu_to_le32(page1_dma);
-
- dprintk((KERN_INFO MYNAM ": %s: Sending Config request LAN_PAGE_1\n",
- ioc->name));
+ /* Identify Hidden Physical Disk Id's */
+ nPhys = pIoc2->NumActivePhysDisks;
+ if (nPhys == 0) {
+ /* No physical disks. Done.
+ */
+ } else {
+ /* There is at least one physical disk.
+ * Read and save IOC Page 3
+ */
+ header.PageVersion = 0;
+ header.PageLength = 0;
+ header.PageNumber = 3;
+ header.PageType = MPI_CONFIG_PAGETYPE_IOC;
+ cfg.hdr = &header;
+ cfg.physAddr = -1;
+ cfg.pageAddr = 0;
+ cfg.action = MPI_CONFIG_ACTION_PAGE_HEADER;
+ cfg.dir = 0;
+ cfg.timeout = 0;
+ if (mpt_config(ioc, &cfg) != 0)
+ goto done_and_free;
+
+ if (header.PageLength == 0)
+ goto done_and_free;
+
+ /* Read Header good, alloc memory
+ */
+ iocpage3sz = header.PageLength * 4;
+ pIoc3 = pci_alloc_consistent(ioc->pcidev, iocpage3sz, &ioc3_dma);
+ if (!pIoc3)
+ goto done_and_free;
- i = HandShakeReqAndReply(ioc, req_sz, (u32*)&config_req,
- reply_sz, (u16*)&config_reply, 3);
- pci_unmap_single(ioc->pcidev, page1_dma, data_sz, PCI_DMA_FROMDEVICE);
- if (i != 0)
- return i;
+ /* Read the Page and save the data
+ * into malloc'd memory.
+ */
+ cfg.physAddr = ioc3_dma;
+ cfg.action = MPI_CONFIG_ACTION_PAGE_READ_CURRENT;
+ if (mpt_config(ioc, &cfg) == 0) {
+ mem = kmalloc(iocpage3sz, GFP_ATOMIC);
+ if (mem) {
+ memcpy(mem, (u8 *)pIoc3, iocpage3sz);
+ ioc->spi_data.pIocPg3 = (IOCPage3_t *) mem;
+ }
+ }
+ }
- /* Now byte swap the necessary LANPage1 fields */
+done_and_free:
+ if (pIoc2) {
+ pci_free_consistent(ioc->pcidev, iocpage2sz, pIoc2, ioc2_dma);
+ pIoc2 = NULL;
+ }
- return 0;
+ if (pIoc3) {
+ pci_free_consistent(ioc->pcidev, iocpage3sz, pIoc3, ioc3_dma);
+ pIoc3 = NULL;
+ }
+
+ return rc;
}
+
/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
-/**
+/*
* SendEventNotification - Send EventNotification (on or off) request
* to MPT adapter.
* @ioc: Pointer to MPT_ADAPTER structure
evnp = (EventNotification_t *) mpt_get_msg_frame(mpt_base_index, ioc->id);
if (evnp == NULL) {
- dprintk((KERN_WARNING MYNAM ": %s: WARNING - Unable to allocate a event request frame!\n",
+ dprintk((MYIOC_s_WARN_FMT "Unable to allocate event request frame!\n",
ioc->name));
return 0;
}
memset(evnp, 0, sizeof(*evnp));
- dprintk((KERN_INFO MYNAM ": %s: Sending EventNotification(%d)\n", ioc->name, EvSwitch));
+ dprintk((MYIOC_s_INFO_FMT "Sending EventNotification(%d)\n", ioc->name, EvSwitch));
evnp->Function = MPI_FUNCTION_EVENT_NOTIFICATION;
evnp->ChainOffset = 0;
EventAck_t *pAck;
if ((pAck = (EventAck_t *) mpt_get_msg_frame(mpt_base_index, ioc->id)) == NULL) {
- printk(KERN_WARNING MYNAM ": %s: WARNING - Unable to allocate event ACK request frame!\n",
+ printk(MYIOC_s_WARN_FMT "Unable to allocate event ACK request frame!\n",
ioc->name);
return -1;
}
memset(pAck, 0, sizeof(*pAck));
- dprintk((KERN_INFO MYNAM ": %s: Sending EventAck\n", ioc->name));
+ dprintk((MYIOC_s_INFO_FMT "Sending EventAck\n", ioc->name));
pAck->Function = MPI_FUNCTION_EVENT_ACK;
pAck->ChainOffset = 0;
return 0;
}
-#ifdef CONFIG_PROC_FS /* { */
+/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
+/**
+ * mpt_config - Generic function to issue config message
+ * @ioc - Pointer to an adapter structure
+ * @cfg - Pointer to a configuration structure. Struct contains
+ * action, page address, direction, physical address
+ * and pointer to a configuration page header
+ * Page header is updated.
+ *
+ * Returns 0 for success
+ * -EPERM if not allowed due to ISR context
+ * -EAGAIN if no msg frames currently available
+ * -EFAULT for non-successful reply or no reply (timeout)
+ */
+int
+mpt_config(MPT_ADAPTER *ioc, CONFIGPARMS *pCfg)
+{
+ Config_t *pReq;
+ MPT_FRAME_HDR *mf;
+ unsigned long flags;
+ int ii, rc;
+ int flagsLength;
+ int in_isr;
+
+ /* (Bugzilla:fibrebugs, #513)
+ * Bug fix (part 1)! 20010905 -sralston
+ * Prevent calling wait_event() (below), if caller happens
+ * to be in ISR context, because that is fatal!
+ */
+ in_isr = in_interrupt();
+ if (in_isr) {
+ dprintk((MYIOC_s_WARN_FMT "Config request not allowed in ISR context!\n",
+ ioc->name));
+ return -EPERM;
+ }
+
+ /* Get and Populate a free Frame
+ */
+ if ((mf = mpt_get_msg_frame(mpt_base_index, ioc->id)) == NULL) {
+ dprintk((MYIOC_s_WARN_FMT "mpt_config: no msg frames!\n",
+ ioc->name));
+ return -EAGAIN;
+ }
+ pReq = (Config_t *)mf;
+ pReq->Action = pCfg->action;
+ pReq->Reserved = 0;
+ pReq->ChainOffset = 0;
+ pReq->Function = MPI_FUNCTION_CONFIG;
+ pReq->Reserved1[0] = 0;
+ pReq->Reserved1[1] = 0;
+ pReq->Reserved1[2] = 0;
+ pReq->MsgFlags = 0;
+ for (ii=0; ii < 8; ii++)
+ pReq->Reserved2[ii] = 0;
+
+ pReq->Header.PageVersion = pCfg->hdr->PageVersion;
+ pReq->Header.PageLength = pCfg->hdr->PageLength;
+ pReq->Header.PageNumber = pCfg->hdr->PageNumber;
+ pReq->Header.PageType = (pCfg->hdr->PageType & MPI_CONFIG_PAGETYPE_MASK);
+ pReq->PageAddress = cpu_to_le32(pCfg->pageAddr);
+
+ /* Add a SGE to the config request.
+ */
+ if (pCfg->dir)
+ flagsLength = MPT_SGE_FLAGS_SSIMPLE_WRITE;
+ else
+ flagsLength = MPT_SGE_FLAGS_SSIMPLE_READ;
+
+ flagsLength |= pCfg->hdr->PageLength * 4;
+
+ mpt_add_sge((char *)&pReq->PageBufferSGE, flagsLength, pCfg->physAddr);
+
+ dprintk((MYIOC_s_INFO_FMT "Sending Config request type %d, page %d and action %d\n",
+ ioc->name, pReq->Header.PageType, pReq->Header.PageNumber, pReq->Action));
+
+ /* Append pCfg pointer to end of mf
+ */
+ *((void **) (((u8 *) mf) + (ioc->req_sz - sizeof(void *)))) = (void *) pCfg;
+
+ /* Initalize the timer
+ */
+ init_timer(&pCfg->timer);
+ pCfg->timer.data = (unsigned long) ioc;
+ pCfg->timer.function = mpt_timer_expired;
+ pCfg->wait_done = 0;
+
+ /* Set the timer; ensure 10 second minimum */
+ if (pCfg->timeout < 10)
+ pCfg->timer.expires = jiffies + HZ*10;
+ else
+ pCfg->timer.expires = jiffies + HZ*pCfg->timeout;
+
+ /* Add to end of Q, set timer and then issue this command */
+ spin_lock_irqsave(&ioc->FreeQlock, flags);
+ Q_ADD_TAIL(&ioc->configQ.head, &pCfg->linkage, Q_ITEM);
+ spin_unlock_irqrestore(&ioc->FreeQlock, flags);
+
+ add_timer(&pCfg->timer);
+ mpt_put_msg_frame(mpt_base_index, ioc->id, mf);
+ wait_event(mpt_waitq, pCfg->wait_done);
+
+ /* mf has been freed - do not access */
+
+ rc = pCfg->status;
+
+ return rc;
+}
+
/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
/*
- * procfs (%MPT_PROCFS_MPTBASEDIR/...) support stuff...
+ * mpt_timer_expired - Call back for timer process.
+ * Used only internal config functionality.
+ * @data: Pointer to MPT_SCSI_HOST recast as an unsigned long
+ */
+static void
+mpt_timer_expired(unsigned long data)
+{
+ MPT_ADAPTER *ioc = (MPT_ADAPTER *) data;
+
+ dprintk((MYIOC_s_WARN_FMT "mpt_timer_expired! \n", ioc->name));
+
+ /* Perform a FW reload */
+ if (mpt_HardResetHandler(ioc, NO_SLEEP) < 0)
+ printk(MYIOC_s_WARN_FMT "Firmware Reload FAILED!\n", ioc->name);
+
+ /* No more processing.
+ * Hard reset clean-up will wake up
+ * process and free all resources.
+ */
+ dprintk((MYIOC_s_WARN_FMT "mpt_timer_expired complete!\n", ioc->name));
+
+ return;
+}
+
+/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
+/*
+ * mpt_ioc_reset - Base cleanup for hard reset
+ * @ioc: Pointer to the adapter structure
+ * @reset_phase: Indicates pre- or post-reset functionality
+ *
+ * Remark: Free's resources with internally generated commands.
*/
+static int
+mpt_ioc_reset(MPT_ADAPTER *ioc, int reset_phase)
+{
+ CONFIGPARMS *pCfg;
+ unsigned long flags;
+
+ dprintk((KERN_WARNING MYNAM
+ ": IOC %s_reset routed to MPT base driver!\n",
+ reset_phase==MPT_IOC_PRE_RESET ? "pre" : "post"));
+
+ if (reset_phase == MPT_IOC_PRE_RESET) {
+ /* If the internal config Q is not empty -
+ * delete timer. MF resources will be freed when
+ * the FIFO's are primed.
+ */
+ spin_lock_irqsave(&ioc->FreeQlock, flags);
+ if (! Q_IS_EMPTY(&ioc->configQ)){
+ pCfg = (CONFIGPARMS *)ioc->configQ.head;
+ do {
+ del_timer(&pCfg->timer);
+ pCfg = (CONFIGPARMS *) (pCfg->linkage.forw);
+ } while (pCfg != (CONFIGPARMS *)&ioc->configQ);
+ }
+ spin_unlock_irqrestore(&ioc->FreeQlock, flags);
+
+ } else {
+ CONFIGPARMS *pNext;
+
+ /* Search the configQ for internal commands.
+ * Flush the Q, and wake up all suspended threads.
+ */
+#if 1
+ spin_lock_irqsave(&ioc->FreeQlock, flags);
+ if (! Q_IS_EMPTY(&ioc->configQ)){
+ pCfg = (CONFIGPARMS *)ioc->configQ.head;
+ do {
+ pNext = (CONFIGPARMS *) pCfg->linkage.forw;
+
+ Q_DEL_ITEM(&pCfg->linkage);
+
+ pCfg->status = MPT_CONFIG_ERROR;
+ pCfg->wait_done = 1;
+ wake_up(&mpt_waitq);
-#define PROC_MPT_READ_RETURN(page,start,off,count,eof,len) \
-{ \
- len -= off; \
- if (len < count) { \
- *eof = 1; \
- if (len <= 0) \
- return 0; \
- } else \
- len = count; \
- *start = page + off; \
- return len; \
+ pCfg = pNext;
+ } while (pCfg != (CONFIGPARMS *)&ioc->configQ);
+ }
+ spin_unlock_irqrestore(&ioc->FreeQlock, flags);
+#else
+ while (1) {
+ spin_lock_irqsave(&ioc->FreeQlock, flags);
+ if (! Q_IS_EMPTY(&ioc->configQ)){
+ spin_unlock_irqrestore(&ioc->FreeQlock, flags);
+ break;
+ }
+ pCfg = (CONFIGPARMS *)ioc->configQ.head;
+
+ Q_DEL_ITEM(&pCfg->linkage);
+ spin_unlock_irqrestore(&ioc->FreeQlock, flags);
+
+ pCfg->status = MPT_CONFIG_ERROR;
+ pCfg->wait_done = 1;
+ wake_up(&mpt_waitq);
+ }
+#endif
+ }
+
+ return 1; /* currently means nothing really */
}
+
+#ifdef CONFIG_PROC_FS /* { */
+/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
+/*
+ * procfs (%MPT_PROCFS_MPTBASEDIR/...) support stuff...
+ */
/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
/*
* procmpt_create - Create %MPT_PROCFS_MPTBASEDIR entries.
static int
procmpt_create(void)
{
- MPT_ADAPTER *ioc;
- struct proc_dir_entry *ent;
- int errcnt = 0;
+ MPT_ADAPTER *ioc;
+ struct proc_dir_entry *ent;
+ int ii;
/*
- * BEWARE: If/when MPT_PROCFS_MPTBASEDIR changes from "mpt"
- * (single level) to multi level (e.g. "driver/message/fusion")
- * something here needs to change. -sralston
+ * BEWARE: If/when MPT_PROCFS_MPTBASEDIR changes from "mpt"
+ * (single level) to multi level (e.g. "driver/message/fusion")
+ * something here needs to change. -sralston
*/
- procmpt_root_dir = CREATE_PROCDIR_ENTRY(MPT_PROCFS_MPTBASEDIR, NULL);
- if (procmpt_root_dir == NULL)
+ mpt_proc_root_dir = proc_mkdir(MPT_PROCFS_MPTBASEDIR, NULL);
+ if (mpt_proc_root_dir == NULL)
return -ENOTDIR;
- if ((ioc = mpt_adapter_find_first()) != NULL) {
- ent = create_proc_read_entry(MPT_PROCFS_SUMMARY_NODE, 0, NULL, procmpt_read_summary, NULL);
- if (ent == NULL) {
- printk(KERN_WARNING MYNAM ": WARNING - Could not create %s entry!\n",
- MPT_PROCFS_SUMMARY_PATHNAME);
- errcnt++;
+ for (ii=0; ii < MPT_PROC_ENTRIES; ii++) {
+ ent = create_proc_entry(mpt_proc_list[ii].name,
+ S_IFREG|S_IRUGO, mpt_proc_root_dir);
+ if (!ent) {
+ printk(KERN_WARNING MYNAM
+ ": WARNING - Could not create /proc/mpt/%s entry\n",
+ mpt_proc_list[ii].name);
+ continue;
}
+ ent->read_proc = mpt_proc_list[ii].f;
+ ent->data = NULL;
}
+ ioc = mpt_adapter_find_first();
while (ioc != NULL) {
- char pname[32];
- int namelen;
+ struct proc_dir_entry *dent;
/*
* Create "/proc/mpt/iocN" subdirectory entry for each MPT adapter.
*/
- namelen = sprintf(pname, MPT_PROCFS_MPTBASEDIR "/%s", ioc->name);
- if ((ent = CREATE_PROCDIR_ENTRY(pname, NULL)) != NULL) {
+ if ((dent = proc_mkdir(ioc->name, mpt_proc_root_dir)) != NULL) {
/*
- * And populate it with: "summary" and "dbg" file entries.
+ * And populate it with mpt_ioc_proc_list[] entries.
*/
- (void) sprintf(pname+namelen, "/summary");
- ent = create_proc_read_entry(pname, 0, NULL, procmpt_read_summary, ioc);
- if (ent == NULL) {
- errcnt++;
- printk(KERN_WARNING MYNAM ": %s: WARNING - Could not create /proc/%s entry!\n",
- ioc->name, pname);
- }
-//#ifdef MPT_DEBUG
- /* DEBUG aid! */
- (void) sprintf(pname+namelen, "/dbg");
- ent = create_proc_read_entry(pname, 0, NULL, procmpt_read_dbg, ioc);
- if (ent == NULL) {
- errcnt++;
- printk(KERN_WARNING MYNAM ": %s: WARNING - Could not create /proc/%s entry!\n",
- ioc->name, pname);
+ for (ii=0; ii < MPT_IOC_PROC_ENTRIES; ii++) {
+ ent = create_proc_entry(mpt_ioc_proc_list[ii].name,
+ S_IFREG|S_IRUGO, dent);
+ if (!ent) {
+ printk(KERN_WARNING MYNAM
+ ": WARNING - Could not create /proc/mpt/%s/%s entry!\n",
+ ioc->name,
+ mpt_ioc_proc_list[ii].name);
+ continue;
+ }
+ ent->read_proc = mpt_ioc_proc_list[ii].f;
+ ent->data = ioc;
}
-//#endif
} else {
- errcnt++;
- printk(KERN_WARNING MYNAM ": %s: WARNING - Could not create /proc/%s entry!\n",
- ioc->name, pname);
-
+ printk(MYIOC_s_WARN_FMT "Could not create /proc/mpt/%s subdir entry!\n",
+ ioc->name, mpt_ioc_proc_list[ii].name);
}
-
ioc = mpt_adapter_find_next(ioc);
}
- if (errcnt) {
-// remove_proc_entry("mpt", 0);
- return -ENOTDIR;
- }
-
return 0;
}
static int
procmpt_destroy(void)
{
- MPT_ADAPTER *ioc;
+ MPT_ADAPTER *ioc;
+ int ii;
- if (!procmpt_root_dir)
+ if (!mpt_proc_root_dir)
return 0;
/*
- * BEWARE: If/when MPT_PROCFS_MPTBASEDIR changes from "mpt"
- * (single level) to multi level (e.g. "driver/message/fusion")
- * something here needs to change. -sralston
+ * BEWARE: If/when MPT_PROCFS_MPTBASEDIR changes from "mpt"
+ * (single level) to multi level (e.g. "driver/message/fusion")
+ * something here needs to change. -sralston
*/
ioc = mpt_adapter_find_first();
- if (ioc != NULL) {
- remove_proc_entry(MPT_PROCFS_SUMMARY_NODE, 0);
- }
-
while (ioc != NULL) {
char pname[32];
int namelen;
+
+ namelen = sprintf(pname, MPT_PROCFS_MPTBASEDIR "/%s", ioc->name);
+
/*
* Tear down each "/proc/mpt/iocN" subdirectory.
*/
- namelen = sprintf(pname, MPT_PROCFS_MPTBASEDIR "/%s", ioc->name);
- (void) sprintf(pname+namelen, "/summary");
- remove_proc_entry(pname, 0);
-//#ifdef MPT_DEBUG
- (void) sprintf(pname+namelen, "/dbg");
- remove_proc_entry(pname, 0);
-//#endif
- (void) sprintf(pname, MPT_PROCFS_MPTBASEDIR "/%s", ioc->name);
- remove_proc_entry(pname, 0);
+ for (ii=0; ii < MPT_IOC_PROC_ENTRIES; ii++) {
+ (void) sprintf(pname+namelen, "/%s", mpt_ioc_proc_list[ii].name);
+ remove_proc_entry(pname, NULL);
+ }
+
+ remove_proc_entry(ioc->name, mpt_proc_root_dir);
ioc = mpt_adapter_find_next(ioc);
}
- if (atomic_read((atomic_t *)&procmpt_root_dir->count) == 0) {
- remove_proc_entry(MPT_PROCFS_MPTBASEDIR, 0);
- procmpt_root_dir = NULL;
+ for (ii=0; ii < MPT_PROC_ENTRIES; ii++)
+ remove_proc_entry(mpt_proc_list[ii].name, mpt_proc_root_dir);
+
+ if (atomic_read((atomic_t *)&mpt_proc_root_dir->count) == 0) {
+ remove_proc_entry(MPT_PROCFS_MPTBASEDIR, NULL);
+ mpt_proc_root_dir = NULL;
return 0;
}
}
/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
-/**
- * procmpt_read_summary - Handle read request from /proc/mpt/summary
+/*
+ * procmpt_summary_read - Handle read request from /proc/mpt/summary
* or from /proc/mpt/iocN/summary.
- * @page: Pointer to area to write information
+ * @buf: Pointer to area to write information
* @start: Pointer to start pointer
- * @off: Offset to start writing
- * @count:
+ * @offset: Offset to start writing
+ * @request:
* @eof: Pointer to EOF integer
- * @data: Pointer
+ * @data: Pointer
*
- * Returns numbers of characters written to process performing the read.
+ * Returns number of characters written to process performing the read.
*/
static int
-procmpt_read_summary(char *page, char **start, off_t off, int count, int *eof, void *data)
+procmpt_summary_read(char *buf, char **start, off_t offset, int request, int *eof, void *data)
{
MPT_ADAPTER *ioc;
- char *out = page;
+ char *out = buf;
int len;
if (data == NULL)
else
ioc = data;
-// Too verbose!
-// out += sprintf(out, "Attached Fusion MPT I/O Controllers:%s\n", ioc ? "" : " none");
-
while (ioc) {
int more = 0;
-// Too verbose!
-// mpt_print_ioc_facts(ioc, out, &more, 0);
mpt_print_ioc_summary(ioc, out, &more, 0, 1);
out += more;
- if ((out-page) >= count) {
+ if ((out-buf) >= request) {
break;
}
if (data == NULL)
ioc = mpt_adapter_find_next(ioc);
else
- ioc = NULL; /* force exit for iocN */
+ ioc = NULL; /* force exit for iocN */
}
- len = out - page;
+ len = out - buf;
- PROC_MPT_READ_RETURN(page,start,off,count,eof,len);
+ MPT_PROC_READ_RETURN(buf,start,offset,request,eof,len);
}
-// debug aid!
/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
-/**
- * procmpt_read_dbg - Handle read request from /proc/mpt/iocN/dbg.
- * @page: Pointer to area to write information
+/*
+ * procmpt_version_read - Handle read request from /proc/mpt/version.
+ * @buf: Pointer to area to write information
* @start: Pointer to start pointer
- * @off: Offset to start writing
- * @count:
+ * @offset: Offset to start writing
+ * @request:
* @eof: Pointer to EOF integer
- * @data: Pointer
+ * @data: Pointer
*
- * Returns numbers of characters written to process performing the read.
+ * Returns number of characters written to process performing the read.
*/
static int
-procmpt_read_dbg(char *page, char **start, off_t off, int count, int *eof, void *data)
+procmpt_version_read(char *buf, char **start, off_t offset, int request, int *eof, void *data)
{
- MPT_ADAPTER *ioc;
- char *out = page;
- int len;
+ int ii;
+ int scsi, lan, ctl, targ, dmp;
+ char *drvname;
+ int len;
+
+ len = sprintf(buf, "%s-%s\n", "mptlinux", MPT_LINUX_VERSION_COMMON);
+ len += sprintf(buf+len, " Fusion MPT base driver\n");
+
+ scsi = lan = ctl = targ = dmp = 0;
+ for (ii=MPT_MAX_PROTOCOL_DRIVERS-1; ii; ii--) {
+ drvname = NULL;
+ if (MptCallbacks[ii]) {
+ switch (MptDriverClass[ii]) {
+ case MPTSCSIH_DRIVER:
+ if (!scsi++) drvname = "SCSI host";
+ break;
+ case MPTLAN_DRIVER:
+ if (!lan++) drvname = "LAN";
+ break;
+ case MPTSTM_DRIVER:
+ if (!targ++) drvname = "SCSI target";
+ break;
+ case MPTCTL_DRIVER:
+ if (!ctl++) drvname = "ioctl";
+ break;
+ case MPTDMP_DRIVER:
+ if (!dmp++) drvname = "DMP";
+ break;
+ }
+
+ if (drvname)
+ len += sprintf(buf+len, " Fusion MPT %s driver\n", drvname);
+ /*
+ * Handle isense special case, because it
+ * doesn't do a formal mpt_register call.
+ */
+ if (isense_idx == ii)
+ len += sprintf(buf+len, " Fusion MPT isense driver\n");
+ } else
+ break;
+ }
- ioc = data;
+ MPT_PROC_READ_RETURN(buf,start,offset,request,eof,len);
+}
- while (ioc) {
- int more = 0;
+/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
+/*
+ * procmpt_iocinfo_read - Handle read request from /proc/mpt/iocN/info.
+ * @buf: Pointer to area to write information
+ * @start: Pointer to start pointer
+ * @offset: Offset to start writing
+ * @request:
+ * @eof: Pointer to EOF integer
+ * @data: Pointer
+ *
+ * Returns number of characters written to process performing the read.
+ */
+static int
+procmpt_iocinfo_read(char *buf, char **start, off_t offset, int request, int *eof, void *data)
+{
+ MPT_ADAPTER *ioc = data;
+ int len;
+ char expVer[32];
+ int sz;
+ int p;
+
+ mpt_get_fw_exp_ver(expVer, ioc);
- mpt_print_ioc_facts(ioc, out, &more, 0);
+ len = sprintf(buf, "%s:", ioc->name);
+ if (ioc->facts.Flags & MPI_IOCFACTS_FLAGS_FW_DOWNLOAD_BOOT)
+ len += sprintf(buf+len, " (f/w download boot flag set)");
+// if (ioc->facts.IOCExceptions & MPI_IOCFACTS_EXCEPT_CONFIG_CHECKSUM_FAIL)
+// len += sprintf(buf+len, " CONFIG_CHECKSUM_FAIL!");
+
+ len += sprintf(buf+len, "\n ProductID = 0x%04x (%s)\n",
+ ioc->facts.ProductID,
+ ioc->prod_name);
+ len += sprintf(buf+len, " FWVersion = 0x%08x%s", ioc->facts.FWVersion.Word, expVer);
+ if (ioc->facts.FWImageSize)
+ len += sprintf(buf+len, " (fw_size=%d)", ioc->facts.FWImageSize);
+ len += sprintf(buf+len, "\n MsgVersion = 0x%04x\n", ioc->facts.MsgVersion);
+ len += sprintf(buf+len, " FirstWhoInit = 0x%02x\n", ioc->FirstWhoInit);
+ len += sprintf(buf+len, " EventState = 0x%02x\n", ioc->facts.EventState);
+
+ len += sprintf(buf+len, " CurrentHostMfaHighAddr = 0x%08x\n",
+ ioc->facts.CurrentHostMfaHighAddr);
+ len += sprintf(buf+len, " CurrentSenseBufferHighAddr = 0x%08x\n",
+ ioc->facts.CurrentSenseBufferHighAddr);
- out += more;
- if ((out-page) >= count) {
- break;
+ len += sprintf(buf+len, " MaxChainDepth = 0x%02x frames\n", ioc->facts.MaxChainDepth);
+ len += sprintf(buf+len, " MinBlockSize = 0x%02x bytes\n", 4*ioc->facts.BlockSize);
+
+ len += sprintf(buf+len, " RequestFrames @ 0x%p (Dma @ 0x%p)\n",
+ (void *)ioc->req_alloc, (void *)(ulong)ioc->req_alloc_dma);
+ /*
+ * Rounding UP to nearest 4-kB boundary here...
+ */
+ sz = (ioc->req_sz * ioc->req_depth) + 128;
+ sz = ((sz + 0x1000UL - 1UL) / 0x1000) * 0x1000;
+ len += sprintf(buf+len, " {CurReqSz=%d} x {CurReqDepth=%d} = %d bytes ^= 0x%x\n",
+ ioc->req_sz, ioc->req_depth, ioc->req_sz*ioc->req_depth, sz);
+ len += sprintf(buf+len, " {MaxReqSz=%d} {MaxReqDepth=%d}\n",
+ 4*ioc->facts.RequestFrameSize,
+ ioc->facts.GlobalCredits);
+
+ len += sprintf(buf+len, " ReplyFrames @ 0x%p (Dma @ 0x%p)\n",
+ (void *)ioc->reply_alloc, (void *)(ulong)ioc->reply_alloc_dma);
+ sz = (ioc->reply_sz * ioc->reply_depth) + 128;
+ len += sprintf(buf+len, " {CurRepSz=%d} x {CurRepDepth=%d} = %d bytes ^= 0x%x\n",
+ ioc->reply_sz, ioc->reply_depth, ioc->reply_sz*ioc->reply_depth, sz);
+ len += sprintf(buf+len, " {MaxRepSz=%d} {MaxRepDepth=%d}\n",
+ ioc->facts.CurReplyFrameSize,
+ ioc->facts.ReplyQueueDepth);
+
+ len += sprintf(buf+len, " MaxDevices = %d\n",
+ (ioc->facts.MaxDevices==0) ? 255 : ioc->facts.MaxDevices);
+ len += sprintf(buf+len, " MaxBuses = %d\n", ioc->facts.MaxBuses);
+
+ /* per-port info */
+ for (p=0; p < ioc->facts.NumberOfPorts; p++) {
+ len += sprintf(buf+len, " PortNumber = %d (of %d)\n",
+ p+1,
+ ioc->facts.NumberOfPorts);
+ if ((int)ioc->chip_type <= (int)FC929) {
+ if (ioc->pfacts[p].ProtocolFlags & MPI_PORTFACTS_PROTOCOL_LAN) {
+ u8 *a = (u8*)&ioc->lan_cnfg_page1.HardwareAddressLow;
+ len += sprintf(buf+len, " LanAddr = %02X:%02X:%02X:%02X:%02X:%02X\n",
+ a[5], a[4], a[3], a[2], a[1], a[0]);
+ }
+ len += sprintf(buf+len, " WWN = %08X%08X:%08X%08X\n",
+ ioc->fc_port_page0[p].WWNN.High,
+ ioc->fc_port_page0[p].WWNN.Low,
+ ioc->fc_port_page0[p].WWPN.High,
+ ioc->fc_port_page0[p].WWPN.Low);
}
- ioc = NULL;
}
- len = out - page;
- PROC_MPT_READ_RETURN(page,start,off,count,eof,len);
+ MPT_PROC_READ_RETURN(buf,start,offset,request,eof,len);
}
+
#endif /* CONFIG_PROC_FS } */
/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
static void
mpt_get_fw_exp_ver(char *buf, MPT_ADAPTER *ioc)
{
- if ((ioc->facts.FWVersion & 0xF000) == 0xE000)
+ buf[0] ='\0';
+ if ((ioc->facts.FWVersion.Word >> 24) == 0x0E) {
sprintf(buf, " (Exp %02d%02d)",
- (ioc->facts.FWVersion & 0x0F00) >> 8, /* Month */
- ioc->facts.FWVersion & 0x001F); /* Day */
- else
- buf[0] ='\0';
+ (ioc->facts.FWVersion.Word >> 16) & 0x00FF, /* Month */
+ (ioc->facts.FWVersion.Word >> 8) & 0x1F); /* Day */
- /* insider hack! */
- if (ioc->facts.FWVersion & 0x0080) {
- strcat(buf, " [MDBG]");
+ /* insider hack! */
+ if ((ioc->facts.FWVersion.Word >> 8) & 0x80)
+ strcat(buf, " [MDBG]");
}
}
* @len: Offset at which to start writing in buffer
* @showlan: Display LAN stuff?
*
- * This routine writes (english readable) ASCII text, which represents
- * a summary of IOC information, to a buffer.
+ * This routine writes (english readable) ASCII text, which represents
+ * a summary of IOC information, to a buffer.
*/
void
mpt_print_ioc_summary(MPT_ADAPTER *ioc, char *buffer, int *size, int len, int showlan)
/*
* Shorter summary of attached ioc's...
*/
- y = sprintf(buffer+len, "%s: %s, %s%04xh%s, Ports=%d, MaxQ=%d",
+ y = sprintf(buffer+len, "%s: %s, %s%08xh%s, Ports=%d, MaxQ=%d",
ioc->name,
ioc->prod_name,
MPT_FW_REV_MAGIC_ID_STRING, /* "FwRev=" or somesuch */
- ioc->facts.FWVersion,
+ ioc->facts.FWVersion.Word,
expVer,
ioc->facts.NumberOfPorts,
ioc->req_depth);
a[5], a[4], a[3], a[2], a[1], a[0]);
}
- if (ioc->pci_irq < 100)
- y += sprintf(buffer+len+y, ", IRQ=%d", ioc->pci_irq);
+#ifndef __sparc__
+ y += sprintf(buffer+len+y, ", IRQ=%d", ioc->pci_irq);
+#else
+ y += sprintf(buffer+len+y, ", IRQ=%s", __irq_itoa(ioc->pci_irq));
+#endif
if (!ioc->active)
y += sprintf(buffer+len+y, " (disabled)");
*size = y;
}
+/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
+/*
+ * Reset Handling
+ */
/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
/**
- * mpt_print_ioc_facts - Write ASCII summary of IOC facts to a buffer.
+ * mpt_HardResetHandler - Generic reset handler, issue SCSI Task
+ * Management call based on input arg values. If TaskMgmt fails,
+ * return associated SCSI request.
* @ioc: Pointer to MPT_ADAPTER structure
- * @buffer: Pointer to buffer where IOC facts should be written
- * @size: Pointer to number of bytes we wrote (set by this routine)
- * @len: Offset at which to start writing in buffer
+ * @sleepFlag: Indicates if sleep or schedule must be called.
+ *
+ * Remark: _HardResetHandler can be invoked from an interrupt thread (timer)
+ * or a non-interrupt thread. In the former, must not call schedule().
+ *
+ * Remark: A return of -1 is a FATAL error case, as it means a
+ * FW reload/initialization failed.
*
- * This routine writes (english readable) ASCII text, which represents
- * a summary of the IOC facts, to a buffer.
+ * Returns 0 for SUCCESS or -1 if FAILED.
*/
-void
-mpt_print_ioc_facts(MPT_ADAPTER *ioc, char *buffer, int *size, int len)
+int
+mpt_HardResetHandler(MPT_ADAPTER *ioc, int sleepFlag)
{
- char expVer[32];
- char iocName[16];
- int sz;
- int y;
- int p;
-
- mpt_get_fw_exp_ver(expVer, ioc);
+ int rc;
+ unsigned long flags;
- strcpy(iocName, ioc->name);
- y = sprintf(buffer+len, "%s:\n", iocName);
+ dprintk((MYIOC_s_INFO_FMT "HardResetHandler Entered!\n", ioc->name));
+#ifdef MFCNT
+ printk(MYIOC_s_INFO_FMT "HardResetHandler Entered!\n", ioc->name);
+ printk("MF count 0x%x !\n", ioc->mfcnt);
+#endif
- y += sprintf(buffer+len+y, " ProductID = 0x%04x\n", ioc->facts.ProductID);
- for (p=0; p < ioc->facts.NumberOfPorts; p++) {
- y += sprintf(buffer+len+y, " PortNumber = %d (of %d)\n",
- p+1,
- ioc->facts.NumberOfPorts);
- if (ioc->pfacts[p].ProtocolFlags & MPI_PORTFACTS_PROTOCOL_LAN) {
- u8 *a = (u8*)&ioc->lan_cnfg_page1.HardwareAddressLow;
- y += sprintf(buffer+len+y, " LanAddr = 0x%02x:%02x:%02x:%02x:%02x:%02x\n",
- a[5], a[4], a[3], a[2], a[1], a[0]);
- }
- }
- y += sprintf(buffer+len+y, " FWVersion = 0x%04x%s\n", ioc->facts.FWVersion, expVer);
- y += sprintf(buffer+len+y, " MsgVersion = 0x%04x\n", ioc->facts.MsgVersion);
- y += sprintf(buffer+len+y, " FirstWhoInit = 0x%02x\n", ioc->FirstWhoInit);
- y += sprintf(buffer+len+y, " EventState = 0x%02x\n", ioc->facts.EventState);
- y += sprintf(buffer+len+y, " CurrentHostMfaHighAddr = 0x%08x\n",
- ioc->facts.CurrentHostMfaHighAddr);
- y += sprintf(buffer+len+y, " CurrentSenseBufferHighAddr = 0x%08x\n",
- ioc->facts.CurrentSenseBufferHighAddr);
- y += sprintf(buffer+len+y, " MaxChainDepth = 0x%02x frames\n", ioc->facts.MaxChainDepth);
- y += sprintf(buffer+len+y, " MinBlockSize = 0x%02x bytes\n", 4*ioc->facts.BlockSize);
+ /* Reset the adapter. Prevent more than 1 call to
+ * mpt_do_ioc_recovery at any instant in time.
+ */
+ spin_lock_irqsave(&ioc->diagLock, flags);
+ if ((ioc->diagPending) || (ioc->alt_ioc && ioc->alt_ioc->diagPending)){
+ spin_unlock_irqrestore(&ioc->diagLock, flags);
+ return 0;
+ } else {
+ ioc->diagPending = 1;
+ }
+ spin_unlock_irqrestore(&ioc->diagLock, flags);
- y += sprintf(buffer+len+y, " RequestFrames @ 0x%p (Dma @ 0x%08x)\n",
- ioc->req_alloc, ioc->req_alloc_dma);
- /*
- * Rounding UP to nearest 4-kB boundary here...
+ /* FIXME: If do_ioc_recovery fails, repeat....
*/
- sz = (ioc->req_sz * ioc->req_depth) + 128;
- sz = ((sz + 0x1000UL - 1UL) / 0x1000) * 0x1000;
- y += sprintf(buffer+len+y, " {CurReqSz=%d} x {CurReqDepth=%d} = %d bytes ^= 0x%x\n",
- ioc->req_sz, ioc->req_depth, ioc->req_sz*ioc->req_depth, sz);
- y += sprintf(buffer+len+y, " {MaxReqSz=%d} {MaxReqDepth=%d}\n",
- 4*ioc->facts.RequestFrameSize,
- ioc->facts.GlobalCredits);
- y += sprintf(buffer+len+y, " ReplyFrames @ 0x%p (Dma @ 0x%08x)\n",
- ioc->reply_alloc, ioc->reply_alloc_dma);
- sz = (ioc->reply_sz * ioc->reply_depth) + 128;
- y += sprintf(buffer+len+y, " {CurRepSz=%d} x {CurRepDepth=%d} = %d bytes ^= 0x%x\n",
- ioc->reply_sz, ioc->reply_depth, ioc->reply_sz*ioc->reply_depth, sz);
- y += sprintf(buffer+len+y, " {MaxRepSz=%d} {MaxRepDepth=%d}\n",
- ioc->facts.CurReplyFrameSize,
- ioc->facts.ReplyQueueDepth);
+ if ((rc = mpt_do_ioc_recovery(ioc, MPT_HOSTEVENT_IOC_RECOVER, sleepFlag)) != 0) {
+ printk(KERN_WARNING MYNAM ": WARNING - (%d) Cannot recover %s\n",
+ rc, ioc->name);
+ }
- *size = y;
+ spin_lock_irqsave(&ioc->diagLock, flags);
+ ioc->diagPending = 0;
+ if (ioc->alt_ioc)
+ ioc->alt_ioc->diagPending = 0;
+ spin_unlock_irqrestore(&ioc->diagLock, flags);
+
+ dprintk((MYIOC_s_INFO_FMT "HardResetHandler rc = %d!\n", ioc->name, rc));
+
+ return rc;
}
/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
ds = "External Bus Reset";
break;
case MPI_EVENT_RESCAN:
- ds = "Bus Rescan Event";
+ ds = "Bus Rescan Event";
/* Ok, do we need to do anything here? As far as
I can tell, this is when a new device gets added
to the loop. */
else
ds = "Events(OFF) Change";
break;
+ case MPI_EVENT_INTEGRATED_RAID:
+ ds = "Integrated Raid";
+ break;
/*
* MPT base "custom" events may be added here...
*/
}
/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
-/**
+/*
* ProcessEventNotification - Route a received EventNotificationReply to
* all currently regeistered event handlers.
* @ioc: Pointer to MPT_ADAPTER structure
u16 evDataLen;
u32 evData0 = 0;
// u32 evCtx;
- int i;
+ int ii;
int r = 0;
int handlers = 0;
char *evStr;
}
evStr = EventDescriptionStr(event, evData0);
- dprintk((KERN_INFO MYNAM ": %s: MPT event (%s=%02Xh) detected!\n",
+ dprintk((MYIOC_s_INFO_FMT "MPT event (%s=%02Xh) detected!\n",
ioc->name,
evStr,
event));
#if defined(MPT_DEBUG) || defined(MPT_DEBUG_EVENTS)
printk(KERN_INFO MYNAM ": Event data:\n" KERN_INFO);
- for (i = 0; i < evDataLen; i++)
- printk(" %08x", le32_to_cpu(pEventReply->Data[i]));
+ for (ii = 0; ii < evDataLen; ii++)
+ printk(" %08x", le32_to_cpu(pEventReply->Data[ii]));
printk("\n");
#endif
case MPI_EVENT_LINK_STATUS_CHANGE: /* 07 */
case MPI_EVENT_LOOP_STATE_CHANGE: /* 08 */
case MPI_EVENT_LOGOUT: /* 09 */
+ case MPI_EVENT_INTEGRATED_RAID: /* 0B */
+ case MPI_EVENT_SCSI_DEVICE_STATUS_CHANGE: /* 0C */
default:
break;
case MPI_EVENT_EVENT_CHANGE: /* 0A */
break;
}
+ /*
+ * Should this event be logged? Events are written sequentially.
+ * When buffer is full, start again at the top.
+ */
+ if (ioc->events && (ioc->eventTypes & ( 1 << event))) {
+ int idx;
+
+ idx = ioc->eventContext % ioc->eventLogSize;
+
+ ioc->events[idx].event = event;
+ ioc->events[idx].eventContext = ioc->eventContext;
+
+ for (ii = 0; ii < 2; ii++) {
+ if (ii < evDataLen)
+ ioc->events[idx].data[ii] = le32_to_cpu(pEventReply->Data[ii]);
+ else
+ ioc->events[idx].data[ii] = 0;
+ }
+
+ ioc->eventContext++;
+ }
+
+
/*
* Call each currently registered protocol event handler.
*/
- for (i=MPT_MAX_PROTOCOL_DRIVERS-1; i; i--) {
- if (MptEvHandlers[i]) {
- dprintk((KERN_INFO MYNAM ": %s: Routing Event to event handler #%d\n",
- ioc->name, i));
- r += (*(MptEvHandlers[i]))(ioc, pEventReply);
+ for (ii=MPT_MAX_PROTOCOL_DRIVERS-1; ii; ii--) {
+ if (MptEvHandlers[ii]) {
+ dprintk((MYIOC_s_INFO_FMT "Routing Event to event handler #%d\n",
+ ioc->name, ii));
+ r += (*(MptEvHandlers[ii]))(ioc, pEventReply);
handlers++;
}
}
* If needed, send (a single) EventAck.
*/
if (pEventReply->AckRequired == MPI_EVENT_NOTIFICATION_ACK_REQUIRED) {
- if ((i = SendEventAck(ioc, pEventReply)) != 0) {
+ if ((ii = SendEventAck(ioc, pEventReply)) != 0) {
+ printk(MYIOC_s_WARN_FMT "SendEventAck returned %d\n",
+ ioc->name, ii);
}
}
switch(log_info) {
/* FCP Initiator */
- case MPI_IOCLOGINFO_FC_INIT_ERROR_OUT_OF_ORDER_FRAME:
+ case MPI_IOCLOGINFO_FC_INIT_ERROR_OUT_OF_ORDER_FRAME:
desc = "Received an out of order frame - unsupported";
break;
case MPI_IOCLOGINFO_FC_INIT_ERROR_BAD_START_OF_FRAME:
desc = "Not sent because login to remote node not validated";
break;
case MPI_IOCLOGINFO_FC_TARGET_FROM_OUTBOUND:
- desc = "Cleared from the outbound after a logout";
+ desc = "Cleared from the outbound queue after a logout";
break;
case MPI_IOCLOGINFO_FC_TARGET_WAITING_FOR_DATA_IN:
desc = "Cleared waiting for data after a logout";
break;
}
- printk(KERN_INFO MYNAM ": %s: LogInfo(0x%08x): SubCl={%s}",
+ printk(MYIOC_s_INFO_FMT "LogInfo(0x%08x): SubCl={%s}",
ioc->name, log_info, subcl_str[subcl]);
if (SubCl == MPI_IOCLOGINFO_FC_INVALID_FIELD_BYTE_OFFSET)
printk(", byte_offset=%d\n", log_info & MPI_IOCLOGINFO_FC_INVALID_FIELD_MAX_OFFSET);
mpt_sp_log_info(MPT_ADAPTER *ioc, u32 log_info)
{
/* FIXME! */
- printk(KERN_INFO MYNAM ": %s: LogInfo(0x%08x)\n", ioc->name, log_info);
+ printk(MYIOC_s_INFO_FMT "LogInfo(0x%08x)\n", ioc->name, log_info);
}
/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
* Specialized driver registration routine for the isense driver.
*/
int
-mpt_register_ascqops_strings(/*ASCQ_Table_t*/void *ascqTable, int ascqtbl_sz, const char **opsTable)
+mpt_register_ascqops_strings(void *ascqTable, int ascqtbl_sz, const char **opsTable)
{
int r = 0;
mpt_ASCQ_TableSz = ascqtbl_sz;
mpt_ScsiOpcodesPtr = opsTable;
printk(KERN_INFO MYNAM ": English readable SCSI-3 strings enabled:-)\n");
+ isense_idx = last_drv_idx;
r = 1;
}
MOD_INC_USE_COUNT;
mpt_ASCQ_TableSz = 0;
mpt_ScsiOpcodesPtr = NULL;
printk(KERN_INFO MYNAM ": English readable SCSI-3 strings disabled)-:\n");
+ isense_idx = -1;
MOD_DEC_USE_COUNT;
}
/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
+EXPORT_SYMBOL(mpt_adapters);
+EXPORT_SYMBOL(mpt_proc_root_dir);
+EXPORT_SYMBOL(DmpService);
EXPORT_SYMBOL(mpt_register);
EXPORT_SYMBOL(mpt_deregister);
EXPORT_SYMBOL(mpt_event_register);
EXPORT_SYMBOL(mpt_get_msg_frame);
EXPORT_SYMBOL(mpt_put_msg_frame);
EXPORT_SYMBOL(mpt_free_msg_frame);
+EXPORT_SYMBOL(mpt_add_sge);
+EXPORT_SYMBOL(mpt_add_chain);
EXPORT_SYMBOL(mpt_send_handshake_request);
+EXPORT_SYMBOL(mpt_handshake_req_reply_wait);
EXPORT_SYMBOL(mpt_adapter_find_first);
EXPORT_SYMBOL(mpt_adapter_find_next);
EXPORT_SYMBOL(mpt_verify_adapter);
+EXPORT_SYMBOL(mpt_GetIocState);
EXPORT_SYMBOL(mpt_print_ioc_summary);
EXPORT_SYMBOL(mpt_lan_index);
EXPORT_SYMBOL(mpt_stm_index);
+EXPORT_SYMBOL(mpt_HardResetHandler);
+EXPORT_SYMBOL(mpt_config);
+EXPORT_SYMBOL(mpt_alloc_fw_memory);
+EXPORT_SYMBOL(mpt_free_fw_memory);
EXPORT_SYMBOL(mpt_register_ascqops_strings);
EXPORT_SYMBOL(mpt_deregister_ascqops_strings);
EXPORT_SYMBOL(mpt_ScsiOpcodesPtr);
/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
-/**
+/*
* fusion_init - Fusion MPT base driver initialization routine.
*
* Returns 0 for success, non-zero for failure.
*/
-int __init fusion_init(void)
+int __init
+fusion_init(void)
{
int i;
MptResetHandlers[i] = NULL;
}
+ DmpService = NULL;
+
/* NEW! 20010120 -sralston
* Register ourselves (mptbase) in order to facilitate
* EventNotification handling.
*/
mpt_base_index = mpt_register(mpt_base_reply, MPTBASE_DRIVER);
+ /* Register for hard reset handling callbacks.
+ */
+ if (mpt_reset_register(mpt_base_index, mpt_ioc_reset) == 0) {
+ dprintk((KERN_INFO MYNAM ": Register for IOC reset notification\n"));
+ } else {
+ /* FIXME! */
+ }
+
if ((i = mpt_pci_scan()) < 0)
return i;
}
/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
-/**
+/*
* fusion_exit - Perform driver unload cleanup.
*
* This routine frees all resources associated with each MPT adapter
* and removes all %MPT_PROCFS_MPTBASEDIR entries.
*/
-static void fusion_exit(void)
+static void
+fusion_exit(void)
{
MPT_ADAPTER *this;
* Moved this *above* removal of all MptAdapters!
*/
#ifdef CONFIG_PROC_FS
- procmpt_destroy();
+ (void) procmpt_destroy();
#endif
while (! Q_IS_EMPTY(&MptAdapters)) {
Q_DEL_ITEM(this);
mpt_adapter_dispose(this);
}
+
+ mpt_reset_deregister(mpt_base_index);
}
/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
* Credits:
* (see mptbase.c)
*
- * Copyright (c) 1999-2001 LSI Logic Corporation
+ * Copyright (c) 1999-2002 LSI Logic Corporation
* Originally By: Steven J. Ralston
- * (mailto:Steve.Ralston@lsil.com)
+ * (mailto:sjralston1@netscape.net)
+ * (mailto:Pam.Delaney@lsil.com)
*
- * $Id: mptbase.h,v 1.46.2.2.2.2 2001/09/18 03:22:29 sralston Exp $
+ * $Id: mptbase.h,v 1.123 2002/06/20 13:28:16 pdelaney Exp $
*/
/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
/*
/*{-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
#include "linux_compat.h" /* linux-2.2.x (vs. -2.4.x) tweaks */
+#include "scsi3.h" /* SCSI defines */
#include "lsi/mpi_type.h"
#include "lsi/mpi.h" /* Fusion MPI(nterface) basic defs */
#include "lsi/mpi_cnfg.h" /* IOC configuration support */
#include "lsi/mpi_init.h" /* SCSI Host (initiator) protocol support */
#include "lsi/mpi_lan.h" /* LAN over FC protocol support */
+#include "lsi/mpi_raid.h" /* Integrated Mirroring support */
#include "lsi/mpi_fc.h" /* Fibre Channel (lowlevel) support */
#include "lsi/mpi_targ.h" /* SCSI/FCP Target protcol support */
#endif
#ifndef COPYRIGHT
-#define COPYRIGHT "Copyright (c) 1999-2001 " MODULEAUTHOR
+#define COPYRIGHT "Copyright (c) 1999-2002 " MODULEAUTHOR
#endif
-#define MPT_LINUX_VERSION_COMMON "1.02.02"
-#define MPT_LINUX_PACKAGE_NAME "@(#)mptlinux-1.02.02"
+#define MPT_LINUX_VERSION_COMMON "2.01.06"
+#define MPT_LINUX_PACKAGE_NAME "@(#)mptlinux-2.01.06"
#define WHAT_MAGIC_STRING "@" "(" "#" ")"
#define show_mptmod_ver(s,ver) \
* Fusion MPT(linux) driver configurable stuff...
*/
#define MPT_MAX_ADAPTERS 16
-#define MPT_MAX_PROTOCOL_DRIVERS 8
+#define MPT_MAX_PROTOCOL_DRIVERS 16
+#define MPT_MAX_BUS 1
#define MPT_MAX_FC_DEVICES 255
+#define MPT_MAX_SCSI_DEVICES 16
+#define MPT_LAST_LUN 31
+#define MPT_SENSE_BUFFER_ALLOC 64
+ /* allow for 256 max sense alloc, but only 255 max request */
+#if MPT_SENSE_BUFFER_ALLOC >= 256
+# undef MPT_SENSE_BUFFER_ALLOC
+# define MPT_SENSE_BUFFER_ALLOC 256
+# define MPT_SENSE_BUFFER_SIZE 255
+#else
+# define MPT_SENSE_BUFFER_SIZE MPT_SENSE_BUFFER_ALLOC
+#endif
-#define MPT_MISCDEV_BASENAME "mptctl"
-#define MPT_MISCDEV_PATHNAME "/dev/" MPT_MISCDEV_BASENAME
+#define MPT_NAME_LENGTH 32
#define MPT_PROCFS_MPTBASEDIR "mpt"
/* chg it to "driver/fusion" ? */
-#define MPT_PROCFS_SUMMARY_NODE MPT_PROCFS_MPTBASEDIR "/summary"
-#define MPT_PROCFS_SUMMARY_PATHNAME "/proc/" MPT_PROCFS_SUMMARY_NODE
-#define MPT_FW_REV_MAGIC_ID_STRING "FwRev="
+#define MPT_PROCFS_SUMMARY_ALL_NODE MPT_PROCFS_MPTBASEDIR "/summary"
+#define MPT_PROCFS_SUMMARY_ALL_PATHNAME "/proc/" MPT_PROCFS_SUMMARY_ALL_NODE
+#define MPT_FW_REV_MAGIC_ID_STRING "FwRev="
-#ifdef __KERNEL__ /* { */
#define MPT_MAX_REQ_DEPTH 1023
-#define MPT_REQ_DEPTH 256
+#define MPT_DEFAULT_REQ_DEPTH 256
#define MPT_MIN_REQ_DEPTH 128
#define MPT_MAX_REPLY_DEPTH MPT_MAX_REQ_DEPTH
-#define MPT_REPLY_DEPTH 128
+#define MPT_DEFAULT_REPLY_DEPTH 128
#define MPT_MIN_REPLY_DEPTH 8
#define MPT_MAX_REPLIES_PER_ISR 32
#define MPT_MAX_FRAME_SIZE 128
-#define MPT_REQ_SIZE 128
-#define MPT_REPLY_SIZE 128
+#define MPT_DEFAULT_FRAME_SIZE 128
-#define MPT_SG_BUCKETS_PER_HUNK 1
+#define MPT_SG_REQ_128_SCALE 1
+#define MPT_SG_REQ_96_SCALE 2
+#define MPT_SG_REQ_64_SCALE 4
-#ifdef MODULE
-#define MPT_REQ_DEPTH_RANGE_STR __MODULE_STRING(MPT_MIN_REQ_DEPTH) "-" __MODULE_STRING(MPT_MAX_REQ_DEPTH)
-#define MPT_REPLY_DEPTH_RANGE_STR __MODULE_STRING(MPT_MIN_REPLY_DEPTH) "-" __MODULE_STRING(MPT_MAX_REPLY_DEPTH)
-#define MPT_REPLY_SIZE_RANGE_STR __MODULE_STRING(MPT_MIN_REPLY_SIZE) "-" __MODULE_STRING(MPT_MAX_FRAME_SIZE)
-#endif
+#define CAN_SLEEP 1
+#define NO_SLEEP 0
+
+/*
+ * SCSI transfer rate defines.
+ */
+#define MPT_ULTRA320 0x08
+#define MPT_ULTRA160 0x09
+#define MPT_ULTRA2 0x0A
+#define MPT_ULTRA 0x0C
+#define MPT_FAST 0x19
+#define MPT_SCSI 0x32
+#define MPT_ASYNC 0xFF
+
+#define MPT_NARROW 0
+#define MPT_WIDE 1
+
+#ifdef __KERNEL__ /* { */
+/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
+
+#include <linux/proc_fs.h>
+
+/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
+/*
+ * Attempt semi-consistent error & warning msgs across
+ * MPT drivers. NOTE: Users of these macro defs must
+ * themselves define their own MYNAM.
+ */
+#define MYIOC_s_INFO_FMT KERN_INFO MYNAM ": %s: "
+#define MYIOC_s_NOTE_FMT KERN_NOTICE MYNAM ": %s: "
+#define MYIOC_s_WARN_FMT KERN_WARNING MYNAM ": %s: WARNING - "
+#define MYIOC_s_ERR_FMT KERN_ERR MYNAM ": %s: ERROR - "
/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
/*
MPTSCSIH_DRIVER, /* MPT SCSI host (initiator) class */
MPTLAN_DRIVER, /* MPT LAN class */
MPTSTM_DRIVER, /* MPT SCSI target mode class */
+ MPTDMP_DRIVER, /* MPT Dynamic Multi-pathing class */
MPTUNKNOWN_DRIVER
} MPT_DRIVER_CLASS;
struct _MPT_FRAME_HDR *forw;
struct _MPT_FRAME_HDR *back;
u32 arg1;
+ u32 pad;
void *argp1;
+#ifndef MPT_SCSI_USE_NEW_EH
+ void *argp2;
+#endif
} linkage;
/*
- * NOTE: On non-32-bit systems, where pointers are LARGE,
+ * NOTE: When request frames are free, on the linkage structure
+ * contets are valid. All other values are invalid.
+ * In particular, do NOT reply on offset [2]
+ * (in words) being the * message context.
+ * The message context must be reset (computed via base address
+ * + an offset) prior to issuing any command.
+ *
+ * NOTE2: On non-32-bit systems, where pointers are LARGE,
* using the linkage pointers destroys our sacred MsgContext
* field contents. But we don't care anymore because these
* are now reset in mpt_put_msg_frame() just prior to sending
} fld;
} msgctxu;
} hwhdr;
+ /*
+ * Remark: 32 bit identifier:
+ * 31-24: reserved
+ * 23-16: call back index
+ * 15-0 : request index
+ */
} MPT_FRAME_TRACKER;
/*
} u;
} MPT_FRAME_HDR;
+#define MPT_REQ_MSGFLAGS_DROPME 0x80
+
+/* Used for tracking the free request frames
+ * and free reply frames.
+ */
typedef struct _MPT_Q_TRACKER {
MPT_FRAME_HDR *head;
MPT_FRAME_HDR *tail;
struct _Q_ITEM *tail;
} Q_TRACKER;
+typedef struct _MPT_DONE_Q {
+ struct _MPT_DONE_Q *forw;
+ struct _MPT_DONE_Q *back;
+ void *argp;
+} MPT_DONE_Q;
+
+typedef struct _DONE_Q_TRACKER {
+ MPT_DONE_Q *head;
+ MPT_DONE_Q *tail;
+} DONE_Q_TRACKER;
/*
- * Chip-specific stuff...
+ * Chip-specific stuff... FC929 delineates break between
+ * FC and Parallel SCSI parts. Do NOT re-order.
*/
typedef enum {
+ FC919X = 0x0819,
+ FC929X = 0x0829,
FC909 = 0x0909,
FC919 = 0x0919,
FC929 = 0x0929,
u32 WriteSequence; /* 04 Write Sequence register */
u32 Diagnostic; /* 08 Diagnostic register */
u32 TestBase; /* 0C Test Base Address */
- u32 Reserved1[8]; /* 10-2F reserved for future use */
+ u32 DiagRwData; /* 10 Read Write Data (fw download) */
+ u32 DiagRwAddress; /* 14 Read Write Address (fw download)*/
+ u32 Reserved1[6]; /* 18-2F reserved for future use */
u32 IntStatus; /* 30 Interrupt Status */
u32 IntMask; /* 34 Interrupt Mask */
u32 Reserved2[2]; /* 38-3F reserved for future use */
*/
+/*
+ * Dynamic Multi-Pathing specific stuff...
+ */
+#define DMP_MAX_PATHS 8
+
+typedef struct _PathInfo {
+ u8 ioc;
+ u8 target;
+ u8 pad;
+ u8 pflags;
+} PathInfo;
+
+#define PATHINFO_FLAGS_OWNED 0x01
+#define PATHINFO_FLAGS_EXISTS 0x02
+#define PATHINFO_FLAGS_AVAILABLE 0x04
+#define PATHINFO_FLAGS_SECONDARY 0x08
+
+#define PFLAGS_EXISTS_AND_AVAIL (PATHINFO_FLAGS_EXISTS|PATHINFO_FLAGS_AVAILABLE)
+#define PFLAGS_AVAIL_AND_OWNED (PATHINFO_FLAGS_AVAILABLE|PATHINFO_FLAGS_OWNED)
+
+typedef struct _ScsiCmndTracker {
+ void *head;
+ void *tail;
+} ScsiCmndTracker;
+
+
+/*
+ * VirtDevice - FC LUN device or SCSI target device
+ * (used to be FCSCSI_TARGET)
+ */
+typedef struct _VirtDevice {
+ struct _VirtDevice *forw;
+ struct _VirtDevice *back;
+ rwlock_t VdevLock;
+ int ref_cnt;
+ u8 tflags;
+ u8 ioc_id;
+ u8 target_id;
+ u8 bus_id;
+ u8 minSyncFactor; /* 0xFF is async */
+ u8 maxOffset; /* 0 if async */
+ u8 maxWidth; /* 0 if narrow, 1 if wide*/
+ u8 negoFlags; /* bit field, 0 if WDTR/SDTR/QAS allowed */
+ u8 raidVolume; /* set, if RAID Volume */
+ u8 rsvd;
+ u16 rsvd1raid;
+ int npaths;
+ u16 fc_phys_lun;
+ u16 fc_xlat_lun;
+ int stall_detected;
+ PathInfo path[DMP_MAX_PATHS];
+ struct timer_list stall_timer;
+ struct timer_list retry_timer;
+ struct timer_list gone_timer;
+ ScsiCmndTracker WaitQ;
+ ScsiCmndTracker SentQ;
+ ScsiCmndTracker DoneQ;
+//--- LUN split here?
+ u8 sense[SCSI_STD_SENSE_BYTES]; /* 18 */
+ u8 rsvd2[2]; /* alignment */
+ u32 luns; /* Max LUNs is 32 */
+ u8 inq_data[SCSI_STD_INQUIRY_BYTES]; /* 36 */
+ u8 pad0[4];
+ u8 inq00_data[20];
+ u8 pad1[4];
+ /* IEEE Registered Extended Identifier
+ obtained via INQUIRY VPD page 0x83 */
+ /* NOTE: Do not separate uniq_prepad and uniq_data
+ as they are treateed as a single entity in the code */
+ u8 uniq_prepad[8];
+ u8 uniq_data[20];
+ u8 pad2[4];
+ u8 inqC3_data[12];
+ u8 pad3[4];
+ u8 inqC9_data[12];
+ u8 pad4[4];
+ u8 dev_vol_name[64];
+} VirtDevice;
+
+/*
+ * Fibre Channel (SCSI) target device and associated defines...
+ */
+#define MPT_TARGET_DEFAULT_DV_STATUS 0
+#define MPT_TARGET_FLAGS_VALID_NEGO 0x01
+#define MPT_TARGET_FLAGS_VALID_INQUIRY 0x02
+#define MPT_TARGET_FLAGS_VALID_SENSE 0x04
+#define MPT_TARGET_FLAGS_Q_YES 0x08
+
+#define MPT_TARGET_NO_NEGO_WIDE 0x01
+#define MPT_TARGET_NO_NEGO_SYNC 0x02
+#define MPT_TARGET_NO_NEGO_QAS 0x04
+
+typedef struct _VirtDevTracker {
+ struct _VirtDevice *head;
+ struct _VirtDevice *tail;
+ rwlock_t VlistLock;
+ int pad;
+} VirtDevTracker;
+
+
+/*
+ * /proc/mpt interface
+ */
+typedef struct {
+ const char *name;
+ mode_t mode;
+ int pad;
+ read_proc_t *read_proc;
+ write_proc_t *write_proc;
+} mpt_proc_entry_t;
+
+#define MPT_PROC_READ_RETURN(buf,start,offset,request,eof,len) \
+do { \
+ len -= offset; \
+ if (len < request) { \
+ *eof = 1; \
+ if (len <= 0) \
+ return 0; \
+ } else \
+ len = request; \
+ *start = buf + offset; \
+ return len; \
+} while (0)
+
+
+/*
+ * IOCTL structure and associated defines
+ */
+
+#define MPT_IOCTL_STATUS_DID_IOCRESET 0x01 /* IOC Reset occurred on the current*/
+#define MPT_IOCTL_STATUS_RF_VALID 0x02 /* The Reply Frame is VALID */
+#define MPT_IOCTL_STATUS_TIMER_ACTIVE 0x04 /* The timer is running */
+#define MPT_IOCTL_STATUS_SENSE_VALID 0x08 /* Sense data is valid */
+#define MPT_IOCTL_STATUS_COMMAND_GOOD 0x10 /* Command Status GOOD */
+#define MPT_IOCTL_STATUS_TMTIMER_ACTIVE 0x20 /* The TM timer is running */
+#define MPT_IOCTL_STATUS_TM_FAILED 0x40 /* User TM request failed */
+
+#define MPTCTL_RESET_OK 0x01 /* Issue Bus Reset */
+
+typedef struct _MPT_IOCTL {
+ struct _MPT_ADAPTER *ioc;
+ struct timer_list timer; /* timer function for this adapter */
+ u8 ReplyFrame[MPT_DEFAULT_FRAME_SIZE]; /* reply frame data */
+ u8 sense[MPT_SENSE_BUFFER_ALLOC];
+ int wait_done; /* wake-up value for this ioc */
+ u8 rsvd;
+ u8 status; /* current command status */
+ u8 reset; /* 1 if bus reset allowed */
+ u8 target; /* target for reset */
+ void *tmPtr;
+ struct timer_list TMtimer; /* timer function for this adapter */
+} MPT_IOCTL;
+
+/*
+ * Event Structure and define
+ */
+#define MPTCTL_EVENT_LOG_SIZE (0x0000000A)
+typedef struct _mpt_ioctl_events {
+ u32 event; /* Specified by define above */
+ u32 eventContext; /* Index or counter */
+ int data[2]; /* First 8 bytes of Event Data */
+} MPT_IOCTL_EVENTS;
+
+/*
+ * CONFIGPARM status defines
+ */
+#define MPT_CONFIG_GOOD MPI_IOCSTATUS_SUCCESS
+#define MPT_CONFIG_ERROR 0x002F
+
+/*
+ * Substructure to store SCSI specific configuration page data
+ */
+ /* dvStatus defines: */
+#define MPT_SCSICFG_NEGOTIATE 0x01 /* Negotiate on next IO */
+#define MPT_SCSICFG_NEED_DV 0x02 /* Schedule DV */
+#define MPT_SCSICFG_DV_PENDING 0x04 /* DV on this physical id pending */
+#define MPT_SCSICFG_DV_DONE 0x08 /* DV on this physical id complete */
+#define MPT_SCSICFG_BLK_NEGO 0x10 /* WriteSDP1 with WDTR and SDTR disabled */
+
+ /* Args passed to writeSDP1: */
+#define MPT_SCSICFG_USE_NVRAM 0x01 /* WriteSDP1 using NVRAM */
+#define MPT_SCSICFG_ALL_IDS 0x02 /* WriteSDP1 to all IDS */
+/* #define MPT_SCSICFG_BLK_NEGO 0x10 WriteSDP1 with WDTR and SDTR disabled */
+
+typedef struct _ScsiCfgData {
+ int *nvram; /* table of device NVRAM values */
+ IOCPage3_t *pIocPg3; /* table of physical disks */
+ u8 dvStatus[MPT_MAX_SCSI_DEVICES];
+ int isRaid; /* bit field, 1 if RAID */
+ u8 minSyncFactor; /* 0xFF if async */
+ u8 maxSyncOffset; /* 0 if async */
+ u8 maxBusWidth; /* 0 if narrow, 1 if wide */
+ u8 busType; /* SE, LVD, HD */
+ u8 sdp1version; /* SDP1 version */
+ u8 sdp1length; /* SDP1 length */
+ u8 sdp0version; /* SDP0 version */
+ u8 sdp0length; /* SDP0 length */
+ u8 dvScheduled; /* 1 if scheduled */
+ u8 forceDv; /* 1 to force DV scheduling */
+ u8 noQas; /* Disable QAS for this adapter */
+ u8 rsvd[2];
+} ScsiCfgData;
+
+typedef struct _fw_image {
+ char *fw;
+ dma_addr_t fw_dma;
+ u32 size;
+ u32 rsvd;
+} fw_image_t;
+
+/*
+ * Adapter Structure - pci_dev specific. Maximum: MPT_MAX_ADAPTERS
+ */
typedef struct _MPT_ADAPTER
{
struct _MPT_ADAPTER *forw;
struct _MPT_ADAPTER *back;
- int id; /* Unique adapter id {0,1,2,...} */
- int pci_irq;
- char name[32]; /* "iocN" */
+ int id; /* Unique adapter id N {0,1,2,...} */
+ int pci_irq; /* This irq */
+ char name[MPT_NAME_LENGTH]; /* "iocN" */
char *prod_name; /* "LSIFC9x9" */
- u32 mem_phys; /* == f4020000 (mmap) */
volatile SYSIF_REGS *chip; /* == c8817000 (mmap) */
- CHIP_TYPE chip_type;
- int mem_size;
+ volatile SYSIF_REGS *pio_chip; /* Programmed IO (downloadboot) */
+ u32 mem_phys; /* == f4020000 (mmap) */
+ u32 pio_mem_phys; /* Programmed IO (downloadboot) */
+ int mem_size; /* mmap memory size */
int alloc_total;
u32 last_state;
int active;
- int sod_reset;
- unsigned long last_kickstart;
- u8 *reply_alloc; /* Reply frames alloc ptr */
+ u8 *reply_alloc; /* Reply frames alloc ptr */
dma_addr_t reply_alloc_dma;
- MPT_FRAME_HDR *reply_frames; /* Reply frames - rounded up! */
+ MPT_FRAME_HDR *reply_frames; /* Reply msg frames - rounded up! */
dma_addr_t reply_frames_dma;
- int reply_depth;
- int reply_sz;
+ u32 reply_frames_low_dma;
+ int reply_depth; /* Num Allocated reply frames */
+ int reply_sz; /* Reply frame size */
+ CHIP_TYPE chip_type;
/* We (host driver) get to manage our own RequestQueue! */
- u8 *req_alloc; /* Request frames alloc ptr */
+ u8 *req_alloc; /* Request frames alloc ptr */
dma_addr_t req_alloc_dma;
- MPT_FRAME_HDR *req_frames; /* Request msg frames for PULL mode! */
+ MPT_FRAME_HDR *req_frames; /* Request msg frames - rounded up! */
dma_addr_t req_frames_dma;
- int req_depth;
- int req_sz;
- MPT_Q_TRACKER FreeQ;
+ u32 req_frames_low_dma;
+ int req_depth; /* Number of request frames */
+ int req_sz; /* Request frame size (bytes) */
spinlock_t FreeQlock;
+ MPT_Q_TRACKER FreeQ;
/* Pool of SCSI sense buffers for commands coming from
* the SCSI mid-layer. We have one 256 byte sense buffer
* for each REQ entry.
*/
u8 *sense_buf_pool;
dma_addr_t sense_buf_pool_dma;
- struct pci_dev *pcidev;
-/* atomic_t userCnt; */
- u8 *memmap;
+ u32 sense_buf_low_dma;
int mtrr_reg;
- struct Scsi_Host *sh;
+ void *pcidev; /* struct pci_dev pointer */
+ u8 *memmap; /* mmap address */
+ struct Scsi_Host *sh; /* Scsi Host pointer */
+ ScsiCfgData spi_data; /* Scsi config. data */
+ MPT_IOCTL *ioctl; /* ioctl data pointer */
struct proc_dir_entry *ioc_dentry;
- struct _MPT_ADAPTER *alt_ioc;
+ struct _MPT_ADAPTER *alt_ioc; /* ptr to 929 bound adapter port */
+ spinlock_t diagLock; /* diagnostic reset lock */
+ int diagPending;
+ u32 biosVersion; /* BIOS version from IO Unit Page 2 */
+ int eventTypes; /* Event logging parameters */
+ int eventContext; /* Next event context */
+ int eventLogSize; /* Max number of cached events */
+ struct _mpt_ioctl_events *events; /* pointer to event log */
+ fw_image_t **cached_fw; /* Pointer to FW SG List */
+ Q_TRACKER configQ; /* linked list of config. requests */
+ int num_fw_frags; /* Number of SGE in FW SG List */
int hs_reply_idx;
+#ifndef MFCNT
+ u32 pad0;
+#else
+ u32 mfcnt;
+#endif
u32 hs_req[MPT_MAX_FRAME_SIZE/sizeof(u32)];
u16 hs_reply[MPT_MAX_FRAME_SIZE/sizeof(u16)];
IOCFactsReply_t facts;
PortFactsReply_t pfacts[2];
+ FCPortPage0_t fc_port_page0[2];
LANPage0_t lan_cnfg_page0;
LANPage1_t lan_cnfg_page1;
u8 FirstWhoInit;
- u8 pad1[3];
+ u8 upload_fw; /* If set, do a fw upload */
+ u8 pad1[6];
} MPT_ADAPTER;
* 0 = not Ok ...
*/
typedef int (*MPT_CALLBACK)(MPT_ADAPTER *ioc, MPT_FRAME_HDR *req, MPT_FRAME_HDR *reply);
-
typedef int (*MPT_EVHANDLER)(MPT_ADAPTER *ioc, EventNotificationReply_t *evReply);
typedef int (*MPT_RESETHANDLER)(MPT_ADAPTER *ioc, int reset_phase);
/* reset_phase defs */
#define MPT_HOSTEVENT_IOC_BRINGUP 0x91
#define MPT_HOSTEVENT_IOC_RECOVER 0x92
+/* Define the generic types based on the size
+ * of the dma_addr_t type.
+ */
+typedef struct _mpt_sge {
+ u32 FlagsLength;
+ dma_addr_t Address;
+} MptSge_t;
+
+#define mpt_addr_size() \
+ ((sizeof(dma_addr_t) == sizeof(u64)) ? MPI_SGE_FLAGS_64_BIT_ADDRESSING : \
+ MPI_SGE_FLAGS_32_BIT_ADDRESSING)
+
+#define mpt_msg_flags() \
+ ((sizeof(dma_addr_t) == sizeof(u64)) ? MPI_SCSIIO_MSGFLGS_SENSE_WIDTH_64 : \
+ MPI_SCSIIO_MSGFLGS_SENSE_WIDTH_32)
+
/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
/*
* Funky (private) macros...
#define dhsprintk(x)
#endif
-#if defined(MPT_DEBUG) || defined(MPT_DEBUG_MSG_FRAME)
+//#if defined(MPT_DEBUG) || defined(MPT_DEBUG_MSG_FRAME)
+#if defined(MPT_DEBUG_MSG_FRAME)
#define dmfprintk(x) printk x
#else
#define dmfprintk(x)
#define dirqprintk(x)
#endif
-#ifdef MPT_DEBUG_EVENTS
-#define deventprintk(x) printk x
+#ifdef MPT_DEBUG_SG
+#define dsgprintk(x) printk x
#else
-#define deventprintk(x)
+#define dsgprintk(x)
#endif
-#ifdef MPT_DEBUG_SPINLOCK
-#define dslprintk(x) printk x
+#ifdef MPT_DEBUG_DV
+#define ddvprintk(x) printk x
#else
-#define dslprintk(x)
+#define ddvprintk(x)
#endif
-#ifdef MPT_DEBUG_SG
-#define dsgprintk(x) printk x
+#if defined(MPT_DEBUG_DV) || defined(MPT_DEBUG_DV_TINY)
+#define ddvtprintk(x) printk x
#else
-#define dsgprintk(x)
+#define ddvtprintk(x)
+#endif
+
+#ifdef MPT_DEBUG_IOCTL
+#define dctlprintk(x) printk x
+#else
+#define dctlprintk(x)
#endif
+#ifdef MPT_DEBUG_RESET
+#define dtmprintk(x) printk x
+#else
+#define dtmprintk(x)
+#endif
+
+#ifdef MPT_DEBUG_NEH
+#define nehprintk(x) printk x
+#else
+#define nehprintk(x)
+#endif
#define MPT_INDEX_2_MFPTR(ioc,idx) \
(MPT_FRAME_HDR*)( (u8*)(ioc)->req_frames + (ioc)->req_sz * (idx) )
#define MFPTR_2_MPT_INDEX(ioc,mf) \
(int)( ((u8*)mf - (u8*)(ioc)->req_frames) / (ioc)->req_sz )
+#define MPT_INDEX_2_RFPTR(ioc,idx) \
+ (MPT_FRAME_HDR*)( (u8*)(ioc)->reply_frames + (ioc)->req_sz * (idx) )
+
#define Q_INIT(q,type) (q)->head = (q)->tail = (type*)(q)
#define Q_IS_EMPTY(q) ((Q_ITEM*)(q)->head == (Q_ITEM*)(q))
_forw->back = _back; \
}
-
#define SWAB4(value) \
(u32)( (((value) & 0x000000ff) << 24) \
| (((value) & 0x0000ff00) << 8) \
/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
-#endif /* } __KERNEL__ */
+/*
+ * MPT_SCSI_HOST defines - Used by the IOCTL and the SCSI drivers
+ * Private to the driver.
+ */
+/* LOCAL structure and fields used when processing
+ * internally generated commands. These include:
+ * bus scan, dv and config requests.
+ */
+typedef struct _MPT_LOCAL_REPLY {
+ ConfigPageHeader_t header;
+ int completion;
+ u8 sense[SCSI_STD_SENSE_BYTES];
+ u8 scsiStatus;
+ u8 skip;
+ u32 pad;
+} MPT_LOCAL_REPLY;
+
+#define MPT_HOST_BUS_UNKNOWN (0xFF)
+#define MPT_HOST_TOO_MANY_TM (0x05)
+#define MPT_HOST_NVRAM_INVALID (0xFFFFFFFF)
+#define MPT_HOST_NO_CHAIN (0xFFFFFFFF)
+#define MPT_NVRAM_MASK_TIMEOUT (0x000000FF)
+#define MPT_NVRAM_SYNC_MASK (0x0000FF00)
+#define MPT_NVRAM_SYNC_SHIFT (8)
+#define MPT_NVRAM_DISCONNECT_ENABLE (0x00010000)
+#define MPT_NVRAM_ID_SCAN_ENABLE (0x00020000)
+#define MPT_NVRAM_LUN_SCAN_ENABLE (0x00040000)
+#define MPT_NVRAM_TAG_QUEUE_ENABLE (0x00080000)
+#define MPT_NVRAM_WIDE_DISABLE (0x00100000)
+#define MPT_NVRAM_BOOT_CHOICE (0x00200000)
+
+#ifdef MPT_SCSI_USE_NEW_EH
+/* The TM_STATE variable is used to provide strict single threading of TM
+ * requests as well as communicate TM error conditions.
+ */
+#define TM_STATE_NONE (0)
+#define TM_STATE_IN_PROGRESS (1)
+#define TM_STATE_ERROR (2)
+#endif
+
+typedef struct _MPT_SCSI_HOST {
+ MPT_ADAPTER *ioc;
+ int port;
+ u32 pad0;
+ struct scsi_cmnd **ScsiLookup;
+ /* Pool of buffers for chaining. ReqToChain
+ * and ChainToChain track index of chain buffers.
+ * ChainBuffer (DMA) virt/phys addresses.
+ * FreeChainQ (lock) locking mechanisms.
+ */
+ int *ReqToChain;
+ int *ChainToChain;
+ u8 *ChainBuffer;
+ dma_addr_t ChainBufferDMA;
+ MPT_Q_TRACKER FreeChainQ;
+ spinlock_t FreeChainQlock;
+ u32 qtag_tick;
+ VirtDevice **Targets;
+ MPT_LOCAL_REPLY *pLocal; /* used for internal commands */
+ struct timer_list timer;
+ struct timer_list TMtimer; /* Timer for TM commands ONLY */
+ /* Pool of memory for holding SCpnts before doing
+ * OS callbacks. freeQ is the free pool.
+ */
+ u8 *memQ;
+ DONE_Q_TRACKER freeQ;
+ DONE_Q_TRACKER doneQ; /* Holds Linux formmatted requests */
+ DONE_Q_TRACKER pendingQ; /* Holds MPI formmatted requests */
+ MPT_Q_TRACKER taskQ; /* TM request Q */
+ spinlock_t freedoneQlock;
+ int taskQcnt;
+ u8 numTMrequests;
+ u8 tmPending;
+ u8 resetPending;
+ u8 is_spi; /* Parallel SCSI i/f */
+ u8 negoNvram; /* DV disabled, nego NVRAM */
+ u8 is_multipath; /* Multi-path compatible */
+#ifdef MPT_SCSI_USE_NEW_EH
+ u8 tmState;
+ u8 rsvd[1];
+#else
+ u8 rsvd[2];
+#endif
+ MPT_FRAME_HDR *tmPtr; /* Ptr to TM request*/
+ MPT_FRAME_HDR *cmdPtr; /* Ptr to nonOS request */
+ struct scsi_cmnd *abortSCpnt;
+ MPT_LOCAL_REPLY localReply; /* internal cmd reply struct */
+} MPT_SCSI_HOST;
-/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
+/*
+ * Structure for overlaying onto scsi_cmnd->SCp area
+ * NOTE: SCp area is 36 bytes min, 44 bytes max?
+ */
+typedef struct _scPrivate {
+ struct scsi_cmnd *forw;
+ struct scsi_cmnd *back;
+ void *p1;
+ void *p2;
+ u8 io_path_id; /* DMP */
+ u8 pad[7];
+} scPrivate;
+/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
/*
- * MPT Control IOCTLs and structures
+ * More Dynamic Multi-Pathing stuff...
*/
-#define MPT_MAGIC_NUMBER 'm'
-#define MPTRWPERF _IOWR(MPT_MAGIC_NUMBER,0,struct mpt_raw_r_w)
-#define MPTRWPERF_CHK _IOR(MPT_MAGIC_NUMBER,13,struct mpt_raw_r_w)
-#define MPTRWPERF_RESET _IOR(MPT_MAGIC_NUMBER,14,struct mpt_raw_r_w)
-#define MPTFWDOWNLOAD _IOWR(MPT_MAGIC_NUMBER,15,struct mpt_fw_xfer)
-#define MPTSCSICMD _IOWR(MPT_MAGIC_NUMBER,16,struct mpt_scsi_cmd)
+
+/* Forward decl, a strange C thing, to prevent gcc compiler warnings */
+struct scsi_cmnd;
/*
- * Define something *vague* enough that caller doesn't
- * really need to know anything about device parameters
- * (blk_size, capacity, etc.)
- */
-struct mpt_raw_r_w {
- unsigned int iocnum; /* IOC unit number */
- unsigned int port; /* IOC port number */
- unsigned int target; /* SCSI Target */
- unsigned int lun; /* SCSI LUN */
- unsigned int iters; /* N iterations */
- unsigned short nblks; /* number of blocks per IO */
- unsigned short qdepth; /* max Q depth on this device */
- unsigned char range; /* 0-100% of FULL disk capacity, 0=use (nblks X iters) */
- unsigned char skip; /* % of disk to skip */
- unsigned char rdwr; /* 0-100%, 0=pure ReaDs, 100=pure WRites */
- unsigned char seqran; /* 0-100%, 0=pure SEQential, 100=pure RANdom */
- unsigned int cache_sz; /* In Kb! Optimize hits to N Kb cache size */
-};
-
-struct mpt_fw_xfer {
- unsigned int iocnum; /* IOC unit number */
-/* u8 flags;*/ /* Message flags - bit field */
- unsigned int fwlen;
- void *bufp; /* Pointer to firmware buffer */
-};
-
-struct mpt_scsi_cmd {
- unsigned int iocnum; /* IOC unit number */
- unsigned int port; /* IOC port number */
- unsigned int target; /* SCSI Target */
- unsigned int lun; /* SCSI LUN */
- SCSIIORequest_t scsi_req;
- SCSIIOReply_t scsi_reply;
-};
-
-struct mpt_ioctl_sanity {
- unsigned int iocnum;
-};
+ * DMP service layer structure / API interface
+ */
+typedef struct _DmpServices {
+ VirtDevTracker VdevList;
+ struct semaphore *Daemon;
+ int (*ScsiPathSelect)
+ (struct scsi_cmnd *, MPT_SCSI_HOST **hd, int *target, int *lun);
+ int (*DmpIoDoneChk)
+ (MPT_SCSI_HOST *, struct scsi_cmnd *,
+ SCSIIORequest_t *,
+ SCSIIOReply_t *);
+ void (*mptscsih_scanVlist)
+ (MPT_SCSI_HOST *, int portnum);
+ int (*ScsiAbort)
+ (struct scsi_cmnd *);
+ int (*ScsiBusReset)
+ (struct scsi_cmnd *);
+} DmpServices_t;
-#ifdef __KERNEL__ /* { */
/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
+/*
+ * Generic structure passed to the base mpt_config function.
+ */
+typedef struct _x_config_parms {
+ Q_ITEM linkage; /* linked list */
+ struct timer_list timer; /* timer function for this request */
+ ConfigPageHeader_t *hdr;
+ dma_addr_t physAddr;
+ int wait_done; /* wait for this request */
+ u32 pageAddr; /* properly formatted */
+ u8 action;
+ u8 dir;
+ u8 timeout; /* seconds */
+ u8 pad1;
+ u16 status;
+ u16 pad2;
+} CONFIGPARMS;
+/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
/*
* Public entry points...
*/
extern void mpt_event_deregister(int cb_idx);
extern int mpt_reset_register(int cb_idx, MPT_RESETHANDLER reset_func);
extern void mpt_reset_deregister(int cb_idx);
-extern int mpt_register_ascqops_strings(/*ASCQ_Table_t*/void *ascqTable, int ascqtbl_sz, const char **opsTable);
+extern int mpt_register_ascqops_strings(void *ascqTable, int ascqtbl_sz, const char **opsTable);
extern void mpt_deregister_ascqops_strings(void);
extern MPT_FRAME_HDR *mpt_get_msg_frame(int handle, int iocid);
extern void mpt_free_msg_frame(int handle, int iocid, MPT_FRAME_HDR *mf);
extern void mpt_put_msg_frame(int handle, int iocid, MPT_FRAME_HDR *mf);
-extern int mpt_send_handshake_request(int handle, int iocid, int reqBytes, u32 *req);
+extern void mpt_add_sge(char *pAddr, u32 flagslength, dma_addr_t dma_addr);
+extern void mpt_add_chain(char *pAddr, u8 next, u16 length, dma_addr_t dma_addr);
+
+extern int mpt_send_handshake_request(int handle, int iocid, int reqBytes, u32 *req, int sleepFlag);
+extern int mpt_handshake_req_reply_wait(MPT_ADAPTER *ioc, int reqBytes, u32 *req, int replyBytes, u16 *u16reply, int maxwait, int sleepFlag);
extern int mpt_verify_adapter(int iocid, MPT_ADAPTER **iocpp);
extern MPT_ADAPTER *mpt_adapter_find_first(void);
extern MPT_ADAPTER *mpt_adapter_find_next(MPT_ADAPTER *prev);
+extern u32 mpt_GetIocState(MPT_ADAPTER *ioc, int cooked);
extern void mpt_print_ioc_summary(MPT_ADAPTER *ioc, char *buf, int *size, int len, int showlan);
-extern void mpt_print_ioc_facts(MPT_ADAPTER *ioc, char *buf, int *size, int len);
+extern int mpt_HardResetHandler(MPT_ADAPTER *ioc, int sleepFlag);
+extern int mpt_config(MPT_ADAPTER *ioc, CONFIGPARMS *cfg);
+extern void *mpt_alloc_fw_memory(MPT_ADAPTER *ioc, int size, int *frags, int *alloc_sz);
+extern void mpt_free_fw_memory(MPT_ADAPTER *ioc, fw_image_t **alt_img);
/*
* Public data decl's...
*/
+extern MPT_ADAPTER *mpt_adapters[MPT_MAX_ADAPTERS];
+extern struct proc_dir_entry *mpt_proc_root_dir;
+extern DmpServices_t *DmpService;
+
extern int mpt_lan_index; /* needed by mptlan.c */
extern int mpt_stm_index; /* needed by mptstm.c */
#define offsetof(t, m) ((size_t) (&((t *)0)->m))
#endif
-#if defined(__alpha__) || defined(__sparc_v9__)
+#if defined(__alpha__) || defined(__sparc_v9__) || defined(__ia64__)
#define CAST_U32_TO_PTR(x) ((void *)(u64)x)
#define CAST_PTR_TO_U32(x) ((u32)(u64)x)
#else
((pflags) & MPI_PORTFACTS_PROTOCOL_LAN) ? 'L' : 'l', \
((pflags) & MPI_PORTFACTS_PROTOCOL_LOGBUSADDR) ? 'B' : 'b'
+/*
+ * Shifted SGE Defines - Use in SGE with FlagsLength member.
+ * Otherwise, use MPI_xxx defines (refer to "lsi/mpi.h" header).
+ * Defaults: 32 bit SGE, SYSTEM_ADDRESS if direction bit is 0, read
+ */
+#define MPT_TRANSFER_IOC_TO_HOST (0x00000000)
+#define MPT_TRANSFER_HOST_TO_IOC (0x04000000)
+#define MPT_SGE_FLAGS_LAST_ELEMENT (0x80000000)
+#define MPT_SGE_FLAGS_END_OF_BUFFER (0x40000000)
+#define MPT_SGE_FLAGS_LOCAL_ADDRESS (0x08000000)
+#define MPT_SGE_FLAGS_DIRECTION (0x04000000)
+#define MPT_SGE_FLAGS_ADDRESSING (mpt_addr_size() << MPI_SGE_FLAGS_SHIFT)
+#define MPT_SGE_FLAGS_END_OF_LIST (0x01000000)
+
+#define MPT_SGE_FLAGS_TRANSACTION_ELEMENT (0x00000000)
+#define MPT_SGE_FLAGS_SIMPLE_ELEMENT (0x10000000)
+#define MPT_SGE_FLAGS_CHAIN_ELEMENT (0x30000000)
+#define MPT_SGE_FLAGS_ELEMENT_MASK (0x30000000)
+
+#define MPT_SGE_FLAGS_SSIMPLE_READ \
+ (MPT_SGE_FLAGS_LAST_ELEMENT | \
+ MPT_SGE_FLAGS_END_OF_BUFFER | \
+ MPT_SGE_FLAGS_END_OF_LIST | \
+ MPT_SGE_FLAGS_SIMPLE_ELEMENT | \
+ MPT_SGE_FLAGS_ADDRESSING | \
+ MPT_TRANSFER_IOC_TO_HOST)
+#define MPT_SGE_FLAGS_SSIMPLE_WRITE \
+ (MPT_SGE_FLAGS_LAST_ELEMENT | \
+ MPT_SGE_FLAGS_END_OF_BUFFER | \
+ MPT_SGE_FLAGS_END_OF_LIST | \
+ MPT_SGE_FLAGS_SIMPLE_ELEMENT | \
+ MPT_SGE_FLAGS_ADDRESSING | \
+ MPT_TRANSFER_HOST_TO_IOC)
+
/*}-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
#endif
* This driver would not exist if not for Alan Cox's development
* of the linux i2o driver.
*
+ * A special thanks to Pamela Delaney (LSI Logic) for tons of work
+ * and countless enhancements while adding support for the 1030
+ * chip family. Pam has been instrumental in the development of
+ * of the 2.xx.xx series fusion drivers, and her contributions are
+ * far too numerous to hope to list in one place.
+ *
* A huge debt of gratitude is owed to David S. Miller (DaveM)
* for fixing much of the stupid and broken stuff in the early
* driver while porting to sparc64 platform. THANK YOU!
* (plus Eddie's other helpful hints and insights)
*
* Thanks to Arnaldo Carvalho de Melo for finding and patching
- * a potential memory leak in mpt_ioctl_do_fw_download(),
+ * a potential memory leak in mptctl_do_fw_download(),
* and for some kmalloc insight:-)
*
* (see also mptbase.c)
*
- * Copyright (c) 1999-2001 LSI Logic Corporation
+ * Copyright (c) 1999-2002 LSI Logic Corporation
* Originally By: Steven J. Ralston, Noah Romer
- * (mailto:Steve.Ralston@lsil.com)
+ * (mailto:sjralston1@netscape.net)
+ * (mailto:Pam.Delaney@lsil.com)
*
- * $Id: mptctl.c,v 1.25.4.1 2001/08/24 20:07:06 sralston Exp $
+ * $Id: mptctl.c,v 1.55 2002/06/20 13:28:16 pdelaney Exp $
*/
/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
/*
#include <linux/version.h>
#include <linux/kernel.h>
#include <linux/module.h>
-#include <linux/major.h>
#include <linux/errno.h>
#include <linux/init.h>
#include <linux/slab.h>
#include <asm/io.h>
#include <asm/uaccess.h>
-#include <linux/proc_fs.h>
+#include <linux/kdev_t.h> /* needed for access to Scsi_Host struct */
+#include <linux/blkdev.h>
+#include <linux/blk.h> /* for io_request_lock (spinlock) decl */
+#include "../../scsi/scsi.h"
+#include "../../scsi/hosts.h"
#define COPYRIGHT "Copyright (c) 1999-2001 LSI Logic Corporation"
-#define MODULEAUTHOR "Steven J. Ralston, Noah Romer"
+#define MODULEAUTHOR "Steven J. Ralston, Noah Romer, Pamela Delaney"
#include "mptbase.h"
+#include "mptctl.h"
/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
#define my_NAME "Fusion MPT misc device (ioctl) driver"
#define my_VERSION MPT_LINUX_VERSION_COMMON
#define MYNAM "mptctl"
+EXPORT_NO_SYMBOLS;
MODULE_AUTHOR(MODULEAUTHOR);
MODULE_DESCRIPTION(my_NAME);
MODULE_LICENSE("GPL");
-
/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
static int mptctl_id = -1;
-static int rwperf_reset = 0;
static struct semaphore mptctl_syscall_sem_ioc[MPT_MAX_ADAPTERS];
+static DECLARE_WAIT_QUEUE_HEAD ( mptctl_wait );
+
/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
-static int mpt_ioctl_rwperf(unsigned long arg);
-static int mpt_ioctl_rwperf_status(unsigned long arg);
-static int mpt_ioctl_rwperf_reset(unsigned long arg);
-static int mpt_ioctl_fw_download(unsigned long arg);
-static int mpt_ioctl_do_fw_download(int ioc, char *ufwbuf, size_t fwlen);
-static int mpt_ioctl_scsi_cmd(unsigned long arg);
+struct buflist {
+ u8 *kptr;
+ int len;
+};
+
+/*
+ * Function prototypes. Called from OS entry point mptctl_ioctl.
+ * arg contents specific to function.
+ */
+static int mptctl_fw_download(unsigned long arg);
+static int mptctl_getiocinfo (unsigned long arg, unsigned int cmd);
+static int mptctl_gettargetinfo (unsigned long arg);
+static int mptctl_readtest (unsigned long arg);
+static int mptctl_mpt_command (unsigned long arg);
+static int mptctl_eventquery (unsigned long arg);
+static int mptctl_eventenable (unsigned long arg);
+static int mptctl_eventreport (unsigned long arg);
+static int mptctl_replace_fw (unsigned long arg);
+
+static int mptctl_do_reset(unsigned long arg);
+
+static int mptctl_compaq_ioctl(struct file *file, unsigned int cmd, unsigned long arg);
+static int mptctl_cpq_getpciinfo(unsigned long arg);
+static int mptctl_cpq_getdriver(unsigned long arg);
+static int mptctl_cpq_ctlr_status(unsigned long arg);
+static int mptctl_cpq_target_address(unsigned long arg);
+static int mptctl_cpq_passthru(unsigned long arg);
+static int mptctl_compaq_scsiio(VENDOR_IOCTL_REQ *pVenReq, cpqfc_passthru_t *pPass);
+
+/*
+ * Private function calls.
+ */
+static int mptctl_do_mpt_command (struct mpt_ioctl_command karg, char *mfPtr, int local);
+static int mptctl_do_fw_download(int ioc, char *ufwbuf, size_t fwlen);
+static MptSge_t *kbuf_alloc_2_sgl( int bytes, u32 dir, int sge_offset, int *frags,
+ struct buflist **blp, dma_addr_t *sglbuf_dma, MPT_ADAPTER *ioc);
+static void kfree_sgl( MptSge_t *sgl, dma_addr_t sgl_dma,
+ struct buflist *buflist, MPT_ADAPTER *ioc);
+static void mptctl_timer_expired (unsigned long data);
+static int mptctl_bus_reset(MPT_IOCTL *ioctl);
+static int mptctl_set_tm_flags(MPT_SCSI_HOST *hd);
+static void mptctl_free_tm_flags(MPT_ADAPTER *ioc);
+
+/*
+ * Reset Handler cleanup function
+ */
+static int mptctl_ioc_reset(MPT_ADAPTER *ioc, int reset_phase);
/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
/*
/* linux only seems to ever give 128kB MAX contiguous (GFP_USER) mem bytes */
#define MAX_KMALLOC_SZ (128*1024)
-struct buflist {
- u8 *kptr;
- int len;
-};
-
-#define myMAX_TARGETS (1<<4)
-#define myMAX_LUNS (1<<3)
-#define myMAX_T_MASK (myMAX_TARGETS-1)
-#define myMAX_L_MASK (myMAX_LUNS-1)
-static u8 DevInUse[myMAX_TARGETS][myMAX_LUNS] = {{0,0}};
-static u32 DevIosCount[myMAX_TARGETS][myMAX_LUNS] = {{0,0}};
+#define MPT_IOCTL_DEFAULT_TIMEOUT 10 /* Default timeout value (seconds) */
static u32 fwReplyBuffer[16];
static pMPIDefaultReply_t ReplyMsg = NULL;
-/* some private forw protos */
-static SGESimple32_t *kbuf_alloc_2_sgl( int bytes, u32 dir, int *frags,
- struct buflist **blp, dma_addr_t *sglbuf_dma, MPT_ADAPTER *ioc);
-static void kfree_sgl( SGESimple32_t *sgl, dma_addr_t sgl_dma,
- struct buflist *buflist, MPT_ADAPTER *ioc);
-
/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
/**
* mptctl_syscall_down - Down the MPT adapter syscall semaphore.
* @ioc: Pointer to MPT adapter
* @nonblock: boolean, non-zero if O_NONBLOCK is set
*
- * All of the mptctl commands can potentially sleep, which is illegal
+ * All of the ioctl commands can potentially sleep, which is illegal
* with a spinlock held, thus we perform mutual exclusion here.
*
* Returns negative errno on error, or zero for success.
static inline int
mptctl_syscall_down(MPT_ADAPTER *ioc, int nonblock)
{
- dprintk((KERN_INFO MYNAM "::mpt_syscall_down(%p,%d) called\n", ioc, nonblock));
+ int rc = 0;
+ dctlprintk((KERN_INFO MYNAM "::mptctl_syscall_down(%p,%d) called\n", ioc, nonblock));
+
+ if (ioc->ioctl->tmPtr != NULL) {
+ dctlprintk((KERN_INFO MYNAM "::mptctl_syscall_down BUSY\n"));
+ return -EBUSY;
+ }
+#if defined(__sparc__) && defined(__sparc_v9__) /*{*/
+ if (!nonblock) {
+ if (down_interruptible(&mptctl_syscall_sem_ioc[ioc->id]))
+ rc = -ERESTARTSYS;
+ } else {
+ rc = -EPERM;
+ }
+#else
if (nonblock) {
if (down_trylock(&mptctl_syscall_sem_ioc[ioc->id]))
- return -EAGAIN;
+ rc = -EAGAIN;
} else {
if (down_interruptible(&mptctl_syscall_sem_ioc[ioc->id]))
- return -ERESTARTSYS;
+ rc = -ERESTARTSYS;
}
- return 0;
+#endif
+ dctlprintk((KERN_INFO MYNAM "::mptctl_syscall_down return %d\n", rc));
+ return rc;
}
/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
static int
mptctl_reply(MPT_ADAPTER *ioc, MPT_FRAME_HDR *req, MPT_FRAME_HDR *reply)
{
- u8 targ;
+ char *sense_data;
+ int sz, req_index;
+ u16 iocStatus;
+ u8 cmd;
+
+ dctlprintk((MYIOC_s_INFO_FMT ": mptctl_reply()!\n", ioc->name));
+ if (req)
+ cmd = req->u.hdr.Function;
+ else
+ return 1;
+
+ if (ioc->ioctl) {
+ /* If timer is not running, then an error occurred.
+ * A timeout will call the reset routine to reload the messaging
+ * queues.
+ * Main callback will free message and reply frames.
+ */
+ if (reply && (cmd == MPI_FUNCTION_SCSI_TASK_MGMT) &&
+ (ioc->ioctl->status & MPT_IOCTL_STATUS_TMTIMER_ACTIVE)) {
+ /* This is internally generated TM
+ */
+ del_timer (&ioc->ioctl->TMtimer);
+ ioc->ioctl->status &= ~MPT_IOCTL_STATUS_TMTIMER_ACTIVE;
+
+ mptctl_free_tm_flags(ioc);
+
+ /* If TM failed, reset the timer on the existing command,
+ * will trigger an adapter reset.
+ */
+ iocStatus = reply->u.reply.IOCStatus & MPI_IOCSTATUS_MASK;
+ if (iocStatus == MPI_IOCSTATUS_SCSI_TASK_MGMT_FAILED) {
+ if (ioc->ioctl->status & MPT_IOCTL_STATUS_TIMER_ACTIVE) {
+ del_timer (&ioc->ioctl->timer);
+ ioc->ioctl->timer.expires = jiffies + HZ;
+ add_timer(&ioc->ioctl->timer);
+ }
+ }
+ ioc->ioctl->tmPtr = NULL;
+
+ } else if (ioc->ioctl->status & MPT_IOCTL_STATUS_TIMER_ACTIVE) {
+ /* Delete this timer
+ */
+ del_timer (&ioc->ioctl->timer);
+ ioc->ioctl->status &= ~MPT_IOCTL_STATUS_TIMER_ACTIVE;
+
+ /* Set the overall status byte. Good if:
+ * IOC status is good OR if no reply and a SCSI IO request
+ */
+ if (reply) {
+ /* Copy the reply frame (which much exist
+ * for non-SCSI I/O) to the IOC structure.
+ */
+ dctlprintk((MYIOC_s_INFO_FMT ": Copying Reply Frame @%p to IOC!\n",
+ ioc->name, reply));
+ memcpy(ioc->ioctl->ReplyFrame, reply,
+ MIN(ioc->reply_sz, 4*reply->u.reply.MsgLength));
+ ioc->ioctl->status |= MPT_IOCTL_STATUS_RF_VALID;
+
+ /* Set the command status to GOOD if IOC Status is GOOD
+ * OR if SCSI I/O cmd and data underrun or recovered error.
+ */
+ iocStatus = reply->u.reply.IOCStatus & MPI_IOCSTATUS_MASK;
+ if (iocStatus == MPI_IOCSTATUS_SUCCESS)
+ ioc->ioctl->status |= MPT_IOCTL_STATUS_COMMAND_GOOD;
+
+ if ((cmd == MPI_FUNCTION_SCSI_IO_REQUEST) ||
+ (cmd == MPI_FUNCTION_RAID_SCSI_IO_PASSTHROUGH)) {
+ ioc->ioctl->reset &= ~MPTCTL_RESET_OK;
+
+ if ((iocStatus == MPI_IOCSTATUS_SCSI_DATA_UNDERRUN) ||
+ (iocStatus == MPI_IOCSTATUS_SCSI_RECOVERED_ERROR)) {
+ ioc->ioctl->status |= MPT_IOCTL_STATUS_COMMAND_GOOD;
+ }
+ }
+
+ /* Copy the sense data - if present
+ */
+ if ((cmd == MPI_FUNCTION_SCSI_IO_REQUEST) &&
+ (reply->u.sreply.SCSIState & MPI_SCSI_STATE_AUTOSENSE_VALID)){
+
+ sz = req->u.scsireq.SenseBufferLength;
+ req_index = le16_to_cpu(req->u.frame.hwhdr.msgctxu.fld.req_idx);
+ sense_data = ((u8 *)ioc->sense_buf_pool + (req_index * MPT_SENSE_BUFFER_ALLOC));
+ memcpy(ioc->ioctl->sense, sense_data, sz);
+ ioc->ioctl->status |= MPT_IOCTL_STATUS_SENSE_VALID;
+ }
+
+ if (cmd == MPI_FUNCTION_SCSI_TASK_MGMT)
+ mptctl_free_tm_flags(ioc);
+
+
+ } else if ((cmd == MPI_FUNCTION_SCSI_IO_REQUEST) ||
+ (cmd == MPI_FUNCTION_RAID_SCSI_IO_PASSTHROUGH)) {
+ ioc->ioctl->status |= MPT_IOCTL_STATUS_COMMAND_GOOD;
+ ioc->ioctl->reset &= ~MPTCTL_RESET_OK;
+ }
+
+ /* We are done, issue wake up
+ */
+ ioc->ioctl->wait_done = 1;
+ wake_up (&mptctl_wait);
+ } else if (reply && cmd == MPI_FUNCTION_FW_DOWNLOAD) {
+ /* Two paths to FW DOWNLOAD! */
+ // NOTE: Expects/requires non-Turbo reply!
+ dctlprintk((MYIOC_s_INFO_FMT ":Caching MPI_FUNCTION_FW_DOWNLOAD reply!\n",
+ ioc->name));
+ memcpy(fwReplyBuffer, reply, MIN(sizeof(fwReplyBuffer), 4*reply->u.reply.MsgLength));
+ ReplyMsg = (pMPIDefaultReply_t) fwReplyBuffer;
+ }
+ }
+ return 1;
+}
+
+/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
+/* mptctl_timer_expired
+ *
+ * Call back for timer process. Used only for ioctl functionality.
+ *
+ */
+static void mptctl_timer_expired (unsigned long data)
+{
+ MPT_IOCTL *ioctl = (MPT_IOCTL *) data;
+ int rc = 1;
+
+ dctlprintk((KERN_NOTICE MYNAM ": Timer Expired! Host %d\n",
+ ioctl->ioc->id));
+ if (ioctl == NULL)
+ return;
+
+ if (ioctl->reset & MPTCTL_RESET_OK)
+ rc = mptctl_bus_reset(ioctl);
+
+ if (rc) {
+ /* Issue a reset for this device.
+ * The IOC is not responding.
+ */
+ mpt_HardResetHandler(ioctl->ioc, NO_SLEEP);
+ }
+ return;
+
+}
+
+/* mptctl_bus_reset
+ *
+ * Bus reset code.
+ *
+ */
+static int mptctl_bus_reset(MPT_IOCTL *ioctl)
+{
+ MPT_FRAME_HDR *mf;
+ SCSITaskMgmt_t *pScsiTm;
+ MPT_SCSI_HOST *hd;
+ int ii;
+ int retval;
+
+
+ ioctl->reset &= ~MPTCTL_RESET_OK;
+
+ if (ioctl->ioc->sh == NULL)
+ return -EPERM;
+
+ hd = (MPT_SCSI_HOST *) ioctl->ioc->sh->hostdata;
+ if (hd == NULL)
+ return -EPERM;
+
+ /* Single threading ....
+ */
+ if (mptctl_set_tm_flags(hd) != 0)
+ return -EPERM;
+
+ /* Send request
+ */
+ if ((mf = mpt_get_msg_frame(mptctl_id, ioctl->ioc->id)) == NULL) {
+ dtmprintk((MYIOC_s_WARN_FMT "IssueTaskMgmt, no msg frames!!\n",
+ ioctl->ioc->name));
+
+ mptctl_free_tm_flags(ioctl->ioc);
+ return -ENOMEM;
+ }
+
+ dtmprintk((MYIOC_s_INFO_FMT "IssueTaskMgmt request @ %p\n",
+ ioctl->ioc->name, mf));
+
+ pScsiTm = (SCSITaskMgmt_t *) mf;
+ pScsiTm->TargetID = ioctl->target;
+ pScsiTm->Bus = hd->port; /* 0 */
+ pScsiTm->ChainOffset = 0;
+ pScsiTm->Function = MPI_FUNCTION_SCSI_TASK_MGMT;
+ pScsiTm->Reserved = 0;
+ pScsiTm->TaskType = MPI_SCSITASKMGMT_TASKTYPE_RESET_BUS;
+ pScsiTm->Reserved1 = 0;
+ pScsiTm->MsgFlags = MPI_SCSITASKMGMT_MSGFLAGS_LIPRESET_RESET_OPTION;
+
+ for (ii= 0; ii < 8; ii++)
+ pScsiTm->LUN[ii] = 0;
+
+ for (ii=0; ii < 7; ii++)
+ pScsiTm->Reserved2[ii] = 0;
+
+ pScsiTm->TaskMsgContext = 0;
+ dtmprintk((MYIOC_s_INFO_FMT "mptctl_bus_reset: issued.\n", ioctl->ioc->name));
+
+ ioctl->tmPtr = mf;
+ ioctl->TMtimer.expires = jiffies + HZ * 20; /* 20 seconds */
+ ioctl->status |= MPT_IOCTL_STATUS_TMTIMER_ACTIVE;
+ add_timer(&ioctl->TMtimer);
+
+ retval = mpt_send_handshake_request(mptctl_id, ioctl->ioc->id,
+ sizeof(SCSITaskMgmt_t), (u32*)pScsiTm, NO_SLEEP);
+
+ if (retval != 0) {
+ dtmprintk((MYIOC_s_WARN_FMT "_send_handshake FAILED!"
+ " (hd %p, ioc %p, mf %p) \n", ioctl->ioc->name, hd, hd->ioc, mf));
+
+ mptctl_free_tm_flags(ioctl->ioc);
+ del_timer(&ioctl->TMtimer);
+ mpt_free_msg_frame(mptctl_id, ioctl->ioc->id, mf);
+ ioctl->tmPtr = NULL;
+ }
+
+ return retval;
+}
+
+static int
+mptctl_set_tm_flags(MPT_SCSI_HOST *hd) {
+ unsigned long flags;
+
+ spin_lock_irqsave(&hd->ioc->FreeQlock, flags);
+#ifdef MPT_SCSI_USE_NEW_EH
+ if (hd->tmState == TM_STATE_NONE) {
+ hd->tmState = TM_STATE_IN_PROGRESS;
+ hd->tmPending = 1;
+ spin_unlock_irqrestore(&hd->ioc->FreeQlock, flags);
+ } else {
+ spin_unlock_irqrestore(&hd->ioc->FreeQlock, flags);
+ return -EBUSY;
+ }
+#else
+ if (hd->tmPending) {
+ spin_unlock_irqrestore(&hd->ioc->FreeQlock, flags);
+ return -EBUSY;
+ } else {
+ hd->tmPending = 1;
+ spin_unlock_irqrestore(&hd->ioc->FreeQlock, flags);
+ }
+#endif
+ return 0;
+}
+
+static void
+mptctl_free_tm_flags(MPT_ADAPTER *ioc)
+{
+ MPT_SCSI_HOST * hd;
+ unsigned long flags;
+
+ hd = (MPT_SCSI_HOST *) ioc->sh->hostdata;
+ if (hd == NULL)
+ return;
+
+ spin_lock_irqsave(&ioc->FreeQlock, flags);
+#ifdef MPT_SCSI_USE_NEW_EH
+ hd->tmState = TM_STATE_ERROR;
+ hd->tmPending = 0;
+ spin_unlock_irqrestore(&ioc->FreeQlock, flags);
+#else
+ hd->tmPending = 0;
+ spin_unlock_irqrestore(&ioc->FreeQlock, flags);
+#endif
+
+ return;
+}
- //dprintk((KERN_DEBUG MYNAM ": Got mptctl_reply()!\n"));
- if (req && req->u.hdr.Function == MPI_FUNCTION_SCSI_IO_REQUEST) {
- targ = req->u.scsireq.TargetID & myMAX_T_MASK;
- DevIosCount[targ][0]--;
- } else if (reply && req && req->u.hdr.Function == MPI_FUNCTION_FW_DOWNLOAD) {
- // NOTE: Expects/requires non-Turbo reply!
- dprintk((KERN_INFO MYNAM ": Caching MPI_FUNCTION_FW_DOWNLOAD reply!\n"));
- memcpy(fwReplyBuffer, reply, MIN(sizeof(fwReplyBuffer), 4*reply->u.reply.MsgLength));
- ReplyMsg = (pMPIDefaultReply_t) fwReplyBuffer;
+/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
+/* mptctl_ioc_reset
+ *
+ * Clean-up functionality. Used only if there has been a
+ * reload of the FW due.
+ *
+ */
+static int
+mptctl_ioc_reset(MPT_ADAPTER *ioc, int reset_phase)
+{
+ MPT_IOCTL *ioctl = ioc->ioctl;
+ dctlprintk((KERN_INFO MYNAM ": IOC %s_reset routed to IOCTL driver!\n",
+ reset_phase==MPT_IOC_PRE_RESET ? "pre" : "post"));
+
+ if (reset_phase == MPT_IOC_PRE_RESET){
+
+ /* Someone has called the reset handler to
+ * do a hard reset. No more replies from the FW.
+ * Delete the timer. TM flags cleaned up by SCSI driver.
+ * Do not need to free msg frame, as re-initialized
+ */
+ if (ioctl && (ioctl->status & MPT_IOCTL_STATUS_TIMER_ACTIVE)){
+ del_timer(&ioctl->timer);
+ }
+ if (ioctl && (ioctl->status & MPT_IOCTL_STATUS_TMTIMER_ACTIVE)){
+ ioctl->status &= ~MPT_IOCTL_STATUS_TMTIMER_ACTIVE;
+ del_timer(&ioctl->TMtimer);
+ mpt_free_msg_frame(mptctl_id, ioc->id, ioctl->tmPtr);
+ }
+
+ } else {
+ /* Set the status and continue IOCTL
+ * processing. All memory will be free'd
+ * by originating thread after wake_up is
+ * called.
+ */
+ if (ioctl && (ioctl->status & MPT_IOCTL_STATUS_TIMER_ACTIVE)){
+ ioctl->status = MPT_IOCTL_STATUS_DID_IOCRESET;
+
+ /* Wake up the calling process
+ */
+ ioctl->wait_done = 1;
+ wake_up(&mptctl_wait);
+ }
}
return 1;
/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
/*
- * struct file_operations functionality.
+ * struct file_operations functionality.
* Members:
* llseek, write, read, ioctl, open, release
*/
static ssize_t
mptctl_read(struct file *file, char *buf, size_t count, loff_t *ptr)
{
+ printk(KERN_ERR MYNAM ": ioctl READ not yet supported\n");
return 0;
}
/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
/*
* MPT ioctl handler
+ * cmd - specify the particular IOCTL command to be issued
+ * arg - data specific to the command. Must not be null.
*/
static int
-mpt_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg)
+mptctl_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg)
{
- struct mpt_ioctl_sanity *usanity = (struct mpt_ioctl_sanity *) arg;
- struct mpt_ioctl_sanity ksanity;
+ mpt_ioctl_header *uhdr = (mpt_ioctl_header *) arg;
+ mpt_ioctl_header khdr;
int iocnum;
unsigned iocnumX;
int nonblock = (file->f_flags & O_NONBLOCK);
int ret;
MPT_ADAPTER *iocp = NULL;
- dprintk((KERN_INFO MYNAM "::mpt_ioctl() called\n"));
+ dctlprintk(("mptctl_ioctl() called\n"));
- if (copy_from_user(&ksanity, usanity, sizeof(ksanity))) {
- printk(KERN_ERR "%s::mpt_ioctl() @%d - "
- "Unable to copy mpt_ioctl_sanity data @ %p\n",
- __FILE__, __LINE__, (void*)usanity);
+ if (copy_from_user(&khdr, uhdr, sizeof(khdr))) {
+ printk(KERN_ERR "%s::mptctl_ioctl() @%d - "
+ "Unable to copy mpt_ioctl_header data @ %p\n",
+ __FILE__, __LINE__, (void*)uhdr);
return -EFAULT;
}
ret = -ENXIO; /* (-6) No such device or address */
- /* Verify intended MPT adapter */
- iocnumX = ksanity.iocnum & 0xFF;
+
+ /* Test for Compaq-specific IOCTL's.
+ */
+ if ((cmd == CPQFCTS_GETPCIINFO) || (cmd == CPQFCTS_CTLR_STATUS) ||
+ (cmd == CPQFCTS_GETDRIVER) || (cmd == CPQFCTS_SCSI_PASSTHRU) ||
+ (cmd == CPQFCTS_SCSI_IOCTL_FC_TARGET_ADDRESS))
+ return mptctl_compaq_ioctl(file, cmd, arg);
+
+ /* Verify intended MPT adapter - set iocnum and the adapter
+ * pointer (iocp)
+ */
+ iocnumX = khdr.iocnum & 0xFF;
if (((iocnum = mpt_verify_adapter(iocnumX, &iocp)) < 0) ||
(iocp == NULL)) {
- printk(KERN_ERR "%s::mpt_ioctl() @%d - ioc%d not found!\n",
+ printk(KERN_ERR "%s::mptctl_ioctl() @%d - ioc%d not found!\n",
__FILE__, __LINE__, iocnumX);
return -ENODEV;
}
+
+
+ /* Handle those commands that are just returning
+ * information stored in the driver.
+ * These commands should never time out and are unaffected
+ * by TM and FW reloads.
+ */
+ if ((cmd & ~IOCSIZE_MASK) == (MPTIOCINFO & ~IOCSIZE_MASK)) {
+ return mptctl_getiocinfo(arg, _IOC_SIZE(cmd));
+ } else if (cmd == MPTTARGETINFO) {
+ return mptctl_gettargetinfo(arg);
+ } else if (cmd == MPTTEST) {
+ return mptctl_readtest(arg);
+ } else if (cmd == MPTEVENTQUERY) {
+ return mptctl_eventquery(arg);
+ } else if (cmd == MPTEVENTENABLE) {
+ return mptctl_eventenable(arg);
+ } else if (cmd == MPTEVENTREPORT) {
+ return mptctl_eventreport(arg);
+ } else if (cmd == MPTFWREPLACE) {
+ return mptctl_replace_fw(arg);
+ }
+
+ /* All of these commands require an interrupt or
+ * are unknown/illegal.
+ */
if ((ret = mptctl_syscall_down(iocp, nonblock)) != 0)
return ret;
- dprintk((KERN_INFO MYNAM "::mpt_ioctl() - Using %s\n", iocp->name));
+ dctlprintk((MYIOC_s_INFO_FMT ": mptctl_ioctl()\n", iocp->name));
switch(cmd) {
- case MPTRWPERF:
- ret = mpt_ioctl_rwperf(arg);
- break;
- case MPTRWPERF_CHK:
- ret = mpt_ioctl_rwperf_status(arg);
- break;
- case MPTRWPERF_RESET:
- ret = mpt_ioctl_rwperf_reset(arg);
- break;
case MPTFWDOWNLOAD:
- ret = mpt_ioctl_fw_download(arg);
+ ret = mptctl_fw_download(arg);
+ break;
+ case MPTCOMMAND:
+ ret = mptctl_mpt_command(arg);
break;
- case MPTSCSICMD:
- ret = mpt_ioctl_scsi_cmd(arg);
+ case MPTHARDRESET:
+ ret = mptctl_do_reset(arg);
break;
default:
ret = -EINVAL;
return ret;
}
+static int mptctl_do_reset(unsigned long arg)
+{
+ struct mpt_ioctl_diag_reset *urinfo = (struct mpt_ioctl_diag_reset *) arg;
+ struct mpt_ioctl_diag_reset krinfo;
+ MPT_ADAPTER *iocp;
+
+ dctlprintk((KERN_INFO "mptctl_do_reset called.\n"));
+
+ if (copy_from_user(&krinfo, urinfo, sizeof(struct mpt_ioctl_diag_reset))) {
+ printk(KERN_ERR "%s@%d::mptctl_do_reset - "
+ "Unable to copy mpt_ioctl_diag_reset struct @ %p\n",
+ __FILE__, __LINE__, (void*)urinfo);
+ return -EFAULT;
+ }
+
+ if (mpt_verify_adapter(krinfo.hdr.iocnum, &iocp) < 0) {
+ printk(KERN_ERR "%s@%d::mptctl_do_reset - ioc%d not found!\n",
+ __FILE__, __LINE__, krinfo.hdr.iocnum);
+ return -ENXIO; /* (-6) No such device or address */
+ }
+
+ if (mpt_HardResetHandler(iocp, NO_SLEEP) != 0) {
+ printk (KERN_ERR "%s@%d::mptctl_do_reset - reset failed.\n",
+ __FILE__, __LINE__);
+ return -1;
+ }
+
+ return 0;
+}
+
/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
static int mptctl_open(struct inode *inode, struct file *file)
{
}
/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
+/*
+ * MPT FW download function. Cast the arg into the mpt_fw_xfer structure.
+ * This structure contains: iocnum, firmware length (bytes),
+ * pointer to user space memory where the fw image is stored.
+ *
+ * Outputs: None.
+ * Return: 0 if successful
+ * -EFAULT if data unavailable
+ * -ENXIO if no such device
+ * -EAGAIN if resource problem
+ * -ENOMEM if no memory for SGE
+ * -EMLINK if too many chain buffers required
+ * -EBADRQC if adapter does not support FW download
+ * -EBUSY if adapter is busy
+ * -ENOMSG if FW upload returned bad status
+ */
static int
-mpt_ioctl_fw_download(unsigned long arg)
+mptctl_fw_download(unsigned long arg)
{
struct mpt_fw_xfer *ufwdl = (struct mpt_fw_xfer *) arg;
struct mpt_fw_xfer kfwdl;
- dprintk((KERN_INFO "mpt_ioctl_fwdl called. mptctl_id = %xh\n", mptctl_id)); //tc
+ dctlprintk((KERN_INFO "mptctl_fwdl called. mptctl_id = %xh\n", mptctl_id)); //tc
if (copy_from_user(&kfwdl, ufwdl, sizeof(struct mpt_fw_xfer))) {
printk(KERN_ERR "%s@%d::_ioctl_fwdl - "
"Unable to copy mpt_fw_xfer struct @ %p\n",
return -EFAULT;
}
- return mpt_ioctl_do_fw_download(kfwdl.iocnum, kfwdl.bufp, kfwdl.fwlen);
+ return mptctl_do_fw_download(kfwdl.iocnum, kfwdl.bufp, kfwdl.fwlen);
}
/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
/*
- * MPT FW Download
+ * FW Download engine.
+ * Outputs: None.
+ * Return: 0 if successful
+ * -EFAULT if data unavailable
+ * -ENXIO if no such device
+ * -EAGAIN if resource problem
+ * -ENOMEM if no memory for SGE
+ * -EMLINK if too many chain buffers required
+ * -EBADRQC if adapter does not support FW download
+ * -EBUSY if adapter is busy
+ * -ENOMSG if FW upload returned bad status
*/
static int
-mpt_ioctl_do_fw_download(int ioc, char *ufwbuf, size_t fwlen)
+mptctl_do_fw_download(int ioc, char *ufwbuf, size_t fwlen)
{
FWDownload_t *dlmsg;
MPT_FRAME_HDR *mf;
MPT_ADAPTER *iocp;
-// char *fwbuf;
-// dma_addr_t fwbuf_dma;
- FWDownloadTCSGE_t *fwVoodoo;
-// SGEAllUnion_t *fwSgl;
+ FWDownloadTCSGE_t *ptsge;
+ MptSge_t *sgl, *sgIn;
+ char *sgOut;
+ struct buflist *buflist;
+ struct buflist *bl;
+ dma_addr_t sgl_dma;
int ret;
-
- SGESimple32_t *sgl;
- SGESimple32_t *sgOut, *sgIn;
- dma_addr_t sgl_dma;
- struct buflist *buflist = NULL;
- struct buflist *bl = NULL;
- int numfrags = 0;
- int maxfrags;
- int n = 0;
- u32 sgdir;
- u32 nib;
- int fw_bytes_copied = 0;
- u16 iocstat;
- int i;
-
- dprintk((KERN_INFO "mpt_ioctl_do_fwdl called. mptctl_id = %xh.\n", mptctl_id));
-
- dprintk((KERN_INFO "DbG: kfwdl.bufp = %p\n", ufwbuf));
- dprintk((KERN_INFO "DbG: kfwdl.fwlen = %d\n", (int)fwlen));
- dprintk((KERN_INFO "DbG: kfwdl.ioc = %04xh\n", ioc));
+ int numfrags = 0;
+ int maxfrags;
+ int n = 0;
+ u32 sgdir;
+ u32 nib;
+ int fw_bytes_copied = 0;
+ int i;
+ int cntdn;
+ int sge_offset = 0;
+ u16 iocstat;
+
+ dctlprintk((KERN_INFO "mptctl_do_fwdl called. mptctl_id = %xh.\n", mptctl_id));
+
+ dctlprintk((KERN_INFO "DbG: kfwdl.bufp = %p\n", ufwbuf));
+ dctlprintk((KERN_INFO "DbG: kfwdl.fwlen = %d\n", (int)fwlen));
+ dctlprintk((KERN_INFO "DbG: kfwdl.ioc = %04xh\n", ioc));
if ((ioc = mpt_verify_adapter(ioc, &iocp)) < 0) {
printk("%s@%d::_ioctl_fwdl - ioc%d not found!\n",
return -ENXIO; /* (-6) No such device or address */
}
+ /* Valid device. Get a message frame and construct the FW download message.
+ */
if ((mf = mpt_get_msg_frame(mptctl_id, ioc)) == NULL)
return -EAGAIN;
dlmsg = (FWDownload_t*) mf;
- fwVoodoo = (FWDownloadTCSGE_t *) &dlmsg->SGL;
- sgOut = (SGESimple32_t *) (fwVoodoo + 1);
+ ptsge = (FWDownloadTCSGE_t *) &dlmsg->SGL;
+ sgOut = (char *) (ptsge + 1);
/*
* Construct f/w download request
dlmsg->Reserved1[0] = dlmsg->Reserved1[1] = dlmsg->Reserved1[2] = 0;
dlmsg->MsgFlags = 0;
- fwVoodoo->Reserved = 0;
- fwVoodoo->ContextSize = 0;
- fwVoodoo->DetailsLength = 12;
- fwVoodoo->Flags = MPI_SGE_FLAGS_TRANSACTION_ELEMENT;
- fwVoodoo->Reserved1 = 0;
- fwVoodoo->ImageOffset = 0;
- fwVoodoo->ImageSize = cpu_to_le32(fwlen);
+ /* Set up the Transaction SGE.
+ */
+ ptsge->Reserved = 0;
+ ptsge->ContextSize = 0;
+ ptsge->DetailsLength = 12;
+ ptsge->Flags = MPI_SGE_FLAGS_TRANSACTION_ELEMENT;
+ ptsge->Reserved_0100_Checksum = 0;
+ ptsge->ImageOffset = 0;
+ ptsge->ImageSize = cpu_to_le32(fwlen);
+
+ /* Add the SGL
+ */
/*
* Need to kmalloc area(s) for holding firmware image bytes.
* But we need to do it piece meal, using a proper
* scatter gather list (with 128kB MAX hunks).
- *
+ *
* A practical limit here might be # of sg hunks that fit into
* a single IOC request frame; 12 or 8 (see below), so:
* For FC9xx: 12 x 128kB == 1.5 mB (max)
* For C1030: 8 x 128kB == 1 mB (max)
* We could support chaining, but things get ugly(ier:)
+ *
+ * Set the sge_offset to the start of the sgl (bytes).
*/
sgdir = 0x04000000; /* IOC will READ from sys mem */
- if ((sgl = kbuf_alloc_2_sgl(fwlen, sgdir, &numfrags, &buflist, &sgl_dma, iocp)) == NULL)
+ sge_offset = sizeof(MPIHeader_t) + sizeof(FWDownloadTCSGE_t);
+ if ((sgl = kbuf_alloc_2_sgl(fwlen, sgdir, sge_offset,
+ &numfrags, &buflist, &sgl_dma, iocp)) == NULL)
return -ENOMEM;
/*
* for FC9xx f/w image, but calculate max number of sge hunks
* we can fit into a request frame, and limit ourselves to that.
* (currently no chain support)
- * For FC9xx: (128-12-16)/8 = 12.5 = 12
- * For C1030: (96-12-16)/8 = 8.5 = 8
+ * maxfrags = (Request Size - FWdownload Size ) / Size of 32 bit SGE
+ * Request maxfrags
+ * 128 12
+ * 96 8
+ * 64 4
*/
- maxfrags = (iocp->req_sz - sizeof(MPIHeader_t) - sizeof(FWDownloadTCSGE_t)) / sizeof(SGESimple32_t);
+ maxfrags = (iocp->req_sz - sizeof(MPIHeader_t) - sizeof(FWDownloadTCSGE_t))
+ / (sizeof(dma_addr_t) + sizeof(u32));
if (numfrags > maxfrags) {
ret = -EMLINK;
goto fwdl_out;
}
- dprintk((KERN_INFO "DbG: sgl buffer = %p, sgfrags = %d\n", sgl, numfrags));
+ dctlprintk((KERN_INFO "DbG: sgl buffer = %p, sgfrags = %d\n", sgl, numfrags));
/*
* Parse SG list, copying sgl itself,
sgIn = sgl;
bl = buflist;
for (i=0; i < numfrags; i++) {
- nib = (le32_to_cpu(sgIn->FlagsLength) & 0xF0000000) >> 28;
- /* skip ignore/chain. */
+
+ /* Get the SGE type: 0 - TCSGE, 3 - Chain, 1 - Simple SGE
+ * Skip everything but Simple. If simple, copy from
+ * user space into kernel space.
+ * Note: we should not have anything but Simple as
+ * Chain SGE are illegal.
+ */
+ nib = (sgIn->FlagsLength & 0x30000000) >> 28;
if (nib == 0 || nib == 3) {
;
} else if (sgIn->Address) {
- *sgOut = *sgIn;
+ mpt_add_sge(sgOut, sgIn->FlagsLength, sgIn->Address);
n++;
if (copy_from_user(bl->kptr, ufwbuf+fw_bytes_copied, bl->len)) {
printk(KERN_ERR "%s@%d::_ioctl_fwdl - "
}
sgIn++;
bl++;
- sgOut++;
+ sgOut += (sizeof(dma_addr_t) + sizeof(u32));
}
#ifdef MPT_DEBUG
/*
* Wait until the reply has been received
*/
- {
- int foo = 0;
-
- while (ReplyMsg == NULL) {
- if (!(foo%1000000)) {
- dprintk((KERN_INFO "DbG::_do_fwdl: "
- "In ReplyMsg loop - iteration %d\n",
- foo)); //tc
- }
+ for (cntdn=HZ*60, i=1; ReplyMsg == NULL; cntdn--, i++) {
+ if (!cntdn) {
ret = -ETIME;
- if (++foo > 60000000)
- goto fwdl_out;
- mb();
- schedule();
- barrier();
+ goto fwdl_out;
+ }
+
+ if (!(i%HZ)) {
+ dctlprintk((KERN_INFO "DbG::_do_fwdl: "
+ "In ReplyMsg loop - iteration %d\n",
+ i));
}
+
+ set_current_state(TASK_INTERRUPTIBLE);
+ schedule_timeout(1);
}
if (sgl)
- kfree_sgl(sgl, sgl_dma, buflist, iocp);
+ kfree_sgl(sgl, sgl_dma, buflist, iocp);
iocstat = le16_to_cpu(ReplyMsg->IOCStatus) & MPI_IOCSTATUS_MASK;
if (iocstat == MPI_IOCSTATUS_SUCCESS) {
/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
/*
- * NEW rwperf (read/write performance) stuff starts here...
+ * SGE Allocation routine
+ *
+ * Inputs: bytes - number of bytes to be transferred
+ * sgdir - data direction
+ * sge_offset - offset (in bytes) from the start of the request
+ * frame to the first SGE
+ * ioc - pointer to the mptadapter
+ * Outputs: frags - number of scatter gather elements
+ * blp - point to the buflist pointer
+ * sglbuf_dma - pointer to the (dma) sgl
+ * Returns: Null if failes
+ * pointer to the (virtual) sgl if successful.
*/
-
-/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
-static SGESimple32_t *
-kbuf_alloc_2_sgl(int bytes, u32 sgdir, int *frags,
+static MptSge_t *
+kbuf_alloc_2_sgl(int bytes, u32 sgdir, int sge_offset, int *frags,
struct buflist **blp, dma_addr_t *sglbuf_dma, MPT_ADAPTER *ioc)
{
- SGESimple32_t *sglbuf = NULL;
- struct buflist *buflist = NULL;
+ MptSge_t *sglbuf = NULL; /* pointer to array of SGE */
+ /* and chain buffers */
+ struct buflist *buflist = NULL; /* kernel routine */
+ MptSge_t *sgl;
int numfrags = 0;
int fragcnt = 0;
int alloc_sz = MIN(bytes,MAX_KMALLOC_SZ); // avoid kernel warning msg!
int bytes_allocd = 0;
int this_alloc;
- SGESimple32_t *sgl;
- u32 pa; // phys addr
- SGEChain32_t *last_chain = NULL;
- SGEChain32_t *old_chain = NULL;
- int chaincnt = 0;
+ dma_addr_t pa; // phys addr
int i, buflist_ent;
int sg_spill = MAX_FRAGS_SPILL1;
int dir;
-
+ /* initialization */
*frags = 0;
*blp = NULL;
+
+ /* Allocate and initialize an array of kernel
+ * structures for the SG elements.
+ */
i = MAX_SGL_BYTES / 8;
buflist = kmalloc(i, GFP_USER);
if (buflist == NULL)
memset(buflist, 0, i);
buflist_ent = 0;
+ /* Allocate a single block of memory to store the sg elements and
+ * the chain buffers. The calling routine is responsible for
+ * copying the data in this array into the correct place in the
+ * request and chain buffers.
+ */
sglbuf = pci_alloc_consistent(ioc->pcidev, MAX_SGL_BYTES, sglbuf_dma);
if (sglbuf == NULL)
goto free_and_fail;
else
dir = PCI_DMA_FROMDEVICE;
+ /* At start:
+ * sgl = sglbuf = point to beginning of sg buffer
+ * buflist_ent = 0 = first kernel structure
+ * sg_spill = number of SGE that can be written before the first
+ * chain element.
+ *
+ */
sgl = sglbuf;
+ sg_spill = ((ioc->req_sz - sge_offset)/(sizeof(dma_addr_t) + sizeof(u32))) - 1;
while (bytes_allocd < bytes) {
this_alloc = MIN(alloc_sz, bytes-bytes_allocd);
buflist[buflist_ent].len = this_alloc;
dma_addr_t dma_addr;
bytes_allocd += this_alloc;
-
- /* Write one SIMPLE sge */
- sgl->FlagsLength = cpu_to_le32(0x10000000|sgdir|this_alloc);
+ sgl->FlagsLength = (0x10000000|MPT_SGE_FLAGS_ADDRESSING|sgdir|this_alloc);
dma_addr = pci_map_single(ioc->pcidev, buflist[buflist_ent].kptr, this_alloc, dir);
- sgl->Address = cpu_to_le32(dma_addr);
+ sgl->Address = dma_addr;
fragcnt++;
numfrags++;
/* Need to chain? */
if (fragcnt == sg_spill) {
- dma_addr_t chain_link;
-
- if (last_chain != NULL)
- last_chain->NextChainOffset = 0x1E;
-
- fragcnt = 0;
- sg_spill = MAX_FRAGS_SPILL2;
-
- /* fixup previous SIMPLE sge */
- sgl[-1].FlagsLength |= cpu_to_le32(0x80000000);
-
- chain_link = (*sglbuf_dma) +
- ((u8 *)(sgl+1) - (u8 *)sglbuf);
-
- /* Write one CHAIN sge */
- sgl->FlagsLength = cpu_to_le32(0x30000080);
- sgl->Address = cpu_to_le32(chain_link);
-
- old_chain = last_chain;
- last_chain = (SGEChain32_t*)sgl;
- chaincnt++;
- numfrags++;
- sgl++;
+ printk(KERN_WARNING MYNAM "-SG: No can do - " "Chain required! :-(\n");
+ printk(KERN_WARNING MYNAM "(freeing %d frags)\n", numfrags);
+ goto free_and_fail;
}
/* overflow check... */
- if (numfrags*8 > MAX_SGL_BYTES) {
+ if (numfrags*8 > MAX_SGL_BYTES){
/* GRRRRR... */
printk(KERN_WARNING MYNAM "-SG: No can do - "
"too many SG frags! :-(\n");
}
/* Last sge fixup: set LE+eol+eob bits */
- sgl[-1].FlagsLength |= cpu_to_le32(0xC1000000);
-
- /* Chain fixup needed? */
- if (last_chain != NULL && fragcnt < 16)
- last_chain->Length = cpu_to_le16(fragcnt * 8);
+ sgl[-1].FlagsLength |= 0xC1000000;
*frags = numfrags;
*blp = buflist;
- dprintk((KERN_INFO MYNAM "-SG: kbuf_alloc_2_sgl() - "
- "%d SG frags generated! (%d CHAIN%s)\n",
- numfrags, chaincnt, chaincnt>1?"s":""));
+ dctlprintk((KERN_INFO MYNAM "-SG: kbuf_alloc_2_sgl() - "
+ "%d SG frags generated!\n",
+ numfrags));
- dprintk((KERN_INFO MYNAM "-SG: kbuf_alloc_2_sgl() - "
+ dctlprintk((KERN_INFO MYNAM "-SG: kbuf_alloc_2_sgl() - "
"last (big) alloc_sz=%d\n",
alloc_sz));
u8 *kptr;
int len;
- if ((le32_to_cpu(sglbuf[i].FlagsLength) >> 24) == 0x30)
+ if ((sglbuf[i].FlagsLength >> 24) == 0x30)
continue;
- dma_addr = le32_to_cpu(sglbuf[i].Address);
+ dma_addr = sglbuf[i].Address;
kptr = buflist[i].kptr;
len = buflist[i].len;
}
/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
+/*
+ * Routine to free the SGL elements.
+ */
static void
-kfree_sgl(SGESimple32_t *sgl, dma_addr_t sgl_dma, struct buflist *buflist, MPT_ADAPTER *ioc)
+kfree_sgl(MptSge_t *sgl, dma_addr_t sgl_dma, struct buflist *buflist, MPT_ADAPTER *ioc)
{
- SGESimple32_t *sg = sgl;
+ MptSge_t *sg = sgl;
struct buflist *bl = buflist;
u32 nib;
int dir;
int n = 0;
- if (le32_to_cpu(sg->FlagsLength) & 0x04000000)
+ if (sg->FlagsLength & 0x04000000)
dir = PCI_DMA_TODEVICE;
else
dir = PCI_DMA_FROMDEVICE;
- nib = (le32_to_cpu(sg->FlagsLength) & 0xF0000000) >> 28;
+ nib = (sg->FlagsLength & 0xF0000000) >> 28;
while (! (nib & 0x4)) { /* eob */
/* skip ignore/chain. */
if (nib == 0 || nib == 3) {
void *kptr;
int len;
- dma_addr = le32_to_cpu(sg->Address);
+ dma_addr = sg->Address;
kptr = bl->kptr;
len = bl->len;
pci_unmap_single(ioc->pcidev, dma_addr, len, dir);
void *kptr;
int len;
- dma_addr = le32_to_cpu(sg->Address);
+ dma_addr = sg->Address;
kptr = bl->kptr;
len = bl->len;
pci_unmap_single(ioc->pcidev, dma_addr, len, dir);
pci_free_consistent(ioc->pcidev, MAX_SGL_BYTES, sgl, sgl_dma);
kfree(buflist);
- dprintk((KERN_INFO MYNAM "-SG: Free'd 1 SGL buf + %d kbufs!\n", n));
+ dctlprintk((KERN_INFO MYNAM "-SG: Free'd 1 SGL buf + %d kbufs!\n", n));
}
/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
+/*
+ * mptctl_getiocinfo - Query the host adapter for IOC information.
+ * @arg: User space argument
+ *
+ * Outputs: None.
+ * Return: 0 if successful
+ * -EFAULT if data unavailable
+ * -ENODEV if no such device/adapter
+ */
static int
-mpt_ioctl_rwperf_init(struct mpt_raw_r_w *dest, unsigned long src,
- char *caller, MPT_ADAPTER **iocpp)
+mptctl_getiocinfo (unsigned long arg, unsigned int data_size)
{
- char *myname = "_rwperf_init()";
- int ioc;
-
- /* get copy of structure passed from user space */
- if (copy_from_user(dest, (void*)src, sizeof(*dest))) {
- printk(KERN_ERR MYNAM "::%s() @%d - Can't copy mpt_raw_r_w data @ %p\n",
- myname, __LINE__, (void*)src);
- return -EFAULT; /* (-14) Bad address */
- } else {
- dprintk((KERN_INFO MYNAM "-perf: PerfInfo.{ioc,targ,qd,iters,nblks}"
- ": %d %d %d %d %d\n",
- dest->iocnum, dest->target,
- (int)dest->qdepth, dest->iters, dest->nblks ));
- dprintk((KERN_INFO MYNAM "-perf: PerfInfo.{cache,skip,range,rdwr,seqran}"
- ": %d %d %d %d %d\n",
- dest->cache_sz, dest->skip, dest->range,
- dest->rdwr, dest->seqran ));
-
- /* Get the MPT adapter id. */
- if ((ioc = mpt_verify_adapter(dest->iocnum, iocpp)) < 0) {
- printk(KERN_ERR MYNAM "::%s() @%d - ioc%d not found!\n",
- myname, __LINE__, dest->iocnum);
- return -ENXIO; /* (-6) No such device or address */
- } else {
- dprintk((MYNAM "-perf: %s using mpt/ioc%x, target %02xh\n",
- caller, dest->iocnum, dest->target));
- }
- }
+ struct mpt_ioctl_iocinfo *uarg = (struct mpt_ioctl_iocinfo *) arg;
+ struct mpt_ioctl_iocinfo karg;
+ MPT_ADAPTER *ioc;
+ struct pci_dev *pdev;
+ struct Scsi_Host *sh;
+ MPT_SCSI_HOST *hd;
+ int iocnum;
+ int numDevices = 0;
+ unsigned int max_id;
+ int ii;
+ int port;
+ int cim_rev;
+ u8 revision;
+
+ dctlprintk((": mptctl_getiocinfo called.\n"));
+ if (data_size == sizeof(struct mpt_ioctl_iocinfo))
+ cim_rev = 1;
+ else if (data_size == (sizeof(struct mpt_ioctl_iocinfo) - sizeof(struct mpt_ioctl_pci_info)))
+ cim_rev = 0;
+ else
+ return -EFAULT;
- return ioc;
-}
+ if (copy_from_user(&karg, uarg, data_size)) {
+ printk(KERN_ERR "%s@%d::mptctl_getiocinfo - "
+ "Unable to read in mpt_ioctl_iocinfo struct @ %p\n",
+ __FILE__, __LINE__, (void*)uarg);
+ return -EFAULT;
+ }
-/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
+ if (((iocnum = mpt_verify_adapter(karg.hdr.iocnum, &ioc)) < 0) ||
+ (ioc == NULL)) {
+ printk(KERN_ERR "%s::mptctl_getiocinfo() @%d - ioc%d not found!\n",
+ __FILE__, __LINE__, iocnum);
+ return -ENODEV;
+ }
-/* Treat first N blocks of disk as sacred! */
-#define SACRED_BLOCKS 100
+ /* Verify the data transfer size is correct.
+ * Ignore the port setting.
+ */
+ if (karg.hdr.maxDataSize != data_size) {
+ printk(KERN_ERR "%s@%d::mptctl_getiocinfo - "
+ "Structure size mismatch. Command not completed.\n",
+ __FILE__, __LINE__);
+ return -EFAULT;
+ }
-/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
-static int
-mpt_ioctl_rwperf(unsigned long arg)
-{
- struct mpt_raw_r_w kPerfInfo;
- /* NOTE: local copy, on stack==KERNEL_SPACE! */
- u8 target, targetM;
- u8 lun, lunM;
- u8 scsiop;
- int qdepth;
- int iters;
- int cache_sz;
- u32 xferbytes;
- u32 scsidir;
- u32 qtag;
- u32 scsictl;
- u32 sgdir;
- u32 blkno;
- u32 sbphys;
- SGESimple32_t *sgl;
- dma_addr_t sgl_dma;
- struct buflist *buflist;
- SGESimple32_t *sgOut, *sgIn;
- int numfrags;
- u32 *msg;
- int i;
- int ioc;
- MPT_FRAME_HDR *mf;
- MPT_ADAPTER *iocp;
- int sgfragcpycnt;
- int blklo, blkhi;
- u8 nextchainoffset;
- u8 *SenseBuf;
- dma_addr_t SenseBufDMA;
- char *myname = "_rwperf()";
-
- dprintk((KERN_INFO "%s - starting...\n", myname));
+ /* Fill in the data and return the structure to the calling
+ * program
+ */
+ if (ioc->chip_type == C1030)
+ karg.adapterType = MPT_IOCTL_INTERFACE_SCSI;
+ else
+ karg.adapterType = MPT_IOCTL_INTERFACE_FC;
- /* Validate target device */
- if ((ioc = mpt_ioctl_rwperf_init(&kPerfInfo, arg, myname, &iocp)) < 0)
- return ioc;
-
- /* Allocate DMA'able memory for the sense buffer. */
- SenseBuf = pci_alloc_consistent(iocp->pcidev, 256, &SenseBufDMA);
-
- /* set perf parameters from input */
- target = kPerfInfo.target & 0x0FF;
- targetM = target & myMAX_T_MASK;
- lun = kPerfInfo.lun & 0x1F; // LUN=31 max
- lunM = lun & myMAX_L_MASK;
- qdepth = kPerfInfo.qdepth;
- iters = kPerfInfo.iters;
- xferbytes = ((u32)kPerfInfo.nblks)<<9;
-
- DevInUse[targetM][lunM] = 1;
- DevIosCount[targetM][lunM] = 0;
-
- cache_sz = kPerfInfo.cache_sz * 1024; // CacheSz in kB!
-
- /* ToDo: */
- /* get capacity (?) */
-
-
- // pre-build, one time, everything we can for speed in the loops below...
-
- scsiop = 0x28; // default to SCSI READ!
- scsidir = MPI_SCSIIO_CONTROL_READ; // DATA IN (host<--ioc<--dev)
- // 02000000
- qtag = MPI_SCSIIO_CONTROL_SIMPLEQ; // 00000000
-
- if (xferbytes == 0) {
- // Do 0-byte READ!!!
- // IMPORTANT! Need to set no SCSI DIR for this!
- scsidir = MPI_SCSIIO_CONTROL_NODATATRANSFER;
- }
-
- scsictl = scsidir | qtag;
-
- /*
- * Set sgdir for DMA transfer.
- */
-// sgdir = 0x04000000; // SCSI WRITE
- sgdir = 0x00000000; // SCSI READ
-
- if ((sgl = kbuf_alloc_2_sgl(MAX(512,xferbytes), sgdir, &numfrags, &buflist, &sgl_dma, iocp)) == NULL)
- return -ENOMEM;
-
- sgfragcpycnt = MIN(10,numfrags);
- nextchainoffset = 0;
- if (numfrags > 10)
- nextchainoffset = 0x1E;
-
- sbphys = SenseBufDMA;
-
- rwperf_reset = 0;
-
-// do { // target-loop
-
- blkno = SACRED_BLOCKS; // Treat first N blocks as sacred!
- // FIXME! Skip option
- blklo = blkno;
- blkhi = blkno;
-
- do { // inner-loop
-
- while ((mf = mpt_get_msg_frame(mptctl_id, ioc)) == NULL) {
- mb();
- schedule();
- barrier();
- }
- msg = (u32*)mf;
-
- /* Start piecing the SCSIIORequest together */
- msg[0] = 0x00000000 | nextchainoffset<<16 | target;
- msg[1] = 0x0000FF0A; // 255 sense bytes, 10-byte CDB!
- msg[3] = lun << 8;
- msg[4] = 0;
- msg[5] = scsictl;
-
- // 16 bytes of CDB @ msg[6,7,8,9] are below...
-
- msg[6] = ( ((blkno & 0xFF000000) >> 8)
- | ((blkno & 0x00FF0000) << 8)
- | scsiop );
- msg[7] = ( (((u32)kPerfInfo.nblks & 0x0000FF00) << 16)
- | ((blkno & 0x000000FF) << 8)
- | ((blkno & 0x0000FF00) >> 8) );
- msg[8] = (kPerfInfo.nblks & 0x00FF);
- msg[9] = 0;
-
- msg[10] = xferbytes;
-
-// msg[11] = 0xD0000100;
-// msg[12] = sbphys;
-// msg[13] = 0;
- msg[11] = sbphys;
-
- // Copy the SGL...
- if (xferbytes) {
- sgOut = (SGESimple32_t*)&msg[12];
- sgIn = sgl;
- for (i=0; i < sgfragcpycnt; i++)
- *sgOut++ = *sgIn++;
- }
-
- // fubar! QueueDepth issue!!!
- while ( !rwperf_reset
- && (DevIosCount[targetM][lunM] >= MIN(qdepth,64)) )
- {
- mb();
- schedule();
- barrier();
- }
-
-// blkno += kPerfInfo.nblks;
-// EXP Stuff!
-// Try optimizing to certain cache size for the target!
-// by keeping blkno within cache range if at all possible
-#if 0
- if ( cache_sz
- && ((2 * kPerfInfo.nblks) <= (cache_sz>>9))
- && ((blkno + kPerfInfo.nblks) > ((cache_sz>>9) + SACRED_BLOCKS)) )
- blkno = SACRED_BLOCKS;
- else
- blkno += kPerfInfo.nblks;
-#endif
-// Ok, cheat!
- if (cache_sz && ((blkno + kPerfInfo.nblks) > ((cache_sz>>9) + SACRED_BLOCKS)) )
- blkno = SACRED_BLOCKS;
- else
- blkno += kPerfInfo.nblks;
+ port = karg.hdr.port;
- if (blkno > blkhi)
- blkhi = blkno;
+ karg.port = port;
+ pdev = (struct pci_dev *) ioc->pcidev;
- DevIosCount[targetM][lunM]++;
+ karg.pciId = pdev->device;
+ pci_read_config_byte(pdev, PCI_CLASS_REVISION, &revision);
+ karg.hwRev = revision;
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0)
+ karg.subSystemDevice = pdev->subsystem_device;
+ karg.subSystemVendor = pdev->subsystem_vendor;
+#endif
- /*
- * Finally, post the request
- */
- mpt_put_msg_frame(mptctl_id, ioc, mf);
+ if (cim_rev == 1) {
+ /* Get the PCI bus, device, and function numbers for the IOC
+ */
+ karg.pciInfo.u.bits.busNumber = pdev->bus->number;
+ karg.pciInfo.u.bits.deviceNumber = PCI_SLOT( pdev->devfn );
+ karg.pciInfo.u.bits.functionNumber = PCI_FUNC( pdev->devfn );
+ }
+ /* Get number of devices
+ */
+ if ((sh = ioc->sh) != NULL) {
+ /* sh->max_id = maximum target ID + 1
+ */
+ max_id = sh->max_id - 1;
+ hd = (MPT_SCSI_HOST *) sh->hostdata;
+
+ /* Check all of the target structures and
+ * keep a counter.
+ */
+ if (hd && hd->Targets) {
+ for (ii = 0; ii <= max_id; ii++) {
+ if (hd->Targets[ii])
+ numDevices++;
+ }
+ }
+ }
+ karg.numDevices = numDevices;
- /* let linux breath! */
- mb();
- schedule();
- barrier();
+ /* Set the BIOS and FW Version
+ */
+ karg.FWVersion = ioc->facts.FWVersion.Word;
+ karg.BIOSVersion = ioc->biosVersion;
- //dprintk((KERN_DEBUG MYNAM "-perf: inner-loop, cnt=%d\n", iters));
+ /* Set the Version Strings.
+ */
+ strncpy (karg.driverVersion, MPT_LINUX_PACKAGE_NAME, MPT_IOCTL_VERSION_LENGTH);
- } while ((--iters > 0) && !rwperf_reset);
+ karg.busChangeEvent = 0;
+ karg.hostId = ioc->pfacts[port].PortSCSIID;
+ karg.rsvd[0] = karg.rsvd[1] = 0;
- dprintk((KERN_INFO MYNAM "-perf: DbG: blklo=%d, blkhi=%d\n", blklo, blkhi));
- dprintk((KERN_INFO MYNAM "-perf: target-loop, thisTarget=%d\n", target));
+ /* Copy the data from kernel memory to user memory
+ */
+ if (copy_to_user((char *)arg, &karg, data_size)) {
+ printk(KERN_ERR "%s@%d::mptctl_getiocinfo - "
+ "Unable to write out mpt_ioctl_iocinfo struct @ %p\n",
+ __FILE__, __LINE__, (void*)uarg);
+ return -EFAULT;
+ }
-// // TEMPORARY!
-// target = 0;
+ return 0;
+}
-// } while (target);
+/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
+/*
+ * mptctl_gettargetinfo - Query the host adapter for target information.
+ * @arg: User space argument
+ *
+ * Outputs: None.
+ * Return: 0 if successful
+ * -EFAULT if data unavailable
+ * -ENODEV if no such device/adapter
+ */
+static int
+mptctl_gettargetinfo (unsigned long arg)
+{
+ struct mpt_ioctl_targetinfo *uarg = (struct mpt_ioctl_targetinfo *) arg;
+ struct mpt_ioctl_targetinfo karg;
+ MPT_ADAPTER *ioc;
+ struct Scsi_Host *sh;
+ MPT_SCSI_HOST *hd;
+ char *pmem;
+ int *pdata;
+ int iocnum;
+ int numDevices = 0;
+ unsigned int max_id;
+ int ii, jj, lun;
+ int maxWordsLeft;
+ int numBytes;
+ u8 port;
+
+ dctlprintk(("mptctl_gettargetinfo called.\n"));
+ if (copy_from_user(&karg, uarg, sizeof(struct mpt_ioctl_targetinfo))) {
+ printk(KERN_ERR "%s@%d::mptctl_gettargetinfo - "
+ "Unable to read in mpt_ioctl_targetinfo struct @ %p\n",
+ __FILE__, __LINE__, (void*)uarg);
+ return -EFAULT;
+ }
+ if (((iocnum = mpt_verify_adapter(karg.hdr.iocnum, &ioc)) < 0) ||
+ (ioc == NULL)) {
+ printk(KERN_ERR "%s::mptctl_gettargetinfo() @%d - ioc%d not found!\n",
+ __FILE__, __LINE__, iocnum);
+ return -ENODEV;
+ }
- if (DevIosCount[targetM][lunM]) {
- dprintk((KERN_INFO " DbG: DevIosCount[%d][%d]=%d\n",
- targetM, lunM, DevIosCount[targetM][lunM]));
- }
+ /* Get the port number and set the maximum number of bytes
+ * in the returned structure.
+ * Ignore the port setting.
+ */
+ numBytes = karg.hdr.maxDataSize - sizeof(mpt_ioctl_header);
+ maxWordsLeft = numBytes/sizeof(int);
+ port = karg.hdr.port;
- while (DevIosCount[targetM][lunM]) {
- //dprintk((KERN_DEBUG " DbG: Waiting... DevIosCount[%d][%d]=%d\n",
- // targetM, lunM, DevIosCount[targetM][lunM]));
- mb();
- schedule();
- barrier();
- }
- DevInUse[targetM][lunM] = 0;
+ if (maxWordsLeft <= 0) {
+ printk(KERN_ERR "%s::mptctl_gettargetinfo() @%d - no memory available!\n",
+ __FILE__, __LINE__);
+ return -ENOMEM;
+ }
- pci_free_consistent(iocp->pcidev, 256, SenseBuf, SenseBufDMA);
+ /* Fill in the data and return the structure to the calling
+ * program
+ */
- if (sgl)
- kfree_sgl(sgl, sgl_dma, buflist, iocp);
+ /* struct mpt_ioctl_targetinfo does not contain sufficient space
+ * for the target structures so when the IOCTL is called, there is
+ * not sufficient stack space for the structure. Allocate memory,
+ * populate the memory, copy back to the user, then free memory.
+ * targetInfo format:
+ * bits 31-24: reserved
+ * 23-16: LUN
+ * 15- 8: Bus Number
+ * 7- 0: Target ID
+ */
+ pmem = kmalloc(numBytes, GFP_KERNEL);
+ if (pmem == NULL) {
+ printk(KERN_ERR "%s::mptctl_gettargetinfo() @%d - no memory available!\n",
+ __FILE__, __LINE__);
+ return -ENOMEM;
+ }
+ memset(pmem, 0, numBytes);
+ pdata = (int *) pmem;
+
+ /* Get number of devices
+ */
+ if ( (sh = ioc->sh) != NULL) {
+
+ max_id = sh->max_id - 1;
+ hd = (MPT_SCSI_HOST *) sh->hostdata;
+
+ /* Check all of the target structures.
+ * Save the Id and increment the counter,
+ * if ptr non-null.
+ * sh->max_id = maximum target ID + 1
+ */
+ if (hd && hd->Targets) {
+ ii = 0;
+ while (ii <= max_id) {
+ if (hd->Targets[ii]) {
+ for (jj = 0; jj <= MPT_LAST_LUN; jj++) {
+ lun = (1 << jj);
+ if (hd->Targets[ii]->luns & lun) {
+ numDevices++;
+ *pdata = (jj << 16) | ii;
+ --maxWordsLeft;
+
+ pdata++;
+
+ if (maxWordsLeft <= 0) {
+ break;
+ }
+ }
+ }
+ }
+ ii++;
+ }
+ }
+ }
+ karg.numDevices = numDevices;
- dprintk((KERN_INFO " *** done ***\n"));
+ /* Copy part of the data from kernel memory to user memory
+ */
+ if (copy_to_user((char *)arg, &karg,
+ sizeof(struct mpt_ioctl_targetinfo))) {
+ printk(KERN_ERR "%s@%d::mptctl_gettargetinfo - "
+ "Unable to write out mpt_ioctl_targetinfo struct @ %p\n",
+ __FILE__, __LINE__, (void*)uarg);
+ kfree(pmem);
+ return -EFAULT;
+ }
- return 0;
-}
+ /* Copy the remaining data from kernel memory to user memory
+ */
+ if (copy_to_user((char *) uarg->targetInfo, pmem, numBytes)) {
+ printk(KERN_ERR "%s@%d::mptctl_gettargetinfo - "
+ "Unable to write out mpt_ioctl_targetinfo struct @ %p\n",
+ __FILE__, __LINE__, (void*)pdata);
+ kfree(pmem);
+ return -EFAULT;
+ }
-/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
-static int
-mpt_ioctl_rwperf_status(unsigned long arg)
-{
- struct mpt_raw_r_w kPerfInfo;
- /* NOTE: local copy, on stack==KERNEL_SPACE! */
- MPT_ADAPTER *iocp;
- int ioc;
-// u8 targ;
-// u8 lun;
- int T, L;
- char *myname = "_rwperf_status()";
-
-
- dprintk((KERN_INFO "%s - starting...\n", myname));
-
- /* Get a pointer to the MPT adapter. */
- if ((ioc = mpt_ioctl_rwperf_init(&kPerfInfo, arg, myname, &iocp)) < 0)
- return ioc;
-
- /* set perf parameters from input */
-// targ = kPerfInfo.target & 0xFF;
-// lun = kPerfInfo.lun & 0x1F;
-
- for (T=0; T < myMAX_TARGETS; T++)
- for (L=0; L < myMAX_LUNS; L++)
- if (DevIosCount[T][L]) {
- printk(KERN_INFO "%s: ioc%d->00:%02x:%02x"
- ", IosCnt=%d\n",
- myname, ioc, T, L, DevIosCount[T][L] );
- }
+ kfree(pmem);
return 0;
}
/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
+/* MPT IOCTL Test function.
+ *
+ * Outputs: None.
+ * Return: 0 if successful
+ * -EFAULT if data unavailable
+ * -ENODEV if no such device/adapter
+ */
static int
-mpt_ioctl_rwperf_reset(unsigned long arg)
+mptctl_readtest (unsigned long arg)
{
- struct mpt_raw_r_w kPerfInfo;
- /* NOTE: local copy, on stack==KERNEL_SPACE! */
- MPT_ADAPTER *iocp;
- int ioc;
-// u8 targ;
-// u8 lun;
- int T, L;
- int i;
- char *myname = "_rwperf_reset()";
-
- dprintk((KERN_INFO "%s - starting...\n", myname));
-
- /* Get MPT adapter id. */
- if ((ioc = mpt_ioctl_rwperf_init(&kPerfInfo, arg, myname, &iocp)) < 0)
- return ioc;
-
- /* set perf parameters from input */
-// targ = kPerfInfo.target & 0xFF;
-// lun = kPerfInfo.lun & 0x1F;
-
- rwperf_reset = 1;
- for (i=0; i < 1000000; i++) {
- mb();
- schedule();
- barrier();
- }
- rwperf_reset = 0;
-
- for (T=0; T < myMAX_TARGETS; T++)
- for (L=0; L < myMAX_LUNS; L++)
- if (DevIosCount[T][L]) {
- printk(KERN_INFO "%s: ioc%d->00:%02x:%02x, "
- "IosCnt RESET! (from %d to 0)\n",
- myname, ioc, T, L, DevIosCount[T][L] );
- DevIosCount[T][L] = 0;
- DevInUse[T][L] = 0;
- }
+ struct mpt_ioctl_test *uarg = (struct mpt_ioctl_test *) arg;
+ struct mpt_ioctl_test karg;
+ MPT_ADAPTER *ioc;
+ int iocnum;
+
+ dctlprintk(("mptctl_readtest called.\n"));
+ if (copy_from_user(&karg, uarg, sizeof(struct mpt_ioctl_test))) {
+ printk(KERN_ERR "%s@%d::mptctl_readtest - "
+ "Unable to read in mpt_ioctl_test struct @ %p\n",
+ __FILE__, __LINE__, (void*)uarg);
+ return -EFAULT;
+ }
+
+ if (((iocnum = mpt_verify_adapter(karg.hdr.iocnum, &ioc)) < 0) ||
+ (ioc == NULL)) {
+ printk(KERN_ERR "%s::mptctl_readtest() @%d - ioc%d not found!\n",
+ __FILE__, __LINE__, iocnum);
+ return -ENODEV;
+ }
+
+ /* Fill in the data and return the structure to the calling
+ * program
+ */
+
+#ifdef MFCNT
+ karg.chip_type = ioc->mfcnt;
+#else
+ karg.chip_type = ioc->chip_type;
+#endif
+ strncpy (karg.name, ioc->name, MPT_MAX_NAME);
+ strncpy (karg.product, ioc->prod_name, MPT_PRODUCT_LENGTH);
+
+ /* Copy the data from kernel memory to user memory
+ */
+ if (copy_to_user((char *)arg, &karg, sizeof(struct mpt_ioctl_test))) {
+ printk(KERN_ERR "%s@%d::mptctl_readtest - "
+ "Unable to write out mpt_ioctl_test struct @ %p\n",
+ __FILE__, __LINE__, (void*)uarg);
+ return -EFAULT;
+ }
+
+ return 0;
+}
+
+/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
+/*
+ * mptctl_eventquery - Query the host adapter for the event types
+ * that are being logged.
+ * @arg: User space argument
+ *
+ * Outputs: None.
+ * Return: 0 if successful
+ * -EFAULT if data unavailable
+ * -ENODEV if no such device/adapter
+ */
+static int
+mptctl_eventquery (unsigned long arg)
+{
+ struct mpt_ioctl_eventquery *uarg = (struct mpt_ioctl_eventquery *) arg;
+ struct mpt_ioctl_eventquery karg;
+ MPT_ADAPTER *ioc;
+ int iocnum;
+
+ dctlprintk(("mptctl_eventquery called.\n"));
+ if (copy_from_user(&karg, uarg, sizeof(struct mpt_ioctl_eventquery))) {
+ printk(KERN_ERR "%s@%d::mptctl_eventquery - "
+ "Unable to read in mpt_ioctl_eventquery struct @ %p\n",
+ __FILE__, __LINE__, (void*)uarg);
+ return -EFAULT;
+ }
+
+ if (((iocnum = mpt_verify_adapter(karg.hdr.iocnum, &ioc)) < 0) ||
+ (ioc == NULL)) {
+ printk(KERN_ERR "%s::mptctl_eventquery() @%d - ioc%d not found!\n",
+ __FILE__, __LINE__, iocnum);
+ return -ENODEV;
+ }
+
+ karg.eventEntries = ioc->eventLogSize;
+ karg.eventTypes = ioc->eventTypes;
+
+ /* Copy the data from kernel memory to user memory
+ */
+ if (copy_to_user((char *)arg, &karg, sizeof(struct mpt_ioctl_eventquery))) {
+ printk(KERN_ERR "%s@%d::mptctl_eventquery - "
+ "Unable to write out mpt_ioctl_eventquery struct @ %p\n",
+ __FILE__, __LINE__, (void*)uarg);
+ return -EFAULT;
+ }
+ return 0;
+}
+
+/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
+static int
+mptctl_eventenable (unsigned long arg)
+{
+ struct mpt_ioctl_eventenable *uarg = (struct mpt_ioctl_eventenable *) arg;
+ struct mpt_ioctl_eventenable karg;
+ MPT_ADAPTER *ioc;
+ int iocnum;
+
+ dctlprintk(("mptctl_eventenable called.\n"));
+ if (copy_from_user(&karg, uarg, sizeof(struct mpt_ioctl_eventenable))) {
+ printk(KERN_ERR "%s@%d::mptctl_eventenable - "
+ "Unable to read in mpt_ioctl_eventenable struct @ %p\n",
+ __FILE__, __LINE__, (void*)uarg);
+ return -EFAULT;
+ }
+
+ if (((iocnum = mpt_verify_adapter(karg.hdr.iocnum, &ioc)) < 0) ||
+ (ioc == NULL)) {
+ printk(KERN_ERR "%s::mptctl_eventenable() @%d - ioc%d not found!\n",
+ __FILE__, __LINE__, iocnum);
+ return -ENODEV;
+ }
+
+ if (ioc->events == NULL) {
+ /* Have not yet allocated memory - do so now.
+ */
+ int sz = MPTCTL_EVENT_LOG_SIZE * sizeof(MPT_IOCTL_EVENTS);
+ ioc->events = kmalloc(sz, GFP_KERNEL);
+ if (ioc->events == NULL) {
+ printk(KERN_ERR MYNAM ": ERROR - Insufficient memory to add adapter!\n");
+ return -ENOMEM;
+ }
+ memset(ioc->events, 0, sz);
+ ioc->alloc_total += sz;
+
+ ioc->eventLogSize = MPTCTL_EVENT_LOG_SIZE;
+ ioc->eventContext = 0;
+ }
+
+ /* Update the IOC event logging flag.
+ */
+ ioc->eventTypes = karg.eventTypes;
+
+ return 0;
+}
+
+/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
+static int
+mptctl_eventreport (unsigned long arg)
+{
+ struct mpt_ioctl_eventreport *uarg = (struct mpt_ioctl_eventreport *) arg;
+ struct mpt_ioctl_eventreport karg;
+ MPT_ADAPTER *ioc;
+ int iocnum;
+ int numBytes, maxEvents, max;
+
+ dctlprintk(("mptctl_eventreport called.\n"));
+ if (copy_from_user(&karg, uarg, sizeof(struct mpt_ioctl_eventreport))) {
+ printk(KERN_ERR "%s@%d::mptctl_eventreport - "
+ "Unable to read in mpt_ioctl_eventreport struct @ %p\n",
+ __FILE__, __LINE__, (void*)uarg);
+ return -EFAULT;
+ }
+
+ if (((iocnum = mpt_verify_adapter(karg.hdr.iocnum, &ioc)) < 0) ||
+ (ioc == NULL)) {
+ printk(KERN_ERR "%s::mptctl_eventreport() @%d - ioc%d not found!\n",
+ __FILE__, __LINE__, iocnum);
+ return -ENODEV;
+ }
+
+ numBytes = karg.hdr.maxDataSize - sizeof(mpt_ioctl_header);
+ maxEvents = numBytes/sizeof(MPT_IOCTL_EVENTS);
+
+
+ max = ioc->eventLogSize < maxEvents ? ioc->eventLogSize : maxEvents;
+
+ /* If fewer than 1 event is requested, there must have
+ * been some type of error.
+ */
+ if ((max < 1) || !ioc->events)
+ return -ENODATA;
+
+ /* Copy the data from kernel memory to user memory
+ */
+ numBytes = max * sizeof(MPT_IOCTL_EVENTS);
+ if (copy_to_user((char *) uarg->eventData, ioc->events, numBytes)) {
+ printk(KERN_ERR "%s@%d::mptctl_eventreport - "
+ "Unable to write out mpt_ioctl_eventreport struct @ %p\n",
+ __FILE__, __LINE__, (void*)ioc->events);
+ return -EFAULT;
+ }
return 0;
}
/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
static int
-mpt_ioctl_scsi_cmd(unsigned long arg)
+mptctl_replace_fw (unsigned long arg)
{
- return -ENOSYS;
+ struct mpt_ioctl_replace_fw *uarg = (struct mpt_ioctl_replace_fw *) arg;
+ struct mpt_ioctl_replace_fw karg;
+ MPT_ADAPTER *ioc;
+ fw_image_t **fwmem = NULL;
+ int iocnum;
+ int newFwSize;
+ int num_frags, alloc_sz;
+ int ii;
+ u32 offset;
+
+ dctlprintk(("mptctl_replace_fw called.\n"));
+ if (copy_from_user(&karg, uarg, sizeof(struct mpt_ioctl_replace_fw))) {
+ printk(KERN_ERR "%s@%d::mptctl_replace_fw - "
+ "Unable to read in mpt_ioctl_replace_fw struct @ %p\n",
+ __FILE__, __LINE__, (void*)uarg);
+ return -EFAULT;
+ }
+
+ if (((iocnum = mpt_verify_adapter(karg.hdr.iocnum, &ioc)) < 0) ||
+ (ioc == NULL)) {
+ printk(KERN_ERR "%s::mptctl_replace_fw() @%d - ioc%d not found!\n",
+ __FILE__, __LINE__, iocnum);
+ return -ENODEV;
+ }
+
+ /* If not caching FW, return 0
+ */
+ if ((ioc->cached_fw == NULL) && (ioc->alt_ioc) && (ioc->alt_ioc->cached_fw == NULL))
+ return 0;
+
+ /* Allocate memory for the new FW image
+ */
+ newFwSize = karg.newImageSize;
+ fwmem = mpt_alloc_fw_memory(ioc, newFwSize, &num_frags, &alloc_sz);
+ if (fwmem == NULL)
+ return -ENOMEM;
+
+ offset = 0;
+ for (ii = 0; ii < num_frags; ii++) {
+ /* Copy the data from user memory to kernel space
+ */
+ if (copy_from_user(fwmem[ii]->fw, uarg->newImage + offset, fwmem[ii]->size)) {
+ printk(KERN_ERR "%s@%d::mptctl_replace_fw - "
+ "Unable to read in mpt_ioctl_replace_fw image @ %p\n",
+ __FILE__, __LINE__, (void*)uarg);
+
+ mpt_free_fw_memory(ioc, fwmem);
+ return -EFAULT;
+ }
+ offset += fwmem[ii]->size;
+ }
+
+
+ /* Free the old FW image
+ */
+ if (ioc->cached_fw) {
+ mpt_free_fw_memory(ioc, 0);
+ ioc->cached_fw = fwmem;
+ ioc->alloc_total += alloc_sz;
+ } else if ((ioc->alt_ioc) && (ioc->alt_ioc->cached_fw)) {
+ mpt_free_fw_memory(ioc->alt_ioc, 0);
+ ioc->alt_ioc->cached_fw = fwmem;
+ ioc->alt_ioc->alloc_total += alloc_sz;
+ }
+
+ /* Update IOCFactsReply
+ */
+ ioc->facts.FWImageSize = newFwSize;
+ if (ioc->alt_ioc)
+ ioc->alt_ioc->facts.FWImageSize = newFwSize;
+
+ return 0;
+}
+
+/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
+/* MPT IOCTL MPTCOMMAND function.
+ * Cast the arg into the mpt_ioctl_mpt_command structure.
+ *
+ * Outputs: None.
+ * Return: 0 if successful
+ * -EBUSY if previous command timout and IOC reset is not complete.
+ * -EFAULT if data unavailable
+ * -ENODEV if no such device/adapter
+ * -ETIME if timer expires
+ * -ENOMEM if memory allocation error
+ */
+static int
+mptctl_mpt_command (unsigned long arg)
+{
+ struct mpt_ioctl_command *uarg = (struct mpt_ioctl_command *) arg;
+ struct mpt_ioctl_command karg;
+ MPT_ADAPTER *ioc;
+ int iocnum;
+ int rc;
+
+ dctlprintk(("mptctl_command called.\n"));
+
+ if (copy_from_user(&karg, uarg, sizeof(struct mpt_ioctl_command))) {
+ printk(KERN_ERR "%s@%d::mptctl_mpt_command - "
+ "Unable to read in mpt_ioctl_command struct @ %p\n",
+ __FILE__, __LINE__, (void*)uarg);
+ return -EFAULT;
+ }
+
+ if (((iocnum = mpt_verify_adapter(karg.hdr.iocnum, &ioc)) < 0) ||
+ (ioc == NULL)) {
+ printk(KERN_ERR "%s::mptctl_mpt_command() @%d - ioc%d not found!\n",
+ __FILE__, __LINE__, iocnum);
+ return -ENODEV;
+ }
+
+ rc = mptctl_do_mpt_command (karg, (char *) &uarg->MF, 0);
+
+ return rc;
+}
+
+/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
+/* Worker routine for the IOCTL MPTCOMMAND and MPTCOMMAND32 (sparc) commands.
+ *
+ * Outputs: None.
+ * Return: 0 if successful
+ * -EBUSY if previous command timout and IOC reset is not complete.
+ * -EFAULT if data unavailable
+ * -ENODEV if no such device/adapter
+ * -ETIME if timer expires
+ * -ENOMEM if memory allocation error
+ * -EPERM if SCSI I/O and target is untagged
+ */
+static int
+mptctl_do_mpt_command (struct mpt_ioctl_command karg, char *mfPtr, int local)
+{
+ MPT_ADAPTER *ioc;
+ MPT_FRAME_HDR *mf = NULL;
+ MPIHeader_t *hdr;
+ char *psge;
+ MptSge_t *this_sge = NULL;
+ MptSge_t *sglbuf = NULL;
+ struct buflist bufIn; /* data In buffer */
+ struct buflist bufOut; /* data Out buffer */
+ dma_addr_t sglbuf_dma;
+ dma_addr_t dma_addr;
+ int dir; /* PCI data direction */
+ int sgSize = 0; /* Num SG elements */
+ int this_alloc;
+ int iocnum, flagsLength;
+ int sz, rc = 0;
+ int msgContext;
+ int tm_flags_set = 0;
+ u16 req_idx;
+
+ dctlprintk(("mptctl_do_mpt_command called.\n"));
+
+ if (((iocnum = mpt_verify_adapter(karg.hdr.iocnum, &ioc)) < 0) ||
+ (ioc == NULL)) {
+ printk(KERN_ERR "%s::mptctl_do_mpt_command() @%d - ioc%d not found!\n",
+ __FILE__, __LINE__, iocnum);
+ return -ENODEV;
+ }
+ if (!ioc->ioctl) {
+ printk(KERN_ERR "%s@%d::mptctl_do_mpt_command - "
+ "No memory available during driver init.\n",
+ __FILE__, __LINE__);
+ return -ENOMEM;
+ } else if (ioc->ioctl->status & MPT_IOCTL_STATUS_DID_IOCRESET) {
+ printk(KERN_ERR "%s@%d::mptctl_do_mpt_command - "
+ "Busy with IOC Reset \n", __FILE__, __LINE__);
+ return -EBUSY;
+ }
+
+ /* Verify that the final request frame will not be too large.
+ */
+ sz = karg.dataSgeOffset * 4;
+ if (karg.dataInSize > 0)
+ sz += sizeof(dma_addr_t) + sizeof(u32);
+ if (karg.dataOutSize > 0)
+ sz += sizeof(dma_addr_t) + sizeof(u32);
+
+ if ( sz > ioc->req_sz) {
+ printk(KERN_ERR "%s@%d::mptctl_do_mpt_command - "
+ "Request frame too large (%d) maximum (%d)\n",
+ __FILE__, __LINE__, sz, ioc->req_sz);
+ return -EFAULT;
+ }
+
+ /* Get a free request frame and save the message context.
+ */
+ if ((mf = mpt_get_msg_frame(mptctl_id, ioc->id)) == NULL)
+ return -EAGAIN;
+
+ hdr = (MPIHeader_t *) mf;
+ msgContext = le32_to_cpu(hdr->MsgContext);
+ req_idx = le16_to_cpu(mf->u.frame.hwhdr.msgctxu.fld.req_idx);
+
+ /* Copy the request frame
+ * Reset the saved message context.
+ */
+ if (local) {
+ /* Request frame in kernel space
+ */
+ memcpy((char *)mf, (char *) mfPtr, karg.dataSgeOffset * 4);
+ } else {
+ /* Request frame in user space
+ */
+ if (copy_from_user((char *)mf, (char *) mfPtr,
+ karg.dataSgeOffset * 4)){
+ printk(KERN_ERR "%s@%d::mptctl_do_mpt_command - "
+ "Unable to read MF from mpt_ioctl_command struct @ %p\n",
+ __FILE__, __LINE__, (void*)mfPtr);
+ rc = -EFAULT;
+ goto done_free_mem;
+ }
+ }
+ hdr->MsgContext = cpu_to_le32(msgContext);
+
+
+ /* Verify that this request is allowed.
+ */
+ switch (hdr->Function) {
+ case MPI_FUNCTION_IOC_FACTS:
+ case MPI_FUNCTION_PORT_FACTS:
+ case MPI_FUNCTION_CONFIG:
+ case MPI_FUNCTION_FC_COMMON_TRANSPORT_SEND:
+ case MPI_FUNCTION_FC_EX_LINK_SRVC_SEND:
+ case MPI_FUNCTION_FW_UPLOAD:
+ case MPI_FUNCTION_SCSI_ENCLOSURE_PROCESSOR:
+ case MPI_FUNCTION_FW_DOWNLOAD:
+ break;
+
+ case MPI_FUNCTION_SCSI_IO_REQUEST:
+ if (ioc->sh) {
+ SCSIIORequest_t *pScsiReq = (SCSIIORequest_t *) mf;
+ VirtDevice *pTarget = NULL;
+ MPT_SCSI_HOST *hd = NULL;
+ int qtag = MPI_SCSIIO_CONTROL_UNTAGGED;
+ int scsidir = 0;
+ int target = (int) pScsiReq->TargetID;
+ int dataSize;
+
+ pScsiReq->MsgFlags = mpt_msg_flags();
+
+ /* verify that app has not requested
+ * more sense data than driver
+ * can provide, if so, reset this parameter
+ * set the sense buffer pointer low address
+ * update the control field to specify Q type
+ */
+ if (karg.maxSenseBytes > MPT_SENSE_BUFFER_SIZE)
+ pScsiReq->SenseBufferLength = MPT_SENSE_BUFFER_SIZE;
+
+ pScsiReq->SenseBufferLowAddr =
+ cpu_to_le32(ioc->sense_buf_low_dma
+ + (req_idx * MPT_SENSE_BUFFER_ALLOC));
+
+ if ( (hd = (MPT_SCSI_HOST *) ioc->sh->hostdata)) {
+ if (hd->Targets)
+ pTarget = hd->Targets[target];
+ }
+
+ if (pTarget &&(pTarget->tflags & MPT_TARGET_FLAGS_Q_YES))
+ qtag = MPI_SCSIIO_CONTROL_SIMPLEQ;
+ else {
+ rc = -EPERM;
+ goto done_free_mem;
+ }
+
+ /* Have the IOCTL driver set the direction based
+ * on the dataOutSize (ordering issue with Sparc).
+ */
+ if (karg.dataOutSize > 0 ) {
+ scsidir = MPI_SCSIIO_CONTROL_WRITE;
+ dataSize = karg.dataOutSize;
+ }
+ else {
+ scsidir = MPI_SCSIIO_CONTROL_READ;
+ dataSize = karg.dataInSize;
+ }
+
+ pScsiReq->Control = cpu_to_le32(scsidir | qtag);
+ pScsiReq->DataLength = cpu_to_le32(dataSize);
+
+ ioc->ioctl->reset = MPTCTL_RESET_OK;
+ ioc->ioctl->target = target;
+
+ } else {
+ printk(KERN_ERR "%s@%d::mptctl_do_mpt_command - "
+ "SCSI driver is not loaded. \n",
+ __FILE__, __LINE__);
+ rc = -EFAULT;
+ goto done_free_mem;
+ }
+ break;
+
+ case MPI_FUNCTION_RAID_ACTION:
+ /* Just add a SGE
+ */
+ break;
+
+ case MPI_FUNCTION_RAID_SCSI_IO_PASSTHROUGH:
+ if (ioc->sh) {
+ SCSIIORequest_t *pScsiReq = (SCSIIORequest_t *) mf;
+ int qtag = MPI_SCSIIO_CONTROL_SIMPLEQ;
+ int scsidir = MPI_SCSIIO_CONTROL_READ;
+ int dataSize;
+
+ pScsiReq->MsgFlags = mpt_msg_flags();
+
+ /* verify that app has not requested
+ * more sense data than driver
+ * can provide, if so, reset this parameter
+ * set the sense buffer pointer low address
+ * update the control field to specify Q type
+ */
+ if (karg.maxSenseBytes > MPT_SENSE_BUFFER_SIZE)
+ pScsiReq->SenseBufferLength = MPT_SENSE_BUFFER_SIZE;
+
+ pScsiReq->SenseBufferLowAddr =
+ cpu_to_le32(ioc->sense_buf_low_dma
+ + (req_idx * MPT_SENSE_BUFFER_ALLOC));
+
+ /* All commands to physical devices are tagged
+ */
+
+ /* Have the IOCTL driver set the direction based
+ * on the dataOutSize (ordering issue with Sparc).
+ */
+ if (karg.dataOutSize > 0 ) {
+ scsidir = MPI_SCSIIO_CONTROL_WRITE;
+ dataSize = karg.dataOutSize;
+ }
+ else {
+ scsidir = MPI_SCSIIO_CONTROL_READ;
+ dataSize = karg.dataInSize;
+ }
+
+ pScsiReq->Control = cpu_to_le32(scsidir | qtag);
+ pScsiReq->DataLength = cpu_to_le32(dataSize);
+
+ ioc->ioctl->reset = MPTCTL_RESET_OK;
+ ioc->ioctl->target = pScsiReq->TargetID;
+ } else {
+ printk(KERN_ERR "%s@%d::mptctl_do_mpt_command - "
+ "SCSI driver is not loaded. \n",
+ __FILE__, __LINE__);
+ rc = -EFAULT;
+ goto done_free_mem;
+ }
+ break;
+
+ case MPI_FUNCTION_SCSI_TASK_MGMT:
+ {
+ MPT_SCSI_HOST *hd = NULL;
+ if ((ioc->sh == NULL) || ((hd = (MPT_SCSI_HOST *)ioc->sh->hostdata) == NULL)) {
+ printk(KERN_ERR "%s@%d::mptctl_do_mpt_command - "
+ "SCSI driver not loaded or SCSI host not found. \n",
+ __FILE__, __LINE__);
+ rc = -EFAULT;
+ goto done_free_mem;
+ } else if (mptctl_set_tm_flags(hd) != 0) {
+ rc = -EPERM;
+ goto done_free_mem;
+ }
+ tm_flags_set = 1;
+ }
+ break;
+
+ default:
+ /*
+ * MPI_FUNCTION_IOC_INIT
+ * MPI_FUNCTION_PORT_ENABLE
+ * MPI_FUNCTION_TARGET_CMD_BUFFER_POST
+ * MPI_FUNCTION_TARGET_ASSIST
+ * MPI_FUNCTION_TARGET_STATUS_SEND
+ * MPI_FUNCTION_TARGET_MODE_ABORT
+ * MPI_FUNCTION_IOC_MESSAGE_UNIT_RESET
+ * MPI_FUNCTION_IO_UNIT_RESET
+ * MPI_FUNCTION_HANDSHAKE
+ * MPI_FUNCTION_REPLY_FRAME_REMOVAL
+ * MPI_FUNCTION_EVENT_NOTIFICATION
+ * (driver handles event notification)
+ * MPI_FUNCTION_EVENT_ACK
+ */
+
+ /* What to do with these??? CHECK ME!!!
+ MPI_FUNCTION_FC_LINK_SRVC_BUF_POST
+ MPI_FUNCTION_FC_LINK_SRVC_RSP
+ MPI_FUNCTION_FC_ABORT
+ MPI_FUNCTION_FC_PRIMITIVE_SEND
+ MPI_FUNCTION_LAN_SEND
+ MPI_FUNCTION_LAN_RECEIVE
+ MPI_FUNCTION_LAN_RESET
+ */
+
+ printk(KERN_ERR "%s@%d::mptctl_do_mpt_command - "
+ "Illegal request (function 0x%x) \n",
+ __FILE__, __LINE__, hdr->Function);
+ rc = -EFAULT;
+ goto done_free_mem;
+ }
+
+ /* Add the SGL ( at most one data in SGE and one data out SGE )
+ * In the case of two SGE's - the data out (write) will always
+ * preceede the data in (read) SGE. psgList is used to free the
+ * allocated memory.
+ */
+ psge = (char *) ( ((int *) mf) + karg.dataSgeOffset);
+ flagsLength = 0;
+
+ /* bufIn and bufOut are used for user to kernel space transfers
+ */
+ bufIn.kptr = bufOut.kptr = NULL;
+ bufIn.len = bufOut.len = 0;
+
+ if (karg.dataOutSize > 0 )
+ sgSize ++;
+
+ if (karg.dataInSize > 0 )
+ sgSize ++;
+
+ if (sgSize > 0) {
+
+ /* Allocate memory for the SGL.
+ * Used to free kernel memory once
+ * the MF is freed.
+ */
+ sglbuf = pci_alloc_consistent (ioc->pcidev,
+ sgSize*sizeof(MptSge_t), &sglbuf_dma);
+ if (sglbuf == NULL) {
+ rc = -ENOMEM;
+ goto done_free_mem;
+ }
+ this_sge = sglbuf;
+
+ /* Set up the dataOut memory allocation */
+ if (karg.dataOutSize > 0) {
+ dir = PCI_DMA_TODEVICE;
+ if (karg.dataInSize > 0 ) {
+ flagsLength = ( MPI_SGE_FLAGS_SIMPLE_ELEMENT |
+ MPI_SGE_FLAGS_DIRECTION |
+ mpt_addr_size() )
+ << MPI_SGE_FLAGS_SHIFT;
+ } else {
+ flagsLength = MPT_SGE_FLAGS_SSIMPLE_WRITE;
+ }
+ flagsLength |= karg.dataOutSize;
+
+ this_alloc = karg.dataOutSize;
+ bufOut.len = this_alloc;
+ bufOut.kptr = pci_alloc_consistent(
+ ioc->pcidev, this_alloc, &dma_addr);
+
+ if (bufOut.kptr == NULL) {
+ rc = -ENOMEM;
+ goto done_free_mem;
+ } else {
+ /* Copy user data to kernel space.
+ */
+ if (copy_from_user(bufOut.kptr,
+ karg.dataOutBufPtr,
+ bufOut.len)) {
+
+ printk(KERN_ERR
+ "%s@%d::mptctl_do_mpt_command - Unable "
+ "to read user data "
+ "struct @ %p\n",
+ __FILE__, __LINE__,(void*)karg.dataOutBufPtr);
+ rc = -EFAULT;
+ goto done_free_mem;
+ }
+
+ /* Set up this SGE.
+ * Copy to MF and to sglbuf
+ */
+ mpt_add_sge(psge, flagsLength, dma_addr);
+ psge += (sizeof(u32) + sizeof(dma_addr_t));
+
+ this_sge->FlagsLength = flagsLength;
+ this_sge->Address = dma_addr;
+ this_sge++;
+ }
+ }
+
+ if (karg.dataInSize > 0) {
+ dir = PCI_DMA_FROMDEVICE;
+ flagsLength = MPT_SGE_FLAGS_SSIMPLE_READ;
+ flagsLength |= karg.dataInSize;
+
+ this_alloc = karg.dataInSize;
+ bufIn.len = this_alloc;
+ bufIn.kptr = pci_alloc_consistent(ioc->pcidev,
+ this_alloc, &dma_addr);
+ if (bufIn.kptr == NULL) {
+ rc = -ENOMEM;
+ goto done_free_mem;
+ } else {
+ /* Set up this SGE
+ * Copy to MF and to sglbuf
+ */
+ mpt_add_sge(psge, flagsLength, dma_addr);
+
+ this_sge->FlagsLength = flagsLength;
+ this_sge->Address = dma_addr;
+ this_sge++;
+ }
+ }
+ } else {
+ /* Add a NULL SGE
+ */
+ mpt_add_sge(psge, flagsLength, (dma_addr_t) -1);
+ }
+
+ /* The request is complete. Set the timer parameters
+ * and issue the request.
+ */
+ if (karg.timeout > 0) {
+ ioc->ioctl->timer.expires = jiffies + HZ*karg.timeout;
+ } else {
+ ioc->ioctl->timer.expires = jiffies + HZ*MPT_IOCTL_DEFAULT_TIMEOUT;
+ }
+
+ ioc->ioctl->wait_done = 0;
+ ioc->ioctl->status |= MPT_IOCTL_STATUS_TIMER_ACTIVE;
+ add_timer(&ioc->ioctl->timer);
+
+ if (hdr->Function == MPI_FUNCTION_SCSI_TASK_MGMT) {
+ rc = mpt_send_handshake_request(mptctl_id, ioc->id,
+ sizeof(SCSITaskMgmt_t), (u32*)mf, NO_SLEEP);
+ if (rc == 0) {
+ wait_event(mptctl_wait, ioc->ioctl->wait_done);
+ } else {
+ mptctl_free_tm_flags(ioc);
+ tm_flags_set= 0;
+ del_timer(&ioc->ioctl->timer);
+ ioc->ioctl->status &= ~MPT_IOCTL_STATUS_TIMER_ACTIVE;
+ ioc->ioctl->status = MPT_IOCTL_STATUS_TM_FAILED;
+ }
+ } else {
+ mpt_put_msg_frame(mptctl_id, ioc->id, mf);
+ wait_event(mptctl_wait, ioc->ioctl->wait_done);
+ }
+
+ /* The command is complete. * Return data to the user.
+ *
+ * If command completed, mf has been freed so cannot
+ * use this memory.
+ *
+ * If timeout, a recovery mechanism has been called.
+ * Need to free the mf.
+ */
+ if (ioc->ioctl->status & MPT_IOCTL_STATUS_DID_IOCRESET) {
+
+ /* A timeout - there is no data to return to the
+ * the user other than an error.
+ * The timer callback deleted the
+ * timer and reset the adapter queues.
+ */
+ printk(KERN_WARNING "%s@%d::mptctl_do_mpt_command - "
+ "Timeout Occurred on IOCTL! Reset IOC.\n", __FILE__, __LINE__);
+ tm_flags_set= 0;
+ rc = -ETIME;
+
+ /* Free memory and return to the calling function
+ */
+ goto done_free_mem;
+ } else if (ioc->ioctl->status & MPT_IOCTL_STATUS_TM_FAILED) {
+ /* User TM request failed!
+ */
+ rc = -ENODATA;
+ } else {
+ /* Callback freed request frame.
+ */
+ mf = NULL;
+
+ /* If a valid reply frame, copy to the user.
+ * Offset 2: reply length in U32's
+ */
+ if (ioc->ioctl->status & MPT_IOCTL_STATUS_RF_VALID) {
+ if (karg.maxReplyBytes < ioc->reply_sz) {
+ sz = MIN(karg.maxReplyBytes, 4*ioc->ioctl->ReplyFrame[2]);
+ } else {
+ sz = MIN(ioc->reply_sz, 4*ioc->ioctl->ReplyFrame[2]);
+ }
+
+ if (sz > 0) {
+ if (copy_to_user((char *)karg.replyFrameBufPtr,
+ &ioc->ioctl->ReplyFrame, sz)){
+
+ printk(KERN_ERR "%s@%d::mptctl_do_mpt_command - "
+ "Unable to write out reply frame %p\n",
+ __FILE__, __LINE__, (void*)karg.replyFrameBufPtr);
+ rc = -ENODATA;
+ goto done_free_mem;
+ }
+ }
+ }
+
+ /* If valid sense data, copy to user.
+ */
+ if (ioc->ioctl->status & MPT_IOCTL_STATUS_SENSE_VALID) {
+ sz = MIN(karg.maxSenseBytes, MPT_SENSE_BUFFER_SIZE);
+ if (sz > 0) {
+ if (copy_to_user((char *)karg.senseDataPtr, ioc->ioctl->sense, sz)) {
+ printk(KERN_ERR "%s@%d::mptctl_do_mpt_command - "
+ "Unable to write sense data to user %p\n",
+ __FILE__, __LINE__,
+ (void*)karg.senseDataPtr);
+ rc = -ENODATA;
+ goto done_free_mem;
+ }
+ }
+ }
+
+ /* If the overall status is _GOOD and data in, copy data
+ * to user.
+ */
+ if ((ioc->ioctl->status & MPT_IOCTL_STATUS_COMMAND_GOOD) &&
+ (karg.dataInSize > 0) && (bufIn.kptr)) {
+
+ if (copy_to_user((char *)karg.dataInBufPtr,
+ bufIn.kptr, karg.dataInSize)) {
+ printk(KERN_ERR "%s@%d::mptctl_do_mpt_command - "
+ "Unable to write data to user %p\n",
+ __FILE__, __LINE__,
+ (void*)karg.dataInBufPtr);
+ rc = -ENODATA;
+ }
+ }
+ }
+
+done_free_mem:
+ /* Clear status bits.
+ */
+ ioc->ioctl->status = 0;
+
+ if (tm_flags_set)
+ mptctl_free_tm_flags(ioc);
+
+ if (sglbuf) {
+ this_sge = sglbuf;
+
+ /* Free the allocated memory.
+ */
+ if (bufOut.kptr != NULL ) {
+ dma_addr = this_sge->Address;
+ this_sge++; /* go to next structure */
+ this_alloc = bufOut.len;
+ pci_free_consistent(ioc->pcidev,
+ this_alloc, (void *) &bufOut, dma_addr);
+ }
+
+ if (bufIn.kptr != NULL ) {
+ dma_addr = this_sge->Address;
+ this_alloc = bufIn.len;
+
+ pci_free_consistent(ioc->pcidev,
+ this_alloc, (void *) &bufIn, dma_addr);
+ }
+
+ this_alloc = sgSize * sizeof(MptSge_t);
+ pci_free_consistent(ioc->pcidev,
+ this_alloc, (void *) sglbuf, sglbuf_dma);
+
+ }
+
+ /* mf will be null if allocation failed OR
+ * if command completed OK (callback freed)
+ */
+ if (mf)
+ mpt_free_msg_frame(mptctl_id, ioc->id, mf);
+
+ return rc;
}
+/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
+/* Routine for the Compaq IOCTL commands.
+ *
+ * Outputs: None.
+ * Return: 0 if successful
+ * -EBUSY if previous command timout and IOC reset is not complete.
+ * -EFAULT if data unavailable
+ * -ENODEV if no such device/adapter
+ * -ETIME if timer expires
+ * -ENOMEM if memory allocation error
+ */
+static int
+mptctl_compaq_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
+{
+ int iocnum = 0;
+ unsigned iocnumX = 0;
+ int ret;
+ int nonblock = (file->f_flags & O_NONBLOCK);
+ MPT_ADAPTER *iocp = NULL;
+
+ if (cmd == CPQFCTS_SCSI_PASSTHRU) {
+ /* Update the iocnum */
+ if (copy_from_user(&iocnumX, (int *)arg, sizeof(int))) {
+ printk(KERN_ERR "%s::mptctl_compaq_ioctl() @%d - "
+ "Unable to read controller number @ %p\n",
+ __FILE__, __LINE__, (void*)arg);
+ return -EFAULT;
+ }
+ iocnumX &= 0xFF;
+ }
+
+ if (((iocnum = mpt_verify_adapter(iocnumX, &iocp)) < 0) ||
+ (iocp == NULL)) {
+ printk(KERN_ERR "%s::mptctl_compaq_ioctl() @%d - ioc%d not found!\n",
+ __FILE__, __LINE__, iocnumX);
+ return -ENODEV;
+ }
+
+ /* All of these commands require an interrupt or
+ * are unknown/illegal.
+ */
+ if ((ret = mptctl_syscall_down(iocp, nonblock)) != 0)
+ return ret;
+
+ dctlprintk((MYIOC_s_INFO_FMT ": mptctl_compaq_ioctl()\n", iocp->name));
+
+ switch(cmd) {
+ case CPQFCTS_GETPCIINFO:
+ ret = mptctl_cpq_getpciinfo(arg);
+ break;
+ case CPQFCTS_GETDRIVER:
+ ret = mptctl_cpq_getdriver(arg);
+ break;
+ case CPQFCTS_CTLR_STATUS:
+ ret = mptctl_cpq_ctlr_status(arg);
+ break;
+ case CPQFCTS_SCSI_IOCTL_FC_TARGET_ADDRESS:
+ ret = mptctl_cpq_target_address(arg);
+ break;
+ case CPQFCTS_SCSI_PASSTHRU:
+ ret = mptctl_cpq_passthru(arg);
+ break;
+ default:
+ ret = -EINVAL;
+ }
+
+ up(&mptctl_syscall_sem_ioc[iocp->id]);
+
+ return ret;
+
+}
+
+/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
+/* mptctl_cpq_getpciinfo - Get PCI Information in format desired by Compaq
+ *
+ * Outputs: None.
+ * Return: 0 if successful
+ * -EBUSY if previous command timout and IOC reset is not complete.
+ * -EFAULT if data unavailable
+ * -ENODEV if no such device/adapter
+ * -ETIME if timer expires
+ */
+static int
+mptctl_cpq_getpciinfo(unsigned long arg)
+{
+ cpqfc_pci_info_struct *uarg = (cpqfc_pci_info_struct *) arg;
+ cpqfc_pci_info_struct karg;
+ MPT_ADAPTER *ioc;
+ struct pci_dev *pdev;
+ CONFIGPARMS cfg;
+ ConfigPageHeader_t hdr;
+ int iocnum = 0, iocnumX = 0;
+ dma_addr_t buf_dma;
+ u8 *pbuf = NULL;
+ int failed;
+
+ dctlprintk((": mptctl_cpq_pciinfo called.\n"));
+ if (copy_from_user(&karg, uarg, sizeof(cpqfc_pci_info_struct))) {
+ printk(KERN_ERR "%s@%d::mptctl_cpq_pciinfo - "
+ "Unable to read in cpqfc_pci_info_struct @ %p\n",
+ __FILE__, __LINE__, (void*)uarg);
+ return -EINVAL;
+ }
+
+ if (((iocnum = mpt_verify_adapter(iocnumX, &ioc)) < 0) ||
+ (ioc == NULL)) {
+ printk(KERN_ERR "%s::mptctl_pciinfo() @%d - ioc%d not found!\n",
+ __FILE__, __LINE__, iocnum);
+ return -ENODEV;
+ }
+
+ pdev = (struct pci_dev *) ioc->pcidev;
+
+ /* Populate the structure. */
+ karg.bus = pdev->bus->number;
+ karg.bus_type = 1; /* 1 = PCI; 4 = unknown */
+ karg.device_fn = PCI_FUNC(pdev->devfn);
+ karg.slot_number = PCI_SLOT(pdev->devfn);
+ karg.vendor_id = pdev->vendor;
+ karg.device_id = pdev->device;
+ karg.board_id = (karg.device_id | (karg.vendor_id << 16));
+ karg.class_code = pdev->class;
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0)
+ karg.sub_vendor_id = pdev->subsystem_vendor;
+ karg.sub_device_id = pdev->subsystem_device;
+#endif
+
+ /* Issue a config request to get the device serial number
+ */
+ hdr.PageVersion = 0;
+ hdr.PageLength = 0;
+ hdr.PageNumber = 0;
+ hdr.PageType = MPI_CONFIG_PAGETYPE_MANUFACTURING;
+ cfg.hdr = &hdr;
+ cfg.physAddr = -1;
+ cfg.pageAddr = 0;
+ cfg.action = MPI_CONFIG_ACTION_PAGE_HEADER;
+ cfg.dir = 0; /* read */
+ cfg.timeout = 10;
+
+ failed = 1;
+
+ if (mpt_config(ioc, &cfg) == 0) {
+ if (cfg.hdr->PageLength > 0) {
+ /* Issue the second config page request */
+ cfg.action = MPI_CONFIG_ACTION_PAGE_READ_CURRENT;
+
+ pbuf = pci_alloc_consistent(ioc->pcidev, hdr.PageLength * 4, &buf_dma);
+ if (pbuf) {
+ cfg.physAddr = buf_dma;
+ if (mpt_config(ioc, &cfg) == 0) {
+ ManufacturingPage0_t *pdata = (ManufacturingPage0_t *) pbuf;
+ strncpy(karg.serial_number, pdata->BoardTracerNumber, 17);
+ failed = 0;
+ }
+ pci_free_consistent(ioc->pcidev, hdr.PageLength * 4, pbuf, buf_dma);
+ pbuf = NULL;
+ }
+ }
+ }
+ if (failed)
+ strncpy(karg.serial_number, " ", 17);
+
+ /* Copy the data from kernel memory to user memory
+ */
+ if (copy_to_user((char *)arg, &karg,
+ sizeof(cpqfc_pci_info_struct))) {
+ printk(KERN_ERR "%s@%d::mptctl_cpq_pciinfo - "
+ "Unable to write out cpqfc_pci_info_struct @ %p\n",
+ __FILE__, __LINE__, (void*)uarg);
+ return -EFAULT;
+ }
+
+ return 0;
+}
+
+/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
+/* mptctl_cpq_getdriver - Get Driver Version in format desired by Compaq
+ *
+ * Outputs: None.
+ * Return: 0 if successful
+ * -EFAULT if data unavailable
+ * -ENODEV if no such device/adapter
+ */
+static int
+mptctl_cpq_getdriver(unsigned long arg)
+{
+ int *uarg = (int *)arg;
+ int karg;
+ MPT_ADAPTER *ioc = NULL;
+ int iocnum = 0, iocnumX = 0;
+ int ii, jj;
+ char version[10];
+ char val;
+ char *vptr = NULL;
+ char *pptr = NULL;
+
+ dctlprintk((": mptctl_cpq_getdriver called.\n"));
+ if (copy_from_user(&karg, uarg, sizeof(int))) {
+ printk(KERN_ERR "%s@%d::mptctl_cpq_getdriver - "
+ "Unable to read in struct @ %p\n",
+ __FILE__, __LINE__, (void*)uarg);
+ return -EFAULT;
+ }
+
+ if (((iocnum = mpt_verify_adapter(iocnumX, &ioc)) < 0) ||
+ (ioc == NULL)) {
+ printk(KERN_ERR "%s::mptctl_cpq_getdriver() @%d - ioc%d not found!\n",
+ __FILE__, __LINE__, iocnum);
+ return -ENODEV;
+ }
+
+ strncpy(version, MPT_LINUX_VERSION_COMMON, 8);
+
+ karg = 0;
+ vptr = version;
+ ii = 3;
+ while (ii > 0) {
+ pptr = strchr(vptr, '.');
+ if (pptr) {
+ *pptr = '\0';
+ val = 0;
+ for (jj=0; vptr[jj]>='0' && vptr[jj]<='9'; jj++)
+ val = 10 * val + (vptr[jj] - '0');
+ karg |= (val << (8*ii));
+ pptr++;
+ vptr = pptr;
+ } else
+ break;
+ ii--;
+ }
+
+ /* Copy the data from kernel memory to user memory
+ */
+ if (copy_to_user((char *)arg, &karg,
+ sizeof(int))) {
+ printk(KERN_ERR "%s@%d::mptctl_cpq_getdriver - "
+ "Unable to write out stuct @ %p\n",
+ __FILE__, __LINE__, (void*)uarg);
+ return -EFAULT;
+ }
+
+ return 0;
+}
+
+/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
+/* mptctl_cpq_ctlr_status - Get controller status in format desired by Compaq
+ *
+ * Outputs: None.
+ * Return: 0 if successful
+ * -EFAULT if data unavailable
+ * -ENODEV if no such device/adapter
+ */
+static int
+mptctl_cpq_ctlr_status(unsigned long arg)
+{
+ cpqfc_ctlr_status *uarg = (cpqfc_ctlr_status *) arg;
+ cpqfc_ctlr_status karg;
+ MPT_ADAPTER *ioc;
+ int iocnum = 0, iocnumX = 0;
+
+ dctlprintk((": mptctl_cpq_pciinfo called.\n"));
+ if (copy_from_user(&karg, uarg, sizeof(cpqfc_ctlr_status))) {
+ printk(KERN_ERR "%s@%d::mptctl_cpq_ctlr_status - "
+ "Unable to read in cpqfc_ctlr_status @ %p\n",
+ __FILE__, __LINE__, (void*)uarg);
+ return -EFAULT;
+ }
+
+ if (((iocnum = mpt_verify_adapter(iocnumX, &ioc)) < 0) ||
+ (ioc == NULL)) {
+ printk(KERN_ERR "%s::mptctl_cpq_ctlr_status() @%d - ioc%d not found!\n",
+ __FILE__, __LINE__, iocnum);
+ return -ENODEV;
+ }
+
+ karg.status = ioc->last_state;
+ karg.offline_reason = 0;
+
+ /* Copy the data from kernel memory to user memory
+ */
+ if (copy_to_user((char *)arg, &karg,
+ sizeof(cpqfc_ctlr_status))) {
+ printk(KERN_ERR "%s@%d::mptctl_cpq_ctlr_status - "
+ "Unable to write out cpqfc_ctlr_status @ %p\n",
+ __FILE__, __LINE__, (void*)uarg);
+ return -EFAULT;
+ }
+
+ return 0;
+}
+
+/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
+/* mptctl_cpq_target_address - Get WWN Information in format desired by Compaq
+ *
+ * Outputs: None.
+ * Return: 0 if successful
+ * -EBUSY if previous command timout and IOC reset is not complete.
+ * -EFAULT if data unavailable
+ * -ENODEV if no such device/adapter
+ * -ETIME if timer expires
+ */
+static int
+mptctl_cpq_target_address(unsigned long arg)
+{
+ Scsi_FCTargAddress *uarg = (Scsi_FCTargAddress *) arg;
+ Scsi_FCTargAddress karg;
+ MPT_ADAPTER *ioc;
+ int iocnum = 0, iocnumX = 0;
+ CONFIGPARMS cfg;
+ ConfigPageHeader_t hdr;
+ dma_addr_t buf_dma;
+ u8 *pbuf = NULL;
+ FCPortPage0_t *ppp0;
+ int ii, failed;
+ u32 low, high;
+
+ dctlprintk((": mptctl_cpq_target_address called.\n"));
+ if (copy_from_user(&karg, uarg, sizeof(Scsi_FCTargAddress))) {
+ printk(KERN_ERR "%s@%d::mptctl_cpq_target_address - "
+ "Unable to read in Scsi_FCTargAddress @ %p\n",
+ __FILE__, __LINE__, (void*)uarg);
+ return -EFAULT;
+ }
+
+ if (((iocnum = mpt_verify_adapter(iocnumX, &ioc)) < 0) ||
+ (ioc == NULL)) {
+ printk(KERN_ERR "%s::mptctl_cpq_target_address() @%d - ioc%d not found!\n",
+ __FILE__, __LINE__, iocnum);
+ return -ENODEV;
+ }
+
+ karg.host_port_id = 0;
+
+ /* Issue a config request to get the device wwn
+ */
+ hdr.PageVersion = 0;
+ hdr.PageLength = 0;
+ hdr.PageNumber = 0;
+ hdr.PageType = MPI_CONFIG_PAGETYPE_FC_PORT;
+ cfg.hdr = &hdr;
+ cfg.physAddr = -1;
+ cfg.pageAddr = 0;
+ cfg.action = MPI_CONFIG_ACTION_PAGE_HEADER;
+ cfg.dir = 0; /* read */
+ cfg.timeout = 10;
+
+ failed = 1;
+
+ if (mpt_config(ioc, &cfg) == 0) {
+ if (cfg.hdr->PageLength > 0) {
+ /* Issue the second config page request */
+ cfg.action = MPI_CONFIG_ACTION_PAGE_READ_CURRENT;
+
+ pbuf = pci_alloc_consistent(ioc->pcidev, hdr.PageLength * 4, &buf_dma);
+ if (pbuf) {
+ cfg.physAddr = buf_dma;
+ if (mpt_config(ioc, &cfg) == 0) {
+ ppp0 = (FCPortPage0_t *) pbuf;
+
+ low = le32_to_cpu(ppp0->WWNN.Low);
+ high = le32_to_cpu(ppp0->WWNN.High);
+
+ for (ii = 0; ii < 4; ii++) {
+ karg.host_wwn[7-ii] = low & 0xFF;
+ karg.host_wwn[3-ii] = high & 0xFF;
+ low = (low >> 8);
+ high = (high >> 8);
+ }
+ failed = 0;
+ }
+ pci_free_consistent(ioc->pcidev, hdr.PageLength * 4, pbuf, buf_dma);
+ pbuf = NULL;
+ }
+ }
+ }
+
+ if (failed) {
+ for (ii = 7; ii >= 0; ii--)
+ karg.host_wwn[ii] = 0;
+ }
+
+ /* Copy the data from kernel memory to user memory
+ */
+ if (copy_to_user((char *)arg, &karg,
+ sizeof(Scsi_FCTargAddress))) {
+ printk(KERN_ERR "%s@%d::mptctl_cpq_target_address - "
+ "Unable to write out Scsi_FCTargAddress @ %p\n",
+ __FILE__, __LINE__, (void*)uarg);
+ return -EFAULT;
+ }
+
+ return 0;
+}
+
+/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
+/* mptctl_cpq_passthru - Construct and issue a SCSI IO Passthru
+ *
+ * Requires the SCSI host driver to be loaded.
+ * I386 version.
+ *
+ * Outputs: None.
+ * Return: 0 if successful
+ * -EBUSY if previous command timout and IOC reset is not complete.
+ * -EFAULT if data unavailable
+ * -ENODEV if no such device/adapter
+ * -ETIME if timer expires
+ */
+static int
+mptctl_cpq_passthru(unsigned long arg)
+{
+ VENDOR_IOCTL_REQ *uarg = (VENDOR_IOCTL_REQ *) arg;
+ VENDOR_IOCTL_REQ karg;
+ cpqfc_passthru_t kpass;
+ MPT_ADAPTER *ioc;
+ int iocnum = 0, iocnumX = 0;
+ int rc;
+
+ dctlprintk((": mptctl_cpq_passthru called.\n"));
+ if (copy_from_user(&karg, uarg, sizeof(VENDOR_IOCTL_REQ))) {
+ printk(KERN_ERR "%s@%d::mptctl_cpq_passthru - "
+ "Unable to read in VENDOR_IOCTL_REQ @ %p\n",
+ __FILE__, __LINE__, (void*)uarg);
+ return -EFAULT;
+ }
+
+ /* Set the IOC number */
+ iocnumX = karg.lc & 0xFF;
+ if (((iocnum = mpt_verify_adapter(iocnumX, &ioc)) < 0) ||
+ (ioc == NULL)) {
+ printk(KERN_ERR "%s::mptctl_cpq_passthru() @%d - ioc%d not found!\n",
+ __FILE__, __LINE__, iocnum);
+ return -ENODEV;
+ }
+
+ if (ioc->sh == NULL) {
+ printk(KERN_ERR "%s::mptctl_cpq_passthru() @%d - SCSI Host driver not loaded!\n",
+ __FILE__, __LINE__);
+ return -EFAULT;
+ }
+
+ /* Read in the second buffer */
+ if (copy_from_user(&kpass, uarg->argp, sizeof(cpqfc_passthru_t))) {
+ printk(KERN_ERR "%s@%d::mptctl_cpq_passthru - "
+ "Unable to read in cpqfc_passthru_t @ %p\n",
+ __FILE__, __LINE__, (void*)uarg);
+ return -EFAULT;
+ }
+
+
+ /* Generate the SCSI IO command and issue */
+ rc = mptctl_compaq_scsiio(&karg, &kpass);
+ return rc;
+}
+
+/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
+/* mptctl_compaq_scsiio - Reformat Compaq structures into driver structures
+ * Call the generic _do_mpt_command function.
+ *
+ * Requires the SCSI host driver to be loaded.
+ * I386 version.
+ *
+ * Outputs: None.
+ * Return: 0 if successful
+ * -EBUSY if previous command timout and IOC reset is not complete.
+ * -EFAULT if data unavailable
+ * -ENODEV if no such device/adapter
+ * -ETIME if timer expires
+ */
+static int
+mptctl_compaq_scsiio(VENDOR_IOCTL_REQ *pVenReq, cpqfc_passthru_t *pPass)
+{
+ struct mpt_ioctl_command karg;
+ SCSIIORequest_t request ;
+ SCSIIORequest_t *pMf;
+ int ii, rc;
+ u8 opcode;
+
+ /* Fill in parameters to karg */
+ karg.hdr.iocnum = pVenReq->lc;
+ karg.hdr.port = 0;
+ karg.hdr.maxDataSize = 0; /* not used */
+ karg.timeout = 0; /* use default */
+
+ karg.replyFrameBufPtr = NULL; /* no reply data */
+ karg.maxReplyBytes = 0;
+
+ karg.senseDataPtr = pPass->sense_data;
+ karg.maxSenseBytes = pPass->sense_len; /* max is 40 */
+
+ if (pPass->rw_flag == MPT_COMPAQ_WRITE) {
+ karg.dataOutBufPtr = pPass->bufp;
+ karg.dataOutSize = pPass->len;
+ karg.dataInBufPtr = NULL;
+ karg.dataInSize = 0;
+ } else {
+ karg.dataInBufPtr = pPass->bufp;
+ karg.dataInSize = pPass->len;
+ karg.dataOutBufPtr = NULL;
+ karg.dataOutSize = 0;
+ }
+
+ karg.dataSgeOffset = (sizeof(SCSIIORequest_t) - sizeof(SGE_IO_UNION))/4;
+
+ /* Construct the Message frame */
+ pMf = &request;
+
+ pMf->TargetID = (u8) pVenReq->ld; /* ???? FIXME */
+ pMf->Bus = (u8) pPass->bus;
+ pMf->ChainOffset = 0;
+ pMf->Function = MPI_FUNCTION_SCSI_IO_REQUEST;
+
+ /* May need some tweaking here */
+ opcode = (u8) pPass->cdb[0];
+ if (opcode < 0x20)
+ pMf->CDBLength = 6;
+ else if (opcode < 0x60)
+ pMf->CDBLength = 10;
+ else if ((opcode < 0xC0) && (opcode >= 0xA0))
+ pMf->CDBLength = 12;
+ else
+ pMf->CDBLength = 16;
+
+ pMf->SenseBufferLength = karg.maxSenseBytes; /* max is 40 */
+ pMf->Reserved = 0;
+ pMf->MsgFlags = 0; /* set later */
+ pMf->MsgContext = 0; /* set later */
+
+ for (ii = 0; ii < 8; ii++)
+ pMf->LUN[ii] = 0;
+ pMf->LUN[1] = 0; /* ???? FIXME */
+
+ /* Tag values set by _do_mpt_command */
+ if (pPass->rw_flag == MPT_COMPAQ_WRITE)
+ pMf->Control = MPI_SCSIIO_CONTROL_WRITE;
+ else
+ pMf->Control = MPI_SCSIIO_CONTROL_READ;
+
+ for (ii = 0; ii < 16; ii++)
+ pMf->CDB[ii] = pPass->cdb[ii];
+
+ pMf->DataLength = pPass->len;
+
+ /* All remaining fields are set by the next function
+ */
+ rc = mptctl_do_mpt_command (karg, (char *)pMf, 1);
+ return rc;
+}
+
+
/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,3,51)
llseek: no_llseek,
read: mptctl_read,
write: mptctl_write,
- ioctl: mpt_ioctl,
+ ioctl: mptctl_ioctl,
open: mptctl_open,
release: mptctl_release,
};
unsigned long,
struct file *));
int unregister_ioctl32_conversion(unsigned int cmd);
-
-struct mpt_fw_xfer32 {
- unsigned int iocnum;
- unsigned int fwlen;
- u32 bufp;
-};
-
-#define MPTFWDOWNLOAD32 _IOWR(MPT_MAGIC_NUMBER,15,struct mpt_fw_xfer32)
-
extern asmlinkage int sys_ioctl(unsigned int fd, unsigned int cmd, unsigned long arg);
/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
+/* sparc32_XXX functions are used to provide a conversion between
+ * pointers and u32's. If the arg does not contain any pointers, then
+ * a specialized function (sparc32_XXX) is not needed. If the arg
+ * does contain pointer(s), then the specialized function is used
+ * to ensure the structure contents is properly processed by mptctl.
+ */
static int
sparc32_mptfwxfer_ioctl(unsigned int fd, unsigned int cmd,
unsigned long arg, struct file *filp)
int nonblock = (filp->f_flags & O_NONBLOCK);
int ret;
- dprintk((KERN_INFO MYNAM "::sparc32_mptfwxfer_ioctl() called\n"));
+ dctlprintk((KERN_INFO MYNAM "::sparc32_mptfwxfer_ioctl() called\n"));
if (copy_from_user(&kfw32, (char *)arg, sizeof(kfw32)))
return -EFAULT;
kfw.fwlen = kfw32.fwlen;
kfw.bufp = (void *)(unsigned long)kfw32.bufp;
- ret = mpt_ioctl_do_fw_download(kfw.iocnum, kfw.bufp, kfw.fwlen);
+ ret = mptctl_do_fw_download(kfw.iocnum, kfw.bufp, kfw.fwlen);
+
+ up(&mptctl_syscall_sem_ioc[iocp->id]);
+
+ return ret;
+}
+
+static int
+sparc32_mpt_command(unsigned int fd, unsigned int cmd,
+ unsigned long arg, struct file *filp)
+{
+ struct mpt_ioctl_command32 karg32;
+ struct mpt_ioctl_command32 *uarg = (struct mpt_ioctl_command32 *) arg;
+ struct mpt_ioctl_command karg;
+ MPT_ADAPTER *iocp = NULL;
+ int iocnum, iocnumX;
+ int nonblock = (filp->f_flags & O_NONBLOCK);
+ int ret;
+
+ dctlprintk((KERN_INFO MYNAM "::sparc32_mpt_command() called\n"));
+
+ if (copy_from_user(&karg32, (char *)arg, sizeof(karg32)))
+ return -EFAULT;
+
+ /* Verify intended MPT adapter */
+ iocnumX = karg32.hdr.iocnum & 0xFF;
+ if (((iocnum = mpt_verify_adapter(iocnumX, &iocp)) < 0) ||
+ (iocp == NULL)) {
+ printk(KERN_ERR MYNAM "::sparc32_mpt_command @%d - ioc%d not found!\n",
+ __LINE__, iocnumX);
+ return -ENODEV;
+ }
+
+ if ((ret = mptctl_syscall_down(iocp, nonblock)) != 0)
+ return ret;
+
+ /* Copy data to karg */
+ karg.hdr.iocnum = karg32.hdr.iocnum;
+ karg.hdr.port = karg32.hdr.port;
+ karg.timeout = karg32.timeout;
+ karg.maxReplyBytes = karg32.maxReplyBytes;
+
+ karg.dataInSize = karg32.dataInSize;
+ karg.dataOutSize = karg32.dataOutSize;
+ karg.maxSenseBytes = karg32.maxSenseBytes;
+ karg.dataSgeOffset = karg32.dataSgeOffset;
+
+ karg.replyFrameBufPtr = (char *)(unsigned long)karg32.replyFrameBufPtr;
+ karg.dataInBufPtr = (char *)(unsigned long)karg32.dataInBufPtr;
+ karg.dataOutBufPtr = (char *)(unsigned long)karg32.dataOutBufPtr;
+ karg.senseDataPtr = (char *)(unsigned long)karg32.senseDataPtr;
+
+ /* Pass new structure to do_mpt_command
+ */
+ ret = mptctl_do_mpt_command (karg, (char *) &uarg->MF, 0);
up(&mptctl_syscall_sem_ioc[iocp->id]);
return ret;
}
+static int
+sparc32_mptctl_cpq_passthru(unsigned int fd, unsigned int cmd,
+ unsigned long arg, struct file *filp)
+{
+ VENDOR_IOCTL_REQ32 *uarg = (VENDOR_IOCTL_REQ32 *) arg;
+ VENDOR_IOCTL_REQ32 karg32;
+ VENDOR_IOCTL_REQ karg;
+ cpqfc_passthru32_t kpass32;
+ cpqfc_passthru_t kpass;
+ MPT_ADAPTER *ioc;
+ int nonblock = (filp->f_flags & O_NONBLOCK);
+ int iocnum = 0, iocnumX = 0;
+ int rc;
+ int ii;
+
+ dctlprintk((KERN_INFO MYNAM "::sparc32_mptctl_cpq_passthru() called\n"));
+
+ if (copy_from_user(&karg32, (char *)arg, sizeof(karg32)))
+ return -EFAULT;
+
+ /* Verify intended MPT adapter */
+ iocnumX = karg32.lc & 0xFF;
+ if (((iocnum = mpt_verify_adapter(iocnumX, &ioc)) < 0) ||
+ (ioc == NULL)) {
+ printk(KERN_ERR MYNAM "::sparc32_mpt_command @%d - ioc%d not found!\n",
+ __LINE__, iocnumX);
+ return -ENODEV;
+ }
+
+ if ((rc = mptctl_syscall_down(ioc, nonblock)) != 0)
+ return rc;
+
+ /* Copy data to karg */
+ karg.ld = karg32.ld;
+ karg.node = karg32.node;
+ karg.lc = karg32.lc;
+ karg.nexus = karg32.nexus;
+ karg.argp = (void *)(unsigned long)karg32.argp;
+
+ /* Read in the second buffer */
+ if (copy_from_user(&kpass32, karg.argp, sizeof(cpqfc_passthru32_t))) {
+ printk(KERN_ERR "%s@%d::sparc32_mptctl_cpq_passthru - "
+ "Unable to read in cpqfc_passthru_t @ %p\n",
+ __FILE__, __LINE__, (void*)uarg);
+ return -EFAULT;
+ }
+
+ /* Copy the 32bit buffer to kpass */
+ for (ii = 0; ii < 16; ii++)
+ kpass.cdb[ii] = kpass32.cdb[ii];
+ kpass.bus = kpass32.bus;
+ kpass.pdrive = kpass32.pdrive;
+ kpass.len = kpass32.len;
+ kpass.sense_len = kpass32.sense_len;
+ kpass.bufp = (void *)(unsigned long)kpass32.bufp;
+ kpass.rw_flag = kpass32.rw_flag;
+
+ /* Generate the SCSI IO command and issue */
+ rc = mptctl_compaq_scsiio(&karg, &kpass);
+
+ up(&mptctl_syscall_sem_ioc[ioc->id]);
+ return rc;
+}
+
#endif /*} linux >= 2.3.x */
#endif /*} sparc */
int err;
int i;
int where = 1;
+ int sz;
+ u8 *mem;
+ MPT_ADAPTER *ioc = NULL;
+ int iocnum;
show_mptmod_ver(my_NAME, my_VERSION);
for (i=0; i<MPT_MAX_ADAPTERS; i++) {
sema_init(&mptctl_syscall_sem_ioc[i], 1);
+
+ ioc = NULL;
+ if (((iocnum = mpt_verify_adapter(i, &ioc)) < 0) ||
+ (ioc == NULL)) {
+ continue;
+ }
+ else {
+ /* This adapter instance is found.
+ * Allocate and inite a MPT_IOCTL structure
+ */
+ sz = sizeof (MPT_IOCTL);
+ mem = kmalloc(sz, GFP_KERNEL);
+ if (mem == NULL) {
+ err = -ENOMEM;
+ goto out_fail;
+ }
+
+ memset(mem, 0, sz);
+ ioc->ioctl = (MPT_IOCTL *) mem;
+ ioc->ioctl->ioc = ioc;
+ init_timer (&ioc->ioctl->timer);
+ ioc->ioctl->timer.data = (unsigned long) ioc->ioctl;
+ ioc->ioctl->timer.function = mptctl_timer_expired;
+ init_timer (&ioc->ioctl->TMtimer);
+ ioc->ioctl->TMtimer.data = (unsigned long) ioc->ioctl;
+ ioc->ioctl->TMtimer.function = mptctl_timer_expired;
+ }
}
#if defined(__sparc__) && defined(__sparc_v9__) /*{*/
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,3,0) /*{*/
- err = register_ioctl32_conversion(MPTRWPERF, NULL);
+ err = register_ioctl32_conversion(MPTIOCINFO, NULL);
+ if (++where && err) goto out_fail;
+ err = register_ioctl32_conversion(MPTTARGETINFO, NULL);
+ if (++where && err) goto out_fail;
+ err = register_ioctl32_conversion(MPTTEST, NULL);
+ if (++where && err) goto out_fail;
+ err = register_ioctl32_conversion(MPTEVENTQUERY, NULL);
+ if (++where && err) goto out_fail;
+ err = register_ioctl32_conversion(MPTEVENTENABLE, NULL);
+ if (++where && err) goto out_fail;
+ err = register_ioctl32_conversion(MPTEVENTREPORT, NULL);
+ if (++where && err) goto out_fail;
+ err = register_ioctl32_conversion(MPTHARDRESET, NULL);
+ if (++where && err) goto out_fail;
+ err = register_ioctl32_conversion(MPTCOMMAND32, sparc32_mpt_command);
+ if (++where && err) goto out_fail;
+ err = register_ioctl32_conversion(MPTFWDOWNLOAD32,
+ sparc32_mptfwxfer_ioctl);
if (++where && err) goto out_fail;
- err = register_ioctl32_conversion(MPTRWPERF_CHK, NULL);
+ err = register_ioctl32_conversion(CPQFCTS_GETPCIINFO, NULL);
if (++where && err) goto out_fail;
- err = register_ioctl32_conversion(MPTRWPERF_RESET, NULL);
+ err = register_ioctl32_conversion(CPQFCTS_CTLR_STATUS, NULL);
if (++where && err) goto out_fail;
- err = register_ioctl32_conversion(MPTFWDOWNLOAD32, sparc32_mptfwxfer_ioctl);
+ err = register_ioctl32_conversion(CPQFCTS_GETDRIVER, NULL);
+ if (++where && err) goto out_fail;
+ err = register_ioctl32_conversion(CPQFCTS_SCSI_IOCTL_FC_TARGET_ADDRESS, NULL);
+ if (++where && err) goto out_fail;
+ err = register_ioctl32_conversion(CPQFCTS_SCSI_PASSTHRU32, sparc32_mptctl_cpq_passthru);
if (++where && err) goto out_fail;
#endif /*} linux >= 2.3.x */
#endif /*} sparc */
+ /* Register this device */
if (misc_register(&mptctl_miscdev) == -1) {
printk(KERN_ERR MYNAM ": Can't register misc device [minor=%d].\n", MPT_MINOR);
err = -EBUSY;
* Install our handler
*/
++where;
- if ((mptctl_id = mpt_register(mptctl_reply, MPTCTL_DRIVER)) <= 0) {
+ if ((mptctl_id = mpt_register(mptctl_reply, MPTCTL_DRIVER)) < 0) {
printk(KERN_ERR MYNAM ": ERROR: Failed to register with Fusion MPT base driver\n");
misc_deregister(&mptctl_miscdev);
err = -EBUSY;
goto out_fail;
}
+ if (mpt_reset_register(mptctl_id, mptctl_ioc_reset) == 0) {
+ dprintk((KERN_INFO MYNAM ": Registered for IOC reset notifications\n"));
+ } else {
+ /* FIXME! */
+ }
+
return 0;
out_fail:
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,3,0) /*{*/
printk(KERN_ERR MYNAM ": ERROR: Failed to register ioctl32_conversion!"
" (%d:err=%d)\n", where, err);
- unregister_ioctl32_conversion(MPTRWPERF);
- unregister_ioctl32_conversion(MPTRWPERF_CHK);
- unregister_ioctl32_conversion(MPTRWPERF_RESET);
+ unregister_ioctl32_conversion(MPTIOCINFO);
+ unregister_ioctl32_conversion(MPTTARGETINFO);
+ unregister_ioctl32_conversion(MPTTEST);
+ unregister_ioctl32_conversion(MPTEVENTQUERY);
+ unregister_ioctl32_conversion(MPTEVENTENABLE);
+ unregister_ioctl32_conversion(MPTEVENTREPORT);
+ unregister_ioctl32_conversion(MPTHARDRESET);
+ unregister_ioctl32_conversion(MPTCOMMAND32);
unregister_ioctl32_conversion(MPTFWDOWNLOAD32);
+ unregister_ioctl32_conversion(CPQFCTS_GETPCIINFO);
+ unregister_ioctl32_conversion(CPQFCTS_GETDRIVER);
+ unregister_ioctl32_conversion(CPQFCTS_CTLR_STATUS);
+ unregister_ioctl32_conversion(CPQFCTS_SCSI_IOCTL_FC_TARGET_ADDRESS);
+ unregister_ioctl32_conversion(CPQFCTS_SCSI_PASSTHRU32);
#endif /*} linux >= 2.3.x */
#endif /*} sparc */
+ for (i=0; i<MPT_MAX_ADAPTERS; i++) {
+ ioc = NULL;
+ if (((iocnum = mpt_verify_adapter(i, &ioc)) < 0) ||
+ (ioc == NULL)) {
+ continue;
+ }
+ else {
+ if (ioc->ioctl) {
+ kfree ( ioc->ioctl );
+ ioc->ioctl = NULL;
+ }
+ }
+ }
return err;
}
/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
void mptctl_exit(void)
{
-
-#if defined(__sparc__) && defined(__sparc_v9__) /*{*/
-#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,3,0) /*{*/
- unregister_ioctl32_conversion(MPTRWPERF);
- unregister_ioctl32_conversion(MPTRWPERF_CHK);
- unregister_ioctl32_conversion(MPTRWPERF_RESET);
- unregister_ioctl32_conversion(MPTFWDOWNLOAD32);
-#endif /*} linux >= 2.3.x */
-#endif /*} sparc */
+ int i;
+ MPT_ADAPTER *ioc;
+ int iocnum;
misc_deregister(&mptctl_miscdev);
- printk(KERN_INFO MYNAM ": /dev/%s @ (major,minor=%d,%d)\n",
+ printk(KERN_INFO MYNAM ": Deregistered /dev/%s @ (major,minor=%d,%d)\n",
mptctl_miscdev.name, MISC_MAJOR, mptctl_miscdev.minor);
- printk(KERN_INFO MYNAM ": Deregistered from Fusion MPT base driver\n");
+ /* De-register reset handler from base module */
+ mpt_reset_deregister(mptctl_id);
+ dprintk((KERN_INFO MYNAM ": Deregistered for IOC reset notifications\n"));
+
+ /* De-register callback handler from base module */
mpt_deregister(mptctl_id);
+ printk(KERN_INFO MYNAM ": Deregistered from Fusion MPT base driver\n");
+
+ /* Free allocated memory */
+ for (i=0; i<MPT_MAX_ADAPTERS; i++) {
+ ioc = NULL;
+ if (((iocnum = mpt_verify_adapter(i, &ioc)) < 0) ||
+ (ioc == NULL)) {
+ continue;
+ }
+ else {
+ if (ioc->ioctl) {
+ kfree ( ioc->ioctl );
+ ioc->ioctl = NULL;
+ }
+ }
+ }
}
/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
--- /dev/null
+/*
+ * linux/drivers/message/fusion/mptioctl.h
+ * Fusion MPT misc device (ioctl) driver.
+ * For use with PCI chip/adapter(s):
+ * LSIFC9xx/LSI409xx Fibre Channel
+ * running LSI Logic Fusion MPT (Message Passing Technology) firmware.
+ *
+ * Credits:
+ * This driver would not exist if not for Alan Cox's development
+ * of the linux i2o driver.
+ *
+ * A huge debt of gratitude is owed to David S. Miller (DaveM)
+ * for fixing much of the stupid and broken stuff in the early
+ * driver while porting to sparc64 platform. THANK YOU!
+ *
+ * (see also mptbase.c)
+ *
+ * Copyright (c) 1999-2002 LSI Logic Corporation
+ * Originally By: Steven J. Ralston
+ * (mailto:sjralston1@netscape.net)
+ * (mailto:Pam.Delaney@lsil.com)
+ *
+ * $Id: mptctl.h,v 1.10 2002/05/28 15:57:16 pdelaney Exp $
+ */
+/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
+/*
+ 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
+*/
+
+#ifndef MPTCTL_H_INCLUDED
+#define MPTCTL_H_INCLUDED
+/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
+
+#include "linux/version.h"
+
+
+/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
+/*
+ *
+ */
+#define MPT_MISCDEV_BASENAME "mptctl"
+#define MPT_MISCDEV_PATHNAME "/dev/" MPT_MISCDEV_BASENAME
+
+#define MPT_PRODUCT_LENGTH 12
+
+/*
+ * Generic MPT Control IOCTLs and structures
+ */
+#define MPT_MAGIC_NUMBER 'm'
+
+#define MPTRWPERF _IOWR(MPT_MAGIC_NUMBER,0,struct mpt_raw_r_w)
+
+#define MPTFWDOWNLOAD _IOWR(MPT_MAGIC_NUMBER,15,struct mpt_fw_xfer)
+#define MPTCOMMAND _IOWR(MPT_MAGIC_NUMBER,20,struct mpt_ioctl_command)
+
+#if defined(__KERNEL__) && defined(__sparc__) && defined(__sparc_v9__) /*{*/
+#define MPTFWDOWNLOAD32 _IOWR(MPT_MAGIC_NUMBER,15,struct mpt_fw_xfer32)
+#define MPTCOMMAND32 _IOWR(MPT_MAGIC_NUMBER,20,struct mpt_ioctl_command32)
+#endif /*}*/
+
+#define MPTIOCINFO _IOWR(MPT_MAGIC_NUMBER,17,struct mpt_ioctl_iocinfo)
+#define MPTTARGETINFO _IOWR(MPT_MAGIC_NUMBER,18,struct mpt_ioctl_targetinfo)
+#define MPTTEST _IOWR(MPT_MAGIC_NUMBER,19,struct mpt_ioctl_test)
+#define MPTEVENTQUERY _IOWR(MPT_MAGIC_NUMBER,21,struct mpt_ioctl_eventquery)
+#define MPTEVENTENABLE _IOWR(MPT_MAGIC_NUMBER,22,struct mpt_ioctl_eventenable)
+#define MPTEVENTREPORT _IOWR(MPT_MAGIC_NUMBER,23,struct mpt_ioctl_eventreport)
+#define MPTHARDRESET _IOWR(MPT_MAGIC_NUMBER,24,struct mpt_ioctl_diag_reset)
+#define MPTFWREPLACE _IOWR(MPT_MAGIC_NUMBER,25,struct mpt_ioctl_replace_fw)
+
+/*
+ * SPARC PLATFORM REMARK:
+ * IOCTL data structures that contain pointers
+ * will have different sizes in the driver and applications
+ * (as the app. will not use 8-byte pointers).
+ * Apps should use MPTFWDOWNLOAD and MPTCOMMAND.
+ * The driver will convert data from
+ * mpt_fw_xfer32 (mpt_ioctl_command32) to mpt_fw_xfer (mpt_ioctl_command)
+ * internally.
+ */
+struct mpt_fw_xfer {
+ unsigned int iocnum; /* IOC unit number */
+ unsigned int fwlen;
+ void *bufp; /* Pointer to firmware buffer */
+};
+
+#if defined(__KERNEL__) && defined(__sparc__) && defined(__sparc_v9__) /*{*/
+struct mpt_fw_xfer32 {
+ unsigned int iocnum;
+ unsigned int fwlen;
+ u32 bufp;
+};
+#endif /*}*/
+
+/*
+ * IOCTL header structure.
+ * iocnum - must be defined.
+ * port - must be defined for all IOCTL commands other than MPTIOCINFO
+ * maxDataSize - ignored on MPTCOMMAND commands
+ * - ignored on MPTFWREPLACE commands
+ * - on query commands, reports the maximum number of bytes to be returned
+ * to the host driver (count includes the header).
+ * That is, set to sizeof(struct mpt_ioctl_iocinfo) for fixed sized commands.
+ * Set to sizeof(struct mpt_ioctl_targetinfo) + datasize for variable
+ * sized commands. (MPTTARGETINFO, MPTEVENTREPORT)
+ */
+typedef struct _mpt_ioctl_header {
+ unsigned int iocnum; /* IOC unit number */
+ unsigned int port; /* IOC port number */
+ int maxDataSize; /* Maximum Num. bytes to transfer on read */
+} mpt_ioctl_header;
+
+/*
+ * Issue a diagnostic reset
+ */
+struct mpt_ioctl_diag_reset {
+ mpt_ioctl_header hdr;
+};
+
+
+/*
+ * PCI bus/device/function information structure.
+ */
+struct mpt_ioctl_pci_info {
+ union {
+ struct {
+ unsigned long deviceNumber : 5;
+ unsigned long functionNumber : 3;
+ unsigned long busNumber : 24;
+ } bits;
+ unsigned long asUlong;
+ } u;
+};
+
+/*
+ * Adapter Information Page
+ * Read only.
+ * Data starts at offset 0xC
+ */
+#define MPT_IOCTL_INTERFACE_FC (0x01)
+#define MPT_IOCTL_INTERFACE_SCSI (0x00)
+#define MPT_IOCTL_VERSION_LENGTH (32)
+
+struct mpt_ioctl_iocinfo {
+ mpt_ioctl_header hdr;
+ int adapterType; /* SCSI or FCP */
+ int port; /* port number */
+ int pciId; /* PCI Id. */
+ int hwRev; /* hardware revision */
+ int subSystemDevice; /* PCI subsystem Device ID */
+ int subSystemVendor; /* PCI subsystem Vendor ID */
+ int numDevices; /* number of devices */
+ int FWVersion; /* FW Version (integer) */
+ int BIOSVersion; /* BIOS Version (integer) */
+ char driverVersion[MPT_IOCTL_VERSION_LENGTH]; /* Driver Version (string) */
+ char busChangeEvent;
+ char hostId;
+ char rsvd[2];
+ struct mpt_ioctl_pci_info pciInfo; /* Added Rev 1 */
+};
+
+/*
+ * Device Information Page
+ * Report the number of, and ids of, all targets
+ * on this IOC. The ids array is a packed structure
+ * of the known targetInfo.
+ * bits 31-24: reserved
+ * 23-16: LUN
+ * 15- 8: Bus Number
+ * 7- 0: Target ID
+ */
+struct mpt_ioctl_targetinfo {
+ mpt_ioctl_header hdr;
+ int numDevices; /* Num targets on this ioc */
+ int targetInfo[1];
+};
+
+
+/*
+ * Event reporting IOCTL's. These IOCTL's will
+ * use the following defines:
+ */
+struct mpt_ioctl_eventquery {
+ mpt_ioctl_header hdr;
+ unsigned short eventEntries;
+ unsigned short reserved;
+ unsigned int eventTypes;
+};
+
+struct mpt_ioctl_eventenable {
+ mpt_ioctl_header hdr;
+ unsigned int eventTypes;
+};
+
+#ifndef __KERNEL__
+typedef struct {
+ uint event;
+ uint eventContext;
+ uint data[2];
+} MPT_IOCTL_EVENTS;
+#endif
+
+struct mpt_ioctl_eventreport {
+ mpt_ioctl_header hdr;
+ MPT_IOCTL_EVENTS eventData[1];
+};
+
+#define MPT_MAX_NAME 32
+struct mpt_ioctl_test {
+ mpt_ioctl_header hdr;
+ u8 name[MPT_MAX_NAME];
+ int chip_type;
+ u8 product [MPT_PRODUCT_LENGTH];
+};
+
+/* Replace the FW image cached in host driver memory
+ * newImageSize - image size in bytes
+ * newImage - first byte of the new image
+ */
+typedef struct mpt_ioctl_replace_fw {
+ mpt_ioctl_header hdr;
+ int newImageSize;
+ u8 newImage[1];
+} mpt_ioctl_replace_fw_t;
+
+/* General MPT Pass through data strucutre
+ *
+ * iocnum
+ * timeout - in seconds, command timeout. If 0, set by driver to
+ * default value.
+ * replyFrameBufPtr - reply location
+ * dataInBufPtr - destination for read
+ * dataOutBufPtr - data source for write
+ * senseDataPtr - sense data location
+ * maxReplyBytes - maximum number of reply bytes to be sent to app.
+ * dataInSize - num bytes for data transfer in (read)
+ * dataOutSize - num bytes for data transfer out (write)
+ * dataSgeOffset - offset in words from the start of the request message
+ * to the first SGL
+ * MF[1];
+ *
+ * Remark: Some config pages have bi-directional transfer,
+ * both a read and a write. The basic structure allows for
+ * a bidirectional set up. Normal messages will have one or
+ * both of these buffers NULL.
+ */
+struct mpt_ioctl_command {
+ mpt_ioctl_header hdr;
+ int timeout; /* optional (seconds) */
+ char *replyFrameBufPtr;
+ char *dataInBufPtr;
+ char *dataOutBufPtr;
+ char *senseDataPtr;
+ int maxReplyBytes;
+ int dataInSize;
+ int dataOutSize;
+ int maxSenseBytes;
+ int dataSgeOffset;
+ char MF[1];
+};
+
+/*
+ * SPARC PLATFORM: See earlier remark.
+ */
+#if defined(__KERNEL__) && defined(__sparc__) && defined(__sparc_v9__) /*{*/
+struct mpt_ioctl_command32 {
+ mpt_ioctl_header hdr;
+ int timeout;
+ u32 replyFrameBufPtr;
+ u32 dataInBufPtr;
+ u32 dataOutBufPtr;
+ u32 senseDataPtr;
+ int maxReplyBytes;
+ int dataInSize;
+ int dataOutSize;
+ int maxSenseBytes;
+ int dataSgeOffset;
+ char MF[1];
+};
+#endif /*}*/
+
+
+
+/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
+ /*
+ * COMPAQ Specific IOCTL Defines and Structures
+ */
+
+#define CPQFCTS_IOC_MAGIC 'Z'
+
+#define CPQFCTS_GETPCIINFO _IOR(CPQFCTS_IOC_MAGIC, 1, cpqfc_pci_info_struct)
+#define CPQFCTS_GETDRIVER _IOR(CPQFCTS_IOC_MAGIC, 2, int)
+#define CPQFCTS_CTLR_STATUS _IOR(CPQFCTS_IOC_MAGIC, 3, struct _cpqfc_ctlr_status)
+#define CPQFCTS_SCSI_IOCTL_FC_TARGET_ADDRESS _IOR(CPQFCTS_IOC_MAGIC, 4, struct scsi_fctargaddress)
+#define CPQFCTS_SCSI_PASSTHRU _IOWR(CPQFCTS_IOC_MAGIC, 5, VENDOR_IOCTL_REQ)
+#if defined(__sparc__) && defined(__sparc_v9__)
+#define CPQFCTS_SCSI_PASSTHRU32 _IOWR(CPQFCTS_IOC_MAGIC, 5, VENDOR_IOCTL_REQ32)
+#endif
+
+typedef struct {
+ unsigned short bus;
+ unsigned short bus_type;
+ unsigned short device_fn;
+ u32 board_id;
+ u32 slot_number;
+ unsigned short vendor_id;
+ unsigned short device_id;
+ unsigned short class_code;
+ unsigned short sub_vendor_id;
+ unsigned short sub_device_id;
+ u8 serial_number[81];
+} cpqfc_pci_info_struct;
+
+
+typedef struct scsi_fctargaddress {
+ unsigned int host_port_id;
+ u8 host_wwn[8]; /* WW Network Name */
+} Scsi_FCTargAddress;
+
+typedef struct _cpqfc_ctlr_status {
+ u32 status;
+ u32 offline_reason;
+} cpqfc_ctlr_status;
+
+
+/* Compaq SCSI I/O Passthru structures.
+ */
+#define MPT_COMPAQ_READ 0x26
+#define MPT_COMPAQ_WRITE 0x27
+
+typedef struct {
+ int lc; /* controller number */
+ int node; /* node number */
+ int ld; /* target logical id */
+ u32 nexus;
+ void *argp;
+} VENDOR_IOCTL_REQ;
+
+#if defined(__KERNEL__) && defined(__sparc__) && defined(__sparc_v9__) /*{*/
+typedef struct {
+ int lc; /* controller number */
+ int node; /* node number */
+ int ld; /* target logical id */
+ u32 nexus;
+ u32 argp;
+} VENDOR_IOCTL_REQ32;
+#endif
+
+typedef struct {
+ char cdb[16]; /* cdb */
+ unsigned short bus; /* bus number */
+ unsigned short pdrive; /* physical drive */
+ int len; /* data area size */
+ int sense_len; /* sense size */
+ char sense_data[40]; /* sense buffer */
+ void *bufp; /* data buffer pointer */
+ char rw_flag;
+} cpqfc_passthru_t;
+
+#if defined(__KERNEL__) && defined(__sparc__) && defined(__sparc_v9__) /*{*/
+typedef struct {
+ char cdb[16]; /* cdb */
+ unsigned short bus; /* bus number */
+ unsigned short pdrive; /* physical drive */
+ int len; /* data area size */
+ int sense_len; /* sense size */
+ char sense_data[40]; /* sense buffer */
+ u32 bufp; /* data buffer pointer */
+ char rw_flag;
+} cpqfc_passthru32_t;
+#endif
+
+/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
+
+
+/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
+
+#endif
+
*
* (see also mptbase.c)
*
- * Copyright (c) 2000-2001 LSI Logic Corporation
+ * Copyright (c) 2000-2002 LSI Logic Corporation
* Originally By: Noah Romer
*
- * $Id: mptlan.c,v 1.32.2.2 2001/07/12 19:43:33 nromer Exp $
+ * $Id: mptlan.c,v 1.52 2002/05/06 13:45:07 sshirron Exp $
*/
/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
/*
#define MYNAM "mptlan"
+MODULE_LICENSE("GPL");
+
/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
/*
* MPT LAN message sizes without variable part.
MPT_ADAPTER *mpt_dev;
u8 pnum; /* Port number in the IOC. This is not a Unix network port! */
- atomic_t buckets_out; /* number of unused buckets on IOC */
- int bucketthresh; /* Send more when this many used */
+ atomic_t buckets_out; /* number of unused buckets on IOC */
+ int bucketthresh; /* Send more when this many left */
int *mpt_txfidx; /* Free Tx Context list */
int mpt_txfidx_tail;
struct BufferControl *RcvCtl; /* Receive BufferControl structs */
struct BufferControl *SendCtl; /* Send BufferControl structs */
- int max_buckets_out; /* Max buckets to send to IOC */
- int tx_max_out; /* IOC's Tx queue len */
+ int max_buckets_out; /* Max buckets to send to IOC */
+ int tx_max_out; /* IOC's Tx queue len */
u32 total_posted;
u32 total_received;
static int mpt_lan_reset(struct net_device *dev);
static int mpt_lan_close(struct net_device *dev);
static void mpt_lan_post_receive_buckets(void *dev_id);
-static void mpt_lan_wake_post_buckets_task(struct net_device *dev);
+static void mpt_lan_wake_post_buckets_task(struct net_device *dev,
+ int priority);
static int mpt_lan_receive_post_turbo(struct net_device *dev, u32 tmsg);
static int mpt_lan_receive_post_reply(struct net_device *dev,
LANReceivePostReply_t *pRecvRep);
static struct net_device *mpt_landev[MPT_MAX_ADAPTERS+1];
+#ifdef QLOGIC_NAA_WORKAROUND
static struct NAA_Hosed *mpt_bad_naa = NULL;
rwlock_t bad_naa_lock;
+#endif
/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
/*
case LAN_REPLY_FORM_SEND_SINGLE:
// dioprintk((MYNAM "/lan_reply: "
// "calling mpt_lan_send_reply (turbo)\n"));
-
+
// Potential BUG here? -sralston
// FreeReqFrame = mpt_lan_send_turbo(dev, tmsg);
// If/when mpt_lan_send_turbo would return 1 here,
struct net_device *dev = mpt_landev[ioc->id];
struct mpt_lan_priv *priv = (struct mpt_lan_priv *) dev->priv;
- dprintk((KERN_INFO MYNAM ": IOC %s_reset routed to LAN driver!\n",
+ dlprintk((KERN_INFO MYNAM ": IOC %s_reset routed to LAN driver!\n",
reset_phase==MPT_IOC_PRE_RESET ? "pre" : "post"));
if (priv->mpt_rxfidx == NULL)
if (reset_phase == MPT_IOC_PRE_RESET) {
int i;
unsigned long flags;
-
+
netif_stop_queue(dev);
+ dlprintk ((KERN_INFO "mptlan/ioc_reset: called netif_stop_queue for %s.\n", dev->name));
+
atomic_set(&priv->buckets_out, 0);
/* Reset Rx Free Tail index and re-populate the queue. */
static int
mpt_lan_event_process(MPT_ADAPTER *ioc, EventNotificationReply_t *pEvReply)
{
- dprintk((KERN_INFO MYNAM ": MPT event routed to LAN driver!\n"));
+ dlprintk((KERN_INFO MYNAM ": MPT event routed to LAN driver!\n"));
switch (le32_to_cpu(pEvReply->Event)) {
case MPI_EVENT_NONE: /* 00 */
if (mpt_lan_reset(dev) != 0) {
MPT_ADAPTER *mpt_dev = priv->mpt_dev;
-
+
printk (KERN_WARNING MYNAM "/lan_open: lan_reset failed.");
-
+
if (mpt_dev->active)
printk ("The ioc is active. Perhaps it needs to be"
" reset?\n");
priv->mpt_txfidx[++priv->mpt_txfidx_tail] = i;
}
- dprintk((KERN_INFO MYNAM "@lo: Finished initializing SendCtl\n"));
+ dlprintk((KERN_INFO MYNAM "@lo: Finished initializing SendCtl\n"));
priv->mpt_rxfidx = kmalloc(priv->max_buckets_out * sizeof(int),
GFP_KERNEL);
priv->mpt_rxfidx[++priv->mpt_rxfidx_tail] = i;
}
-/**/ dprintk((KERN_INFO MYNAM "/lo: txfidx contains - "));
+/**/ dlprintk((KERN_INFO MYNAM "/lo: txfidx contains - "));
/**/ for (i = 0; i < priv->tx_max_out; i++)
-/**/ dprintk((" %xh", priv->mpt_txfidx[i]));
-/**/ dprintk(("\n"));
+/**/ dlprintk((" %xh", priv->mpt_txfidx[i]));
+/**/ dlprintk(("\n"));
- dprintk((KERN_INFO MYNAM "/lo: Finished initializing RcvCtl\n"));
+ dlprintk((KERN_INFO MYNAM "/lo: Finished initializing RcvCtl\n"));
mpt_lan_post_receive_buckets(dev);
printk(KERN_INFO MYNAM ": %s/%s: interface up & active\n",
}
netif_start_queue(dev);
- dprintk((KERN_INFO MYNAM "/lo: Done.\n"));
+ dlprintk((KERN_INFO MYNAM "/lo: Done.\n"));
return 0;
out_mpt_rxfidx:
mf = mpt_get_msg_frame(LanCtx, priv->mpt_dev->id);
if (mf == NULL) {
-/* dprintk((KERN_ERR MYNAM "/reset: Evil funkiness abounds! "
+/* dlprintk((KERN_ERR MYNAM "/reset: Evil funkiness abounds! "
"Unable to allocate a request frame.\n"));
*/
return -1;
unsigned int timeout;
int i;
- dprintk((KERN_INFO MYNAM ": mpt_lan_close called\n"));
+ dlprintk((KERN_INFO MYNAM ": mpt_lan_close called\n"));
mpt_event_deregister(LanCtx);
- dprintk((KERN_INFO MYNAM ":lan_close: Posted %d buckets "
+ dlprintk((KERN_INFO MYNAM ":lan_close: Posted %d buckets "
"since driver was loaded, %d still out\n",
priv->total_posted,atomic_read(&priv->buckets_out)));
timeout = 2 * HZ;
while (atomic_read(&priv->buckets_out) && --timeout) {
- current->state = TASK_INTERRUPTIBLE;
+ set_current_state(TASK_INTERRUPTIBLE);
schedule_timeout(1);
}
for (i = 0; i < priv->max_buckets_out; i++) {
if (priv->RcvCtl[i].skb != NULL) {
-/**/ dprintk((KERN_INFO MYNAM "/lan_close: bucket %05x "
+/**/ dlprintk((KERN_INFO MYNAM "/lan_close: bucket %05x "
/**/ "is still out\n", i));
pci_unmap_single(mpt_dev->pcidev, priv->RcvCtl[i].dma,
- priv->RcvCtl[i].len,
+ priv->RcvCtl[i].len,
PCI_DMA_FROMDEVICE);
- dev_kfree_skb(priv->RcvCtl[i].skb);
+ dev_kfree_skb(priv->RcvCtl[i].skb);
}
}
kfree (priv->mpt_rxfidx);
for (i = 0; i < priv->tx_max_out; i++) {
- if (priv->SendCtl[i].skb != NULL) {
+ if (priv->SendCtl[i].skb != NULL) {
pci_unmap_single(mpt_dev->pcidev, priv->SendCtl[i].dma,
priv->SendCtl[i].len,
PCI_DMA_TODEVICE);
- dev_kfree_skb(priv->SendCtl[i].skb);
+ dev_kfree_skb(priv->SendCtl[i].skb);
}
}
static void
mpt_lan_tx_timeout(struct net_device *dev)
{
- netif_wake_queue(dev);
+ struct mpt_lan_priv *priv = (struct mpt_lan_priv *) dev->priv;
+ MPT_ADAPTER *mpt_dev = priv->mpt_dev;
+
+ if (mpt_dev->active) {
+ dlprintk (("mptlan/tx_timeout: calling netif_wake_queue for %s.\n", dev->name));
+ netif_wake_queue(dev);
+ }
}
/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
/* Add check for Loginfo Flag in IOCStatus */
- switch (le16_to_cpu(pSendRep->IOCStatus)) {
+ switch (le16_to_cpu(pSendRep->IOCStatus) & MPI_IOCSTATUS_MASK) {
case MPI_IOCSTATUS_SUCCESS:
priv->stats.tx_packets += count;
break;
dma_addr_t dma;
unsigned long flags;
int ctx;
- struct NAA_Hosed *nh;
u16 cur_naa = 0x1000;
dioprintk((KERN_INFO MYNAM ": %s called, skb_addr = %p\n",
mf = mpt_get_msg_frame(LanCtx, mpt_dev->id);
if (mf == NULL) {
netif_stop_queue(dev);
- dev_kfree_skb(skb);
spin_unlock_irqrestore(&priv->txfidx_lock, flags);
printk (KERN_ERR "%s: Unable to alloc request frame\n",
// IOC_AND_NETDEV_NAMES_s_s(dev),
// ctx, skb, skb->data));
+#ifdef QLOGIC_NAA_WORKAROUND
+{
+ struct NAA_Hosed *nh;
+
/* Munge the NAA for Tx packets to QLogic boards, which don't follow
RFC 2625. The longer I look at this, the more my opinion of Qlogic
drops. */
(nh->ieee[4] == skb->mac.raw[4]) &&
(nh->ieee[5] == skb->mac.raw[5])) {
cur_naa = nh->NAA;
- dprintk ((KERN_INFO "mptlan/sdu_send: using NAA value "
+ dlprintk ((KERN_INFO "mptlan/sdu_send: using NAA value "
"= %04x.\n", cur_naa));
break;
}
}
read_unlock_irq(&bad_naa_lock);
+}
+#endif
pTrans->TransactionDetails[0] = cpu_to_le32((cur_naa << 16) |
(skb->mac.raw[0] << 8) |
pSimple = (SGESimple64_t *) &pTrans->TransactionDetails[2];
/* If we ever decide to send more than one Simple SGE per LANSend, then
- we will need to make sure that LAST_ELEMENT only gets set on the
+ we will need to make sure that LAST_ELEMENT only gets set on the
last one. Otherwise, bad voodoo and evil funkiness will commence. */
pSimple->FlagsLength = cpu_to_le32(
- ((MPI_SGE_FLAGS_LAST_ELEMENT |
+ ((MPI_SGE_FLAGS_LAST_ELEMENT |
MPI_SGE_FLAGS_END_OF_BUFFER |
MPI_SGE_FLAGS_SIMPLE_ELEMENT |
MPI_SGE_FLAGS_SYSTEM_ADDRESS |
dev->trans_start = jiffies;
dioprintk((KERN_INFO MYNAM ": %s/%s: Sending packet. FlagsLength = %08x.\n",
- IOC_AND_NETDEV_NAMES_s_s(dev),
- le32_to_cpu(pSimple->FlagsLength)));
+ IOC_AND_NETDEV_NAMES_s_s(dev),
+ le32_to_cpu(pSimple->FlagsLength)));
return 0;
}
/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
static inline void
-mpt_lan_wake_post_buckets_task(struct net_device *dev)
+mpt_lan_wake_post_buckets_task(struct net_device *dev, int priority)
+/*
+ * @priority: 0 = put it on the timer queue, 1 = put it on the immediate queue
+ */
{
struct mpt_lan_priv *priv = dev->priv;
-
+
if (test_and_set_bit(0, &priv->post_buckets_active) == 0) {
- queue_task(&priv->post_buckets_task, &tq_immediate);
- mark_bh(IMMEDIATE_BH);
- dioprintk((KERN_INFO MYNAM ": %s/%s: Queued post_buckets task.\n",
- IOC_AND_NETDEV_NAMES_s_s(dev) ));
+ if (priority) {
+ queue_task(&priv->post_buckets_task, &tq_immediate);
+ mark_bh(IMMEDIATE_BH);
+ } else {
+ queue_task(&priv->post_buckets_task, &tq_timer);
+ dioprintk((KERN_INFO MYNAM ": post_buckets queued on "
+ "timer.\n"));
+ }
+ dioprintk((KERN_INFO MYNAM ": %s/%s: Queued post_buckets task.\n",
+ IOC_AND_NETDEV_NAMES_s_s(dev) ));
}
}
skb->protocol = mpt_lan_type_trans(skb, dev);
- dioprintk((KERN_INFO MYNAM ": %s/%s: Incoming packet (%d bytes) "
+ dioprintk((KERN_INFO MYNAM ": %s/%s: Incoming packet (%d bytes) "
"delivered to upper level.\n",
IOC_AND_NETDEV_NAMES_s_s(dev), skb->len));
atomic_read(&priv->buckets_out)));
if (atomic_read(&priv->buckets_out) < priv->bucketthresh)
- mpt_lan_wake_post_buckets_task(dev);
+ mpt_lan_wake_post_buckets_task(dev, 1);
dioprintk((KERN_INFO MYNAM "/receive_post_reply: %d buckets "
"remaining, %d received back since sod\n",
unsigned long flags;
struct sk_buff *skb;
u32 ctx;
- u8 count;
+ int count;
int i;
count = pRecvRep->NumberOfContexts;
-/**/ dprintk((KERN_INFO MYNAM "/receive_post_reply: "
+/**/ dlprintk((KERN_INFO MYNAM "/receive_post_reply: "
"IOC returned %d buckets, freeing them...\n", count));
spin_lock_irqsave(&priv->rxfidx_lock, flags);
skb = priv->RcvCtl[ctx].skb;
-// dprintk((KERN_INFO MYNAM ": %s: dev_name = %s\n",
+// dlprintk((KERN_INFO MYNAM ": %s: dev_name = %s\n",
// IOC_AND_NETDEV_NAMES_s_s(dev)));
-// dprintk((KERN_INFO MYNAM "@rpr[2], priv = %p, buckets_out addr = %p",
-// priv, &(priv->buckets_out)));
-// dprintk((KERN_INFO MYNAM "@rpr[2] TC + 3\n"));
+// dlprintk((KERN_INFO MYNAM "@rpr[2], priv = %p, buckets_out addr = %p",
+// priv, &(priv->buckets_out)));
+// dlprintk((KERN_INFO MYNAM "@rpr[2] TC + 3\n"));
priv->RcvCtl[ctx].skb = NULL;
pci_unmap_single(mpt_dev->pcidev, priv->RcvCtl[ctx].dma,
// for (i = 0; i < priv->max_buckets_out; i++)
// if (priv->RcvCtl[i].skb != NULL)
-// dprintk((KERN_INFO MYNAM "@rpr: bucket %03x "
+// dlprintk((KERN_INFO MYNAM "@rpr: bucket %03x "
// "is still out\n", i));
-/* dprintk((KERN_INFO MYNAM "/receive_post_reply: freed %d buckets\n",
+/* dlprintk((KERN_INFO MYNAM "/receive_post_reply: freed %d buckets\n",
count));
*/
-/**/ dprintk((KERN_INFO MYNAM "@receive_post_reply: %d buckets "
+/**/ dlprintk((KERN_INFO MYNAM "@receive_post_reply: %d buckets "
/**/ "remaining, %d received back since sod.\n",
/**/ atomic_read(&priv->buckets_out), priv->total_received));
return 0;
MPT_ADAPTER *mpt_dev = priv->mpt_dev;
struct sk_buff *skb, *old_skb;
unsigned long flags;
- u32 len, ctx;
- u32 offset;
- u8 count;
+ u32 len, ctx, offset;
+ u32 remaining = le32_to_cpu(pRecvRep->BucketsRemaining);
+ int count;
int i, l;
dioprintk((KERN_INFO MYNAM ": mpt_lan_receive_post_reply called\n"));
dioprintk((KERN_INFO MYNAM ": receive_post_reply: IOCStatus: %04x\n",
le16_to_cpu(pRecvRep->IOCStatus)));
- if (le16_to_cpu(pRecvRep->IOCStatus) & MPI_IOCSTATUS_LAN_CANCELED)
+ if ((le16_to_cpu(pRecvRep->IOCStatus) & MPI_IOCSTATUS_MASK) ==
+ MPI_IOCSTATUS_LAN_CANCELED)
return mpt_lan_receive_post_free(dev, pRecvRep);
len = le32_to_cpu(pRecvRep->PacketLength);
if (!skb) {
printk (KERN_ERR MYNAM ": %s/%s: ERROR - Can't allocate skb! (%s@%d)\n",
IOC_AND_NETDEV_NAMES_s_s(dev),
- __FILE__, __LINE__);
+ __FILE__, __LINE__);
return -ENOMEM;
}
if (!skb) {
printk (KERN_ERR MYNAM ": %s/%s: ERROR - Can't allocate skb! (%s@%d)\n",
IOC_AND_NETDEV_NAMES_s_s(dev),
- __FILE__, __LINE__);
+ __FILE__, __LINE__);
return -ENOMEM;
}
"Arrgghh! We've done it again!\n");
}
-#if 0
- {
- u32 remaining = le32_to_cpu(pRecvRep->BucketsRemaining);
- if (remaining < priv->bucketthresh)
- mpt_lan_wake_post_buckets_task(dev);
-
- if (remaining == 0)
- printk (KERN_WARNING MYNAM ": %s/%s: WARNING - IOC out of buckets! "
- "(priv->buckets_out = %d)\n",
- IOC_AND_NETDEV_NAMES_s_s(dev),
- atomic_read(&priv->buckets_out));
- else
- printk (KERN_INFO MYNAM ": %s/%s: IOC says %d buckets left. "
- "(priv->buckets_out = %d)\n",
- IOC_AND_NETDEV_NAMES_s_s(dev),
- remaining, atomic_read(&priv->buckets_out));
+ if (remaining == 0)
+ printk (KERN_WARNING MYNAM ": %s/%s: WARNING - IOC out of buckets! "
+ "(priv->buckets_out = %d)\n",
+ IOC_AND_NETDEV_NAMES_s_s(dev),
+ atomic_read(&priv->buckets_out));
+ else if (remaining < 10)
+ printk (KERN_INFO MYNAM ": %s/%s: IOC says %d buckets left. "
+ "(priv->buckets_out = %d)\n",
+ IOC_AND_NETDEV_NAMES_s_s(dev),
+ remaining, atomic_read(&priv->buckets_out));
+
+ if ((remaining < priv->bucketthresh) &&
+ ((atomic_read(&priv->buckets_out) - remaining) >
+ MPT_LAN_BUCKETS_REMAIN_MISMATCH_THRESH)) {
+
+ printk (KERN_WARNING MYNAM " Mismatch between driver's "
+ "buckets_out count and fw's BucketsRemaining "
+ "count has crossed the threshold, issuing a "
+ "LanReset to clear the fw's hashtable. You may "
+ "want to check your /var/log/messages for \"CRC "
+ "error\" event notifications.\n");
+
+ mpt_lan_reset(dev);
+ mpt_lan_wake_post_buckets_task(dev, 0);
}
-#endif
-
+
return mpt_lan_receive_skb(dev, skb);
}
if (skb == NULL) {
skb = dev_alloc_skb(len);
if (skb == NULL) {
-/**/ printk (KERN_WARNING
-/**/ MYNAM "/%s: Can't alloc skb\n",
-/**/ __FUNCTION__);
+ printk (KERN_WARNING
+ MYNAM "/%s: Can't alloc skb\n",
+ __FUNCTION__);
priv->mpt_rxfidx[++priv->mpt_rxfidx_tail] = ctx;
spin_unlock_irqrestore(&priv->rxfidx_lock, flags);
break;
}
- dma = pci_map_single(mpt_dev->pcidev, skb->data,
+ dma = pci_map_single(mpt_dev->pcidev, skb->data,
len, PCI_DMA_FROMDEVICE);
priv->RcvCtl[ctx].skb = skb;
dioprintk((KERN_INFO MYNAM "/%s: End_buckets = %u, priv->buckets_out = %u\n",
__FUNCTION__, buckets, atomic_read(&priv->buckets_out)));
dioprintk((KERN_INFO MYNAM "/%s: Posted %u buckets and received %u back\n",
- __FUNCTION__, priv->total_posted, priv->total_received));
+ __FUNCTION__, priv->total_posted, priv->total_received));
clear_bit(0, &priv->post_buckets_active);
}
priv->post_buckets_task.data = dev;
priv->post_buckets_active = 0;
- dprintk((KERN_INFO MYNAM "@%d: bucketlen = %d\n",
+ dlprintk((KERN_INFO MYNAM "@%d: bucketlen = %d\n",
__LINE__, dev->mtu + dev->hard_header_len + 4));
atomic_set(&priv->buckets_out, 0);
if (mpt_dev->pfacts[0].MaxLanBuckets < max_buckets_out)
priv->max_buckets_out = mpt_dev->pfacts[0].MaxLanBuckets;
- dprintk((KERN_INFO MYNAM "@%d: MaxLanBuckets=%d, max_buckets_out/priv=%d/%d\n",
+ dlprintk((KERN_INFO MYNAM "@%d: MaxLanBuckets=%d, max_buckets_out/priv=%d/%d\n",
__LINE__,
mpt_dev->pfacts[0].MaxLanBuckets,
max_buckets_out,
dev->tx_timeout = mpt_lan_tx_timeout;
dev->watchdog_timeo = MPT_LAN_TX_TIMEOUT;
- dprintk((KERN_INFO MYNAM ": Finished registering dev "
+ dlprintk((KERN_INFO MYNAM ": Finished registering dev "
"and setting initial values\n"));
SET_MODULE_OWNER(dev);
{
struct net_device *dev;
MPT_ADAPTER *curadapter;
- int i = 0, j;
+ int i, j;
show_mptmod_ver(LANAME, LANVER);
- /* Init the global r/w lock for the bad_naa list. We want to do this
+#ifdef QLOGIC_NAA_WORKAROUND
+ /* Init the global r/w lock for the bad_naa list. We want to do this
before any boards are initialized and may be used. */
rwlock_init(&bad_naa_lock);
+#endif
if ((LanCtx = mpt_register(lan_reply, MPTLAN_DRIVER)) <= 0) {
printk (KERN_ERR MYNAM ": Failed to register with MPT base driver\n");
/* Set the callback index to be used by driver core for turbo replies */
mpt_lan_index = LanCtx;
- dprintk((KERN_INFO MYNAM ": assigned context of %d\n", LanCtx));
+ dlprintk((KERN_INFO MYNAM ": assigned context of %d\n", LanCtx));
if (mpt_reset_register(LanCtx, mpt_lan_ioc_reset) == 0) {
- dprintk((KERN_INFO MYNAM ": Registered for IOC reset notifications\n"));
+ dlprintk((KERN_INFO MYNAM ": Registered for IOC reset notifications\n"));
} else {
printk(KERN_ERR MYNAM ": Eieee! unable to register a reset "
"handler with mptbase! The world is at an end! "
for (j = 0; j < MPT_MAX_ADAPTERS; j++) {
mpt_landev[j] = NULL;
}
- j = 0;
curadapter = mpt_adapter_find_first();
while (curadapter != NULL) {
// printk (KERN_INFO MYNAM ": %s/%s: Max_TX_outstanding = %d\n",
// IOC_AND_NETDEV_NAMES_s_s(dev),
// NETDEV_TO_LANPRIV_PTR(dev)->tx_max_out);
+ j = curadapter->id;
mpt_landev[j] = dev;
- dprintk((KERN_INFO MYNAM "/init: dev_addr=%p, mpt_landev[%d]=%p\n",
+ dlprintk((KERN_INFO MYNAM "/init: dev_addr=%p, mpt_landev[%d]=%p\n",
dev, j, mpt_landev[j]));
- j++;
} else {
printk (KERN_ERR MYNAM ": %s: Unable to register port%d as a LAN device\n",
curadapter->name,
MODULE_PARM(tx_max_out_p, "i");
MODULE_PARM(max_buckets_out, "i"); // Debug stuff. FIXME!
-MODULE_LICENSE("GPL");
-
module_init(mpt_lan_init);
module_exit(mpt_lan_exit);
/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
static unsigned short
-mpt_lan_type_trans(struct sk_buff *skb, struct net_device *dev)
+mpt_lan_type_trans(struct sk_buff *skb, struct net_device *dev)
{
struct mpt_lan_ohdr *fch = (struct mpt_lan_ohdr *)skb->data;
struct fcllc *fcllc;
- u16 source_naa = fch->stype, found = 0;
skb->mac.raw = skb->data;
skb_pull(skb, sizeof(struct mpt_lan_ohdr));
printk (KERN_WARNING MYNAM ": %s: WARNING - Broadcast swap F/W bug detected!\n",
NETDEV_PTR_TO_IOC_NAME_s(dev));
printk (KERN_WARNING MYNAM ": Please update sender @ MAC_addr = %02x:%02x:%02x:%02x:%02x:%02x\n",
- fch->saddr[0], fch->saddr[1], fch->saddr[2],
+ fch->saddr[0], fch->saddr[1], fch->saddr[2],
fch->saddr[3], fch->saddr[4], fch->saddr[5]);
}
fcllc = (struct fcllc *)skb->data;
+#ifdef QLOGIC_NAA_WORKAROUND
+{
+ u16 source_naa = fch->stype, found = 0;
+
/* Workaround for QLogic not following RFC 2625 in regards to the NAA
value. */
source_naa = swab16(source_naa);
if (fcllc->ethertype == htons(ETH_P_ARP))
- dprintk ((KERN_INFO "mptlan/type_trans: got arp req/rep w/ naa of "
+ dlprintk ((KERN_INFO "mptlan/type_trans: got arp req/rep w/ naa of "
"%04x.\n", source_naa));
- if ((fcllc->ethertype == htons(ETH_P_ARP)) &&
+ if ((fcllc->ethertype == htons(ETH_P_ARP)) &&
((source_naa >> 12) != MPT_LAN_NAA_RFC2625)){
struct NAA_Hosed *nh, *prevnh;
int i;
- dprintk ((KERN_INFO "mptlan/type_trans: ARP Req/Rep from "
+ dlprintk ((KERN_INFO "mptlan/type_trans: ARP Req/Rep from "
"system with non-RFC 2625 NAA value (%04x).\n",
source_naa));
(nh->ieee[4] == fch->saddr[4]) &&
(nh->ieee[5] == fch->saddr[5])) {
found = 1;
- dprintk ((KERN_INFO "mptlan/type_trans: ARP Re"
+ dlprintk ((KERN_INFO "mptlan/type_trans: ARP Re"
"q/Rep w/ bad NAA from system already"
" in DB.\n"));
break;
}
}
-
+
if ((!found) && (nh == NULL)) {
nh = kmalloc(sizeof(struct NAA_Hosed), GFP_KERNEL);
- dprintk ((KERN_INFO "mptlan/type_trans: ARP Req/Rep w/"
+ dlprintk ((KERN_INFO "mptlan/type_trans: ARP Req/Rep w/"
" bad NAA from system not yet in DB.\n"));
if (nh != NULL) {
mpt_bad_naa = nh;
if (prevnh)
prevnh->next = nh;
-
+
nh->NAA = source_naa; /* Set the S_NAA value. */
for (i = 0; i < FC_ALEN; i++)
nh->ieee[i] = fch->saddr[i];
- dprintk ((KERN_INFO "Got ARP from %02x:%02x:%02x:%02x:"
+ dlprintk ((KERN_INFO "Got ARP from %02x:%02x:%02x:%02x:"
"%02x:%02x with non-compliant S_NAA value.\n",
fch->saddr[0], fch->saddr[1], fch->saddr[2],
fch->saddr[3], fch->saddr[4],fch->saddr[5]));
}
write_unlock_irq(&bad_naa_lock);
}
-
+}
+#endif
- /* Strip the SNAP header from ARP packets since we don't
+ /* Strip the SNAP header from ARP packets since we don't
* pass them through to the 802.2/SNAP layers.
*/
if (fcllc->dsap == EXTENDED_SAP &&
#include <linux/miscdevice.h>
#include <linux/spinlock.h>
#include <linux/tqueue.h>
+#include <linux/delay.h>
// #include <linux/trdevice.h>
#include <asm/uaccess.h>
#define MPT_LAN_MAX_BUCKETS_OUT 256
#define MPT_LAN_BUCKET_THRESH 18 /* 9 buckets in one message */
+#define MPT_LAN_BUCKETS_REMAIN_MISMATCH_THRESH 10
#define MPT_LAN_RX_COPYBREAK 200
-#define MPT_LAN_TX_TIMEOUT (1*HZ)
+#define MPT_LAN_TX_TIMEOUT (1*HZ)
#define MPT_TX_MAX_OUT_LIM 127
#define MPT_LAN_MIN_MTU 96 /* RFC2625 */
#define MPT_LAN_MAX_MTU 65280 /* RFC2625 */
-#define MPT_LAN_MTU 16128 /* be nice to slab allocator */
+#define MPT_LAN_MTU 13312 /* Max perf range + lower mem
+ usage than 16128 */
#define MPT_LAN_NAA_RFC2625 0x1
#define MPT_LAN_NAA_QLOGIC 0x2
#define dioprintk(x)
#endif
+#ifdef MPT_LAN_DEBUG
+#define dlprintk(x) printk x
+#else
+#define dlprintk(x)
+#endif
+
#define NETDEV_TO_LANPRIV_PTR(d) ((struct mpt_lan_priv *)(d)->priv)
#define NETDEV_PTR_TO_IOC_NAME_s(d) (NETDEV_TO_LANPRIV_PTR(d)->mpt_dev->name)
#define IOC_AND_NETDEV_NAMES_s_s(d) NETDEV_PTR_TO_IOC_NAME_s(d), (d)->name
* This driver would not exist if not for Alan Cox's development
* of the linux i2o driver.
*
+ * A special thanks to Pamela Delaney (LSI Logic) for tons of work
+ * and countless enhancements while adding support for the 1030
+ * chip family. Pam has been instrumental in the development of
+ * of the 2.xx.xx series fusion drivers, and her contributions are
+ * far too numerous to hope to list in one place.
+ *
* A huge debt of gratitude is owed to David S. Miller (DaveM)
* for fixing much of the stupid and broken stuff in the early
* driver while porting to sparc64 platform. THANK YOU!
*
* (see mptbase.c)
*
- * Copyright (c) 1999-2001 LSI Logic Corporation
+ * Copyright (c) 1999-2002 LSI Logic Corporation
* Original author: Steven J. Ralston
- * (mailto:Steve.Ralston@lsil.com)
+ * (mailto:sjralston1@netscape.net)
+ * (mailto:Pam.Delaney@lsil.com)
*
- * $Id: mptscsih.c,v 1.29.4.1 2001/09/18 03:22:30 sralston Exp $
+ * $Id: mptscsih.c,v 1.95 2002/06/20 13:28:17 pdelaney Exp $
*/
/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
/*
#include <linux/errno.h>
#include <linux/kdev_t.h>
#include <linux/blkdev.h>
-#include <linux/blk.h>
+#include <linux/blk.h> /* for io_request_lock (spinlock) decl */
+#include <linux/delay.h> /* for mdelay */
+#include <linux/interrupt.h> /* needed for in_interrupt() proto */
+#include <linux/reboot.h> /* notifier code */
#include "../../scsi/scsi.h"
#include "../../scsi/hosts.h"
#include "../../scsi/sd.h"
MODULE_DESCRIPTION(my_NAME);
MODULE_LICENSE("GPL");
+/* Set string for command line args from insmod */
+#ifdef MODULE
+char *mptscsih = 0;
+MODULE_PARM(mptscsih, "s");
+#endif
/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
typedef struct _BIG_SENSE_BUF {
- u8 data[256];
+ u8 data[MPT_SENSE_BUFFER_ALLOC];
} BIG_SENSE_BUF;
-typedef struct _MPT_SCSI_HOST {
- MPT_ADAPTER *ioc;
- int port;
- struct scsi_cmnd **ScsiLookup;
- u8 *SgHunks;
- dma_addr_t SgHunksDMA;
- u32 qtag_tick;
-} MPT_SCSI_HOST;
-
-typedef struct _MPT_SCSI_DEV {
- struct _MPT_SCSI_DEV *forw;
- struct _MPT_SCSI_DEV *back;
- MPT_ADAPTER *ioc;
- int sense_sz;
- BIG_SENSE_BUF CachedSense;
- unsigned long io_cnt;
- unsigned long read_cnt;
-} MPT_SCSI_DEV;
+#define MPT_SCANDV_GOOD (0x00000000) /* must be 0 */
+#define MPT_SCANDV_DID_RESET (0x00000001)
+#define MPT_SCANDV_SENSE (0x00000002)
+#define MPT_SCANDV_SOME_ERROR (0x00000004)
+#define MPT_SCANDV_SELECTION_TIMEOUT (0x00000008)
+
+#define MPT_SCANDV_MAX_RETRIES (10)
+
+#define MPT_ICFLAG_BUF_CAP 0x01 /* ReadBuffer Read Capacity format */
+#define MPT_ICFLAG_ECHO 0x02 /* ReadBuffer Echo buffer format */
+#define MPT_ICFLAG_PHYS_DISK 0x04 /* Any SCSI IO but do Phys Disk Format */
+#define MPT_ICFLAG_TAGGED_CMD 0x08 /* Do tagged IO */
+#define MPT_ICFLAG_DID_RESET 0x20 /* Bus Reset occured with this command */
+#define MPT_ICFLAG_RESERVED 0x40 /* Reserved has been issued */
+
+typedef struct _internal_cmd {
+ char *data; /* data pointer */
+ dma_addr_t data_dma; /* data dma address */
+ int size; /* transfer size */
+ u8 cmd; /* SCSI Op Code */
+ u8 bus; /* bus number */
+ u8 id; /* SCSI ID (virtual) */
+ u8 lun;
+ u8 flags; /* Bit Field - See above */
+ u8 physDiskNum; /* Phys disk number, -1 else */
+ u8 rsvd2;
+ u8 rsvd;
+} INTERNAL_CMD;
+
+typedef struct _negoparms {
+ u8 width;
+ u8 offset;
+ u8 factor;
+ u8 flags;
+} NEGOPARMS;
+
+typedef struct _dv_parameters {
+ NEGOPARMS max;
+ NEGOPARMS now;
+ u8 cmd;
+ u8 id;
+ u16 pad1;
+} DVPARAMETERS;
+
/*
* Other private/forward protos...
*/
-
static int mptscsih_io_done(MPT_ADAPTER *ioc, MPT_FRAME_HDR *mf, MPT_FRAME_HDR *r);
static void mptscsih_report_queue_full(Scsi_Cmnd *sc, SCSIIOReply_t *pScsiReply, SCSIIORequest_t *pScsiReq);
static int mptscsih_taskmgmt_complete(MPT_ADAPTER *ioc, MPT_FRAME_HDR *mf, MPT_FRAME_HDR *r);
static int mptscsih_io_direction(Scsi_Cmnd *cmd);
+
+static int mptscsih_AddSGE(MPT_SCSI_HOST *hd, Scsi_Cmnd *SCpnt,
+ SCSIIORequest_t *pReq, int req_idx);
+static int mptscsih_getFreeChainBuffer(MPT_SCSI_HOST *hd, int *retIndex);
+static void mptscsih_freeChainBuffers(MPT_SCSI_HOST *hd, int req_idx);
+static int mptscsih_initChainBuffers (MPT_SCSI_HOST *hd, int init);
+
static void copy_sense_data(Scsi_Cmnd *sc, MPT_SCSI_HOST *hd, MPT_FRAME_HDR *mf, SCSIIOReply_t *pScsiReply);
-static u32 SCPNT_TO_MSGCTX(Scsi_Cmnd *sc);
+#ifndef MPT_SCSI_USE_NEW_EH
+static void search_taskQ_for_cmd(Scsi_Cmnd *sc, MPT_SCSI_HOST *hd);
+#else
+static int mptscsih_tm_pending_wait(MPT_SCSI_HOST * hd);
+#endif
+static u32 SCPNT_TO_LOOKUP_IDX(Scsi_Cmnd *sc);
+static MPT_FRAME_HDR *mptscsih_search_pendingQ(MPT_SCSI_HOST *hd, int scpnt_idx);
+static void post_pendingQ_commands(MPT_SCSI_HOST *hd);
+
+static int mptscsih_TMHandler(MPT_SCSI_HOST *hd, u8 type, u8 target, u8 lun, int ctx2abort, int sleepFlag);
+static int mptscsih_IssueTaskMgmt(MPT_SCSI_HOST *hd, u8 type, u8 target, u8 lun, int ctx2abort, int sleepFlag);
static int mptscsih_ioc_reset(MPT_ADAPTER *ioc, int post_reset);
static int mptscsih_event_process(MPT_ADAPTER *ioc, EventNotificationReply_t *pEvReply);
+static VirtDevice *mptscsih_initTarget(MPT_SCSI_HOST *hd, int bus_id, int target_id, u8 lun, char *data, int dlen);
+void mptscsih_setTargetNegoParms(MPT_SCSI_HOST *hd, VirtDevice *target, char byte56);
+static void clear_sense_flag(MPT_SCSI_HOST *hd, SCSIIORequest_t *pReq);
+static void mptscsih_set_dvflags(MPT_SCSI_HOST *hd, SCSIIORequest_t *pReq, char *data);
+static void mptscsih_setDevicePage1Flags (u8 width, u8 factor, u8 offset, int *requestedPtr, int *configurationPtr, u8 flags);
+static void mptscsih_no_negotiate(MPT_SCSI_HOST *hd, int target_id);
+static int mptscsih_writeSDP1(MPT_SCSI_HOST *hd, int portnum, int target, int flags);
+static int mptscsih_scandv_complete(MPT_ADAPTER *ioc, MPT_FRAME_HDR *mf, MPT_FRAME_HDR *r);
+static void mptscsih_timer_expired(unsigned long data);
+static void mptscsih_taskmgmt_timeout(unsigned long data);
+static int mptscsih_do_cmd(MPT_SCSI_HOST *hd, INTERNAL_CMD *iocmd);
+static int mptscsih_synchronize_cache(MPT_SCSI_HOST *hd, int portnum);
+
+#ifndef MPTSCSIH_DISABLE_DOMAIN_VALIDATION
+static int mptscsih_do_raid(MPT_SCSI_HOST *hd, u8 action, INTERNAL_CMD *io);
+static void mptscsih_domainValidation(void *hd);
+static int mptscsih_is_phys_disk(MPT_ADAPTER *ioc, int id);
+static void mptscsih_qas_check(MPT_SCSI_HOST *hd);
+static void mptscsih_doDv(MPT_SCSI_HOST *hd, int portnum, int target);
+static void mptscsih_dv_parms(MPT_SCSI_HOST *hd, DVPARAMETERS *dv,void *pPage);
+static void mptscsih_fillbuf(char *buffer, int size, int index, int width);
+#endif
+static int mptscsih_setup(char *str);
+static int mptscsih_halt(struct notifier_block *nb, ulong event, void *buf);
+
+/*
+ * Reboot Notification
+ */
+static struct notifier_block mptscsih_notifier = {
+ mptscsih_halt, NULL, 0
+};
+
+/*
+ * Private data...
+ */
static int mpt_scsi_hosts = 0;
static atomic_t queue_depth;
static int ScsiDoneCtx = -1;
static int ScsiTaskCtx = -1;
+static int ScsiScanDvCtx = -1; /* Used only for bus scan and dv */
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,3,28)
static struct proc_dir_entry proc_mpt_scsihost =
};
#endif
-#define SNS_LEN(scp) sizeof((scp)->sense_buffer)
+#define SNS_LEN(scp) sizeof((scp)->sense_buffer)
#ifndef MPT_SCSI_USE_NEW_EH
/*
* Stuff to handle single-threading SCSI TaskMgmt
* (abort/reset) requests...
*/
-static spinlock_t mpt_scsih_taskQ_lock = SPIN_LOCK_UNLOCKED;
-static MPT_Q_TRACKER mpt_scsih_taskQ = {
- (MPT_FRAME_HDR*) &mpt_scsih_taskQ,
- (MPT_FRAME_HDR*) &mpt_scsih_taskQ
-};
-static int mpt_scsih_taskQ_cnt = 0;
-static int mpt_scsih_taskQ_bh_active = 0;
-static MPT_FRAME_HDR *mpt_scsih_active_taskmgmt_mf = NULL;
+static spinlock_t mytaskQ_lock = SPIN_LOCK_UNLOCKED;
+static int mytaskQ_bh_active = 0;
+static struct tq_struct mptscsih_ptaskfoo;
+static atomic_t mpt_taskQdepth;
+#endif
+
+#ifndef MPTSCSIH_DISABLE_DOMAIN_VALIDATION
+/*
+ * Domain Validation task structure
+ */
+static spinlock_t dvtaskQ_lock = SPIN_LOCK_UNLOCKED;
+static int dvtaskQ_active = 0;
+static int dvtaskQ_release = 0;
+static struct tq_struct mptscsih_dvTask;
#endif
+/*
+ * Wait Queue setup
+ */
+static DECLARE_WAIT_QUEUE_HEAD (scandv_waitq);
+static int scandv_wait_done = 1;
+
+/* Driver default setup
+ */
+static struct mptscsih_driver_setup
+ driver_setup = MPTSCSIH_DRIVER_SETUP;
+
/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
/*
* mptscsih_io_done - Main SCSI IO callback routine registered to
* Returns 1 indicating alloc'd request frame ptr should be freed.
*/
static int
-mptscsih_io_done(MPT_ADAPTER *ioc, MPT_FRAME_HDR *mf, MPT_FRAME_HDR *r)
+mptscsih_io_done(MPT_ADAPTER *ioc, MPT_FRAME_HDR *mf, MPT_FRAME_HDR *mr)
{
Scsi_Cmnd *sc;
MPT_SCSI_HOST *hd;
- MPT_SCSI_DEV *mpt_sdev = NULL;
+ SCSIIORequest_t *pScsiReq;
+ SCSIIOReply_t *pScsiReply;
+#ifndef MPT_SCSI_USE_NEW_EH
+ unsigned long flags;
+#endif
u16 req_idx;
+ hd = (MPT_SCSI_HOST *) ioc->sh->hostdata;
+
if ((mf == NULL) ||
(mf >= MPT_INDEX_2_MFPTR(ioc, ioc->req_depth))) {
- printk(KERN_ERR MYNAM ": ERROR! NULL or BAD req frame ptr (=%p)!\n", mf);
- return 1;
+ printk(MYIOC_s_ERR_FMT "%s req frame ptr! (=%p)!\n",
+ ioc->name, mf?"BAD":"NULL", (void *) mf);
+ /* return 1; CHECKME SteveR. Don't free. */
+ return 0;
}
- hd = (MPT_SCSI_HOST *) ioc->sh->hostdata;
req_idx = le16_to_cpu(mf->u.frame.hwhdr.msgctxu.fld.req_idx);
sc = hd->ScsiLookup[req_idx];
- hd->ScsiLookup[req_idx] = NULL;
+ if (sc == NULL) {
+ MPIHeader_t *hdr = (MPIHeader_t *)mf;
- dmfprintk((KERN_INFO MYNAM ": ScsiDone (req:sc:reply=%p:%p:%p)\n", mf, sc, r));
+ atomic_dec(&queue_depth);
- atomic_dec(&queue_depth);
+ /* writeSDP1 will use the ScsiDoneCtx
+ * There is no processing for the reply.
+ * Just return to the calling function.
+ */
+ if (hdr->Function == MPI_FUNCTION_SCSI_IO_REQUEST)
+ printk(MYIOC_s_ERR_FMT "NULL ScsiCmd ptr!\n", ioc->name);
- /*
- * Check for {1st} {IO} completion to "new" device.
- * How do we know it's a new device?
- * If we haven't set SDpnt->hostdata I guess...
- */
- if (sc && sc->device) {
- mpt_sdev = (MPT_SCSI_DEV*)sc->device->hostdata;
- if (!mpt_sdev) {
- dprintk((KERN_INFO MYNAM ": *NEW* SCSI device (%d:%d:%d)!\n",
- sc->device->id, sc->device->lun, sc->device->channel));
- if ((sc->device->hostdata = kmalloc(sizeof(MPT_SCSI_DEV), GFP_ATOMIC)) == NULL) {
- printk(KERN_ERR MYNAM ": ERROR - kmalloc(%d) FAILED!\n", (int)sizeof(MPT_SCSI_DEV));
- } else {
- memset(sc->device->hostdata, 0, sizeof(MPT_SCSI_DEV));
- mpt_sdev = (MPT_SCSI_DEV *) sc->device->hostdata;
- mpt_sdev->ioc = ioc;
- }
- } else {
- if (++mpt_sdev->io_cnt && mptscsih_io_direction(sc) < 0) {
- if (++mpt_sdev->read_cnt == 3) {
- dprintk((KERN_INFO MYNAM ": 3rd DATA_IN, CDB[0]=%02x\n",
- sc->cmnd[0]));
- }
- }
-#if 0
- if (mpt_sdev->sense_sz) {
- /*
- * Completion of first IO down this path
- * *should* invalidate device SenseData...
- */
- mpt_sdev->sense_sz = 0;
- }
-#endif
- }
+ mptscsih_freeChainBuffers(hd, req_idx);
+ return 1;
}
-#if 0
-{
- MPT_FRAME_HDR *mf_chk;
+ dmfprintk((MYIOC_s_INFO_FMT "ScsiDone (mf=%p,mr=%p,sc=%p)\n",
+ ioc->name, mf, mr, sc));
- /* This, I imagine, is a costly check, but...
- * If abort/reset active, check to see if this is a IO
- * that completed while ABORT/RESET for it is waiting
- * on our taskQ!
- */
- if (! Q_IS_EMPTY(&mpt_scsih_taskQ)) {
- /* If ABORT for this IO is queued, zap it! */
- mf_chk = search_taskQ(1,sc,MPI_SCSITASKMGMT_TASKTYPE_ABORT_TASK);
- if (mf_chk != NULL) {
- sc->result = DID_ABORT << 16;
- spin_lock_irqsave(sc->host->host_lock, flags);
- sc->scsi_done(sc);
- spin_unlock_irqrestore(sc->host->host_lock, flags);
- return 1;
- }
- }
-}
-#endif
+ atomic_dec(&queue_depth);
- if (r != NULL && sc != NULL) {
- SCSIIOReply_t *pScsiReply;
- SCSIIORequest_t *pScsiReq;
- u16 status;
+ sc->result = DID_OK << 16; /* Set default reply as OK */
+ pScsiReq = (SCSIIORequest_t *) mf;
+ pScsiReply = (SCSIIOReply_t *) mr;
+
+ if (pScsiReply == NULL) {
+ /* special context reply handling */
+
+ /* If regular Inquiry cmd - save inquiry data
+ */
+ if (pScsiReq->CDB[0] == INQUIRY && !(pScsiReq->CDB[1] & 0x3)) {
+ int dlen;
+
+ dlen = le32_to_cpu(pScsiReq->DataLength);
+ if (dlen >= SCSI_STD_INQUIRY_BYTES) {
+ mptscsih_initTarget(hd,
+ hd->port,
+ sc->target,
+ pScsiReq->LUN[1],
+ sc->buffer,
+ dlen);
+ }
+ }
+ clear_sense_flag(hd, pScsiReq);
- pScsiReply = (SCSIIOReply_t *) r;
- pScsiReq = (SCSIIORequest_t *) mf;
+ if (hd->is_spi)
+ mptscsih_set_dvflags(hd, pScsiReq, sc->buffer);
+ } else {
+ u32 xfer_cnt;
+ u16 status;
+ u8 scsi_state;
status = le16_to_cpu(pScsiReply->IOCStatus) & MPI_IOCSTATUS_MASK;
+ scsi_state = pScsiReply->SCSIState;
- dprintk((KERN_NOTICE MYNAM ": Uh-Oh! (req:sc:reply=%p:%p:%p)\n", mf, sc, r));
+ dprintk((KERN_NOTICE " Uh-Oh! (%d:%d:%d) mf=%p, mr=%p, sc=%p\n",
+ ioc->id, pScsiReq->TargetID, pScsiReq->LUN[1],
+ mf, mr, sc));
dprintk((KERN_NOTICE " IOCStatus=%04xh, SCSIState=%02xh"
- ", SCSIStatus=%02xh, IOCLogInfo=%08xh\n",
- status, pScsiReply->SCSIState, pScsiReply->SCSIStatus,
- le32_to_cpu(pScsiReply->IOCLogInfo)));
+ ", SCSIStatus=%02xh, IOCLogInfo=%08xh\n",
+ status, scsi_state, pScsiReply->SCSIStatus,
+ le32_to_cpu(pScsiReply->IOCLogInfo)));
+
+ if (scsi_state & MPI_SCSI_STATE_AUTOSENSE_VALID)
+ copy_sense_data(sc, hd, mf, pScsiReply);
/*
* Look for + dump FCP ResponseInfo[]!
*/
- if (pScsiReply->SCSIState & MPI_SCSI_STATE_RESPONSE_INFO_VALID) {
+ if (scsi_state & MPI_SCSI_STATE_RESPONSE_INFO_VALID) {
dprintk((KERN_NOTICE " FCP_ResponseInfo=%08xh\n",
le32_to_cpu(pScsiReply->ResponseInfo)));
}
switch(status) {
case MPI_IOCSTATUS_BUSY: /* 0x0002 */
- /*sc->result = DID_BUS_BUSY << 16;*/ /* YIKES! - Seems to
- * kill linux interrupt
- * handler
- */
- sc->result = STS_BUSY; /* Try SCSI BUSY! */
- break;
-
- case MPI_IOCSTATUS_SCSI_RECOVERED_ERROR: /* 0x0040 */
- /* Not real sure here... */
- sc->result = DID_OK << 16;
+ /* CHECKME!
+ * Maybe: DRIVER_BUSY | SUGGEST_RETRY | DID_SOFT_ERROR (retry)
+ * But not: DID_BUS_BUSY lest one risk
+ * killing interrupt handler:-(
+ */
+ sc->result = STS_BUSY;
break;
case MPI_IOCSTATUS_SCSI_INVALID_BUS: /* 0x0041 */
break;
case MPI_IOCSTATUS_SCSI_DEVICE_NOT_THERE: /* 0x0043 */
- /* Spoof to SCSI Selection Timeout! */
+ /* Spoof to SCSI Selection Timeout! */
sc->result = DID_NO_CONNECT << 16;
break;
+ case MPI_IOCSTATUS_SCSI_TASK_TERMINATED: /* 0x0048 */
+#ifndef MPT_SCSI_USE_NEW_EH
+ search_taskQ_for_cmd(sc, hd);
+#endif
+ /* Linux handles an unsolicited DID_RESET better
+ * than an unsolicited DID_ABORT.
+ */
+ sc->result = DID_RESET << 16;
+
+ /* GEM Workaround. */
+ if (hd->is_spi)
+ mptscsih_no_negotiate(hd, sc->target);
+ break;
+
+ case MPI_IOCSTATUS_SCSI_IOC_TERMINATED: /* 0x004B */
+ case MPI_IOCSTATUS_SCSI_EXT_TERMINATED: /* 0x004C */
+#ifndef MPT_SCSI_USE_NEW_EH
+ search_taskQ_for_cmd(sc, hd);
+#endif
+ sc->result = DID_RESET << 16;
+
+ /* GEM Workaround. */
+ if (hd->is_spi)
+ mptscsih_no_negotiate(hd, sc->target);
+ break;
+
+ case MPI_IOCSTATUS_SCSI_RESIDUAL_MISMATCH: /* 0x0049 */
case MPI_IOCSTATUS_SCSI_DATA_UNDERRUN: /* 0x0045 */
/*
* YIKES! I just discovered that SCSI IO which
* Do upfront check for valid SenseData and give it
* precedence!
*/
+ sc->result = (DID_OK << 16) | pScsiReply->SCSIStatus;
+ clear_sense_flag(hd, pScsiReq);
if (pScsiReply->SCSIState & MPI_SCSI_STATE_AUTOSENSE_VALID) {
- copy_sense_data(sc, hd, mf, pScsiReply);
- sc->result = pScsiReply->SCSIStatus;
- break;
+ /* Have already saved the status and sense data
+ */
+ ;
+ } else if (pScsiReply->SCSIState & (MPI_SCSI_STATE_AUTOSENSE_FAILED | MPI_SCSI_STATE_NO_SCSI_STATUS)) {
+ /* What to do?
+ */
+ sc->result = DID_SOFT_ERROR << 16;
+ }
+ else if (pScsiReply->SCSIState & MPI_SCSI_STATE_TERMINATED) {
+ /* Not real sure here either... */
+ sc->result = DID_RESET << 16;
}
- dprintk((KERN_NOTICE MYNAM ": sc->underflow={report ERR if < %02xh bytes xfer'd}\n", sc->underflow));
- dprintk((KERN_NOTICE MYNAM ": ActBytesXferd=%02xh\n", le32_to_cpu(pScsiReply->TransferCount)));
+ /* Give report and update residual count.
+ */
+ xfer_cnt = le32_to_cpu(pScsiReply->TransferCount);
+ dprintk((KERN_NOTICE " sc->underflow={report ERR if < %02xh bytes xfer'd}\n",
+ sc->underflow));
+ dprintk((KERN_NOTICE " ActBytesXferd=%02xh\n", xfer_cnt));
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,3,0)
- sc->resid = sc->request_bufflen - le32_to_cpu(pScsiReply->TransferCount);
- dprintk((KERN_NOTICE MYNAM ": SET sc->resid=%02xh\n", sc->resid));
+ sc->resid = sc->request_bufflen - xfer_cnt;
+ dprintk((KERN_NOTICE " SET sc->resid=%02xh\n", sc->resid));
#endif
- if (pScsiReq->CDB[0] == INQUIRY) {
- sc->result = (DID_OK << 16);
- break;
- }
+ /* Report Queue Full
+ */
+ if (sc->result == MPI_SCSI_STATUS_TASK_SET_FULL)
+ mptscsih_report_queue_full(sc, pScsiReply, pScsiReq);
- /* workaround attempts... */
-#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,3,0)
- if (sc->resid >= 0x200) {
- /* GRRRRR...
- * //sc->result = DID_SOFT_ERROR << 16;
- * Try spoofing to BUSY
- */
- sc->result = STS_BUSY;
- } else {
- sc->result = 0;
+ /* If regular Inquiry cmd and some data was transferred,
+ * save inquiry data
+ */
+ if ( pScsiReq->CDB[0] == INQUIRY
+ && !(pScsiReq->CDB[1] & 0x3)
+ && xfer_cnt >= SCSI_STD_INQUIRY_BYTES
+ ) {
+ mptscsih_initTarget(hd,
+ hd->port,
+ sc->target,
+ pScsiReq->LUN[1],
+ sc->buffer,
+ xfer_cnt);
}
-#else
- sc->result = 0;
-#endif
- break;
-
- case MPI_IOCSTATUS_SCSI_TASK_TERMINATED: /* 0x0048 */
- sc->result = DID_ABORT << 16;
- break;
- case MPI_IOCSTATUS_SCSI_IOC_TERMINATED: /* 0x004B */
- case MPI_IOCSTATUS_SCSI_EXT_TERMINATED: /* 0x004C */
- sc->result = DID_RESET << 16;
+ if (hd->is_spi)
+ mptscsih_set_dvflags(hd, pScsiReq, sc->buffer);
break;
+ case MPI_IOCSTATUS_SCSI_RECOVERED_ERROR: /* 0x0040 */
case MPI_IOCSTATUS_SUCCESS: /* 0x0000 */
- sc->result = pScsiReply->SCSIStatus;
+ sc->result = (DID_OK << 16) | pScsiReply->SCSIStatus;
+ clear_sense_flag(hd, pScsiReq);
if (pScsiReply->SCSIState & MPI_SCSI_STATE_AUTOSENSE_VALID) {
- copy_sense_data(sc, hd, mf, pScsiReply);
-
- /* If running agains circa 200003dd 909 MPT f/w,
- * may get this (AUTOSENSE_VALID) for actual TASK_SET_FULL
- * (QUEUE_FULL) returned from device! --> get 0x0000?128
- * and with SenseBytes set to 0.
+ /*
+ * If running agains circa 200003dd 909 MPT f/w,
+ * may get this (AUTOSENSE_VALID) for actual TASK_SET_FULL
+ * (QUEUE_FULL) returned from device! --> get 0x0000?128
+ * and with SenseBytes set to 0.
*/
if (pScsiReply->SCSIStatus == MPI_SCSI_STATUS_TASK_SET_FULL)
mptscsih_report_queue_full(sc, pScsiReply, pScsiReq);
+
+#ifndef MPT_SCSI_USE_NEW_EH
+ /* ADDED 20011120 -sralston
+ * Scsi mid-layer (old_eh) doesn't seem to like it
+ * when RAID returns SCSIStatus=02 (CHECK CONDITION),
+ * SenseKey=01 (RECOVERED ERROR), ASC/ASCQ=95/01.
+ * Seems to be * treating this as a IO error:-(
+ *
+ * So just lie about it altogether here.
+ *
+ * NOTE: It still gets reported to syslog via
+ * mpt_ScsiHost_ErrorReport from copy_sense_data
+ * call far above.
+ */
+ if ( pScsiReply->SCSIStatus == STS_CHECK_CONDITION
+ && SD_Sense_Key(sc->sense_buffer) == SK_RECOVERED_ERROR
+ ) {
+ sc->result = 0;
+ }
+#endif
+
}
- else if (pScsiReply->SCSIState & (MPI_SCSI_STATE_AUTOSENSE_FAILED | MPI_SCSI_STATE_NO_SCSI_STATUS)) {
+ else if (pScsiReply->SCSIState &
+ (MPI_SCSI_STATE_AUTOSENSE_FAILED | MPI_SCSI_STATE_NO_SCSI_STATUS)
+ ) {
/*
- * What to do?
+ * What to do?
*/
sc->result = DID_SOFT_ERROR << 16;
}
else if (pScsiReply->SCSIState & MPI_SCSI_STATE_TERMINATED) {
/* Not real sure here either... */
- sc->result = DID_ABORT << 16;
+ sc->result = DID_RESET << 16;
+ }
+ else if (pScsiReply->SCSIState & MPI_SCSI_STATE_QUEUE_TAG_REJECTED) {
+ /* Device Inq. data indicates that it supports
+ * QTags, but rejects QTag messages.
+ * This command completed OK.
+ *
+ * Not real sure here either so do nothing... */
}
if (sc->result == MPI_SCSI_STATUS_TASK_SET_FULL)
mptscsih_report_queue_full(sc, pScsiReply, pScsiReq);
+ /* Add handling of:
+ * Reservation Conflict, Busy,
+ * Command Terminated, CHECK
+ */
+
+ /* If regular Inquiry cmd - save inquiry data
+ */
+ xfer_cnt = le32_to_cpu(pScsiReply->TransferCount);
+ if ( sc->result == (DID_OK << 16)
+ && pScsiReq->CDB[0] == INQUIRY
+ && !(pScsiReq->CDB[1] & 0x3)
+ && xfer_cnt >= SCSI_STD_INQUIRY_BYTES
+ ) {
+ mptscsih_initTarget(hd,
+ hd->port,
+ sc->target,
+ pScsiReq->LUN[1],
+ sc->buffer,
+ xfer_cnt);
+ }
+
+ if (hd->is_spi)
+ mptscsih_set_dvflags(hd, pScsiReq, sc->buffer);
+ break;
+
+ case MPI_IOCSTATUS_SCSI_PROTOCOL_ERROR: /* 0x0047 */
+ if (pScsiReply->SCSIState & MPI_SCSI_STATE_TERMINATED) {
+ /* Not real sure here either... */
+ sc->result = DID_RESET << 16;
+ } else
+ sc->result = DID_SOFT_ERROR << 16;
break;
case MPI_IOCSTATUS_INVALID_FUNCTION: /* 0x0001 */
case MPI_IOCSTATUS_INVALID_STATE: /* 0x0008 */
case MPI_IOCSTATUS_SCSI_DATA_OVERRUN: /* 0x0044 */
case MPI_IOCSTATUS_SCSI_IO_DATA_ERROR: /* 0x0046 */
- case MPI_IOCSTATUS_SCSI_PROTOCOL_ERROR: /* 0x0047 */
- case MPI_IOCSTATUS_SCSI_RESIDUAL_MISMATCH: /* 0x0049 */
case MPI_IOCSTATUS_SCSI_TASK_MGMT_FAILED: /* 0x004A */
default:
/*
- * What to do?
+ * What to do?
*/
sc->result = DID_SOFT_ERROR << 16;
break;
} /* switch(status) */
- dprintk((KERN_NOTICE MYNAM ": sc->result set to %08xh\n", sc->result));
+ dprintk((KERN_NOTICE " sc->result set to %08xh\n", sc->result));
+ } /* end of address reply case */
+
+ /* Unmap the DMA buffers, if any. */
+ if (sc->use_sg) {
+ pci_unmap_sg(ioc->pcidev, (struct scatterlist *) sc->request_buffer,
+ sc->use_sg, scsi_to_pci_dma_dir(sc->sc_data_direction));
+ } else if (sc->request_bufflen) {
+ scPrivate *my_priv;
+
+ my_priv = (scPrivate *) &sc->SCp;
+ pci_unmap_single(ioc->pcidev, (dma_addr_t)(ulong)my_priv->p1,
+ sc->request_bufflen,
+ scsi_to_pci_dma_dir(sc->sc_data_direction));
}
- if (sc != NULL) {
- unsigned long flags;
+ hd->ScsiLookup[req_idx] = NULL;
- /* Unmap the DMA buffers, if any. */
- if (sc->use_sg) {
- pci_unmap_sg(ioc->pcidev,
- (struct scatterlist *) sc->request_buffer,
- sc->use_sg,
- scsi_to_pci_dma_dir(sc->sc_data_direction));
- } else if (sc->request_bufflen) {
- pci_unmap_single(ioc->pcidev,
- (dma_addr_t)((long)sc->SCp.ptr),
- sc->request_bufflen,
- scsi_to_pci_dma_dir(sc->sc_data_direction));
- }
+ sc->host_scribble = NULL; /* CHECKME! - Do we need to clear this??? */
- spin_lock_irqsave(sc->host->host_lock, flags);
- sc->scsi_done(sc);
- spin_unlock_irqrestore(sc->host->host_lock, flags);
- }
+ MPT_HOST_LOCK(flags);
+ sc->scsi_done(sc); /* Issue the command callback */
+ MPT_HOST_UNLOCK(flags);
+ /* Free Chain buffers */
+ mptscsih_freeChainBuffers(hd, req_idx);
return 1;
}
-#ifndef MPT_SCSI_USE_NEW_EH
+#ifndef MPT_SCSI_USE_NEW_EH /* { */
/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
/*
* search_taskQ - Search SCSI task mgmt request queue for specific
- * request type
+ * request type.
* @remove: (Boolean) Should request be removed if found?
* @sc: Pointer to Scsi_Cmnd structure
* @task_type: Task type to search for
* was not found.
*/
static MPT_FRAME_HDR *
-search_taskQ(int remove, Scsi_Cmnd *sc, u8 task_type)
+search_taskQ(int remove, Scsi_Cmnd *sc, MPT_SCSI_HOST *hd, u8 task_type)
{
MPT_FRAME_HDR *mf = NULL;
unsigned long flags;
int count = 0;
int list_sz;
- dslprintk((KERN_INFO MYNAM ": spinlock#1\n"));
- spin_lock_irqsave(&mpt_scsih_taskQ_lock, flags);
- list_sz = mpt_scsih_taskQ_cnt;
- if (! Q_IS_EMPTY(&mpt_scsih_taskQ)) {
- mf = mpt_scsih_taskQ.head;
+ dprintk((KERN_INFO MYNAM ": search_taskQ(%d,sc=%p,%d) called\n",
+ remove, sc, task_type));
+ spin_lock_irqsave(&hd->ioc->FreeQlock, flags);
+ list_sz = hd->taskQcnt;
+ if (! Q_IS_EMPTY(&hd->taskQ)) {
+ mf = hd->taskQ.head;
do {
count++;
if (mf->u.frame.linkage.argp1 == sc &&
mf->u.frame.linkage.arg1 == task_type) {
if (remove) {
Q_DEL_ITEM(&mf->u.frame.linkage);
- mpt_scsih_taskQ_cnt--;
+ hd->taskQcnt--;
+ atomic_dec(&mpt_taskQdepth);
+
+ /* Don't save mf into nextmf because
+ * exit after command has been deleted.
+ */
+
+ /* Place the MF back on the FreeQ */
+ Q_ADD_TAIL(&hd->ioc->FreeQ,
+ &mf->u.frame.linkage,
+ MPT_FRAME_HDR);
+#ifdef MFCNT
+ hd->ioc->mfcnt--;
+#endif
}
break;
}
- } while ((mf = mf->u.frame.linkage.forw) != (MPT_FRAME_HDR*)&mpt_scsih_taskQ);
- if (mf == (MPT_FRAME_HDR*)&mpt_scsih_taskQ) {
+ } while ((mf = mf->u.frame.linkage.forw) != (MPT_FRAME_HDR*)&hd->taskQ);
+ if (mf == (MPT_FRAME_HDR*)&hd->taskQ) {
mf = NULL;
}
}
- spin_unlock_irqrestore(&mpt_scsih_taskQ_lock, flags);
+ spin_unlock_irqrestore(&hd->ioc->FreeQlock, flags);
if (list_sz) {
- dprintk((KERN_INFO MYNAM ": search_taskQ(%d,%p,%d) results=%p (%sFOUND%s)!\n",
- remove, sc, task_type,
+ dprintk((KERN_INFO " Results=%p (%sFOUND%s)!\n",
mf,
mf ? "" : "NOT_",
(mf && remove) ? "+REMOVED" : "" ));
- dprintk((KERN_INFO MYNAM ": (searched thru %d of %d items on taskQ)\n",
+ dprintk((KERN_INFO " (searched thru %d of %d items on taskQ)\n",
count,
list_sz ));
}
return mf;
}
-#endif
-
-/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
-
-/*
- * Hack! I'd like to report if a device is returning QUEUE_FULL
- * but maybe not each and every time...
- */
-static long last_queue_full = 0;
-
-/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
/*
- * mptscsih_report_queue_full - Report QUEUE_FULL status returned
- * from a SCSI target device.
- * @sc: Pointer to Scsi_Cmnd structure
- * @pScsiReply: Pointer to SCSIIOReply_t
- * @pScsiReq: Pointer to original SCSI request
+ * clean_taskQ - Clean the SCSI task mgmt request for
+ * this SCSI host instance.
+ * @hd: MPT_SCSI_HOST pointer
*
- * This routine periodically reports QUEUE_FULL status returned from a
- * SCSI target device. It reports this to the console via kernel
- * printk() API call, not more than once every 10 seconds.
+ * Returns: None.
*/
static void
-mptscsih_report_queue_full(Scsi_Cmnd *sc, SCSIIOReply_t *pScsiReply, SCSIIORequest_t *pScsiReq)
+clean_taskQ(MPT_SCSI_HOST *hd)
{
- long time = jiffies;
+ MPT_FRAME_HDR *mf = NULL;
+ MPT_FRAME_HDR *nextmf = NULL;
+ MPT_ADAPTER *ioc = hd->ioc;
+ unsigned long flags;
- if (time - last_queue_full > 10 * HZ) {
- printk(KERN_WARNING MYNAM ": Device reported QUEUE_FULL! SCSI bus:target:lun = %d:%d:%d\n",
- 0, sc->target, sc->lun);
- last_queue_full = time;
+ dprintk((KERN_INFO MYNAM ": clean_taskQ called\n"));
+
+ spin_lock_irqsave(&ioc->FreeQlock, flags);
+ if (! Q_IS_EMPTY(&hd->taskQ)) {
+ mf = hd->taskQ.head;
+ do {
+ Q_DEL_ITEM(&mf->u.frame.linkage);
+ hd->taskQcnt--;
+ atomic_dec(&mpt_taskQdepth);
+
+ nextmf = mf->u.frame.linkage.forw;
+
+ /* Place the MF back on the FreeQ */
+ Q_ADD_TAIL(&ioc->FreeQ, &mf->u.frame.linkage,
+ MPT_FRAME_HDR);
+#ifdef MFCNT
+ hd->ioc->mfcnt--;
+#endif
+ } while ((mf = nextmf) != (MPT_FRAME_HDR*)&hd->taskQ);
}
-}
+ spin_unlock_irqrestore(&ioc->FreeQlock, flags);
-/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
-static int BeenHereDoneThat = 0;
+ return;
+}
-/* SCSI fops start here... */
-/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
-/**
- * mptscsih_detect - Register MPT adapter(s) as SCSI host(s) with
- * linux scsi mid-layer.
- * @tpnt: Pointer to Scsi_Host_Template structure
- *
- * (linux Scsi_Host_Template.detect routine)
+/*
+ * search_taskQ_for_cmd - Search the SCSI task mgmt request queue for
+ * the specified command. If found, delete
+ * @hd: MPT_SCSI_HOST pointer
*
- * Returns number of SCSI host adapters that were successfully
- * registered with the linux scsi mid-layer via the scsi_register()
- * API call.
+ * Returns: None.
*/
-int
-mptscsih_detect(Scsi_Host_Template *tpnt)
+static void
+search_taskQ_for_cmd(Scsi_Cmnd *sc, MPT_SCSI_HOST *hd)
{
- struct Scsi_Host *sh = NULL;
- MPT_SCSI_HOST *hd = NULL;
- MPT_ADAPTER *this;
- unsigned long flags;
- int sz;
- u8 *mem;
-
- if (! BeenHereDoneThat++) {
- show_mptmod_ver(my_NAME, my_VERSION);
+ MPT_FRAME_HDR *mf = NULL;
+ unsigned long flags;
+ int count = 0;
- if ((ScsiDoneCtx = mpt_register(mptscsih_io_done, MPTSCSIH_DRIVER)) <= 0) {
- printk(KERN_ERR MYNAM ": Failed to register callback1 with MPT base driver\n");
- return mpt_scsi_hosts;
- }
- if ((ScsiTaskCtx = mpt_register(mptscsih_taskmgmt_complete, MPTSCSIH_DRIVER)) <= 0) {
- printk(KERN_ERR MYNAM ": Failed to register callback2 with MPT base driver\n");
- return mpt_scsi_hosts;
- }
+ dprintk((KERN_INFO MYNAM ": search_taskQ_for_cmd(sc=%p) called\n", sc));
+ spin_lock_irqsave(&hd->ioc->FreeQlock, flags);
+ if (! Q_IS_EMPTY(&hd->taskQ)) {
+ mf = hd->taskQ.head;
+ do {
+ count++;
+ if (mf->u.frame.linkage.argp1 == sc) {
+ Q_DEL_ITEM(&mf->u.frame.linkage);
+ hd->taskQcnt--;
+ atomic_dec(&mpt_taskQdepth);
+ dprintk((KERN_INFO MYNAM
+ ": Cmd %p found! Deleting.\n", sc));
+
+ /* Don't save mf into nextmf because
+ * exit after command has been deleted.
+ */
-#ifndef MPT_SCSI_USE_NEW_EH
- Q_INIT(&mpt_scsih_taskQ, MPT_FRAME_HDR);
- spin_lock_init(&mpt_scsih_taskQ_lock);
+ /* Place the MF back on the FreeQ */
+ Q_ADD_TAIL(&hd->ioc->FreeQ,
+ &mf->u.frame.linkage,
+ MPT_FRAME_HDR);
+#ifdef MFCNT
+ hd->ioc->mfcnt--;
#endif
+ break;
+ }
+ } while ((mf = mf->u.frame.linkage.forw) != (MPT_FRAME_HDR*)&hd->taskQ);
+ }
+ spin_unlock_irqrestore(&hd->ioc->FreeQlock, flags);
- if (mpt_event_register(ScsiDoneCtx, mptscsih_event_process) == 0) {
- dprintk((KERN_INFO MYNAM ": Registered for IOC event notifications\n"));
- } else {
- /* FIXME! */
- }
+ return;
+}
- if (mpt_reset_register(ScsiDoneCtx, mptscsih_ioc_reset) == 0) {
- dprintk((KERN_INFO MYNAM ": Registered for IOC reset notifications\n"));
- } else {
- /* FIXME! */
- }
- }
+#endif /* } MPT_SCSI_USE_NEW_EH */
- dprintk((KERN_INFO MYNAM ": mpt_scsih_detect()\n"));
- this = mpt_adapter_find_first();
- while (this != NULL) {
- /* FIXME! Multi-port (aka FC929) support...
- * for (i = 0; i < this->facts.NumberOfPorts; i++)
- */
+/*
+ * Flush all commands on the doneQ.
+ * Lock Q when deleting/adding members
+ * Lock io_request_lock for OS callback.
+ */
+static void
+flush_doneQ(MPT_SCSI_HOST *hd)
+{
+ MPT_DONE_Q *buffer;
+ Scsi_Cmnd *SCpnt;
+ unsigned long flags;
- /* 20010215 -sralston
- * Added sanity check on SCSI Initiator-mode enabled
- * for this MPT adapter.
- */
- if (!(this->pfacts[0].ProtocolFlags & MPI_PORTFACTS_PROTOCOL_INITIATOR)) {
- printk(KERN_ERR MYNAM ": Skipping %s because SCSI Initiator mode is NOT enabled!\n",
- this->name);
- this = mpt_adapter_find_next(this);
- continue;
+ /* Flush the doneQ.
+ */
+ dprintk((KERN_INFO MYNAM ": flush_doneQ called\n"));
+ while (1) {
+ spin_lock_irqsave(&hd->freedoneQlock, flags);
+ if (Q_IS_EMPTY(&hd->doneQ)) {
+ spin_unlock_irqrestore(&hd->freedoneQlock, flags);
+ break;
}
- /* 20010202 -sralston
- * Added sanity check on readiness of the MPT adapter.
+ buffer = hd->doneQ.head;
+ /* Delete from Q
*/
- if (this->last_state != MPI_IOC_STATE_OPERATIONAL) {
- printk(KERN_ERR MYNAM ": ERROR - Skipping %s because it's not operational!\n",
- this->name);
- this = mpt_adapter_find_next(this);
- continue;
- }
-
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,3,0)
- tpnt->proc_dir = &proc_mpt_scsihost;
-#endif
- sh = scsi_register(tpnt, sizeof(MPT_SCSI_HOST));
- if (sh != NULL) {
- save_flags(flags);
- cli();
- sh->io_port = 0;
- sh->n_io_port = 0;
- sh->irq = 0;
-
- /* Yikes! This is important!
- * Otherwise, by default, linux only scans target IDs 0-7!
- *
- * BUG FIX! 20010618 -sralston & pdelaney
- * FC919 testing was encountering "duplicate" FC devices,
- * as it turns out because the 919 was returning 512
- * for PortFacts.MaxDevices, causing a wraparound effect
- * in SCSI IO requests. So instead of using:
- * sh->max_id = this->pfacts[0].MaxDevices - 1
- * we'll use a definitive max here.
- */
- sh->max_id = MPT_MAX_FC_DEVICES;
-
- sh->this_id = this->pfacts[0].PortSCSIID;
+ Q_DEL_ITEM(buffer);
- restore_flags(flags);
+ /* Set the Scsi_Cmnd pointer
+ */
+ SCpnt = (Scsi_Cmnd *) buffer->argp;
+ buffer->argp = NULL;
- hd = (MPT_SCSI_HOST *) sh->hostdata;
- hd->ioc = this;
- hd->port = 0; /* FIXME! */
+ /* Add to the freeQ
+ */
+ Q_ADD_TAIL(&hd->freeQ.head, buffer, MPT_DONE_Q);
+ spin_unlock_irqrestore(&hd->freedoneQlock, flags);
- /* SCSI needs Scsi_Cmnd lookup table!
- * (with size equal to req_depth*PtrSz!)
- */
- sz = hd->ioc->req_depth * sizeof(void *);
- mem = kmalloc(sz, GFP_KERNEL);
- if (mem == NULL)
- return mpt_scsi_hosts;
+ /* Do the OS callback.
+ */
+ MPT_HOST_LOCK(flags);
+ SCpnt->scsi_done(SCpnt);
+ MPT_HOST_UNLOCK(flags);
+ }
- memset(mem, 0, sz);
- hd->ScsiLookup = (struct scsi_cmnd **) mem;
+ return;
+}
- dprintk((KERN_INFO MYNAM ": ScsiLookup @ %p, sz=%d\n",
- hd->ScsiLookup, sz));
-
- /* SCSI also needs SG buckets/hunk management!
- * (with size equal to N * req_sz * req_depth!)
- * (where N is number of SG buckets per hunk)
- */
- sz = MPT_SG_BUCKETS_PER_HUNK * hd->ioc->req_sz * hd->ioc->req_depth;
- mem = pci_alloc_consistent(hd->ioc->pcidev, sz,
- &hd->SgHunksDMA);
- if (mem == NULL)
- return mpt_scsi_hosts;
-
- memset(mem, 0, sz);
- hd->SgHunks = (u8*)mem;
+/*
+ * Search the doneQ for a specific command. If found, delete from Q.
+ * Calling function will finish processing.
+ */
+static void
+search_doneQ_for_cmd(MPT_SCSI_HOST *hd, Scsi_Cmnd *SCpnt)
+{
+ unsigned long flags;
+ MPT_DONE_Q *buffer;
- dprintk((KERN_INFO MYNAM ": SgHunks @ %p(%08x), sz=%d\n",
- hd->SgHunks, hd->SgHunksDMA, sz));
+ spin_lock_irqsave(&hd->freedoneQlock, flags);
+ if (!Q_IS_EMPTY(&hd->doneQ)) {
+ buffer = hd->doneQ.head;
+ do {
+ Scsi_Cmnd *sc = (Scsi_Cmnd *) buffer->argp;
+ if (SCpnt == sc) {
+ Q_DEL_ITEM(buffer);
+ SCpnt->result = sc->result;
- hd->qtag_tick = jiffies;
+ /* Set the Scsi_Cmnd pointer
+ */
+ buffer->argp = NULL;
- this->sh = sh;
- mpt_scsi_hosts++;
- }
- this = mpt_adapter_find_next(this);
+ /* Add to the freeQ
+ */
+ Q_ADD_TAIL(&hd->freeQ.head, buffer, MPT_DONE_Q);
+ break;
+ }
+ } while ((buffer = buffer->forw) != (MPT_DONE_Q *) &hd->doneQ);
}
-
- return mpt_scsi_hosts;
+ spin_unlock_irqrestore(&hd->freedoneQlock, flags);
+ return;
}
-/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
- static char *info_kbuf = NULL;
-/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
-/**
- * mptscsih_release - Unregister SCSI host from linux scsi mid-layer
- * @host: Pointer to Scsi_Host structure
+/*
+ * mptscsih_flush_running_cmds - For each command found, search
+ * Scsi_Host instance taskQ and reply to OS.
+ * Called only if recovering from a FW reload.
+ * @hd: Pointer to a SCSI HOST structure
*
- * (linux Scsi_Host_Template.release routine)
- * This routine releases all resources associated with the SCSI host
- * adapter.
+ * Returns: None.
*
- * Returns 0 for success.
+ * Must be called while new I/Os are being queued.
*/
-int
-mptscsih_release(struct Scsi_Host *host)
+static void
+mptscsih_flush_running_cmds(MPT_SCSI_HOST *hd)
{
- MPT_SCSI_HOST *hd;
+ Scsi_Cmnd *SCpnt = NULL;
+ MPT_FRAME_HDR *mf = NULL;
+ int ii;
+ int max = hd->ioc->req_depth;
+
#ifndef MPT_SCSI_USE_NEW_EH
unsigned long flags;
+#endif
- spin_lock_irqsave(&mpt_scsih_taskQ_lock, flags);
- if (mpt_scsih_taskQ_bh_active) {
- int count = 10 * HZ;
+ dprintk((KERN_INFO MYNAM ": flush_ScsiLookup called\n"));
+ for (ii= 0; ii < max; ii++) {
+ if ((SCpnt = hd->ScsiLookup[ii]) != NULL) {
- dprintk((KERN_INFO MYNAM ": Info: Zapping TaskMgmt thread!\n"));
+ /* Command found.
+ */
+
+#ifndef MPT_SCSI_USE_NEW_EH
+ /* Search taskQ, if found, delete.
+ */
+ search_taskQ_for_cmd(SCpnt, hd);
+#endif
- /* Zap the taskQ! */
- Q_INIT(&mpt_scsih_taskQ, MPT_FRAME_HDR);
- spin_unlock_irqrestore(&mpt_scsih_taskQ_lock, flags);
+ /* Search pendingQ, if found,
+ * delete from Q. If found, do not decrement
+ * queue_depth, command never posted.
+ */
+ if (mptscsih_search_pendingQ(hd, ii) == NULL)
+ atomic_dec(&queue_depth);
- while(mpt_scsih_taskQ_bh_active && --count) {
- current->state = TASK_INTERRUPTIBLE;
- schedule_timeout(1);
+ /* Null ScsiLookup index
+ */
+ hd->ScsiLookup[ii] = NULL;
+
+ mf = MPT_INDEX_2_MFPTR(hd->ioc, ii);
+ dmfprintk(( "flush: ScsiDone (mf=%p,sc=%p)\n",
+ mf, SCpnt));
+
+ /* Set status
+ * Do OS callback
+ * Free chain buffers
+ * Free message frame
+ */
+ SCpnt->result = DID_RESET << 16;
+ SCpnt->host_scribble = NULL;
+ MPT_HOST_LOCK(flags);
+ SCpnt->scsi_done(SCpnt); /* Issue the command callback */
+ MPT_HOST_UNLOCK(flags);
+
+ /* Free Chain buffers */
+ mptscsih_freeChainBuffers(hd, ii);
+
+ /* Free Message frames */
+ mpt_free_msg_frame(ScsiDoneCtx, hd->ioc->id, mf);
}
- if (!count)
- printk(KERN_ERR MYNAM ": ERROR! TaskMgmt thread still active!\n");
}
- spin_unlock_irqrestore(&mpt_scsih_taskQ_lock, flags);
-#endif
+ return;
+}
- hd = (MPT_SCSI_HOST *) host->hostdata;
- if (hd != NULL) {
- int sz1, sz2;
+/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
+/*
+ * mptscsih_initChainBuffers - Allocate memory for and initialize
+ * chain buffers, chain buffer control arrays and spinlock.
+ * @hd: Pointer to MPT_SCSI_HOST structure
+ * @init: If set, initialize the spin lock.
+ */
+static int
+mptscsih_initChainBuffers (MPT_SCSI_HOST *hd, int init)
+{
+ MPT_FRAME_HDR *chain;
+ u8 *mem;
+ unsigned long flags;
+ int sz, ii, numChain;
- sz1 = sz2 = 0;
- if (hd->ScsiLookup != NULL) {
- sz1 = hd->ioc->req_depth * sizeof(void *);
- kfree(hd->ScsiLookup);
- hd->ScsiLookup = NULL;
- }
- if (hd->SgHunks != NULL) {
+ /* Chain buffer allocations
+ * Allocate and initialize tracker structures
+ */
+ if (hd->ioc->req_sz <= 64)
+ numChain = MPT_SG_REQ_64_SCALE * hd->ioc->req_depth;
+ else if (hd->ioc->req_sz <= 96)
+ numChain = MPT_SG_REQ_96_SCALE * hd->ioc->req_depth;
+ else
+ numChain = MPT_SG_REQ_128_SCALE * hd->ioc->req_depth;
- sz2 = MPT_SG_BUCKETS_PER_HUNK * hd->ioc->req_sz * hd->ioc->req_depth;
- pci_free_consistent(hd->ioc->pcidev, sz2,
- hd->SgHunks, hd->SgHunksDMA);
- hd->SgHunks = NULL;
- }
- dprintk((KERN_INFO MYNAM ": Free'd ScsiLookup (%d) and SgHunks (%d) memory\n", sz1, sz2));
+ sz = numChain * sizeof(int);
+
+ if (hd->ReqToChain == NULL) {
+ mem = kmalloc(sz, GFP_ATOMIC);
+ if (mem == NULL)
+ return -1;
+
+ hd->ReqToChain = (int *) mem;
+ } else {
+ mem = (u8 *) hd->ReqToChain;
}
+ memset(mem, 0xFF, sz);
- if (mpt_scsi_hosts) {
- if (--mpt_scsi_hosts == 0) {
-#if 0
- mptscsih_flush_pending();
-#endif
- mpt_reset_deregister(ScsiDoneCtx);
- dprintk((KERN_INFO MYNAM ": Deregistered for IOC reset notifications\n"));
+ if (hd->ChainToChain == NULL) {
+ mem = kmalloc(sz, GFP_ATOMIC);
+ if (mem == NULL)
+ return -1;
- mpt_event_deregister(ScsiDoneCtx);
- dprintk((KERN_INFO MYNAM ": Deregistered for IOC event notifications\n"));
+ hd->ChainToChain = (int *) mem;
+ } else {
+ mem = (u8 *) hd->ChainToChain;
+ }
+ memset(mem, 0xFF, sz);
- mpt_deregister(ScsiDoneCtx);
- mpt_deregister(ScsiTaskCtx);
+ if (hd->ChainBuffer == NULL) {
+ /* Allocate free chain buffer pool
+ */
+ sz = numChain * hd->ioc->req_sz;
+ mem = pci_alloc_consistent(hd->ioc->pcidev, sz, &hd->ChainBufferDMA);
+ if (mem == NULL)
+ return -1;
- if (info_kbuf != NULL)
- kfree(info_kbuf);
- }
+ hd->ChainBuffer = (u8*)mem;
+ } else {
+ mem = (u8 *) hd->ChainBuffer;
+ }
+ memset(mem, 0, sz);
+
+ dprintk((KERN_INFO " ChainBuffer @ %p(%p), sz=%d\n",
+ hd->ChainBuffer, (void *)(ulong)hd->ChainBufferDMA, sz));
+
+ /* Initialize the free chain Q.
+ */
+ if (init) {
+ spin_lock_init(&hd->FreeChainQlock);
+ }
+
+ spin_lock_irqsave (&hd->FreeChainQlock, flags);
+ Q_INIT(&hd->FreeChainQ, MPT_FRAME_HDR);
+
+ /* Post the chain buffers to the FreeChainQ.
+ */
+ mem = (u8 *)hd->ChainBuffer;
+ for (ii=0; ii < numChain; ii++) {
+ chain = (MPT_FRAME_HDR *) mem;
+ Q_ADD_TAIL(&hd->FreeChainQ.head, &chain->u.frame.linkage, MPT_FRAME_HDR);
+ mem += hd->ioc->req_sz;
}
+ spin_unlock_irqrestore(&hd->FreeChainQlock, flags);
return 0;
}
/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
-/**
- * mptscsih_info - Return information about MPT adapter
- * @SChost: Pointer to Scsi_Host structure
- *
- * (linux Scsi_Host_Template.info routine)
+/*
+ * Hack! It might be nice to report if a device is returning QUEUE_FULL
+ * but maybe not each and every time...
+ */
+static long last_queue_full = 0;
+
+/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
+/*
+ * mptscsih_report_queue_full - Report QUEUE_FULL status returned
+ * from a SCSI target device.
+ * @sc: Pointer to Scsi_Cmnd structure
+ * @pScsiReply: Pointer to SCSIIOReply_t
+ * @pScsiReq: Pointer to original SCSI request
*
- * Returns pointer to buffer where information was written.
+ * This routine periodically reports QUEUE_FULL status returned from a
+ * SCSI target device. It reports this to the console via kernel
+ * printk() API call, not more than once every 10 seconds.
*/
-const char *
-mptscsih_info(struct Scsi_Host *SChost)
+static void
+mptscsih_report_queue_full(Scsi_Cmnd *sc, SCSIIOReply_t *pScsiReply, SCSIIORequest_t *pScsiReq)
{
- MPT_SCSI_HOST *h;
- int size = 0;
-
- if (info_kbuf == NULL)
- if ((info_kbuf = kmalloc(0x1000 /* 4Kb */, GFP_KERNEL)) == NULL)
- return info_kbuf;
+ long time = jiffies;
- h = (MPT_SCSI_HOST *)SChost->hostdata;
- info_kbuf[0] = '\0';
- mpt_print_ioc_summary(h->ioc, info_kbuf, &size, 0, 0);
- info_kbuf[size-1] = '\0';
+ if (time - last_queue_full > 10 * HZ) {
+ char *ioc_str = "ioc?";
- return info_kbuf;
+ if (sc->host != NULL && sc->host->hostdata != NULL)
+ ioc_str = ((MPT_SCSI_HOST *)sc->host->hostdata)->ioc->name;
+ printk(MYIOC_s_WARN_FMT "Device (%d:%d:%d) reported QUEUE_FULL!\n",
+ ioc_str, 0, sc->target, sc->lun);
+ last_queue_full = time;
+ }
}
/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
- static int max_qd = 1;
-#ifdef MPT_DEBUG
- static int max_sges = 0;
- static int max_xfer = 0;
-#endif
-#if 0
- static int max_num_sges = 0;
- static int max_sgent_len = 0;
-#endif
-#if 0
-static int index_log[128];
-static int index_ent = 0;
-static __inline__ void ADD_INDEX_LOG(int req_ent)
-{
- int i = index_ent++;
+static int BeenHereDoneThat = 0;
- index_log[i & (128 - 1)] = req_ent;
-}
-#else
-#define ADD_INDEX_LOG(req_ent) do { } while(0)
-#endif
+/* SCSI host fops start here... */
/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
/**
- * mptscsih_qcmd - Primary Fusion MPT SCSI initiator IO start routine.
- * @SCpnt: Pointer to Scsi_Cmnd structure
- * @done: Pointer SCSI mid-layer IO completion function
+ * mptscsih_detect - Register MPT adapter(s) as SCSI host(s) with
+ * linux scsi mid-layer.
+ * @tpnt: Pointer to Scsi_Host_Template structure
*
- * (linux Scsi_Host_Template.queuecommand routine)
- * This is the primary SCSI IO start routine. Create a MPI SCSIIORequest
- * from a linux Scsi_Cmnd request and send it to the IOC.
+ * (linux Scsi_Host_Template.detect routine)
*
- * Returns 0. (rtn value discarded by linux scsi mid-layer)
+ * Returns number of SCSI host adapters that were successfully
+ * registered with the linux scsi mid-layer via the scsi_register()
+ * API call.
*/
int
-mptscsih_qcmd(Scsi_Cmnd *SCpnt, void (*done)(Scsi_Cmnd *))
+mptscsih_detect(Scsi_Host_Template *tpnt)
{
- struct Scsi_Host *host;
- MPT_SCSI_HOST *hd;
- MPT_FRAME_HDR *mf;
- SCSIIORequest_t *pScsiReq;
- int datadir;
- u32 len;
- u32 sgdir;
- u32 scsictl;
- u32 scsidir;
- u32 qtag;
- u32 *mptr;
- int sge_spill1;
- int frm_sz;
- int sges_left;
- u32 chain_offset;
- int my_idx;
- int i;
+ struct Scsi_Host *sh = NULL;
+ MPT_SCSI_HOST *hd = NULL;
+ MPT_ADAPTER *this;
+ MPT_DONE_Q *freedoneQ;
+ unsigned long flags;
+ int sz, ii;
+ int numSGE = 0;
+ int scale;
+ u8 *mem;
- dmfprintk((KERN_INFO MYNAM "_qcmd: SCpnt=%p, done()=%p\n",
- SCpnt, done));
+ if (! BeenHereDoneThat++) {
+ show_mptmod_ver(my_NAME, my_VERSION);
- host = SCpnt->host;
- hd = (MPT_SCSI_HOST *) host->hostdata;
-
-#if 0
- if (host->host_busy >= 60) {
- MPT_ADAPTER *ioc = hd->ioc;
- u16 pci_command, pci_status;
-
- /* The IOC is probably hung, investigate status. */
- printk("MPI: IOC probably hung IOCSTAT[%08x] INTSTAT[%08x] REPLYFIFO[%08x]\n",
- readl(&ioc->chip.fc9xx->DoorbellValue),
- readl(&ioc->chip.fc9xx->IntStatus),
- readl(&ioc->chip.fc9xx->ReplyFifo));
- pci_read_config_word(ioc->pcidev, PCI_COMMAND, &pci_command);
- pci_read_config_word(ioc->pcidev, PCI_STATUS, &pci_status);
- printk("MPI: PCI command[%04x] status[%04x]\n", pci_command, pci_status);
- {
- /* DUMP req index logger. */
- int begin, end;
+ ScsiDoneCtx = mpt_register(mptscsih_io_done, MPTSCSIH_DRIVER);
+ ScsiTaskCtx = mpt_register(mptscsih_taskmgmt_complete, MPTSCSIH_DRIVER);
+ ScsiScanDvCtx = mpt_register(mptscsih_scandv_complete, MPTSCSIH_DRIVER);
- begin = (index_ent - 65) & (128 - 1);
- end = index_ent & (128 - 1);
- printk("MPI: REQ_INDEX_HIST[");
- while (begin != end) {
- printk("(%04x)", index_log[begin]);
- begin = (begin + 1) & (128 - 1);
- }
- printk("\n");
+#ifndef MPT_SCSI_USE_NEW_EH
+ spin_lock_init(&mytaskQ_lock);
+#endif
+
+ if (mpt_event_register(ScsiDoneCtx, mptscsih_event_process) == 0) {
+ dprintk((KERN_INFO MYNAM ": Registered for IOC event notifications\n"));
+ } else {
+ /* FIXME! */
+ }
+
+ if (mpt_reset_register(ScsiDoneCtx, mptscsih_ioc_reset) == 0) {
+ dprintk((KERN_INFO MYNAM ": Registered for IOC reset notifications\n"));
+ } else {
+ /* FIXME! */
}
- sti();
- while(1)
- barrier();
}
-#endif
+ dprintk((KERN_INFO MYNAM ": mpt_scsih_detect()\n"));
- SCpnt->scsi_done = done;
+#ifdef MODULE
+ /* Evaluate the command line arguments, if any */
+ if (mptscsih)
+ mptscsih_setup(mptscsih);
+#endif
+#ifndef MPT_SCSI_USE_NEW_EH
+ atomic_set(&mpt_taskQdepth, 0);
+#endif
- /* 20000617 -sralston
- * GRRRRR... Shouldn't have to do this but...
- * Do explicit check for REQUEST_SENSE and cached SenseData.
- * If yes, return cached SenseData.
- */
-#ifdef MPT_SCSI_CACHE_AUTOSENSE
- {
- MPT_SCSI_DEV *mpt_sdev;
+ this = mpt_adapter_find_first();
+ while (this != NULL) {
+ int portnum;
+ for (portnum=0; portnum < this->facts.NumberOfPorts; portnum++) {
- mpt_sdev = (MPT_SCSI_DEV *) SCpnt->device->hostdata;
- if (mpt_sdev && SCpnt->cmnd[0] == REQUEST_SENSE) {
- u8 *dest = NULL;
+ /* 20010215 -sralston
+ * Added sanity check on SCSI Initiator-mode enabled
+ * for this MPT adapter.
+ */
+ if (!(this->pfacts[portnum].ProtocolFlags & MPI_PORTFACTS_PROTOCOL_INITIATOR)) {
+ printk(MYIOC_s_WARN_FMT "Skipping because SCSI Initiator mode is NOT enabled!\n",
+ this->name);
+ continue;
+ }
- if (!SCpnt->use_sg)
- dest = SCpnt->request_buffer;
- else {
- struct scatterlist *sg = (struct scatterlist *) SCpnt->request_buffer;
- if (sg)
- dest = (u8 *) (unsigned long)sg_dma_address(sg);
- }
-
- if (dest && mpt_sdev->sense_sz) {
- memcpy(dest, mpt_sdev->CachedSense.data, mpt_sdev->sense_sz);
-#ifdef MPT_DEBUG
- {
- int i;
- u8 *sb;
-
- sb = mpt_sdev->CachedSense.data;
- if (sb && ((sb[0] & 0x70) == 0x70)) {
- printk(KERN_WARNING MYNAM ": Returning last cached SCSI (hex) SenseData:\n");
- printk(KERN_WARNING " ");
- for (i = 0; i < (8 + sb[7]); i++)
- printk("%s%02x", i == 13 ? "-" : " ", sb[i]);
- printk("\n");
- }
- }
-#endif
+ /* 20010202 -sralston
+ * Added sanity check on readiness of the MPT adapter.
+ */
+ if (this->last_state != MPI_IOC_STATE_OPERATIONAL) {
+ printk(MYIOC_s_WARN_FMT "Skipping because it's not operational!\n",
+ this->name);
+ continue;
}
- SCpnt->resid = SCpnt->request_bufflen - mpt_sdev->sense_sz;
- SCpnt->result = 0;
-/* spin_lock(SCpnt->host->host_lock); */
- SCpnt->scsi_done(SCpnt);
-/* spin_unlock(SCpnt->host->host_lock); */
- return 0;
- }
- }
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,3,0)
+ tpnt->proc_dir = &proc_mpt_scsihost;
#endif
+ sh = scsi_register(tpnt, sizeof(MPT_SCSI_HOST));
+ if (sh != NULL) {
+ save_flags(flags);
+ cli();
+ sh->io_port = 0;
+ sh->n_io_port = 0;
+ sh->irq = 0;
+
+ /* Yikes! This is important!
+ * Otherwise, by default, linux
+ * only scans target IDs 0-7!
+ * pfactsN->MaxDevices unreliable
+ * (not supported in early
+ * versions of the FW).
+ * max_id = 1 + actual max id,
+ * max_lun = 1 + actual last lun,
+ * see hosts.h :o(
+ */
+ if ((int)this->chip_type > (int)FC929)
+ sh->max_id = MPT_MAX_SCSI_DEVICES;
+ else {
+ /* For FC, increase the queue depth
+ * from MPT_SCSI_CAN_QUEUE (31)
+ * to MPT_FC_CAN_QUEUE (63).
+ */
+ sh->can_queue = MPT_FC_CAN_QUEUE;
+ sh->max_id = MPT_MAX_FC_DEVICES<256 ? MPT_MAX_FC_DEVICES : 255;
+ }
+ sh->max_lun = MPT_LAST_LUN + 1;
- if ((mf = mpt_get_msg_frame(ScsiDoneCtx, hd->ioc->id)) == NULL) {
-/* SCpnt->result = DID_SOFT_ERROR << 16; */
- SCpnt->result = STS_BUSY;
- SCpnt->scsi_done(SCpnt);
-/* return 1; */
- return 0;
- }
- pScsiReq = (SCSIIORequest_t *) mf;
+ sh->this_id = this->pfacts[portnum].PortSCSIID;
- my_idx = le16_to_cpu(mf->u.frame.hwhdr.msgctxu.fld.req_idx);
+ /* OS entry to allow host drivers to force
+ * a queue depth on a per device basis.
+ */
+ sh->select_queue_depths = mptscsih_select_queue_depths;
+
+ /* Verify that we won't exceed the maximum
+ * number of chain buffers
+ * We can optimize: ZZ = req_sz/sizeof(SGE)
+ * For 32bit SGE's:
+ * numSGE = 1 + (ZZ-1)*(maxChain -1) + ZZ
+ * + (req_sz - 64)/sizeof(SGE)
+ * A slightly different algorithm is required for
+ * 64bit SGEs.
+ */
+ scale = this->req_sz/(sizeof(dma_addr_t) + sizeof(u32));
+ if (sizeof(dma_addr_t) == sizeof(u64)) {
+ numSGE = (scale - 1) * (this->facts.MaxChainDepth-1) + scale +
+ (this->req_sz - 60) / (sizeof(dma_addr_t) + sizeof(u32));
+ } else {
+ numSGE = 1 + (scale - 1) * (this->facts.MaxChainDepth-1) + scale +
+ (this->req_sz - 64) / (sizeof(dma_addr_t) + sizeof(u32));
+ }
+
+ if (numSGE < sh->sg_tablesize) {
+ /* Reset this value */
+ dprintk((MYIOC_s_INFO_FMT
+ "Resetting sg_tablesize to %d from %d\n",
+ this->name, numSGE, sh->sg_tablesize));
+ sh->sg_tablesize = numSGE;
+ }
- ADD_INDEX_LOG(my_idx);
+ /* Set the pci device pointer in Scsi_Host structure.
+ */
+ scsi_set_pci_device(sh, this->pcidev);
- /* Map the data portion, if any. */
- sges_left = SCpnt->use_sg;
- if (sges_left) {
- sges_left = pci_map_sg(hd->ioc->pcidev,
- (struct scatterlist *) SCpnt->request_buffer,
- sges_left,
- scsi_to_pci_dma_dir(SCpnt->sc_data_direction));
- } else if (SCpnt->request_bufflen) {
- dma_addr_t buf_dma_addr;
+ restore_flags(flags);
- buf_dma_addr = pci_map_single(hd->ioc->pcidev,
- SCpnt->request_buffer,
- SCpnt->request_bufflen,
- scsi_to_pci_dma_dir(SCpnt->sc_data_direction));
+ hd = (MPT_SCSI_HOST *) sh->hostdata;
+ hd->ioc = this;
- /* We hide it here for later unmap. */
- SCpnt->SCp.ptr = (char *)(unsigned long) buf_dma_addr;
- }
+ if ((int)this->chip_type > (int)FC929)
+ hd->is_spi = 1;
- /*
- * Put together a MPT SCSI request...
- */
-
- /* Assume SimpleQ, NO DATA XFER for now */
-
- len = SCpnt->request_bufflen;
- sgdir = 0x00000000; /* SGL IN (host<--ioc) */
- scsidir = MPI_SCSIIO_CONTROL_NODATATRANSFER;
-
- /*
- * The scsi layer should be handling this stuff
- * (In 2.3.x it does -DaveM)
- */
-
- /* BUG FIX! 19991030 -sralston
- * TUR's being issued with scsictl=0x02000000 (DATA_IN)!
- * Seems we may receive a buffer (len>0) even when there
- * will be no data transfer! GRRRRR...
- */
- datadir = mptscsih_io_direction(SCpnt);
- if (datadir < 0) {
- scsidir = MPI_SCSIIO_CONTROL_READ; /* DATA IN (host<--ioc<--dev) */
- } else if (datadir > 0) {
- sgdir = 0x04000000; /* SGL OUT (host-->ioc) */
- scsidir = MPI_SCSIIO_CONTROL_WRITE; /* DATA OUT (host-->ioc-->dev) */
- } else {
- len = 0;
- }
+ if (DmpService &&
+ (this->chip_type == FC919 || this->chip_type == FC929))
+ hd->is_multipath = 1;
- qtag = MPI_SCSIIO_CONTROL_SIMPLEQ;
-
- /*
- * Attach tags to the devices
- */
- if (SCpnt->device->tagged_supported) {
- /*
- * Some drives are too stupid to handle fairness issues
- * with tagged queueing. We throw in the odd ordered
- * tag to stop them starving themselves.
- */
- if ((jiffies - hd->qtag_tick) > (5*HZ)) {
- qtag = MPI_SCSIIO_CONTROL_ORDEREDQ;
- hd->qtag_tick = jiffies;
+ hd->port = 0; /* FIXME! */
-#if 0
- /* These are ALWAYS zero!
- * (Because this is a place for the device driver to dynamically
- * assign tag numbers any way it sees fit. That's why -DaveM)
- */
- dprintk((KERN_DEBUG MYNAM ": sc->device->current_tag = %08x\n",
- SCpnt->device->current_tag));
- dprintk((KERN_DEBUG MYNAM ": sc->tag = %08x\n",
- SCpnt->tag));
-#endif
- }
-#if 0
- else {
- /* Hmmm... I always see value of 0 here,
- * of which {HEAD_OF, ORDERED, SIMPLE} are NOT! -sralston
- * (Because this is a place for the device driver to dynamically
- * assign tag numbers any way it sees fit. That's why -DaveM)
- *
- * if (SCpnt->tag == HEAD_OF_QUEUE_TAG)
- */
- if (SCpnt->device->current_tag == HEAD_OF_QUEUE_TAG)
- qtag = MPI_SCSIIO_CONTROL_HEADOFQ;
- else if (SCpnt->tag == ORDERED_QUEUE_TAG)
- qtag = MPI_SCSIIO_CONTROL_ORDEREDQ;
- }
-#endif
- }
+ /* SCSI needs Scsi_Cmnd lookup table!
+ * (with size equal to req_depth*PtrSz!)
+ */
+ sz = hd->ioc->req_depth * sizeof(void *);
+ mem = kmalloc(sz, GFP_ATOMIC);
+ if (mem == NULL)
+ goto done;
- scsictl = scsidir | qtag;
+ memset(mem, 0, sz);
+ hd->ScsiLookup = (struct scsi_cmnd **) mem;
- frm_sz = hd->ioc->req_sz;
+ dprintk((MYIOC_s_INFO_FMT "ScsiLookup @ %p, sz=%d\n",
+ this->name, hd->ScsiLookup, sz));
- /* Ack!
- * sge_spill1 = 9;
- */
- sge_spill1 = (frm_sz - (sizeof(SCSIIORequest_t) - sizeof(SGEIOUnion_t) + sizeof(SGEChain32_t))) / 8;
- /* spill1: for req_sz == 128 (128-48==80, 80/8==10 SGEs max, first time!), --> use 9
- * spill1: for req_sz == 96 ( 96-48==48, 48/8== 6 SGEs max, first time!), --> use 5
- */
- dsgprintk((KERN_INFO MYNAM ": SG: %x spill1 = %d\n",
- my_idx, sge_spill1));
+ if (mptscsih_initChainBuffers(hd, 1) < 0)
+ goto done;
-#ifdef MPT_DEBUG
- if (sges_left > max_sges) {
- max_sges = sges_left;
- dprintk((KERN_INFO MYNAM ": MPT_MaxSges = %d\n", max_sges));
- }
-#endif
-#if 0
- if (sges_left > max_num_sges) {
- max_num_sges = sges_left;
- printk(KERN_INFO MYNAM ": MPT_MaxNumSges = %d\n", max_num_sges);
- }
-#endif
+ /* Allocate memory for free and doneQ's
+ */
+ sz = sh->can_queue * sizeof(MPT_DONE_Q);
+ mem = kmalloc(sz, GFP_ATOMIC);
+ if (mem == NULL)
+ goto done;
- dsgprintk((KERN_INFO MYNAM ": SG: %x sges_left = %d (initially)\n",
- my_idx, sges_left));
+ memset(mem, 0xFF, sz);
+ hd->memQ = mem;
- chain_offset = 0;
- if (sges_left > (sge_spill1+1)) {
-#if 0
- chain_offset = 0x1E;
-#endif
- chain_offset = (frm_sz - 8) / 4;
- }
+ /* Initialize the free, done and pending Qs.
+ */
+ Q_INIT(&hd->freeQ, MPT_DONE_Q);
+ Q_INIT(&hd->doneQ, MPT_DONE_Q);
+ Q_INIT(&hd->pendingQ, MPT_DONE_Q);
+ spin_lock_init(&hd->freedoneQlock);
+
+ mem = hd->memQ;
+ for (ii=0; ii < sh->can_queue; ii++) {
+ freedoneQ = (MPT_DONE_Q *) mem;
+ Q_ADD_TAIL(&hd->freeQ.head, freedoneQ, MPT_DONE_Q);
+ mem += sizeof(MPT_DONE_Q);
+ }
- pScsiReq->TargetID = SCpnt->target;
- pScsiReq->Bus = hd->port;
- pScsiReq->ChainOffset = chain_offset;
- pScsiReq->Function = MPI_FUNCTION_SCSI_IO_REQUEST;
- pScsiReq->CDBLength = SCpnt->cmd_len;
+ /* Initialize this Scsi_Host
+ * internal task Q.
+ */
+ Q_INIT(&hd->taskQ, MPT_FRAME_HDR);
+ hd->taskQcnt = 0;
-/* We have 256 bytes alloc'd per IO; let's use it. */
-/* pScsiReq->SenseBufferLength = SNS_LEN(SCpnt); */
- pScsiReq->SenseBufferLength = 255;
+ /* Allocate memory for the device structures.
+ * A non-Null pointer at an offset
+ * indicates a device exists.
+ * max_id = 1 + maximum id (hosts.h)
+ */
+ sz = sh->max_id * sizeof(void *);
+ mem = kmalloc(sz, GFP_ATOMIC);
+ if (mem == NULL)
+ goto done;
- pScsiReq->Reserved = 0;
- pScsiReq->MsgFlags = 0;
- pScsiReq->LUN[0] = 0;
- pScsiReq->LUN[1] = SCpnt->lun;
- pScsiReq->LUN[2] = 0;
- pScsiReq->LUN[3] = 0;
- pScsiReq->LUN[4] = 0;
- pScsiReq->LUN[5] = 0;
- pScsiReq->LUN[6] = 0;
- pScsiReq->LUN[7] = 0;
- pScsiReq->Control = cpu_to_le32(scsictl);
+ memset(mem, 0, sz);
+ hd->Targets = (VirtDevice **) mem;
- /*
- * Write SCSI CDB into the message
- */
- for (i = 0; i < 12; i++)
- pScsiReq->CDB[i] = SCpnt->cmnd[i];
- for (i = 12; i < 16; i++)
- pScsiReq->CDB[i] = 0;
+ dprintk((KERN_INFO " Targets @ %p, sz=%d\n", hd->Targets, sz));
- /* DataLength */
- pScsiReq->DataLength = cpu_to_le32(len);
- /* SenseBuffer low address */
- pScsiReq->SenseBufferLowAddr = cpu_to_le32(hd->ioc->sense_buf_pool_dma + (my_idx * 256));
+ /* Clear the TM flags
+ */
+ hd->tmPending = 0;
+#ifdef MPT_SCSI_USE_NEW_EH
+ hd->tmState = TM_STATE_NONE;
+#endif
+ hd->resetPending = 0;
+ hd->abortSCpnt = NULL;
+ hd->tmPtr = NULL;
+ hd->numTMrequests = 0;
+
+ /* Clear the pointer used to store
+ * single-threaded commands, i.e., those
+ * issued during a bus scan, dv and
+ * configuration pages.
+ */
+ hd->cmdPtr = NULL;
- mptr = (u32 *) &pScsiReq->SGL;
+ /* Attach the SCSI Host to the IOC structure
+ */
+ this->sh = sh;
- /*
- * Now fill in the SGList...
- * NOTES: For 128 byte req_sz, we can hold up to 10 simple SGE's
- * in the remaining request frame. We -could- do unlimited chains
- * but each chain buffer can only be req_sz bytes in size, and
- * we lose one SGE whenever we chain.
- * For 128 req_sz, we can hold up to 16 SGE's per chain buffer.
- * For practical reasons, limit ourselves to 1 overflow chain buffer;
- * giving us 9 + 16 == 25 SGE's max.
- * At 4 Kb per SGE, that yields 100 Kb max transfer.
- *
- * (This code needs to be completely changed when/if 64-bit DMA
- * addressing is used, since we will be able to fit much less than
- * 10 embedded SG entries. -DaveM)
- */
- if (sges_left) {
- struct scatterlist *sg = (struct scatterlist *) SCpnt->request_buffer;
- u32 v1, v2;
- int sge_spill2;
- int sge_cur_spill;
- int sgCnt;
- u8 *pSgBucket;
- int chain_sz;
-
- len = 0;
-
- /* sge_spill2 = 15;
- * spill2: for req_sz == 128 (128/8==16 SGEs max, first time!), --> use 15
- * spill2: for req_sz == 96 ( 96/8==12 SGEs max, first time!), --> use 11
- */
- sge_spill2 = frm_sz / 8 - 1;
- dsgprintk((KERN_INFO MYNAM ": SG: %x spill2 = %d\n",
- my_idx, sge_spill2));
-
- pSgBucket = NULL;
- sgCnt = 0;
- sge_cur_spill = sge_spill1;
- while (sges_left) {
-#if 0
- if (sg_dma_len(sg) > max_sgent_len) {
- max_sgent_len = sg_dma_len(sg);
- printk(KERN_INFO MYNAM ": MPT_MaxSgentLen = %d\n", max_sgent_len);
- }
-#endif
- /* Write one simple SGE */
- v1 = sgdir | 0x10000000 | sg_dma_len(sg);
- len += sg_dma_len(sg);
- v2 = sg_dma_address(sg);
- dsgprintk((KERN_INFO MYNAM ": SG: %x Writing SGE @%p: %08x %08x, sges_left=%d\n",
- my_idx, mptr, v1, v2, sges_left));
- *mptr++ = cpu_to_le32(v1);
- *mptr++ = cpu_to_le32(v2);
- sg++;
- sgCnt++;
-
- if (--sges_left == 0) {
- /* re-write 1st word of previous SGE with SIMPLE,
- * LE, EOB, and EOL bits!
+ /* Initialize this SCSI Hosts' timers
+ * To use, set the timer expires field
+ * and add_timer
*/
- v1 = 0xD1000000 | sgdir | sg_dma_len(sg-1);
- dsgprintk((KERN_INFO MYNAM ": SG: %x (re)Writing SGE @%p: %08x (VERY LAST SGE!)\n",
- my_idx, mptr-2, v1));
- *(mptr - 2) = cpu_to_le32(v1);
- } else {
- if ((sges_left > 1) && ((sgCnt % sge_cur_spill) == 0)) {
- dsgprintk((KERN_INFO MYNAM ": SG: %x SG spill at modulo 0!\n",
- my_idx));
-
- /* Fixup previous SGE with LE bit! */
- v1 = sgdir | 0x90000000 | sg_dma_len(sg-1);
- dsgprintk((KERN_INFO MYNAM ": SG: %x (re)Writing SGE @%p: %08x (LAST BUCKET SGE!)\n",
- my_idx, mptr-2, v1));
- *(mptr - 2) = cpu_to_le32(v1);
-
- chain_offset = 0;
- /* Going to need another chain? */
- if (sges_left > (sge_spill2+1)) {
-#if 0
- chain_offset = 0x1E;
+ init_timer(&hd->timer);
+ hd->timer.data = (unsigned long) hd;
+ hd->timer.function = mptscsih_timer_expired;
+
+ init_timer(&hd->TMtimer);
+ hd->TMtimer.data = (unsigned long) hd;
+ hd->TMtimer.function = mptscsih_taskmgmt_timeout;
+ hd->qtag_tick = jiffies;
+
+ /* Moved Earlier Pam D */
+ /* this->sh = sh; */
+
+ if (hd->is_spi) {
+ /* Update with the driver setup
+ * values.
+ */
+ if (hd->ioc->spi_data.maxBusWidth > driver_setup.max_width)
+ hd->ioc->spi_data.maxBusWidth = driver_setup.max_width;
+ if (hd->ioc->spi_data.minSyncFactor < driver_setup.min_sync_fac)
+ hd->ioc->spi_data.minSyncFactor = driver_setup.min_sync_fac;
+
+ if (hd->ioc->spi_data.minSyncFactor == MPT_ASYNC)
+ hd->ioc->spi_data.maxSyncOffset = 0;
+
+ hd->negoNvram = 0;
+#ifdef MPTSCSIH_DISABLE_DOMAIN_VALIDATION
+ hd->negoNvram = MPT_SCSICFG_USE_NVRAM;
#endif
- chain_offset = (frm_sz - 8) / 4;
- chain_sz = frm_sz;
- } else {
- chain_sz = sges_left * 8;
- }
+ if (driver_setup.dv == 0)
+ hd->negoNvram = MPT_SCSICFG_USE_NVRAM;
- /* write chain SGE at mptr. */
- v1 = 0x30000000 | chain_offset<<16 | chain_sz;
- if (pSgBucket == NULL) {
- pSgBucket = hd->SgHunks
- + (my_idx * frm_sz * MPT_SG_BUCKETS_PER_HUNK);
- } else {
- pSgBucket += frm_sz;
- }
- v2 = (hd->SgHunksDMA +
- ((u8 *)pSgBucket - (u8 *)hd->SgHunks));
- dsgprintk((KERN_INFO MYNAM ": SG: %x Writing SGE @%p: %08x %08x (CHAIN!)\n",
- my_idx, mptr, v1, v2));
- *(mptr++) = cpu_to_le32(v1);
- *(mptr) = cpu_to_le32(v2);
-
- mptr = (u32 *) pSgBucket;
- sgCnt = 0;
- sge_cur_spill = sge_spill2;
- }
- }
- }
- } else {
- dsgprintk((KERN_INFO MYNAM ": SG: non-SG for %p, len=%d\n",
- SCpnt, SCpnt->request_bufflen));
+ hd->ioc->spi_data.forceDv = 0;
+ for (ii=0; ii < MPT_MAX_SCSI_DEVICES; ii++)
+ hd->ioc->spi_data.dvStatus[ii] = MPT_SCSICFG_NEGOTIATE;
- if (len > 0) {
- dma_addr_t buf_dma_addr;
- buf_dma_addr = (dma_addr_t) (unsigned long)SCpnt->SCp.ptr;
- *(mptr++) = cpu_to_le32(0xD1000000|sgdir|SCpnt->request_bufflen);
- *(mptr++) = cpu_to_le32(buf_dma_addr);
- }
- }
+ ddvprintk((MYIOC_s_INFO_FMT
+ "dv %x width %x factor %x \n",
+ hd->ioc->name, driver_setup.dv,
+ driver_setup.max_width,
+ driver_setup.min_sync_fac));
-#ifdef MPT_DEBUG
- /* if (SCpnt->request_bufflen > max_xfer) */
- if (len > max_xfer) {
- max_xfer = len;
- dprintk((KERN_INFO MYNAM ": MPT_MaxXfer = %d\n", max_xfer));
- }
-#endif
+ }
- hd->ScsiLookup[my_idx] = SCpnt;
+ mpt_scsi_hosts++;
+ }
- /* Main banana... */
- mpt_put_msg_frame(ScsiDoneCtx, hd->ioc->id, mf);
+ } /* for each adapter port */
- atomic_inc(&queue_depth);
- if (atomic_read(&queue_depth) > max_qd) {
- max_qd = atomic_read(&queue_depth);
- dprintk((KERN_INFO MYNAM ": Queue depth now %d.\n", max_qd));
+ this = mpt_adapter_find_next(this);
}
- dmfprintk((KERN_INFO MYNAM ": Issued SCSI cmd (%p)\n", SCpnt));
+done:
+ if (mpt_scsi_hosts > 0)
+ register_reboot_notifier(&mptscsih_notifier);
- return 0;
+ return mpt_scsi_hosts;
}
-#ifdef MPT_SCSI_USE_NEW_EH /* { */
/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
-/*
- mptscsih_abort
- Returns: 0=SUCCESS, else FAILED
-*/
+ static char *info_kbuf = NULL;
/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
/**
- * mptscsih_abort - Abort linux Scsi_Cmnd routine, new_eh variant
- * @SCpnt: Pointer to Scsi_Cmnd structure, IO to be aborted
+ * mptscsih_release - Unregister SCSI host from linux scsi mid-layer
+ * @host: Pointer to Scsi_Host structure
*
- * (linux Scsi_Host_Template.eh_abort_handler routine)
+ * (linux Scsi_Host_Template.release routine)
+ * This routine releases all resources associated with the SCSI host
+ * adapter.
*
- * Returns SUCCESS or FAILED.
+ * Returns 0 for success.
*/
int
-mptscsih_abort(Scsi_Cmnd * SCpnt)
+mptscsih_release(struct Scsi_Host *host)
{
- MPT_FRAME_HDR *mf;
- SCSITaskMgmt_t *pScsiTm;
MPT_SCSI_HOST *hd;
- u32 *msg;
- u32 ctx2abort;
- int i;
+ int count;
unsigned long flags;
- printk(KERN_WARNING MYNAM ": Attempting _ABORT SCSI IO (=%p)\n", SCpnt);
- printk(KERN_WARNING MYNAM ": IOs outstanding = %d\n", atomic_read(&queue_depth));
+ hd = (MPT_SCSI_HOST *) host->hostdata;
- hd = (MPT_SCSI_HOST *) SCpnt->host->hostdata;
+#ifndef MPT_SCSI_USE_NEW_EH
+#ifndef MPTSCSIH_DISABLE_DOMAIN_VALIDATION
+ spin_lock_irqsave(&dvtaskQ_lock, flags);
+ dvtaskQ_release = 1;
+ spin_unlock_irqrestore(&dvtaskQ_lock, flags);
+#endif
- if ((mf = mpt_get_msg_frame(ScsiTaskCtx, hd->ioc->id)) == NULL) {
-/* SCpnt->result = DID_SOFT_ERROR << 16; */
- SCpnt->result = STS_BUSY;
- SCpnt->scsi_done(SCpnt);
- return FAILED;
+ count = 10 * HZ;
+ spin_lock_irqsave(&mytaskQ_lock, flags);
+ if (mytaskQ_bh_active) {
+ spin_unlock_irqrestore(&mytaskQ_lock, flags);
+ dprintk((KERN_INFO MYNAM ": Info: Zapping TaskMgmt thread!\n"));
+ clean_taskQ(hd);
+
+ while(mytaskQ_bh_active && --count) {
+ set_current_state(TASK_INTERRUPTIBLE);
+ schedule_timeout(1);
+ }
+ } else {
+ spin_unlock_irqrestore(&mytaskQ_lock, flags);
}
+ if (!count)
+ printk(KERN_ERR MYNAM ": ERROR - TaskMgmt thread still active!\n");
- pScsiTm = (SCSITaskMgmt_t *) mf;
- msg = (u32 *) mf;
+#endif
- pScsiTm->TargetID = SCpnt->target;
- pScsiTm->Bus = hd->port;
- pScsiTm->ChainOffset = 0;
- pScsiTm->Function = MPI_FUNCTION_SCSI_TASK_MGMT;
+#ifndef MPTSCSIH_DISABLE_DOMAIN_VALIDATION
+ /* Check DV thread active */
+ count = 10 * HZ;
+ spin_lock_irqsave(&dvtaskQ_lock, flags);
+ if (dvtaskQ_active) {
+ spin_unlock_irqrestore(&dvtaskQ_lock, flags);
+ while(dvtaskQ_active && --count) {
+ set_current_state(TASK_INTERRUPTIBLE);
+ schedule_timeout(1);
+ }
+ } else {
+ spin_unlock_irqrestore(&dvtaskQ_lock, flags);
+ }
+ if (!count)
+ printk(KERN_ERR MYNAM ": ERROR - DV thread still active!\n");
+#if defined(MPT_DEBUG_DV) || defined(MPT_DEBUG_DV_TINY)
+ else
+ printk(KERN_ERR MYNAM ": DV thread orig %d, count %d\n", 10 * HZ, count);
+#endif
+#endif
- pScsiTm->Reserved = 0;
- pScsiTm->TaskType = MPI_SCSITASKMGMT_TASKTYPE_ABORT_TASK;
- pScsiTm->Reserved1 = 0;
- pScsiTm->MsgFlags = 0;
+ unregister_reboot_notifier(&mptscsih_notifier);
- for (i = 0; i < 8; i++) {
- u8 val = 0;
- if (i == 1)
- val = SCpnt->lun;
- pScsiTm->LUN[i] = val;
- }
+ if (hd != NULL) {
+ int sz1, sz2, sz3, sztarget=0;
+ int szchain = 0;
+ int szQ = 0;
+ int scale;
- for (i = 0; i < 7; i++)
- pScsiTm->Reserved2[i] = 0;
+ /* Synchronize disk caches
+ */
+ (void) mptscsih_synchronize_cache(hd, 0);
- /* Most important! Set TaskMsgContext to SCpnt's MsgContext!
- * (the IO to be ABORT'd)
- *
- * NOTE: Since we do not byteswap MsgContext, we do not
- * swap it here either. It is an opaque cookie to
- * the controller, so it does not matter. -DaveM
- */
- ctx2abort = SCPNT_TO_MSGCTX(SCpnt);
- if (ctx2abort == -1) {
- printk(KERN_ERR MYNAM ": ERROR - ScsiLookup fail(#2) for SCpnt=%p\n", SCpnt);
- SCpnt->result = DID_SOFT_ERROR << 16;
- spin_lock_irqsave(SCpnt->host->host_lock, flags);
- SCpnt->scsi_done(SCpnt);
- spin_unlock_irqrestore(SCpnt->host->host_lock, flags);
- mpt_free_msg_frame(ScsiTaskCtx, hd->ioc->id, mf);
- } else {
- dprintk((KERN_INFO MYNAM ":DbG: ctx2abort = %08x\n", ctx2abort));
- pScsiTm->TaskMsgContext = ctx2abort;
+ sz1 = sz2 = sz3 = 0;
+ if (hd->ioc->req_sz <= 64)
+ scale = MPT_SG_REQ_64_SCALE;
+ else if (hd->ioc->req_sz <= 96)
+ scale = MPT_SG_REQ_96_SCALE;
+ else
+ scale = MPT_SG_REQ_128_SCALE;
- /* MPI v0.10 requires SCSITaskMgmt requests be sent via Doorbell/handshake
- mpt_put_msg_frame(hd->ioc->id, mf);
- */
- if ((i = mpt_send_handshake_request(ScsiTaskCtx, hd->ioc->id,
- sizeof(SCSITaskMgmt_t), msg))
- != 0) {
- printk(KERN_WARNING MYNAM
- ": WARNING[2] - IOC error (%d) processing TaskMgmt request (mf=%p:sc=%p)\n",
- i, mf, SCpnt);
- SCpnt->result = DID_SOFT_ERROR << 16;
- spin_lock_irqsave(SCpnt->host->host_lock, flags);
- SCpnt->scsi_done(SCpnt);
- spin_unlock_irqrestore(SCpnt->host->host_lock, flags);
- mpt_free_msg_frame(ScsiTaskCtx, hd->ioc->id, mf);
+ if (hd->ScsiLookup != NULL) {
+ sz1 = hd->ioc->req_depth * sizeof(void *);
+ kfree(hd->ScsiLookup);
+ hd->ScsiLookup = NULL;
}
- }
- //return SUCCESS;
- return FAILED;
-}
+ if (hd->ReqToChain != NULL) {
+ szchain += scale * hd->ioc->req_depth * sizeof(int);
+ kfree(hd->ReqToChain);
+ hd->ReqToChain = NULL;
+ }
-/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
-/**
- * mptscsih_dev_reset - Perform a SCSI TARGET_RESET! new_eh variant
- * @SCpnt: Pointer to Scsi_Cmnd structure, IO which reset is due to
- *
- * (linux Scsi_Host_Template.eh_dev_reset_handler routine)
- *
- * Returns SUCCESS or FAILED.
- */
-int
-mptscsih_dev_reset(Scsi_Cmnd * SCpnt)
-{
- MPT_FRAME_HDR *mf;
- SCSITaskMgmt_t *pScsiTm;
- MPT_SCSI_HOST *hd;
- u32 *msg;
- int i;
- unsigned long flags;
+ if (hd->ChainToChain != NULL) {
+ szchain += scale * hd->ioc->req_depth * sizeof(int);
+ kfree(hd->ChainToChain);
+ hd->ChainToChain = NULL;
+ }
- printk(KERN_WARNING MYNAM ": Attempting _TARGET_RESET (%p)\n", SCpnt);
- printk(KERN_WARNING MYNAM ": IOs outstanding = %d\n", atomic_read(&queue_depth));
+ if (hd->ChainBuffer != NULL) {
+ sz2 = scale * hd->ioc->req_depth * hd->ioc->req_sz;
+ szchain += sz2;
- hd = (MPT_SCSI_HOST *) SCpnt->host->hostdata;
+ pci_free_consistent(hd->ioc->pcidev, sz2,
+ hd->ChainBuffer, hd->ChainBufferDMA);
+ hd->ChainBuffer = NULL;
+ }
- if ((mf = mpt_get_msg_frame(ScsiTaskCtx, hd->ioc->id)) == NULL) {
-/* SCpnt->result = DID_SOFT_ERROR << 16; */
- SCpnt->result = STS_BUSY;
- SCpnt->scsi_done(SCpnt);
- return FAILED;
- }
+ if (hd->memQ != NULL) {
+ szQ = host->can_queue * sizeof(MPT_DONE_Q);
+ kfree(hd->memQ);
+ hd->memQ = NULL;
+ }
- pScsiTm = (SCSITaskMgmt_t *) mf;
- msg = (u32*)mf;
+ if (hd->Targets != NULL) {
+ int max, ii;
- pScsiTm->TargetID = SCpnt->target;
- pScsiTm->Bus = hd->port;
- pScsiTm->ChainOffset = 0;
- pScsiTm->Function = MPI_FUNCTION_SCSI_TASK_MGMT;
+ /*
+ * Free any target structures that were allocated.
+ */
+ if (hd->is_spi) {
+ max = MPT_MAX_SCSI_DEVICES;
+ } else {
+ max = MPT_MAX_FC_DEVICES;
+ }
+ for (ii=0; ii < max; ii++) {
+ if (hd->Targets[ii]) {
+ kfree(hd->Targets[ii]);
+ hd->Targets[ii] = NULL;
+ sztarget += sizeof(VirtDevice);
+ }
+ }
- pScsiTm->Reserved = 0;
- pScsiTm->TaskType = MPI_SCSITASKMGMT_TASKTYPE_TARGET_RESET;
- pScsiTm->Reserved1 = 0;
- pScsiTm->MsgFlags = 0;
+ /*
+ * Free pointer array.
+ */
+ sz3 = max * sizeof(void *);
+ kfree(hd->Targets);
+ hd->Targets = NULL;
+ }
- /* _TARGET_RESET goes to LUN 0 always! */
- for (i = 0; i < 8; i++)
- pScsiTm->LUN[i] = 0;
+ dprintk((MYIOC_s_INFO_FMT "Free'd ScsiLookup (%d), chain (%d) and Target (%d+%d) memory\n",
+ hd->ioc->name, sz1, szchain, sz3, sztarget));
+ dprintk(("Free'd done and free Q (%d) memory\n", szQ));
+ }
+ /* NULL the Scsi_Host pointer
+ */
+ hd->ioc->sh = NULL;
+ scsi_unregister(host);
- /* Control: No data direction, set task mgmt bit? */
- for (i = 0; i < 7; i++)
- pScsiTm->Reserved2[i] = 0;
+ if (mpt_scsi_hosts) {
+ if (--mpt_scsi_hosts == 0) {
+ mpt_reset_deregister(ScsiDoneCtx);
+ dprintk((KERN_INFO MYNAM ": Deregistered for IOC reset notifications\n"));
- pScsiTm->TaskMsgContext = cpu_to_le32(0);
+ mpt_event_deregister(ScsiDoneCtx);
+ dprintk((KERN_INFO MYNAM ": Deregistered for IOC event notifications\n"));
-/* MPI v0.10 requires SCSITaskMgmt requests be sent via Doorbell/handshake
- mpt_put_msg_frame(hd->ioc->id, mf);
-*/
-/* FIXME! Check return status! */
- if ((i = mpt_send_handshake_request(ScsiTaskCtx, hd->ioc->id,
- sizeof(SCSITaskMgmt_t), msg))
- != 0) {
- printk(KERN_WARNING MYNAM
- ": WARNING[3] - IOC error (%d) processing TaskMgmt request (mf=%p:sc=%p)\n",
- i, mf, SCpnt);
- SCpnt->result = DID_SOFT_ERROR << 16;
- spin_lock_irqsave(SCpnt->host->host_lock, flags);
- SCpnt->scsi_done(SCpnt);
- spin_unlock_irqrestore(SCpnt->host->host_lock, flags);
- mpt_free_msg_frame(ScsiTaskCtx, hd->ioc->id, mf);
+ mpt_deregister(ScsiScanDvCtx);
+ mpt_deregister(ScsiTaskCtx);
+ mpt_deregister(ScsiDoneCtx);
+
+ if (info_kbuf != NULL)
+ kfree(info_kbuf);
+ }
}
- //return SUCCESS;
- return FAILED;
+ return 0;
}
/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
/**
- * mptscsih_bus_reset - Perform a SCSI BUS_RESET! new_eh variant
- * @SCpnt: Pointer to Scsi_Cmnd structure, IO which reset is due to
+ * mptscsih_halt - Process the reboot notification
+ * @nb: Pointer to a struct notifier_block (ignored)
+ * @event: event (SYS_HALT, SYS_RESTART, SYS_POWER_OFF)
+ * @buf: Pointer to a data buffer (ignored)
*
- * (linux Scsi_Host_Template.eh_bus_reset_handler routine)
+ * This routine called if a system shutdown or reboot is to occur.
*
- * Returns SUCCESS or FAILED.
+ * Return NOTIFY_DONE if this is something other than a reboot message.
+ * NOTIFY_OK if this is a reboot message.
*/
-int
-mptscsih_bus_reset(Scsi_Cmnd * SCpnt)
+static int
+mptscsih_halt(struct notifier_block *nb, ulong event, void *buf)
{
- MPT_FRAME_HDR *mf;
- SCSITaskMgmt_t *pScsiTm;
- MPT_SCSI_HOST *hd;
- u32 *msg;
- int i;
- unsigned long flags;
-
- printk(KERN_WARNING MYNAM ": Attempting _BUS_RESET (%p)\n", SCpnt);
- printk(KERN_WARNING MYNAM ": IOs outstanding = %d\n", atomic_read(&queue_depth));
+ MPT_ADAPTER *ioc = NULL;
+ MPT_SCSI_HOST *hd = NULL;
- hd = (MPT_SCSI_HOST *) SCpnt->host->hostdata;
+ /* Ignore all messages other than reboot message
+ */
+ if ((event != SYS_RESTART) && (event != SYS_HALT)
+ && (event != SYS_POWER_OFF))
+ return (NOTIFY_DONE);
- if ((mf = mpt_get_msg_frame(ScsiTaskCtx, hd->ioc->id)) == NULL) {
-/* SCpnt->result = DID_SOFT_ERROR << 16; */
- SCpnt->result = STS_BUSY;
- SCpnt->scsi_done(SCpnt);
- return FAILED;
+ for (ioc = mpt_adapter_find_first(); ioc != NULL; ioc = mpt_adapter_find_next(ioc)) {
+ /* Flush the cache of this adapter
+ */
+ if (ioc->sh) {
+ hd = (MPT_SCSI_HOST *) ioc->sh->hostdata;
+ if (hd) {
+ mptscsih_synchronize_cache(hd, 0);
+ }
+ }
}
- pScsiTm = (SCSITaskMgmt_t *) mf;
- msg = (u32 *) mf;
+ unregister_reboot_notifier(&mptscsih_notifier);
+ return NOTIFY_OK;
+}
- pScsiTm->TargetID = SCpnt->target;
- pScsiTm->Bus = hd->port;
- pScsiTm->ChainOffset = 0;
- pScsiTm->Function = MPI_FUNCTION_SCSI_TASK_MGMT;
-
- pScsiTm->Reserved = 0;
- pScsiTm->TaskType = MPI_SCSITASKMGMT_TASKTYPE_RESET_BUS;
- pScsiTm->Reserved1 = 0;
- pScsiTm->MsgFlags = 0;
+/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
+/**
+ * mptscsih_info - Return information about MPT adapter
+ * @SChost: Pointer to Scsi_Host structure
+ *
+ * (linux Scsi_Host_Template.info routine)
+ *
+ * Returns pointer to buffer where information was written.
+ */
+const char *
+mptscsih_info(struct Scsi_Host *SChost)
+{
+ MPT_SCSI_HOST *h;
+ int size = 0;
- for (i = 0; i < 8; i++)
- pScsiTm->LUN[i] = 0;
+ if (info_kbuf == NULL)
+ if ((info_kbuf = kmalloc(0x1000 /* 4Kb */, GFP_KERNEL)) == NULL)
+ return info_kbuf;
- /* Control: No data direction, set task mgmt bit? */
- for (i = 0; i < 7; i++)
- pScsiTm->Reserved2[i] = 0;
+ h = (MPT_SCSI_HOST *)SChost->hostdata;
+ info_kbuf[0] = '\0';
+ mpt_print_ioc_summary(h->ioc, info_kbuf, &size, 0, 0);
+ info_kbuf[size-1] = '\0';
- pScsiTm->TaskMsgContext = cpu_to_le32(0);
+ return info_kbuf;
+}
-/* MPI v0.10 requires SCSITaskMgmt requests be sent via Doorbell/handshake
- mpt_put_msg_frame(hd->ioc->id, mf);
-*/
-/* FIXME! Check return status! */
- if ((i = mpt_send_handshake_request(ScsiTaskCtx, hd->ioc->id,
- sizeof(SCSITaskMgmt_t), msg))
- != 0) {
- printk(KERN_WARNING MYNAM
- ": WARNING[4] - IOC error (%d) processing TaskMgmt request (mf=%p:sc=%p)\n",
- i, mf, SCpnt);
- SCpnt->result = DID_SOFT_ERROR << 16;
- spin_lock_irqsave(SCpnt->host->host_lock, flags);
- SCpnt->scsi_done(SCpnt);
- spin_unlock_irqrestore(SCpnt->host->host_lock, flags);
- mpt_free_msg_frame(ScsiTaskCtx, hd->ioc->id, mf);
- }
+/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
+ static int max_qd = 1;
+#if 0
+static int index_log[128];
+static int index_ent = 0;
+static __inline__ void ADD_INDEX_LOG(int req_ent)
+{
+ int i = index_ent++;
- return SUCCESS;
+ index_log[i & (128 - 1)] = req_ent;
}
+#else
+#define ADD_INDEX_LOG(req_ent) do { } while(0)
+#endif
+
+#ifdef DROP_TEST
+#define DROP_IOC 1 /* IOC to force failures */
+#define DROP_TARGET 3 /* Target ID to force failures */
+#define DROP_THIS_CMD 10000 /* iteration to drop command */
+static int dropCounter = 0;
+static int dropTestOK = 0; /* num did good */
+static int dropTestBad = 0; /* num did bad */
+static int dropTestNum = 0; /* total = good + bad + incomplete */
+static int numTotCmds = 0;
+static MPT_FRAME_HDR *dropMfPtr = NULL;
+static int numTMrequested = 0;
+#endif
-#if 0 /* { */
/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
-/**
- * mptscsih_host_reset - Perform a SCSI host adapter RESET!
- * new_eh variant
- * @SCpnt: Pointer to Scsi_Cmnd structure, IO which reset is due to
+/*
+ * mptscsih_put_msgframe - Wrapper routine to post message frame to F/W.
+ * @context: Call back context (ScsiDoneCtx, ScsiScanDvCtx)
+ * @id: IOC id number
+ * @mf: Pointer to message frame
*
- * (linux Scsi_Host_Template.eh_host_reset_handler routine)
+ * Handles the call to mptbase for posting request and queue depth
+ * tracking.
*
- * Returns SUCCESS or FAILED.
+ * Returns none.
*/
-int
-mptscsih_host_reset(Scsi_Cmnd * SCpnt)
+static void
+mptscsih_put_msgframe(int context, int id, MPT_FRAME_HDR *mf)
{
- return FAILED;
+ /* Main banana... */
+ atomic_inc(&queue_depth);
+ if (atomic_read(&queue_depth) > max_qd) {
+ max_qd = atomic_read(&queue_depth);
+ dprintk((KERN_INFO MYNAM ": Queue depth now %d.\n", max_qd));
+ }
+
+ mpt_put_msg_frame(context, id, mf);
+
+ return;
}
-#endif /* } */
-#else /* MPT_SCSI old EH stuff... */
/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
/**
- * mptscsih_old_abort - Abort linux Scsi_Cmnd routine
- * @SCpnt: Pointer to Scsi_Cmnd structure, IO to be aborted
+ * mptscsih_qcmd - Primary Fusion MPT SCSI initiator IO start routine.
+ * @SCpnt: Pointer to Scsi_Cmnd structure
+ * @done: Pointer SCSI mid-layer IO completion function
*
- * (linux Scsi_Host_Template.abort routine)
+ * (linux Scsi_Host_Template.queuecommand routine)
+ * This is the primary SCSI IO start routine. Create a MPI SCSIIORequest
+ * from a linux Scsi_Cmnd request and send it to the IOC.
*
- * Returns SCSI_ABORT_{SUCCESS,BUSY,PENDING}.
+ * Returns 0. (rtn value discarded by linux scsi mid-layer)
*/
int
-mptscsih_old_abort(Scsi_Cmnd *SCpnt)
+mptscsih_qcmd(Scsi_Cmnd *SCpnt, void (*done)(Scsi_Cmnd *))
{
MPT_SCSI_HOST *hd;
MPT_FRAME_HDR *mf;
- struct tq_struct *ptaskfoo;
+ SCSIIORequest_t *pScsiReq;
+ VirtDevice *pTarget;
+ MPT_DONE_Q *buffer = NULL;
unsigned long flags;
+ int target;
+ int lun;
+ int datadir;
+ u32 datalen;
+ u32 scsictl;
+ u32 scsidir;
+ u32 qtag;
+ u32 cmd_len;
+ int my_idx;
+ int ii;
+ int rc;
+ int did_errcode;
+ int issueCmd;
- printk(KERN_WARNING MYNAM ": Scheduling _ABORT SCSI IO (=%p)\n", SCpnt);
- printk(KERN_WARNING MYNAM ": IOs outstanding = %d\n", atomic_read(&queue_depth));
+ did_errcode = 0;
+ hd = (MPT_SCSI_HOST *) SCpnt->host->hostdata;
+ target = SCpnt->target;
+ lun = SCpnt->lun;
+ SCpnt->scsi_done = done;
- if ((hd = (MPT_SCSI_HOST *) SCpnt->host->hostdata) == NULL) {
- SCpnt->result = DID_ABORT << 16;
- SCpnt->scsi_done(SCpnt);
- return SCSI_ABORT_SUCCESS;
- }
+ pTarget = hd->Targets[target];
- /*
- * Check to see if there's already an ABORT queued for this guy.
+ dmfprintk((MYIOC_s_INFO_FMT "qcmd: SCpnt=%p, done()=%p\n",
+ (hd && hd->ioc) ? hd->ioc->name : "ioc?", SCpnt, done));
+
+ /* 20000617 -sralston
+ * GRRRRR... Shouldn't have to do this but...
+ * Do explicit check for REQUEST_SENSE and cached SenseData.
+ * If yes, return cached SenseData.
*/
- mf = search_taskQ(0,SCpnt,MPI_SCSITASKMGMT_TASKTYPE_ABORT_TASK);
- if (mf != NULL) {
- return SCSI_ABORT_PENDING;
+ if (SCpnt->cmnd[0] == REQUEST_SENSE) {
+ u8 *dest = NULL;
+ int sz;
+
+ if (pTarget && (pTarget->tflags & MPT_TARGET_FLAGS_VALID_SENSE)) {
+ pTarget->tflags &= ~MPT_TARGET_FLAGS_VALID_SENSE; //sjr-moved-here
+ if (!SCpnt->use_sg) {
+ dest = SCpnt->request_buffer;
+ } else {
+ struct scatterlist *sg = (struct scatterlist *) SCpnt->request_buffer;
+ if (sg)
+ dest = (u8 *)(ulong)sg_dma_address(sg);
+ }
+
+ if (dest) {
+ sz = MIN (SCSI_STD_SENSE_BYTES, SCpnt->request_bufflen);
+ memcpy(dest, pTarget->sense, sz);
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,3,0)
+ SCpnt->resid = SCpnt->request_bufflen - sz;
+#endif
+ SCpnt->result = 0;
+ SCpnt->scsi_done(SCpnt);
+
+ //sjr-moved-up//pTarget->tflags &= ~MPT_TARGET_FLAGS_VALID_SENSE;
+
+ return 0;
+ }
+ }
}
- if ((mf = mpt_get_msg_frame(ScsiTaskCtx, hd->ioc->id)) == NULL) {
-/* SCpnt->result = DID_SOFT_ERROR << 16; */
- SCpnt->result = STS_BUSY;
- SCpnt->scsi_done(SCpnt);
- return SCSI_ABORT_BUSY;
+ if (hd->resetPending) {
+ /* Prevent new commands from being issued
+ * while reloading the FW.
+ */
+ did_errcode = 1;
+ goto did_error;
}
/*
- * Add ourselves to (end of) mpt_scsih_taskQ.
- * Check to see if our _bh is running. If NOT, schedule it.
+ * Put together a MPT SCSI request...
*/
- dslprintk((KERN_INFO MYNAM ": spinlock#2\n"));
- spin_lock_irqsave(&mpt_scsih_taskQ_lock, flags);
- Q_ADD_TAIL(&mpt_scsih_taskQ, &mf->u.frame.linkage, MPT_FRAME_HDR);
- mpt_scsih_taskQ_cnt++;
- /* Yikes - linkage! */
-/* SCpnt->host_scribble = (unsigned char *)mf; */
- mf->u.frame.linkage.arg1 = MPI_SCSITASKMGMT_TASKTYPE_ABORT_TASK;
- mf->u.frame.linkage.argp1 = SCpnt;
- if (! mpt_scsih_taskQ_bh_active) {
- mpt_scsih_taskQ_bh_active = 1;
- /*
- * Oh how cute, no alloc/free/mgmt needed if we use
- * (bottom/unused portion of) MPT request frame.
- */
- ptaskfoo = (struct tq_struct *) ((u8*)mf + hd->ioc->req_sz - sizeof(*ptaskfoo));
- ptaskfoo->sync = 0;
- ptaskfoo->routine = mptscsih_taskmgmt_bh;
- ptaskfoo->data = SCpnt;
-
- SCHEDULE_TASK(ptaskfoo);
+ if ((mf = mpt_get_msg_frame(ScsiDoneCtx, hd->ioc->id)) == NULL) {
+ dprintk((MYIOC_s_WARN_FMT "QueueCmd, no msg frames!!\n",
+ hd->ioc->name));
+ did_errcode = 2;
+ goto did_error;
}
- spin_unlock_irqrestore(&mpt_scsih_taskQ_lock, flags);
-
- return SCSI_ABORT_PENDING;
-}
-/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
-/**
- * mptscsih_old_reset - Perform a SCSI BUS_RESET!
- * @SCpnt: Pointer to Scsi_Cmnd structure, IO which reset is due to
- * @reset_flags: (not used?)
- *
- * (linux Scsi_Host_Template.reset routine)
- *
- * Returns SCSI_RESET_{SUCCESS,PUNT,PENDING}.
- */
-int
-mptscsih_old_reset(Scsi_Cmnd *SCpnt, unsigned int reset_flags)
-{
- MPT_SCSI_HOST *hd;
- MPT_FRAME_HDR *mf;
- struct tq_struct *ptaskfoo;
- unsigned long flags;
+ pScsiReq = (SCSIIORequest_t *) mf;
- printk(KERN_WARNING MYNAM ": Scheduling _BUS_RESET (=%p)\n", SCpnt);
- printk(KERN_WARNING MYNAM ": IOs outstanding = %d\n", atomic_read(&queue_depth));
+ my_idx = le16_to_cpu(mf->u.frame.hwhdr.msgctxu.fld.req_idx);
- if ((hd = (MPT_SCSI_HOST *) SCpnt->host->hostdata) == NULL) {
- SCpnt->result = DID_RESET << 16;
- SCpnt->scsi_done(SCpnt);
- return SCSI_RESET_SUCCESS;
- }
+ ADD_INDEX_LOG(my_idx);
/*
- * Check to see if there's already a BUS_RESET queued for this guy.
+ * The scsi layer should be handling this stuff
+ * (In 2.3.x it does -DaveM)
*/
- mf = search_taskQ(0,SCpnt,MPI_SCSITASKMGMT_TASKTYPE_RESET_BUS);
- if (mf != NULL) {
- return SCSI_RESET_PENDING;
- }
- if ((mf = mpt_get_msg_frame(ScsiTaskCtx, hd->ioc->id)) == NULL) {
-/* SCpnt->result = DID_SOFT_ERROR << 16; */
- SCpnt->result = STS_BUSY;
- SCpnt->scsi_done(SCpnt);
- return SCSI_RESET_PUNT;
+ /* BUG FIX! 19991030 -sralston
+ * TUR's being issued with scsictl=0x02000000 (DATA_IN)!
+ * Seems we may receive a buffer (datalen>0) even when there
+ * will be no data transfer! GRRRRR...
+ */
+ datadir = mptscsih_io_direction(SCpnt);
+ if (datadir == SCSI_DATA_READ) {
+ datalen = SCpnt->request_bufflen;
+ scsidir = MPI_SCSIIO_CONTROL_READ; /* DATA IN (host<--ioc<--dev) */
+ } else if (datadir == SCSI_DATA_WRITE) {
+ datalen = SCpnt->request_bufflen;
+ scsidir = MPI_SCSIIO_CONTROL_WRITE; /* DATA OUT (host-->ioc-->dev) */
+ } else {
+ datalen = 0;
+ scsidir = MPI_SCSIIO_CONTROL_NODATATRANSFER;
}
- /*
- * Add ourselves to (end of) mpt_scsih_taskQ.
- * Check to see if our _bh is running. If NOT, schedule it.
+ /* Default to untagged. Once a target structure has been allocated,
+ * use the Inquiry data to determine if device supports tagged.
*/
- dslprintk((KERN_INFO MYNAM ": spinlock#3\n"));
- spin_lock_irqsave(&mpt_scsih_taskQ_lock, flags);
- Q_ADD_TAIL(&mpt_scsih_taskQ, &mf->u.frame.linkage, MPT_FRAME_HDR);
- mpt_scsih_taskQ_cnt++;
- /* Yikes - linkage! */
-/* SCpnt->host_scribble = (unsigned char *)mf; */
- mf->u.frame.linkage.arg1 = MPI_SCSITASKMGMT_TASKTYPE_RESET_BUS;
- mf->u.frame.linkage.argp1 = SCpnt;
- if (! mpt_scsih_taskQ_bh_active) {
- mpt_scsih_taskQ_bh_active = 1;
+ qtag = MPI_SCSIIO_CONTROL_UNTAGGED;
+ if (pTarget && (pTarget->tflags & MPT_TARGET_FLAGS_Q_YES)
+ && (SCpnt->device->tagged_supported)) {
/*
- * Oh how cute, no alloc/free/mgmt needed if we use
- * (bottom/unused portion of) MPT request frame.
+ * Some drives are too stupid to handle fairness issues
+ * with tagged queueing. We throw in the odd ordered
+ * tag to stop them starving themselves.
*/
- ptaskfoo = (struct tq_struct *) ((u8*)mf + hd->ioc->req_sz - sizeof(*ptaskfoo));
- ptaskfoo->sync = 0;
- ptaskfoo->routine = mptscsih_taskmgmt_bh;
- ptaskfoo->data = SCpnt;
-
- SCHEDULE_TASK(ptaskfoo);
+ if ((jiffies - hd->qtag_tick) > (5*HZ)) {
+ qtag = MPI_SCSIIO_CONTROL_ORDEREDQ;
+ hd->qtag_tick = jiffies;
+ }
+ else
+ qtag = MPI_SCSIIO_CONTROL_SIMPLEQ;
}
- spin_unlock_irqrestore(&mpt_scsih_taskQ_lock, flags);
+ scsictl = scsidir | qtag;
- return SCSI_RESET_PENDING;
-}
+ /* Use the above information to set up the message frame
+ */
+ pScsiReq->TargetID = target;
+ pScsiReq->Bus = hd->port;
+ pScsiReq->ChainOffset = 0;
+ pScsiReq->Function = MPI_FUNCTION_SCSI_IO_REQUEST;
+ pScsiReq->CDBLength = SCpnt->cmd_len;
+ pScsiReq->SenseBufferLength = MPT_SENSE_BUFFER_SIZE;
+ pScsiReq->Reserved = 0;
+ pScsiReq->MsgFlags = mpt_msg_flags();
+ pScsiReq->LUN[0] = 0;
+ pScsiReq->LUN[1] = lun;
+ pScsiReq->LUN[2] = 0;
+ pScsiReq->LUN[3] = 0;
+ pScsiReq->LUN[4] = 0;
+ pScsiReq->LUN[5] = 0;
+ pScsiReq->LUN[6] = 0;
+ pScsiReq->LUN[7] = 0;
+ pScsiReq->Control = cpu_to_le32(scsictl);
-/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
-/*
- * mptscsih_taskmgmt_bh - SCSI task mgmt bottom half handler
- * @sc: (unused)
- *
- * This routine (thread) is active whenever there are any outstanding
- * SCSI task management requests for a SCSI host adapter.
- * IMPORTANT! This routine is scheduled therefore should never be
- * running in ISR context. i.e., it's safe to sleep here.
- */
-void
-mptscsih_taskmgmt_bh(void *sc)
-{
- Scsi_Cmnd *SCpnt;
- MPT_FRAME_HDR *mf;
- SCSITaskMgmt_t *pScsiTm;
- MPT_SCSI_HOST *hd;
- u32 ctx2abort = 0;
- int i;
- unsigned long flags;
- u8 task_type;
+ /*
+ * Write SCSI CDB into the message
+ */
+ cmd_len = SCpnt->cmd_len;
+ for (ii=0; ii < cmd_len; ii++)
+ pScsiReq->CDB[ii] = SCpnt->cmnd[ii];
+ for (ii=cmd_len; ii < 16; ii++)
+ pScsiReq->CDB[ii] = 0;
- dslprintk((KERN_INFO MYNAM ": spinlock#4\n"));
- spin_lock_irqsave(&mpt_scsih_taskQ_lock, flags);
- mpt_scsih_taskQ_bh_active = 1;
- spin_unlock_irqrestore(&mpt_scsih_taskQ_lock, flags);
+ /* DataLength */
+ pScsiReq->DataLength = cpu_to_le32(datalen);
- while (1) {
- current->state = TASK_INTERRUPTIBLE;
- schedule_timeout(HZ/4);
+ /* SenseBuffer low address */
+ pScsiReq->SenseBufferLowAddr = cpu_to_le32(hd->ioc->sense_buf_low_dma
+ + (my_idx * MPT_SENSE_BUFFER_ALLOC));
- /*
- * We MUST remove item from taskQ *before* we format the
- * frame as a SCSITaskMgmt request and send it down to the IOC.
- */
- dslprintk((KERN_INFO MYNAM ": spinlock#5\n"));
- spin_lock_irqsave(&mpt_scsih_taskQ_lock, flags);
- if (Q_IS_EMPTY(&mpt_scsih_taskQ)) {
- spin_unlock_irqrestore(&mpt_scsih_taskQ_lock, flags);
- break;
- }
- mf = mpt_scsih_taskQ.head;
- Q_DEL_ITEM(&mf->u.frame.linkage);
- mpt_scsih_taskQ_cnt--;
- mpt_scsih_active_taskmgmt_mf = mf;
- spin_unlock_irqrestore(&mpt_scsih_taskQ_lock, flags);
+ /* Now add the SG list
+ * Always have a SGE even if null length.
+ */
+ rc = SUCCESS;
+ if (datalen == 0) {
+ /* Add a NULL SGE */
+ mpt_add_sge((char *)&pScsiReq->SGL, MPT_SGE_FLAGS_SSIMPLE_READ | 0,
+ (dma_addr_t) -1);
+ } else {
+ /* Add a 32 or 64 bit SGE */
+ rc = mptscsih_AddSGE(hd, SCpnt, pScsiReq, my_idx);
+ }
- SCpnt = (Scsi_Cmnd*)mf->u.frame.linkage.argp1;
- if (SCpnt == NULL) {
- printk(KERN_ERR MYNAM ": ERROR - TaskMgmt has NULL SCpnt! (%p:%p)\n", mf, SCpnt);
- continue;
- }
- hd = (MPT_SCSI_HOST *) SCpnt->host->hostdata;
- pScsiTm = (SCSITaskMgmt_t *) mf;
- for (i = 0; i < 8; i++) {
- pScsiTm->LUN[i] = 0;
- }
+ if (rc == SUCCESS) {
+ hd->ScsiLookup[my_idx] = SCpnt;
+ SCpnt->host_scribble = NULL;
+
+#ifdef DROP_TEST
+ numTotCmds++;
+ /* If the IOC number and target match, increment
+ * counter. If counter matches DROP_THIS, do not
+ * issue command to FW to force a reset.
+ * Save the MF pointer so we can free resources
+ * when task mgmt completes.
+ */
+ if ((hd->ioc->id == DROP_IOC) && (target == DROP_TARGET)) {
+ dropCounter++;
- task_type = mf->u.frame.linkage.arg1;
- if (task_type == MPI_SCSITASKMGMT_TASKTYPE_ABORT_TASK) {
- printk(KERN_WARNING MYNAM ": Attempting _ABORT SCSI IO! (mf=%p:sc=%p)\n",
- mf, SCpnt);
+ if (dropCounter == DROP_THIS_CMD) {
+ dropCounter = 0;
- /* Most important! Set TaskMsgContext to SCpnt's MsgContext!
- * (the IO to be ABORT'd)
- *
- * NOTE: Since we do not byteswap MsgContext, we do not
- * swap it here either. It is an opaque cookie to
- * the controller, so it does not matter. -DaveM
- */
- ctx2abort = SCPNT_TO_MSGCTX(SCpnt);
- if (ctx2abort == -1) {
- printk(KERN_ERR MYNAM ": ERROR - ScsiLookup fail(#1) for SCpnt=%p\n", SCpnt);
- SCpnt->result = DID_SOFT_ERROR << 16;
- spin_lock_irqsave(SCpnt->host->host_lock, flags);
- SCpnt->scsi_done(SCpnt);
- spin_unlock_irqrestore(SCpnt->host->host_lock, flags);
- mpt_free_msg_frame(ScsiTaskCtx, hd->ioc->id, mf);
- continue;
+ /* If global is set, then we are already
+ * doing something - so keep issuing commands.
+ */
+ if (dropMfPtr == NULL) {
+ dropTestNum++;
+ dropMfPtr = mf;
+ atomic_inc(&queue_depth);
+ printk(MYIOC_s_INFO_FMT
+ "Dropped SCSI cmd (%p)\n",
+ hd->ioc->name, SCpnt);
+ printk("mf (%p) req (%4x) tot cmds (%d)\n",
+ mf, my_idx, numTotCmds);
+
+ return 0;
+ }
}
- pScsiTm->LUN[1] = SCpnt->lun;
}
- else if (task_type == MPI_SCSITASKMGMT_TASKTYPE_RESET_BUS)
- {
- printk(KERN_WARNING MYNAM ": Attempting _BUS_RESET! (against SCSI IO mf=%p:sc=%p)\n", mf, SCpnt);
- }
-#if 0
- else if (task_type == MPI_SCSITASKMGMT_TASKTYPE_TARGET_RESET) {}
- else if (task_type == MPI_SCSITASKMGMT_TASKTYPE_ABRT_TASK_SET) {}
#endif
- printk(KERN_WARNING MYNAM ": IOs outstanding = %d\n", atomic_read(&queue_depth));
-
- pScsiTm->TargetID = SCpnt->target;
- pScsiTm->Bus = hd->port;
- pScsiTm->ChainOffset = 0;
- pScsiTm->Function = MPI_FUNCTION_SCSI_TASK_MGMT;
-
- pScsiTm->Reserved = 0;
- pScsiTm->TaskType = task_type;
- pScsiTm->Reserved1 = 0;
- pScsiTm->MsgFlags = 0;
-
- for (i = 0; i < 7; i++)
- pScsiTm->Reserved2[i] = 0;
+ /* SCSI specific processing */
+ issueCmd = 1;
+ if (hd->is_spi) {
+ int dvStatus = hd->ioc->spi_data.dvStatus[target];
+
+ if (dvStatus || hd->ioc->spi_data.forceDv) {
+
+ /* Write SDP1 on this I/O to this target */
+ if (dvStatus & MPT_SCSICFG_NEGOTIATE) {
+ mptscsih_writeSDP1(hd, 0, target, hd->negoNvram);
+ dvStatus &= ~MPT_SCSICFG_NEGOTIATE;
+ hd->ioc->spi_data.dvStatus[target] = dvStatus;
+ } else if (dvStatus & MPT_SCSICFG_BLK_NEGO) {
+ mptscsih_writeSDP1(hd, 0, target, MPT_SCSICFG_BLK_NEGO);
+ dvStatus &= ~MPT_SCSICFG_BLK_NEGO;
+ hd->ioc->spi_data.dvStatus[target] = dvStatus;
+ }
- dprintk((KERN_INFO MYNAM ":DbG: ctx2abort = %08x\n", ctx2abort));
- pScsiTm->TaskMsgContext = ctx2abort;
+#ifndef MPTSCSIH_DISABLE_DOMAIN_VALIDATION
+ if ((dvStatus & MPT_SCSICFG_NEED_DV) || hd->ioc->spi_data.forceDv) {
+ unsigned long lflags;
+ /* Schedule DV if necessary */
+ spin_lock_irqsave(&dvtaskQ_lock, lflags);
+ if (!dvtaskQ_active) {
+ dvtaskQ_active = 1;
+ spin_unlock_irqrestore(&dvtaskQ_lock, lflags);
+ mptscsih_dvTask.sync = 0;
+ mptscsih_dvTask.routine = mptscsih_domainValidation;
+ mptscsih_dvTask.data = (void *) hd;
+
+ SCHEDULE_TASK(&mptscsih_dvTask);
+ } else {
+ spin_unlock_irqrestore(&dvtaskQ_lock, lflags);
+ }
+ hd->ioc->spi_data.forceDv = 0;
+ }
- /* Control: No data direction, set task mgmt bit? */
+ /* Trying to do DV to this target, extend timeout.
+ * Wait to issue intil flag is clear
+ */
+ if (dvStatus & MPT_SCSICFG_DV_PENDING) {
+ mod_timer(&SCpnt->eh_timeout, jiffies + 40 * HZ);
+ issueCmd = 0;
+ }
+#endif
+ }
+ }
- /*
- * As of MPI v0.10 this request can NOT be sent (normally)
- * via FIFOs. So we can't:
- * mpt_put_msg_frame(ScsiTaskCtx, hd->ioc->id, mf);
- * SCSITaskMgmt requests MUST be sent ONLY via
- * Doorbell/handshake now. :-(
- */
- if ((i = mpt_send_handshake_request(ScsiTaskCtx, hd->ioc->id,
- sizeof(SCSITaskMgmt_t), (u32*) mf))
- != 0) {
- printk(KERN_WARNING MYNAM ": WARNING[1] - IOC error (%d) processing TaskMgmt request (mf=%p:sc=%p)\n", i, mf, SCpnt);
- SCpnt->result = DID_SOFT_ERROR << 16;
- spin_lock_irqsave(SCpnt->host->host_lock, flags);
- SCpnt->scsi_done(SCpnt);
- spin_unlock_irqrestore(SCpnt->host->host_lock, flags);
- mpt_free_msg_frame(ScsiTaskCtx, hd->ioc->id, mf);
+ if (issueCmd) {
+ mptscsih_put_msgframe(ScsiDoneCtx, hd->ioc->id, mf);
+ dmfprintk((MYIOC_s_INFO_FMT "Issued SCSI cmd (%p)\n",
+ hd->ioc->name, SCpnt));
} else {
- /* Spin-Wait for TaskMgmt complete!!! */
- while (mpt_scsih_active_taskmgmt_mf != NULL) {
- current->state = TASK_INTERRUPTIBLE;
- schedule_timeout(HZ/4);
+ ddvtprintk((MYIOC_s_INFO_FMT "Pending SCSI cmd (%p)\n",
+ hd->ioc->name, SCpnt));
+ /* Place this command on the pendingQ if possible */
+ spin_lock_irqsave(&hd->freedoneQlock, flags);
+ if (!Q_IS_EMPTY(&hd->freeQ)) {
+ buffer = hd->freeQ.head;
+ Q_DEL_ITEM(buffer);
+
+ /* Save the mf pointer
+ */
+ buffer->argp = (void *)mf;
+
+ /* Add to the pendingQ
+ */
+ Q_ADD_TAIL(&hd->pendingQ.head, buffer, MPT_DONE_Q);
+ spin_unlock_irqrestore(&hd->freedoneQlock, flags);
+ } else {
+ spin_unlock_irqrestore(&hd->freedoneQlock, flags);
+ SCpnt->result = (DID_BUS_BUSY << 16);
+ SCpnt->scsi_done(SCpnt);
}
}
+ } else {
+ mptscsih_freeChainBuffers(hd, my_idx);
+ mpt_free_msg_frame(ScsiDoneCtx, hd->ioc->id, mf);
+ did_errcode = 3;
+ goto did_error;
}
- dslprintk((KERN_INFO MYNAM ": spinlock#6\n"));
- spin_lock_irqsave(&mpt_scsih_taskQ_lock, flags);
- mpt_scsih_taskQ_bh_active = 0;
- spin_unlock_irqrestore(&mpt_scsih_taskQ_lock, flags);
+ return 0;
- return;
-}
+did_error:
+ dprintk((MYIOC_s_WARN_FMT "_qcmd did_errcode=%d (sc=%p)\n",
+ hd->ioc->name, did_errcode, SCpnt));
+ /* Just wish OS to issue a retry */
+ SCpnt->result = (DID_BUS_BUSY << 16);
+ spin_lock_irqsave(&hd->freedoneQlock, flags);
+ if (!Q_IS_EMPTY(&hd->freeQ)) {
+ buffer = hd->freeQ.head;
+ Q_DEL_ITEM(buffer);
+
+ /* Set the Scsi_Cmnd pointer
+ */
+ buffer->argp = (void *)SCpnt;
-#endif /* } */
+ /* Add to the doneQ
+ */
+ Q_ADD_TAIL(&hd->doneQ.head, buffer, MPT_DONE_Q);
+ spin_unlock_irqrestore(&hd->freedoneQlock, flags);
+ } else {
+ spin_unlock_irqrestore(&hd->freedoneQlock, flags);
+ SCpnt->scsi_done(SCpnt);
+ }
+
+ return 0;
+}
/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
-/**
- * mptscsih_taskmgmt_complete - Callback routine, gets registered to
- * Fusion MPT base driver
- * @ioc: Pointer to MPT_ADAPTER structure
- * @mf: Pointer to SCSI task mgmt request frame
- * @r: Pointer to SCSI task mgmt reply frame
- *
- * This routine is called from mptbase.c::mpt_interrupt() at the completion
- * of any SCSI task management request.
- * This routine is registered with the MPT (base) driver at driver
- * load/init time via the mpt_register() API call.
+/*
+ * mptscsih_AddSGE - Add a SGE (plus chain buffers) to the
+ * SCSIIORequest_t Message Frame.
+ * @hd: Pointer to MPT_SCSI_HOST structure
+ * @SCpnt: Pointer to Scsi_Cmnd structure
+ * @pReq: Pointer to SCSIIORequest_t structure
*
- * Returns 1 indicating alloc'd request frame ptr should be freed.
+ * Returns ...
*/
static int
-mptscsih_taskmgmt_complete(MPT_ADAPTER *ioc, MPT_FRAME_HDR *mf, MPT_FRAME_HDR *r)
+mptscsih_AddSGE(MPT_SCSI_HOST *hd, Scsi_Cmnd *SCpnt,
+ SCSIIORequest_t *pReq, int req_idx)
{
- SCSITaskMgmtReply_t *pScsiTmReply;
- SCSITaskMgmt_t *pScsiTmReq;
- u8 tmType;
-#ifndef MPT_SCSI_USE_NEW_EH
- unsigned long flags;
-#endif
+ char *psge;
+ char *chainSge;
+ struct scatterlist *sg;
+ int frm_sz;
+ int sges_left, sg_done;
+ int chain_idx = MPT_HOST_NO_CHAIN;
+ int sgeOffset;
+ int numSgeSlots, numSgeThisFrame;
+ u32 sgflags, sgdir, thisxfer = 0;
+ int chain_dma_off = 0;
+ int newIndex;
+ int ii;
+ dma_addr_t v2;
+
+ sgdir = le32_to_cpu(pReq->Control) & MPI_SCSIIO_CONTROL_DATADIRECTION_MASK;
+ if (sgdir == MPI_SCSIIO_CONTROL_WRITE) {
+ sgdir = MPT_TRANSFER_HOST_TO_IOC;
+ } else {
+ sgdir = MPT_TRANSFER_IOC_TO_HOST;
+ }
- dprintk((KERN_INFO MYNAM ": SCSI TaskMgmt completed mf=%p, r=%p\n",
- mf, r));
+ psge = (char *) &pReq->SGL;
+ frm_sz = hd->ioc->req_sz;
-#ifndef MPT_SCSI_USE_NEW_EH
- dslprintk((KERN_INFO MYNAM ": spinlock#7\n"));
- spin_lock_irqsave(&mpt_scsih_taskQ_lock, flags);
- /* It better be the active one! */
- if (mf != mpt_scsih_active_taskmgmt_mf) {
- printk(KERN_ERR MYNAM ": ERROR! Non-active TaskMgmt (=%p) completed!\n", mf);
- mpt_scsih_active_taskmgmt_mf = NULL;
- spin_unlock_irqrestore(&mpt_scsih_taskQ_lock, flags);
- return 1;
+ /* Map the data portion, if any.
+ * sges_left = 0 if no data transfer.
+ */
+ sges_left = SCpnt->use_sg;
+ if (SCpnt->use_sg) {
+ sges_left = pci_map_sg(hd->ioc->pcidev,
+ (struct scatterlist *) SCpnt->request_buffer,
+ SCpnt->use_sg,
+ scsi_to_pci_dma_dir(SCpnt->sc_data_direction));
+ } else if (SCpnt->request_bufflen) {
+ dma_addr_t buf_dma_addr;
+ scPrivate *my_priv;
+
+ buf_dma_addr = pci_map_single(hd->ioc->pcidev,
+ SCpnt->request_buffer,
+ SCpnt->request_bufflen,
+ scsi_to_pci_dma_dir(SCpnt->sc_data_direction));
+
+ /* We hide it here for later unmap. */
+ my_priv = (scPrivate *) &SCpnt->SCp;
+ my_priv->p1 = (void *)(ulong) buf_dma_addr;
+
+ dsgprintk((MYIOC_s_INFO_FMT "SG: non-SG for %p, len=%d\n",
+ hd->ioc->name, SCpnt, SCpnt->request_bufflen));
+
+ mpt_add_sge((char *) &pReq->SGL,
+ 0xD1000000|MPT_SGE_FLAGS_ADDRESSING|sgdir|SCpnt->request_bufflen,
+ buf_dma_addr);
+
+ return SUCCESS;
}
-#ifdef MPT_DEBUG
- if ((mf == NULL) ||
- (mf >= MPT_INDEX_2_MFPTR(ioc, ioc->req_depth))) {
- printk(KERN_ERR MYNAM ": ERROR! NULL or BAD TaskMgmt ptr (=%p)!\n", mf);
- mpt_scsih_active_taskmgmt_mf = NULL;
- spin_unlock_irqrestore(&mpt_scsih_taskQ_lock, flags);
- return 1;
+ /* Handle the SG case.
+ */
+ sg = (struct scatterlist *) SCpnt->request_buffer;
+ sg_done = 0;
+ sgeOffset = sizeof(SCSIIORequest_t) - sizeof(SGE_IO_UNION);
+ chainSge = NULL;
+
+ /* Prior to entering this loop - the following must be set
+ * current MF: sgeOffset (bytes)
+ * chainSge (Null if original MF is not a chain buffer)
+ * sg_done (num SGE done for this MF)
+ */
+
+nextSGEset:
+ numSgeSlots = ((frm_sz - sgeOffset) / (sizeof(u32) + sizeof(dma_addr_t)) );
+ numSgeThisFrame = (sges_left < numSgeSlots) ? sges_left : numSgeSlots;
+
+ sgflags = MPT_SGE_FLAGS_SIMPLE_ELEMENT | MPT_SGE_FLAGS_ADDRESSING | sgdir;
+
+ /* Get first (num - 1) SG elements
+ * Skip any SG entries with a length of 0
+ * NOTE: at finish, sg and psge pointed to NEXT data/location positions
+ */
+ for (ii=0; ii < (numSgeThisFrame-1); ii++) {
+ thisxfer = sg_dma_len(sg);
+ if (thisxfer == 0) {
+ sg ++; /* Get next SG element from the OS */
+ sg_done++;
+ continue;
+ }
+
+ v2 = sg_dma_address(sg);
+ mpt_add_sge(psge, sgflags | thisxfer, v2);
+
+ sg++; /* Get next SG element from the OS */
+ psge += (sizeof(u32) + sizeof(dma_addr_t));
+ sgeOffset += (sizeof(u32) + sizeof(dma_addr_t));
+ sg_done++;
}
-#endif
- spin_unlock_irqrestore(&mpt_scsih_taskQ_lock, flags);
-#endif
- if (r != NULL) {
- pScsiTmReply = (SCSITaskMgmtReply_t*)r;
- pScsiTmReq = (SCSITaskMgmt_t*)mf;
+ if (numSgeThisFrame == sges_left) {
+ /* Add last element, end of buffer and end of list flags.
+ */
+ sgflags |= MPT_SGE_FLAGS_LAST_ELEMENT |
+ MPT_SGE_FLAGS_END_OF_BUFFER |
+ MPT_SGE_FLAGS_END_OF_LIST;
- /* Figure out if this was ABORT_TASK, TARGET_RESET, or BUS_RESET! */
- tmType = pScsiTmReq->TaskType;
+ /* Add last SGE and set termination flags.
+ * Note: Last SGE may have a length of 0 - which should be ok.
+ */
+ thisxfer = sg_dma_len(sg);
- dprintk((KERN_INFO MYNAM ": TaskType = %d\n", tmType));
- dprintk((KERN_INFO MYNAM ": TerminationCount = %d\n",
- le32_to_cpu(pScsiTmReply->TerminationCount)));
+ v2 = sg_dma_address(sg);
+ mpt_add_sge(psge, sgflags | thisxfer, v2);
+ /*
+ sg++;
+ psge += (sizeof(u32) + sizeof(dma_addr_t));
+ */
+ sgeOffset += (sizeof(u32) + sizeof(dma_addr_t));
+ sg_done++;
+
+ if (chainSge) {
+ /* The current buffer is a chain buffer,
+ * but there is not another one.
+ * Update the chain element
+ * Offset and Length fields.
+ */
+ mpt_add_chain((char *)chainSge, 0, sgeOffset, hd->ChainBufferDMA + chain_dma_off);
+ } else {
+ /* The current buffer is the original MF
+ * and there is no Chain buffer.
+ */
+ pReq->ChainOffset = 0;
+ }
+ } else {
+ /* At least one chain buffer is needed.
+ * Complete the first MF
+ * - last SGE element, set the LastElement bit
+ * - set ChainOffset (words) for orig MF
+ * (OR finish previous MF chain buffer)
+ * - update MFStructPtr ChainIndex
+ * - Populate chain element
+ * Also
+ * Loop until done.
+ */
- /* Error? (anything non-zero?) */
- if (*(u32 *)&pScsiTmReply->Reserved2[0]) {
- dprintk((KERN_INFO MYNAM ": SCSI TaskMgmt (%d) - Oops!\n", tmType));
- dprintk((KERN_INFO MYNAM ": IOCStatus = %04xh\n",
- le16_to_cpu(pScsiTmReply->IOCStatus)));
- dprintk((KERN_INFO MYNAM ": IOCLogInfo = %08xh\n",
- le32_to_cpu(pScsiTmReply->IOCLogInfo)));
+ dsgprintk((MYIOC_s_INFO_FMT "SG: Chain Required! sg done %d\n",
+ hd->ioc->name, sg_done));
+
+ /* Set LAST_ELEMENT flag for last non-chain element
+ * in the buffer. Since psge points at the NEXT
+ * SGE element, go back one SGE element, update the flags
+ * and reset the pointer. (Note: sgflags & thisxfer are already
+ * set properly).
+ */
+ if (sg_done) {
+ u32 *ptmp = (u32 *) (psge - (sizeof(u32) + sizeof(dma_addr_t)));
+ sgflags = le32_to_cpu(*ptmp);
+ sgflags |= MPT_SGE_FLAGS_LAST_ELEMENT;
+ *ptmp = cpu_to_le32(sgflags);
+ }
+
+ if (chainSge) {
+ /* The current buffer is a chain buffer.
+ * chainSge points to the previous Chain Element.
+ * Update its chain element Offset and Length (must
+ * include chain element size) fields.
+ * Old chain element is now complete.
+ */
+ u8 nextChain = (u8) (sgeOffset >> 2);
+ sgeOffset += (sizeof(u32) + sizeof(dma_addr_t));
+ mpt_add_chain((char *)chainSge, nextChain, sgeOffset, hd->ChainBufferDMA + chain_dma_off);
} else {
- dprintk((KERN_INFO MYNAM ": SCSI TaskMgmt (%d) SUCCESS!\n", tmType));
+ /* The original MF buffer requires a chain buffer -
+ * set the offset.
+ * Last element in this MF is a chain element.
+ */
+ pReq->ChainOffset = (u8) (sgeOffset >> 2);
}
- }
-#ifndef MPT_SCSI_USE_NEW_EH
- /*
- * Signal to _bh thread that we finished.
- */
- dslprintk((KERN_INFO MYNAM ": spinlock#8\n"));
- spin_lock_irqsave(&mpt_scsih_taskQ_lock, flags);
- mpt_scsih_active_taskmgmt_mf = NULL;
- spin_unlock_irqrestore(&mpt_scsih_taskQ_lock, flags);
-#endif
+ sges_left -= sg_done;
- return 1;
-}
-/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
-/*
- * This is anyones guess quite frankly.
- */
+ /* NOTE: psge points to the beginning of the chain element
+ * in current buffer. Get a chain buffer.
+ */
+ if ((mptscsih_getFreeChainBuffer(hd, &newIndex)) == FAILED)
+ return FAILED;
-int
-mptscsih_bios_param(Disk * disk, kdev_t dev, int *ip)
-{
- int size;
+ /* Update the tracking arrays.
+ * If chainSge == NULL, update ReqToChain, else ChainToChain
+ */
+ if (chainSge) {
+ hd->ChainToChain[chain_idx] = newIndex;
+ } else {
+ hd->ReqToChain[req_idx] = newIndex;
+ }
+ chain_idx = newIndex;
+ chain_dma_off = hd->ioc->req_sz * chain_idx;
- size = disk->capacity;
- ip[0] = 64; /* heads */
- ip[1] = 32; /* sectors */
- if ((ip[2] = size >> 11) > 1024) { /* cylinders, test for big disk */
- ip[0] = 255; /* heads */
- ip[1] = 63; /* sectors */
- ip[2] = size / (255 * 63); /* cylinders */
+ /* Populate the chainSGE for the current buffer.
+ * - Set chain buffer pointer to psge and fill
+ * out the Address and Flags fields.
+ */
+ chainSge = (char *) psge;
+ dsgprintk((KERN_INFO " Current buff @ %p (index 0x%x)",
+ psge, req_idx));
+
+ /* Start the SGE for the next buffer
+ */
+ psge = (char *) (hd->ChainBuffer + chain_dma_off);
+ sgeOffset = 0;
+ sg_done = 0;
+
+ dsgprintk((KERN_INFO " Chain buff @ %p (index 0x%x)\n",
+ psge, chain_idx));
+
+ /* Start the SGE for the next buffer
+ */
+
+ goto nextSGEset;
}
- return 0;
+
+ return SUCCESS;
}
/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
/*
- * Private routines...
- */
-/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
-/* 19991030 -sralston
- * Return absolute SCSI data direction:
- * 1 = _DATA_OUT
- * 0 = _DIR_NONE
- * -1 = _DATA_IN
+ * mptscsih_getFreeChainBuffes - Function to get a free chain
+ * from the MPT_SCSI_HOST FreeChainQ.
+ * @hd: Pointer to the MPT_SCSI_HOST instance
+ * @req_idx: Index of the SCSI IO request frame. (output)
+ *
+ * return SUCCESS or FAILED
*/
static int
-mptscsih_io_direction(Scsi_Cmnd *cmd)
+mptscsih_getFreeChainBuffer(MPT_SCSI_HOST *hd, int *retIndex)
{
- switch (cmd->cmnd[0]) {
- /* _DATA_OUT commands */
- case WRITE_6: case WRITE_10: case WRITE_12:
- case WRITE_LONG: case WRITE_SAME: case WRITE_BUFFER:
- case WRITE_VERIFY: case WRITE_VERIFY_12:
- case COMPARE: case COPY: case COPY_VERIFY:
- case SEARCH_EQUAL: case SEARCH_HIGH: case SEARCH_LOW:
- case SEARCH_EQUAL_12: case SEARCH_HIGH_12: case SEARCH_LOW_12:
- case MODE_SELECT: case MODE_SELECT_10: case LOG_SELECT:
- case SEND_DIAGNOSTIC: case CHANGE_DEFINITION: case UPDATE_BLOCK:
- case SET_WINDOW: case MEDIUM_SCAN: case SEND_VOLUME_TAG:
- case REASSIGN_BLOCKS:
- case PERSISTENT_RESERVE_OUT:
- case 0xea:
- return 1;
+ MPT_FRAME_HDR *chainBuf = NULL;
+ unsigned long flags;
+ int rc = FAILED;
+ int chain_idx = MPT_HOST_NO_CHAIN;
- /* No data transfer commands */
- case SEEK_6: case SEEK_10:
- case RESERVE: case RELEASE:
- case TEST_UNIT_READY:
- case START_STOP:
- case ALLOW_MEDIUM_REMOVAL:
- return 0;
+ //spin_lock_irqsave(&hd->FreeChainQlock, flags);
+ spin_lock_irqsave(&hd->ioc->FreeQlock, flags);
+ if (!Q_IS_EMPTY(&hd->FreeChainQ)) {
- /* Conditional data transfer commands */
- case FORMAT_UNIT:
- if (cmd->cmnd[1] & 0x10) /* FmtData (data out phase)? */
- return 1;
- else
- return 0;
+ int offset;
- case VERIFY:
- if (cmd->cmnd[1] & 0x02) /* VERIFY:BYTCHK (data out phase)? */
- return 1;
- else
- return 0;
+ chainBuf = hd->FreeChainQ.head;
+ Q_DEL_ITEM(&chainBuf->u.frame.linkage);
+ offset = (u8 *)chainBuf - (u8 *)hd->ChainBuffer;
+ chain_idx = offset / hd->ioc->req_sz;
+ rc = SUCCESS;
+ }
+ //spin_unlock_irqrestore(&hd->FreeChainQlock, flags);
+ spin_unlock_irqrestore(&hd->ioc->FreeQlock, flags);
- case RESERVE_10:
- if (cmd->cmnd[1] & 0x03) /* RESERSE:{LongID|Extent} (data out phase)? */
- return 1;
- else
- return 0;
-#if 0
- case REZERO_UNIT: /* (or REWIND) */
- case SPACE:
- case ERASE: case ERASE_10:
- case SYNCHRONIZE_CACHE:
- case LOCK_UNLOCK_CACHE:
-#endif
+ *retIndex = chain_idx;
- /* Must be data _IN! */
- default:
- return -1;
- }
+ dsgprintk((MYIOC_s_INFO_FMT "getFreeChainBuffer (index %d), got buf=%p\n",
+ hd->ioc->name, *retIndex, chainBuf));
+
+ return rc;
}
/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
+/*
+ * mptscsih_freeChainBuffers - Function to free chain buffers associated
+ * with a SCSI IO request
+ * @hd: Pointer to the MPT_SCSI_HOST instance
+ * @req_idx: Index of the SCSI IO request frame.
+ *
+ * Called if SG chain buffer allocation fails and mptscsih callbacks.
+ * No return.
+ */
static void
-copy_sense_data(Scsi_Cmnd *sc, MPT_SCSI_HOST *hd, MPT_FRAME_HDR *mf, SCSIIOReply_t *pScsiReply)
+mptscsih_freeChainBuffers(MPT_SCSI_HOST *hd, int req_idx)
{
- MPT_SCSI_DEV *mpt_sdev = NULL;
- u32 sense_count = le32_to_cpu(pScsiReply->SenseCount);
- char devFoo[32];
- IO_Info_t thisIo;
+ MPT_FRAME_HDR *chain = NULL;
+ unsigned long flags;
+ int chain_idx;
+ int next;
- if (sc && sc->device)
- mpt_sdev = (MPT_SCSI_DEV*) sc->device->hostdata;
+ /* Get the first chain index and reset
+ * tracker state.
+ */
+ chain_idx = hd->ReqToChain[req_idx];
+ hd->ReqToChain[req_idx] = MPT_HOST_NO_CHAIN;
- if (sense_count) {
- u8 *sense_data;
- int req_index;
+ while (chain_idx != MPT_HOST_NO_CHAIN) {
- /* Copy the sense received into the scsi command block. */
- req_index = le16_to_cpu(mf->u.frame.hwhdr.msgctxu.fld.req_idx);
- sense_data = ((u8 *)hd->ioc->sense_buf_pool + (req_index * 256));
- memcpy(sc->sense_buffer, sense_data, SNS_LEN(sc));
- /* Cache SenseData for this SCSI device! */
- if (mpt_sdev) {
- memcpy(mpt_sdev->CachedSense.data, sense_data, sense_count);
- mpt_sdev->sense_sz = sense_count;
- }
- } else {
- dprintk((KERN_INFO MYNAM ": Hmmm... SenseData len=0! (?)\n"));
- }
+ /* Save the next chain buffer index */
+ next = hd->ChainToChain[chain_idx];
+ /* Free this chain buffer and reset
+ * tracker
+ */
+ hd->ChainToChain[chain_idx] = MPT_HOST_NO_CHAIN;
- thisIo.cdbPtr = sc->cmnd;
- thisIo.sensePtr = sc->sense_buffer;
- thisIo.SCSIStatus = pScsiReply->SCSIStatus;
- thisIo.DoDisplay = 1;
- sprintf(devFoo, "ioc%d,scsi%d:%d", hd->ioc->id, sc->target, sc->lun);
- thisIo.DevIDStr = devFoo;
-/* fubar */
- thisIo.dataPtr = NULL;
- thisIo.inqPtr = NULL;
- if (sc->device) {
- thisIo.inqPtr = sc->device->vendor-8; /* FIXME!!! */
- }
- (void) mpt_ScsiHost_ErrorReport(&thisIo);
+ chain = (MPT_FRAME_HDR *) (hd->ChainBuffer
+ + (chain_idx * hd->ioc->req_sz));
+ spin_lock_irqsave(&hd->ioc->FreeQlock, flags);
+ Q_ADD_TAIL(&hd->FreeChainQ.head,
+ &chain->u.frame.linkage, MPT_FRAME_HDR);
+ spin_unlock_irqrestore(&hd->ioc->FreeQlock, flags);
+ dmfprintk((MYIOC_s_INFO_FMT "FreeChainBuffers (index %d)\n",
+ hd->ioc->name, chain_idx));
+
+ /* handle next */
+ chain_idx = next;
+ }
return;
}
/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
-static u32
-SCPNT_TO_MSGCTX(Scsi_Cmnd *sc)
+/*
+ * Reset Handling
+ */
+
+/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
+/*
+ * mptscsih_TMHandler - Generic handler for SCSI Task Management.
+ * Fall through to mpt_HardResetHandler if: not operational, too many
+ * failed TM requests or handshake failure.
+ *
+ * @ioc: Pointer to MPT_ADAPTER structure
+ * @type: Task Management type
+ * @target: Logical Target ID for reset (if appropriate)
+ * @lun: Logical Unit for reset (if appropriate)
+ * @ctx2abort: Context for the task to be aborted (if appropriate)
+ * @sleepFlag: If set, use udelay instead of schedule in handshake code.
+ *
+ * Remark: Currently invoked from a non-interrupt thread (_bh).
+ *
+ * Remark: With old EH code, at most 1 SCSI TaskMgmt function per IOC
+ * will be active.
+ *
+ * Returns 0 for SUCCESS or -1 if FAILED.
+ */
+static int
+mptscsih_TMHandler(MPT_SCSI_HOST *hd, u8 type, u8 target, u8 lun, int ctx2abort, int sleepFlag)
{
- MPT_SCSI_HOST *hd;
- MPT_FRAME_HDR *mf;
- int i;
+ MPT_ADAPTER *ioc = NULL;
+ int rc = -1;
+ int doTask = 1;
+ u32 ioc_raw_state;
+ unsigned long flags;
- hd = (MPT_SCSI_HOST *) sc->host->hostdata;
+ /* If FW is being reloaded currently, return success to
+ * the calling function.
+ */
+ if (hd == NULL)
+ return 0;
- for (i = 0; i < hd->ioc->req_depth; i++) {
- if (hd->ScsiLookup[i] == sc) {
- mf = MPT_INDEX_2_MFPTR(hd->ioc, i);
- return mf->u.frame.hwhdr.msgctxu.MsgContext;
- }
+ ioc = hd->ioc;
+ dtmprintk((MYIOC_s_INFO_FMT "TMHandler Entered!\n", ioc->name));
+
+ if (ioc == NULL) {
+ printk(KERN_ERR MYNAM " TMHandler" " NULL ioc!\n");
+ return 0;
}
- return -1;
-}
+ // SJR - CHECKME - Can we avoid this here?
+ // (mpt_HardResetHandler has this check...)
+ spin_lock_irqsave(&ioc->diagLock, flags);
+ if ((ioc->diagPending) || (ioc->alt_ioc && ioc->alt_ioc->diagPending)) {
+ spin_unlock_irqrestore(&ioc->diagLock, flags);
+ return 0;
+ }
+ spin_unlock_irqrestore(&ioc->diagLock, flags);
-/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
+ /* Do not do a Task Management if there are
+ * too many failed TMs on this adapter.
+ */
+ if (hd->numTMrequests > MPT_HOST_TOO_MANY_TM)
+ doTask = 0;
-/* see mptscsih.h */
+ /* Is operational?
+ */
+ ioc_raw_state = mpt_GetIocState(hd->ioc, 0);
-#ifdef MPT_SCSIHOST_NEED_ENTRY_EXIT_HOOKUPS
- static Scsi_Host_Template driver_template = MPT_SCSIHOST;
-# include "../../scsi/scsi_module.c"
+#ifdef MPT_DEBUG_RESET
+ if ((ioc_raw_state & MPI_IOC_STATE_MASK) != MPI_IOC_STATE_OPERATIONAL) {
+ printk(MYIOC_s_WARN_FMT
+ "TM Handler: IOC Not operational! state 0x%x Calling HardResetHandler\n",
+ hd->ioc->name, ioc_raw_state);
+ }
#endif
+ if (doTask && ((ioc_raw_state & MPI_IOC_STATE_MASK) == MPI_IOC_STATE_OPERATIONAL)
+ && !(ioc_raw_state & MPI_DOORBELL_ACTIVE)) {
-/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
-static int
-mptscsih_ioc_reset(MPT_ADAPTER *ioc, int reset_phase)
-{
- dprintk((KERN_INFO MYNAM ": IOC %s_reset routed to SCSI host driver!\n",
- reset_phase==MPT_IOC_PRE_RESET ? "pre" : "post"));
+ /* Isse the Task Mgmt request.
+ */
+ rc = mptscsih_IssueTaskMgmt(hd, type, target, lun, ctx2abort, sleepFlag);
+ if (rc) {
+#ifdef MPT_SCSI_USE_NEW_EH
+ hd->tmState = TM_STATE_ERROR;
+#endif
+ printk(MYIOC_s_INFO_FMT "Issue of TaskMgmt failed!\n", hd->ioc->name);
+ } else {
+ printk(MYIOC_s_INFO_FMT "Issue of TaskMgmt Successful!\n", hd->ioc->name);
+ }
+ }
+#ifdef DROP_TEST
+ numTMrequested++;
+ if (numTMrequested > 5) {
+ rc = 0; /* set to 1 to force a hard reset */
+ numTMrequested = 0;
+ }
+#endif
- if (reset_phase == MPT_IOC_PRE_RESET) {
- /* FIXME! Do pre-reset cleanup */
- } else {
- /* FIXME! Do post-reset cleanup */
+ if (rc) {
+ dtmprintk((MYIOC_s_INFO_FMT "Falling through to HardReset! \n",
+ hd->ioc->name));
+ rc = mpt_HardResetHandler(hd->ioc, sleepFlag);
}
- return 1; /* currently means nothing really */
+ dtmprintk((MYIOC_s_INFO_FMT "TMHandler rc = %d!\n", hd->ioc->name, rc));
+#ifndef MPT_SCSI_USE_NEW_EH
+ dtmprintk((MYIOC_s_INFO_FMT "TMHandler: _bh_handler state (%d) taskQ count (%d)\n",
+ ioc->name, mytaskQ_bh_active, hd->taskQcnt));
+#endif
+
+ return rc;
}
+
/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
+/*
+ * mptscsih_IssueTaskMgmt - Generic send Task Management function.
+ * @hd: Pointer to MPT_SCSI_HOST structure
+ * @type: Task Management type
+ * @target: Logical Target ID for reset (if appropriate)
+ * @lun: Logical Unit for reset (if appropriate)
+ * @ctx2abort: Context for the task to be aborted (if appropriate)
+ * @sleepFlag: If set, use udelay instead of schedule in handshake code.
+ *
+ * Remark: _HardResetHandler can be invoked from an interrupt thread (timer)
+ * or a non-interrupt thread. In the former, must not call schedule().
+ *
+ * Not all fields are meaningfull for all task types.
+ *
+ * Returns 0 for SUCCESS, -999 for "no msg frames",
+ * else other non-zero value returned.
+ */
static int
-mptscsih_event_process(MPT_ADAPTER *ioc, EventNotificationReply_t *pEvReply)
+mptscsih_IssueTaskMgmt(MPT_SCSI_HOST *hd, u8 type, u8 target, u8 lun, int ctx2abort, int sleepFlag)
{
- u8 event = le32_to_cpu(pEvReply->Event) & 0xFF;
+ MPT_FRAME_HDR *mf;
+ SCSITaskMgmt_t *pScsiTm;
+ int ii;
+ int retval;
- dprintk((KERN_INFO MYNAM ": MPT event (=%02Xh) routed to SCSI host driver!\n", event));
+ /* Return Fail to calling function if no message frames available.
+ */
+ if ((mf = mpt_get_msg_frame(ScsiTaskCtx, hd->ioc->id)) == NULL) {
+ dtmprintk((MYIOC_s_WARN_FMT "IssueTaskMgmt, no msg frames!!\n",
+ hd->ioc->name));
+ //return FAILED;
+ return -999;
+ }
+ dtmprintk((MYIOC_s_INFO_FMT "IssueTaskMgmt request @ %p\n",
+ hd->ioc->name, mf));
- switch (event) {
- case MPI_EVENT_UNIT_ATTENTION: /* 03 */
- /* FIXME! */
- break;
- case MPI_EVENT_IOC_BUS_RESET: /* 04 */
- /* FIXME! */
- break;
- case MPI_EVENT_EXT_BUS_RESET: /* 05 */
- /* FIXME! */
- break;
- case MPI_EVENT_LOGOUT: /* 09 */
- /* FIXME! */
- break;
+ /* Format the Request
+ */
+ pScsiTm = (SCSITaskMgmt_t *) mf;
+ pScsiTm->TargetID = target;
+ pScsiTm->Bus = hd->port;
+ pScsiTm->ChainOffset = 0;
+ pScsiTm->Function = MPI_FUNCTION_SCSI_TASK_MGMT;
- /*
- * CHECKME! Don't think we need to do
- * anything for these, but...
+ pScsiTm->Reserved = 0;
+ pScsiTm->TaskType = type;
+ pScsiTm->Reserved1 = 0;
+ pScsiTm->MsgFlags = (type == MPI_SCSITASKMGMT_TASKTYPE_RESET_BUS)
+ ? MPI_SCSITASKMGMT_MSGFLAGS_LIPRESET_RESET_OPTION : 0;
+
+ for (ii= 0; ii < 8; ii++) {
+ pScsiTm->LUN[ii] = 0;
+ }
+ pScsiTm->LUN[1] = lun;
+
+ for (ii=0; ii < 7; ii++)
+ pScsiTm->Reserved2[ii] = 0;
+
+ pScsiTm->TaskMsgContext = ctx2abort;
+ dtmprintk((MYIOC_s_INFO_FMT "IssueTaskMgmt, ctx2abort (0x%08x), type (%d)\n",
+ hd->ioc->name, ctx2abort, type));
+
+ /* MPI v0.10 requires SCSITaskMgmt requests be sent via Doorbell/handshake
+ mpt_put_msg_frame(hd->ioc->id, mf);
+ * Save the MF pointer in case the request times out.
+ */
+ hd->tmPtr = mf;
+ hd->numTMrequests++;
+ hd->TMtimer.expires = jiffies + HZ*20; /* 20 seconds */
+ add_timer(&hd->TMtimer);
+
+ if ((retval = mpt_send_handshake_request(ScsiTaskCtx, hd->ioc->id,
+ sizeof(SCSITaskMgmt_t), (u32*)pScsiTm, sleepFlag))
+ != 0) {
+ dtmprintk((MYIOC_s_WARN_FMT "_send_handshake FAILED!"
+ " (hd %p, ioc %p, mf %p) \n", hd->ioc->name, hd, hd->ioc, mf));
+ hd->numTMrequests--;
+ hd->tmPtr = NULL;
+ del_timer(&hd->TMtimer);
+ mpt_free_msg_frame(ScsiTaskCtx, hd->ioc->id, mf);
+ }
+
+ return retval;
+}
+
+#ifdef MPT_SCSI_USE_NEW_EH /* { */
+/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
+/**
+ * mptscsih_abort - Abort linux Scsi_Cmnd routine, new_eh variant
+ * @SCpnt: Pointer to Scsi_Cmnd structure, IO to be aborted
+ *
+ * (linux Scsi_Host_Template.eh_abort_handler routine)
+ *
+ * Returns SUCCESS or FAILED.
+ */
+int
+mptscsih_abort(Scsi_Cmnd * SCpnt)
+{
+ MPT_SCSI_HOST *hd;
+ MPT_FRAME_HDR *mf;
+ unsigned long flags;
+ u32 ctx2abort;
+ int scpnt_idx;
+
+ /* If we can't locate our host adapter structure, return FAILED status.
+ */
+ if ((hd = (MPT_SCSI_HOST *) SCpnt->host->hostdata) == NULL) {
+ SCpnt->result = DID_RESET << 16;
+ SCpnt->scsi_done(SCpnt);
+ nehprintk((KERN_WARNING MYNAM ": mptscsih_abort: "
+ "Can't locate host! (sc=%p)\n",
+ SCpnt));
+ return FAILED;
+ }
+
+ printk(KERN_WARNING MYNAM ": %s: >> Attempting task abort! (sc=%p)\n",
+ hd->ioc->name, SCpnt);
+ printk(KERN_WARNING MYNAM ": %s: IOs outstanding = %d\n",
+ hd->ioc->name, atomic_read(&queue_depth));
+
+ /* Find this command
+ */
+ if ((scpnt_idx = SCPNT_TO_LOOKUP_IDX(SCpnt)) < 0) {
+ /* Cmd not found in ScsiLookup. If found in
+ * doneQ, delete from Q. Do OS callback.
*/
- case MPI_EVENT_RESCAN: /* 06 */
- case MPI_EVENT_LINK_STATUS_CHANGE: /* 07 */
- case MPI_EVENT_LOOP_STATE_CHANGE: /* 08 */
- /*
- * CHECKME! Falling thru...
+ search_doneQ_for_cmd(hd, SCpnt);
+
+ SCpnt->result = DID_RESET << 16;
+ SCpnt->scsi_done(SCpnt);
+ nehprintk((KERN_WARNING MYNAM ": %s: mptscsih_abort: "
+ "Command not in the active list! (sc=%p)\n",
+ hd->ioc->name, SCpnt));
+ return SUCCESS;
+ }
+
+ /* Wait a fixed amount of time for the TM pending flag to be cleared.
+ * If we time out, then we return a FAILED status to the caller. This
+ * call to mptscsih_tm_pending_wait() will set the pending flag if we are
+ * successful.
+ */
+ if (mptscsih_tm_pending_wait(hd) == FAILED){
+ nehprintk((KERN_WARNING MYNAM ": %s: mptscsih_abort: "
+ "Timed out waiting for previous TM to complete! "
+ "(sc = %p)\n",
+ hd->ioc->name, SCpnt));
+ return FAILED;
+ }
+
+ /* If this command is pended, then timeout/hang occurred
+ * during DV. Post command and flush pending Q
+ * and then following up with the reset request.
+ */
+ if ((mf = mptscsih_search_pendingQ(hd, scpnt_idx)) != NULL) {
+ mptscsih_put_msgframe(ScsiDoneCtx, hd->ioc->id, mf);
+ post_pendingQ_commands(hd);
+ nehprintk((KERN_WARNING MYNAM ": %s: mptscsih_abort: "
+ "Found command in pending queue! (sc=%p)\n",
+ hd->ioc->name, SCpnt));
+ }
+
+ /* Most important! Set TaskMsgContext to SCpnt's MsgContext!
+ * (the IO to be ABORT'd)
+ *
+ * NOTE: Since we do not byteswap MsgContext, we do not
+ * swap it here either. It is an opaque cookie to
+ * the controller, so it does not matter. -DaveM
+ */
+ mf = MPT_INDEX_2_MFPTR(hd->ioc, scpnt_idx);
+ ctx2abort = mf->u.frame.hwhdr.msgctxu.MsgContext;
+
+ hd->abortSCpnt = SCpnt;
+
+ if (mptscsih_TMHandler(hd, MPI_SCSITASKMGMT_TASKTYPE_ABORT_TASK,
+ SCpnt->target, SCpnt->lun, ctx2abort, CAN_SLEEP)
+ < 0
+ || hd->tmState == TM_STATE_ERROR) {
+
+ /* The TM request failed and the subsequent FW-reload failed!
+ * Fatal error case.
*/
+ printk(MYIOC_s_WARN_FMT "Error issuing abort task! (sc=%p)\n",
+ hd->ioc->name, SCpnt);
- case MPI_EVENT_NONE: /* 00 */
- case MPI_EVENT_LOG_DATA: /* 01 */
- case MPI_EVENT_STATE_CHANGE: /* 02 */
- case MPI_EVENT_EVENT_CHANGE: /* 0A */
- default:
- dprintk((KERN_INFO MYNAM ": Ignoring event (=%02Xh)\n", event));
- break;
+ /* If command not found, do not do callback,
+ * just return failed. CHECKME
+ */
+ if (hd->ScsiLookup[scpnt_idx] != NULL) {
+ SCpnt->result = STS_BUSY;
+ SCpnt->scsi_done(SCpnt);
+ }
+
+ /* We must clear our pending flag before clearing our state.
+ */
+ hd->tmPending = 0;
+ hd->tmState = TM_STATE_NONE;
+
+ return FAILED;
}
- return 1; /* currently means nothing really */
+ /* Our task management request will either complete or time out. So we
+ * spin until tmPending is != 1. If tmState is set to TM_STATE_ERROR,
+ * we encountered an error executing the task management request.
+ */
+ while (hd->tmPending == 1){
+ set_current_state(TASK_INTERRUPTIBLE);
+ schedule_timeout(HZ/4);
+ }
+ spin_lock_irqsave(&hd->ioc->FreeQlock, flags);
+ if (hd->tmState == TM_STATE_ERROR){
+ hd->tmState = TM_STATE_NONE;
+ spin_unlock_irqrestore(&hd->ioc->FreeQlock, flags);
+ nehprintk((KERN_WARNING MYNAM ": %s: mptscsih_abort: "
+ "TM timeout error! (sc=%p)\n",
+ hd->ioc->name,
+ SCpnt));
+ return FAILED;
+ }
+ hd->tmState = TM_STATE_NONE;
+ spin_unlock_irqrestore(&hd->ioc->FreeQlock, flags);
+
+ nehprintk((KERN_WARNING MYNAM ": %s: mptscsih_abort: "
+ "Abort was successful! (sc=%p)\n",
+ hd->ioc->name,
+ SCpnt));
+
+ return SUCCESS;
}
-#if 0 /* { */
/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
-/*
- * scsiherr.c - Fusion MPT SCSI Host driver error handling/reporting.
+/**
+ * mptscsih_dev_reset - Perform a SCSI TARGET_RESET! new_eh variant
+ * @SCpnt: Pointer to Scsi_Cmnd structure, IO which reset is due to
*
- * drivers/message/fusion/scsiherr.c
+ * (linux Scsi_Host_Template.eh_dev_reset_handler routine)
+ *
+ * Returns SUCCESS or FAILED.
*/
+int
+mptscsih_dev_reset(Scsi_Cmnd * SCpnt)
+{
+ MPT_SCSI_HOST *hd;
+ unsigned long flags;
-//extern const char **mpt_ScsiOpcodesPtr; /* needed by mptscsih.c */
-//extern ASCQ_Table_t *mpt_ASCQ_TablePtr;
-//extern int mpt_ASCQ_TableSz;
+ /* If we can't locate our host adapter structure, return FAILED status.
+ */
+ if ((hd = (MPT_SCSI_HOST *) SCpnt->host->hostdata) == NULL){
+ nehprintk((KERN_WARNING MYNAM ": mptscsih_dev_reset: "
+ "Can't locate host! (sc=%p)\n",
+ SCpnt));
+ return FAILED;
+ }
-/* Lie! */
-#define MYNAM "mptscsih"
+ printk(KERN_WARNING MYNAM ": %s: >> Attempting target reset! (sc=%p)\n",
+ hd->ioc->name, SCpnt);
+ printk(KERN_WARNING MYNAM ": %s: IOs outstanding = %d\n",
+ hd->ioc->name, atomic_read(&queue_depth));
-#endif /* } */
+ /* Wait a fixed amount of time for the TM pending flag to be cleared.
+ * If we time out, then we return a FAILED status to the caller. This
+ * call to mptscsih_tm_pending_wait() will set the pending flag if we are
+ * successful.
+ */
+ if (mptscsih_tm_pending_wait(hd) == FAILED) {
+ nehprintk((KERN_WARNING MYNAM ": %s: mptscsih_dev_reset: "
+ "Timed out waiting for previous TM to complete! "
+ "(sc = %p)\n",
+ hd->ioc->name, SCpnt));
+ return FAILED;
+ }
+
+ if (mptscsih_TMHandler(hd, MPI_SCSITASKMGMT_TASKTYPE_TARGET_RESET,
+ SCpnt->target, 0, 0, CAN_SLEEP)
+ < 0){
+ /* The TM request failed and the subsequent FW-reload failed!
+ * Fatal error case.
+ */
+ printk(MYIOC_s_WARN_FMT "Error processing TaskMgmt request (sc=%p)\n",
+ hd->ioc->name, SCpnt);
+ hd->tmPending = 0;
+ hd->tmState = TM_STATE_NONE;
+ return FAILED;
+ }
+
+ /* Our task management request will either complete or time out. So we
+ * spin until tmPending is != 1. If tmState is set to TM_STATE_ERROR,
+ * we encountered an error executing the task management request.
+ */
+ while (hd->tmPending == 1){
+ set_current_state(TASK_INTERRUPTIBLE);
+ schedule_timeout(HZ/4);
+ }
+ spin_lock_irqsave(&hd->ioc->FreeQlock, flags);
+ if (hd->tmState == TM_STATE_ERROR){
+ hd->tmState = TM_STATE_NONE;
+ spin_unlock_irqrestore(&hd->ioc->FreeQlock, flags);
+ nehprintk((KERN_WARNING MYNAM ": %s: mptscsih_dev_reset: "
+ "TM timeout error! (sc=%p)\n",
+ hd->ioc->name,
+ SCpnt));
+ return FAILED;
+ }
+ hd->tmState = TM_STATE_NONE;
+ spin_unlock_irqrestore(&hd->ioc->FreeQlock, flags);
+
+ nehprintk((KERN_WARNING MYNAM ": %s: mptscsih_dev_reset: "
+ "Device reset was successful! (sc=%p)\n",
+ hd->ioc->name,
+ SCpnt));
+
+ return SUCCESS;
+}
/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
-/*
- * Private data...
+/**
+ * mptscsih_bus_reset - Perform a SCSI BUS_RESET! new_eh variant
+ * @SCpnt: Pointer to Scsi_Cmnd structure, IO which reset is due to
+ *
+ * (linux Scsi_Host_Template.eh_bus_reset_handler routine)
+ *
+ * Returns SUCCESS or FAILED.
*/
-static ASCQ_Table_t *mptscsih_ASCQ_TablePtr;
+int
+mptscsih_bus_reset(Scsi_Cmnd * SCpnt)
+{
+ MPT_SCSI_HOST *hd;
+ unsigned long flags;
+
+ /* If we can't locate our host adapter structure, return FAILED status.
+ */
+ if ((hd = (MPT_SCSI_HOST *) SCpnt->host->hostdata) == NULL){
+ nehprintk((KERN_WARNING MYNAM ": mptscsih_bus_reset: "
+ "Can't locate host! (sc=%p)\n",
+ SCpnt ) );
+ return FAILED;
+ }
+
+ printk(KERN_WARNING MYNAM ": %s: >> Attempting bus reset! (sc=%p)\n",
+ hd->ioc->name, SCpnt);
+ printk(KERN_WARNING MYNAM ": %s: IOs outstanding = %d\n",
+ hd->ioc->name, atomic_read(&queue_depth));
+
+ /* Wait a fixed amount of time for the TM pending flag to be cleared.
+ * If we time out, then we return a FAILED status to the caller. This
+ * call to mptscsih_tm_pending_wait() will set the pending flag if we are
+ * successful.
+ */
+ if (mptscsih_tm_pending_wait(hd) == FAILED) {
+ nehprintk((KERN_WARNING MYNAM ": %s: mptscsih_bus_reset: "
+ "Timed out waiting for previous TM to complete! "
+ "(sc = %p)\n",
+ hd->ioc->name, SCpnt ) );
+ return FAILED;
+ }
+
+ /* We are now ready to execute the task management request. */
+ if (mptscsih_TMHandler(hd, MPI_SCSITASKMGMT_TASKTYPE_RESET_BUS,
+ 0, 0, 0, CAN_SLEEP)
+ < 0){
+
+ /* The TM request failed and the subsequent FW-reload failed!
+ * Fatal error case.
+ */
+ printk(MYIOC_s_WARN_FMT
+ "Error processing TaskMgmt request (sc=%p)\n",
+ hd->ioc->name, SCpnt);
+ hd->tmPending = 0;
+ hd->tmState = TM_STATE_NONE;
+ return FAILED;
+ }
+
+ /* Our task management request will either complete or time out. So we
+ * spin until tmPending is != 1. If tmState is set to TM_STATE_ERROR,
+ * we encountered an error executing the task management request.
+ */
+ while (hd->tmPending == 1){
+ set_current_state(TASK_INTERRUPTIBLE);
+ schedule_timeout(HZ/4);
+ }
+ spin_lock_irqsave(&hd->ioc->FreeQlock, flags);
+ if (hd->tmState == TM_STATE_ERROR){
+ hd->tmState = TM_STATE_NONE;
+ spin_unlock_irqrestore(&hd->ioc->FreeQlock, flags);
+ nehprintk((KERN_WARNING MYNAM ": %s: mptscsih_bus_reset: "
+ "TM timeout error! (sc=%p)\n",
+ hd->ioc->name,
+ SCpnt));
+ return FAILED;
+ }
+ hd->tmState = TM_STATE_NONE;
+ spin_unlock_irqrestore(&hd->ioc->FreeQlock, flags);
+
+ nehprintk((KERN_WARNING MYNAM ": %s: mptscsih_bus_reset: "
+ "Bus reset was successful! (sc=%p)\n",
+ hd->ioc->name,
+ SCpnt));
+
+ return SUCCESS;
+}
/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
-/* old symsense.c stuff... */
-/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
-/*
- * Private data...
- * To protect ourselves against those that would pass us bogus pointers
+/**
+ * mptscsih_host_reset - Perform a SCSI host adapter RESET!
+ * new_eh variant
+ * @SCpnt: Pointer to Scsi_Cmnd structure, IO which reset is due to
+ *
+ * (linux Scsi_Host_Template.eh_host_reset_handler routine)
+ *
+ * Returns SUCCESS or FAILED.
*/
-static u8 dummyInqData[SCSI_STD_INQUIRY_BYTES]
- = { 0x1F, 0x00, 0x00, 0x00,
- 0x1F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
-static u8 dummySenseData[SCSI_STD_SENSE_BYTES]
- = { 0x70, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0A,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00 };
-static u8 dummyCDB[16]
- = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
-static u8 dummyScsiData[16]
- = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
+int
+mptscsih_host_reset(Scsi_Cmnd *SCpnt)
+{
+ MPT_SCSI_HOST * hd;
+ int status = SUCCESS;
+
+ /* If we can't locate the host to reset, then we failed. */
+ if ((hd = (MPT_SCSI_HOST *) SCpnt->host->hostdata) == NULL){
+ nehprintk( ( KERN_WARNING MYNAM ": mptscsih_host_reset: "
+ "Can't locate host! (sc=%p)\n",
+ SCpnt ) );
+ return FAILED;
+ }
-#if 0
-static const char *PeripheralDeviceTypeString[32] = {
- "Direct-access", /* 00h */
- "Sequential-access", /* 01h */
- "Printer", /* 02h */
- "Processor", /* 03h */
- /*"Write-Once-Read-Multiple",*/ /* 04h */
- "WORM", /* 04h */
- "CD-ROM", /* 05h */
+ printk(KERN_WARNING MYNAM ": %s: >> Attempting host reset! (sc=%p)\n",
+ hd->ioc->name, SCpnt);
+ printk(KERN_WARNING MYNAM ": %s: IOs outstanding = %d\n",
+ hd->ioc->name, atomic_read(&queue_depth));
+
+ /* If our attempts to reset the host failed, then return a failed
+ * status. The host will be taken off line by the SCSI mid-layer.
+ */
+ if (mpt_HardResetHandler(hd->ioc, CAN_SLEEP) < 0){
+ status = FAILED;
+ } else {
+ /* Make sure TM pending is cleared and TM state is set to
+ * NONE.
+ */
+ hd->tmPending = 0;
+ hd->tmState = TM_STATE_NONE;
+ }
+
+
+ nehprintk( ( KERN_WARNING MYNAM ": mptscsih_host_reset: "
+ "Status = %s\n",
+ (status == SUCCESS) ? "SUCCESS" : "FAILED" ) );
+
+ return status;
+}
+
+/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
+/**
+ * mptscsih_tm_pending_wait - wait for pending task management request to
+ * complete.
+ * @hd: Pointer to MPT host structure.
+ *
+ * Returns {SUCCESS,FAILED}.
+ */
+static int
+mptscsih_tm_pending_wait(MPT_SCSI_HOST * hd)
+{
+ unsigned long flags;
+ int loop_count = 60 * 4; /* Wait 60 seconds */
+ int status = FAILED;
+
+ do {
+ spin_lock_irqsave(&hd->ioc->FreeQlock, flags);
+ if (hd->tmState == TM_STATE_NONE) {
+ hd->tmState = TM_STATE_IN_PROGRESS;
+ hd->tmPending = 1;
+ spin_unlock_irqrestore(&hd->ioc->FreeQlock, flags);
+ status = SUCCESS;
+ break;
+ }
+ spin_unlock_irqrestore(&hd->ioc->FreeQlock, flags);
+ set_current_state(TASK_INTERRUPTIBLE);
+ schedule_timeout(HZ/4);
+
+ } while (--loop_count);
+
+ return status;
+}
+
+#else /* MPT_SCSI old EH stuff... */
+/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
+/**
+ * mptscsih_old_abort - Abort linux Scsi_Cmnd routine
+ * @SCpnt: Pointer to Scsi_Cmnd structure, IO to be aborted
+ *
+ * (linux Scsi_Host_Template.abort routine)
+ *
+ * Returns SCSI_ABORT_{SUCCESS,BUSY,PENDING}.
+ */
+int
+mptscsih_old_abort(Scsi_Cmnd *SCpnt)
+{
+ MPT_SCSI_HOST *hd;
+ MPT_FRAME_HDR *mf;
+ struct tq_struct *ptaskfoo;
+ unsigned long flags;
+ int scpnt_idx;
+
+ printk(KERN_WARNING MYNAM ": OldAbort scheduling ABORT SCSI IO (sc=%p)\n", (void *) SCpnt);
+ printk(KERN_WARNING " IOs outstanding = %d\n", atomic_read(&queue_depth));
+
+ if ((hd = (MPT_SCSI_HOST *) SCpnt->host->hostdata) == NULL) {
+ printk(KERN_WARNING " WARNING - OldAbort, NULL hostdata ptr!!\n");
+ SCpnt->result = DID_ERROR << 16;
+ SCpnt->scsi_done(SCpnt);
+ return SCSI_ABORT_NOT_RUNNING;
+ }
+
+ if ((scpnt_idx = SCPNT_TO_LOOKUP_IDX(SCpnt)) < 0) {
+ /* Cmd not found in ScsiLookup.
+ * If found in doneQ, delete from Q.
+ * Do OS callback.
+ */
+ search_doneQ_for_cmd(hd, SCpnt);
+
+ SCpnt->result = DID_RESET << 16;
+ SCpnt->scsi_done(SCpnt);
+ return SCSI_ABORT_SUCCESS;
+ } else {
+ /* If this command is pended, then timeout/hang occurred
+ * during DV. Force bus reset by posting command to F/W
+ * and then following up with the reset request.
+ */
+ if ((mf = mptscsih_search_pendingQ(hd, scpnt_idx)) != NULL) {
+ mptscsih_put_msgframe(ScsiDoneCtx, hd->ioc->id, mf);
+ post_pendingQ_commands(hd);
+ }
+ }
+
+ /*
+ * Check to see if there's already an ABORT queued for this guy.
+ */
+ mf = search_taskQ(0, SCpnt, hd, MPI_SCSITASKMGMT_TASKTYPE_ABORT_TASK);
+ if (mf != NULL) {
+ dtmprintk((MYIOC_s_INFO_FMT "OldAbort:Abort Task PENDING cmd (%p) taskQ depth (%d)\n",
+ hd->ioc->name, SCpnt, hd->taskQcnt));
+ return SCSI_ABORT_PENDING;
+ }
+
+ // SJR - CHECKME - Can we avoid this here?
+ // (mpt_HardResetHandler has this check...)
+ /* If IOC is reloading FW, return PENDING.
+ */
+ spin_lock_irqsave(&hd->ioc->diagLock, flags);
+ if (hd->ioc->diagPending) {
+ spin_unlock_irqrestore(&hd->ioc->diagLock, flags);
+ return SCSI_ABORT_PENDING;
+ }
+ spin_unlock_irqrestore(&hd->ioc->diagLock, flags);
+
+ /* If there are no message frames what should we do?
+ */
+ if ((mf = mpt_get_msg_frame(ScsiTaskCtx, hd->ioc->id)) == NULL) {
+ printk((KERN_WARNING " WARNING - OldAbort, no msg frames!!\n"));
+ /* We are out of message frames!
+ * Call the reset handler to do a FW reload.
+ */
+ printk((KERN_WARNING " Reloading Firmware!!\n"));
+ if (mpt_HardResetHandler(hd->ioc, NO_SLEEP) < 0) {
+ printk((KERN_WARNING " Firmware Reload FAILED!!\n"));
+ }
+ return SCSI_ABORT_PENDING;
+ }
+
+ /*
+ * Add ourselves to (end of) taskQ .
+ * Check to see if our _bh is running. If NOT, schedule it.
+ */
+ spin_lock_irqsave(&hd->ioc->FreeQlock, flags);
+ Q_ADD_TAIL(&hd->taskQ, &mf->u.frame.linkage, MPT_FRAME_HDR);
+ hd->taskQcnt++;
+ atomic_inc(&mpt_taskQdepth);
+ spin_unlock_irqrestore(&hd->ioc->FreeQlock, flags);
+
+ spin_lock_irqsave(&mytaskQ_lock, flags);
+
+ /* Save the original SCpnt mf pointer
+ */
+ SCpnt->host_scribble = (u8 *) MPT_INDEX_2_MFPTR (hd->ioc, scpnt_idx);
+
+ /* For the time being, force bus reset on any abort
+ * requests for the 1030 FW.
+ */
+ if (hd->is_spi)
+ mf->u.frame.linkage.arg1 = MPI_SCSITASKMGMT_TASKTYPE_RESET_BUS;
+ else
+ mf->u.frame.linkage.arg1 = MPI_SCSITASKMGMT_TASKTYPE_ABORT_TASK;
+
+ mf->u.frame.linkage.argp1 = SCpnt;
+ mf->u.frame.linkage.argp2 = (void *) hd;
+
+ dtmprintk((MYIOC_s_INFO_FMT "OldAbort:_bh_handler state (%d) taskQ count (%d)\n",
+ hd->ioc->name, mytaskQ_bh_active, hd->taskQcnt));
+
+ if (! mytaskQ_bh_active) {
+ mytaskQ_bh_active = 1;
+ spin_unlock_irqrestore(&mytaskQ_lock, flags);
+
+ /*
+ * Oh how cute, no alloc/free/mgmt needed if we use
+ * (bottom/unused portion of) MPT request frame.
+ */
+ ptaskfoo = (struct tq_struct *) &mptscsih_ptaskfoo;
+ ptaskfoo->sync = 0;
+ ptaskfoo->routine = mptscsih_taskmgmt_bh;
+ ptaskfoo->data = SCpnt;
+
+ SCHEDULE_TASK(ptaskfoo);
+ } else {
+ spin_unlock_irqrestore(&mytaskQ_lock, flags);
+ }
+
+ return SCSI_ABORT_PENDING;
+}
+
+/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
+/**
+ * mptscsih_old_reset - Perform a SCSI BUS_RESET!
+ * @SCpnt: Pointer to Scsi_Cmnd structure, IO which reset is due to
+ * @reset_flags: (not used?)
+ *
+ * (linux Scsi_Host_Template.reset routine)
+ *
+ * Returns SCSI_RESET_{SUCCESS,PUNT,PENDING}.
+ */
+int
+mptscsih_old_reset(Scsi_Cmnd *SCpnt, unsigned int reset_flags)
+{
+ MPT_SCSI_HOST *hd;
+ MPT_FRAME_HDR *mf;
+ struct tq_struct *ptaskfoo;
+ unsigned long flags;
+ int scpnt_idx;
+
+ printk(KERN_WARNING MYNAM ": OldReset scheduling BUS_RESET (sc=%p)\n", (void *) SCpnt);
+ printk(KERN_WARNING " IOs outstanding = %d\n", atomic_read(&queue_depth));
+
+ if ((hd = (MPT_SCSI_HOST *) SCpnt->host->hostdata) == NULL) {
+ SCpnt->result = DID_RESET << 16;
+ SCpnt->scsi_done(SCpnt);
+ return SCSI_RESET_SUCCESS;
+ }
+
+ if ((scpnt_idx = SCPNT_TO_LOOKUP_IDX(SCpnt)) < 0) {
+ /* Cmd not found in ScsiLookup.
+ * If found in doneQ, delete from Q.
+ * Do OS callback.
+ */
+ search_doneQ_for_cmd(hd, SCpnt);
+
+ SCpnt->result = DID_RESET << 16;
+ SCpnt->scsi_done(SCpnt);
+ return SCSI_RESET_SUCCESS;
+ } else {
+ /* If this command is pended, then timeout/hang occurred
+ * during DV. Force bus reset by posting command to F/W
+ * and then following up with the reset request.
+ */
+ if ((mf = mptscsih_search_pendingQ(hd, scpnt_idx)) != NULL) {
+ mptscsih_put_msgframe(ScsiDoneCtx, hd->ioc->id, mf);
+ post_pendingQ_commands(hd);
+ }
+ }
+
+ /*
+ * Check to see if there's an ABORT_TASK queued for this guy.
+ * If so, delete.
+ */
+ search_taskQ(1, SCpnt, hd, MPI_SCSITASKMGMT_TASKTYPE_ABORT_TASK);
+
+ /*
+ * Check to see if there's already a BUS_RESET queued for this guy.
+ */
+ mf = search_taskQ(0, SCpnt, hd, MPI_SCSITASKMGMT_TASKTYPE_RESET_BUS);
+ if (mf != NULL) {
+ dtmprintk((MYIOC_s_INFO_FMT "OldReset:Reset Task PENDING cmd (%p) taskQ depth (%d)\n",
+ hd->ioc->name, SCpnt, hd->taskQcnt));
+ return SCSI_RESET_PENDING;
+ }
+
+ // SJR - CHECKME - Can we avoid this here?
+ // (mpt_HardResetHandler has this check...)
+ /* If IOC is reloading FW, return PENDING.
+ */
+ spin_lock_irqsave(&hd->ioc->diagLock, flags);
+ if (hd->ioc->diagPending) {
+ spin_unlock_irqrestore(&hd->ioc->diagLock, flags);
+ return SCSI_RESET_PENDING;
+ }
+ spin_unlock_irqrestore(&hd->ioc->diagLock, flags);
+
+ if ((mf = mpt_get_msg_frame(ScsiTaskCtx, hd->ioc->id)) == NULL) {
+ /* We are out of message frames!
+ * Call the reset handler to do a FW reload.
+ */
+ printk((KERN_WARNING " Reloading Firmware!!\n"));
+ if (mpt_HardResetHandler(hd->ioc, NO_SLEEP) < 0) {
+ printk((KERN_WARNING " Firmware Reload FAILED!!\n"));
+ }
+ return SCSI_RESET_PENDING;
+ }
+
+ /*
+ * Add ourselves to (end of) taskQ.
+ * Check to see if our _bh is running. If NOT, schedule it.
+ */
+ spin_lock_irqsave(&hd->ioc->FreeQlock, flags);
+ Q_ADD_TAIL(&hd->taskQ, &mf->u.frame.linkage, MPT_FRAME_HDR);
+ hd->taskQcnt++;
+ atomic_inc(&mpt_taskQdepth);
+ spin_unlock_irqrestore(&hd->ioc->FreeQlock, flags);
+
+
+ dtmprintk((MYIOC_s_INFO_FMT "OldReset: _bh_handler state (%d) taskQ count (%d)\n",
+ hd->ioc->name, mytaskQ_bh_active, hd->taskQcnt));
+
+ spin_lock_irqsave(&mytaskQ_lock, flags);
+ /* Save the original SCpnt mf pointer
+ */
+ SCpnt->host_scribble = (u8 *) MPT_INDEX_2_MFPTR (hd->ioc, scpnt_idx);
+
+ mf->u.frame.linkage.arg1 = MPI_SCSITASKMGMT_TASKTYPE_RESET_BUS;
+ mf->u.frame.linkage.argp1 = SCpnt;
+ mf->u.frame.linkage.argp2 = (void *) hd;
+
+ if (! mytaskQ_bh_active) {
+ mytaskQ_bh_active = 1;
+ spin_unlock_irqrestore(&mytaskQ_lock, flags);
+ /*
+ * Oh how cute, no alloc/free/mgmt needed if we use
+ * (bottom/unused portion of) MPT request frame.
+ */
+ ptaskfoo = (struct tq_struct *) &mptscsih_ptaskfoo;
+ ptaskfoo->sync = 0;
+ ptaskfoo->routine = mptscsih_taskmgmt_bh;
+ ptaskfoo->data = SCpnt;
+
+ SCHEDULE_TASK(ptaskfoo);
+ } else {
+ spin_unlock_irqrestore(&mytaskQ_lock, flags);
+ }
+ return SCSI_RESET_PENDING;
+}
+
+/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
+/*
+ * mptscsih_taskmgmt_bh - SCSI task mgmt bottom half handler
+ * @sc: (unused)
+ *
+ * This routine (thread) is active whenever there are any outstanding
+ * SCSI task management requests for a SCSI host adapter.
+ * IMPORTANT! This routine is scheduled therefore should never be
+ * running in ISR context. i.e., it's safe to sleep here.
+ */
+void
+mptscsih_taskmgmt_bh(void *sc)
+{
+ MPT_ADAPTER *ioc;
+ Scsi_Cmnd *SCpnt;
+ MPT_FRAME_HDR *mf = NULL;
+ MPT_SCSI_HOST *hd;
+ u32 ctx2abort = 0;
+ unsigned long flags;
+ int scpnt_idx;
+ int did;
+ u8 task_type;
+
+ spin_lock_irqsave(&mytaskQ_lock, flags);
+ mytaskQ_bh_active = 1;
+ spin_unlock_irqrestore(&mytaskQ_lock, flags);
+
+ do {
+ set_current_state(TASK_INTERRUPTIBLE);
+ schedule_timeout(HZ/4);
+ did = 0;
+
+ for (ioc = mpt_adapter_find_first(); ioc != NULL; ioc = mpt_adapter_find_next(ioc)) {
+ if (ioc->sh) {
+ hd = (MPT_SCSI_HOST *) ioc->sh->hostdata;
+ if (hd == NULL) {
+ printk(KERN_ERR MYNAM
+ ": ERROR - TaskMgmt NULL SCSI Host!"
+ "(ioc=%p, sh=%p hd=%p)\n",
+ (void *) ioc, (void *) ioc->sh, (void *) hd);
+ continue;
+ }
+
+ spin_lock_irqsave(&ioc->FreeQlock, flags);
+ if (Q_IS_EMPTY(&hd->taskQ)) {
+ spin_unlock_irqrestore(&ioc->FreeQlock, flags);
+ continue;
+ }
+
+ /* If we ever find a non-empty queue,
+ * keep the handler alive
+ */
+ did++;
+
+ /* tmPending is SMP lock-protected */
+ if (hd->tmPending || hd->tmPtr) {
+ spin_unlock_irqrestore(&ioc->FreeQlock, flags);
+ continue;
+ }
+ hd->tmPending = 1;
+
+ /* Process this request
+ */
+ mf = hd->taskQ.head;
+ Q_DEL_ITEM(&mf->u.frame.linkage);
+ hd->taskQcnt--;
+ atomic_dec(&mpt_taskQdepth);
+ spin_unlock_irqrestore(&ioc->FreeQlock, flags);
+
+ SCpnt = (Scsi_Cmnd*)mf->u.frame.linkage.argp1;
+ if (SCpnt == NULL) {
+ printk(KERN_ERR MYNAM ": ERROR - TaskMgmt has NULL SCpnt! (mf=%p:sc=%p)\n",
+ (void *) mf, (void *) SCpnt);
+ mpt_free_msg_frame(ScsiTaskCtx, hd->ioc->id, mf);
+ spin_lock_irqsave(&ioc->FreeQlock, flags);
+ hd->tmPending = 0;
+ spin_unlock_irqrestore(&ioc->FreeQlock, flags);
+ continue;
+ }
+
+ /* Get the ScsiLookup index pointer
+ * from the SC pointer.
+ */
+ if (!SCpnt->host_scribble || ((MPT_SCSI_HOST *)SCpnt->host->hostdata != hd)) {
+ /* The command associated with the
+ * abort/reset request must have
+ * completed and this is a stale
+ * request. We are done.
+ * Free the current MF and continue.
+ */
+ mpt_free_msg_frame(ScsiTaskCtx, hd->ioc->id, mf);
+ spin_lock_irqsave(&ioc->FreeQlock, flags);
+ hd->tmPending = 0;
+ spin_unlock_irqrestore(&ioc->FreeQlock, flags);
+ continue;
+ }
+
+ scpnt_idx = MFPTR_2_MPT_INDEX(hd->ioc, SCpnt->host_scribble);
+ if (scpnt_idx != SCPNT_TO_LOOKUP_IDX(SCpnt)) {
+ /* Error! this should never happen!!
+ */
+ mpt_free_msg_frame(ScsiTaskCtx, hd->ioc->id, mf);
+ spin_lock_irqsave(&ioc->FreeQlock, flags);
+ hd->tmPending = 0;
+ spin_unlock_irqrestore(&ioc->FreeQlock, flags);
+ continue;
+ }
+
+ task_type = mf->u.frame.linkage.arg1;
+ ctx2abort = 0;
+ if (task_type == MPI_SCSITASKMGMT_TASKTYPE_ABORT_TASK) {
+ MPT_FRAME_HDR *SCpntMf;
+
+ /*
+ * Most important! Set TaskMsgContext to SCpnt's MsgContext!
+ * (the IO to be ABORT'd)
+ *
+ * NOTE: Since we do not byteswap MsgContext, we do not
+ * swap it here either. It is an opaque cookie to
+ * the controller, so it does not matter. -DaveM
+ */
+ SCpntMf = (MPT_FRAME_HDR *) SCpnt->host_scribble;
+ ctx2abort = SCpntMf->u.frame.hwhdr.msgctxu.MsgContext;
+
+ hd->abortSCpnt = SCpnt;
+ printk(KERN_WARNING MYNAM ": Attempting ABORT SCSI IO! (mf=%p:sc=%p)\n",
+ (void *) mf, (void *) SCpnt);
+ }
+
+ /* The TM handler will allocate a new mf,
+ * so free the current mf.
+ */
+ mpt_free_msg_frame(ScsiTaskCtx, hd->ioc->id, mf);
+ mf = NULL;
+
+ if (mptscsih_TMHandler(hd, task_type, SCpnt->target, SCpnt->lun, ctx2abort, NO_SLEEP) < 0) {
+
+ /* The TM request failed and the subsequent FW-reload failed!
+ * Fatal error case.
+ */
+ printk(KERN_WARNING MYNAM
+ ": WARNING[1] - IOC error processing TaskMgmt request (sc=%p)\n", (void *) SCpnt);
+
+ if (hd->ScsiLookup[scpnt_idx] != NULL) {
+ atomic_dec(&queue_depth);
+ SCpnt->result = DID_SOFT_ERROR << 16;
+ MPT_HOST_LOCK(flags);
+ SCpnt->scsi_done(SCpnt);
+ MPT_HOST_UNLOCK(flags);
+ mpt_free_msg_frame(ScsiTaskCtx, hd->ioc->id, mf);
+ }
+ spin_lock_irqsave(&ioc->FreeQlock, flags);
+ hd->tmPending = 0;
+ spin_unlock_irqrestore(&ioc->FreeQlock, flags);
+ hd->abortSCpnt = NULL;
+ }
+ }
+ }
+ if (atomic_read(&mpt_taskQdepth) > 0)
+ did++;
+
+ } while ( did );
+
+ spin_lock_irqsave(&mytaskQ_lock, flags);
+ mytaskQ_bh_active = 0;
+ spin_unlock_irqrestore(&mytaskQ_lock, flags);
+
+ return;
+}
+#endif /* } */
+
+/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
+/**
+ * mptscsih_taskmgmt_complete - Registered with Fusion MPT base driver
+ * @ioc: Pointer to MPT_ADAPTER structure
+ * @mf: Pointer to SCSI task mgmt request frame
+ * @mr: Pointer to SCSI task mgmt reply frame
+ *
+ * This routine is called from mptbase.c::mpt_interrupt() at the completion
+ * of any SCSI task management request.
+ * This routine is registered with the MPT (base) driver at driver
+ * load/init time via the mpt_register() API call.
+ *
+ * Returns 1 indicating alloc'd request frame ptr should be freed.
+ */
+static int
+mptscsih_taskmgmt_complete(MPT_ADAPTER *ioc, MPT_FRAME_HDR *mf, MPT_FRAME_HDR *mr)
+{
+ SCSITaskMgmtReply_t *pScsiTmReply;
+ SCSITaskMgmt_t *pScsiTmReq;
+ MPT_SCSI_HOST *hd = NULL;
+ unsigned long flags;
+ u8 tmType = 0;
+
+ dtmprintk((MYIOC_s_INFO_FMT "SCSI TaskMgmt completed (mf=%p,r=%p)\n",
+ ioc->name, mf, mr));
+ if (ioc->sh) {
+ /* Depending on the thread, a timer is activated for
+ * the TM request. Delete this timer on completion of TM.
+ * Decrement count of outstanding TM requests.
+ */
+ hd = (MPT_SCSI_HOST *)ioc->sh->hostdata;
+ if (hd->tmPtr) {
+ del_timer(&hd->TMtimer);
+ }
+ dtmprintk((MYIOC_s_INFO_FMT "taskQcnt (%d)\n",
+ ioc->name, hd->taskQcnt));
+ } else {
+ dtmprintk((MYIOC_s_WARN_FMT "TaskMgmt Complete: NULL Scsi Host Ptr\n",
+ ioc->name));
+ return 1;
+ }
+
+ if (mr == NULL) {
+ dtmprintk((MYIOC_s_WARN_FMT "ERROR! TaskMgmt Reply: NULL Request %p\n",
+ ioc->name, mf));
+ return 1;
+ } else {
+ pScsiTmReply = (SCSITaskMgmtReply_t*)mr;
+ pScsiTmReq = (SCSITaskMgmt_t*)mf;
+
+ /* Figure out if this was ABORT_TASK, TARGET_RESET, or BUS_RESET! */
+ tmType = pScsiTmReq->TaskType;
+
+ dtmprintk((KERN_INFO " TaskType = %d, TerminationCount=%d\n",
+ tmType, le32_to_cpu(pScsiTmReply->TerminationCount)));
+
+ /* Error? (anything non-zero?) */
+ if (*(u32 *)&pScsiTmReply->Reserved2[0]) {
+ u16 iocstatus;
+
+ iocstatus = le16_to_cpu(pScsiTmReply->IOCStatus) & MPI_IOCSTATUS_MASK;
+ dtmprintk((KERN_INFO " SCSI TaskMgmt (%d) - Oops!\n", tmType));
+ dtmprintk((KERN_INFO " IOCStatus = %04xh\n", iocstatus));
+ dtmprintk((KERN_INFO " IOCLogInfo = %08xh\n",
+ le32_to_cpu(pScsiTmReply->IOCLogInfo)));
+
+ /* clear flags and continue.
+ */
+ if (tmType == MPI_SCSITASKMGMT_TASKTYPE_ABORT_TASK)
+ hd->abortSCpnt = NULL;
+#ifdef DROP_TEST
+ if (dropMfPtr)
+ dropTestBad++;
+#endif
+ /* If an internal command is present
+ * or the TM failed - reload the FW.
+ * FC FW may respond FAILED to an ABORT
+ */
+ if (tmType == MPI_SCSITASKMGMT_TASKTYPE_RESET_BUS) {
+ if ((hd->cmdPtr) ||
+ (iocstatus == MPI_IOCSTATUS_SCSI_TASK_MGMT_FAILED)) {
+ if (mpt_HardResetHandler(ioc, NO_SLEEP) < 0) {
+ printk((KERN_WARNING
+ " Firmware Reload FAILED!!\n"));
+ }
+ }
+ }
+#ifdef MPT_SCSI_USE_NEW_EH
+ hd->tmState = TM_STATE_ERROR;
+#endif
+ } else {
+ dtmprintk((KERN_INFO " SCSI TaskMgmt SUCCESS!\n"));
+
+#ifndef MPT_SCSI_USE_NEW_EH
+ if (tmType == MPI_SCSITASKMGMT_TASKTYPE_RESET_BUS) {
+ /* clean taskQ - remove tasks associated with
+ * completed commands.
+ */
+ clean_taskQ(hd);
+ } else if (tmType == MPI_SCSITASKMGMT_TASKTYPE_ABORT_TASK) {
+ /* If taskQ contains another request
+ * for this SCpnt, delete this request.
+ */
+ search_taskQ_for_cmd(hd->abortSCpnt, hd);
+ }
+#endif
+ hd->numTMrequests--;
+ hd->abortSCpnt = NULL;
+ flush_doneQ(hd);
+
+#ifdef DROP_TEST
+ if (dropMfPtr)
+ dropTestOK++;
+#endif
+ }
+ }
+
+#ifdef DROP_TEST
+ {
+ Scsi_Cmnd *sc;
+ unsigned long flags;
+ u16 req_idx;
+
+ /* Free resources for the drop test MF and chain buffers.
+ */
+ if (dropMfPtr) {
+ req_idx = le16_to_cpu(dropMfPtr->u.frame.hwhdr.msgctxu.fld.req_idx);
+ sc = hd->ScsiLookup[req_idx];
+ if (sc == NULL) {
+ printk(MYIOC_s_ERR_FMT
+ "Drop Test: NULL ScsiCmd ptr!\n",
+ ioc->name);
+ } else {
+ sc->host_scribble = NULL;
+ if (tmType == MPI_SCSITASKMGMT_TASKTYPE_RESET_BUS)
+ sc->result = DID_RESET << 16;
+ else
+ sc->result = DID_ABORT << 16;
+
+ hd->ScsiLookup[req_idx] = NULL;
+ atomic_dec(&queue_depth);
+ MPT_HOST_LOCK(flags);
+ sc->scsi_done(sc); /* Issue callback */
+ MPT_HOST_UNLOCK(flags);
+
+ mptscsih_freeChainBuffers(hd, req_idx);
+ mpt_free_msg_frame(ScsiDoneCtx, ioc->id, dropMfPtr);
+
+ printk(MYIOC_s_INFO_FMT
+ "Free'd Dropped cmd (%p)\n",
+ hd->ioc->name, sc);
+ printk(MYIOC_s_INFO_FMT
+ "mf (%p) reqidx (%4x)\n",
+ hd->ioc->name, dropMfPtr,
+ req_idx);
+ printk(MYIOC_s_INFO_FMT
+ "Num Tot (%d) Good (%d) Bad (%d) \n",
+ hd->ioc->name, dropTestNum,
+ dropTestOK, dropTestBad);
+ }
+ dropMfPtr = NULL;
+ }
+ }
+#endif
+
+#ifndef MPT_SCSI_USE_NEW_EH
+ /*
+ * Signal to _bh thread that we finished.
+ * This IOC can now process another TM command.
+ */
+ dtmprintk((MYIOC_s_INFO_FMT "taskmgmt_complete: (=%p) done! Num Failed(%d) Task Count (%d)\n",
+ ioc->name, mf, hd->numTMrequests, hd->taskQcnt));
+#endif
+ hd->tmPtr = NULL;
+ spin_lock_irqsave(&ioc->FreeQlock, flags);
+ hd->tmPending = 0;
+ spin_unlock_irqrestore(&ioc->FreeQlock, flags);
+
+ return 1;
+}
+
+/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
+/*
+ * This is anyones guess quite frankly.
+ */
+
+int
+mptscsih_bios_param(Disk * disk, kdev_t dev, int *ip)
+{
+ int size;
+
+ size = disk->capacity;
+ ip[0] = 64; /* heads */
+ ip[1] = 32; /* sectors */
+ if ((ip[2] = size >> 11) > 1024) { /* cylinders, test for big disk */
+ ip[0] = 255; /* heads */
+ ip[1] = 63; /* sectors */
+ ip[2] = size / (255 * 63); /* cylinders */
+ }
+ return 0;
+}
+
+/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
+/*
+ * OS entry point to adjust the queue_depths on a per-device basis.
+ * Called once per device the bus scan. Use it to force the queue_depth
+ * member to 1 if a device does not support Q tags.
+ */
+void
+mptscsih_select_queue_depths(struct Scsi_Host *sh, Scsi_Device *sdList)
+{
+ struct scsi_device *device;
+ VirtDevice *pTarget;
+ MPT_SCSI_HOST *hd;
+ int ii, max;
+
+ for (device = sdList; device != NULL; device = device->next) {
+
+ if (device->host != sh)
+ continue;
+
+ hd = (MPT_SCSI_HOST *) sh->hostdata;
+ if (hd == NULL)
+ continue;
+
+ if (hd->Targets != NULL) {
+ if (hd->is_spi)
+ max = MPT_MAX_SCSI_DEVICES;
+ else
+ max = MPT_MAX_FC_DEVICES;
+
+ for (ii=0; ii < max; ii++) {
+ pTarget = hd->Targets[ii];
+ if (pTarget && !(pTarget->tflags & MPT_TARGET_FLAGS_Q_YES)) {
+ device->queue_depth = 1;
+ }
+ }
+ }
+ }
+}
+
+/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
+/*
+ * Private routines...
+ */
+/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
+/* 19991030 -sralston
+ * Return absolute SCSI data direction:
+ * 1 = _DATA_OUT
+ * 0 = _DIR_NONE
+ * -1 = _DATA_IN
+ *
+ * Changed: 3-20-2002 pdelaney to use the default data
+ * direction and the defines set up in the
+ * 2.4 kernel series
+ * 1 = _DATA_OUT changed to SCSI_DATA_WRITE (1)
+ * 0 = _DIR_NONE changed to SCSI_DATA_NONE (3)
+ * -1 = _DATA_IN changed to SCSI_DATA_READ (2)
+ * If the direction is unknown, fall through to original code.
+ */
+static int
+mptscsih_io_direction(Scsi_Cmnd *cmd)
+{
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0)
+ if (cmd->sc_data_direction != SCSI_DATA_UNKNOWN)
+ return cmd->sc_data_direction;
+#endif
+ switch (cmd->cmnd[0]) {
+ /* _DATA_OUT commands */
+ case WRITE_6: case WRITE_10: case WRITE_12:
+ case WRITE_LONG: case WRITE_SAME: case WRITE_BUFFER:
+ case WRITE_VERIFY: case WRITE_VERIFY_12:
+ case COMPARE: case COPY: case COPY_VERIFY:
+ case SEARCH_EQUAL: case SEARCH_HIGH: case SEARCH_LOW:
+ case SEARCH_EQUAL_12: case SEARCH_HIGH_12: case SEARCH_LOW_12:
+ case MODE_SELECT: case MODE_SELECT_10: case LOG_SELECT:
+ case SEND_DIAGNOSTIC: case CHANGE_DEFINITION: case UPDATE_BLOCK:
+ case SET_WINDOW: case MEDIUM_SCAN: case SEND_VOLUME_TAG:
+ case REASSIGN_BLOCKS:
+ case PERSISTENT_RESERVE_OUT:
+ case 0xea:
+ case 0xa3:
+ return SCSI_DATA_WRITE;
+
+ /* No data transfer commands */
+ case SEEK_6: case SEEK_10:
+ case RESERVE: case RELEASE:
+ case TEST_UNIT_READY:
+ case START_STOP:
+ case ALLOW_MEDIUM_REMOVAL:
+ return SCSI_DATA_NONE;
+
+ /* Conditional data transfer commands */
+ case FORMAT_UNIT:
+ if (cmd->cmnd[1] & 0x10) /* FmtData (data out phase)? */
+ return SCSI_DATA_WRITE;
+ else
+ return SCSI_DATA_NONE;
+
+ case VERIFY:
+ if (cmd->cmnd[1] & 0x02) /* VERIFY:BYTCHK (data out phase)? */
+ return SCSI_DATA_WRITE;
+ else
+ return SCSI_DATA_NONE;
+
+ case RESERVE_10:
+ if (cmd->cmnd[1] & 0x03) /* RESERVE:{LongID|Extent} (data out phase)? */
+ return SCSI_DATA_WRITE;
+ else
+ return SCSI_DATA_NONE;
+
+#if 0
+ case REZERO_UNIT: /* (or REWIND) */
+ case SPACE:
+ case ERASE: case ERASE_10:
+ case SYNCHRONIZE_CACHE:
+ case LOCK_UNLOCK_CACHE:
+#endif
+
+ /* Must be data _IN! */
+ default:
+ return SCSI_DATA_READ;
+ }
+}
+
+/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
+/* Utility function to copy sense data from the scsi_cmnd buffer
+ * to the FC and SCSI target structures.
+ *
+ */
+static void
+copy_sense_data(Scsi_Cmnd *sc, MPT_SCSI_HOST *hd, MPT_FRAME_HDR *mf, SCSIIOReply_t *pScsiReply)
+{
+ VirtDevice *target;
+ SCSIIORequest_t *pReq;
+ u32 sense_count = le32_to_cpu(pScsiReply->SenseCount);
+ int index;
+ char devFoo[96];
+ IO_Info_t thisIo;
+
+ /* Get target structure
+ */
+ pReq = (SCSIIORequest_t *) mf;
+ index = (int) pReq->TargetID;
+ target = hd->Targets[index];
+ if (hd->is_multipath && sc->device->hostdata)
+ target = (VirtDevice *) sc->device->hostdata;
+
+ if (sense_count) {
+ u8 *sense_data;
+ int req_index;
+
+ /* Copy the sense received into the scsi command block. */
+ req_index = le16_to_cpu(mf->u.frame.hwhdr.msgctxu.fld.req_idx);
+ sense_data = ((u8 *)hd->ioc->sense_buf_pool + (req_index * MPT_SENSE_BUFFER_ALLOC));
+ memcpy(sc->sense_buffer, sense_data, SNS_LEN(sc));
+
+ /* save sense data to the target device
+ */
+ if (target) {
+ int sz;
+
+ sz = MIN(pReq->SenseBufferLength, sense_count);
+ if (sz > SCSI_STD_SENSE_BYTES)
+ sz = SCSI_STD_SENSE_BYTES;
+ memcpy(target->sense, sense_data, sz);
+ target->tflags |= MPT_TARGET_FLAGS_VALID_SENSE;
+ }
+
+ /* Log SMART data (asc = 0x5D, non-IM case only) if required.
+ */
+ if ((hd->ioc->events) && (hd->ioc->eventTypes & (1 << MPI_EVENT_SCSI_DEVICE_STATUS_CHANGE))) {
+ if ((sense_data[12] == 0x5D) && (target->raidVolume == 0)) {
+ int idx;
+ MPT_ADAPTER *ioc = hd->ioc;
+
+ idx = ioc->eventContext % ioc->eventLogSize;
+ ioc->events[idx].event = MPI_EVENT_SCSI_DEVICE_STATUS_CHANGE;
+ ioc->events[idx].eventContext = ioc->eventContext;
+
+ ioc->events[idx].data[0] = (pReq->LUN[1] << 24) ||
+ (MPI_EVENT_SCSI_DEV_STAT_RC_SMART_DATA << 16) ||
+ (pReq->Bus << 8) || pReq->TargetID;
+
+ ioc->events[idx].data[1] = (sense_data[13] << 8) || sense_data[12];
+
+ ioc->eventContext++;
+ }
+ }
+
+ /* Print an error report for the user.
+ */
+ thisIo.cdbPtr = sc->cmnd;
+ thisIo.sensePtr = sc->sense_buffer;
+ thisIo.SCSIStatus = pScsiReply->SCSIStatus;
+ thisIo.DoDisplay = 1;
+ if (hd->is_multipath)
+ sprintf(devFoo, "%d:%d:%d \"%s\"",
+ hd->ioc->id,
+ pReq->TargetID,
+ pReq->LUN[1],
+ target->dev_vol_name);
+ else
+ sprintf(devFoo, "%d:%d:%d", hd->ioc->id, sc->target, sc->lun);
+ thisIo.DevIDStr = devFoo;
+/* fubar */
+ thisIo.dataPtr = NULL;
+ thisIo.inqPtr = NULL;
+ if (sc->device) {
+ thisIo.inqPtr = sc->device->vendor-8; /* FIXME!!! */
+ }
+ (void) mpt_ScsiHost_ErrorReport(&thisIo);
+
+ } else {
+ dprintk((MYIOC_s_INFO_FMT "Hmmm... SenseData len=0! (?)\n",
+ hd->ioc->name));
+ }
+
+ return;
+}
+
+static u32
+SCPNT_TO_LOOKUP_IDX(Scsi_Cmnd *sc)
+{
+ MPT_SCSI_HOST *hd;
+ int i;
+
+ hd = (MPT_SCSI_HOST *) sc->host->hostdata;
+
+ for (i = 0; i < hd->ioc->req_depth; i++) {
+ if (hd->ScsiLookup[i] == sc) {
+ return i;
+ }
+ }
+
+ return -1;
+}
+
+/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
+
+/* see mptscsih.h */
+
+#ifdef MPT_SCSIHOST_NEED_ENTRY_EXIT_HOOKUPS
+ static Scsi_Host_Template driver_template = MPT_SCSIHOST;
+# include "../../scsi/scsi_module.c"
+#endif
+
+/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
+/* Search the pendingQ for a command with specific index.
+ * If found, delete and return mf pointer
+ * If not found, return NULL
+ */
+static MPT_FRAME_HDR *
+mptscsih_search_pendingQ(MPT_SCSI_HOST *hd, int scpnt_idx)
+{
+ unsigned long flags;
+ MPT_DONE_Q *buffer;
+ MPT_FRAME_HDR *mf = NULL;
+ MPT_FRAME_HDR *cmdMfPtr = NULL;
+
+ ddvtprintk((MYIOC_s_INFO_FMT ": search_pendingQ called...", hd->ioc->name));
+ cmdMfPtr = MPT_INDEX_2_MFPTR(hd->ioc, scpnt_idx);
+ spin_lock_irqsave(&hd->freedoneQlock, flags);
+ if (!Q_IS_EMPTY(&hd->pendingQ)) {
+ buffer = hd->pendingQ.head;
+ do {
+ mf = (MPT_FRAME_HDR *) buffer->argp;
+ if (mf == cmdMfPtr) {
+ Q_DEL_ITEM(buffer);
+
+ /* clear the arg pointer
+ */
+ buffer->argp = NULL;
+
+ /* Add to the freeQ
+ */
+ Q_ADD_TAIL(&hd->freeQ.head, buffer, MPT_DONE_Q);
+ break;
+ }
+ mf = NULL;
+ } while ((buffer = buffer->forw) != (MPT_DONE_Q *) &hd->pendingQ);
+ }
+ spin_unlock_irqrestore(&hd->freedoneQlock, flags);
+ ddvtprintk((" ...return %p\n", mf));
+ return mf;
+}
+
+/* Post all commands on the pendingQ to the FW.
+ * Lock Q when deleting/adding members
+ * Lock io_request_lock for OS callback.
+ */
+static void
+post_pendingQ_commands(MPT_SCSI_HOST *hd)
+{
+ MPT_FRAME_HDR *mf;
+ MPT_DONE_Q *buffer;
+ unsigned long flags;
+
+ /* Flush the pendingQ.
+ */
+ ddvtprintk((MYIOC_s_INFO_FMT ": post_pendingQ_commands called\n", hd->ioc->name));
+ while (1) {
+ spin_lock_irqsave(&hd->freedoneQlock, flags);
+ if (Q_IS_EMPTY(&hd->pendingQ)) {
+ spin_unlock_irqrestore(&hd->freedoneQlock, flags);
+ break;
+ }
+
+ buffer = hd->pendingQ.head;
+ /* Delete from Q
+ */
+ Q_DEL_ITEM(buffer);
+
+ mf = (MPT_FRAME_HDR *) buffer->argp;
+ buffer->argp = NULL;
+
+ /* Add to the freeQ
+ */
+ Q_ADD_TAIL(&hd->freeQ.head, buffer, MPT_DONE_Q);
+ spin_unlock_irqrestore(&hd->freedoneQlock, flags);
+
+ if (!mf) {
+ /* This should never happen */
+ printk(MYIOC_s_WARN_FMT "post_pendingQ_commands: mf %p\n", hd->ioc->name, (void *) mf);
+ continue;
+ }
+
+ mptscsih_put_msgframe(ScsiDoneCtx, hd->ioc->id, mf);
+ ddvtprintk((MYIOC_s_INFO_FMT "Issued SCSI cmd (mf=%p)\n",
+ hd->ioc->name, mf));
+ }
+
+ return;
+}
+
+/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
+static int
+mptscsih_ioc_reset(MPT_ADAPTER *ioc, int reset_phase)
+{
+ MPT_SCSI_HOST *hd = NULL;
+ unsigned long flags;
+
+ dtmprintk((KERN_WARNING MYNAM
+ ": IOC %s_reset routed to SCSI host driver!\n",
+ reset_phase==MPT_IOC_PRE_RESET ? "pre" : "post"));
+
+ /* If a FW reload request arrives after base installed but
+ * before all scsi hosts have been attached, then an alt_ioc
+ * may have a NULL sh pointer.
+ */
+ if ((ioc->sh == NULL) || (ioc->sh->hostdata == NULL))
+ return 0;
+ else
+ hd = (MPT_SCSI_HOST *) ioc->sh->hostdata;
+
+ if (reset_phase == MPT_IOC_PRE_RESET) {
+ dtmprintk((MYIOC_s_WARN_FMT "Do Pre-Diag Reset handling\n",
+ ioc->name));
+
+ /* Clean Up:
+ * 1. Set Hard Reset Pending Flag
+ * All new commands go to doneQ
+ */
+ hd->resetPending = 1;
+
+ /* 2. Flush running commands
+ * Clean drop test code - if compiled
+ * Clean ScsiLookup (and associated memory)
+ * AND clean mytaskQ
+ */
+
+ /* 2a. Drop Test Command.
+ */
+#ifdef DROP_TEST
+ {
+ Scsi_Cmnd *sc;
+ unsigned long flags;
+ u16 req_idx;
+
+ /* Free resources for the drop test MF
+ * and chain buffers.
+ */
+ if (dropMfPtr) {
+ req_idx = le16_to_cpu(dropMfPtr->u.frame.hwhdr.msgctxu.fld.req_idx);
+ sc = hd->ScsiLookup[req_idx];
+ if (sc == NULL) {
+ printk(MYIOC_s_ERR_FMT
+ "Drop Test: NULL ScsiCmd ptr!\n",
+ ioc->name);
+ } else {
+ sc->host_scribble = NULL;
+ sc->result = DID_RESET << 16;
+ hd->ScsiLookup[req_idx] = NULL;
+ atomic_dec(&queue_depth);
+ MPT_HOST_LOCK(flags);
+ sc->scsi_done(sc); /* Issue callback */
+ MPT_HOST_UNLOCK(flags);
+ }
+
+ mptscsih_freeChainBuffers(hd, req_idx);
+ mpt_free_msg_frame(ScsiDoneCtx, ioc->id, dropMfPtr);
+ printk(MYIOC_s_INFO_FMT
+ "Free'd: mf (%p) reqidx (%4x)\n",
+ hd->ioc->name, dropMfPtr,
+ req_idx);
+ }
+ dropMfPtr = NULL;
+ }
+#endif
+
+ /* 2b. Reply to OS all known outstanding I/O commands.
+ */
+ mptscsih_flush_running_cmds(hd);
+
+ /* 2c. If there was an internal command that
+ * has not completed, configuration or io request,
+ * free these resources.
+ */
+ if (hd->cmdPtr) {
+ del_timer(&hd->timer);
+ mpt_free_msg_frame(ScsiScanDvCtx, ioc->id, hd->cmdPtr);
+ atomic_dec(&queue_depth);
+ }
+
+ /* 2d. If a task management has not completed,
+ * free resources associated with this request.
+ */
+ if (hd->tmPtr) {
+ del_timer(&hd->TMtimer);
+ mpt_free_msg_frame(ScsiTaskCtx, ioc->id, hd->tmPtr);
+ }
+
+#ifndef MPT_SCSI_USE_NEW_EH
+ /* 2e. Delete all commands on taskQ
+ * Should be superfluous - as this taskQ should
+ * be empty.
+ */
+ clean_taskQ(hd);
+#endif
+ dtmprintk((MYIOC_s_WARN_FMT "Pre-Reset handling complete.\n",
+ ioc->name));
+
+ } else {
+ dtmprintk((MYIOC_s_WARN_FMT "Do Post-Diag Reset handling\n",
+ ioc->name));
+
+ /* Once a FW reload begins, all new OS commands are
+ * redirected to the doneQ w/ a reset status.
+ * Init all control structures.
+ */
+
+ /* ScsiLookup initialization
+ */
+ {
+ int ii;
+ for (ii=0; ii < hd->ioc->req_depth; ii++)
+ hd->ScsiLookup[ii] = NULL;
+ }
+
+ /* 2. Chain Buffer initialization
+ */
+ mptscsih_initChainBuffers(hd, 0);
+
+ /* 3. tmPtr clear
+ */
+ if (hd->tmPtr) {
+ hd->tmPtr = NULL;
+ }
+
+ /* 4. Renegotiate to all devices, if SCSI
+ */
+ if (hd->is_spi)
+ mptscsih_writeSDP1(hd, 0, 0, MPT_SCSICFG_ALL_IDS | MPT_SCSICFG_USE_NVRAM);
+
+ /* 5. Enable new commands to be posted
+ */
+ spin_lock_irqsave(&ioc->FreeQlock, flags);
+ hd->tmPending = 0;
+ spin_unlock_irqrestore(&ioc->FreeQlock, flags);
+ hd->resetPending = 0;
+ hd->numTMrequests = 0;
+
+ /* 6. If there was an internal command,
+ * wake this process up.
+ */
+ if (hd->cmdPtr) {
+ /*
+ * Wake up the original calling thread
+ */
+ hd->pLocal = &hd->localReply;
+ hd->pLocal->completion = MPT_SCANDV_DID_RESET;
+ scandv_wait_done = 1;
+ wake_up(&scandv_waitq);
+ hd->cmdPtr = NULL;
+ }
+
+ /* 7. Flush doneQ
+ */
+ flush_doneQ(hd);
+
+ dtmprintk((MYIOC_s_WARN_FMT "Post-Reset handling complete.\n",
+ ioc->name));
+ }
+
+ return 1; /* currently means nothing really */
+}
+
+/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
+static int
+mptscsih_event_process(MPT_ADAPTER *ioc, EventNotificationReply_t *pEvReply)
+{
+ MPT_SCSI_HOST *hd;
+ u8 event = le32_to_cpu(pEvReply->Event) & 0xFF;
+
+ dprintk((MYIOC_s_INFO_FMT "MPT event (=%02Xh) routed to SCSI host driver!\n",
+ ioc->name, event));
+
+ switch (event) {
+ case MPI_EVENT_UNIT_ATTENTION: /* 03 */
+ /* FIXME! */
+ break;
+ case MPI_EVENT_IOC_BUS_RESET: /* 04 */
+ /* FIXME! */
+ break;
+ case MPI_EVENT_EXT_BUS_RESET: /* 05 */
+ /* FIXME! */
+ break;
+ case MPI_EVENT_LOGOUT: /* 09 */
+ /* FIXME! */
+ break;
+
+ /*
+ * CHECKME! Don't think we need to do
+ * anything for these, but...
+ */
+ case MPI_EVENT_RESCAN: /* 06 */
+ case MPI_EVENT_LINK_STATUS_CHANGE: /* 07 */
+ case MPI_EVENT_LOOP_STATE_CHANGE: /* 08 */
+ /*
+ * CHECKME! Falling thru...
+ */
+ break;
+
+ case MPI_EVENT_INTEGRATED_RAID: /* 0B */
+#ifndef MPTSCSIH_DISABLE_DOMAIN_VALIDATION
+ /* negoNvram set to 0 if DV enabled and to USE_NVRAM if
+ * if DV disabled. Need to check for target mode.
+ */
+ hd = NULL;
+ if (ioc->sh)
+ hd = (MPT_SCSI_HOST *) ioc->sh->hostdata;
+
+ if (hd && (hd->is_spi) && (hd->negoNvram == 0)) {
+ ScsiCfgData *pSpi;
+ Ioc3PhysDisk_t *pPDisk;
+ int numPDisk;
+ u8 reason;
+ u8 physDiskNum;
+
+ reason = (le32_to_cpu(pEvReply->Data[0]) & 0x00FF0000) >> 16;
+ if (reason == MPI_EVENT_RAID_RC_DOMAIN_VAL_NEEDED) {
+ /* New or replaced disk.
+ * Set DV flag and schedule DV.
+ */
+ pSpi = &ioc->spi_data;
+ physDiskNum = (le32_to_cpu(pEvReply->Data[0]) & 0xFF000000) >> 24;
+ if (pSpi->pIocPg3) {
+ pPDisk = pSpi->pIocPg3->PhysDisk;
+ numPDisk =pSpi->pIocPg3->NumPhysDisks;
+
+ while (numPDisk) {
+ if (physDiskNum == pPDisk->PhysDiskNum) {
+ pSpi->dvStatus[pPDisk->PhysDiskID] = MPT_SCSICFG_NEED_DV;
+ pSpi->forceDv = MPT_SCSICFG_NEED_DV;
+ ddvtprintk(("NEED_DV set for phys disk id %d\n", pPDisk->PhysDiskID));
+ break;
+ }
+ pPDisk++;
+ numPDisk--;
+ }
+ }
+ }
+ }
+#endif
+
+#if defined(MPT_DEBUG_DV) || defined(MPT_DEBUG_DV_TINY)
+ printk("Raid Event RF: ");
+ {
+ u32 *m = (u32 *)pEvReply;
+ int ii;
+ int n = (int)pEvReply->MsgLength;
+ for (ii=6; ii < n; ii++)
+ printk(" %08x", le32_to_cpu(m[ii]));
+ printk("\n");
+ }
+#endif
+ break;
+
+ case MPI_EVENT_NONE: /* 00 */
+ case MPI_EVENT_LOG_DATA: /* 01 */
+ case MPI_EVENT_STATE_CHANGE: /* 02 */
+ case MPI_EVENT_EVENT_CHANGE: /* 0A */
+ default:
+ dprintk((KERN_INFO " Ignoring event (=%02Xh)\n", event));
+ break;
+ }
+
+ return 1; /* currently means nothing really */
+}
+
+#if 0 /* { */
+/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
+/*
+ * scsiherr.c - Fusion MPT SCSI Host driver error handling/reporting.
+ *
+ * drivers/message/fusion/scsiherr.c
+ */
+
+//extern const char **mpt_ScsiOpcodesPtr; /* needed by mptscsih.c */
+//extern ASCQ_Table_t *mpt_ASCQ_TablePtr;
+//extern int mpt_ASCQ_TableSz;
+
+#define MYNAM "mptscsih"
+
+#endif /* } */
+
+/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
+/*
+ * Private data...
+ */
+static ASCQ_Table_t *mptscsih_ASCQ_TablePtr;
+
+/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
+/* old symsense.c stuff... */
+/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
+/*
+ * Private data...
+ * To protect ourselves against those that would pass us bogus pointers
+ */
+static u8 dummyInqData[SCSI_STD_INQUIRY_BYTES]
+ = { 0x1F, 0x00, 0x00, 0x00,
+ 0x1F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
+static u8 dummySenseData[SCSI_STD_SENSE_BYTES]
+ = { 0x70, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0A,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00 };
+static u8 dummyCDB[16]
+ = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
+static u8 dummyScsiData[16]
+ = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
+
+#if 0
+static const char *PeripheralDeviceTypeString[32] = {
+ "Direct-access", /* 00h */
+ "Sequential-access", /* 01h */
+ "Printer", /* 02h */
+ "Processor", /* 03h */
+ /*"Write-Once-Read-Multiple",*/ /* 04h */
+ "WORM", /* 04h */
+ "CD-ROM", /* 05h */
"Scanner", /* 06h */
"Optical memory", /* 07h */
"Media Changer", /* 08h */
};
#endif
-static char *ScsiStatusString[] = {
- "GOOD", /* 00h */
- NULL, /* 01h */
- "CHECK CONDITION", /* 02h */
- NULL, /* 03h */
- "CONDITION MET", /* 04h */
- NULL, /* 05h */
- NULL, /* 06h */
- NULL, /* 07h */
- "BUSY", /* 08h */
- NULL, /* 09h */
- NULL, /* 0Ah */
- NULL, /* 0Bh */
- NULL, /* 0Ch */
- NULL, /* 0Dh */
- NULL, /* 0Eh */
- NULL, /* 0Fh */
- "INTERMEDIATE", /* 10h */
- NULL, /* 11h */
- NULL, /* 12h */
- NULL, /* 13h */
- "INTERMEDIATE-CONDITION MET", /* 14h */
- NULL, /* 15h */
- NULL, /* 16h */
- NULL, /* 17h */
- "RESERVATION CONFLICT", /* 18h */
- NULL, /* 19h */
- NULL, /* 1Ah */
- NULL, /* 1Bh */
- NULL, /* 1Ch */
- NULL, /* 1Dh */
- NULL, /* 1Eh */
- NULL, /* 1Fh */
- NULL, /* 20h */
- NULL, /* 21h */
- "COMMAND TERMINATED", /* 22h */
- NULL, /* 23h */
- NULL, /* 24h */
- NULL, /* 25h */
- NULL, /* 26h */
- NULL, /* 27h */
- "TASK SET FULL", /* 28h */
- NULL, /* 29h */
- NULL, /* 2Ah */
- NULL, /* 2Bh */
- NULL, /* 2Ch */
- NULL, /* 2Dh */
- NULL, /* 2Eh */
- NULL, /* 2Fh */
- "ACA ACTIVE", /* 30h */
- NULL
-};
+static char *ScsiStatusString[] = {
+ "GOOD", /* 00h */
+ NULL, /* 01h */
+ "CHECK CONDITION", /* 02h */
+ NULL, /* 03h */
+ "CONDITION MET", /* 04h */
+ NULL, /* 05h */
+ NULL, /* 06h */
+ NULL, /* 07h */
+ "BUSY", /* 08h */
+ NULL, /* 09h */
+ NULL, /* 0Ah */
+ NULL, /* 0Bh */
+ NULL, /* 0Ch */
+ NULL, /* 0Dh */
+ NULL, /* 0Eh */
+ NULL, /* 0Fh */
+ "INTERMEDIATE", /* 10h */
+ NULL, /* 11h */
+ NULL, /* 12h */
+ NULL, /* 13h */
+ "INTERMEDIATE-CONDITION MET", /* 14h */
+ NULL, /* 15h */
+ NULL, /* 16h */
+ NULL, /* 17h */
+ "RESERVATION CONFLICT", /* 18h */
+ NULL, /* 19h */
+ NULL, /* 1Ah */
+ NULL, /* 1Bh */
+ NULL, /* 1Ch */
+ NULL, /* 1Dh */
+ NULL, /* 1Eh */
+ NULL, /* 1Fh */
+ NULL, /* 20h */
+ NULL, /* 21h */
+ "COMMAND TERMINATED", /* 22h */
+ NULL, /* 23h */
+ NULL, /* 24h */
+ NULL, /* 25h */
+ NULL, /* 26h */
+ NULL, /* 27h */
+ "TASK SET FULL", /* 28h */
+ NULL, /* 29h */
+ NULL, /* 2Ah */
+ NULL, /* 2Bh */
+ NULL, /* 2Ch */
+ NULL, /* 2Dh */
+ NULL, /* 2Eh */
+ NULL, /* 2Fh */
+ "ACA ACTIVE", /* 30h */
+ NULL
+};
+
+static const char *ScsiCommonOpString[] = {
+ "TEST UNIT READY", /* 00h */
+ "REZERO UNIT (REWIND)", /* 01h */
+ NULL, /* 02h */
+ "REQUEST_SENSE", /* 03h */
+ "FORMAT UNIT (MEDIUM)", /* 04h */
+ "READ BLOCK LIMITS", /* 05h */
+ NULL, /* 06h */
+ "REASSIGN BLOCKS", /* 07h */
+ "READ(6)", /* 08h */
+ NULL, /* 09h */
+ "WRITE(6)", /* 0Ah */
+ "SEEK(6)", /* 0Bh */
+ NULL, /* 0Ch */
+ NULL, /* 0Dh */
+ NULL, /* 0Eh */
+ "READ REVERSE", /* 0Fh */
+ "WRITE_FILEMARKS", /* 10h */
+ "SPACE(6)", /* 11h */
+ "INQUIRY", /* 12h */
+ NULL
+};
+
+static const char *SenseKeyString[] = {
+ "NO SENSE", /* 0h */
+ "RECOVERED ERROR", /* 1h */
+ "NOT READY", /* 2h */
+ "MEDIUM ERROR", /* 3h */
+ "HARDWARE ERROR", /* 4h */
+ "ILLEGAL REQUEST", /* 5h */
+ "UNIT ATTENTION", /* 6h */
+ "DATA PROTECT", /* 7h */
+ "BLANK CHECK", /* 8h */
+ "VENDOR-SPECIFIC", /* 9h */
+ "ABORTED COPY", /* Ah */
+ "ABORTED COMMAND", /* Bh */
+ "EQUAL (obsolete)", /* Ch */
+ "VOLUME OVERFLOW", /* Dh */
+ "MISCOMPARE", /* Eh */
+ "RESERVED", /* Fh */
+ NULL
+};
+
+#define SPECIAL_ASCQ(c,q) \
+ (((c) == 0x40 && (q) != 0x00) || ((c) == 0x4D) || ((c) == 0x70))
+
+#if 0
+/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
+/*
+ * Sense_Key_Specific() - If Sense_Key_Specific_Valid bit is set,
+ * then print additional information via
+ * a call to SDMS_SystemAlert().
+ */
+static void Sense_Key_Specific(IO_Info_t *ioop, char *msg1)
+{
+ u8 *sd;
+ u8 BadValue;
+ u8 SenseKey;
+ int Offset;
+ int len = strlen(msg1);
+
+ sd = ioop->sensePtr;
+ if (SD_Additional_Sense_Length(sd) < 8)
+ return;
+
+ SenseKey = SD_Sense_Key(sd);
+
+ if (SD_Sense_Key_Specific_Valid(sd)) {
+ if (SenseKey == SK_ILLEGAL_REQUEST) {
+ Offset = SD_Bad_Byte(sd);
+ if (SD_Was_Illegal_Request(sd)) {
+ BadValue = ioop->cdbPtr[Offset];
+ len += sprintf(msg1+len, "\n Illegal CDB value=%02Xh found at CDB ",
+ BadValue);
+ } else {
+ BadValue = ioop->dataPtr[Offset];
+ len += sprintf(msg1+len, "\n Illegal DATA value=%02Xh found at DATA ",
+ BadValue);
+ }
+ len += sprintf(msg1+len, "byte=%02Xh", Offset);
+ if (SD_SKS_Bit_Pointer_Valid(sd))
+ len += sprintf(msg1+len, "/bit=%1Xh", SD_SKS_Bit_Pointer(sd));
+ } else if ((SenseKey == SK_RECOVERED_ERROR) ||
+ (SenseKey == SK_HARDWARE_ERROR) ||
+ (SenseKey == SK_MEDIUM_ERROR)) {
+ len += sprintf(msg1+len, "\n Recovery algorithm Actual_Retry_Count=%02Xh",
+ SD_Actual_Retry_Count(sd));
+ }
+ }
+}
+#endif
+
+/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
+static int dump_cdb(char *foo, unsigned char *cdb)
+{
+ int i, grpCode, cdbLen;
+ int l = 0;
+
+ grpCode = cdb[0] >> 5;
+ if (grpCode < 1)
+ cdbLen = 6;
+ else if (grpCode < 3)
+ cdbLen = 10;
+ else if (grpCode == 5)
+ cdbLen = 12;
+ else
+ cdbLen = 16;
+
+ for (i=0; i < cdbLen; i++)
+ l += sprintf(foo+l, " %02X", cdb[i]);
+
+ return l;
+}
+
+/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
+static int dump_sd(char *foo, unsigned char *sd)
+{
+ int snsLen = 8 + SD_Additional_Sense_Length(sd);
+ int l = 0;
+ int i;
+
+ for (i=0; i < MIN(snsLen,18); i++)
+ l += sprintf(foo+l, " %02X", sd[i]);
+ l += sprintf(foo+l, "%s", snsLen>18 ? " ..." : "");
+
+ return l;
+}
+
+/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
+/* Do ASC/ASCQ lookup/grindage to English readable string(s) */
+static const char * ascq_set_strings_4max(
+ u8 ASC, u8 ASCQ,
+ const char **s1, const char **s2, const char **s3, const char **s4)
+{
+ static const char *asc_04_part1_string = "LOGICAL UNIT ";
+ static const char *asc_04_part2a_string = "NOT READY, ";
+ static const char *asc_04_part2b_string = "IS ";
+ static const char *asc_04_ascq_NN_part3_strings[] = { /* ASC ASCQ (hex) */
+ "CAUSE NOT REPORTABLE", /* 04 00 */
+ "IN PROCESS OF BECOMING READY", /* 04 01 */
+ "INITIALIZING CMD. REQUIRED", /* 04 02 */
+ "MANUAL INTERVENTION REQUIRED", /* 04 03 */
+ /* Add " IN PROGRESS" to all the following... */
+ "FORMAT", /* 04 04 */
+ "REBUILD", /* 04 05 */
+ "RECALCULATION", /* 04 06 */
+ "OPERATION", /* 04 07 */
+ "LONG WRITE", /* 04 08 */
+ "SELF-TEST", /* 04 09 */
+ NULL
+ };
+ static char *asc_04_part4_string = " IN PROGRESS";
+
+ static char *asc_29_ascq_NN_strings[] = { /* ASC ASCQ (hex) */
+ "POWER ON, RESET, OR BUS DEVICE RESET OCCURRED", /* 29 00 */
+ "POWER ON OCCURRED", /* 29 01 */
+ "SCSI BUS RESET OCCURRED", /* 29 02 */
+ "BUS DEVICE RESET FUNCTION OCCURRED", /* 29 03 */
+ "DEVICE INTERNAL RESET", /* 29 04 */
+ "TRANSCEIVER MODE CHANGED TO SINGLE-ENDED", /* 29 05 */
+ "TRANSCEIVER MODE CHANGED TO LVD", /* 29 06 */
+ NULL
+ };
+ static char *ascq_vendor_uniq = "(Vendor Unique)";
+ static char *ascq_noone = "(no matching ASC/ASCQ description found)";
+ int idx;
+
+ *s1 = *s2 = *s3 = *s4 = ""; /* set'em all to the empty "" string */
+
+ /* CHECKME! Need lock/sem?
+ * Update and examine for isense module presense.
+ */
+ mptscsih_ASCQ_TablePtr = (ASCQ_Table_t *)mpt_v_ASCQ_TablePtr;
+
+ if (mptscsih_ASCQ_TablePtr == NULL) {
+ /* 2nd chances... */
+ if (ASC == 0x04 && (ASCQ < sizeof(asc_04_ascq_NN_part3_strings)/sizeof(char*)-1)) {
+ *s1 = asc_04_part1_string;
+ *s2 = (ASCQ == 0x01) ? asc_04_part2b_string : asc_04_part2a_string;
+ *s3 = asc_04_ascq_NN_part3_strings[ASCQ];
+ /* check for " IN PROGRESS" ones */
+ if (ASCQ >= 0x04)
+ *s4 = asc_04_part4_string;
+ } else if (ASC == 0x29 && (ASCQ < sizeof(asc_29_ascq_NN_strings)/sizeof(char*)-1))
+ *s1 = asc_29_ascq_NN_strings[ASCQ];
+ /*
+ * Else { leave all *s[1-4] values pointing to the empty "" string }
+ */
+ return *s1;
+ }
+
+ /*
+ * Need to check ASC here; if it is "special," then
+ * the ASCQ is variable, and indicates failed component number.
+ * We must treat the ASCQ as a "dont care" while searching the
+ * mptscsih_ASCQ_Table[] by masking it off, and then restoring it later
+ * on when we actually need to identify the failed component.
+ */
+ if (SPECIAL_ASCQ(ASC,ASCQ))
+ ASCQ = 0xFF;
+
+ /* OK, now search mptscsih_ASCQ_Table[] for a matching entry */
+ for (idx = 0; mptscsih_ASCQ_TablePtr && idx < mpt_ASCQ_TableSz; idx++)
+ if ((ASC == mptscsih_ASCQ_TablePtr[idx].ASC) && (ASCQ == mptscsih_ASCQ_TablePtr[idx].ASCQ)) {
+ *s1 = mptscsih_ASCQ_TablePtr[idx].Description;
+ return *s1;
+ }
+
+ if ((ASC >= 0x80) || (ASCQ >= 0x80))
+ *s1 = ascq_vendor_uniq;
+ else
+ *s1 = ascq_noone;
+
+ return *s1;
+}
+
+/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
+/*
+ * SCSI Error Report; desired output format...
+ *---
+SCSI Error Report =-=-=-=-=-=-=-=-=-=-=-=-=-= (ioc0,scsi0:0)
+ SCSI_Status=02h (CHECK CONDITION)
+ Original_CDB[]: 00 00 00 00 00 00 - TestUnitReady
+ SenseData[12h]: 70 00 06 00 00 00 00 0A 00 00 00 00 29 00 03 00 00 00
+ SenseKey=6h (UNIT ATTENTION); FRU=03h
+ ASC/ASCQ=29h/00h, "POWER ON, RESET, OR BUS DEVICE RESET OCCURRED"
+ *---
+ */
+
+int mpt_ScsiHost_ErrorReport(IO_Info_t *ioop)
+{
+ char foo[512];
+ char buf2[32];
+ char *statstr;
+ const char *opstr;
+ int sk = SD_Sense_Key(ioop->sensePtr);
+ const char *skstr = SenseKeyString[sk];
+ unsigned char asc = SD_ASC(ioop->sensePtr);
+ unsigned char ascq = SD_ASCQ(ioop->sensePtr);
+ int l;
+
+ /*
+ * More quiet mode.
+ * Filter out common, repetitive, warning-type errors... like:
+ * POWER ON (06,29/00 or 06,29/01),
+ * SPINNING UP (02,04/01),
+ * LOGICAL UNIT NOT SUPPORTED (05,25/00), etc.
+ */
+ if (sk == SK_NO_SENSE) {
+ return 0;
+ }
+ if ( (sk==SK_UNIT_ATTENTION && asc==0x29 && (ascq==0x00 || ascq==0x01))
+ || (sk==SK_NOT_READY && asc==0x04 && (ascq==0x01 || ascq==0x02))
+ || (sk==SK_ILLEGAL_REQUEST && asc==0x25 && ascq==0x00)
+ )
+ {
+ /* Do nothing! */
+ return 0;
+ }
+
+ /*
+ * Protect ourselves...
+ */
+ if (ioop->cdbPtr == NULL)
+ ioop->cdbPtr = dummyCDB;
+ if (ioop->sensePtr == NULL)
+ ioop->sensePtr = dummySenseData;
+ if (ioop->inqPtr == NULL)
+ ioop->inqPtr = dummyInqData;
+ if (ioop->dataPtr == NULL)
+ ioop->dataPtr = dummyScsiData;
+
+ statstr = NULL;
+ if ((ioop->SCSIStatus >= sizeof(ScsiStatusString)/sizeof(char*)-1) ||
+ ((statstr = (char*)ScsiStatusString[ioop->SCSIStatus]) == NULL)) {
+ (void) sprintf(buf2, "Bad-Reserved-%02Xh", ioop->SCSIStatus);
+ statstr = buf2;
+ }
+
+ opstr = NULL;
+ if (1+ioop->cdbPtr[0] <= sizeof(ScsiCommonOpString)/sizeof(char*))
+ opstr = ScsiCommonOpString[ioop->cdbPtr[0]];
+ else if (mpt_ScsiOpcodesPtr)
+ opstr = mpt_ScsiOpcodesPtr[ioop->cdbPtr[0]];
+
+ l = sprintf(foo, "SCSI Error Report =-=-= (%s)\n"
+ " SCSI_Status=%02Xh (%s)\n"
+ " Original_CDB[]:",
+ ioop->DevIDStr,
+ ioop->SCSIStatus,
+ statstr);
+ l += dump_cdb(foo+l, ioop->cdbPtr);
+ if (opstr)
+ l += sprintf(foo+l, " - \"%s\"", opstr);
+ l += sprintf(foo+l, "\n SenseData[%02Xh]:", 8+SD_Additional_Sense_Length(ioop->sensePtr));
+ l += dump_sd(foo+l, ioop->sensePtr);
+ l += sprintf(foo+l, "\n SenseKey=%Xh (%s); FRU=%02Xh\n ASC/ASCQ=%02Xh/%02Xh",
+ sk, skstr, SD_FRU(ioop->sensePtr), asc, ascq );
+
+ {
+ const char *x1, *x2, *x3, *x4;
+ x1 = x2 = x3 = x4 = "";
+ x1 = ascq_set_strings_4max(asc, ascq, &x1, &x2, &x3, &x4);
+ if (x1 != NULL) {
+ if (x1[0] != '(')
+ l += sprintf(foo+l, " \"%s%s%s%s\"", x1,x2,x3,x4);
+ else
+ l += sprintf(foo+l, " %s%s%s%s", x1,x2,x3,x4);
+ }
+ }
+
+#if 0
+ if (SPECIAL_ASCQ(asc,ascq))
+ l += sprintf(foo+l, " (%02Xh)", ascq);
+#endif
+
+ PrintF(("%s\n", foo));
+
+ return l;
+}
+
+/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
+
+/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
+/*
+ * mptscsih_initTarget - Target, LUN alloc/free functionality.
+ * @hd: Pointer to MPT_SCSI_HOST structure
+ * @bus_id: Bus number (?)
+ * @target_id: SCSI target id
+ * @lun: SCSI LUN id
+ * @data: Pointer to data
+ * @dlen: Number of INQUIRY bytes
+ *
+ * NOTE: It's only SAFE to call this routine if data points to
+ * sane & valid STANDARD INQUIRY data!
+ *
+ * Allocate and initialize memory for this target.
+ * Save inquiry data.
+ *
+ * Returns pointer to VirtDevice structure.
+ */
+static VirtDevice *
+mptscsih_initTarget(MPT_SCSI_HOST *hd, int bus_id, int target_id, u8 lun, char *data, int dlen)
+{
+ VirtDevice *vdev;
+ int sz;
+
+ dprintk((MYIOC_s_INFO_FMT "initTarget (%d,%d,%d) called, hd=%p\n",
+ hd->ioc->name, bus_id, target_id, lun, hd));
+
+ if ((vdev = hd->Targets[target_id]) == NULL) {
+ if ((vdev = kmalloc(sizeof(VirtDevice), GFP_ATOMIC)) == NULL) {
+ printk(MYIOC_s_ERR_FMT "initTarget kmalloc(%d) FAILED!\n",
+ hd->ioc->name, (int)sizeof(VirtDevice));
+ } else {
+ memset(vdev, 0, sizeof(VirtDevice));
+ rwlock_init(&vdev->VdevLock);
+ Q_INIT(&vdev->WaitQ, void);
+ Q_INIT(&vdev->SentQ, void);
+ Q_INIT(&vdev->DoneQ, void);
+ vdev->tflags = 0;
+ vdev->ioc_id = hd->ioc->id;
+ vdev->target_id = target_id;
+ vdev->bus_id = bus_id;
+
+ hd->Targets[target_id] = vdev;
+ dprintk((KERN_INFO " *NEW* Target structure (id %d) @ %p\n",
+ target_id, vdev));
+ }
+ }
+
+ if (vdev) {
+ if (hd->ioc->spi_data.isRaid & (1 << target_id))
+ vdev->raidVolume = 1;
+ else
+ vdev->raidVolume = 0;
+ }
+
+ if (vdev && data) {
+ if (!(vdev->tflags & MPT_TARGET_FLAGS_VALID_INQUIRY)) {
+
+ /* Copy the inquiry data - if we haven't yet.
+ */
+ sz = MIN(dlen, SCSI_STD_INQUIRY_BYTES);
+
+ memcpy (vdev->inq_data, data, sz);
+ vdev->tflags |= MPT_TARGET_FLAGS_VALID_INQUIRY;
+
+ /* Update the target capabilities
+ */
+ if (dlen > 56)
+ mptscsih_setTargetNegoParms(hd, vdev, data[56]);
+ else
+ mptscsih_setTargetNegoParms(hd, vdev, 0);
+ }
+
+ /* Is LUN supported? If so, upper 3 bits will be 0
+ * in first byte of inquiry data.
+ */
+ if ((*data & 0xe0) == 0)
+ vdev->luns |= (1 << lun);
+ }
+
+
+ dprintk((KERN_INFO " target = %p\n", vdev));
+ return vdev;
+}
+
+/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
+/*
+ * Update the target negotiation parameters based on the
+ * the Inquiry data, adapter capabilities, and NVRAM settings.
+ *
+ */
+void mptscsih_setTargetNegoParms(MPT_SCSI_HOST *hd, VirtDevice *target, char byte56)
+{
+ ScsiCfgData *pspi_data = &hd->ioc->spi_data;
+ int id = (int) target->target_id;
+ int nvram;
+ char canQ = 0;
+ u8 width = MPT_NARROW;
+ u8 factor = MPT_ASYNC;
+ u8 offset = 0;
+ u8 version, nfactor;
+ u8 noQas = 1;
+
+ /* Set flags based on Inquiry data
+ */
+ if (target->tflags & MPT_TARGET_FLAGS_VALID_INQUIRY) {
+ version = target->inq_data[2] & 0x03;
+ if (version < 2) {
+ width = 0;
+ factor = MPT_ULTRA2;
+ offset = pspi_data->maxSyncOffset;
+ } else {
+ if (target->inq_data[7] & 0x20) {
+ width = 1;
+ }
+
+ if (target->inq_data[7] & 0x10) {
+ /* bits 2 & 3 show DT support
+ */
+ if ((byte56 & 0x04) == 0)
+ factor = MPT_ULTRA2;
+ else
+ factor = MPT_ULTRA320;
+
+ /* bit 1 QAS support, non-raid only
+ */
+ if ((target->raidVolume == 0) && (byte56 & 0x02) != 0)
+ noQas = 0;
+
+ offset = pspi_data->maxSyncOffset;
+ } else {
+ factor = MPT_ASYNC;
+ offset = 0;
+ }
+ }
+
+ if (target->inq_data[7] & 0x02) {
+ canQ = 1;
+ }
+
+ /* Update tflags based on NVRAM settings. (SCSI only)
+ */
+ if (pspi_data->nvram && (pspi_data->nvram[id] != MPT_HOST_NVRAM_INVALID)) {
+ nvram = pspi_data->nvram[id];
+ nfactor = (nvram & MPT_NVRAM_SYNC_MASK) >> 8;
+
+ if (width)
+ width = nvram & MPT_NVRAM_WIDE_DISABLE ? 0 : 1;
+
+ if (offset > 0) {
+ /* Ensure factor is set to the
+ * maximum of: adapter, nvram, inquiry
+ */
+ if (nfactor) {
+ if (nfactor < pspi_data->minSyncFactor )
+ nfactor = pspi_data->minSyncFactor;
+
+ factor = MAX (factor, nfactor);
+ if (factor == MPT_ASYNC)
+ offset = 0;
+ } else {
+ offset = 0;
+ factor = MPT_ASYNC;
+ }
+ } else {
+ factor = MPT_ASYNC;
+ }
+ }
+
+ /* Make sure data is consistent
+ */
+ if ((!width) && (factor < MPT_ULTRA2)) {
+ factor = MPT_ULTRA2;
+ }
+
+ /* Save the data to the target structure.
+ */
+ target->minSyncFactor = factor;
+ target->maxOffset = offset;
+ target->maxWidth = width;
+ if (canQ) {
+ target->tflags |= MPT_TARGET_FLAGS_Q_YES;
+ }
+
+ target->tflags |= MPT_TARGET_FLAGS_VALID_NEGO;
+
+ /* Disable unused features.
+ */
+ target->negoFlags = pspi_data->noQas;
+ if (!width)
+ target->negoFlags |= MPT_TARGET_NO_NEGO_WIDE;
+
+ if (!offset)
+ target->negoFlags |= MPT_TARGET_NO_NEGO_SYNC;
+
+ if (noQas)
+ target->negoFlags |= MPT_TARGET_NO_NEGO_QAS;
+
+ /* GEM, processor WORKAROUND
+ */
+ if (((target->inq_data[0] & 0x1F) == 0x03) || ((target->inq_data[0] & 0x1F) > 0x08)){
+ target->negoFlags |= (MPT_TARGET_NO_NEGO_WIDE | MPT_TARGET_NO_NEGO_SYNC);
+ pspi_data->dvStatus[id] |= MPT_SCSICFG_BLK_NEGO;
+ }
+
+ /* Disable QAS if mixed configuration case
+ */
+ if ((noQas) && (!pspi_data->noQas) && ((target->inq_data[0] & 0x1F) == 0x00)){
+ VirtDevice *vdev;
+ int ii;
+
+ pspi_data->noQas = MPT_TARGET_NO_NEGO_QAS;
+ for (ii = 0; ii < id; ii++) {
+ vdev = hd->Targets[id];
+ if (vdev != NULL)
+ vdev->negoFlags |= MPT_TARGET_NO_NEGO_QAS;
+ }
+ }
+
+ }
+
+ return;
+}
+
+/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
+/*
+ * Clear sense valid flag.
+ */
+static void clear_sense_flag(MPT_SCSI_HOST *hd, SCSIIORequest_t *pReq)
+{
+ VirtDevice *target;
+ int index = (int) pReq->TargetID;
+
+ if ((target = hd->Targets[index])) {
+ target->tflags &= ~MPT_TARGET_FLAGS_VALID_SENSE;
+ }
+
+ return;
+}
+
+/*
+ * If DV disabled (negoNvram set to USE_NVARM) or if not LUN 0, return.
+ * Else set the NEED_DV flag after Read Capacity Issued (disks)
+ * or Mode Sense (cdroms). Tapes, key off of Inquiry command.
+ */
+static void mptscsih_set_dvflags(MPT_SCSI_HOST *hd, SCSIIORequest_t *pReq, char *data)
+{
+ u8 cmd = pReq->CDB[0];
+
+ if (pReq->LUN[1] != 0)
+ return;
+
+ if (hd->negoNvram != 0)
+ return;
+
+ if ((cmd == READ_CAPACITY) || (cmd == MODE_SENSE) ||
+ ((cmd == INQUIRY) && ((data[0] & 0x1F) == 0x01))) {
+ u8 dvStatus = hd->ioc->spi_data.dvStatus[pReq->TargetID];
+ if (!(dvStatus & MPT_SCSICFG_DV_DONE)) {
+ ScsiCfgData *pSpi = &hd->ioc->spi_data;
+ if ((pSpi->isRaid & (1 << pReq->TargetID)) && pSpi->pIocPg3) {
+ /* Set NEED_DV for all hidden disks
+ */
+ Ioc3PhysDisk_t *pPDisk = pSpi->pIocPg3->PhysDisk;
+ int numPDisk = pSpi->pIocPg3->NumPhysDisks;
+
+ while (numPDisk) {
+ pSpi->dvStatus[pPDisk->PhysDiskID] |= MPT_SCSICFG_NEED_DV;
+ ddvtprintk(("NEED_DV set for phys disk id %d\n", pPDisk->PhysDiskID));
+ pPDisk++;
+ numPDisk--;
+ }
+ }
+ pSpi->dvStatus[pReq->TargetID] |= MPT_SCSICFG_NEED_DV;
+ ddvtprintk(("NEED_DV set for visible disk id %d\n",
+ pReq->TargetID));
+ };
+ }
+}
+
+/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
+/*
+ * If no Target, bus reset on 1st I/O. Set the flag to
+ * prevent any future negotiations to this device.
+ */
+static void mptscsih_no_negotiate(MPT_SCSI_HOST *hd, int target_id)
+{
+
+ if ((hd->Targets) && (hd->Targets[target_id] == NULL))
+ hd->ioc->spi_data.dvStatus[target_id] |= MPT_SCSICFG_BLK_NEGO;
+
+ return;
+}
+
+/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
+/*
+ * SCSI Config Page functionality ...
+ */
+/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
+/* mptscsih_setDevicePage1Flags - add Requested and Configuration fields flags
+ * based on width, factor and offset parameters.
+ * @width: bus width
+ * @factor: sync factor
+ * @offset: sync offset
+ * @requestedPtr: pointer to requested values (updated)
+ * @configurationPtr: pointer to configuration values (updated)
+ * @flags: flags to block WDTR or SDTR negotiation
+ *
+ * Return: None.
+ *
+ * Remark: Called by writeSDP1 and _dv_params
+ */
+static void
+mptscsih_setDevicePage1Flags (u8 width, u8 factor, u8 offset, int *requestedPtr, int *configurationPtr, u8 flags)
+{
+ u8 nowide = flags & MPT_TARGET_NO_NEGO_WIDE;
+ u8 nosync = flags & MPT_TARGET_NO_NEGO_SYNC;
+
+ *configurationPtr = 0;
+ *requestedPtr = width ? MPI_SCSIDEVPAGE1_RP_WIDE : 0;
+ *requestedPtr |= (offset << 16) | (factor << 8);
+
+ if (width && offset && !nowide && !nosync) {
+ if (factor < MPT_ULTRA160) {
+ *requestedPtr |= (MPI_SCSIDEVPAGE1_RP_IU + MPI_SCSIDEVPAGE1_RP_DT);
+ if ((flags & MPT_TARGET_NO_NEGO_QAS) == 0)
+ *requestedPtr |= MPI_SCSIDEVPAGE1_RP_QAS;
+ } else if (factor < MPT_ULTRA2) {
+ *requestedPtr |= MPI_SCSIDEVPAGE1_RP_DT;
+ }
+ }
+
+ if (nowide)
+ *configurationPtr |= MPI_SCSIDEVPAGE1_CONF_WDTR_DISALLOWED;
+
+ if (nosync)
+ *configurationPtr |= MPI_SCSIDEVPAGE1_CONF_SDTR_DISALLOWED;
+
+ return;
+}
+
+/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
+/* mptscsih_writeSDP1 - write SCSI Device Page 1
+ * @hd: Pointer to a SCSI Host Strucutre
+ * @portnum: IOC port number
+ * @target_id: writeSDP1 for single ID
+ * @flags: MPT_SCSICFG_ALL_IDS, MPT_SCSICFG_USE_NVRAM, MPT_SCSICFG_BLK_NEGO
+ *
+ * Return: -EFAULT if read of config page header fails
+ * or 0 if success.
+ *
+ * Remark: If a target has been found, the settings from the
+ * target structure are used, else the device is set
+ * to async/narrow.
+ *
+ * Remark: Called during init and after a FW reload.
+ * Remark: We do not wait for a return, write pages sequentially.
+ */
+static int
+mptscsih_writeSDP1(MPT_SCSI_HOST *hd, int portnum, int target_id, int flags)
+{
+ MPT_ADAPTER *ioc = hd->ioc;
+ Config_t *pReq = NULL;
+ SCSIDevicePage1_t *pData = NULL;
+ VirtDevice *pTarget = NULL;
+ MPT_FRAME_HDR *mf;
+ dma_addr_t dataDma;
+ u16 req_idx;
+ u32 frameOffset;
+ u32 requested, configuration, flagsLength;
+ int ii, nvram;
+ int id = 0, maxid = 0;
+ u8 width;
+ u8 factor;
+ u8 offset;
+ u8 bus = 0;
+ u8 negoFlags;
+
+ if (ioc->spi_data.sdp1length == 0)
+ return 0;
+
+ if (flags & MPT_SCSICFG_ALL_IDS) {
+ id = 0;
+ maxid = ioc->sh->max_id - 1;
+ } else if (ioc->sh) {
+ id = target_id;
+ maxid = MIN(id, ioc->sh->max_id - 1);
+ }
+
+ for (; id <= maxid; id++) {
+ if (id == ioc->pfacts[portnum].PortSCSIID)
+ continue;
+
+ if (flags & MPT_SCSICFG_USE_NVRAM) {
+ /* Use NVRAM, adapter maximums and target settings.
+ * Data over-riden by target structure information, if present
+ */
+ width = ioc->spi_data.maxBusWidth;
+ offset = ioc->spi_data.maxSyncOffset;
+ factor = ioc->spi_data.minSyncFactor;
+ if (ioc->spi_data.nvram && (ioc->spi_data.nvram[id] != MPT_HOST_NVRAM_INVALID)) {
+ nvram = ioc->spi_data.nvram[id];
+
+ if (width)
+ width = nvram & MPT_NVRAM_WIDE_DISABLE ? 0 : 1;
+
+ if (offset > 0) {
+ factor = (nvram & MPT_NVRAM_SYNC_MASK) >> 8;
+ if (factor == 0) {
+ /* Key for async */
+ factor = MPT_ASYNC;
+ offset = 0;
+ } else if (factor < ioc->spi_data.minSyncFactor) {
+ factor = ioc->spi_data.minSyncFactor;
+ }
+ } else
+ factor = MPT_ASYNC;
+ }
+
+ /* Set the negotiation flags.
+ */
+ negoFlags = ioc->spi_data.noQas;
+ if (!width)
+ negoFlags |= MPT_TARGET_NO_NEGO_WIDE;
+
+ if (!offset)
+ negoFlags |= MPT_TARGET_NO_NEGO_SYNC;
+ } else {
+ width = 0;
+ factor = MPT_ASYNC;
+ offset = 0;
+ negoFlags = MPT_TARGET_NO_NEGO_SYNC;
+ }
+
+ /* If id is not a raid volume, get the updated
+ * transmission settings from the target structure.
+ */
+ if (hd->Targets && (pTarget = hd->Targets[id]) && !pTarget->raidVolume) {
+ width = pTarget->maxWidth;
+ factor = pTarget->minSyncFactor;
+ offset = pTarget->maxOffset;
+ negoFlags = pTarget->negoFlags;
+ pTarget = NULL;
+ }
+
+ if (flags & MPT_SCSICFG_BLK_NEGO)
+ negoFlags = MPT_TARGET_NO_NEGO_WIDE | MPT_TARGET_NO_NEGO_SYNC;
+
+ /* REMOVE - can this be removed without problems?????
+ else if (negoFlags == (MPT_TARGET_NO_NEGO_WIDE | MPT_TARGET_NO_NEGO_SYNC))
+ continue;
+ */
+
+ mptscsih_setDevicePage1Flags(width, factor, offset,
+ &requested, &configuration, negoFlags);
+
+
+ /* Get a MF for this command.
+ */
+ if ((mf = mpt_get_msg_frame(ScsiDoneCtx, ioc->id)) == NULL) {
+ dprintk((MYIOC_s_WARN_FMT "write SDP1: no msg frames!\n",
+ ioc->name));
+ return -EAGAIN;
+ }
+
+ /* Set the request and the data pointers.
+ * Request takes: 36 bytes (32 bit SGE)
+ * SCSI Device Page 1 requires 16 bytes
+ * 40 + 16 <= size of SCSI IO Request = 56 bytes
+ * and MF size >= 64 bytes.
+ * Place data at end of MF.
+ */
+ pReq = (Config_t *)mf;
+
+ req_idx = le16_to_cpu(mf->u.frame.hwhdr.msgctxu.fld.req_idx);
+ frameOffset = ioc->req_sz - sizeof(SCSIDevicePage1_t);
+
+ pData = (SCSIDevicePage1_t *)((u8 *) mf + frameOffset);
+ dataDma = ioc->req_frames_dma + (req_idx * ioc->req_sz) + frameOffset;
+
+ /* Complete the request frame (same for all requests).
+ */
+ pReq->Action = MPI_CONFIG_ACTION_PAGE_WRITE_CURRENT;
+ pReq->Reserved = 0;
+ pReq->ChainOffset = 0;
+ pReq->Function = MPI_FUNCTION_CONFIG;
+ pReq->Reserved1[0] = 0;
+ pReq->Reserved1[1] = 0;
+ pReq->Reserved1[2] = 0;
+ pReq->MsgFlags = 0;
+ for (ii=0; ii < 8; ii++) {
+ pReq->Reserved2[ii] = 0;
+ }
+ pReq->Header.PageVersion = ioc->spi_data.sdp1version;
+ pReq->Header.PageLength = ioc->spi_data.sdp1length;
+ pReq->Header.PageNumber = 1;
+ pReq->Header.PageType = MPI_CONFIG_PAGETYPE_SCSI_DEVICE;
+ pReq->PageAddress = cpu_to_le32(id | (bus << 8 ));
+
+ /* Add a SGE to the config request.
+ */
+ flagsLength = MPT_SGE_FLAGS_SSIMPLE_WRITE | ioc->spi_data.sdp1length * 4;
+
+ mpt_add_sge((char *)&pReq->PageBufferSGE, flagsLength, dataDma);
+
+ /* Set up the common data portion
+ */
+ pData->Header.PageVersion = pReq->Header.PageVersion;
+ pData->Header.PageLength = pReq->Header.PageLength;
+ pData->Header.PageNumber = pReq->Header.PageNumber;
+ pData->Header.PageType = pReq->Header.PageType;
+ pData->RequestedParameters = cpu_to_le32(requested);
+ pData->Reserved = 0;
+ pData->Configuration = cpu_to_le32(configuration);
+
+ dprintk((MYIOC_s_INFO_FMT
+ "write SDP1: id %d pgaddr 0x%x req 0x%x config 0x%x\n",
+ ioc->name, id, (id | (bus<<8)),
+ requested, configuration));
+
+ mptscsih_put_msgframe(ScsiDoneCtx, ioc->id, mf);
+ }
+
+ return 0;
+}
+
+/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
+/* mptscsih_taskmgmt_timeout - Call back for timeout on a
+ * task management request.
+ * @data: Pointer to MPT_SCSI_HOST recast as an unsigned long
+ *
+ */
+static void mptscsih_taskmgmt_timeout(unsigned long data)
+{
+ MPT_SCSI_HOST *hd = (MPT_SCSI_HOST *) data;
+
+ dtmprintk((KERN_WARNING MYNAM ": %s: mptscsih_taskmgmt_timeout: "
+ "TM request timed out!\n", hd->ioc->name));
+
+ /* Delete the timer that triggered this callback.
+ * Remark: del_timer checks to make sure timer is active
+ * before deleting.
+ */
+ del_timer(&hd->TMtimer);
+
+#ifdef MPT_SCSI_USE_NEW_EH
+ /* Set the error flag to 1 so that the function that started the
+ * task management request knows it timed out.
+ */
+ hd->tmState = TM_STATE_ERROR;
+#endif
+
+ /* Call the reset handler. Already had a TM request
+ * timeout - so issue a diagnostic reset
+ */
+ if (mpt_HardResetHandler(hd->ioc, NO_SLEEP) < 0) {
+ printk((KERN_WARNING " Firmware Reload FAILED!!\n"));
+ }
+#ifdef MPT_SCSI_USE_NEW_EH
+ else {
+ /* Because we have reset the IOC, no TM requests can be
+ * pending. So let's make sure the tmPending flag is reset.
+ */
+ nehprintk((KERN_WARNING MYNAM
+ ": %s: mptscsih_taskmgmt_timeout\n",
+ hd->ioc->name));
+ hd->tmPending = 0;
+ }
+#endif
+
+ return;
+}
+
+/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
+/*
+ * Bus Scan and Domain Validation functionality ...
+ */
+
+/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
+/*
+ * mptscsih_scandv_complete - Scan and DV callback routine registered
+ * to Fustion MPT (base) driver.
+ *
+ * @ioc: Pointer to MPT_ADAPTER structure
+ * @mf: Pointer to original MPT request frame
+ * @mr: Pointer to MPT reply frame (NULL if TurboReply)
+ *
+ * This routine is called from mpt.c::mpt_interrupt() at the completion
+ * of any SCSI IO request.
+ * This routine is registered with the Fusion MPT (base) driver at driver
+ * load/init time via the mpt_register() API call.
+ *
+ * Returns 1 indicating alloc'd request frame ptr should be freed.
+ *
+ * Remark: Sets a completion code and (possibly) saves sense data
+ * in the IOC member localReply structure.
+ * Used ONLY for bus scan, DV and other internal commands.
+ */
+static int
+mptscsih_scandv_complete(MPT_ADAPTER *ioc, MPT_FRAME_HDR *mf, MPT_FRAME_HDR *mr)
+{
+ MPT_SCSI_HOST *hd;
+ SCSIIORequest_t *pReq;
+ int completionCode;
+ u16 req_idx;
+
+ if ((mf == NULL) ||
+ (mf >= MPT_INDEX_2_MFPTR(ioc, ioc->req_depth))) {
+ printk(MYIOC_s_ERR_FMT
+ "ScanDvComplete, %s req frame ptr! (=%p)\n",
+ ioc->name, mf?"BAD":"NULL", (void *) mf);
+ goto wakeup;
+ }
+
+ hd = (MPT_SCSI_HOST *) ioc->sh->hostdata;
+ del_timer(&hd->timer);
+ req_idx = le16_to_cpu(mf->u.frame.hwhdr.msgctxu.fld.req_idx);
+ hd->ScsiLookup[req_idx] = NULL;
+ pReq = (SCSIIORequest_t *) mf;
+
+ if (mf != hd->cmdPtr) {
+ printk(MYIOC_s_WARN_FMT "ScanDvComplete (mf=%p, cmdPtr=%p)\n",
+ hd->ioc->name, (void *)mf, (void *) hd->cmdPtr);
+ }
+ hd->cmdPtr = NULL;
+
+ ddvprintk((MYIOC_s_INFO_FMT "ScanDvComplete (mf=%p,mr=%p)\n",
+ hd->ioc->name, mf, mr));
+
+ atomic_dec(&queue_depth);
+
+ hd->pLocal = &hd->localReply;
+
+ /* If target struct exists, clear sense valid flag.
+ */
+ clear_sense_flag(hd, pReq);
+
+ if (mr == NULL) {
+ completionCode = MPT_SCANDV_GOOD;
+ } else {
+ SCSIIOReply_t *pReply;
+ u16 status;
+
+ pReply = (SCSIIOReply_t *) mr;
+
+ status = le16_to_cpu(pReply->IOCStatus) & MPI_IOCSTATUS_MASK;
+
+ ddvprintk((KERN_NOTICE " IOCStatus=%04xh, SCSIState=%02xh, SCSIStatus=%02xh, IOCLogInfo=%08xh\n",
+ status, pReply->SCSIState, pReply->SCSIStatus,
+ le32_to_cpu(pReply->IOCLogInfo)));
+
+ switch(status) {
+
+ case MPI_IOCSTATUS_SCSI_DEVICE_NOT_THERE: /* 0x0043 */
+ completionCode = MPT_SCANDV_SELECTION_TIMEOUT;
+ break;
+
+ case MPI_IOCSTATUS_SCSI_IO_DATA_ERROR: /* 0x0046 */
+ case MPI_IOCSTATUS_SCSI_TASK_TERMINATED: /* 0x0048 */
+ case MPI_IOCSTATUS_SCSI_IOC_TERMINATED: /* 0x004B */
+ case MPI_IOCSTATUS_SCSI_EXT_TERMINATED: /* 0x004C */
+ completionCode = MPT_SCANDV_DID_RESET;
+ break;
+
+ case MPI_IOCSTATUS_SCSI_DATA_UNDERRUN: /* 0x0045 */
+ case MPI_IOCSTATUS_SCSI_RECOVERED_ERROR: /* 0x0040 */
+ case MPI_IOCSTATUS_SUCCESS: /* 0x0000 */
+ if (pReply->Function == MPI_FUNCTION_CONFIG) {
+ ConfigReply_t *pr = (ConfigReply_t *)mr;
+ completionCode = MPT_SCANDV_GOOD;
+ hd->pLocal->header.PageVersion = pr->Header.PageVersion;
+ hd->pLocal->header.PageLength = pr->Header.PageLength;
+ hd->pLocal->header.PageNumber = pr->Header.PageNumber;
+ hd->pLocal->header.PageType = pr->Header.PageType;
+
+ } else if (pReply->Function == MPI_FUNCTION_RAID_ACTION) {
+ /* If the RAID Volume request is successful,
+ * return GOOD, else indicate that
+ * some type of error occurred.
+ */
+ MpiRaidActionReply_t *pr = (MpiRaidActionReply_t *)mr;
+ if (pr->ActionStatus == MPI_RAID_ACTION_ASTATUS_SUCCESS)
+ completionCode = MPT_SCANDV_GOOD;
+ else
+ completionCode = MPT_SCANDV_SOME_ERROR;
+
+ } else if (pReply->SCSIState & MPI_SCSI_STATE_AUTOSENSE_VALID) {
+ VirtDevice *target;
+ u8 *sense_data;
+ int sz;
+
+ /* save sense data in global & target structure
+ */
+ completionCode = MPT_SCANDV_SENSE;
+ hd->pLocal->scsiStatus = pReply->SCSIStatus;
+ sense_data = ((u8 *)hd->ioc->sense_buf_pool +
+ (req_idx * MPT_SENSE_BUFFER_ALLOC));
+
+ sz = MIN (pReq->SenseBufferLength,
+ SCSI_STD_SENSE_BYTES);
+ memcpy(hd->pLocal->sense, sense_data, sz);
+
+ target = hd->Targets[pReq->TargetID];
+ if (target) {
+ memcpy(target->sense, sense_data, sz);
+ target->tflags
+ |= MPT_TARGET_FLAGS_VALID_SENSE;
+ }
+
+ ddvprintk((KERN_NOTICE " Check Condition, sense ptr %p\n",
+ sense_data));
+ } else if (pReply->SCSIState & (MPI_SCSI_STATE_AUTOSENSE_FAILED |
+ MPI_SCSI_STATE_NO_SCSI_STATUS)) {
+ completionCode = MPT_SCANDV_DID_RESET;
+ } else if (pReply->SCSIState & MPI_SCSI_STATE_TERMINATED) {
+ completionCode = MPT_SCANDV_DID_RESET;
+ } else {
+ /* If no error, this will be equivalent
+ * to MPT_SCANDV_GOOD
+ */
+ completionCode = (int) pReply->SCSIStatus;
+ }
+ break;
+
+ case MPI_IOCSTATUS_SCSI_PROTOCOL_ERROR: /* 0x0047 */
+ if (pReply->SCSIState & MPI_SCSI_STATE_TERMINATED)
+ completionCode = MPT_SCANDV_DID_RESET;
+ else
+ completionCode = MPT_SCANDV_SOME_ERROR;
+ break;
+
+ default:
+ completionCode = MPT_SCANDV_SOME_ERROR;
+ break;
+
+ } /* switch(status) */
+
+ ddvprintk((KERN_NOTICE " completionCode set to %08xh\n",
+ completionCode));
+ } /* end of address reply case */
+
+ hd->pLocal->completion = completionCode;
+
+ /* MF and RF are freed in mpt_interrupt
+ */
+wakeup:
+ /* Free Chain buffers (will never chain) in scan or dv */
+ //mptscsih_freeChainBuffers(hd, req_idx);
+
+ /*
+ * Wake up the original calling thread
+ */
+ scandv_wait_done = 1;
+ wake_up(&scandv_waitq);
+
+ return 1;
+}
+
+/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
+/* mptscsih_timer_expired - Call back for timer process.
+ * Used only for dv functionality.
+ * @data: Pointer to MPT_SCSI_HOST recast as an unsigned long
+ *
+ */
+static void mptscsih_timer_expired(unsigned long data)
+{
+ MPT_SCSI_HOST *hd = (MPT_SCSI_HOST *) data;
+#ifndef MPT_SCSI_USE_NEW_EH
+ unsigned long flags;
+#endif
+
+
+ ddvprintk((MYIOC_s_WARN_FMT "Timer Expired! Cmd %p\n", hd->ioc->name, hd->cmdPtr));
+
+ if (hd->cmdPtr) {
+ MPIHeader_t *cmd = (MPIHeader_t *)hd->cmdPtr;
+
+ if (cmd->Function == MPI_FUNCTION_SCSI_IO_REQUEST) {
+ /* Desire to issue a task management request here.
+ * TM requests MUST be single threaded.
+ * If old eh code and no TM current, issue request.
+ * If new eh code, do nothing. Wait for OS cmd timeout
+ * for bus reset.
+ */
+#ifndef MPT_SCSI_USE_NEW_EH
+ spin_lock_irqsave(&hd->ioc->FreeQlock, flags);
+ if (hd->tmPending) {
+ spin_unlock_irqrestore(&hd->ioc->FreeQlock, flags);
+ return;
+ } else
+ hd->tmPending = 1;
+ spin_unlock_irqrestore(&hd->ioc->FreeQlock, flags);
+
+ if (mptscsih_TMHandler(hd, MPI_SCSITASKMGMT_TASKTYPE_RESET_BUS,
+ 0, 0, 0, NO_SLEEP) < 0) {
+ printk(MYIOC_s_WARN_FMT "TM FAILED!\n", hd->ioc->name);
+ }
+#else
+ ddvtprintk((MYIOC_s_NOTE_FMT "DV Cmd Timeout: NoOp\n", hd->ioc->name));
+#endif
+ } else {
+ /* Perform a FW reload */
+ if (mpt_HardResetHandler(hd->ioc, NO_SLEEP) < 0) {
+ printk(MYIOC_s_WARN_FMT "Firmware Reload FAILED!\n", hd->ioc->name);
+ }
+ }
+ } else {
+ /* This should NEVER happen */
+ printk(MYIOC_s_WARN_FMT "Null cmdPtr!!!!\n", hd->ioc->name);
+ }
+
+ /* No more processing.
+ * TM call will generate an interrupt for SCSI TM Management.
+ * The FW will reply to all outstanding commands, callback will finish cleanup.
+ * Hard reset clean-up will free all resources.
+ */
+ ddvprintk((MYIOC_s_WARN_FMT "Timer Expired Complete!\n", hd->ioc->name));
+
+ return;
+}
+
+#ifndef MPTSCSIH_DISABLE_DOMAIN_VALIDATION
+/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
+/* mptscsih_do_raid - Format and Issue a RAID volume request message.
+ * @hd: Pointer to scsi host structure
+ * @action: What do be done.
+ * @id: Logical target id.
+ * @bus: Target locations bus.
+ *
+ * Returns: < 0 on a fatal error
+ * 0 on success
+ *
+ * Remark: Wait to return until reply processed by the ISR.
+ */
+static int
+mptscsih_do_raid(MPT_SCSI_HOST *hd, u8 action, INTERNAL_CMD *io)
+{
+ MpiRaidActionRequest_t *pReq;
+ MPT_FRAME_HDR *mf;
+ int in_isr;
+
+ in_isr = in_interrupt();
+ if (in_isr) {
+ dprintk((MYIOC_s_WARN_FMT "Internal raid request not allowed in ISR context!\n",
+ hd->ioc->name));
+ return -EPERM;
+ }
-static const char *ScsiCommonOpString[] = {
- "TEST UNIT READY", /* 00h */
- "REZERO UNIT (REWIND)", /* 01h */
- NULL, /* 02h */
- "REQUEST_SENSE", /* 03h */
- "FORMAT UNIT (MEDIUM)", /* 04h */
- "READ BLOCK LIMITS", /* 05h */
- NULL, /* 06h */
- "REASSIGN BLOCKS", /* 07h */
- "READ(6)", /* 08h */
- NULL, /* 09h */
- "WRITE(6)", /* 0Ah */
- "SEEK(6)", /* 0Bh */
- NULL, /* 0Ch */
- NULL, /* 0Dh */
- NULL, /* 0Eh */
- "READ REVERSE", /* 0Fh */
- "WRITE_FILEMARKS", /* 10h */
- "SPACE(6)", /* 11h */
- "INQUIRY", /* 12h */
- NULL
-};
+ /* Get and Populate a free Frame
+ */
+ if ((mf = mpt_get_msg_frame(ScsiScanDvCtx, hd->ioc->id)) == NULL) {
+ ddvprintk((MYIOC_s_WARN_FMT "_do_raid: no msg frames!\n",
+ hd->ioc->name));
+ return -EAGAIN;
+ }
+ pReq = (MpiRaidActionRequest_t *)mf;
+ pReq->Action = action;
+ pReq->Reserved1 = 0;
+ pReq->ChainOffset = 0;
+ pReq->Function = MPI_FUNCTION_RAID_ACTION;
+ pReq->VolumeID = io->id;
+ pReq->VolumeBus = io->bus;
+ pReq->PhysDiskNum = io->physDiskNum;
+ pReq->MsgFlags = 0;
+ pReq->Reserved2 = 0;
+ pReq->ActionDataWord = 0; /* Reserved for this action */
+ //pReq->ActionDataSGE = 0;
+
+ mpt_add_sge((char *)&pReq->ActionDataSGE,
+ MPT_SGE_FLAGS_SSIMPLE_READ | 0, (dma_addr_t) -1);
+
+ ddvprintk((MYIOC_s_INFO_FMT "RAID Volume action %x id %d\n",
+ hd->ioc->name, action, io->id));
+
+ hd->pLocal = NULL;
+ hd->timer.expires = jiffies + HZ*2; /* 2 second timeout */
+ scandv_wait_done = 0;
+
+ /* Save cmd pointer, for resource free if timeout or
+ * FW reload occurs
+ */
+ hd->cmdPtr = mf;
+
+ add_timer(&hd->timer);
+ mptscsih_put_msgframe(ScsiScanDvCtx, hd->ioc->id, mf);
+ wait_event(scandv_waitq, scandv_wait_done);
+
+ if ((hd->pLocal == NULL) || (hd->pLocal->completion != MPT_SCANDV_GOOD))
+ return -1;
+
+ return 0;
+}
+#endif /* ~MPTSCSIH_DISABLE_DOMAIN_VALIDATION */
+
+/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
+/**
+ * mptscsih_do_cmd - Do internal command.
+ * @hd: MPT_SCSI_HOST pointer
+ * @io: INTERNAL_CMD pointer.
+ *
+ * Issue the specified internally generated command and do command
+ * specific cleanup. For bus scan / DV only.
+ * NOTES: If command is Inquiry and status is good,
+ * initialize a target structure, save the data
+ *
+ * Remark: Single threaded access only.
+ *
+ * Return:
+ * < 0 if an illegal command or no resources
+ *
+ * 0 if good
+ *
+ * > 0 if command complete but some type of completion error.
+ */
+static int
+mptscsih_do_cmd(MPT_SCSI_HOST *hd, INTERNAL_CMD *io)
+{
+ MPT_FRAME_HDR *mf;
+ SCSIIORequest_t *pScsiReq;
+ SCSIIORequest_t ReqCopy;
+ int my_idx, ii, dir;
+ int rc, cmdTimeout;
+ int in_isr;
+ char cmdLen;
+ char CDB[]={0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};
+ char cmd = io->cmd;
+
+ in_isr = in_interrupt();
+ if (in_isr) {
+ dprintk((MYIOC_s_WARN_FMT "Internal SCSI IO request not allowed in ISR context!\n",
+ hd->ioc->name));
+ return -EPERM;
+ }
+
+
+ /* Set command specific information
+ */
+ switch (cmd) {
+ case CMD_Inquiry:
+ cmdLen = 6;
+ dir = MPI_SCSIIO_CONTROL_READ;
+ CDB[0] = cmd;
+ CDB[4] = io->size;
+ cmdTimeout = 10;
+ break;
+
+ case CMD_TestUnitReady:
+ cmdLen = 6;
+ dir = MPI_SCSIIO_CONTROL_READ;
+ cmdTimeout = 10;
+ break;
+
+ case CMD_StartStopUnit:
+ cmdLen = 6;
+ dir = MPI_SCSIIO_CONTROL_READ;
+ CDB[0] = cmd;
+ CDB[4] = 1; /*Spin up the disk */
+ cmdTimeout = 15;
+ break;
+
+ case CMD_ReadBuffer:
+ cmdLen = 10;
+ dir = MPI_SCSIIO_CONTROL_READ;
+ CDB[0] = cmd;
+ if (io->flags & MPT_ICFLAG_ECHO) {
+ CDB[1] = 0x0A;
+ } else {
+ CDB[1] = 0x02;
+ }
+
+ if (io->flags & MPT_ICFLAG_BUF_CAP) {
+ CDB[1] |= 0x01;
+ }
+ CDB[6] = (io->size >> 16) & 0xFF;
+ CDB[7] = (io->size >> 8) & 0xFF;
+ CDB[8] = io->size & 0xFF;
+ cmdTimeout = 10;
+ break;
+
+ case CMD_WriteBuffer:
+ cmdLen = 10;
+ dir = MPI_SCSIIO_CONTROL_WRITE;
+ CDB[0] = cmd;
+ if (io->flags & MPT_ICFLAG_ECHO) {
+ CDB[1] = 0x0A;
+ } else {
+ CDB[1] = 0x02;
+ }
+ CDB[6] = (io->size >> 16) & 0xFF;
+ CDB[7] = (io->size >> 8) & 0xFF;
+ CDB[8] = io->size & 0xFF;
+ cmdTimeout = 10;
+ break;
+
+ case CMD_Reserve6:
+ cmdLen = 6;
+ dir = MPI_SCSIIO_CONTROL_READ;
+ CDB[0] = cmd;
+ cmdTimeout = 10;
+ break;
+
+ case CMD_Release6:
+ cmdLen = 6;
+ dir = MPI_SCSIIO_CONTROL_READ;
+ CDB[0] = cmd;
+ cmdTimeout = 10;
+ break;
+
+ case CMD_SynchronizeCache:
+ cmdLen = 10;
+ dir = MPI_SCSIIO_CONTROL_READ;
+ CDB[0] = cmd;
+// CDB[1] = 0x02; /* set immediate bit */
+ cmdTimeout = 10;
+ break;
+
+ default:
+ /* Error Case */
+ return -EFAULT;
+ }
+
+ /* Get and Populate a free Frame
+ */
+ if ((mf = mpt_get_msg_frame(ScsiScanDvCtx, hd->ioc->id)) == NULL) {
+ ddvprintk((MYIOC_s_WARN_FMT "No msg frames!\n",
+ hd->ioc->name));
+ return -EBUSY;
+ }
+
+ pScsiReq = (SCSIIORequest_t *) mf;
+
+ /* Get the request index */
+ my_idx = le16_to_cpu(mf->u.frame.hwhdr.msgctxu.fld.req_idx);
+ ADD_INDEX_LOG(my_idx); /* for debug */
+
+ if (io->flags & MPT_ICFLAG_PHYS_DISK) {
+ pScsiReq->TargetID = io->physDiskNum;
+ pScsiReq->Bus = 0;
+ pScsiReq->ChainOffset = 0;
+ pScsiReq->Function = MPI_FUNCTION_RAID_SCSI_IO_PASSTHROUGH;
+ } else {
+ pScsiReq->TargetID = io->id;
+ pScsiReq->Bus = io->bus;
+ pScsiReq->ChainOffset = 0;
+ pScsiReq->Function = MPI_FUNCTION_SCSI_IO_REQUEST;
+ }
+
+ pScsiReq->CDBLength = cmdLen;
+ pScsiReq->SenseBufferLength = MPT_SENSE_BUFFER_SIZE;
+
+ pScsiReq->Reserved = 0;
+
+ pScsiReq->MsgFlags = mpt_msg_flags();
+ /* MsgContext set in mpt_get_msg_fram call */
+
+ for (ii=0; ii < 8; ii++)
+ pScsiReq->LUN[ii] = 0;
+ pScsiReq->LUN[1] = io->lun;
+
+ if (io->flags & MPT_ICFLAG_TAGGED_CMD)
+ pScsiReq->Control = cpu_to_le32(dir | MPI_SCSIIO_CONTROL_SIMPLEQ);
+ else
+ pScsiReq->Control = cpu_to_le32(dir | MPI_SCSIIO_CONTROL_UNTAGGED);
+
+ for (ii=0; ii < 16; ii++)
+ pScsiReq->CDB[ii] = CDB[ii];
+
+ pScsiReq->DataLength = cpu_to_le32(io->size);
+ pScsiReq->SenseBufferLowAddr = cpu_to_le32(hd->ioc->sense_buf_low_dma
+ + (my_idx * MPT_SENSE_BUFFER_ALLOC));
+
+ ddvprintk((MYIOC_s_INFO_FMT "Sending Command 0x%x for (%d:%d:%d)\n",
+ hd->ioc->name, cmd, io->bus, io->id, io->lun));
+
+ if (dir == MPI_SCSIIO_CONTROL_READ) {
+ mpt_add_sge((char *) &pScsiReq->SGL,
+ MPT_SGE_FLAGS_SSIMPLE_READ | io->size,
+ io->data_dma);
+ } else {
+ mpt_add_sge((char *) &pScsiReq->SGL,
+ MPT_SGE_FLAGS_SSIMPLE_WRITE | io->size,
+ io->data_dma);
+ }
+
+ /* The ISR will free the request frame, but we need
+ * the information to initialize the target. Duplicate.
+ */
+ memcpy(&ReqCopy, pScsiReq, sizeof(SCSIIORequest_t));
+
+ /* Issue this command after:
+ * finish init
+ * add timer
+ * Wait until the reply has been received
+ * ScsiScanDvCtx callback function will
+ * set hd->pLocal;
+ * set scandv_wait_done and call wake_up
+ */
+ hd->pLocal = NULL;
+ hd->timer.expires = jiffies + HZ*cmdTimeout;
+ scandv_wait_done = 0;
+
+ /* Save cmd pointer, for resource free if timeout or
+ * FW reload occurs
+ */
+ hd->cmdPtr = mf;
+
+ add_timer(&hd->timer);
+ mptscsih_put_msgframe(ScsiScanDvCtx, hd->ioc->id, mf);
+ wait_event(scandv_waitq, scandv_wait_done);
+
+ if (hd->pLocal) {
+ rc = hd->pLocal->completion;
+ hd->pLocal->skip = 0;
+
+ /* Always set fatal error codes in some cases.
+ */
+ if (rc == MPT_SCANDV_SELECTION_TIMEOUT)
+ rc = -ENXIO;
+ else if (rc == MPT_SCANDV_SOME_ERROR)
+ rc = -rc;
+ } else {
+ rc = -EFAULT;
+ /* This should never happen. */
+ ddvprintk((MYIOC_s_INFO_FMT "_do_cmd: Null pLocal!!!\n",
+ hd->ioc->name));
+ }
+
+ return rc;
+}
+
+/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
+/**
+ * mptscsih_synchronize_cache - Send SYNCHRONIZE_CACHE to all disks.
+ * @hd: Pointer to MPT_SCSI_HOST structure
+ * @portnum: IOC port number
+ *
+ * Uses the ISR, but with special processing.
+ * MUST be single-threaded.
+ *
+ * Return: 0 on completion
+ */
+static int
+mptscsih_synchronize_cache(MPT_SCSI_HOST *hd, int portnum)
+{
+ MPT_ADAPTER *ioc= hd->ioc;
+ VirtDevice *pTarget = NULL;
+ SCSIDevicePage1_t *pcfg1Data = NULL;
+ INTERNAL_CMD iocmd;
+ CONFIGPARMS cfg;
+ dma_addr_t cfg1_dma_addr = -1;
+ ConfigPageHeader_t header1;
+ int bus = 0;
+ int id = 0;
+ int lun = 0;
+ int hostId = ioc->pfacts[portnum].PortSCSIID;
+ int max_id;
+ int requested, configuration, data;
+ int doConfig = 0;
+ u8 flags, factor;
+
+ max_id = ioc->sh->max_id - 1;
+
+ /* Following parameters will not change
+ * in this routine.
+ */
+ iocmd.cmd = CMD_SynchronizeCache;
+ iocmd.flags = 0;
+ iocmd.physDiskNum = -1;
+ iocmd.data = NULL;
+ iocmd.data_dma = -1;
+ iocmd.size = 0;
+ iocmd.rsvd = iocmd.rsvd2 = 0;
+
+ /* No SCSI hosts
+ */
+ if (hd->Targets == NULL)
+ return 0;
+
+ /* Skip the host
+ */
+ if (id == hostId)
+ id++;
+
+ /* Write SDP1 for all SCSI devices
+ * Alloc memory and set up config buffer
+ */
+ if (hd->is_spi) {
+ if (ioc->spi_data.sdp1length > 0) {
+ pcfg1Data = (SCSIDevicePage1_t *)pci_alloc_consistent(ioc->pcidev,
+ ioc->spi_data.sdp1length * 4, &cfg1_dma_addr);
+
+ if (pcfg1Data != NULL) {
+ doConfig = 1;
+ header1.PageVersion = ioc->spi_data.sdp1version;
+ header1.PageLength = ioc->spi_data.sdp1length;
+ header1.PageNumber = 1;
+ header1.PageType = MPI_CONFIG_PAGETYPE_SCSI_DEVICE;
+ cfg.hdr = &header1;
+ cfg.physAddr = cfg1_dma_addr;
+ cfg.action = MPI_CONFIG_ACTION_PAGE_WRITE_CURRENT;
+ cfg.dir = 1;
+ cfg.timeout = 0;
+ }
+ }
+ }
+
+ /* loop through all devices on this port
+ */
+ while (bus < MPT_MAX_BUS) {
+ iocmd.bus = bus;
+ iocmd.id = id;
+ pTarget = hd->Targets[(int)id];
+
+ if (doConfig) {
+
+ /* Set the negotiation flags */
+ if (pTarget && (pTarget = hd->Targets[id]) && !pTarget->raidVolume) {
+ flags = pTarget->negoFlags;
+ } else {
+ flags = hd->ioc->spi_data.noQas;
+ if (hd->ioc->spi_data.nvram && (hd->ioc->spi_data.nvram[id] != MPT_HOST_NVRAM_INVALID)) {
+ data = hd->ioc->spi_data.nvram[id];
+
+ if (data & MPT_NVRAM_WIDE_DISABLE)
+ flags |= MPT_TARGET_NO_NEGO_WIDE;
+
+ factor = (data & MPT_NVRAM_SYNC_MASK) >> MPT_NVRAM_SYNC_SHIFT;
+ if ((factor == 0) || (factor == MPT_ASYNC))
+ flags |= MPT_TARGET_NO_NEGO_SYNC;
+ }
+ }
+
+ /* Force to async, narrow */
+ mptscsih_setDevicePage1Flags(0, MPT_ASYNC, 0, &requested,
+ &configuration, flags);
+ pcfg1Data->RequestedParameters = le32_to_cpu(requested);
+ pcfg1Data->Reserved = 0;
+ pcfg1Data->Configuration = le32_to_cpu(configuration);
+ cfg.pageAddr = (bus<<8) | id;
+ mpt_config(hd->ioc, &cfg);
+ }
+
+ /* If target Ptr NULL or if this target is NOT a disk, skip.
+ */
+ // if (pTarget && ((pTarget->inq_data[0] & 0x1F) == 0)) {
+ if (pTarget) {
+ for (lun=0; lun <= MPT_LAST_LUN; lun++) {
+ /* If LUN present, issue the command
+ */
+ if (pTarget->luns & (1<<lun)) {
+ iocmd.lun = lun;
+ (void) mptscsih_do_cmd(hd, &iocmd);
+ }
+ }
+ }
+
+ /* get next relevant device */
+ id++;
+
+ if (id == hostId)
+ id++;
+
+ if (id > max_id) {
+ id = 0;
+ bus++;
+ }
+ }
+
+ if (pcfg1Data) {
+ pci_free_consistent(ioc->pcidev, header1.PageLength * 4, pcfg1Data, cfg1_dma_addr);
+ }
+
+ return 0;
+}
+
+#ifndef MPTSCSIH_DISABLE_DOMAIN_VALIDATION
+/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
+/**
+ * mptscsih_domainValidation - Top level handler for domain validation.
+ * @hd: Pointer to MPT_SCSI_HOST structure.
+ *
+ * Uses the ISR, but with special processing.
+ * Called from schedule, should not be in interrupt mode.
+ * While thread alive, do dv for all devices needing dv
+ *
+ * Return: None.
+ */
+static void
+mptscsih_domainValidation(void *arg)
+{
+ MPT_SCSI_HOST *hd = NULL;
+ MPT_ADAPTER *ioc = NULL;
+ unsigned long flags;
+ int id, maxid, dvStatus, did;
+ int ii, isPhysDisk;
+ u8 oldQas;
+
+ spin_lock_irqsave(&dvtaskQ_lock, flags);
+ dvtaskQ_active = 1;
+ if (dvtaskQ_release) {
+ dvtaskQ_active = 0;
+ spin_unlock_irqrestore(&dvtaskQ_lock, flags);
+ return;
+ }
+ spin_unlock_irqrestore(&dvtaskQ_lock, flags);
+
+ /* For this ioc, loop through all devices and do dv to each device.
+ * When complete with this ioc, search through the ioc list, and
+ * for each scsi ioc found, do dv for all devices. Exit when no
+ * device needs dv.
+ */
+ did = 1;
+ while (did) {
+ did = 0;
+ for (ioc = mpt_adapter_find_first(); ioc != NULL; ioc = mpt_adapter_find_next(ioc)) {
+ spin_lock_irqsave(&dvtaskQ_lock, flags);
+ if (dvtaskQ_release) {
+ dvtaskQ_active = 0;
+ spin_unlock_irqrestore(&dvtaskQ_lock, flags);
+ return;
+ }
+ spin_unlock_irqrestore(&dvtaskQ_lock, flags);
+
+ set_current_state(TASK_INTERRUPTIBLE);
+ schedule_timeout(HZ/4);
+
+ /* DV only to SCSI adapters */
+ if ((int)ioc->chip_type <= (int)FC929)
+ continue;
+
+ /* Make sure everything looks ok */
+ if (ioc->sh == NULL)
+ continue;
+
+ hd = (MPT_SCSI_HOST *) ioc->sh->hostdata;
+ if (hd == NULL)
+ continue;
+
+ maxid = MIN (ioc->sh->max_id, MPT_MAX_SCSI_DEVICES);
+
+ for (id = 0; id < maxid; id++) {
+ oldQas = hd->ioc->spi_data.noQas;
+ spin_lock_irqsave(&dvtaskQ_lock, flags);
+ if (dvtaskQ_release) {
+ dvtaskQ_active = 0;
+ spin_unlock_irqrestore(&dvtaskQ_lock, flags);
+ return;
+ }
+ spin_unlock_irqrestore(&dvtaskQ_lock, flags);
+ dvStatus = hd->ioc->spi_data.dvStatus[id];
+
+ if (dvStatus & MPT_SCSICFG_NEED_DV) {
+
+ hd->ioc->spi_data.dvStatus[id] |= MPT_SCSICFG_DV_PENDING;
+ hd->ioc->spi_data.dvStatus[id] &= ~MPT_SCSICFG_NEED_DV;
+
+ set_current_state(TASK_INTERRUPTIBLE);
+ schedule_timeout(HZ/4);
+
+ /* If hidden phys disk, block IO's to all
+ * raid volumes
+ * else, process normally
+ */
+ isPhysDisk = mptscsih_is_phys_disk(ioc, id);
+ if (isPhysDisk) {
+ for (ii=0; ii < MPT_MAX_SCSI_DEVICES; ii++) {
+ if (hd->ioc->spi_data.isRaid & (1 << ii)) {
+ hd->ioc->spi_data.dvStatus[ii] |= MPT_SCSICFG_DV_PENDING;
+ }
+ }
+ }
+
+ mptscsih_doDv(hd, 0, id);
+ did++;
+ hd->ioc->spi_data.dvStatus[id] |= MPT_SCSICFG_DV_DONE;
+ hd->ioc->spi_data.dvStatus[id] &= ~MPT_SCSICFG_DV_PENDING;
+
+ if (isPhysDisk) {
+ for (ii=0; ii < MPT_MAX_SCSI_DEVICES; ii++) {
+ if (hd->ioc->spi_data.isRaid & (1 << ii)) {
+ hd->ioc->spi_data.dvStatus[ii] &= ~MPT_SCSICFG_DV_PENDING;
+ }
+ }
+ }
+
+ /* Post OS IOs that were pended while
+ * DV running.
+ */
+ post_pendingQ_commands(hd);
+
+ if ((!oldQas) && (hd->ioc->spi_data.noQas))
+ mptscsih_qas_check(hd);
+ }
+ }
+ }
+ }
+
+ spin_lock_irqsave(&dvtaskQ_lock, flags);
+ dvtaskQ_active = 0;
+ spin_unlock_irqrestore(&dvtaskQ_lock, flags);
+
+ return;
+}
+
+/* Search IOC page 3 to determine if this is hidden physical disk
+ */
+static int mptscsih_is_phys_disk(MPT_ADAPTER *ioc, int id)
+{
+ if (ioc->spi_data.pIocPg3) {
+ Ioc3PhysDisk_t *pPDisk = ioc->spi_data.pIocPg3->PhysDisk;
+ int numPDisk = ioc->spi_data.pIocPg3->NumPhysDisks;
+
+ while (numPDisk) {
+ if (pPDisk->PhysDiskID == id) {
+ return 1;
+ }
+ pPDisk++;
+ numPDisk--;
+ }
+ }
+ return 0;
+}
-static const char *SenseKeyString[] = {
- "NO SENSE", /* 0h */
- "RECOVERED ERROR", /* 1h */
- "NOT READY", /* 2h */
- "MEDIUM ERROR", /* 3h */
- "HARDWARE ERROR", /* 4h */
- "ILLEGAL REQUEST", /* 5h */
- "UNIT ATTENTION", /* 6h */
- "DATA PROTECT", /* 7h */
- "BLANK CHECK", /* 8h */
- "VENDOR-SPECIFIC", /* 9h */
- "ABORTED COPY", /* Ah */
- "ABORTED COMMAND", /* Bh */
- "EQUAL (obsolete)", /* Ch */
- "VOLUME OVERFLOW", /* Dh */
- "MISCOMPARE", /* Eh */
- "RESERVED", /* Fh */
- NULL
-};
+/* Write SDP1 if no QAS has been enabled
+ */
+static void mptscsih_qas_check(MPT_SCSI_HOST *hd)
+{
+ VirtDevice *pTarget = NULL;
+ int ii;
+ int change = 0;
-#define SPECIAL_ASCQ(c,q) \
- (((c) == 0x40 && (q) != 0x00) || ((c) == 0x4D) || ((c) == 0x70))
+ if (hd->Targets == NULL)
+ return;
+
+ for (ii=0; ii < MPT_MAX_SCSI_DEVICES; ii++) {
+ change = 0;
+ pTarget = hd->Targets[ii];
+
+ if ((pTarget != NULL) && (!pTarget->raidVolume)) {
+ if ((pTarget->negoFlags & hd->ioc->spi_data.noQas) == 0) {
+ change = 1;
+ pTarget->negoFlags |= hd->ioc->spi_data.noQas;
+ }
+ } else {
+ if (mptscsih_is_phys_disk(hd->ioc, ii) == 1)
+ change = 1;
+ }
+
+ if ((change) && (hd->ioc->spi_data.dvStatus[ii] & MPT_SCSICFG_DV_DONE))
+ mptscsih_writeSDP1(hd, 0, ii, 0);
+ }
+ return;
+}
+
+
+
+#define MPT_GET_NVRAM_VALS 0x01
+#define MPT_UPDATE_MAX 0x02
+#define MPT_SET_MAX 0x04
+#define MPT_SET_MIN 0x08
+#define MPT_FALLBACK 0x10
+#define MPT_SAVE 0x20
-#if 0
/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
-/*
- * Sense_Key_Specific() - If Sense_Key_Specific_Valid bit is set,
- * then print additional information via
- * a call to SDMS_SystemAlert().
+/**
+ * mptscsih_doDv - Perform domain validation to a target.
+ * @hd: Pointer to MPT_SCSI_HOST structure.
+ * @portnum: IOC port number.
+ * @target: Physical ID of this target
+ *
+ * Uses the ISR, but with special processing.
+ * MUST be single-threaded.
+ * Test will exit if target is at async & narrow.
*
- * Return: nothing
+ * Return: None.
*/
-static void Sense_Key_Specific(IO_Info_t *ioop, char *msg1)
+static void
+mptscsih_doDv(MPT_SCSI_HOST *hd, int portnum, int id)
{
- u8 *sd;
- u8 BadValue;
- u8 SenseKey;
- int Offset;
- int len = strlen(msg1);
+ MPT_ADAPTER *ioc = hd->ioc;
+ VirtDevice *pTarget = NULL;
+ SCSIDevicePage1_t *pcfg1Data = NULL;
+ SCSIDevicePage0_t *pcfg0Data = NULL;
+ u8 *pbuf1 = NULL;
+ u8 *pbuf2 = NULL;
+ u8 *pDvBuf = NULL;
+ dma_addr_t dvbuf_dma = -1;
+ dma_addr_t buf1_dma = -1;
+ dma_addr_t buf2_dma = -1;
+ dma_addr_t cfg1_dma_addr = -1;
+ dma_addr_t cfg0_dma_addr = -1;
+ ConfigPageHeader_t header1;
+ ConfigPageHeader_t header0;
+ DVPARAMETERS dv;
+ INTERNAL_CMD iocmd;
+ CONFIGPARMS cfg;
+ int dv_alloc = 0;
+ int rc, sz = 0;
+ int bufsize = 0;
+ int dataBufSize = 0;
+ int echoBufSize = 0;
+ int notDone;
+ int patt;
+ int repeat;
+ char firstPass = 1;
+ char doFallback = 0;
+ char readPage0;
+ char bus, lun;
+ char inq0 = 0;
+
+ if (ioc->spi_data.sdp1length == 0)
+ return;
- sd = ioop->sensePtr;
- if (SD_Additional_Sense_Length(sd) < 8)
+ if (ioc->spi_data.sdp0length == 0)
return;
- SenseKey = SD_Sense_Key(sd);
+ if (id == ioc->pfacts[portnum].PortSCSIID)
+ return;
- if (SD_Sense_Key_Specific_Valid(sd)) {
- if (SenseKey == SK_ILLEGAL_REQUEST) {
- Offset = SD_Bad_Byte(sd);
- if (SD_Was_Illegal_Request(sd)) {
- BadValue = ioop->cdbPtr[Offset];
- len += sprintf(msg1+len, "\n Illegal CDB value=%02Xh found at CDB ",
- BadValue);
+ lun = 0;
+ bus = 0;
+ ddvtprintk((MYIOC_s_NOTE_FMT
+ "DV started: numIOs %d bus=%d, id %d dv @ %p\n",
+ ioc->name, atomic_read(&queue_depth), bus, id, &dv));
+
+ /* Prep DV structure
+ */
+ memset (&dv, 0, sizeof(DVPARAMETERS));
+ dv.id = id;
+
+ /* Populate tmax with the current maximum
+ * transfer parameters for this target.
+ * Exit if narrow and async.
+ */
+ dv.cmd = MPT_GET_NVRAM_VALS;
+ mptscsih_dv_parms(hd, &dv, NULL);
+ if ((!dv.max.width) && (!dv.max.offset))
+ return;
+
+ /* Prep SCSI IO structure
+ */
+ iocmd.id = id;
+ iocmd.bus = bus;
+ iocmd.lun = lun;
+ iocmd.flags = 0;
+ iocmd.physDiskNum = -1;
+ iocmd.rsvd = iocmd.rsvd2 = 0;
+
+ /* Use tagged commands if possible.
+ */
+ pTarget = hd->Targets[id];
+ if (pTarget && (pTarget->tflags & MPT_TARGET_FLAGS_Q_YES))
+ iocmd.flags |= MPT_ICFLAG_TAGGED_CMD;
+
+ if (pTarget && (pTarget->tflags & MPT_TARGET_FLAGS_VALID_INQUIRY)) {
+ /* Another GEM workaround. Check peripheral device type,
+ * if PROCESSOR, quit DV.
+ */
+ if (((pTarget->inq_data[0] & 0x1F) == 0x03) || ((pTarget->inq_data[0] & 0x1F) > 0x08)) {
+ pTarget->negoFlags |= (MPT_TARGET_NO_NEGO_WIDE | MPT_TARGET_NO_NEGO_SYNC);
+ return;
+ }
+ }
+
+ /* Prep cfg structure
+ */
+ cfg.pageAddr = (bus<<8) | id;
+ cfg.hdr = NULL;
+
+ /* Prep SDP0 header
+ */
+ header0.PageVersion = ioc->spi_data.sdp0version;
+ header0.PageLength = ioc->spi_data.sdp0length;
+ header0.PageNumber = 0;
+ header0.PageType = MPI_CONFIG_PAGETYPE_SCSI_DEVICE;
+
+ /* Prep SDP1 header
+ */
+ header1.PageVersion = ioc->spi_data.sdp1version;
+ header1.PageLength = ioc->spi_data.sdp1length;
+ header1.PageNumber = 1;
+ header1.PageType = MPI_CONFIG_PAGETYPE_SCSI_DEVICE;
+
+ if (header0.PageLength & 1)
+ dv_alloc = (header0.PageLength * 4) + 4;
+
+ dv_alloc += (2048 + (header1.PageLength * 4));
+
+ pDvBuf = pci_alloc_consistent(ioc->pcidev, dv_alloc, &dvbuf_dma);
+ if (pDvBuf == NULL)
+ return;
+
+ sz = 0;
+ pbuf1 = (u8 *)pDvBuf;
+ buf1_dma = dvbuf_dma;
+ sz +=1024;
+
+ pbuf2 = (u8 *) (pDvBuf + sz);
+ buf2_dma = dvbuf_dma + sz;
+ sz +=1024;
+
+ pcfg0Data = (SCSIDevicePage0_t *) (pDvBuf + sz);
+ cfg0_dma_addr = dvbuf_dma + sz;
+ sz += header0.PageLength * 4;
+
+ /* 8-byte alignment
+ */
+ if (header0.PageLength & 1)
+ sz += 4;
+
+ pcfg1Data = (SCSIDevicePage1_t *) (pDvBuf + sz);
+ cfg1_dma_addr = dvbuf_dma + sz;
+
+ /* Skip this ID? Set cfg.hdr to force config page write
+ */
+ if ((ioc->spi_data.nvram[id] != MPT_HOST_NVRAM_INVALID) &&
+ (!(ioc->spi_data.nvram[id] & MPT_NVRAM_ID_SCAN_ENABLE))) {
+
+ ddvprintk((MYIOC_s_NOTE_FMT "DV Skipped: bus, id, lun (%d, %d, %d)\n",
+ ioc->name, bus, id, lun));
+
+ dv.cmd = MPT_SET_MAX;
+ mptscsih_dv_parms(hd, &dv, (void *)pcfg1Data);
+ cfg.hdr = &header1;
+ goto target_done;
+ }
+
+ /* Finish iocmd inititialization - hidden or visible disk? */
+ if (ioc->spi_data.pIocPg3) {
+ /* Searc IOC page 3 for matching id
+ */
+ Ioc3PhysDisk_t *pPDisk = ioc->spi_data.pIocPg3->PhysDisk;
+ int numPDisk = ioc->spi_data.pIocPg3->NumPhysDisks;
+
+ while (numPDisk) {
+ if (pPDisk->PhysDiskID == id) {
+ /* match */
+ iocmd.flags |= MPT_ICFLAG_PHYS_DISK;
+ iocmd.physDiskNum = pPDisk->PhysDiskNum;
+
+ /* Quiesce the IM
+ */
+ if (mptscsih_do_raid(hd, MPI_RAID_ACTION_QUIESCE_PHYS_IO, &iocmd) < 0) {
+ ddvprintk((MYIOC_s_ERR_FMT "RAID Queisce FAILED!\n", ioc->name));
+ goto target_done;
+ }
+ break;
+ }
+ pPDisk++;
+ numPDisk--;
+ }
+ }
+
+ /* RAID Volume ID's may double for a physical device. If RAID but
+ * not a physical ID as well, skip DV.
+ */
+ if ((hd->ioc->spi_data.isRaid & (1 << id)) && !(iocmd.flags & MPT_ICFLAG_PHYS_DISK))
+ goto target_done;
+
+
+ /* Basic Test.
+ * Async & Narrow - Inquiry
+ * Async & Narrow - Inquiry
+ * Maximum transfer rate - Inquiry
+ * Compare buffers:
+ * If compare, test complete.
+ * If miscompare and first pass, repeat
+ * If miscompare and not first pass, fall back and repeat
+ */
+ hd->pLocal = NULL;
+ readPage0 = 0;
+ sz = SCSI_STD_INQUIRY_BYTES;
+ while (1) {
+ ddvprintk((MYIOC_s_NOTE_FMT "DV: Start Basic test.\n", ioc->name));
+ dv.cmd = MPT_SET_MIN;
+ mptscsih_dv_parms(hd, &dv, (void *)pcfg1Data);
+
+ cfg.hdr = &header1;
+ cfg.physAddr = cfg1_dma_addr;
+ cfg.action = MPI_CONFIG_ACTION_PAGE_WRITE_CURRENT;
+ cfg.dir = 1;
+ if (mpt_config(hd->ioc, &cfg) != 0)
+ goto target_done;
+
+ iocmd.cmd = CMD_Inquiry;
+ iocmd.data_dma = buf1_dma;
+ iocmd.data = pbuf1;
+ iocmd.size = sz;
+ if (mptscsih_do_cmd(hd, &iocmd) < 0)
+ goto target_done;
+
+ /* Another GEM workaround. Check peripheral device type,
+ * if PROCESSOR, quit DV.
+ */
+ if (((pbuf1[0] & 0x1F) == 0x03) || ((pbuf1[0] & 0x1F) > 0x08))
+ goto target_done;
+
+ if (mptscsih_do_cmd(hd, &iocmd) < 0)
+ goto target_done;
+
+ if (doFallback)
+ dv.cmd = MPT_FALLBACK;
+ else
+ dv.cmd = MPT_SET_MAX;
+
+ mptscsih_dv_parms(hd, &dv, (void *)pcfg1Data);
+ if (mpt_config(hd->ioc, &cfg) != 0)
+ goto target_done;
+
+ if ((!dv.now.width) && (!dv.now.offset))
+ goto target_done;
+
+ iocmd.cmd = CMD_Inquiry;
+ iocmd.data_dma = buf2_dma;
+ iocmd.data = pbuf2;
+ iocmd.size = sz;
+ if (mptscsih_do_cmd(hd, &iocmd) < 0)
+ goto target_done;
+ else if (hd->pLocal == NULL)
+ goto target_done;
+ else {
+ /* Save the return code.
+ * If this is the first pass,
+ * read SCSI Device Page 0
+ * and update the target max parameters.
+ */
+ rc = hd->pLocal->completion;
+ doFallback = 0;
+ if (rc == MPT_SCANDV_GOOD) {
+ if (!readPage0) {
+ u32 sdp0_info;
+ u32 sdp0_nego;
+
+ cfg.hdr = &header0;
+ cfg.physAddr = cfg0_dma_addr;
+ cfg.action = MPI_CONFIG_ACTION_PAGE_READ_CURRENT;
+ cfg.dir = 0;
+ if (mpt_config(hd->ioc, &cfg) != 0)
+ goto target_done;
+
+ sdp0_info = le32_to_cpu(pcfg0Data->Information) & 0x0E;
+ sdp0_nego = (le32_to_cpu(pcfg0Data->NegotiatedParameters) & 0xFF00 ) >> 8;
+
+ /* Quantum and Fujitsu workarounds.
+ * Quantum: PPR U320 -> PPR reply with Ultra2 and wide
+ * Fujitsu: PPR U320 -> Msg Reject and Ultra2 and wide
+ * Resetart with a request for U160.
+ */
+ if ((dv.now.factor == MPT_ULTRA320) && (sdp0_nego == MPT_ULTRA2)) {
+ doFallback = 1;
+ } else {
+ dv.cmd = MPT_UPDATE_MAX;
+ mptscsih_dv_parms(hd, &dv, (void *)pcfg0Data);
+ /* Update the SCSI device page 1 area
+ */
+ pcfg1Data->RequestedParameters = pcfg0Data->NegotiatedParameters;
+ readPage0 = 1;
+ }
+ }
+
+ /* Quantum workaround. Restart this test will the fallback
+ * flag set.
+ */
+ if (doFallback == 0) {
+ if (memcmp(pbuf1, pbuf2, sz) != 0) {
+ if (!firstPass)
+ doFallback = 1;
+ } else
+ break; /* test complete */
+ }
+
+
+ } else if ((rc == MPT_SCANDV_DID_RESET) || (rc == MPT_SCANDV_SENSE))
+ doFallback = 1; /* set fallback flag */
+ else
+ goto target_done;
+
+ firstPass = 0;
+ }
+ }
+ ddvprintk((MYIOC_s_NOTE_FMT "DV: Basic test completed OK.\n", ioc->name));
+ inq0 = (*pbuf1) & 0x1F;
+
+ /* Continue only for disks
+ */
+ if (inq0 != 0)
+ goto target_done;
+
+ /* Start the Enhanced Test.
+ * 0) issue TUR to clear out check conditions
+ * 1) read capacity of echo (regular) buffer
+ * 2) reserve device
+ * 3) do write-read-compare data pattern test
+ * 4) release
+ * 5) update nego parms to target struct
+ */
+ cfg.hdr = &header1;
+ cfg.physAddr = cfg1_dma_addr;
+ cfg.action = MPI_CONFIG_ACTION_PAGE_WRITE_CURRENT;
+ cfg.dir = 1;
+
+ iocmd.cmd = CMD_TestUnitReady;
+ iocmd.data_dma = -1;
+ iocmd.data = NULL;
+ iocmd.size = 0;
+ notDone = 1;
+ while (notDone) {
+ if (mptscsih_do_cmd(hd, &iocmd) < 0)
+ goto target_done;
+
+ if (hd->pLocal == NULL)
+ goto target_done;
+
+ rc = hd->pLocal->completion;
+ if (rc == MPT_SCANDV_GOOD)
+ notDone = 0;
+ else if (rc == MPT_SCANDV_SENSE) {
+ u8 skey = hd->pLocal->sense[2] & 0x0F;
+ u8 asc = hd->pLocal->sense[12];
+ u8 ascq = hd->pLocal->sense[13];
+ ddvprintk((MYIOC_s_INFO_FMT
+ "SenseKey:ASC:ASCQ = (%x:%02x:%02x)\n",
+ ioc->name, skey, asc, ascq));
+
+ if (skey == SK_UNIT_ATTENTION)
+ notDone++; /* repeat */
+ else if ((skey == SK_NOT_READY) &&
+ (asc == 0x04)&&(ascq == 0x01)) {
+ /* wait then repeat */
+ mdelay (2000);
+ notDone++;
+ } else if ((skey == SK_NOT_READY) && (asc == 0x3A)) {
+ /* no medium, try read test anyway */
+ notDone = 0;
+ } else {
+ /* All other errors are fatal.
+ */
+ ddvprintk((MYIOC_s_INFO_FMT "DV: fatal error.",
+ ioc->name));
+ goto target_done;
+ }
+ } else
+ goto target_done;
+ }
+
+ iocmd.cmd = CMD_ReadBuffer;
+ iocmd.data_dma = buf1_dma;
+ iocmd.data = pbuf1;
+ iocmd.size = 4;
+ iocmd.flags |= MPT_ICFLAG_BUF_CAP;
+
+ dataBufSize = 0;
+ echoBufSize = 0;
+ for (patt = 0; patt < 2; patt++) {
+ if (patt == 0)
+ iocmd.flags |= MPT_ICFLAG_ECHO;
+ else
+ iocmd.flags &= ~MPT_ICFLAG_ECHO;
+
+ notDone = 1;
+ while (notDone) {
+ bufsize = 0;
+
+ /* If not ready after 8 trials,
+ * give up on this device.
+ */
+ if (notDone > 8)
+ goto target_done;
+
+ if (mptscsih_do_cmd(hd, &iocmd) < 0)
+ goto target_done;
+ else if (hd->pLocal == NULL)
+ goto target_done;
+ else {
+ rc = hd->pLocal->completion;
+ ddvprintk(("ReadBuffer Comp Code %d", rc));
+ ddvprintk((" buff: %0x %0x %0x %0x\n",
+ pbuf1[0], pbuf1[1], pbuf1[2], pbuf1[3]));
+
+ if (rc == MPT_SCANDV_GOOD) {
+ notDone = 0;
+ if (iocmd.flags & MPT_ICFLAG_ECHO) {
+ bufsize = ((pbuf1[2] & 0x1F) <<8) | pbuf1[3];
+ } else {
+ bufsize = pbuf1[1]<<16 | pbuf1[2]<<8 | pbuf1[3];
+ }
+ } else if (rc == MPT_SCANDV_SENSE) {
+ u8 skey = hd->pLocal->sense[2] & 0x0F;
+ u8 asc = hd->pLocal->sense[12];
+ u8 ascq = hd->pLocal->sense[13];
+ ddvprintk((MYIOC_s_INFO_FMT
+ "SenseKey:ASC:ASCQ = (%x:%02x:%02x)\n",
+ ioc->name, skey, asc, ascq));
+ if (skey == SK_ILLEGAL_REQUEST) {
+ notDone = 0;
+ } else if (skey == SK_UNIT_ATTENTION) {
+ notDone++; /* repeat */
+ } else if ((skey == SK_NOT_READY) &&
+ (asc == 0x04)&&(ascq == 0x01)) {
+ /* wait then repeat */
+ mdelay (2000);
+ notDone++;
+ } else {
+ /* All other errors are fatal.
+ */
+ ddvprintk((MYIOC_s_INFO_FMT "DV: fatal error.",
+ ioc->name));
+ goto target_done;
+ }
+ } else {
+ /* All other errors are fatal
+ */
+ goto target_done;
+ }
+ }
+ }
+
+ if (iocmd.flags & MPT_ICFLAG_ECHO)
+ echoBufSize = bufsize;
+ else
+ dataBufSize = bufsize;
+ }
+ sz = 0;
+ iocmd.flags &= ~MPT_ICFLAG_BUF_CAP;
+
+ /* Use echo buffers if possible,
+ * Exit if both buffers are 0.
+ */
+ if (echoBufSize > 0) {
+ iocmd.flags |= MPT_ICFLAG_ECHO;
+ if (dataBufSize > 0)
+ bufsize = MIN(echoBufSize, dataBufSize);
+ else
+ bufsize = echoBufSize;
+ } else if (dataBufSize == 0)
+ goto target_done;
+
+ ddvprintk((MYIOC_s_INFO_FMT "%s Buffer Capacity %d\n", ioc->name,
+ (iocmd.flags & MPT_ICFLAG_ECHO) ? "Echo" : " ", bufsize));
+
+ /* Data buffers for write-read-compare test max 1K.
+ */
+ sz = MIN(bufsize, 1024);
+
+ /* --- loop ----
+ * On first pass, always issue a reserve.
+ * On additional loops, only if a reset has occurred.
+ * iocmd.flags indicates if echo or regular buffer
+ */
+ for (patt = 0; patt < 4; patt++) {
+ ddvprintk(("Pattern %d\n", patt));
+ if ((iocmd.flags & MPT_ICFLAG_RESERVED) && (iocmd.flags & MPT_ICFLAG_DID_RESET)) {
+ iocmd.cmd = CMD_TestUnitReady;
+ iocmd.data_dma = -1;
+ iocmd.data = NULL;
+ iocmd.size = 0;
+ if (mptscsih_do_cmd(hd, &iocmd) < 0)
+ goto target_done;
+
+ iocmd.cmd = CMD_Release6;
+ iocmd.data_dma = -1;
+ iocmd.data = NULL;
+ iocmd.size = 0;
+ if (mptscsih_do_cmd(hd, &iocmd) < 0)
+ goto target_done;
+ else if (hd->pLocal == NULL)
+ goto target_done;
+ else {
+ rc = hd->pLocal->completion;
+ ddvprintk(("Release rc %d\n", rc));
+ if (rc == MPT_SCANDV_GOOD)
+ iocmd.flags &= ~MPT_ICFLAG_RESERVED;
+ else
+ goto target_done;
+ }
+ iocmd.flags &= ~MPT_ICFLAG_RESERVED;
+ }
+ iocmd.flags &= ~MPT_ICFLAG_DID_RESET;
+
+ repeat = 5;
+ while (repeat && (!(iocmd.flags & MPT_ICFLAG_RESERVED))) {
+ iocmd.cmd = CMD_Reserve6;
+ iocmd.data_dma = -1;
+ iocmd.data = NULL;
+ iocmd.size = 0;
+ if (mptscsih_do_cmd(hd, &iocmd) < 0)
+ goto target_done;
+ else if (hd->pLocal == NULL)
+ goto target_done;
+ else {
+ rc = hd->pLocal->completion;
+ if (rc == MPT_SCANDV_GOOD) {
+ iocmd.flags |= MPT_ICFLAG_RESERVED;
+ } else if (rc == MPT_SCANDV_SENSE) {
+ /* Wait if coming ready
+ */
+ u8 skey = hd->pLocal->sense[2] & 0x0F;
+ u8 asc = hd->pLocal->sense[12];
+ u8 ascq = hd->pLocal->sense[13];
+ ddvprintk((MYIOC_s_INFO_FMT
+ "DV: Reserve Failed: ", ioc->name));
+ ddvprintk(("SenseKey:ASC:ASCQ = (%x:%02x:%02x)\n",
+ skey, asc, ascq));
+
+ if ((skey == SK_NOT_READY) && (asc == 0x04)&&
+ (ascq == 0x01)) {
+ /* wait then repeat */
+ mdelay (2000);
+ notDone++;
+ } else {
+ ddvprintk((MYIOC_s_INFO_FMT
+ "DV: Reserved Failed.", ioc->name));
+ goto target_done;
+ }
+ } else {
+ ddvprintk((MYIOC_s_INFO_FMT "DV: Reserved Failed.",
+ ioc->name));
+ goto target_done;
+ }
+ }
+ }
+
+ mptscsih_fillbuf(pbuf1, sz, patt, 1);
+ iocmd.cmd = CMD_WriteBuffer;
+ iocmd.data_dma = buf1_dma;
+ iocmd.data = pbuf1;
+ iocmd.size = sz;
+ if (mptscsih_do_cmd(hd, &iocmd) < 0)
+ goto target_done;
+ else if (hd->pLocal == NULL)
+ goto target_done;
+ else {
+ rc = hd->pLocal->completion;
+ if (rc == MPT_SCANDV_GOOD)
+ ; /* Issue read buffer */
+ else if (rc == MPT_SCANDV_DID_RESET) {
+ /* If using echo buffers, reset to data buffers.
+ * Else do Fallback and restart
+ * this test (re-issue reserve
+ * because of bus reset).
+ */
+ if ((iocmd.flags & MPT_ICFLAG_ECHO) && (dataBufSize >= bufsize)) {
+ iocmd.flags &= ~MPT_ICFLAG_ECHO;
+ } else {
+ dv.cmd = MPT_FALLBACK;
+ mptscsih_dv_parms(hd, &dv, (void *)pcfg1Data);
+
+ if (mpt_config(hd->ioc, &cfg) != 0)
+ goto target_done;
+
+ if ((!dv.now.width) && (!dv.now.offset))
+ goto target_done;
+ }
+
+ iocmd.flags |= MPT_ICFLAG_DID_RESET;
+ patt = -1;
+ continue;
+ } else if (rc == MPT_SCANDV_SENSE) {
+ /* Restart data test if UA, else quit.
+ */
+ u8 skey = hd->pLocal->sense[2] & 0x0F;
+ ddvprintk((MYIOC_s_INFO_FMT
+ "SenseKey:ASC:ASCQ = (%x:%02x:%02x)\n", ioc->name, skey,
+ hd->pLocal->sense[12], hd->pLocal->sense[13]));
+ if (skey == SK_UNIT_ATTENTION) {
+ patt = -1;
+ continue;
+ } else if (skey == SK_ILLEGAL_REQUEST) {
+ if (iocmd.flags & MPT_ICFLAG_ECHO) {
+ if (dataBufSize >= bufsize) {
+ iocmd.flags &= ~MPT_ICFLAG_ECHO;
+ patt = -1;
+ continue;
+ }
+ }
+ goto target_done;
+ }
+ else
+ goto target_done;
+ } else {
+ /* fatal error */
+ goto target_done;
+ }
+ }
+
+ iocmd.cmd = CMD_ReadBuffer;
+ iocmd.data_dma = buf2_dma;
+ iocmd.data = pbuf2;
+ iocmd.size = sz;
+ if (mptscsih_do_cmd(hd, &iocmd) < 0)
+ goto target_done;
+ else if (hd->pLocal == NULL)
+ goto target_done;
+ else {
+ rc = hd->pLocal->completion;
+ if (rc == MPT_SCANDV_GOOD) {
+ /* If buffers compare,
+ * go to next pattern,
+ * else, do a fallback and restart
+ * data transfer test.
+ */
+ if (memcmp (pbuf1, pbuf2, sz) == 0) {
+ ; /* goto next pattern */
+ } else {
+ /* Miscompare with Echo buffer, go to data buffer,
+ * if that buffer exists.
+ * Miscompare with Data buffer, check first 4 bytes,
+ * some devices return capacity. Exit in this case.
+ */
+ if (iocmd.flags & MPT_ICFLAG_ECHO) {
+ if (dataBufSize >= bufsize)
+ iocmd.flags &= ~MPT_ICFLAG_ECHO;
+ else
+ goto target_done;
+ } else {
+ if (dataBufSize == (pbuf2[1]<<16 | pbuf2[2]<<8 | pbuf2[3])) {
+ /* Argh. Device returning wrong data.
+ * Quit DV for this device.
+ */
+ goto target_done;
+ }
+
+ /* Had an actual miscompare. Slow down.*/
+ dv.cmd = MPT_FALLBACK;
+ mptscsih_dv_parms(hd, &dv, (void *)pcfg1Data);
+
+ if (mpt_config(hd->ioc, &cfg) != 0)
+ goto target_done;
+
+ if ((!dv.now.width) && (!dv.now.offset))
+ goto target_done;
+ }
+
+ patt = -1;
+ continue;
+ }
+ } else if (rc == MPT_SCANDV_DID_RESET) {
+ /* Do Fallback and restart
+ * this test (re-issue reserve
+ * because of bus reset).
+ */
+ dv.cmd = MPT_FALLBACK;
+ mptscsih_dv_parms(hd, &dv, (void *)pcfg1Data);
+
+ if (mpt_config(hd->ioc, &cfg) != 0)
+ goto target_done;
+
+ if ((!dv.now.width) && (!dv.now.offset))
+ goto target_done;
+
+ iocmd.flags |= MPT_ICFLAG_DID_RESET;
+ patt = -1;
+ continue;
+ } else if (rc == MPT_SCANDV_SENSE) {
+ /* Restart data test if UA, else quit.
+ */
+ u8 skey = hd->pLocal->sense[2] & 0x0F;
+ ddvprintk((MYIOC_s_INFO_FMT
+ "SenseKey:ASC:ASCQ = (%x:%02x:%02x)\n", ioc->name, skey,
+ hd->pLocal->sense[12], hd->pLocal->sense[13]));
+ if (skey == SK_UNIT_ATTENTION) {
+ patt = -1;
+ continue;
+ }
+ else
+ goto target_done;
+ } else {
+ /* fatal error */
+ goto target_done;
+ }
+ }
+
+ } /* --- end of patt loop ---- */
+
+target_done:
+ if (iocmd.flags & MPT_ICFLAG_RESERVED) {
+ iocmd.cmd = CMD_Release6;
+ iocmd.data_dma = -1;
+ iocmd.data = NULL;
+ iocmd.size = 0;
+ if (mptscsih_do_cmd(hd, &iocmd) < 0)
+ printk(MYIOC_s_INFO_FMT "DV: Release failed. id %d",
+ ioc->name, id);
+ else if (hd->pLocal) {
+ if (hd->pLocal->completion == MPT_SCANDV_GOOD)
+ iocmd.flags &= ~MPT_ICFLAG_RESERVED;
} else {
- BadValue = ioop->dataPtr[Offset];
- len += sprintf(msg1+len, "\n Illegal DATA value=%02Xh found at DATA ",
- BadValue);
+ printk(MYIOC_s_INFO_FMT "DV: Release failed. id %d",
+ ioc->name, id);
+ }
+ }
+
+
+ /* Set if cfg1_dma_addr contents is valid
+ */
+ if (cfg.hdr != NULL) {
+
+ /* If disk, not U320, disable QAS
+ */
+ if ((inq0 == 0) && (dv.now.factor > MPT_ULTRA320))
+ hd->ioc->spi_data.noQas = MPT_TARGET_NO_NEGO_QAS;
+
+ dv.cmd = MPT_SAVE;
+ mptscsih_dv_parms(hd, &dv, (void *)pcfg1Data);
+
+ /* Save the final negotiated settings to
+ * SCSI device page 1.
+ */
+ cfg.hdr = &header1;
+ cfg.physAddr = cfg1_dma_addr;
+ cfg.action = MPI_CONFIG_ACTION_PAGE_WRITE_CURRENT;
+ cfg.dir = 1;
+ mpt_config(hd->ioc, &cfg);
+ }
+
+ /* If this is a RAID Passthrough, enable internal IOs
+ */
+ if (iocmd.flags & MPT_ICFLAG_PHYS_DISK) {
+ if (mptscsih_do_raid(hd, MPI_RAID_ACTION_ENABLE_PHYS_IO, &iocmd) < 0)
+ ddvprintk((MYIOC_s_ERR_FMT "RAID Queisce FAILED!\n", ioc->name));
+ }
+
+ /* Done with the DV scan of the current target
+ */
+ if (pDvBuf)
+ pci_free_consistent(ioc->pcidev, dv_alloc, pDvBuf, dvbuf_dma);
+
+ ddvtprintk((MYIOC_s_INFO_FMT "DV Done. IOs outstanding = %d\n",
+ ioc->name, atomic_read(&queue_depth)));
+
+ return;
+}
+
+/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
+/* mptscsih_dv_parms - perform a variety of operations on the
+ * parameters used for negotiation.
+ * @hd: Pointer to a SCSI host.
+ * @dv: Pointer to a structure that contains the maximum and current
+ * negotiated parameters.
+ */
+static void
+mptscsih_dv_parms(MPT_SCSI_HOST *hd, DVPARAMETERS *dv,void *pPage)
+{
+ VirtDevice *pTarget = NULL;
+ SCSIDevicePage0_t *pPage0 = NULL;
+ SCSIDevicePage1_t *pPage1 = NULL;
+ int val = 0, data, configuration;
+ u8 width = 0;
+ u8 offset = 0;
+ u8 factor = 0;
+ u8 negoFlags = 0;
+ u8 cmd = dv->cmd;
+ u8 id = dv->id;
+
+ switch (cmd) {
+ case MPT_GET_NVRAM_VALS:
+ ddvprintk((MYIOC_s_NOTE_FMT "Getting NVRAM: ",
+ hd->ioc->name));
+ /* Get the NVRAM values and save in tmax
+ * If not an LVD bus, the adapter minSyncFactor has been
+ * already throttled back.
+ */
+ if ((hd->Targets)&&((pTarget = hd->Targets[(int)id]) != NULL) && !pTarget->raidVolume) {
+ width = pTarget->maxWidth;
+ offset = pTarget->maxOffset;
+ factor = pTarget->minSyncFactor;
+ negoFlags = pTarget->negoFlags;
+ } else {
+ if (hd->ioc->spi_data.nvram && (hd->ioc->spi_data.nvram[id] != MPT_HOST_NVRAM_INVALID)) {
+ data = hd->ioc->spi_data.nvram[id];
+ width = data & MPT_NVRAM_WIDE_DISABLE ? 0 : 1;
+ if ((offset = hd->ioc->spi_data.maxSyncOffset) == 0)
+ factor = MPT_ASYNC;
+ else {
+ factor = (data & MPT_NVRAM_SYNC_MASK) >> MPT_NVRAM_SYNC_SHIFT;
+ if ((factor == 0) || (factor == MPT_ASYNC)){
+ factor = MPT_ASYNC;
+ offset = 0;
+ }
+ }
+ } else {
+ width = MPT_NARROW;
+ offset = 0;
+ factor = MPT_ASYNC;
+ }
+
+ /* Set the negotiation flags */
+ negoFlags = hd->ioc->spi_data.noQas;
+ if (!width)
+ negoFlags |= MPT_TARGET_NO_NEGO_WIDE;
+
+ if (!offset)
+ negoFlags |= MPT_TARGET_NO_NEGO_SYNC;
+ }
+
+ /* limit by adapter capabilities */
+ width = MIN(width, hd->ioc->spi_data.maxBusWidth);
+ offset = MIN(offset, hd->ioc->spi_data.maxSyncOffset);
+ factor = MAX(factor, hd->ioc->spi_data.minSyncFactor);
+
+ /* Check Consistency */
+ if (offset && (factor < MPT_ULTRA2) && !width)
+ factor = MPT_ULTRA2;
+
+ dv->max.width = width;
+ dv->max.offset = offset;
+ dv->max.factor = factor;
+ dv->max.flags = negoFlags;
+ ddvprintk((" width %d, factor %x, offset %x flags %x\n",
+ width, factor, offset, negoFlags));
+ break;
+
+ case MPT_UPDATE_MAX:
+ ddvprintk((MYIOC_s_NOTE_FMT
+ "Updating with SDP0 Data: ", hd->ioc->name));
+ /* Update tmax values with those from Device Page 0.*/
+ pPage0 = (SCSIDevicePage0_t *) pPage;
+ if (pPage0) {
+ val = cpu_to_le32(pPage0->NegotiatedParameters);
+ dv->max.width = val & MPI_SCSIDEVPAGE0_NP_WIDE ? 1 : 0;
+ dv->max.offset = (val&MPI_SCSIDEVPAGE0_NP_NEG_SYNC_OFFSET_MASK) >> 16;
+ dv->max.factor = (val&MPI_SCSIDEVPAGE0_NP_NEG_SYNC_PERIOD_MASK) >> 8;
+ }
+
+ dv->now.width = dv->max.width;
+ dv->now.offset = dv->max.offset;
+ dv->now.factor = dv->max.factor;
+ ddvprintk(("width %d, factor %x, offset %x, flags %x\n",
+ dv->now.width, dv->now.factor, dv->now.offset, dv->now.flags));
+ break;
+
+ case MPT_SET_MAX:
+ ddvprintk((MYIOC_s_NOTE_FMT "Setting Max: ",
+ hd->ioc->name));
+ /* Set current to the max values. Update the config page.*/
+ dv->now.width = dv->max.width;
+ dv->now.offset = dv->max.offset;
+ dv->now.factor = dv->max.factor;
+ dv->now.flags = dv->max.flags;
+
+ pPage1 = (SCSIDevicePage1_t *)pPage;
+ if (pPage1) {
+ mptscsih_setDevicePage1Flags (dv->now.width, dv->now.factor,
+ dv->now.offset, &val, &configuration, dv->now.flags);
+ pPage1->RequestedParameters = le32_to_cpu(val);
+ pPage1->Reserved = 0;
+ pPage1->Configuration = le32_to_cpu(configuration);
+
+ }
+
+ ddvprintk(("width %d, factor %x, offset %x request %x, config %x\n",
+ dv->now.width, dv->now.factor, dv->now.offset, val, configuration));
+ break;
+
+ case MPT_SET_MIN:
+ ddvprintk((MYIOC_s_NOTE_FMT "Setting Min: ",
+ hd->ioc->name));
+ /* Set page to asynchronous and narrow
+ * Do not update now, breaks fallback routine. */
+ width = MPT_NARROW;
+ offset = 0;
+ factor = MPT_ASYNC;
+ negoFlags = dv->max.flags;
+
+ pPage1 = (SCSIDevicePage1_t *)pPage;
+ if (pPage1) {
+ mptscsih_setDevicePage1Flags (width, factor,
+ offset, &val, &configuration, negoFlags);
+ pPage1->RequestedParameters = le32_to_cpu(val);
+ pPage1->Reserved = 0;
+ pPage1->Configuration = le32_to_cpu(configuration);
+ }
+ ddvprintk(("width %d, factor %x, offset %x request %x config %x\n",
+ dv->now.width, dv->now.factor,
+ dv->now.offset, val, configuration));
+ break;
+
+ case MPT_FALLBACK:
+ ddvprintk((MYIOC_s_NOTE_FMT
+ "Fallback: Start: offset %d, factor %x, width %d \n",
+ hd->ioc->name, dv->now.offset,
+ dv->now.factor, dv->now.width));
+ width = dv->now.width;
+ offset = dv->now.offset;
+ factor = dv->now.factor;
+ if ((offset) && (dv->max.width)) {
+ if (factor < MPT_ULTRA160)
+ factor = MPT_ULTRA160;
+ else if (factor < MPT_ULTRA2) {
+ factor = MPT_ULTRA2;
+ width = MPT_WIDE;
+ } else if ((factor == MPT_ULTRA2) && width) {
+ factor = MPT_ULTRA2;
+ width = MPT_NARROW;
+ } else if (factor < MPT_ULTRA) {
+ factor = MPT_ULTRA;
+ width = MPT_WIDE;
+ } else if ((factor == MPT_ULTRA) && width) {
+ factor = MPT_ULTRA;
+ width = MPT_NARROW;
+ } else if (factor < MPT_FAST) {
+ factor = MPT_FAST;
+ width = MPT_WIDE;
+ } else if ((factor == MPT_FAST) && width) {
+ factor = MPT_FAST;
+ width = MPT_NARROW;
+ } else if (factor < MPT_SCSI) {
+ factor = MPT_SCSI;
+ width = MPT_WIDE;
+ } else if ((factor == MPT_SCSI) && width) {
+ factor = MPT_SCSI;
+ width = MPT_NARROW;
+ } else {
+ factor = MPT_ASYNC;
+ offset = 0;
+ }
+
+ } else if (offset) {
+ width = MPT_NARROW;
+ if (factor < MPT_ULTRA)
+ factor = MPT_ULTRA;
+ else if (factor < MPT_FAST)
+ factor = MPT_FAST;
+ else if (factor < MPT_SCSI)
+ factor = MPT_SCSI;
+ else {
+ factor = MPT_ASYNC;
+ offset = 0;
+ }
+
+ } else {
+ width = MPT_NARROW;
+ factor = MPT_ASYNC;
}
- len += sprintf(msg1+len, "byte=%02Xh", Offset);
- if (SD_SKS_Bit_Pointer_Valid(sd))
- len += sprintf(msg1+len, "/bit=%1Xh", SD_SKS_Bit_Pointer(sd));
- } else if ((SenseKey == SK_RECOVERED_ERROR) ||
- (SenseKey == SK_HARDWARE_ERROR) ||
- (SenseKey == SK_MEDIUM_ERROR)) {
- len += sprintf(msg1+len, "\n Recovery algorithm Actual_Retry_Count=%02Xh",
- SD_Actual_Retry_Count(sd));
+ dv->max.flags |= MPT_TARGET_NO_NEGO_QAS;
+
+ dv->now.width = width;
+ dv->now.offset = offset;
+ dv->now.factor = factor;
+ dv->now.flags = dv->max.flags;
+
+ pPage1 = (SCSIDevicePage1_t *)pPage;
+ if (pPage1) {
+ mptscsih_setDevicePage1Flags (width, factor, offset, &val,
+ &configuration, dv->now.flags);
+
+ pPage1->RequestedParameters = le32_to_cpu(val);
+ pPage1->Reserved = 0;
+ pPage1->Configuration = le32_to_cpu(configuration);
}
- }
-}
-#endif
-/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
-static int dump_cdb(char *foo, unsigned char *cdb)
-{
- int i, grpCode, cdbLen;
- int l = 0;
+ ddvprintk(("Finish: offset %d, factor %x, width %d, request %x config %x\n",
+ dv->now.offset, dv->now.factor, dv->now.width, val, configuration));
+ break;
- grpCode = cdb[0] >> 5;
- if (grpCode < 1)
- cdbLen = 6;
- else if (grpCode < 3)
- cdbLen = 10;
- else if (grpCode == 5)
- cdbLen = 12;
- else
- cdbLen = 16;
+ case MPT_SAVE:
+ ddvprintk((MYIOC_s_NOTE_FMT
+ "Saving to Target structure: ", hd->ioc->name));
+ ddvprintk(("offset %d, factor %x, width %d \n",
+ dv->now.offset, dv->now.factor, dv->now.width));
- for (i=0; i < cdbLen; i++)
- l += sprintf(foo+l, " %02X", cdb[i]);
+ /* Save these values to target structures
+ * or overwrite nvram (phys disks only).
+ */
- return l;
-}
+ if ((hd->Targets)&&((pTarget = hd->Targets[(int)id]) != NULL) && !pTarget->raidVolume ) {
+ pTarget->maxWidth = dv->now.width;
+ pTarget->maxOffset = dv->now.offset;
+ pTarget->minSyncFactor = dv->now.factor;
+ pTarget->negoFlags = dv->now.flags;
+ } else {
+ /* Preserv all flags, use
+ * read-modify-write algorithm
+ */
+ if (hd->ioc->spi_data.nvram) {
+ data = hd->ioc->spi_data.nvram[id];
-/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
-static int dump_sd(char *foo, unsigned char *sd)
-{
- int snsLen = 8 + SD_Additional_Sense_Length(sd);
- int l = 0;
- int i;
+ if (dv->now.width)
+ data &= ~MPT_NVRAM_WIDE_DISABLE;
+ else
+ data |= MPT_NVRAM_WIDE_DISABLE;
- for (i=0; i < MIN(snsLen,18); i++)
- l += sprintf(foo+l, " %02X", sd[i]);
- l += sprintf(foo+l, "%s", snsLen>18 ? " ..." : "");
+ if (!dv->now.offset)
+ factor = MPT_ASYNC;
- return l;
+ data &= ~MPT_NVRAM_SYNC_MASK;
+ data |= (dv->now.factor << MPT_NVRAM_SYNC_SHIFT) & MPT_NVRAM_SYNC_MASK;
+
+ hd->ioc->spi_data.nvram[id] = data;
+ }
+ }
+ break;
+ }
}
/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
-/* Do ASC/ASCQ lookup/grindage to English readable string(s) */
-static const char * ascq_set_strings_4max(
- u8 ASC, u8 ASCQ,
- const char **s1, const char **s2, const char **s3, const char **s4)
+/* mptscsih_fillbuf - fill a buffer with a special data pattern
+ * cleanup. For bus scan only.
+ *
+ * @buffer: Pointer to data buffer to be filled.
+ * @size: Number of bytes to fill
+ * @index: Pattern index
+ * @width: bus width, 0 (8 bits) or 1 (16 bits)
+ */
+static void
+mptscsih_fillbuf(char *buffer, int size, int index, int width)
{
- static const char *asc_04_part1_string = "LOGICAL UNIT ";
- static const char *asc_04_part2a_string = "NOT READY, ";
- static const char *asc_04_part2b_string = "IS ";
- static const char *asc_04_ascq_NN_part3_strings[] = { /* ASC ASCQ (hex) */
- "CAUSE NOT REPORTABLE", /* 04 00 */
- "IN PROCESS OF BECOMING READY", /* 04 01 */
- "INITIALIZING CMD. REQUIRED", /* 04 02 */
- "MANUAL INTERVENTION REQUIRED", /* 04 03 */
- /* Add " IN PROGRESS" to all the following... */
- "FORMAT", /* 04 04 */
- "REBUILD", /* 04 05 */
- "RECALCULATION", /* 04 06 */
- "OPERATION", /* 04 07 */
- "LONG WRITE", /* 04 08 */
- "SELF-TEST", /* 04 09 */
- NULL
- };
- static char *asc_04_part4_string = " IN PROGRESS";
+ char *ptr = buffer;
+ int ii;
+ char byte;
+ short val;
- static char *asc_29_ascq_NN_strings[] = { /* ASC ASCQ (hex) */
- "POWER ON, RESET, OR BUS DEVICE RESET OCCURRED", /* 29 00 */
- "POWER ON OCCURRED", /* 29 01 */
- "SCSI BUS RESET OCCURRED", /* 29 02 */
- "BUS DEVICE RESET FUNCTION OCCURRED", /* 29 03 */
- "DEVICE INTERNAL RESET", /* 29 04 */
- "TRANSCEIVER MODE CHANGED TO SINGLE-ENDED", /* 29 05 */
- "TRANSCEIVER MODE CHANGED TO LVD", /* 29 06 */
- NULL
- };
- static char *ascq_vendor_uniq = "(Vendor Unique)";
- static char *ascq_noone = "(no matching ASC/ASCQ description found)";
- int idx;
+ switch (index) {
+ case 0:
- *s1 = *s2 = *s3 = *s4 = ""; /* set'em all to the empty "" string */
+ if (width) {
+ /* Pattern: 0000 FFFF 0000 FFFF
+ */
+ for (ii=0; ii < size; ii++, ptr++) {
+ if (ii & 0x02)
+ *ptr = 0xFF;
+ else
+ *ptr = 0x00;
+ }
+ } else {
+ /* Pattern: 00 FF 00 FF
+ */
+ for (ii=0; ii < size; ii++, ptr++) {
+ if (ii & 0x01)
+ *ptr = 0xFF;
+ else
+ *ptr = 0x00;
+ }
+ }
+ break;
- /* CHECKME! Need lock/sem?
- * Update and examine for isense module presense.
- */
- mptscsih_ASCQ_TablePtr = (ASCQ_Table_t *)mpt_v_ASCQ_TablePtr;
+ case 1:
+ if (width) {
+ /* Pattern: 5555 AAAA 5555 AAAA 5555
+ */
+ for (ii=0; ii < size; ii++, ptr++) {
+ if (ii & 0x02)
+ *ptr = 0xAA;
+ else
+ *ptr = 0x55;
+ }
+ } else {
+ /* Pattern: 55 AA 55 AA 55
+ */
+ for (ii=0; ii < size; ii++, ptr++) {
+ if (ii & 0x01)
+ *ptr = 0xAA;
+ else
+ *ptr = 0x55;
+ }
+ }
+ break;
- if (mptscsih_ASCQ_TablePtr == NULL) {
- /* 2nd chances... */
- if (ASC == 0x04 && (ASCQ < sizeof(asc_04_ascq_NN_part3_strings)/sizeof(char*)-1)) {
- *s1 = asc_04_part1_string;
- *s2 = (ASCQ == 0x01) ? asc_04_part2b_string : asc_04_part2a_string;
- *s3 = asc_04_ascq_NN_part3_strings[ASCQ];
- /* check for " IN PROGRESS" ones */
- if (ASCQ >= 0x04)
- *s4 = asc_04_part4_string;
- } else if (ASC == 0x29 && (ASCQ < sizeof(asc_29_ascq_NN_strings)/sizeof(char*)-1))
- *s1 = asc_29_ascq_NN_strings[ASCQ];
- /*
- * else { leave all *s[1-4] values pointing to the empty "" string }
+ case 2:
+ /* Pattern: 00 01 02 03 04 05
+ * ... FE FF 00 01..
*/
- return *s1;
- }
-
- /*
- * Need to check ASC here; if it is "special," then
- * the ASCQ is variable, and indicates failed component number.
- * We must treat the ASCQ as a "don't care" while searching the
- * mptscsih_ASCQ_Table[] by masking it off, and then restoring it later
- * on when we actually need to identify the failed component.
- */
- if (SPECIAL_ASCQ(ASC,ASCQ))
- ASCQ = 0xFF;
+ for (ii=0; ii < size; ii++, ptr++)
+ *ptr = (char) ii;
+ break;
- /* OK, now search mptscsih_ASCQ_Table[] for a matching entry */
- for (idx = 0; mptscsih_ASCQ_TablePtr && idx < mpt_ASCQ_TableSz; idx++)
- if ((ASC == mptscsih_ASCQ_TablePtr[idx].ASC) && (ASCQ == mptscsih_ASCQ_TablePtr[idx].ASCQ))
- return (*s1 = mptscsih_ASCQ_TablePtr[idx].Description);
+ case 3:
+ if (width) {
+ /* Wide Pattern: FFFE 0001 FFFD 0002
+ * ... 4000 DFFF 8000 EFFF
+ */
+ byte = 0;
+ for (ii=0; ii < size/2; ii++) {
+ /* Create the base pattern
+ */
+ val = (1 << byte);
+ /* every 64 (0x40) bytes flip the pattern
+ * since we fill 2 bytes / iteration,
+ * test for ii = 0x20
+ */
+ if (ii & 0x20)
+ val = ~(val);
+
+ if (ii & 0x01) {
+ *ptr = (char)( (val & 0xFF00) >> 8);
+ ptr++;
+ *ptr = (char)(val & 0xFF);
+ byte++;
+ byte &= 0x0F;
+ } else {
+ val = ~val;
+ *ptr = (char)( (val & 0xFF00) >> 8);
+ ptr++;
+ *ptr = (char)(val & 0xFF);
+ }
- if ((ASC >= 0x80) || (ASCQ >= 0x80))
- *s1 = ascq_vendor_uniq;
- else
- *s1 = ascq_noone;
+ ptr++;
+ }
+ } else {
+ /* Narrow Pattern: FE 01 FD 02 FB 04
+ * .. 7F 80 01 FE 02 FD ... 80 7F
+ */
+ byte = 0;
+ for (ii=0; ii < size; ii++, ptr++) {
+ /* Base pattern - first 32 bytes
+ */
+ if (ii & 0x01) {
+ *ptr = (1 << byte);
+ byte++;
+ byte &= 0x07;
+ } else {
+ *ptr = (char) (~(1 << byte));
+ }
- return *s1;
+ /* Flip the pattern every 32 bytes
+ */
+ if (ii & 0x20)
+ *ptr = ~(*ptr);
+ }
+ }
+ break;
+ }
}
+#endif /* ~MPTSCSIH_DISABLE_DOMAIN_VALIDATION */
/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
-/*
- * SCSI Error Report; desired output format...
- *---
-SCSI Error Report =-=-=-=-=-=-=-=-=-=-=-=-=-= (ioc0,scsi0:0)
- SCSI_Status=02h (CHECK CONDITION)
- Original_CDB[]: 00 00 00 00 00 00 - TestUnitReady
- SenseData[12h]: 70 00 06 00 00 00 00 0A 00 00 00 00 29 00 03 00 00 00
- SenseKey=6h (UNIT ATTENTION); FRU=03h
- ASC/ASCQ=29h/00h, "POWER ON, RESET, OR BUS DEVICE RESET OCCURRED"
- *---
+/* Commandline Parsing routines and defines.
+ *
+ * insmod format:
+ * insmod mptscsih mptscsih="width:1 dv:n factor:0x09"
+ * boot format:
+ * mptscsih=width:1,dv:n,factor:0x8
+ *
*/
+#ifdef MODULE
+#define ARG_SEP ' '
+#else
+#define ARG_SEP ','
+#endif
-int mpt_ScsiHost_ErrorReport(IO_Info_t *ioop)
-{
- char foo[512];
- char buf2[32];
- char *statstr;
- const char *opstr;
- int sk = SD_Sense_Key(ioop->sensePtr);
- const char *skstr = SenseKeyString[sk];
- unsigned char asc = SD_ASC(ioop->sensePtr);
- unsigned char ascq = SD_ASCQ(ioop->sensePtr);
- int l;
-
- /*
- * More quiet mode.
- * Filter out common, repetitive, warning-type errors... like:
- * POWER ON (06,29/00 or 06,29/01),
- * SPINNING UP (02,04/01),
- * LOGICAL UNIT NOT SUPPORTED (05,25/00), etc.
- */
- if ( (sk==SK_UNIT_ATTENTION && asc==0x29 && (ascq==0x00 || ascq==0x01))
- || (sk==SK_NOT_READY && asc==0x04 && ascq==0x01)
- || (sk==SK_ILLEGAL_REQUEST && asc==0x25 && ascq==0x00)
- )
- {
- /* Do nothing! */
- return 0;
- }
+static char setup_token[] __initdata =
+ "dv:"
+ "width:"
+ "factor:"
+ ; /* DONNOT REMOVE THIS ';' */
- /*
- * Protect ourselves...
- */
- if (ioop->cdbPtr == NULL)
- ioop->cdbPtr = dummyCDB;
- if (ioop->sensePtr == NULL)
- ioop->sensePtr = dummySenseData;
- if (ioop->inqPtr == NULL)
- ioop->inqPtr = dummyInqData;
- if (ioop->dataPtr == NULL)
- ioop->dataPtr = dummyScsiData;
+#define OPT_DV 1
+#define OPT_MAX_WIDTH 2
+#define OPT_MIN_SYNC_FACTOR 3
- statstr = NULL;
- if ((ioop->SCSIStatus >= sizeof(ScsiStatusString)/sizeof(char*)-1) ||
- ((statstr = (char*)ScsiStatusString[ioop->SCSIStatus]) == NULL)) {
- (void) sprintf(buf2, "Bad-Reserved-%02Xh", ioop->SCSIStatus);
- statstr = buf2;
+/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
+static int
+__init get_setup_token(char *p)
+{
+ char *cur = setup_token;
+ char *pc;
+ int i = 0;
+
+ while (cur != NULL && (pc = strchr(cur, ':')) != NULL) {
+ ++pc;
+ ++i;
+ if (!strncmp(p, cur, pc - cur))
+ return i;
+ cur = pc;
}
+ return 0;
+}
- opstr = NULL;
- if (1+ioop->cdbPtr[0] <= sizeof(ScsiCommonOpString)/sizeof(char*))
- opstr = ScsiCommonOpString[ioop->cdbPtr[0]];
- else if (mpt_ScsiOpcodesPtr)
- opstr = mpt_ScsiOpcodesPtr[ioop->cdbPtr[0]];
+/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
+static int
+__init mptscsih_setup(char *str)
+{
+ char *cur = str;
+ char *pc, *pv;
+ unsigned long val;
+ int c;
+
+ while (cur != NULL && (pc = strchr(cur, ':')) != NULL) {
+ char *pe;
+
+ val = 0;
+ pv = pc;
+ c = *++pv;
+
+ if (c == 'n')
+ val = 0;
+ else if (c == 'y')
+ val = 1;
+ else
+ val = (int) simple_strtoul(pv, &pe, 0);
- l = sprintf(foo, "SCSI Error Report =-=-= (%s)\n"
- " SCSI_Status=%02Xh (%s)\n"
- " Original_CDB[]:",
- ioop->DevIDStr,
- ioop->SCSIStatus,
- statstr);
- l += dump_cdb(foo+l, ioop->cdbPtr);
- if (opstr)
- l += sprintf(foo+l, " - \"%s\"", opstr);
- l += sprintf(foo+l, "\n SenseData[%02Xh]:", 8+SD_Additional_Sense_Length(ioop->sensePtr));
- l += dump_sd(foo+l, ioop->sensePtr);
- l += sprintf(foo+l, "\n SenseKey=%Xh (%s); FRU=%02Xh\n ASC/ASCQ=%02Xh/%02Xh",
- sk, skstr, SD_FRU(ioop->sensePtr), asc, ascq );
+ printk("Found Token: %s, value %x\n", cur, (int)val);
+ switch (get_setup_token(cur)) {
+ case OPT_DV:
+ driver_setup.dv = val;
+ break;
- {
- const char *x1, *x2, *x3, *x4;
- x1 = x2 = x3 = x4 = "";
- x1 = ascq_set_strings_4max(asc, ascq, &x1, &x2, &x3, &x4);
- if (x1 != NULL) {
- if (x1[0] != '(')
- l += sprintf(foo+l, " \"%s%s%s%s\"", x1,x2,x3,x4);
- else
- l += sprintf(foo+l, " %s%s%s%s", x1,x2,x3,x4);
- }
- }
+ case OPT_MAX_WIDTH:
+ driver_setup.max_width = val;
+ break;
-#if 0
- if (SPECIAL_ASCQ(asc,ascq))
- l += sprintf(foo+l, " (%02Xh)", ascq);
-#endif
+ case OPT_MIN_SYNC_FACTOR:
+ driver_setup.min_sync_fac = val;
+ break;
- PrintF(("%s\n", foo));
+ default:
+ printk("mptscsih_setup: unexpected boot option '%.*s' ignored\n", (int)(pc-cur+1), cur);
+ break;
+ }
- return l;
+ if ((cur = strchr(cur, ARG_SEP)) != NULL)
+ ++cur;
+ }
+ return 1;
}
/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
*
* (see also mptbase.c)
*
- * Copyright (c) 1999-2001 LSI Logic Corporation
+ * Copyright (c) 1999-2002 LSI Logic Corporation
* Originally By: Steven J. Ralston
- * (mailto:Steve.Ralston@lsil.com)
+ * (mailto:netscape.net)
+ * (mailto:Pam.Delaney@lsil.com)
*
- * $Id: mptscsih.h,v 1.7 2001/01/11 16:56:43 sralston Exp $
+ * $Id: mptscsih.h,v 1.18 2002/06/06 15:32:52 pdelaney Exp $
*/
/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
/*
* SCSI Public stuff...
*/
-#ifdef __sparc__
-#define MPT_SCSI_CAN_QUEUE 63
-#define MPT_SCSI_CMD_PER_LUN 63
- /* FIXME! Still investigating qd=64 hang on sparc64... */
-#else
-#define MPT_SCSI_CAN_QUEUE 64
-#define MPT_SCSI_CMD_PER_LUN 64
-#endif
+/*
+ * Try to keep these at 2^N-1
+ */
+#define MPT_FC_CAN_QUEUE 63
+//#define MPT_SCSI_CAN_QUEUE 31
+#define MPT_SCSI_CAN_QUEUE MPT_FC_CAN_QUEUE
+#define MPT_SCSI_CMD_PER_LUN 7
+
+#define MPT_SCSI_SG_DEPTH 40
+
+/* To disable domain validation, uncomment the
+ * following line. No effect for FC devices.
+ * For SCSI devices, driver will negotiate to
+ * NVRAM settings (if available) or to maximum adapter
+ * capabilities.
+ */
+/* #define MPTSCSIH_DISABLE_DOMAIN_VALIDATION */
+
+
+/* SCSI driver setup structure. Settings can be overridden
+ * by command line options.
+ */
+#define MPTSCSIH_DOMAIN_VALIDATION 1
+#define MPTSCSIH_MAX_WIDTH 1
+#define MPTSCSIH_MIN_SYNC 0x08
+
+struct mptscsih_driver_setup
+{
+ u8 dv;
+ u8 max_width;
+ u8 min_sync_fac;
+};
+
+
+#define MPTSCSIH_DRIVER_SETUP \
+{ \
+ MPTSCSIH_DOMAIN_VALIDATION, \
+ MPTSCSIH_MAX_WIDTH, \
+ MPTSCSIH_MIN_SYNC, \
+}
+
+
/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
/*
#define x_scsi_dev_reset mptscsih_dev_reset
#define x_scsi_host_reset mptscsih_host_reset
#define x_scsi_bios_param mptscsih_bios_param
+#define x_scsi_select_queue_depths mptscsih_select_queue_depths
#define x_scsi_taskmgmt_bh mptscsih_taskmgmt_bh
#define x_scsi_old_abort mptscsih_old_abort
extern int x_scsi_detect(Scsi_Host_Template *);
extern int x_scsi_release(struct Scsi_Host *host);
extern const char *x_scsi_info(struct Scsi_Host *);
-/*extern int x_scsi_command(Scsi_Cmnd *);*/
extern int x_scsi_queuecommand(Scsi_Cmnd *, void (*done)(Scsi_Cmnd *));
#ifdef MPT_SCSI_USE_NEW_EH
extern int x_scsi_abort(Scsi_Cmnd *);
extern int x_scsi_bus_reset(Scsi_Cmnd *);
extern int x_scsi_dev_reset(Scsi_Cmnd *);
-/*extern int x_scsi_host_reset(Scsi_Cmnd *);*/
+extern int x_scsi_host_reset(Scsi_Cmnd *);
#else
extern int x_scsi_old_abort(Scsi_Cmnd *);
extern int x_scsi_old_reset(Scsi_Cmnd *, unsigned int);
#endif
extern int x_scsi_bios_param(Disk *, kdev_t, int *);
+extern void x_scsi_select_queue_depths(struct Scsi_Host *, Scsi_Device *);
extern void x_scsi_taskmgmt_bh(void *);
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,3,0)
#ifdef MPT_SCSI_USE_NEW_EH
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,5,1)
+
+#define MPT_SCSIHOST { \
+ next: NULL, \
+ PROC_SCSI_DECL \
+ name: "MPT SCSI Host", \
+ detect: x_scsi_detect, \
+ release: x_scsi_release, \
+ info: x_scsi_info, \
+ command: NULL, \
+ queuecommand: x_scsi_queuecommand, \
+ eh_strategy_handler: NULL, \
+ eh_abort_handler: x_scsi_abort, \
+ eh_device_reset_handler: x_scsi_dev_reset, \
+ eh_bus_reset_handler: x_scsi_bus_reset, \
+ eh_host_reset_handler: x_scsi_host_reset, \
+ bios_param: x_scsi_bios_param, \
+ can_queue: MPT_SCSI_CAN_QUEUE, \
+ this_id: -1, \
+ sg_tablesize: MPT_SCSI_SG_DEPTH, \
+ cmd_per_lun: MPT_SCSI_CMD_PER_LUN, \
+ unchecked_isa_dma: 0, \
+ use_clustering: ENABLE_CLUSTERING, \
+}
+
+#else /* LINUX_VERSION_CODE >= KERNEL_VERSION(2,5,1) */
+
#define MPT_SCSIHOST { \
next: NULL, \
PROC_SCSI_DECL \
bios_param: x_scsi_bios_param, \
can_queue: MPT_SCSI_CAN_QUEUE, \
this_id: -1, \
- sg_tablesize: 25, \
+ sg_tablesize: MPT_SCSI_SG_DEPTH, \
cmd_per_lun: MPT_SCSI_CMD_PER_LUN, \
unchecked_isa_dma: 0, \
use_clustering: ENABLE_CLUSTERING, \
+ use_new_eh_code: 1 \
}
-#else
+#endif /* LINUX_VERSION_CODE >= KERNEL_VERSION(2,5,1) */
+
+#else /* MPT_SCSI_USE_NEW_EH */
#define MPT_SCSIHOST { \
next: NULL, \
bios_param: x_scsi_bios_param, \
can_queue: MPT_SCSI_CAN_QUEUE, \
this_id: -1, \
- sg_tablesize: 25, \
+ sg_tablesize: MPT_SCSI_SG_DEPTH, \
cmd_per_lun: MPT_SCSI_CMD_PER_LUN, \
unchecked_isa_dma: 0, \
use_clustering: ENABLE_CLUSTERING \
}
-#endif
+#endif /* MPT_SCSI_USE_NEW_EH */
/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
* (Ultimately) SCSI-3 definitions; for now, inheriting
* SCSI-2 definitions.
*
- * Copyright (c) 1996-2001 Steven J. Ralston
+ * Copyright (c) 1996-2002 Steven J. Ralston
* Written By: Steven J. Ralston (19960517)
- * (mailto:Steve.Ralston@lsil.com)
+ * (mailto:sjralston1@netscape.net)
+ * (mailto:Pam.Delaney@lsil.com)
*
- * $Id: scsi3.h,v 1.5 2001/04/06 14:31:32 sralston Exp $
+ * $Id: scsi3.h,v 1.9 2002/02/27 18:45:02 sralston Exp $
*/
#ifndef SCSI3_H_INCLUDED
#define CMD_Write10 0x2A
#define CMD_WriteVerify 0x2E
#define CMD_Verify 0x2F
+#define CMD_SynchronizeCache 0x35
#define CMD_ReadDefectData 0x37
+#define CMD_WriteBuffer 0x3B
+#define CMD_ReadBuffer 0x3C
#define CMD_ReadLong 0x3E
#define CMD_LogSelect 0x4C
#define CMD_LogSense 0x4D