[Zrouter-src-freebsd] ZRouter.org: push to FreeBSD HEAD tree

zrouter-src-freebsd at zrouter.org zrouter-src-freebsd at zrouter.org
Wed Feb 1 10:56:15 UTC 2012


details:   http://zrouter.org/hg/FreeBSD/head//rev/6b8317170ea4
changeset: 300:6b8317170ea4
user:      ray at terran.dlink.ua
date:      Wed Feb 01 11:28:20 2012 +0200
description:
Update mps driver

diffstat:

 head/sys/dev/mps/mpi/mpi2.h           |    62 +-
 head/sys/dev/mps/mpi/mpi2_cnfg.h      |   321 ++-
 head/sys/dev/mps/mpi/mpi2_hbd.h       |    42 +-
 head/sys/dev/mps/mpi/mpi2_history.txt |   148 +-
 head/sys/dev/mps/mpi/mpi2_init.h      |    61 +-
 head/sys/dev/mps/mpi/mpi2_ioc.h       |   162 +-
 head/sys/dev/mps/mpi/mpi2_ra.h        |    33 +-
 head/sys/dev/mps/mpi/mpi2_raid.h      |    37 +-
 head/sys/dev/mps/mpi/mpi2_sas.h       |    45 +-
 head/sys/dev/mps/mpi/mpi2_targ.h      |    39 +-
 head/sys/dev/mps/mpi/mpi2_tool.h      |    87 +-
 head/sys/dev/mps/mpi/mpi2_type.h      |    33 +-
 head/sys/dev/mps/mps.c                |   763 ++++++-
 head/sys/dev/mps/mps_config.c         |  1393 ++++++++++++++
 head/sys/dev/mps/mps_ioctl.h          |   287 ++-
 head/sys/dev/mps/mps_mapping.c        |  2268 +++++++++++++++++++++++
 head/sys/dev/mps/mps_mapping.h        |    71 +
 head/sys/dev/mps/mps_pci.c            |    64 +-
 head/sys/dev/mps/mps_sas.c            |  3165 ++++++++++++++++++++++----------
 head/sys/dev/mps/mps_sas.h            |   161 +
 head/sys/dev/mps/mps_sas_lsi.c        |   865 +++++++++
 head/sys/dev/mps/mps_table.c          |     8 +-
 head/sys/dev/mps/mps_user.c           |  1497 +++++++++++++++-
 head/sys/dev/mps/mpsvar.h             |   402 ++++-
 24 files changed, 10624 insertions(+), 1390 deletions(-)

diffs (14172 lines):

diff -r 52ad4c882614 -r 6b8317170ea4 head/sys/dev/mps/mpi/mpi2.h
--- a/head/sys/dev/mps/mpi/mpi2.h	Wed Feb 01 11:28:01 2012 +0200
+++ b/head/sys/dev/mps/mpi/mpi2.h	Wed Feb 01 11:28:20 2012 +0200
@@ -1,6 +1,35 @@
-/* $FreeBSD$ */
+/*-
+ * Copyright (c) 2011 LSI Corp.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * 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 OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * LSI MPT-Fusion Host Adapter FreeBSD
+ *
+ * $FreeBSD: head/sys/dev/mps/mpi/mpi2.h 230592 2012-01-26 18:17:21Z ken $
+ */
+
 /*
- *  Copyright (c) 2000-2009 LSI Corporation.
+ *  Copyright (c) 2000-2011 LSI Corporation.
  *
  *
  *           Name:  mpi2.h
@@ -9,7 +38,7 @@
  *                  scatter/gather formats.
  *  Creation Date:  June 21, 2006
  *
- *  mpi2.h Version:  02.00.14
+ *  mpi2.h Version:  02.00.18
  *
  *  Version History
  *  ---------------
@@ -58,6 +87,15 @@
  *                      Added MSI-x index mask and shift for Reply Post Host
  *                      Index register.
  *                      Added function code for Host Based Discovery Action.
+ *  02-10-10  02.00.15  Bumped MPI2_HEADER_VERSION_UNIT.
+ *                      Added define for MPI2_FUNCTION_PWR_MGMT_CONTROL.
+ *                      Added defines for product-specific range of message
+ *                      function codes, 0xF0 to 0xFF.
+ *  05-12-10  02.00.16  Bumped MPI2_HEADER_VERSION_UNIT.
+ *                      Added alternative defines for the SGE Direction bit.
+ *  08-11-10  02.00.17  Bumped MPI2_HEADER_VERSION_UNIT.
+ *  11-10-10  02.00.18  Bumped MPI2_HEADER_VERSION_UNIT.
+ *                      Added MPI2_IEEE_SGE_FLAGS_SYSTEMPLBCPI_ADDR define.
  *  --------------------------------------------------------------------------
  */
 
@@ -83,7 +121,7 @@
 #define MPI2_VERSION_02_00                  (0x0200)
 
 /* versioning for this MPI header set */
-#define MPI2_HEADER_VERSION_UNIT            (0x0E)
+#define MPI2_HEADER_VERSION_UNIT            (0x12)
 #define MPI2_HEADER_VERSION_DEV             (0x00)
 #define MPI2_HEADER_VERSION_UNIT_MASK       (0xFF00)
 #define MPI2_HEADER_VERSION_UNIT_SHIFT      (8)
@@ -476,8 +514,6 @@
 /*****************************************************************************
 *
 *        Message Functions
-*              0x80 -> 0x8F reserved for private message use per product
-*
 *
 *****************************************************************************/
 
@@ -508,6 +544,9 @@
 #define MPI2_FUNCTION_TARGET_CMD_BUF_LIST_POST      (0x25) /* Target Command Buffer Post List */
 #define MPI2_FUNCTION_RAID_ACCELERATOR              (0x2C) /* RAID Accelerator */
 #define MPI2_FUNCTION_HOST_BASED_DISCOVERY_ACTION   (0x2F) /* Host Based Discovery Action */
+#define MPI2_FUNCTION_PWR_MGMT_CONTROL              (0x30) /* Power Management Control */
+#define MPI2_FUNCTION_MIN_PRODUCT_SPECIFIC          (0xF0) /* beginning of product-specific range */
+#define MPI2_FUNCTION_MAX_PRODUCT_SPECIFIC          (0xFF) /* end of product-specific range */
 
 
 
@@ -922,6 +961,9 @@
 #define MPI2_SGE_FLAGS_IOC_TO_HOST              (0x00)
 #define MPI2_SGE_FLAGS_HOST_TO_IOC              (0x04)
 
+#define MPI2_SGE_FLAGS_DEST                     (MPI2_SGE_FLAGS_IOC_TO_HOST)
+#define MPI2_SGE_FLAGS_SOURCE                   (MPI2_SGE_FLAGS_HOST_TO_IOC)
+
 /* Address Size */
 
 #define MPI2_SGE_FLAGS_32_BIT_ADDRESSING        (0x00)
@@ -1046,11 +1088,11 @@
 /* Data Location Address Space */
 
 #define MPI2_IEEE_SGE_FLAGS_ADDR_MASK           (0x03)
-#define MPI2_IEEE_SGE_FLAGS_SYSTEM_ADDR         (0x00)
-#define MPI2_IEEE_SGE_FLAGS_IOCDDR_ADDR         (0x01)
+#define MPI2_IEEE_SGE_FLAGS_SYSTEM_ADDR         (0x00) /* IEEE Simple Element only */
+#define MPI2_IEEE_SGE_FLAGS_IOCDDR_ADDR         (0x01) /* IEEE Simple Element only */
 #define MPI2_IEEE_SGE_FLAGS_IOCPLB_ADDR         (0x02)
-#define MPI2_IEEE_SGE_FLAGS_IOCPLBNTA_ADDR      (0x03)
-
+#define MPI2_IEEE_SGE_FLAGS_IOCPLBNTA_ADDR      (0x03) /* IEEE Simple Element only */
+#define MPI2_IEEE_SGE_FLAGS_SYSTEMPLBCPI_ADDR   (0x03) /* IEEE Chain Element only */
 
 /****************************************************************************
 *  IEEE SGE operation Macros
diff -r 52ad4c882614 -r 6b8317170ea4 head/sys/dev/mps/mpi/mpi2_cnfg.h
--- a/head/sys/dev/mps/mpi/mpi2_cnfg.h	Wed Feb 01 11:28:01 2012 +0200
+++ b/head/sys/dev/mps/mpi/mpi2_cnfg.h	Wed Feb 01 11:28:20 2012 +0200
@@ -1,13 +1,42 @@
-/* $FreeBSD$ */
+/*-
+ * Copyright (c) 2011 LSI Corp.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * 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 OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * LSI MPT-Fusion Host Adapter FreeBSD
+ *
+ * $FreeBSD: head/sys/dev/mps/mpi/mpi2_cnfg.h 230592 2012-01-26 18:17:21Z ken $
+ */
+
 /*
- *  Copyright (c) 2000-2009 LSI Corporation.
+ *  Copyright (c) 2000-2011 LSI Corporation.
  *
  *
  *           Name:  mpi2_cnfg.h
  *          Title:  MPI Configuration messages and pages
  *  Creation Date:  November 10, 2006
  *
- *    mpi2_cnfg.h Version:  02.00.13
+ *    mpi2_cnfg.h Version:  02.00.17
  *
  *  Version History
  *  ---------------
@@ -110,6 +139,31 @@
  *                      Added Ethernet configuration pages.
  *  10-28-09  02.00.13  Added MPI2_IOUNITPAGE1_ENABLE_HOST_BASED_DISCOVERY.
  *                      Added SAS PHY Page 4 structure and defines.
+ *  02-10-10  02.00.14  Modified the comments for the configuration page
+ *                      structures that contain an array of data. The host
+ *                      should use the "count" field in the page data (e.g. the
+ *                      NumPhys field) to determine the number of valid elements
+ *                      in the array.
+ *                      Added/modified some MPI2_MFGPAGE_DEVID_SAS defines.
+ *                      Added PowerManagementCapabilities to IO Unit Page 7.
+ *                      Added PortWidthModGroup field to
+ *                      MPI2_SAS_IO_UNIT5_PHY_PM_SETTINGS.
+ *                      Added MPI2_CONFIG_PAGE_SASIOUNIT_6 and related defines.
+ *                      Added MPI2_CONFIG_PAGE_SASIOUNIT_7 and related defines.
+ *                      Added MPI2_CONFIG_PAGE_SASIOUNIT_8 and related defines.
+ *  05-12-10  02.00.15  Added MPI2_RAIDVOL0_STATUS_FLAG_VOL_NOT_CONSISTENT
+ *                      define.
+ *                      Added MPI2_PHYSDISK0_INCOMPATIBLE_MEDIA_TYPE define.
+ *                      Added MPI2_SAS_NEG_LINK_RATE_UNSUPPORTED_PHY define.
+ *  08-11-10  02.00.16  Removed IO Unit Page 1 device path (multi-pathing)
+ *                      defines.
+ *  11-10-10  02.00.17  Added ReceptacleID field (replacing Reserved1) to
+ *                      MPI2_MANPAGE7_CONNECTOR_INFO and reworked defines for
+ *                      the Pinout field.
+ *                      Added BoardTemperature and BoardTemperatureUnits fields
+ *                      to MPI2_CONFIG_PAGE_IO_UNIT_7.
+ *                      Added MPI2_CONFIG_EXTPAGETYPE_EXT_MANUFACTURING define
+ *                      and MPI2_CONFIG_PAGE_EXT_MAN_PS structure.
  *  --------------------------------------------------------------------------
  */
 
@@ -193,6 +247,7 @@
 #define MPI2_CONFIG_EXTPAGETYPE_DRIVER_MAPPING      (0x17)
 #define MPI2_CONFIG_EXTPAGETYPE_SAS_PORT            (0x18)
 #define MPI2_CONFIG_EXTPAGETYPE_ETHERNET            (0x19)
+#define MPI2_CONFIG_EXTPAGETYPE_EXT_MANUFACTURING   (0x1A)
 
 
 /*****************************************************************************
@@ -322,7 +377,7 @@
 #define MPI2_CONFIG_ACTION_PAGE_READ_NVRAM          (0x06)
 #define MPI2_CONFIG_ACTION_PAGE_GET_CHANGEABLE      (0x07)
 
-/* values for SGLFlags field are in the SGL section of mpi2.h */
+/* use MPI2_SGLFLAGS_ defines from mpi2.h for the SGLFlags field */
 
 
 /* Config Reply Message */
@@ -368,14 +423,19 @@
 #define MPI2_MFGPAGE_DEVID_SAS2116_1                (0x0064)
 #define MPI2_MFGPAGE_DEVID_SAS2116_2                (0x0065)
 
+#define MPI2_MFGPAGE_DEVID_SSS6200                  (0x007E)
+
 #define MPI2_MFGPAGE_DEVID_SAS2208_1                (0x0080)
 #define MPI2_MFGPAGE_DEVID_SAS2208_2                (0x0081)
 #define MPI2_MFGPAGE_DEVID_SAS2208_3                (0x0082)
 #define MPI2_MFGPAGE_DEVID_SAS2208_4                (0x0083)
 #define MPI2_MFGPAGE_DEVID_SAS2208_5                (0x0084)
 #define MPI2_MFGPAGE_DEVID_SAS2208_6                (0x0085)
-#define MPI2_MFGPAGE_DEVID_SAS2208_7                (0x0086)
-#define MPI2_MFGPAGE_DEVID_SAS2208_8                (0x0087)
+#define MPI2_MFGPAGE_DEVID_SAS2308_1                (0x0086)
+#define MPI2_MFGPAGE_DEVID_SAS2308_2                (0x0087)
+#define MPI2_MFGPAGE_DEVID_SAS2308_3                (0x006E)
+
+
 
 
 /* Manufacturing Page 0 */
@@ -541,7 +601,7 @@
 
 /*
  * Host code (drivers, BIOS, utilities, etc.) should leave this define set to
- * one and check Header.PageLength or NumPhys at runtime.
+ * one and check the value returned for NumPhys at runtime.
  */
 #ifndef MPI2_MAN_PAGE_5_PHY_ENTRIES
 #define MPI2_MAN_PAGE_5_PHY_ENTRIES         (1)
@@ -590,23 +650,31 @@
     U32                         Pinout;                 /* 0x00 */
     U8                          Connector[16];          /* 0x04 */
     U8                          Location;               /* 0x14 */
-    U8                          Reserved1;              /* 0x15 */
+    U8                          ReceptacleID;           /* 0x15 */
     U16                         Slot;                   /* 0x16 */
     U32                         Reserved2;              /* 0x18 */
 } MPI2_MANPAGE7_CONNECTOR_INFO, MPI2_POINTER PTR_MPI2_MANPAGE7_CONNECTOR_INFO,
   Mpi2ManPage7ConnectorInfo_t, MPI2_POINTER pMpi2ManPage7ConnectorInfo_t;
 
 /* defines for the Pinout field */
-#define MPI2_MANPAGE7_PINOUT_SFF_8484_L4                (0x00080000)
-#define MPI2_MANPAGE7_PINOUT_SFF_8484_L3                (0x00040000)
-#define MPI2_MANPAGE7_PINOUT_SFF_8484_L2                (0x00020000)
-#define MPI2_MANPAGE7_PINOUT_SFF_8484_L1                (0x00010000)
-#define MPI2_MANPAGE7_PINOUT_SFF_8470_L4                (0x00000800)
-#define MPI2_MANPAGE7_PINOUT_SFF_8470_L3                (0x00000400)
-#define MPI2_MANPAGE7_PINOUT_SFF_8470_L2                (0x00000200)
-#define MPI2_MANPAGE7_PINOUT_SFF_8470_L1                (0x00000100)
-#define MPI2_MANPAGE7_PINOUT_SFF_8482                   (0x00000002)
-#define MPI2_MANPAGE7_PINOUT_CONNECTION_UNKNOWN         (0x00000001)
+#define MPI2_MANPAGE7_PINOUT_LANE_MASK                  (0x0000FF00)
+#define MPI2_MANPAGE7_PINOUT_LANE_SHIFT                 (8)
+
+#define MPI2_MANPAGE7_PINOUT_TYPE_MASK                  (0x000000FF)
+#define MPI2_MANPAGE7_PINOUT_TYPE_UNKNOWN               (0x00)
+#define MPI2_MANPAGE7_PINOUT_SATA_SINGLE                (0x01)
+#define MPI2_MANPAGE7_PINOUT_SFF_8482                   (0x02)
+#define MPI2_MANPAGE7_PINOUT_SFF_8486                   (0x03)
+#define MPI2_MANPAGE7_PINOUT_SFF_8484                   (0x04)
+#define MPI2_MANPAGE7_PINOUT_SFF_8087                   (0x05)
+#define MPI2_MANPAGE7_PINOUT_SFF_8643_4I                (0x06)
+#define MPI2_MANPAGE7_PINOUT_SFF_8643_8I                (0x07)
+#define MPI2_MANPAGE7_PINOUT_SFF_8470                   (0x08)
+#define MPI2_MANPAGE7_PINOUT_SFF_8088                   (0x09)
+#define MPI2_MANPAGE7_PINOUT_SFF_8644_4X                (0x0A)
+#define MPI2_MANPAGE7_PINOUT_SFF_8644_8X                (0x0B)
+#define MPI2_MANPAGE7_PINOUT_SFF_8644_16X               (0x0C)
+#define MPI2_MANPAGE7_PINOUT_SFF_8436                   (0x0D)
 
 /* defines for the Location field */
 #define MPI2_MANPAGE7_LOCATION_UNKNOWN                  (0x01)
@@ -619,7 +687,7 @@
 
 /*
  * Host code (drivers, BIOS, utilities, etc.) should leave this define set to
- * one and check NumPhys at runtime.
+ * one and check the value returned for NumPhys at runtime.
  */
 #ifndef MPI2_MANPAGE7_CONNECTOR_INFO_MAX
 #define MPI2_MANPAGE7_CONNECTOR_INFO_MAX  (1)
@@ -640,7 +708,7 @@
   MPI2_POINTER PTR_MPI2_CONFIG_PAGE_MAN_7,
   Mpi2ManufacturingPage7_t, MPI2_POINTER pMpi2ManufacturingPage7_t;
 
-#define MPI2_MANUFACTURING7_PAGEVERSION                 (0x00)
+#define MPI2_MANUFACTURING7_PAGEVERSION                 (0x01)
 
 /* defines for the Flags field */
 #define MPI2_MANPAGE7_FLAG_USE_SLOT_INFO                (0x00000001)
@@ -717,6 +785,7 @@
 /* IO Unit Page 1 Flags defines */
 #define MPI2_IOUNITPAGE1_ENABLE_HOST_BASED_DISCOVERY    (0x00000800)
 #define MPI2_IOUNITPAGE1_MASK_SATA_WRITE_CACHE          (0x00000600)
+#define MPI2_IOUNITPAGE1_SATA_WRITE_CACHE_SHIFT         (9)
 #define MPI2_IOUNITPAGE1_ENABLE_SATA_WRITE_CACHE        (0x00000000)
 #define MPI2_IOUNITPAGE1_DISABLE_SATA_WRITE_CACHE       (0x00000200)
 #define MPI2_IOUNITPAGE1_UNCHANGED_SATA_WRITE_CACHE     (0x00000400)
@@ -724,15 +793,13 @@
 #define MPI2_IOUNITPAGE1_DISABLE_IR                     (0x00000040)
 #define MPI2_IOUNITPAGE1_DISABLE_TASK_SET_FULL_HANDLING (0x00000020)
 #define MPI2_IOUNITPAGE1_IR_USE_STATIC_VOLUME_ID        (0x00000004)
-#define MPI2_IOUNITPAGE1_MULTI_PATHING                  (0x00000002)
-#define MPI2_IOUNITPAGE1_SINGLE_PATHING                 (0x00000000)
 
 
 /* IO Unit Page 3 */
 
 /*
  * Host code (drivers, BIOS, utilities, etc.) should leave this define set to
- * one and check Header.PageLength at runtime.
+ * one and check the value returned for GPIOCount at runtime.
  */
 #ifndef MPI2_IO_UNIT_PAGE_3_GPIO_VAL_MAX
 #define MPI2_IO_UNIT_PAGE_3_GPIO_VAL_MAX    (1)
@@ -761,7 +828,7 @@
 
 /*
  * Upper layer code (drivers, utilities, etc.) should leave this define set to
- * one and check Header.PageLength or NumDmaEngines at runtime.
+ * one and check the value returned for NumDmaEngines at runtime.
  */
 #ifndef MPI2_IOUNITPAGE5_DMAENGINE_ENTRIES
 #define MPI2_IOUNITPAGE5_DMAENGINE_ENTRIES      (1)
@@ -826,15 +893,17 @@
     U8                      PCIeWidth;                              /* 0x06 */
     U8                      PCIeSpeed;                              /* 0x07 */
     U32                     ProcessorState;                         /* 0x08 */
-    U32                     Reserved2;                              /* 0x0C */
+    U32                     PowerManagementCapabilities;            /* 0x0C */
     U16                     IOCTemperature;                         /* 0x10 */
     U8                      IOCTemperatureUnits;                    /* 0x12 */
     U8                      IOCSpeed;                               /* 0x13 */
-    U32                     Reserved3;                              /* 0x14 */
+    U16                     BoardTemperature;                       /* 0x14 */
+    U8                      BoardTemperatureUnits;                  /* 0x16 */
+    U8                      Reserved3;                              /* 0x17 */
 } MPI2_CONFIG_PAGE_IO_UNIT_7, MPI2_POINTER PTR_MPI2_CONFIG_PAGE_IO_UNIT_7,
   Mpi2IOUnitPage7_t, MPI2_POINTER pMpi2IOUnitPage7_t;
 
-#define MPI2_IOUNITPAGE7_PAGEVERSION                    (0x00)
+#define MPI2_IOUNITPAGE7_PAGEVERSION                    (0x02)
 
 /* defines for IO Unit Page 7 PCIeWidth field */
 #define MPI2_IOUNITPAGE7_PCIE_WIDTH_X1              (0x01)
@@ -855,6 +924,13 @@
 #define MPI2_IOUNITPAGE7_PSTATE_DISABLED            (0x01)
 #define MPI2_IOUNITPAGE7_PSTATE_ENABLED             (0x02)
 
+/* defines for IO Unit Page 7 PowerManagementCapabilities field */
+#define MPI2_IOUNITPAGE7_PMCAP_12_5_PCT_IOCSPEED    (0x00000400)
+#define MPI2_IOUNITPAGE7_PMCAP_25_0_PCT_IOCSPEED    (0x00000200)
+#define MPI2_IOUNITPAGE7_PMCAP_50_0_PCT_IOCSPEED    (0x00000100)
+#define MPI2_IOUNITPAGE7_PMCAP_PCIE_WIDTH_CHANGE    (0x00000008)
+#define MPI2_IOUNITPAGE7_PMCAP_PCIE_SPEED_CHANGE    (0x00000004)
+
 /* defines for IO Unit Page 7 IOCTemperatureUnits field */
 #define MPI2_IOUNITPAGE7_IOC_TEMP_NOT_PRESENT       (0x00)
 #define MPI2_IOUNITPAGE7_IOC_TEMP_FAHRENHEIT        (0x01)
@@ -866,6 +942,11 @@
 #define MPI2_IOUNITPAGE7_IOC_SPEED_QUARTER          (0x04)
 #define MPI2_IOUNITPAGE7_IOC_SPEED_EIGHTH           (0x08)
 
+/* defines for IO Unit Page 7 BoardTemperatureUnits field */
+#define MPI2_IOUNITPAGE7_BOARD_TEMP_NOT_PRESENT     (0x00)
+#define MPI2_IOUNITPAGE7_BOARD_TEMP_FAHRENHEIT      (0x01)
+#define MPI2_IOUNITPAGE7_BOARD_TEMP_CELSIUS         (0x02)
+
 
 
 /****************************************************************************
@@ -1198,7 +1279,7 @@
 
 /*
  * Host code (drivers, BIOS, utilities, etc.) should leave this define set to
- * one and check Header.PageLength or NumPhys at runtime.
+ * one and check the value returned for NumPhys at runtime.
  */
 #ifndef MPI2_BIOS_PAGE_4_PHY_ENTRIES
 #define MPI2_BIOS_PAGE_4_PHY_ENTRIES        (1)
@@ -1272,7 +1353,7 @@
 
 /*
  * Host code (drivers, BIOS, utilities, etc.) should leave this define set to
- * one and check Header.PageLength at runtime.
+ * one and check the value returned for NumPhysDisks at runtime.
  */
 #ifndef MPI2_RAID_VOL_PAGE_0_PHYSDISK_MAX
 #define MPI2_RAID_VOL_PAGE_0_PHYSDISK_MAX       (1)
@@ -1329,6 +1410,7 @@
 #define MPI2_RAIDVOL0_STATUS_FLAG_CAPACITY_EXPANSION        (0x00040000)
 #define MPI2_RAIDVOL0_STATUS_FLAG_BACKGROUND_INIT           (0x00020000)
 #define MPI2_RAIDVOL0_STATUS_FLAG_RESYNC_IN_PROGRESS        (0x00010000)
+#define MPI2_RAIDVOL0_STATUS_FLAG_VOL_NOT_CONSISTENT        (0x00000080)
 #define MPI2_RAIDVOL0_STATUS_FLAG_OCE_ALLOWED               (0x00000040)
 #define MPI2_RAIDVOL0_STATUS_FLAG_BGI_COMPLETE              (0x00000020)
 #define MPI2_RAIDVOL0_STATUS_FLAG_1E_OFFSET_MIRROR          (0x00000000)
@@ -1451,11 +1533,15 @@
 #define MPI2_PHYSDISK0_INCOMPATIBLE_MAX_LBA             (0x03)
 #define MPI2_PHYSDISK0_INCOMPATIBLE_SATA_EXTENDED_CMD   (0x04)
 #define MPI2_PHYSDISK0_INCOMPATIBLE_REMOVEABLE_MEDIA    (0x05)
+#define MPI2_PHYSDISK0_INCOMPATIBLE_MEDIA_TYPE          (0x06)
 #define MPI2_PHYSDISK0_INCOMPATIBLE_UNKNOWN             (0xFF)
 
 /* PhysDiskAttributes defines */
+#define MPI2_PHYSDISK0_ATTRIB_MEDIA_MASK                (0x0C)
 #define MPI2_PHYSDISK0_ATTRIB_SOLID_STATE_DRIVE         (0x08)
 #define MPI2_PHYSDISK0_ATTRIB_HARD_DISK_DRIVE           (0x04)
+
+#define MPI2_PHYSDISK0_ATTRIB_PROTOCOL_MASK             (0x03)
 #define MPI2_PHYSDISK0_ATTRIB_SAS_PROTOCOL              (0x02)
 #define MPI2_PHYSDISK0_ATTRIB_SATA_PROTOCOL             (0x01)
 
@@ -1474,7 +1560,7 @@
 
 /*
  * Host code (drivers, BIOS, utilities, etc.) should leave this define set to
- * one and check Header.PageLength or NumPhysDiskPaths at runtime.
+ * one and check the value returned for NumPhysDiskPaths at runtime.
  */
 #ifndef MPI2_RAID_PHYS_DISK1_PATH_MAX
 #define MPI2_RAID_PHYS_DISK1_PATH_MAX   (1)
@@ -1527,6 +1613,7 @@
 #define MPI2_SAS_NEG_LINK_RATE_SATA_OOB_COMPLETE        (0x03)
 #define MPI2_SAS_NEG_LINK_RATE_PORT_SELECTOR            (0x04)
 #define MPI2_SAS_NEG_LINK_RATE_SMP_RESET_IN_PROGRESS    (0x05)
+#define MPI2_SAS_NEG_LINK_RATE_UNSUPPORTED_PHY          (0x06)
 #define MPI2_SAS_NEG_LINK_RATE_1_5                      (0x08)
 #define MPI2_SAS_NEG_LINK_RATE_3_0                      (0x09)
 #define MPI2_SAS_NEG_LINK_RATE_6_0                      (0x0A)
@@ -1553,6 +1640,7 @@
 #define MPI2_SAS_PHYINFO_PHY_VACANT                     (0x80000000)
 
 #define MPI2_SAS_PHYINFO_PHY_POWER_CONDITION_MASK       (0x18000000)
+#define MPI2_SAS_PHYINFO_SHIFT_PHY_POWER_CONDITION      (27)
 #define MPI2_SAS_PHYINFO_PHY_POWER_ACTIVE               (0x00000000)
 #define MPI2_SAS_PHYINFO_PHY_POWER_PARTIAL              (0x08000000)
 #define MPI2_SAS_PHYINFO_PHY_POWER_SLUMBER              (0x10000000)
@@ -1636,7 +1724,7 @@
 
 /*
  * Host code (drivers, BIOS, utilities, etc.) should leave this define set to
- * one and check Header.ExtPageLength or NumPhys at runtime.
+ * one and check the value returned for NumPhys at runtime.
  */
 #ifndef MPI2_SAS_IOUNIT0_PHY_MAX
 #define MPI2_SAS_IOUNIT0_PHY_MAX        (1)
@@ -1707,7 +1795,7 @@
 
 /*
  * Host code (drivers, BIOS, utilities, etc.) should leave this define set to
- * one and check Header.ExtPageLength or NumPhys at runtime.
+ * one and check the value returned for NumPhys at runtime.
  */
 #ifndef MPI2_SAS_IOUNIT1_PHY_MAX
 #define MPI2_SAS_IOUNIT1_PHY_MAX        (1)
@@ -1798,7 +1886,7 @@
 
 /*
  * Host code (drivers, BIOS, utilities, etc.) should leave this define set to
- * four and check Header.ExtPageLength or NumPhys at runtime.
+ * one and check the value returned for NumPhys at runtime.
  */
 #ifndef MPI2_SAS_IOUNIT4_PHY_MAX
 #define MPI2_SAS_IOUNIT4_PHY_MAX        (4)
@@ -1837,7 +1925,7 @@
 typedef struct _MPI2_SAS_IO_UNIT5_PHY_PM_SETTINGS
 {
     U8          ControlFlags;               /* 0x00 */
-    U8          Reserved1;                  /* 0x01 */
+    U8          PortWidthModGroup;          /* 0x01 */
     U16         InactivityTimerExponent;    /* 0x02 */
     U8          SATAPartialTimeout;         /* 0x04 */
     U8          Reserved2;                  /* 0x05 */
@@ -1857,6 +1945,9 @@
 #define MPI2_SASIOUNIT5_CONTROL_SATA_SLUMBER_ENABLE     (0x02)
 #define MPI2_SASIOUNIT5_CONTROL_SATA_PARTIAL_ENABLE     (0x01)
 
+/* defines for PortWidthModeGroup field */
+#define MPI2_SASIOUNIT5_PWMG_DISABLE                    (0xFF)
+
 /* defines for InactivityTimerExponent field */
 #define MPI2_SASIOUNIT5_ITE_MASK_SAS_SLUMBER            (0x7000)
 #define MPI2_SASIOUNIT5_ITE_SHIFT_SAS_SLUMBER           (12)
@@ -1878,7 +1969,7 @@
 
 /*
  * Host code (drivers, BIOS, utilities, etc.) should leave this define set to
- * one and check Header.ExtPageLength or NumPhys at runtime.
+ * one and check the value returned for NumPhys at runtime.
  */
 #ifndef MPI2_SAS_IOUNIT5_PHY_MAX
 #define MPI2_SAS_IOUNIT5_PHY_MAX        (1)
@@ -1896,7 +1987,137 @@
   MPI2_POINTER PTR_MPI2_CONFIG_PAGE_SASIOUNIT_5,
   Mpi2SasIOUnitPage5_t, MPI2_POINTER pMpi2SasIOUnitPage5_t;
 
-#define MPI2_SASIOUNITPAGE5_PAGEVERSION     (0x00)
+#define MPI2_SASIOUNITPAGE5_PAGEVERSION     (0x01)
+
+
+/* SAS IO Unit Page 6 */
+
+typedef struct _MPI2_SAS_IO_UNIT6_PORT_WIDTH_MOD_GROUP_STATUS
+{
+    U8          CurrentStatus;              /* 0x00 */
+    U8          CurrentModulation;          /* 0x01 */
+    U8          CurrentUtilization;         /* 0x02 */
+    U8          Reserved1;                  /* 0x03 */
+    U32         Reserved2;                  /* 0x04 */
+} MPI2_SAS_IO_UNIT6_PORT_WIDTH_MOD_GROUP_STATUS,
+  MPI2_POINTER PTR_MPI2_SAS_IO_UNIT6_PORT_WIDTH_MOD_GROUP_STATUS,
+  Mpi2SasIOUnit6PortWidthModGroupStatus_t,
+  MPI2_POINTER pMpi2SasIOUnit6PortWidthModGroupStatus_t;
+
+/* defines for CurrentStatus field */
+#define MPI2_SASIOUNIT6_STATUS_UNAVAILABLE                      (0x00)
+#define MPI2_SASIOUNIT6_STATUS_UNCONFIGURED                     (0x01)
+#define MPI2_SASIOUNIT6_STATUS_INVALID_CONFIG                   (0x02)
+#define MPI2_SASIOUNIT6_STATUS_LINK_DOWN                        (0x03)
+#define MPI2_SASIOUNIT6_STATUS_OBSERVATION_ONLY                 (0x04)
+#define MPI2_SASIOUNIT6_STATUS_INACTIVE                         (0x05)
+#define MPI2_SASIOUNIT6_STATUS_ACTIVE_IOUNIT                    (0x06)
+#define MPI2_SASIOUNIT6_STATUS_ACTIVE_HOST                      (0x07)
+
+/* defines for CurrentModulation field */
+#define MPI2_SASIOUNIT6_MODULATION_25_PERCENT                   (0x00)
+#define MPI2_SASIOUNIT6_MODULATION_50_PERCENT                   (0x01)
+#define MPI2_SASIOUNIT6_MODULATION_75_PERCENT                   (0x02)
+#define MPI2_SASIOUNIT6_MODULATION_100_PERCENT                  (0x03)
+
+/*
+ * Host code (drivers, BIOS, utilities, etc.) should leave this define set to
+ * one and check the value returned for NumGroups at runtime.
+ */
+#ifndef MPI2_SAS_IOUNIT6_GROUP_MAX
+#define MPI2_SAS_IOUNIT6_GROUP_MAX      (1)
+#endif
+
+typedef struct _MPI2_CONFIG_PAGE_SASIOUNIT_6
+{
+    MPI2_CONFIG_EXTENDED_PAGE_HEADER    Header;                     /* 0x00 */
+    U32                                 Reserved1;                  /* 0x08 */
+    U32                                 Reserved2;                  /* 0x0C */
+    U8                                  NumGroups;                  /* 0x10 */
+    U8                                  Reserved3;                  /* 0x11 */
+    U16                                 Reserved4;                  /* 0x12 */
+    MPI2_SAS_IO_UNIT6_PORT_WIDTH_MOD_GROUP_STATUS
+        PortWidthModulationGroupStatus[MPI2_SAS_IOUNIT6_GROUP_MAX]; /* 0x14 */
+} MPI2_CONFIG_PAGE_SASIOUNIT_6,
+  MPI2_POINTER PTR_MPI2_CONFIG_PAGE_SASIOUNIT_6,
+  Mpi2SasIOUnitPage6_t, MPI2_POINTER pMpi2SasIOUnitPage6_t;
+
+#define MPI2_SASIOUNITPAGE6_PAGEVERSION     (0x00)
+
+
+/* SAS IO Unit Page 7 */
+
+typedef struct _MPI2_SAS_IO_UNIT7_PORT_WIDTH_MOD_GROUP_SETTINGS
+{
+    U8          Flags;                      /* 0x00 */
+    U8          Reserved1;                  /* 0x01 */
+    U16         Reserved2;                  /* 0x02 */
+    U8          Threshold75Pct;             /* 0x04 */
+    U8          Threshold50Pct;             /* 0x05 */
+    U8          Threshold25Pct;             /* 0x06 */
+    U8          Reserved3;                  /* 0x07 */
+} MPI2_SAS_IO_UNIT7_PORT_WIDTH_MOD_GROUP_SETTINGS,
+  MPI2_POINTER PTR_MPI2_SAS_IO_UNIT7_PORT_WIDTH_MOD_GROUP_SETTINGS,
+  Mpi2SasIOUnit7PortWidthModGroupSettings_t,
+  MPI2_POINTER pMpi2SasIOUnit7PortWidthModGroupSettings_t;
+
+/* defines for Flags field */
+#define MPI2_SASIOUNIT7_FLAGS_ENABLE_PORT_WIDTH_MODULATION  (0x01)
+
+
+/*
+ * Host code (drivers, BIOS, utilities, etc.) should leave this define set to
+ * one and check the value returned for NumGroups at runtime.
+ */
+#ifndef MPI2_SAS_IOUNIT7_GROUP_MAX
+#define MPI2_SAS_IOUNIT7_GROUP_MAX      (1)
+#endif
+
+typedef struct _MPI2_CONFIG_PAGE_SASIOUNIT_7
+{
+    MPI2_CONFIG_EXTENDED_PAGE_HEADER            Header;             /* 0x00 */
+    U8                                          SamplingInterval;   /* 0x08 */
+    U8                                          WindowLength;       /* 0x09 */
+    U16                                         Reserved1;          /* 0x0A */
+    U32                                         Reserved2;          /* 0x0C */
+    U32                                         Reserved3;          /* 0x10 */
+    U8                                          NumGroups;          /* 0x14 */
+    U8                                          Reserved4;          /* 0x15 */
+    U16                                         Reserved5;          /* 0x16 */
+    MPI2_SAS_IO_UNIT7_PORT_WIDTH_MOD_GROUP_SETTINGS
+        PortWidthModulationGroupSettings[MPI2_SAS_IOUNIT7_GROUP_MAX]; /* 0x18 */
+} MPI2_CONFIG_PAGE_SASIOUNIT_7,
+  MPI2_POINTER PTR_MPI2_CONFIG_PAGE_SASIOUNIT_7,
+  Mpi2SasIOUnitPage7_t, MPI2_POINTER pMpi2SasIOUnitPage7_t;
+
+#define MPI2_SASIOUNITPAGE7_PAGEVERSION     (0x00)
+
+
+/* SAS IO Unit Page 8 */
+
+typedef struct _MPI2_CONFIG_PAGE_SASIOUNIT_8
+{
+    MPI2_CONFIG_EXTENDED_PAGE_HEADER    Header;                             /* 0x00 */
+    U32                                 Reserved1;                          /* 0x08 */
+    U32                                 PowerManagementCapabilities;        /* 0x0C */
+    U32                                 Reserved2;                          /* 0x10 */
+} MPI2_CONFIG_PAGE_SASIOUNIT_8,
+  MPI2_POINTER PTR_MPI2_CONFIG_PAGE_SASIOUNIT_8,
+  Mpi2SasIOUnitPage8_t, MPI2_POINTER pMpi2SasIOUnitPage8_t;
+
+#define MPI2_SASIOUNITPAGE8_PAGEVERSION     (0x00)
+
+/* defines for PowerManagementCapabilities field */
+#define MPI2_SASIOUNIT8_PM_HOST_PORT_WIDTH_MOD          (0x000001000)
+#define MPI2_SASIOUNIT8_PM_HOST_SAS_SLUMBER_MODE        (0x000000800)
+#define MPI2_SASIOUNIT8_PM_HOST_SAS_PARTIAL_MODE        (0x000000400)
+#define MPI2_SASIOUNIT8_PM_HOST_SATA_SLUMBER_MODE       (0x000000200)
+#define MPI2_SASIOUNIT8_PM_HOST_SATA_PARTIAL_MODE       (0x000000100)
+#define MPI2_SASIOUNIT8_PM_IOUNIT_PORT_WIDTH_MOD        (0x000000010)
+#define MPI2_SASIOUNIT8_PM_IOUNIT_SAS_SLUMBER_MODE      (0x000000008)
+#define MPI2_SASIOUNIT8_PM_IOUNIT_SAS_PARTIAL_MODE      (0x000000004)
+#define MPI2_SASIOUNIT8_PM_IOUNIT_SATA_SLUMBER_MODE     (0x000000002)
+#define MPI2_SASIOUNIT8_PM_IOUNIT_SATA_PARTIAL_MODE     (0x000000001)
 
 
 
@@ -2187,7 +2408,7 @@
 
 /*
  * Host code (drivers, BIOS, utilities, etc.) should leave this define set to
- * one and check Header.ExtPageLength or NumPhyEvents at runtime.
+ * one and check the value returned for NumPhyEvents at runtime.
  */
 #ifndef MPI2_SASPHY2_PHY_EVENT_MAX
 #define MPI2_SASPHY2_PHY_EVENT_MAX      (1)
@@ -2280,7 +2501,7 @@
 
 /*
  * Host code (drivers, BIOS, utilities, etc.) should leave this define set to
- * one and check Header.ExtPageLength or NumPhyEvents at runtime.
+ * one and check the value returned for NumPhyEvents at runtime.
  */
 #ifndef MPI2_SASPHY3_PHY_EVENT_MAX
 #define MPI2_SASPHY3_PHY_EVENT_MAX      (1)
@@ -2392,7 +2613,7 @@
 
 /*
  * Host code (drivers, BIOS, utilities, etc.) should leave this define set to
- * one and check Header.ExtPageLength or NumPhys at runtime.
+ * one and check the value returned for NumLogEntries at runtime.
  */
 #ifndef MPI2_LOG_0_NUM_LOG_ENTRIES
 #define MPI2_LOG_0_NUM_LOG_ENTRIES          (1)
@@ -2442,7 +2663,7 @@
 
 /*
  * Host code (drivers, BIOS, utilities, etc.) should leave this define set to
- * one and check Header.ExtPageLength or NumPhys at runtime.
+ * one and check the value returned for NumElements at runtime.
  */
 #ifndef MPI2_RAIDCONFIG0_MAX_ELEMENTS
 #define MPI2_RAIDCONFIG0_MAX_ELEMENTS       (1)
@@ -2642,5 +2863,25 @@
 #define MPI2_ETHPG1_MS_DATA_RATE_1GBIT              (0x03)
 
 
+/****************************************************************************
+*   Extended Manufacturing Config Pages
+****************************************************************************/
+
+/*
+ * Generic structure to use for product-specific extended manufacturing pages
+ * (currently Extended Manufacturing Page 40 through Extended Manufacturing
+ * Page 60).
+ */
+
+typedef struct _MPI2_CONFIG_PAGE_EXT_MAN_PS
+{
+    MPI2_CONFIG_EXTENDED_PAGE_HEADER    Header;                 /* 0x00 */
+    U32                                 ProductSpecificInfo;    /* 0x08 */
+} MPI2_CONFIG_PAGE_EXT_MAN_PS,
+  MPI2_POINTER PTR_MPI2_CONFIG_PAGE_EXT_MAN_PS,
+  Mpi2ExtManufacturingPagePS_t, MPI2_POINTER pMpi2ExtManufacturingPagePS_t;
+
+/* PageVersion should be provided by product-specific code */
+
 #endif
 
diff -r 52ad4c882614 -r 6b8317170ea4 head/sys/dev/mps/mpi/mpi2_hbd.h
--- a/head/sys/dev/mps/mpi/mpi2_hbd.h	Wed Feb 01 11:28:01 2012 +0200
+++ b/head/sys/dev/mps/mpi/mpi2_hbd.h	Wed Feb 01 11:28:20 2012 +0200
@@ -1,13 +1,42 @@
-/* $FreeBSD$ */
+/*-
+ * Copyright (c) 2011 LSI Corp.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * 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 OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * LSI MPT-Fusion Host Adapter FreeBSD
+ *
+ * $FreeBSD: head/sys/dev/mps/mpi/mpi2_hbd.h 230592 2012-01-26 18:17:21Z ken $
+ */
+
 /*
- *  Copyright (c) 2009 LSI Corporation.
+ *  Copyright (c) 2009-2011 LSI Corporation.
  *
  *
  *           Name:  mpi2_hbd.h
  *          Title:  MPI Host Based Discovery messages and structures
  *  Creation Date:  October 21, 2009
  *
- *  mpi2_hbd.h Version:  02.00.00
+ *  mpi2_hbd.h Version:  02.00.01
  *
  *  Version History
  *  ---------------
@@ -15,6 +44,8 @@
  *  Date      Version   Description
  *  --------  --------  ------------------------------------------------------
  *  10-28-09  02.00.00  Initial version.
+ *  08-11-10  02.00.01  Removed PortGroups, DmaGroup, and ControlGroup from
+ *                      HBD Action request, replaced by AdditionalInfo field.
  *  --------------------------------------------------------------------------
  */
 
@@ -48,10 +79,7 @@
     U8                      Port;               /* 0x25 */
     U8                      MaxConnections;     /* 0x26 */
     U8                      MaxRate;            /* 0x27 */
-    U8                      PortGroups;         /* 0x28 */
-    U8                      DmaGroup;           /* 0x29 */
-    U8                      ControlGroup;       /* 0x2A */
-    U8                      Reserved6;          /* 0x2B */
+    U32                     AdditionalInfo;     /* 0x28 */
     U16                     InitialAWT;         /* 0x2C */
     U16                     Reserved7;          /* 0x2E */
     U32                     Reserved8;          /* 0x30 */
diff -r 52ad4c882614 -r 6b8317170ea4 head/sys/dev/mps/mpi/mpi2_history.txt
--- a/head/sys/dev/mps/mpi/mpi2_history.txt	Wed Feb 01 11:28:01 2012 +0200
+++ b/head/sys/dev/mps/mpi/mpi2_history.txt	Wed Feb 01 11:28:20 2012 +0200
@@ -1,29 +1,58 @@
-/* $FreeBSD$ */
+/*-
+ * Copyright (c) 2011 LSI Corp.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * 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 OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * LSI MPT-Fusion Host Adapter FreeBSD
+ *
+ * $FreeBSD: head/sys/dev/mps/mpi/mpi2_history.txt 230592 2012-01-26 18:17:21Z ken $
+ */
+
  ==============================
  Fusion-MPT MPI 2.0 Header File Change History
  ==============================
 
- Copyright (c) 2000-2009 LSI Corporation.
+ Copyright (c) 2000-2011 LSI Corporation.
 
  ---------------------------------------
- Header Set Release Version:    02.00.14
- Header Set Release Date:       10-28-09
+ Header Set Release Version:    02.00.18
+ Header Set Release Date:       11-10-10
  ---------------------------------------
 
  Filename               Current version     Prior version
  ----------             ---------------     -------------
- mpi2.h                 02.00.14            02.00.13
- mpi2_cnfg.h            02.00.13            02.00.12
- mpi2_init.h            02.00.08            02.00.07
- mpi2_ioc.h             02.00.13            02.00.12
- mpi2_raid.h            02.00.04            02.00.04
- mpi2_sas.h             02.00.03            02.00.02
- mpi2_targ.h            02.00.03            02.00.03
- mpi2_tool.h            02.00.04            02.00.04
+ mpi2.h                 02.00.18            02.00.17
+ mpi2_cnfg.h            02.00.17            02.00.16
+ mpi2_init.h            02.00.11            02.00.10
+ mpi2_ioc.h             02.00.16            02.00.15
+ mpi2_raid.h            02.00.05            02.00.05
+ mpi2_sas.h             02.00.05            02.00.05
+ mpi2_targ.h            02.00.04            02.00.04
+ mpi2_tool.h            02.00.06            02.00.06
  mpi2_type.h            02.00.00            02.00.00
  mpi2_ra.h              02.00.00            02.00.00
- mpi2_hbd.h             02.00.00
- mpi2_history.txt       02.00.14            02.00.13
+ mpi2_hbd.h             02.00.01            02.00.01
+ mpi2_history.txt       02.00.18            02.00.17
 
 
  *  Date      Version   Description
@@ -72,6 +101,15 @@
  *                      Added MSI-x index mask and shift for Reply Post Host
  *                      Index register.
  *                      Added function code for Host Based Discovery Action.
+ *  02-10-10  02.00.15  Bumped MPI2_HEADER_VERSION_UNIT.
+ *                      Added define for MPI2_FUNCTION_PWR_MGMT_CONTROL.
+ *                      Added defines for product-specific range of message
+ *                      function codes, 0xF0 to 0xFF.
+ *  05-12-10  02.00.16  Bumped MPI2_HEADER_VERSION_UNIT.
+ *                      Added alternative defines for the SGE Direction bit.
+ *  08-11-10  02.00.17  Bumped MPI2_HEADER_VERSION_UNIT.
+ *  11-10-10  02.00.18  Bumped MPI2_HEADER_VERSION_UNIT.
+ *                      Added MPI2_IEEE_SGE_FLAGS_SYSTEMPLBCPI_ADDR define.
  *  --------------------------------------------------------------------------
 
 mpi2_cnfg.h
@@ -171,6 +209,31 @@
  *                      Added Ethernet configuration pages.
  *  10-28-09  02.00.13  Added MPI2_IOUNITPAGE1_ENABLE_HOST_BASED_DISCOVERY.
  *                      Added SAS PHY Page 4 structure and defines.
+ *  02-10-10  02.00.14  Modified the comments for the configuration page
+ *                      structures that contain an array of data. The host
+ *                      should use the "count" field in the page data (e.g. the
+ *                      NumPhys field) to determine the number of valid elements
+ *                      in the array.
+ *                      Added/modified some MPI2_MFGPAGE_DEVID_SAS defines.
+ *                      Added PowerManagementCapabilities to IO Unit Page 7.
+ *                      Added PortWidthModGroup field to
+ *                      MPI2_SAS_IO_UNIT5_PHY_PM_SETTINGS.
+ *                      Added MPI2_CONFIG_PAGE_SASIOUNIT_6 and related defines.
+ *                      Added MPI2_CONFIG_PAGE_SASIOUNIT_7 and related defines.
+ *                      Added MPI2_CONFIG_PAGE_SASIOUNIT_8 and related defines.
+ *  05-12-10  02.00.15  Added MPI2_RAIDVOL0_STATUS_FLAG_VOL_NOT_CONSISTENT
+ *                      define.
+ *                      Added MPI2_PHYSDISK0_INCOMPATIBLE_MEDIA_TYPE define.
+ *                      Added MPI2_SAS_NEG_LINK_RATE_UNSUPPORTED_PHY define.
+ *  08-11-10  02.00.16  Removed IO Unit Page 1 device path (multi-pathing)
+ *                      defines.
+ *  11-10-10  02.00.17  Added ReceptacleID field (replacing Reserved1) to
+ *                      MPI2_MANPAGE7_CONNECTOR_INFO and reworked defines for
+ *                      the Pinout field.
+ *                      Added BoardTemperature and BoardTemperatureUnits fields
+ *                      to MPI2_CONFIG_PAGE_IO_UNIT_7.
+ *                      Added MPI2_CONFIG_EXTPAGETYPE_EXT_MANUFACTURING define
+ *                      and MPI2_CONFIG_PAGE_EXT_MAN_PS structure.
  *  --------------------------------------------------------------------------
 
 mpi2_init.h
@@ -192,6 +255,9 @@
  *                      both SCSI IO Error Reply and SCSI Task Management Reply.
  *                      Added ResponseInfo field to MPI2_SCSI_TASK_MANAGE_REPLY.
  *                      Added MPI2_SCSITASKMGMT_RSP_TM_OVERLAPPED_TAG define.
+ *  02-10-10  02.00.09  Removed unused structure that had "#if 0" around it.
+ *  05-12-10  02.00.10  Added optional vendor-unique region to SCSI IO Request.
+ *  11-10-10  02.00.11  Added MPI2_SCSIIO_NUM_SGLOFFSETS define.
  *  --------------------------------------------------------------------------
 
 mpi2_ioc.h
@@ -280,6 +346,12 @@
  *                      (MPI2_FW_HEADER_PID_).
  *                      Modified values for SAS ProductID Family
  *                      (MPI2_FW_HEADER_PID_FAMILY_).
+ *  02-10-10  02.00.14  Added SAS Quiesce Event structure and defines.
+ *                      Added PowerManagementControl Request structures and
+ *                      defines.
+ *  05-12-10  02.00.15  Marked Task Set Full Event as obsolete.
+ *                      Added MPI2_EVENT_SAS_TOPO_LR_UNSUPPORTED_PHY define.
+ *  11-10-10  02.00.16  Added MPI2_FW_DOWNLOAD_ITYPE_MIN_PRODUCT_SPECIFIC.
  *  --------------------------------------------------------------------------
 
 mpi2_raid.h
@@ -292,6 +364,7 @@
  *                      can be sized by the build environment.
  *  07-30-09  02.00.04  Added proper define for the Use Default Settings bit of
  *                      VolumeCreationFlags and marked the old one as obsolete.
+ *  05-12-10  02.00.05  Added MPI2_RAID_VOL_FLAGS_OP_MDC define.
  *  --------------------------------------------------------------------------
 
 mpi2_sas.h
@@ -302,6 +375,8 @@
  *                      Request.
  *  10-28-09  02.00.03  Changed the type of SGL in MPI2_SATA_PASSTHROUGH_REQUEST
  *                      to MPI2_SGE_IO_UNION since it supports chained SGLs.
+ *  05-12-10  02.00.04  Modified some comments.
+ *  08-11-10  02.00.05  Added NCQ operations to SAS IO Unit Control.
  *  --------------------------------------------------------------------------
 
 mpi2_targ.h
@@ -313,6 +388,7 @@
  *                      MPI2_TARGET_CMD_BUF_POST_BASE_REQUEST.
  *                      Target Status Send Request only takes a single SGE for
  *                      response data.
+ *  02-10-10  02.00.04  Added comment to MPI2_TARGET_SSP_RSP_IU structure.
  *  --------------------------------------------------------------------------
 
 mpi2_tool.h
@@ -325,6 +401,9 @@
  *                      and reply messages.
  *                      Added MPI2_DIAG_BUF_TYPE_EXTENDED.
  *                      Incremented MPI2_DIAG_BUF_TYPE_COUNT.
+ *  05-12-10  02.00.05  Added Diagnostic Data Upload tool.
+ *  08-11-10  02.00.06  Added defines that were missing for Diagnostic Buffer
+ *                      Post Request.
  *  --------------------------------------------------------------------------
 
 mpi2_type.h
@@ -337,24 +416,40 @@
 
 mpi2_hbd.h
  *  10-28-09  02.00.00  Initial version.
+ *  08-11-10  02.00.01  Removed PortGroups, DmaGroup, and ControlGroup from
+ *                      HBD Action request, replaced by AdditionalInfo field.
  *  --------------------------------------------------------------------------
 
 
 mpi2_history.txt         Parts list history
 
-Filename     02.00.14  02.00.13  02.00.12
-----------   --------  --------  --------
-mpi2.h       02.00.14  02.00.13  02.00.12
-mpi2_cnfg.h  02.00.13  02.00.12  02.00.11
-mpi2_init.h  02.00.08  02.00.07  02.00.07
-mpi2_ioc.h   02.00.13  02.00.12  02.00.11
-mpi2_raid.h  02.00.04  02.00.04  02.00.03
-mpi2_sas.h   02.00.03  02.00.02  02.00.02
-mpi2_targ.h  02.00.03  02.00.03  02.00.03
-mpi2_tool.h  02.00.04  02.00.04  02.00.03
-mpi2_type.h  02.00.00  02.00.00  02.00.00
-mpi2_ra.h    02.00.00  02.00.00  02.00.00
-mpi2_hbd.h   02.00.00
+Filename     02.00.18
+----------   --------
+mpi2.h       02.00.18
+mpi2_cnfg.h  02.00.17
+mpi2_init.h  02.00.11
+mpi2_ioc.h   02.00.16
+mpi2_raid.h  02.00.05
+mpi2_sas.h   02.00.05
+mpi2_targ.h  02.00.04
+mpi2_tool.h  02.00.06
+mpi2_type.h  02.00.00
+mpi2_ra.h    02.00.00
+mpi2_hbd.h   02.00.01
+
+Filename     02.00.17  02.00.16  02.00.15  02.00.14  02.00.13  02.00.12
+----------   --------  --------  --------  --------  --------  --------
+mpi2.h       02.00.17  02.00.16  02.00.15  02.00.14  02.00.13  02.00.12
+mpi2_cnfg.h  02.00.16  02.00.15  02.00.14  02.00.13  02.00.12  02.00.11
+mpi2_init.h  02.00.10  02.00.10  02.00.09  02.00.08  02.00.07  02.00.07
+mpi2_ioc.h   02.00.15  02.00.15  02.00.14  02.00.13  02.00.12  02.00.11
+mpi2_raid.h  02.00.05  02.00.05  02.00.04  02.00.04  02.00.04  02.00.03
+mpi2_sas.h   02.00.05  02.00.04  02.00.03  02.00.03  02.00.02  02.00.02
+mpi2_targ.h  02.00.04  02.00.04  02.00.04  02.00.03  02.00.03  02.00.03
+mpi2_tool.h  02.00.06  02.00.05  02.00.04  02.00.04  02.00.04  02.00.03
+mpi2_type.h  02.00.00  02.00.00  02.00.00  02.00.00  02.00.00  02.00.00
+mpi2_ra.h    02.00.00  02.00.00  02.00.00  02.00.00  02.00.00  02.00.00
+mpi2_hbd.h   02.00.01  02.00.00  02.00.00  02.00.00
 
 Filename     02.00.11  02.00.10  02.00.09  02.00.08  02.00.07  02.00.06
 ----------   --------  --------  --------  --------  --------  --------
diff -r 52ad4c882614 -r 6b8317170ea4 head/sys/dev/mps/mpi/mpi2_init.h
--- a/head/sys/dev/mps/mpi/mpi2_init.h	Wed Feb 01 11:28:01 2012 +0200
+++ b/head/sys/dev/mps/mpi/mpi2_init.h	Wed Feb 01 11:28:20 2012 +0200
@@ -1,13 +1,42 @@
-/* $FreeBSD$ */
+/*-
+ * Copyright (c) 2011 LSI Corp.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * 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 OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * LSI MPT-Fusion Host Adapter FreeBSD
+ *
+ * $FreeBSD: head/sys/dev/mps/mpi/mpi2_init.h 230592 2012-01-26 18:17:21Z ken $
+ */
+
 /*
- *  Copyright (c) 2000-2009 LSI Corporation.
+ *  Copyright (c) 2000-2011 LSI Corporation.
  *
  *
  *           Name:  mpi2_init.h
  *          Title:  MPI SCSI initiator mode messages and structures
  *  Creation Date:  June 23, 2006
  *
- *    mpi2_init.h Version:  02.00.08
+ *    mpi2_init.h Version:  02.00.11
  *
  *  Version History
  *  ---------------
@@ -32,6 +61,9 @@
  *                      both SCSI IO Error Reply and SCSI Task Management Reply.
  *                      Added ResponseInfo field to MPI2_SCSI_TASK_MANAGE_REPLY.
  *                      Added MPI2_SCSITASKMGMT_RSP_TM_OVERLAPPED_TAG define.
+ *  02-10-10  02.00.09  Removed unused structure that had "#if 0" around it.
+ *  05-12-10  02.00.10  Added optional vendor-unique region to SCSI IO Request.
+ *  11-10-10  02.00.11  Added MPI2_SCSIIO_NUM_SGLOFFSETS define.
  *  --------------------------------------------------------------------------
  */
 
@@ -58,20 +90,6 @@
 } MPI2_SCSI_IO_CDB_EEDP32, MPI2_POINTER PTR_MPI2_SCSI_IO_CDB_EEDP32,
   Mpi2ScsiIoCdbEedp32_t, MPI2_POINTER pMpi2ScsiIoCdbEedp32_t;
 
-/* TBD: I don't think this is needed for MPI2/Gen2 */
-#if 0
-typedef struct
-{
-    U8                      CDB[16];                    /* 0x00 */
-    U32                     DataLength;                 /* 0x10 */
-    U32                     PrimaryReferenceTag;        /* 0x14 */
-    U16                     PrimaryApplicationTag;      /* 0x18 */
-    U16                     PrimaryApplicationTagMask;  /* 0x1A */
-    U32                     TransferLength;             /* 0x1C */
-} MPI2_SCSI_IO32_CDB_EEDP16, MPI2_POINTER PTR_MPI2_SCSI_IO32_CDB_EEDP16,
-  Mpi2ScsiIo32CdbEedp16_t, MPI2_POINTER pMpi2ScsiIo32CdbEedp16_t;
-#endif
-
 typedef union
 {
     U8                      CDB32[32];
@@ -112,7 +130,13 @@
     U8                      LUN[8];                         /* 0x34 */
     U32                     Control;                        /* 0x3C */
     MPI2_SCSI_IO_CDB_UNION  CDB;                            /* 0x40 */
+
+#ifdef MPI2_SCSI_IO_VENDOR_UNIQUE_REGION /* typically this is left undefined */
+    MPI2_SCSI_IO_VENDOR_UNIQUE VendorRegion;
+#endif
+
     MPI2_SGE_IO_UNION       SGL;                            /* 0x60 */
+
 } MPI2_SCSI_IO_REQUEST, MPI2_POINTER PTR_MPI2_SCSI_IO_REQUEST,
   Mpi2SCSIIORequest_t, MPI2_POINTER pMpi2SCSIIORequest_t;
 
@@ -146,6 +170,9 @@
 #define MPI2_SCSIIO_SGLFLAGS_SGL1_SHIFT             (4)
 #define MPI2_SCSIIO_SGLFLAGS_SGL0_SHIFT             (0)
 
+/* number of SGLOffset fields */
+#define MPI2_SCSIIO_NUM_SGLOFFSETS                  (4)
+
 /* SCSI IO IoFlags bits */
 
 /* Large CDB Address Space */
diff -r 52ad4c882614 -r 6b8317170ea4 head/sys/dev/mps/mpi/mpi2_ioc.h
--- a/head/sys/dev/mps/mpi/mpi2_ioc.h	Wed Feb 01 11:28:01 2012 +0200
+++ b/head/sys/dev/mps/mpi/mpi2_ioc.h	Wed Feb 01 11:28:20 2012 +0200
@@ -1,13 +1,42 @@
-/* $FreeBSD$ */
+/*-
+ * Copyright (c) 2011 LSI Corp.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * 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 OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * LSI MPT-Fusion Host Adapter FreeBSD
+ *
+ * $FreeBSD: head/sys/dev/mps/mpi/mpi2_ioc.h 230592 2012-01-26 18:17:21Z ken $
+ */
+
 /*
- *  Copyright (c) 2000-2009 LSI Corporation.
+ *  Copyright (c) 2000-2011 LSI Corporation.
  *
  *
  *           Name:  mpi2_ioc.h
  *          Title:  MPI IOC, Port, Event, FW Download, and FW Upload messages
  *  Creation Date:  October 11, 2006
  *
- *  mpi2_ioc.h Version:  02.00.13
+ *  mpi2_ioc.h Version:  02.00.16
  *
  *  Version History
  *  ---------------
@@ -99,6 +128,12 @@
  *                      (MPI2_FW_HEADER_PID_).
  *                      Modified values for SAS ProductID Family
  *                      (MPI2_FW_HEADER_PID_FAMILY_).
+ *  02-10-10  02.00.14  Added SAS Quiesce Event structure and defines.
+ *                      Added PowerManagementControl Request structures and
+ *                      defines.
+ *  05-12-10  02.00.15  Marked Task Set Full Event as obsolete.
+ *                      Added MPI2_EVENT_SAS_TOPO_LR_UNSUPPORTED_PHY define.
+ *  11-10-10  02.00.16  Added MPI2_FW_DOWNLOAD_ITYPE_MIN_PRODUCT_SPECIFIC.
  *  --------------------------------------------------------------------------
  */
 
@@ -454,7 +489,7 @@
 #define MPI2_EVENT_STATE_CHANGE                     (0x0002)
 #define MPI2_EVENT_HARD_RESET_RECEIVED              (0x0005)
 #define MPI2_EVENT_EVENT_CHANGE                     (0x000A)
-#define MPI2_EVENT_TASK_SET_FULL                    (0x000E)
+#define MPI2_EVENT_TASK_SET_FULL                    (0x000E) /* obsolete */
 #define MPI2_EVENT_SAS_DEVICE_STATUS_CHANGE         (0x000F)
 #define MPI2_EVENT_IR_OPERATION_STATUS              (0x0014)
 #define MPI2_EVENT_SAS_DISCOVERY                    (0x0016)
@@ -470,6 +505,7 @@
 #define MPI2_EVENT_SAS_PHY_COUNTER                  (0x0022)
 #define MPI2_EVENT_GPIO_INTERRUPT                   (0x0023)
 #define MPI2_EVENT_HOST_BASED_DISCOVERY_PHY         (0x0024)
+#define MPI2_EVENT_SAS_QUIESCE                      (0x0025)
 
 
 /* Log Entry Added Event data */
@@ -515,6 +551,7 @@
   MPI2_POINTER pMpi2EventDataHardResetReceived_t;
 
 /* Task Set Full Event data */
+/*   this event is obsolete */
 
 typedef struct _MPI2_EVENT_DATA_TASK_SET_FULL
 {
@@ -829,6 +866,7 @@
 #define MPI2_EVENT_SAS_TOPO_LR_SATA_OOB_COMPLETE            (0x03)
 #define MPI2_EVENT_SAS_TOPO_LR_PORT_SELECTOR                (0x04)
 #define MPI2_EVENT_SAS_TOPO_LR_SMP_RESET_IN_PROGRESS        (0x05)
+#define MPI2_EVENT_SAS_TOPO_LR_UNSUPPORTED_PHY              (0x06)
 #define MPI2_EVENT_SAS_TOPO_LR_RATE_1_5                     (0x08)
 #define MPI2_EVENT_SAS_TOPO_LR_RATE_3_0                     (0x09)
 #define MPI2_EVENT_SAS_TOPO_LR_RATE_6_0                     (0x0A)
@@ -896,6 +934,23 @@
 /* use MPI2_SASPHY3_TFLAGS_ values from mpi2_cnfg.h for the ThresholdFlags field */
 
 
+/* SAS Quiesce Event data */
+
+typedef struct _MPI2_EVENT_DATA_SAS_QUIESCE
+{
+    U8                      ReasonCode;                 /* 0x00 */
+    U8                      Reserved1;                  /* 0x01 */
+    U16                     Reserved2;                  /* 0x02 */
+    U32                     Reserved3;                  /* 0x04 */
+} MPI2_EVENT_DATA_SAS_QUIESCE,
+  MPI2_POINTER PTR_MPI2_EVENT_DATA_SAS_QUIESCE,
+  Mpi2EventDataSasQuiesce_t, MPI2_POINTER pMpi2EventDataSasQuiesce_t;
+
+/* SAS Quiesce Event data ReasonCode values */
+#define MPI2_EVENT_SAS_QUIESCE_RC_STARTED                   (0x01)
+#define MPI2_EVENT_SAS_QUIESCE_RC_COMPLETED                 (0x02)
+
+
 /* Host Based Discovery Phy Event data */
 
 typedef struct _MPI2_EVENT_HBD_PHY_SAS
@@ -1009,7 +1064,9 @@
 #define MPI2_FW_DOWNLOAD_ITYPE_CONFIG_1             (0x07)
 #define MPI2_FW_DOWNLOAD_ITYPE_CONFIG_2             (0x08)
 #define MPI2_FW_DOWNLOAD_ITYPE_MEGARAID             (0x09)
+#define MPI2_FW_DOWNLOAD_ITYPE_COMPLETE             (0x0A)
 #define MPI2_FW_DOWNLOAD_ITYPE_COMMON_BOOT_BLOCK    (0x0B)
+#define MPI2_FW_DOWNLOAD_ITYPE_MIN_PRODUCT_SPECIFIC (0xF0)
 
 /* FWDownload TransactionContext Element */
 typedef struct _MPI2_FW_DOWNLOAD_TCSGE
@@ -1186,7 +1243,6 @@
 
 #define MPI2_FW_HEADER_PID_PROD_MASK                    (0x0F00)
 #define MPI2_FW_HEADER_PID_PROD_A                       (0x0000)
-#define MPI2_FW_HEADER_PID_PROD_MASK                    (0x0F00)
 #define MPI2_FW_HEADER_PID_PROD_TARGET_INITIATOR_SCSI   (0x0200)
 #define MPI2_FW_HEADER_PID_PROD_IR_SCSI                 (0x0700)
 
@@ -1410,5 +1466,101 @@
 #define MPI2_INIT_IMAGE_RESETVECTOR_OFFSET      (0x14)
 
 
+/****************************************************************************
+*  PowerManagementControl message
+****************************************************************************/
+
+/* PowerManagementControl Request message */
+typedef struct _MPI2_PWR_MGMT_CONTROL_REQUEST
+{
+    U8                      Feature;                    /* 0x00 */
+    U8                      Reserved1;                  /* 0x01 */
+    U8                      ChainOffset;                /* 0x02 */
+    U8                      Function;                   /* 0x03 */
+    U16                     Reserved2;                  /* 0x04 */
+    U8                      Reserved3;                  /* 0x06 */
+    U8                      MsgFlags;                   /* 0x07 */
+    U8                      VP_ID;                      /* 0x08 */
+    U8                      VF_ID;                      /* 0x09 */
+    U16                     Reserved4;                  /* 0x0A */
+    U8                      Parameter1;                 /* 0x0C */
+    U8                      Parameter2;                 /* 0x0D */
+    U8                      Parameter3;                 /* 0x0E */
+    U8                      Parameter4;                 /* 0x0F */
+    U32                     Reserved5;                  /* 0x10 */
+    U32                     Reserved6;                  /* 0x14 */
+} MPI2_PWR_MGMT_CONTROL_REQUEST, MPI2_POINTER PTR_MPI2_PWR_MGMT_CONTROL_REQUEST,
+  Mpi2PwrMgmtControlRequest_t, MPI2_POINTER pMpi2PwrMgmtControlRequest_t;
+
+/* defines for the Feature field */
+#define MPI2_PM_CONTROL_FEATURE_DA_PHY_POWER_COND       (0x01)
+#define MPI2_PM_CONTROL_FEATURE_PORT_WIDTH_MODULATION   (0x02)
+#define MPI2_PM_CONTROL_FEATURE_PCIE_LINK               (0x03)
+#define MPI2_PM_CONTROL_FEATURE_IOC_SPEED               (0x04)
+#define MPI2_PM_CONTROL_FEATURE_MIN_PRODUCT_SPECIFIC    (0x80)
+#define MPI2_PM_CONTROL_FEATURE_MAX_PRODUCT_SPECIFIC    (0xFF)
+
+/* parameter usage for the MPI2_PM_CONTROL_FEATURE_DA_PHY_POWER_COND Feature */
+/* Parameter1 contains a PHY number */
+/* Parameter2 indicates power condition action using these defines */
+#define MPI2_PM_CONTROL_PARAM2_PARTIAL                  (0x01)
+#define MPI2_PM_CONTROL_PARAM2_SLUMBER                  (0x02)
+#define MPI2_PM_CONTROL_PARAM2_EXIT_PWR_MGMT            (0x03)
+/* Parameter3 and Parameter4 are reserved */
+
+/* parameter usage for the MPI2_PM_CONTROL_FEATURE_PORT_WIDTH_MODULATION Feature */
+/* Parameter1 contains SAS port width modulation group number */
+/* Parameter2 indicates IOC action using these defines */
+#define MPI2_PM_CONTROL_PARAM2_REQUEST_OWNERSHIP        (0x01)
+#define MPI2_PM_CONTROL_PARAM2_CHANGE_MODULATION        (0x02)
+#define MPI2_PM_CONTROL_PARAM2_RELINQUISH_OWNERSHIP     (0x03)
+/* Parameter3 indicates desired modulation level using these defines */
+#define MPI2_PM_CONTROL_PARAM3_25_PERCENT               (0x00)
+#define MPI2_PM_CONTROL_PARAM3_50_PERCENT               (0x01)
+#define MPI2_PM_CONTROL_PARAM3_75_PERCENT               (0x02)
+#define MPI2_PM_CONTROL_PARAM3_100_PERCENT              (0x03)
+/* Parameter4 is reserved */
+
+/* parameter usage for the MPI2_PM_CONTROL_FEATURE_PCIE_LINK Feature */
+/* Parameter1 indicates desired PCIe link speed using these defines */
+#define MPI2_PM_CONTROL_PARAM1_PCIE_2_5_GBPS            (0x00)
+#define MPI2_PM_CONTROL_PARAM1_PCIE_5_0_GBPS            (0x01)
+#define MPI2_PM_CONTROL_PARAM1_PCIE_8_0_GBPS            (0x02)
+/* Parameter2 indicates desired PCIe link width using these defines */
+#define MPI2_PM_CONTROL_PARAM2_WIDTH_X1                 (0x01)
+#define MPI2_PM_CONTROL_PARAM2_WIDTH_X2                 (0x02)
+#define MPI2_PM_CONTROL_PARAM2_WIDTH_X4                 (0x04)
+#define MPI2_PM_CONTROL_PARAM2_WIDTH_X8                 (0x08)
+/* Parameter3 and Parameter4 are reserved */
+
+/* parameter usage for the MPI2_PM_CONTROL_FEATURE_IOC_SPEED Feature */
+/* Parameter1 indicates desired IOC hardware clock speed using these defines */
+#define MPI2_PM_CONTROL_PARAM1_FULL_IOC_SPEED           (0x01)
+#define MPI2_PM_CONTROL_PARAM1_HALF_IOC_SPEED           (0x02)
+#define MPI2_PM_CONTROL_PARAM1_QUARTER_IOC_SPEED        (0x04)
+#define MPI2_PM_CONTROL_PARAM1_EIGHTH_IOC_SPEED         (0x08)
+/* Parameter2, Parameter3, and Parameter4 are reserved */
+
+
+/* PowerManagementControl Reply message */
+typedef struct _MPI2_PWR_MGMT_CONTROL_REPLY
+{
+    U8                      Feature;                    /* 0x00 */
+    U8                      Reserved1;                  /* 0x01 */
+    U8                      MsgLength;                  /* 0x02 */
+    U8                      Function;                   /* 0x03 */
+    U16                     Reserved2;                  /* 0x04 */
+    U8                      Reserved3;                  /* 0x06 */
+    U8                      MsgFlags;                   /* 0x07 */
+    U8                      VP_ID;                      /* 0x08 */
+    U8                      VF_ID;                      /* 0x09 */
+    U16                     Reserved4;                  /* 0x0A */
+    U16                     Reserved5;                  /* 0x0C */
+    U16                     IOCStatus;                  /* 0x0E */
+    U32                     IOCLogInfo;                 /* 0x10 */
+} MPI2_PWR_MGMT_CONTROL_REPLY, MPI2_POINTER PTR_MPI2_PWR_MGMT_CONTROL_REPLY,
+  Mpi2PwrMgmtControlReply_t, MPI2_POINTER pMpi2PwrMgmtControlReply_t;
+
+
 #endif
 
diff -r 52ad4c882614 -r 6b8317170ea4 head/sys/dev/mps/mpi/mpi2_ra.h
--- a/head/sys/dev/mps/mpi/mpi2_ra.h	Wed Feb 01 11:28:01 2012 +0200
+++ b/head/sys/dev/mps/mpi/mpi2_ra.h	Wed Feb 01 11:28:20 2012 +0200
@@ -1,6 +1,35 @@
-/* $FreeBSD$ */
+/*-
+ * Copyright (c) 2011 LSI Corp.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * 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 OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * LSI MPT-Fusion Host Adapter FreeBSD
+ *
+ * $FreeBSD: head/sys/dev/mps/mpi/mpi2_ra.h 230592 2012-01-26 18:17:21Z ken $
+ */
+
 /*
- *  Copyright (c) 2009 LSI Corporation.
+ *  Copyright (c) 2011 LSI Corporation.
  *
  *
  *           Name:  mpi2_ra.h
diff -r 52ad4c882614 -r 6b8317170ea4 head/sys/dev/mps/mpi/mpi2_raid.h
--- a/head/sys/dev/mps/mpi/mpi2_raid.h	Wed Feb 01 11:28:01 2012 +0200
+++ b/head/sys/dev/mps/mpi/mpi2_raid.h	Wed Feb 01 11:28:20 2012 +0200
@@ -1,13 +1,42 @@
-/* $FreeBSD$ */
+/*-
+ * Copyright (c) 2011 LSI Corp.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * 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 OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * LSI MPT-Fusion Host Adapter FreeBSD
+ *
+ * $FreeBSD: head/sys/dev/mps/mpi/mpi2_raid.h 230592 2012-01-26 18:17:21Z ken $
+ */
+
 /*
- *  Copyright (c) 2000-2008 LSI Corporation.
+ *  Copyright (c) 2000-2011 LSI Corporation.
  *
  *
  *           Name:  mpi2_raid.h
  *          Title:  MPI Integrated RAID messages and structures
  *  Creation Date:  April 26, 2007
  *
- *    mpi2_raid.h Version:  02.00.04
+ *    mpi2_raid.h Version:  02.00.05
  *
  *  Version History
  *  ---------------
@@ -23,6 +52,7 @@
  *                      can be sized by the build environment.
  *  07-30-09  02.00.04  Added proper define for the Use Default Settings bit of
  *                      VolumeCreationFlags and marked the old one as obsolete.
+ *  05-12-10  02.00.05  Added MPI2_RAID_VOL_FLAGS_OP_MDC define.
  *  --------------------------------------------------------------------------
  */
 
@@ -261,6 +291,7 @@
 #define MPI2_RAID_VOL_FLAGS_OP_ONLINE_CAP_EXPANSION (0x00000001)
 #define MPI2_RAID_VOL_FLAGS_OP_CONSISTENCY_CHECK    (0x00000002)
 #define MPI2_RAID_VOL_FLAGS_OP_RESYNC               (0x00000003)
+#define MPI2_RAID_VOL_FLAGS_OP_MDC                  (0x00000004)
 
 
 /* RAID Action Reply ActionData union */
diff -r 52ad4c882614 -r 6b8317170ea4 head/sys/dev/mps/mpi/mpi2_sas.h
--- a/head/sys/dev/mps/mpi/mpi2_sas.h	Wed Feb 01 11:28:01 2012 +0200
+++ b/head/sys/dev/mps/mpi/mpi2_sas.h	Wed Feb 01 11:28:20 2012 +0200
@@ -1,13 +1,42 @@
-/* $FreeBSD$ */
+/*-
+ * Copyright (c) 2011 LSI Corp.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * 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 OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * LSI MPT-Fusion Host Adapter FreeBSD
+ *
+ * $FreeBSD: head/sys/dev/mps/mpi/mpi2_sas.h 230592 2012-01-26 18:17:21Z ken $
+ */
+
 /*
- *  Copyright (c) 2000-2007 LSI Corporation.
+ *  Copyright (c) 2000-2011 LSI Corporation.
  *
  *
  *           Name:  mpi2_sas.h
  *          Title:  MPI Serial Attached SCSI structures and definitions
  *  Creation Date:  February 9, 2007
  *
- *  mpi2.h Version:  02.00.03
+ *  mpi2_sas.h Version:  02.00.05
  *
  *  Version History
  *  ---------------
@@ -21,6 +50,8 @@
  *                      Request.
  *  10-28-09  02.00.03  Changed the type of SGL in MPI2_SATA_PASSTHROUGH_REQUEST
  *                      to MPI2_SGE_IO_UNION since it supports chained SGLs.
+ *  05-12-10  02.00.04  Modified some comments.
+ *  08-11-10  02.00.05  Added NCQ operations to SAS IO Unit Control.
  *  --------------------------------------------------------------------------
  */
 
@@ -111,7 +142,7 @@
 /* values for PassthroughFlags field */
 #define MPI2_SMP_PT_REQ_PT_FLAGS_IMMEDIATE      (0x80)
 
-/* values for SGLFlags field are in the SGL section of mpi2.h */
+/* use MPI2_SGLFLAGS_ defines from mpi2.h for the SGLFlags field */
 
 
 /* SMP Passthrough Reply Message */
@@ -163,7 +194,7 @@
     U32                     Reserved4;          /* 0x14 */
     U32                     DataLength;         /* 0x18 */
     U8                      CommandFIS[20];     /* 0x1C */
-    MPI2_SGE_IO_UNION       SGL;                /* 0x20 */
+    MPI2_SGE_IO_UNION       SGL;                /* 0x30 */
 } MPI2_SATA_PASSTHROUGH_REQUEST, MPI2_POINTER PTR_MPI2_SATA_PASSTHROUGH_REQUEST,
   Mpi2SataPassthroughRequest_t, MPI2_POINTER pMpi2SataPassthroughRequest_t;
 
@@ -175,7 +206,7 @@
 #define MPI2_SATA_PT_REQ_PT_FLAGS_WRITE             (0x0002)
 #define MPI2_SATA_PT_REQ_PT_FLAGS_READ              (0x0001)
 
-/* values for SGLFlags field are in the SGL section of mpi2.h */
+/* use MPI2_SGLFLAGS_ defines from mpi2.h for the SGLFlags field */
 
 
 /* SATA Passthrough Reply Message */
@@ -246,6 +277,8 @@
 #define MPI2_SAS_OP_REMOVE_DEVICE               (0x0D)
 #define MPI2_SAS_OP_LOOKUP_MAPPING              (0x0E)
 #define MPI2_SAS_OP_SET_IOC_PARAMETER           (0x0F)
+#define MPI2_SAS_OP_DEV_ENABLE_NCQ              (0x14)
+#define MPI2_SAS_OP_DEV_DISABLE_NCQ             (0x15)
 #define MPI2_SAS_OP_PRODUCT_SPECIFIC_MIN        (0x80)
 
 /* values for the PrimFlags field */
diff -r 52ad4c882614 -r 6b8317170ea4 head/sys/dev/mps/mpi/mpi2_targ.h
--- a/head/sys/dev/mps/mpi/mpi2_targ.h	Wed Feb 01 11:28:01 2012 +0200
+++ b/head/sys/dev/mps/mpi/mpi2_targ.h	Wed Feb 01 11:28:20 2012 +0200
@@ -1,13 +1,42 @@
-/* $FreeBSD$ */
+/*-
+ * Copyright (c) 2011 LSI Corp.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * 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 OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * LSI MPT-Fusion Host Adapter FreeBSD
+ *
+ * $FreeBSD: head/sys/dev/mps/mpi/mpi2_targ.h 230592 2012-01-26 18:17:21Z ken $
+ */
+
 /*
- *  Copyright (c) 2000-2008 LSI Corporation.
+ *  Copyright (c) 2000-2011 LSI Corporation.
  *
  *
  *           Name:  mpi2_targ.h
  *          Title:  MPI Target mode messages and structures
  *  Creation Date:  September 8, 2006
  *
- *    mpi2_targ.h Version: 02.00.03
+ *    mpi2_targ.h Version: 02.00.04
  *
  *  Version History
  *  ---------------
@@ -22,6 +51,7 @@
  *                      MPI2_TARGET_CMD_BUF_POST_BASE_REQUEST.
  *                      Target Status Send Request only takes a single SGE for
  *                      response data.
+ *  02-10-10  02.00.04  Added comment to MPI2_TARGET_SSP_RSP_IU structure.
  *  --------------------------------------------------------------------------
  */
 
@@ -343,6 +373,7 @@
 typedef struct _MPI2_TARGET_SSP_RSP_IU
 {
     U32     Reserved0[6]; /* reserved for SSP header */ /* 0x00 */
+
     /* start of RESPONSE information unit */
     U32     Reserved1;                                  /* 0x18 */
     U32     Reserved2;                                  /* 0x1C */
@@ -352,6 +383,8 @@
     U32     Reserved4;                                  /* 0x24 */
     U32     SenseDataLength;                            /* 0x28 */
     U32     ResponseDataLength;                         /* 0x2C */
+
+    /* start of Response or Sense Data (size may vary dynamically) */
     U8      ResponseSenseData[4];                       /* 0x30 */
 } MPI2_TARGET_SSP_RSP_IU, MPI2_POINTER PTR_MPI2_TARGET_SSP_RSP_IU,
   Mpi2TargetSspRspIu_t, MPI2_POINTER pMpi2TargetSspRspIu_t;
diff -r 52ad4c882614 -r 6b8317170ea4 head/sys/dev/mps/mpi/mpi2_tool.h
--- a/head/sys/dev/mps/mpi/mpi2_tool.h	Wed Feb 01 11:28:01 2012 +0200
+++ b/head/sys/dev/mps/mpi/mpi2_tool.h	Wed Feb 01 11:28:20 2012 +0200
@@ -1,13 +1,42 @@
-/* $FreeBSD$ */
+/*-
+ * Copyright (c) 2011 LSI Corp.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * 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 OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * LSI MPT-Fusion Host Adapter FreeBSD
+ *
+ * $FreeBSD: head/sys/dev/mps/mpi/mpi2_tool.h 230592 2012-01-26 18:17:21Z ken $
+ */
+
 /*
- *  Copyright (c) 2000-2009 LSI Corporation.
+ *  Copyright (c) 2000-2011 LSI Corporation.
  *
  *
  *           Name:  mpi2_tool.h
  *          Title:  MPI diagnostic tool structures and definitions
  *  Creation Date:  March 26, 2007
  *
- *    mpi2_tool.h Version:  02.00.04
+ *    mpi2_tool.h Version:  02.00.06
  *
  *  Version History
  *  ---------------
@@ -23,6 +52,9 @@
  *                      and reply messages.
  *                      Added MPI2_DIAG_BUF_TYPE_EXTENDED.
  *                      Incremented MPI2_DIAG_BUF_TYPE_COUNT.
+ *  05-12-10  02.00.05  Added Diagnostic Data Upload tool.
+ *  08-11-10  02.00.06  Added defines that were missing for Diagnostic Buffer
+ *                      Post Request.
  *  --------------------------------------------------------------------------
  */
 
@@ -38,6 +70,7 @@
 /* defines for the Tools */
 #define MPI2_TOOLBOX_CLEAN_TOOL                     (0x00)
 #define MPI2_TOOLBOX_MEMORY_MOVE_TOOL               (0x01)
+#define MPI2_TOOLBOX_DIAG_DATA_UPLOAD_TOOL          (0x02)
 #define MPI2_TOOLBOX_ISTWI_READ_WRITE_TOOL          (0x03)
 #define MPI2_TOOLBOX_BEACON_TOOL                    (0x05)
 #define MPI2_TOOLBOX_DIAGNOSTIC_CLI_TOOL            (0x06)
@@ -121,6 +154,46 @@
 
 
 /****************************************************************************
+*  Toolbox Diagnostic Data Upload request
+****************************************************************************/
+
+typedef struct _MPI2_TOOLBOX_DIAG_DATA_UPLOAD_REQUEST
+{
+    U8                      Tool;                       /* 0x00 */
+    U8                      Reserved1;                  /* 0x01 */
+    U8                      ChainOffset;                /* 0x02 */
+    U8                      Function;                   /* 0x03 */
+    U16                     Reserved2;                  /* 0x04 */
+    U8                      Reserved3;                  /* 0x06 */
+    U8                      MsgFlags;                   /* 0x07 */
+    U8                      VP_ID;                      /* 0x08 */
+    U8                      VF_ID;                      /* 0x09 */
+    U16                     Reserved4;                  /* 0x0A */
+    U8                      SGLFlags;                   /* 0x0C */
+    U8                      Reserved5;                  /* 0x0D */
+    U16                     Reserved6;                  /* 0x0E */
+    U32                     Flags;                      /* 0x10 */
+    U32                     DataLength;                 /* 0x14 */
+    MPI2_SGE_SIMPLE_UNION   SGL;                        /* 0x18 */
+} MPI2_TOOLBOX_DIAG_DATA_UPLOAD_REQUEST,
+  MPI2_POINTER PTR_MPI2_TOOLBOX_DIAG_DATA_UPLOAD_REQUEST,
+  Mpi2ToolboxDiagDataUploadRequest_t,
+  MPI2_POINTER pMpi2ToolboxDiagDataUploadRequest_t;
+
+/* use MPI2_SGLFLAGS_ defines from mpi2.h for the SGLFlags field */
+
+
+typedef struct _MPI2_DIAG_DATA_UPLOAD_HEADER
+{
+    U32                     DiagDataLength;             /* 00h */
+    U8                      FormatCode;                 /* 04h */
+    U8                      Reserved1;                  /* 05h */
+    U16                     Reserved2;                  /* 06h */
+} MPI2_DIAG_DATA_UPLOAD_HEADER, MPI2_POINTER PTR_MPI2_DIAG_DATA_UPLOAD_HEADER,
+  Mpi2DiagDataUploadHeader_t, MPI2_POINTER pMpi2DiagDataUploadHeader_t;
+
+
+/****************************************************************************
 *  Toolbox ISTWI Read Write Tool
 ****************************************************************************/
 
@@ -164,7 +237,7 @@
 #define MPI2_TOOL_ISTWI_ACTION_RELEASE_BUS          (0x11)
 #define MPI2_TOOL_ISTWI_ACTION_RESET                (0x12)
 
-/* values for SGLFlags field are in the SGL section of mpi2.h */
+/* use MPI2_SGLFLAGS_ defines from mpi2.h for the SGLFlags field */
 
 
 /* Toolbox ISTWI Read Write Tool reply message */
@@ -251,7 +324,7 @@
   Mpi2ToolboxDiagnosticCliRequest_t,
   MPI2_POINTER pMpi2ToolboxDiagnosticCliRequest_t;
 
-/* values for SGLFlags field are in the SGL section of mpi2.h */
+/* use MPI2_SGLFLAGS_ defines from mpi2.h for the SGLFlags field */
 
 
 /* Toolbox Diagnostic CLI Tool reply message */
@@ -319,6 +392,10 @@
 /* count of the number of buffer types */
 #define MPI2_DIAG_BUF_TYPE_COUNT                    (0x03)
 
+/* values for the Flags field */
+#define MPI2_DIAG_BUF_FLAG_RELEASE_ON_FULL          (0x00000002)
+#define MPI2_DIAG_BUF_FLAG_IMMEDIATE_RELEASE        (0x00000001)
+
 
 /****************************************************************************
 *  Diagnostic Buffer Post reply
diff -r 52ad4c882614 -r 6b8317170ea4 head/sys/dev/mps/mpi/mpi2_type.h
--- a/head/sys/dev/mps/mpi/mpi2_type.h	Wed Feb 01 11:28:01 2012 +0200
+++ b/head/sys/dev/mps/mpi/mpi2_type.h	Wed Feb 01 11:28:20 2012 +0200
@@ -1,6 +1,35 @@
-/* $FreeBSD$ */
+/*-
+ * Copyright (c) 2011 LSI Corp.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * 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 OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * LSI MPT-Fusion Host Adapter FreeBSD
+ *
+ * $FreeBSD: head/sys/dev/mps/mpi/mpi2_type.h 230592 2012-01-26 18:17:21Z ken $
+ */
+
 /*
- *  Copyright (c) 2000-2007 LSI Corporation.
+ *  Copyright (c) 2000-2011 LSI Corporation.
  *
  *
  *           Name:  mpi2_type.h
diff -r 52ad4c882614 -r 6b8317170ea4 head/sys/dev/mps/mps.c
--- a/head/sys/dev/mps/mps.c	Wed Feb 01 11:28:01 2012 +0200
+++ b/head/sys/dev/mps/mps.c	Wed Feb 01 11:28:20 2012 +0200
@@ -22,13 +22,44 @@
  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  * SUCH DAMAGE.
+ *
+ */
+/*-
+ * Copyright (c) 2011 LSI Corp.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * 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 OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * LSI MPT-Fusion Host Adapter FreeBSD
+ *
+ * $FreeBSD: head/sys/dev/mps/mps.c 230592 2012-01-26 18:17:21Z ken $
  */
 
 #include <sys/cdefs.h>
-__FBSDID("$FreeBSD$");
+__FBSDID("$FreeBSD: head/sys/dev/mps/mps.c 230592 2012-01-26 18:17:21Z ken $");
 
 /* Communications core for LSI MPT2 */
 
+/* TODO Move headers to mpsvar */
 #include <sys/types.h>
 #include <sys/param.h>
 #include <sys/systm.h>
@@ -43,29 +74,44 @@
 #include <sys/malloc.h>
 #include <sys/uio.h>
 #include <sys/sysctl.h>
+#include <sys/queue.h>
+#include <sys/kthread.h>
 #include <sys/endian.h>
+#include <sys/eventhandler.h>
 
 #include <machine/bus.h>
 #include <machine/resource.h>
 #include <sys/rman.h>
 
+#include <dev/pci/pcivar.h>
+
 #include <cam/scsi/scsi_all.h>
 
 #include <dev/mps/mpi/mpi2_type.h>
 #include <dev/mps/mpi/mpi2.h>
 #include <dev/mps/mpi/mpi2_ioc.h>
+#include <dev/mps/mpi/mpi2_sas.h>
 #include <dev/mps/mpi/mpi2_cnfg.h>
+#include <dev/mps/mpi/mpi2_init.h>
+#include <dev/mps/mpi/mpi2_tool.h>
+#include <dev/mps/mps_ioctl.h>
 #include <dev/mps/mpsvar.h>
 #include <dev/mps/mps_table.h>
 
+static int mps_diag_reset(struct mps_softc *sc);
+static int mps_init_queues(struct mps_softc *sc);
+static int mps_message_unit_reset(struct mps_softc *sc);
+static int mps_transition_operational(struct mps_softc *sc);
 static void mps_startup(void *arg);
-static void mps_startup_complete(struct mps_softc *sc, struct mps_command *cm);
 static int mps_send_iocinit(struct mps_softc *sc);
 static int mps_attach_log(struct mps_softc *sc);
 static __inline void mps_complete_command(struct mps_command *cm);
-static void mps_dispatch_event(struct mps_softc *sc, uintptr_t data, MPI2_EVENT_NOTIFICATION_REPLY *reply);
+static void mps_dispatch_event(struct mps_softc *sc, uintptr_t data,
+    MPI2_EVENT_NOTIFICATION_REPLY *reply);
 static void mps_config_complete(struct mps_softc *sc, struct mps_command *cm);
 static void mps_periodic(void *);
+static int mps_reregister_events(struct mps_softc *sc);
+static void mps_enqueue_request(struct mps_softc *sc, struct mps_command *cm);
 
 SYSCTL_NODE(_hw, OID_AUTO, mps, CTLFLAG_RD, 0, "MPS Driver Parameters");
 
@@ -78,7 +124,7 @@
 static char mpt2_reset_magic[] = { 0x00, 0x0f, 0x04, 0x0b, 0x02, 0x07, 0x0d };
 
 static int
-mps_hard_reset(struct mps_softc *sc)
+mps_diag_reset(struct mps_softc *sc)
 {
 	uint32_t reg;
 	int i, error, tries = 0;
@@ -129,7 +175,7 @@
 }
 
 static int
-mps_soft_reset(struct mps_softc *sc)
+mps_message_unit_reset(struct mps_softc *sc)
 {
 
 	mps_dprint(sc, MPS_TRACE, "%s\n", __func__);
@@ -160,7 +206,7 @@
 		 * resetting it.
 		 */
 		if (reg & MPI2_DOORBELL_USED) {
-			mps_hard_reset(sc);
+			mps_diag_reset(sc);
 			DELAY(50000);
 			continue;
 		}
@@ -181,10 +227,10 @@
 		} else if (state == MPI2_IOC_STATE_FAULT) {
 			mps_dprint(sc, MPS_INFO, "IOC in fault state 0x%x\n",
 			    state & MPI2_DOORBELL_FAULT_CODE_MASK);
-			mps_hard_reset(sc);
+			mps_diag_reset(sc);
 		} else if (state == MPI2_IOC_STATE_OPERATIONAL) {
 			/* Need to take ownership */
-			mps_soft_reset(sc);
+			mps_message_unit_reset(sc);
 		} else if (state == MPI2_IOC_STATE_RESET) {
 			/* Wait a bit, IOC might be in transition */
 			mps_dprint(sc, MPS_FAULT,
@@ -220,14 +266,108 @@
 
 	state = reg & MPI2_IOC_STATE_MASK;
 	if (state != MPI2_IOC_STATE_READY) {
-		if ((error = mps_transition_ready(sc)) != 0)
+		if ((error = mps_transition_ready(sc)) != 0) {
+			mps_dprint(sc, MPS_FAULT, 
+			    "%s failed to transition ready\n", __func__);
 			return (error);
+		}
 	}
 
 	error = mps_send_iocinit(sc);
 	return (error);
 }
 
+/* 
+ * XXX Some of this should probably move to mps.c
+ *
+ * The terms diag reset and hard reset are used interchangeably in the MPI
+ * docs to mean resetting the controller chip.  In this code diag reset
+ * cleans everything up, and the hard reset function just sends the reset
+ * sequence to the chip.  This should probably be refactored so that every
+ * subsystem gets a reset notification of some sort, and can clean up
+ * appropriately.
+ */
+int
+mps_reinit(struct mps_softc *sc)
+{
+	int error;
+	uint32_t db;
+
+	mps_printf(sc, "%s sc %p\n", __func__, sc);
+
+	mtx_assert(&sc->mps_mtx, MA_OWNED);
+
+	if (sc->mps_flags & MPS_FLAGS_DIAGRESET) {
+		mps_printf(sc, "%s reset already in progress\n", __func__);
+		return 0;
+	}
+
+	/* make sure the completion callbacks can recognize they're getting
+	 * a NULL cm_reply due to a reset.
+	 */
+	sc->mps_flags |= MPS_FLAGS_DIAGRESET;
+
+	mps_printf(sc, "%s mask interrupts\n", __func__);
+	mps_mask_intr(sc);
+
+	error = mps_diag_reset(sc);
+	if (error != 0) {
+		panic("%s hard reset failed with error %d\n",
+		    __func__, error);
+	}
+
+	/* Restore the PCI state, including the MSI-X registers */
+	mps_pci_restore(sc);
+
+	/* Give the I/O subsystem special priority to get itself prepared */
+	mpssas_handle_reinit(sc);
+
+	/* reinitialize queues after the reset */
+	bzero(sc->free_queue, sc->fqdepth * 4);
+	mps_init_queues(sc);
+
+	/* get the chip out of the reset state */
+	error = mps_transition_operational(sc);
+	if (error != 0)
+		panic("%s transition operational failed with error %d\n",
+		    __func__, error);
+
+	/* Reinitialize the reply queue. This is delicate because this
+	 * function is typically invoked by task mgmt completion callbacks,
+	 * which are called by the interrupt thread.  We need to make sure
+	 * the interrupt handler loop will exit when we return to it, and
+	 * that it will recognize the indexes we've changed.
+	 */
+	sc->replypostindex = 0;
+	mps_regwrite(sc, MPI2_REPLY_FREE_HOST_INDEX_OFFSET, sc->replyfreeindex);
+	mps_regwrite(sc, MPI2_REPLY_POST_HOST_INDEX_OFFSET, sc->replypostindex);
+
+	db = mps_regread(sc, MPI2_DOORBELL_OFFSET);
+	mps_printf(sc, "%s doorbell 0x%08x\n", __func__, db);
+
+	mps_printf(sc, "%s unmask interrupts post %u free %u\n", __func__,
+	    sc->replypostindex, sc->replyfreeindex);
+
+	mps_unmask_intr(sc);
+
+	mps_printf(sc, "%s restarting post %u free %u\n", __func__,
+	    sc->replypostindex, sc->replyfreeindex);
+
+	/* restart will reload the event masks clobbered by the reset, and
+	 * then enable the port.
+	 */
+	mps_reregister_events(sc);
+
+	/* the end of discovery will release the simq, so we're done. */
+	mps_printf(sc, "%s finished sc %p post %u free %u\n", 
+	    __func__, sc, 
+	    sc->replypostindex, sc->replyfreeindex);
+
+	sc->mps_flags &= ~MPS_FLAGS_DIAGRESET;
+
+	return 0;
+}
+
 /* Wait for the chip to ACK a word that we've put into its FIFO */
 static int
 mps_wait_db_ack(struct mps_softc *sc)
@@ -382,51 +522,25 @@
 	return (0);
 }
 
-void
+static void
 mps_enqueue_request(struct mps_softc *sc, struct mps_command *cm)
 {
 
-	mps_dprint(sc, MPS_TRACE, "%s\n", __func__);
+	mps_dprint(sc, MPS_TRACE, "%s SMID %u cm %p ccb %p\n", __func__,
+	    cm->cm_desc.Default.SMID, cm, cm->cm_ccb);
 
 	if (sc->mps_flags & MPS_FLAGS_ATTACH_DONE)
 		mtx_assert(&sc->mps_mtx, MA_OWNED);
 
-	if ((cm->cm_desc.Default.SMID < 1)
-	 || (cm->cm_desc.Default.SMID >= sc->num_reqs)) {
-		mps_printf(sc, "%s: invalid SMID %d, desc %#x %#x\n",
-			   __func__, cm->cm_desc.Default.SMID,
-			   cm->cm_desc.Words.High, cm->cm_desc.Words.Low);
-	}
+	if (++sc->io_cmds_active > sc->io_cmds_highwater)
+		sc->io_cmds_highwater++;
+
 	mps_regwrite(sc, MPI2_REQUEST_DESCRIPTOR_POST_LOW_OFFSET,
 	    cm->cm_desc.Words.Low);
 	mps_regwrite(sc, MPI2_REQUEST_DESCRIPTOR_POST_HIGH_OFFSET,
 	    cm->cm_desc.Words.High);
 }
 
-int
-mps_request_polled(struct mps_softc *sc, struct mps_command *cm)
-{
-	int error, timeout = 0;
-
-	error = 0;
-
-	cm->cm_flags |= MPS_CM_FLAGS_POLLED;
-	cm->cm_complete = NULL;
-	mps_map_command(sc, cm);
-
-	while ((cm->cm_flags & MPS_CM_FLAGS_COMPLETE) == 0) {
-		mps_intr(sc);
-		DELAY(50 * 1000);
-		if (timeout++ > 1000) {
-			mps_dprint(sc, MPS_FAULT, "polling failed\n");
-			error = ETIMEDOUT;
-			break;
-		}
-	}
-
-	return (error);
-}
-
 /*
  * Just the FACTS, ma'am.
  */
@@ -469,9 +583,19 @@
 	cm->cm_data = NULL;
 	error = mps_request_polled(sc, cm);
 	reply = (MPI2_PORT_FACTS_REPLY *)cm->cm_reply;
-	if ((reply->IOCStatus & MPI2_IOCSTATUS_MASK) != MPI2_IOCSTATUS_SUCCESS)
+	if (reply == NULL) {
+		mps_printf(sc, "%s NULL reply\n", __func__);
+		goto done;
+	}
+	if ((reply->IOCStatus & MPI2_IOCSTATUS_MASK) != MPI2_IOCSTATUS_SUCCESS) {
+		mps_printf(sc, 
+		    "%s error %d iocstatus 0x%x iocloginfo 0x%x type 0x%x\n",
+		    __func__, error, reply->IOCStatus, reply->IOCLogInfo, 
+		    reply->PortType);
 		error = ENXIO;
+	}
 	bcopy(reply, facts, sizeof(MPI2_PORT_FACTS_REPLY));
+done:
 	mps_free_command(sc, cm);
 
 	return (error);
@@ -522,35 +646,6 @@
 	return (error);
 }
 
-static int
-mps_send_portenable(struct mps_softc *sc)
-{
-	MPI2_PORT_ENABLE_REQUEST *request;
-	struct mps_command *cm;
-
-	mps_dprint(sc, MPS_TRACE, "%s\n", __func__);
-
-	if ((cm = mps_alloc_command(sc)) == NULL)
-		return (EBUSY);
-	request = (MPI2_PORT_ENABLE_REQUEST *)cm->cm_req;
-	request->Function = MPI2_FUNCTION_PORT_ENABLE;
-	request->MsgFlags = 0;
-	request->VP_ID = 0;
-	cm->cm_desc.Default.RequestFlags = MPI2_REQ_DESCRIPT_FLAGS_DEFAULT_TYPE;
-	cm->cm_complete = mps_startup_complete;
-
-	mps_enqueue_request(sc, cm);
-	return (0);
-}
-
-static int
-mps_send_mur(struct mps_softc *sc)
-{
-
-	/* Placeholder */
-	return (0);
-}
-
 void
 mps_memaddr_cb(void *arg, bus_dma_segment_t *segs, int nsegs, int error)
 {
@@ -685,7 +780,7 @@
         bus_dmamap_load(sc->req_dmat, sc->req_map, sc->req_frames, rsize,
 	    mps_memaddr_cb, &sc->req_busaddr, 0);
 
-	rsize = sc->facts->IOCRequestFrameSize * MPS_CHAIN_FRAMES * 4;
+	rsize = sc->facts->IOCRequestFrameSize * sc->max_chains * 4;
         if (bus_dma_tag_create( sc->mps_parent_dmat,    /* parent */
 				16, 0,			/* algnmnt, boundary */
 				BUS_SPACE_MAXADDR_32BIT,/* lowaddr */
@@ -733,9 +828,9 @@
         bus_dmamap_load(sc->sense_dmat, sc->sense_map, sc->sense_frames, rsize,
 	    mps_memaddr_cb, &sc->sense_busaddr, 0);
 
-	sc->chains = malloc(sizeof(struct mps_chain) * MPS_CHAIN_FRAMES,
-	    M_MPT2, M_WAITOK | M_ZERO);
-	for (i = 0; i < MPS_CHAIN_FRAMES; i++) {
+	sc->chains = malloc(sizeof(struct mps_chain) * sc->max_chains, M_MPT2,
+	    M_WAITOK | M_ZERO);
+	for (i = 0; i < sc->max_chains; i++) {
 		chain = &sc->chains[i];
 		chain->chain = (MPI2_SGE_IO_UNION *)(sc->chain_frames +
 		    i * sc->facts->IOCRequestFrameSize * 4);
@@ -759,7 +854,7 @@
                                 busdma_lock_mutex,	/* lockfunc */
 				&sc->mps_mtx,		/* lockarg */
                                 &sc->buffer_dmat)) {
-		device_printf(sc->mps_dev, "Cannot allocate sense DMA tag\n");
+		device_printf(sc->mps_dev, "Cannot allocate buffer DMA tag\n");
 		return (ENOMEM);
         }
 
@@ -780,12 +875,16 @@
 		cm->cm_desc.Default.SMID = i;
 		cm->cm_sc = sc;
 		TAILQ_INIT(&cm->cm_chain_list);
-		callout_init(&cm->cm_callout, 1 /*MPSAFE*/);
+		callout_init_mtx(&cm->cm_callout, &sc->mps_mtx, 0);
 
 		/* XXX Is a failure here a critical problem? */
 		if (bus_dmamap_create(sc->buffer_dmat, 0, &cm->cm_dmamap) == 0)
-			mps_free_command(sc, cm);
+			if (i <= sc->facts->HighPriorityCredit)
+				mps_free_high_priority_command(sc, cm);
+			else
+				mps_free_command(sc, cm);
 		else {
+			panic("failed to allocate command %d\n", i);
 			sc->num_reqs = i;
 			break;
 		}
@@ -819,28 +918,53 @@
 	return (0);
 }
 
-int
-mps_attach(struct mps_softc *sc)
+/* Get the driver parameter tunables.  Lowest priority are the driver defaults.
+ * Next are the global settings, if they exist.  Highest are the per-unit
+ * settings, if they exist.
+ */
+static void
+mps_get_tunables(struct mps_softc *sc)
 {
-	int i, error;
-	char tmpstr[80], tmpstr2[80];
+	char tmpstr[80];
+
+	/* XXX default to some debugging for now */
+	sc->mps_debug = MPS_FAULT;
+	sc->disable_msix = 0;
+	sc->disable_msi = 0;
+	sc->max_chains = MPS_CHAIN_FRAMES;
 
 	/*
-	 * Grab any tunable-set debug level so that tracing works as early
-	 * as possible.
+	 * Grab the global variables.
 	 */
-	snprintf(tmpstr, sizeof(tmpstr), "hw.mps.%d.debug_level",
+	TUNABLE_INT_FETCH("hw.mps.debug_level", &sc->mps_debug);
+	TUNABLE_INT_FETCH("hw.mps.disable_msix", &sc->disable_msix);
+	TUNABLE_INT_FETCH("hw.mps.disable_msi", &sc->disable_msi);
+	TUNABLE_INT_FETCH("hw.mps.max_chains", &sc->max_chains);
+
+	/* Grab the unit-instance variables */
+	snprintf(tmpstr, sizeof(tmpstr), "dev.mps.%d.debug_level",
 	    device_get_unit(sc->mps_dev));
 	TUNABLE_INT_FETCH(tmpstr, &sc->mps_debug);
-	snprintf(tmpstr, sizeof(tmpstr), "hw.mps.%d.allow_multiple_tm_cmds",
+
+	snprintf(tmpstr, sizeof(tmpstr), "dev.mps.%d.disable_msix",
 	    device_get_unit(sc->mps_dev));
-	TUNABLE_INT_FETCH(tmpstr, &sc->allow_multiple_tm_cmds);
+	TUNABLE_INT_FETCH(tmpstr, &sc->disable_msix);
 
-	mps_dprint(sc, MPS_TRACE, "%s\n", __func__);
+	snprintf(tmpstr, sizeof(tmpstr), "dev.mps.%d.disable_msi",
+	    device_get_unit(sc->mps_dev));
+	TUNABLE_INT_FETCH(tmpstr, &sc->disable_msi);
 
-	mtx_init(&sc->mps_mtx, "MPT2SAS lock", NULL, MTX_DEF);
-	callout_init_mtx(&sc->periodic, &sc->mps_mtx, 0);
-	TAILQ_INIT(&sc->event_list);
+	snprintf(tmpstr, sizeof(tmpstr), "dev.mps.%d.max_chains",
+	    device_get_unit(sc->mps_dev));
+	TUNABLE_INT_FETCH(tmpstr, &sc->max_chains);
+}
+
+static void
+mps_setup_sysctl(struct mps_softc *sc)
+{
+	struct sysctl_ctx_list	*sysctl_ctx = NULL;
+	struct sysctl_oid	*sysctl_tree = NULL;
+	char tmpstr[80], tmpstr2[80];
 
 	/*
 	 * Setup the sysctl variable so the user can change the debug level
@@ -850,44 +974,85 @@
 	    device_get_unit(sc->mps_dev));
 	snprintf(tmpstr2, sizeof(tmpstr2), "%d", device_get_unit(sc->mps_dev));
 
-	sysctl_ctx_init(&sc->sysctl_ctx);
-	sc->sysctl_tree = SYSCTL_ADD_NODE(&sc->sysctl_ctx,
-	    SYSCTL_STATIC_CHILDREN(_hw_mps), OID_AUTO, tmpstr2, CTLFLAG_RD,
-	    0, tmpstr);
-	if (sc->sysctl_tree == NULL)
-		return (ENOMEM);
+	sysctl_ctx = device_get_sysctl_ctx(sc->mps_dev);
+	if (sysctl_ctx != NULL)
+		sysctl_tree = device_get_sysctl_tree(sc->mps_dev);
 
-	SYSCTL_ADD_UINT(&sc->sysctl_ctx, SYSCTL_CHILDREN(sc->sysctl_tree),
+	if (sysctl_tree == NULL) {
+		sysctl_ctx_init(&sc->sysctl_ctx);
+		sc->sysctl_tree = SYSCTL_ADD_NODE(&sc->sysctl_ctx,
+		    SYSCTL_STATIC_CHILDREN(_hw_mps), OID_AUTO, tmpstr2,
+		    CTLFLAG_RD, 0, tmpstr);
+		if (sc->sysctl_tree == NULL)
+			return;
+		sysctl_ctx = &sc->sysctl_ctx;
+		sysctl_tree = sc->sysctl_tree;
+	}
+
+	SYSCTL_ADD_INT(sysctl_ctx, SYSCTL_CHILDREN(sysctl_tree),
 	    OID_AUTO, "debug_level", CTLFLAG_RW, &sc->mps_debug, 0,
 	    "mps debug level");
 
-	SYSCTL_ADD_UINT(&sc->sysctl_ctx, SYSCTL_CHILDREN(sc->sysctl_tree),
-	    OID_AUTO, "allow_multiple_tm_cmds", CTLFLAG_RW,
-	    &sc->allow_multiple_tm_cmds, 0,
-	    "allow multiple simultaneous task management cmds");
+	SYSCTL_ADD_INT(sysctl_ctx, SYSCTL_CHILDREN(sysctl_tree),
+	    OID_AUTO, "disable_msix", CTLFLAG_RD, &sc->disable_msix, 0,
+	    "Disable the use of MSI-X interrupts");
 
-	SYSCTL_ADD_INT(&sc->sysctl_ctx, SYSCTL_CHILDREN(sc->sysctl_tree),
+	SYSCTL_ADD_INT(sysctl_ctx, SYSCTL_CHILDREN(sysctl_tree),
+	    OID_AUTO, "disable_msi", CTLFLAG_RD, &sc->disable_msi, 0,
+	    "Disable the use of MSI interrupts");
+
+	SYSCTL_ADD_STRING(sysctl_ctx, SYSCTL_CHILDREN(sysctl_tree),
+	    OID_AUTO, "firmware_version", CTLFLAG_RW, &sc->fw_version,
+	    strlen(sc->fw_version), "firmware version");
+
+	SYSCTL_ADD_STRING(sysctl_ctx, SYSCTL_CHILDREN(sysctl_tree),
+	    OID_AUTO, "driver_version", CTLFLAG_RW, MPS_DRIVER_VERSION,
+	    strlen(MPS_DRIVER_VERSION), "driver version");
+
+	SYSCTL_ADD_INT(sysctl_ctx, SYSCTL_CHILDREN(sysctl_tree),
 	    OID_AUTO, "io_cmds_active", CTLFLAG_RD,
 	    &sc->io_cmds_active, 0, "number of currently active commands");
 
-	SYSCTL_ADD_INT(&sc->sysctl_ctx, SYSCTL_CHILDREN(sc->sysctl_tree),
+	SYSCTL_ADD_INT(sysctl_ctx, SYSCTL_CHILDREN(sysctl_tree),
 	    OID_AUTO, "io_cmds_highwater", CTLFLAG_RD,
 	    &sc->io_cmds_highwater, 0, "maximum active commands seen");
 
-	SYSCTL_ADD_INT(&sc->sysctl_ctx, SYSCTL_CHILDREN(sc->sysctl_tree),
+	SYSCTL_ADD_INT(sysctl_ctx, SYSCTL_CHILDREN(sysctl_tree),
 	    OID_AUTO, "chain_free", CTLFLAG_RD,
 	    &sc->chain_free, 0, "number of free chain elements");
 
-	SYSCTL_ADD_INT(&sc->sysctl_ctx, SYSCTL_CHILDREN(sc->sysctl_tree),
+	SYSCTL_ADD_INT(sysctl_ctx, SYSCTL_CHILDREN(sysctl_tree),
 	    OID_AUTO, "chain_free_lowwater", CTLFLAG_RD,
 	    &sc->chain_free_lowwater, 0,"lowest number of free chain elements");
 
-	SYSCTL_ADD_UQUAD(&sc->sysctl_ctx, SYSCTL_CHILDREN(sc->sysctl_tree),
+	SYSCTL_ADD_INT(sysctl_ctx, SYSCTL_CHILDREN(sysctl_tree),
+	    OID_AUTO, "max_chains", CTLFLAG_RD,
+	    &sc->max_chains, 0,"maximum chain frames that will be allocated");
+
+#if __FreeBSD_version >= 900030
+	SYSCTL_ADD_UQUAD(sysctl_ctx, SYSCTL_CHILDREN(sysctl_tree),
 	    OID_AUTO, "chain_alloc_fail", CTLFLAG_RD,
 	    &sc->chain_alloc_fail, "chain allocation failures");
+#endif //FreeBSD_version >= 900030
+}
 
-	if ((error = mps_transition_ready(sc)) != 0)
+int
+mps_attach(struct mps_softc *sc)
+{
+	int i, error;
+
+	mps_get_tunables(sc);
+
+	mps_dprint(sc, MPS_TRACE, "%s\n", __func__);
+
+	mtx_init(&sc->mps_mtx, "MPT2SAS lock", NULL, MTX_DEF);
+	callout_init_mtx(&sc->periodic, &sc->mps_mtx, 0);
+	TAILQ_INIT(&sc->event_list);
+
+	if ((error = mps_transition_ready(sc)) != 0) {
+		mps_printf(sc, "%s failed to transition ready\n", __func__);
 		return (error);
+	}
 
 	sc->facts = malloc(sizeof(MPI2_IOC_FACTS_REPLY), M_MPT2,
 	    M_ZERO|M_NOWAIT);
@@ -896,11 +1061,15 @@
 
 	mps_print_iocfacts(sc, sc->facts);
 
-	mps_printf(sc, "Firmware: %02d.%02d.%02d.%02d\n",
+	snprintf(sc->fw_version, sizeof(sc->fw_version), 
+	    "%02d.%02d.%02d.%02d", 
 	    sc->facts->FWVersion.Struct.Major,
 	    sc->facts->FWVersion.Struct.Minor,
 	    sc->facts->FWVersion.Struct.Unit,
 	    sc->facts->FWVersion.Struct.Dev);
+
+	mps_printf(sc, "Firmware: %s, Driver: %s\n", sc->fw_version,
+	    MPS_DRIVER_VERSION);
 	mps_printf(sc, "IOCCapabilities: %b\n", sc->facts->IOCCapabilities,
 	    "\20" "\3ScsiTaskFull" "\4DiagTrace" "\5SnapBuf" "\6ExtBuf"
 	    "\7EEDP" "\10BiDirTarg" "\11Multicast" "\14TransRetry" "\15IR"
@@ -914,34 +1083,66 @@
 	 */
 	if ((sc->facts->IOCCapabilities &
 	    MPI2_IOCFACTS_CAPABILITY_EVENT_REPLAY) == 0) {
-		mps_hard_reset(sc);
+		mps_diag_reset(sc);
 		if ((error = mps_transition_ready(sc)) != 0)
 			return (error);
 	}
 
 	/*
+	 * Set flag if IR Firmware is loaded.
+	 */
+	if (sc->facts->IOCCapabilities &
+	    MPI2_IOCFACTS_CAPABILITY_INTEGRATED_RAID)
+		sc->ir_firmware = 1;
+
+	/*
+	 * Check if controller supports FW diag buffers and set flag to enable
+	 * each type.
+	 */
+	if (sc->facts->IOCCapabilities &
+	    MPI2_IOCFACTS_CAPABILITY_DIAG_TRACE_BUFFER)
+		sc->fw_diag_buffer_list[MPI2_DIAG_BUF_TYPE_TRACE].enabled =
+		    TRUE;
+	if (sc->facts->IOCCapabilities &
+	    MPI2_IOCFACTS_CAPABILITY_SNAPSHOT_BUFFER)
+		sc->fw_diag_buffer_list[MPI2_DIAG_BUF_TYPE_SNAPSHOT].enabled =
+		    TRUE;
+	if (sc->facts->IOCCapabilities &
+	    MPI2_IOCFACTS_CAPABILITY_EXTENDED_BUFFER)
+		sc->fw_diag_buffer_list[MPI2_DIAG_BUF_TYPE_EXTENDED].enabled =
+		    TRUE;
+
+	/*
+	 * Set flag if EEDP is supported and if TLR is supported.
+	 */
+	if (sc->facts->IOCCapabilities & MPI2_IOCFACTS_CAPABILITY_EEDP)
+		sc->eedp_enabled = TRUE;
+	if (sc->facts->IOCCapabilities & MPI2_IOCFACTS_CAPABILITY_TLR)
+		sc->control_TLR = TRUE;
+
+	/*
 	 * Size the queues. Since the reply queues always need one free entry,
 	 * we'll just deduct one reply message here.
 	 */
 	sc->num_reqs = MIN(MPS_REQ_FRAMES, sc->facts->RequestCredit);
 	sc->num_replies = MIN(MPS_REPLY_FRAMES + MPS_EVT_REPLY_FRAMES,
 	    sc->facts->MaxReplyDescriptorPostQueueDepth) - 1;
-	mps_dprint(sc, MPS_INFO, "num_reqs %d, num_replies %d\n", sc->num_reqs,
-		   sc->num_replies);
 	TAILQ_INIT(&sc->req_list);
+	TAILQ_INIT(&sc->high_priority_req_list);
 	TAILQ_INIT(&sc->chain_list);
 	TAILQ_INIT(&sc->tm_list);
-	TAILQ_INIT(&sc->io_list);
 
 	if (((error = mps_alloc_queues(sc)) != 0) ||
 	    ((error = mps_alloc_replies(sc)) != 0) ||
 	    ((error = mps_alloc_requests(sc)) != 0)) {
+		mps_printf(sc, "%s failed to alloc\n", __func__);
 		mps_free(sc);
 		return (error);
 	}
 
 	if (((error = mps_init_queues(sc)) != 0) ||
 	    ((error = mps_transition_operational(sc)) != 0)) {
+		mps_printf(sc, "%s failed to transition operational\n", __func__);
 		mps_free(sc);
 		return (error);
 	}
@@ -964,6 +1165,8 @@
 	    sc->facts->NumberOfPorts, M_MPT2, M_ZERO|M_WAITOK);
 	for (i = 0; i < sc->facts->NumberOfPorts; i++) {
 		if ((error = mps_get_portfacts(sc, &sc->pfacts[i], i)) != 0) {
+			mps_printf(sc, "%s failed to get portfacts for port %d\n",
+			    __func__, i);
 			mps_free(sc);
 			return (error);
 		}
@@ -982,10 +1185,17 @@
 	}
 
 	if ((error = mps_pci_setup_interrupts(sc)) != 0) {
+		mps_printf(sc, "%s failed to setup interrupts\n", __func__);
 		mps_free(sc);
 		return (error);
 	}
 
+	/*
+	 * The static page function currently read is ioc page8.  Others can be
+	 * added in future.
+	 */
+	mps_base_static_config_pages(sc);
+
 	/* Start the periodic watchdog check on the IOC Doorbell */
 	mps_periodic(sc);
 
@@ -1001,11 +1211,24 @@
 		error = EINVAL;
 	}
 
+	/*
+	 * Allow IR to shutdown gracefully when shutdown occurs.
+	 */
+	sc->shutdown_eh = EVENTHANDLER_REGISTER(shutdown_final,
+	    mpssas_ir_shutdown, sc, SHUTDOWN_PRI_DEFAULT);
+
+	if (sc->shutdown_eh == NULL)
+		mps_dprint(sc, MPS_FAULT, "shutdown event registration "
+		    "failed\n");
+
+	mps_setup_sysctl(sc);
+
 	sc->mps_flags |= MPS_FLAGS_ATTACH_DONE;
 
 	return (error);
 }
 
+/* Run through any late-start handlers. */
 static void
 mps_startup(void *arg)
 {
@@ -1015,7 +1238,9 @@
 
 	mps_lock(sc);
 	mps_unmask_intr(sc);
-	mps_send_portenable(sc);
+	/* initialize device mapping tables */
+	mps_mapping_initialize(sc);
+	mpssas_startup(sc);
 	mps_unlock(sc);
 }
 
@@ -1033,36 +1258,14 @@
 	db = mps_regread(sc, MPI2_DOORBELL_OFFSET);
 	if ((db & MPI2_IOC_STATE_MASK) == MPI2_IOC_STATE_FAULT) {
 		device_printf(sc->mps_dev, "IOC Fault 0x%08x, Resetting\n", db);
-		/* XXX Need to broaden this to re-initialize the chip */
-		mps_hard_reset(sc);
-		db = mps_regread(sc, MPI2_DOORBELL_OFFSET);
-		if ((db & MPI2_IOC_STATE_MASK) == MPI2_IOC_STATE_FAULT) {
-			device_printf(sc->mps_dev, "Second IOC Fault 0x%08x, "
-			    "Giving up!\n", db);
-			return;
-		}
+
+		mps_reinit(sc);
 	}
 
 	callout_reset(&sc->periodic, MPS_PERIODIC_DELAY * hz, mps_periodic, sc);
 }
 
 static void
-mps_startup_complete(struct mps_softc *sc, struct mps_command *cm)
-{
-	MPI2_PORT_ENABLE_REPLY *reply;
-
-	mps_dprint(sc, MPS_TRACE, "%s\n", __func__);
-
-	reply = (MPI2_PORT_ENABLE_REPLY *)cm->cm_reply;
-	if ((reply->IOCStatus & MPI2_IOCSTATUS_MASK) != MPI2_IOCSTATUS_SUCCESS)
-		mps_dprint(sc, MPS_FAULT, "Portenable failed\n");
-
-	mps_free_command(sc, cm);
-	config_intrhook_disestablish(&sc->mps_ich);
-
-}
-
-static void
 mps_log_evt_handler(struct mps_softc *sc, uintptr_t data,
     MPI2_EVENT_NOTIFICATION_REPLY *event)
 {
@@ -1134,7 +1337,7 @@
 
 	/* Put the IOC back in the READY state. */
 	mps_lock(sc);
-	if ((error = mps_send_mur(sc)) != 0) {
+	if ((error = mps_transition_ready(sc)) != 0) {
 		mps_unlock(sc);
 		return (error);
 	}
@@ -1197,6 +1400,12 @@
 	if (sc->sysctl_tree != NULL)
 		sysctl_ctx_free(&sc->sysctl_ctx);
 
+	mps_mapping_free_memory(sc);
+
+	/* Deregister the shutdown function */
+	if (sc->shutdown_eh != NULL)
+		EVENTHANDLER_DEREGISTER(shutdown_final, sc->shutdown_eh);
+
 	mtx_destroy(&sc->mps_mtx);
 
 	return (0);
@@ -1208,14 +1417,26 @@
 	if (cm->cm_flags & MPS_CM_FLAGS_POLLED)
 		cm->cm_flags |= MPS_CM_FLAGS_COMPLETE;
 
-	if (cm->cm_complete != NULL)
+	if (cm->cm_complete != NULL) {
+		mps_dprint(cm->cm_sc, MPS_TRACE,
+			   "%s cm %p calling cm_complete %p data %p reply %p\n",
+			   __func__, cm, cm->cm_complete, cm->cm_complete_data,
+			   cm->cm_reply);
 		cm->cm_complete(cm->cm_sc, cm);
+	}
 
 	if (cm->cm_flags & MPS_CM_FLAGS_WAKEUP) {
 		mps_dprint(cm->cm_sc, MPS_TRACE, "%s: waking up %p\n",
 			   __func__, cm);
 		wakeup(cm);
 	}
+
+	if (cm->cm_sc->io_cmds_active != 0) {
+		cm->cm_sc->io_cmds_active--;
+	} else {
+		mps_dprint(cm->cm_sc, MPS_INFO, "Warning: io_cmds_active is "
+		    "out of sync - resynching to 0\n");
+	}
 }
 
 void
@@ -1251,6 +1472,7 @@
 	struct mps_softc *sc;
 
 	sc = (struct mps_softc *)data;
+	mps_dprint(sc, MPS_TRACE, "%s\n", __func__);
 	mps_lock(sc);
 	mps_intr_locked(data);
 	mps_unlock(sc);
@@ -1268,20 +1490,35 @@
 	struct mps_command *cm = NULL;
 	uint8_t flags;
 	u_int pq;
+	MPI2_DIAG_RELEASE_REPLY *rel_rep;
+	mps_fw_diagnostic_buffer_t *pBuffer;
 
 	sc = (struct mps_softc *)data;
 
 	pq = sc->replypostindex;
+	mps_dprint(sc, MPS_TRACE,
+	    "%s sc %p starting with replypostindex %u\n", 
+	    __func__, sc, sc->replypostindex);
 
 	for ( ;; ) {
 		cm = NULL;
-		desc = &sc->post_queue[pq];
+		desc = &sc->post_queue[sc->replypostindex];
 		flags = desc->Default.ReplyFlags &
 		    MPI2_RPY_DESCRIPT_FLAGS_TYPE_MASK;
 		if ((flags == MPI2_RPY_DESCRIPT_FLAGS_UNUSED)
 		 || (desc->Words.High == 0xffffffff))
 			break;
 
+		/* increment the replypostindex now, so that event handlers
+		 * and cm completion handlers which decide to do a diag
+		 * reset can zero it without it getting incremented again
+		 * afterwards, and we break out of this loop on the next
+		 * iteration since the reply post queue has been cleared to
+		 * 0xFF and all descriptors look unused (which they are).
+		 */
+		if (++sc->replypostindex >= sc->pqdepth)
+			sc->replypostindex = 0;
+
 		switch (flags) {
 		case MPI2_RPY_DESCRIPT_FLAGS_SCSI_IO_SUCCESS:
 			cm = &sc->commands[desc->SCSIIOSuccess.SMID];
@@ -1323,8 +1560,32 @@
 				panic("Reply address out of range");
 			}
 			if (desc->AddressReply.SMID == 0) {
-				mps_dispatch_event(sc, baddr,
-				   (MPI2_EVENT_NOTIFICATION_REPLY *) reply);
+				if (((MPI2_DEFAULT_REPLY *)reply)->Function ==
+				    MPI2_FUNCTION_DIAG_BUFFER_POST) {
+					/*
+					 * If SMID is 0 for Diag Buffer Post,
+					 * this implies that the reply is due to
+					 * a release function with a status that
+					 * the buffer has been released.  Set
+					 * the buffer flags accordingly.
+					 */
+					rel_rep =
+					    (MPI2_DIAG_RELEASE_REPLY *)reply;
+					if (rel_rep->IOCStatus ==
+					    MPI2_IOCSTATUS_DIAGNOSTIC_RELEASED)
+					    {
+						pBuffer =
+						    &sc->fw_diag_buffer_list[
+						    rel_rep->BufferType];
+						pBuffer->valid_data = TRUE;
+						pBuffer->owned_by_firmware =
+						    FALSE;
+						pBuffer->immediate = FALSE;
+					}
+				} else
+					mps_dispatch_event(sc, baddr,
+					    (MPI2_EVENT_NOTIFICATION_REPLY *)
+					    reply);
 			} else {
 				cm = &sc->commands[desc->AddressReply.SMID];
 				cm->cm_reply = reply;
@@ -1349,14 +1610,13 @@
 
 		desc->Words.Low = 0xffffffff;
 		desc->Words.High = 0xffffffff;
-		if (++pq >= sc->pqdepth)
-			pq = 0;
 	}
 
 	if (pq != sc->replypostindex) {
-		mps_dprint(sc, MPS_INFO, "writing postindex %d\n", pq);
-		mps_regwrite(sc, MPI2_REPLY_POST_HOST_INDEX_OFFSET, pq);
-		sc->replypostindex = pq;
+		mps_dprint(sc, MPS_TRACE,
+		    "%s sc %p writing postindex %d\n",
+		    __func__, sc, sc->replypostindex);
+		mps_regwrite(sc, MPI2_REPLY_POST_HOST_INDEX_OFFSET, sc->replypostindex);
 	}
 
 	return;
@@ -1379,6 +1639,28 @@
 
 	if (handled == 0)
 		device_printf(sc->mps_dev, "Unhandled event 0x%x\n", event);
+
+	/*
+	 * This is the only place that the event/reply should be freed.
+	 * Anything wanting to hold onto the event data should have
+	 * already copied it into their own storage.
+	 */
+	mps_free_reply(sc, data);
+}
+
+static void
+mps_reregister_events_complete(struct mps_softc *sc, struct mps_command *cm)
+{
+	mps_dprint(sc, MPS_TRACE, "%s\n", __func__);
+
+	if (cm->cm_reply)
+		mps_print_event(sc,
+			(MPI2_EVENT_NOTIFICATION_REPLY *)cm->cm_reply);
+
+	mps_free_command(sc, cm);
+
+	/* next, send a port enable */
+	mpssas_startup(sc);
 }
 
 /*
@@ -1445,14 +1727,60 @@
 
 	error = mps_request_polled(sc, cm);
 	reply = (MPI2_EVENT_NOTIFICATION_REPLY *)cm->cm_reply;
-	if ((reply->IOCStatus & MPI2_IOCSTATUS_MASK) != MPI2_IOCSTATUS_SUCCESS)
+	if ((reply == NULL) ||
+	    (reply->IOCStatus & MPI2_IOCSTATUS_MASK) != MPI2_IOCSTATUS_SUCCESS)
 		error = ENXIO;
 	mps_print_event(sc, reply);
+	mps_dprint(sc, MPS_TRACE, "%s finished error %d\n", __func__, error);
 
 	mps_free_command(sc, cm);
 	return (error);
 }
 
+static int
+mps_reregister_events(struct mps_softc *sc)
+{
+	MPI2_EVENT_NOTIFICATION_REQUEST *evtreq;
+	struct mps_command *cm;
+	struct mps_event_handle *eh;
+	int error, i;
+
+	mps_dprint(sc, MPS_TRACE, "%s\n", __func__);
+
+	/* first, reregister events */
+
+	memset(sc->event_mask, 0xff, 16);
+
+	TAILQ_FOREACH(eh, &sc->event_list, eh_list) {
+		for (i = 0; i < 16; i++)
+			sc->event_mask[i] &= ~eh->mask[i];
+	}
+
+	if ((cm = mps_alloc_command(sc)) == NULL)
+		return (EBUSY);
+	evtreq = (MPI2_EVENT_NOTIFICATION_REQUEST *)cm->cm_req;
+	evtreq->Function = MPI2_FUNCTION_EVENT_NOTIFICATION;
+	evtreq->MsgFlags = 0;
+	evtreq->SASBroadcastPrimitiveMasks = 0;
+#ifdef MPS_DEBUG_ALL_EVENTS
+	{
+		u_char fullmask[16];
+		memset(fullmask, 0x00, 16);
+		bcopy(fullmask, (uint8_t *)&evtreq->EventMasks, 16);
+	}
+#else
+		bcopy(sc->event_mask, (uint8_t *)&evtreq->EventMasks, 16);
+#endif
+	cm->cm_desc.Default.RequestFlags = MPI2_REQ_DESCRIPT_FLAGS_DEFAULT_TYPE;
+	cm->cm_data = NULL;
+	cm->cm_complete = mps_reregister_events_complete;
+
+	error = mps_map_command(sc, cm);
+
+	mps_dprint(sc, MPS_TRACE, "%s finished with error %d\n", __func__, error);
+	return (error);
+}
+
 int
 mps_deregister_events(struct mps_softc *sc, struct mps_event_handle *handle)
 {
@@ -1511,6 +1839,7 @@
 	MPI2_SGE_TRANSACTION_UNION *tc = sgep;
 	MPI2_SGE_SIMPLE64 *sge = sgep;
 	int error, type;
+	uint32_t saved_buf_len, saved_address_low, saved_address_high;
 
 	type = (tc->Flags & MPI2_SGE_FLAGS_ELEMENT_MASK);
 
@@ -1609,12 +1938,48 @@
 
 	if (segsleft == 1 && type == MPI2_SGE_FLAGS_SIMPLE_ELEMENT) {
 		/*
-		 * Last element of the last segment of the entire
-		 * buffer.
+		 * If this is a bi-directional request, need to account for that
+		 * here.  Save the pre-filled sge values.  These will be used
+		 * either for the 2nd SGL or for a single direction SGL.  If
+		 * cm_out_len is non-zero, this is a bi-directional request, so
+		 * fill in the OUT SGL first, then the IN SGL, otherwise just
+		 * fill in the IN SGL.  Note that at this time, when filling in
+		 * 2 SGL's for a bi-directional request, they both use the same
+		 * DMA buffer (same cm command).
 		 */
-		sge->FlagsLength |= ((MPI2_SGE_FLAGS_LAST_ELEMENT |
+		saved_buf_len = sge->FlagsLength & 0x00FFFFFF;
+		saved_address_low = sge->Address.Low;
+		saved_address_high = sge->Address.High;
+		if (cm->cm_out_len) {
+			sge->FlagsLength = cm->cm_out_len |
+			    ((uint32_t)(MPI2_SGE_FLAGS_SIMPLE_ELEMENT |
+			    MPI2_SGE_FLAGS_END_OF_BUFFER |
+			    MPI2_SGE_FLAGS_HOST_TO_IOC |
+			    MPI2_SGE_FLAGS_64_BIT_ADDRESSING) <<
+			    MPI2_SGE_FLAGS_SHIFT);
+			cm->cm_sglsize -= len;
+			bcopy(sgep, cm->cm_sge, len);
+			cm->cm_sge = (MPI2_SGE_IO_UNION *)((uintptr_t)cm->cm_sge
+			    + len);
+		}
+		sge->FlagsLength = saved_buf_len |
+		    ((uint32_t)(MPI2_SGE_FLAGS_SIMPLE_ELEMENT |
 		    MPI2_SGE_FLAGS_END_OF_BUFFER |
-		    MPI2_SGE_FLAGS_END_OF_LIST) << MPI2_SGE_FLAGS_SHIFT);
+		    MPI2_SGE_FLAGS_LAST_ELEMENT |
+		    MPI2_SGE_FLAGS_END_OF_LIST |
+		    MPI2_SGE_FLAGS_64_BIT_ADDRESSING) <<
+		    MPI2_SGE_FLAGS_SHIFT);
+		if (cm->cm_flags & MPS_CM_FLAGS_DATAIN) {
+			sge->FlagsLength |=
+			    ((uint32_t)(MPI2_SGE_FLAGS_IOC_TO_HOST) <<
+			    MPI2_SGE_FLAGS_SHIFT);
+		} else {
+			sge->FlagsLength |=
+			    ((uint32_t)(MPI2_SGE_FLAGS_HOST_TO_IOC) <<
+			    MPI2_SGE_FLAGS_SHIFT);
+		}
+		sge->Address.Low = saved_address_low;
+		sge->Address.High = saved_address_high;
 	}
 
 	cm->cm_sglsize -= len;
@@ -1633,10 +1998,10 @@
 	MPI2_SGE_SIMPLE64 sge;
 
 	/*
-	 * This driver always uses 64-bit address elements for
-	 * simplicity.
+	 * This driver always uses 64-bit address elements for simplicity.
 	 */
-	flags |= MPI2_SGE_FLAGS_SIMPLE_ELEMENT | MPI2_SGE_FLAGS_ADDRESS_SIZE;
+	flags |= MPI2_SGE_FLAGS_SIMPLE_ELEMENT |
+	    MPI2_SGE_FLAGS_64_BIT_ADDRESSING;
 	sge.FlagsLength = len | (flags << MPI2_SGE_FLAGS_SHIFT);
 	mps_from_u64(pa, &sge.Address);
 
@@ -1664,8 +2029,8 @@
 	}
 
 	/*
-	 * Set up DMA direction flags.  Note that we don't support
-	 * bi-directional transfers, with the exception of SMP passthrough.
+	 * Set up DMA direction flags.  Bi-directional requests are also handled
+	 * here.  In that case, both direction flags will be set.
 	 */
 	sflags = 0;
 	if (cm->cm_flags & MPS_CM_FLAGS_SMP_PASS) {
@@ -1691,14 +2056,13 @@
 		sflags |= MPI2_SGE_FLAGS_DIRECTION |
 			  MPI2_SGE_FLAGS_END_OF_BUFFER;
 	} else if (cm->cm_flags & MPS_CM_FLAGS_DATAOUT) {
-		sflags |= MPI2_SGE_FLAGS_DIRECTION;
+		sflags |= MPI2_SGE_FLAGS_HOST_TO_IOC;
 		dir = BUS_DMASYNC_PREWRITE;
 	} else
 		dir = BUS_DMASYNC_PREREAD;
 
 	for (i = 0; i < nsegs; i++) {
-		if ((cm->cm_flags & MPS_CM_FLAGS_SMP_PASS)
-		 && (i != 0)) {
+		if ((cm->cm_flags & MPS_CM_FLAGS_SMP_PASS) && (i != 0)) {
 			sflags &= ~MPI2_SGE_FLAGS_DIRECTION;
 		}
 		error = mps_add_dmaseg(cm, segs[i].ds_addr, segs[i].ds_len,
@@ -1726,8 +2090,11 @@
 }
 
 /*
+ * This is the routine to enqueue commands ansynchronously.
  * Note that the only error path here is from bus_dmamap_load(), which can
- * return EINPROGRESS if it is waiting for resources.
+ * return EINPROGRESS if it is waiting for resources.  Other than this, it's
+ * assumed that if you have a command in-hand, then you have enough credits
+ * to use it.
  */
 int
 mps_map_command(struct mps_softc *sc, struct mps_command *cm)
@@ -1752,7 +2119,58 @@
 			    MPI2_SGE_FLAGS_SHIFT;
 			sge->Address = 0;
 		}
-		mps_enqueue_request(sc, cm);
+		mps_enqueue_request(sc, cm);	
+	}
+
+	return (error);
+}
+
+/*
+ * This is the routine to enqueue commands synchronously.  An error of
+ * EINPROGRESS from mps_map_command() is ignored since the command will
+ * be executed and enqueued automatically.  Other errors come from msleep().
+ */
+int
+mps_wait_command(struct mps_softc *sc, struct mps_command *cm, int timeout)
+{
+	int error;
+
+	mtx_assert(&sc->mps_mtx, MA_OWNED);
+
+	cm->cm_complete = NULL;
+	cm->cm_flags |= MPS_CM_FLAGS_WAKEUP;
+	error = mps_map_command(sc, cm);
+	if ((error != 0) && (error != EINPROGRESS))
+		return (error);
+	error = msleep(cm, &sc->mps_mtx, 0, "mpswait", timeout);
+	if (error == EWOULDBLOCK)
+		error = ETIMEDOUT;
+	return (error);
+}
+
+/*
+ * This is the routine to enqueue a command synchonously and poll for
+ * completion.  Its use should be rare.
+ */
+int
+mps_request_polled(struct mps_softc *sc, struct mps_command *cm)
+{
+	int error, timeout = 0;
+
+	error = 0;
+
+	cm->cm_flags |= MPS_CM_FLAGS_POLLED;
+	cm->cm_complete = NULL;
+	mps_map_command(sc, cm);
+
+	while ((cm->cm_flags & MPS_CM_FLAGS_COMPLETE) == 0) {
+		mps_intr_locked(sc);
+		DELAY(50 * 1000);
+		if (timeout++ > 1000) {
+			mps_dprint(sc, MPS_FAULT, "polling failed\n");
+			error = ETIMEDOUT;
+			break;
+		}
 	}
 
 	return (error);
@@ -1816,11 +2234,13 @@
 		cm->cm_complete = mps_config_complete;
 		return (mps_map_command(sc, cm));
 	} else {
-		cm->cm_complete = NULL;
-		cm->cm_flags |= MPS_CM_FLAGS_WAKEUP;
-		if ((error = mps_map_command(sc, cm)) != 0)
+		error = mps_wait_command(sc, cm, 0);
+		if (error) {
+			mps_dprint(sc, MPS_FAULT,
+			    "Error %d reading config page\n", error);
+			mps_free_command(sc, cm);
 			return (error);
-		msleep(cm, &sc->mps_mtx, 0, "mpswait", 0);
+		}
 		mps_config_complete(sc, cm);
 	}
 
@@ -1853,10 +2273,14 @@
 	 */
 	if ((cm->cm_flags & MPS_CM_FLAGS_ERROR_MASK) != 0) {
 		params->status = MPI2_IOCSTATUS_BUSY;
-		goto bailout;
+		goto done;
 	}
 
 	reply = (MPI2_CONFIG_REPLY *)cm->cm_reply;
+	if (reply == NULL) {
+		params->status = MPI2_IOCSTATUS_BUSY;
+		goto done;
+	}
 	params->status = reply->IOCStatus;
 	if (params->hdr.Ext.ExtPageType != 0) {
 		params->hdr.Ext.ExtPageType = reply->ExtPageType;
@@ -1868,8 +2292,7 @@
 		params->hdr.Struct.PageVersion = reply->Header.PageVersion;
 	}
 
-bailout:
-
+done:
 	mps_free_command(sc, cm);
 	if (params->callback != NULL)
 		params->callback(sc, params);
diff -r 52ad4c882614 -r 6b8317170ea4 head/sys/dev/mps/mps_config.c
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/head/sys/dev/mps/mps_config.c	Wed Feb 01 11:28:20 2012 +0200
@@ -0,0 +1,1393 @@
+/*-
+ * Copyright (c) 2011 LSI Corp.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * 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 OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * LSI MPT-Fusion Host Adapter FreeBSD
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD: head/sys/dev/mps/mps_config.c 230592 2012-01-26 18:17:21Z ken $");
+
+/* TODO Move headers to mpsvar */
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/lock.h>
+#include <sys/mutex.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <sys/malloc.h>
+#include <sys/kthread.h>
+#include <sys/taskqueue.h>
+#include <sys/bus.h>
+#include <sys/endian.h>
+#include <sys/sysctl.h>
+#include <sys/eventhandler.h>
+#include <sys/uio.h>
+#include <machine/bus.h>
+#include <machine/resource.h>
+#include <dev/mps/mpi/mpi2_type.h>
+#include <dev/mps/mpi/mpi2.h>
+#include <dev/mps/mpi/mpi2_ioc.h>
+#include <dev/mps/mpi/mpi2_sas.h>
+#include <dev/mps/mpi/mpi2_cnfg.h>
+#include <dev/mps/mpi/mpi2_init.h>
+#include <dev/mps/mpi/mpi2_tool.h>
+#include <dev/mps/mps_ioctl.h>
+#include <dev/mps/mpsvar.h>
+
+/**
+ * mps_config_get_ioc_pg8 - obtain ioc page 8
+ * @sc: per adapter object
+ * @mpi_reply: reply mf payload returned from firmware
+ * @config_page: contents of the config page
+ * Context: sleep.
+ *
+ * Returns 0 for success, non-zero for failure.
+ */
+int
+mps_config_get_ioc_pg8(struct mps_softc *sc, Mpi2ConfigReply_t *mpi_reply,
+    Mpi2IOCPage8_t *config_page)
+{
+	MPI2_CONFIG_REQUEST *request;
+	MPI2_CONFIG_REPLY *reply;
+	struct mps_command *cm;
+	MPI2_CONFIG_PAGE_IOC_8 *page = NULL;
+	int error = 0;
+	u16 ioc_status;
+
+	mps_dprint(sc, MPS_TRACE, "%s\n", __func__);
+
+	if ((cm = mps_alloc_command(sc)) == NULL) {
+		printf("%s: command alloc failed @ line %d\n", __func__,
+		    __LINE__);
+		error = EBUSY;
+		goto out;
+	}
+	request = (MPI2_CONFIG_REQUEST *)cm->cm_req;
+	bzero(request, sizeof(MPI2_CONFIG_REQUEST));
+	request->Function = MPI2_FUNCTION_CONFIG;
+	request->Action = MPI2_CONFIG_ACTION_PAGE_HEADER;
+	request->Header.PageType = MPI2_CONFIG_PAGETYPE_IOC;
+	request->Header.PageNumber = 8;
+	request->Header.PageVersion = MPI2_IOCPAGE8_PAGEVERSION;
+	cm->cm_desc.Default.RequestFlags = MPI2_REQ_DESCRIPT_FLAGS_DEFAULT_TYPE;
+	cm->cm_data = NULL;
+	error = mps_request_polled(sc, cm);
+	reply = (MPI2_CONFIG_REPLY *)cm->cm_reply;
+	if (error || (reply == NULL)) {
+		/* FIXME */
+		/* If the poll returns error then we need to do diag reset */ 
+		printf("%s: poll for header completed with error %d",
+		    __func__, error);
+		error = ENXIO;
+		goto out;
+	}
+	ioc_status = le16toh(reply->IOCStatus) & MPI2_IOCSTATUS_MASK;
+	bcopy(reply, mpi_reply, sizeof(MPI2_CONFIG_REPLY));
+	if (ioc_status != MPI2_IOCSTATUS_SUCCESS) {
+		/* FIXME */
+		/* If the poll returns error then we need to do diag reset */ 
+		printf("%s: header read with error; iocstatus = 0x%x\n",
+		    __func__, ioc_status);
+		error = ENXIO;
+		goto out;
+	}
+	/* We have to do free and alloc for the reply-free and reply-post
+	 * counters to match - Need to review the reply FIFO handling.
+	 */
+	mps_free_command(sc, cm);
+	
+	if ((cm = mps_alloc_command(sc)) == NULL) {
+		printf("%s: command alloc failed @ line %d\n", __func__,
+		    __LINE__);
+		error = EBUSY;
+		goto out;
+	}
+	request = (MPI2_CONFIG_REQUEST *)cm->cm_req;
+	bzero(request, sizeof(MPI2_CONFIG_REQUEST));
+	request->Function = MPI2_FUNCTION_CONFIG;
+	request->Action = MPI2_CONFIG_ACTION_PAGE_READ_CURRENT;
+	request->Header.PageType = MPI2_CONFIG_PAGETYPE_IOC;
+	request->Header.PageNumber = 8;
+	request->Header.PageVersion = MPI2_IOCPAGE8_PAGEVERSION;
+	request->Header.PageLength = mpi_reply->Header.PageLength;
+	cm->cm_length =  le16toh(mpi_reply->Header.PageLength) * 4;
+	cm->cm_sge = &request->PageBufferSGE;
+	cm->cm_sglsize = sizeof(MPI2_SGE_IO_UNION);
+	cm->cm_flags = MPS_CM_FLAGS_SGE_SIMPLE | MPS_CM_FLAGS_DATAIN;
+	cm->cm_desc.Default.RequestFlags = MPI2_REQ_DESCRIPT_FLAGS_DEFAULT_TYPE;
+	page = malloc((cm->cm_length), M_MPT2, M_ZERO | M_NOWAIT);
+	if (!page) {
+		printf("%s: page alloc failed\n", __func__);
+		error = ENOMEM;
+		goto out;
+	}
+	cm->cm_data = page;
+	error = mps_request_polled(sc, cm);
+	reply = (MPI2_CONFIG_REPLY *)cm->cm_reply;
+	if (error || (reply == NULL)) {
+		/* FIXME */
+		/* If the poll returns error then we need to do diag reset */ 
+		printf("%s: poll for page completed with error %d",
+		    __func__, error);
+		error = ENXIO;
+		goto out;
+	}
+	ioc_status = le16toh(reply->IOCStatus) & MPI2_IOCSTATUS_MASK;
+	bcopy(reply, mpi_reply, sizeof(MPI2_CONFIG_REPLY));
+	if (ioc_status != MPI2_IOCSTATUS_SUCCESS) {
+		/* FIXME */
+		/* If the poll returns error then we need to do diag reset */ 
+		printf("%s: page read with error; iocstatus = 0x%x\n",
+		    __func__, ioc_status);
+		error = ENXIO;
+		goto out;
+	}
+	bcopy(page, config_page, MIN(cm->cm_length, (sizeof(Mpi2IOCPage8_t))));
+
+out:
+	free(page, M_MPT2);
+	if (cm)
+		mps_free_command(sc, cm);
+	return (error);
+}
+
+/**
+ * mps_config_get_man_pg10 - obtain Manufacturing Page 10 data and set flags
+ *   accordingly.  Currently, this page does not need to return to caller.
+ * @sc: per adapter object
+ * @mpi_reply: reply mf payload returned from firmware
+ * Context: sleep.
+ *
+ * Returns 0 for success, non-zero for failure.
+ */
+int
+mps_config_get_man_pg10(struct mps_softc *sc, Mpi2ConfigReply_t *mpi_reply)
+{
+	MPI2_CONFIG_REQUEST *request;
+	MPI2_CONFIG_REPLY *reply;
+	struct mps_command *cm;
+	pMpi2ManufacturingPagePS_t page = NULL;
+	uint32_t *pPS_info;
+	uint8_t OEM_Value = 0;
+	int error = 0;
+	u16 ioc_status;
+
+	mps_dprint(sc, MPS_TRACE, "%s\n", __func__);
+
+	if ((cm = mps_alloc_command(sc)) == NULL) {
+		printf("%s: command alloc failed @ line %d\n", __func__,
+		    __LINE__);
+		error = EBUSY;
+		goto out;
+	}
+	request = (MPI2_CONFIG_REQUEST *)cm->cm_req;
+	bzero(request, sizeof(MPI2_CONFIG_REQUEST));
+	request->Function = MPI2_FUNCTION_CONFIG;
+	request->Action = MPI2_CONFIG_ACTION_PAGE_HEADER;
+	request->Header.PageType = MPI2_CONFIG_PAGETYPE_MANUFACTURING;
+	request->Header.PageNumber = 10;
+	request->Header.PageVersion = MPI2_MANUFACTURING10_PAGEVERSION;
+	cm->cm_desc.Default.RequestFlags = MPI2_REQ_DESCRIPT_FLAGS_DEFAULT_TYPE;
+	cm->cm_data = NULL;
+	error = mps_request_polled(sc, cm);
+	reply = (MPI2_CONFIG_REPLY *)cm->cm_reply;
+	if (error || (reply == NULL)) {
+		/* FIXME */
+		/* If the poll returns error then we need to do diag reset */ 
+		printf("%s: poll for header completed with error %d",
+		    __func__, error);
+		error = ENXIO;
+		goto out;
+	}
+	ioc_status = le16toh(reply->IOCStatus) & MPI2_IOCSTATUS_MASK;
+	bcopy(reply, mpi_reply, sizeof(MPI2_CONFIG_REPLY));
+	if (ioc_status != MPI2_IOCSTATUS_SUCCESS) {
+		/* FIXME */
+		/* If the poll returns error then we need to do diag reset */ 
+		printf("%s: header read with error; iocstatus = 0x%x\n",
+		    __func__, ioc_status);
+		error = ENXIO;
+		goto out;
+	}
+	/* We have to do free and alloc for the reply-free and reply-post
+	 * counters to match - Need to review the reply FIFO handling.
+	 */
+	mps_free_command(sc, cm);
+	
+	if ((cm = mps_alloc_command(sc)) == NULL) {
+		printf("%s: command alloc failed @ line %d\n", __func__,
+		    __LINE__);
+		error = EBUSY;
+		goto out;
+	}
+	request = (MPI2_CONFIG_REQUEST *)cm->cm_req;
+	bzero(request, sizeof(MPI2_CONFIG_REQUEST));
+	request->Function = MPI2_FUNCTION_CONFIG;
+	request->Action = MPI2_CONFIG_ACTION_PAGE_READ_CURRENT;
+	request->Header.PageType = MPI2_CONFIG_PAGETYPE_MANUFACTURING;
+	request->Header.PageNumber = 10;
+	request->Header.PageVersion = MPI2_MANUFACTURING10_PAGEVERSION;
+	request->Header.PageLength = mpi_reply->Header.PageLength;
+	cm->cm_length =  le16toh(mpi_reply->Header.PageLength) * 4;
+	cm->cm_sge = &request->PageBufferSGE;
+	cm->cm_sglsize = sizeof(MPI2_SGE_IO_UNION);
+	cm->cm_flags = MPS_CM_FLAGS_SGE_SIMPLE | MPS_CM_FLAGS_DATAIN;
+	cm->cm_desc.Default.RequestFlags = MPI2_REQ_DESCRIPT_FLAGS_DEFAULT_TYPE;
+	page = malloc(MPS_MAN_PAGE10_SIZE, M_MPT2, M_ZERO | M_NOWAIT);
+	if (!page) {
+		printf("%s: page alloc failed\n", __func__);
+		error = ENOMEM;
+		goto out;
+	}
+	cm->cm_data = page;
+	error = mps_request_polled(sc, cm);
+	reply = (MPI2_CONFIG_REPLY *)cm->cm_reply;
+	if (error || (reply == NULL)) {
+		/* FIXME */
+		/* If the poll returns error then we need to do diag reset */ 
+		printf("%s: poll for page completed with error %d",
+		    __func__, error);
+		error = ENXIO;
+		goto out;
+	}
+	ioc_status = le16toh(reply->IOCStatus) & MPI2_IOCSTATUS_MASK;
+	bcopy(reply, mpi_reply, sizeof(MPI2_CONFIG_REPLY));
+	if (ioc_status != MPI2_IOCSTATUS_SUCCESS) {
+		/* FIXME */
+		/* If the poll returns error then we need to do diag reset */ 
+		printf("%s: page read with error; iocstatus = 0x%x\n",
+		    __func__, ioc_status);
+		error = ENXIO;
+		goto out;
+	}
+
+	/*
+	 * If OEM ID is unknown, fail the request.
+	 */
+	sc->WD_hide_expose = MPS_WD_HIDE_ALWAYS;
+	OEM_Value = (uint8_t)(page->ProductSpecificInfo & 0x000000FF);
+	if (OEM_Value != MPS_WD_LSI_OEM) {
+		mps_dprint(sc, MPS_FAULT, "Unknown OEM value for WarpDrive "
+		    "(0x%x)\n", OEM_Value);
+		error = ENXIO;
+		goto out;
+	}
+
+	/*
+	 * Set the phys disks hide/expose value.
+	 */
+	pPS_info = &page->ProductSpecificInfo;
+	sc->WD_hide_expose = (uint8_t)(pPS_info[5]);
+	sc->WD_hide_expose &= MPS_WD_HIDE_EXPOSE_MASK;
+	if ((sc->WD_hide_expose != MPS_WD_HIDE_ALWAYS) &&
+	    (sc->WD_hide_expose != MPS_WD_EXPOSE_ALWAYS) &&
+	    (sc->WD_hide_expose != MPS_WD_HIDE_IF_VOLUME)) {
+		mps_dprint(sc, MPS_FAULT, "Unknown value for WarpDrive "
+		    "hide/expose: 0x%x\n", sc->WD_hide_expose);
+		error = ENXIO;
+		goto out;
+	}
+
+out:
+	free(page, M_MPT2);
+	if (cm)
+		mps_free_command(sc, cm);
+	return (error);
+}
+
+/**
+ * mps_base_static_config_pages - static start of day config pages.
+ * @sc: per adapter object
+ *
+ * Return nothing.
+ */
+void
+mps_base_static_config_pages(struct mps_softc *sc)
+{
+	Mpi2ConfigReply_t	mpi_reply;
+	int			retry;
+
+	retry = 0;
+	while (mps_config_get_ioc_pg8(sc, &mpi_reply, &sc->ioc_pg8)) {
+		retry++;
+		if (retry > 5) {
+			/* We need to Handle this situation */
+			/*FIXME*/
+			break;
+		}
+	}
+}
+
+/**
+ * mps_wd_config_pages - get info required to support WarpDrive.  This needs to
+ *    be called after discovery is complete to guarentee that IR info is there.
+ * @sc: per adapter object
+ *
+ * Return nothing.
+ */
+void
+mps_wd_config_pages(struct mps_softc *sc)
+{
+	Mpi2ConfigReply_t	mpi_reply;
+	pMpi2RaidVolPage0_t	raid_vol_pg0 = NULL;
+	Mpi2RaidPhysDiskPage0_t	phys_disk_pg0;
+	pMpi2RaidVol0PhysDisk_t	pRVPD;
+	uint32_t		stripe_size, phys_disk_page_address;
+	uint16_t		block_size;
+	uint8_t			index, stripe_exp = 0, block_exp = 0;
+
+	/*
+	 * Get the WD settings from manufacturing page 10 if using a WD HBA.
+	 * This will be used to determine if phys disks should always be
+	 * hidden, hidden only if part of a WD volume, or never hidden.  Also,
+	 * get the WD RAID Volume info and fail if volume does not exist or if
+	 * volume does not meet the requirements for a WD volume.  No retry
+	 * here.  Just default to HIDE ALWAYS if man Page10 fails, or clear WD
+	 * Valid flag if Volume info fails.
+	 */
+	sc->WD_valid_config = FALSE;
+	if (sc->mps_flags & MPS_FLAGS_WD_AVAILABLE) {
+		if (mps_config_get_man_pg10(sc, &mpi_reply)) {
+			mps_dprint(sc, MPS_FAULT,
+			    "mps_config_get_man_pg10 failed! Using 0 (Hide "
+			    "Always) for WarpDrive hide/expose value.\n");
+			sc->WD_hide_expose = MPS_WD_HIDE_ALWAYS;
+		}
+
+		/*
+		 * Get first RAID Volume Page0 using GET_NEXT_HANDLE.
+		 */
+		raid_vol_pg0 = malloc(sizeof(Mpi2RaidVolPage0_t) +
+		    (sizeof(Mpi2RaidVol0PhysDisk_t) * MPS_MAX_DISKS_IN_VOL),
+		    M_MPT2, M_ZERO | M_NOWAIT);
+		if (!raid_vol_pg0) {
+			printf("%s: page alloc failed\n", __func__);
+			goto out;
+		}
+
+		if (mps_config_get_raid_volume_pg0(sc, &mpi_reply, raid_vol_pg0,
+		    0x0000FFFF)) {
+			mps_dprint(sc, MPS_INFO,
+			    "mps_config_get_raid_volume_pg0 failed! Assuming "
+			    "WarpDrive IT mode.\n");
+			goto out;
+		}
+
+		/*
+		 * Check for valid WD configuration:
+		 *   volume type is RAID0
+		 *   number of phys disks in the volume is no more than 8
+		 */
+		if ((raid_vol_pg0->VolumeType != MPI2_RAID_VOL_TYPE_RAID0) ||
+		    (raid_vol_pg0->NumPhysDisks > 8)) {
+			mps_dprint(sc, MPS_FAULT,
+			    "Invalid WarpDrive configuration. Direct Drive I/O "
+			    "will not be used.\n");
+			goto out;
+		}
+
+		/*
+		 * Save the WD RAID data to be used during WD I/O.
+		 */
+		sc->DD_max_lba = le64toh((uint64_t)raid_vol_pg0->MaxLBA.High <<
+		    32 | (uint64_t)raid_vol_pg0->MaxLBA.Low);
+		sc->DD_num_phys_disks = raid_vol_pg0->NumPhysDisks;
+		sc->DD_dev_handle = raid_vol_pg0->DevHandle;
+		sc->DD_stripe_size = raid_vol_pg0->StripeSize;
+		sc->DD_block_size = raid_vol_pg0->BlockSize;
+
+		/*
+		 * Find power of 2 of stripe size and set this as the exponent.
+		 * Fail if stripe size is 0.
+		 */
+		stripe_size = raid_vol_pg0->StripeSize;
+		for (index = 0; index < 32; index++) {
+			if (stripe_size & 1)
+				break;
+			stripe_exp++;
+			stripe_size >>= 1;
+		}
+		if (index == 32) {
+			mps_dprint(sc, MPS_FAULT,
+			    "RAID Volume's stripe size is 0. Direct Drive I/O "
+			    "will not be used.\n");
+			goto out;
+		}
+		sc->DD_stripe_exponent = stripe_exp;
+
+		/*
+		 * Find power of 2 of block size and set this as the exponent.
+		 * Fail if block size is 0.
+		 */
+		block_size = raid_vol_pg0->BlockSize;
+		for (index = 0; index < 16; index++) {
+			if (block_size & 1)
+				break;
+			block_exp++;
+			block_size >>= 1;
+		}
+		if (index == 16) {
+			mps_dprint(sc, MPS_FAULT,
+			    "RAID Volume's block size is 0. Direct Drive I/O "
+			    "will not be used.\n");
+			goto out;
+		}
+		sc->DD_block_exponent = block_exp;
+
+		/*
+		 * Loop through all of the volume's Phys Disks to map the phys
+		 * disk number into the columm map.  This is used during Direct
+		 * Drive I/O to send the request to the correct SSD.
+		 */
+		pRVPD = (pMpi2RaidVol0PhysDisk_t)&raid_vol_pg0->PhysDisk;
+		for (index = 0; index < raid_vol_pg0->NumPhysDisks; index++) {
+			sc->DD_column_map[pRVPD->PhysDiskMap].phys_disk_num =
+			    pRVPD->PhysDiskNum;
+			pRVPD++;
+		}
+
+		/*
+		 * Get second RAID Volume Page0 using previous handle.  This
+		 * page should not exist.  If it does, must not proceed with WD
+		 * handling.
+		 */
+		if (mps_config_get_raid_volume_pg0(sc, &mpi_reply,
+		    raid_vol_pg0, (u32)raid_vol_pg0->DevHandle)) {
+			if (mpi_reply.IOCStatus !=
+			    MPI2_IOCSTATUS_CONFIG_INVALID_PAGE) {
+				mps_dprint(sc, MPS_FAULT,
+				    "Multiple RAID Volume Page0! Direct Drive "
+				    "I/O will not be used.\n");
+				goto out;
+			}
+		} else {
+			mps_dprint(sc, MPS_FAULT,
+			    "Multiple volumes! Direct Drive I/O will not be "
+			    "used.\n");
+			goto out;
+		}
+
+		/*
+		 * Get RAID Volume Phys Disk Page 0 for all SSDs in the volume.
+		 */
+		for (index = 0; index < raid_vol_pg0->NumPhysDisks; index++) {
+			phys_disk_page_address =
+			    MPI2_PHYSDISK_PGAD_FORM_PHYSDISKNUM +
+			    sc->DD_column_map[index].phys_disk_num;
+			if (mps_config_get_raid_pd_pg0(sc, &mpi_reply,
+			    &phys_disk_pg0, phys_disk_page_address)) {
+				mps_dprint(sc, MPS_FAULT,
+				    "mps_config_get_raid_pd_pg0 failed! Direct "
+				    "Drive I/O will not be used.\n");
+				goto out;
+			}
+			if (phys_disk_pg0.DevHandle == 0xFFFF) {
+				mps_dprint(sc, MPS_FAULT,
+				    "Invalid Phys Disk DevHandle! Direct Drive "
+				    "I/O will not be used.\n");
+				goto out;
+			}
+			sc->DD_column_map[index].dev_handle =
+			    phys_disk_pg0.DevHandle;
+		}
+		sc->WD_valid_config = TRUE;
+out:
+		if (raid_vol_pg0)
+			free(raid_vol_pg0, M_MPT2);
+	}
+}
+
+/**
+ * mps_config_get_dpm_pg0 - obtain driver persistent mapping page0
+ * @sc: per adapter object
+ * @mpi_reply: reply mf payload returned from firmware
+ * @config_page: contents of the config page
+ * @sz: size of buffer passed in config_page
+ * Context: sleep.
+ *
+ * Returns 0 for success, non-zero for failure.
+ */
+int
+mps_config_get_dpm_pg0(struct mps_softc *sc, Mpi2ConfigReply_t *mpi_reply,
+    Mpi2DriverMappingPage0_t *config_page, u16 sz)
+{
+	MPI2_CONFIG_REQUEST *request;
+	MPI2_CONFIG_REPLY *reply;
+	struct mps_command *cm;
+	Mpi2DriverMappingPage0_t *page = NULL;
+	int error = 0;
+	u16 ioc_status;
+
+	mps_dprint(sc, MPS_TRACE, "%s\n", __func__);
+
+	memset(config_page, 0, sz);
+	if ((cm = mps_alloc_command(sc)) == NULL) {
+		printf("%s: command alloc failed @ line %d\n", __func__,
+		    __LINE__);
+		error = EBUSY;
+		goto out;
+	}
+	request = (MPI2_CONFIG_REQUEST *)cm->cm_req;
+	bzero(request, sizeof(MPI2_CONFIG_REQUEST));
+	request->Function = MPI2_FUNCTION_CONFIG;
+	request->Action = MPI2_CONFIG_ACTION_PAGE_HEADER;
+	request->Header.PageType = MPI2_CONFIG_PAGETYPE_EXTENDED;
+	request->ExtPageType = MPI2_CONFIG_EXTPAGETYPE_DRIVER_MAPPING;
+	request->Header.PageNumber = 0;
+	request->Header.PageVersion = MPI2_DRIVERMAPPING0_PAGEVERSION;
+	request->PageAddress = sc->max_dpm_entries <<
+	    MPI2_DPM_PGAD_ENTRY_COUNT_SHIFT;
+	cm->cm_desc.Default.RequestFlags = MPI2_REQ_DESCRIPT_FLAGS_DEFAULT_TYPE;
+	cm->cm_data = NULL;
+	error = mps_request_polled(sc, cm);
+	reply = (MPI2_CONFIG_REPLY *)cm->cm_reply;
+	if (error || (reply == NULL)) {
+		/* FIXME */
+		/* If the poll returns error then we need to do diag reset */ 
+		printf("%s: poll for header completed with error %d",
+		    __func__, error);
+		error = ENXIO;
+		goto out;
+	}
+	ioc_status = le16toh(reply->IOCStatus) & MPI2_IOCSTATUS_MASK;
+	bcopy(reply, mpi_reply, sizeof(MPI2_CONFIG_REPLY));
+	if (ioc_status != MPI2_IOCSTATUS_SUCCESS) {
+		/* FIXME */
+		/* If the poll returns error then we need to do diag reset */ 
+		printf("%s: header read with error; iocstatus = 0x%x\n",
+		    __func__, ioc_status);
+		error = ENXIO;
+		goto out;
+	}
+	/* We have to do free and alloc for the reply-free and reply-post
+	 * counters to match - Need to review the reply FIFO handling.
+	 */
+	mps_free_command(sc, cm);
+
+	if ((cm = mps_alloc_command(sc)) == NULL) {
+		printf("%s: command alloc failed @ line %d\n", __func__,
+		    __LINE__);
+		error = EBUSY;
+		goto out;
+	}
+	request = (MPI2_CONFIG_REQUEST *)cm->cm_req;
+	bzero(request, sizeof(MPI2_CONFIG_REQUEST));
+	request->Function = MPI2_FUNCTION_CONFIG;
+	request->Action = MPI2_CONFIG_ACTION_PAGE_READ_NVRAM;
+	request->Header.PageType = MPI2_CONFIG_PAGETYPE_EXTENDED;
+	request->ExtPageType = MPI2_CONFIG_EXTPAGETYPE_DRIVER_MAPPING;
+	request->Header.PageNumber = 0;
+	request->Header.PageVersion = MPI2_DRIVERMAPPING0_PAGEVERSION;
+	request->PageAddress = sc->max_dpm_entries <<
+	    MPI2_DPM_PGAD_ENTRY_COUNT_SHIFT;
+	request->ExtPageLength = mpi_reply->ExtPageLength;
+	cm->cm_length =  le16toh(request->ExtPageLength) * 4;
+	cm->cm_sge = &request->PageBufferSGE;
+	cm->cm_sglsize = sizeof(MPI2_SGE_IO_UNION);
+	cm->cm_flags = MPS_CM_FLAGS_SGE_SIMPLE | MPS_CM_FLAGS_DATAIN;
+	cm->cm_desc.Default.RequestFlags = MPI2_REQ_DESCRIPT_FLAGS_DEFAULT_TYPE;
+	page = malloc(cm->cm_length, M_MPT2, M_ZERO|M_NOWAIT);
+	if (!page) {
+		printf("%s: page alloc failed\n", __func__);
+		error = ENOMEM;
+		goto out;
+	}
+	cm->cm_data = page;
+	error = mps_request_polled(sc, cm);
+	reply = (MPI2_CONFIG_REPLY *)cm->cm_reply;
+	if (error || (reply == NULL)) {
+		/* FIXME */
+		/* If the poll returns error then we need to do diag reset */ 
+		printf("%s: poll for page completed with error %d",
+		    __func__, error);
+		error = ENXIO;
+		goto out;
+	}
+	ioc_status = le16toh(reply->IOCStatus) & MPI2_IOCSTATUS_MASK;
+	bcopy(reply, mpi_reply, sizeof(MPI2_CONFIG_REPLY));
+	if (ioc_status != MPI2_IOCSTATUS_SUCCESS) {
+		/* FIXME */
+		/* If the poll returns error then we need to do diag reset */ 
+		printf("%s: page read with error; iocstatus = 0x%x\n",
+		    __func__, ioc_status);
+		error = ENXIO;
+		goto out;
+	}
+	bcopy(page, config_page, MIN(cm->cm_length, sz));
+out:
+	free(page, M_MPT2);
+	if (cm)
+		mps_free_command(sc, cm);
+	return (error);
+}
+
+/**
+ * mps_config_set_dpm_pg0 - write an entry in driver persistent mapping page0
+ * @sc: per adapter object
+ * @mpi_reply: reply mf payload returned from firmware
+ * @config_page: contents of the config page
+ * @entry_idx: entry index in DPM Page0 to be modified
+ * Context: sleep.
+ *
+ * Returns 0 for success, non-zero for failure.
+ */
+
+int mps_config_set_dpm_pg0(struct mps_softc *sc, Mpi2ConfigReply_t *mpi_reply,
+    Mpi2DriverMappingPage0_t *config_page, u16 entry_idx)
+{
+	MPI2_CONFIG_REQUEST *request;
+	MPI2_CONFIG_REPLY *reply;
+	struct mps_command *cm;
+	MPI2_CONFIG_PAGE_DRIVER_MAPPING_0 *page = NULL;	
+	int error = 0;
+	u16 ioc_status;
+
+	mps_dprint(sc, MPS_TRACE, "%s\n", __func__);
+
+	if ((cm = mps_alloc_command(sc)) == NULL) {
+		printf("%s: command alloc failed @ line %d\n", __func__,
+		    __LINE__);
+		error = EBUSY;
+		goto out;
+	}
+	request = (MPI2_CONFIG_REQUEST *)cm->cm_req;
+	bzero(request, sizeof(MPI2_CONFIG_REQUEST));
+	request->Function = MPI2_FUNCTION_CONFIG;
+	request->Action = MPI2_CONFIG_ACTION_PAGE_HEADER;
+	request->Header.PageType = MPI2_CONFIG_PAGETYPE_EXTENDED;
+	request->ExtPageType = MPI2_CONFIG_EXTPAGETYPE_DRIVER_MAPPING;
+	request->Header.PageNumber = 0;
+	request->Header.PageVersion = MPI2_DRIVERMAPPING0_PAGEVERSION;
+	request->PageAddress = 1 << MPI2_DPM_PGAD_ENTRY_COUNT_SHIFT;
+	request->PageAddress |= htole16(entry_idx);
+	cm->cm_desc.Default.RequestFlags = MPI2_REQ_DESCRIPT_FLAGS_DEFAULT_TYPE;
+	cm->cm_data = NULL;
+	error = mps_request_polled(sc, cm);
+	reply = (MPI2_CONFIG_REPLY *)cm->cm_reply;
+	if (error || (reply == NULL)) {
+		/* FIXME */
+		/* If the poll returns error then we need to do diag reset */ 
+		printf("%s: poll for header completed with error %d",
+		    __func__, error);
+		error = ENXIO;
+		goto out;
+	}
+	ioc_status = le16toh(reply->IOCStatus) & MPI2_IOCSTATUS_MASK;
+	bcopy(reply, mpi_reply, sizeof(MPI2_CONFIG_REPLY));
+	if (ioc_status != MPI2_IOCSTATUS_SUCCESS) {
+		/* FIXME */
+		/* If the poll returns error then we need to do diag reset */ 
+		printf("%s: header read with error; iocstatus = 0x%x\n",
+		    __func__, ioc_status);
+		error = ENXIO;
+		goto out;
+	}
+	/* We have to do free and alloc for the reply-free and reply-post
+	 * counters to match - Need to review the reply FIFO handling.
+	 */	
+	mps_free_command(sc, cm);
+
+	if ((cm = mps_alloc_command(sc)) == NULL) {
+		printf("%s: command alloc failed @ line %d\n", __func__,
+		    __LINE__);
+		error = EBUSY;
+		goto out;
+	}
+	request = (MPI2_CONFIG_REQUEST *)cm->cm_req;
+	bzero(request, sizeof(MPI2_CONFIG_REQUEST));
+	request->Function = MPI2_FUNCTION_CONFIG;
+	request->Action = MPI2_CONFIG_ACTION_PAGE_WRITE_NVRAM;
+	request->Header.PageType = MPI2_CONFIG_PAGETYPE_EXTENDED;
+	request->ExtPageType = MPI2_CONFIG_EXTPAGETYPE_DRIVER_MAPPING;
+	request->Header.PageNumber = 0;
+	request->Header.PageVersion = MPI2_DRIVERMAPPING0_PAGEVERSION;
+	request->ExtPageLength = mpi_reply->ExtPageLength;
+	request->PageAddress = 1 << MPI2_DPM_PGAD_ENTRY_COUNT_SHIFT;
+	request->PageAddress |= htole16(entry_idx);
+	cm->cm_length = le16toh(mpi_reply->ExtPageLength) * 4;
+	cm->cm_sge = &request->PageBufferSGE;
+	cm->cm_sglsize = sizeof(MPI2_SGE_IO_UNION);
+	cm->cm_flags = MPS_CM_FLAGS_SGE_SIMPLE | MPS_CM_FLAGS_DATAOUT;
+	cm->cm_desc.Default.RequestFlags = MPI2_REQ_DESCRIPT_FLAGS_DEFAULT_TYPE;
+	page = malloc(cm->cm_length, M_MPT2, M_ZERO | M_NOWAIT);
+	if (!page) {
+		printf("%s: page alloc failed\n", __func__);
+		error = ENOMEM;
+		goto out;
+	}
+	bcopy(config_page, page, MIN(cm->cm_length, 
+	    (sizeof(Mpi2DriverMappingPage0_t))));
+	cm->cm_data = page;
+	error = mps_request_polled(sc, cm);
+	reply = (MPI2_CONFIG_REPLY *)cm->cm_reply;
+	if (error || (reply == NULL)) {
+		/* FIXME */
+		/* If the poll returns error then we need to do diag reset */ 
+		printf("%s: poll for page completed with error %d",
+		    __func__, error);
+		error = ENXIO;
+		goto out;
+	}
+	ioc_status = le16toh(reply->IOCStatus) & MPI2_IOCSTATUS_MASK;
+	bcopy(reply, mpi_reply, sizeof(MPI2_CONFIG_REPLY));
+	if (ioc_status != MPI2_IOCSTATUS_SUCCESS) {
+		/* FIXME */
+		/* If the poll returns error then we need to do diag reset */ 
+		printf("%s: page written with error; iocstatus = 0x%x\n",
+		    __func__, ioc_status);
+		error = ENXIO;
+		goto out;
+	}
+out:
+	free(page, M_MPT2);
+	if (cm)
+		mps_free_command(sc, cm);
+	return (error);
+}
+
+/**
+ * mps_config_get_sas_device_pg0 - obtain sas device page 0
+ * @sc: per adapter object
+ * @mpi_reply: reply mf payload returned from firmware
+ * @config_page: contents of the config page
+ * @form: GET_NEXT_HANDLE or HANDLE
+ * @handle: device handle
+ * Context: sleep.
+ *
+ * Returns 0 for success, non-zero for failure.
+ */
+int
+mps_config_get_sas_device_pg0(struct mps_softc *sc, Mpi2ConfigReply_t
+    *mpi_reply, Mpi2SasDevicePage0_t *config_page, u32 form, u16 handle)
+{
+	MPI2_CONFIG_REQUEST *request;
+	MPI2_CONFIG_REPLY *reply;
+	struct mps_command *cm;
+	Mpi2SasDevicePage0_t *page = NULL;
+	int error = 0;
+	u16 ioc_status;
+
+	mps_dprint(sc, MPS_TRACE, "%s\n", __func__);
+
+	if ((cm = mps_alloc_command(sc)) == NULL) {
+		printf("%s: command alloc failed @ line %d\n", __func__,
+		    __LINE__);
+		error = EBUSY;
+		goto out;
+	}
+	request = (MPI2_CONFIG_REQUEST *)cm->cm_req;
+	bzero(request, sizeof(MPI2_CONFIG_REQUEST));
+	request->Function = MPI2_FUNCTION_CONFIG;
+	request->Action = MPI2_CONFIG_ACTION_PAGE_HEADER;
+	request->Header.PageType = MPI2_CONFIG_PAGETYPE_EXTENDED;
+	request->ExtPageType = MPI2_CONFIG_EXTPAGETYPE_SAS_DEVICE;
+	request->Header.PageNumber = 0;
+	request->Header.PageVersion = MPI2_SASDEVICE0_PAGEVERSION;
+	cm->cm_desc.Default.RequestFlags = MPI2_REQ_DESCRIPT_FLAGS_DEFAULT_TYPE;
+	cm->cm_data = NULL;
+	error = mps_request_polled(sc, cm);
+	reply = (MPI2_CONFIG_REPLY *)cm->cm_reply;
+	if (error || (reply == NULL)) {
+		/* FIXME */
+		/* If the poll returns error then we need to do diag reset */ 
+		printf("%s: poll for header completed with error %d",
+		    __func__, error);
+		error = ENXIO;
+		goto out;
+	}
+	ioc_status = le16toh(reply->IOCStatus) & MPI2_IOCSTATUS_MASK;
+	bcopy(reply, mpi_reply, sizeof(MPI2_CONFIG_REPLY));
+	if (ioc_status != MPI2_IOCSTATUS_SUCCESS) {
+		/* FIXME */
+		/* If the poll returns error then we need to do diag reset */ 
+		printf("%s: header read with error; iocstatus = 0x%x\n",
+		    __func__, ioc_status);
+		error = ENXIO;
+		goto out;
+	}
+	/* We have to do free and alloc for the reply-free and reply-post
+	 * counters to match - Need to review the reply FIFO handling.
+	 */
+	mps_free_command(sc, cm);
+
+	if ((cm = mps_alloc_command(sc)) == NULL) {
+		printf("%s: command alloc failed @ line %d\n", __func__,
+		    __LINE__);
+		error = EBUSY;
+		goto out;
+	}
+	request = (MPI2_CONFIG_REQUEST *)cm->cm_req;
+	bzero(request, sizeof(MPI2_CONFIG_REQUEST));
+	request->Function = MPI2_FUNCTION_CONFIG;
+	request->Action = MPI2_CONFIG_ACTION_PAGE_READ_CURRENT;
+	request->Header.PageType = MPI2_CONFIG_PAGETYPE_EXTENDED;
+	request->ExtPageType = MPI2_CONFIG_EXTPAGETYPE_SAS_DEVICE;
+	request->Header.PageNumber = 0;
+	request->Header.PageVersion = MPI2_SASDEVICE0_PAGEVERSION;
+	request->ExtPageLength = mpi_reply->ExtPageLength;
+	request->PageAddress = htole32(form | handle);
+	cm->cm_length = le16toh(mpi_reply->ExtPageLength) * 4;
+	cm->cm_sge = &request->PageBufferSGE;
+	cm->cm_sglsize = sizeof(MPI2_SGE_IO_UNION);
+	cm->cm_flags = MPS_CM_FLAGS_SGE_SIMPLE | MPS_CM_FLAGS_DATAIN;
+	cm->cm_desc.Default.RequestFlags = MPI2_REQ_DESCRIPT_FLAGS_DEFAULT_TYPE;
+	page = malloc(cm->cm_length, M_MPT2, M_ZERO | M_NOWAIT);
+	if (!page) {
+		printf("%s: page alloc failed\n", __func__);
+		error = ENOMEM;
+		goto out;
+	}
+	cm->cm_data = page;
+
+	error = mps_request_polled(sc, cm);
+	reply = (MPI2_CONFIG_REPLY *)cm->cm_reply;
+	if (error || (reply == NULL)) {
+		/* FIXME */
+		/* If the poll returns error then we need to do diag reset */ 
+		printf("%s: poll for page completed with error %d",
+		    __func__, error);
+		error = ENXIO;
+		goto out;
+	}
+	ioc_status = le16toh(reply->IOCStatus) & MPI2_IOCSTATUS_MASK;
+	bcopy(reply, mpi_reply, sizeof(MPI2_CONFIG_REPLY));
+	if (ioc_status != MPI2_IOCSTATUS_SUCCESS) {
+		/* FIXME */
+		/* If the poll returns error then we need to do diag reset */ 
+		printf("%s: page read with error; iocstatus = 0x%x\n",
+		    __func__, ioc_status);
+		error = ENXIO;
+		goto out;
+	}
+	bcopy(page, config_page, MIN(cm->cm_length, 
+	    sizeof(Mpi2SasDevicePage0_t)));
+out:
+	free(page, M_MPT2);
+	if (cm)
+		mps_free_command(sc, cm);
+	return (error);
+}
+
+/**
+ * mps_config_get_bios_pg3 - obtain BIOS page 3
+ * @sc: per adapter object
+ * @mpi_reply: reply mf payload returned from firmware
+ * @config_page: contents of the config page
+ * Context: sleep.
+ *
+ * Returns 0 for success, non-zero for failure.
+ */
+int
+mps_config_get_bios_pg3(struct mps_softc *sc, Mpi2ConfigReply_t *mpi_reply,
+    Mpi2BiosPage3_t *config_page)
+{
+	MPI2_CONFIG_REQUEST *request;
+	MPI2_CONFIG_REPLY *reply;
+	struct mps_command *cm;
+	Mpi2BiosPage3_t *page = NULL;
+	int error = 0;
+	u16 ioc_status;
+
+	mps_dprint(sc, MPS_TRACE, "%s\n", __func__);
+
+	if ((cm = mps_alloc_command(sc)) == NULL) {
+		printf("%s: command alloc failed @ line %d\n", __func__,
+		    __LINE__);
+		error = EBUSY;
+		goto out;
+	}
+	request = (MPI2_CONFIG_REQUEST *)cm->cm_req;
+	bzero(request, sizeof(MPI2_CONFIG_REQUEST));
+	request->Function = MPI2_FUNCTION_CONFIG;
+	request->Action = MPI2_CONFIG_ACTION_PAGE_HEADER;
+	request->Header.PageType = MPI2_CONFIG_PAGETYPE_BIOS;
+	request->Header.PageNumber = 3;
+	request->Header.PageVersion = MPI2_BIOSPAGE3_PAGEVERSION;
+	cm->cm_desc.Default.RequestFlags = MPI2_REQ_DESCRIPT_FLAGS_DEFAULT_TYPE;
+	cm->cm_data = NULL;
+	error = mps_request_polled(sc, cm);
+	reply = (MPI2_CONFIG_REPLY *)cm->cm_reply;
+	if (error || (reply == NULL)) {
+		/* FIXME */
+		/* If the poll returns error then we need to do diag reset */ 
+		printf("%s: poll for header completed with error %d",
+		    __func__, error);
+		error = ENXIO;
+		goto out;
+	}
+	ioc_status = le16toh(reply->IOCStatus) & MPI2_IOCSTATUS_MASK;
+	bcopy(reply, mpi_reply, sizeof(MPI2_CONFIG_REPLY));
+	if (ioc_status != MPI2_IOCSTATUS_SUCCESS) {
+		/* FIXME */
+		/* If the poll returns error then we need to do diag reset */ 
+		printf("%s: header read with error; iocstatus = 0x%x\n",
+		    __func__, ioc_status);
+		error = ENXIO;
+		goto out;
+	}
+	/* We have to do free and alloc for the reply-free and reply-post
+	 * counters to match - Need to review the reply FIFO handling.
+	 */
+	mps_free_command(sc, cm);
+
+	if ((cm = mps_alloc_command(sc)) == NULL) {
+		printf("%s: command alloc failed @ line %d\n", __func__,
+		    __LINE__);
+		error = EBUSY;
+		goto out;
+	}
+	request = (MPI2_CONFIG_REQUEST *)cm->cm_req;
+	bzero(request, sizeof(MPI2_CONFIG_REQUEST));
+	request->Function = MPI2_FUNCTION_CONFIG;
+	request->Action = MPI2_CONFIG_ACTION_PAGE_READ_CURRENT;
+	request->Header.PageType = MPI2_CONFIG_PAGETYPE_BIOS;
+	request->Header.PageNumber = 3;
+	request->Header.PageVersion = MPI2_BIOSPAGE3_PAGEVERSION;
+	request->Header.PageLength = mpi_reply->Header.PageLength;
+	cm->cm_length = le16toh(mpi_reply->Header.PageLength) * 4;
+	cm->cm_sge = &request->PageBufferSGE;
+	cm->cm_sglsize = sizeof(MPI2_SGE_IO_UNION);
+	cm->cm_flags = MPS_CM_FLAGS_SGE_SIMPLE | MPS_CM_FLAGS_DATAIN;
+	cm->cm_desc.Default.RequestFlags = MPI2_REQ_DESCRIPT_FLAGS_DEFAULT_TYPE;
+	page = malloc(cm->cm_length, M_MPT2, M_ZERO | M_NOWAIT);
+	if (!page) {
+		printf("%s: page alloc failed\n", __func__);
+		error = ENOMEM;
+		goto out;
+	}
+	cm->cm_data = page;
+
+	error = mps_request_polled(sc, cm);
+	reply = (MPI2_CONFIG_REPLY *)cm->cm_reply;
+	if (error || (reply == NULL)) {
+		/* FIXME */
+		/* If the poll returns error then we need to do diag reset */ 
+		printf("%s: poll for page completed with error %d",
+		    __func__, error);
+		error = ENXIO;
+		goto out;
+	}
+	ioc_status = le16toh(reply->IOCStatus) & MPI2_IOCSTATUS_MASK;
+	bcopy(reply, mpi_reply, sizeof(MPI2_CONFIG_REPLY));
+	if (ioc_status != MPI2_IOCSTATUS_SUCCESS) {
+		/* FIXME */
+		/* If the poll returns error then we need to do diag reset */ 
+		printf("%s: page read with error; iocstatus = 0x%x\n",
+		    __func__, ioc_status);
+		error = ENXIO;
+		goto out;
+	}
+	bcopy(page, config_page, MIN(cm->cm_length, sizeof(Mpi2BiosPage3_t)));
+out:
+	free(page, M_MPT2);
+	if (cm)
+		mps_free_command(sc, cm);
+	return (error);
+}
+
+/**
+ * mps_config_get_raid_volume_pg0 - obtain raid volume page 0
+ * @sc: per adapter object
+ * @mpi_reply: reply mf payload returned from firmware
+ * @config_page: contents of the config page
+ * @page_address: form and handle value used to get page
+ * Context: sleep.
+ *
+ * Returns 0 for success, non-zero for failure.
+ */
+int
+mps_config_get_raid_volume_pg0(struct mps_softc *sc, Mpi2ConfigReply_t
+    *mpi_reply, Mpi2RaidVolPage0_t *config_page, u32 page_address)
+{
+	MPI2_CONFIG_REQUEST *request;
+	MPI2_CONFIG_REPLY *reply;
+	struct mps_command *cm;
+	Mpi2RaidVolPage0_t *page = NULL;
+	int error = 0;
+	u16 ioc_status;
+
+	mps_dprint(sc, MPS_TRACE, "%s\n", __func__);
+
+	if ((cm = mps_alloc_command(sc)) == NULL) {
+		printf("%s: command alloc failed @ line %d\n", __func__,
+		    __LINE__);
+		error = EBUSY;
+		goto out;
+	}
+	request = (MPI2_CONFIG_REQUEST *)cm->cm_req;
+	bzero(request, sizeof(MPI2_CONFIG_REQUEST));
+	request->Function = MPI2_FUNCTION_CONFIG;
+	request->Action = MPI2_CONFIG_ACTION_PAGE_HEADER;
+	request->Header.PageType = MPI2_CONFIG_PAGETYPE_RAID_VOLUME;
+	request->Header.PageNumber = 0;
+	request->Header.PageVersion = MPI2_RAIDVOLPAGE0_PAGEVERSION;
+	cm->cm_desc.Default.RequestFlags = MPI2_REQ_DESCRIPT_FLAGS_DEFAULT_TYPE;
+	cm->cm_data = NULL;
+	error = mps_request_polled(sc, cm);
+	reply = (MPI2_CONFIG_REPLY *)cm->cm_reply;
+	if (error || (reply == NULL)) {
+		/* FIXME */
+		/* If the poll returns error then we need to do diag reset */ 
+		printf("%s: poll for header completed with error %d",
+		    __func__, error);
+		error = ENXIO;
+		goto out;
+	}
+	ioc_status = le16toh(reply->IOCStatus) & MPI2_IOCSTATUS_MASK;
+	bcopy(reply, mpi_reply, sizeof(MPI2_CONFIG_REPLY));
+	if (ioc_status != MPI2_IOCSTATUS_SUCCESS) {
+		/* FIXME */
+		/* If the poll returns error then we need to do diag reset */ 
+		printf("%s: header read with error; iocstatus = 0x%x\n",
+		    __func__, ioc_status);
+		error = ENXIO;
+		goto out;
+	}
+	/* We have to do free and alloc for the reply-free and reply-post
+	 * counters to match - Need to review the reply FIFO handling.
+	 */
+	mps_free_command(sc, cm);
+
+	if ((cm = mps_alloc_command(sc)) == NULL) {
+		printf("%s: command alloc failed @ line %d\n", __func__,
+		    __LINE__);
+		error = EBUSY;
+		goto out;
+	}
+	request = (MPI2_CONFIG_REQUEST *)cm->cm_req;
+	bzero(request, sizeof(MPI2_CONFIG_REQUEST));
+	request->Function = MPI2_FUNCTION_CONFIG;
+	request->Action = MPI2_CONFIG_ACTION_PAGE_READ_CURRENT;
+	request->Header.PageType = MPI2_CONFIG_PAGETYPE_RAID_VOLUME;
+	request->Header.PageNumber = 0;
+	request->Header.PageLength = mpi_reply->Header.PageLength;
+	request->Header.PageVersion = mpi_reply->Header.PageVersion;
+	request->PageAddress = page_address;
+	cm->cm_length = le16toh(mpi_reply->Header.PageLength) * 4;
+	cm->cm_sge = &request->PageBufferSGE;
+	cm->cm_sglsize = sizeof(MPI2_SGE_IO_UNION);
+	cm->cm_flags = MPS_CM_FLAGS_SGE_SIMPLE | MPS_CM_FLAGS_DATAIN;
+	cm->cm_desc.Default.RequestFlags = MPI2_REQ_DESCRIPT_FLAGS_DEFAULT_TYPE;
+	page = malloc(cm->cm_length, M_MPT2, M_ZERO | M_NOWAIT);
+	if (!page) {
+		printf("%s: page alloc failed\n", __func__);
+		error = ENOMEM;
+		goto out;
+	}
+	cm->cm_data = page;
+
+	error = mps_request_polled(sc, cm);
+	reply = (MPI2_CONFIG_REPLY *)cm->cm_reply;
+	if (error || (reply == NULL)) {
+		/* FIXME */
+		/* If the poll returns error then we need to do diag reset */ 
+		printf("%s: poll for page completed with error %d",
+		    __func__, error);
+		error = ENXIO;
+		goto out;
+	}
+	ioc_status = le16toh(reply->IOCStatus) & MPI2_IOCSTATUS_MASK;
+	bcopy(reply, mpi_reply, sizeof(MPI2_CONFIG_REPLY));
+	if (ioc_status != MPI2_IOCSTATUS_SUCCESS) {
+		/* FIXME */
+		/* If the poll returns error then we need to do diag reset */ 
+		printf("%s: page read with error; iocstatus = 0x%x\n",
+		    __func__, ioc_status);
+		error = ENXIO;
+		goto out;
+	}
+	bcopy(page, config_page, cm->cm_length);
+out:
+	free(page, M_MPT2);
+	if (cm)
+		mps_free_command(sc, cm);
+	return (error);
+}
+
+/**
+ * mps_config_get_raid_volume_pg1 - obtain raid volume page 1
+ * @sc: per adapter object
+ * @mpi_reply: reply mf payload returned from firmware
+ * @config_page: contents of the config page
+ * @form: GET_NEXT_HANDLE or HANDLE
+ * @handle: volume handle
+ * Context: sleep.
+ *
+ * Returns 0 for success, non-zero for failure.
+ */
+int
+mps_config_get_raid_volume_pg1(struct mps_softc *sc, Mpi2ConfigReply_t
+    *mpi_reply, Mpi2RaidVolPage1_t *config_page, u32 form, u16 handle)
+{
+	MPI2_CONFIG_REQUEST *request;
+	MPI2_CONFIG_REPLY *reply;
+	struct mps_command *cm;
+	Mpi2RaidVolPage1_t *page = NULL;
+	int error = 0;
+	u16 ioc_status;
+
+	mps_dprint(sc, MPS_TRACE, "%s\n", __func__);
+
+	if ((cm = mps_alloc_command(sc)) == NULL) {
+		printf("%s: command alloc failed @ line %d\n", __func__,
+		    __LINE__);
+		error = EBUSY;
+		goto out;
+	}
+	request = (MPI2_CONFIG_REQUEST *)cm->cm_req;
+	bzero(request, sizeof(MPI2_CONFIG_REQUEST));
+	request->Function = MPI2_FUNCTION_CONFIG;
+	request->Action = MPI2_CONFIG_ACTION_PAGE_HEADER;
+	request->Header.PageType = MPI2_CONFIG_PAGETYPE_RAID_VOLUME;
+	request->Header.PageNumber = 1;
+	request->Header.PageVersion = MPI2_RAIDVOLPAGE1_PAGEVERSION;
+	cm->cm_desc.Default.RequestFlags = MPI2_REQ_DESCRIPT_FLAGS_DEFAULT_TYPE;
+	cm->cm_data = NULL;
+	error = mps_request_polled(sc, cm);
+	reply = (MPI2_CONFIG_REPLY *)cm->cm_reply;
+	if (error || (reply == NULL)) {
+		/* FIXME */
+		/* If the poll returns error then we need to do diag reset */ 
+		printf("%s: poll for header completed with error %d",
+		    __func__, error);
+		error = ENXIO;
+		goto out;
+	}
+	ioc_status = le16toh(reply->IOCStatus) & MPI2_IOCSTATUS_MASK;
+	bcopy(reply, mpi_reply, sizeof(MPI2_CONFIG_REPLY));
+	if (ioc_status != MPI2_IOCSTATUS_SUCCESS) {
+		/* FIXME */
+		/* If the poll returns error then we need to do diag reset */ 
+		printf("%s: header read with error; iocstatus = 0x%x\n",
+		    __func__, ioc_status);
+		error = ENXIO;
+		goto out;
+	}
+	/* We have to do free and alloc for the reply-free and reply-post
+	 * counters to match - Need to review the reply FIFO handling.
+	 */
+	mps_free_command(sc, cm);
+
+	if ((cm = mps_alloc_command(sc)) == NULL) {
+		printf("%s: command alloc failed @ line %d\n", __func__,
+		    __LINE__);
+		error = EBUSY;
+		goto out;
+	}
+	request = (MPI2_CONFIG_REQUEST *)cm->cm_req;
+	bzero(request, sizeof(MPI2_CONFIG_REQUEST));
+	request->Function = MPI2_FUNCTION_CONFIG;
+	request->Action = MPI2_CONFIG_ACTION_PAGE_READ_CURRENT;
+	request->Header.PageType = MPI2_CONFIG_PAGETYPE_RAID_VOLUME;
+	request->Header.PageNumber = 1;
+	request->Header.PageLength = mpi_reply->Header.PageLength;
+	request->Header.PageVersion = mpi_reply->Header.PageVersion;
+	request->PageAddress = htole32(form | handle);
+	cm->cm_length = le16toh(mpi_reply->Header.PageLength) * 4;
+	cm->cm_sge = &request->PageBufferSGE;
+	cm->cm_sglsize = sizeof(MPI2_SGE_IO_UNION);
+	cm->cm_flags = MPS_CM_FLAGS_SGE_SIMPLE | MPS_CM_FLAGS_DATAIN;
+	cm->cm_desc.Default.RequestFlags = MPI2_REQ_DESCRIPT_FLAGS_DEFAULT_TYPE;
+	page = malloc(cm->cm_length, M_MPT2, M_ZERO | M_NOWAIT);
+	if (!page) {
+		printf("%s: page alloc failed\n", __func__);
+		error = ENOMEM;
+		goto out;
+	}
+	cm->cm_data = page;
+
+	error = mps_request_polled(sc, cm);
+	reply = (MPI2_CONFIG_REPLY *)cm->cm_reply;
+	if (error || (reply == NULL)) {
+		/* FIXME */
+		/* If the poll returns error then we need to do diag reset */ 
+		printf("%s: poll for page completed with error %d",
+		    __func__, error);
+		error = ENXIO;
+		goto out;
+	}
+	ioc_status = le16toh(reply->IOCStatus) & MPI2_IOCSTATUS_MASK;
+	bcopy(reply, mpi_reply, sizeof(MPI2_CONFIG_REPLY));
+	if (ioc_status != MPI2_IOCSTATUS_SUCCESS) {
+		/* FIXME */
+		/* If the poll returns error then we need to do diag reset */ 
+		printf("%s: page read with error; iocstatus = 0x%x\n",
+		    __func__, ioc_status);
+		error = ENXIO;
+		goto out;
+	}
+	bcopy(page, config_page, MIN(cm->cm_length,
+	    sizeof(Mpi2RaidVolPage1_t)));
+out:
+	free(page, M_MPT2);
+	if (cm)
+		mps_free_command(sc, cm);
+	return (error);
+}
+
+/**
+ * mps_config_get_volume_wwid - returns wwid given the volume handle
+ * @sc: per adapter object
+ * @volume_handle: volume handle
+ * @wwid: volume wwid
+ * Context: sleep.
+ *
+ * Returns 0 for success, non-zero for failure.
+ */
+int
+mps_config_get_volume_wwid(struct mps_softc *sc, u16 volume_handle, u64 *wwid)
+{
+	Mpi2ConfigReply_t mpi_reply;
+	Mpi2RaidVolPage1_t raid_vol_pg1;
+
+	*wwid = 0;
+	if (!(mps_config_get_raid_volume_pg1(sc, &mpi_reply, &raid_vol_pg1,
+	    MPI2_RAID_VOLUME_PGAD_FORM_HANDLE, volume_handle))) {
+		*wwid = le64toh((u64)raid_vol_pg1.WWID.High << 32 |
+		    raid_vol_pg1.WWID.Low);
+		return 0;
+	} else
+		return -1;
+}
+
+/**
+ * mps_config_get_pd_pg0 - obtain raid phys disk page 0
+ * @sc: per adapter object
+ * @mpi_reply: reply mf payload returned from firmware
+ * @config_page: contents of the config page
+ * @page_address: form and handle value used to get page
+ * Context: sleep.
+ *
+ * Returns 0 for success, non-zero for failure.
+ */
+int
+mps_config_get_raid_pd_pg0(struct mps_softc *sc, Mpi2ConfigReply_t *mpi_reply,
+    Mpi2RaidPhysDiskPage0_t *config_page, u32 page_address)
+{
+	MPI2_CONFIG_REQUEST *request;
+	MPI2_CONFIG_REPLY *reply;
+	struct mps_command *cm;
+	Mpi2RaidPhysDiskPage0_t *page = NULL;
+	int error = 0;
+	u16 ioc_status;
+
+	mps_dprint(sc, MPS_TRACE, "%s\n", __func__);
+
+	if ((cm = mps_alloc_command(sc)) == NULL) {
+		printf("%s: command alloc failed @ line %d\n", __func__,
+		    __LINE__);
+		error = EBUSY;
+		goto out;
+	}
+	request = (MPI2_CONFIG_REQUEST *)cm->cm_req;
+	bzero(request, sizeof(MPI2_CONFIG_REQUEST));
+	request->Function = MPI2_FUNCTION_CONFIG;
+	request->Action = MPI2_CONFIG_ACTION_PAGE_HEADER;
+	request->Header.PageType = MPI2_CONFIG_PAGETYPE_RAID_PHYSDISK;
+	request->Header.PageNumber = 0;
+	request->Header.PageVersion = MPI2_RAIDPHYSDISKPAGE0_PAGEVERSION;
+	cm->cm_desc.Default.RequestFlags = MPI2_REQ_DESCRIPT_FLAGS_DEFAULT_TYPE;
+	cm->cm_data = NULL;
+	error = mps_request_polled(sc, cm);
+	reply = (MPI2_CONFIG_REPLY *)cm->cm_reply;
+	if (error || (reply == NULL)) {
+		/* FIXME */
+		/* If the poll returns error then we need to do diag reset */ 
+		printf("%s: poll for header completed with error %d",
+		    __func__, error);
+		error = ENXIO;
+		goto out;
+	}
+	ioc_status = le16toh(reply->IOCStatus) & MPI2_IOCSTATUS_MASK;
+	bcopy(reply, mpi_reply, sizeof(MPI2_CONFIG_REPLY));
+	if (ioc_status != MPI2_IOCSTATUS_SUCCESS) {
+		/* FIXME */
+		/* If the poll returns error then we need to do diag reset */ 
+		printf("%s: header read with error; iocstatus = 0x%x\n",
+		    __func__, ioc_status);
+		error = ENXIO;
+		goto out;
+	}
+	/* We have to do free and alloc for the reply-free and reply-post
+	 * counters to match - Need to review the reply FIFO handling.
+	 */
+	mps_free_command(sc, cm);
+
+	if ((cm = mps_alloc_command(sc)) == NULL) {
+		printf("%s: command alloc failed @ line %d\n", __func__,
+		    __LINE__);
+		error = EBUSY;
+		goto out;
+	}
+	request = (MPI2_CONFIG_REQUEST *)cm->cm_req;
+	bzero(request, sizeof(MPI2_CONFIG_REQUEST));
+	request->Function = MPI2_FUNCTION_CONFIG;
+	request->Action = MPI2_CONFIG_ACTION_PAGE_READ_CURRENT;
+	request->Header.PageType = MPI2_CONFIG_PAGETYPE_RAID_PHYSDISK;
+	request->Header.PageNumber = 0;
+	request->Header.PageLength = mpi_reply->Header.PageLength;
+	request->Header.PageVersion = mpi_reply->Header.PageVersion;
+	request->PageAddress = page_address;
+	cm->cm_length = le16toh(mpi_reply->Header.PageLength) * 4;
+	cm->cm_sge = &request->PageBufferSGE;
+	cm->cm_sglsize = sizeof(MPI2_SGE_IO_UNION);
+	cm->cm_flags = MPS_CM_FLAGS_SGE_SIMPLE | MPS_CM_FLAGS_DATAIN;
+	cm->cm_desc.Default.RequestFlags = MPI2_REQ_DESCRIPT_FLAGS_DEFAULT_TYPE;
+	page = malloc(cm->cm_length, M_MPT2, M_ZERO | M_NOWAIT);
+	if (!page) {
+		printf("%s: page alloc failed\n", __func__);
+		error = ENOMEM;
+		goto out;
+	}
+	cm->cm_data = page;
+
+	error = mps_request_polled(sc, cm);
+	reply = (MPI2_CONFIG_REPLY *)cm->cm_reply;
+	if (error || (reply == NULL)) {
+		/* FIXME */
+		/* If the poll returns error then we need to do diag reset */ 
+		printf("%s: poll for page completed with error %d",
+		    __func__, error);
+		error = ENXIO;
+		goto out;
+	}
+	ioc_status = le16toh(reply->IOCStatus) & MPI2_IOCSTATUS_MASK;
+	bcopy(reply, mpi_reply, sizeof(MPI2_CONFIG_REPLY));
+	if (ioc_status != MPI2_IOCSTATUS_SUCCESS) {
+		/* FIXME */
+		/* If the poll returns error then we need to do diag reset */ 
+		printf("%s: page read with error; iocstatus = 0x%x\n",
+		    __func__, ioc_status);
+		error = ENXIO;
+		goto out;
+	}
+	bcopy(page, config_page, MIN(cm->cm_length,
+	    sizeof(Mpi2RaidPhysDiskPage0_t)));
+out:
+	free(page, M_MPT2);
+	if (cm)
+		mps_free_command(sc, cm);
+	return (error);
+}
diff -r 52ad4c882614 -r 6b8317170ea4 head/sys/dev/mps/mps_ioctl.h
--- a/head/sys/dev/mps/mps_ioctl.h	Wed Feb 01 11:28:01 2012 +0200
+++ b/head/sys/dev/mps/mps_ioctl.h	Wed Feb 01 11:28:20 2012 +0200
@@ -29,7 +29,36 @@
  *
  * LSI MPT-Fusion Host Adapter FreeBSD userland interface
  *
- * $FreeBSD$
+ * $FreeBSD: head/sys/dev/mps/mps_ioctl.h 230592 2012-01-26 18:17:21Z ken $
+ */
+/*-
+ * Copyright (c) 2011 LSI Corp.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * 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 OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * LSI MPT-Fusion Host Adapter FreeBSD
+ *
+ * $FreeBSD: head/sys/dev/mps/mps_ioctl.h 230592 2012-01-26 18:17:21Z ken $
  */
 
 #ifndef _MPS_IOCTL_H_
@@ -93,8 +122,237 @@
 	uint32_t flags;
 };
 
-#define MPSIO_MPS_COMMAND_FLAG_VERBOSE 0x01
-#define MPSIO_MPS_COMMAND_FLAG_DEBUG 0x02
+typedef struct mps_pci_bits
+{
+	union {
+		struct {
+			uint32_t	DeviceNumber	:5;
+			uint32_t	FunctionNumber	:3;
+			uint32_t	BusNumber	:24;
+		} bits;
+		uint32_t	AsDWORD;
+	} u;
+	uint32_t	PciSegmentId;
+} mps_pci_bits_t;
+
+/*
+ *  The following is the MPSIOCTL_GET_ADAPTER_DATA data structure.  This data
+ *  structure is setup so that we hopefully are properly aligned for both
+ *  32-bit and 64-bit mode applications.
+ *
+ *  Adapter Type - Value = 4 = SCSI Protocol through SAS-2 adapter
+ *
+ *  MPI Port Number - The PCI Function number for this device
+ *
+ *  PCI Device HW Id - The PCI device number for this device
+ *
+ */
+#define	MPSIOCTL_ADAPTER_TYPE_SAS2		4
+#define	MPSIOCTL_ADAPTER_TYPE_SAS2_SSS6200	5
+typedef struct mps_adapter_data
+{
+	uint32_t	StructureLength;
+	uint32_t	AdapterType;
+	uint32_t	MpiPortNumber;
+	uint32_t	PCIDeviceHwId;
+	uint32_t	PCIDeviceHwRev;
+	uint32_t	SubSystemId;
+	uint32_t	SubsystemVendorId;
+	uint32_t	Reserved1;
+	uint32_t	MpiFirmwareVersion;
+	uint32_t	BiosVersion;
+	uint8_t		DriverVersion[32];
+	uint8_t		Reserved2;
+	uint8_t		ScsiId;
+	uint16_t	Reserved3;
+	mps_pci_bits_t	PciInformation;
+} mps_adapter_data_t;
+
+
+typedef struct mps_update_flash
+{
+	uint64_t	PtrBuffer;
+	uint32_t	ImageChecksum;
+	uint32_t	ImageOffset;
+	uint32_t	ImageSize;
+	uint32_t	ImageType;
+} mps_update_flash_t;
+
+
+#define	MPS_PASS_THRU_DIRECTION_NONE	0
+#define	MPS_PASS_THRU_DIRECTION_READ	1
+#define	MPS_PASS_THRU_DIRECTION_WRITE	2
+#define	MPS_PASS_THRU_DIRECTION_BOTH	3
+
+typedef struct mps_pass_thru
+{
+	uint64_t	PtrRequest;
+	uint64_t	PtrReply;
+	uint64_t	PtrData;
+	uint32_t	RequestSize;
+	uint32_t	ReplySize;
+	uint32_t	DataSize;
+	uint32_t	DataDirection;
+	uint64_t	PtrDataOut;
+	uint32_t	DataOutSize;
+	uint32_t	Timeout;
+} mps_pass_thru_t;
+
+
+/*
+ * Event queue defines
+ */
+#define	MPS_EVENT_QUEUE_SIZE		(50) /* Max Events stored in driver */
+#define	MPS_MAX_EVENT_DATA_LENGTH	(48) /* Size of each event in Dwords */
+
+typedef struct mps_event_query
+{
+	uint16_t	Entries;
+	uint16_t	Reserved;
+	uint32_t	Types[4];
+} mps_event_query_t;
+
+typedef struct mps_event_enable
+{
+	uint32_t	Types[4];
+} mps_event_enable_t;
+
+/*
+ * Event record entry for ioctl.
+ */
+typedef struct mps_event_entry
+{
+	uint32_t	Type;
+	uint32_t	Number;
+	uint32_t	Data[MPS_MAX_EVENT_DATA_LENGTH];
+} mps_event_entry_t;
+
+typedef struct mps_event_report
+{
+	uint32_t	Size;
+	uint64_t	PtrEvents;
+} mps_event_report_t;
+
+
+typedef struct mps_pci_info
+{
+	uint32_t	BusNumber;
+	uint8_t		DeviceNumber;
+	uint8_t		FunctionNumber;
+	uint16_t	InterruptVector;
+	uint8_t		PciHeader[256];
+} mps_pci_info_t;
+
+
+typedef struct mps_diag_action
+{
+	uint32_t	Action;
+	uint32_t	Length;
+	uint64_t	PtrDiagAction;
+	uint32_t	ReturnCode;
+} mps_diag_action_t;
+
+#define	MPS_FW_DIAGNOSTIC_UID_NOT_FOUND	(0xFF)
+
+#define	MPS_FW_DIAG_NEW				(0x806E6577)
+
+#define	MPS_FW_DIAG_TYPE_REGISTER		(0x00000001)
+#define	MPS_FW_DIAG_TYPE_UNREGISTER		(0x00000002)
+#define	MPS_FW_DIAG_TYPE_QUERY			(0x00000003)
+#define	MPS_FW_DIAG_TYPE_READ_BUFFER		(0x00000004)
+#define	MPS_FW_DIAG_TYPE_RELEASE		(0x00000005)
+
+#define	MPS_FW_DIAG_INVALID_UID			(0x00000000)
+
+#define MPS_DIAG_SUCCESS			0
+#define MPS_DIAG_FAILURE			1
+
+#define	MPS_FW_DIAG_ERROR_SUCCESS		(0x00000000)
+#define	MPS_FW_DIAG_ERROR_FAILURE		(0x00000001)
+#define	MPS_FW_DIAG_ERROR_INVALID_PARAMETER	(0x00000002)
+#define	MPS_FW_DIAG_ERROR_POST_FAILED		(0x00000010)
+#define	MPS_FW_DIAG_ERROR_INVALID_UID		(0x00000011)
+#define	MPS_FW_DIAG_ERROR_RELEASE_FAILED	(0x00000012)
+#define	MPS_FW_DIAG_ERROR_NO_BUFFER		(0x00000013)
+#define	MPS_FW_DIAG_ERROR_ALREADY_RELEASED	(0x00000014)
+
+
+typedef struct mps_fw_diag_register
+{
+	uint8_t		ExtendedType;
+	uint8_t		BufferType;
+	uint16_t	ApplicationFlags;
+	uint32_t	DiagnosticFlags;
+	uint32_t	ProductSpecific[23];
+	uint32_t	RequestedBufferSize;
+	uint32_t	UniqueId;
+} mps_fw_diag_register_t;
+
+typedef struct mps_fw_diag_unregister
+{
+	uint32_t	UniqueId;
+} mps_fw_diag_unregister_t;
+
+#define	MPS_FW_DIAG_FLAG_APP_OWNED		(0x0001)
+#define	MPS_FW_DIAG_FLAG_BUFFER_VALID		(0x0002)
+#define	MPS_FW_DIAG_FLAG_FW_BUFFER_ACCESS	(0x0004)
+
+typedef struct mps_fw_diag_query
+{
+	uint8_t		ExtendedType;
+	uint8_t		BufferType;
+	uint16_t	ApplicationFlags;
+	uint32_t	DiagnosticFlags;
+	uint32_t	ProductSpecific[23];
+	uint32_t	TotalBufferSize;
+	uint32_t	DriverAddedBufferSize;
+	uint32_t	UniqueId;
+} mps_fw_diag_query_t;
+
+typedef struct mps_fw_diag_release
+{
+	uint32_t	UniqueId;
+} mps_fw_diag_release_t;
+
+#define	MPS_FW_DIAG_FLAG_REREGISTER	(0x0001)
+#define	MPS_FW_DIAG_FLAG_FORCE_RELEASE	(0x0002)
+
+typedef struct mps_diag_read_buffer
+{
+	uint8_t		Status;
+	uint8_t		Reserved;
+	uint16_t	Flags;
+	uint32_t	StartingOffset;
+	uint32_t	BytesToRead;
+	uint32_t	UniqueId;
+	uint64_t	PtrDataBuffer;
+} mps_diag_read_buffer_t;
+
+/*
+ * Register Access
+ */
+#define	REG_IO_READ	1
+#define	REG_IO_WRITE	2
+#define	REG_MEM_READ	3
+#define	REG_MEM_WRITE	4
+
+typedef struct mps_reg_access
+{
+	uint32_t	Command;
+	uint32_t	RegOffset;
+	uint32_t	RegData;
+} mps_reg_access_t;
+
+typedef struct mps_btdh_mapping
+{
+	uint16_t	TargetID;
+	uint16_t	Bus;
+	uint16_t	DevHandle;
+	uint16_t	Reserved;
+} mps_btdh_mapping_t;
+
+#define MPSIO_MPS_COMMAND_FLAG_VERBOSE	0x01
+#define MPSIO_MPS_COMMAND_FLAG_DEBUG	0x02
 #define	MPSIO_READ_CFG_HEADER	_IOWR('M', 200, struct mps_cfg_page_req)
 #define	MPSIO_READ_CFG_PAGE	_IOWR('M', 201, struct mps_cfg_page_req)
 #define	MPSIO_READ_EXT_CFG_HEADER _IOWR('M', 202, struct mps_ext_cfg_page_req)
@@ -103,4 +361,27 @@
 #define	MPSIO_RAID_ACTION	_IOWR('M', 205, struct mps_raid_action)
 #define	MPSIO_MPS_COMMAND	_IOWR('M', 210, struct mps_usr_command)
 
+#define	MPTIOCTL			('I')
+#define	MPTIOCTL_GET_ADAPTER_DATA	_IOWR(MPTIOCTL, 1,\
+    struct mps_adapter_data)
+#define	MPTIOCTL_UPDATE_FLASH		_IOWR(MPTIOCTL, 2,\
+    struct mps_update_flash)
+#define	MPTIOCTL_RESET_ADAPTER		_IO(MPTIOCTL, 3)
+#define	MPTIOCTL_PASS_THRU		_IOWR(MPTIOCTL, 4,\
+    struct mps_pass_thru)
+#define	MPTIOCTL_EVENT_QUERY		_IOWR(MPTIOCTL, 5,\
+    struct mps_event_query)
+#define	MPTIOCTL_EVENT_ENABLE		_IOWR(MPTIOCTL, 6,\
+    struct mps_event_enable)
+#define	MPTIOCTL_EVENT_REPORT		_IOWR(MPTIOCTL, 7,\
+    struct mps_event_report)
+#define	MPTIOCTL_GET_PCI_INFO		_IOWR(MPTIOCTL, 8,\
+    struct mps_pci_info)
+#define	MPTIOCTL_DIAG_ACTION		_IOWR(MPTIOCTL, 9,\
+    struct mps_diag_action)
+#define	MPTIOCTL_REG_ACCESS		_IOWR(MPTIOCTL, 10,\
+    struct mps_reg_access)
+#define	MPTIOCTL_BTDH_MAPPING		_IOWR(MPTIOCTL, 11,\
+    struct mps_btdh_mapping)
+
 #endif /* !_MPS_IOCTL_H_ */
diff -r 52ad4c882614 -r 6b8317170ea4 head/sys/dev/mps/mps_mapping.c
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/head/sys/dev/mps/mps_mapping.c	Wed Feb 01 11:28:20 2012 +0200
@@ -0,0 +1,2268 @@
+/*-
+ * Copyright (c) 2011 LSI Corp.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * 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 OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * LSI MPT-Fusion Host Adapter FreeBSD
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD: head/sys/dev/mps/mps_mapping.c 230592 2012-01-26 18:17:21Z ken $");
+
+/* TODO Move headers to mpsvar */
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/lock.h>
+#include <sys/mutex.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <sys/malloc.h>
+#include <sys/kthread.h>
+#include <sys/taskqueue.h>
+#include <sys/bus.h>
+#include <sys/endian.h>
+#include <sys/sysctl.h>
+#include <sys/eventhandler.h>
+#include <sys/uio.h>
+#include <machine/bus.h>
+#include <machine/resource.h>
+#include <dev/mps/mpi/mpi2_type.h>
+#include <dev/mps/mpi/mpi2.h>
+#include <dev/mps/mpi/mpi2_ioc.h>
+#include <dev/mps/mpi/mpi2_sas.h>
+#include <dev/mps/mpi/mpi2_cnfg.h>
+#include <dev/mps/mpi/mpi2_init.h>
+#include <dev/mps/mpi/mpi2_tool.h>
+#include <dev/mps/mps_ioctl.h>
+#include <dev/mps/mpsvar.h>
+#include <dev/mps/mps_mapping.h>
+
+/**
+ * _mapping_clear_entry - Clear a particular mapping entry.
+ * @map_entry: map table entry
+ *
+ * Returns nothing.
+ */
+static inline void
+_mapping_clear_map_entry(struct dev_mapping_table *map_entry)
+{
+	map_entry->physical_id = 0;
+	map_entry->device_info = 0;
+	map_entry->phy_bits = 0;
+	map_entry->dpm_entry_num = MPS_DPM_BAD_IDX;
+	map_entry->dev_handle = 0;
+	map_entry->channel = -1;
+	map_entry->id = -1;
+	map_entry->missing_count = 0;
+	map_entry->init_complete = 0;
+	map_entry->TLR_bits = (u8)MPI2_SCSIIO_CONTROL_NO_TLR;
+}
+
+/**
+ * _mapping_clear_enc_entry - Clear a particular enclosure table entry.
+ * @enc_entry: enclosure table entry
+ *
+ * Returns nothing.
+ */
+static inline void
+_mapping_clear_enc_entry(struct enc_mapping_table *enc_entry)
+{
+	enc_entry->enclosure_id = 0;
+	enc_entry->start_index = MPS_MAPTABLE_BAD_IDX;
+	enc_entry->phy_bits = 0;
+	enc_entry->dpm_entry_num = MPS_DPM_BAD_IDX;
+	enc_entry->enc_handle = 0;
+	enc_entry->num_slots = 0;
+	enc_entry->start_slot = 0;
+	enc_entry->missing_count = 0;
+	enc_entry->removal_flag = 0;
+	enc_entry->skip_search = 0;
+	enc_entry->init_complete = 0;
+}
+
+/**
+ * _mapping_commit_enc_entry - write a particular enc entry in DPM page0.
+ * @sc: per adapter object
+ * @enc_entry: enclosure table entry
+ *
+ * Returns 0 for success, non-zero for failure.
+ */
+static int
+_mapping_commit_enc_entry(struct mps_softc *sc,
+    struct enc_mapping_table *et_entry)
+{
+	Mpi2DriverMap0Entry_t *dpm_entry;
+	struct dev_mapping_table *mt_entry;
+	Mpi2ConfigReply_t mpi_reply;
+	Mpi2DriverMappingPage0_t config_page;
+
+	if (!sc->is_dpm_enable)
+		return 0;
+
+	memset(&config_page, 0, sizeof(Mpi2DriverMappingPage0_t));
+	memcpy(&config_page.Header, (u8 *) sc->dpm_pg0,
+	    sizeof(MPI2_CONFIG_EXTENDED_PAGE_HEADER));
+	dpm_entry = (Mpi2DriverMap0Entry_t *)((u8 *)sc->dpm_pg0 +
+	    sizeof(MPI2_CONFIG_EXTENDED_PAGE_HEADER));
+	dpm_entry += et_entry->dpm_entry_num;
+	dpm_entry->PhysicalIdentifier.Low =
+	    ( 0xFFFFFFFF & et_entry->enclosure_id);
+	dpm_entry->PhysicalIdentifier.High =
+	    ( et_entry->enclosure_id >> 32);
+	mt_entry = &sc->mapping_table[et_entry->start_index];
+	dpm_entry->DeviceIndex = htole16(mt_entry->id);
+	dpm_entry->MappingInformation = et_entry->num_slots;
+	dpm_entry->MappingInformation <<= MPI2_DRVMAP0_MAPINFO_SLOT_SHIFT;
+	dpm_entry->MappingInformation |= et_entry->missing_count;
+	dpm_entry->MappingInformation = htole16(dpm_entry->MappingInformation);
+	dpm_entry->PhysicalBitsMapping = htole32(et_entry->phy_bits);
+	dpm_entry->Reserved1 = 0;
+
+	memcpy(&config_page.Entry, (u8 *)dpm_entry,
+	    sizeof(Mpi2DriverMap0Entry_t));
+	if (mps_config_set_dpm_pg0(sc, &mpi_reply, &config_page,
+	    et_entry->dpm_entry_num)) {
+		printf("%s: write of dpm entry %d for enclosure failed\n",
+		    __func__, et_entry->dpm_entry_num);
+		dpm_entry->MappingInformation = le16toh(dpm_entry->
+		    MappingInformation);
+		dpm_entry->DeviceIndex = le16toh(dpm_entry->DeviceIndex);
+		dpm_entry->PhysicalBitsMapping =
+		    le32toh(dpm_entry->PhysicalBitsMapping);
+		return -1;
+	}
+	dpm_entry->MappingInformation = le16toh(dpm_entry->
+	    MappingInformation);
+	dpm_entry->DeviceIndex = le16toh(dpm_entry->DeviceIndex);
+	dpm_entry->PhysicalBitsMapping =
+	    le32toh(dpm_entry->PhysicalBitsMapping);
+	return 0;
+}
+
+/**
+ * _mapping_commit_map_entry - write a particular map table entry in DPM page0.
+ * @sc: per adapter object
+ * @enc_entry: enclosure table entry
+ *
+ * Returns 0 for success, non-zero for failure.
+ */
+
+static int
+_mapping_commit_map_entry(struct mps_softc *sc,
+    struct dev_mapping_table *mt_entry)
+{
+	Mpi2DriverMap0Entry_t *dpm_entry;
+	Mpi2ConfigReply_t mpi_reply;
+	Mpi2DriverMappingPage0_t config_page;
+
+	if (!sc->is_dpm_enable)
+		return 0;
+
+	memset(&config_page, 0, sizeof(Mpi2DriverMappingPage0_t));
+	memcpy(&config_page.Header, (u8 *)sc->dpm_pg0,
+	    sizeof(MPI2_CONFIG_EXTENDED_PAGE_HEADER));
+	dpm_entry = (Mpi2DriverMap0Entry_t *)((u8 *) sc->dpm_pg0 +
+	    sizeof(MPI2_CONFIG_EXTENDED_PAGE_HEADER));
+	dpm_entry = dpm_entry + mt_entry->dpm_entry_num;
+	dpm_entry->PhysicalIdentifier.Low = (0xFFFFFFFF &
+	    mt_entry->physical_id);
+	dpm_entry->PhysicalIdentifier.High = (mt_entry->physical_id >> 32);
+	dpm_entry->DeviceIndex = htole16(mt_entry->id);
+	dpm_entry->MappingInformation = htole16(mt_entry->missing_count);
+	dpm_entry->PhysicalBitsMapping = 0;
+	dpm_entry->Reserved1 = 0;
+	dpm_entry->MappingInformation = htole16(dpm_entry->MappingInformation);
+	memcpy(&config_page.Entry, (u8 *)dpm_entry,
+	    sizeof(Mpi2DriverMap0Entry_t));
+	if (mps_config_set_dpm_pg0(sc, &mpi_reply, &config_page,
+	    mt_entry->dpm_entry_num)) {
+		printf("%s: write of dpm entry %d for device failed\n",
+		    __func__, mt_entry->dpm_entry_num);
+		dpm_entry->MappingInformation = le16toh(dpm_entry->
+		    MappingInformation);
+		dpm_entry->DeviceIndex = le16toh(dpm_entry->DeviceIndex);
+		return -1;
+	}
+
+	dpm_entry->MappingInformation = le16toh(dpm_entry->MappingInformation);
+	dpm_entry->DeviceIndex = le16toh(dpm_entry->DeviceIndex);
+	return 0;
+}
+
+/**
+ * _mapping_get_ir_maprange - get start and end index for IR map range.
+ * @sc: per adapter object
+ * @start_idx: place holder for start index
+ * @end_idx: place holder for end index
+ *
+ * The IR volumes can be mapped either at start or end of the mapping table
+ * this function gets the detail of where IR volume mapping starts and ends
+ * in the device mapping table
+ *
+ * Returns nothing.
+ */
+static void
+_mapping_get_ir_maprange(struct mps_softc *sc, u32 *start_idx, u32 *end_idx)
+{
+	u16 volume_mapping_flags;
+	u16 ioc_pg8_flags = le16toh(sc->ioc_pg8.Flags);
+
+	volume_mapping_flags = le16toh(sc->ioc_pg8.IRVolumeMappingFlags) &
+	    MPI2_IOCPAGE8_IRFLAGS_MASK_VOLUME_MAPPING_MODE;
+	if (volume_mapping_flags == MPI2_IOCPAGE8_IRFLAGS_LOW_VOLUME_MAPPING) {
+		*start_idx = 0;
+		if (ioc_pg8_flags & MPI2_IOCPAGE8_FLAGS_RESERVED_TARGETID_0)
+			*start_idx = 1;
+	} else
+		*start_idx = sc->max_devices - sc->max_volumes;
+	*end_idx = *start_idx + sc->max_volumes - 1;
+}
+
+/**
+ * _mapping_get_enc_idx_from_id - get enclosure index from enclosure ID
+ * @sc: per adapter object
+ * @enc_id: enclosure logical identifier
+ *
+ * Returns the index of enclosure entry on success or bad index.
+ */
+static u8
+_mapping_get_enc_idx_from_id(struct mps_softc *sc, u64 enc_id,
+    u64 phy_bits)
+{
+	struct enc_mapping_table *et_entry;
+	u8 enc_idx = 0;
+
+	for (enc_idx = 0; enc_idx < sc->num_enc_table_entries; enc_idx++) {
+		et_entry = &sc->enclosure_table[enc_idx];
+		if ((et_entry->enclosure_id == le64toh(enc_id)) &&
+		    (!et_entry->phy_bits || (et_entry->phy_bits &
+		    le32toh(phy_bits))))
+			return enc_idx;
+	}
+	return MPS_ENCTABLE_BAD_IDX;
+}
+
+/**
+ * _mapping_get_enc_idx_from_handle - get enclosure index from handle
+ * @sc: per adapter object
+ * @enc_id: enclosure handle
+ *
+ * Returns the index of enclosure entry on success or bad index.
+ */
+static u8
+_mapping_get_enc_idx_from_handle(struct mps_softc *sc, u16 handle)
+{
+	struct enc_mapping_table *et_entry;
+	u8 enc_idx = 0;
+
+	for (enc_idx = 0; enc_idx < sc->num_enc_table_entries; enc_idx++) {
+		et_entry = &sc->enclosure_table[enc_idx];
+		if (et_entry->missing_count)
+			continue;
+		if (et_entry->enc_handle == handle)
+			return enc_idx;
+	}
+	return MPS_ENCTABLE_BAD_IDX;
+}
+
+/**
+ * _mapping_get_high_missing_et_idx - get missing enclosure index
+ * @sc: per adapter object
+ *
+ * Search through the enclosure table and identifies the enclosure entry
+ * with high missing count and returns it's index
+ *
+ * Returns the index of enclosure entry on success or bad index.
+ */
+static u8
+_mapping_get_high_missing_et_idx(struct mps_softc *sc)
+{
+	struct enc_mapping_table *et_entry;
+	u8 high_missing_count = 0;
+	u8 enc_idx, high_idx = MPS_ENCTABLE_BAD_IDX;
+
+	for (enc_idx = 0; enc_idx < sc->num_enc_table_entries; enc_idx++) {
+		et_entry = &sc->enclosure_table[enc_idx];
+		if ((et_entry->missing_count > high_missing_count) &&
+		    !et_entry->skip_search) {
+			high_missing_count =  et_entry->missing_count;
+			high_idx = enc_idx;
+		}
+	}
+	return high_idx;
+}
+
+/**
+ * _mapping_get_high_missing_mt_idx - get missing map table index
+ * @sc: per adapter object
+ *
+ * Search through the map table and identifies the device entry
+ * with high missing count and returns it's index
+ *
+ * Returns the index of map table entry on success or bad index.
+ */
+static u32
+_mapping_get_high_missing_mt_idx(struct mps_softc *sc)
+{
+	u32 map_idx, high_idx = MPS_ENCTABLE_BAD_IDX;
+	u8 high_missing_count = 0;
+	u32 start_idx, end_idx, start_idx_ir, end_idx_ir;
+	struct dev_mapping_table *mt_entry;
+	u16 ioc_pg8_flags = le16toh(sc->ioc_pg8.Flags);
+
+	start_idx = 0;
+	end_idx = sc->max_devices;
+	if (ioc_pg8_flags & MPI2_IOCPAGE8_FLAGS_RESERVED_TARGETID_0)
+		start_idx = 1;
+	if (sc->ir_firmware)
+		_mapping_get_ir_maprange(sc, &start_idx_ir, &end_idx_ir);
+	if (start_idx == start_idx_ir)
+		start_idx = end_idx_ir + 1;
+	else
+		end_idx = start_idx_ir;
+	mt_entry = &sc->mapping_table[start_idx];
+	for (map_idx = start_idx; map_idx < end_idx; map_idx++, mt_entry++) {
+		if (mt_entry->missing_count > high_missing_count) {
+			high_missing_count =  mt_entry->missing_count;
+			high_idx = map_idx;
+		}
+	}
+	return high_idx;
+}
+
+/**
+ * _mapping_get_ir_mt_idx_from_wwid - get map table index from volume WWID
+ * @sc: per adapter object
+ * @wwid: world wide unique ID of the volume
+ *
+ * Returns the index of map table entry on success or bad index.
+ */
+static u32
+_mapping_get_ir_mt_idx_from_wwid(struct mps_softc *sc, u64 wwid)
+{
+	u32 start_idx, end_idx, map_idx;
+	struct dev_mapping_table *mt_entry;
+
+	_mapping_get_ir_maprange(sc, &start_idx, &end_idx);
+	mt_entry = &sc->mapping_table[start_idx];
+	for (map_idx  = start_idx; map_idx <= end_idx; map_idx++, mt_entry++)
+		if (mt_entry->physical_id == wwid)
+			return map_idx;
+
+	return MPS_MAPTABLE_BAD_IDX;
+}
+
+/**
+ * _mapping_get_mt_idx_from_id - get map table index from a device ID
+ * @sc: per adapter object
+ * @dev_id: device identifer (SAS Address)
+ *
+ * Returns the index of map table entry on success or bad index.
+ */
+static u32
+_mapping_get_mt_idx_from_id(struct mps_softc *sc, u64 dev_id)
+{
+	u32 map_idx;
+	struct dev_mapping_table *mt_entry;
+
+	for (map_idx = 0; map_idx < sc->max_devices; map_idx++) {
+		mt_entry = &sc->mapping_table[map_idx];
+		if (mt_entry->physical_id == dev_id)
+			return map_idx;
+	}
+	return MPS_MAPTABLE_BAD_IDX;
+}
+
+/**
+ * _mapping_get_ir_mt_idx_from_handle - get map table index from volume handle
+ * @sc: per adapter object
+ * @wwid: volume device handle
+ *
+ * Returns the index of map table entry on success or bad index.
+ */
+static u32
+_mapping_get_ir_mt_idx_from_handle(struct mps_softc *sc, u16 volHandle)
+{
+	u32 start_idx, end_idx, map_idx;
+	struct dev_mapping_table *mt_entry;
+
+	_mapping_get_ir_maprange(sc, &start_idx, &end_idx);
+	mt_entry = &sc->mapping_table[start_idx];
+	for (map_idx = start_idx; map_idx <= end_idx; map_idx++, mt_entry++)
+		if (mt_entry->dev_handle == volHandle)
+			return map_idx;
+
+	return MPS_MAPTABLE_BAD_IDX;
+}
+
+/**
+ * _mapping_get_mt_idx_from_handle - get map table index from handle
+ * @sc: per adapter object
+ * @dev_id: device handle
+ *
+ * Returns the index of map table entry on success or bad index.
+ */
+static u32
+_mapping_get_mt_idx_from_handle(struct mps_softc *sc, u16 handle)
+{
+	u32 map_idx;
+	struct dev_mapping_table *mt_entry;
+
+	for (map_idx = 0; map_idx < sc->max_devices; map_idx++) {
+		mt_entry = &sc->mapping_table[map_idx];
+		if (mt_entry->dev_handle == handle)
+			return map_idx;
+	}
+	return MPS_MAPTABLE_BAD_IDX;
+}
+
+/**
+ * _mapping_get_free_ir_mt_idx - get first free index for a volume
+ * @sc: per adapter object
+ *
+ * Search through mapping table for free index for a volume and if no free
+ * index then looks for a volume with high mapping index
+ *
+ * Returns the index of map table entry on success or bad index.
+ */
+static u32
+_mapping_get_free_ir_mt_idx(struct mps_softc *sc)
+{
+	u8 high_missing_count = 0;
+	u32 start_idx, end_idx, map_idx;
+	u32 high_idx = MPS_MAPTABLE_BAD_IDX;
+	struct dev_mapping_table *mt_entry;
+
+	_mapping_get_ir_maprange(sc, &start_idx, &end_idx);
+
+	mt_entry = &sc->mapping_table[start_idx];
+	for (map_idx  = start_idx; map_idx <= end_idx; map_idx++, mt_entry++)
+		if (!(mt_entry->device_info & MPS_MAP_IN_USE))
+			return map_idx;
+
+	mt_entry = &sc->mapping_table[start_idx];
+	for (map_idx  = start_idx; map_idx <= end_idx; map_idx++, mt_entry++) {
+		if (mt_entry->missing_count > high_missing_count) {
+			high_missing_count = mt_entry->missing_count;
+			high_idx = map_idx;
+		}
+	}
+	return high_idx;
+}
+
+/**
+ * _mapping_get_free_mt_idx - get first free index for a device
+ * @sc: per adapter object
+ * @start_idx: offset in the table to start search
+ *
+ * Returns the index of map table entry on success or bad index.
+ */
+static u32
+_mapping_get_free_mt_idx(struct mps_softc *sc, u32 start_idx)
+{
+	u32 map_idx, max_idx = sc->max_devices;
+	struct dev_mapping_table *mt_entry = &sc->mapping_table[start_idx];
+	u16 volume_mapping_flags;
+
+	volume_mapping_flags = le16toh(sc->ioc_pg8.IRVolumeMappingFlags) &
+	    MPI2_IOCPAGE8_IRFLAGS_MASK_VOLUME_MAPPING_MODE;
+	if (sc->ir_firmware && (volume_mapping_flags ==
+	    MPI2_IOCPAGE8_IRFLAGS_HIGH_VOLUME_MAPPING))
+		max_idx -= sc->max_volumes;
+	for (map_idx  = start_idx; map_idx < max_idx; map_idx++, mt_entry++)
+		if (!(mt_entry->device_info & (MPS_MAP_IN_USE |
+		    MPS_DEV_RESERVED)))
+			return map_idx;
+
+	return MPS_MAPTABLE_BAD_IDX;
+}
+
+/**
+ * _mapping_get_dpm_idx_from_id - get DPM index from ID
+ * @sc: per adapter object
+ * @id: volume WWID or enclosure ID or device ID
+ *
+ * Returns the index of DPM entry on success or bad index.
+ */
+static u16
+_mapping_get_dpm_idx_from_id(struct mps_softc *sc, u64 id, u32 phy_bits)
+{
+	u16 entry_num;
+	uint64_t PhysicalIdentifier;
+	Mpi2DriverMap0Entry_t *dpm_entry;
+
+	dpm_entry = (Mpi2DriverMap0Entry_t *)((u8 *)sc->dpm_pg0 +
+	    sizeof(MPI2_CONFIG_EXTENDED_PAGE_HEADER));
+	PhysicalIdentifier = dpm_entry->PhysicalIdentifier.High;
+	PhysicalIdentifier = (PhysicalIdentifier << 32) | 
+	    dpm_entry->PhysicalIdentifier.Low;
+	for (entry_num = 0; entry_num < sc->max_dpm_entries; entry_num++,
+	    dpm_entry++)
+		if ((id == PhysicalIdentifier) &&
+		    (!phy_bits || !dpm_entry->PhysicalBitsMapping ||
+		    (phy_bits & dpm_entry->PhysicalBitsMapping)))
+			return entry_num;
+
+	return MPS_DPM_BAD_IDX;
+}
+
+
+/**
+ * _mapping_get_free_dpm_idx - get first available DPM index
+ * @sc: per adapter object
+ *
+ * Returns the index of DPM entry on success or bad index.
+ */
+static u32
+_mapping_get_free_dpm_idx(struct mps_softc *sc)
+{
+	u16 entry_num;
+
+	for (entry_num = 0; entry_num < sc->max_dpm_entries; entry_num++) {
+		if (!sc->dpm_entry_used[entry_num])
+			return entry_num;
+	}
+	return MPS_DPM_BAD_IDX;
+}
+
+/**
+ * _mapping_update_ir_missing_cnt - Updates missing count for a volume
+ * @sc: per adapter object
+ * @map_idx: map table index of the volume
+ * @element: IR configuration change element
+ * @wwid: IR volume ID.
+ *
+ * Updates the missing count in the map table and in the DPM entry for a volume
+ *
+ * Returns nothing.
+ */
+static void
+_mapping_update_ir_missing_cnt(struct mps_softc *sc, u32 map_idx,
+    Mpi2EventIrConfigElement_t *element, u64 wwid)
+{
+	struct dev_mapping_table *mt_entry;
+	u8 missing_cnt, reason = element->ReasonCode;
+	u16 dpm_idx;
+	Mpi2DriverMap0Entry_t *dpm_entry;
+
+	if (!sc->is_dpm_enable)
+		return;
+	mt_entry = &sc->mapping_table[map_idx];
+	if (reason == MPI2_EVENT_IR_CHANGE_RC_ADDED) {
+		mt_entry->missing_count = 0;
+	} else if (reason == MPI2_EVENT_IR_CHANGE_RC_VOLUME_CREATED) {
+		mt_entry->missing_count = 0;
+		mt_entry->init_complete = 0;
+	} else if ((reason == MPI2_EVENT_IR_CHANGE_RC_REMOVED) ||
+	    (reason == MPI2_EVENT_IR_CHANGE_RC_VOLUME_DELETED)) {
+		if (!mt_entry->init_complete) {
+			if (mt_entry->missing_count < MPS_MAX_MISSING_COUNT)
+				mt_entry->missing_count++;
+			else
+				mt_entry->init_complete = 1;
+		}
+		if (!mt_entry->missing_count)
+			mt_entry->missing_count++;
+		mt_entry->dev_handle = 0;
+	}
+
+	dpm_idx = mt_entry->dpm_entry_num;
+	if (dpm_idx == MPS_DPM_BAD_IDX) {
+		if ((reason == MPI2_EVENT_IR_CHANGE_RC_ADDED) ||
+		    (reason == MPI2_EVENT_IR_CHANGE_RC_REMOVED))
+			dpm_idx = _mapping_get_dpm_idx_from_id(sc,
+			    mt_entry->physical_id, 0);
+		else if (reason == MPI2_EVENT_IR_CHANGE_RC_VOLUME_DELETED)
+			return;
+	}
+	if (dpm_idx != MPS_DPM_BAD_IDX) {
+		dpm_entry = (Mpi2DriverMap0Entry_t *)((u8 *)sc->dpm_pg0 +
+		    sizeof(MPI2_CONFIG_EXTENDED_PAGE_HEADER));
+		dpm_entry += dpm_idx;
+		missing_cnt = dpm_entry->MappingInformation &
+		    MPI2_DRVMAP0_MAPINFO_MISSING_MASK;
+		if ((mt_entry->physical_id ==
+		    le64toh((u64)dpm_entry->PhysicalIdentifier.High |
+		    dpm_entry->PhysicalIdentifier.Low)) && (missing_cnt ==
+		    mt_entry->missing_count))
+			mt_entry->init_complete = 1;
+	} else {
+		dpm_idx = _mapping_get_free_dpm_idx(sc);
+		mt_entry->init_complete = 0;
+	}
+
+	if ((dpm_idx != MPS_DPM_BAD_IDX) && !mt_entry->init_complete) {
+		mt_entry->init_complete = 1;
+		mt_entry->dpm_entry_num = dpm_idx;
+		dpm_entry = (Mpi2DriverMap0Entry_t *)((u8 *)sc->dpm_pg0 +
+		    sizeof(MPI2_CONFIG_EXTENDED_PAGE_HEADER));
+		dpm_entry += dpm_idx;
+		dpm_entry->PhysicalIdentifier.Low =
+		    (0xFFFFFFFF & mt_entry->physical_id);
+		dpm_entry->PhysicalIdentifier.High =
+		    (mt_entry->physical_id >> 32);
+		dpm_entry->DeviceIndex = map_idx;
+		dpm_entry->MappingInformation = mt_entry->missing_count;
+		dpm_entry->PhysicalBitsMapping = 0;
+		dpm_entry->Reserved1 = 0;
+		sc->dpm_flush_entry[dpm_idx] = 1;
+		sc->dpm_entry_used[dpm_idx] = 1;
+	} else if (dpm_idx == MPS_DPM_BAD_IDX) {
+		printf("%s: no space to add entry in DPM table\n", __func__);
+		mt_entry->init_complete = 1;
+	}
+}
+
+/**
+ * _mapping_add_to_removal_table - mark an entry for removal
+ * @sc: per adapter object
+ * @handle: Handle of enclosures/device/volume
+ *
+ * Adds the handle or DPM entry number in removal table.
+ *
+ * Returns nothing.
+ */
+static void
+_mapping_add_to_removal_table(struct mps_softc *sc, u16 handle,
+    u16 dpm_idx)
+{
+	struct map_removal_table *remove_entry;
+	u32 i;
+	u16 ioc_pg8_flags = le16toh(sc->ioc_pg8.Flags);
+
+	remove_entry = sc->removal_table;
+
+	for (i = 0; i < sc->max_devices; i++, remove_entry++) {
+		if (remove_entry->dev_handle || remove_entry->dpm_entry_num !=
+		    MPS_DPM_BAD_IDX)
+			continue;
+		if ((ioc_pg8_flags & MPI2_IOCPAGE8_FLAGS_MASK_MAPPING_MODE) ==
+		    MPI2_IOCPAGE8_FLAGS_ENCLOSURE_SLOT_MAPPING) {
+			if (dpm_idx)
+				remove_entry->dpm_entry_num = dpm_idx;
+			if (remove_entry->dpm_entry_num == MPS_DPM_BAD_IDX)
+				remove_entry->dev_handle = handle;
+		} else if ((ioc_pg8_flags &
+		    MPI2_IOCPAGE8_FLAGS_MASK_MAPPING_MODE) ==
+		    MPI2_IOCPAGE8_FLAGS_DEVICE_PERSISTENCE_MAPPING)
+			remove_entry->dev_handle = handle;
+		break;
+	}
+
+}
+
+/**
+ * _mapping_update_missing_count - Update missing count for a device
+ * @sc: per adapter object
+ * @topo_change: Topology change event entry
+ *
+ * Search through the topology change list and if any device is found not
+ * responding it's associated map table entry and DPM entry is updated
+ *
+ * Returns nothing.
+ */
+static void
+_mapping_update_missing_count(struct mps_softc *sc,
+    struct _map_topology_change *topo_change)
+{
+	u16 ioc_pg8_flags = le16toh(sc->ioc_pg8.Flags);
+	u8 entry;
+	struct _map_phy_change *phy_change;
+	u32 map_idx;
+	struct dev_mapping_table *mt_entry;
+	Mpi2DriverMap0Entry_t *dpm_entry;
+
+	for (entry = 0; entry < topo_change->num_entries; entry++) {
+		phy_change = &topo_change->phy_details[entry];
+		if (!phy_change->dev_handle || (phy_change->reason !=
+		    MPI2_EVENT_SAS_TOPO_RC_TARG_NOT_RESPONDING))
+			continue;
+		map_idx = _mapping_get_mt_idx_from_handle(sc, phy_change->
+		    dev_handle);
+		phy_change->is_processed = 1;
+		if (map_idx == MPS_MAPTABLE_BAD_IDX) {
+			printf("%s: device is already removed from mapping "
+			    "table\n", __func__);
+			continue;
+		}
+		mt_entry = &sc->mapping_table[map_idx];
+		if (!mt_entry->init_complete) {
+			if (mt_entry->missing_count < MPS_MAX_MISSING_COUNT)
+				mt_entry->missing_count++;
+			else
+				mt_entry->init_complete = 1;
+		}
+		if (!mt_entry->missing_count)
+			mt_entry->missing_count++;
+		_mapping_add_to_removal_table(sc, mt_entry->dev_handle, 0);
+		mt_entry->dev_handle = 0;
+
+		if (((ioc_pg8_flags & MPI2_IOCPAGE8_FLAGS_MASK_MAPPING_MODE) ==
+		    MPI2_IOCPAGE8_FLAGS_DEVICE_PERSISTENCE_MAPPING) &&
+		    sc->is_dpm_enable && !mt_entry->init_complete &&
+		    mt_entry->dpm_entry_num != MPS_DPM_BAD_IDX) {
+			dpm_entry =
+			    (Mpi2DriverMap0Entry_t *) ((u8 *)sc->dpm_pg0 +
+			    sizeof(MPI2_CONFIG_EXTENDED_PAGE_HEADER));
+			dpm_entry += mt_entry->dpm_entry_num;
+			dpm_entry->MappingInformation = mt_entry->missing_count;
+			sc->dpm_flush_entry[mt_entry->dpm_entry_num] = 1;
+		}
+		mt_entry->init_complete = 1;
+	}
+}
+
+/**
+ * _mapping_find_enc_map_space -find map table entries for enclosure
+ * @sc: per adapter object
+ * @et_entry: enclosure entry
+ *
+ * Search through the mapping table defragment it and provide contiguous
+ * space in map table for a particular enclosure entry
+ *
+ * Returns start index in map table or bad index.
+ */
+static u32
+_mapping_find_enc_map_space(struct mps_softc *sc,
+    struct enc_mapping_table *et_entry)
+{
+	u16 vol_mapping_flags;
+	u32 skip_count, end_of_table, map_idx, enc_idx;
+	u16 num_found;
+	u32 start_idx = MPS_MAPTABLE_BAD_IDX;
+	struct dev_mapping_table *mt_entry;
+	struct enc_mapping_table *enc_entry;
+	unsigned char done_flag = 0, found_space;
+	u16 max_num_phy_ids = le16toh(sc->ioc_pg8.MaxNumPhysicalMappedIDs);
+
+	skip_count = sc->num_rsvd_entries;
+	num_found = 0;
+
+	vol_mapping_flags = le16toh(sc->ioc_pg8.IRVolumeMappingFlags) &
+	    MPI2_IOCPAGE8_IRFLAGS_MASK_VOLUME_MAPPING_MODE;
+
+	if (!sc->ir_firmware)
+		end_of_table = sc->max_devices;
+	else if (vol_mapping_flags == MPI2_IOCPAGE8_IRFLAGS_LOW_VOLUME_MAPPING)
+		end_of_table = sc->max_devices;
+	else
+		end_of_table = sc->max_devices - sc->max_volumes;
+
+	for (map_idx = (max_num_phy_ids + skip_count);
+	    map_idx < end_of_table; map_idx++) {
+		mt_entry = &sc->mapping_table[map_idx];
+		if ((et_entry->enclosure_id == mt_entry->physical_id) &&
+		    (!mt_entry->phy_bits || (mt_entry->phy_bits &
+		    et_entry->phy_bits))) {
+			num_found += 1;
+			if (num_found == et_entry->num_slots) {
+				start_idx = (map_idx - num_found) + 1;
+				return start_idx;
+			}
+		} else
+			num_found = 0;
+	}
+	for (map_idx = (max_num_phy_ids + skip_count);
+	    map_idx < end_of_table; map_idx++) {
+		mt_entry = &sc->mapping_table[map_idx];
+		if (!(mt_entry->device_info & MPS_DEV_RESERVED)) {
+			num_found += 1;
+			if (num_found == et_entry->num_slots) {
+				start_idx = (map_idx - num_found) + 1;
+				return start_idx;
+			}
+		} else
+			num_found = 0;
+	}
+
+	while (!done_flag) {
+		enc_idx = _mapping_get_high_missing_et_idx(sc);
+		if (enc_idx == MPS_ENCTABLE_BAD_IDX)
+			return MPS_MAPTABLE_BAD_IDX;
+		enc_entry = &sc->enclosure_table[enc_idx];
+		/*VSP FIXME*/
+		enc_entry->skip_search = 1;
+		mt_entry = &sc->mapping_table[enc_entry->start_index];
+		for (map_idx = enc_entry->start_index; map_idx <
+		    (enc_entry->start_index + enc_entry->num_slots); map_idx++,
+		    mt_entry++)
+			mt_entry->device_info  &= ~MPS_DEV_RESERVED;
+		found_space = 0;
+		for (map_idx = (max_num_phy_ids +
+		    skip_count); map_idx < end_of_table; map_idx++) {
+			mt_entry = &sc->mapping_table[map_idx];
+			if (!(mt_entry->device_info & MPS_DEV_RESERVED)) {
+				num_found += 1;
+				if (num_found == et_entry->num_slots) {
+					start_idx = (map_idx - num_found) + 1;
+					found_space = 1;
+				}
+			} else
+				num_found = 0;
+		}
+
+		if (!found_space)
+			continue;
+		for (map_idx = start_idx; map_idx < (start_idx + num_found);
+		    map_idx++) {
+			enc_entry = sc->enclosure_table;
+			for (enc_idx = 0; enc_idx < sc->num_enc_table_entries;
+			    enc_idx++, enc_entry++) {
+				if (map_idx < enc_entry->start_index ||
+				    map_idx > (enc_entry->start_index +
+				    enc_entry->num_slots))
+					continue;
+				if (!enc_entry->removal_flag) {
+					enc_entry->removal_flag = 1;
+					_mapping_add_to_removal_table(sc, 0,
+					    enc_entry->dpm_entry_num);
+				}
+				mt_entry = &sc->mapping_table[map_idx];
+				if (mt_entry->device_info &
+				    MPS_MAP_IN_USE) {
+					_mapping_add_to_removal_table(sc,
+					    mt_entry->dev_handle, 0);
+					_mapping_clear_map_entry(mt_entry);
+				}
+				if (map_idx == (enc_entry->start_index +
+				    enc_entry->num_slots - 1))
+					_mapping_clear_enc_entry(et_entry);
+			}
+		}
+		enc_entry = sc->enclosure_table;
+		for (enc_idx = 0; enc_idx < sc->num_enc_table_entries;
+		    enc_idx++, enc_entry++) {
+			if (!enc_entry->removal_flag) {
+				mt_entry = &sc->mapping_table[enc_entry->
+				    start_index];
+				for (map_idx = enc_entry->start_index; map_idx <
+				    (enc_entry->start_index +
+				    enc_entry->num_slots); map_idx++,
+				    mt_entry++)
+					mt_entry->device_info |=
+					    MPS_DEV_RESERVED;
+				et_entry->skip_search = 0;
+			}
+		}
+		done_flag = 1;
+	}
+	return start_idx;
+}
+
+/**
+ * _mapping_get_dev_info -get information about newly added devices
+ * @sc: per adapter object
+ * @topo_change: Topology change event entry
+ *
+ * Search through the topology change event list and issues sas device pg0
+ * requests for the newly added device and reserved entries in tables
+ *
+ * Returns nothing
+ */
+static void
+_mapping_get_dev_info(struct mps_softc *sc,
+    struct _map_topology_change *topo_change)
+{
+	u16 ioc_pg8_flags = le16toh(sc->ioc_pg8.Flags);
+	Mpi2ConfigReply_t mpi_reply;
+	Mpi2SasDevicePage0_t sas_device_pg0;
+	u8 entry, enc_idx, phy_idx;
+	u32 map_idx, index, device_info;
+	struct _map_phy_change *phy_change, *tmp_phy_change;
+	uint64_t sas_address;
+	struct enc_mapping_table *et_entry;
+	struct dev_mapping_table *mt_entry;
+	u8 add_code = MPI2_EVENT_SAS_TOPO_RC_TARG_ADDED;
+	int rc;
+
+	for (entry = 0; entry < topo_change->num_entries; entry++) {
+		phy_change = &topo_change->phy_details[entry];
+		if (phy_change->is_processed || !phy_change->dev_handle ||
+		    phy_change->reason != MPI2_EVENT_SAS_TOPO_RC_TARG_ADDED)
+			continue;
+		if (mps_config_get_sas_device_pg0(sc, &mpi_reply,
+		    &sas_device_pg0, MPI2_SAS_DEVICE_PGAD_FORM_HANDLE,
+		    phy_change->dev_handle)) {
+			phy_change->is_processed = 1;
+			continue;
+		}
+
+		device_info = le32toh(sas_device_pg0.DeviceInfo);
+		if ((ioc_pg8_flags & MPI2_IOCPAGE8_FLAGS_MASK_MAPPING_MODE) ==
+		    MPI2_IOCPAGE8_FLAGS_DEVICE_PERSISTENCE_MAPPING) {
+			if ((device_info & MPI2_SAS_DEVICE_INFO_END_DEVICE) &&
+			    (device_info & MPI2_SAS_DEVICE_INFO_SATA_DEVICE)) {
+				rc = mpssas_get_sas_address_for_sata_disk(sc,
+				    &sas_address, phy_change->dev_handle,
+				    device_info);
+				if (rc) {
+					printf("%s: failed to compute the "
+					    "hashed SAS Address for SATA "
+					    "device with handle 0x%04x\n",
+					    __func__, phy_change->dev_handle);
+					sas_address =
+					    sas_device_pg0.SASAddress.High;
+					sas_address = (sas_address << 32) |
+					    sas_device_pg0.SASAddress.Low;
+				}
+				mps_dprint(sc, MPS_INFO, "SAS Address for SATA "
+					   "device = %jx\n", sas_address);
+			} else {
+				sas_address =
+					sas_device_pg0.SASAddress.High;
+				sas_address = (sas_address << 32) |
+					sas_device_pg0.SASAddress.Low;
+			}
+		} else {
+			sas_address = sas_device_pg0.SASAddress.High;
+			sas_address = (sas_address << 32) |
+			   sas_device_pg0.SASAddress.Low;
+		}
+		phy_change->physical_id = sas_address;
+		phy_change->slot = le16toh(sas_device_pg0.Slot);
+		phy_change->device_info =
+		    le32toh(sas_device_pg0.DeviceInfo);
+
+		if ((ioc_pg8_flags & MPI2_IOCPAGE8_FLAGS_MASK_MAPPING_MODE) ==
+		    MPI2_IOCPAGE8_FLAGS_ENCLOSURE_SLOT_MAPPING) {
+			enc_idx = _mapping_get_enc_idx_from_handle(sc,
+			    topo_change->enc_handle);
+			if (enc_idx == MPS_ENCTABLE_BAD_IDX) {
+				phy_change->is_processed = 1;
+				printf("%s: failed to add the device with "
+				    "handle 0x%04x because the enclosure is "
+				    "not in the mapping table\n", __func__,
+				    phy_change->dev_handle);
+				continue;
+			}
+			if (!((phy_change->device_info &
+			    MPI2_SAS_DEVICE_INFO_END_DEVICE) &&
+			    (phy_change->device_info &
+			    (MPI2_SAS_DEVICE_INFO_SSP_TARGET |
+			    MPI2_SAS_DEVICE_INFO_STP_TARGET |
+			    MPI2_SAS_DEVICE_INFO_SATA_DEVICE)))) {
+				phy_change->is_processed = 1;
+				continue;
+			}
+			et_entry = &sc->enclosure_table[enc_idx];
+			if (et_entry->start_index != MPS_MAPTABLE_BAD_IDX)
+				continue;
+			if (!topo_change->exp_handle) {
+				map_idx	= sc->num_rsvd_entries;
+				et_entry->start_index = map_idx;
+			} else {
+				map_idx = _mapping_find_enc_map_space(sc,
+				    et_entry);
+				et_entry->start_index = map_idx;
+				if (et_entry->start_index ==
+				    MPS_MAPTABLE_BAD_IDX) {
+					phy_change->is_processed = 1;
+					for (phy_idx = 0; phy_idx <
+					    topo_change->num_entries;
+					    phy_idx++) {
+						tmp_phy_change =
+						    &topo_change->phy_details
+						    [phy_idx];
+						if (tmp_phy_change->reason ==
+						    add_code)
+							tmp_phy_change->
+							    is_processed = 1;
+					}
+					break;
+				}
+			}
+			mt_entry = &sc->mapping_table[map_idx];
+			for (index = map_idx; index < (et_entry->num_slots
+			    + map_idx); index++, mt_entry++) {
+				mt_entry->device_info = MPS_DEV_RESERVED;
+				mt_entry->physical_id = et_entry->enclosure_id;
+				mt_entry->phy_bits = et_entry->phy_bits;
+			}
+		}
+	}
+}
+
+/**
+ * _mapping_set_mid_to_eid -set map table data from enclosure table
+ * @sc: per adapter object
+ * @et_entry: enclosure entry
+ *
+ * Returns nothing
+ */
+static inline void
+_mapping_set_mid_to_eid(struct mps_softc *sc,
+    struct enc_mapping_table *et_entry)
+{
+	struct dev_mapping_table *mt_entry;
+	u16 slots = et_entry->num_slots, map_idx;
+	u32 start_idx = et_entry->start_index;
+	if (start_idx != MPS_MAPTABLE_BAD_IDX) {
+		mt_entry = &sc->mapping_table[start_idx];
+		for (map_idx = 0; map_idx < slots; map_idx++, mt_entry++)
+			mt_entry->physical_id = et_entry->enclosure_id;
+	}
+}
+
+/**
+ * _mapping_clear_removed_entries - mark the entries to be cleared
+ * @sc: per adapter object
+ *
+ * Search through the removal table and mark the entries which needs to be
+ * flushed to DPM and also updates the map table and enclosure table by
+ * clearing the corresponding entries.
+ *
+ * Returns nothing
+ */
+static void
+_mapping_clear_removed_entries(struct mps_softc *sc)
+{
+	u32 remove_idx;
+	struct map_removal_table *remove_entry;
+	Mpi2DriverMap0Entry_t *dpm_entry;
+	u8 done_flag = 0, num_entries, m, i;
+	struct enc_mapping_table *et_entry, *from, *to;
+	u16 ioc_pg8_flags = le16toh(sc->ioc_pg8.Flags);
+
+	if (sc->is_dpm_enable) {
+		remove_entry = sc->removal_table;
+		for (remove_idx = 0; remove_idx < sc->max_devices;
+		    remove_idx++, remove_entry++) {
+			if (remove_entry->dpm_entry_num != MPS_DPM_BAD_IDX) {
+				dpm_entry = (Mpi2DriverMap0Entry_t *)
+				    ((u8 *) sc->dpm_pg0 +
+				    sizeof(MPI2_CONFIG_EXTENDED_PAGE_HEADER));
+				dpm_entry += remove_entry->dpm_entry_num;
+				dpm_entry->PhysicalIdentifier.Low = 0;
+				dpm_entry->PhysicalIdentifier.High = 0;
+				dpm_entry->DeviceIndex = 0;
+				dpm_entry->MappingInformation = 0;
+				dpm_entry->PhysicalBitsMapping = 0;
+				sc->dpm_flush_entry[remove_entry->
+				    dpm_entry_num] = 1;
+				sc->dpm_entry_used[remove_entry->dpm_entry_num]
+				    = 0;
+				remove_entry->dpm_entry_num = MPS_DPM_BAD_IDX;
+			}
+		}
+	}
+	if ((ioc_pg8_flags & MPI2_IOCPAGE8_FLAGS_MASK_MAPPING_MODE) ==
+	    MPI2_IOCPAGE8_FLAGS_ENCLOSURE_SLOT_MAPPING) {
+		num_entries = sc->num_enc_table_entries;
+		while (!done_flag) {
+			done_flag = 1;
+			et_entry = sc->enclosure_table;
+			for (i = 0; i < num_entries; i++, et_entry++) {
+				if (!et_entry->enc_handle && et_entry->
+				    init_complete) {
+					done_flag = 0;
+					if (i != (num_entries - 1)) {
+						from = &sc->enclosure_table
+						    [i+1];
+						to = &sc->enclosure_table[i];
+						for (m = i; m < (num_entries -
+						    1); m++, from++, to++) {
+							_mapping_set_mid_to_eid
+							    (sc, to);
+							*to = *from;
+						}
+						_mapping_clear_enc_entry(to);
+						sc->num_enc_table_entries--;
+						num_entries =
+						    sc->num_enc_table_entries;
+					} else {
+						_mapping_clear_enc_entry
+						    (et_entry);
+						sc->num_enc_table_entries--;
+						num_entries =
+						    sc->num_enc_table_entries;
+					}
+				}
+			}
+		}
+	}
+}
+
+/**
+ * _mapping_add_new_device -Add the new device into mapping table
+ * @sc: per adapter object
+ * @topo_change: Topology change event entry
+ *
+ * Search through the topology change event list and updates map table,
+ * enclosure table and DPM pages for for the newly added devices.
+ *
+ * Returns nothing
+ */
+static void
+_mapping_add_new_device(struct mps_softc *sc,
+    struct _map_topology_change *topo_change)
+{
+	u8 enc_idx, missing_cnt, is_removed = 0;
+	u16 dpm_idx;
+	u32 search_idx, map_idx;
+	u32 entry;
+	struct dev_mapping_table *mt_entry;
+	struct enc_mapping_table *et_entry;
+	struct _map_phy_change *phy_change;
+	u16 ioc_pg8_flags = le16toh(sc->ioc_pg8.Flags);
+	Mpi2DriverMap0Entry_t *dpm_entry;
+	uint64_t temp64_var;
+	u8 map_shift = MPI2_DRVMAP0_MAPINFO_SLOT_SHIFT;
+	u8 hdr_sz = sizeof(MPI2_CONFIG_EXTENDED_PAGE_HEADER);
+	u16 max_num_phy_ids = le16toh(sc->ioc_pg8.MaxNumPhysicalMappedIDs);
+
+	for (entry = 0; entry < topo_change->num_entries; entry++) {
+		phy_change = &topo_change->phy_details[entry];
+		if (phy_change->is_processed)
+			continue;
+		if (phy_change->reason != MPI2_EVENT_SAS_TOPO_RC_TARG_ADDED ||
+		    !phy_change->dev_handle) {
+			phy_change->is_processed = 1;
+			continue;
+		}
+		if ((ioc_pg8_flags & MPI2_IOCPAGE8_FLAGS_MASK_MAPPING_MODE) ==
+		    MPI2_IOCPAGE8_FLAGS_ENCLOSURE_SLOT_MAPPING) {
+			enc_idx = _mapping_get_enc_idx_from_handle
+			    (sc, topo_change->enc_handle);
+			if (enc_idx == MPS_ENCTABLE_BAD_IDX) {
+				phy_change->is_processed = 1;
+				printf("%s: failed to add the device with "
+				    "handle 0x%04x because the enclosure is "
+				    "not in the mapping table\n", __func__,
+				    phy_change->dev_handle);
+				continue;
+			}
+			et_entry = &sc->enclosure_table[enc_idx];
+			if (et_entry->start_index == MPS_MAPTABLE_BAD_IDX) {
+				phy_change->is_processed = 1;
+				if (!sc->mt_full_retry) {
+					sc->mt_add_device_failed = 1;
+					continue;
+				}
+				printf("%s: failed to add the device with "
+				    "handle 0x%04x because there is no free "
+				    "space available in the mapping table\n",
+				    __func__, phy_change->dev_handle);
+				continue;
+			}
+			map_idx = et_entry->start_index + phy_change->slot -
+			    et_entry->start_slot;
+			mt_entry = &sc->mapping_table[map_idx];
+			mt_entry->physical_id = phy_change->physical_id;
+			mt_entry->channel = 0;
+			mt_entry->id = map_idx;
+			mt_entry->dev_handle = phy_change->dev_handle;
+			mt_entry->missing_count = 0;
+			mt_entry->dpm_entry_num = et_entry->dpm_entry_num;
+			mt_entry->device_info = phy_change->device_info |
+			    (MPS_DEV_RESERVED | MPS_MAP_IN_USE);
+			if (sc->is_dpm_enable) {
+				dpm_idx = et_entry->dpm_entry_num;
+				if (dpm_idx == MPS_DPM_BAD_IDX)
+					dpm_idx = _mapping_get_dpm_idx_from_id
+					    (sc, et_entry->enclosure_id,
+					     et_entry->phy_bits);
+				if (dpm_idx == MPS_DPM_BAD_IDX) {
+					dpm_idx = _mapping_get_free_dpm_idx(sc);
+					if (dpm_idx != MPS_DPM_BAD_IDX) {
+						dpm_entry =
+						    (Mpi2DriverMap0Entry_t *)
+						    ((u8 *) sc->dpm_pg0 +
+						     hdr_sz);
+						dpm_entry += dpm_idx;
+						dpm_entry->
+						    PhysicalIdentifier.Low =
+						    (0xFFFFFFFF &
+						    et_entry->enclosure_id);
+						dpm_entry->
+						    PhysicalIdentifier.High =
+						    ( et_entry->enclosure_id
+						     >> 32);
+						dpm_entry->DeviceIndex =
+						    (U16)et_entry->start_index;
+						dpm_entry->MappingInformation =
+							et_entry->num_slots;
+						dpm_entry->MappingInformation
+						    <<= map_shift;
+						dpm_entry->PhysicalBitsMapping
+						    = et_entry->phy_bits;
+						et_entry->dpm_entry_num =
+						    dpm_idx;
+		/* FIXME Do I need to set the dpm_idxin mt_entry too */
+						sc->dpm_entry_used[dpm_idx] = 1;
+						sc->dpm_flush_entry[dpm_idx] =
+						    1;
+						phy_change->is_processed = 1;
+					} else {
+						phy_change->is_processed = 1;
+						printf("%s: failed to add the "
+						    "device with handle 0x%04x "
+						    "to persistent table "
+						    "because there is no free "
+						    "space available\n",
+						    __func__,
+						    phy_change->dev_handle);
+					}
+				} else {
+					et_entry->dpm_entry_num = dpm_idx;
+					mt_entry->dpm_entry_num = dpm_idx;
+				}
+			}
+			/* FIXME Why not mt_entry too? */
+			et_entry->init_complete = 1;
+		} else if ((ioc_pg8_flags &
+		    MPI2_IOCPAGE8_FLAGS_MASK_MAPPING_MODE) ==
+		    MPI2_IOCPAGE8_FLAGS_DEVICE_PERSISTENCE_MAPPING) {
+			map_idx = _mapping_get_mt_idx_from_id
+			    (sc, phy_change->physical_id);
+			if (map_idx == MPS_MAPTABLE_BAD_IDX) {
+				search_idx = sc->num_rsvd_entries;
+				if (topo_change->exp_handle)
+					search_idx += max_num_phy_ids;
+				map_idx = _mapping_get_free_mt_idx(sc,
+				    search_idx);
+			}
+			if (map_idx == MPS_MAPTABLE_BAD_IDX) {
+				map_idx = _mapping_get_high_missing_mt_idx(sc);
+				if (map_idx != MPS_MAPTABLE_BAD_IDX) {
+					mt_entry = &sc->mapping_table[map_idx];
+					if (mt_entry->dev_handle) {
+						_mapping_add_to_removal_table
+						    (sc, mt_entry->dev_handle,
+						     0);
+						is_removed = 1;
+					}
+					mt_entry->init_complete = 0;
+				}
+			}
+			if (map_idx != MPS_MAPTABLE_BAD_IDX) {
+				mt_entry = &sc->mapping_table[map_idx];
+				mt_entry->physical_id = phy_change->physical_id;
+				mt_entry->channel = 0;
+				mt_entry->id = map_idx;
+				mt_entry->dev_handle = phy_change->dev_handle;
+				mt_entry->missing_count = 0;
+				mt_entry->device_info = phy_change->device_info
+				    | (MPS_DEV_RESERVED | MPS_MAP_IN_USE);
+			} else {
+				phy_change->is_processed = 1;
+				if (!sc->mt_full_retry) {
+					sc->mt_add_device_failed = 1;
+					continue;
+				}
+				printf("%s: failed to add the device with "
+				    "handle 0x%04x because there is no free "
+				    "space available in the mapping table\n",
+				    __func__, phy_change->dev_handle);
+				continue;
+			}
+			if (sc->is_dpm_enable) {
+				if (mt_entry->dpm_entry_num !=
+				    MPS_DPM_BAD_IDX) {
+					dpm_idx = mt_entry->dpm_entry_num;
+					dpm_entry = (Mpi2DriverMap0Entry_t *)
+					    ((u8 *)sc->dpm_pg0 + hdr_sz);
+					dpm_entry += dpm_idx;
+					missing_cnt = dpm_entry->
+					    MappingInformation &
+					    MPI2_DRVMAP0_MAPINFO_MISSING_MASK;
+					temp64_var = dpm_entry->
+					    PhysicalIdentifier.High;
+					temp64_var = (temp64_var << 32) |
+					   dpm_entry->PhysicalIdentifier.Low;
+					if ((mt_entry->physical_id ==
+					    temp64_var) && !missing_cnt)
+						mt_entry->init_complete = 1;
+				} else {
+					dpm_idx = _mapping_get_free_dpm_idx(sc);
+					mt_entry->init_complete = 0;
+				}
+				if (dpm_idx != MPS_DPM_BAD_IDX &&
+				    !mt_entry->init_complete) {
+					mt_entry->init_complete = 1;
+					mt_entry->dpm_entry_num = dpm_idx;
+					dpm_entry = (Mpi2DriverMap0Entry_t *)
+					    ((u8 *)sc->dpm_pg0 + hdr_sz);
+					dpm_entry += dpm_idx;
+					dpm_entry->PhysicalIdentifier.Low =
+					    (0xFFFFFFFF &
+					    mt_entry->physical_id);
+					dpm_entry->PhysicalIdentifier.High =
+					    (mt_entry->physical_id >> 32);
+					dpm_entry->DeviceIndex = (U16) map_idx;
+					dpm_entry->MappingInformation = 0;
+					dpm_entry->PhysicalBitsMapping = 0;
+					sc->dpm_entry_used[dpm_idx] = 1;
+					sc->dpm_flush_entry[dpm_idx] = 1;
+					phy_change->is_processed = 1;
+				} else if (dpm_idx == MPS_DPM_BAD_IDX) {
+						phy_change->is_processed = 1;
+						printf("%s: failed to add the "
+						    "device with handle 0x%04x "
+						    "to persistent table "
+						    "because there is no free "
+						    "space available\n",
+						    __func__,
+						    phy_change->dev_handle);
+				}
+			}
+			mt_entry->init_complete = 1;
+		}
+
+		phy_change->is_processed = 1;
+	}
+	if (is_removed)
+		_mapping_clear_removed_entries(sc);
+}
+
+/**
+ * _mapping_flush_dpm_pages -Flush the DPM pages to NVRAM
+ * @sc: per adapter object
+ *
+ * Returns nothing
+ */
+static void
+_mapping_flush_dpm_pages(struct mps_softc *sc)
+{
+	Mpi2DriverMap0Entry_t *dpm_entry;
+	Mpi2ConfigReply_t mpi_reply;
+	Mpi2DriverMappingPage0_t config_page;
+	u16 entry_num;
+
+	for (entry_num = 0; entry_num < sc->max_dpm_entries; entry_num++) {
+		if (!sc->dpm_flush_entry[entry_num])
+			continue;
+		memset(&config_page, 0, sizeof(Mpi2DriverMappingPage0_t));
+		memcpy(&config_page.Header, (u8 *)sc->dpm_pg0,
+		    sizeof(MPI2_CONFIG_EXTENDED_PAGE_HEADER));
+		dpm_entry = (Mpi2DriverMap0Entry_t *) ((u8 *)sc->dpm_pg0 +
+		    sizeof(MPI2_CONFIG_EXTENDED_PAGE_HEADER));
+		dpm_entry += entry_num;
+		dpm_entry->MappingInformation = htole16(dpm_entry->
+		    MappingInformation);
+		dpm_entry->DeviceIndex = htole16(dpm_entry->DeviceIndex);
+		dpm_entry->PhysicalBitsMapping = htole32(dpm_entry->
+		    PhysicalBitsMapping);
+		memcpy(&config_page.Entry, (u8 *)dpm_entry,
+		    sizeof(Mpi2DriverMap0Entry_t));
+		/* TODO-How to handle failed writes? */
+		if (mps_config_set_dpm_pg0(sc, &mpi_reply, &config_page,
+		    entry_num)) {
+			printf("%s: write of dpm entry %d for device failed\n",
+			     __func__, entry_num);
+		} else
+			sc->dpm_flush_entry[entry_num] = 0;
+		dpm_entry->MappingInformation = le16toh(dpm_entry->
+		    MappingInformation);
+		dpm_entry->DeviceIndex = le16toh(dpm_entry->DeviceIndex);
+		dpm_entry->PhysicalBitsMapping = le32toh(dpm_entry->
+		    PhysicalBitsMapping);
+	}
+}
+
+/**
+ * _mapping_allocate_memory- allocates the memory required for mapping tables
+ * @sc: per adapter object
+ *
+ * Allocates the memory for all the tables required for host mapping
+ *
+ * Return 0 on success or non-zero on failure.
+ */
+int
+mps_mapping_allocate_memory(struct mps_softc *sc)
+{
+	uint32_t dpm_pg0_sz;
+
+	sc->mapping_table = malloc((sizeof(struct dev_mapping_table) *
+	    sc->max_devices), M_MPT2, M_ZERO|M_NOWAIT);
+	if (!sc->mapping_table)
+		goto free_resources;
+
+	sc->removal_table = malloc((sizeof(struct map_removal_table) *
+	    sc->max_devices), M_MPT2, M_ZERO|M_NOWAIT);
+	if (!sc->removal_table)
+		goto free_resources;
+
+	sc->enclosure_table = malloc((sizeof(struct enc_mapping_table) *
+	    sc->max_enclosures), M_MPT2, M_ZERO|M_NOWAIT);
+	if (!sc->enclosure_table)
+		goto free_resources;
+
+	sc->dpm_entry_used = malloc((sizeof(u8) * sc->max_dpm_entries),
+	    M_MPT2, M_ZERO|M_NOWAIT);
+	if (!sc->dpm_entry_used)
+		goto free_resources;
+
+	sc->dpm_flush_entry = malloc((sizeof(u8) * sc->max_dpm_entries),
+	    M_MPT2, M_ZERO|M_NOWAIT);
+	if (!sc->dpm_flush_entry)
+		goto free_resources;
+
+	dpm_pg0_sz = sizeof(MPI2_CONFIG_EXTENDED_PAGE_HEADER) +
+	    (sc->max_dpm_entries * sizeof(MPI2_CONFIG_PAGE_DRIVER_MAP0_ENTRY));
+
+	sc->dpm_pg0 = malloc(dpm_pg0_sz, M_MPT2, M_ZERO|M_NOWAIT);
+	if (!sc->dpm_pg0) {
+		printf("%s: memory alloc failed for dpm page; disabling dpm\n",
+		    __func__);
+		sc->is_dpm_enable = 0;
+	}
+
+	return 0;
+
+free_resources:
+	free(sc->mapping_table, M_MPT2);
+	free(sc->removal_table, M_MPT2);
+	free(sc->enclosure_table, M_MPT2);
+	free(sc->dpm_entry_used, M_MPT2);
+	free(sc->dpm_flush_entry, M_MPT2);
+	free(sc->dpm_pg0, M_MPT2);
+	printf("%s: device initialization failed due to failure in mapping "
+	    "table memory allocation\n", __func__);
+	return -1;
+}
+
+/**
+ * mps_mapping_free_memory- frees the memory allocated for mapping tables
+ * @sc: per adapter object
+ *
+ * Returns nothing.
+ */
+void
+mps_mapping_free_memory(struct mps_softc *sc)
+{
+	free(sc->mapping_table, M_MPT2);
+	free(sc->removal_table, M_MPT2);
+	free(sc->enclosure_table, M_MPT2);
+	free(sc->dpm_entry_used, M_MPT2);
+	free(sc->dpm_flush_entry, M_MPT2);
+	free(sc->dpm_pg0, M_MPT2);
+}
+
+
+static void
+_mapping_process_dpm_pg0(struct mps_softc *sc)
+{
+	u8 missing_cnt, enc_idx;
+	u16 slot_id, entry_num, num_slots;
+	u32 map_idx, dev_idx, start_idx, end_idx;
+	struct dev_mapping_table *mt_entry;
+	Mpi2DriverMap0Entry_t *dpm_entry;
+	u16 ioc_pg8_flags = le16toh(sc->ioc_pg8.Flags);
+	u16 max_num_phy_ids = le16toh(sc->ioc_pg8.MaxNumPhysicalMappedIDs);
+	struct enc_mapping_table *et_entry;
+	u64 physical_id;
+	u32 phy_bits = 0;
+
+	if (sc->ir_firmware)
+		_mapping_get_ir_maprange(sc, &start_idx, &end_idx);
+
+	dpm_entry = (Mpi2DriverMap0Entry_t *) ((uint8_t *) sc->dpm_pg0 +
+	    sizeof(MPI2_CONFIG_EXTENDED_PAGE_HEADER));
+	for (entry_num = 0; entry_num < sc->max_dpm_entries; entry_num++, 
+	    dpm_entry++) {
+		physical_id = dpm_entry->PhysicalIdentifier.High;
+		physical_id = (physical_id << 32) | 
+		    dpm_entry->PhysicalIdentifier.Low;
+		if (!physical_id) {
+			sc->dpm_entry_used[entry_num] = 0;
+			continue;
+		}
+		sc->dpm_entry_used[entry_num] = 1;
+		dpm_entry->MappingInformation = le16toh(dpm_entry->
+		    MappingInformation);
+		missing_cnt = dpm_entry->MappingInformation &
+		    MPI2_DRVMAP0_MAPINFO_MISSING_MASK;
+		dev_idx = le16toh(dpm_entry->DeviceIndex);
+		phy_bits = le32toh(dpm_entry->PhysicalBitsMapping);
+		if (sc->ir_firmware && (dev_idx >= start_idx) &&
+		    (dev_idx <= end_idx)) {
+			mt_entry = &sc->mapping_table[dev_idx];
+			mt_entry->physical_id = dpm_entry->PhysicalIdentifier.High;
+			mt_entry->physical_id = (mt_entry->physical_id << 32) |
+			    dpm_entry->PhysicalIdentifier.Low;
+			mt_entry->channel = MPS_RAID_CHANNEL;
+			mt_entry->id = dev_idx;
+			mt_entry->missing_count = missing_cnt;
+			mt_entry->dpm_entry_num = entry_num;
+			mt_entry->device_info = MPS_DEV_RESERVED;
+			continue;
+		}
+		if ((ioc_pg8_flags & MPI2_IOCPAGE8_FLAGS_MASK_MAPPING_MODE) ==
+		    MPI2_IOCPAGE8_FLAGS_ENCLOSURE_SLOT_MAPPING) {
+			if (dev_idx <  (sc->num_rsvd_entries +
+			    max_num_phy_ids)) {
+				slot_id = 0;
+				if (ioc_pg8_flags &
+				    MPI2_IOCPAGE8_FLAGS_DA_START_SLOT_1)
+					slot_id = 1;
+				num_slots = max_num_phy_ids;
+			} else {
+				slot_id = 0;
+				num_slots = dpm_entry->MappingInformation &
+				    MPI2_DRVMAP0_MAPINFO_SLOT_MASK;
+				num_slots >>= MPI2_DRVMAP0_MAPINFO_SLOT_SHIFT;
+			}
+			enc_idx = sc->num_enc_table_entries;
+			if (enc_idx >= sc->max_enclosures) {
+				printf("%s: enclosure entries exceed max "
+				    "enclosures of %d\n", __func__,
+				    sc->max_enclosures);
+				break;
+			}
+			sc->num_enc_table_entries++;
+			et_entry = &sc->enclosure_table[enc_idx];
+			physical_id = dpm_entry->PhysicalIdentifier.High;
+			et_entry->enclosure_id = (physical_id << 32) |
+			    dpm_entry->PhysicalIdentifier.Low;
+			et_entry->start_index = dev_idx;
+			et_entry->dpm_entry_num = entry_num;
+			et_entry->num_slots = num_slots;
+			et_entry->start_slot = slot_id;
+			et_entry->missing_count = missing_cnt;
+			et_entry->phy_bits = phy_bits;
+
+			mt_entry = &sc->mapping_table[dev_idx];
+			for (map_idx = dev_idx; map_idx < (dev_idx + num_slots);
+			    map_idx++, mt_entry++) {
+				if (mt_entry->dpm_entry_num !=
+				    MPS_DPM_BAD_IDX) {
+					printf("%s: conflict in mapping table "
+					    "for enclosure %d\n", __func__,
+					    enc_idx);
+					break;
+				}
+				physical_id = dpm_entry->PhysicalIdentifier.High;
+				mt_entry->physical_id = (physical_id << 32) |
+				    dpm_entry->PhysicalIdentifier.Low;
+				mt_entry->phy_bits = phy_bits;
+				mt_entry->channel = 0;
+				mt_entry->id = dev_idx;
+				mt_entry->dpm_entry_num = entry_num;
+				mt_entry->missing_count = missing_cnt;
+				mt_entry->device_info = MPS_DEV_RESERVED;
+			}
+		} else if ((ioc_pg8_flags &
+		    MPI2_IOCPAGE8_FLAGS_MASK_MAPPING_MODE) ==
+		    MPI2_IOCPAGE8_FLAGS_DEVICE_PERSISTENCE_MAPPING) {
+			map_idx = dev_idx;
+			mt_entry = &sc->mapping_table[map_idx];
+			if (mt_entry->dpm_entry_num != MPS_DPM_BAD_IDX) {
+				printf("%s: conflict in mapping table for "
+				    "device %d\n", __func__, map_idx);
+				break;
+			}
+			physical_id = dpm_entry->PhysicalIdentifier.High;
+			mt_entry->physical_id = (physical_id << 32) |
+			    dpm_entry->PhysicalIdentifier.Low;
+			mt_entry->phy_bits = phy_bits;
+			mt_entry->channel = 0;
+			mt_entry->id = dev_idx;
+			mt_entry->missing_count = missing_cnt;
+			mt_entry->dpm_entry_num = entry_num;
+			mt_entry->device_info = MPS_DEV_RESERVED;
+		}
+	} /*close the loop for DPM table */
+}
+
+/*
+ * mps_mapping_check_devices - start of the day check for device availabilty
+ * @sc: per adapter object
+ * @sleep_flag: Flag indicating whether this function can sleep or not
+ *
+ * Returns nothing.
+ */
+void
+mps_mapping_check_devices(struct mps_softc *sc, int sleep_flag)
+{
+	u32 i;
+/*	u32 cntdn, i;
+	u32 timeout = 60;*/
+	struct dev_mapping_table *mt_entry;
+	u16 ioc_pg8_flags = le16toh(sc->ioc_pg8.Flags);
+	struct enc_mapping_table *et_entry;
+	u32 start_idx, end_idx;
+
+	/* We need to ucomment this when this function is called
+	 * from the port enable complete */
+#if 0
+	sc->track_mapping_events = 0;
+	cntdn = (sleep_flag == CAN_SLEEP) ? 1000*timeout : 2000*timeout;
+	do {
+		if (!sc->pending_map_events)
+			break;
+		if (sleep_flag == CAN_SLEEP)
+			pause("mps_pause", (hz/1000));/* 1msec sleep */
+		else
+			DELAY(500); /* 500 useconds delay */
+	} while (--cntdn);
+
+
+	if (!cntdn)
+		printf("%s: there are %d"
+		    " pending events after %d seconds of delay\n",
+		    __func__, sc->pending_map_events, timeout);
+#endif
+	sc->pending_map_events = 0;
+
+	if ((ioc_pg8_flags & MPI2_IOCPAGE8_FLAGS_MASK_MAPPING_MODE) ==
+	    MPI2_IOCPAGE8_FLAGS_ENCLOSURE_SLOT_MAPPING) {
+		et_entry = sc->enclosure_table;
+		for (i = 0; i < sc->num_enc_table_entries; i++, et_entry++) {
+			if (!et_entry->init_complete) {
+				if (et_entry->missing_count <
+				    MPS_MAX_MISSING_COUNT) {
+					et_entry->missing_count++;
+					if (et_entry->dpm_entry_num !=
+					    MPS_DPM_BAD_IDX)
+						_mapping_commit_enc_entry(sc,
+						    et_entry);
+				}
+				et_entry->init_complete = 1;
+			}
+		}
+		if (!sc->ir_firmware)
+			return;
+		_mapping_get_ir_maprange(sc, &start_idx, &end_idx);
+		mt_entry = &sc->mapping_table[start_idx];
+		for (i = start_idx; i < (end_idx + 1); i++, mt_entry++) {
+			if (mt_entry->device_info & MPS_DEV_RESERVED
+			    && !mt_entry->physical_id)
+				mt_entry->init_complete = 1;
+			else if (mt_entry->device_info & MPS_DEV_RESERVED) {
+				if (!mt_entry->init_complete) {
+					if (mt_entry->missing_count <
+					    MPS_MAX_MISSING_COUNT) {
+						mt_entry->missing_count++;
+						if (mt_entry->dpm_entry_num !=
+						    MPS_DPM_BAD_IDX)
+						_mapping_commit_map_entry(sc,
+						    mt_entry);
+					}
+					mt_entry->init_complete = 1;
+				}
+			}
+		}
+	} else if ((ioc_pg8_flags & MPI2_IOCPAGE8_FLAGS_MASK_MAPPING_MODE) ==
+	    MPI2_IOCPAGE8_FLAGS_DEVICE_PERSISTENCE_MAPPING) {
+		mt_entry = sc->mapping_table;
+		for (i = 0; i < sc->max_devices; i++, mt_entry++) {
+			if (mt_entry->device_info & MPS_DEV_RESERVED
+			    && !mt_entry->physical_id)
+				mt_entry->init_complete = 1;
+			else if (mt_entry->device_info & MPS_DEV_RESERVED) {
+				if (!mt_entry->init_complete) {
+					if (mt_entry->missing_count <
+					    MPS_MAX_MISSING_COUNT) {
+						mt_entry->missing_count++;
+						if (mt_entry->dpm_entry_num !=
+						    MPS_DPM_BAD_IDX)
+						_mapping_commit_map_entry(sc,
+						    mt_entry);
+					}
+					mt_entry->init_complete = 1;
+				}
+			}
+		}
+	}
+}
+
+
+/**
+ * mps_mapping_is_reinit_required - check whether event replay required
+ * @sc: per adapter object
+ *
+ * Checks the per ioc flags and decide whether reinit of events required
+ *
+ * Returns 1 for reinit of ioc 0 for not.
+ */
+int mps_mapping_is_reinit_required(struct mps_softc *sc)
+{
+	if (!sc->mt_full_retry && sc->mt_add_device_failed) {
+		sc->mt_full_retry = 1;
+		sc->mt_add_device_failed = 0;
+		_mapping_flush_dpm_pages(sc);
+		return 1;
+	}
+	sc->mt_full_retry = 1;
+	return 0;
+}
+
+/**
+ * mps_mapping_initialize - initialize mapping tables
+ * @sc: per adapter object
+ *
+ * Read controller persitant mapping tables into internal data area.
+ *
+ * Return 0 for success or non-zero for failure.
+ */
+int
+mps_mapping_initialize(struct mps_softc *sc)
+{
+	uint16_t volume_mapping_flags, dpm_pg0_sz;
+	uint32_t i;
+	Mpi2ConfigReply_t mpi_reply;
+	int error;
+	uint8_t retry_count;
+	uint16_t ioc_pg8_flags = le16toh(sc->ioc_pg8.Flags);
+
+	/* The additional 1 accounts for the virtual enclosure
+	 * created for the controller
+	 */
+	sc->max_enclosures = sc->facts->MaxEnclosures + 1;
+	sc->max_expanders = sc->facts->MaxSasExpanders;
+	sc->max_volumes = sc->facts->MaxVolumes;
+	sc->max_devices = sc->facts->MaxTargets + sc->max_volumes;
+	sc->pending_map_events = 0;
+	sc->num_enc_table_entries = 0;
+	sc->num_rsvd_entries = 0;
+	sc->num_channels = 1;
+	sc->max_dpm_entries = sc->ioc_pg8.MaxPersistentEntries;
+	sc->is_dpm_enable = (sc->max_dpm_entries) ? 1 : 0;
+	sc->track_mapping_events = 0;
+	
+	if (ioc_pg8_flags & MPI2_IOCPAGE8_FLAGS_DISABLE_PERSISTENT_MAPPING)
+		sc->is_dpm_enable = 0;
+
+	if (ioc_pg8_flags & MPI2_IOCPAGE8_FLAGS_RESERVED_TARGETID_0)
+		sc->num_rsvd_entries = 1;
+
+	volume_mapping_flags = sc->ioc_pg8.IRVolumeMappingFlags &
+	    MPI2_IOCPAGE8_IRFLAGS_MASK_VOLUME_MAPPING_MODE;
+	if (sc->ir_firmware && (volume_mapping_flags ==
+	    MPI2_IOCPAGE8_IRFLAGS_LOW_VOLUME_MAPPING))
+		sc->num_rsvd_entries += sc->max_volumes;
+
+	error = mps_mapping_allocate_memory(sc);
+	if (error)
+		return (error);
+
+	for (i = 0; i < sc->max_devices; i++)
+		_mapping_clear_map_entry(sc->mapping_table + i);
+
+	for (i = 0; i < sc->max_enclosures; i++)
+		_mapping_clear_enc_entry(sc->enclosure_table + i);
+
+	for (i = 0; i < sc->max_devices; i++) {
+		sc->removal_table[i].dev_handle = 0;
+		sc->removal_table[i].dpm_entry_num = MPS_DPM_BAD_IDX;
+	}
+
+	memset(sc->dpm_entry_used, 0, sc->max_dpm_entries);
+	memset(sc->dpm_flush_entry, 0, sc->max_dpm_entries);
+
+	if (sc->is_dpm_enable) {
+		dpm_pg0_sz = sizeof(MPI2_CONFIG_EXTENDED_PAGE_HEADER) +
+		    (sc->max_dpm_entries *
+		     sizeof(MPI2_CONFIG_PAGE_DRIVER_MAP0_ENTRY));
+		retry_count = 0;
+
+retry_read_dpm:
+		if (mps_config_get_dpm_pg0(sc, &mpi_reply, sc->dpm_pg0,
+		    dpm_pg0_sz)) {
+			printf("%s: dpm page read failed; disabling dpm\n",
+			    __func__);
+			if (retry_count < 3) {
+				retry_count++;
+				goto retry_read_dpm;
+			}
+			sc->is_dpm_enable = 0;
+		}
+	}
+
+	if (sc->is_dpm_enable)
+		_mapping_process_dpm_pg0(sc);
+
+	sc->track_mapping_events = 1;
+	return 0;
+}
+
+/**
+ * mps_mapping_exit - clear mapping table and associated memory
+ * @sc: per adapter object
+ *
+ * Returns nothing.
+ */
+void
+mps_mapping_exit(struct mps_softc *sc)
+{
+	_mapping_flush_dpm_pages(sc);
+	mps_mapping_free_memory(sc);
+}
+
+/**
+ * mps_mapping_get_sas_id - assign a target id for sas device
+ * @sc: per adapter object
+ * @sas_address: sas address of the device
+ * @handle: device handle
+ *
+ * Returns valid ID on success or BAD_ID.
+ */
+unsigned int
+mps_mapping_get_sas_id(struct mps_softc *sc, uint64_t sas_address, u16 handle)
+{
+	u32 map_idx;
+	struct dev_mapping_table *mt_entry;
+
+	for (map_idx = 0; map_idx < sc->max_devices; map_idx++) {
+		mt_entry = &sc->mapping_table[map_idx];
+		if (mt_entry->dev_handle == handle && mt_entry->physical_id ==
+		    sas_address)
+			return mt_entry->id;
+	}
+
+	return MPS_MAP_BAD_ID;
+}
+
+/**
+ * mps_mapping_get_sas_id_from_handle - find a target id in mapping table using
+ * only the dev handle.  This is just a wrapper function for the local function
+ * _mapping_get_mt_idx_from_handle.
+ * @sc: per adapter object
+ * @handle: device handle
+ *
+ * Returns valid ID on success or BAD_ID.
+ */
+unsigned int
+mps_mapping_get_sas_id_from_handle(struct mps_softc *sc, u16 handle)
+{
+	return (_mapping_get_mt_idx_from_handle(sc, handle));
+}
+
+/**
+ * mps_mapping_get_raid_id - assign a target id for raid device
+ * @sc: per adapter object
+ * @wwid: world wide identifier for raid volume
+ * @handle: device handle
+ *
+ * Returns valid ID on success or BAD_ID.
+ */
+unsigned int
+mps_mapping_get_raid_id(struct mps_softc *sc, u64 wwid, u16 handle)
+{
+	u32 map_idx;
+	struct dev_mapping_table *mt_entry;
+
+	for (map_idx = 0; map_idx < sc->max_devices; map_idx++) {
+		mt_entry = &sc->mapping_table[map_idx];
+		if (mt_entry->dev_handle == handle && mt_entry->physical_id ==
+		    wwid)
+			return mt_entry->id;
+	}
+
+	return MPS_MAP_BAD_ID;
+}
+
+/**
+ * mps_mapping_get_raid_id_from_handle - find raid device in mapping table
+ * using only the volume dev handle.  This is just a wrapper function for the
+ * local function _mapping_get_ir_mt_idx_from_handle.
+ * @sc: per adapter object
+ * @volHandle: volume device handle
+ *
+ * Returns valid ID on success or BAD_ID.
+ */
+unsigned int
+mps_mapping_get_raid_id_from_handle(struct mps_softc *sc, u16 volHandle)
+{
+	return (_mapping_get_ir_mt_idx_from_handle(sc, volHandle));
+}
+
+/**
+ * mps_mapping_enclosure_dev_status_change_event - handle enclosure events
+ * @sc: per adapter object
+ * @event_data: event data payload
+ *
+ * Return nothing.
+ */
+void
+mps_mapping_enclosure_dev_status_change_event(struct mps_softc *sc,
+    Mpi2EventDataSasEnclDevStatusChange_t *event_data)
+{
+	u8 enc_idx, missing_count;
+	struct enc_mapping_table *et_entry;
+	Mpi2DriverMap0Entry_t *dpm_entry;
+	u16 ioc_pg8_flags = le16toh(sc->ioc_pg8.Flags);
+	u8 map_shift = MPI2_DRVMAP0_MAPINFO_SLOT_SHIFT;
+	u8 update_phy_bits = 0;
+	u32 saved_phy_bits;
+	uint64_t temp64_var;
+
+	if ((ioc_pg8_flags & MPI2_IOCPAGE8_FLAGS_MASK_MAPPING_MODE) !=
+	    MPI2_IOCPAGE8_FLAGS_ENCLOSURE_SLOT_MAPPING)
+		goto out;
+
+	dpm_entry = (Mpi2DriverMap0Entry_t *)((u8 *)sc->dpm_pg0 +
+	    sizeof(MPI2_CONFIG_EXTENDED_PAGE_HEADER));
+
+	if (event_data->ReasonCode == MPI2_EVENT_SAS_ENCL_RC_ADDED) {
+		if (!event_data->NumSlots) {
+			printf("%s: enclosure with handle = 0x%x reported 0 "
+			    "slots\n", __func__,
+			    le16toh(event_data->EnclosureHandle));
+			goto out;
+		}
+		temp64_var = event_data->EnclosureLogicalID.High;
+		temp64_var = (temp64_var << 32) |
+		    event_data->EnclosureLogicalID.Low;
+		enc_idx = _mapping_get_enc_idx_from_id(sc, temp64_var,
+		    event_data->PhyBits);
+		if (enc_idx != MPS_ENCTABLE_BAD_IDX) {
+			et_entry = &sc->enclosure_table[enc_idx];
+			if (et_entry->init_complete &&
+			    !et_entry->missing_count) {
+				printf("%s: enclosure %d is already present "
+				    "with handle = 0x%x\n",__func__, enc_idx,
+				    et_entry->enc_handle);
+				goto out;
+			}
+			et_entry->enc_handle = le16toh(event_data->
+			    EnclosureHandle);
+			et_entry->start_slot = le16toh(event_data->StartSlot);
+			saved_phy_bits = et_entry->phy_bits;
+			et_entry->phy_bits |= le32toh(event_data->PhyBits);
+			if (saved_phy_bits != et_entry->phy_bits)
+				update_phy_bits = 1;
+			if (et_entry->missing_count || update_phy_bits) {
+				et_entry->missing_count = 0;
+				if (sc->is_dpm_enable &&
+				    et_entry->dpm_entry_num !=
+				    MPS_DPM_BAD_IDX) {
+					dpm_entry += et_entry->dpm_entry_num;
+					missing_count =
+					    (u8)(dpm_entry->MappingInformation &
+					    MPI2_DRVMAP0_MAPINFO_MISSING_MASK);
+					if (!et_entry->init_complete && (
+					    missing_count || update_phy_bits)) {
+						dpm_entry->MappingInformation
+						    = et_entry->num_slots;
+						dpm_entry->MappingInformation
+						    <<= map_shift;
+						dpm_entry->PhysicalBitsMapping
+						    = et_entry->phy_bits;
+						sc->dpm_flush_entry[et_entry->
+						    dpm_entry_num] = 1;
+					}
+				}
+			}
+		} else {
+			enc_idx = sc->num_enc_table_entries;
+			if (enc_idx >= sc->max_enclosures) {
+				printf("%s: enclosure can not be added; "
+				    "mapping table is full\n", __func__);
+				goto out;
+			}
+			sc->num_enc_table_entries++;
+			et_entry = &sc->enclosure_table[enc_idx];
+			et_entry->enc_handle = le16toh(event_data->
+			    EnclosureHandle);
+			et_entry->enclosure_id = event_data->
+			    EnclosureLogicalID.High;
+			et_entry->enclosure_id = ( et_entry->enclosure_id << 
+			    32) | event_data->EnclosureLogicalID.Low;
+			et_entry->start_index = MPS_MAPTABLE_BAD_IDX;
+			et_entry->dpm_entry_num = MPS_DPM_BAD_IDX;
+			et_entry->num_slots = le16toh(event_data->NumSlots);
+			et_entry->start_slot = le16toh(event_data->StartSlot);
+			et_entry->phy_bits = le32toh(event_data->PhyBits);
+		}
+		et_entry->init_complete = 1;
+	} else if (event_data->ReasonCode ==
+	    MPI2_EVENT_SAS_ENCL_RC_NOT_RESPONDING) {
+		enc_idx = _mapping_get_enc_idx_from_handle(sc,
+		    le16toh(event_data->EnclosureHandle));
+		if (enc_idx == MPS_ENCTABLE_BAD_IDX) {
+			printf("%s: cannot unmap enclosure %d because it has "
+			    "already been deleted", __func__, enc_idx);
+			goto out;
+		}
+		et_entry = &sc->enclosure_table[enc_idx];
+		if (!et_entry->init_complete) {
+			if (et_entry->missing_count < MPS_MAX_MISSING_COUNT)
+				et_entry->missing_count++;
+			else
+				et_entry->init_complete = 1;
+		}
+		if (!et_entry->missing_count)
+			et_entry->missing_count++;
+		if (sc->is_dpm_enable && !et_entry->init_complete &&
+		    et_entry->dpm_entry_num != MPS_DPM_BAD_IDX) {
+			dpm_entry += et_entry->dpm_entry_num;
+			dpm_entry->MappingInformation = et_entry->num_slots;
+			dpm_entry->MappingInformation <<= map_shift;
+			dpm_entry->MappingInformation |=
+			    et_entry->missing_count;
+			sc->dpm_flush_entry[et_entry->dpm_entry_num] = 1;
+		}
+		et_entry->init_complete = 1;
+	}
+
+out:
+	_mapping_flush_dpm_pages(sc);
+	if (sc->pending_map_events)
+		sc->pending_map_events--;
+}
+
+/**
+ * mps_mapping_topology_change_event - handle topology change events
+ * @sc: per adapter object
+ * @event_data: event data payload
+ *
+ * Returns nothing.
+ */
+void
+mps_mapping_topology_change_event(struct mps_softc *sc,
+    Mpi2EventDataSasTopologyChangeList_t *event_data)
+{
+	struct _map_topology_change topo_change;
+	struct _map_phy_change *phy_change;
+	Mpi2EventSasTopoPhyEntry_t *event_phy_change;
+	u8 i, num_entries;
+
+	topo_change.enc_handle = le16toh(event_data->EnclosureHandle);
+	topo_change.exp_handle = le16toh(event_data->ExpanderDevHandle);
+	num_entries = event_data->NumEntries;
+	topo_change.num_entries = num_entries;
+	topo_change.start_phy_num = event_data->StartPhyNum;
+	topo_change.num_phys = event_data->NumPhys;
+	topo_change.exp_status = event_data->ExpStatus;
+	event_phy_change = event_data->PHY;
+	topo_change.phy_details = NULL;
+
+	if (!num_entries)
+		goto out;
+	phy_change = malloc(sizeof(struct _map_phy_change) * num_entries,
+	    M_MPT2, M_NOWAIT|M_ZERO);
+	topo_change.phy_details = phy_change;
+	if (!phy_change)
+		goto out;
+	for (i = 0; i < num_entries; i++, event_phy_change++, phy_change++) {
+		phy_change->dev_handle = le16toh(event_phy_change->
+		    AttachedDevHandle);
+		phy_change->reason = event_phy_change->PhyStatus &
+		    MPI2_EVENT_SAS_TOPO_RC_MASK;
+	}
+	_mapping_update_missing_count(sc, &topo_change);
+	_mapping_get_dev_info(sc, &topo_change);
+	_mapping_clear_removed_entries(sc);
+	_mapping_add_new_device(sc, &topo_change);
+
+out:
+	free(topo_change.phy_details, M_MPT2);
+	_mapping_flush_dpm_pages(sc);
+	if (sc->pending_map_events)
+		sc->pending_map_events--;
+}
+
+/**
+ * _mapping_check_update_ir_mt_idx - Check and update IR map table index
+ * @sc: per adapter object
+ * @event_data: event data payload
+ * @evt_idx: current event index
+ * @map_idx: current index and the place holder for new map table index
+ * @wwid_table: world wide name for volumes in the element table
+ *
+ * pass through IR events and find whether any events matches and if so
+ * tries to find new index if not returns failure
+ *
+ * Returns 0 on success and 1 on failure
+ */
+static int
+_mapping_check_update_ir_mt_idx(struct mps_softc *sc,
+    Mpi2EventDataIrConfigChangeList_t *event_data, int evt_idx, u32 *map_idx,
+    u64 *wwid_table)
+{
+	struct dev_mapping_table *mt_entry;
+	u32 st_idx, end_idx, mt_idx = *map_idx;
+	u8 match = 0;
+	Mpi2EventIrConfigElement_t *element;
+	u16 element_flags;
+	int i;
+
+	mt_entry = &sc->mapping_table[mt_idx];
+	_mapping_get_ir_maprange(sc, &st_idx, &end_idx);
+search_again:
+	match = 0;
+	for (i = evt_idx + 1; i < event_data->NumElements; i++) {
+		element = (Mpi2EventIrConfigElement_t *)
+		    &event_data->ConfigElement[i];
+		element_flags = le16toh(element->ElementFlags);
+		if ((element_flags &
+		    MPI2_EVENT_IR_CHANGE_EFLAGS_ELEMENT_TYPE_MASK) !=
+		    MPI2_EVENT_IR_CHANGE_EFLAGS_VOLUME_ELEMENT)
+			continue;
+		if (element->ReasonCode == MPI2_EVENT_IR_CHANGE_RC_ADDED ||
+		    element->ReasonCode ==
+		    MPI2_EVENT_IR_CHANGE_RC_VOLUME_CREATED) {
+			if (mt_entry->physical_id == wwid_table[i]) {
+				match = 1;
+				break;
+			}
+		}
+	}
+
+	if (match) {
+		do {
+			mt_idx++;
+			if (mt_idx > end_idx)
+				return 1;
+			mt_entry = &sc->mapping_table[mt_idx];
+		} while (mt_entry->device_info & MPS_MAP_IN_USE);
+		goto search_again;
+	}
+	*map_idx = mt_idx;
+	return 0;
+}
+
+/**
+ * mps_mapping_ir_config_change_event - handle IR config change list events
+ * @sc: per adapter object
+ * @event_data: event data payload
+ *
+ * Returns nothing.
+ */
+void
+mps_mapping_ir_config_change_event(struct mps_softc *sc,
+    Mpi2EventDataIrConfigChangeList_t *event_data)
+{
+	Mpi2EventIrConfigElement_t *element;
+	int i;
+	u64 *wwid_table;
+	u32 map_idx, flags;
+	struct dev_mapping_table *mt_entry;
+	u16 element_flags;
+	u8 log_full_error = 0;
+
+	wwid_table = malloc(sizeof(u64) * event_data->NumElements, M_MPT2,
+	    M_NOWAIT | M_ZERO);
+	if (!wwid_table)
+		goto out;
+	element = (Mpi2EventIrConfigElement_t *)&event_data->ConfigElement[0];
+	flags = le32toh(event_data->Flags);
+	for (i = 0; i < event_data->NumElements; i++, element++) {
+		element_flags = le16toh(element->ElementFlags);
+		if ((element->ReasonCode != MPI2_EVENT_IR_CHANGE_RC_ADDED) &&
+		    (element->ReasonCode != MPI2_EVENT_IR_CHANGE_RC_REMOVED) &&
+		    (element->ReasonCode != MPI2_EVENT_IR_CHANGE_RC_NO_CHANGE)
+		    && (element->ReasonCode !=
+			MPI2_EVENT_IR_CHANGE_RC_VOLUME_CREATED))
+			continue;
+		if ((element_flags &
+		    MPI2_EVENT_IR_CHANGE_EFLAGS_ELEMENT_TYPE_MASK) ==
+		    MPI2_EVENT_IR_CHANGE_EFLAGS_VOLUME_ELEMENT) {
+			mps_config_get_volume_wwid(sc,
+			    le16toh(element->VolDevHandle), &wwid_table[i]);
+			map_idx = _mapping_get_ir_mt_idx_from_wwid(sc,
+			    wwid_table[i]);
+			if (map_idx != MPS_MAPTABLE_BAD_IDX) {
+				mt_entry = &sc->mapping_table[map_idx];
+				mt_entry->device_info |= MPS_MAP_IN_USE;
+			}
+		}
+	}
+	if (flags == MPI2_EVENT_IR_CHANGE_FLAGS_FOREIGN_CONFIG)
+		goto out;
+	else {
+		element = (Mpi2EventIrConfigElement_t *)&event_data->
+		    ConfigElement[0];
+		for (i = 0; i < event_data->NumElements; i++, element++) {
+			if (element->ReasonCode ==
+			    MPI2_EVENT_IR_CHANGE_RC_ADDED ||
+			    element->ReasonCode ==
+			    MPI2_EVENT_IR_CHANGE_RC_VOLUME_CREATED) {
+				map_idx = _mapping_get_ir_mt_idx_from_wwid
+				    (sc, wwid_table[i]);
+				if (map_idx != MPS_MAPTABLE_BAD_IDX) {
+					mt_entry = &sc->mapping_table[map_idx];
+					mt_entry->channel = MPS_RAID_CHANNEL;
+					mt_entry->id = map_idx;
+					mt_entry->dev_handle = le16toh
+					    (element->VolDevHandle);
+					mt_entry->device_info =
+					    MPS_DEV_RESERVED | MPS_MAP_IN_USE;
+					_mapping_update_ir_missing_cnt(sc,
+					    map_idx, element, wwid_table[i]);
+					continue;
+				}
+				map_idx = _mapping_get_free_ir_mt_idx(sc);
+				if (map_idx == MPS_MAPTABLE_BAD_IDX)
+					log_full_error = 1;
+				else if (i < (event_data->NumElements - 1)) {
+					log_full_error =
+					    _mapping_check_update_ir_mt_idx
+					    (sc, event_data, i, &map_idx,
+					     wwid_table);
+				}
+				if (log_full_error) {
+					printf("%s: no space to add the RAID "
+					    "volume with handle 0x%04x in "
+					    "mapping table\n", __func__, le16toh
+					    (element->VolDevHandle));
+					continue;
+				}
+				mt_entry = &sc->mapping_table[map_idx];
+				mt_entry->physical_id = wwid_table[i];
+				mt_entry->channel = MPS_RAID_CHANNEL;
+				mt_entry->id = map_idx;
+				mt_entry->dev_handle = le16toh(element->
+				    VolDevHandle);
+				mt_entry->device_info = MPS_DEV_RESERVED |
+				    MPS_MAP_IN_USE;
+				mt_entry->init_complete = 0;
+				_mapping_update_ir_missing_cnt(sc, map_idx,
+				    element, wwid_table[i]);
+			} else if (element->ReasonCode ==
+			    MPI2_EVENT_IR_CHANGE_RC_REMOVED) {
+				map_idx = _mapping_get_ir_mt_idx_from_wwid(sc,
+				    wwid_table[i]);
+				if (map_idx == MPS_MAPTABLE_BAD_IDX) {
+					printf("%s: failed to remove a volume "
+					    "because it has already been "
+					    "removed\n", __func__);
+					continue;
+				}
+				_mapping_update_ir_missing_cnt(sc, map_idx,
+				    element, wwid_table[i]);
+			} else if (element->ReasonCode ==
+			    MPI2_EVENT_IR_CHANGE_RC_VOLUME_DELETED) {
+				map_idx = _mapping_get_mt_idx_from_handle(sc,
+				    le16toh(element->VolDevHandle));
+				if (map_idx == MPS_MAPTABLE_BAD_IDX) {
+					printf("%s: failed to remove volume "
+					    "with handle 0x%04x because it has "
+					    "already been removed\n", __func__,
+					    le16toh(element->VolDevHandle));
+					continue;
+				}
+				mt_entry = &sc->mapping_table[map_idx];
+				_mapping_update_ir_missing_cnt(sc, map_idx,
+				    element, mt_entry->physical_id);
+			}
+		}
+	}
+
+out:
+	_mapping_flush_dpm_pages(sc);
+	free(wwid_table, M_MPT2);
+	if (sc->pending_map_events)
+		sc->pending_map_events--;
+}
diff -r 52ad4c882614 -r 6b8317170ea4 head/sys/dev/mps/mps_mapping.h
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/head/sys/dev/mps/mps_mapping.h	Wed Feb 01 11:28:20 2012 +0200
@@ -0,0 +1,71 @@
+/*-
+ * Copyright (c) 2011 LSI Corp.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * 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 OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * LSI MPT-Fusion Host Adapter FreeBSD
+ *
+ * $FreeBSD: head/sys/dev/mps/mps_mapping.h 230592 2012-01-26 18:17:21Z ken $
+ */
+
+#ifndef _MPS_MAPPING_H
+#define _MPS_MAPPING_H
+
+/**
+ * struct _map_phy_change - PHY entries recieved in Topology change list
+ * @physical_id: SAS address of the device attached with the associate PHY
+ * @device_info: bitfield provides detailed info about the device
+ * @dev_handle: device handle for the device pointed by this entry
+ * @slot: slot ID
+ * @is_processed: Flag to indicate whether this entry is processed or not
+ */
+struct _map_phy_change {
+	uint64_t	physical_id;
+	uint32_t	device_info;
+	uint16_t	dev_handle;
+	uint16_t	slot;
+	uint8_t	reason;
+	uint8_t	is_processed;
+};
+
+/**
+ * struct _map_topology_change - entries to be removed from mapping table
+ * @dpm_entry_num: index of this device in device persistent map table
+ * @dev_handle: device handle for the device pointed by this entry
+ */
+struct _map_topology_change {
+	uint16_t	enc_handle;
+	uint16_t	exp_handle;
+	uint8_t	num_entries;
+	uint8_t	start_phy_num;
+	uint8_t	num_phys;
+	uint8_t	exp_status;
+	struct _map_phy_change *phy_details;
+};
+
+
+extern int
+mpssas_get_sas_address_for_sata_disk(struct mps_softc *ioc,
+    u64 *sas_address, u16 handle, u32 device_info);
+
+#endif
diff -r 52ad4c882614 -r 6b8317170ea4 head/sys/dev/mps/mps_pci.c
--- a/head/sys/dev/mps/mps_pci.c	Wed Feb 01 11:28:01 2012 +0200
+++ b/head/sys/dev/mps/mps_pci.c	Wed Feb 01 11:28:20 2012 +0200
@@ -25,10 +25,11 @@
  */
 
 #include <sys/cdefs.h>
-__FBSDID("$FreeBSD: head/sys/dev/mps/mps_pci.c 227843 2011-11-22 21:28:20Z marius $");
+__FBSDID("$FreeBSD: head/sys/dev/mps/mps_pci.c 230592 2012-01-26 18:17:21Z ken $");
 
 /* PCI/PCI-X/PCIe bus interface for the LSI MPT2 controllers */
 
+/* TODO Move headers to mpsvar */
 #include <sys/types.h>
 #include <sys/param.h>
 #include <sys/systm.h>
@@ -46,12 +47,17 @@
 
 #include <dev/pci/pcireg.h>
 #include <dev/pci/pcivar.h>
+#include <dev/pci/pci_private.h>
 
 #include <dev/mps/mpi/mpi2_type.h>
 #include <dev/mps/mpi/mpi2.h>
 #include <dev/mps/mpi/mpi2_ioc.h>
 #include <dev/mps/mpi/mpi2_cnfg.h>
+#include <dev/mps/mpi/mpi2_tool.h>
 
+#include <sys/queue.h>
+#include <sys/kthread.h>
+#include <dev/mps/mps_ioctl.h>
 #include <dev/mps/mpsvar.h>
 
 static int	mps_pci_probe(device_t);
@@ -63,15 +69,6 @@
 static int	mps_alloc_msix(struct mps_softc *sc, int msgs);
 static int	mps_alloc_msi(struct mps_softc *sc, int msgs);
 
-int mps_disable_msix = 0;
-TUNABLE_INT("hw.mps.disable_msix", &mps_disable_msix);
-SYSCTL_INT(_hw_mps, OID_AUTO, disable_msix, CTLFLAG_RD, &mps_disable_msix, 0,
-    "Disable MSIX interrupts\n");
-int mps_disable_msi = 0;
-TUNABLE_INT("hw.mps.disable_msi", &mps_disable_msi);
-SYSCTL_INT(_hw_mps, OID_AUTO, disable_msi, CTLFLAG_RD, &mps_disable_msi, 0,
-    "Disable MSI interrupts\n");
-
 static device_method_t mps_methods[] = {
 	DEVMETHOD(device_probe,		mps_pci_probe),
 	DEVMETHOD(device_attach,	mps_pci_attach),
@@ -125,10 +122,24 @@
 	    0xffff, 0xffff, 0, "LSI SAS2208" },
 	{ MPI2_MFGPAGE_VENDORID_LSI, MPI2_MFGPAGE_DEVID_SAS2208_6,
 	    0xffff, 0xffff, 0, "LSI SAS2208" },
-	{ MPI2_MFGPAGE_VENDORID_LSI, MPI2_MFGPAGE_DEVID_SAS2208_7,
-	    0xffff, 0xffff, 0, "LSI SAS2208" },
-	{ MPI2_MFGPAGE_VENDORID_LSI, MPI2_MFGPAGE_DEVID_SAS2208_8,
-	    0xffff, 0xffff, 0, "LSI SAS2208" },
+	{ MPI2_MFGPAGE_VENDORID_LSI, MPI2_MFGPAGE_DEVID_SAS2308_1,
+	    0xffff, 0xffff, 0, "LSI SAS2308" },
+	// Add Customer specific vender/subdevice id before generic
+	// (0xffff) vender/subdevice id.
+	{ MPI2_MFGPAGE_VENDORID_LSI, MPI2_MFGPAGE_DEVID_SAS2308_2,
+	    0x8086, 0x3516, 0, "Intel(R) Integrated RAID Module RMS25JB080" },
+	{ MPI2_MFGPAGE_VENDORID_LSI, MPI2_MFGPAGE_DEVID_SAS2308_2,
+	    0x8086, 0x3517, 0, "Intel(R) Integrated RAID Module RMS25JB040" },
+	{ MPI2_MFGPAGE_VENDORID_LSI, MPI2_MFGPAGE_DEVID_SAS2308_2,
+	    0x8086, 0x3518, 0, "Intel(R) Integrated RAID Module RMS25KB080" },
+	{ MPI2_MFGPAGE_VENDORID_LSI, MPI2_MFGPAGE_DEVID_SAS2308_2,
+	    0x8086, 0x3519, 0, "Intel(R) Integrated RAID Module RMS25KB040" },
+	{ MPI2_MFGPAGE_VENDORID_LSI, MPI2_MFGPAGE_DEVID_SAS2308_2,
+	    0xffff, 0xffff, 0, "LSI SAS2308" },
+	{ MPI2_MFGPAGE_VENDORID_LSI, MPI2_MFGPAGE_DEVID_SAS2308_3,
+	    0xffff, 0xffff, 0, "LSI SAS2308" },
+	{ MPI2_MFGPAGE_VENDORID_LSI, MPI2_MFGPAGE_DEVID_SSS6200,
+	    0xffff, 0xffff, MPS_FLAGS_WD_AVAILABLE, "LSI SSS6200" },
 	{ 0, 0, 0, 0, 0, NULL }
 };
 
@@ -161,7 +172,7 @@
 
 	if ((id = mps_find_ident(dev)) != NULL) {
 		device_set_desc(dev, id->desc);
-		return (BUS_PROBE_DEFAULT);
+		return (BUS_PROBE_VENDOR);
 	}
 	return (ENXIO);
 }
@@ -205,7 +216,7 @@
 	sc->mps_bhandle = rman_get_bushandle(sc->mps_regs_resource);
 
 	/* Allocate the parent DMA tag */
-	if (bus_dma_tag_create( NULL,			/* parent */
+	if (bus_dma_tag_create( bus_get_dma_tag(dev),	/* parent */
 				1, 0,			/* algnmnt, boundary */
 				BUS_SPACE_MAXADDR,	/* lowaddr */
 				BUS_SPACE_MAXADDR,	/* highaddr */
@@ -235,10 +246,10 @@
 
 	dev = sc->mps_dev;
 	error = ENXIO;
-	if ((mps_disable_msix == 0) &&
+	if ((sc->disable_msix == 0) &&
 	    ((msgs = pci_msix_count(dev)) >= MPS_MSI_COUNT))
 		error = mps_alloc_msix(sc, MPS_MSI_COUNT);
-	if ((error != 0) && (mps_disable_msi == 0) &&
+	if ((error != 0) && (sc->disable_msi == 0) &&
 	    ((msgs = pci_msi_count(dev)) >= MPS_MSI_COUNT))
 		error = mps_alloc_msi(sc, MPS_MSI_COUNT);
 
@@ -362,3 +373,20 @@
 	return (error);
 }
 
+int
+mps_pci_restore(struct mps_softc *sc)
+{
+	struct pci_devinfo *dinfo;
+
+	mps_dprint(sc, MPS_TRACE, "%s\n", __func__);
+
+	dinfo = device_get_ivars(sc->mps_dev);
+	if (dinfo == NULL) {
+		mps_dprint(sc, MPS_FAULT, "%s: NULL dinfo\n", __func__);
+		return (EINVAL);
+	}
+
+	pci_cfg_restore(sc->mps_dev, dinfo);
+	return (0);
+}
+
diff -r 52ad4c882614 -r 6b8317170ea4 head/sys/dev/mps/mps_sas.c
--- a/head/sys/dev/mps/mps_sas.c	Wed Feb 01 11:28:01 2012 +0200
+++ b/head/sys/dev/mps/mps_sas.c	Wed Feb 01 11:28:20 2012 +0200
@@ -23,12 +23,42 @@
  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  * SUCH DAMAGE.
  */
+/*-
+ * Copyright (c) 2011 LSI Corp.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * 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 OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * LSI MPT-Fusion Host Adapter FreeBSD
+ *
+ * $FreeBSD: head/sys/dev/mps/mps_sas.c 230592 2012-01-26 18:17:21Z ken $
+ */
 
 #include <sys/cdefs.h>
-__FBSDID("$FreeBSD: head/sys/dev/mps/mps_sas.c 228939 2011-12-28 22:49:28Z mav $");
+__FBSDID("$FreeBSD: head/sys/dev/mps/mps_sas.c 230592 2012-01-26 18:17:21Z ken $");
 
 /* Communications core for LSI MPT2 */
 
+/* TODO Move headers to mpsvar */
 #include <sys/types.h>
 #include <sys/param.h>
 #include <sys/systm.h>
@@ -41,15 +71,21 @@
 #include <sys/malloc.h>
 #include <sys/uio.h>
 #include <sys/sysctl.h>
-#include <sys/sglist.h>
 #include <sys/endian.h>
+#include <sys/queue.h>
+#include <sys/kthread.h>
+#include <sys/taskqueue.h>
+#include <sys/sbuf.h>
 
 #include <machine/bus.h>
 #include <machine/resource.h>
 #include <sys/rman.h>
 
+#include <machine/stdarg.h>
+
 #include <cam/cam.h>
 #include <cam/cam_ccb.h>
+#include <cam/cam_xpt.h>
 #include <cam/cam_debug.h>
 #include <cam/cam_sim.h>
 #include <cam/cam_xpt_sim.h>
@@ -67,186 +103,80 @@
 #include <dev/mps/mpi/mpi2_sas.h>
 #include <dev/mps/mpi/mpi2_cnfg.h>
 #include <dev/mps/mpi/mpi2_init.h>
+#include <dev/mps/mpi/mpi2_tool.h>
+#include <dev/mps/mps_ioctl.h>
 #include <dev/mps/mpsvar.h>
 #include <dev/mps/mps_table.h>
-
-struct mpssas_target {
-	uint16_t	handle;
-	uint8_t		linkrate;
-	uint64_t	devname;
-	uint64_t	sasaddr;
-	uint32_t	devinfo;
-	uint16_t	encl_handle;
-	uint16_t	encl_slot;
-	uint16_t	parent_handle;
-	int		flags;
-#define MPSSAS_TARGET_INABORT	(1 << 0)
-#define MPSSAS_TARGET_INRESET	(1 << 1)
-#define MPSSAS_TARGET_INCHIPRESET (1 << 2)
-#define MPSSAS_TARGET_INRECOVERY 0x7
-	uint16_t	tid;
-};
-
-struct mpssas_softc {
-	struct mps_softc	*sc;
-	u_int			flags;
-#define MPSSAS_IN_DISCOVERY	(1 << 0)
-#define MPSSAS_IN_STARTUP	(1 << 1)
-#define MPSSAS_DISCOVERY_TIMEOUT_PENDING	(1 << 2)
-#define MPSSAS_QUEUE_FROZEN	(1 << 3)
-	struct mpssas_target	*targets;
-	struct cam_devq		*devq;
-	struct cam_sim		*sim;
-	struct cam_path		*path;
-	struct intr_config_hook	sas_ich;
-	struct callout		discovery_callout;
-	u_int			discovery_timeouts;
-	struct mps_event_handle	*mpssas_eh;
-};
-
-struct mpssas_devprobe {
-	struct mps_config_params	params;
-	u_int			state;
-#define MPSSAS_PROBE_DEV1	0x01
-#define MPSSAS_PROBE_DEV2	0x02
-#define MPSSAS_PROBE_PHY	0x03
-#define MPSSAS_PROBE_EXP	0x04
-#define MPSSAS_PROBE_PHY2	0x05
-#define MPSSAS_PROBE_EXP2	0x06
-	struct mpssas_target	target;
-};
+#include <dev/mps/mps_sas.h>
 
 #define MPSSAS_DISCOVERY_TIMEOUT	20
 #define MPSSAS_MAX_DISCOVERY_TIMEOUTS	10 /* 200 seconds */
 
-static MALLOC_DEFINE(M_MPSSAS, "MPSSAS", "MPS SAS memory");
-
-static __inline int mpssas_set_lun(uint8_t *lun, u_int ccblun);
-static struct mpssas_target * mpssas_alloc_target(struct mpssas_softc *,
-    struct mpssas_target *);
-static struct mpssas_target * mpssas_find_target(struct mpssas_softc *, int,
-     uint16_t);
-static void mpssas_announce_device(struct mpssas_softc *,
-     struct mpssas_target *);
-static void mpssas_startup(void *data);
-static void mpssas_discovery_end(struct mpssas_softc *sassc);
+/*
+ * static array to check SCSI OpCode for EEDP protection bits
+ */
+#define	PRO_R MPI2_SCSIIO_EEDPFLAGS_CHECK_REMOVE_OP
+#define	PRO_W MPI2_SCSIIO_EEDPFLAGS_INSERT_OP
+#define	PRO_V MPI2_SCSIIO_EEDPFLAGS_INSERT_OP
+static uint8_t op_code_prot[256] = {
+	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+	0, 0, 0, 0, 0, 0, 0, 0, PRO_R, 0, PRO_W, 0, 0, 0, PRO_W, PRO_V,
+	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+	0, PRO_W, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+	0, 0, 0, 0, 0, 0, 0, 0, PRO_R, 0, PRO_W, 0, 0, 0, PRO_W, PRO_V,
+	0, 0, 0, PRO_W, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+	0, 0, 0, 0, 0, 0, 0, 0, PRO_R, 0, PRO_W, 0, 0, 0, PRO_W, PRO_V,
+	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+};
+
+MALLOC_DEFINE(M_MPSSAS, "MPSSAS", "MPS SAS memory");
+
+static struct mpssas_target * mpssas_find_target_by_handle(struct mpssas_softc *, int, uint16_t);
 static void mpssas_discovery_timeout(void *data);
-static void mpssas_prepare_remove(struct mpssas_softc *,
-    MPI2_EVENT_SAS_TOPO_PHY_ENTRY *);
 static void mpssas_remove_device(struct mps_softc *, struct mps_command *);
 static void mpssas_remove_complete(struct mps_softc *, struct mps_command *);
 static void mpssas_action(struct cam_sim *sim, union ccb *ccb);
 static void mpssas_poll(struct cam_sim *sim);
-static void mpssas_probe_device(struct mps_softc *sc, uint16_t handle);
-static void mpssas_probe_device_complete(struct mps_softc *sc,
-     struct mps_config_params *params);
 static void mpssas_scsiio_timeout(void *data);
 static void mpssas_abort_complete(struct mps_softc *sc, struct mps_command *cm);
-static void mpssas_recovery(struct mps_softc *, struct mps_command *);
-static int mpssas_map_tm_request(struct mps_softc *sc, struct mps_command *cm);
-static void mpssas_issue_tm_request(struct mps_softc *sc,
-				    struct mps_command *cm);
-static void mpssas_tm_complete(struct mps_softc *sc, struct mps_command *cm,
-			       int error);
-static int mpssas_complete_tm_request(struct mps_softc *sc,
-				      struct mps_command *cm, int free_cm);
+static void mpssas_direct_drive_io(struct mpssas_softc *sassc,
+    struct mps_command *cm, union ccb *ccb);
 static void mpssas_action_scsiio(struct mpssas_softc *, union ccb *);
 static void mpssas_scsiio_complete(struct mps_softc *, struct mps_command *);
+static void mpssas_action_resetdev(struct mpssas_softc *, union ccb *);
 #if __FreeBSD_version >= 900026
 static void mpssas_smpio_complete(struct mps_softc *sc, struct mps_command *cm);
 static void mpssas_send_smpcmd(struct mpssas_softc *sassc, union ccb *ccb,
 			       uint64_t sasaddr);
 static void mpssas_action_smpio(struct mpssas_softc *sassc, union ccb *ccb);
-#endif /* __FreeBSD_version >= 900026 */
-static void mpssas_resetdev(struct mpssas_softc *, struct mps_command *);
-static void mpssas_action_resetdev(struct mpssas_softc *, union ccb *);
+#endif //FreeBSD_version >= 900026
 static void mpssas_resetdev_complete(struct mps_softc *, struct mps_command *);
-static void mpssas_freeze_device(struct mpssas_softc *, struct mpssas_target *);
-static void mpssas_unfreeze_device(struct mpssas_softc *, struct mpssas_target *) __unused;
-
-/*
- * Abstracted so that the driver can be backwards and forwards compatible
- * with future versions of CAM that will provide this functionality.
- */
-#define MPS_SET_LUN(lun, ccblun)	\
-	mpssas_set_lun(lun, ccblun)
-
-static __inline int
-mpssas_set_lun(uint8_t *lun, u_int ccblun)
-{
-	uint64_t *newlun;
-
-	newlun = (uint64_t *)lun;
-	*newlun = 0;
-	if (ccblun <= 0xff) {
-		/* Peripheral device address method, LUN is 0 to 255 */
-		lun[1] = ccblun;
-	} else if (ccblun <= 0x3fff) {
-		/* Flat space address method, LUN is <= 16383 */
-		scsi_ulto2b(ccblun, lun);
-		lun[0] |= 0x40;
-	} else if (ccblun <= 0xffffff) {
-		/* Extended flat space address method, LUN is <= 16777215 */
-		scsi_ulto3b(ccblun, &lun[1]);
-		/* Extended Flat space address method */
-		lun[0] = 0xc0;
-		/* Length = 1, i.e. LUN is 3 bytes long */
-		lun[0] |= 0x10;
-		/* Extended Address Method */
-		lun[0] |= 0x02;
-	} else {
-		return (EINVAL);
-	}
-
-	return (0);
-}
+static int  mpssas_send_abort(struct mps_softc *sc, struct mps_command *tm, struct mps_command *cm);
+static int  mpssas_send_reset(struct mps_softc *sc, struct mps_command *tm, uint8_t type);
+static void mpssas_rescan(struct mpssas_softc *sassc, union ccb *ccb);
+static void mpssas_rescan_done(struct cam_periph *periph, union ccb *done_ccb);
+static void mpssas_scanner_thread(void *arg);
+#if __FreeBSD_version >= 1000006
+static void mpssas_async(void *callback_arg, uint32_t code,
+			 struct cam_path *path, void *arg);
+#else
+static void mpssas_check_eedp(struct mpssas_softc *sassc);
+static void mpssas_read_cap_done(struct cam_periph *periph, union ccb *done_ccb);
+#endif
+static int mpssas_send_portenable(struct mps_softc *sc);
+static void mpssas_portenable_complete(struct mps_softc *sc,
+    struct mps_command *cm);
 
 static struct mpssas_target *
-mpssas_alloc_target(struct mpssas_softc *sassc, struct mpssas_target *probe)
-{
-	struct mpssas_target *target;
-	int start;
-
-	mps_dprint(sassc->sc, MPS_TRACE, "%s\n", __func__);
-
-	/*
-	 * If it's not a sata or sas target, CAM won't be able to see it.  Put
-	 * it into a high-numbered slot so that it's accessible but not
-	 * interrupting the target numbering sequence of real drives.
-	 */
-	if ((probe->devinfo & (MPI2_SAS_DEVICE_INFO_SSP_TARGET |
-	    MPI2_SAS_DEVICE_INFO_STP_TARGET | MPI2_SAS_DEVICE_INFO_SATA_DEVICE))
-	    == 0) {
-		start = 200;
-	} else {
-		/*
-		 * Use the enclosure number and slot number as a hint for target
-		 * numbering.  If that doesn't produce a sane result, search the
-		 * entire space.
-		 */
-#if 0
-		start = probe->encl_handle * 16 + probe->encl_slot;
-#else
-		start = probe->encl_slot;
-#endif
-		if (start >= sassc->sc->facts->MaxTargets)
-			start = 0;
-	}
-
-	target = mpssas_find_target(sassc, start, 0);
-
-	/*
-	 * Nothing found on the first pass, try a second pass that searches the
-	 * entire space.
-	 */
-	if (target == NULL)
-		target = mpssas_find_target(sassc, 0, 0);
-
-	return (target);
-}
-
-static struct mpssas_target *
-mpssas_find_target(struct mpssas_softc *sassc, int start, uint16_t handle)
+mpssas_find_target_by_handle(struct mpssas_softc *sassc, int start, uint16_t handle)
 {
 	struct mpssas_target *target;
 	int i;
@@ -260,233 +190,232 @@
 	return (NULL);
 }
 
-/*
- * Start the probe sequence for a given device handle.  This will not
- * block.
+/* we need to freeze the simq during attach and diag reset, to avoid failing
+ * commands before device handles have been found by discovery.  Since
+ * discovery involves reading config pages and possibly sending commands,
+ * discovery actions may continue even after we receive the end of discovery
+ * event, so refcount discovery actions instead of assuming we can unfreeze
+ * the simq when we get the event.
  */
-static void
-mpssas_probe_device(struct mps_softc *sc, uint16_t handle)
+void
+mpssas_startup_increment(struct mpssas_softc *sassc)
 {
-	struct mpssas_devprobe *probe;
-	struct mps_config_params *params;
-	MPI2_CONFIG_EXTENDED_PAGE_HEADER *hdr;
-	int error;
-
-	mps_dprint(sc, MPS_TRACE, "%s\n", __func__);
-
-	probe = malloc(sizeof(*probe), M_MPSSAS, M_NOWAIT | M_ZERO);
-	if (probe == NULL) {
-		mps_dprint(sc, MPS_FAULT, "Out of memory starting probe\n");
+	if ((sassc->flags & MPSSAS_IN_STARTUP) != 0) {
+		if (sassc->startup_refcount++ == 0) {
+			/* just starting, freeze the simq */
+			mps_dprint(sassc->sc, MPS_INFO,
+			    "%s freezing simq\n", __func__);
+			xpt_freeze_simq(sassc->sim, 1);
+		}
+		mps_dprint(sassc->sc, MPS_TRACE, "%s refcount %u\n", __func__,
+		    sassc->startup_refcount);
+	}
+}
+
+void
+mpssas_startup_decrement(struct mpssas_softc *sassc)
+{
+	if ((sassc->flags & MPSSAS_IN_STARTUP) != 0) {
+		if (--sassc->startup_refcount == 0) {
+			/* finished all discovery-related actions, release
+			 * the simq and rescan for the latest topology.
+			 */
+			mps_dprint(sassc->sc, MPS_INFO,
+			    "%s releasing simq\n", __func__);
+			sassc->flags &= ~MPSSAS_IN_STARTUP;
+			xpt_release_simq(sassc->sim, 1);
+			mpssas_rescan_target(sassc->sc, NULL);
+		}
+		mps_dprint(sassc->sc, MPS_TRACE, "%s refcount %u\n", __func__,
+		    sassc->startup_refcount);
+	}
+}
+
+/* LSI's firmware requires us to stop sending commands when we're doing task
+ * management, so refcount the TMs and keep the simq frozen when any are in
+ * use.
+ */
+struct mps_command *
+mpssas_alloc_tm(struct mps_softc *sc)
+{
+	struct mps_command *tm;
+
+	tm = mps_alloc_high_priority_command(sc);
+	if (tm != NULL) {
+		if (sc->sassc->tm_count++ == 0) {
+			mps_printf(sc, "%s freezing simq\n", __func__);
+			xpt_freeze_simq(sc->sassc->sim, 1);
+		}
+		mps_dprint(sc, MPS_TRACE, "%s tm_count %u\n", __func__,
+		    sc->sassc->tm_count);
+	}
+	return tm;
+}
+
+void
+mpssas_free_tm(struct mps_softc *sc, struct mps_command *tm)
+{
+	if (tm == NULL)
+		return;
+
+	/* if there are no TMs in use, we can release the simq.  We use our
+	 * own refcount so that it's easier for a diag reset to cleanup and
+	 * release the simq.
+	 */
+	if (--sc->sassc->tm_count == 0) {
+		mps_printf(sc, "%s releasing simq\n", __func__);
+		xpt_release_simq(sc->sassc->sim, 1);
+	}
+	mps_dprint(sc, MPS_TRACE, "%s tm_count %u\n", __func__,
+	    sc->sassc->tm_count);
+
+	mps_free_high_priority_command(sc, tm);
+}
+
+
+void
+mpssas_rescan_target(struct mps_softc *sc, struct mpssas_target *targ)
+{
+	struct mpssas_softc *sassc = sc->sassc;
+	path_id_t pathid;
+	target_id_t targetid;
+	union ccb *ccb;
+
+	pathid = cam_sim_path(sassc->sim);
+	if (targ == NULL)
+		targetid = CAM_TARGET_WILDCARD;
+	else
+		targetid = targ - sassc->targets;
+
+	/*
+	 * Allocate a CCB and schedule a rescan.
+	 */
+	ccb = xpt_alloc_ccb_nowait();
+	if (ccb == NULL) {
+		mps_dprint(sc, MPS_FAULT, "unable to alloc CCB for rescan\n");
 		return;
 	}
-	params = &probe->params;
-	hdr = &params->hdr.Ext;
-
-	params->action = MPI2_CONFIG_ACTION_PAGE_HEADER;
-	params->page_address = MPI2_SAS_DEVICE_PGAD_FORM_HANDLE | handle;
-	hdr->ExtPageType = MPI2_CONFIG_EXTPAGETYPE_SAS_DEVICE;
-	hdr->ExtPageLength = 0;
-	hdr->PageNumber = 0;
-	hdr->PageVersion = 0;
-	params->buffer = NULL;
-	params->length = 0;
-	params->callback = mpssas_probe_device_complete;
-	params->cbdata = probe;
-	probe->target.handle = handle;
-	probe->state = MPSSAS_PROBE_DEV1;
-
-	if ((error = mps_read_config_page(sc, params)) != 0) {
-		free(probe, M_MPSSAS);
-		mps_dprint(sc, MPS_FAULT, "Failure starting device probe\n");
+
+	if (xpt_create_path(&ccb->ccb_h.path, xpt_periph, pathid,
+		            targetid, CAM_LUN_WILDCARD) != CAM_REQ_CMP) {
+		mps_dprint(sc, MPS_FAULT, "unable to create path for rescan\n");
+		xpt_free_ccb(ccb);
 		return;
 	}
+
+	/* XXX Hardwired to scan the bus for now */
+	ccb->ccb_h.func_code = XPT_SCAN_BUS;
+	mps_dprint(sc, MPS_TRACE, "%s targetid %u\n", __func__, targetid);
+	mpssas_rescan(sassc, ccb);
 }
 
 static void
-mpssas_probe_device_complete(struct mps_softc *sc,
-    struct mps_config_params *params)
+mpssas_log_command(struct mps_command *cm, const char *fmt, ...)
 {
-	MPI2_CONFIG_EXTENDED_PAGE_HEADER *hdr;
-	struct mpssas_devprobe *probe;
-	int error;
-
-	mps_dprint(sc, MPS_TRACE, "%s\n", __func__);
-
-	hdr = &params->hdr.Ext;
-	probe = params->cbdata;
-
-	switch (probe->state) {
-	case MPSSAS_PROBE_DEV1:
-	case MPSSAS_PROBE_PHY:
-	case MPSSAS_PROBE_EXP:
-		if (params->status != MPI2_IOCSTATUS_SUCCESS) {
-			mps_dprint(sc, MPS_FAULT,
-			    "Probe Failure 0x%x state %d\n", params->status,
-			    probe->state);
-			free(probe, M_MPSSAS);
-			return;
+	struct sbuf sb;
+	va_list ap;
+	char str[192];
+	char path_str[64];
+
+	if (cm == NULL)
+		return;
+
+	sbuf_new(&sb, str, sizeof(str), 0);
+
+	va_start(ap, fmt);
+
+	if (cm->cm_ccb != NULL) {
+		xpt_path_string(cm->cm_ccb->csio.ccb_h.path, path_str,
+				sizeof(path_str));
+		sbuf_cat(&sb, path_str);
+		if (cm->cm_ccb->ccb_h.func_code == XPT_SCSI_IO) {
+			scsi_command_string(&cm->cm_ccb->csio, &sb);
+			sbuf_printf(&sb, "length %d ",
+				    cm->cm_ccb->csio.dxfer_len);
 		}
-		params->action = MPI2_CONFIG_ACTION_PAGE_READ_CURRENT;
-		params->length = hdr->ExtPageLength * 4;
-		params->buffer = malloc(params->length, M_MPSSAS,
-		    M_ZERO|M_NOWAIT);
-		if (params->buffer == NULL) {
-			mps_dprint(sc, MPS_FAULT, "Out of memory at state "
-			   "0x%x, size 0x%x\n", probe->state, params->length);
-			free(probe, M_MPSSAS);
-			return;
-		}
-		if (probe->state == MPSSAS_PROBE_DEV1)
-			probe->state = MPSSAS_PROBE_DEV2;
-		else if (probe->state == MPSSAS_PROBE_PHY)
-			probe->state = MPSSAS_PROBE_PHY2;
-		else if (probe->state == MPSSAS_PROBE_EXP)
-			probe->state = MPSSAS_PROBE_EXP2;
-		error = mps_read_config_page(sc, params);
-		break;
-	case MPSSAS_PROBE_DEV2:
-	{
-		MPI2_CONFIG_PAGE_SAS_DEV_0 *buf;
-
-		if (params->status != MPI2_IOCSTATUS_SUCCESS) {
-			mps_dprint(sc, MPS_FAULT,
-			    "Probe Failure 0x%x state %d\n", params->status,
-			    probe->state);
-			free(params->buffer, M_MPSSAS);
-			free(probe, M_MPSSAS);
-			return;
-		}
-		buf = params->buffer;
-		mps_print_sasdev0(sc, buf);
-
-		probe->target.devname = mps_to_u64(&buf->DeviceName);
-		probe->target.devinfo = buf->DeviceInfo;
-		probe->target.encl_handle = buf->EnclosureHandle;
-		probe->target.encl_slot = buf->Slot;
-		probe->target.sasaddr = mps_to_u64(&buf->SASAddress);
-		probe->target.parent_handle = buf->ParentDevHandle;
-
-		if (buf->DeviceInfo & MPI2_SAS_DEVICE_INFO_DIRECT_ATTACH) {
-			params->page_address =
-			    MPI2_SAS_PHY_PGAD_FORM_PHY_NUMBER | buf->PhyNum;
-			hdr->ExtPageType = MPI2_CONFIG_EXTPAGETYPE_SAS_PHY;
-			hdr->PageNumber = 0;
-			probe->state = MPSSAS_PROBE_PHY;
-		} else {
-			params->page_address =
-			    MPI2_SAS_EXPAND_PGAD_FORM_HNDL_PHY_NUM |
-			    buf->ParentDevHandle | (buf->PhyNum << 16);
-			hdr->ExtPageType = MPI2_CONFIG_EXTPAGETYPE_SAS_EXPANDER;
-			hdr->PageNumber = 1;
-			probe->state = MPSSAS_PROBE_EXP;
-		}
-		params->action = MPI2_CONFIG_ACTION_PAGE_HEADER;
-		hdr->ExtPageLength = 0;
-		hdr->PageVersion = 0;
-		params->buffer = NULL;
-		params->length = 0;
-		free(buf, M_MPSSAS);
-		error = mps_read_config_page(sc, params);
-		break;
 	}
-	case MPSSAS_PROBE_PHY2:
-	case MPSSAS_PROBE_EXP2:
-	{
-		MPI2_CONFIG_PAGE_SAS_PHY_0 *phy;
-		MPI2_CONFIG_PAGE_EXPANDER_1 *exp;
-		struct mpssas_softc *sassc;
-		struct mpssas_target *targ;
-		char devstring[80];
-		uint16_t handle;
-
-		if (params->status != MPI2_IOCSTATUS_SUCCESS) {
-			mps_dprint(sc, MPS_FAULT,
-			    "Probe Failure 0x%x state %d\n", params->status,
-			    probe->state);
-			free(params->buffer, M_MPSSAS);
-			free(probe, M_MPSSAS);
-			return;
-		}
-
-		if (probe->state == MPSSAS_PROBE_PHY2) {
-			phy = params->buffer;
-			mps_print_sasphy0(sc, phy);
-			probe->target.linkrate = phy->NegotiatedLinkRate & 0xf;
-		} else {
-			exp = params->buffer;
-			mps_print_expander1(sc, exp);
-			probe->target.linkrate = exp->NegotiatedLinkRate & 0xf;
-		}
-		free(params->buffer, M_MPSSAS);
-
-		sassc = sc->sassc;
-		handle = probe->target.handle;
-		if ((targ = mpssas_find_target(sassc, 0, handle)) != NULL) {
-			mps_printf(sc, "Ignoring dup device handle 0x%04x\n",
-			    handle);
-			free(probe, M_MPSSAS);
-			return;
-		}
-		if ((targ = mpssas_alloc_target(sassc, &probe->target)) == NULL) {
-			mps_printf(sc, "Target table overflow, handle 0x%04x\n",
-			    handle);
-			free(probe, M_MPSSAS);
-			return;
-		}
-
-		*targ = probe->target;	/* Copy the attributes */
-		targ->tid = targ - sassc->targets;
-		mps_describe_devinfo(targ->devinfo, devstring, 80);
-		if (bootverbose)
-			mps_printf(sc, "Found device <%s> <%s> <0x%04x> "
-			    "<%d/%d>\n", devstring,
-			    mps_describe_table(mps_linkrate_names,
-			    targ->linkrate), targ->handle, targ->encl_handle,
-			    targ->encl_slot);
-
-		free(probe, M_MPSSAS);
-		mpssas_announce_device(sassc, targ);
-		break;
+	else {
+		sbuf_printf(&sb, "(noperiph:%s%d:%u:%u:%u): ",
+		    cam_sim_name(cm->cm_sc->sassc->sim),
+		    cam_sim_unit(cm->cm_sc->sassc->sim),
+		    cam_sim_bus(cm->cm_sc->sassc->sim),
+		    cm->cm_targ ? cm->cm_targ->tid : 0xFFFFFFFF,
+		    cm->cm_lun);
 	}
-	default:
-		printf("what?\n");
+
+	sbuf_printf(&sb, "SMID %u ", cm->cm_desc.Default.SMID);
+	sbuf_vprintf(&sb, fmt, ap);
+	sbuf_finish(&sb);
+	printf("%s", sbuf_data(&sb));
+
+	va_end(ap);
+}
+
+static void
+mpssas_lost_target(struct mps_softc *sc, struct mpssas_target *targ)
+{
+	struct mpssas_softc *sassc = sc->sassc;
+	path_id_t pathid = cam_sim_path(sassc->sim);
+	struct cam_path *path;
+
+	mps_printf(sc, "%s targetid %u\n", __func__, targ->tid);
+	if (xpt_create_path(&path, NULL, pathid, targ->tid, 0) != CAM_REQ_CMP) {
+		mps_printf(sc, "unable to create path for lost target %d\n", 
+		    targ->tid);
+		return;
 	}
+
+	xpt_async(AC_LOST_DEVICE, path, NULL);
+	xpt_free_path(path);
 }
 
 /*
- * The MPT2 firmware performs debounce on the link to avoid transient link errors
- * and false removals.  When it does decide that link has been lost and a device
- * need to go away, it expects that the host will perform a target reset and then
- * an op remove.  The reset has the side-effect of aborting any outstanding
- * requests for the device, which is required for the op-remove to succeed.  It's
- * not clear if the host should check for the device coming back alive after the
- * reset.
+ * The MPT2 firmware performs debounce on the link to avoid transient link
+ * errors and false removals.  When it does decide that link has been lost
+ * and a device need to go away, it expects that the host will perform a
+ * target reset and then an op remove.  The reset has the side-effect of
+ * aborting any outstanding requests for the device, which is required for
+ * the op-remove to succeed.  It's not clear if the host should check for
+ * the device coming back alive after the reset.
  */
-static void
-mpssas_prepare_remove(struct mpssas_softc *sassc, MPI2_EVENT_SAS_TOPO_PHY_ENTRY *phy)
+void
+mpssas_prepare_remove(struct mpssas_softc *sassc, uint16_t handle)
 {
 	MPI2_SCSI_TASK_MANAGE_REQUEST *req;
 	struct mps_softc *sc;
 	struct mps_command *cm;
 	struct mpssas_target *targ = NULL;
-	uint16_t handle;
 
 	mps_dprint(sassc->sc, MPS_TRACE, "%s\n", __func__);
 
-	handle = phy->AttachedDevHandle;
-	targ = mpssas_find_target(sassc, 0, handle);
-	if (targ == NULL)
-		/* We don't know about this device? */
-		return;
-
+	/*
+	 * If this is a WD controller, determine if the disk should be exposed
+	 * to the OS or not.  If disk should be exposed, return from this
+	 * function without doing anything.
+	 */
 	sc = sassc->sc;
-	cm = mps_alloc_command(sc);
-	if (cm == NULL) {
-		mps_printf(sc, "comand alloc failure in mpssas_prepare_remove\n");
+	if ((sc->mps_flags & MPS_FLAGS_WD_AVAILABLE) && (sc->WD_hide_expose ==
+	    MPS_WD_EXPOSE_ALWAYS)) {
 		return;
 	}
 
-	mps_dprint(sc, MPS_INFO, "Preparing to remove target %d\n", targ->tid);
+	targ = mpssas_find_target_by_handle(sassc, 0, handle);
+	if (targ == NULL) {
+		/* FIXME: what is the action? */
+		/* We don't know about this device? */
+		printf("%s: invalid handle 0x%x \n", __func__, handle);
+		return;
+	}
+
+	targ->flags |= MPSSAS_TARGET_INREMOVAL;
+
+	cm = mpssas_alloc_tm(sc);
+	if (cm == NULL) {
+		mps_printf(sc, "%s: command alloc failure\n", __func__);
+		return;
+	}
+
+	mpssas_lost_target(sc, targ);
 
 	req = (MPI2_SCSI_TASK_MANAGE_REQUEST *)cm->cm_req;
 	memset(req, 0, sizeof(*req));
@@ -497,15 +426,16 @@
 	/* SAS Hard Link Reset / SATA Link Reset */
 	req->MsgFlags = MPI2_SCSITASKMGMT_MSGFLAGS_LINK_RESET;
 
+	cm->cm_targ = targ;
 	cm->cm_data = NULL;
-	cm->cm_desc.Default.RequestFlags = MPI2_REQ_DESCRIPT_FLAGS_DEFAULT_TYPE;
+	cm->cm_desc.HighPriority.RequestFlags = MPI2_REQ_DESCRIPT_FLAGS_HIGH_PRIORITY;
 	cm->cm_complete = mpssas_remove_device;
-	cm->cm_targ = targ;
-	mpssas_issue_tm_request(sc, cm);
+	cm->cm_complete_data = (void *)(uintptr_t)handle;
+	mps_map_command(sc, cm);
 }
 
 static void
-mpssas_remove_device(struct mps_softc *sc, struct mps_command *cm)
+mpssas_remove_device(struct mps_softc *sc, struct mps_command *tm)
 {
 	MPI2_SCSI_TASK_MANAGE_REPLY *reply;
 	MPI2_SAS_IOUNIT_CONTROL_REQUEST *req;
@@ -515,150 +445,122 @@
 
 	mps_dprint(sc, MPS_TRACE, "%s\n", __func__);
 
-	reply = (MPI2_SCSI_TASK_MANAGE_REPLY *)cm->cm_reply;
-	handle = cm->cm_targ->handle;
-
-	mpssas_complete_tm_request(sc, cm, /*free_cm*/ 0);
+	reply = (MPI2_SCSI_TASK_MANAGE_REPLY *)tm->cm_reply;
+	handle = (uint16_t)(uintptr_t)tm->cm_complete_data;
+	targ = tm->cm_targ;
 
 	/*
 	 * Currently there should be no way we can hit this case.  It only
 	 * happens when we have a failure to allocate chain frames, and
 	 * task management commands don't have S/G lists.
 	 */
-	if ((cm->cm_flags & MPS_CM_FLAGS_ERROR_MASK) != 0) {
+	if ((tm->cm_flags & MPS_CM_FLAGS_ERROR_MASK) != 0) {
 		mps_printf(sc, "%s: cm_flags = %#x for remove of handle %#04x! "
-			   "This should not happen!\n", __func__, cm->cm_flags,
+			   "This should not happen!\n", __func__, tm->cm_flags,
 			   handle);
+		mpssas_free_tm(sc, tm);
 		return;
 	}
 
-	if (reply->IOCStatus != MPI2_IOCSTATUS_SUCCESS) {
-		mps_printf(sc, "Failure 0x%x reseting device 0x%04x\n", 
-		   reply->IOCStatus, handle);
-		mps_free_command(sc, cm);
+	if (reply == NULL) {
+		/* XXX retry the remove after the diag reset completes? */
+		mps_printf(sc, "%s NULL reply reseting device 0x%04x\n", 
+		    __func__, handle);
+		mpssas_free_tm(sc, tm);
 		return;
 	}
 
+	if (reply->IOCStatus != MPI2_IOCSTATUS_SUCCESS) {
+		mps_printf(sc, "IOCStatus = 0x%x while resetting device 0x%x\n",
+		   reply->IOCStatus, handle);
+		mpssas_free_tm(sc, tm);
+		return;
+	}
+
 	mps_dprint(sc, MPS_INFO, "Reset aborted %u commands\n",
 	    reply->TerminationCount);
-	mps_free_reply(sc, cm->cm_reply_data);
+	mps_free_reply(sc, tm->cm_reply_data);
+	tm->cm_reply = NULL;	/* Ensures the the reply won't get re-freed */
 
 	/* Reuse the existing command */
-	req = (MPI2_SAS_IOUNIT_CONTROL_REQUEST *)cm->cm_req;
+	req = (MPI2_SAS_IOUNIT_CONTROL_REQUEST *)tm->cm_req;
 	memset(req, 0, sizeof(*req));
 	req->Function = MPI2_FUNCTION_SAS_IO_UNIT_CONTROL;
 	req->Operation = MPI2_SAS_OP_REMOVE_DEVICE;
 	req->DevHandle = handle;
-	cm->cm_data = NULL;
-	cm->cm_desc.Default.RequestFlags = MPI2_REQ_DESCRIPT_FLAGS_DEFAULT_TYPE;
-	cm->cm_flags &= ~MPS_CM_FLAGS_COMPLETE;
-	cm->cm_complete = mpssas_remove_complete;
-
-	mps_map_command(sc, cm);
-
-	mps_dprint(sc, MPS_INFO, "clearing target handle 0x%04x\n", handle);
-	TAILQ_FOREACH_SAFE(cm, &sc->io_list, cm_link, next_cm) {
+	tm->cm_data = NULL;
+	tm->cm_desc.Default.RequestFlags = MPI2_REQ_DESCRIPT_FLAGS_DEFAULT_TYPE;
+	tm->cm_complete = mpssas_remove_complete;
+	tm->cm_complete_data = (void *)(uintptr_t)handle;
+
+	mps_map_command(sc, tm);
+
+	mps_dprint(sc, MPS_INFO, "clearing target %u handle 0x%04x\n",
+		   targ->tid, handle);
+	TAILQ_FOREACH_SAFE(tm, &targ->commands, cm_link, next_cm) {
 		union ccb *ccb;
 
-		if (cm->cm_targ->handle != handle)
-			continue;
-
-		mps_dprint(sc, MPS_INFO, "Completing missed command %p\n", cm);
-		ccb = cm->cm_complete_data;
+		mps_dprint(sc, MPS_INFO, "Completing missed command %p\n", tm);
+		ccb = tm->cm_complete_data;
 		ccb->ccb_h.status = CAM_DEV_NOT_THERE;
-		mpssas_scsiio_complete(sc, cm);
-	}
-	targ = mpssas_find_target(sc->sassc, 0, handle);
-	if (targ != NULL) {
-		targ->handle = 0x0;
-		mpssas_announce_device(sc->sassc, targ);
+		mpssas_scsiio_complete(sc, tm);
 	}
 }
 
 static void
-mpssas_remove_complete(struct mps_softc *sc, struct mps_command *cm)
+mpssas_remove_complete(struct mps_softc *sc, struct mps_command *tm)
 {
 	MPI2_SAS_IOUNIT_CONTROL_REPLY *reply;
+	uint16_t handle;
+	struct mpssas_target *targ;
 
 	mps_dprint(sc, MPS_TRACE, "%s\n", __func__);
 
-	reply = (MPI2_SAS_IOUNIT_CONTROL_REPLY *)cm->cm_reply;
-
-	mps_printf(sc, "mpssas_remove_complete on target 0x%04x,"
-	   " IOCStatus= 0x%x\n", cm->cm_targ->tid, reply->IOCStatus);
-
-	mps_free_command(sc, cm);
-}
-
-static void
-mpssas_evt_handler(struct mps_softc *sc, uintptr_t data,
-    MPI2_EVENT_NOTIFICATION_REPLY *event)
-{
-	struct mpssas_softc *sassc;
-
-	mps_dprint(sc, MPS_TRACE, "%s\n", __func__);
-
-	sassc = sc->sassc;
-	mps_print_evt_sas(sc, event);
-
-	switch (event->Event) {
-	case MPI2_EVENT_SAS_DISCOVERY:
-	{
-		MPI2_EVENT_DATA_SAS_DISCOVERY *data;
-
-		data = (MPI2_EVENT_DATA_SAS_DISCOVERY *)&event->EventData;
-
-		if (data->ReasonCode & MPI2_EVENT_SAS_DISC_RC_STARTED)
-			mps_dprint(sc, MPS_TRACE,"SAS discovery start event\n");
-		if (data->ReasonCode & MPI2_EVENT_SAS_DISC_RC_COMPLETED) {
-			mps_dprint(sc, MPS_TRACE, "SAS discovery end event\n");
-			sassc->flags &= ~MPSSAS_IN_DISCOVERY;
-			mpssas_discovery_end(sassc);
-		}
-		break;
+	reply = (MPI2_SAS_IOUNIT_CONTROL_REPLY *)tm->cm_reply;
+	handle = (uint16_t)(uintptr_t)tm->cm_complete_data;
+
+	/*
+	 * Currently there should be no way we can hit this case.  It only
+	 * happens when we have a failure to allocate chain frames, and
+	 * task management commands don't have S/G lists.
+	 */
+	if ((tm->cm_flags & MPS_CM_FLAGS_ERROR_MASK) != 0) {
+		mps_printf(sc, "%s: cm_flags = %#x for remove of handle %#04x! "
+			   "This should not happen!\n", __func__, tm->cm_flags,
+			   handle);
+		mpssas_free_tm(sc, tm);
+		return;
 	}
-	case MPI2_EVENT_SAS_TOPOLOGY_CHANGE_LIST:
-	{
-		MPI2_EVENT_DATA_SAS_TOPOLOGY_CHANGE_LIST *data;
-		MPI2_EVENT_SAS_TOPO_PHY_ENTRY *phy;
-		int i;
-
-		data = (MPI2_EVENT_DATA_SAS_TOPOLOGY_CHANGE_LIST *)
-		    &event->EventData;
-
-		if (data->ExpStatus == MPI2_EVENT_SAS_TOPO_ES_ADDED) {
-			if (bootverbose)
-				printf("Expander found at enclosure %d\n",
-				    data->EnclosureHandle);
-			mpssas_probe_device(sc, data->ExpanderDevHandle);
-		}
-
-		for (i = 0; i < data->NumEntries; i++) {
-			phy = &data->PHY[i];
-			switch (phy->PhyStatus & MPI2_EVENT_SAS_TOPO_RC_MASK) {
-			case MPI2_EVENT_SAS_TOPO_RC_TARG_ADDED:
-				mpssas_probe_device(sc, phy->AttachedDevHandle);
-				break;
-			case MPI2_EVENT_SAS_TOPO_RC_TARG_NOT_RESPONDING:
-				mpssas_prepare_remove(sassc, phy);
-				break;
-			case MPI2_EVENT_SAS_TOPO_RC_PHY_CHANGED:
-			case MPI2_EVENT_SAS_TOPO_RC_NO_CHANGE:
-			case MPI2_EVENT_SAS_TOPO_RC_DELAY_NOT_RESPONDING:
-			default:
-				break;
-			}
-		}
-
-		break;
+
+	if (reply == NULL) {
+		/* most likely a chip reset */
+		mps_printf(sc, "%s NULL reply removing device 0x%04x\n",
+		    __func__, handle);
+		mpssas_free_tm(sc, tm);
+		return;
 	}
-	case MPI2_EVENT_SAS_ENCL_DEVICE_STATUS_CHANGE:
-		break;
-	default:
-		break;
+
+	mps_printf(sc, "%s on handle 0x%04x, IOCStatus= 0x%x\n", __func__, 
+	    handle, reply->IOCStatus);
+
+	/*
+	 * Don't clear target if remove fails because things will get confusing.
+	 * Leave the devname and sasaddr intact so that we know to avoid reusing
+	 * this target id if possible, and so we can assign the same target id
+	 * to this device if it comes back in the future.
+	 */
+	if (reply->IOCStatus == MPI2_IOCSTATUS_SUCCESS) {
+		targ = tm->cm_targ;
+		targ->handle = 0x0;
+		targ->encl_handle = 0x0;
+		targ->encl_slot = 0x0;
+		targ->exp_dev_handle = 0x0;
+		targ->phy_num = 0x0;
+		targ->linkrate = 0x0;
+		targ->devinfo = 0x0;
 	}
 
-	mps_free_reply(sc, data);
+	mpssas_free_tm(sc, tm);
 }
 
 static int
@@ -674,6 +576,11 @@
 	setbit(events, MPI2_EVENT_SAS_INIT_TABLE_OVERFLOW);
 	setbit(events, MPI2_EVENT_SAS_TOPOLOGY_CHANGE_LIST);
 	setbit(events, MPI2_EVENT_SAS_ENCL_DEVICE_STATUS_CHANGE);
+	setbit(events, MPI2_EVENT_IR_CONFIGURATION_CHANGE_LIST);
+	setbit(events, MPI2_EVENT_IR_VOLUME);
+	setbit(events, MPI2_EVENT_IR_PHYSICAL_DISK);
+	setbit(events, MPI2_EVENT_IR_OPERATION_STATUS);
+	setbit(events, MPI2_EVENT_LOG_ENTRY_ADDED);
 
 	mps_register_events(sc, events, mpssas_evt_handler, NULL,
 	    &sc->sassc->mpssas_eh);
@@ -685,8 +592,10 @@
 mps_attach_sas(struct mps_softc *sc)
 {
 	struct mpssas_softc *sassc;
-	int error = 0;
-	int num_sim_reqs;
+#if __FreeBSD_version >= 1000006
+	cam_status status;
+#endif
+	int unit, error = 0;
 
 	mps_dprint(sc, MPS_TRACE, "%s\n", __func__);
 
@@ -696,42 +605,48 @@
 	sc->sassc = sassc;
 	sassc->sc = sc;
 
-	/*
-	 * Tell CAM that we can handle 5 fewer requests than we have
-	 * allocated.  If we allow the full number of requests, all I/O
-	 * will halt when we run out of resources.  Things work fine with
-	 * just 1 less request slot given to CAM than we have allocated.
-	 * We also need a couple of extra commands so that we can send down
-	 * abort, reset, etc. requests when commands time out.  Otherwise
-	 * we could wind up in a situation with sc->num_reqs requests down
-	 * on the card and no way to send an abort.
-	 *
-	 * XXX KDM need to figure out why I/O locks up if all commands are
-	 * used.
-	 */
-	num_sim_reqs = sc->num_reqs - 5;
-
-	if ((sassc->devq = cam_simq_alloc(num_sim_reqs)) == NULL) {
+	if ((sassc->devq = cam_simq_alloc(sc->num_reqs)) == NULL) {
 		mps_dprint(sc, MPS_FAULT, "Cannot allocate SIMQ\n");
 		error = ENOMEM;
 		goto out;
 	}
 
+	unit = device_get_unit(sc->mps_dev);
 	sassc->sim = cam_sim_alloc(mpssas_action, mpssas_poll, "mps", sassc,
-	    device_get_unit(sc->mps_dev), &sc->mps_mtx, num_sim_reqs,
-	    num_sim_reqs, sassc->devq);
+	    unit, &sc->mps_mtx, sc->num_reqs, sc->num_reqs, sassc->devq);
 	if (sassc->sim == NULL) {
 		mps_dprint(sc, MPS_FAULT, "Cannot allocate SIM\n");
 		error = EINVAL;
 		goto out;
 	}
 
+	TAILQ_INIT(&sassc->ev_queue);
+
+	/* Initialize taskqueue for Event Handling */
+	TASK_INIT(&sassc->ev_task, 0, mpssas_firmware_event_work, sc);
+	sassc->ev_tq = taskqueue_create("mps_taskq", M_NOWAIT | M_ZERO,
+	    taskqueue_thread_enqueue, &sassc->ev_tq);
+
+	/* Run the task queue with lowest priority */
+	taskqueue_start_threads(&sassc->ev_tq, 1, 255, "%s taskq", 
+	    device_get_nameunit(sc->mps_dev));
+
+	TAILQ_INIT(&sassc->ccb_scanq);
+	error = mps_kproc_create(mpssas_scanner_thread, sassc,
+	    &sassc->rescan_thread, 0, 0, "mps_scan%d", unit);
+	if (error) {
+		mps_printf(sc, "Error %d starting rescan thread\n", error);
+		goto out;
+	}
+
+	mps_lock(sc);
+	sassc->flags |= MPSSAS_SCANTHREAD;
+
 	/*
 	 * XXX There should be a bus for every port on the adapter, but since
 	 * we're just going to fake the topology for now, we'll pretend that
 	 * everything is just a target on a single bus.
 	 */
-	mps_lock(sc);
 	if ((error = xpt_bus_register(sassc->sim, sc->mps_dev, 0)) != 0) {
 		mps_dprint(sc, MPS_FAULT, "Error %d registering SCSI bus\n",
 		    error);
@@ -744,14 +659,25 @@
 	 * the simq will prevent the CAM boottime scanner from running
 	 * before discovery is complete.
 	 */
-	sassc->flags = MPSSAS_IN_STARTUP | MPSSAS_IN_DISCOVERY;
+	sassc->flags |= MPSSAS_IN_STARTUP | MPSSAS_IN_DISCOVERY;
 	xpt_freeze_simq(sassc->sim, 1);
-
-	mps_unlock(sc);
+	sc->sassc->startup_refcount = 0;
 
 	callout_init(&sassc->discovery_callout, 1 /*mpsafe*/);
 	sassc->discovery_timeouts = 0;
 
+	sassc->tm_count = 0;
+
+#if __FreeBSD_version >= 1000006
+	status = xpt_register_async(AC_ADVINFO_CHANGED, mpssas_async, sc, NULL);
+	if (status != CAM_REQ_CMP) {
+		mps_printf(sc, "Error %#x registering async handler for "
+			   "AC_ADVINFO_CHANGED events\n", status);
+	}
+#endif
+
+	mps_unlock(sc);
+
 	mpssas_register_events(sc);
 out:
 	if (error)
@@ -770,22 +696,41 @@
 		return (0);
 
 	sassc = sc->sassc;
+	mps_deregister_events(sc, sassc->mpssas_eh);
+
+	/*
+	 * Drain and free the event handling taskqueue with the lock
+	 * unheld so that any parallel processing tasks drain properly
+	 * without deadlocking.
+	 */
+	if (sassc->ev_tq != NULL)
+		taskqueue_free(sassc->ev_tq);
 
 	/* Make sure CAM doesn't wedge if we had to bail out early. */
 	mps_lock(sc);
+
+	/* Deregister our async handler */
+#if __FreeBSD_version >= 1000006
+	xpt_register_async(0, mpssas_async, sc, NULL);
+#endif
+
 	if (sassc->flags & MPSSAS_IN_STARTUP)
 		xpt_release_simq(sassc->sim, 1);
-	mps_unlock(sc);
-
-	if (sassc->mpssas_eh != NULL)
-		mps_deregister_events(sc, sassc->mpssas_eh);
-
-	mps_lock(sc);
 
 	if (sassc->sim != NULL) {
 		xpt_bus_deregister(cam_sim_path(sassc->sim));
 		cam_sim_free(sassc->sim, FALSE);
 	}
+
+	if (sassc->flags & MPSSAS_SCANTHREAD) {
+		sassc->flags |= MPSSAS_SHUTDOWN;
+		wakeup(&sassc->ccb_scanq);
+		
+		if (sassc->flags & MPSSAS_SCANTHREAD) {
+			msleep(&sassc->flags, &sc->mps_mtx, PRIBIO,
+			       "mps_shutdown", 30 * hz);
+		}
+	}
 	mps_unlock(sc);
 
 	if (sassc->devq != NULL)
@@ -798,7 +743,7 @@
 	return (0);
 }
 
-static void
+void
 mpssas_discovery_end(struct mpssas_softc *sassc)
 {
 	struct mps_softc *sc = sassc->sc;
@@ -808,59 +753,28 @@
 	if (sassc->flags & MPSSAS_DISCOVERY_TIMEOUT_PENDING)
 		callout_stop(&sassc->discovery_callout);
 
-	if ((sassc->flags & MPSSAS_IN_STARTUP) != 0) {
-		mps_dprint(sc, MPS_INFO,
-		    "mpssas_discovery_end: removing confighook\n");
-		sassc->flags &= ~MPSSAS_IN_STARTUP;
-		xpt_release_simq(sassc->sim, 1);
-	}
-#if 0
-	mpssas_announce_device(sassc, NULL);
-#endif
-
 }
 
 static void
-mpssas_announce_device(struct mpssas_softc *sassc, struct mpssas_target *targ)
-{
-	union ccb *ccb;
-	int bus, tid, lun;
-
-	/*
-	 * Force a rescan, a hackish way to announce devices.
-	 * XXX Doing a scan on an individual device is hackish in that it
-	 *     won't scan the LUNs.
-	 * XXX Does it matter if any of this fails?
-	 */
-	bus = cam_sim_path(sassc->sim);
-	if (targ != NULL) {
-		tid = targ->tid;
-		lun = 0;
-	} else {
-		tid = CAM_TARGET_WILDCARD;
-		lun = CAM_LUN_WILDCARD;
-	}
-	ccb = xpt_alloc_ccb_nowait();
-	if (ccb == NULL)
-		return;
-	if (xpt_create_path(&ccb->ccb_h.path, xpt_periph, bus, tid,
-	    CAM_LUN_WILDCARD) != CAM_REQ_CMP) { 
-		xpt_free_ccb(ccb);
-		return;
-	}
-	mps_dprint(sassc->sc, MPS_INFO, "Triggering rescan of %d:%d:-1\n",
-	    bus, tid);
-	xpt_rescan(ccb);
-}
-
-static void
-mpssas_startup(void *data)
+mpssas_discovery_timeout(void *data)
 {
 	struct mpssas_softc *sassc = data;
-
-	mps_dprint(sassc->sc, MPS_TRACE, "%s\n", __func__);
-
-	mps_lock(sassc->sc);
+	struct mps_softc *sc;
+
+	sc = sassc->sc;
+	mps_dprint(sc, MPS_TRACE, "%s\n", __func__);
+
+	mps_lock(sc);
+	mps_printf(sc,
+	    "Timeout waiting for discovery, interrupts may not be working!\n");
+	sassc->flags &= ~MPSSAS_DISCOVERY_TIMEOUT_PENDING;
+
+	/* Poll the hardware for events in case interrupts aren't working */
+	mps_intr_locked(sc);
+
+	mps_printf(sassc->sc,
+	    "Finished polling after discovery timeout at %d\n", ticks);
+
 	if ((sassc->flags & MPSSAS_IN_DISCOVERY) == 0) {
 		mpssas_discovery_end(sassc);
 	} else {
@@ -877,31 +791,8 @@
 			mpssas_discovery_end(sassc);
 		}
 	}
-	mps_unlock(sassc->sc);
-
-	return;
-}
-
-static void
-mpssas_discovery_timeout(void *data)
-{
-	struct mpssas_softc *sassc = data;
-	struct mps_softc *sc;
-
-	sc = sassc->sc;
-	mps_dprint(sc, MPS_TRACE, "%s\n", __func__);
-
-	mps_lock(sc);
-	mps_printf(sc,
-	    "Timeout waiting for discovery, interrupts may not be working!\n");
-	sassc->flags &= ~MPSSAS_DISCOVERY_TIMEOUT_PENDING;
-
-	/* Poll the hardware for events in case interrupts aren't working */
-	mps_intr_locked(sc);
+
 	mps_unlock(sc);
-
-	/* Check the status of discovery and re-arm the timeout if needed */
-	mpssas_startup(sassc);
 }
 
 static void
@@ -911,8 +802,9 @@
 
 	sassc = cam_sim_softc(sim);
 
-	mps_dprint(sassc->sc, MPS_TRACE, "%s func 0x%x\n", __func__,
+	mps_dprint(sassc->sc, MPS_TRACE, "%s func 0x%x\n", __func__, 
 	    ccb->ccb_h.func_code);
+	mtx_assert(&sassc->sc->mps_mtx, MA_OWNED);
 
 	switch (ccb->ccb_h.func_code) {
 	case XPT_PATH_INQ:
@@ -925,7 +817,7 @@
 		cpi->hba_misc = PIM_NOBUSRESET;
 		cpi->hba_eng_cnt = 0;
 		cpi->max_target = sassc->sc->facts->MaxTargets - 1;
-		cpi->max_lun = 8;
+		cpi->max_lun = 0;
 		cpi->initiator_id = 255;
 		strncpy(cpi->sim_vid, "FreeBSD", SIM_IDLEN);
 		strncpy(cpi->hba_vid, "LSILogic", HBA_IDLEN);
@@ -937,7 +829,12 @@
 		cpi->transport_version = 0;
 		cpi->protocol = PROTO_SCSI;
 		cpi->protocol_version = SCSI_REV_SPC;
-		cpi->maxio = MAXPHYS;
+#if __FreeBSD_version >= 800001
+		/*
+		 * XXX KDM where does this number come from?
+		 */
+		cpi->maxio = 256 * 1024;
+#endif
 		cpi->ccb_h.status = CAM_REQ_CMP;
 		break;
 	}
@@ -989,11 +886,14 @@
 		ccb->ccb_h.status = CAM_REQ_CMP;
 		break;
 	case XPT_RESET_DEV:
+		mps_printf(sassc->sc, "mpssas_action XPT_RESET_DEV\n");
 		mpssas_action_resetdev(sassc, ccb);
 		return;
 	case XPT_RESET_BUS:
 	case XPT_ABORT:
 	case XPT_TERM_IO:
+		mps_printf(sassc->sc, "mpssas_action faking success for "
+			   "abort or reset\n");
 		ccb->ccb_h.status = CAM_REQ_CMP;
 		break;
 	case XPT_SCSI_IO:
@@ -1003,7 +903,7 @@
 	case XPT_SMP_IO:
 		mpssas_action_smpio(sassc, ccb);
 		return;
-#endif /* __FreeBSD_version >= 900026 */
+#endif
 	default:
 		ccb->ccb_h.status = CAM_FUNC_NOTAVAIL;
 		break;
@@ -1012,349 +912,554 @@
 
 }
 
-#if 0
 static void
-mpssas_resettimeout_complete(struct mps_softc *sc, struct mps_command *cm)
+mpssas_announce_reset(struct mps_softc *sc, uint32_t ac_code,
+    target_id_t target_id, lun_id_t lun_id)
 {
-	MPI2_SCSI_TASK_MANAGE_REPLY *resp;
-	uint16_t code;
-
-	mps_dprint(sc, MPS_TRACE, "%s\n", __func__);
-
-	resp = (MPI2_SCSI_TASK_MANAGE_REPLY *)cm->cm_reply;
-	code = resp->ResponseCode;
-
-	mps_free_command(sc, cm);
-	mpssas_unfreeze_device(sassc, targ);
-
-	if (code != MPI2_SCSITASKMGMT_RSP_TM_COMPLETE) {
-		mps_reset_controller(sc);
+	path_id_t path_id = cam_sim_path(sc->sassc->sim);
+	struct cam_path *path;
+
+	mps_printf(sc, "%s code %x target %d lun %d\n", __func__,
+	    ac_code, target_id, lun_id);
+
+	if (xpt_create_path(&path, NULL, 
+		path_id, target_id, lun_id) != CAM_REQ_CMP) {
+		mps_printf(sc, "unable to create path for reset "
+			   "notification\n");
+		return;
 	}
 
-	return;
+	xpt_async(ac_code, path, NULL);
+	xpt_free_path(path);
 }
-#endif
+
+static void 
+mpssas_complete_all_commands(struct mps_softc *sc)
+{
+	struct mps_command *cm;
+	int i;
+	int completed;
+
+	mps_printf(sc, "%s\n", __func__);
+	mtx_assert(&sc->mps_mtx, MA_OWNED);
+
+	/* complete all commands with a NULL reply */
+	for (i = 1; i < sc->num_reqs; i++) {
+		cm = &sc->commands[i];
+		cm->cm_reply = NULL;
+		completed = 0;
+
+		if (cm->cm_flags & MPS_CM_FLAGS_POLLED)
+			cm->cm_flags |= MPS_CM_FLAGS_COMPLETE;
+
+		if (cm->cm_complete != NULL) {
+			mpssas_log_command(cm, 
+			    "completing cm %p state %x ccb %p for diag reset\n", 
+			    cm, cm->cm_state, cm->cm_ccb);
+
+			cm->cm_complete(sc, cm);
+			completed = 1;
+		}
+
+		if (cm->cm_flags & MPS_CM_FLAGS_WAKEUP) {
+			mpssas_log_command(cm, 
+			    "waking up cm %p state %x ccb %p for diag reset\n", 
+			    cm, cm->cm_state, cm->cm_ccb);
+			wakeup(cm);
+			completed = 1;
+		}
+		
+		if ((completed == 0) && (cm->cm_state != MPS_CM_STATE_FREE)) {
+			/* this should never happen, but if it does, log */
+			mpssas_log_command(cm, 
+			    "cm %p state %x flags 0x%x ccb %p during diag "
+			    "reset\n", cm, cm->cm_state, cm->cm_flags,
+			    cm->cm_ccb);
+		}
+	}
+}
+
+void
+mpssas_handle_reinit(struct mps_softc *sc)
+{
+	int i;
+
+	/* Go back into startup mode and freeze the simq, so that CAM
+	 * doesn't send any commands until after we've rediscovered all
+	 * targets and found the proper device handles for them.
+	 *
+	 * After the reset, portenable will trigger discovery, and after all
+	 * discovery-related activities have finished, the simq will be
+	 * released.
+	 */
+	mps_printf(sc, "%s startup\n", __func__);
+	sc->sassc->flags |= MPSSAS_IN_STARTUP;
+	sc->sassc->flags |= MPSSAS_IN_DISCOVERY;
+	xpt_freeze_simq(sc->sassc->sim, 1);
+
+	/* notify CAM of a bus reset */
+	mpssas_announce_reset(sc, AC_BUS_RESET, CAM_TARGET_WILDCARD, 
+	    CAM_LUN_WILDCARD);
+
+	/* complete and cleanup after all outstanding commands */
+	mpssas_complete_all_commands(sc);
+
+	mps_printf(sc, "%s startup %u tm %u after command completion\n",
+	    __func__, sc->sassc->startup_refcount, sc->sassc->tm_count);
+
+	/*
+	 * The simq was explicitly frozen above, so set the refcount to 0.
+	 * The simq will be explicitly released after port enable completes.
+	 */
+	sc->sassc->startup_refcount = 0;
+
+	/* zero all the target handles, since they may change after the
+	 * reset, and we have to rediscover all the targets and use the new
+	 * handles.  
+	 */
+	for (i = 0; i < sc->facts->MaxTargets; i++) {
+		if (sc->sassc->targets[i].outstanding != 0)
+			mps_printf(sc, "target %u outstanding %u\n", 
+			    i, sc->sassc->targets[i].outstanding);
+		sc->sassc->targets[i].handle = 0x0;
+		sc->sassc->targets[i].exp_dev_handle = 0x0;
+		sc->sassc->targets[i].outstanding = 0;
+		sc->sassc->targets[i].flags = MPSSAS_TARGET_INDIAGRESET;
+	}
+}
+static void
+mpssas_tm_timeout(void *data)
+{
+	struct mps_command *tm = data;
+	struct mps_softc *sc = tm->cm_sc;
+
+	mtx_assert(&sc->mps_mtx, MA_OWNED);
+
+	mpssas_log_command(tm, "task mgmt %p timed out\n", tm);
+	mps_reinit(sc);
+}
+
+static void
+mpssas_logical_unit_reset_complete(struct mps_softc *sc, struct mps_command *tm)
+{
+	MPI2_SCSI_TASK_MANAGE_REPLY *reply;
+	MPI2_SCSI_TASK_MANAGE_REQUEST *req;
+	unsigned int cm_count = 0;
+	struct mps_command *cm;
+	struct mpssas_target *targ;
+
+	callout_stop(&tm->cm_callout);
+
+	req = (MPI2_SCSI_TASK_MANAGE_REQUEST *)tm->cm_req;
+	reply = (MPI2_SCSI_TASK_MANAGE_REPLY *)tm->cm_reply;
+	targ = tm->cm_targ;
+
+	/*
+	 * Currently there should be no way we can hit this case.  It only
+	 * happens when we have a failure to allocate chain frames, and
+	 * task management commands don't have S/G lists.
+	 */
+	if ((tm->cm_flags & MPS_CM_FLAGS_ERROR_MASK) != 0) {
+		mps_printf(sc, "%s: cm_flags = %#x for LUN reset! "
+			   "This should not happen!\n", __func__, tm->cm_flags);
+		mpssas_free_tm(sc, tm);
+		return;
+	}
+
+	if (reply == NULL) {
+		mpssas_log_command(tm, "NULL reset reply for tm %p\n", tm);
+		if ((sc->mps_flags & MPS_FLAGS_DIAGRESET) != 0) {
+			/* this completion was due to a reset, just cleanup */
+			targ->flags &= ~MPSSAS_TARGET_INRESET;
+			targ->tm = NULL;
+			mpssas_free_tm(sc, tm);
+		}
+		else {
+			/* we should have gotten a reply. */
+			mps_reinit(sc);
+		}
+		return;
+	}
+
+	mpssas_log_command(tm, 
+	    "logical unit reset status 0x%x code 0x%x count %u\n",
+	    reply->IOCStatus, reply->ResponseCode,
+	    reply->TerminationCount);
+		
+	/* See if there are any outstanding commands for this LUN.
+	 * This could be made more efficient by using a per-LU data
+	 * structure of some sort.
+	 */
+	TAILQ_FOREACH(cm, &targ->commands, cm_link) {
+		if (cm->cm_lun == tm->cm_lun)
+			cm_count++;
+	}
+
+	if (cm_count == 0) {
+		mpssas_log_command(tm, 
+		    "logical unit %u finished recovery after reset\n",
+		    tm->cm_lun, tm);
+
+		mpssas_announce_reset(sc, AC_SENT_BDR, tm->cm_targ->tid, 
+		    tm->cm_lun);
+
+		/* we've finished recovery for this logical unit.  check and
+		 * see if some other logical unit has a timedout command
+		 * that needs to be processed.
+		 */
+		cm = TAILQ_FIRST(&targ->timedout_commands);
+		if (cm) {
+			mpssas_send_abort(sc, tm, cm);
+		}
+		else {
+			targ->tm = NULL;
+			mpssas_free_tm(sc, tm);
+		}
+	}
+	else {
+		/* if we still have commands for this LUN, the reset
+		 * effectively failed, regardless of the status reported.
+		 * Escalate to a target reset.
+		 */
+		mpssas_log_command(tm, 
+		    "logical unit reset complete for tm %p, but still have %u command(s)\n",
+		    tm, cm_count);
+		mpssas_send_reset(sc, tm,
+		    MPI2_SCSITASKMGMT_TASKTYPE_TARGET_RESET);
+	}
+}
+
+static void
+mpssas_target_reset_complete(struct mps_softc *sc, struct mps_command *tm)
+{
+	MPI2_SCSI_TASK_MANAGE_REPLY *reply;
+	MPI2_SCSI_TASK_MANAGE_REQUEST *req;
+	struct mpssas_target *targ;
+
+	callout_stop(&tm->cm_callout);
+
+	req = (MPI2_SCSI_TASK_MANAGE_REQUEST *)tm->cm_req;
+	reply = (MPI2_SCSI_TASK_MANAGE_REPLY *)tm->cm_reply;
+	targ = tm->cm_targ;
+
+	/*
+	 * Currently there should be no way we can hit this case.  It only
+	 * happens when we have a failure to allocate chain frames, and
+	 * task management commands don't have S/G lists.
+	 */
+	if ((tm->cm_flags & MPS_CM_FLAGS_ERROR_MASK) != 0) {
+		mps_printf(sc, "%s: cm_flags = %#x for target reset! "
+			   "This should not happen!\n", __func__, tm->cm_flags);
+		mpssas_free_tm(sc, tm);
+		return;
+	}
+
+	if (reply == NULL) {
+		mpssas_log_command(tm, "NULL reset reply for tm %p\n", tm);
+		if ((sc->mps_flags & MPS_FLAGS_DIAGRESET) != 0) {
+			/* this completion was due to a reset, just cleanup */
+			targ->flags &= ~MPSSAS_TARGET_INRESET;
+			targ->tm = NULL;
+			mpssas_free_tm(sc, tm);
+		}
+		else {
+			/* we should have gotten a reply. */
+			mps_reinit(sc);
+		}
+		return;
+	}
+
+	mpssas_log_command(tm, 
+	    "target reset status 0x%x code 0x%x count %u\n",
+	    reply->IOCStatus, reply->ResponseCode,
+	    reply->TerminationCount);
+
+	targ->flags &= ~MPSSAS_TARGET_INRESET;
+
+	if (targ->outstanding == 0) {
+		/* we've finished recovery for this target and all
+		 * of its logical units.
+		 */
+		mpssas_log_command(tm, 
+		    "recovery finished after target reset\n");
+
+		mpssas_announce_reset(sc, AC_SENT_BDR, tm->cm_targ->tid,
+		    CAM_LUN_WILDCARD);
+
+		targ->tm = NULL;
+		mpssas_free_tm(sc, tm);
+	}
+	else {
+		/* after a target reset, if this target still has
+		 * outstanding commands, the reset effectively failed,
+		 * regardless of the status reported.  escalate.
+		 */
+		mpssas_log_command(tm, 
+		    "target reset complete for tm %p, but still have %u command(s)\n", 
+		    tm, targ->outstanding);
+		mps_reinit(sc);
+	}
+}
+
+#define MPS_RESET_TIMEOUT 30
+
+static int
+mpssas_send_reset(struct mps_softc *sc, struct mps_command *tm, uint8_t type)
+{
+	MPI2_SCSI_TASK_MANAGE_REQUEST *req;
+	struct mpssas_target *target;
+	int err;
+
+	target = tm->cm_targ;
+	if (target->handle == 0) {
+		mps_printf(sc, "%s null devhandle for target_id %d\n",
+		    __func__, target->tid);
+		return -1;
+	}
+
+	req = (MPI2_SCSI_TASK_MANAGE_REQUEST *)tm->cm_req;
+	req->DevHandle = target->handle;
+	req->Function = MPI2_FUNCTION_SCSI_TASK_MGMT;
+	req->TaskType = type;
+
+	if (type == MPI2_SCSITASKMGMT_TASKTYPE_LOGICAL_UNIT_RESET) {
+		/* XXX Need to handle invalid LUNs */
+		MPS_SET_LUN(req->LUN, tm->cm_lun);
+		tm->cm_targ->logical_unit_resets++;
+		mpssas_log_command(tm, "sending logical unit reset\n");
+		tm->cm_complete = mpssas_logical_unit_reset_complete;
+	}
+	else if (type == MPI2_SCSITASKMGMT_TASKTYPE_TARGET_RESET) {
+		/* Target reset method =  SAS Hard Link Reset / SATA Link Reset */
+		req->MsgFlags = MPI2_SCSITASKMGMT_MSGFLAGS_LINK_RESET;
+		tm->cm_targ->target_resets++;
+		tm->cm_targ->flags |= MPSSAS_TARGET_INRESET;
+		mpssas_log_command(tm, "sending target reset\n");
+		tm->cm_complete = mpssas_target_reset_complete;
+	}
+	else {
+		mps_printf(sc, "unexpected reset type 0x%x\n", type);
+		return -1;
+	}
+
+	tm->cm_data = NULL;
+	tm->cm_desc.HighPriority.RequestFlags = MPI2_REQ_DESCRIPT_FLAGS_HIGH_PRIORITY;
+	tm->cm_complete_data = (void *)tm;
+
+	callout_reset(&tm->cm_callout, MPS_RESET_TIMEOUT * hz,
+	    mpssas_tm_timeout, tm);
+
+	err = mps_map_command(sc, tm);
+	if (err)
+		mpssas_log_command(tm,
+		    "error %d sending reset type %u\n",
+		    err, type);
+
+	return err;
+}
+
+
+static void
+mpssas_abort_complete(struct mps_softc *sc, struct mps_command *tm)
+{
+	struct mps_command *cm;
+	MPI2_SCSI_TASK_MANAGE_REPLY *reply;
+	MPI2_SCSI_TASK_MANAGE_REQUEST *req;
+	struct mpssas_target *targ;
+
+	callout_stop(&tm->cm_callout);
+
+	req = (MPI2_SCSI_TASK_MANAGE_REQUEST *)tm->cm_req;
+	reply = (MPI2_SCSI_TASK_MANAGE_REPLY *)tm->cm_reply;
+	targ = tm->cm_targ;
+
+	/*
+	 * Currently there should be no way we can hit this case.  It only
+	 * happens when we have a failure to allocate chain frames, and
+	 * task management commands don't have S/G lists.
+	 */
+	if ((tm->cm_flags & MPS_CM_FLAGS_ERROR_MASK) != 0) {
+		mpssas_log_command(tm,
+		    "cm_flags = %#x for abort %p TaskMID %u!\n", 
+		    tm->cm_flags, tm, req->TaskMID);
+		mpssas_free_tm(sc, tm);
+		return;
+	}
+
+	if (reply == NULL) {
+		mpssas_log_command(tm,
+		    "NULL abort reply for tm %p TaskMID %u\n", 
+		    tm, req->TaskMID);
+		if ((sc->mps_flags & MPS_FLAGS_DIAGRESET) != 0) {
+			/* this completion was due to a reset, just cleanup */
+			targ->tm = NULL;
+			mpssas_free_tm(sc, tm);
+		}
+		else {
+			/* we should have gotten a reply. */
+			mps_reinit(sc);
+		}
+		return;
+	}
+
+	mpssas_log_command(tm,
+	    "abort TaskMID %u status 0x%x code 0x%x count %u\n",
+	    req->TaskMID,
+	    reply->IOCStatus, reply->ResponseCode,
+	    reply->TerminationCount);
+
+	cm = TAILQ_FIRST(&tm->cm_targ->timedout_commands);
+	if (cm == NULL) {
+		/* if there are no more timedout commands, we're done with
+		 * error recovery for this target.
+		 */
+		mpssas_log_command(tm, 
+		    "finished recovery after aborting TaskMID %u\n",
+		    req->TaskMID);
+
+		targ->tm = NULL;
+		mpssas_free_tm(sc, tm);
+	}
+	else if (req->TaskMID != cm->cm_desc.Default.SMID) {
+		/* abort success, but we have more timedout commands to abort */
+		mpssas_log_command(tm, 
+		    "continuing recovery after aborting TaskMID %u\n",
+		    req->TaskMID);
+		
+		mpssas_send_abort(sc, tm, cm);
+	}
+	else {
+		/* we didn't get a command completion, so the abort
+		 * failed as far as we're concerned.  escalate.
+		 */
+		mpssas_log_command(tm, 
+		    "abort failed for TaskMID %u tm %p\n",
+		    req->TaskMID, tm);
+
+		mpssas_send_reset(sc, tm, 
+		    MPI2_SCSITASKMGMT_TASKTYPE_LOGICAL_UNIT_RESET);
+	}
+}
+
+#define MPS_ABORT_TIMEOUT 5
+
+static int
+mpssas_send_abort(struct mps_softc *sc, struct mps_command *tm, struct mps_command *cm)
+{
+	MPI2_SCSI_TASK_MANAGE_REQUEST *req;
+	struct mpssas_target *targ;
+	int err;
+
+	targ = cm->cm_targ;
+	if (targ->handle == 0) {
+		mps_printf(sc, "%s null devhandle for target_id %d\n",
+		    __func__, cm->cm_ccb->ccb_h.target_id);
+		return -1;
+	}
+
+	req = (MPI2_SCSI_TASK_MANAGE_REQUEST *)tm->cm_req;
+	req->DevHandle = targ->handle;
+	req->Function = MPI2_FUNCTION_SCSI_TASK_MGMT;
+	req->TaskType = MPI2_SCSITASKMGMT_TASKTYPE_ABORT_TASK;
+
+	/* XXX Need to handle invalid LUNs */
+	MPS_SET_LUN(req->LUN, cm->cm_ccb->ccb_h.target_lun);
+
+	req->TaskMID = cm->cm_desc.Default.SMID;
+
+	tm->cm_data = NULL;
+	tm->cm_desc.HighPriority.RequestFlags = MPI2_REQ_DESCRIPT_FLAGS_HIGH_PRIORITY;
+	tm->cm_complete = mpssas_abort_complete;
+	tm->cm_complete_data = (void *)tm;
+	tm->cm_targ = cm->cm_targ;
+	tm->cm_lun = cm->cm_lun;
+
+	callout_reset(&tm->cm_callout, MPS_ABORT_TIMEOUT * hz,
+	    mpssas_tm_timeout, tm);
+
+	targ->aborts++;
+
+	err = mps_map_command(sc, tm);
+	if (err)
+		mpssas_log_command(tm,
+		    "error %d sending abort for cm %p SMID %u\n",
+		    err, cm, req->TaskMID);
+	return err;
+}
+
 
 static void
 mpssas_scsiio_timeout(void *data)
 {
-	union ccb *ccb;
 	struct mps_softc *sc;
 	struct mps_command *cm;
 	struct mpssas_target *targ;
-#if 0
-	char cdb_str[(SCSI_MAX_CDBLEN * 3) + 1];
-#endif
 
 	cm = (struct mps_command *)data;
 	sc = cm->cm_sc;
 
+	mtx_assert(&sc->mps_mtx, MA_OWNED);
+
+	mps_printf(sc, "%s checking sc %p cm %p\n", __func__, sc, cm);
+
 	/*
 	 * Run the interrupt handler to make sure it's not pending.  This
 	 * isn't perfect because the command could have already completed
 	 * and been re-used, though this is unlikely.
 	 */
-	mps_lock(sc);
 	mps_intr_locked(sc);
 	if (cm->cm_state == MPS_CM_STATE_FREE) {
-		mps_unlock(sc);
+		mps_printf(sc, "SCSI command %p sc %p almost timed out\n", cm, sc);
 		return;
 	}
 
-	ccb = cm->cm_complete_data;
-	targ = cm->cm_targ;
-	if (targ == 0x00)
-		/* Driver bug */
-		targ = &sc->sassc->targets[ccb->ccb_h.target_id];
-
-	xpt_print(ccb->ccb_h.path, "SCSI command timeout on device handle "
-		  "0x%04x SMID %d\n", targ->handle, cm->cm_desc.Default.SMID);
-	/*
-	 * XXX KDM this is useful for debugging purposes, but the existing
-	 * scsi_op_desc() implementation can't handle a NULL value for
-	 * inq_data.  So this will remain commented out until I bring in
-	 * those changes as well.
-	 */
-#if 0
-	xpt_print(ccb->ccb_h.path, "Timed out command: %s. CDB %s\n",
-		  scsi_op_desc((ccb->ccb_h.flags & CAM_CDB_POINTER) ?
-		  		ccb->csio.cdb_io.cdb_ptr[0] :
-				ccb->csio.cdb_io.cdb_bytes[0], NULL),
-		  scsi_cdb_string((ccb->ccb_h.flags & CAM_CDB_POINTER) ?
-				   ccb->csio.cdb_io.cdb_ptr :
-				   ccb->csio.cdb_io.cdb_bytes, cdb_str,
-		  		   sizeof(cdb_str)));
-#endif
-
-	/* Inform CAM about the timeout and that recovery is starting. */
-#if 0
-	if ((targ->flags & MPSSAS_TARGET_INRECOVERY) == 0) {
-		mpssas_freeze_device(sc->sassc, targ);
-		ccb->ccb_h.status = CAM_CMD_TIMEOUT;
-		xpt_done(ccb);
-	}
-#endif
-	mpssas_freeze_device(sc->sassc, targ);
-	ccb->ccb_h.status = CAM_CMD_TIMEOUT;
-
-	/*
-	 * recycle the command into recovery so that there's no risk of
-	 * command allocation failure.
-	 */
-	cm->cm_state = MPS_CM_STATE_TIMEDOUT;
-	mpssas_recovery(sc, cm);
-	mps_unlock(sc);
-}
-
-static void
-mpssas_abort_complete(struct mps_softc *sc, struct mps_command *cm)
-{
-	MPI2_SCSI_TASK_MANAGE_REQUEST *req;
-
-	req = (MPI2_SCSI_TASK_MANAGE_REQUEST *)cm->cm_req;
-
-	/*
-	 * Currently there should be no way we can hit this case.  It only
-	 * happens when we have a failure to allocate chain frames, and
-	 * task management commands don't have S/G lists.
-	 */
-	if ((cm->cm_flags & MPS_CM_FLAGS_ERROR_MASK) != 0) {
-		mps_printf(sc, "%s: cm_flags = %#x for abort on handle %#04x! "
-			   "This should not happen!\n", __func__, cm->cm_flags,
-			   req->DevHandle);
-	}
-
-	mps_printf(sc, "%s: abort request on handle %#04x SMID %d "
-		   "complete\n", __func__, req->DevHandle, req->TaskMID);
-
-	mpssas_complete_tm_request(sc, cm, /*free_cm*/ 1);
-}
-
-static void
-mpssas_recovery(struct mps_softc *sc, struct mps_command *abort_cm)
-{
-	struct mps_command *cm;
-	MPI2_SCSI_TASK_MANAGE_REQUEST *req, *orig_req;
-
-	cm = mps_alloc_command(sc);
-	if (cm == NULL) {
-		mps_printf(sc, "%s: command allocation failure\n", __func__);
+	if (cm->cm_ccb == NULL) {
+		mps_printf(sc, "command timeout with NULL ccb\n");
 		return;
 	}
 
-	cm->cm_targ = abort_cm->cm_targ;
-	cm->cm_complete = mpssas_abort_complete;
-
-	req = (MPI2_SCSI_TASK_MANAGE_REQUEST *)cm->cm_req;
-	orig_req = (MPI2_SCSI_TASK_MANAGE_REQUEST *)abort_cm->cm_req;
-	req->DevHandle = abort_cm->cm_targ->handle;
-	req->Function = MPI2_FUNCTION_SCSI_TASK_MGMT;
-	req->TaskType = MPI2_SCSITASKMGMT_TASKTYPE_ABORT_TASK;
-	memcpy(req->LUN, orig_req->LUN, sizeof(req->LUN));
-	req->TaskMID = abort_cm->cm_desc.Default.SMID;
-
-	cm->cm_data = NULL;
-	cm->cm_desc.Default.RequestFlags = MPI2_REQ_DESCRIPT_FLAGS_DEFAULT_TYPE;
-
-	mpssas_issue_tm_request(sc, cm);
-
-}
-
-/*
- * Can return 0 or EINPROGRESS on success.  Any other value means failure.
- */
-static int
-mpssas_map_tm_request(struct mps_softc *sc, struct mps_command *cm)
-{
-	int error;
-
-	error = 0;
-
-	cm->cm_flags |= MPS_CM_FLAGS_ACTIVE;
-	error = mps_map_command(sc, cm);
-	if ((error == 0)
-	 || (error == EINPROGRESS))
-		sc->tm_cmds_active++;
-
-	return (error);
-}
-
-static void
-mpssas_issue_tm_request(struct mps_softc *sc, struct mps_command *cm)
-{
-	int freeze_queue, send_command, error;
-
-	freeze_queue = 0;
-	send_command = 0;
-	error = 0;
-
-	mtx_assert(&sc->mps_mtx, MA_OWNED);
-
-	/*
-	 * If there are no other pending task management commands, go
-	 * ahead and send this one.  There is a small amount of anecdotal
-	 * evidence that sending lots of task management commands at once
-	 * may cause the controller to lock up.  Or, if the user has
-	 * configured the driver (via the allow_multiple_tm_cmds variable) to
-	 * not serialize task management commands, go ahead and send the
-	 * command if even other task management commands are pending.
+	mpssas_log_command(cm, "command timeout cm %p ccb %p\n", 
+	    cm, cm->cm_ccb);
+
+	targ = cm->cm_targ;
+	targ->timeouts++;
+
+	/* XXX first, check the firmware state, to see if it's still
+	 * operational.  if not, do a diag reset.
 	 */
-	if (TAILQ_FIRST(&sc->tm_list) == NULL) {
-		send_command = 1;
-		freeze_queue = 1;
-	} else if (sc->allow_multiple_tm_cmds != 0)
-		send_command = 1;
-
-	TAILQ_INSERT_TAIL(&sc->tm_list, cm, cm_link);
-	if (send_command != 0) {
-		/*
-		 * Freeze the SIM queue while we issue the task management
-		 * command.  According to the Fusion-MPT 2.0 spec, task
-		 * management requests are serialized, and so the host
-		 * should not send any I/O requests while task management
-		 * requests are pending.
+
+	cm->cm_ccb->ccb_h.status = CAM_CMD_TIMEOUT;
+	cm->cm_state = MPS_CM_STATE_TIMEDOUT;
+	TAILQ_INSERT_TAIL(&targ->timedout_commands, cm, cm_recovery);
+
+	if (targ->tm != NULL) {
+		/* target already in recovery, just queue up another
+		 * timedout command to be processed later.
 		 */
-		if (freeze_queue != 0)
-			xpt_freeze_simq(sc->sassc->sim, 1);
-
-		error = mpssas_map_tm_request(sc, cm);
-
-		/*
-		 * At present, there is no error path back from
-		 * mpssas_map_tm_request() (which calls mps_map_command())
-		 * when cm->cm_data == NULL.  But since there is a return
-		 * value, we check it just in case the implementation
-		 * changes later.
+		mps_printf(sc, "queued timedout cm %p for processing by tm %p\n",
+		    cm, targ->tm);
+	}
+	else if ((targ->tm = mpssas_alloc_tm(sc)) != NULL) {
+		mps_printf(sc, "timedout cm %p allocated tm %p\n",
+		    cm, targ->tm);
+
+		/* start recovery by aborting the first timedout command */
+		mpssas_send_abort(sc, targ->tm, cm);
+	}
+	else {
+		/* XXX queue this target up for recovery once a TM becomes
+		 * available.  The firmware only has a limited number of
+		 * HighPriority credits for the high priority requests used
+		 * for task management, and we ran out.
+		 * 
+		 * Isilon: don't worry about this for now, since we have
+		 * more credits than disks in an enclosure, and limit
+		 * ourselves to one TM per target for recovery.
 		 */
-		if ((error != 0)
-		 && (error != EINPROGRESS))
-			mpssas_tm_complete(sc, cm,
-			    MPI2_SCSITASKMGMT_RSP_TM_FAILED);
+		mps_printf(sc, "timedout cm %p failed to allocate a tm\n",
+		    cm);
 	}
-}
-
-static void
-mpssas_tm_complete(struct mps_softc *sc, struct mps_command *cm, int error)
-{
-	MPI2_SCSI_TASK_MANAGE_REPLY *resp;
-
-	resp = (MPI2_SCSI_TASK_MANAGE_REPLY *)cm->cm_reply;
-
-	if (resp != NULL)
-		resp->ResponseCode = error;
-
-	/*
-	 * Call the callback for this command, it will be
-	 * removed from the list and freed via the callback.
-	 */
-	cm->cm_complete(sc, cm);
-}
-
-/*
- * Complete a task management request.  The basic completion operation will
- * always succeed.  Returns status for sending any further task management
- * commands that were queued.
- */
-static int
-mpssas_complete_tm_request(struct mps_softc *sc, struct mps_command *cm,
-			   int free_cm)
-{
-	int error;
-
-	error = 0;
-
-	mtx_assert(&sc->mps_mtx, MA_OWNED);
-
-	TAILQ_REMOVE(&sc->tm_list, cm, cm_link);
-	cm->cm_flags &= ~MPS_CM_FLAGS_ACTIVE;
-	sc->tm_cmds_active--;
-
-	if (free_cm != 0)
-		mps_free_command(sc, cm);
-
-	if (TAILQ_FIRST(&sc->tm_list) == NULL) {
-		/*
-		 * Release the SIM queue, we froze it when we sent the first
-		 * task management request.
-		 */
-		xpt_release_simq(sc->sassc->sim, 1);
-	} else if ((sc->tm_cmds_active == 0)
-		|| (sc->allow_multiple_tm_cmds != 0)) {
-		int error;
-		struct mps_command *cm2;
-
-restart_traversal:
-
-		/*
-		 * We don't bother using TAILQ_FOREACH_SAFE here, but
-		 * rather use the standard version and just restart the
-		 * list traversal if we run into the error case.
-		 * TAILQ_FOREACH_SAFE allows safe removal of the current
-		 * list element, but if you have a queue of task management
-		 * commands, all of which have mapping errors, you'll end
-		 * up with recursive calls to this routine and so you could
-		 * wind up removing more than just the current list element.
-		 */
-		TAILQ_FOREACH(cm2, &sc->tm_list, cm_link) {
-			MPI2_SCSI_TASK_MANAGE_REQUEST *req;
-
-			/* This command is active, no need to send it again */
-			if (cm2->cm_flags & MPS_CM_FLAGS_ACTIVE)
-				continue;
-
-			req = (MPI2_SCSI_TASK_MANAGE_REQUEST *)cm2->cm_req;
-
-			mps_printf(sc, "%s: sending deferred task management "
-			    "request for handle %#04x SMID %d\n", __func__,
-			    req->DevHandle, req->TaskMID);
-
-			error = mpssas_map_tm_request(sc, cm2);
-
-			/*
-			 * Check for errors.  If we had an error, complete
-			 * this command with an error, and keep going through
-			 * the list until we are able to send at least one
-			 * command or all of them are completed with errors.
-			 *
-			 * We don't want to wind up in a situation where
-			 * we're stalled out with no way for queued task
-			 * management commands to complete.
-			 *
-			 * Note that there is not currently an error path
-			 * back from mpssas_map_tm_request() (which calls
-			 * mps_map_command()) when cm->cm_data == NULL.
-			 * But we still want to check for errors here in
-			 * case the implementation changes, or in case
-			 * there is some reason for a data payload here.
-			 */
-			if ((error != 0)
-			 && (error != EINPROGRESS)) {
-				mpssas_tm_complete(sc, cm,
-				    MPI2_SCSITASKMGMT_RSP_TM_FAILED);
-
-				/*
-				 * If we don't currently have any commands
-				 * active, go back to the beginning and see
-				 * if there are any more that can be started.
-				 * Otherwise, we're done here.
-				 */
-				if (sc->tm_cmds_active == 0)
-					goto restart_traversal;
-				else
-					break;
-			}
-
-			/*
-			 * If the user only wants one task management command
-			 * active at a time, we're done, since we've
-			 * already successfully sent a command at this point.
-			 */
-			if (sc->allow_multiple_tm_cmds == 0)
-				break;
-		}
-	}
-
-	return (error);
+
 }
 
 static void
@@ -1364,16 +1469,41 @@
 	struct ccb_scsiio *csio;
 	struct mps_softc *sc;
 	struct mpssas_target *targ;
+	struct mpssas_lun *lun;
 	struct mps_command *cm;
-
-	mps_dprint(sassc->sc, MPS_TRACE, "%s\n", __func__);
+	uint8_t i, lba_byte, *ref_tag_addr;
+	uint16_t eedp_flags;
 
 	sc = sassc->sc;
+	mps_dprint(sc, MPS_TRACE, "%s ccb %p\n", __func__, ccb);
+	mtx_assert(&sc->mps_mtx, MA_OWNED);
 
 	csio = &ccb->csio;
 	targ = &sassc->targets[csio->ccb_h.target_id];
 	if (targ->handle == 0x0) {
-		csio->ccb_h.status = CAM_SEL_TIMEOUT;
+		mps_dprint(sc, MPS_TRACE, "%s NULL handle for target %u\n", 
+		    __func__, csio->ccb_h.target_id);
+		csio->ccb_h.status = CAM_TID_INVALID;
+		xpt_done(ccb);
+		return;
+	}
+	/*
+	 * If devinfo is 0 this will be a volume.  In that case don't tell CAM
+	 * that the volume has timed out.  We want volumes to be enumerated
+	 * until they are deleted/removed, not just failed.
+	 */
+	if (targ->flags & MPSSAS_TARGET_INREMOVAL) {
+		if (targ->devinfo == 0)
+			csio->ccb_h.status = CAM_REQ_CMP;
+		else
+			csio->ccb_h.status = CAM_SEL_TIMEOUT;
+		xpt_done(ccb);
+		return;
+	}
+
+	if ((sc->mps_flags & MPS_FLAGS_SHUTDOWN) != 0) {
+		mps_dprint(sc, MPS_TRACE, "%s shutting down\n", __func__);
+		csio->ccb_h.status = CAM_TID_INVALID;
 		xpt_done(ccb);
 		return;
 	}
@@ -1446,6 +1576,7 @@
 		req->Control |= MPI2_SCSIIO_CONTROL_SIMPLEQ;
 		break;
 	}
+	req->Control |= sc->mapping_table[csio->ccb_h.target_id].TLR_bits;
 
 	if (MPS_SET_LUN(req->LUN, csio->ccb_h.target_lun) != 0) {
 		mps_free_command(sc, cm);
@@ -1461,8 +1592,57 @@
 	req->IoFlags = csio->cdb_len;
 
 	/*
-	 * XXX need to handle S/G lists and physical addresses here.
+	 * Check if EEDP is supported and enabled.  If it is then check if the
+	 * SCSI opcode could be using EEDP.  If so, make sure the LUN exists and
+	 * is formatted for EEDP support.  If all of this is true, set CDB up
+	 * for EEDP transfer.
 	 */
+	eedp_flags = op_code_prot[req->CDB.CDB32[0]];
+	if (sc->eedp_enabled && eedp_flags) {
+		SLIST_FOREACH(lun, &targ->luns, lun_link) {
+			if (lun->lun_id == csio->ccb_h.target_lun) {
+				break;
+			}
+		}
+
+		if ((lun != NULL) && (lun->eedp_formatted)) {
+			req->EEDPBlockSize = lun->eedp_block_size;
+			eedp_flags |= (MPI2_SCSIIO_EEDPFLAGS_INC_PRI_REFTAG |
+			    MPI2_SCSIIO_EEDPFLAGS_CHECK_REFTAG |
+			    MPI2_SCSIIO_EEDPFLAGS_CHECK_GUARD);
+			req->EEDPFlags = eedp_flags;
+
+			/*
+			 * If CDB less than 32, fill in Primary Ref Tag with
+			 * low 4 bytes of LBA.  If CDB is 32, tag stuff is
+			 * already there.  Also, set protection bit.  FreeBSD
+			 * currently does not support CDBs bigger than 16, but
+			 * the code doesn't hurt, and will be here for the
+			 * future.
+			 */
+			if (csio->cdb_len != 32) {
+				lba_byte = (csio->cdb_len == 16) ? 6 : 2;
+				ref_tag_addr = (uint8_t *)&req->CDB.EEDP32.
+				    PrimaryReferenceTag;
+				for (i = 0; i < 4; i++) {
+					*ref_tag_addr =
+					    req->CDB.CDB32[lba_byte + i];
+					ref_tag_addr++;
+				}
+				req->CDB.EEDP32.PrimaryApplicationTagMask =
+				    0xFFFF;
+				req->CDB.CDB32[1] = (req->CDB.CDB32[1] & 0x1F) |
+				    0x20;
+			} else {
+				eedp_flags |=
+				    MPI2_SCSIIO_EEDPFLAGS_INC_PRI_APPTAG;
+				req->EEDPFlags = eedp_flags;
+				req->CDB.CDB32[10] = (req->CDB.CDB32[10] &
+				    0x1F) | 0x20;
+			}
+		}
+	}
+
 	cm->cm_data = csio->data_ptr;
 	cm->cm_length = csio->dxfer_len;
 	cm->cm_sge = &req->SGL;
@@ -1472,15 +1652,33 @@
 	cm->cm_complete = mpssas_scsiio_complete;
 	cm->cm_complete_data = ccb;
 	cm->cm_targ = targ;
-
-	sc->io_cmds_active++;
-	if (sc->io_cmds_active > sc->io_cmds_highwater)
-		sc->io_cmds_highwater = sc->io_cmds_active;
-
-	TAILQ_INSERT_TAIL(&sc->io_list, cm, cm_link);
+	cm->cm_lun = csio->ccb_h.target_lun;
+	cm->cm_ccb = ccb;
+
+	/*
+	 * If HBA is a WD and the command is not for a retry, try to build a
+	 * direct I/O message. If failed, or the command is for a retry, send
+	 * the I/O to the IR volume itself.
+	 */
+	if (sc->WD_valid_config) {
+		if (ccb->ccb_h.status != MPS_WD_RETRY) {
+			mpssas_direct_drive_io(sassc, cm, ccb);
+		} else {
+			ccb->ccb_h.status = CAM_REQ_INPROG;
+		}
+	}
+
 	callout_reset(&cm->cm_callout, (ccb->ccb_h.timeout * hz) / 1000,
 	   mpssas_scsiio_timeout, cm);
 
+	targ->issued++;
+	targ->outstanding++;
+	TAILQ_INSERT_TAIL(&targ->commands, cm, cm_link);
+
+	if ((sc->mps_debug & MPS_TRACE) != 0)
+		mpssas_log_command(cm, "%s cm %p ccb %p outstanding %u\n",
+		    __func__, cm, ccb, targ->outstanding);
+
 	mps_map_command(sc, cm);
 	return;
 }
@@ -1490,19 +1688,25 @@
 {
 	MPI2_SCSI_IO_REPLY *rep;
 	union ccb *ccb;
+	struct ccb_scsiio *csio;
 	struct mpssas_softc *sassc;
-	int dir = 0;
-
-	mps_dprint(sc, MPS_TRACE, "%s\n", __func__);
+	struct scsi_vpd_supported_page_list *vpd_list = NULL;
+	u8 *TLR_bits, TLR_on;
+	int dir = 0, i;
+	u16 alloc_len;
+
+	mps_dprint(sc, MPS_TRACE,
+	    "%s cm %p SMID %u ccb %p reply %p outstanding %u\n",
+	    __func__, cm, cm->cm_desc.Default.SMID, cm->cm_ccb, cm->cm_reply,
+	    cm->cm_targ->outstanding);
 
 	callout_stop(&cm->cm_callout);
-	TAILQ_REMOVE(&sc->io_list, cm, cm_link);
-	sc->io_cmds_active--;
+	mtx_assert(&sc->mps_mtx, MA_OWNED);
 
 	sassc = sc->sassc;
 	ccb = cm->cm_complete_data;
+	csio = &ccb->csio;
 	rep = (MPI2_SCSI_IO_REPLY *)cm->cm_reply;
-
 	/*
 	 * XXX KDM if the chain allocation fails, does it matter if we do
 	 * the sync and unload here?  It is simpler to do it in every case,
@@ -1517,6 +1721,41 @@
 		bus_dmamap_unload(sc->buffer_dmat, cm->cm_dmamap);
 	}
 
+	cm->cm_targ->completed++;
+	cm->cm_targ->outstanding--;
+	TAILQ_REMOVE(&cm->cm_targ->commands, cm, cm_link);
+
+	if (cm->cm_state == MPS_CM_STATE_TIMEDOUT) {
+		TAILQ_REMOVE(&cm->cm_targ->timedout_commands, cm, cm_recovery);
+		if (cm->cm_reply != NULL)
+			mpssas_log_command(cm, 
+			    "completed timedout cm %p ccb %p during recovery "
+			    "ioc %x scsi %x state %x xfer %u\n",
+			    cm, cm->cm_ccb,
+			    rep->IOCStatus, rep->SCSIStatus, rep->SCSIState,
+			    rep->TransferCount);
+		else
+			mpssas_log_command(cm, 
+			    "completed timedout cm %p ccb %p during recovery\n",
+			    cm, cm->cm_ccb);
+	} else if (cm->cm_targ->tm != NULL) {
+		if (cm->cm_reply != NULL)
+			mpssas_log_command(cm, 
+			    "completed cm %p ccb %p during recovery "
+			    "ioc %x scsi %x state %x xfer %u\n",
+			    cm, cm->cm_ccb,
+			    rep->IOCStatus, rep->SCSIStatus, rep->SCSIState,
+			    rep->TransferCount);
+		else
+			mpssas_log_command(cm, 
+			    "completed cm %p ccb %p during recovery\n",
+			    cm, cm->cm_ccb);
+	} else if ((sc->mps_flags & MPS_FLAGS_DIAGRESET) != 0) {
+		mpssas_log_command(cm, 
+		    "reset completed cm %p ccb %p\n",
+		    cm, cm->cm_ccb);
+	}
+
 	if ((cm->cm_flags & MPS_CM_FLAGS_ERROR_MASK) != 0) {
 		/*
 		 * We ran into an error after we tried to map the command,
@@ -1550,16 +1789,31 @@
 	/* Take the fast path to completion */
 	if (cm->cm_reply == NULL) {
 		if ((ccb->ccb_h.status & CAM_STATUS_MASK) == CAM_REQ_INPROG) {
-			ccb->ccb_h.status = CAM_REQ_CMP;
-			ccb->csio.scsi_status = SCSI_STATUS_OK;
-
+			if ((sc->mps_flags & MPS_FLAGS_DIAGRESET) != 0)
+				ccb->ccb_h.status = CAM_SCSI_BUS_RESET;
+			else {
+				ccb->ccb_h.status = CAM_REQ_CMP;
+				ccb->csio.scsi_status = SCSI_STATUS_OK;
+			}
 			if (sassc->flags & MPSSAS_QUEUE_FROZEN) {
 				ccb->ccb_h.status |= CAM_RELEASE_SIMQ;
 				sassc->flags &= ~MPSSAS_QUEUE_FROZEN;
 				mps_dprint(sc, MPS_INFO,
 					   "Unfreezing SIM queue\n");
 			}
-		} else {
+		} 
+
+		/*
+		 * There are two scenarios where the status won't be
+		 * CAM_REQ_CMP.  The first is if MPS_CM_FLAGS_ERROR_MASK is
+		 * set, the second is in the MPS_FLAGS_DIAGRESET above.
+		 */
+		if ((ccb->ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP) {
+			/*
+			 * Freeze the dev queue so that commands are
+			 * executed in the correct order with after error
+			 * recovery.
+			 */
 			ccb->ccb_h.status |= CAM_DEV_QFRZN;
 			xpt_freeze_devq(ccb->ccb_h.path, /*count*/ 1);
 		}
@@ -1568,37 +1822,149 @@
 		return;
 	}
 
-	mps_dprint(sc, MPS_INFO, "(%d:%d:%d) IOCStatus= 0x%x, "
-	    "ScsiStatus= 0x%x, SCSIState= 0x%x TransferCount= 0x%x\n",
-	    xpt_path_path_id(ccb->ccb_h.path),
-	    xpt_path_target_id(ccb->ccb_h.path),
-	    xpt_path_lun_id(ccb->ccb_h.path), rep->IOCStatus,
-	    rep->SCSIStatus, rep->SCSIState, rep->TransferCount);
+	if (sc->mps_debug & MPS_TRACE)
+		mpssas_log_command(cm, 
+		    "ioc %x scsi %x state %x xfer %u\n",
+		    rep->IOCStatus, rep->SCSIStatus, 
+		    rep->SCSIState, rep->TransferCount);
+
+	/*
+	 * If this is a Direct Drive I/O, reissue the I/O to the original IR
+	 * Volume if an error occurred (normal I/O retry).  Use the original
+	 * CCB, but set a flag that this will be a retry so that it's sent to
+	 * the original volume.  Free the command but reuse the CCB.
+	 */
+	if (cm->cm_flags & MPS_CM_FLAGS_DD_IO) {
+		mps_free_command(sc, cm);
+		ccb->ccb_h.status = MPS_WD_RETRY;
+		mpssas_action_scsiio(sassc, ccb);
+		return;
+	}
 
 	switch (rep->IOCStatus & MPI2_IOCSTATUS_MASK) {
-	case MPI2_IOCSTATUS_BUSY:
-	case MPI2_IOCSTATUS_INSUFFICIENT_RESOURCES:
-		/*
-		 * The controller is overloaded, try waiting a bit for it
-		 * to free up.
-		 */
-		ccb->ccb_h.status = CAM_BUSY;
-		break;
 	case MPI2_IOCSTATUS_SCSI_DATA_UNDERRUN:
-		ccb->csio.resid = cm->cm_length - rep->TransferCount;
+		csio->resid = cm->cm_length - rep->TransferCount;
 		/* FALLTHROUGH */
 	case MPI2_IOCSTATUS_SUCCESS:
 	case MPI2_IOCSTATUS_SCSI_RECOVERED_ERROR:
-		ccb->ccb_h.status = CAM_REQ_CMP;
-		break;
-	case MPI2_IOCSTATUS_SCSI_DATA_OVERRUN:
-		/* resid is ignored for this condition */
-		ccb->csio.resid = 0;
-		ccb->ccb_h.status = CAM_DATA_RUN_ERR;
+
+		if ((rep->IOCStatus & MPI2_IOCSTATUS_MASK) ==
+		    MPI2_IOCSTATUS_SCSI_RECOVERED_ERROR)
+			mpssas_log_command(cm, "recovered error\n");
+
+		/* Completion failed at the transport level. */
+		if (rep->SCSIState & (MPI2_SCSI_STATE_NO_SCSI_STATUS |
+		    MPI2_SCSI_STATE_TERMINATED)) {
+			ccb->ccb_h.status = CAM_REQ_CMP_ERR;
+			break;
+		}
+
+		/* In a modern packetized environment, an autosense failure
+		 * implies that there's not much else that can be done to
+		 * recover the command.
+		 */
+		if (rep->SCSIState & MPI2_SCSI_STATE_AUTOSENSE_FAILED) {
+			ccb->ccb_h.status = CAM_AUTOSENSE_FAIL;
+			break;
+		}
+
+		/*
+		 * CAM doesn't care about SAS Response Info data, but if this is
+		 * the state check if TLR should be done.  If not, clear the
+		 * TLR_bits for the target.
+		 */
+		if ((rep->SCSIState & MPI2_SCSI_STATE_RESPONSE_INFO_VALID) &&
+		    ((rep->ResponseInfo & MPI2_SCSI_RI_MASK_REASONCODE) ==
+		    MPS_SCSI_RI_INVALID_FRAME)) {
+			sc->mapping_table[csio->ccb_h.target_id].TLR_bits =
+			    (u8)MPI2_SCSIIO_CONTROL_NO_TLR;
+		}
+
+		/*
+		 * Intentionally override the normal SCSI status reporting
+		 * for these two cases.  These are likely to happen in a
+		 * multi-initiator environment, and we want to make sure that
+		 * CAM retries these commands rather than fail them.
+		 */
+		if ((rep->SCSIStatus == MPI2_SCSI_STATUS_COMMAND_TERMINATED) ||
+		    (rep->SCSIStatus == MPI2_SCSI_STATUS_TASK_ABORTED)) {
+			ccb->ccb_h.status = CAM_REQ_ABORTED;
+			break;
+		}
+
+		/* Handle normal status and sense */
+		csio->scsi_status = rep->SCSIStatus;
+		if (rep->SCSIStatus == MPI2_SCSI_STATUS_GOOD)
+			ccb->ccb_h.status = CAM_REQ_CMP;
+		else
+			ccb->ccb_h.status = CAM_SCSI_STATUS_ERROR;
+
+		if (rep->SCSIState & MPI2_SCSI_STATE_AUTOSENSE_VALID) {
+			int sense_len, returned_sense_len;
+
+			returned_sense_len = min(rep->SenseCount,
+			    sizeof(struct scsi_sense_data));
+			if (returned_sense_len < ccb->csio.sense_len)
+				ccb->csio.sense_resid = ccb->csio.sense_len -
+					returned_sense_len;
+			else
+				ccb->csio.sense_resid = 0;
+
+			sense_len = min(returned_sense_len,
+			    ccb->csio.sense_len - ccb->csio.sense_resid);
+			bzero(&ccb->csio.sense_data,
+			      sizeof(&ccb->csio.sense_data));
+			bcopy(cm->cm_sense, &ccb->csio.sense_data, sense_len);
+			ccb->ccb_h.status |= CAM_AUTOSNS_VALID;
+		}
+
+		/*
+		 * Check if this is an INQUIRY command.  If it's a VPD inquiry,
+		 * and it's page code 0 (Supported Page List), and there is
+		 * inquiry data, and this is for a sequential access device, and
+		 * the device is an SSP target, and TLR is supported by the
+		 * controller, turn the TLR_bits value ON if page 0x90 is
+		 * supported.
+		 */
+		if ((csio->cdb_io.cdb_bytes[0] == INQUIRY) &&
+		    (csio->cdb_io.cdb_bytes[1] & SI_EVPD) &&
+		    (csio->cdb_io.cdb_bytes[2] == SVPD_SUPPORTED_PAGE_LIST) &&
+		    (csio->data_ptr != NULL) && (((uint8_t *)cm->cm_data)[0] ==
+		    T_SEQUENTIAL) && (sc->control_TLR) &&
+		    (sc->mapping_table[csio->ccb_h.target_id].device_info &
+		    MPI2_SAS_DEVICE_INFO_SSP_TARGET)) {
+			vpd_list = (struct scsi_vpd_supported_page_list *)
+			    csio->data_ptr;
+			TLR_bits = &sc->mapping_table[csio->ccb_h.target_id].
+			    TLR_bits;
+			*TLR_bits = (u8)MPI2_SCSIIO_CONTROL_NO_TLR;
+			TLR_on = (u8)MPI2_SCSIIO_CONTROL_TLR_ON;
+			alloc_len = ((u16)csio->cdb_io.cdb_bytes[3] << 8) +
+			    csio->cdb_io.cdb_bytes[4];
+			for (i = 0; i < MIN(vpd_list->length, alloc_len); i++) {
+				if (vpd_list->list[i] == 0x90) {
+					*TLR_bits = TLR_on;
+					break;
+				}
+			}
+		}
 		break;
 	case MPI2_IOCSTATUS_SCSI_INVALID_DEVHANDLE:
 	case MPI2_IOCSTATUS_SCSI_DEVICE_NOT_THERE:
-		ccb->ccb_h.status = CAM_DEV_NOT_THERE;
+		/*
+		 * If devinfo is 0 this will be a volume.  In that case don't
+		 * tell CAM that the volume is not there.  We want volumes to
+		 * be enumerated until they are deleted/removed, not just
+		 * failed.
+		 */
+		if (cm->cm_targ->devinfo == 0)
+			ccb->ccb_h.status = CAM_REQ_CMP;
+		else
+			ccb->ccb_h.status = CAM_DEV_NOT_THERE;
+		break;
+	case MPI2_IOCSTATUS_INVALID_SGL:
+		mps_print_scsiio_cmd(sc, cm);
+		ccb->ccb_h.status = CAM_UNREC_HBA_ERROR;
 		break;
 	case MPI2_IOCSTATUS_SCSI_TASK_TERMINATED:
 		/*
@@ -1615,22 +1981,23 @@
 		else
 			ccb->ccb_h.status = CAM_REQ_ABORTED;
 		break;
+	case MPI2_IOCSTATUS_SCSI_DATA_OVERRUN:
+		/* resid is ignored for this condition */
+		csio->resid = 0;
+		ccb->ccb_h.status = CAM_DATA_RUN_ERR;
+		break;
 	case MPI2_IOCSTATUS_SCSI_IOC_TERMINATED:
 	case MPI2_IOCSTATUS_SCSI_EXT_TERMINATED:
-#if 0
-		ccb->ccb_h.status = CAM_REQ_ABORTED;
-#endif
-		mps_printf(sc, "(%d:%d:%d) terminated ioc %x scsi %x state %x "
-			   "xfer %u\n", xpt_path_path_id(ccb->ccb_h.path),
-			   xpt_path_target_id(ccb->ccb_h.path),
-			   xpt_path_lun_id(ccb->ccb_h.path),
-			   rep->IOCStatus, rep->SCSIStatus, rep->SCSIState,
-			   rep->TransferCount);
+		/*
+		 * Since these are generally external (i.e. hopefully
+		 * transient transport-related) errors, retry these without
+		 * decrementing the retry count.
+		 */
 		ccb->ccb_h.status = CAM_REQUEUE_REQ;
-		break;
-	case MPI2_IOCSTATUS_INVALID_SGL:
-		mps_print_scsiio_cmd(sc, cm);
-		ccb->ccb_h.status = CAM_UNREC_HBA_ERROR;
+		mpssas_log_command(cm, 
+		    "terminated ioc %x scsi %x state %x xfer %u\n",
+		    rep->IOCStatus, rep->SCSIStatus, rep->SCSIState,
+		    rep->TransferCount);
 		break;
 	case MPI2_IOCSTATUS_INVALID_FUNCTION:
 	case MPI2_IOCSTATUS_INTERNAL_ERROR:
@@ -1643,63 +2010,291 @@
 	case MPI2_IOCSTATUS_SCSI_RESIDUAL_MISMATCH:
 	case MPI2_IOCSTATUS_SCSI_TASK_MGMT_FAILED:
 	default:
+		mpssas_log_command(cm, 
+		    "completed ioc %x scsi %x state %x xfer %u\n",
+		    rep->IOCStatus, rep->SCSIStatus, rep->SCSIState,
+		    rep->TransferCount);
+		csio->resid = cm->cm_length;
 		ccb->ccb_h.status = CAM_REQ_CMP_ERR;
+		break;
 	}
 
-
-	if ((rep->SCSIState & MPI2_SCSI_STATE_NO_SCSI_STATUS) == 0) {
-		ccb->csio.scsi_status = rep->SCSIStatus;
-
-		switch (rep->SCSIStatus) {
-		case MPI2_SCSI_STATUS_TASK_SET_FULL:
-		case MPI2_SCSI_STATUS_CHECK_CONDITION:
-			ccb->ccb_h.status = CAM_SCSI_STATUS_ERROR;
-			break;
-		case MPI2_SCSI_STATUS_COMMAND_TERMINATED:
-		case MPI2_SCSI_STATUS_TASK_ABORTED:
-			ccb->ccb_h.status = CAM_REQ_ABORTED;
-			break;
-		case MPI2_SCSI_STATUS_GOOD:
-		default:
-			break;
-		}
-	}
-
-	if (rep->SCSIState & MPI2_SCSI_STATE_AUTOSENSE_VALID) {
-		int sense_len;
-
-		if (rep->SenseCount < ccb->csio.sense_len)
-			ccb->csio.sense_resid = ccb->csio.sense_len -
-				rep->SenseCount;
-		else
-			ccb->csio.sense_resid = 0;
-
-		sense_len = min(rep->SenseCount, ccb->csio.sense_len -
-		    ccb->csio.sense_resid);
-		bzero(&ccb->csio.sense_data, sizeof(&ccb->csio.sense_data));
-		bcopy(cm->cm_sense, &ccb->csio.sense_data, sense_len);
-		ccb->ccb_h.status |= CAM_AUTOSNS_VALID;
-	}
-
-	if (rep->SCSIState & MPI2_SCSI_STATE_AUTOSENSE_FAILED)
-		ccb->ccb_h.status = CAM_AUTOSENSE_FAIL;
-
-	if (rep->SCSIState & MPI2_SCSI_STATE_RESPONSE_INFO_VALID)
-		ccb->ccb_h.status = CAM_REQ_CMP_ERR;
-
 	if (sassc->flags & MPSSAS_QUEUE_FROZEN) {
 		ccb->ccb_h.status |= CAM_RELEASE_SIMQ;
 		sassc->flags &= ~MPSSAS_QUEUE_FROZEN;
-		mps_printf(sc, "Command completed, unfreezing SIM queue\n");
+		mps_dprint(sc, MPS_INFO, "Command completed, "
+			   "unfreezing SIM queue\n");
 	}
+
 	if ((ccb->ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP) {
 		ccb->ccb_h.status |= CAM_DEV_QFRZN;
 		xpt_freeze_devq(ccb->ccb_h.path, /*count*/ 1);
 	}
+
 	mps_free_command(sc, cm);
 	xpt_done(ccb);
 }
 
+static void
+mpssas_direct_drive_io(struct mpssas_softc *sassc, struct mps_command *cm,
+    union ccb *ccb) {
+	pMpi2SCSIIORequest_t	pIO_req;
+	struct mps_softc	*sc = sassc->sc;
+	uint64_t		virtLBA;
+	uint32_t		physLBA, stripe_offset, stripe_unit;
+	uint32_t		io_size, column;
+	uint8_t			*ptrLBA, lba_idx, physLBA_byte, *CDB;
+
+	/*
+	 * If this is a valid SCSI command (Read6, Read10, Read16, Write6,
+	 * Write10, or Write16), build a direct I/O message.  Otherwise, the I/O
+	 * will be sent to the IR volume itself.  Since Read6 and Write6 are a
+	 * bit different than the 10/16 CDBs, handle them separately.
+	 */
+	pIO_req = (pMpi2SCSIIORequest_t)cm->cm_req;
+	CDB = pIO_req->CDB.CDB32;
+
+	/*
+	 * Handle 6 byte CDBs.
+	 */
+	if ((pIO_req->DevHandle == sc->DD_dev_handle) && ((CDB[0] == READ_6) ||
+	    (CDB[0] == WRITE_6))) {
+		/*
+		 * Get the transfer size in blocks.
+		 */
+		io_size = (cm->cm_length >> sc->DD_block_exponent);
+
+		/*
+		 * Get virtual LBA given in the CDB.
+		 */
+		virtLBA = ((uint64_t)(CDB[1] & 0x1F) << 16) |
+		    ((uint64_t)CDB[2] << 8) | (uint64_t)CDB[3];
+
+		/*
+		 * Check that LBA range for I/O does not exceed volume's
+		 * MaxLBA.
+		 */
+		if ((virtLBA + (uint64_t)io_size - 1) <=
+		    sc->DD_max_lba) {
+			/*
+			 * Check if the I/O crosses a stripe boundary.  If not,
+			 * translate the virtual LBA to a physical LBA and set
+			 * the DevHandle for the PhysDisk to be used.  If it
+			 * does cross a boundry, do normal I/O.  To get the
+			 * right DevHandle to use, get the map number for the
+			 * column, then use that map number to look up the
+			 * DevHandle of the PhysDisk.
+			 */
+			stripe_offset = (uint32_t)virtLBA &
+			    (sc->DD_stripe_size - 1);
+			if ((stripe_offset + io_size) <= sc->DD_stripe_size) {
+				physLBA = (uint32_t)virtLBA >>
+				    sc->DD_stripe_exponent;
+				stripe_unit = physLBA / sc->DD_num_phys_disks;
+				column = physLBA % sc->DD_num_phys_disks;
+				pIO_req->DevHandle =
+				    sc->DD_column_map[column].dev_handle;
+				cm->cm_desc.SCSIIO.DevHandle =
+				    pIO_req->DevHandle;
+
+				physLBA = (stripe_unit <<
+				    sc->DD_stripe_exponent) + stripe_offset;
+				ptrLBA = &pIO_req->CDB.CDB32[1];
+				physLBA_byte = (uint8_t)(physLBA >> 16);
+				*ptrLBA = physLBA_byte;
+				ptrLBA = &pIO_req->CDB.CDB32[2];
+				physLBA_byte = (uint8_t)(physLBA >> 8);
+				*ptrLBA = physLBA_byte;
+				ptrLBA = &pIO_req->CDB.CDB32[3];
+				physLBA_byte = (uint8_t)physLBA;
+				*ptrLBA = physLBA_byte;
+
+				/*
+				 * Set flag that Direct Drive I/O is
+				 * being done.
+				 */
+				cm->cm_flags |= MPS_CM_FLAGS_DD_IO;
+			}
+		}
+		return;
+	}
+
+	/*
+	 * Handle 10 or 16 byte CDBs.
+	 */
+	if ((pIO_req->DevHandle == sc->DD_dev_handle) && ((CDB[0] == READ_10) ||
+	    (CDB[0] == WRITE_10) || (CDB[0] == READ_16) ||
+	    (CDB[0] == WRITE_16))) {
+		/*
+		 * For 16-byte CDB's, verify that the upper 4 bytes of the CDB
+		 * are 0.  If not, this is accessing beyond 2TB so handle it in
+		 * the else section.  10-byte CDB's are OK.
+		 */
+		if ((CDB[0] < READ_16) ||
+		    !(CDB[2] | CDB[3] | CDB[4] | CDB[5])) {
+			/*
+			 * Get the transfer size in blocks.
+			 */
+			io_size = (cm->cm_length >> sc->DD_block_exponent);
+
+			/*
+			 * Get virtual LBA.  Point to correct lower 4 bytes of
+			 * LBA in the CDB depending on command.
+			 */
+			lba_idx = (CDB[0] < READ_16) ? 2 : 6;
+			virtLBA = ((uint64_t)CDB[lba_idx] << 24) |
+			    ((uint64_t)CDB[lba_idx + 1] << 16) |
+			    ((uint64_t)CDB[lba_idx + 2] << 8) |
+			    (uint64_t)CDB[lba_idx + 3];
+
+			/*
+			 * Check that LBA range for I/O does not exceed volume's
+			 * MaxLBA.
+			 */
+			if ((virtLBA + (uint64_t)io_size - 1) <=
+			    sc->DD_max_lba) {
+				/*
+				 * Check if the I/O crosses a stripe boundary.
+				 * If not, translate the virtual LBA to a
+				 * physical LBA and set the DevHandle for the
+				 * PhysDisk to be used.  If it does cross a
+				 * boundry, do normal I/O.  To get the right
+				 * DevHandle to use, get the map number for the
+				 * column, then use that map number to look up
+				 * the DevHandle of the PhysDisk.
+				 */
+				stripe_offset = (uint32_t)virtLBA &
+				    (sc->DD_stripe_size - 1);
+				if ((stripe_offset + io_size) <=
+				    sc->DD_stripe_size) {
+					physLBA = (uint32_t)virtLBA >>
+					    sc->DD_stripe_exponent;
+					stripe_unit = physLBA /
+					    sc->DD_num_phys_disks;
+					column = physLBA %
+					    sc->DD_num_phys_disks;
+					pIO_req->DevHandle =
+					    sc->DD_column_map[column].
+					    dev_handle;
+					cm->cm_desc.SCSIIO.DevHandle =
+					    pIO_req->DevHandle;
+
+					physLBA = (stripe_unit <<
+					    sc->DD_stripe_exponent) +
+					    stripe_offset;
+					ptrLBA =
+					    &pIO_req->CDB.CDB32[lba_idx];
+					physLBA_byte = (uint8_t)(physLBA >> 24);
+					*ptrLBA = physLBA_byte;
+					ptrLBA =
+					    &pIO_req->CDB.CDB32[lba_idx + 1];
+					physLBA_byte = (uint8_t)(physLBA >> 16);
+					*ptrLBA = physLBA_byte;
+					ptrLBA =
+					    &pIO_req->CDB.CDB32[lba_idx + 2];
+					physLBA_byte = (uint8_t)(physLBA >> 8);
+					*ptrLBA = physLBA_byte;
+					ptrLBA =
+					    &pIO_req->CDB.CDB32[lba_idx + 3];
+					physLBA_byte = (uint8_t)physLBA;
+					*ptrLBA = physLBA_byte;
+
+					/*
+					 * Set flag that Direct Drive I/O is
+					 * being done.
+					 */
+					cm->cm_flags |= MPS_CM_FLAGS_DD_IO;
+				}
+			}
+		} else {
+			/*
+			 * 16-byte CDB and the upper 4 bytes of the CDB are not
+			 * 0.  Get the transfer size in blocks.
+			 */
+			io_size = (cm->cm_length >> sc->DD_block_exponent);
+
+			/*
+			 * Get virtual LBA.
+			 */
+			virtLBA = ((uint64_t)CDB[2] << 54) |
+			    ((uint64_t)CDB[3] << 48) |
+			    ((uint64_t)CDB[4] << 40) |
+			    ((uint64_t)CDB[5] << 32) |
+			    ((uint64_t)CDB[6] << 24) |
+			    ((uint64_t)CDB[7] << 16) |
+			    ((uint64_t)CDB[8] << 8) |
+			    (uint64_t)CDB[9]; 
+
+			/*
+			 * Check that LBA range for I/O does not exceed volume's
+			 * MaxLBA.
+			 */
+			if ((virtLBA + (uint64_t)io_size - 1) <=
+			    sc->DD_max_lba) {
+				/*
+				 * Check if the I/O crosses a stripe boundary.
+				 * If not, translate the virtual LBA to a
+				 * physical LBA and set the DevHandle for the
+				 * PhysDisk to be used.  If it does cross a
+				 * boundry, do normal I/O.  To get the right
+				 * DevHandle to use, get the map number for the
+				 * column, then use that map number to look up
+				 * the DevHandle of the PhysDisk.
+				 */
+				stripe_offset = (uint32_t)virtLBA &
+				    (sc->DD_stripe_size - 1);
+				if ((stripe_offset + io_size) <=
+				    sc->DD_stripe_size) {
+					physLBA = (uint32_t)(virtLBA >>
+					    sc->DD_stripe_exponent);
+					stripe_unit = physLBA /
+					    sc->DD_num_phys_disks;
+					column = physLBA %
+					    sc->DD_num_phys_disks;
+					pIO_req->DevHandle =
+					    sc->DD_column_map[column].
+					    dev_handle;
+					cm->cm_desc.SCSIIO.DevHandle =
+					    pIO_req->DevHandle;
+
+					physLBA = (stripe_unit <<
+					    sc->DD_stripe_exponent) +
+					    stripe_offset;
+
+					/*
+					 * Set upper 4 bytes of LBA to 0.  We
+					 * assume that the phys disks are less
+					 * than 2 TB's in size.  Then, set the
+					 * lower 4 bytes.
+					 */
+					pIO_req->CDB.CDB32[2] = 0;
+					pIO_req->CDB.CDB32[3] = 0;
+					pIO_req->CDB.CDB32[4] = 0;
+					pIO_req->CDB.CDB32[5] = 0;
+					ptrLBA = &pIO_req->CDB.CDB32[6];
+					physLBA_byte = (uint8_t)(physLBA >> 24);
+					*ptrLBA = physLBA_byte;
+					ptrLBA = &pIO_req->CDB.CDB32[7];
+					physLBA_byte = (uint8_t)(physLBA >> 16);
+					*ptrLBA = physLBA_byte;
+					ptrLBA = &pIO_req->CDB.CDB32[8];
+					physLBA_byte = (uint8_t)(physLBA >> 8);
+					*ptrLBA = physLBA_byte;
+					ptrLBA = &pIO_req->CDB.CDB32[9];
+					physLBA_byte = (uint8_t)physLBA;
+					*ptrLBA = physLBA_byte;
+
+					/*
+					 * Set flag that Direct Drive I/O is
+					 * being done.
+					 */
+					cm->cm_flags |= MPS_CM_FLAGS_DD_IO;
+				}
+			}
+		}
+	}
+}
+
 #if __FreeBSD_version >= 900026
 static void
 mpssas_smpio_complete(struct mps_softc *sc, struct mps_command *cm)
@@ -1722,7 +2317,7 @@
 			   __func__, cm->cm_flags);
 		ccb->ccb_h.status = CAM_REQ_CMP_ERR;
 		goto bailout;
-	}
+        }
 
 	rpl = (MPI2_SMP_PASSTHROUGH_REPLY *)cm->cm_reply;
 	if (rpl == NULL) {
@@ -1989,7 +2584,9 @@
 	 * the parent device of this device, which is probably the expander.
 	 */
 	if (sasaddr == 0) {
+#ifdef OLD_MPS_PROBE
 		struct mpssas_target *parent_target;
+#endif
 
 		if (targ->parent_handle == 0x0) {
 			mps_printf(sc, "%s: handle %d does not have a valid "
@@ -1997,8 +2594,9 @@
 			ccb->ccb_h.status = CAM_REQ_INVALID;
 			goto bailout;
 		}
-		parent_target = mpssas_find_target(sassc, 0,
-						   targ->parent_handle);
+#ifdef OLD_MPS_PROBE
+		parent_target = mpssas_find_target_by_handle(sassc, 0,
+			targ->parent_handle);
 
 		if (parent_target == NULL) {
 			mps_printf(sc, "%s: handle %d does not have a valid "
@@ -2018,6 +2616,27 @@
 		}
 
 		sasaddr = parent_target->sasaddr;
+#else /* OLD_MPS_PROBE */
+		if ((targ->parent_devinfo &
+		     MPI2_SAS_DEVICE_INFO_SMP_TARGET) == 0) {
+			mps_printf(sc, "%s: handle %d parent %d does not "
+				   "have an SMP target!\n", __func__,
+				   targ->handle, targ->parent_handle);
+			ccb->ccb_h.status = CAM_REQ_INVALID;
+			goto bailout;
+
+		}
+		if (targ->parent_sasaddr == 0x0) {
+			mps_printf(sc, "%s: handle %d parent handle %d does "
+				   "not have a valid SAS address!\n",
+				   __func__, targ->handle, targ->parent_handle);
+			ccb->ccb_h.status = CAM_REQ_INVALID;
+			goto bailout;
+		}
+
+		sasaddr = targ->parent_sasaddr;
+#endif /* OLD_MPS_PROBE */
+
 	}
 
 	if (sasaddr == 0) {
@@ -2034,99 +2653,87 @@
 	xpt_done(ccb);
 
 }
-
-#endif /* __FreeBSD_version >= 900026 */
+#endif //__FreeBSD_version >= 900026
 
 static void
 mpssas_action_resetdev(struct mpssas_softc *sassc, union ccb *ccb)
 {
+	MPI2_SCSI_TASK_MANAGE_REQUEST *req;
 	struct mps_softc *sc;
-	struct mps_command *cm;
+	struct mps_command *tm;
 	struct mpssas_target *targ;
 
+	mps_dprint(sassc->sc, MPS_TRACE, __func__);
+	mtx_assert(&sassc->sc->mps_mtx, MA_OWNED);
+
 	sc = sassc->sc;
-	targ = &sassc->targets[ccb->ccb_h.target_id];
-
-	if (targ->flags & MPSSAS_TARGET_INRECOVERY) {
+	tm = mps_alloc_command(sc);
+	if (tm == NULL) {
+		mps_printf(sc, "comand alloc failure in mpssas_action_resetdev\n");
 		ccb->ccb_h.status = CAM_RESRC_UNAVAIL;
 		xpt_done(ccb);
 		return;
 	}
 
-	cm = mps_alloc_command(sc);
-	if (cm == NULL) {
-		mps_printf(sc, "%s: cannot alloc command\n", __func__);
-		ccb->ccb_h.status = CAM_RESRC_UNAVAIL;
-		xpt_done(ccb);
-		return;
-	}
-
-	cm->cm_targ = targ;
-	cm->cm_complete = mpssas_resetdev_complete;
-	cm->cm_complete_data = ccb;
-
-	mpssas_resetdev(sassc, cm);
-}
-
-static void
-mpssas_resetdev(struct mpssas_softc *sassc, struct mps_command *cm)
-{
-	MPI2_SCSI_TASK_MANAGE_REQUEST *req;
-	struct mps_softc *sc;
-
-	mps_dprint(sassc->sc, MPS_TRACE, "%s\n", __func__);
-
-	sc = sassc->sc;
-
-	req = (MPI2_SCSI_TASK_MANAGE_REQUEST *)cm->cm_req;
-	req->DevHandle = cm->cm_targ->handle;
+	targ = &sassc->targets[ccb->ccb_h.target_id];
+	req = (MPI2_SCSI_TASK_MANAGE_REQUEST *)tm->cm_req;
+	req->DevHandle = targ->handle;
 	req->Function = MPI2_FUNCTION_SCSI_TASK_MGMT;
 	req->TaskType = MPI2_SCSITASKMGMT_TASKTYPE_TARGET_RESET;
 
 	/* SAS Hard Link Reset / SATA Link Reset */
 	req->MsgFlags = MPI2_SCSITASKMGMT_MSGFLAGS_LINK_RESET;
 
-	cm->cm_data = NULL;
-	cm->cm_desc.Default.RequestFlags = MPI2_REQ_DESCRIPT_FLAGS_DEFAULT_TYPE;
-
-	mpssas_issue_tm_request(sc, cm);
+	tm->cm_data = NULL;
+	tm->cm_desc.HighPriority.RequestFlags = MPI2_REQ_DESCRIPT_FLAGS_HIGH_PRIORITY;
+	tm->cm_complete = mpssas_resetdev_complete;
+	tm->cm_complete_data = ccb;
+	mps_map_command(sc, tm);
 }
 
 static void
-mpssas_resetdev_complete(struct mps_softc *sc, struct mps_command *cm)
+mpssas_resetdev_complete(struct mps_softc *sc, struct mps_command *tm)
 {
 	MPI2_SCSI_TASK_MANAGE_REPLY *resp;
 	union ccb *ccb;
 
-	mps_dprint(sc, MPS_TRACE, "%s\n", __func__);
-
-	resp = (MPI2_SCSI_TASK_MANAGE_REPLY *)cm->cm_reply;
-	ccb = cm->cm_complete_data;
-
-	if ((cm->cm_flags & MPS_CM_FLAGS_ERROR_MASK) != 0) {
+	mps_dprint(sc, MPS_TRACE, __func__);
+	mtx_assert(&sc->mps_mtx, MA_OWNED);
+
+	resp = (MPI2_SCSI_TASK_MANAGE_REPLY *)tm->cm_reply;
+	ccb = tm->cm_complete_data;
+
+	/*
+	 * Currently there should be no way we can hit this case.  It only
+	 * happens when we have a failure to allocate chain frames, and
+	 * task management commands don't have S/G lists.
+	 */
+	if ((tm->cm_flags & MPS_CM_FLAGS_ERROR_MASK) != 0) {
 		MPI2_SCSI_TASK_MANAGE_REQUEST *req;
 
-		req = (MPI2_SCSI_TASK_MANAGE_REQUEST *)cm->cm_req;
+		req = (MPI2_SCSI_TASK_MANAGE_REQUEST *)tm->cm_req;
 
 		mps_printf(sc, "%s: cm_flags = %#x for reset of handle %#04x! "
-			   "This should not happen!\n", __func__, cm->cm_flags,
+			   "This should not happen!\n", __func__, tm->cm_flags,
 			   req->DevHandle);
-
 		ccb->ccb_h.status = CAM_REQ_CMP_ERR;
 		goto bailout;
 	}
 
-	printf("resetdev complete IOCStatus= 0x%x ResponseCode= 0x%x\n",
+	printf("%s: IOCStatus = 0x%x ResponseCode = 0x%x\n", __func__,
 	    resp->IOCStatus, resp->ResponseCode);
 
-	if (resp->ResponseCode == MPI2_SCSITASKMGMT_RSP_TM_COMPLETE)
+	if (resp->ResponseCode == MPI2_SCSITASKMGMT_RSP_TM_COMPLETE) {
 		ccb->ccb_h.status = CAM_REQ_CMP;
+		mpssas_announce_reset(sc, AC_SENT_BDR, tm->cm_targ->tid,
+		    CAM_LUN_WILDCARD);
+	}
 	else
 		ccb->ccb_h.status = CAM_REQ_CMP_ERR;
 
 bailout:
-	mpssas_complete_tm_request(sc, cm, /*free_cm*/ 1);
-
+
+	mpssas_free_tm(sc, tm);
 	xpt_done(ccb);
 }
 
@@ -2136,16 +2743,502 @@
 	struct mpssas_softc *sassc;
 
 	sassc = cam_sim_softc(sim);
+
+	if (sassc->sc->mps_debug & MPS_TRACE) {
+		/* frequent debug messages during a panic just slow
+		 * everything down too much.
+		 */
+		mps_printf(sassc->sc, "%s clearing MPS_TRACE\n", __func__);
+		sassc->sc->mps_debug &= ~MPS_TRACE;
+	}
+
 	mps_intr_locked(sassc->sc);
 }
 
 static void
-mpssas_freeze_device(struct mpssas_softc *sassc, struct mpssas_target *targ)
+mpssas_rescan_done(struct cam_periph *periph, union ccb *done_ccb)
 {
+	struct mpssas_softc *sassc;
+	char path_str[64];
+
+	if (done_ccb == NULL)
+		return;
+
+	sassc = (struct mpssas_softc *)done_ccb->ccb_h.ppriv_ptr1;
+
+	mtx_assert(&sassc->sc->mps_mtx, MA_OWNED);
+
+	xpt_path_string(done_ccb->ccb_h.path, path_str, sizeof(path_str));
+	mps_dprint(sassc->sc, MPS_INFO, "Completing rescan for %s\n", path_str);
+
+	xpt_free_path(done_ccb->ccb_h.path);
+	xpt_free_ccb(done_ccb);
+
+#if __FreeBSD_version < 1000006
+	/*
+	 * Before completing scan, get EEDP stuff for all of the existing
+	 * targets.
+	 */
+	mpssas_check_eedp(sassc);
+#endif
+
 }
 
+/* thread to handle bus rescans */
 static void
-mpssas_unfreeze_device(struct mpssas_softc *sassc, struct mpssas_target *targ)
+mpssas_scanner_thread(void *arg)
 {
+	struct mpssas_softc *sassc;
+	struct mps_softc *sc;
+	union ccb	*ccb;
+
+	sassc = (struct mpssas_softc *)arg;
+	sc = sassc->sc;
+
+	mps_dprint(sc, MPS_TRACE, "%s\n", __func__);
+
+	mps_lock(sc);
+	for (;;) {
+		msleep(&sassc->ccb_scanq, &sc->mps_mtx, PRIBIO,
+		       "mps_scanq", 0);
+		if (sassc->flags & MPSSAS_SHUTDOWN) {
+			mps_dprint(sc, MPS_TRACE, "Scanner shutting down\n");
+			break;
+		}
+		ccb = (union ccb *)TAILQ_FIRST(&sassc->ccb_scanq);
+		if (ccb == NULL)
+			continue;
+		TAILQ_REMOVE(&sassc->ccb_scanq, &ccb->ccb_h, sim_links.tqe);
+		xpt_action(ccb);
+	}
+
+	sassc->flags &= ~MPSSAS_SCANTHREAD;
+	wakeup(&sassc->flags);
+	mps_unlock(sc);
+	mps_dprint(sc, MPS_TRACE, "Scanner exiting\n");
+	mps_kproc_exit(0);
 }
 
+static void
+mpssas_rescan(struct mpssas_softc *sassc, union ccb *ccb)
+{
+	char path_str[64];
+
+	mps_dprint(sassc->sc, MPS_TRACE, "%s\n", __func__);
+
+	mtx_assert(&sassc->sc->mps_mtx, MA_OWNED);
+
+	if (ccb == NULL)
+		return;
+
+	xpt_path_string(ccb->ccb_h.path, path_str, sizeof(path_str));
+	mps_dprint(sassc->sc, MPS_INFO, "Queueing rescan for %s\n", path_str);
+
+	/* Prepare request */
+	ccb->ccb_h.ppriv_ptr1 = sassc;
+	ccb->ccb_h.cbfcnp = mpssas_rescan_done;
+	xpt_setup_ccb(&ccb->ccb_h, ccb->ccb_h.path, MPS_PRIORITY_XPT);
+	TAILQ_INSERT_TAIL(&sassc->ccb_scanq, &ccb->ccb_h, sim_links.tqe);
+	wakeup(&sassc->ccb_scanq);
+}
+
+#if __FreeBSD_version >= 1000006
+static void
+mpssas_async(void *callback_arg, uint32_t code, struct cam_path *path,
+	     void *arg)
+{
+	struct mps_softc *sc;
+
+	sc = (struct mps_softc *)callback_arg;
+
+	switch (code) {
+	case AC_ADVINFO_CHANGED: {
+		struct mpssas_target *target;
+		struct mpssas_softc *sassc;
+		struct scsi_read_capacity_data_long rcap_buf;
+		struct ccb_dev_advinfo cdai;
+		struct mpssas_lun *lun;
+		lun_id_t lunid;
+		int found_lun;
+		uintptr_t buftype;
+
+		buftype = (uintptr_t)arg;
+
+		found_lun = 0;
+		sassc = sc->sassc;
+
+		/*
+		 * We're only interested in read capacity data changes.
+		 */
+		if (buftype != CDAI_TYPE_RCAPLONG)
+			break;
+
+		/*
+		 * We're only interested in devices that are attached to
+		 * this controller.
+		 */
+		if (xpt_path_path_id(path) != sassc->sim->path_id)
+			break;
+
+		/*
+		 * We should have a handle for this, but check to make sure.
+		 */
+		target = &sassc->targets[xpt_path_target_id(path)];
+		if (target->handle == 0)
+			break;
+
+		lunid = xpt_path_lun_id(path);
+
+		SLIST_FOREACH(lun, &target->luns, lun_link) {
+			if (lun->lun_id == lunid) {
+				found_lun = 1;
+				break;
+			}
+		}
+
+		if (found_lun == 0) {
+			lun = malloc(sizeof(struct mpssas_lun), M_MPT2,
+				     M_NOWAIT | M_ZERO);
+			if (lun == NULL) {
+				mps_dprint(sc, MPS_FAULT, "Unable to alloc "
+					   "LUN for EEDP support.\n");
+				break;
+			}
+			lun->lun_id = lunid;
+			SLIST_INSERT_HEAD(&target->luns, lun, lun_link);
+		}
+
+		bzero(&rcap_buf, sizeof(rcap_buf));
+		xpt_setup_ccb(&cdai.ccb_h, path, CAM_PRIORITY_NORMAL);
+		cdai.ccb_h.func_code = XPT_DEV_ADVINFO;
+		cdai.ccb_h.flags = CAM_DIR_IN;
+		cdai.buftype = CDAI_TYPE_RCAPLONG;
+		cdai.flags = 0;
+		cdai.bufsiz = sizeof(rcap_buf);
+		cdai.buf = (uint8_t *)&rcap_buf;
+		xpt_action((union ccb *)&cdai);
+		if ((cdai.ccb_h.status & CAM_DEV_QFRZN) != 0)
+			cam_release_devq(cdai.ccb_h.path,
+					 0, 0, 0, FALSE);
+
+		if (((cdai.ccb_h.status & CAM_STATUS_MASK) == CAM_REQ_CMP)
+		 && (rcap_buf.prot & SRC16_PROT_EN)) {
+			lun->eedp_formatted = TRUE;
+			lun->eedp_block_size = scsi_4btoul(rcap_buf.length);
+		} else {
+			lun->eedp_formatted = FALSE;
+			lun->eedp_block_size = 0;
+		}
+		break;
+	}
+	default:
+		break;
+	}
+}
+#else /* __FreeBSD_version >= 1000006 */
+
+static void
+mpssas_check_eedp(struct mpssas_softc *sassc)
+{
+	struct mps_softc *sc = sassc->sc;
+	struct ccb_scsiio *csio;
+	struct scsi_read_capacity_16 *scsi_cmd;
+	struct scsi_read_capacity_eedp *rcap_buf;
+	union ccb *ccb;
+	path_id_t pathid = cam_sim_path(sassc->sim);
+	target_id_t targetid;
+	lun_id_t lunid;
+	struct cam_periph *found_periph;
+	struct mpssas_target *target;
+	struct mpssas_lun *lun;
+	uint8_t	found_lun;
+
+	/*
+	 * Issue a READ CAPACITY 16 command to each LUN of each target.  This
+	 * info is used to determine if the LUN is formatted for EEDP support.
+	 */
+	for (targetid = 0; targetid < sc->facts->MaxTargets; targetid++) {
+		target = &sassc->targets[targetid];
+		if (target->handle == 0x0) {
+			continue;
+		}
+
+		lunid = 0;
+		do {
+			rcap_buf =
+			    malloc(sizeof(struct scsi_read_capacity_eedp),
+			    M_MPT2, M_NOWAIT | M_ZERO);
+			if (rcap_buf == NULL) {
+				mps_dprint(sc, MPS_FAULT, "Unable to alloc read "
+				    "capacity buffer for EEDP support.\n");
+				return;
+			}
+			ccb = xpt_alloc_ccb_nowait();
+			if (ccb == NULL) {
+				mps_dprint(sc, MPS_FAULT, "Unable to alloc CCB "
+				    "for EEDP support.\n");
+				free(rcap_buf, M_MPT2);
+				return;
+			}
+
+			if (xpt_create_path(&ccb->ccb_h.path, xpt_periph,
+			    pathid, targetid, lunid) != CAM_REQ_CMP) {
+				mps_dprint(sc, MPS_FAULT, "Unable to create "
+				    "path for EEDP support\n");
+				free(rcap_buf, M_MPT2);
+				xpt_free_ccb(ccb);
+				return;
+			}
+
+			/*
+			 * If a periph is returned, the LUN exists.  Create an
+			 * entry in the target's LUN list.
+			 */
+			if ((found_periph = cam_periph_find(ccb->ccb_h.path,
+			    NULL)) != NULL) {
+				/*
+				 * If LUN is already in list, don't create a new
+				 * one.
+				 */
+				found_lun = FALSE;
+				SLIST_FOREACH(lun, &target->luns, lun_link) {
+					if (lun->lun_id == lunid) {
+						found_lun = TRUE;
+						break;
+					}
+				}
+				if (!found_lun) {
+					lun = malloc(sizeof(struct mpssas_lun),
+					    M_MPT2, M_WAITOK | M_ZERO);
+					if (lun == NULL) {
+						mps_dprint(sc, MPS_FAULT,
+						    "Unable to alloc LUN for "
+						    "EEDP support.\n");
+						free(rcap_buf, M_MPT2);
+						xpt_free_path(ccb->ccb_h.path);
+						xpt_free_ccb(ccb);
+						return;
+					}
+					lun->lun_id = lunid;
+					SLIST_INSERT_HEAD(&target->luns, lun,
+					    lun_link);
+				}
+				lunid++;
+
+				/*
+				 * Issue a READ CAPACITY 16 command for the LUN.
+				 * The mpssas_read_cap_done function will load
+				 * the read cap info into the LUN struct.
+				 */
+				csio = &ccb->csio;
+				csio->ccb_h.func_code = XPT_SCSI_IO;
+				csio->ccb_h.flags = CAM_DIR_IN;
+				csio->ccb_h.retry_count = 4;	
+				csio->ccb_h.cbfcnp = mpssas_read_cap_done;
+				csio->ccb_h.timeout = 60000;
+				csio->data_ptr = (uint8_t *)rcap_buf;
+				csio->dxfer_len = sizeof(struct
+				    scsi_read_capacity_eedp);
+				csio->sense_len = MPS_SENSE_LEN;
+				csio->cdb_len = sizeof(*scsi_cmd);
+				csio->tag_action = MSG_SIMPLE_Q_TAG;
+
+				scsi_cmd = (struct scsi_read_capacity_16 *)
+				    &csio->cdb_io.cdb_bytes;
+				bzero(scsi_cmd, sizeof(*scsi_cmd));
+				scsi_cmd->opcode = 0x9E;
+				scsi_cmd->service_action = SRC16_SERVICE_ACTION;
+				((uint8_t *)scsi_cmd)[13] = sizeof(struct
+				    scsi_read_capacity_eedp);
+
+				/*
+				 * Set the path, target and lun IDs for the READ
+				 * CAPACITY request.
+				 */
+				ccb->ccb_h.path_id =
+				    xpt_path_path_id(ccb->ccb_h.path);
+				ccb->ccb_h.target_id =
+				    xpt_path_target_id(ccb->ccb_h.path);
+				ccb->ccb_h.target_lun =
+				    xpt_path_lun_id(ccb->ccb_h.path);
+
+				ccb->ccb_h.ppriv_ptr1 = sassc;
+				xpt_action(ccb);
+			} else {
+				free(rcap_buf, M_MPT2);
+				xpt_free_path(ccb->ccb_h.path);
+				xpt_free_ccb(ccb);
+			}
+		} while (found_periph);
+	}
+}
+
+
+static void
+mpssas_read_cap_done(struct cam_periph *periph, union ccb *done_ccb)
+{
+	struct mpssas_softc *sassc;
+	struct mpssas_target *target;
+	struct mpssas_lun *lun;
+	struct scsi_read_capacity_eedp *rcap_buf;
+
+	if (done_ccb == NULL)
+		return;
+
+	rcap_buf = (struct scsi_read_capacity_eedp *)done_ccb->csio.data_ptr;
+
+	/*
+	 * Get the LUN ID for the path and look it up in the LUN list for the
+	 * target.
+	 */
+	sassc = (struct mpssas_softc *)done_ccb->ccb_h.ppriv_ptr1;
+	target = &sassc->targets[done_ccb->ccb_h.target_id];
+	SLIST_FOREACH(lun, &target->luns, lun_link) {
+		if (lun->lun_id != done_ccb->ccb_h.target_lun)
+			continue;
+
+		/*
+		 * Got the LUN in the target's LUN list.  Fill it in
+		 * with EEDP info.  If the READ CAP 16 command had some
+		 * SCSI error (common if command is not supported), mark
+		 * the lun as not supporting EEDP and set the block size
+		 * to 0.
+		 */
+		if (((done_ccb->ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP)
+		 || (done_ccb->csio.scsi_status != SCSI_STATUS_OK)) {
+			lun->eedp_formatted = FALSE;
+			lun->eedp_block_size = 0;
+			break;
+		}
+
+		if (rcap_buf->protect & 0x01) {
+			lun->eedp_formatted = TRUE;
+			lun->eedp_block_size = scsi_4btoul(rcap_buf->length);
+		}
+		break;
+	}
+
+	// Finished with this CCB and path.
+	free(rcap_buf, M_MPT2);
+	xpt_free_path(done_ccb->ccb_h.path);
+	xpt_free_ccb(done_ccb);
+}
+#endif /* __FreeBSD_version >= 1000006 */
+
+int
+mpssas_startup(struct mps_softc *sc)
+{
+	struct mpssas_softc *sassc;
+
+	/*
+	 * Send the port enable message and set the wait_for_port_enable flag.
+	 * This flag helps to keep the simq frozen until all discovery events
+	 * are processed.
+	 */
+	sassc = sc->sassc;
+	mpssas_startup_increment(sassc);
+	sc->wait_for_port_enable = 1;
+	mpssas_send_portenable(sc);
+	return (0);
+}
+
+static int
+mpssas_send_portenable(struct mps_softc *sc)
+{
+	MPI2_PORT_ENABLE_REQUEST *request;
+	struct mps_command *cm;
+
+	mps_dprint(sc, MPS_TRACE, "%s\n", __func__);
+
+	if ((cm = mps_alloc_command(sc)) == NULL)
+		return (EBUSY);
+	request = (MPI2_PORT_ENABLE_REQUEST *)cm->cm_req;
+	request->Function = MPI2_FUNCTION_PORT_ENABLE;
+	request->MsgFlags = 0;
+	request->VP_ID = 0;
+	cm->cm_desc.Default.RequestFlags = MPI2_REQ_DESCRIPT_FLAGS_DEFAULT_TYPE;
+	cm->cm_complete = mpssas_portenable_complete;
+	cm->cm_data = NULL;
+	cm->cm_sge = NULL;
+
+	mps_map_command(sc, cm);
+	mps_dprint(sc, MPS_TRACE, 
+	    "mps_send_portenable finished cm %p req %p complete %p\n",
+	    cm, cm->cm_req, cm->cm_complete);
+	return (0);
+}
+
+static void
+mpssas_portenable_complete(struct mps_softc *sc, struct mps_command *cm)
+{
+	MPI2_PORT_ENABLE_REPLY *reply;
+	struct mpssas_softc *sassc;
+	struct mpssas_target *target;
+	int i;
+
+	mps_dprint(sc, MPS_TRACE, "%s\n", __func__);
+	sassc = sc->sassc;
+
+	/*
+	 * Currently there should be no way we can hit this case.  It only
+	 * happens when we have a failure to allocate chain frames, and
+	 * port enable commands don't have S/G lists.
+	 */
+	if ((cm->cm_flags & MPS_CM_FLAGS_ERROR_MASK) != 0) {
+		mps_printf(sc, "%s: cm_flags = %#x for port enable! "
+			   "This should not happen!\n", __func__, cm->cm_flags);
+	}
+
+	reply = (MPI2_PORT_ENABLE_REPLY *)cm->cm_reply;
+	if (reply == NULL)
+		mps_dprint(sc, MPS_FAULT, "Portenable NULL reply\n");
+	else if ((reply->IOCStatus & MPI2_IOCSTATUS_MASK) !=
+	    MPI2_IOCSTATUS_SUCCESS)
+		mps_dprint(sc, MPS_FAULT, "Portenable failed\n");
+
+	mps_free_command(sc, cm);
+	if (sc->mps_ich.ich_arg != NULL) {
+		mps_dprint(sc, MPS_INFO, "disestablish config intrhook\n");
+		config_intrhook_disestablish(&sc->mps_ich);
+		sc->mps_ich.ich_arg = NULL;
+	}
+
+	/*
+	 * Get WarpDrive info after discovery is complete but before the scan
+	 * starts.  At this point, all devices are ready to be exposed to the
+	 * OS.  If devices should be hidden instead, take them out of the
+	 * 'targets' array before the scan.  The devinfo for a disk will have
+	 * some info and a volume's will be 0.  Use that to remove disks.
+	 */
+	mps_wd_config_pages(sc);
+	if (((sc->mps_flags & MPS_FLAGS_WD_AVAILABLE)
+	  && (sc->WD_hide_expose == MPS_WD_HIDE_ALWAYS))
+	 || (sc->WD_valid_config && (sc->WD_hide_expose ==
+	    MPS_WD_HIDE_IF_VOLUME))) {
+		for (i = 0; i < sassc->sc->facts->MaxTargets; i++) {
+			target = &sassc->targets[i];
+			if (target->devinfo) {
+				target->devinfo = 0x0;
+				target->encl_handle = 0x0;
+				target->encl_slot = 0x0;
+				target->handle = 0x0;
+				target->tid = 0x0;
+				target->linkrate = 0x0;
+				target->flags = 0x0;
+			}
+		}
+	}
+
+	/*
+	 * Done waiting for port enable to complete.  Decrement the refcount.
+	 * If refcount is 0, discovery is complete and a rescan of the bus can
+	 * take place.  Since the simq was explicitly frozen before port
+	 * enable, it must be explicitly released here to keep the
+	 * freeze/release count in sync.
+	 */
+	sc->wait_for_port_enable = 0;
+	sc->port_enable_complete = 1;
+	mpssas_startup_decrement(sassc);
+	xpt_release_simq(sassc->sim, 1);
+}
+
diff -r 52ad4c882614 -r 6b8317170ea4 head/sys/dev/mps/mps_sas.h
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/head/sys/dev/mps/mps_sas.h	Wed Feb 01 11:28:20 2012 +0200
@@ -0,0 +1,161 @@
+/*-
+ * Copyright (c) 2011 LSI Corp.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * 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 OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * LSI MPT-Fusion Host Adapter FreeBSD
+ *
+ * $FreeBSD: head/sys/dev/mps/mps_sas.h 230592 2012-01-26 18:17:21Z ken $
+ */
+
+struct mps_fw_event_work;
+
+struct mpssas_lun {
+	SLIST_ENTRY(mpssas_lun) lun_link;
+	lun_id_t	lun_id;
+	uint8_t		eedp_formatted;
+	uint32_t	eedp_block_size;
+};
+
+struct mpssas_target {
+	uint16_t	handle;
+	uint8_t		linkrate;
+	uint64_t	devname;
+	uint32_t	devinfo;
+	uint16_t	encl_handle;
+	uint16_t	encl_slot;
+	uint8_t		flags;
+#define MPSSAS_TARGET_INABORT	(1 << 0)
+#define MPSSAS_TARGET_INRESET	(1 << 1)
+#define MPSSAS_TARGET_INDIAGRESET (1 << 2)
+#define MPSSAS_TARGET_INREMOVAL	(1 << 3)
+#define MPSSAS_TARGET_INRECOVERY (MPSSAS_TARGET_INABORT | \
+    MPSSAS_TARGET_INRESET | MPSSAS_TARGET_INCHIPRESET)
+#define MPSSAS_TARGET_ADD       (1 << 29)
+#define MPSSAS_TARGET_REMOVE    (1 << 30)
+	uint16_t	tid;
+	SLIST_HEAD(, mpssas_lun) luns;
+	TAILQ_HEAD(, mps_command) commands;
+	struct mps_command *tm;
+	TAILQ_HEAD(, mps_command) timedout_commands;
+	uint16_t        exp_dev_handle;
+	uint16_t        phy_num;
+	uint64_t	sasaddr;
+	uint16_t	parent_handle;
+	uint64_t	parent_sasaddr;
+	uint32_t	parent_devinfo;
+	struct sysctl_ctx_list sysctl_ctx;
+	struct sysctl_oid *sysctl_tree;
+	TAILQ_ENTRY(mpssas_target) sysctl_link;
+	uint64_t        issued;
+	uint64_t        completed;
+	unsigned int    outstanding;
+	unsigned int    timeouts;
+	unsigned int    aborts;
+	unsigned int    logical_unit_resets;
+	unsigned int    target_resets;
+};
+
+struct mpssas_softc {
+	struct mps_softc	*sc;
+	u_int			flags;
+#define MPSSAS_IN_DISCOVERY	(1 << 0)
+#define MPSSAS_IN_STARTUP	(1 << 1)
+#define MPSSAS_DISCOVERY_TIMEOUT_PENDING	(1 << 2)
+#define MPSSAS_QUEUE_FROZEN	(1 << 3)
+#define	MPSSAS_SHUTDOWN		(1 << 4)
+#define	MPSSAS_SCANTHREAD	(1 << 5)
+	struct mpssas_target	*targets;
+	struct cam_devq		*devq;
+	struct cam_sim		*sim;
+	struct cam_path		*path;
+	struct intr_config_hook	sas_ich;
+	struct callout		discovery_callout;
+	u_int			discovery_timeouts;
+	struct mps_event_handle	*mpssas_eh;
+
+	u_int                   startup_refcount;
+	u_int                   tm_count;
+	struct proc             *sysctl_proc;
+
+	TAILQ_HEAD(, ccb_hdr) ccb_scanq;
+	struct proc		*rescan_thread;
+
+	struct taskqueue	*ev_tq;
+	struct task		ev_task;
+	TAILQ_HEAD(, mps_fw_event_work)	ev_queue;
+};
+
+MALLOC_DECLARE(M_MPSSAS);
+
+/*
+ * Abstracted so that the driver can be backwards and forwards compatible
+ * with future versions of CAM that will provide this functionality.
+ */
+#define MPS_SET_LUN(lun, ccblun)	\
+	mpssas_set_lun(lun, ccblun)
+
+static __inline int
+mpssas_set_lun(uint8_t *lun, u_int ccblun)
+{
+	uint64_t *newlun;
+
+	newlun = (uint64_t *)lun;
+	*newlun = 0;
+	if (ccblun <= 0xff) {
+		/* Peripheral device address method, LUN is 0 to 255 */
+		lun[1] = ccblun;
+	} else if (ccblun <= 0x3fff) {
+		/* Flat space address method, LUN is <= 16383 */
+		scsi_ulto2b(ccblun, lun);
+		lun[0] |= 0x40;
+	} else if (ccblun <= 0xffffff) {
+		/* Extended flat space address method, LUN is <= 16777215 */
+		scsi_ulto3b(ccblun, &lun[1]);
+		/* Extended Flat space address method */
+		lun[0] = 0xc0;
+		/* Length = 1, i.e. LUN is 3 bytes long */
+		lun[0] |= 0x10;
+		/* Extended Address Method */
+		lun[0] |= 0x02;
+	} else {
+		return (EINVAL);
+	}
+
+	return (0);
+}
+
+#define MPS_SET_SINGLE_LUN(req, lun)	\
+do {					\
+	bzero((req)->LUN, 8);		\
+	(req)->LUN[1] = lun;		\
+} while(0)
+
+void mpssas_rescan_target(struct mps_softc *sc, struct mpssas_target *targ);
+void mpssas_discovery_end(struct mpssas_softc *sassc);
+void mpssas_startup_increment(struct mpssas_softc *sassc);
+void mpssas_startup_decrement(struct mpssas_softc *sassc);
+
+struct mps_command * mpssas_alloc_tm(struct mps_softc *sc);
+void mpssas_free_tm(struct mps_softc *sc, struct mps_command *tm);
+void mpssas_firmware_event_work(void *arg, int pending);
diff -r 52ad4c882614 -r 6b8317170ea4 head/sys/dev/mps/mps_sas_lsi.c
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/head/sys/dev/mps/mps_sas_lsi.c	Wed Feb 01 11:28:20 2012 +0200
@@ -0,0 +1,865 @@
+/*-
+ * Copyright (c) 2011 LSI Corp.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * 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 OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * LSI MPT-Fusion Host Adapter FreeBSD
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD: head/sys/dev/mps/mps_sas_lsi.c 230592 2012-01-26 18:17:21Z ken $");
+
+/* Communications core for LSI MPT2 */
+
+/* TODO Move headers to mpsvar */
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <sys/selinfo.h>
+#include <sys/module.h>
+#include <sys/bus.h>
+#include <sys/conf.h>
+#include <sys/bio.h>
+#include <sys/malloc.h>
+#include <sys/uio.h>
+#include <sys/sysctl.h>
+#include <sys/endian.h>
+#include <sys/queue.h>
+#include <sys/kthread.h>
+#include <sys/taskqueue.h>
+#include <sys/sbuf.h>
+
+#include <machine/bus.h>
+#include <machine/resource.h>
+#include <sys/rman.h>
+
+#include <machine/stdarg.h>
+
+#include <cam/cam.h>
+#include <cam/cam_ccb.h>
+#include <cam/cam_debug.h>
+#include <cam/cam_sim.h>
+#include <cam/cam_xpt_sim.h>
+#include <cam/cam_xpt_periph.h>
+#include <cam/cam_periph.h>
+#include <cam/scsi/scsi_all.h>
+#include <cam/scsi/scsi_message.h>
+
+#include <dev/mps/mpi/mpi2_type.h>
+#include <dev/mps/mpi/mpi2.h>
+#include <dev/mps/mpi/mpi2_ioc.h>
+#include <dev/mps/mpi/mpi2_sas.h>
+#include <dev/mps/mpi/mpi2_cnfg.h>
+#include <dev/mps/mpi/mpi2_init.h>
+#include <dev/mps/mpi/mpi2_raid.h>
+#include <dev/mps/mpi/mpi2_tool.h>
+#include <dev/mps/mps_ioctl.h>
+#include <dev/mps/mpsvar.h>
+#include <dev/mps/mps_table.h>
+#include <dev/mps/mps_sas.h>
+
+/* For Hashed SAS Address creation for SATA Drives */
+#define MPT2SAS_SN_LEN 20
+#define MPT2SAS_MN_LEN 40
+
+struct mps_fw_event_work {
+	u16			event;
+	void			*event_data;
+	TAILQ_ENTRY(mps_fw_event_work)	ev_link;
+};
+
+union _sata_sas_address {
+	u8 wwid[8];
+	struct {
+		u32 high;
+		u32 low;
+	} word;
+};
+
+/*
+ * define the IDENTIFY DEVICE structure
+ */
+struct _ata_identify_device_data {
+	u16 reserved1[10];	/* 0-9 */
+	u16 serial_number[10];	/* 10-19 */
+	u16 reserved2[7];	/* 20-26 */
+	u16 model_number[20];	/* 27-46*/
+	u16 reserved3[209];	/* 47-255*/
+};
+
+static void mpssas_fw_work(struct mps_softc *sc,
+    struct mps_fw_event_work *fw_event);
+static void mpssas_fw_event_free(struct mps_softc *,
+    struct mps_fw_event_work *);
+static int mpssas_add_device(struct mps_softc *sc, u16 handle, u8 linkrate);
+static int mpssas_get_sata_identify(struct mps_softc *sc, u16 handle,
+    Mpi2SataPassthroughReply_t *mpi_reply, char *id_buffer, int sz,
+    u32 devinfo);
+int mpssas_get_sas_address_for_sata_disk(struct mps_softc *sc,
+    u64 *sas_address, u16 handle, u32 device_info);
+static int mpssas_volume_add(struct mps_softc *sc,
+    Mpi2EventIrConfigElement_t *element);
+
+void
+mpssas_evt_handler(struct mps_softc *sc, uintptr_t data,
+    MPI2_EVENT_NOTIFICATION_REPLY *event)
+{
+	struct mps_fw_event_work *fw_event;
+	u16 sz;
+
+	mps_dprint(sc, MPS_TRACE, "%s\n", __func__);
+	mps_print_evt_sas(sc, event);
+	mpssas_record_event(sc, event);
+
+	fw_event = malloc(sizeof(struct mps_fw_event_work), M_MPT2,
+	     M_ZERO|M_NOWAIT);
+	if (!fw_event) {
+		printf("%s: allocate failed for fw_event\n", __func__);
+		return;
+	}
+	sz = le16toh(event->EventDataLength) * 4;
+	fw_event->event_data = malloc(sz, M_MPT2, M_ZERO|M_NOWAIT);
+	if (!fw_event->event_data) {
+		printf("%s: allocate failed for event_data\n", __func__);
+		free(fw_event, M_MPT2);
+		return;
+	}
+
+	bcopy(event->EventData, fw_event->event_data, sz);
+	fw_event->event = event->Event;
+	if ((event->Event == MPI2_EVENT_SAS_TOPOLOGY_CHANGE_LIST ||
+	    event->Event == MPI2_EVENT_SAS_ENCL_DEVICE_STATUS_CHANGE ||
+	    event->Event == MPI2_EVENT_IR_CONFIGURATION_CHANGE_LIST) &&
+	    sc->track_mapping_events)
+		sc->pending_map_events++;
+
+	/*
+	 * When wait_for_port_enable flag is set, make sure that all the events
+	 * are processed. Increment the startup_refcount and decrement it after
+	 * events are processed.
+	 */
+	if ((event->Event == MPI2_EVENT_SAS_TOPOLOGY_CHANGE_LIST ||
+	    event->Event == MPI2_EVENT_IR_CONFIGURATION_CHANGE_LIST) &&
+	    sc->wait_for_port_enable)
+		mpssas_startup_increment(sc->sassc);
+
+	TAILQ_INSERT_TAIL(&sc->sassc->ev_queue, fw_event, ev_link);
+	taskqueue_enqueue(sc->sassc->ev_tq, &sc->sassc->ev_task);
+
+}
+
+static void
+mpssas_fw_event_free(struct mps_softc *sc, struct mps_fw_event_work *fw_event)
+{
+
+	free(fw_event->event_data, M_MPT2);
+	free(fw_event, M_MPT2);
+}
+
+/**
+ * _mps_fw_work - delayed task for processing firmware events
+ * @sc: per adapter object
+ * @fw_event: The fw_event_work object
+ * Context: user.
+ *
+ * Return nothing.
+ */
+static void
+mpssas_fw_work(struct mps_softc *sc, struct mps_fw_event_work *fw_event)
+{
+	struct mpssas_softc *sassc;
+	sassc = sc->sassc;
+
+	switch (fw_event->event) {
+	case MPI2_EVENT_SAS_TOPOLOGY_CHANGE_LIST: 
+	{
+		MPI2_EVENT_DATA_SAS_TOPOLOGY_CHANGE_LIST *data;
+		MPI2_EVENT_SAS_TOPO_PHY_ENTRY *phy;
+		int i;
+
+		data = (MPI2_EVENT_DATA_SAS_TOPOLOGY_CHANGE_LIST *)
+		    fw_event->event_data;
+
+		mps_mapping_topology_change_event(sc, fw_event->event_data);
+
+		for (i = 0; i < data->NumEntries; i++) {
+			phy = &data->PHY[i];
+			switch (phy->PhyStatus & MPI2_EVENT_SAS_TOPO_RC_MASK) {
+			case MPI2_EVENT_SAS_TOPO_RC_TARG_ADDED:
+				if (mpssas_add_device(sc,
+				    phy->AttachedDevHandle, phy->LinkRate)){
+					printf("%s: failed to add device with "
+					    "handle 0x%x\n", __func__,
+					    phy->AttachedDevHandle);
+					mpssas_prepare_remove(sassc, phy->
+					    AttachedDevHandle);
+				}
+				break;
+			case MPI2_EVENT_SAS_TOPO_RC_TARG_NOT_RESPONDING:
+				mpssas_prepare_remove(sassc, phy->
+				    AttachedDevHandle);
+				break;
+			case MPI2_EVENT_SAS_TOPO_RC_PHY_CHANGED:
+			case MPI2_EVENT_SAS_TOPO_RC_NO_CHANGE:
+			case MPI2_EVENT_SAS_TOPO_RC_DELAY_NOT_RESPONDING:
+			default:
+				break;
+			}
+		}
+		/*
+		 * refcount was incremented for this event in
+		 * mpssas_evt_handler.  Decrement it here because the event has
+		 * been processed.
+		 */
+		mpssas_startup_decrement(sassc);
+		break;
+	}
+	case MPI2_EVENT_SAS_DISCOVERY:
+	{
+		MPI2_EVENT_DATA_SAS_DISCOVERY *data;
+
+		data = (MPI2_EVENT_DATA_SAS_DISCOVERY *)fw_event->event_data;
+
+		if (data->ReasonCode & MPI2_EVENT_SAS_DISC_RC_STARTED)
+			mps_dprint(sc, MPS_TRACE,"SAS discovery start event\n");
+		if (data->ReasonCode & MPI2_EVENT_SAS_DISC_RC_COMPLETED) {
+			mps_dprint(sc, MPS_TRACE,"SAS discovery stop event\n");
+			sassc->flags &= ~MPSSAS_IN_DISCOVERY;
+			mpssas_discovery_end(sassc);
+		}
+		break;
+	}
+	case MPI2_EVENT_SAS_ENCL_DEVICE_STATUS_CHANGE:
+	{
+		Mpi2EventDataSasEnclDevStatusChange_t *data;
+		data = (Mpi2EventDataSasEnclDevStatusChange_t *)
+		    fw_event->event_data;
+		mps_mapping_enclosure_dev_status_change_event(sc,
+		    fw_event->event_data);
+		break;
+	}
+	case MPI2_EVENT_IR_CONFIGURATION_CHANGE_LIST:
+	{
+		Mpi2EventIrConfigElement_t *element;
+		int i;
+		u8 foreign_config;
+		Mpi2EventDataIrConfigChangeList_t *event_data;
+		struct mpssas_target *targ;
+		unsigned int id;
+
+		event_data = fw_event->event_data;
+		foreign_config = (le32toh(event_data->Flags) &
+		    MPI2_EVENT_IR_CHANGE_FLAGS_FOREIGN_CONFIG) ? 1 : 0;
+
+		element =
+		    (Mpi2EventIrConfigElement_t *)&event_data->ConfigElement[0];
+		id = mps_mapping_get_raid_id_from_handle
+		    (sc, element->VolDevHandle);
+
+		mps_mapping_ir_config_change_event(sc, event_data);
+
+		for (i = 0; i < event_data->NumElements; i++, element++) {
+			switch (element->ReasonCode) {
+			case MPI2_EVENT_IR_CHANGE_RC_VOLUME_CREATED:
+			case MPI2_EVENT_IR_CHANGE_RC_ADDED:
+				if (!foreign_config) {
+					if (mpssas_volume_add(sc, element)) {
+						printf("%s: failed to add RAID "
+						    "volume with handle 0x%x\n",
+						    __func__, le16toh(element->
+						    VolDevHandle));
+					}
+				}
+				break;
+			case MPI2_EVENT_IR_CHANGE_RC_VOLUME_DELETED:
+			case MPI2_EVENT_IR_CHANGE_RC_REMOVED:
+				/*
+				 * Rescan after volume is deleted or removed.
+				 */
+				if (!foreign_config) {
+					if (id == MPS_MAP_BAD_ID) {
+						printf("%s: could not get ID "
+						    "for volume with handle "
+						    "0x%04x\n", __func__,
+						    element->VolDevHandle);
+						break;
+					}
+					
+					targ = &sassc->targets[id];
+					targ->handle = 0x0;
+					targ->encl_slot = 0x0;
+					targ->encl_handle = 0x0;
+					targ->exp_dev_handle = 0x0;
+					targ->phy_num = 0x0;
+					targ->linkrate = 0x0;
+					mpssas_rescan_target(sc, targ);
+					printf("RAID target id 0x%x removed\n",
+					    targ->tid);
+				}
+				break;
+			case MPI2_EVENT_IR_CHANGE_RC_PD_CREATED:
+				/*
+				 * Phys Disk of a volume has been created.  Hide
+				 * it from the OS.
+				 */
+				mpssas_prepare_remove(sassc, element->
+				    PhysDiskDevHandle);
+				break;
+			case MPI2_EVENT_IR_CHANGE_RC_PD_DELETED:
+				/*
+				 * Phys Disk of a volume has been deleted.
+				 * Expose it to the OS.
+				 */
+				if (mpssas_add_device(sc,
+				    element->PhysDiskDevHandle, 0)){
+					printf("%s: failed to add device with "
+					    "handle 0x%x\n", __func__,
+					    element->PhysDiskDevHandle);
+					mpssas_prepare_remove(sassc, element->
+					    PhysDiskDevHandle);
+				}
+				break;
+			}
+		}
+		/*
+		 * refcount was incremented for this event in
+		 * mpssas_evt_handler.  Decrement it here because the event has
+		 * been processed.
+		 */
+		mpssas_startup_decrement(sassc);
+		break;
+	}
+	case MPI2_EVENT_IR_VOLUME:
+	{
+		Mpi2EventDataIrVolume_t *event_data = fw_event->event_data;
+
+		/*
+		 * Informational only.
+		 */
+		mps_dprint(sc, MPS_INFO, "Received IR Volume event:\n");
+		switch (event_data->ReasonCode) {
+		case MPI2_EVENT_IR_VOLUME_RC_SETTINGS_CHANGED:
+			mps_dprint(sc, MPS_INFO, "   Volume Settings "
+			    "changed from 0x%x to 0x%x for Volome with "
+			    "handle 0x%x", event_data->PreviousValue,
+			    event_data->NewValue,
+			    event_data->VolDevHandle);
+			break;
+		case MPI2_EVENT_IR_VOLUME_RC_STATUS_FLAGS_CHANGED:
+			mps_dprint(sc, MPS_INFO, "   Volume Status "
+			    "changed from 0x%x to 0x%x for Volome with "
+			    "handle 0x%x", event_data->PreviousValue,
+			    event_data->NewValue,
+			    event_data->VolDevHandle);
+			break;
+		case MPI2_EVENT_IR_VOLUME_RC_STATE_CHANGED:
+			mps_dprint(sc, MPS_INFO, "   Volume State "
+			    "changed from 0x%x to 0x%x for Volome with "
+			    "handle 0x%x", event_data->PreviousValue,
+			    event_data->NewValue,
+			    event_data->VolDevHandle);
+			break;
+		default:
+			break;
+		}
+		break;
+	}
+	case MPI2_EVENT_IR_PHYSICAL_DISK:
+	{
+		Mpi2EventDataIrPhysicalDisk_t *event_data =
+		    fw_event->event_data;
+
+		/*
+		 * Informational only.
+		 */
+		mps_dprint(sc, MPS_INFO, "Received IR Phys Disk event:\n");
+		switch (event_data->ReasonCode) {
+		case MPI2_EVENT_IR_PHYSDISK_RC_SETTINGS_CHANGED:
+			mps_dprint(sc, MPS_INFO, "   Phys Disk Settings "
+			    "changed from 0x%x to 0x%x for Phys Disk Number "
+			    "%d and handle 0x%x at Enclosure handle 0x%x, Slot "
+			    "%d", event_data->PreviousValue,
+			    event_data->NewValue, event_data->PhysDiskNum,
+			    event_data->PhysDiskDevHandle,
+			    event_data->EnclosureHandle, event_data->Slot);
+			break;
+		case MPI2_EVENT_IR_PHYSDISK_RC_STATUS_FLAGS_CHANGED:
+			mps_dprint(sc, MPS_INFO, "   Phys Disk Status changed "
+			    "from 0x%x to 0x%x for Phys Disk Number %d and "
+			    "handle 0x%x at Enclosure handle 0x%x, Slot %d",
+			    event_data->PreviousValue, event_data->NewValue,
+			    event_data->PhysDiskNum,
+			    event_data->PhysDiskDevHandle,
+			    event_data->EnclosureHandle, event_data->Slot);
+			break;
+		case MPI2_EVENT_IR_PHYSDISK_RC_STATE_CHANGED:
+			mps_dprint(sc, MPS_INFO, "   Phys Disk State changed "
+			    "from 0x%x to 0x%x for Phys Disk Number %d and "
+			    "handle 0x%x at Enclosure handle 0x%x, Slot %d",
+			    event_data->PreviousValue, event_data->NewValue,
+			    event_data->PhysDiskNum,
+			    event_data->PhysDiskDevHandle,
+			    event_data->EnclosureHandle, event_data->Slot);
+			break;
+		default:
+			break;
+		}
+		break;
+	}
+	case MPI2_EVENT_IR_OPERATION_STATUS:
+	{
+		Mpi2EventDataIrOperationStatus_t *event_data =
+		    fw_event->event_data;
+
+		/*
+		 * Informational only.
+		 */
+		mps_dprint(sc, MPS_INFO, "Received IR Op Status event:\n");
+		mps_dprint(sc, MPS_INFO, "   RAID Operation of %d is %d "
+		    "percent complete for Volume with handle 0x%x",
+		    event_data->RAIDOperation, event_data->PercentComplete,
+		    event_data->VolDevHandle);
+		break;
+	}
+	case MPI2_EVENT_LOG_ENTRY_ADDED:
+	{
+		pMpi2EventDataLogEntryAdded_t	logEntry;
+		uint16_t			logQualifier;
+		uint8_t				logCode;
+
+		logEntry = (pMpi2EventDataLogEntryAdded_t)fw_event->event_data;
+		logQualifier = logEntry->LogEntryQualifier;
+
+		if (logQualifier == MPI2_WD_LOG_ENTRY) {
+			logCode = logEntry->LogData[0];
+
+			switch (logCode) {
+			case MPI2_WD_SSD_THROTTLING:
+				printf("WarpDrive Warning: IO Throttling has "
+				    "occurred in the WarpDrive subsystem. "
+				    "Check WarpDrive documentation for "
+				    "additional details\n");
+				break;
+			case MPI2_WD_DRIVE_LIFE_WARN:
+				printf("WarpDrive Warning: Program/Erase "
+				    "Cycles for the WarpDrive subsystem in "
+				    "degraded range. Check WarpDrive "
+				    "documentation for additional details\n");
+				break;
+			case MPI2_WD_DRIVE_LIFE_DEAD:
+				printf("WarpDrive Fatal Error: There are no "
+				    "Program/Erase Cycles for the WarpDrive "
+				    "subsystem. The storage device will be in "
+				    "read-only mode. Check WarpDrive "
+				    "documentation for additional details\n");
+				break;
+			case MPI2_WD_RAIL_MON_FAIL:
+				printf("WarpDrive Fatal Error: The Backup Rail "
+				    "Monitor has failed on the WarpDrive "
+				    "subsystem. Check WarpDrive documentation "
+				    "for additional details\n");
+				break;
+			default:
+				break;
+			}
+		}
+		break;
+	}
+	case MPI2_EVENT_SAS_DEVICE_STATUS_CHANGE:
+	case MPI2_EVENT_SAS_BROADCAST_PRIMITIVE:
+	default:
+		mps_dprint(sc, MPS_TRACE,"Unhandled event 0x%0X\n",
+		    fw_event->event);
+		break;
+
+	}
+	mpssas_fw_event_free(sc, fw_event);
+}
+
+void
+mpssas_firmware_event_work(void *arg, int pending)
+{
+	struct mps_fw_event_work *fw_event;
+	struct mps_softc *sc;
+
+	sc = (struct mps_softc *)arg;
+	mps_lock(sc);
+	while ((fw_event = TAILQ_FIRST(&sc->sassc->ev_queue)) != NULL) {
+		TAILQ_REMOVE(&sc->sassc->ev_queue, fw_event, ev_link);
+		mpssas_fw_work(sc, fw_event);
+	}
+	mps_unlock(sc);
+}
+
+static int
+mpssas_add_device(struct mps_softc *sc, u16 handle, u8 linkrate){
+	char devstring[80];
+	struct mpssas_softc *sassc;
+	struct mpssas_target *targ;
+	Mpi2ConfigReply_t mpi_reply;
+	Mpi2SasDevicePage0_t config_page;
+	uint64_t sas_address, sata_sas_address;
+	uint64_t parent_sas_address = 0;
+	u16 ioc_pg8_flags = le16toh(sc->ioc_pg8.Flags);
+	u32 device_info, parent_devinfo = 0;
+	unsigned int id;
+	int ret;
+	int error = 0;
+
+	sassc = sc->sassc;
+	mpssas_startup_increment(sassc);
+	if ((mps_config_get_sas_device_pg0(sc, &mpi_reply, &config_page,
+	     MPI2_SAS_DEVICE_PGAD_FORM_HANDLE, handle))) {
+		printf("%s: error reading SAS device page0\n", __func__);
+		error = ENXIO;
+		goto out;
+	}
+
+	device_info = le32toh(config_page.DeviceInfo);
+
+	if (((device_info & MPI2_SAS_DEVICE_INFO_SMP_TARGET) == 0)
+	 && (config_page.ParentDevHandle != 0)) {
+		Mpi2ConfigReply_t tmp_mpi_reply;
+		Mpi2SasDevicePage0_t parent_config_page;
+
+		if ((mps_config_get_sas_device_pg0(sc, &tmp_mpi_reply,
+		     &parent_config_page, MPI2_SAS_DEVICE_PGAD_FORM_HANDLE,
+		     le16toh(config_page.ParentDevHandle)))) {
+			printf("%s: error reading SAS device %#x page0\n",
+			       __func__, le16toh(config_page.ParentDevHandle));
+		} else {
+			parent_sas_address = parent_config_page.SASAddress.High;
+			parent_sas_address = (parent_sas_address << 32) |
+				parent_config_page.SASAddress.Low;
+			parent_devinfo = le32toh(parent_config_page.DeviceInfo);
+		}
+	}
+	/* TODO Check proper endianess */
+	sas_address = config_page.SASAddress.High;
+	sas_address = (sas_address << 32) | 
+	    config_page.SASAddress.Low;
+
+	if ((ioc_pg8_flags & MPI2_IOCPAGE8_FLAGS_MASK_MAPPING_MODE)
+		    == MPI2_IOCPAGE8_FLAGS_DEVICE_PERSISTENCE_MAPPING) {
+		if (device_info & MPI2_SAS_DEVICE_INFO_SATA_DEVICE) {
+			ret = mpssas_get_sas_address_for_sata_disk(sc,
+			    &sata_sas_address, handle, device_info);
+			if (!ret)
+				id = mps_mapping_get_sas_id(sc,
+				    sata_sas_address, handle);
+			else
+				id = mps_mapping_get_sas_id(sc,
+				    sas_address, handle);
+		} else
+			id = mps_mapping_get_sas_id(sc, sas_address,
+			    handle);
+	} else
+		id = mps_mapping_get_sas_id(sc, sas_address, handle);
+
+	if (id == MPS_MAP_BAD_ID) {
+		printf("failure at %s:%d/%s()! Could not get ID for device "
+		    "with handle 0x%04x\n", __FILE__, __LINE__, __func__,
+		    handle);
+		error = ENXIO;
+		goto out;
+	}
+	mps_vprintf(sc, "SAS Address from SAS device page0 = %jx\n",
+	    sas_address);
+	targ = &sassc->targets[id];
+	targ->devinfo = device_info;
+	targ->devname = le32toh(config_page.DeviceName.High);
+	targ->devname = (targ->devname << 32) | 
+	    le32toh(config_page.DeviceName.Low);
+	targ->encl_handle = le16toh(config_page.EnclosureHandle);
+	targ->encl_slot = le16toh(config_page.Slot);
+	targ->handle = handle;
+	targ->parent_handle = le16toh(config_page.ParentDevHandle);
+	targ->sasaddr = mps_to_u64(&config_page.SASAddress);
+	targ->parent_sasaddr = le64toh(parent_sas_address);
+	targ->parent_devinfo = parent_devinfo;
+	targ->tid = id;
+	targ->linkrate = (linkrate>>4);
+	targ->flags = 0;
+	TAILQ_INIT(&targ->commands);
+	TAILQ_INIT(&targ->timedout_commands);
+	SLIST_INIT(&targ->luns);
+	mps_describe_devinfo(targ->devinfo, devstring, 80);
+	mps_vprintf(sc, "Found device <%s> <%s> <0x%04x> <%d/%d>\n", devstring,
+	    mps_describe_table(mps_linkrate_names, targ->linkrate),
+	    targ->handle, targ->encl_handle, targ->encl_slot);
+	if ((sassc->flags & MPSSAS_IN_STARTUP) == 0)
+		mpssas_rescan_target(sc, targ);
+	mps_vprintf(sc, "Target id 0x%x added\n", targ->tid);
+out:
+	mpssas_startup_decrement(sassc);
+	return (error);
+	
+}
+	
+int
+mpssas_get_sas_address_for_sata_disk(struct mps_softc *sc,
+    u64 *sas_address, u16 handle, u32 device_info)
+{
+	Mpi2SataPassthroughReply_t mpi_reply;
+	int i, rc, try_count;
+	u32 *bufferptr;
+	union _sata_sas_address hash_address;
+	struct _ata_identify_device_data ata_identify;
+	u8 buffer[MPT2SAS_MN_LEN + MPT2SAS_SN_LEN];
+	u32 ioc_status;
+	u8 sas_status;
+
+	memset(&ata_identify, 0, sizeof(ata_identify));
+	try_count = 0;
+	do {
+		rc = mpssas_get_sata_identify(sc, handle, &mpi_reply,
+		    (char *)&ata_identify, sizeof(ata_identify), device_info);
+		try_count++;
+		ioc_status = le16toh(mpi_reply.IOCStatus)
+		    & MPI2_IOCSTATUS_MASK;
+		sas_status = mpi_reply.SASStatus;
+	} while ((rc == -EAGAIN || ioc_status || sas_status) &&
+	    (try_count < 5));
+
+	if (rc == 0 && !ioc_status && !sas_status) {
+		mps_dprint(sc, MPS_INFO, "%s: got SATA identify successfully "
+			   "for handle = 0x%x with try_count = %d\n",
+			   __func__, handle, try_count);
+	} else {
+		mps_dprint(sc, MPS_INFO, "%s: handle = 0x%x failed\n",
+			   __func__, handle);
+		return -1;
+	}
+	/* Copy & byteswap the 40 byte model number to a buffer */
+	for (i = 0; i < MPT2SAS_MN_LEN; i += 2) {
+		buffer[i] = ((u8 *)ata_identify.model_number)[i + 1];
+		buffer[i + 1] = ((u8 *)ata_identify.model_number)[i];
+	}
+	/* Copy & byteswap the 20 byte serial number to a buffer */
+	for (i = 0; i < MPT2SAS_SN_LEN; i += 2) {
+		buffer[MPT2SAS_MN_LEN + i] =
+			((u8 *)ata_identify.serial_number)[i + 1];
+		buffer[MPT2SAS_MN_LEN + i + 1] =
+			((u8 *)ata_identify.serial_number)[i];
+	}
+	bufferptr = (u32 *)buffer;
+	/* There are 60 bytes to hash down to 8. 60 isn't divisible by 8,
+	 * so loop through the first 56 bytes (7*8),
+	 * and then add in the last dword.
+	 */
+	hash_address.word.low  = 0;
+	hash_address.word.high = 0;
+	for (i = 0; (i < ((MPT2SAS_MN_LEN+MPT2SAS_SN_LEN)/8)); i++) {
+		hash_address.word.low += *bufferptr;
+		bufferptr++;
+		hash_address.word.high += *bufferptr;
+		bufferptr++;
+	}
+	/* Add the last dword */
+	hash_address.word.low += *bufferptr;
+	/* Make sure the hash doesn't start with 5, because it could clash
+	 * with a SAS address. Change 5 to a D.
+	 */
+	if ((hash_address.word.high & 0x000000F0) == (0x00000050))
+		hash_address.word.high |= 0x00000080;
+	*sas_address = (u64)hash_address.wwid[0] << 56 |
+	    (u64)hash_address.wwid[1] << 48 | (u64)hash_address.wwid[2] << 40 |
+	    (u64)hash_address.wwid[3] << 32 | (u64)hash_address.wwid[4] << 24 |
+	    (u64)hash_address.wwid[5] << 16 | (u64)hash_address.wwid[6] <<  8 |
+	    (u64)hash_address.wwid[7];
+	return 0;
+}
+
+static int
+mpssas_get_sata_identify(struct mps_softc *sc, u16 handle,
+    Mpi2SataPassthroughReply_t *mpi_reply, char *id_buffer, int sz, u32 devinfo)
+{
+	Mpi2SataPassthroughRequest_t *mpi_request;
+	Mpi2SataPassthroughReply_t *reply;
+	struct mps_command *cm;
+	char *buffer;
+	int error = 0;
+
+	buffer = malloc( sz, M_MPT2, M_NOWAIT | M_ZERO);
+	if (!buffer)
+		return ENOMEM;
+
+	if ((cm = mps_alloc_command(sc)) == NULL)
+		return (EBUSY);
+	mpi_request = (MPI2_SATA_PASSTHROUGH_REQUEST *)cm->cm_req;
+	bzero(mpi_request,sizeof(MPI2_SATA_PASSTHROUGH_REQUEST));
+	mpi_request->Function = MPI2_FUNCTION_SATA_PASSTHROUGH;
+	mpi_request->VF_ID = 0;
+	mpi_request->DevHandle = htole16(handle);
+	mpi_request->PassthroughFlags = (MPI2_SATA_PT_REQ_PT_FLAGS_PIO |
+	    MPI2_SATA_PT_REQ_PT_FLAGS_READ);
+	mpi_request->DataLength = htole32(sz);
+	mpi_request->CommandFIS[0] = 0x27;
+	mpi_request->CommandFIS[1] = 0x80;
+	mpi_request->CommandFIS[2] =  (devinfo &
+	    MPI2_SAS_DEVICE_INFO_ATAPI_DEVICE) ? 0xA1 : 0xEC;
+	cm->cm_sge = &mpi_request->SGL;
+	cm->cm_sglsize = sizeof(MPI2_SGE_IO_UNION);
+	cm->cm_flags = MPS_CM_FLAGS_SGE_SIMPLE | MPS_CM_FLAGS_DATAIN;
+	cm->cm_desc.Default.RequestFlags = MPI2_REQ_DESCRIPT_FLAGS_DEFAULT_TYPE;
+	cm->cm_data = buffer;
+	cm->cm_length = htole32(sz);
+	error = mps_request_polled(sc, cm);
+	reply = (Mpi2SataPassthroughReply_t *)cm->cm_reply;
+	if (error || (reply == NULL)) {
+		/* FIXME */
+		/* If the poll returns error then we need to do diag reset */ 
+		printf("%s: poll for page completed with error %d",
+		    __func__, error);
+		error = ENXIO;
+		goto out;
+	}
+	bcopy(buffer, id_buffer, sz);
+	bcopy(reply, mpi_reply, sizeof(Mpi2SataPassthroughReply_t));
+	if ((reply->IOCStatus & MPI2_IOCSTATUS_MASK) !=
+	    MPI2_IOCSTATUS_SUCCESS) {
+		printf("%s: error reading SATA PASSTHRU; iocstatus = 0x%x\n",
+		    __func__, reply->IOCStatus);
+		error = ENXIO;
+		goto out;
+	}
+out:
+	mps_free_command(sc, cm);
+	free(buffer, M_MPT2);	
+	return (error);
+}
+
+static int
+mpssas_volume_add(struct mps_softc *sc, Mpi2EventIrConfigElement_t *element)
+{
+	struct mpssas_softc *sassc;
+	struct mpssas_target *targ;
+	u64 wwid;
+	u16 handle = le16toh(element->VolDevHandle);
+	unsigned int id;
+	int error = 0;
+
+	sassc = sc->sassc;
+	mpssas_startup_increment(sassc);
+	mps_config_get_volume_wwid(sc, handle, &wwid);
+	if (!wwid) {
+		printf("%s: invalid WWID; cannot add volume to mapping table\n",
+		    __func__);
+		error = ENXIO;
+		goto out;
+	}
+
+	id = mps_mapping_get_raid_id(sc, wwid, handle);
+	if (id == MPS_MAP_BAD_ID) {
+		printf("%s: could not get ID for volume with handle 0x%04x and "
+		    "WWID 0x%016llx\n", __func__, handle,
+		    (unsigned long long)wwid);
+		error = ENXIO;
+		goto out;
+	}
+
+	targ = &sassc->targets[id];
+	targ->tid = id;
+	targ->handle = handle;
+	targ->devname = wwid;
+	TAILQ_INIT(&targ->commands);
+	TAILQ_INIT(&targ->timedout_commands);
+	SLIST_INIT(&targ->luns);
+	if ((sassc->flags & MPSSAS_IN_STARTUP) == 0)
+		mpssas_rescan_target(sc, targ);
+	mps_dprint(sc, MPS_INFO, "RAID target id %d added (WWID = 0x%jx)\n",
+	    targ->tid, wwid);
+out:
+	mpssas_startup_decrement(sassc);
+	return (error);
+}
+
+/**
+ * mpssas_ir_shutdown - IR shutdown notification
+ * @sc: per adapter object
+ *
+ * Sending RAID Action to alert the Integrated RAID subsystem of the IOC that
+ * the host system is shutting down.
+ *
+ * Return nothing.
+ */
+void
+mpssas_ir_shutdown(struct mps_softc *sc)
+{
+	u16 volume_mapping_flags;
+	u16 ioc_pg8_flags = le16toh(sc->ioc_pg8.Flags);
+	struct dev_mapping_table *mt_entry;
+	u32 start_idx, end_idx;
+	unsigned int id, found_volume = 0;
+	struct mps_command *cm;
+	Mpi2RaidActionRequest_t	*action;
+
+	mps_dprint(sc, MPS_TRACE, "%s\n", __func__);
+
+	/* is IR firmware build loaded? */
+	if (!sc->ir_firmware)
+		return;
+
+	/* are there any volumes?  Look at IR target IDs. */
+	// TODO-later, this should be looked up in the RAID config structure
+	// when it is implemented.
+	volume_mapping_flags = le16toh(sc->ioc_pg8.IRVolumeMappingFlags) &
+	    MPI2_IOCPAGE8_IRFLAGS_MASK_VOLUME_MAPPING_MODE;
+	if (volume_mapping_flags == MPI2_IOCPAGE8_IRFLAGS_LOW_VOLUME_MAPPING) {
+		start_idx = 0;
+		if (ioc_pg8_flags & MPI2_IOCPAGE8_FLAGS_RESERVED_TARGETID_0)
+			start_idx = 1;
+	} else
+		start_idx = sc->max_devices - sc->max_volumes;
+	end_idx = start_idx + sc->max_volumes - 1;
+
+	for (id = start_idx; id < end_idx; id++) {
+		mt_entry = &sc->mapping_table[id];
+		if ((mt_entry->physical_id != 0) &&
+		    (mt_entry->missing_count == 0)) {
+			found_volume = 1;
+			break;
+		}
+	}
+
+	if (!found_volume)
+		return;
+
+	if ((cm = mps_alloc_command(sc)) == NULL) {
+		printf("%s: command alloc failed\n", __func__);
+		return;
+	}
+
+	action = (MPI2_RAID_ACTION_REQUEST *)cm->cm_req;
+	action->Function = MPI2_FUNCTION_RAID_ACTION;
+	action->Action = MPI2_RAID_ACTION_SYSTEM_SHUTDOWN_INITIATED;
+	cm->cm_desc.Default.RequestFlags = MPI2_REQ_DESCRIPT_FLAGS_DEFAULT_TYPE;
+	mps_request_polled(sc, cm);
+
+	/*
+	 * Don't check for reply, just leave.
+	 */
+	if (cm)
+		mps_free_command(sc, cm);
+}
diff -r 52ad4c882614 -r 6b8317170ea4 head/sys/dev/mps/mps_table.c
--- a/head/sys/dev/mps/mps_table.c	Wed Feb 01 11:28:01 2012 +0200
+++ b/head/sys/dev/mps/mps_table.c	Wed Feb 01 11:28:20 2012 +0200
@@ -25,10 +25,11 @@
  */
 
 #include <sys/cdefs.h>
-__FBSDID("$FreeBSD$");
+__FBSDID("$FreeBSD: head/sys/dev/mps/mps_table.c 230592 2012-01-26 18:17:21Z ken $");
 
 /* Debugging tables for MPT2 */
 
+/* TODO Move headers to mpsvar */
 #include <sys/types.h>
 #include <sys/param.h>
 #include <sys/systm.h>
@@ -41,6 +42,9 @@
 #include <sys/malloc.h>
 #include <sys/uio.h>
 #include <sys/sysctl.h>
+#include <sys/queue.h>
+#include <sys/kthread.h>
+#include <sys/taskqueue.h>
 
 #include <machine/bus.h>
 #include <machine/resource.h>
@@ -53,6 +57,8 @@
 #include <dev/mps/mpi/mpi2_ioc.h>
 #include <dev/mps/mpi/mpi2_cnfg.h>
 #include <dev/mps/mpi/mpi2_init.h>
+#include <dev/mps/mpi/mpi2_tool.h>
+#include <dev/mps/mps_ioctl.h>
 #include <dev/mps/mpsvar.h>
 #include <dev/mps/mps_table.h>
 
diff -r 52ad4c882614 -r 6b8317170ea4 head/sys/dev/mps/mps_user.c
--- a/head/sys/dev/mps/mps_user.c	Wed Feb 01 11:28:01 2012 +0200
+++ b/head/sys/dev/mps/mps_user.c	Wed Feb 01 11:28:20 2012 +0200
@@ -27,14 +27,44 @@
  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  * SUCH DAMAGE.
  *
- * LSI MPS-Fusion Host Adapter FreeBSD userland interface
+ * LSI MPT-Fusion Host Adapter FreeBSD userland interface
+ */
+/*-
+ * Copyright (c) 2011 LSI Corp.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * 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 OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * LSI MPT-Fusion Host Adapter FreeBSD
+ *
+ * $FreeBSD: head/sys/dev/mps/mps_user.c 230592 2012-01-26 18:17:21Z ken $
  */
 
 #include <sys/cdefs.h>
-__FBSDID("$FreeBSD$");
+__FBSDID("$FreeBSD: head/sys/dev/mps/mps_user.c 230592 2012-01-26 18:17:21Z ken $");
 
 #include "opt_compat.h"
 
+/* TODO Move headers to mpsvar */
 #include <sys/types.h>
 #include <sys/param.h>
 #include <sys/systm.h>
@@ -49,6 +79,9 @@
 #include <sys/sysctl.h>
 #include <sys/ioccom.h>
 #include <sys/endian.h>
+#include <sys/queue.h>
+#include <sys/kthread.h>
+#include <sys/taskqueue.h>
 #include <sys/proc.h>
 #include <sys/sysent.h>
 
@@ -56,15 +89,21 @@
 #include <machine/resource.h>
 #include <sys/rman.h>
 
+#include <cam/cam.h>
 #include <cam/scsi/scsi_all.h>
 
 #include <dev/mps/mpi/mpi2_type.h>
 #include <dev/mps/mpi/mpi2.h>
 #include <dev/mps/mpi/mpi2_ioc.h>
 #include <dev/mps/mpi/mpi2_cnfg.h>
+#include <dev/mps/mpi/mpi2_init.h>
+#include <dev/mps/mpi/mpi2_tool.h>
+#include <dev/mps/mps_ioctl.h>
 #include <dev/mps/mpsvar.h>
 #include <dev/mps/mps_table.h>
-#include <dev/mps/mps_ioctl.h>
+#include <dev/mps/mps_sas.h>
+#include <dev/pci/pcivar.h>
+#include <dev/pci/pcireg.h>
 
 static d_open_t		mps_open;
 static d_close_t	mps_close;
@@ -103,8 +142,52 @@
 				  struct mps_usr_command *);
 static int mps_user_command(struct mps_softc *, struct mps_usr_command *);
 
+static int mps_user_pass_thru(struct mps_softc *sc, mps_pass_thru_t *data);
+static void mps_user_get_adapter_data(struct mps_softc *sc,
+    mps_adapter_data_t *data);
+static void mps_user_read_pci_info(struct mps_softc *sc,
+    mps_pci_info_t *data);
+static uint8_t mps_get_fw_diag_buffer_number(struct mps_softc *sc,
+    uint32_t unique_id);
+static int mps_post_fw_diag_buffer(struct mps_softc *sc,
+    mps_fw_diagnostic_buffer_t *pBuffer, uint32_t *return_code);
+static int mps_release_fw_diag_buffer(struct mps_softc *sc,
+    mps_fw_diagnostic_buffer_t *pBuffer, uint32_t *return_code,
+    uint32_t diag_type);
+static int mps_diag_register(struct mps_softc *sc,
+    mps_fw_diag_register_t *diag_register, uint32_t *return_code);
+static int mps_diag_unregister(struct mps_softc *sc,
+    mps_fw_diag_unregister_t *diag_unregister, uint32_t *return_code);
+static int mps_diag_query(struct mps_softc *sc, mps_fw_diag_query_t *diag_query,
+    uint32_t *return_code);
+static int mps_diag_read_buffer(struct mps_softc *sc,
+    mps_diag_read_buffer_t *diag_read_buffer, uint8_t *ioctl_buf,
+    uint32_t *return_code);
+static int mps_diag_release(struct mps_softc *sc,
+    mps_fw_diag_release_t *diag_release, uint32_t *return_code);
+static int mps_do_diag_action(struct mps_softc *sc, uint32_t action,
+    uint8_t *diag_action, uint32_t length, uint32_t *return_code);
+static int mps_user_diag_action(struct mps_softc *sc, mps_diag_action_t *data);
+static void mps_user_event_query(struct mps_softc *sc, mps_event_query_t *data);
+static void mps_user_event_enable(struct mps_softc *sc,
+    mps_event_enable_t *data);
+static int mps_user_event_report(struct mps_softc *sc,
+    mps_event_report_t *data);
+static int mps_user_reg_access(struct mps_softc *sc, mps_reg_access_t *data);
+static int mps_user_btdh(struct mps_softc *sc, mps_btdh_mapping_t *data);
+
 static MALLOC_DEFINE(M_MPSUSER, "mps_user", "Buffers for mps(4) ioctls");
 
+/* Macros from compat/freebsd32/freebsd32.h */
+#define	PTRIN(v)	(void *)(uintptr_t)(v)
+#define	PTROUT(v)	(uint32_t)(uintptr_t)(v)
+
+#define	CP(src,dst,fld) do { (dst).fld = (src).fld; } while (0)
+#define	PTRIN_CP(src,dst,fld)				\
+	do { (dst).fld = PTRIN((src).fld); } while (0)
+#define	PTROUT_CP(src,dst,fld) \
+	do { (dst).fld = PTROUT((src).fld); } while (0)
+
 int
 mps_attach_user(struct mps_softc *sc)
 {
@@ -625,18 +708,17 @@
 		cm->cm_length = 0;
 	}
 
-	cm->cm_flags = MPS_CM_FLAGS_SGE_SIMPLE | MPS_CM_FLAGS_WAKEUP;
+	cm->cm_flags = MPS_CM_FLAGS_SGE_SIMPLE;
 	cm->cm_desc.Default.RequestFlags = MPI2_REQ_DESCRIPT_FLAGS_DEFAULT_TYPE;
 
 	mps_lock(sc);
-	err = mps_map_command(sc, cm);
+	err = mps_wait_command(sc, cm, 0);
 
-	if (err != 0 && err != EINPROGRESS) {
+	if (err) {
 		mps_printf(sc, "%s: invalid request: error %d\n",
 		    __func__, err);
 		goto Ret;
 	}
-	msleep(cm, &sc->mps_mtx, 0, "mpsuser", 0);
 
 	rpl = (MPI2_DEFAULT_REPLY *)cm->cm_reply;
 	sz = rpl->MsgLength * 4;
@@ -664,7 +746,1302 @@
 	if (buf != NULL)
 		free(buf, M_MPSUSER);
 	return (err);
-}	
+}
+
+static int
+mps_user_pass_thru(struct mps_softc *sc, mps_pass_thru_t *data)
+{
+	MPI2_REQUEST_HEADER	*hdr, tmphdr;	
+	MPI2_DEFAULT_REPLY	*rpl;
+	struct mps_command	*cm = NULL;
+	int			err = 0, dir = 0, sz;
+	uint8_t			function = 0;
+	u_int			sense_len;
+
+	/*
+	 * Only allow one passthru command at a time.  Use the MPS_FLAGS_BUSY
+	 * bit to denote that a passthru is being processed.
+	 */
+	mps_lock(sc);
+	if (sc->mps_flags & MPS_FLAGS_BUSY) {
+		mps_dprint(sc, MPS_INFO, "%s: Only one passthru command "
+		    "allowed at a single time.", __func__);
+		mps_unlock(sc);
+		return (EBUSY);
+	}
+	sc->mps_flags |= MPS_FLAGS_BUSY;
+	mps_unlock(sc);
+
+	/*
+	 * Do some validation on data direction.  Valid cases are:
+	 *    1) DataSize is 0 and direction is NONE
+	 *    2) DataSize is non-zero and one of:
+	 *        a) direction is READ or
+	 *        b) direction is WRITE or
+	 *        c) direction is BOTH and DataOutSize is non-zero
+	 * If valid and the direction is BOTH, change the direction to READ.
+	 * if valid and the direction is not BOTH, make sure DataOutSize is 0.
+	 */
+	if (((data->DataSize == 0) &&
+	    (data->DataDirection == MPS_PASS_THRU_DIRECTION_NONE)) ||
+	    ((data->DataSize != 0) &&
+	    ((data->DataDirection == MPS_PASS_THRU_DIRECTION_READ) ||
+	    (data->DataDirection == MPS_PASS_THRU_DIRECTION_WRITE) ||
+	    ((data->DataDirection == MPS_PASS_THRU_DIRECTION_BOTH) &&
+	    (data->DataOutSize != 0))))) {
+		if (data->DataDirection == MPS_PASS_THRU_DIRECTION_BOTH)
+			data->DataDirection = MPS_PASS_THRU_DIRECTION_READ;
+		else
+			data->DataOutSize = 0;
+	} else
+		return (EINVAL);
+
+	mps_dprint(sc, MPS_INFO, "%s: req 0x%jx %d  rpl 0x%jx %d "
+	    "data in 0x%jx %d data out 0x%jx %d data dir %d\n", __func__,
+	    data->PtrRequest, data->RequestSize, data->PtrReply,
+	    data->ReplySize, data->PtrData, data->DataSize,
+	    data->PtrDataOut, data->DataOutSize, data->DataDirection);
+
+	/*
+	 * copy in the header so we know what we're dealing with before we
+	 * commit to allocating a command for it.
+	 */
+	err = copyin(PTRIN(data->PtrRequest), &tmphdr, data->RequestSize);
+	if (err != 0)
+		goto RetFreeUnlocked;
+
+	if (data->RequestSize > (int)sc->facts->IOCRequestFrameSize * 4) {
+		err = EINVAL;
+		goto RetFreeUnlocked;
+	}
+
+	function = tmphdr.Function;
+	mps_dprint(sc, MPS_INFO, "%s: Function %02X MsgFlags %02X\n", __func__,
+	    function, tmphdr.MsgFlags);
+
+	/*
+	 * Handle a passthru TM request.
+	 */
+	if (function == MPI2_FUNCTION_SCSI_TASK_MGMT) {
+		MPI2_SCSI_TASK_MANAGE_REQUEST	*task;
+
+		mps_lock(sc);
+		cm = mpssas_alloc_tm(sc);
+		if (cm == NULL) {
+			err = EINVAL;
+			goto Ret;
+		}
+
+		/* Copy the header in.  Only a small fixup is needed. */
+		task = (MPI2_SCSI_TASK_MANAGE_REQUEST *)cm->cm_req;
+		bcopy(&tmphdr, task, data->RequestSize);
+		task->TaskMID = cm->cm_desc.Default.SMID;
+
+		cm->cm_data = NULL;
+		cm->cm_desc.HighPriority.RequestFlags = MPI2_REQ_DESCRIPT_FLAGS_HIGH_PRIORITY;
+		cm->cm_complete = NULL;
+		cm->cm_complete_data = NULL;
+
+		err = mps_wait_command(sc, cm, 0);
+
+		if (err != 0) {
+			err = EIO;
+			mps_dprint(sc, MPS_FAULT, "%s: task management failed",
+			    __func__);
+		}
+		/*
+		 * Copy the reply data and sense data to user space.
+		 */
+		if (cm->cm_reply != NULL) {
+			rpl = (MPI2_DEFAULT_REPLY *)cm->cm_reply;
+			sz = rpl->MsgLength * 4;
+	
+			if (sz > data->ReplySize) {
+				mps_printf(sc, "%s: reply buffer too small: %d, "
+				    "required: %d\n", __func__, data->ReplySize, sz);
+				err = EINVAL;
+			} else {
+				mps_unlock(sc);
+				copyout(cm->cm_reply, PTRIN(data->PtrReply),
+				    data->ReplySize);
+				mps_lock(sc);
+			}
+		}
+		mpssas_free_tm(sc, cm);
+		goto Ret;
+	}
+
+	mps_lock(sc);
+	cm = mps_alloc_command(sc);
+
+	if (cm == NULL) {
+		mps_printf(sc, "%s: no mps requests\n", __func__);
+		err = ENOMEM;
+		goto Ret;
+	}
+	mps_unlock(sc);
+
+	hdr = (MPI2_REQUEST_HEADER *)cm->cm_req;
+	bcopy(&tmphdr, hdr, data->RequestSize);
+
+	/*
+	 * Do some checking to make sure the IOCTL request contains a valid
+	 * request.  Then set the SGL info.
+	 */
+	mpi_init_sge(cm, hdr, (void *)((uint8_t *)hdr + data->RequestSize));
+
+	/*
+	 * Set up for read, write or both.  From check above, DataOutSize will
+	 * be 0 if direction is READ or WRITE, but it will have some non-zero
+	 * value if the direction is BOTH.  So, just use the biggest size to get
+	 * the cm_data buffer size.  If direction is BOTH, 2 SGLs need to be set
+	 * up; the first is for the request and the second will contain the
+	 * response data. cm_out_len needs to be set here and this will be used
+	 * when the SGLs are set up.
+	 */
+	cm->cm_data = NULL;
+	cm->cm_length = MAX(data->DataSize, data->DataOutSize);
+	cm->cm_out_len = data->DataOutSize;
+	cm->cm_flags = 0;
+	if (cm->cm_length != 0) {
+		cm->cm_data = malloc(cm->cm_length, M_MPSUSER, M_WAITOK |
+		    M_ZERO);
+		if (cm->cm_data == NULL) {
+			mps_dprint(sc, MPS_FAULT, "%s: alloc failed for IOCTL "
+			    "passthru length %d\n", __func__, cm->cm_length);
+		} else {
+			cm->cm_flags = MPS_CM_FLAGS_DATAIN;
+			if (data->DataOutSize) {
+				cm->cm_flags |= MPS_CM_FLAGS_DATAOUT;
+				err = copyin(PTRIN(data->PtrDataOut),
+				    cm->cm_data, data->DataOutSize);
+			} else if (data->DataDirection ==
+			    MPS_PASS_THRU_DIRECTION_WRITE) {
+				cm->cm_flags = MPS_CM_FLAGS_DATAOUT;
+				err = copyin(PTRIN(data->PtrData),
+				    cm->cm_data, data->DataSize);
+			}
+			if (err != 0)
+				mps_dprint(sc, MPS_FAULT, "%s: failed to copy "
+				    "IOCTL data from user space\n", __func__);
+		}
+	}
+	cm->cm_flags |= MPS_CM_FLAGS_SGE_SIMPLE;
+	cm->cm_desc.Default.RequestFlags = MPI2_REQ_DESCRIPT_FLAGS_DEFAULT_TYPE;
+
+	/*
+	 * Set up Sense buffer and SGL offset for IO passthru.  SCSI IO request
+	 * uses SCSI IO descriptor.
+	 */
+	if ((function == MPI2_FUNCTION_SCSI_IO_REQUEST) ||
+	    (function == MPI2_FUNCTION_RAID_SCSI_IO_PASSTHROUGH)) {
+		MPI2_SCSI_IO_REQUEST	*scsi_io_req;
+
+		scsi_io_req = (MPI2_SCSI_IO_REQUEST *)hdr;
+		/*
+		 * Put SGE for data and data_out buffer at the end of
+		 * scsi_io_request message header (64 bytes in total).
+		 * Following above SGEs, the residual space will be used by
+		 * sense data.
+		 */
+		scsi_io_req->SenseBufferLength = (uint8_t)(data->RequestSize -
+		    64);
+		scsi_io_req->SenseBufferLowAddress = cm->cm_sense_busaddr;
+
+		/*
+		 * Set SGLOffset0 value.  This is the number of dwords that SGL
+		 * is offset from the beginning of MPI2_SCSI_IO_REQUEST struct.
+		 */
+		scsi_io_req->SGLOffset0 = 24;
+
+		/*
+		 * Setup descriptor info.  RAID passthrough must use the
+		 * default request descriptor which is already set, so if this
+		 * is a SCSI IO request, change the descriptor to SCSI IO.
+		 * Also, if this is a SCSI IO request, handle the reply in the
+		 * mpssas_scsio_complete function.
+		 */
+		if (function == MPI2_FUNCTION_SCSI_IO_REQUEST) {
+			cm->cm_desc.SCSIIO.RequestFlags =
+			    MPI2_REQ_DESCRIPT_FLAGS_SCSI_IO;
+			cm->cm_desc.SCSIIO.DevHandle = scsi_io_req->DevHandle;
+
+			/*
+			 * Make sure the DevHandle is not 0 because this is a
+			 * likely error.
+			 */
+			if (scsi_io_req->DevHandle == 0) {
+				err = EINVAL;
+				goto RetFreeUnlocked;
+			}
+		}
+	}
+
+	mps_lock(sc);
+
+	err = mps_wait_command(sc, cm, 0);
+
+	if (err) {
+		mps_printf(sc, "%s: invalid request: error %d\n", __func__,
+		    err);
+		mps_unlock(sc);
+		goto RetFreeUnlocked;
+	}
+
+	/*
+	 * Sync the DMA data, if any.  Then copy the data to user space.
+	 */
+	if (cm->cm_data != NULL) {
+		if (cm->cm_flags & MPS_CM_FLAGS_DATAIN)
+			dir = BUS_DMASYNC_POSTREAD;
+		else if (cm->cm_flags & MPS_CM_FLAGS_DATAOUT)
+			dir = BUS_DMASYNC_POSTWRITE;;
+		bus_dmamap_sync(sc->buffer_dmat, cm->cm_dmamap, dir);
+		bus_dmamap_unload(sc->buffer_dmat, cm->cm_dmamap);
+
+		if (cm->cm_flags & MPS_CM_FLAGS_DATAIN) {
+			mps_unlock(sc);
+			err = copyout(cm->cm_data,
+			    PTRIN(data->PtrData), data->DataSize);
+			mps_lock(sc);
+			if (err != 0)
+				mps_dprint(sc, MPS_FAULT, "%s: failed to copy "
+				    "IOCTL data to user space\n", __func__);
+		}
+	}
+
+	/*
+	 * Copy the reply data and sense data to user space.
+	 */
+	if (cm->cm_reply != NULL) {
+		rpl = (MPI2_DEFAULT_REPLY *)cm->cm_reply;
+		sz = rpl->MsgLength * 4;
+
+		if (sz > data->ReplySize) {
+			mps_printf(sc, "%s: reply buffer too small: %d, "
+			    "required: %d\n", __func__, data->ReplySize, sz);
+			err = EINVAL;
+		} else {
+			mps_unlock(sc);
+			copyout(cm->cm_reply, PTRIN(data->PtrReply),
+			    data->ReplySize);
+			mps_lock(sc);
+		}
+
+		if ((function == MPI2_FUNCTION_SCSI_IO_REQUEST) ||
+		    (function == MPI2_FUNCTION_RAID_SCSI_IO_PASSTHROUGH)) {
+			if (((MPI2_SCSI_IO_REPLY *)rpl)->SCSIState &
+			    MPI2_SCSI_STATE_AUTOSENSE_VALID) {
+				sense_len =
+				    MIN(((MPI2_SCSI_IO_REPLY *)rpl)->SenseCount,
+				    sizeof(struct scsi_sense_data));
+				mps_unlock(sc);
+				copyout(cm->cm_sense, cm->cm_req + 64, sense_len);
+				mps_lock(sc);
+			}
+		}
+	}
+	mps_unlock(sc);
+
+RetFreeUnlocked:
+	mps_lock(sc);
+
+	if (cm != NULL) {
+		if (cm->cm_data)
+			free(cm->cm_data, M_MPSUSER);
+		mps_free_command(sc, cm);
+	}
+Ret:
+	sc->mps_flags &= ~MPS_FLAGS_BUSY;
+	mps_unlock(sc);
+
+	return (err);
+}
+
+static void
+mps_user_get_adapter_data(struct mps_softc *sc, mps_adapter_data_t *data)
+{
+	Mpi2ConfigReply_t	mpi_reply;
+	Mpi2BiosPage3_t		config_page;
+
+	/*
+	 * Use the PCI interface functions to get the Bus, Device, and Function
+	 * information.
+	 */
+	data->PciInformation.u.bits.BusNumber = pci_get_bus(sc->mps_dev);
+	data->PciInformation.u.bits.DeviceNumber = pci_get_slot(sc->mps_dev);
+	data->PciInformation.u.bits.FunctionNumber =
+	    pci_get_function(sc->mps_dev);
+
+	/*
+	 * Get the FW version that should already be saved in IOC Facts.
+	 */
+	data->MpiFirmwareVersion = sc->facts->FWVersion.Word;
+
+	/*
+	 * General device info.
+	 */
+	data->AdapterType = MPSIOCTL_ADAPTER_TYPE_SAS2;
+	if (sc->mps_flags & MPS_FLAGS_WD_AVAILABLE)
+		data->AdapterType = MPSIOCTL_ADAPTER_TYPE_SAS2_SSS6200;
+	data->PCIDeviceHwId = pci_get_device(sc->mps_dev);
+	data->PCIDeviceHwRev = pci_read_config(sc->mps_dev, PCIR_REVID, 1);
+	data->SubSystemId = pci_get_subdevice(sc->mps_dev);
+	data->SubsystemVendorId = pci_get_subvendor(sc->mps_dev);
+
+	/*
+	 * Get the driver version.
+	 */
+	strcpy((char *)&data->DriverVersion[0], MPS_DRIVER_VERSION);
+
+	/*
+	 * Need to get BIOS Config Page 3 for the BIOS Version.
+	 */
+	data->BiosVersion = 0;
+	if (mps_config_get_bios_pg3(sc, &mpi_reply, &config_page))
+		printf("%s: Error while retrieving BIOS Version\n", __func__);
+	else
+		data->BiosVersion = config_page.BiosVersion;
+}
+
+static void
+mps_user_read_pci_info(struct mps_softc *sc, mps_pci_info_t *data)
+{
+	int	i;
+
+	/*
+	 * Use the PCI interface functions to get the Bus, Device, and Function
+	 * information.
+	 */
+	data->BusNumber = pci_get_bus(sc->mps_dev);
+	data->DeviceNumber = pci_get_slot(sc->mps_dev);
+	data->FunctionNumber = pci_get_function(sc->mps_dev);
+
+	/*
+	 * Now get the interrupt vector and the pci header.  The vector can
+	 * only be 0 right now.  The header is the first 256 bytes of config
+	 * space.
+	 */
+	data->InterruptVector = 0;
+	for (i = 0; i < sizeof (data->PciHeader); i++) {
+		data->PciHeader[i] = pci_read_config(sc->mps_dev, i, 1);
+	}
+}
+
+static uint8_t
+mps_get_fw_diag_buffer_number(struct mps_softc *sc, uint32_t unique_id)
+{
+	uint8_t	index;
+
+	for (index = 0; index < MPI2_DIAG_BUF_TYPE_COUNT; index++) {
+		if (sc->fw_diag_buffer_list[index].unique_id == unique_id) {
+			return (index);
+		}
+	}
+
+	return (MPS_FW_DIAGNOSTIC_UID_NOT_FOUND);
+}
+
+static int
+mps_post_fw_diag_buffer(struct mps_softc *sc,
+    mps_fw_diagnostic_buffer_t *pBuffer, uint32_t *return_code)
+{
+	MPI2_DIAG_BUFFER_POST_REQUEST	*req;
+	MPI2_DIAG_BUFFER_POST_REPLY	*reply;
+	struct mps_command		*cm = NULL;
+	int				i, status;
+
+	/*
+	 * If buffer is not enabled, just leave.
+	 */
+	*return_code = MPS_FW_DIAG_ERROR_POST_FAILED;
+	if (!pBuffer->enabled) {
+		return (MPS_DIAG_FAILURE);
+	}
+
+	/*
+	 * Clear some flags initially.
+	 */
+	pBuffer->force_release = FALSE;
+	pBuffer->valid_data = FALSE;
+	pBuffer->owned_by_firmware = FALSE;
+
+	/*
+	 * Get a command.
+	 */
+	cm = mps_alloc_command(sc);
+	if (cm == NULL) {
+		mps_printf(sc, "%s: no mps requests\n", __func__);
+		return (MPS_DIAG_FAILURE);
+	}
+
+	/*
+	 * Build the request for releasing the FW Diag Buffer and send it.
+	 */
+	req = (MPI2_DIAG_BUFFER_POST_REQUEST *)cm->cm_req;
+	req->Function = MPI2_FUNCTION_DIAG_BUFFER_POST;
+	req->BufferType = pBuffer->buffer_type;
+	req->ExtendedType = pBuffer->extended_type;
+	req->BufferLength = pBuffer->size;
+	for (i = 0; i < (sizeof(req->ProductSpecific) / 4); i++)
+		req->ProductSpecific[i] = pBuffer->product_specific[i];
+	mps_from_u64(sc->fw_diag_busaddr, &req->BufferAddress);
+	cm->cm_data = NULL;
+	cm->cm_length = 0;
+	cm->cm_desc.Default.RequestFlags = MPI2_REQ_DESCRIPT_FLAGS_DEFAULT_TYPE;
+	cm->cm_complete_data = NULL;
+
+	/*
+	 * Send command synchronously.
+	 */
+	status = mps_wait_command(sc, cm, 0);
+	if (status) {
+		mps_printf(sc, "%s: invalid request: error %d\n", __func__,
+		    status);
+		status = MPS_DIAG_FAILURE;
+		goto done;
+	}
+
+	/*
+	 * Process POST reply.
+	 */
+	reply = (MPI2_DIAG_BUFFER_POST_REPLY *)cm->cm_reply;
+	if (reply->IOCStatus != MPI2_IOCSTATUS_SUCCESS) {
+		status = MPS_DIAG_FAILURE;
+		mps_dprint(sc, MPS_FAULT, "%s: post of FW  Diag Buffer failed "
+		    "with IOCStatus = 0x%x, IOCLogInfo = 0x%x and "
+		    "TransferLength = 0x%x\n", __func__, reply->IOCStatus,
+		    reply->IOCLogInfo, reply->TransferLength);
+		goto done;
+	}
+
+	/*
+	 * Post was successful.
+	 */
+	pBuffer->valid_data = TRUE;
+	pBuffer->owned_by_firmware = TRUE;
+	*return_code = MPS_FW_DIAG_ERROR_SUCCESS;
+	status = MPS_DIAG_SUCCESS;
+
+done:
+	mps_free_command(sc, cm);
+	return (status);
+}
+
+static int
+mps_release_fw_diag_buffer(struct mps_softc *sc,
+    mps_fw_diagnostic_buffer_t *pBuffer, uint32_t *return_code,
+    uint32_t diag_type)
+{
+	MPI2_DIAG_RELEASE_REQUEST	*req;
+	MPI2_DIAG_RELEASE_REPLY		*reply;
+	struct mps_command		*cm = NULL;
+	int				status;
+
+	/*
+	 * If buffer is not enabled, just leave.
+	 */
+	*return_code = MPS_FW_DIAG_ERROR_RELEASE_FAILED;
+	if (!pBuffer->enabled) {
+		mps_dprint(sc, MPS_INFO, "%s: This buffer type is not supported "
+		    "by the IOC", __func__);
+		return (MPS_DIAG_FAILURE);
+	}
+
+	/*
+	 * Clear some flags initially.
+	 */
+	pBuffer->force_release = FALSE;
+	pBuffer->valid_data = FALSE;
+	pBuffer->owned_by_firmware = FALSE;
+
+	/*
+	 * Get a command.
+	 */
+	cm = mps_alloc_command(sc);
+	if (cm == NULL) {
+		mps_printf(sc, "%s: no mps requests\n", __func__);
+		return (MPS_DIAG_FAILURE);
+	}
+
+	/*
+	 * Build the request for releasing the FW Diag Buffer and send it.
+	 */
+	req = (MPI2_DIAG_RELEASE_REQUEST *)cm->cm_req;
+	req->Function = MPI2_FUNCTION_DIAG_RELEASE;
+	req->BufferType = pBuffer->buffer_type;
+	cm->cm_data = NULL;
+	cm->cm_length = 0;
+	cm->cm_desc.Default.RequestFlags = MPI2_REQ_DESCRIPT_FLAGS_DEFAULT_TYPE;
+	cm->cm_complete_data = NULL;
+
+	/*
+	 * Send command synchronously.
+	 */
+	status = mps_wait_command(sc, cm, 0);
+	if (status) {
+		mps_printf(sc, "%s: invalid request: error %d\n", __func__,
+		    status);
+		status = MPS_DIAG_FAILURE;
+		goto done;
+	}
+
+	/*
+	 * Process RELEASE reply.
+	 */
+	reply = (MPI2_DIAG_RELEASE_REPLY *)cm->cm_reply;
+	if ((reply->IOCStatus != MPI2_IOCSTATUS_SUCCESS) ||
+	    pBuffer->owned_by_firmware) {
+		status = MPS_DIAG_FAILURE;
+		mps_dprint(sc, MPS_FAULT, "%s: release of FW Diag Buffer "
+		    "failed with IOCStatus = 0x%x and IOCLogInfo = 0x%x\n",
+		    __func__, reply->IOCStatus, reply->IOCLogInfo);
+		goto done;
+	}
+
+	/*
+	 * Release was successful.
+	 */
+	*return_code = MPS_FW_DIAG_ERROR_SUCCESS;
+	status = MPS_DIAG_SUCCESS;
+
+	/*
+	 * If this was for an UNREGISTER diag type command, clear the unique ID.
+	 */
+	if (diag_type == MPS_FW_DIAG_TYPE_UNREGISTER) {
+		pBuffer->unique_id = MPS_FW_DIAG_INVALID_UID;
+	}
+
+done:
+	return (status);
+}
+
+static int
+mps_diag_register(struct mps_softc *sc, mps_fw_diag_register_t *diag_register,
+    uint32_t *return_code)
+{
+	mps_fw_diagnostic_buffer_t	*pBuffer;
+	uint8_t				extended_type, buffer_type, i;
+	uint32_t			buffer_size;
+	uint32_t			unique_id;
+	int				status;
+
+	extended_type = diag_register->ExtendedType;
+	buffer_type = diag_register->BufferType;
+	buffer_size = diag_register->RequestedBufferSize;
+	unique_id = diag_register->UniqueId;
+
+	/*
+	 * Check for valid buffer type
+	 */
+	if (buffer_type >= MPI2_DIAG_BUF_TYPE_COUNT) {
+		*return_code = MPS_FW_DIAG_ERROR_INVALID_PARAMETER;
+		return (MPS_DIAG_FAILURE);
+	}
+
+	/*
+	 * Get the current buffer and look up the unique ID.  The unique ID
+	 * should not be found.  If it is, the ID is already in use.
+	 */
+	i = mps_get_fw_diag_buffer_number(sc, unique_id);
+	pBuffer = &sc->fw_diag_buffer_list[buffer_type];
+	if (i != MPS_FW_DIAGNOSTIC_UID_NOT_FOUND) {
+		*return_code = MPS_FW_DIAG_ERROR_INVALID_UID;
+		return (MPS_DIAG_FAILURE);
+	}
+
+	/*
+	 * The buffer's unique ID should not be registered yet, and the given
+	 * unique ID cannot be 0.
+	 */
+	if ((pBuffer->unique_id != MPS_FW_DIAG_INVALID_UID) ||
+	    (unique_id == MPS_FW_DIAG_INVALID_UID)) {
+		*return_code = MPS_FW_DIAG_ERROR_INVALID_UID;
+		return (MPS_DIAG_FAILURE);
+	}
+
+	/*
+	 * If this buffer is already posted as immediate, just change owner.
+	 */
+	if (pBuffer->immediate && pBuffer->owned_by_firmware &&
+	    (pBuffer->unique_id == MPS_FW_DIAG_INVALID_UID)) {
+		pBuffer->immediate = FALSE;
+		pBuffer->unique_id = unique_id;
+		return (MPS_DIAG_SUCCESS);
+	}
+
+	/*
+	 * Post a new buffer after checking if it's enabled.  The DMA buffer
+	 * that is allocated will be contiguous (nsegments = 1).
+	 */
+	if (!pBuffer->enabled) {
+		*return_code = MPS_FW_DIAG_ERROR_NO_BUFFER;
+		return (MPS_DIAG_FAILURE);
+	}
+        if (bus_dma_tag_create( sc->mps_parent_dmat,    /* parent */
+				1, 0,			/* algnmnt, boundary */
+				BUS_SPACE_MAXADDR_32BIT,/* lowaddr */
+				BUS_SPACE_MAXADDR,	/* highaddr */
+				NULL, NULL,		/* filter, filterarg */
+                                buffer_size,		/* maxsize */
+                                1,			/* nsegments */
+                                buffer_size,		/* maxsegsize */
+                                0,			/* flags */
+                                NULL, NULL,		/* lockfunc, lockarg */
+                                &sc->fw_diag_dmat)) {
+		device_printf(sc->mps_dev, "Cannot allocate FW diag buffer DMA "
+		    "tag\n");
+		return (ENOMEM);
+        }
+        if (bus_dmamem_alloc(sc->fw_diag_dmat, (void **)&sc->fw_diag_buffer,
+	    BUS_DMA_NOWAIT, &sc->fw_diag_map)) {
+		device_printf(sc->mps_dev, "Cannot allocate FW diag buffer "
+		    "memory\n");
+		return (ENOMEM);
+        }
+        bzero(sc->fw_diag_buffer, buffer_size);
+        bus_dmamap_load(sc->fw_diag_dmat, sc->fw_diag_map, sc->fw_diag_buffer,
+	    buffer_size, mps_memaddr_cb, &sc->fw_diag_busaddr, 0);
+	pBuffer->size = buffer_size;
+
+	/*
+	 * Copy the given info to the diag buffer and post the buffer.
+	 */
+	pBuffer->buffer_type = buffer_type;
+	pBuffer->immediate = FALSE;
+	if (buffer_type == MPI2_DIAG_BUF_TYPE_TRACE) {
+		for (i = 0; i < (sizeof (pBuffer->product_specific) / 4);
+		    i++) {
+			pBuffer->product_specific[i] =
+			    diag_register->ProductSpecific[i];
+		}
+	}
+	pBuffer->extended_type = extended_type;
+	pBuffer->unique_id = unique_id;
+	status = mps_post_fw_diag_buffer(sc, pBuffer, return_code);
+
+	/*
+	 * In case there was a failure, free the DMA buffer.
+	 */
+	if (status == MPS_DIAG_FAILURE) {
+		if (sc->fw_diag_busaddr != 0)
+			bus_dmamap_unload(sc->fw_diag_dmat, sc->fw_diag_map);
+		if (sc->fw_diag_buffer != NULL)
+			bus_dmamem_free(sc->fw_diag_dmat, sc->fw_diag_buffer,
+			    sc->fw_diag_map);
+		if (sc->fw_diag_dmat != NULL)
+			bus_dma_tag_destroy(sc->fw_diag_dmat);
+	}
+
+	return (status);
+}
+
+static int
+mps_diag_unregister(struct mps_softc *sc,
+    mps_fw_diag_unregister_t *diag_unregister, uint32_t *return_code)
+{
+	mps_fw_diagnostic_buffer_t	*pBuffer;
+	uint8_t				i;
+	uint32_t			unique_id;
+	int				status;
+
+	unique_id = diag_unregister->UniqueId;
+
+	/*
+	 * Get the current buffer and look up the unique ID.  The unique ID
+	 * should be there.
+	 */
+	i = mps_get_fw_diag_buffer_number(sc, unique_id);
+	if (i == MPS_FW_DIAGNOSTIC_UID_NOT_FOUND) {
+		*return_code = MPS_FW_DIAG_ERROR_INVALID_UID;
+		return (MPS_DIAG_FAILURE);
+	}
+
+	pBuffer = &sc->fw_diag_buffer_list[i];
+
+	/*
+	 * Try to release the buffer from FW before freeing it.  If release
+	 * fails, don't free the DMA buffer in case FW tries to access it
+	 * later.  If buffer is not owned by firmware, can't release it.
+	 */
+	if (!pBuffer->owned_by_firmware) {
+		status = MPS_DIAG_SUCCESS;
+	} else {
+		status = mps_release_fw_diag_buffer(sc, pBuffer, return_code,
+		    MPS_FW_DIAG_TYPE_UNREGISTER);
+	}
+
+	/*
+	 * At this point, return the current status no matter what happens with
+	 * the DMA buffer.
+	 */
+	pBuffer->unique_id = MPS_FW_DIAG_INVALID_UID;
+	if (status == MPS_DIAG_SUCCESS) {
+		if (sc->fw_diag_busaddr != 0)
+			bus_dmamap_unload(sc->fw_diag_dmat, sc->fw_diag_map);
+		if (sc->fw_diag_buffer != NULL)
+			bus_dmamem_free(sc->fw_diag_dmat, sc->fw_diag_buffer,
+			    sc->fw_diag_map);
+		if (sc->fw_diag_dmat != NULL)
+			bus_dma_tag_destroy(sc->fw_diag_dmat);
+	}
+
+	return (status);
+}
+
+static int
+mps_diag_query(struct mps_softc *sc, mps_fw_diag_query_t *diag_query,
+    uint32_t *return_code)
+{
+	mps_fw_diagnostic_buffer_t	*pBuffer;
+	uint8_t				i;
+	uint32_t			unique_id;
+
+	unique_id = diag_query->UniqueId;
+
+	/*
+	 * If ID is valid, query on ID.
+	 * If ID is invalid, query on buffer type.
+	 */
+	if (unique_id == MPS_FW_DIAG_INVALID_UID) {
+		i = diag_query->BufferType;
+		if (i >= MPI2_DIAG_BUF_TYPE_COUNT) {
+			*return_code = MPS_FW_DIAG_ERROR_INVALID_UID;
+			return (MPS_DIAG_FAILURE);
+		}
+	} else {
+		i = mps_get_fw_diag_buffer_number(sc, unique_id);
+		if (i == MPS_FW_DIAGNOSTIC_UID_NOT_FOUND) {
+			*return_code = MPS_FW_DIAG_ERROR_INVALID_UID;
+			return (MPS_DIAG_FAILURE);
+		}
+	}
+
+	/*
+	 * Fill query structure with the diag buffer info.
+	 */
+	pBuffer = &sc->fw_diag_buffer_list[i];
+	diag_query->BufferType = pBuffer->buffer_type;
+	diag_query->ExtendedType = pBuffer->extended_type;
+	if (diag_query->BufferType == MPI2_DIAG_BUF_TYPE_TRACE) {
+		for (i = 0; i < (sizeof(diag_query->ProductSpecific) / 4);
+		    i++) {
+			diag_query->ProductSpecific[i] =
+			    pBuffer->product_specific[i];
+		}
+	}
+	diag_query->TotalBufferSize = pBuffer->size;
+	diag_query->DriverAddedBufferSize = 0;
+	diag_query->UniqueId = pBuffer->unique_id;
+	diag_query->ApplicationFlags = 0;
+	diag_query->DiagnosticFlags = 0;
+
+	/*
+	 * Set/Clear application flags
+	 */
+	if (pBuffer->immediate) {
+		diag_query->ApplicationFlags &= ~MPS_FW_DIAG_FLAG_APP_OWNED;
+	} else {
+		diag_query->ApplicationFlags |= MPS_FW_DIAG_FLAG_APP_OWNED;
+	}
+	if (pBuffer->valid_data || pBuffer->owned_by_firmware) {
+		diag_query->ApplicationFlags |= MPS_FW_DIAG_FLAG_BUFFER_VALID;
+	} else {
+		diag_query->ApplicationFlags &= ~MPS_FW_DIAG_FLAG_BUFFER_VALID;
+	}
+	if (pBuffer->owned_by_firmware) {
+		diag_query->ApplicationFlags |=
+		    MPS_FW_DIAG_FLAG_FW_BUFFER_ACCESS;
+	} else {
+		diag_query->ApplicationFlags &=
+		    ~MPS_FW_DIAG_FLAG_FW_BUFFER_ACCESS;
+	}
+
+	return (MPS_DIAG_SUCCESS);
+}
+
+static int
+mps_diag_read_buffer(struct mps_softc *sc,
+    mps_diag_read_buffer_t *diag_read_buffer, uint8_t *ioctl_buf,
+    uint32_t *return_code)
+{
+	mps_fw_diagnostic_buffer_t	*pBuffer;
+	uint8_t				i, *pData;
+	uint32_t			unique_id;
+	int				status;
+
+	unique_id = diag_read_buffer->UniqueId;
+
+	/*
+	 * Get the current buffer and look up the unique ID.  The unique ID
+	 * should be there.
+	 */
+	i = mps_get_fw_diag_buffer_number(sc, unique_id);
+	if (i == MPS_FW_DIAGNOSTIC_UID_NOT_FOUND) {
+		*return_code = MPS_FW_DIAG_ERROR_INVALID_UID;
+		return (MPS_DIAG_FAILURE);
+	}
+
+	pBuffer = &sc->fw_diag_buffer_list[i];
+
+	/*
+	 * Make sure requested read is within limits
+	 */
+	if (diag_read_buffer->StartingOffset + diag_read_buffer->BytesToRead >
+	    pBuffer->size) {
+		*return_code = MPS_FW_DIAG_ERROR_INVALID_PARAMETER;
+		return (MPS_DIAG_FAILURE);
+	}
+
+	/*
+	 * Copy the requested data from DMA to the diag_read_buffer.  The DMA
+	 * buffer that was allocated is one contiguous buffer.
+	 */
+	pData = (uint8_t *)(sc->fw_diag_buffer +
+	    diag_read_buffer->StartingOffset);
+	if (copyout(pData, ioctl_buf, diag_read_buffer->BytesToRead) != 0)
+		return (MPS_DIAG_FAILURE);
+	diag_read_buffer->Status = 0;
+
+	/*
+	 * Set or clear the Force Release flag.
+	 */
+	if (pBuffer->force_release) {
+		diag_read_buffer->Flags |= MPS_FW_DIAG_FLAG_FORCE_RELEASE;
+	} else {
+		diag_read_buffer->Flags &= ~MPS_FW_DIAG_FLAG_FORCE_RELEASE;
+	}
+
+	/*
+	 * If buffer is to be reregistered, make sure it's not already owned by
+	 * firmware first.
+	 */
+	status = MPS_DIAG_SUCCESS;
+	if (!pBuffer->owned_by_firmware) {
+		if (diag_read_buffer->Flags & MPS_FW_DIAG_FLAG_REREGISTER) {
+			status = mps_post_fw_diag_buffer(sc, pBuffer,
+			    return_code);
+		}
+	}
+
+	return (status);
+}
+
+static int
+mps_diag_release(struct mps_softc *sc, mps_fw_diag_release_t *diag_release,
+    uint32_t *return_code)
+{
+	mps_fw_diagnostic_buffer_t	*pBuffer;
+	uint8_t				i;
+	uint32_t			unique_id;
+	int				status;
+
+	unique_id = diag_release->UniqueId;
+
+	/*
+	 * Get the current buffer and look up the unique ID.  The unique ID
+	 * should be there.
+	 */
+	i = mps_get_fw_diag_buffer_number(sc, unique_id);
+	if (i == MPS_FW_DIAGNOSTIC_UID_NOT_FOUND) {
+		*return_code = MPS_FW_DIAG_ERROR_INVALID_UID;
+		return (MPS_DIAG_FAILURE);
+	}
+
+	pBuffer = &sc->fw_diag_buffer_list[i];
+
+	/*
+	 * If buffer is not owned by firmware, it's already been released.
+	 */
+	if (!pBuffer->owned_by_firmware) {
+		*return_code = MPS_FW_DIAG_ERROR_ALREADY_RELEASED;
+		return (MPS_DIAG_FAILURE);
+	}
+
+	/*
+	 * Release the buffer.
+	 */
+	status = mps_release_fw_diag_buffer(sc, pBuffer, return_code,
+	    MPS_FW_DIAG_TYPE_RELEASE);
+	return (status);
+}
+
+static int
+mps_do_diag_action(struct mps_softc *sc, uint32_t action, uint8_t *diag_action,
+    uint32_t length, uint32_t *return_code)
+{
+	mps_fw_diag_register_t		diag_register;
+	mps_fw_diag_unregister_t	diag_unregister;
+	mps_fw_diag_query_t		diag_query;
+	mps_diag_read_buffer_t		diag_read_buffer;
+	mps_fw_diag_release_t		diag_release;
+	int				status = MPS_DIAG_SUCCESS;
+	uint32_t			original_return_code;
+
+	original_return_code = *return_code;
+	*return_code = MPS_FW_DIAG_ERROR_SUCCESS;
+
+	switch (action) {
+		case MPS_FW_DIAG_TYPE_REGISTER:
+			if (!length) {
+				*return_code =
+				    MPS_FW_DIAG_ERROR_INVALID_PARAMETER;
+				status = MPS_DIAG_FAILURE;
+				break;
+			}
+			if (copyin(diag_action, &diag_register,
+			    sizeof(diag_register)) != 0)
+				return (MPS_DIAG_FAILURE);
+			status = mps_diag_register(sc, &diag_register,
+			    return_code);
+			break;
+
+		case MPS_FW_DIAG_TYPE_UNREGISTER:
+			if (length < sizeof(diag_unregister)) {
+				*return_code =
+				    MPS_FW_DIAG_ERROR_INVALID_PARAMETER;
+				status = MPS_DIAG_FAILURE;
+				break;
+			}
+			if (copyin(diag_action, &diag_unregister,
+			    sizeof(diag_unregister)) != 0)
+				return (MPS_DIAG_FAILURE);
+			status = mps_diag_unregister(sc, &diag_unregister,
+			    return_code);
+			break;
+
+		case MPS_FW_DIAG_TYPE_QUERY:
+			if (length < sizeof (diag_query)) {
+				*return_code =
+				    MPS_FW_DIAG_ERROR_INVALID_PARAMETER;
+				status = MPS_DIAG_FAILURE;
+				break;
+			}
+			if (copyin(diag_action, &diag_query, sizeof(diag_query))
+			    != 0)
+				return (MPS_DIAG_FAILURE);
+			status = mps_diag_query(sc, &diag_query, return_code);
+			if (status == MPS_DIAG_SUCCESS)
+				if (copyout(&diag_query, diag_action,
+				    sizeof (diag_query)) != 0)
+					return (MPS_DIAG_FAILURE);
+			break;
+
+		case MPS_FW_DIAG_TYPE_READ_BUFFER:
+			if (copyin(diag_action, &diag_read_buffer,
+			    sizeof(diag_read_buffer)) != 0)
+				return (MPS_DIAG_FAILURE);
+			if (length < diag_read_buffer.BytesToRead) {
+				*return_code =
+				    MPS_FW_DIAG_ERROR_INVALID_PARAMETER;
+				status = MPS_DIAG_FAILURE;
+				break;
+			}
+			status = mps_diag_read_buffer(sc, &diag_read_buffer,
+			    PTRIN(diag_read_buffer.PtrDataBuffer),
+			    return_code);
+			if (status == MPS_DIAG_SUCCESS) {
+				if (copyout(&diag_read_buffer, diag_action,
+				    sizeof(diag_read_buffer) -
+				    sizeof(diag_read_buffer.PtrDataBuffer)) !=
+				    0)
+					return (MPS_DIAG_FAILURE);
+			}
+			break;
+
+		case MPS_FW_DIAG_TYPE_RELEASE:
+			if (length < sizeof(diag_release)) {
+				*return_code =
+				    MPS_FW_DIAG_ERROR_INVALID_PARAMETER;
+				status = MPS_DIAG_FAILURE;
+				break;
+			}
+			if (copyin(diag_action, &diag_release,
+			    sizeof(diag_release)) != 0)
+				return (MPS_DIAG_FAILURE);
+			status = mps_diag_release(sc, &diag_release,
+			    return_code);
+			break;
+
+		default:
+			*return_code = MPS_FW_DIAG_ERROR_INVALID_PARAMETER;
+			status = MPS_DIAG_FAILURE;
+			break;
+	}
+
+	if ((status == MPS_DIAG_FAILURE) &&
+	    (original_return_code == MPS_FW_DIAG_NEW) &&
+	    (*return_code != MPS_FW_DIAG_ERROR_SUCCESS))
+		status = MPS_DIAG_SUCCESS;
+
+	return (status);
+}
+
+static int
+mps_user_diag_action(struct mps_softc *sc, mps_diag_action_t *data)
+{
+	int			status;
+
+	/*
+	 * Only allow one diag action at one time.
+	 */
+	if (sc->mps_flags & MPS_FLAGS_BUSY) {
+		mps_dprint(sc, MPS_INFO, "%s: Only one FW diag command "
+		    "allowed at a single time.", __func__);
+		return (EBUSY);
+	}
+	sc->mps_flags |= MPS_FLAGS_BUSY;
+
+	/*
+	 * Send diag action request
+	 */
+	if (data->Action == MPS_FW_DIAG_TYPE_REGISTER ||
+	    data->Action == MPS_FW_DIAG_TYPE_UNREGISTER ||
+	    data->Action == MPS_FW_DIAG_TYPE_QUERY ||
+	    data->Action == MPS_FW_DIAG_TYPE_READ_BUFFER ||
+	    data->Action == MPS_FW_DIAG_TYPE_RELEASE) {
+		status = mps_do_diag_action(sc, data->Action,
+		    PTRIN(data->PtrDiagAction), data->Length,
+		    &data->ReturnCode);
+	} else
+		status = EINVAL;
+
+	sc->mps_flags &= ~MPS_FLAGS_BUSY;
+	return (status);
+}
+
+/*
+ * Copy the event recording mask and the event queue size out.  For
+ * clarification, the event recording mask (events_to_record) is not the same
+ * thing as the event mask (event_mask).  events_to_record has a bit set for
+ * every event type that is to be recorded by the driver, and event_mask has a
+ * bit cleared for every event that is allowed into the driver from the IOC.
+ * They really have nothing to do with each other.
+ */
+static void
+mps_user_event_query(struct mps_softc *sc, mps_event_query_t *data)
+{
+	uint8_t	i;
+
+	mps_lock(sc);
+	data->Entries = MPS_EVENT_QUEUE_SIZE;
+
+	for (i = 0; i < 4; i++) {
+		data->Types[i] = sc->events_to_record[i];
+	}
+	mps_unlock(sc);
+}
+
+/*
+ * Set the driver's event mask according to what's been given.  See
+ * mps_user_event_query for explanation of the event recording mask and the IOC
+ * event mask.  It's the app's responsibility to enable event logging by setting
+ * the bits in events_to_record.  Initially, no events will be logged.
+ */
+static void
+mps_user_event_enable(struct mps_softc *sc, mps_event_enable_t *data)
+{
+	uint8_t	i;
+
+	mps_lock(sc);
+	for (i = 0; i < 4; i++) {
+		sc->events_to_record[i] = data->Types[i];
+	}
+	mps_unlock(sc);
+}
+
+/*
+ * Copy out the events that have been recorded, up to the max events allowed.
+ */
+static int
+mps_user_event_report(struct mps_softc *sc, mps_event_report_t *data)
+{
+	int		status = 0;
+	uint32_t	size;
+
+	mps_lock(sc);
+	size = data->Size;
+	if ((size >= sizeof(sc->recorded_events)) && (status == 0)) {
+		mps_unlock(sc);
+		if (copyout((void *)sc->recorded_events,
+		    PTRIN(data->PtrEvents), size) != 0)
+			status = EFAULT;
+		mps_lock(sc);
+	} else {
+		/*
+		 * data->Size value is not large enough to copy event data.
+		 */
+		status = EFAULT;
+	}
+
+	/*
+	 * Change size value to match the number of bytes that were copied.
+	 */
+	if (status == 0)
+		data->Size = sizeof(sc->recorded_events);
+	mps_unlock(sc);
+
+	return (status);
+}
+
+/*
+ * Record events into the driver from the IOC if they are not masked.
+ */
+void
+mpssas_record_event(struct mps_softc *sc,
+    MPI2_EVENT_NOTIFICATION_REPLY *event_reply)
+{
+	uint32_t	event;
+	int		i, j;
+	uint16_t	event_data_len;
+	boolean_t	sendAEN = FALSE;
+
+	event = event_reply->Event;
+
+	/*
+	 * Generate a system event to let anyone who cares know that a
+	 * LOG_ENTRY_ADDED event has occurred.  This is sent no matter what the
+	 * event mask is set to.
+	 */
+	if (event == MPI2_EVENT_LOG_ENTRY_ADDED) {
+		sendAEN = TRUE;
+	}
+
+	/*
+	 * Record the event only if its corresponding bit is set in
+	 * events_to_record.  event_index is the index into recorded_events and
+	 * event_number is the overall number of an event being recorded since
+	 * start-of-day.  event_index will roll over; event_number will never
+	 * roll over.
+	 */
+	i = (uint8_t)(event / 32);
+	j = (uint8_t)(event % 32);
+	if ((i < 4) && ((1 << j) & sc->events_to_record[i])) {
+		i = sc->event_index;
+		sc->recorded_events[i].Type = event;
+		sc->recorded_events[i].Number = ++sc->event_number;
+		bzero(sc->recorded_events[i].Data, MPS_MAX_EVENT_DATA_LENGTH *
+		    4);
+		event_data_len = event_reply->EventDataLength;
+
+		if (event_data_len > 0) {
+			/*
+			 * Limit data to size in m_event entry
+			 */
+			if (event_data_len > MPS_MAX_EVENT_DATA_LENGTH) {
+				event_data_len = MPS_MAX_EVENT_DATA_LENGTH;
+			}
+			for (j = 0; j < event_data_len; j++) {
+				sc->recorded_events[i].Data[j] =
+				    event_reply->EventData[j];
+			}
+
+			/*
+			 * check for index wrap-around
+			 */
+			if (++i == MPS_EVENT_QUEUE_SIZE) {
+				i = 0;
+			}
+			sc->event_index = (uint8_t)i;
+
+			/*
+			 * Set flag to send the event.
+			 */
+			sendAEN = TRUE;
+		}
+	}
+
+	/*
+	 * Generate a system event if flag is set to let anyone who cares know
+	 * that an event has occurred.
+	 */
+	if (sendAEN) {
+//SLM-how to send a system event (see kqueue, kevent)
+//		(void) ddi_log_sysevent(mpt->m_dip, DDI_VENDOR_LSI, "MPT_SAS",
+//		    "SAS", NULL, NULL, DDI_NOSLEEP);
+	}
+}
+
+static int
+mps_user_reg_access(struct mps_softc *sc, mps_reg_access_t *data)
+{
+	int	status = 0;
+
+	switch (data->Command) {
+		/*
+		 * IO access is not supported.
+		 */
+		case REG_IO_READ:
+		case REG_IO_WRITE:
+			mps_dprint(sc, MPS_INFO, "IO access is not supported. "
+			    "Use memory access.");
+			status = EINVAL;
+			break;
+
+		case REG_MEM_READ:
+			data->RegData = mps_regread(sc, data->RegOffset);
+			break;
+
+		case REG_MEM_WRITE:
+			mps_regwrite(sc, data->RegOffset, data->RegData);
+			break;
+
+		default:
+			status = EINVAL;
+			break;
+	}
+
+	return (status);
+}
+
+static int
+mps_user_btdh(struct mps_softc *sc, mps_btdh_mapping_t *data)
+{
+	uint8_t		bt2dh = FALSE;
+	uint8_t		dh2bt = FALSE;
+	uint16_t	dev_handle, bus, target;
+
+	bus = data->Bus;
+	target = data->TargetID;
+	dev_handle = data->DevHandle;
+
+	/*
+	 * When DevHandle is 0xFFFF and Bus/Target are not 0xFFFF, use Bus/
+	 * Target to get DevHandle.  When Bus/Target are 0xFFFF and DevHandle is
+	 * not 0xFFFF, use DevHandle to get Bus/Target.  Anything else is
+	 * invalid.
+	 */
+	if ((bus == 0xFFFF) && (target == 0xFFFF) && (dev_handle != 0xFFFF))
+		dh2bt = TRUE;
+	if ((dev_handle == 0xFFFF) && (bus != 0xFFFF) && (target != 0xFFFF))
+		bt2dh = TRUE;
+	if (!dh2bt && !bt2dh)
+		return (EINVAL);
+
+	/*
+	 * Only handle bus of 0.  Make sure target is within range.
+	 */
+	if (bt2dh) {
+		if (bus != 0)
+			return (EINVAL);
+
+		if (target > sc->max_devices) {
+			mps_dprint(sc, MPS_FAULT, "Target ID is out of range "
+			   "for Bus/Target to DevHandle mapping.");
+			return (EINVAL);
+		}
+		dev_handle = sc->mapping_table[target].dev_handle;
+		if (dev_handle)
+			data->DevHandle = dev_handle;
+	} else {
+		bus = 0;
+		target = mps_mapping_get_sas_id_from_handle(sc, dev_handle);
+		data->Bus = bus;
+		data->TargetID = target;
+	}
+
+	return (0);
+}
 
 static int
 mps_ioctl(struct cdev *dev, u_long cmd, void *arg, int flag,
@@ -674,7 +2051,7 @@
 	struct mps_cfg_page_req *page_req;
 	struct mps_ext_cfg_page_req *ext_page_req;
 	void *mps_page;
-	int error;
+	int error, reset_loop;
 
 	mps_page = NULL;
 	sc = dev->si_drv1;
@@ -730,6 +2107,98 @@
 	case MPSIO_MPS_COMMAND:
 		error = mps_user_command(sc, (struct mps_usr_command *)arg);
 		break;
+	case MPTIOCTL_PASS_THRU:
+		/*
+		 * The user has requested to pass through a command to be
+		 * executed by the MPT firmware.  Call our routine which does
+		 * this.  Only allow one passthru IOCTL at one time.
+		 */
+		error = mps_user_pass_thru(sc, (mps_pass_thru_t *)arg);
+		break;
+	case MPTIOCTL_GET_ADAPTER_DATA:
+		/*
+		 * The user has requested to read adapter data.  Call our
+		 * routine which does this.
+		 */
+		error = 0;
+		mps_user_get_adapter_data(sc, (mps_adapter_data_t *)arg);
+		break;
+	case MPTIOCTL_GET_PCI_INFO:
+		/*
+		 * The user has requested to read pci info.  Call
+		 * our routine which does this.
+		 */
+		mps_lock(sc);
+		error = 0;
+		mps_user_read_pci_info(sc, (mps_pci_info_t *)arg);
+		mps_unlock(sc);
+		break;
+	case MPTIOCTL_RESET_ADAPTER:
+		mps_lock(sc);
+		sc->port_enable_complete = 0;
+		error = mps_reinit(sc);
+		mps_unlock(sc);
+		/*
+		 * Wait no more than 5 minutes for Port Enable to complete
+		 */
+		for (reset_loop = 0; (reset_loop < MPS_DIAG_RESET_TIMEOUT) &&
+		    (!sc->port_enable_complete); reset_loop++) {
+			DELAY(1000);
+		}
+		if (reset_loop == MPS_DIAG_RESET_TIMEOUT) {
+			printf("Port Enable did not complete after Diag "
+			    "Reset.\n");
+		}
+		break;
+	case MPTIOCTL_DIAG_ACTION:
+		/*
+		 * The user has done a diag buffer action.  Call our routine
+		 * which does this.  Only allow one diag action at one time.
+		 */
+		mps_lock(sc);
+		error = mps_user_diag_action(sc, (mps_diag_action_t *)arg);
+		mps_unlock(sc);
+		break;
+	case MPTIOCTL_EVENT_QUERY:
+		/*
+		 * The user has done an event query. Call our routine which does
+		 * this.
+		 */
+		error = 0;
+		mps_user_event_query(sc, (mps_event_query_t *)arg);
+		break;
+	case MPTIOCTL_EVENT_ENABLE:
+		/*
+		 * The user has done an event enable. Call our routine which
+		 * does this.
+		 */
+		error = 0;
+		mps_user_event_enable(sc, (mps_event_enable_t *)arg);
+		break;
+	case MPTIOCTL_EVENT_REPORT:
+		/*
+		 * The user has done an event report. Call our routine which
+		 * does this.
+		 */
+		error = mps_user_event_report(sc, (mps_event_report_t *)arg);
+		break;
+	case MPTIOCTL_REG_ACCESS:
+		/*
+		 * The user has requested register access.  Call our routine
+		 * which does this.
+		 */
+		mps_lock(sc);
+		error = mps_user_reg_access(sc, (mps_reg_access_t *)arg);
+		mps_unlock(sc);
+		break;
+	case MPTIOCTL_BTDH_MAPPING:
+		/*
+		 * The user has requested to translate a bus/target to a
+		 * DevHandle or a DevHandle to a bus/target.  Call our routine
+		 * which does this.
+		 */
+		error = mps_user_btdh(sc, (mps_btdh_mapping_t *)arg);
+		break;
 	default:
 		error = ENOIOCTL;
 		break;
@@ -743,16 +2212,6 @@
 
 #ifdef COMPAT_FREEBSD32
 
-/* Macros from compat/freebsd32/freebsd32.h */
-#define	PTRIN(v)	(void *)(uintptr_t)(v)
-#define	PTROUT(v)	(uint32_t)(uintptr_t)(v)
-
-#define	CP(src,dst,fld) do { (dst).fld = (src).fld; } while (0)
-#define	PTRIN_CP(src,dst,fld)				\
-	do { (dst).fld = PTRIN((src).fld); } while (0)
-#define	PTROUT_CP(src,dst,fld) \
-	do { (dst).fld = PTROUT((src).fld); } while (0)
-
 struct mps_cfg_page_req32 {
 	MPI2_CONFIG_PAGE_HEADER header;
 	uint32_t page_address;
diff -r 52ad4c882614 -r 6b8317170ea4 head/sys/dev/mps/mpsvar.h
--- a/head/sys/dev/mps/mpsvar.h	Wed Feb 01 11:28:01 2012 +0200
+++ b/head/sys/dev/mps/mpsvar.h	Wed Feb 01 11:28:20 2012 +0200
@@ -22,13 +22,44 @@
  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  * SUCH DAMAGE.
- * 
- * $FreeBSD$
+ *
+ * $FreeBSD: head/sys/dev/mps/mpsvar.h 230592 2012-01-26 18:17:21Z ken $
+ */
+/*-
+ * Copyright (c) 2011 LSI Corp.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * 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 OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * LSI MPT-Fusion Host Adapter FreeBSD
+ *
+ * $FreeBSD: head/sys/dev/mps/mpsvar.h 230592 2012-01-26 18:17:21Z ken $
  */
 
 #ifndef _MPSVAR_H
 #define _MPSVAR_H
 
+#define MPS_DRIVER_VERSION	"11.255.03.00-fbsd"
+
 #define MPS_DB_MAX_WAIT		2500
 
 #define MPS_REQ_FRAMES		1024
@@ -41,12 +72,137 @@
 #define MPS_SGE32_SIZE		8
 #define MPS_SGC_SIZE		8
 
+#define	 CAN_SLEEP			1
+#define  NO_SLEEP			0
+
 #define MPS_PERIODIC_DELAY	1	/* 1 second heartbeat/watchdog check */
 
+#define MPS_SCSI_RI_INVALID_FRAME	(0x00000002)
+
+/*
+ * host mapping related macro definitions
+ */
+#define MPS_MAPTABLE_BAD_IDX	0xFFFFFFFF
+#define MPS_DPM_BAD_IDX		0xFFFF
+#define MPS_ENCTABLE_BAD_IDX	0xFF
+#define MPS_MAX_MISSING_COUNT	0x0F
+#define MPS_DEV_RESERVED	0x20000000
+#define MPS_MAP_IN_USE		0x10000000
+#define MPS_RAID_CHANNEL	1
+#define MPS_MAP_BAD_ID		0xFFFFFFFF
+
+/*
+ * WarpDrive controller
+ */
+#define	MPS_CHIP_WD_DEVICE_ID	0x007E
+#define	MPS_WD_LSI_OEM		0x80
+#define	MPS_WD_HIDE_EXPOSE_MASK	0x03
+#define	MPS_WD_HIDE_ALWAYS	0x00
+#define	MPS_WD_EXPOSE_ALWAYS	0x01
+#define	MPS_WD_HIDE_IF_VOLUME	0x02
+#define	MPS_WD_RETRY		0x01
+#define	MPS_MAN_PAGE10_SIZE	0x5C	/* Hardcode for now */
+#define MPS_MAX_DISKS_IN_VOL	10
+
+/*
+ * WarpDrive Event Logging
+ */
+#define	MPI2_WD_LOG_ENTRY	0x8002
+#define	MPI2_WD_SSD_THROTTLING	0x0041
+#define	MPI2_WD_DRIVE_LIFE_WARN	0x0043
+#define	MPI2_WD_DRIVE_LIFE_DEAD	0x0044
+#define	MPI2_WD_RAIL_MON_FAIL	0x004D
+
+typedef uint8_t u8;
+typedef uint16_t u16;
+typedef uint32_t u32;
+typedef uint64_t u64;
+
+/**
+ * struct dev_mapping_table - device mapping information
+ * @physical_id: SAS address for drives or WWID for RAID volumes
+ * @device_info: bitfield provides detailed info about the device
+ * @phy_bits: bitfields indicating controller phys
+ * @dpm_entry_num: index of this device in device persistent map table
+ * @dev_handle: device handle for the device pointed by this entry
+ * @channel: target channel
+ * @id: target id
+ * @missing_count: number of times the device not detected by driver
+ * @hide_flag: Hide this physical disk/not (foreign configuration)
+ * @init_complete: Whether the start of the day checks completed or not
+ */
+struct dev_mapping_table {
+	u64	physical_id;
+	u32	device_info;
+	u32	phy_bits;
+	u16	dpm_entry_num;
+	u16	dev_handle;
+	u8	reserved1;
+	u8	channel;
+	u16	id;
+	u8	missing_count;
+	u8	init_complete;
+	u8	TLR_bits;
+	u8	reserved2;
+};
+
+/**
+ * struct enc_mapping_table -  mapping information about an enclosure
+ * @enclosure_id: Logical ID of this enclosure
+ * @start_index: index to the entry in dev_mapping_table
+ * @phy_bits: bitfields indicating controller phys
+ * @dpm_entry_num: index of this enclosure in device persistent map table
+ * @enc_handle: device handle for the enclosure pointed by this entry
+ * @num_slots: number of slots in the enclosure
+ * @start_slot: Starting slot id
+ * @missing_count: number of times the device not detected by driver
+ * @removal_flag: used to mark the device for removal
+ * @skip_search: used as a flag to include/exclude enclosure for search
+ * @init_complete: Whether the start of the day checks completed or not
+ */
+struct enc_mapping_table {
+	u64	enclosure_id;
+	u32	start_index;
+	u32	phy_bits;
+	u16	dpm_entry_num;
+	u16	enc_handle;
+	u16	num_slots;
+	u16	start_slot;
+	u8	missing_count;
+	u8	removal_flag;
+	u8	skip_search;
+	u8	init_complete;
+};
+
+/**
+ * struct map_removal_table - entries to be removed from mapping table
+ * @dpm_entry_num: index of this device in device persistent map table
+ * @dev_handle: device handle for the device pointed by this entry
+ */
+struct map_removal_table{
+	u16	dpm_entry_num;
+	u16	dev_handle;
+};
+
+typedef struct mps_fw_diagnostic_buffer {
+	size_t		size;
+	uint8_t		extended_type;
+	uint8_t		buffer_type;
+	uint8_t		force_release;
+	uint32_t	product_specific[23];
+	uint8_t		immediate;
+	uint8_t		enabled;
+	uint8_t		valid_data;
+	uint8_t		owned_by_firmware;
+	uint32_t	unique_id;
+} mps_fw_diagnostic_buffer_t;
+
 struct mps_softc;
 struct mps_command;
 struct mpssas_softc;
+union ccb;
 struct mpssas_target;
+struct mps_column_map;
 
 MALLOC_DECLARE(M_MPT2);
 
@@ -63,13 +219,16 @@
 /*
  * This needs to be at least 2 to support SMP passthrough.
  */
-#define	MPS_IOVEC_COUNT	2
+#define       MPS_IOVEC_COUNT 2
 
 struct mps_command {
 	TAILQ_ENTRY(mps_command)	cm_link;
+	TAILQ_ENTRY(mps_command)	cm_recovery;
 	struct mps_softc		*cm_sc;
+	union ccb			*cm_ccb;
 	void				*cm_data;
 	u_int				cm_length;
+	u_int				cm_out_len;
 	struct uio			cm_uio;
 	struct iovec			cm_iovec[MPS_IOVEC_COUNT];
 	u_int				cm_max_segs;
@@ -82,6 +241,7 @@
 	void				*cm_complete_data;
 	struct mpssas_target		*cm_targ;
 	MPI2_REQUEST_DESCRIPTOR_UNION	cm_desc;
+	u_int	                	cm_lun;
 	u_int				cm_flags;
 #define MPS_CM_FLAGS_POLLED		(1 << 0)
 #define MPS_CM_FLAGS_COMPLETE		(1 << 1)
@@ -89,7 +249,7 @@
 #define MPS_CM_FLAGS_DATAOUT		(1 << 3)
 #define MPS_CM_FLAGS_DATAIN		(1 << 4)
 #define MPS_CM_FLAGS_WAKEUP		(1 << 5)
-#define MPS_CM_FLAGS_ACTIVE		(1 << 6)
+#define MPS_CM_FLAGS_DD_IO		(1 << 6)
 #define MPS_CM_FLAGS_USE_UIO		(1 << 7)
 #define MPS_CM_FLAGS_SMP_PASS		(1 << 8)
 #define	MPS_CM_FLAGS_CHAIN_FAILED	(1 << 9)
@@ -106,6 +266,11 @@
 	struct callout			cm_callout;
 };
 
+struct mps_column_map {
+	uint16_t			dev_handle;
+	uint8_t				phys_disk_num;
+};
+
 struct mps_event_handle {
 	TAILQ_ENTRY(mps_event_handle)	eh_list;
 	mps_evt_callback_t		*callback;
@@ -121,17 +286,24 @@
 #define MPS_FLAGS_MSI		(1 << 1)
 #define MPS_FLAGS_BUSY		(1 << 2)
 #define MPS_FLAGS_SHUTDOWN	(1 << 3)
-#define	MPS_FLAGS_ATTACH_DONE	(1 << 4)
+#define MPS_FLAGS_DIAGRESET	(1 << 4)
+#define	MPS_FLAGS_ATTACH_DONE	(1 << 5)
+#define	MPS_FLAGS_WD_AVAILABLE	(1 << 6)
 	u_int				mps_debug;
-	u_int				allow_multiple_tm_cmds;
+	u_int				disable_msix;
+	u_int				disable_msi;
 	int				tm_cmds_active;
 	int				io_cmds_active;
 	int				io_cmds_highwater;
 	int				chain_free;
+	int				max_chains;
 	int				chain_free_lowwater;
+#if __FreeBSD_version >= 900030
 	uint64_t			chain_alloc_fail;
+#endif
 	struct sysctl_ctx_list		sysctl_ctx;
 	struct sysctl_oid		*sysctl_tree;
+	char                            fw_version[16];
 	struct mps_command		*commands;
 	struct mps_chain		*chains;
 	struct callout			periodic;
@@ -139,9 +311,9 @@
 	struct mpssas_softc		*sassc;
 
 	TAILQ_HEAD(, mps_command)	req_list;
+	TAILQ_HEAD(, mps_command)	high_priority_req_list;
 	TAILQ_HEAD(, mps_chain)		chain_list;
 	TAILQ_HEAD(, mps_command)	tm_list;
-	TAILQ_HEAD(, mps_command)	io_list;
 	int				replypostindex;
 	int				replyfreeindex;
 
@@ -196,6 +368,73 @@
 	bus_addr_t			free_busaddr;
 	bus_dma_tag_t			queues_dmat;
 	bus_dmamap_t			queues_map;
+
+	uint8_t				*fw_diag_buffer;
+	bus_addr_t			fw_diag_busaddr;
+	bus_dma_tag_t			fw_diag_dmat;
+	bus_dmamap_t			fw_diag_map;
+
+	uint8_t				ir_firmware;
+
+	/* static config pages */
+	Mpi2IOCPage8_t			ioc_pg8;
+
+	/* host mapping support */
+	struct dev_mapping_table	*mapping_table;
+	struct enc_mapping_table	*enclosure_table;
+	struct map_removal_table	*removal_table;
+	uint8_t				*dpm_entry_used;
+	uint8_t				*dpm_flush_entry;
+	Mpi2DriverMappingPage0_t	*dpm_pg0;
+	uint16_t			max_devices;
+	uint16_t			max_enclosures;
+	uint16_t			max_expanders;
+	uint8_t				max_volumes;
+	uint8_t				num_enc_table_entries;
+	uint8_t				num_rsvd_entries;
+	uint8_t				num_channels;
+	uint16_t			max_dpm_entries;
+	uint8_t				is_dpm_enable;
+	uint8_t				track_mapping_events;
+	uint32_t			pending_map_events;
+	uint8_t				mt_full_retry;
+	uint8_t				mt_add_device_failed;
+
+	/* FW diag Buffer List */
+	mps_fw_diagnostic_buffer_t
+				fw_diag_buffer_list[MPI2_DIAG_BUF_TYPE_COUNT];
+
+	/* Event Recording IOCTL support */
+	uint32_t			events_to_record[4];
+	mps_event_entry_t		recorded_events[MPS_EVENT_QUEUE_SIZE];
+	uint8_t				event_index;
+	uint32_t			event_number;
+
+	/* EEDP and TLR support */
+	uint8_t				eedp_enabled;
+	uint8_t				control_TLR;
+
+	/* Shutdown Event Handler */
+	eventhandler_tag		shutdown_eh;
+
+	/* To track topo events during reset */
+#define	MPS_DIAG_RESET_TIMEOUT	300000
+	uint8_t				wait_for_port_enable;
+	uint8_t				port_enable_complete;
+
+	/* WD controller */
+	uint8_t				WD_valid_config;
+	uint8_t				WD_hide_expose;
+
+	/* Direct Drive for WarpDrive */
+	uint8_t				DD_num_phys_disks;
+	uint16_t			DD_dev_handle;
+	uint32_t			DD_stripe_size;
+	uint32_t			DD_stripe_exponent;
+	uint32_t			DD_block_size;
+	uint16_t			DD_block_exponent;
+	uint64_t			DD_max_lba;
+	struct mps_column_map		DD_column_map[MPS_MAX_DISKS_IN_VOL];
 };
 
 struct mps_config_params {
@@ -210,6 +449,13 @@
 	void		*cbdata;
 };
 
+struct scsi_read_capacity_eedp
+{
+	uint8_t addr[8];
+	uint8_t length[4];
+	uint8_t protect;
+};
+
 static __inline uint32_t
 mps_regread(struct mps_softc *sc, uint32_t offset)
 {
@@ -225,7 +471,6 @@
 static __inline void
 mps_free_reply(struct mps_softc *sc, uint32_t busaddr)
 {
-
 	if (++sc->replyfreeindex >= sc->fqdepth)
 		sc->replyfreeindex = 0;
 	sc->free_queue[sc->replyfreeindex] = busaddr;
@@ -242,8 +487,11 @@
 		sc->chain_free--;
 		if (sc->chain_free < sc->chain_free_lowwater)
 			sc->chain_free_lowwater = sc->chain_free;
-	} else
+	}
+#if __FreeBSD_version >= 900030
+	else
 		sc->chain_alloc_fail++;
+#endif
 	return (chain);
 }
 
@@ -262,15 +510,16 @@
 {
 	struct mps_chain *chain, *chain_temp;
 
-	if (cm->cm_reply != NULL) {
+	if (cm->cm_reply != NULL)
 		mps_free_reply(sc, cm->cm_reply_data);
-		cm->cm_reply = NULL;
-	}
+	cm->cm_reply = NULL;
 	cm->cm_flags = 0;
 	cm->cm_complete = NULL;
 	cm->cm_complete_data = NULL;
-	cm->cm_targ = 0;
+	cm->cm_ccb = NULL;
+	cm->cm_targ = NULL;
 	cm->cm_max_segs = 0;
+	cm->cm_lun = 0;
 	cm->cm_state = MPS_CM_STATE_FREE;
 	TAILQ_FOREACH_SAFE(chain, &cm->cm_chain_list, chain_link, chain_temp) {
 		TAILQ_REMOVE(&cm->cm_chain_list, chain, chain_link);
@@ -295,6 +544,43 @@
 }
 
 static __inline void
+mps_free_high_priority_command(struct mps_softc *sc, struct mps_command *cm)
+{
+	struct mps_chain *chain, *chain_temp;
+
+	if (cm->cm_reply != NULL)
+		mps_free_reply(sc, cm->cm_reply_data);
+	cm->cm_reply = NULL;
+	cm->cm_flags = 0;
+	cm->cm_complete = NULL;
+	cm->cm_complete_data = NULL;
+	cm->cm_ccb = NULL;
+	cm->cm_targ = NULL;
+	cm->cm_lun = 0;
+	cm->cm_state = MPS_CM_STATE_FREE;
+	TAILQ_FOREACH_SAFE(chain, &cm->cm_chain_list, chain_link, chain_temp) {
+		TAILQ_REMOVE(&cm->cm_chain_list, chain, chain_link);
+		mps_free_chain(sc, chain);
+	}
+	TAILQ_INSERT_TAIL(&sc->high_priority_req_list, cm, cm_link);
+}
+
+static __inline struct mps_command *
+mps_alloc_high_priority_command(struct mps_softc *sc)
+{
+	struct mps_command *cm;
+
+	cm = TAILQ_FIRST(&sc->high_priority_req_list);
+	if (cm == NULL)
+		return (NULL);
+
+	TAILQ_REMOVE(&sc->high_priority_req_list, cm, cm_link);
+	KASSERT(cm->cm_state == MPS_CM_STATE_FREE, ("mps: Allocating busy command\n"));
+	cm->cm_state = MPS_CM_STATE_BUSY;
+	return (cm);
+}
+
+static __inline void
 mps_lock(struct mps_softc *sc)
 {
 	mtx_lock(&sc->mps_mtx);
@@ -315,6 +601,12 @@
 #define mps_printf(sc, args...)				\
 	device_printf((sc)->mps_dev, ##args)
 
+#define mps_vprintf(sc, args...)			\
+do {							\
+	if (bootverbose)				\
+		mps_printf(sc, ##args);			\
+} while (0)
+
 #define mps_dprint(sc, level, msg, args...)		\
 do {							\
 	if (sc->mps_debug & level)			\
@@ -375,7 +667,9 @@
 	mps_regwrite(sc, MPI2_HOST_INTERRUPT_MASK_OFFSET, mask);
 }
 
-int mps_pci_setup_interrupts(struct mps_softc *);
+int mps_pci_setup_interrupts(struct mps_softc *sc);
+int mps_pci_restore(struct mps_softc *sc);
+
 int mps_attach(struct mps_softc *sc);
 int mps_free(struct mps_softc *sc);
 void mps_intr(void *);
@@ -383,23 +677,97 @@
 void mps_intr_locked(void *);
 int mps_register_events(struct mps_softc *, uint8_t *, mps_evt_callback_t *,
     void *, struct mps_event_handle **);
+int mps_restart(struct mps_softc *);
 int mps_update_events(struct mps_softc *, struct mps_event_handle *, uint8_t *);
 int mps_deregister_events(struct mps_softc *, struct mps_event_handle *);
-int mps_request_polled(struct mps_softc *sc, struct mps_command *cm);
-void mps_enqueue_request(struct mps_softc *, struct mps_command *);
 int mps_push_sge(struct mps_command *, void *, size_t, int);
 int mps_add_dmaseg(struct mps_command *, vm_paddr_t, size_t, u_int, int);
 int mps_attach_sas(struct mps_softc *sc);
 int mps_detach_sas(struct mps_softc *sc);
-int mps_map_command(struct mps_softc *sc, struct mps_command *cm);
 int mps_read_config_page(struct mps_softc *, struct mps_config_params *);
 int mps_write_config_page(struct mps_softc *, struct mps_config_params *);
 void mps_memaddr_cb(void *, bus_dma_segment_t *, int , int );
 void mpi_init_sge(struct mps_command *cm, void *req, void *sge);
 int mps_attach_user(struct mps_softc *);
 void mps_detach_user(struct mps_softc *);
+void mpssas_record_event(struct mps_softc *sc,
+    MPI2_EVENT_NOTIFICATION_REPLY *event_reply);
+
+int mps_map_command(struct mps_softc *sc, struct mps_command *cm);
+int mps_wait_command(struct mps_softc *sc, struct mps_command *cm, int timeout);
+int mps_request_polled(struct mps_softc *sc, struct mps_command *cm);
+
+int mps_config_get_bios_pg3(struct mps_softc *sc, Mpi2ConfigReply_t
+    *mpi_reply, Mpi2BiosPage3_t *config_page);
+int mps_config_get_raid_volume_pg0(struct mps_softc *sc, Mpi2ConfigReply_t
+    *mpi_reply, Mpi2RaidVolPage0_t *config_page, u32 page_address);
+int mps_config_get_ioc_pg8(struct mps_softc *sc, Mpi2ConfigReply_t *,
+    Mpi2IOCPage8_t *);
+int mps_config_get_man_pg10(struct mps_softc *sc, Mpi2ConfigReply_t *mpi_reply);
+int mps_config_get_sas_device_pg0(struct mps_softc *, Mpi2ConfigReply_t *,
+    Mpi2SasDevicePage0_t *, u32 , u16 );
+int mps_config_get_dpm_pg0(struct mps_softc *, Mpi2ConfigReply_t *,
+    Mpi2DriverMappingPage0_t *, u16 );
+int mps_config_get_raid_volume_pg1(struct mps_softc *sc,
+    Mpi2ConfigReply_t *mpi_reply, Mpi2RaidVolPage1_t *config_page, u32 form,
+    u16 handle);
+int mps_config_get_volume_wwid(struct mps_softc *sc, u16 volume_handle,
+    u64 *wwid);
+int mps_config_get_raid_pd_pg0(struct mps_softc *sc,
+    Mpi2ConfigReply_t *mpi_reply, Mpi2RaidPhysDiskPage0_t *config_page,
+    u32 page_address);
+void mpssas_ir_shutdown(struct mps_softc *sc);
+
+int mps_reinit(struct mps_softc *sc);
+void mpssas_handle_reinit(struct mps_softc *sc);
+
+void mps_base_static_config_pages(struct mps_softc *sc);
+void mps_wd_config_pages(struct mps_softc *sc);
+
+int mps_mapping_initialize(struct mps_softc *);
+void mps_mapping_topology_change_event(struct mps_softc *,
+    Mpi2EventDataSasTopologyChangeList_t *);
+int mps_mapping_is_reinit_required(struct mps_softc *);
+void mps_mapping_free_memory(struct mps_softc *sc);
+int mps_config_set_dpm_pg0(struct mps_softc *, Mpi2ConfigReply_t *,
+    Mpi2DriverMappingPage0_t *, u16 );
+void mps_mapping_exit(struct mps_softc *);
+void mps_mapping_check_devices(struct mps_softc *, int);
+int mps_mapping_allocate_memory(struct mps_softc *sc);
+unsigned int mps_mapping_get_sas_id(struct mps_softc *, uint64_t , u16);
+unsigned int mps_mapping_get_sas_id_from_handle(struct mps_softc *sc,
+    u16 handle);
+unsigned int mps_mapping_get_raid_id(struct mps_softc *sc, u64 wwid,
+    u16 handle);
+unsigned int mps_mapping_get_raid_id_from_handle(struct mps_softc *sc,
+    u16 volHandle);
+void mps_mapping_enclosure_dev_status_change_event(struct mps_softc *,
+    Mpi2EventDataSasEnclDevStatusChange_t *event_data);
+void mps_mapping_ir_config_change_event(struct mps_softc *sc,
+    Mpi2EventDataIrConfigChangeList_t *event_data);
+
+void mpssas_evt_handler(struct mps_softc *sc, uintptr_t data,
+    MPI2_EVENT_NOTIFICATION_REPLY *event);
+void mpssas_prepare_remove(struct mpssas_softc *sassc, uint16_t handle);
+int mpssas_startup(struct mps_softc *sc);
 
 SYSCTL_DECL(_hw_mps);
 
+/* Compatibility shims for different OS versions */
+#if __FreeBSD_version >= 800001
+#define mps_kproc_create(func, farg, proc_ptr, flags, stackpgs, fmtstr, arg) \
+    kproc_create(func, farg, proc_ptr, flags, stackpgs, fmtstr, arg)
+#define mps_kproc_exit(arg)	kproc_exit(arg)
+#else
+#define mps_kproc_create(func, farg, proc_ptr, flags, stackpgs, fmtstr, arg) \
+    kthread_create(func, farg, proc_ptr, flags, stackpgs, fmtstr, arg)
+#define mps_kproc_exit(arg)	kthread_exit(arg)
 #endif
 
+#if defined(CAM_PRIORITY_XPT)
+#define MPS_PRIORITY_XPT	CAM_PRIORITY_XPT
+#else
+#define MPS_PRIORITY_XPT	5
+#endif
+#endif
+


More information about the Zrouter-src-freebsd mailing list