isp(4): Sync with FreeBSD.
authorSascha Wildner <saw@online.de>
Tue, 3 Jan 2012 12:29:09 +0000 (13:29 +0100)
committerSascha Wildner <saw@online.de>
Tue, 3 Jan 2012 12:30:08 +0000 (13:30 +0100)
Adds support for some newer cards:

* Qlogic ISP 2322 PCI FC-AL Adapter

* Qlogic ISP 2422 PCI FC-AL Adapter

* Qlogic ISP 2432 PCI FC-AL Adapter

* Qlogic ISP 2532 PCI FC-AL Adapter

* Qlogic ISP 5432 PCI FC-AL Adapter

* Qlogic ISP 6312 PCI FC-AL Adapter

* Qlogic ISP 6322 PCI FC-AL Adapter

Also isp(4) can now be loaded as a module. The firmwares have been
separated into per-firmware modules, too.

It was tested with an old DEC KZPBA-CX (1040 based) SCSI adapter.

51 files changed:
share/man/man4/isp.4
share/man/man4/ispfw.4
sys/conf/files
sys/config/GENERIC
sys/config/X86_64_GENERIC
sys/dev/disk/Makefile
sys/dev/disk/isp/DriverManual.txt [new file with mode: 0644]
sys/dev/disk/isp/Hardware.txt [new file with mode: 0644]
sys/dev/disk/isp/Makefile [new file with mode: 0644]
sys/dev/disk/isp/isp.c
sys/dev/disk/isp/isp_freebsd.c
sys/dev/disk/isp/isp_freebsd.h
sys/dev/disk/isp/isp_inline.h [deleted file]
sys/dev/disk/isp/isp_ioctl.h
sys/dev/disk/isp/isp_library.c [new file with mode: 0644]
sys/dev/disk/isp/isp_library.h [new file with mode: 0644]
sys/dev/disk/isp/isp_pci.c
sys/dev/disk/isp/isp_stds.h [new file with mode: 0644]
sys/dev/disk/isp/isp_target.c
sys/dev/disk/isp/isp_target.h
sys/dev/disk/isp/isp_tpublic.h [deleted file]
sys/dev/disk/isp/ispmbox.h
sys/dev/disk/isp/ispreg.h
sys/dev/disk/isp/ispvar.h
sys/dev/disk/ispfw/MAINTAINER [deleted file]
sys/dev/disk/ispfw/Makefile
sys/dev/disk/ispfw/asm_1040.h
sys/dev/disk/ispfw/asm_1080.h
sys/dev/disk/ispfw/asm_12160.h
sys/dev/disk/ispfw/asm_2100.h
sys/dev/disk/ispfw/asm_2200.h
sys/dev/disk/ispfw/asm_2300.h
sys/dev/disk/ispfw/asm_2322.h [new file with mode: 0644]
sys/dev/disk/ispfw/asm_2400.h [new file with mode: 0644]
sys/dev/disk/ispfw/asm_2500.h [new file with mode: 0644]
sys/dev/disk/ispfw/isp_1040/Makefile [new file with mode: 0644]
sys/dev/disk/ispfw/isp_1040_it/Makefile [new file with mode: 0644]
sys/dev/disk/ispfw/isp_1080/Makefile [new file with mode: 0644]
sys/dev/disk/ispfw/isp_1080_it/Makefile [new file with mode: 0644]
sys/dev/disk/ispfw/isp_12160/Makefile [new file with mode: 0644]
sys/dev/disk/ispfw/isp_12160_it/Makefile [new file with mode: 0644]
sys/dev/disk/ispfw/isp_2100/Makefile [new file with mode: 0644]
sys/dev/disk/ispfw/isp_2200/Makefile [new file with mode: 0644]
sys/dev/disk/ispfw/isp_2300/Makefile [new file with mode: 0644]
sys/dev/disk/ispfw/isp_2322/Makefile [new file with mode: 0644]
sys/dev/disk/ispfw/isp_2400/Makefile [new file with mode: 0644]
sys/dev/disk/ispfw/isp_2400_multi/Makefile [new file with mode: 0644]
sys/dev/disk/ispfw/isp_2500/Makefile [new file with mode: 0644]
sys/dev/disk/ispfw/isp_2500_multi/Makefile [new file with mode: 0644]
sys/dev/disk/ispfw/ispfw.c
sys/dev/disk/ispfw/ispfw/Makefile [new file with mode: 0644]

index 1fda830..23da1ba 100644 (file)
@@ -1,8 +1,6 @@
-.\"     $FreeBSD: src/share/man/man4/isp.4,v 1.3.4.9 2001/12/17 11:30:12 ru Exp $
-.\"     $DragonFly: src/share/man/man4/isp.4,v 1.6 2008/05/02 02:05:05 swildner Exp $
 .\"     $NetBSD: isp.4,v 1.5 1999/12/18 18:33:05 mjacob Exp $
 .\"
-.\" Copyright (c) 1998, 1999
+.\" Copyright (c) 1998, 1999, 2001
 .\"     Matthew Jacob, for NASA/Ames Research Center
 .\"
 .\" Redistribution and use in source and binary forms, with or without
 .\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
 .\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 .\"
+.\" Additional Copyright (c) 2006 by Marcus Alves Grando
 .\"
-.Dd February 14, 2000
+.\" $FreeBSD: src/share/man/man4/isp.4,v 1.29 2010/10/08 12:40:16 uqs Exp $
+.\"
+.Dd January 3, 2012
 .Dt ISP 4
 .Os
 .Sh NAME
 .Nm isp
 .Nd Qlogic based SCSI and FibreChannel SCSI Host Adapters
 .Sh SYNOPSIS
+To compile this driver into the kernel,
+place the following lines in your
+kernel configuration file:
+.Bd -ragged -offset indent
+.Cd "device scbus"
 .Cd "device isp"
+.Ed
 .Pp
-For one or more SCSI busses:
-.Cd device scbus0
+Alternatively, to load the driver as a
+module at boot time, place the following line in
+.Xr loader.conf 5 :
+.Bd -literal -offset indent
+isp_load="YES"
+.Ed
 .Sh DESCRIPTION
 This driver provides access to
 .Tn SCSI
@@ -58,65 +69,73 @@ and utilizes Class 3 and Class 2 connections (Qlogic 2100 is Class
 3 only, minor patches to the Qlogic 2200 to force Class 2 mode).
 Support is available for Public and Private loops, and for
 point-to-point connections (Qlogic 2200 only).
-The newer 2-Gigabit cards (2300, 2312) are also supported.
+The newer 2-Gigabit cards (2300, 2312, 2322) and 4-Gigabit (2422, 2432)
+are also supported.
 Command tagging is
 supported for all (in fact,
 .Tn FibreChannel
 requires tagging).
-Fabric support is enabled by default (but may
-be contingent upon the correct firmware being loaded).
+Fabric support is enabled by default for other than 2100 cards.
+Fabric
+support for 2100 cards has been so problematic and these cards are so
+old now that it is just not worth your time to try it.
 .Sh FIRMWARE
-Firmware is now available if the
+Firmware is available if the
 .Xr ispfw 4
 module is loaded during bootstrap (q.v.).
 .Pp
-It is very strongly recommended that you use the firmware available
+It is
+.Ar strongly
+recommended that you use the firmware available
 from
 .Xr ispfw 4
 as it is the most likely to have been tested with this driver.
 .Sh HARDWARE
-Supported cards include:
+Cards supported by the
+.Nm
+driver include:
 .Bl -tag -width xxxxxx -offset indent
 .It ISP1000
 SBus Fast Wide, Ultra Fast Wide cards, Single Ended or Differential
 cards.
-.It PTI SBS440
-Performance Technology ISP1000 variants.
 .It ISP1020
 Qlogic 1020 Fast Wide and Differential Fast Wide PCI cards.
 .It ISP1040
 Qlogic 1040 Ultra Wide and Differential Ultra Wide PCI cards.
-.It PTI SBS450
-Performance Technology ISP1040 variants.
+Also known as the DEC KZPBA-CA (single ended) and KZPBA-CB (HVD differential).
 .It Qlogic 1240
 Qlogic 1240 Dual Bus Ultra Wide and Differential Ultra Wide PCI
 cards.
+.It Qlogic 1020
+Qlogic 1020 SCSI cards.
+.It Qlogic 1040
+Qlogic 1040 Ultra SCSI cards.
 .It Qlogic 1080
 Qlogic 1280 LVD Ultra2 Wide PCI cards.
 .It Qlogic 1280
 Qlogic 1280 Dual Bus LVD Ultra2 Wide PCI cards.
 .It Qlogic 12160
 Qlogic 12160 Dual Bus LVD Ultra3 Wide PCI cards.
-.It Qlogic 2100
+.It Qlogic 210X
 Qlogic 2100 and 2100A Copper and Optical Fibre Channel Arbitrated
-Loop
-.It Qlogic 2102
-Qlogic Dual Loop 2100A Optical Fibre Channel Arbitrated Loop PCI
-cards.
-.It Qlogic 2200
+Loop (single, dual).
+.It Qlogic 220X
 Qlogic 2200 Copper and Optical Fibre Channel Arbitrated Loop PCI
-cards.
-.It Qlogic 2202
-Qlogic 2200 Dual Bus Optical Fibre Channel Arbitrated Loop PCI
-cards.
+cards (single, dual, quad).
 .It Qlogic 2300
 Qlogic 2300 Optical Fibre Channel PCI cards.
 .It Qlogic 2312
 Qlogic 2312 Optical Fibre Channel PCI cards.
-.It PTI SBS470
-Performance Technology ISP2100 variants.
-.It Antares P-0033
-Antares Microsystems ISP2100 variants.
+.It Qlogic 234X
+Qlogic 234X Optical Fibre Channel PCI cards (2312 chipset, single and dual attach).
+.It Qlogic 2322
+Qlogic 2322 Optical Fibre Channel PCIe cards.
+.It Qlogic 200
+Dell Branded version of the QLogic 2312 Fibre Channel PCI cards.
+.It Qlogic 2422
+Qlogic 2422 Optical Fibre Channel PCI cards (4 Gigabit)
+.It Qlogic 2432
+Qlogic 2432 Optical Fibre Channel PCIe cards (4 Gigabit)
 .El
 .Sh CONFIGURATION OPTIONS
 Target mode support may be enabled with the
@@ -125,41 +144,77 @@ Target mode support may be enabled with the
 .Pp
 option.
 .Sh BOOT OPTIONS
-The following options are switchable by setting values in the loader
-configuration file (see the
-.Xr loader 8
-manual page).
-They are:
+The following options are switchable by setting values in
+.Pa /boot/device.hints .
 .Pp
-.Bl -tag -width "isp_no_fwload" -compact
-.It isp_disable
-A bit mask of units to skip configuration for.
-.It isp_mem_map
-A bit mask of units to use PCI Memory space instead of I/O space
+They are:
+.Bl -tag -width indent
+.It Va hint.isp.0.disable
+A hint value to disable driver in kernel.
+.It Va hint.isp.0.fwload_disable
+A hint value to disable loading of firmware
+.Xr ispfw 4 .
+.It Va hint.isp.0.prefer_memmap
+A hint value to use PCI memory space instead of I/O space
 access for.
-.It isp_io_map
-A bit mask of units to use PCI I/O space instead of Memory space
+.It Va hint.isp.0.prefer_iomap
+A hint value to use PCI I/O space instead of Memory space
 access for.
-.It isp_no_nvram
-A bit mask of units that you wish to ignore board NVRAM settings
-for.
-.It isp_nvram
-A bit mask of units that you wish to specifically use board NVRAM
-settings for.
-.It isp_fcduplex
-A bit mask of units that you wish to specifically to set into full
-duplex mode.
-.It isp_no_fcduplex
-A bit mask of units that you wish to specifically to not set into
-full duplex mode.
-.It isp_wwn
+.It Va hint.isp.0.ignore_nvram
+A hint value to ignore board NVRAM settings for.
+Otherwise use NVRAM settings.
+.It Va hint.isp.0.fullduplex
+A hint value to set full duplex mode.
+.It Va hint.isp.0.topology
+A hint value to select topology of connection.
+Supported values are:
+.Pp
+.Bl -tag -width ".Li lport-only" -compact
+.It Li lport
+Prefer loopback and fallback to point to point.
+.It Li nport
+Prefer point to point and fallback to loopback.
+.It Li lport-only
+Loopback only.
+.It Li nport-only
+Point to point only.
+.El
+.It Va hint.isp.0.portwwn
+This should be the full 64 bit World Wide Port Name you would like
+to use, overriding the value in NVRAM for the card.
+.It Va hint.isp.0.nodewwn
 This should be the full 64 bit World Wide Node Name you would like
 to use, overriding the value in NVRAM for the card.
-.It isp_debug
-This is a driver debug level- meaningful from 0 through 7.
-.It isp_tdebug
-This is a driver target mode debug level- meaningful from 0 through
-5.
+.It Va hint.isp.0.iid
+A hint to override or set the Initiator ID or Loop ID.
+For Fibre Channel
+cards in Local Loop topologies it is
+.Ar strongly
+recommended that you set this value to non-zero.
+.It Va hint.isp.0.role
+A hint to define default role for isp instance (target, initiator, both).
+.It Va hint.isp.0.debug
+A hint value for a driver debug level (see the file
+.Pa /usr/src/sys/dev/isp/ispvar.h
+for the values.
+.El
+.Sh SYSCTL OPTIONS
+.Bl -tag -width indent
+.It Va dev.isp.N.loop_down_limit
+This value says how long to wait in seconds after loop has gone down before
+giving up and expiring all of the devices that were visible.
+The default is 300 seconds (5 minutes).
+A separate (nonadjustable) timeout is used when
+booting to not stop booting on lack of FC connectivity.
+.It Va dev.isp.N.gone_device_time
+This value says how long to wait for devices to reappear if they (temporarily)
+disappear due to loop or fabric events.
+While this timeout is running, I/O
+to those devices will simply be held.
+.It Va dev.isp.N.wwnn
+This is the readonly World Wide Node Name value for this port.
+.It Va dev.isp.N.wwpn
+This is the readonly World Wide Port Name value for this port.
 .El
 .Sh SEE ALSO
 .Xr da 4 ,
@@ -170,24 +225,13 @@ This is a driver target mode debug level- meaningful from 0 through
 .Sh AUTHORS
 The
 .Nm
-driver was written by Matthew Jacob for NASA/Ames Research Center.
+driver was written by Matthew Jacob originally for NetBSD at
+NASA/Ames Research Center.
 .Sh BUGS
 The driver currently ignores some NVRAM settings.
 .Pp
-The driver currently doesn't do error recovery for timed out commands
-very gracefully.
-.Pp
-Target mode support isn't completely debugged yet.
+Target mode support is not completely reliable yet.
 It works reasonably
 well for Fibre Channel, somewhat well for Qlogic 1040 cards, but
-doesn't yet work for the other cards (due to last minute unannounced
+does not yet work for the other cards (due to last minute unannounced
 changes in firmware interfaces).
-.Pp
-Sometimes, when booting, the driver gets stuck waiting for the
-Fibre Channel f/w to tell it that the loop port database is ready,
-or waiting for a good loop to be seen (this does not yet support
-booting without being connected to a fibre channel device).
-To
-unwedge the system, unplug and replug the fibre channel connection,
-or otherwise cause a LIP (Loop Initialization Primitive sequence)-
-this will kick the f/w into getting unstuck.
index a1fbfd5..7b40766 100644 (file)
@@ -1,6 +1,3 @@
-.\"     $FreeBSD: src/share/man/man4/ispfw.4,v 1.1.2.4 2001/08/17 13:08:38 ru Exp $
-.\"     $DragonFly: src/share/man/man4/ispfw.4,v 1.2 2003/06/17 04:36:59 dillon Exp $
-.\"
 .\" Copyright (c) 2000
 .\"     Matthew Jacob
 .\"
 .\" (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: src/share/man/man4/ispfw.4,v 1.7 2006/05/20 09:39:28 brueffer Exp $
 .\"
-.Dd July 20, 2000
+.Dd January 3, 2012
 .Dt ISPFW 4
 .Os
 .Sh NAME
 .Nm ispfw
 .Nd "Firmware Module for Qlogic based SCSI and FibreChannel SCSI Host Adapters"
 .Sh SYNOPSIS
+To compile this driver into the kernel,
+place the following line in your
+kernel configuration file:
+.Bd -ragged -offset indent
 .Cd "device ispfw"
+.Ed
+.Pp
+Alternatively, to load the driver as a
+module at boot time, place the following line in
+.Xr loader.conf 5 :
+.Bd -literal -offset indent
+ispfw_load="YES"
+.Ed
 .Sh DESCRIPTION
 This trivial driver provides access to firmware sets for the Qlogic
-based SCSI and FibreChannel SCSI Host Adapters. It may either be
-statically linked into the kernel, or loaded as a module. In either
+based SCSI and FibreChannel SCSI Host Adapters.
+It may either be
+statically linked into the kernel, or loaded as a module.
+In either
 case, the
 .Xr isp 4
 driver will notice that firmware is available to be downloaded onto
index f0e2502..5912b0b 100644 (file)
@@ -382,11 +382,12 @@ dev/powermng/km/km.c              optional km pci
 dev/raid/ida/ida.c             optional ida
 dev/raid/ida/ida_disk.c        optional ida
 dev/raid/ida/ida_pci.c optional ida pci
-dev/disk/isp/isp_freebsd.c     optional isp
 dev/disk/isp/isp.c             optional isp
-dev/disk/isp/isp_pci.c optional isp
+dev/disk/isp/isp_freebsd.c     optional isp
+dev/disk/isp/isp_library.c     optional isp
+dev/disk/isp/isp_pci.c         optional isp pci
 dev/disk/isp/isp_target.c      optional isp
-dev/disk/ispfw/ispfw.c optional ispfw
+dev/disk/ispfw/ispfw.c         optional ispfw
 dev/raid/iir/iir.c             optional iir
 dev/raid/iir/iir_ctrl.c        optional iir
 dev/raid/iir/iir_pci.c optional iir pci
index 8d1ee0f..2c577e7 100644 (file)
@@ -125,6 +125,7 @@ device              ahc             # AHA2940 and onboard AIC7xxx devices
 device         ahd             # AHA39320/29320 and onboard AIC79xx devices
 device         amd             # AMD 53C974 (Tekram DC-390(T))
 device         isp             # Qlogic family
+#device                ispfw           # Firmware for QLogic HBAs- normally a module
 device         mpt             # LSI-Logic MPT/Fusion
 device         ncr             # NCR/Symbios Logic
 device         sym             # NCR/Symbios Logic (newer chipsets)
index 8431757..a67b1ed 100644 (file)
@@ -111,6 +111,7 @@ device              ahc             # AHA2940 and onboard AIC7xxx devices
 device         ahd             # AHA39320/29320 and onboard AIC79xx devices
 device         amd             # AMD 53C974 (Tekram DC-390(T))
 device         isp             # Qlogic family
+#device                ispfw           # Firmware for QLogic HBAs- normally a module
 device         mpt             # LSI-Logic MPT/Fusion
 device         ncr             # NCR/Symbios Logic
 device         sym             # NCR/Symbios Logic (newer chipsets)
index 5aec1b7..f1b2d16 100644 (file)
@@ -1,6 +1,6 @@
 .include "${.CURDIR}/../../platform/${MACHINE_PLATFORM}/Makefile.inc"
 
-SUBDIR= ahci aic aic7xxx ccd dm iscsi ispfw md mmcsd mps mpt ncv nsp
+SUBDIR= ahci aic aic7xxx ccd dm iscsi isp ispfw md mmcsd mps mpt ncv nsp
 SUBDIR+= sbp sdhci sili stg trm vn vpo
 
 .for dir in ${SUBDIR}
diff --git a/sys/dev/disk/isp/DriverManual.txt b/sys/dev/disk/isp/DriverManual.txt
new file mode 100644 (file)
index 0000000..f4be0b6
--- /dev/null
@@ -0,0 +1,633 @@
+/* $FreeBSD: src/sys/dev/isp/DriverManual.txt,v 1.3 2011/11/11 22:27:09 eadler Exp $ */
+
+               Driver Theory of Operation Manual
+
+1. Introduction
+
+This is a short text document that will describe the background, goals
+for, and current theory of operation for the joint Fibre Channel/SCSI
+HBA driver for QLogic hardware.
+
+Because this driver is an ongoing project, do not expect this manual
+to remain entirely up to date. Like a lot of software engineering, the
+ultimate documentation is the driver source. However, this manual should
+serve as a solid basis for attempting to understand where the driver
+started and what is trying to be accomplished with the current source.
+
+The reader is expected to understand the basics of SCSI and Fibre Channel
+and to be familiar with the range of platforms that Solaris, Linux and
+the variant "BSD" Open Source systems are available on. A glossary and
+a few references will be placed at the end of the document.
+
+There will be references to functions and structures within the body of
+this document. These can be easily found within the source using editor
+tags or grep. There will be few code examples here as the code already
+exists where the reader can easily find it.
+
+2. A Brief History for this Driver
+
+This driver originally started as part of work funded by NASA Ames
+Research Center's Numerical Aerodynamic Simulation center ("NAS" for
+short) for the QLogic PCI 1020 and 1040 SCSI Host Adapters as part of my
+work at porting the NetBSD Operating System to the Alpha architectures
+(specifically the AlphaServer 8200 and 8400 platforms).  In short, it
+started just as simple single SCSI HBA driver for just the purpose of
+running off a SCSI disk. This work took place starting in January, 1997.
+
+Because the first implementation was for NetBSD, which runs on a very
+large number of platforms, and because NetBSD supported both systems with
+SBus cards (e.g., Sun SPARC systems) as well as systems with PCI cards,
+and because the QLogic SCSI cards came in both SBus and PCI versions, the
+initial implementation followed the very thoughtful NetBSD design tenet
+of splitting drivers into what are called MI (for Machine Independent)
+and MD (Machine Dependent) portions. The original design therefore was
+from the premise that the driver would drive both SBus and PCI card
+variants. These busses are similar but have quite different constraints,
+and while the QLogic SBus and PCI cards are very similar, there are some
+significant differences.
+
+After this initial goal had been met, there began to be some talk about
+looking into implementing Fibre Channel mass storage at NAS. At this time
+the QLogic 2100 FC/AL HBA was about to become available. After looking at
+the way it was designed I concluded that it was so darned close to being
+just like the SCSI HBAs that it would be insane to *not* leverage off of
+the existing driver. So, we ended up with a driver for NetBSD that drove
+PCI and SBus SCSI cards, and now also drove the QLogic 2100 FC-AL HBA.
+
+After this, ports to non-NetBSD platforms became interesting as well.
+This took the driver out of the interest with NAS and into interested
+support from a number of other places. Since the original NetBSD
+development, the driver has been ported to FreeBSD, OpenBSD, Linux,
+Solaris, and two proprietary systems. Following from the original MI/MD
+design of NetBSD, a rather successful attempt has been made to keep the
+Operating System Platform differences segregated and to a minimum.
+
+Along the way, support for the 2200 as well as full fabric and target
+mode support has been added, and 2300 support as well as an FC-IP stack
+are planned.
+
+3. Driver Design Goals
+
+The driver has not started out as one normally would do such an effort.
+Normally you design via top-down methodologies and set an intial goal
+and meet it. This driver has had a design goal that changes from almost
+the very first. This has been an extremely peculiar, if not risque,
+experience. As a consequence, this section of this document contains
+a bit of "reconstruction after the fact" in that the design goals are
+as I perceive them to be now- not necessarily what they started as.
+
+The primary design goal now is to have a driver that can run both the
+SCSI and Fibre Channel SCSI prototocols on multiple OS platforms with
+as little OS platform support code as possible.
+
+The intended support targets for SCSI HBAs is to support the single and
+dual channel PCI Ultra2 and PCI Ultra3 cards as well as the older PCI
+Ultra single channel cards and SBus cards.
+
+The intended support targets for Fibre Channel HBAs is the 2100, 2200
+and 2300 PCI cards.
+
+Fibre Channel support should include complete fabric and public loop
+as well as private loop and private loop, direct-attach topologies.
+FC-IP support is also a goal.
+
+For both SCSI and Fibre Channel, simultaneous target/initiator mode support
+is a goal.
+
+Pure, raw, performance is not a primary goal of this design. This design,
+because it has a tremendous amount of code common across multiple
+platforms, will undoubtedly never be able to beat the performance of a
+driver that is specifically designed for a single platform and a single
+card. However, it is a good strong secondary goal to make the performance
+penalties in this design as small as possible.
+
+Another primary aim, which almost need not be stated, is that the
+implementation of platform differences must not clutter up the common
+code with platform specific defines. Instead, some reasonable layering
+semantics are defined such that platform specifics can be kept in the
+platform specific code.
+
+4. QLogic Hardware Architecture
+
+In order to make the design of this driver more intelligible, some
+description of the Qlogic hardware architecture is in order. This will
+not be an exhaustive description of how this card works, but will
+note enough of the important features so that the driver design is
+hopefully clearer.
+
+4.1 Basic QLogic hardware
+
+The QLogic HBA cards all contain a tiny 16-bit RISC-like processor and
+varying sizes of SRAM. Each card contains a Bus Interface Unit (BIU)
+as appropriate for the host bus (SBus or PCI).  The BIUs allow access
+to a set of dual-ranked 16 bit incoming and outgoing mailbox registers
+as well as access to control registers that control the RISC or access
+other portions of the card (e.g., Flash BIOS). The term 'dual-ranked'
+means that at the same host visible address if you write a mailbox
+register, that is a write to an (incoming, to the HBA) mailbox register,
+while a read to the same address reads another (outgoing, to the HBA)
+mailbox register with completely different data. Each HBA also then has
+core and auxillary logic which either is used to interface to a SCSI bus
+(or to external bus drivers that connect to a SCSI bus), or to connect
+to a Fibre Channel bus.
+
+4.2 Basic Control Interface
+
+There are two principle I/O control mechanisms by which the driver
+communicates with and controls the QLogic HBA. The first mechanism is to
+use the incoming mailbox registers to interrupt and issue commands to
+the RISC processor (with results usually, but not always, ending up in
+the ougtoing mailbox registers). The second mechanism is to establish,
+via mailbox commands, circular request and response queues in system
+memory that are then shared between the QLogic and the driver. The
+request queue is used to queue requests (e.g., I/O requests) for the
+QLogic HBA's RISC engine to copy into the HBA memory and process. The
+result queue is used by the QLogic HBA's RISC engine to place results of
+requests read from the request queue, as well as to place notification
+of asynchronous events (e.g., incoming commands in target mode).
+
+To give a bit more precise scale to the preceding description, the QLogic
+HBA has 8 dual-ranked 16 bit mailbox registers, mostly for out-of-band
+control purposes. The QLogic HBA then utilizes a circular request queue
+of 64 byte fixed size Queue Entries to receive normal initiator mode
+I/O commands (or continue target mode requests). The request queue may
+be up to 256 elements for the QLogic 1020 and 1040 chipsets, but may
+be quite larger for the QLogic 12X0/12160 SCSI and QLogic 2X00 Fibre
+Channel chipsets.
+
+In addition to synchronously initiated usage of mailbox commands by
+the host system, the QLogic may also deliver asynchronous notifications
+solely in outgoing mailbox registers. These asynchronous notifications in
+mailboxes may be things like notification of SCSI Bus resets, or that the
+Fabric Name server has sent a change notification, or even that a specific
+I/O command completed without error (this is called 'Fast Posting'
+and saves the QLogic HBA from having to write a response queue entry).
+
+The QLogic HBA is an interrupting card, and when servicing an interrupt
+you really only have to check for either a mailbox interrupt or an
+interrupt notification that the response queue has an entry to
+be dequeued.
+
+4.3 Fibre Channel SCSI out of SCSI
+
+QLogic took the approach in introducing the 2X00 cards to just treat
+FC-AL as a 'fat' SCSI bus (a SCSI bus with more than 15 targets). All
+of the things that you really need to do with Fibre Channel with respect
+to providing FC-4 services on top of a Class 3 connection are performed
+by the RISC engine on the QLogic card itself. This means that from
+an HBA driver point of view, very little needs to change that would
+distinguish addressing a Fibre Channel disk from addressing a plain
+old SCSI disk.
+
+However, in the details it's not *quite* that simple. For example, in
+order to manage Fabric Connections, the HBA driver has to do explicit
+binding of entities it's queried from the name server to specific 'target'
+ids (targets, in this case, being a virtual entity).
+
+Still- the HBA firmware does really nearly all of the tedious management
+of Fibre Channel login state. The corollary to this sometimes is the
+lack of ability to say why a particular login connection to a Fibre
+Channel disk is not working well.
+
+There are clear limits with the QLogic card in managing fabric devices.
+The QLogic manages local loop devices (LoopID or Target 0..126) itself,
+but for the management of fabric devices, it has an absolute limit of
+253 simultaneous connections (256 entries less 3 reserved entries).
+
+5. Driver Architecture
+
+5.1 Driver Assumptions
+
+The first basic assumption for this driver is that the requirements for
+a SCSI HBA driver for any system is that of a 2 or 3 layer model where
+there are SCSI target device drivers (drivers which drive SCSI disks,
+SCSI tapes, and so on), possibly a middle services layer, and a bottom
+layer that manages the transport of SCSI CDB's out a SCSI bus (or across
+Fibre Channel) to a SCSI device. It's assumed that each SCSI command is
+a separate structure (or pointer to a structure) that contains the SCSI
+CDB and a place to store SCSI Status and SCSI Sense Data.
+
+This turns out to be a pretty good assumption. All of the Open Source
+systems (*BSD and Linux) and most of the proprietary systems have this
+kind of structure. This has been the way to manage SCSI subsystems for
+at least ten years.
+
+There are some additional basic assumptions that this driver makes- primarily
+in the arena of basic simple services like memory zeroing, memory copying,
+delay, sleep, microtime functions. It doesn't assume much more than this.
+
+5.2 Overall Driver Architecture
+
+The driver is split into a core (machine independent) module and platform
+and bus specific outer modules (machine dependent).
+
+The core code (in the files isp.c, isp_inline.h, ispvar.h, ispreg.h and
+ispmbox.h) handles:
+
+ + Chipset recognition and reset and firmware download (isp_reset)
+ + Board Initialization (isp_init)
+ + First level interrupt handling (response retrieval) (isp_intr)
+ + A SCSI command queueing entry point (isp_start)
+ + A set of control services accessed either via local requirements within
+   the core module or via an externally visible control entry point
+   (isp_control).
+
+The platform/bus specific modules (and definitions) depend on each
+platform, and they provide both definitions and functions for the core
+module's use.  Generally a platform module set is split into a bus
+dependent module (where configuration is begun from and bus specific
+support functions reside) and relatively thin platform specific layer
+which serves as the interconnect with the rest of this platform's SCSI
+subsystem.
+
+For ease of bus specific access issues, a centralized soft state
+structure is maintained for each HBA instance (struct ispsoftc). This
+soft state structure contains a machine/bus dependent vector (mdvec)
+for functions that read and write hardware registers, set up DMA for the
+request/response queues and fibre channel scratch area, set up and tear
+down DMA mappings for a SCSI command, provide a pointer to firmware to
+load, and other minor things.
+
+The machine dependent outer module must provide functional entry points
+for the core module:
+
+ + A SCSI command completion handoff point (isp_done)
+ + An asynchronous event handler (isp_async)
+ + A logging/printing function (isp_prt)
+
+The machine dependent outer module code must also provide a set of
+abstracting definitions which is what the core module utilizes heavily
+to do its job. These are discussed in detail in the comments in the
+file ispvar.h, but to give a sense of the range of what is required,
+let's illustrate two basic classes of these defines.
+
+The first class are "structure definition/access" class. An
+example of these would be:
+
+       XS_T            Platform SCSI transaction type (i.e., command for HBA)
+       ..
+       XS_TGT(xs)      gets the target from an XS_T
+       ..
+       XS_TAG_TYPE(xs) which type of tag to use
+       ..
+
+The second class are 'functional' class definitions. Some examples of
+this class are:
+
+       MEMZERO(dst, src)                       platform zeroing function
+       ..
+       MBOX_WAIT_COMPLETE(struct ispsoftc *)   wait for mailbox cmd to be done
+
+Note that the former is likely to be simple replacement with bzero or
+memset on most systems, while the latter could be quite complex.
+
+This soft state structure also contains different parameter information
+based upon whether this is a SCSI HBA or a Fibre Channel HBA (which is
+filled in by the code module).
+
+In order to clear up what is undoubtedly a seeming confusion of
+interconnects, a description of the typical flow of code that performs
+boards initialization and command transactions may help.
+
+5.3 Initialization Code Flow
+
+Typically a bus specific module for a platform (e.g., one that wants
+to configure a PCI card) is entered via that platform's configuration
+methods. If this module recognizes a card and can utilize or construct the
+space for the HBA instance softc, it does so, and initializes the machine
+dependent vector as well as any other platform specific information that
+can be hidden in or associated with this structure.
+
+Configuration at this point usually involves mapping in board registers
+and registering an interrupt. It's quite possible that the core module's
+isp_intr function is adequate to be the interrupt entry point, but often
+it's more useful have a bus specific wrapper module that calls isp_intr.
+
+After mapping and interrupt registry is done, isp_reset is called.
+Part of the isp_reset call may cause callbacks out to the bus dependent
+module to perform allocation and/or mapping of Request and Response
+queues (as well as a Fibre Channel scratch area if this is a Fibre
+Channel HBA).  The reason this is considered 'bus dependent' is that
+only the bus dependent module may have the information that says how
+one could perform I/O mapping and dependent (e.g., on a Solaris system)
+on the Request and Reponse queues. Another callback can enable the *use*
+of interrupts should this platform be able to finish configuration in
+interrupt driven mode.
+
+If isp_reset is successful at resetting the QLogic chipset and downloading
+new firmware (if available) and setting it running, isp_init is called. If
+isp_init is successful in doing initial board setups (including reading
+NVRAM from the QLogic card), then this bus specicic module will call the
+platform dependent module that takes the appropriate steps to 'register'
+this HBA with this platform's SCSI subsystem.  Examining either the
+OpenBSD or the NetBSD isp_pci.c or isp_sbus.c files may assist the reader
+here in clarifying some of this.
+
+5.4 Initiator Mode Command Code Flow
+
+A succesful execution of isp_init will lead to the driver 'registering'
+itself with this platform's SCSI subsystem. One assumed action for this
+is the registry of a function that the SCSI subsystem for this platform
+will call when it has a SCSI command to run.
+
+The platform specific module function that receives this will do whatever
+it needs to to prepare this command for execution in the core module. This
+sounds vague, but it's also very flexible. In principle, this could be
+a complete marshalling/demarshalling of this platform's SCSI command
+structure (should it be impossible to represent in an XS_T). In addition,
+this function can also block commands from running (if, e.g., Fibre
+Channel loop state would preclude successful starting of the command).
+
+When it's ready to do so, the function isp_start is called with this
+command. This core module tries to allocate request queue space for
+this command. It also calls through the machine dependent vector
+function to make sure any DMA mapping for this command is done.
+
+Now, DMA mapping here is possibly a misnomer, as more than just
+DMA mapping can be done in this bus dependent function. This is
+also the place where any endian byte-swizzling will be done. At any
+rate, this function is called last because the process of establishing
+DMA addresses for any command may in fact consume more Request Queue
+entries than there are currently available. If the mapping and other
+functions are successful, the QLogic mailbox inbox pointer register
+is updated to indicate to the QLogic that it has a new request to
+read.
+
+If this function is unsuccessful, policy as to what to do at this point is
+left to the machine dependent platform function which called isp_start. In
+some platforms, temporary resource shortages can be handled by the main
+SCSI subsystem. In other platforms, the machine dependent code has to
+handle this.
+
+In order to keep track of commands that are in progress, the soft state
+structure contains an array of 'handles' that are associated with each
+active command. When you send a command to the QLogic firmware, a portion
+of the Request Queue entry can contain a non-zero handle identifier so
+that at a later point in time in reading either a Response Queue entry
+or from a Fast Posting mailbox completion interrupt, you can take this
+handle to find the command you were waiting on. It should be noted that
+this is probably one of the most dangerous areas of this driver. Corrupted
+handles will lead to system panics.
+
+At some later point in time an interrupt will occur. Eventually,
+isp_intr will be called. This core module will determine what the cause
+of the interrupt is, and if it is for a completing command. That is,
+it'll determine the handle and fetch the pointer to the command out of
+storage within the soft state structure. Skipping over a lot of details,
+the machine dependent code supplied function isp_done is called with the
+pointer to the completing command. This would then be the glue layer that
+informs the SCSI subsystem for this platform that a command is complete.
+
+5.5 Asynchronous Events
+
+Interrupts occur for events other than commands (mailbox or request queue
+started commands) completing. These are called Asynchronous Mailbox
+interrupts. When some external event causes the SCSI bus to be reset,
+or when a Fibre Channel loop changes state (e.g., a LIP is observed),
+this generates such an asynchronous event.
+
+Each platform module has to provide an isp_async entry point that will
+handle a set of these. This isp_async entry point also handles things
+which aren't properly async events but are simply natural outgrowths
+of code flow for another core function (see discussion on fabric device
+management below).
+
+5.6 Target Mode Code Flow
+
+This section could use a lot of expansion, but this covers the basics.
+
+The QLogic cards, when operating in target mode, follow a code flow that is
+essentially the inverse of that for intiator mode describe above. In this
+scenario, an interrupt occurs, and present on the Response Queue is a
+queue entry element defining a new command arriving from an initiator.
+
+This is passed to possibly external target mode handler. This driver
+provides some handling for this in a core module, but also leaves
+things open enough that a completely different target mode handler
+may accept this incoming queue entry.
+
+The external target mode handler then turns around forms up a response
+to this 'response' that just arrived which is then placed on the Request
+Queue and handled very much like an initiator mode command (i.e., calling
+the bus dependent DMA mapping function). If this entry completes the
+command, no more need occur. But often this handles only part of the
+requested command, so the QLogic firmware will rewrite the response
+to the initial 'response' again onto the Response Queue, whereupon the
+target mode handler will respond to that, and so on until the command
+is completely handled.
+
+Because almost no platform provides basic SCSI Subsystem target mode
+support, this design has been left extremely open ended, and as such
+it's a bit hard to describe in more detail than this.
+
+5.7 Locking Assumptions
+
+The observant reader by now is likely to have asked the question, "but what
+about locking? Or interrupt masking" by now.
+
+The basic assumption about this is that the core module does not know
+anything directly about locking or interrupt masking. It may assume that
+upon entry (e.g., via isp_start, isp_control, isp_intr) that appropriate
+locking and interrupt masking has been done.
+
+The platform dependent code may also therefore assume that if it is
+called (e.g., isp_done or isp_async) that any locking or masking that
+was in place upon the entry to the core module is still there. It is up
+to the platform dependent code to worry about avoiding any lock nesting
+issues. As an example of this, the Linux implementation simply queues
+up commands completed via the callout to isp_done, which it then pushes
+out to the SCSI subsystem after a return from it's calling isp_intr is
+executed (and locks dropped appropriately, as well as avoidance of deep
+interrupt stacks).
+
+Recent changes in the design have now eased what had been an original
+requirement that the while in the core module no locks or interrupt
+masking could be dropped. It's now up to each platform to figure out how
+to implement this. This is principally used in the execution of mailbox
+commands (which are principally used for Loop and Fabric management via
+the isp_control function).
+
+5.8 SCSI Specifics
+
+The driver core or platform dependent architecture issues that are specific
+to SCSI are few. There is a basic assumption that the QLogic firmware
+supported Automatic Request sense will work- there is no particular provision
+for disabling it's usage on a per-command basis.
+
+5.9 Fibre Channel Specifics
+
+Fibre Channel presents an interesting challenge here. The QLogic firmware
+architecture for dealing with Fibre Channel as just a 'fat' SCSI bus
+is fine on the face of it, but there are some subtle and not so subtle
+problems here.
+
+5.9.1 Firmware State
+
+Part of the initialization (isp_init) for Fibre Channel HBAs involves
+sending a command (Initialize Control Block) that establishes Node
+and Port WWNs as well as topology preferences. After this occurs,
+the QLogic firmware tries to traverese through serveral states:
+
+       FW_CONFIG_WAIT
+       FW_WAIT_AL_PA
+       FW_WAIT_LOGIN
+       FW_READY
+       FW_LOSS_OF_SYNC
+       FW_ERROR
+       FW_REINIT
+       FW_NON_PART
+
+It starts with FW_CONFIG_WAIT, attempts to get an AL_PA (if on an FC-AL
+loop instead of being connected as an N-port), waits to log into all
+FC-AL loop entities and then hopefully transitions to FW_READY state.
+
+Clearly, no command should be attempted prior to FW_READY state is
+achieved. The core internal function isp_fclink_test (reachable via
+isp_control with the ISPCTL_FCLINK_TEST function code). This function
+also determines connection topology (i.e., whether we're attached to a
+fabric or not).
+
+5.9.2. Loop State Transitions- From Nil to Ready
+
+Once the firmware has transitioned to a ready state, then the state of the
+connection to either arbitrated loop or to a fabric has to be ascertained,
+and the identity of all loop members (and fabric members validated).
+
+This can be very complicated, and it isn't made easy in that the QLogic
+firmware manages PLOGI and PRLI to devices that are on a local loop, but
+it is the driver that must manage PLOGI/PRLI with devices on the fabric.
+
+In order to manage this state an eight level staging of current "Loop"
+(where "Loop" is taken to mean FC-AL or N- or F-port connections) states
+in the following ascending order:
+
+       LOOP_NIL
+       LOOP_LIP_RCVD
+       LOOP_PDB_RCVD
+       LOOP_SCANNING_FABRIC
+       LOOP_FSCAN_DONE
+       LOOP_SCANNING_LOOP
+       LOOP_LSCAN_DONE
+       LOOP_SYNCING_PDB
+       LOOP_READY
+
+When the core code initializes the QLogic firmware, it sets the loop
+state to LOOP_NIL. The first 'LIP Received' asynchronous event sets state
+to LOOP_LIP_RCVD. This should be followed by a "Port Database Changed"
+asynchronous event which will set the state to LOOP_PDB_RCVD. Each of
+these states, when entered, causes an isp_async event call to the
+machine dependent layers with the ISPASYNC_CHANGE_NOTIFY code.
+
+After the state of LOOP_PDB_RCVD is reached, the internal core function
+isp_scan_fabric (reachable via isp_control(..ISPCTL_SCAN_FABRIC)) will,
+if the connection is to a fabric, use Simple Name Server mailbox mediated
+commands to dump the entire fabric contents. For each new entity, an
+isp_async event will be generated that says a Fabric device has arrived
+(ISPASYNC_FABRIC_DEV). The function that isp_async must perform in this
+step is to insert possibly remove devices that it wants to have the
+QLogic firmware log into (at LOOP_SYNCING_PDB state level)).
+
+After this has occurred, the state LOOP_FSCAN_DONE is set, and then the
+internal function isp_scan_loop (isp_control(...ISPCTL_SCAN_LOOP)) can
+be called which will then scan for any local (FC-AL) entries by asking
+for each possible local loop id the QLogic firmware for a Port Database
+entry. It's at this level some entries cached locally are purged
+or shifting loopids are managed (see section 5.9.4).
+
+The final step after this is to call the internal function isp_pdb_sync
+(isp_control(..ISPCTL_PDB_SYNC)). The purpose of this function is to
+then perform the PLOGI/PRLI functions for fabric devices. The next state
+entered after this is LOOP_READY, which means that the driver is ready
+to process commands to send to Fibre Channel devices.
+
+5.9.3 Fibre Channel variants of Initiator Mode Code Flow
+
+The code flow in isp_start for Fibre Channel devices is the same as it is
+for SCSI devices, but with a notable exception.
+
+Maintained within the fibre channel specific portion of the driver soft
+state structure is a distillation of the existing population of both
+local loop and fabric devices. Because Loop IDs can shift on a local
+loop but we wish to retain a 'constant' Target ID (see 5.9.4), this
+is indexed directly via the Target ID for the command (XS_TGT(xs)).
+
+If there is a valid entry for this Target ID, the command is started
+(with the stored 'Loop ID'). If not the command is completed with
+the error that is just like a SCSI Selection Timeout error.
+
+This code is currently somewhat in transition. Some platforms to
+do firmware and loop state management (as described above) at this
+point. Other platforms manage this from the machine dependent layers. The
+important function to watch in this respect is isp_fc_runstate (in
+isp_inline.h).
+
+5.9.4 "Target" in Fibre Channel is a fixed virtual construct
+
+Very few systems can cope with the notion that "Target" for a disk
+device can change while you're using it. But one of the properties of
+for arbitrated loop is that the physical bus address for a loop member
+(the AL_PA) can change depending on when and how things are inserted in
+the loop.
+
+To illustrate this, let's take an example. Let's say you start with a
+loop that has 5 disks in it. At boot time, the system will likely find
+them and see them in this order:
+
+disk#   Loop ID         Target ID
+disk0   0               0
+disk1   1               1
+disk2   2               2
+disk3   3               3
+disk4   4               4
+
+The driver uses 'Loop ID' when it forms requests to send a comamnd to
+each disk. However, it reports to NetBSD that things exist as 'Target
+ID'. As you can see here, there is perfect correspondence between disk,
+Loop ID and Target ID.
+
+Let's say you add a new disk between disk2 and disk3 while things are
+running. You don't really often see this, but you *could* see this where
+the loop has to renegotiate, and you end up with:
+
+disk#   Loop ID         Target ID
+disk0   0               0
+disk1   1               1
+disk2   2               2
+diskN   3               ?
+disk3   4               ?
+disk4   5               ?
+
+Clearly, you don't want disk3 and disk4's "Target ID" to change while you're
+running since currently mounted filesystems will get trashed.
+
+What the driver is supposed to do (this is the function of isp_scan_loop),
+is regenerate things such that the following then occurs:
+
+disk#   Loop ID         Target ID
+disk0   0               0
+disk1   1               1
+disk2   2               2
+diskN   3               5
+disk3   4               3
+disk4   5               4
+
+So, "Target" is a virtual entity that is maintained while you're running.
+
+6. Glossary
+
+HBA - Host Bus Adapter
+
+SCSI - Small Computer
+
+7. References
+
+Various URLs of interest:
+
+http://www.netbsd.org          -       NetBSD's Web Page
+http://www.openbsd.org         -       OpenBSD's Web Page
+http://www.freebsd.org         -       FreeBSD's Web Page
+
+http://www.t10.org             -       ANSI SCSI Commitee's Web Page
+                                       (SCSI Specs)
+http://www.t11.org             -       NCITS Device Interface Web Page
+                                       (Fibre Channel Specs)
diff --git a/sys/dev/disk/isp/Hardware.txt b/sys/dev/disk/isp/Hardware.txt
new file mode 100644 (file)
index 0000000..522f435
--- /dev/null
@@ -0,0 +1,302 @@
+/* $FreeBSD: src/sys/dev/isp/Hardware.txt,v 1.2 2003/01/01 18:48:50 schweikh Exp $ */
+
+       Hardware that is Known To or Should Work with This Driver
+
+
+0. Intro
+
+       This is not an endorsement for hardware vendors (there will be
+       no "where to buy" URLs here with a couple of exception). This
+       is simply a list of things I know work, or should work, plus
+       maybe a couple of notes as to what you should do to make it
+       work. Corrections accepted. Even better would be to send me
+       hardware to I can test it.
+
+       I'll put a rough range of costs in US$ that I know about. No doubt
+       it'll differ from your expectations.
+
+1. HBAs
+
+Qlogic 2100, 2102
+       2200, 2202, 2204
+
+       There are various suffices that indicate copper or optical
+       connectors, or 33 vs. 66MHz PCI bus operation. None of these
+       have a software impact.
+
+       Approx cost: 1K$ for a 2200
+
+Qlogic 2300, 2312
+
+       These are the new 2-Gigabit cards. Optical only.
+
+       Approx cost: ??????
+
+
+Antares        P-0033, P-0034, P-0036
+
+       There many other vendors that use the Qlogic 2X00 chipset. Some older
+       2100 boards (not on this list) have a bug in the ROM that causes a
+       failure to download newer firmware that is larger than 0x7fff words.
+
+       Approx cost: 850$ for a P-0036
+
+
+
+       In general, the 2200 class chip is to be preferred.
+
+
+2. Hubs
+
+Vixel 1000
+Vixel 2000
+       Of the two, the 1000 (7 ports, vs. 12 ports) has had fewer problems-
+       it's an old workhorse.
+
+
+       Approx cost: 1.5K$ for Vixel 1000, 2.5K$ for 2000
+
+Gadzoox Cappellix 3000
+       Don't forget to use telnet to configure the Cappellix ports
+       to the role you're using them for- otherwise things don't
+       work well at all.
+
+       (cost: I have no idea... certainly less than a switch)
+
+3. Switches
+
+Brocade Silkworm II
+Brocade 2400
+(other brocades should be fine)
+
+       Especially with revision 2 or higher f/w, this is now best
+       of breed for fabrics or segmented loop (which Brocade
+       calls "QuickLoop").
+
+       For the Silkworm II, set operating mode to "Tachyon" (mode 3).
+
+       The web interace isn't good- but telnet is what I prefer anyhow.
+
+       You can't connect a Silkworm II and the other Brocades together
+       as E-ports to make a large fabric (at least with the f/w *I*
+       had for the Silkworm II).
+
+       Approx cost of a Brocade 2400 with no GBICs is about 8K$ when
+       I recently checked the US Government SEWP price list- no doubt
+       it'll be a bit more for others. I'd assume around 10K$.
+
+ANCOR SA-8
+
+       This also is a fine switch, but you have to use a browser
+       with working java to manage it- which is a bit of a pain.
+       This also supports fabric and segmented loop.
+
+       These switches don't form E-ports with each other for a larger
+       fabric.
+
+       (cost: no idea)
+
+McData (model unknown)
+
+       I tried one exactly once for 30 minutes. Seemed to work once
+       I added the "register FC4 types" command to the driver.
+
+       (cost: very very expensive, 40K$ plus)
+
+4. Cables/GBICs
+
+       Multimode optical is adequate for Fibre Channel- the same cable is
+       used for Gigabit Ethernet.
+
+       Copper DB-9 and Copper HSS-DC connectors are also fine. Copper &&
+       Optical both are rated to 1.026Gbit- copper is naturally shorter
+       (the longest I've used is a 15meter cable but it's supposed to go
+       longer).
+
+       The reason to use copper instead of optical is that if step on one of
+       the really fat DB-9 cables you can get, it'll survive. Optical usually
+       dies quickly if you step on it.
+
+       Approx cost: I don't know what optical is- you can expect to pay maybe
+       a 100$ for a 3m copper cable.
+
+GBICs-
+
+       I use Finisar copper and IBM Opticals.
+
+       Approx Cost: Copper GBICs are 70$ each. Opticals are twice that or more.
+
+
+Vendor: (this is the one exception I'll make because it turns out to be
+       an incredible pain to find FC copper cabling and GBICs- the source I
+       use for GBICs and copper cables is http://www.scsi-cables.com)
+
+
+Other:
+       There now is apparently a source for little connector boards
+       to connect to bare drives: http://www.cinonic.com.
+
+
+5. Storage JBODs/RAID
+
+JMR 4-Bay
+
+       Rinky-tink, but a solid 4 bay loop only entry model.
+
+       I paid 1000$ for mine- overprice, IMO.
+
+JMR Fortra
+
+       I rather like this box. The blue LEDs are a very nice touch- you
+       can see them very clearly from 50 feet away.
+
+       I paid 2000$ for one used.
+
+Sun A5X00
+
+       Very expensive (in my opinion) but well crafted. Has two SES
+       instances, so you can use the ses driver (and the example
+       code in /usr/share/examples) for power/thermal/slot monitoring.
+
+       Approx Cost: The last I saw for a price list item on this was 22K$
+       for an unpopulated (no disk drive) A5X00.
+
+
+DataDirect E1000 RAID
+
+       Don't connect both SCSI and FC interfaces at the same time- a SCSI
+       reset will cause the DataDirect to think you want to use the SCSI
+       interface and a LIP on the FC interface will cause it to think you
+       want to use the FC interface. Use only one connector at a time so
+       both you and the DataDirect are sure about what you want.
+
+       Cost: I have no idea.
+
+Veritas ServPoint
+
+       This is a software storage virtualization engine that
+       runs on Sparc/Solaris in target mode for frontend
+       and with other FC or SCSI as the backend storage. FreeBSD
+       has been used extensively to test it.
+
+
+       Cost: I have no idea.
+
+6. Disk Drives
+
+       I have used lots of different Seagate and a few IBM drives and
+       typically have had few problems with them. These are the bare
+       drives with 40-pin SCA connectors in back. They go into the JBODs
+       you assemble.
+
+       Seagate does make, but I can no longer find, a little paddleboard
+       single drive connector that goes from DB-9 FC to the 40-pin SCA
+       connector- primarily for you to try and evaluate a single FC drive.
+
+       All FC-AL disk drives are dual ported (i.e., have separte 'A' and
+       'B' ports- which are completely separate loops). This seems to work
+       reasonably enough, but I haven't tested it much. It really depends
+       on the JBOD you put them to carry this dual port to the outside
+       world. The JMR boxes have it. The Sun A5X00 you have to pay for
+       an extra IB card to carry it out.
+
+       Approx Cost: You'll find that FC drives are the same cost if not
+       slightly cheaper than the equivalent Ultra3 SCSI drives.
+
+7. Recommended Configurations
+
+These are recommendations that are biased toward the cautious side. They
+do not represent formal engineering commitments- just suggestions as to
+what I would expect to work.
+
+A. The simpletst form of a connection topology I can suggest for
+a small SAN (i.e., replacement for SCSI JBOD/RAID):
+
+HOST
+2xxx <----------> Single Unit of Storage (JBOD, RAID)
+
+This is called a PL_DA (Private Loop, Direct Attach) topology.
+
+B. The next most simple form of a connection topology I can suggest for
+a medium local SAN (where you do not plan to do dynamic insertion
+and removal of devices while I/Os are active):
+
+HOST
+2xxx <----------> +--------
+                  | Vixel |
+                  | 1000  |
+                  |       +<---> Storage
+                  |       |
+                  |       +<---> Storage
+                  |       |
+                  |       +<---> Storage
+                  --------
+
+This is a Private Loop topology. Remember that this can get very unstable
+if you make it too long. A good practice is to try it in a staged fashion.
+
+It is possible with some units to "daisy chain", e.g.:
+
+HOST
+2xxx <----------> (JBOD, RAID) <--------> (JBOD, RAID)
+
+In practice I have had poor results with these configurations. They *should*
+work fine, but for both the JMR and the Sun A5X00 I tend to get LIP storms
+and so the second unit just isn't seen and the loop isn't stable.
+
+Now, this could simply be my lack of clean, newer, h/w (or, in general,
+a lack of h/w), but I would recommend the use of a hub if you want to
+stay with Private Loop and have more than one FC target.
+
+You should also note this can begin to be the basis for a shared SAN
+solution. For example, the above configuration can be extended to be:
+
+HOST
+2xxx <----------> +--------
+                  | Vixel |
+                  | 1000  |
+                  |       +<---> Storage
+                  |       |
+                  |       +<---> Storage
+                  |       |
+                  |       +<---> Storage
+HOST              |       |
+2xxx <----------> +--------
+
+However, note that there is nothing to mediate locking of devices, and
+it is also conceivable that the reboot of one host can, by causing
+a LIP storm, cause problems with the I/Os from the other host.
+(in other words, this topology hasn't really been made safe yet for
+this driver).
+
+D. You can repeat the topology in #B with a switch that is set to be
+in segmented loop mode. This avoids LIPs propagating where you don't
+want them to- and this makes for a much more reliable, if more expensive,
+SAN.
+
+E. The next level of complexity is a Switched Fabric. The following topology
+is good when you start to begin to get to want more performance. Private
+and Public Arbitrated Loop, while 100MB/s, is a shared medium. Direct
+connections to a switch can run full-duplex at full speed.
+
+HOST
+2xxx <----------> +---------
+                  | Brocade|
+                  | 2400   |
+                  |        +<---> Storage
+                  |        |
+                  |        +<---> Storage
+                  |        |
+                  |        +<---> Storage
+HOST              |        |
+2xxx <----------> +---------
+
+
+I would call this the best configuration available now. It can expand
+substantially if you cascade switches.
+
+There is a hard limit of about 253 devices for each Qlogic HBA- and the
+fabric login policy is simplistic (log them in as you find them). If
+somebody actually runs into a configuration that's larger, let me know
+and I'll work on some tools that would allow you some policy choices
+as to which would be interesting devices to actually connect to.
diff --git a/sys/dev/disk/isp/Makefile b/sys/dev/disk/isp/Makefile
new file mode 100644 (file)
index 0000000..4182dd7
--- /dev/null
@@ -0,0 +1,8 @@
+# $FreeBSD: src/sys/modules/isp/Makefile,v 1.4 2008/05/04 14:59:24 marius Exp $
+
+KMOD=  isp
+SRCS=  bus_if.h device_if.h pci_if.h \
+       opt_cam.h opt_ddb.h opt_isp.h \
+       isp.c isp_library.c isp_target.c isp_freebsd.c isp_pci.c
+
+.include <bsd.kmod.mk>
index 4d388ca..836fca9 100644 (file)
@@ -1,32 +1,35 @@
-/* $FreeBSD: src/sys/dev/isp/isp.c,v 1.41.2.23 2002/10/11 17:34:28 mjacob Exp $ */
-/*
- * Machine and OS Independent (well, as best as possible)
- * code for the Qlogic ISP SCSI adapters.
+/*-
+ *  Copyright (c) 1997-2009 by Matthew Jacob
+ *  All rights reserved.
  *
- * Copyright (c) 1997, 1998, 1999, 2000, 2001 by Matthew Jacob
- * Feral Software
- * All rights reserved.
+ *  Redistribution and use in source and binary forms, with or without
+ *  modification, are permitted provided that the following conditions
+ *  are met:
  *
- * 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 immediately at the beginning of the file, without modification,
- *    this list of conditions, and the following disclaimer.
- * 2. The name of the author may not be used to endorse or promote products
- *    derived from this software without specific prior written permission.
+ *  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.
+ *  THIS SOFTWARE IS PROVIDED BY 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 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.
+ *
+ * $FreeBSD: src/sys/dev/isp/isp.c,v 1.168 2011/11/16 02:52:24 mjacob Exp $
+ */
+
+/*
+ * Machine and OS Independent (well, as best as possible)
+ * code for the Qlogic ISP SCSI and FC-SCSI adapters.
  */
 
 /*
 /*
  * Include header file appropriate for platform we're building on.
  */
-
-#ifdef __NetBSD__
-#include <dev/ic/isp_netbsd.h>
-#endif
-#if defined(__DragonFly__) || defined(__FreeBSD__)
-#include "isp_freebsd.h"
-#endif
-#ifdef __OpenBSD__
-#include <dev/ic/isp_openbsd.h>
-#endif
-#ifdef __linux__
-#include "isp_linux.h"
-#endif
-#ifdef __svr4__
-#include "isp_solaris.h"
-#endif
+#include <dev/disk/isp/isp_freebsd.h>
 
 /*
  * General defines
  */
-
 #define        MBOX_DELAY_COUNT        1000000 / 100
+#define        ISP_MARK_PORTDB(a, b, c)                                \
+    isp_prt(isp, ISP_LOGSANCFG,                                \
+       "Chan %d ISP_MARK_PORTDB@LINE %d", b, __LINE__);        \
+    isp_mark_portdb(a, b, c)
 
 /*
  * Local static data
  */
-static const char portshift[] =
-    "Target %d Loop ID 0x%x (Port 0x%x) => Loop 0x%x (Port 0x%x)";
-static const char portdup[] =
-    "Target %d duplicates Target %d- killing off both";
-static const char retained[] =
-    "Retaining Loop ID 0x%x for Target %d (Port 0x%x)";
-static const char lretained[] =
-    "Retained login of Target %d (Loop ID 0x%x) Port 0x%x";
-static const char plogout[] =
-    "Logging out Target %d at Loop ID 0x%x (Port 0x%x)";
-static const char plogierr[] =
-    "Command Error in PLOGI for Port 0x%x (0x%x)";
-static const char nopdb[] =
-    "Could not get PDB for Device @ Port 0x%x";
-static const char pdbmfail1[] =
-    "PDB Loop ID info for Device @ Port 0x%x does not match up (0x%x)";
-static const char pdbmfail2[] =
-    "PDB Port info for Device @ Port 0x%x does not match up (0x%x)";
-static const char ldumped[] =
-    "Target %d (Loop ID 0x%x) Port 0x%x dumped after login info mismatch";
-static const char notresp[] =
-  "Not RESPONSE in RESPONSE Queue (type 0x%x) @ idx %d (next %d) nlooked %d";
-static const char xact1[] =
-    "HBA attempted queued transaction with disconnect not set for %d.%d.%d";
-static const char xact2[] =
-    "HBA attempted queued transaction to target routine %d on target %d bus %d";
-static const char xact3[] =
-    "HBA attempted queued cmd for %d.%d.%d when queueing disabled";
-static const char pskip[] =
-    "SCSI phase skipped for target %d.%d.%d";
-static const char topology[] =
-    "Loop ID %d, AL_PA 0x%x, Port ID 0x%x, Loop State 0x%x, Topology '%s'";
-static const char swrej[] =
-    "Fabric Nameserver rejected %s (Reason=0x%x Expl=0x%x) for Port ID 0x%x";
-static const char finmsg[] =
-    "(%d.%d.%d): FIN dl%d resid %d STS 0x%x SKEY %c XS_ERR=0x%x";
-static const char sc0[] =
-    "%s CHAN %d FTHRSH %d IID %d RESETD %d RETRYC %d RETRYD %d ASD 0x%x";
-static const char sc1[] =
-    "%s RAAN 0x%x DLAN 0x%x DDMAB 0x%x CDMAB 0x%x SELTIME %d MQD %d";
-static const char sc2[] = "%s CHAN %d TGT %d FLAGS 0x%x 0x%x/0x%x";
-static const char sc3[] = "Generated";
+static const char fconf[] = "Chan %d PortDB[%d] changed:\n current =(0x%x@0x%06x 0x%08x%08x 0x%08x%08x)\n database=(0x%x@0x%06x 0x%08x%08x 0x%08x%08x)";
+static const char notresp[] = "Not RESPONSE in RESPONSE Queue (type 0x%x) @ idx %d (next %d) nlooked %d";
+static const char topology[] = "Chan %d WWPN 0x%08x%08x PortID 0x%06x N-Port Handle %d, Connection '%s'";
 static const char sc4[] = "NVRAM";
-static const char bun[] =
-    "bad underrun for %d.%d (count %d, resid %d, status %s)";
+static const char bun[] = "bad underrun (count %d, resid %d, status %s)";
+static const char lipd[] = "Chan %d LIP destroyed %d active commands";
+static const char sacq[] = "unable to acquire scratch area";
+
+static const uint8_t alpa_map[] = {
+       0xef, 0xe8, 0xe4, 0xe2, 0xe1, 0xe0, 0xdc, 0xda,
+       0xd9, 0xd6, 0xd5, 0xd4, 0xd3, 0xd2, 0xd1, 0xce,
+       0xcd, 0xcc, 0xcb, 0xca, 0xc9, 0xc7, 0xc6, 0xc5,
+       0xc3, 0xbc, 0xba, 0xb9, 0xb6, 0xb5, 0xb4, 0xb3,
+       0xb2, 0xb1, 0xae, 0xad, 0xac, 0xab, 0xaa, 0xa9,
+       0xa7, 0xa6, 0xa5, 0xa3, 0x9f, 0x9e, 0x9d, 0x9b,
+       0x98, 0x97, 0x90, 0x8f, 0x88, 0x84, 0x82, 0x81,
+       0x80, 0x7c, 0x7a, 0x79, 0x76, 0x75, 0x74, 0x73,
+       0x72, 0x71, 0x6e, 0x6d, 0x6c, 0x6b, 0x6a, 0x69,
+       0x67, 0x66, 0x65, 0x63, 0x5c, 0x5a, 0x59, 0x56,
+       0x55, 0x54, 0x53, 0x52, 0x51, 0x4e, 0x4d, 0x4c,
+       0x4b, 0x4a, 0x49, 0x47, 0x46, 0x45, 0x43, 0x3c,
+       0x3a, 0x39, 0x36, 0x35, 0x34, 0x33, 0x32, 0x31,
+       0x2e, 0x2d, 0x2c, 0x2b, 0x2a, 0x29, 0x27, 0x26,
+       0x25, 0x23, 0x1f, 0x1e, 0x1d, 0x1b, 0x18, 0x17,
+       0x10, 0x0f, 0x08, 0x04, 0x02, 0x01, 0x00
+};
 
 /*
  * Local function prototypes.
  */
-static int isp_parse_async(struct ispsoftc *, u_int16_t);
-static int isp_handle_other_response(struct ispsoftc *, int, isphdr_t *,
-    u_int16_t *);
-static void
-isp_parse_status(struct ispsoftc *, ispstatusreq_t *, XS_T *);
-static void isp_fastpost_complete(struct ispsoftc *, u_int16_t);
-static int isp_mbox_continue(struct ispsoftc *);
-static void isp_scsi_init(struct ispsoftc *);
-static void isp_scsi_channel_init(struct ispsoftc *, int);
-static void isp_fibre_init(struct ispsoftc *);
-static void isp_mark_getpdb_all(struct ispsoftc *);
-static int isp_getmap(struct ispsoftc *, fcpos_map_t *);
-static int isp_getpdb(struct ispsoftc *, int, isp_pdb_t *);
-static u_int64_t isp_get_portname(struct ispsoftc *, int, int);
-static int isp_fclink_test(struct ispsoftc *, int);
-static char *isp2100_fw_statename(int);
-static int isp_pdb_sync(struct ispsoftc *);
-static int isp_scan_loop(struct ispsoftc *);
-static int isp_fabric_mbox_cmd(struct ispsoftc *, mbreg_t *);
-static int isp_scan_fabric(struct ispsoftc *, int);
-static void isp_register_fc4_type(struct ispsoftc *);
-static void isp_fw_state(struct ispsoftc *);
-static void isp_mboxcmd_qnw(struct ispsoftc *, mbreg_t *, int);
-static void isp_mboxcmd(struct ispsoftc *, mbreg_t *, int);
-
-static void isp_update(struct ispsoftc *);
-static void isp_update_bus(struct ispsoftc *, int);
-static void isp_setdfltparm(struct ispsoftc *, int);
-static int isp_read_nvram(struct ispsoftc *);
-static void isp_rdnvram_word(struct ispsoftc *, int, u_int16_t *);
-static void isp_parse_nvram_1020(struct ispsoftc *, u_int8_t *);
-static void isp_parse_nvram_1080(struct ispsoftc *, int, u_int8_t *);
-static void isp_parse_nvram_12160(struct ispsoftc *, int, u_int8_t *);
-static void isp_parse_nvram_2100(struct ispsoftc *, u_int8_t *);
+static int isp_parse_async(ispsoftc_t *, uint16_t);
+static int isp_parse_async_fc(ispsoftc_t *, uint16_t);
+static int isp_handle_other_response(ispsoftc_t *, int, isphdr_t *, uint32_t *);
+static void isp_parse_status(ispsoftc_t *, ispstatusreq_t *, XS_T *, long *); static void
+isp_parse_status_24xx(ispsoftc_t *, isp24xx_statusreq_t *, XS_T *, long *);
+static void isp_fastpost_complete(ispsoftc_t *, uint32_t);
+static int isp_mbox_continue(ispsoftc_t *);
+static void isp_scsi_init(ispsoftc_t *);
+static void isp_scsi_channel_init(ispsoftc_t *, int);
+static void isp_fibre_init(ispsoftc_t *);
+static void isp_fibre_init_2400(ispsoftc_t *);
+static void isp_mark_portdb(ispsoftc_t *, int, int);
+static int isp_plogx(ispsoftc_t *, int, uint16_t, uint32_t, int, int);
+static int isp_port_login(ispsoftc_t *, uint16_t, uint32_t);
+static int isp_port_logout(ispsoftc_t *, uint16_t, uint32_t);
+static int isp_getpdb(ispsoftc_t *, int, uint16_t, isp_pdb_t *, int);
+static void isp_dump_chip_portdb(ispsoftc_t *, int, int);
+static uint64_t isp_get_wwn(ispsoftc_t *, int, int, int);
+static int isp_fclink_test(ispsoftc_t *, int, int);
+static int isp_pdb_sync(ispsoftc_t *, int);
+static int isp_scan_loop(ispsoftc_t *, int);
+static int isp_gid_ft_sns(ispsoftc_t *, int);
+static int isp_gid_ft_ct_passthru(ispsoftc_t *, int);
+static int isp_scan_fabric(ispsoftc_t *, int);
+static int isp_login_device(ispsoftc_t *, int, uint32_t, isp_pdb_t *, uint16_t *);
+static int isp_register_fc4_type(ispsoftc_t *, int);
+static int isp_register_fc4_type_24xx(ispsoftc_t *, int);
+static uint16_t isp_nxt_handle(ispsoftc_t *, int, uint16_t);
+static void isp_fw_state(ispsoftc_t *, int);
+static void isp_mboxcmd_qnw(ispsoftc_t *, mbreg_t *, int);
+static void isp_mboxcmd(ispsoftc_t *, mbreg_t *);
+
+static void isp_spi_update(ispsoftc_t *, int);
+static void isp_setdfltsdparm(ispsoftc_t *);
+static void isp_setdfltfcparm(ispsoftc_t *, int);
+static int isp_read_nvram(ispsoftc_t *, int);
+static int isp_read_nvram_2400(ispsoftc_t *, uint8_t *);
+static void isp_rdnvram_word(ispsoftc_t *, int, uint16_t *);
+static void isp_rd_2400_nvram(ispsoftc_t *, uint32_t, uint32_t *);
+static void isp_parse_nvram_1020(ispsoftc_t *, uint8_t *);
+static void isp_parse_nvram_1080(ispsoftc_t *, int, uint8_t *);
+static void isp_parse_nvram_12160(ispsoftc_t *, int, uint8_t *);
+static void isp_parse_nvram_2100(ispsoftc_t *, uint8_t *);
+static void isp_parse_nvram_2400(ispsoftc_t *, uint8_t *);
 
 /*
  * Reset Hardware.
@@ -157,14 +139,20 @@ static void isp_parse_nvram_2100(struct ispsoftc *, u_int8_t *);
  */
 
 void
-isp_reset(struct ispsoftc *isp)
+isp_reset(ispsoftc_t *isp, int do_load_defaults)
 {
        mbreg_t mbs;
-       u_int16_t code_org;
+       uint32_t code_org, val;
        int loops, i, dodnld = 1;
-       char *btype = "????";
+       const char *btype = "????";
+       static const char dcrc[] = "Downloaded RISC Code Checksum Failure";
 
        isp->isp_state = ISP_NILSTATE;
+       if (isp->isp_dead) {
+               isp_shutdown(isp);
+               ISP_DISABLE_INTS(isp);
+               return;
+       }
 
        /*
         * Basic types (SCSI, FibreChannel and PCI or SBus)
@@ -176,51 +164,44 @@ isp_reset(struct ispsoftc *isp)
         * for SCSI adapters and do other settings for the 2100.
         */
 
+       ISP_DISABLE_INTS(isp);
+
        /*
-        * Get the current running firmware revision out of the
-        * chip before we hit it over the head (if this is our
-        * first time through). Note that we store this as the
-        * 'ROM' firmware revision- which it may not be. In any
-        * case, we don't really use this yet, but we may in
-        * the future.
+        * Pick an initial maxcmds value which will be used
+        * to allocate xflist pointer space. It may be changed
+        * later by the firmware.
         */
-       if (isp->isp_touched == 0) {
-               /*
-                * First see whether or not we're sitting in the ISP PROM.
-                * If we've just been reset, we'll have the string "ISP   "
-                * spread through outgoing mailbox registers 1-3. We do
-                * this for PCI cards because otherwise we really don't
-                * know what state the card is in and we could hang if
-                * we try this command otherwise.
-                *
-                * For SBus cards, we just do this because they almost
-                * certainly will be running firmware by now.
-                */
-               if (ISP_READ(isp, OUTMAILBOX1) != 0x4953 ||
-                   ISP_READ(isp, OUTMAILBOX2) != 0x5020 ||
-                   ISP_READ(isp, OUTMAILBOX3) != 0x2020) {
-                       /*
-                        * Just in case it was paused...
-                        */
-                       ISP_WRITE(isp, HCCR, HCCR_CMD_RELEASE);
-                       mbs.param[0] = MBOX_ABOUT_FIRMWARE;
-                       isp_mboxcmd(isp, &mbs, MBLOGNONE);
-                       if (mbs.param[0] == MBOX_COMMAND_COMPLETE) {
-                               isp->isp_romfw_rev[0] = mbs.param[1];
-                               isp->isp_romfw_rev[1] = mbs.param[2];
-                               isp->isp_romfw_rev[2] = mbs.param[3];
-                       }
-               }
-               isp->isp_touched = 1;
+       if (IS_24XX(isp)) {
+               isp->isp_maxcmds = 4096;
+       } else if (IS_2322(isp)) {
+               isp->isp_maxcmds = 2048;
+       } else if (IS_23XX(isp) || IS_2200(isp)) {
+               isp->isp_maxcmds = 1024;
+       } else {
+               isp->isp_maxcmds = 512;
        }
 
-       DISABLE_INTS(isp);
+       /*
+        * Set up DMA for the request and response queues.
+        *
+        * We do this now so we can use the request queue
+        * for dma to load firmware from.
+        */
+       if (ISP_MBOXDMASETUP(isp) != 0) {
+               isp_prt(isp, ISP_LOGERR, "Cannot setup DMA");
+               return;
+       }
 
        /*
         * Set up default request/response queue in-pointer/out-pointer
         * register indices.
         */
-       if (IS_23XX(isp)) {
+       if (IS_24XX(isp)) {
+               isp->isp_rqstinrp = BIU2400_REQINP;
+               isp->isp_rqstoutrp = BIU2400_REQOUTP;
+               isp->isp_respinrp = BIU2400_RSPINP;
+               isp->isp_respoutrp = BIU2400_RSPOUTP;
+       } else if (IS_23XX(isp)) {
                isp->isp_rqstinrp = BIU_REQINP;
                isp->isp_rqstoutrp = BIU_REQOUTP;
                isp->isp_respinrp = BIU_RSPINP;
@@ -236,7 +217,13 @@ isp_reset(struct ispsoftc *isp)
         * Put the board into PAUSE mode (so we can read the SXP registers
         * or write FPM/FBM registers).
         */
-       ISP_WRITE(isp, HCCR, HCCR_CMD_PAUSE);
+       if (IS_24XX(isp)) {
+               ISP_WRITE(isp, BIU2400_HCCR, HCCR_2400_CMD_CLEAR_HOST_INT);
+               ISP_WRITE(isp, BIU2400_HCCR, HCCR_2400_CMD_CLEAR_RISC_INT);
+               ISP_WRITE(isp, BIU2400_HCCR, HCCR_2400_CMD_PAUSE);
+       } else {
+               ISP_WRITE(isp, HCCR, HCCR_CMD_PAUSE);
+       }
 
        if (IS_FC(isp)) {
                switch (isp->isp_type) {
@@ -252,31 +239,63 @@ isp_reset(struct ispsoftc *isp)
                case ISP_HA_FC_2312:
                        btype = "2312";
                        break;
+               case ISP_HA_FC_2322:
+                       btype = "2322";
+                       break;
+               case ISP_HA_FC_2400:
+                       btype = "2422";
+                       break;
+               case ISP_HA_FC_2500:
+                       btype = "2532";
+                       break;
                default:
                        break;
                }
-               /*
-                * While we're paused, reset the FPM module and FBM fifos.
-                */
-               ISP_WRITE(isp, BIU2100_CSR, BIU2100_FPM0_REGS);
-               ISP_WRITE(isp, FPM_DIAG_CONFIG, FPM_SOFT_RESET);
-               ISP_WRITE(isp, BIU2100_CSR, BIU2100_FB_REGS);
-               ISP_WRITE(isp, FBM_CMD, FBMCMD_FIFO_RESET_ALL);
-               ISP_WRITE(isp, BIU2100_CSR, BIU2100_RISC_REGS);
+
+               if (!IS_24XX(isp)) {
+                       /*
+                        * While we're paused, reset the FPM module and FBM
+                        * fifos.
+                        */
+                       ISP_WRITE(isp, BIU2100_CSR, BIU2100_FPM0_REGS);
+                       ISP_WRITE(isp, FPM_DIAG_CONFIG, FPM_SOFT_RESET);
+                       ISP_WRITE(isp, BIU2100_CSR, BIU2100_FB_REGS);
+                       ISP_WRITE(isp, FBM_CMD, FBMCMD_FIFO_RESET_ALL);
+                       ISP_WRITE(isp, BIU2100_CSR, BIU2100_RISC_REGS);
+               }
        } else if (IS_1240(isp)) {
-               sdparam *sdp = isp->isp_param;
+               sdparam *sdp;
+
                btype = "1240";
                isp->isp_clock = 60;
+               sdp = SDPARAM(isp, 0);
                sdp->isp_ultramode = 1;
-               sdp++;
+               sdp = SDPARAM(isp, 1);
                sdp->isp_ultramode = 1;
                /*
                 * XXX: Should probably do some bus sensing.
                 */
+       } else if (IS_ULTRA3(isp)) {
+               sdparam *sdp = isp->isp_param;
+
+               isp->isp_clock = 100;
+
+               if (IS_10160(isp))
+                       btype = "10160";
+               else if (IS_12160(isp))
+                       btype = "12160";
+               else
+                       btype = "<UNKLVD>";
+               sdp->isp_lvdmode = 1;
+
+               if (IS_DUALBUS(isp)) {
+                       sdp++;
+                       sdp->isp_lvdmode = 1;
+               }
        } else if (IS_ULTRA2(isp)) {
                static const char m[] = "bus %d is in %s Mode";
-               u_int16_t l;
-               sdparam *sdp = isp->isp_param;
+               uint16_t l;
+               sdparam *sdp = SDPARAM(isp, 0);
 
                isp->isp_clock = 100;
 
@@ -284,10 +303,6 @@ isp_reset(struct ispsoftc *isp)
                        btype = "1280";
                else if (IS_1080(isp))
                        btype = "1080";
-               else if (IS_10160(isp))
-                       btype = "10160";
-               else if (IS_12160(isp))
-                       btype = "12160";
                else
                        btype = "<UNKLVD>";
 
@@ -312,10 +327,10 @@ isp_reset(struct ispsoftc *isp)
                }
 
                if (IS_DUALBUS(isp)) {
-                       sdp++;
+                       sdp = SDPARAM(isp, 1);
                        l = ISP_READ(isp, SXP_PINS_DIFF|SXP_BANK1_SELECT);
                        l &= ISP1080_MODE_MASK;
-                       switch(l) {
+                       switch (l) {
                        case ISP1080_LVD_MODE:
                                sdp->isp_lvdmode = 1;
                                isp_prt(isp, ISP_LOGCONFIG, m, 1, "LVD");
@@ -337,7 +352,7 @@ isp_reset(struct ispsoftc *isp)
                        }
                }
        } else {
-               sdparam *sdp = isp->isp_param;
+               sdparam *sdp = SDPARAM(isp, 0);
                i = ISP_READ(isp, BIU_CONF0) & BIU_CONF0_HW_MASK;
                switch (i) {
                default:
@@ -399,7 +414,7 @@ isp_reset(struct ispsoftc *isp)
                        isp_prt(isp, ISP_LOGCONFIG, "Ultra Mode Capable");
                        sdp->isp_ultramode = 1;
                        /*
-                        * If we're in Ultra Mode, we have to be 60Mhz clock-
+                        * If we're in Ultra Mode, we have to be 60MHz clock-
                         * even for the SBus version.
                         */
                        isp->isp_clock = 60;
@@ -432,11 +447,9 @@ isp_reset(struct ispsoftc *isp)
         */
        ISP_RESET0(isp);
 
-again:
-
        /*
         * Hit the chip over the head with hammer,
-        * and give the ISP a chance to recover.
+        * and give it a chance to recover.
         */
 
        if (IS_SCSI(isp)) {
@@ -444,33 +457,65 @@ again:
                /*
                 * A slight delay...
                 */
-               USEC_DELAY(100);
+               ISP_DELAY(100);
 
                /*
                 * Clear data && control DMA engines.
                 */
-               ISP_WRITE(isp, CDMA_CONTROL,
-                   DMA_CNTRL_CLEAR_CHAN | DMA_CNTRL_RESET_INT);
-               ISP_WRITE(isp, DDMA_CONTROL,
-                   DMA_CNTRL_CLEAR_CHAN | DMA_CNTRL_RESET_INT);
+               ISP_WRITE(isp, CDMA_CONTROL, DMA_CNTRL_CLEAR_CHAN | DMA_CNTRL_RESET_INT);
+               ISP_WRITE(isp, DDMA_CONTROL, DMA_CNTRL_CLEAR_CHAN | DMA_CNTRL_RESET_INT);
 
 
+       } else if (IS_24XX(isp)) {
+               /*
+                * Stop DMA and wait for it to stop.
+                */
+               ISP_WRITE(isp, BIU2400_CSR, BIU2400_DMA_STOP|(3 << 4));
+               for (val = loops = 0; loops < 30000; loops++) {
+                       ISP_DELAY(10);
+                       val = ISP_READ(isp, BIU2400_CSR);
+                       if ((val & BIU2400_DMA_ACTIVE) == 0) {
+                               break;
+                       }
+               }
+               if (val & BIU2400_DMA_ACTIVE) {
+                       ISP_RESET0(isp);
+                       isp_prt(isp, ISP_LOGERR, "DMA Failed to Stop on Reset");
+                       return;
+               }
+               /*
+                * Hold it in SOFT_RESET and STOP state for 100us.
+                */
+               ISP_WRITE(isp, BIU2400_CSR, BIU2400_SOFT_RESET|BIU2400_DMA_STOP|(3 << 4));
+               ISP_DELAY(100);
+               for (loops = 0; loops < 10000; loops++) {
+                       ISP_DELAY(5);
+                       val = ISP_READ(isp, OUTMAILBOX0);
+               }
+               for (val = loops = 0; loops < 500000; loops ++) {
+                       val = ISP_READ(isp, BIU2400_CSR);
+                       if ((val & BIU2400_SOFT_RESET) == 0) {
+                               break;
+                       }
+               }
+               if (val & BIU2400_SOFT_RESET) {
+                       ISP_RESET0(isp);
+                       isp_prt(isp, ISP_LOGERR, "Failed to come out of reset");
+                       return;
+               }
        } else {
                ISP_WRITE(isp, BIU2100_CSR, BIU2100_SOFT_RESET);
                /*
                 * A slight delay...
                 */
-               USEC_DELAY(100);
+               ISP_DELAY(100);
 
                /*
                 * Clear data && control DMA engines.
                 */
-               ISP_WRITE(isp, CDMA2100_CONTROL,
-                       DMA_CNTRL2100_CLEAR_CHAN | DMA_CNTRL2100_RESET_INT);
-               ISP_WRITE(isp, TDMA2100_CONTROL,
-                       DMA_CNTRL2100_CLEAR_CHAN | DMA_CNTRL2100_RESET_INT);
-               ISP_WRITE(isp, RDMA2100_CONTROL,
-                       DMA_CNTRL2100_CLEAR_CHAN | DMA_CNTRL2100_RESET_INT);
+               ISP_WRITE(isp, CDMA2100_CONTROL, DMA_CNTRL2100_CLEAR_CHAN | DMA_CNTRL2100_RESET_INT);
+               ISP_WRITE(isp, TDMA2100_CONTROL, DMA_CNTRL2100_CLEAR_CHAN | DMA_CNTRL2100_RESET_INT);
+               ISP_WRITE(isp, RDMA2100_CONTROL, DMA_CNTRL2100_CLEAR_CHAN | DMA_CNTRL2100_RESET_INT);
        }
 
        /*
@@ -479,15 +524,21 @@ again:
        loops = MBOX_DELAY_COUNT;
        for (;;) {
                if (IS_SCSI(isp)) {
-                       if (!(ISP_READ(isp, BIU_ICR) & BIU_ICR_SOFT_RESET))
+                       if (!(ISP_READ(isp, BIU_ICR) & BIU_ICR_SOFT_RESET)) {
                                break;
+                       }
+               } else if (IS_24XX(isp)) {
+                       if (ISP_READ(isp, OUTMAILBOX0) == 0) {
+                               break;
+                       }
                } else {
                        if (!(ISP_READ(isp, BIU2100_CSR) & BIU2100_SOFT_RESET))
                                break;
                }
-               USEC_DELAY(100);
+               ISP_DELAY(100);
                if (--loops < 0) {
                        ISP_DUMPREGS(isp, "chip reset timed out");
+                       ISP_RESET0(isp);
                        return;
                }
        }
@@ -499,25 +550,41 @@ again:
 
        if (IS_SCSI(isp)) {
                ISP_WRITE(isp, BIU_CONF1, 0);
-       } else {
+       } else if (!IS_24XX(isp)) {
                ISP_WRITE(isp, BIU2100_CSR, 0);
        }
 
        /*
         * Reset RISC Processor
         */
-       ISP_WRITE(isp, HCCR, HCCR_CMD_RESET);
-       USEC_DELAY(100);
-       /* Clear semaphore register (just to be sure) */
-       ISP_WRITE(isp, BIU_SEMA, 0);
+       if (IS_24XX(isp)) {
+               ISP_WRITE(isp, BIU2400_HCCR, HCCR_2400_CMD_RESET);
+               ISP_WRITE(isp, BIU2400_HCCR, HCCR_2400_CMD_RELEASE);
+               ISP_WRITE(isp, BIU2400_HCCR, HCCR_2400_CMD_CLEAR_RESET);
+       } else {
+               ISP_WRITE(isp, HCCR, HCCR_CMD_RESET);
+               ISP_DELAY(100);
+               ISP_WRITE(isp, BIU_SEMA, 0);
+       }
 
        /*
-        * Establish some initial burst rate stuff.
-        * (only for the 1XX0 boards). This really should
-        * be done later after fetching from NVRAM.
+        * Post-RISC Reset stuff.
         */
-       if (IS_SCSI(isp)) {
-               u_int16_t tmp = isp->isp_mdvec->dv_conf1;
+       if (IS_24XX(isp)) {
+               for (val = loops = 0; loops < 5000000; loops++) {
+                       ISP_DELAY(5);
+                       val = ISP_READ(isp, OUTMAILBOX0);
+                       if (val == 0) {
+                               break;
+                       }
+               }
+               if (val != 0) {
+                       ISP_RESET0(isp);
+                       isp_prt(isp, ISP_LOGERR, "reset didn't clear");
+                       return;
+               }
+       } else if (IS_SCSI(isp)) {
+               uint16_t tmp = isp->isp_mdvec->dv_conf1;
                /*
                 * Busted FIFO. Turn off all but burst enables.
                 */
@@ -529,30 +596,41 @@ again:
                        ISP_SETBITS(isp, CDMA_CONF, DMA_ENABLE_BURST);
                        ISP_SETBITS(isp, DDMA_CONF, DMA_ENABLE_BURST);
                }
-#ifdef PTI_CARDS
-               if (((sdparam *) isp->isp_param)->isp_ultramode) {
-                       while (ISP_READ(isp, RISC_MTR) != 0x1313) {
-                               ISP_WRITE(isp, RISC_MTR, 0x1313);
-                               ISP_WRITE(isp, HCCR, HCCR_CMD_STEP);
+               if (SDPARAM(isp, 0)->isp_ptisp) {
+                       if (SDPARAM(isp, 0)->isp_ultramode) {
+                               while (ISP_READ(isp, RISC_MTR) != 0x1313) {
+                                       ISP_WRITE(isp, RISC_MTR, 0x1313);
+                                       ISP_WRITE(isp, HCCR, HCCR_CMD_STEP);
+                               }
+                       } else {
+                               ISP_WRITE(isp, RISC_MTR, 0x1212);
                        }
+                       /*
+                        * PTI specific register
+                        */
+                       ISP_WRITE(isp, RISC_EMB, DUAL_BANK);
                } else {
                        ISP_WRITE(isp, RISC_MTR, 0x1212);
                }
-               /*
-                * PTI specific register
-                */
-               ISP_WRITE(isp, RISC_EMB, DUAL_BANK)
-#else
-               ISP_WRITE(isp, RISC_MTR, 0x1212);
-#endif
+               ISP_WRITE(isp, HCCR, HCCR_CMD_RELEASE);
        } else {
                ISP_WRITE(isp, RISC_MTR2100, 0x1212);
                if (IS_2200(isp) || IS_23XX(isp)) {
                        ISP_WRITE(isp, HCCR, HCCR_2X00_DISABLE_PARITY_PAUSE);
                }
+               ISP_WRITE(isp, HCCR, HCCR_CMD_RELEASE);
        }
 
-       ISP_WRITE(isp, HCCR, HCCR_CMD_RELEASE); /* release paused processor */
+       ISP_WRITE(isp, isp->isp_rqstinrp, 0);
+       ISP_WRITE(isp, isp->isp_rqstoutrp, 0);
+       ISP_WRITE(isp, isp->isp_respinrp, 0);
+       ISP_WRITE(isp, isp->isp_respoutrp, 0);
+       if (IS_24XX(isp)) {
+               ISP_WRITE(isp, BIU2400_PRI_REQINP, 0);
+               ISP_WRITE(isp, BIU2400_PRI_REQOUTP, 0);
+               ISP_WRITE(isp, BIU2400_ATIO_RSPINP, 0);
+               ISP_WRITE(isp, BIU2400_ATIO_RSPOUTP, 0);
+       }
 
        /*
         * Do MD specific post initialization
@@ -562,16 +640,17 @@ again:
        /*
         * Wait for everything to finish firing up.
         *
-        * Avoid doing this on the 2312 because you can generate a PCI
+        * Avoid doing this on early 2312s because you can generate a PCI
         * parity error (chip breakage).
         */
-       if (IS_23XX(isp)) {
-               USEC_DELAY(5);
+       if (IS_2312(isp) && isp->isp_revision < 2) {
+               ISP_DELAY(100);
        } else {
                loops = MBOX_DELAY_COUNT;
                while (ISP_READ(isp, OUTMAILBOX0) == MBOX_BUSY) {
-                       USEC_DELAY(100);
+                       ISP_DELAY(100);
                        if (--loops < 0) {
+                               ISP_RESET0(isp);
                                isp_prt(isp, ISP_LOGERR,
                                    "MBOX_BUSY never cleared on reset");
                                return;
@@ -586,32 +665,44 @@ again:
         */
 
        /*
-        * Do some sanity checking.
+        * Do some sanity checking by running a NOP command.
+        * If it succeeds, the ROM firmware is now running.
         */
+       ISP_MEMZERO(&mbs, sizeof (mbs));
        mbs.param[0] = MBOX_NO_OP;
-       isp_mboxcmd(isp, &mbs, MBLOGALL);
+       mbs.logval = MBLOGALL;
+       isp_mboxcmd(isp, &mbs);
        if (mbs.param[0] != MBOX_COMMAND_COMPLETE) {
+               isp_prt(isp, ISP_LOGERR, "NOP command failed (%x)", mbs.param[0]);
+               ISP_RESET0(isp);
                return;
        }
 
-       if (IS_SCSI(isp)) {
+       /*
+        * Do some operational tests
+        */
+
+       if (IS_SCSI(isp) || IS_24XX(isp)) {
+               ISP_MEMZERO(&mbs, sizeof (mbs));
                mbs.param[0] = MBOX_MAILBOX_REG_TEST;
                mbs.param[1] = 0xdead;
                mbs.param[2] = 0xbeef;
                mbs.param[3] = 0xffff;
                mbs.param[4] = 0x1111;
                mbs.param[5] = 0xa5a5;
-               isp_mboxcmd(isp, &mbs, MBLOGALL);
+               mbs.param[6] = 0x0000;
+               mbs.param[7] = 0x0000;
+               mbs.logval = MBLOGALL;
+               isp_mboxcmd(isp, &mbs);
                if (mbs.param[0] != MBOX_COMMAND_COMPLETE) {
+                       ISP_RESET0(isp);
                        return;
                }
                if (mbs.param[1] != 0xdead || mbs.param[2] != 0xbeef ||
                    mbs.param[3] != 0xffff || mbs.param[4] != 0x1111 ||
                    mbs.param[5] != 0xa5a5) {
-                       isp_prt(isp, ISP_LOGERR,
-                           "Register Test Failed (0x%x 0x%x 0x%x 0x%x 0x%x)",
-                           mbs.param[1], mbs.param[2], mbs.param[3],
-                           mbs.param[4], mbs.param[5]);
+                       ISP_RESET0(isp);
+                       isp_prt(isp, ISP_LOGERR, "Register Test Failed (0x%x 0x%x 0x%x 0x%x 0x%x)", mbs.param[1], mbs.param[2], mbs.param[3], mbs.param[4], mbs.param[5]);
                        return;
                }
 
@@ -626,47 +717,242 @@ again:
         * whether we have f/w at all and whether a config flag
         * has disabled our download.
         */
-       if ((isp->isp_mdvec->dv_ispfw == NULL) ||
-           (isp->isp_confopts & ISP_CFG_NORELOAD)) {
+       if ((isp->isp_mdvec->dv_ispfw == NULL) || (isp->isp_confopts & ISP_CFG_NORELOAD)) {
                dodnld = 0;
        }
 
-       if (IS_23XX(isp))
+       if (IS_24XX(isp)) {
+               code_org = ISP_CODE_ORG_2400;
+       } else if (IS_23XX(isp)) {
                code_org = ISP_CODE_ORG_2300;
-       else
+       } else {
                code_org = ISP_CODE_ORG;
+       }
+
+       if (dodnld && IS_24XX(isp)) {
+               const uint32_t *ptr = isp->isp_mdvec->dv_ispfw;
+               int wordload;
 
-       if (dodnld) {
-               isp->isp_mbxworkp = (void *) &isp->isp_mdvec->dv_ispfw[1];
-               isp->isp_mbxwrk0 = isp->isp_mdvec->dv_ispfw[3] - 1;
-               isp->isp_mbxwrk1 = code_org + 1;
-               mbs.param[0] = MBOX_WRITE_RAM_WORD;
-               mbs.param[1] = code_org;
-               mbs.param[2] = isp->isp_mdvec->dv_ispfw[0];
-               isp_mboxcmd(isp, &mbs, MBLOGNONE);
-               if (mbs.param[0] != MBOX_COMMAND_COMPLETE) {
-                       isp_prt(isp, ISP_LOGERR,
-                           "F/W download failed at word %d",
-                           isp->isp_mbxwrk1 - code_org);
-                       dodnld = 0;
-                       goto again;
-               }
                /*
-                * Verify that it downloaded correctly.
+                * Keep loading until we run out of f/w.
                 */
-               mbs.param[0] = MBOX_VERIFY_CHECKSUM;
+               code_org = ptr[2];      /* 1st load address is our start addr */
+               wordload = 0;
+
+               for (;;) {
+                       uint32_t la, wi, wl;
+
+                       isp_prt(isp, ISP_LOGDEBUG0, "load 0x%x words of code at load address 0x%x", ptr[3], ptr[2]);
+
+                       wi = 0;
+                       la = ptr[2];
+                       wl = ptr[3];
+
+                       while (wi < ptr[3]) {
+                               uint32_t *cp;
+                               uint32_t nw;
+
+                               nw = ISP_QUEUE_SIZE(RQUEST_QUEUE_LEN(isp)) >> 2;
+                               if (nw > wl) {
+                                       nw = wl;
+                               }
+                               cp = isp->isp_rquest;
+                               for (i = 0; i < nw; i++) {
+                                       ISP_IOXPUT_32(isp,  ptr[wi++], &cp[i]);
+                                       wl--;
+                               }
+                               MEMORYBARRIER(isp, SYNC_REQUEST, 0, ISP_QUEUE_SIZE(RQUEST_QUEUE_LEN(isp)), -1);
+       again:
+                               ISP_MEMZERO(&mbs, sizeof (mbs));
+                               if (la < 0x10000 && nw < 0x10000) {
+                                       mbs.param[0] = MBOX_LOAD_RISC_RAM_2100;
+                                       mbs.param[1] = la;
+                                       mbs.param[2] = DMA_WD1(isp->isp_rquest_dma);
+                                       mbs.param[3] = DMA_WD0(isp->isp_rquest_dma);
+                                       mbs.param[4] = nw;
+                                       mbs.param[6] = DMA_WD3(isp->isp_rquest_dma);
+                                       mbs.param[7] = DMA_WD2(isp->isp_rquest_dma);
+                                       isp_prt(isp, ISP_LOGDEBUG0, "LOAD RISC RAM 2100 %u words at load address 0x%x", nw, la);
+                               } else if (wordload) {
+                                       union {
+                                               const uint32_t *cp;
+                                               uint32_t *np;
+                                       } ucd;
+                                       ucd.cp = (const uint32_t *)cp;
+                                       mbs.param[0] = MBOX_WRITE_RAM_WORD_EXTENDED;
+                                       mbs.param[1] = la;
+                                       mbs.param[2] = (*ucd.np);
+                                       mbs.param[3] = (*ucd.np) >> 16;
+                                       mbs.param[8] = la >> 16;
+                                       isp->isp_mbxwrk0 = nw - 1;
+                                       isp->isp_mbxworkp = ucd.np+1;
+                                       isp->isp_mbxwrk1 = (la + 1);
+                                       isp->isp_mbxwrk8 = (la + 1) >> 16;
+                                       isp_prt(isp, ISP_LOGDEBUG0, "WRITE RAM WORD EXTENDED %u words at load address 0x%x", nw, la);
+                               } else {
+                                       mbs.param[0] = MBOX_LOAD_RISC_RAM;
+                                       mbs.param[1] = la;
+                                       mbs.param[2] = DMA_WD1(isp->isp_rquest_dma);
+                                       mbs.param[3] = DMA_WD0(isp->isp_rquest_dma);
+                                       mbs.param[4] = nw >> 16;
+                                       mbs.param[5] = nw;
+                                       mbs.param[6] = DMA_WD3(isp->isp_rquest_dma);
+                                       mbs.param[7] = DMA_WD2(isp->isp_rquest_dma);
+                                       mbs.param[8] = la >> 16;
+                                       isp_prt(isp, ISP_LOGDEBUG0, "LOAD RISC RAM %u words at load address 0x%x", nw, la);
+                               }
+                               mbs.logval = MBLOGALL;
+                               isp_mboxcmd(isp, &mbs);
+                               if (mbs.param[0] != MBOX_COMMAND_COMPLETE) {
+                                       if (mbs.param[0] == MBOX_HOST_INTERFACE_ERROR) {
+                                               isp_prt(isp, ISP_LOGERR, "switching to word load");
+                                               wordload = 1;
+                                               goto again;
+                                       }
+                                       isp_prt(isp, ISP_LOGERR, "F/W Risc Ram Load Failed");
+                                       ISP_RESET0(isp);
+                                       return;
+                               }
+                               la += nw;
+                       }
+
+                       if (ptr[1] == 0) {
+                               break;
+                       }
+                       ptr += ptr[3];
+               }
+               isp->isp_loaded_fw = 1;
+       } else if (dodnld && IS_23XX(isp)) {
+               const uint16_t *ptr = isp->isp_mdvec->dv_ispfw;
+               uint16_t wi, wl, segno;
+               uint32_t la;
+
+               la = code_org;
+               segno = 0;
+
+               for (;;) {
+                       uint32_t nxtaddr;
+
+                       isp_prt(isp, ISP_LOGDEBUG0, "load 0x%x words of code at load address 0x%x", ptr[3], la);
+
+                       wi = 0;
+                       wl = ptr[3];
+
+                       while (wi < ptr[3]) {
+                               uint16_t *cp;
+                               uint16_t nw;
+
+                               nw = ISP_QUEUE_SIZE(RQUEST_QUEUE_LEN(isp)) >> 1;
+                               if (nw > wl) {
+                                       nw = wl;
+                               }
+                               if (nw > (1 << 15)) {
+                                       nw = 1 << 15;
+                               }
+                               cp = isp->isp_rquest;
+                               for (i = 0; i < nw; i++) {
+                                       ISP_IOXPUT_16(isp,  ptr[wi++], &cp[i]);
+                                       wl--;
+                               }
+                               MEMORYBARRIER(isp, SYNC_REQUEST, 0, ISP_QUEUE_SIZE(RQUEST_QUEUE_LEN(isp)), -1);
+                               ISP_MEMZERO(&mbs, sizeof (mbs));
+                               if (la < 0x10000) {
+                                       mbs.param[0] = MBOX_LOAD_RISC_RAM_2100;
+                                       mbs.param[1] = la;
+                                       mbs.param[2] = DMA_WD1(isp->isp_rquest_dma);
+                                       mbs.param[3] = DMA_WD0(isp->isp_rquest_dma);
+                                       mbs.param[4] = nw;
+                                       mbs.param[6] = DMA_WD3(isp->isp_rquest_dma);
+                                       mbs.param[7] = DMA_WD2(isp->isp_rquest_dma);
+                                       isp_prt(isp, ISP_LOGDEBUG1, "LOAD RISC RAM 2100 %u words at load address 0x%x\n", nw, la);
+                               } else {
+                                       mbs.param[0] = MBOX_LOAD_RISC_RAM;
+                                       mbs.param[1] = la;
+                                       mbs.param[2] = DMA_WD1(isp->isp_rquest_dma);
+                                       mbs.param[3] = DMA_WD0(isp->isp_rquest_dma);
+                                       mbs.param[4] = nw;
+                                       mbs.param[6] = DMA_WD3(isp->isp_rquest_dma);
+                                       mbs.param[7] = DMA_WD2(isp->isp_rquest_dma);
+                                       mbs.param[8] = la >> 16;
+                                       isp_prt(isp, ISP_LOGDEBUG1, "LOAD RISC RAM %u words at load address 0x%x\n", nw, la);
+                               }
+                               mbs.logval = MBLOGALL;
+                               isp_mboxcmd(isp, &mbs);
+                               if (mbs.param[0] != MBOX_COMMAND_COMPLETE) {
+                                       isp_prt(isp, ISP_LOGERR, "F/W Risc Ram Load Failed");
+                                       ISP_RESET0(isp);
+                                       return;
+                               }
+                               la += nw;
+                       }
+
+                       if (!IS_2322(isp)) {
+                               break;
+                       }
+
+                       if (++segno == 3) {
+                               break;
+                       }
+
+                       /*
+                        * If we're a 2322, the firmware actually comes in
+                        * three chunks. We loaded the first at the code_org
+                        * address. The other two chunks, which follow right
+                        * after each other in memory here, get loaded at
+                        * addresses specfied at offset 0x9..0xB.
+                        */
+
+                       nxtaddr = ptr[3];
+                       ptr = &ptr[nxtaddr];
+                       la = ptr[5] | ((ptr[4] & 0x3f) << 16);
+               }
+               isp->isp_loaded_fw = 1;
+       } else if (dodnld) {
+               union {
+                       const uint16_t *cp;
+                       uint16_t *np;
+               } ucd;
+               ucd.cp = isp->isp_mdvec->dv_ispfw;
+               isp->isp_mbxworkp = &ucd.np[1];
+               isp->isp_mbxwrk0 = ucd.np[3] - 1;
+               isp->isp_mbxwrk1 = code_org + 1;
+               ISP_MEMZERO(&mbs, sizeof (mbs));
+               mbs.param[0] = MBOX_WRITE_RAM_WORD;
                mbs.param[1] = code_org;
-               isp_mboxcmd(isp, &mbs, MBLOGNONE);
+               mbs.param[2] = ucd.np[0];
+               mbs.logval = MBLOGNONE;
+               isp_prt(isp, ISP_LOGDEBUG1, "WRITE RAM %u words at load address 0x%x\n", ucd.np[3], code_org);
+               isp_mboxcmd(isp, &mbs);
                if (mbs.param[0] != MBOX_COMMAND_COMPLETE) {
-                       isp_prt(isp, ISP_LOGERR, "Ram Checksum Failure");
+                       isp_prt(isp, ISP_LOGERR, "F/W download failed at word %d", isp->isp_mbxwrk1 - code_org);
+                       ISP_RESET0(isp);
                        return;
                }
-               isp->isp_loaded_fw = 1;
        } else {
                isp->isp_loaded_fw = 0;
                isp_prt(isp, ISP_LOGDEBUG2, "skipping f/w download");
        }
 
+       /*
+        * If we loaded firmware, verify its checksum
+        */
+       if (isp->isp_loaded_fw) {
+               ISP_MEMZERO(&mbs, sizeof (mbs));
+               mbs.param[0] = MBOX_VERIFY_CHECKSUM;
+               if (IS_24XX(isp)) {
+                       mbs.param[1] = code_org >> 16;
+                       mbs.param[2] = code_org;
+               } else {
+                       mbs.param[1] = code_org;
+               }
+               isp_mboxcmd(isp, &mbs);
+               if (mbs.param[0] != MBOX_COMMAND_COMPLETE) {
+                       isp_prt(isp, ISP_LOGERR, dcrc);
+                       ISP_RESET0(isp);
+                       return;
+               }
+       }
+
        /*
         * Now start it rolling.
         *
@@ -675,29 +961,74 @@ again:
         */
 
 
-       mbs.param[0] = MBOX_EXEC_FIRMWARE;
-       mbs.param[1] = code_org;
-       isp_mboxcmd(isp, &mbs, MBLOGNONE);
+       MBSINIT(&mbs, MBOX_EXEC_FIRMWARE, MBLOGALL, 1000000);
+       if (IS_24XX(isp)) {
+               mbs.param[1] = code_org >> 16;
+               mbs.param[2] = code_org;
+               if (isp->isp_loaded_fw) {
+                       mbs.param[3] = 0;
+               } else {
+                       mbs.param[3] = 1;
+               }
+               if (IS_25XX(isp)) {
+                       mbs.ibits |= 0x10;
+               }
+       } else if (IS_2322(isp)) {
+               mbs.param[1] = code_org;
+               if (isp->isp_loaded_fw) {
+                       mbs.param[2] = 0;
+               } else {
+                       mbs.param[2] = 1;
+               }
+       } else {
+               mbs.param[1] = code_org;
+       }
+       isp_mboxcmd(isp, &mbs);
+       if (IS_2322(isp) || IS_24XX(isp)) {
+               if (mbs.param[0] != MBOX_COMMAND_COMPLETE) {
+                       ISP_RESET0(isp);
+                       return;
+               }
+       }
+
        /*
-        * Give it a chance to start.
+        * Give it a chance to finish starting up.
+        * Give the 24XX more time.
         */
-       USEC_DELAY(500);
-
-       if (IS_SCSI(isp)) {
+       if (IS_24XX(isp)) {
+               ISP_DELAY(500000);
                /*
-                * Set CLOCK RATE, but only if asked to.
+                * Check to see if the 24XX firmware really started.
                 */
-               if (isp->isp_clock) {
-                       mbs.param[0] = MBOX_SET_CLOCK_RATE;
-                       mbs.param[1] = isp->isp_clock;
-                       isp_mboxcmd(isp, &mbs, MBLOGALL);
-                       /* we will try not to care if this fails */
+               if (mbs.param[1] == 0xdead) {
+                       isp_prt(isp, ISP_LOGERR, "f/w didn't *really* start");
+                       ISP_RESET0(isp);
+                       return;
+               }
+       } else {
+               ISP_DELAY(250000);
+               if (IS_SCSI(isp)) {
+                       /*
+                        * Set CLOCK RATE, but only if asked to.
+                        */
+                       if (isp->isp_clock) {
+                               mbs.param[0] = MBOX_SET_CLOCK_RATE;
+                               mbs.param[1] = isp->isp_clock;
+                               mbs.logval = MBLOGNONE;
+                               isp_mboxcmd(isp, &mbs);
+                               /* we will try not to care if this fails */
+                       }
                }
        }
 
-       mbs.param[0] = MBOX_ABOUT_FIRMWARE;
-       isp_mboxcmd(isp, &mbs, MBLOGALL);
+       /*
+        * Ask the chip for the current firmware version.
+        * This should prove that the new firmware is working.
+        */
+       MBSINIT(&mbs, MBOX_ABOUT_FIRMWARE, MBLOGALL, 0);
+       isp_mboxcmd(isp, &mbs);
        if (mbs.param[0] != MBOX_COMMAND_COMPLETE) {
+               ISP_RESET0(isp);
                return;
        }
 
@@ -716,16 +1047,15 @@ again:
                        isp->isp_fwrev[1] = 37;
 #endif
                        isp->isp_fwrev[2] = 0;
-               } 
+               }
        } else {
                isp->isp_fwrev[0] = mbs.param[1];
                isp->isp_fwrev[1] = mbs.param[2];
                isp->isp_fwrev[2] = mbs.param[3];
        }
-       isp_prt(isp, ISP_LOGCONFIG,
-           "Board Type %s, Chip Revision 0x%x, %s F/W Revision %d.%d.%d",
-           btype, isp->isp_revision, dodnld? "loaded" : "resident",
-           isp->isp_fwrev[0], isp->isp_fwrev[1], isp->isp_fwrev[2]);
+
+       isp_prt(isp, ISP_LOGCONFIG, "Board Type %s, Chip Revision 0x%x, %s F/W Revision %d.%d.%d",
+           btype, isp->isp_revision, dodnld? "loaded" : "resident", isp->isp_fwrev[0], isp->isp_fwrev[1], isp->isp_fwrev[2]);
 
        if (IS_FC(isp)) {
                /*
@@ -733,49 +1063,58 @@ again:
                 * than 1.17.0, unless it's the firmware we specifically
                 * are loading.
                 *
-                * Note that all 22XX and 23XX f/w is greater than 1.X.0.
+                * Note that all 22XX and later f/w is greater than 1.X.0.
                 */
-               if (!(ISP_FW_NEWER_THAN(isp, 1, 17, 0))) {
+               if ((ISP_FW_OLDER_THAN(isp, 1, 17, 1))) {
 #ifdef USE_SMALLER_2100_FIRMWARE
-                       FCPARAM(isp)->isp_fwattr = ISP_FW_ATTR_SCCLUN;
+                       isp->isp_fwattr = ISP_FW_ATTR_SCCLUN;
 #else
-                       FCPARAM(isp)->isp_fwattr = 0;
+                       isp->isp_fwattr = 0;
 #endif
                } else {
-                       FCPARAM(isp)->isp_fwattr = mbs.param[6];
-                       isp_prt(isp, ISP_LOGDEBUG0,
-                           "Firmware Attributes = 0x%x", mbs.param[6]);
-               }
-               if (ISP_READ(isp, BIU2100_CSR) & BIU2100_PCI64) {
-                       isp_prt(isp, ISP_LOGCONFIG,
-                           "Installed in 64-Bit PCI slot");
+                       isp->isp_fwattr = mbs.param[6];
+                       isp_prt(isp, ISP_LOGDEBUG0, "Firmware Attributes = 0x%x", mbs.param[6]);
                }
+       } else {
+#ifndef        ISP_TARGET_MODE
+               isp->isp_fwattr = ISP_FW_ATTR_TMODE;
+#else
+               isp->isp_fwattr = 0;
+#endif
        }
 
-       if (isp->isp_romfw_rev[0] || isp->isp_romfw_rev[1] ||
-           isp->isp_romfw_rev[2]) {
-               isp_prt(isp, ISP_LOGCONFIG, "Last F/W revision was %d.%d.%d",
-                   isp->isp_romfw_rev[0], isp->isp_romfw_rev[1],
-                   isp->isp_romfw_rev[2]);
-       }
-
-       mbs.param[0] = MBOX_GET_FIRMWARE_STATUS;
-       isp_mboxcmd(isp, &mbs, MBLOGALL);
-       if (mbs.param[0] != MBOX_COMMAND_COMPLETE) {
-               return;
+       if (!IS_24XX(isp)) {
+               MBSINIT(&mbs, MBOX_GET_FIRMWARE_STATUS, MBLOGALL, 0);
+               isp_mboxcmd(isp, &mbs);
+               if (mbs.param[0] != MBOX_COMMAND_COMPLETE) {
+                       ISP_RESET0(isp);
+                       return;
+               }
+               if (isp->isp_maxcmds >= mbs.param[2]) {
+                       isp->isp_maxcmds = mbs.param[2];
+               }
        }
-       isp->isp_maxcmds = mbs.param[2];
-       isp_prt(isp, ISP_LOGINFO,
-           "%d max I/O commands supported", mbs.param[2]);
-       isp_fw_state(isp);
+       isp_prt(isp, ISP_LOGCONFIG, "%d max I/O command limit set", isp->isp_maxcmds);
 
        /*
-        * Set up DMA for the request and result mailboxes.
+        * If we don't have Multi-ID f/w loaded, we need to restrict channels to one.
+        * Only make this check for non-SCSI cards (I'm not sure firmware attributes
+        * work for them).
         */
-       if (ISP_MBOXDMASETUP(isp) != 0) {
-               isp_prt(isp, ISP_LOGERR, "Cannot setup DMA");
+       if (IS_FC(isp) && ISP_CAP_MULTI_ID(isp) == 0 && isp->isp_nchan > 1) {
+               isp_prt(isp, ISP_LOGWARN, "non-MULTIID f/w loaded, only can enable 1 of %d channels", isp->isp_nchan);
+               isp->isp_nchan = 1;
+       }
+
+       for (i = 0; i < isp->isp_nchan; i++) {
+               isp_fw_state(isp, i);
+       }
+       if (isp->isp_dead) {
+               isp_shutdown(isp);
+               ISP_DISABLE_INTS(isp);
                return;
        }
+
        isp->isp_state = ISP_RESETSTATE;
 
        /*
@@ -790,14 +1129,10 @@ again:
         * It turns out that even for QLogic 2100s with ROM 1.10 and above
         * we do get a firmware attributes word returned in mailbox register 6.
         *
-        * Because the lun is in a different position in the Request Queue
+        * Because the lun is in a different position in the Request Queue
         * Entry structure for Fibre Channel with expanded lun firmware, we
         * can only support one lun (lun zero) when we don't know what kind
         * of firmware we're running.
-        *
-        * Note that we only do this once (the first time thru isp_reset)
-        * because we may be called again after firmware has been loaded once
-        * and released.
         */
        if (IS_SCSI(isp)) {
                if (dodnld) {
@@ -810,12 +1145,27 @@ again:
                        isp->isp_maxluns = 8;
                }
        } else {
-               if (FCPARAM(isp)->isp_fwattr & ISP_FW_ATTR_SCCLUN) {
+               if (ISP_CAP_SCCFW(isp)) {
                        isp->isp_maxluns = 16384;
                } else {
                        isp->isp_maxluns = 16;
                }
        }
+
+       /*
+        * We get some default values established. As a side
+        * effect, NVRAM is read here (unless overriden by
+        * a configuration flag).
+        */
+       if (do_load_defaults) {
+               if (IS_SCSI(isp)) {
+                       isp_setdfltsdparm(isp);
+               } else {
+                       for (i = 0; i < isp->isp_nchan; i++) {
+                               isp_setdfltfcparm(isp, i);
+                       }
+               }
+       }
 }
 
 /*
@@ -825,39 +1175,30 @@ again:
  */
 
 void
-isp_init(struct ispsoftc *isp)
+isp_init(ispsoftc_t *isp)
 {
-       /*
-        * Must do this first to get defaults established.
-        */
-       isp_setdfltparm(isp, 0);
-       if (IS_DUALBUS(isp)) {
-               isp_setdfltparm(isp, 1);
-       }
        if (IS_FC(isp)) {
-               isp_fibre_init(isp);
+               if (IS_24XX(isp)) {
+                       isp_fibre_init_2400(isp);
+               } else {
+                       isp_fibre_init(isp);
+               }
        } else {
                isp_scsi_init(isp);
        }
+       GET_NANOTIME(&isp->isp_init_time);
 }
 
 static void
-isp_scsi_init(struct ispsoftc *isp)
+isp_scsi_init(ispsoftc_t *isp)
 {
        sdparam *sdp_chan0, *sdp_chan1;
        mbreg_t mbs;
 
-       sdp_chan0 = isp->isp_param;
+       sdp_chan0 = SDPARAM(isp, 0);
        sdp_chan1 = sdp_chan0;
        if (IS_DUALBUS(isp)) {
-               sdp_chan1++;
-       }
-
-       /*
-        * If we have no role (neither target nor initiator), return.
-        */
-       if (isp->isp_role == ISP_ROLE_NONE) {
-               return;
+               sdp_chan1 = SDPARAM(isp, 1);
        }
 
        /* First do overall per-card settings. */
@@ -873,13 +1214,12 @@ isp_scsi_init(struct ispsoftc *isp)
         * Set Retry Delay and Count.
         * You set both channels at the same time.
         */
-       mbs.param[0] = MBOX_SET_RETRY_COUNT;
+       MBSINIT(&mbs, MBOX_SET_RETRY_COUNT, MBLOGALL, 0);
        mbs.param[1] = sdp_chan0->isp_retry_count;
        mbs.param[2] = sdp_chan0->isp_retry_delay;
        mbs.param[6] = sdp_chan1->isp_retry_count;
        mbs.param[7] = sdp_chan1->isp_retry_delay;
-
-       isp_mboxcmd(isp, &mbs, MBLOGALL);
+       isp_mboxcmd(isp, &mbs);
        if (mbs.param[0] != MBOX_COMMAND_COMPLETE) {
                return;
        }
@@ -887,10 +1227,10 @@ isp_scsi_init(struct ispsoftc *isp)
        /*
         * Set ASYNC DATA SETUP time. This is very important.
         */
-       mbs.param[0] = MBOX_SET_ASYNC_DATA_SETUP_TIME;
+       MBSINIT(&mbs, MBOX_SET_ASYNC_DATA_SETUP_TIME, MBLOGALL, 0);
        mbs.param[1] = sdp_chan0->isp_async_data_setup;
        mbs.param[2] = sdp_chan1->isp_async_data_setup;
-       isp_mboxcmd(isp, &mbs, MBLOGALL);
+       isp_mboxcmd(isp, &mbs);
        if (mbs.param[0] != MBOX_COMMAND_COMPLETE) {
                return;
        }
@@ -898,15 +1238,14 @@ isp_scsi_init(struct ispsoftc *isp)
        /*
         * Set ACTIVE Negation State.
         */
-       mbs.param[0] = MBOX_SET_ACT_NEG_STATE;
+       MBSINIT(&mbs, MBOX_SET_ACT_NEG_STATE, MBLOGNONE, 0);
        mbs.param[1] =
            (sdp_chan0->isp_req_ack_active_neg << 4) |
            (sdp_chan0->isp_data_line_active_neg << 5);
        mbs.param[2] =
            (sdp_chan1->isp_req_ack_active_neg << 4) |
            (sdp_chan1->isp_data_line_active_neg << 5);
-
-       isp_mboxcmd(isp, &mbs, MBLOGNONE);
+       isp_mboxcmd(isp, &mbs);
        if (mbs.param[0] != MBOX_COMMAND_COMPLETE) {
                isp_prt(isp, ISP_LOGERR,
                    "failed to set active negation state (%d,%d), (%d,%d)",
@@ -922,10 +1261,10 @@ isp_scsi_init(struct ispsoftc *isp)
        /*
         * Set the Tag Aging limit
         */
-       mbs.param[0] = MBOX_SET_TAG_AGE_LIMIT;
+       MBSINIT(&mbs, MBOX_SET_TAG_AGE_LIMIT, MBLOGALL, 0);
        mbs.param[1] = sdp_chan0->isp_tag_aging;
        mbs.param[2] = sdp_chan1->isp_tag_aging;
-       isp_mboxcmd(isp, &mbs, MBLOGALL);
+       isp_mboxcmd(isp, &mbs);
        if (mbs.param[0] != MBOX_COMMAND_COMPLETE) {
                isp_prt(isp, ISP_LOGERR, "failed to set tag age limit (%d,%d)",
                    sdp_chan0->isp_tag_aging, sdp_chan1->isp_tag_aging);
@@ -935,10 +1274,10 @@ isp_scsi_init(struct ispsoftc *isp)
        /*
         * Set selection timeout.
         */
-       mbs.param[0] = MBOX_SET_SELECT_TIMEOUT;
+       MBSINIT(&mbs, MBOX_SET_SELECT_TIMEOUT, MBLOGALL, 0);
        mbs.param[1] = sdp_chan0->isp_selection_timeout;
        mbs.param[2] = sdp_chan1->isp_selection_timeout;
-       isp_mboxcmd(isp, &mbs, MBLOGALL);
+       isp_mboxcmd(isp, &mbs);
        if (mbs.param[0] != MBOX_COMMAND_COMPLETE) {
                return;
        }
@@ -953,49 +1292,49 @@ isp_scsi_init(struct ispsoftc *isp)
         */
 
        if (IS_ULTRA2(isp) || IS_1240(isp)) {
-               mbs.param[0] = MBOX_INIT_RES_QUEUE_A64;
+               MBSINIT(&mbs, MBOX_INIT_RES_QUEUE_A64, MBLOGALL, 0);
                mbs.param[1] = RESULT_QUEUE_LEN(isp);
                mbs.param[2] = DMA_WD1(isp->isp_result_dma);
                mbs.param[3] = DMA_WD0(isp->isp_result_dma);
                mbs.param[4] = 0;
                mbs.param[6] = DMA_WD3(isp->isp_result_dma);
                mbs.param[7] = DMA_WD2(isp->isp_result_dma);
-               isp_mboxcmd(isp, &mbs, MBLOGALL);
+               isp_mboxcmd(isp, &mbs);
                if (mbs.param[0] != MBOX_COMMAND_COMPLETE) {
                        return;
                }
                isp->isp_residx = mbs.param[5];
 
-               mbs.param[0] = MBOX_INIT_REQ_QUEUE_A64;
+               MBSINIT(&mbs, MBOX_INIT_REQ_QUEUE_A64, MBLOGALL, 0);
                mbs.param[1] = RQUEST_QUEUE_LEN(isp);
                mbs.param[2] = DMA_WD1(isp->isp_rquest_dma);
                mbs.param[3] = DMA_WD0(isp->isp_rquest_dma);
                mbs.param[5] = 0;
                mbs.param[6] = DMA_WD3(isp->isp_result_dma);
                mbs.param[7] = DMA_WD2(isp->isp_result_dma);
-               isp_mboxcmd(isp, &mbs, MBLOGALL);
+               isp_mboxcmd(isp, &mbs);
                if (mbs.param[0] != MBOX_COMMAND_COMPLETE) {
                        return;
                }
                isp->isp_reqidx = isp->isp_reqodx = mbs.param[4];
        } else {
-               mbs.param[0] = MBOX_INIT_RES_QUEUE;
+               MBSINIT(&mbs, MBOX_INIT_RES_QUEUE, MBLOGALL, 0);
                mbs.param[1] = RESULT_QUEUE_LEN(isp);
                mbs.param[2] = DMA_WD1(isp->isp_result_dma);
                mbs.param[3] = DMA_WD0(isp->isp_result_dma);
                mbs.param[4] = 0;
-               isp_mboxcmd(isp, &mbs, MBLOGALL);
+               isp_mboxcmd(isp, &mbs);
                if (mbs.param[0] != MBOX_COMMAND_COMPLETE) {
                        return;
                }
                isp->isp_residx = mbs.param[5];
 
-               mbs.param[0] = MBOX_INIT_REQ_QUEUE;
+               MBSINIT(&mbs, MBOX_INIT_REQ_QUEUE, MBLOGALL, 0);
                mbs.param[1] = RQUEST_QUEUE_LEN(isp);
                mbs.param[2] = DMA_WD1(isp->isp_rquest_dma);
                mbs.param[3] = DMA_WD0(isp->isp_rquest_dma);
                mbs.param[5] = 0;
-               isp_mboxcmd(isp, &mbs, MBLOGALL);
+               isp_mboxcmd(isp, &mbs);
                if (mbs.param[0] != MBOX_COMMAND_COMPLETE) {
                        return;
                }
@@ -1003,63 +1342,57 @@ isp_scsi_init(struct ispsoftc *isp)
        }
 
        /*
-        * Turn on Fast Posting, LVD transitions
+        * Turn on LVD transitions for ULTRA2 or better and other features
         *
-        * Ultra2 F/W always has had fast posting (and LVD transitions)
-        *
-        * Ultra and older (i.e., SBus) cards may not. It's just safer
-        * to assume not for them.
+        * Now that we have 32 bit handles, don't do any fast posting
+        * any more. For Ultra2/Ultra3 cards, we can turn on 32 bit RIO
+        * operation or use fast posting. To be conservative, we'll only
+        * do this for Ultra3 cards now because the other cards are so
+        * rare for this author to find and test with.
         */
 
-       mbs.param[0] = MBOX_SET_FW_FEATURES;
-       mbs.param[1] = 0;
+       MBSINIT(&mbs, MBOX_SET_FW_FEATURES, MBLOGALL, 0);
        if (IS_ULTRA2(isp))
                mbs.param[1] |= FW_FEATURE_LVD_NOTIFY;
-#ifndef        ISP_NO_RIO
-       if (IS_ULTRA2(isp) || IS_1240(isp))
-               mbs.param[1] |= FW_FEATURE_RIO_16BIT;
-#else
-#ifndef        ISP_NO_FASTPOST
-       if (IS_ULTRA2(isp) || IS_1240(isp))
+#ifdef ISP_NO_RIO
+       if (IS_ULTRA3(isp))
                mbs.param[1] |= FW_FEATURE_FAST_POST;
-#endif
+#else
+       if (IS_ULTRA3(isp))
+               mbs.param[1] |= FW_FEATURE_RIO_32BIT;
 #endif
        if (mbs.param[1] != 0) {
-               u_int16_t sfeat = mbs.param[1];
-               isp_mboxcmd(isp, &mbs, MBLOGALL);
+               uint16_t sfeat = mbs.param[1];
+               isp_mboxcmd(isp, &mbs);
                if (mbs.param[0] == MBOX_COMMAND_COMPLETE) {
                        isp_prt(isp, ISP_LOGINFO,
                            "Enabled FW features (0x%x)", sfeat);
                }
        }
 
-       /*
-        * Let the outer layers decide whether to issue a SCSI bus reset.
-        */
        isp->isp_state = ISP_INITSTATE;
 }
 
 static void
-isp_scsi_channel_init(struct ispsoftc *isp, int channel)
+isp_scsi_channel_init(ispsoftc_t *isp, int chan)
 {
        sdparam *sdp;
        mbreg_t mbs;
        int tgt;
 
-       sdp = isp->isp_param;
-       sdp += channel;
+       sdp = SDPARAM(isp, chan);
 
        /*
         * Set (possibly new) Initiator ID.
         */
-       mbs.param[0] = MBOX_SET_INIT_SCSI_ID;
-       mbs.param[1] = (channel << 7) | sdp->isp_initiator_id;
-       isp_mboxcmd(isp, &mbs, MBLOGALL);
+       MBSINIT(&mbs, MBOX_SET_INIT_SCSI_ID, MBLOGALL, 0);
+       mbs.param[1] = (chan << 7) | sdp->isp_initiator_id;
+       isp_mboxcmd(isp, &mbs);
        if (mbs.param[0] != MBOX_COMMAND_COMPLETE) {
                return;
        }
-       isp_prt(isp, ISP_LOGINFO, "Initiator ID is %d on Channel %d",
-           sdp->isp_initiator_id, channel);
+       isp_prt(isp, ISP_LOGINFO, "Chan %d Initiator ID is %d",
+           chan, sdp->isp_initiator_id);
 
 
        /*
@@ -1067,7 +1400,7 @@ isp_scsi_channel_init(struct ispsoftc *isp, int channel)
         */
        for (tgt = 0; tgt < MAX_TARGETS; tgt++) {
                int lun;
-               u_int16_t sdf;
+               uint16_t sdf;
 
                if (sdp->isp_devparam[tgt].dev_enable == 0) {
                        continue;
@@ -1095,8 +1428,8 @@ isp_scsi_channel_init(struct ispsoftc *isp, int channel)
                 */
                sdp->isp_devparam[tgt].goal_flags = sdf = DPARM_DEFAULT;
 #endif
-               mbs.param[0] = MBOX_SET_TARGET_PARAMS;
-               mbs.param[1] = (channel << 15) | (tgt << 8);
+               MBSINIT(&mbs, MBOX_SET_TARGET_PARAMS, MBLOGNONE, 0);
+               mbs.param[1] = (chan << 15) | (tgt << 8);
                mbs.param[2] = sdf;
                if ((sdf & DPARM_SYNC) == 0) {
                        mbs.param[3] = 0;
@@ -1105,18 +1438,16 @@ isp_scsi_channel_init(struct ispsoftc *isp, int channel)
                            (sdp->isp_devparam[tgt].goal_offset << 8) |
                            (sdp->isp_devparam[tgt].goal_period);
                }
-               isp_prt(isp, ISP_LOGDEBUG0,
-                   "Initial Settings bus%d tgt%d flags 0x%x off 0x%x per 0x%x",
-                   channel, tgt, mbs.param[2], mbs.param[3] >> 8,
-                   mbs.param[3] & 0xff);
-               isp_mboxcmd(isp, &mbs, MBLOGNONE);
+               isp_prt(isp, ISP_LOGDEBUG0, "Initial Settings bus%d tgt%d flags 0x%x off 0x%x per 0x%x",
+                   chan, tgt, mbs.param[2], mbs.param[3] >> 8, mbs.param[3] & 0xff);
+               isp_mboxcmd(isp, &mbs);
                if (mbs.param[0] != MBOX_COMMAND_COMPLETE) {
                        sdf = DPARM_SAFE_DFLT;
-                       mbs.param[0] = MBOX_SET_TARGET_PARAMS;
-                       mbs.param[1] = (tgt << 8) | (channel << 15);
+                       MBSINIT(&mbs, MBOX_SET_TARGET_PARAMS, MBLOGALL, 0);
+                       mbs.param[1] = (tgt << 8) | (chan << 15);
                        mbs.param[2] = sdf;
                        mbs.param[3] = 0;
-                       isp_mboxcmd(isp, &mbs, MBLOGALL);
+                       isp_mboxcmd(isp, &mbs);
                        if (mbs.param[0] != MBOX_COMMAND_COMPLETE) {
                                continue;
                        }
@@ -1135,11 +1466,11 @@ isp_scsi_channel_init(struct ispsoftc *isp, int channel)
                 */
                sdp->isp_devparam[tgt].actv_flags = sdf & ~DPARM_TQING;
                for (lun = 0; lun < (int) isp->isp_maxluns; lun++) {
-                       mbs.param[0] = MBOX_SET_DEV_QUEUE_PARAMS;
-                       mbs.param[1] = (channel << 15) | (tgt << 8) | lun;
+                       MBSINIT(&mbs, MBOX_SET_DEV_QUEUE_PARAMS, MBLOGALL, 0);
+                       mbs.param[1] = (chan << 15) | (tgt << 8) | lun;
                        mbs.param[2] = sdp->isp_max_queue_depth;
                        mbs.param[3] = sdp->isp_devparam[tgt].exc_throttle;
-                       isp_mboxcmd(isp, &mbs, MBLOGALL);
+                       isp_mboxcmd(isp, &mbs);
                        if (mbs.param[0] != MBOX_COMMAND_COMPLETE) {
                                break;
                        }
@@ -1147,8 +1478,8 @@ isp_scsi_channel_init(struct ispsoftc *isp, int channel)
        }
        for (tgt = 0; tgt < MAX_TARGETS; tgt++) {
                if (sdp->isp_devparam[tgt].dev_refresh) {
-                       isp->isp_sendmarker |= (1 << channel);
-                       isp->isp_update |= (1 << channel);
+                       sdp->sendmarker = 1;
+                       sdp->update = 1;
                        break;
                }
        }
@@ -1156,37 +1487,27 @@ isp_scsi_channel_init(struct ispsoftc *isp, int channel)
 
 /*
  * Fibre Channel specific initialization.
- *
- * Locks are held before coming here.
  */
 static void
-isp_fibre_init(struct ispsoftc *isp)
+isp_fibre_init(ispsoftc_t *isp)
 {
        fcparam *fcp;
        isp_icb_t local, *icbp = &local;
        mbreg_t mbs;
-       int loopid;
-       u_int64_t nwwn, pwwn;
-
-       fcp = isp->isp_param;
+       int ownloopid;
 
        /*
-        * Do this *before* initializing the firmware.
+        * We only support one channel on non-24XX cards
         */
-       isp_mark_getpdb_all(isp);
-       fcp->isp_fwstate = FW_CONFIG_WAIT;
-       fcp->isp_loopstate = LOOP_NIL;
-
-       /*
-        * If we have no role (neither target nor initiator), return.
-        */
-       if (isp->isp_role == ISP_ROLE_NONE) {
+       fcp = FCPARAM(isp, 0);
+       if (fcp->role == ISP_ROLE_NONE) {
+               isp->isp_state = ISP_INITSTATE;
                return;
        }
 
-       loopid = fcp->isp_loopid;
-       MEMZERO(icbp, sizeof (*icbp));
+       ISP_MEMZERO(icbp, sizeof (*icbp));
        icbp->icb_version = ICB_VERSION1;
+       icbp->icb_fwoptions = fcp->isp_fwoptions;
 
        /*
         * Firmware Options are either retrieved from NVRAM or
@@ -1198,8 +1519,8 @@ isp_fibre_init(struct ispsoftc *isp)
        /*
         * If this is a 2100 < revision 5, we have to turn off FAIRNESS.
         */
-       if ((isp->isp_type == ISP_HA_FC_2100) && isp->isp_revision < 5) {
-               fcp->isp_fwoptions &= ~ICBOPT_FAIRNESS;
+       if (IS_2100(isp) && isp->isp_revision < 5) {
+               icbp->icb_fwoptions &= ~ICBOPT_FAIRNESS;
        }
 
        /*
@@ -1208,55 +1529,65 @@ isp_fibre_init(struct ispsoftc *isp)
         * a LIP- this is a known f/w bug for 2100 f/w less than 1.17.0.
         */
        if (!ISP_FW_NEWER_THAN(isp, 1, 17, 0)) {
-               fcp->isp_fwoptions |= ICBOPT_FULL_LOGIN;
+               icbp->icb_fwoptions |= ICBOPT_FULL_LOGIN;
        }
 
        /*
         * Insist on Port Database Update Async notifications
         */
-       fcp->isp_fwoptions |= ICBOPT_PDBCHANGE_AE;
+       icbp->icb_fwoptions |= ICBOPT_PDBCHANGE_AE;
 
        /*
         * Make sure that target role reflects into fwoptions.
         */
-       if (isp->isp_role & ISP_ROLE_TARGET) {
-               fcp->isp_fwoptions |= ICBOPT_TGT_ENABLE;
+       if (fcp->role & ISP_ROLE_TARGET) {
+               icbp->icb_fwoptions |= ICBOPT_TGT_ENABLE;
        } else {
-               fcp->isp_fwoptions &= ~ICBOPT_TGT_ENABLE;
+               icbp->icb_fwoptions &= ~ICBOPT_TGT_ENABLE;
        }
 
-       /*
-        * Propagate all of this into the ICB structure.
-        */
-       icbp->icb_fwoptions = fcp->isp_fwoptions;
-       icbp->icb_maxfrmlen = fcp->isp_maxfrmlen;
-       if (icbp->icb_maxfrmlen < ICB_MIN_FRMLEN ||
-           icbp->icb_maxfrmlen > ICB_MAX_FRMLEN) {
-               isp_prt(isp, ISP_LOGERR,
-                   "bad frame length (%d) from NVRAM- using %d",
-                   fcp->isp_maxfrmlen, ICB_DFLT_FRMLEN);
+       if (fcp->role & ISP_ROLE_INITIATOR) {
+               icbp->icb_fwoptions &= ~ICBOPT_INI_DISABLE;
+       } else {
+               icbp->icb_fwoptions |= ICBOPT_INI_DISABLE;
+       }
+
+       icbp->icb_maxfrmlen = DEFAULT_FRAMESIZE(isp);
+       if (icbp->icb_maxfrmlen < ICB_MIN_FRMLEN || icbp->icb_maxfrmlen > ICB_MAX_FRMLEN) {
+               isp_prt(isp, ISP_LOGERR, "bad frame length (%d) from NVRAM- using %d", DEFAULT_FRAMESIZE(isp), ICB_DFLT_FRMLEN);
                icbp->icb_maxfrmlen = ICB_DFLT_FRMLEN;
        }
        icbp->icb_maxalloc = fcp->isp_maxalloc;
        if (icbp->icb_maxalloc < 1) {
-               isp_prt(isp, ISP_LOGERR,
-                   "bad maximum allocation (%d)- using 16", fcp->isp_maxalloc);
+               isp_prt(isp, ISP_LOGERR, "bad maximum allocation (%d)- using 16", fcp->isp_maxalloc);
                icbp->icb_maxalloc = 16;
        }
-       icbp->icb_execthrottle = fcp->isp_execthrottle;
+       icbp->icb_execthrottle = DEFAULT_EXEC_THROTTLE(isp);
        if (icbp->icb_execthrottle < 1) {
-               isp_prt(isp, ISP_LOGERR,
-                   "bad execution throttle of %d- using 16",
-                   fcp->isp_execthrottle);
+               isp_prt(isp, ISP_LOGERR, "bad execution throttle of %d- using %d", DEFAULT_EXEC_THROTTLE(isp), ICB_DFLT_THROTTLE);
                icbp->icb_execthrottle = ICB_DFLT_THROTTLE;
        }
        icbp->icb_retry_delay = fcp->isp_retry_delay;
        icbp->icb_retry_count = fcp->isp_retry_count;
-       icbp->icb_hardaddr = loopid;
+       icbp->icb_hardaddr = fcp->isp_loopid;
+       ownloopid = (isp->isp_confopts & ISP_CFG_OWNLOOPID) != 0;
+       if (icbp->icb_hardaddr >= LOCAL_LOOP_LIM) {
+               icbp->icb_hardaddr = 0;
+               ownloopid = 0;
+       }
+
+       /*
+        * Our life seems so much better with 2200s and later with
+        * the latest f/w if we set Hard Address.
+        */
+       if (ownloopid || ISP_FW_NEWER_THAN(isp, 2, 2, 5)) {
+               icbp->icb_fwoptions |= ICBOPT_HARD_ADDRESS;
+       }
+
        /*
         * Right now we just set extended options to prefer point-to-point
         * over loop based upon some soft config options.
-        * 
+        *
         * NB: for the 2300, ICBOPT_EXTENDED is required.
         */
        if (IS_2200(isp) || IS_23XX(isp)) {
@@ -1264,7 +1595,7 @@ isp_fibre_init(struct ispsoftc *isp)
                /*
                 * Prefer or force Point-To-Point instead Loop?
                 */
-               switch(isp->isp_confopts & ISP_CFG_PORT_PREF) {
+               switch (isp->isp_confopts & ISP_CFG_PORT_PREF) {
                case ISP_CFG_NPORT:
                        icbp->icb_xfwoptions |= ICBXOPT_PTP_2_LOOP;
                        break;
@@ -1278,18 +1609,29 @@ isp_fibre_init(struct ispsoftc *isp)
                        icbp->icb_xfwoptions |= ICBXOPT_LOOP_2_PTP;
                        break;
                }
-               if (IS_23XX(isp)) {
+               if (IS_2200(isp)) {
+                       /*
+                        * We can't have Fast Posting any more- we now
+                        * have 32 bit handles.
+                        *
+                        * RIO seemed to have to much breakage.
+                        *
+                        * Just opt for safety.
+                        */
+                       icbp->icb_xfwoptions &= ~ICBXOPT_RIO_16BIT;
+                       icbp->icb_fwoptions &= ~ICBOPT_FAST_POST;
+               } else {
                        /*
                         * QLogic recommends that FAST Posting be turned
                         * off for 23XX cards and instead allow the HBA
                         * to write response queue entries and interrupt
                         * after a delay (ZIO).
-                        *
-                        * If we set ZIO, it will disable fast posting,
-                        * so we don't need to clear it in fwoptions.
                         */
-                       icbp->icb_xfwoptions |= ICBXOPT_ZIO;
-
+                       icbp->icb_fwoptions &= ~ICBOPT_FAST_POST;
+                       if ((fcp->isp_xfwoptions & ICBXOPT_TIMER_MASK) == ICBXOPT_ZIO) {
+                               icbp->icb_xfwoptions |= ICBXOPT_ZIO;
+                               icbp->icb_idelaytimer = 10;
+                       }
                        if (isp->isp_confopts & ISP_CFG_ONEGB) {
                                icbp->icb_zfwoptions |= ICBZOPT_RATE_ONEGB;
                        } else if (isp->isp_confopts & ISP_CFG_TWOGB) {
@@ -1297,73 +1639,64 @@ isp_fibre_init(struct ispsoftc *isp)
                        } else {
                                icbp->icb_zfwoptions |= ICBZOPT_RATE_AUTO;
                        }
+                       if (fcp->isp_zfwoptions & ICBZOPT_50_OHM) {
+                               icbp->icb_zfwoptions |= ICBZOPT_50_OHM;
+                       }
                }
        }
 
-#ifndef        ISP_NO_RIO_FC
-       /*
-        * RIO seems to be enabled in 2100s for fw >= 1.17.0.
-        *
-        * I've had some questionable problems with RIO on 2200.
-        * More specifically, on a 2204 I had problems with RIO
-        * on a Linux system where I was dropping commands right
-        * and left. It's not clear to me what the actual problem
-        * was.
-        *
-        * 23XX Cards do not support RIO. Instead they support ZIO.
-        */
-#if    0
-       if (!IS_23XX(isp) && ISP_FW_NEWER_THAN(isp, 1, 17, 0)) {
-               icbp->icb_xfwoptions |= ICBXOPT_RIO_16BIT;
-               icbp->icb_racctimer = 4;
-               icbp->icb_idelaytimer = 8;
-       }
-#endif
-#endif
 
        /*
-        * For 22XX > 2.1.26 && 23XX, set someoptions.
-        * XXX: Probably okay for newer 2100 f/w too.
+        * For 22XX > 2.1.26 && 23XX, set some options.
         */
        if (ISP_FW_NEWER_THAN(isp, 2, 26, 0)) {
-               /*
-                * Turn on LIP F8 async event (1)
-                * Turn on generate AE 8013 on all LIP Resets (2)
-                * Disable LIP F7 switching (8)
-                */
-               mbs.param[0] = MBOX_SET_FIRMWARE_OPTIONS;
-               mbs.param[1] = 0xb;
+               MBSINIT(&mbs, MBOX_SET_FIRMWARE_OPTIONS, MBLOGALL, 0);
+               mbs.param[1] = IFCOPT1_DISF7SWTCH|IFCOPT1_LIPASYNC|IFCOPT1_LIPF8;
                mbs.param[2] = 0;
                mbs.param[3] = 0;
-               isp_mboxcmd(isp, &mbs, MBLOGALL);
-       }
-       icbp->icb_logintime = 30;       /* 30 second login timeout */
-
-       if (IS_23XX(isp)) {
-               ISP_WRITE(isp, isp->isp_rqstinrp, 0);
-               ISP_WRITE(isp, isp->isp_rqstoutrp, 0);
-               ISP_WRITE(isp, isp->isp_respinrp, 0);
-               ISP_WRITE(isp, isp->isp_respoutrp, 0);
+               if (ISP_FW_NEWER_THAN(isp, 3, 16, 0)) {
+                       mbs.param[1] |= IFCOPT1_EQFQASYNC|IFCOPT1_CTIO_RETRY;
+                       if (fcp->role & ISP_ROLE_TARGET) {
+                               mbs.param[3] = IFCOPT3_NOPRLI;
+                       }
+               }
+               isp_mboxcmd(isp, &mbs);
+               if (mbs.param[0] != MBOX_COMMAND_COMPLETE) {
+                       return;
+               }
        }
+       icbp->icb_logintime = ICB_LOGIN_TOV;
+       icbp->icb_lunetimeout = ICB_LUN_ENABLE_TOV;
 
-       nwwn = ISP_NODEWWN(isp);
-       pwwn = ISP_PORTWWN(isp);
-       if (nwwn && pwwn) {
+       if (fcp->isp_wwnn && fcp->isp_wwpn && (fcp->isp_wwnn >> 60) != 2) {
                icbp->icb_fwoptions |= ICBOPT_BOTH_WWNS;
-               MAKE_NODE_NAME_FROM_WWN(icbp->icb_nodename, nwwn);
-               MAKE_NODE_NAME_FROM_WWN(icbp->icb_portname, pwwn);
+               MAKE_NODE_NAME_FROM_WWN(icbp->icb_nodename, fcp->isp_wwnn);
+               MAKE_NODE_NAME_FROM_WWN(icbp->icb_portname, fcp->isp_wwpn);
                isp_prt(isp, ISP_LOGDEBUG1,
                    "Setting ICB Node 0x%08x%08x Port 0x%08x%08x",
-                   ((u_int32_t) (nwwn >> 32)),
-                   ((u_int32_t) (nwwn & 0xffffffff)),
-                   ((u_int32_t) (pwwn >> 32)),
-                   ((u_int32_t) (pwwn & 0xffffffff)));
+                   ((uint32_t) (fcp->isp_wwnn >> 32)),
+                   ((uint32_t) (fcp->isp_wwnn)),
+                   ((uint32_t) (fcp->isp_wwpn >> 32)),
+                   ((uint32_t) (fcp->isp_wwpn)));
+       } else if (fcp->isp_wwpn) {
+               icbp->icb_fwoptions &= ~ICBOPT_BOTH_WWNS;
+               MAKE_NODE_NAME_FROM_WWN(icbp->icb_portname, fcp->isp_wwpn);
+               isp_prt(isp, ISP_LOGDEBUG1,
+                   "Setting ICB Port 0x%08x%08x",
+                   ((uint32_t) (fcp->isp_wwpn >> 32)),
+                   ((uint32_t) (fcp->isp_wwpn)));
        } else {
-               isp_prt(isp, ISP_LOGDEBUG1, "Not using any WWNs");
-               icbp->icb_fwoptions &= ~(ICBOPT_BOTH_WWNS|ICBOPT_FULL_LOGIN);
+               isp_prt(isp, ISP_LOGERR, "No valid WWNs to use");
+               return;
        }
        icbp->icb_rqstqlen = RQUEST_QUEUE_LEN(isp);
+       if (icbp->icb_rqstqlen < 1) {
+               isp_prt(isp, ISP_LOGERR, "bad request queue length");
+       }
        icbp->icb_rsltqlen = RESULT_QUEUE_LEN(isp);
+       if (icbp->icb_rsltqlen < 1) {
+               isp_prt(isp, ISP_LOGERR, "bad result queue length");
+       }
        icbp->icb_rqstaddr[RQRSP_ADDR0015] = DMA_WD0(isp->isp_rquest_dma);
        icbp->icb_rqstaddr[RQRSP_ADDR1631] = DMA_WD1(isp->isp_rquest_dma);
        icbp->icb_rqstaddr[RQRSP_ADDR3247] = DMA_WD2(isp->isp_rquest_dma);
@@ -1372,32 +1705,38 @@ isp_fibre_init(struct ispsoftc *isp)
        icbp->icb_respaddr[RQRSP_ADDR1631] = DMA_WD1(isp->isp_result_dma);
        icbp->icb_respaddr[RQRSP_ADDR3247] = DMA_WD2(isp->isp_result_dma);
        icbp->icb_respaddr[RQRSP_ADDR4863] = DMA_WD3(isp->isp_result_dma);
-       isp_prt(isp, ISP_LOGDEBUG0,
-           "isp_fibre_init: fwopt 0x%x xfwopt 0x%x zfwopt 0x%x",
+
+       if (FC_SCRATCH_ACQUIRE(isp, 0)) {
+               isp_prt(isp, ISP_LOGERR, sacq);
+               return;
+       }
+       isp_prt(isp, ISP_LOGDEBUG0, "isp_fibre_init: fwopt 0x%x xfwopt 0x%x zfwopt 0x%x",
            icbp->icb_fwoptions, icbp->icb_xfwoptions, icbp->icb_zfwoptions);
 
-       FC_SCRATCH_ACQUIRE(isp);
        isp_put_icb(isp, icbp, (isp_icb_t *)fcp->isp_scratch);
 
        /*
         * Init the firmware
         */
-       mbs.param[0] = MBOX_INIT_FIRMWARE;
-       mbs.param[1] = 0;
+       MBSINIT(&mbs, MBOX_INIT_FIRMWARE, MBLOGALL, 30000000);
        mbs.param[2] = DMA_WD1(fcp->isp_scdma);
        mbs.param[3] = DMA_WD0(fcp->isp_scdma);
-       mbs.param[4] = 0;
-       mbs.param[5] = 0;
        mbs.param[6] = DMA_WD3(fcp->isp_scdma);
        mbs.param[7] = DMA_WD2(fcp->isp_scdma);
-       isp_mboxcmd(isp, &mbs, MBLOGALL);
-       FC_SCRATCH_RELEASE(isp);
+       mbs.logval = MBLOGALL;
+       isp_prt(isp, ISP_LOGDEBUG0, "INIT F/W from %p (%08x%08x)",
+           fcp->isp_scratch, (uint32_t) ((uint64_t)fcp->isp_scdma >> 32),
+           (uint32_t) fcp->isp_scdma);
+       MEMORYBARRIER(isp, SYNC_SFORDEV, 0, sizeof (*icbp), 0);
+       isp_mboxcmd(isp, &mbs);
+       FC_SCRATCH_RELEASE(isp, 0);
        if (mbs.param[0] != MBOX_COMMAND_COMPLETE) {
+               isp_print_bytes(isp, "isp_fibre_init", sizeof (*icbp), icbp);
                return;
        }
-       isp->isp_reqidx = isp->isp_reqodx = 0;
+       isp->isp_reqidx = 0;
+       isp->isp_reqodx = 0;
        isp->isp_residx = 0;
-       isp->isp_sendmarker = 1;
 
        /*
         * Whatever happens, we're now committed to being here.
@@ -1405,1481 +1744,2260 @@ isp_fibre_init(struct ispsoftc *isp)
        isp->isp_state = ISP_INITSTATE;
 }
 
-/*
- * Fibre Channel Support- get the port database for the id.
- *
- * Locks are held before coming here. Return 0 if success,
- * else failure.
- */
-
-static int
-isp_getmap(struct ispsoftc *isp, fcpos_map_t *map)
+static void
+isp_fibre_init_2400(ispsoftc_t *isp)
 {
-       fcparam *fcp = (fcparam *) isp->isp_param;
+       fcparam *fcp;
+       isp_icb_2400_t local, *icbp = &local;
        mbreg_t mbs;
+       int chan;
 
-       mbs.param[0] = MBOX_GET_FC_AL_POSITION_MAP;
-       mbs.param[1] = 0;
-       mbs.param[2] = DMA_WD1(fcp->isp_scdma);
-       mbs.param[3] = DMA_WD0(fcp->isp_scdma);
        /*
-        * Unneeded. For the 2100, except for initializing f/w, registers
-        * 4/5 have to not be written to.
-        *      mbs.param[4] = 0;
-        *      mbs.param[5] = 0;
-        *
+        * Check to see whether all channels have *some* kind of role
         */
-       mbs.param[6] = 0;
-       mbs.param[7] = 0;
-       FC_SCRATCH_ACQUIRE(isp);
-       isp_mboxcmd(isp, &mbs, MBLOGALL & ~MBOX_COMMAND_PARAM_ERROR);
-       if (mbs.param[0] == MBOX_COMMAND_COMPLETE) {
-               MEMCPY(map, fcp->isp_scratch, sizeof (fcpos_map_t));
-               map->fwmap = mbs.param[1] != 0;
-               FC_SCRATCH_RELEASE(isp);
-               return (0);
+       for (chan = 0; chan < isp->isp_nchan; chan++) {
+               fcp = FCPARAM(isp, chan);
+               if (fcp->role != ISP_ROLE_NONE) {
+                       break;
+               }
        }
-       FC_SCRATCH_RELEASE(isp);
-       return (-1);
-}
-
-static void
-isp_mark_getpdb_all(struct ispsoftc *isp)
-{
-       fcparam *fcp = (fcparam *) isp->isp_param;
-       int i;
-       for (i = 0; i < MAX_FC_TARG; i++) {
-               fcp->portdb[i].valid = fcp->portdb[i].fabric_dev = 0;
+       if (chan == isp->isp_nchan) {
+               isp_prt(isp, ISP_LOGDEBUG0, "all %d channels with role 'none'", chan);
+               isp->isp_state = ISP_INITSTATE;
+               return;
        }
-}
 
-static int
-isp_getpdb(struct ispsoftc *isp, int id, isp_pdb_t *pdbp)
-{
-       fcparam *fcp = (fcparam *) isp->isp_param;
-       mbreg_t mbs;
+       /*
+        * Start with channel 0.
+        */
+       fcp = FCPARAM(isp, 0);
 
-       mbs.param[0] = MBOX_GET_PORT_DB;
-       mbs.param[1] = id << 8;
-       mbs.param[2] = DMA_WD1(fcp->isp_scdma);
-       mbs.param[3] = DMA_WD0(fcp->isp_scdma);
        /*
-        * Unneeded. For the 2100, except for initializing f/w, registers
-        * 4/5 have to not be written to.
-        *      mbs.param[4] = 0;
-        *      mbs.param[5] = 0;
-        *
+        * Turn on LIP F8 async event (1)
         */
-       mbs.param[6] = DMA_WD3(fcp->isp_scdma);
-       mbs.param[7] = DMA_WD2(fcp->isp_scdma);
-       FC_SCRATCH_ACQUIRE(isp);
-       isp_mboxcmd(isp, &mbs, MBLOGALL & ~MBOX_COMMAND_PARAM_ERROR);
-       if (mbs.param[0] == MBOX_COMMAND_COMPLETE) {
-               isp_get_pdb(isp, (isp_pdb_t *)fcp->isp_scratch, pdbp);
-               FC_SCRATCH_RELEASE(isp);
-               return (0);
+       MBSINIT(&mbs, MBOX_SET_FIRMWARE_OPTIONS, MBLOGALL, 0);
+       mbs.param[1] = 1;
+       isp_mboxcmd(isp, &mbs);
+       if (mbs.param[0] != MBOX_COMMAND_COMPLETE) {
+               return;
        }
-       FC_SCRATCH_RELEASE(isp);
-       return (-1);
-}
 
-static u_int64_t
-isp_get_portname(struct ispsoftc *isp, int loopid, int nodename)
-{
-       u_int64_t wwn = 0;
-       mbreg_t mbs;
+       ISP_MEMZERO(icbp, sizeof (*icbp));
+       icbp->icb_fwoptions1 = fcp->isp_fwoptions;
+       if (fcp->role & ISP_ROLE_TARGET) {
+               icbp->icb_fwoptions1 |= ICB2400_OPT1_TGT_ENABLE;
+       } else {
+               icbp->icb_fwoptions1 &= ~ICB2400_OPT1_TGT_ENABLE;
+       }
 
-       mbs.param[0] = MBOX_GET_PORT_NAME;
-       mbs.param[1] = loopid << 8;
-       if (nodename)
-               mbs.param[1] |= 1;
-       isp_mboxcmd(isp, &mbs, MBLOGALL & ~MBOX_COMMAND_PARAM_ERROR);
-       if (mbs.param[0] == MBOX_COMMAND_COMPLETE) {
-               wwn =
-                   (((u_int64_t)(mbs.param[2] & 0xff)) << 56) |
-                   (((u_int64_t)(mbs.param[2] >> 8))   << 48) |
-                   (((u_int64_t)(mbs.param[3] & 0xff)) << 40) |
-                   (((u_int64_t)(mbs.param[3] >> 8))   << 32) |
-                   (((u_int64_t)(mbs.param[6] & 0xff)) << 24) |
-                   (((u_int64_t)(mbs.param[6] >> 8))   << 16) |
-                   (((u_int64_t)(mbs.param[7] & 0xff)) <<  8) |
-                   (((u_int64_t)(mbs.param[7] >> 8)));
+       if (fcp->role & ISP_ROLE_INITIATOR) {
+               icbp->icb_fwoptions1 &= ~ICB2400_OPT1_INI_DISABLE;
+       } else {
+               icbp->icb_fwoptions1 |= ICB2400_OPT1_INI_DISABLE;
        }
-       return (wwn);
-}
 
-/*
- * Make sure we have good FC link and know our Loop ID.
- */
+       icbp->icb_version = ICB_VERSION1;
+       icbp->icb_maxfrmlen = DEFAULT_FRAMESIZE(isp);
+       if (icbp->icb_maxfrmlen < ICB_MIN_FRMLEN || icbp->icb_maxfrmlen > ICB_MAX_FRMLEN) {
+               isp_prt(isp, ISP_LOGERR, "bad frame length (%d) from NVRAM- using %d", DEFAULT_FRAMESIZE(isp), ICB_DFLT_FRMLEN);
+               icbp->icb_maxfrmlen = ICB_DFLT_FRMLEN;
+       }
 
-static int
-isp_fclink_test(struct ispsoftc *isp, int usdelay)
-{
-       static char *toponames[] = {
-               "Private Loop",
-               "FL Port",
-               "N-Port to N-Port",
-               "F Port",
-               "F Port (no FLOGI_ACC response)"
-       };
-       mbreg_t mbs;
-       int count, check_for_fabric;
-       u_int8_t lwfs;
-       fcparam *fcp;
-       struct lportdb *lp;
-       isp_pdb_t pdb;
+       icbp->icb_execthrottle = DEFAULT_EXEC_THROTTLE(isp);
+       if (icbp->icb_execthrottle < 1) {
+               isp_prt(isp, ISP_LOGERR, "bad execution throttle of %d- using %d", DEFAULT_EXEC_THROTTLE(isp), ICB_DFLT_THROTTLE);
+               icbp->icb_execthrottle = ICB_DFLT_THROTTLE;
+       }
+
+       if (icbp->icb_fwoptions1 & ICB2400_OPT1_TGT_ENABLE) {
+               /*
+                * Get current resource count
+                */
+               MBSINIT(&mbs, MBOX_GET_RESOURCE_COUNT, MBLOGALL, 0);
+               mbs.obits = 0x4cf;
+               isp_mboxcmd(isp, &mbs);
+               if (mbs.param[0] != MBOX_COMMAND_COMPLETE) {
+                       return;
+               }
+               icbp->icb_xchgcnt = mbs.param[3];
+       }
 
-       fcp = isp->isp_param;
 
-       /*
-        * XXX: Here is where we would start a 'loop dead' timeout
-        */
+       icbp->icb_hardaddr = fcp->isp_loopid;
+       if (icbp->icb_hardaddr >= LOCAL_LOOP_LIM) {
+               icbp->icb_hardaddr = 0;
+       }
 
        /*
-        * Wait up to N microseconds for F/W to go to a ready state.
+        * Force this on.
         */
-       lwfs = FW_CONFIG_WAIT;
-       count = 0;
-       while (count < usdelay) {
-               u_int64_t enano;
-               u_int32_t wrk;
-               NANOTIME_T hra, hrb;
-
-               GET_NANOTIME(&hra);
-               isp_fw_state(isp);
-               if (lwfs != fcp->isp_fwstate) {
-                       isp_prt(isp, ISP_LOGINFO, "Firmware State <%s->%s>",
-                           isp2100_fw_statename((int)lwfs),
-                           isp2100_fw_statename((int)fcp->isp_fwstate));
-                       lwfs = fcp->isp_fwstate;
-               }
-               if (fcp->isp_fwstate == FW_READY) {
-                       break;
-               }
-               GET_NANOTIME(&hrb);
+       icbp->icb_fwoptions1 |= ICB2400_OPT1_HARD_ADDRESS;
 
+       icbp->icb_fwoptions2 = fcp->isp_xfwoptions;
+       switch (isp->isp_confopts & ISP_CFG_PORT_PREF) {
+#if    0
+       case ISP_CFG_NPORT:
                /*
-                * Get the elapsed time in nanoseconds.
-                * Always guaranteed to be non-zero.
+                * XXX: This causes the f/w to crash.
                 */
-               enano = NANOTIME_SUB(&hrb, &hra);
+               icbp->icb_fwoptions2 &= ~ICB2400_OPT2_TOPO_MASK;
+               icbp->icb_fwoptions2 |= ICB2400_OPT2_PTP_2_LOOP;
+               break;
+#endif
+       case ISP_CFG_NPORT_ONLY:
+               icbp->icb_fwoptions2 &= ~ICB2400_OPT2_TOPO_MASK;
+               icbp->icb_fwoptions2 |= ICB2400_OPT2_PTP_ONLY;
+               break;
+       case ISP_CFG_LPORT_ONLY:
+               icbp->icb_fwoptions2 &= ~ICB2400_OPT2_TOPO_MASK;
+               icbp->icb_fwoptions2 |= ICB2400_OPT2_LOOP_ONLY;
+               break;
+       default:
+               icbp->icb_fwoptions2 &= ~ICB2400_OPT2_TOPO_MASK;
+               icbp->icb_fwoptions2 |= ICB2400_OPT2_LOOP_2_PTP;
+               break;
+       }
 
-               isp_prt(isp, ISP_LOGDEBUG1,
-                   "usec%d: 0x%lx->0x%lx enano 0x%x%08x",
-                   count, (long) GET_NANOSEC(&hra), (long) GET_NANOSEC(&hrb),
-                   (u_int32_t)(enano >> 32), (u_int32_t)(enano & 0xffffffff));
+       /* force this on for now */
+       icbp->icb_fwoptions2 |= ICB2400_OPT2_ZIO;
 
-               /*
-                * If the elapsed time is less than 1 millisecond,
-                * delay a period of time up to that millisecond of
-                * waiting.
-                *
-                * This peculiar code is an attempt to try and avoid
-                * invoking u_int64_t math support functions for some
-                * platforms where linkage is a problem.
-                */
-               if (enano < (1000 * 1000)) {
-                       count += 1000;
-                       enano = (1000 * 1000) - enano;
-                       while (enano > (u_int64_t) 4000000000U) {
-                               USEC_SLEEP(isp, 4000000);
-                               enano -= (u_int64_t) 4000000000U;
-                       }
-                       wrk = enano;
-                       wrk /= 1000;
-                       USEC_SLEEP(isp, wrk);
-               } else {
-                       while (enano > (u_int64_t) 4000000000U) {
-                               count += 4000000;
-                               enano -= (u_int64_t) 4000000000U;
-                       }
-                       wrk = enano;
-                       count += (wrk / 1000);
-               }
+       switch (icbp->icb_fwoptions2 & ICB2400_OPT2_TIMER_MASK) {
+       case ICB2400_OPT2_ZIO:
+       case ICB2400_OPT2_ZIO1:
+               icbp->icb_idelaytimer = 0;
+               break;
+       case 0:
+               break;
+       default:
+               isp_prt(isp, ISP_LOGWARN, "bad value %x in fwopt2 timer field", icbp->icb_fwoptions2 & ICB2400_OPT2_TIMER_MASK);
+               icbp->icb_fwoptions2 &= ~ICB2400_OPT2_TIMER_MASK;
+               break;
        }
 
        /*
-        * If we haven't gone to 'ready' state, return.
+        * We don't support FCTAPE, so clear it.
         */
-       if (fcp->isp_fwstate != FW_READY) {
-               return (-1);
+       icbp->icb_fwoptions2 &= ~ICB2400_OPT2_FCTAPE;
+
+       icbp->icb_fwoptions3 = fcp->isp_zfwoptions;
+       icbp->icb_fwoptions3 &= ~ICB2400_OPT3_RATE_AUTO;
+       if (isp->isp_confopts & ISP_CFG_ONEGB) {
+               icbp->icb_fwoptions3 |= ICB2400_OPT3_RATE_ONEGB;
+       } else if (isp->isp_confopts & ISP_CFG_TWOGB) {
+               icbp->icb_fwoptions3 |= ICB2400_OPT3_RATE_TWOGB;
+       } else if (isp->isp_confopts & ISP_CFG_FOURGB) {
+               icbp->icb_fwoptions3 |= ICB2400_OPT3_RATE_FOURGB;
+       } else {
+               icbp->icb_fwoptions3 |= ICB2400_OPT3_RATE_AUTO;
        }
 
-       /*
-        * Get our Loop ID (if possible). We really need to have it.
-        */
-       mbs.param[0] = MBOX_GET_LOOP_ID;
-       isp_mboxcmd(isp, &mbs, MBLOGALL);
-       if (mbs.param[0] != MBOX_COMMAND_COMPLETE) {
-               return (-1);
+       if ((isp->isp_confopts & ISP_CFG_OWNLOOPID) == 0) {
+               icbp->icb_fwoptions3 |= ICB2400_OPT3_SOFTID;
        }
-       fcp->isp_loopid = mbs.param[1];
-       if (IS_2200(isp) || IS_23XX(isp)) {
-               int topo = (int) mbs.param[6];
-               if (topo < TOPO_NL_PORT || topo > TOPO_PTP_STUB)
-                       topo = TOPO_PTP_STUB;
-               fcp->isp_topo = topo;
+       icbp->icb_logintime = ICB_LOGIN_TOV;
+
+       if (fcp->isp_wwnn && fcp->isp_wwpn && (fcp->isp_wwnn >> 60) != 2) {
+               icbp->icb_fwoptions1 |= ICB2400_OPT1_BOTH_WWNS;
+               MAKE_NODE_NAME_FROM_WWN(icbp->icb_portname, fcp->isp_wwpn);
+               MAKE_NODE_NAME_FROM_WWN(icbp->icb_nodename, fcp->isp_wwnn);
+               isp_prt(isp, ISP_LOGDEBUG1, "Setting ICB Node 0x%08x%08x Port 0x%08x%08x", ((uint32_t) (fcp->isp_wwnn >> 32)), ((uint32_t) (fcp->isp_wwnn)),
+                   ((uint32_t) (fcp->isp_wwpn >> 32)), ((uint32_t) (fcp->isp_wwpn)));
+       } else if (fcp->isp_wwpn) {
+               icbp->icb_fwoptions1 &= ~ICB2400_OPT1_BOTH_WWNS;
+               MAKE_NODE_NAME_FROM_WWN(icbp->icb_portname, fcp->isp_wwpn);
+               isp_prt(isp, ISP_LOGDEBUG1, "Setting ICB Node to be same as Port 0x%08x%08x", ((uint32_t) (fcp->isp_wwpn >> 32)), ((uint32_t) (fcp->isp_wwpn)));
        } else {
-               fcp->isp_topo = TOPO_NL_PORT;
+               isp_prt(isp, ISP_LOGERR, "No valid WWNs to use");
+               return;
        }
-       fcp->isp_portid = fcp->isp_alpa = mbs.param[2] & 0xff;
+       icbp->icb_retry_count = fcp->isp_retry_count;
 
-       /*
-        * Check to see if we're on a fabric by trying to see if we
-        * can talk to the fabric name server. This can be a bit
-        * tricky because if we're a 2100, we should check always
-        * (in case we're connected to an server doing aliasing).
-        */
-       fcp->isp_onfabric = 0;
+       icbp->icb_rqstqlen = RQUEST_QUEUE_LEN(isp);
+       if (icbp->icb_rqstqlen < 8) {
+               isp_prt(isp, ISP_LOGERR, "bad request queue length %d", icbp->icb_rqstqlen);
+               return;
+       }
+       icbp->icb_rsltqlen = RESULT_QUEUE_LEN(isp);
+       if (icbp->icb_rsltqlen < 8) {
+               isp_prt(isp, ISP_LOGERR, "bad result queue length %d",
+                   icbp->icb_rsltqlen);
+               return;
+       }
+       icbp->icb_rqstaddr[RQRSP_ADDR0015] = DMA_WD0(isp->isp_rquest_dma);
+       icbp->icb_rqstaddr[RQRSP_ADDR1631] = DMA_WD1(isp->isp_rquest_dma);
+       icbp->icb_rqstaddr[RQRSP_ADDR3247] = DMA_WD2(isp->isp_rquest_dma);
+       icbp->icb_rqstaddr[RQRSP_ADDR4863] = DMA_WD3(isp->isp_rquest_dma);
 
-       if (IS_2100(isp)) {
-               /*
-                * Don't bother with fabric if we are using really old
-                * 2100 firmware. It's just not worth it.
-                */
-               if (ISP_FW_NEWER_THAN(isp, 1, 15, 37)) {
-                       check_for_fabric = 1;
-               } else {
-                       check_for_fabric = 0;
-               }
-       } else if (fcp->isp_topo == TOPO_FL_PORT ||
-           fcp->isp_topo == TOPO_F_PORT) {
-               check_for_fabric = 1;
-       } else
-               check_for_fabric = 0;
+       icbp->icb_respaddr[RQRSP_ADDR0015] = DMA_WD0(isp->isp_result_dma);
+       icbp->icb_respaddr[RQRSP_ADDR1631] = DMA_WD1(isp->isp_result_dma);
+       icbp->icb_respaddr[RQRSP_ADDR3247] = DMA_WD2(isp->isp_result_dma);
+       icbp->icb_respaddr[RQRSP_ADDR4863] = DMA_WD3(isp->isp_result_dma);
 
-       if (check_for_fabric && isp_getpdb(isp, FL_PORT_ID, &pdb) == 0) {
-               int loopid = FL_PORT_ID;
-               if (IS_2100(isp)) {
-                       fcp->isp_topo = TOPO_FL_PORT;
-               }
+#ifdef ISP_TARGET_MODE
+       /* unconditionally set up the ATIO queue if we support target mode */
+       icbp->icb_atioqlen = RESULT_QUEUE_LEN(isp);
+       if (icbp->icb_atioqlen < 8) {
+               isp_prt(isp, ISP_LOGERR, "bad ATIO queue length %d", icbp->icb_atioqlen);
+               return;
+       }
+       icbp->icb_atioqaddr[RQRSP_ADDR0015] = DMA_WD0(isp->isp_atioq_dma);
+       icbp->icb_atioqaddr[RQRSP_ADDR1631] = DMA_WD1(isp->isp_atioq_dma);
+       icbp->icb_atioqaddr[RQRSP_ADDR3247] = DMA_WD2(isp->isp_atioq_dma);
+       icbp->icb_atioqaddr[RQRSP_ADDR4863] = DMA_WD3(isp->isp_atioq_dma);
+       isp_prt(isp, ISP_LOGDEBUG0, "isp_fibre_init_2400: atioq %04x%04x%04x%04x", DMA_WD3(isp->isp_atioq_dma), DMA_WD2(isp->isp_atioq_dma),
+           DMA_WD1(isp->isp_atioq_dma), DMA_WD0(isp->isp_atioq_dma));
+#endif
 
-               if (BITS2WORD(pdb.pdb_portid_bits) == 0) {
-                       /*
-                        * Crock.
-                        */
-                       fcp->isp_topo = TOPO_NL_PORT;
-                       goto not_on_fabric;
-               }
-               fcp->isp_portid = mbs.param[2] | ((int) mbs.param[3] << 16);
+       isp_prt(isp, ISP_LOGDEBUG0, "isp_fibre_init_2400: fwopt1 0x%x fwopt2 0x%x fwopt3 0x%x", icbp->icb_fwoptions1, icbp->icb_fwoptions2, icbp->icb_fwoptions3);
 
-               /*
-                * Save the Fabric controller's port database entry.
-                */
-               lp = &fcp->portdb[loopid];
-               lp->node_wwn =
-                   (((u_int64_t)pdb.pdb_nodename[0]) << 56) |
-                   (((u_int64_t)pdb.pdb_nodename[1]) << 48) |
-                   (((u_int64_t)pdb.pdb_nodename[2]) << 40) |
-                   (((u_int64_t)pdb.pdb_nodename[3]) << 32) |
-                   (((u_int64_t)pdb.pdb_nodename[4]) << 24) |
-                   (((u_int64_t)pdb.pdb_nodename[5]) << 16) |
-                   (((u_int64_t)pdb.pdb_nodename[6]) <<  8) |
-                   (((u_int64_t)pdb.pdb_nodename[7]));
-               lp->port_wwn =
-                   (((u_int64_t)pdb.pdb_portname[0]) << 56) |
-                   (((u_int64_t)pdb.pdb_portname[1]) << 48) |
-                   (((u_int64_t)pdb.pdb_portname[2]) << 40) |
-                   (((u_int64_t)pdb.pdb_portname[3]) << 32) |
-                   (((u_int64_t)pdb.pdb_portname[4]) << 24) |
-                   (((u_int64_t)pdb.pdb_portname[5]) << 16) |
-                   (((u_int64_t)pdb.pdb_portname[6]) <<  8) |
-                   (((u_int64_t)pdb.pdb_portname[7]));
-               lp->roles =
-                   (pdb.pdb_prli_svc3 & SVC3_ROLE_MASK) >> SVC3_ROLE_SHIFT;
-               lp->portid = BITS2WORD(pdb.pdb_portid_bits);
-               lp->loopid = pdb.pdb_loopid;
-               lp->loggedin = lp->valid = 1;
-               fcp->isp_onfabric = 1;
-               (void) isp_async(isp, ISPASYNC_PROMENADE, &loopid);
-               isp_register_fc4_type(isp);
-       } else {
-not_on_fabric:
-               fcp->isp_onfabric = 0;
-               fcp->portdb[FL_PORT_ID].valid = 0;
+       isp_prt(isp, ISP_LOGDEBUG0, "isp_fibre_init_2400: rqst %04x%04x%04x%04x rsp %04x%04x%04x%04x", DMA_WD3(isp->isp_rquest_dma), DMA_WD2(isp->isp_rquest_dma),
+           DMA_WD1(isp->isp_rquest_dma), DMA_WD0(isp->isp_rquest_dma), DMA_WD3(isp->isp_result_dma), DMA_WD2(isp->isp_result_dma),
+           DMA_WD1(isp->isp_result_dma), DMA_WD0(isp->isp_result_dma));
+
+       if (isp->isp_dblev & ISP_LOGDEBUG1) {
+               isp_print_bytes(isp, "isp_fibre_init_2400", sizeof (*icbp), icbp);
        }
 
-       fcp->isp_gbspeed = 1;
-       if (IS_23XX(isp)) {
-               mbs.param[0] = MBOX_GET_SET_DATA_RATE;
-               mbs.param[1] = MBGSD_GET_RATE;
-               /* mbs.param[2] undefined if we're just getting rate */
-               isp_mboxcmd(isp, &mbs, MBLOGALL);
-               if (mbs.param[0] == MBOX_COMMAND_COMPLETE) {
-                       if (mbs.param[1] == MBGSD_TWOGB) {
-                               isp_prt(isp, ISP_LOGINFO, "2Gb link speed/s");
-                               fcp->isp_gbspeed = 2;
+       if (FC_SCRATCH_ACQUIRE(isp, 0)) {
+               isp_prt(isp, ISP_LOGERR, sacq);
+               return;
+       }
+       ISP_MEMZERO(fcp->isp_scratch, ISP_FC_SCRLEN);
+       isp_put_icb_2400(isp, icbp, fcp->isp_scratch);
+
+       /*
+        * Now fill in information about any additional channels
+        */
+       if (isp->isp_nchan > 1) {
+               isp_icb_2400_vpinfo_t vpinfo, *vdst;
+               vp_port_info_t pi, *pdst;
+               size_t amt = 0;
+               uint8_t *off;
+
+               vpinfo.vp_count = isp->isp_nchan - 1;
+               vpinfo.vp_global_options = 0;
+               off = fcp->isp_scratch;
+               off += ICB2400_VPINFO_OFF;
+               vdst = (isp_icb_2400_vpinfo_t *) off;
+               isp_put_icb_2400_vpinfo(isp, &vpinfo, vdst);
+               amt = ICB2400_VPINFO_OFF + sizeof (isp_icb_2400_vpinfo_t);
+               for (chan = 1; chan < isp->isp_nchan; chan++) {
+                       fcparam *fcp2;
+
+                       ISP_MEMZERO(&pi, sizeof (pi));
+                       fcp2 = FCPARAM(isp, chan);
+                       if (fcp2->role != ISP_ROLE_NONE) {
+                               pi.vp_port_options = ICB2400_VPOPT_ENABLED;
+                               if (fcp2->role & ISP_ROLE_INITIATOR) {
+                                       pi.vp_port_options |= ICB2400_VPOPT_INI_ENABLE;
+                               }
+                               if ((fcp2->role & ISP_ROLE_TARGET) == 0) {
+                                       pi.vp_port_options |= ICB2400_VPOPT_TGT_DISABLE;
+                               }
+                               MAKE_NODE_NAME_FROM_WWN(pi.vp_port_portname, fcp2->isp_wwpn);
+                               MAKE_NODE_NAME_FROM_WWN(pi.vp_port_nodename, fcp2->isp_wwnn);
                        }
+                       off = fcp->isp_scratch;
+                       off += ICB2400_VPINFO_PORT_OFF(chan);
+                       pdst = (vp_port_info_t *) off;
+                       isp_put_vp_port_info(isp, &pi, pdst);
+                       amt += ICB2400_VPOPT_WRITE_SIZE;
                }
        }
 
-       isp_prt(isp, ISP_LOGCONFIG, topology, fcp->isp_loopid, fcp->isp_alpa,
-           fcp->isp_portid, fcp->isp_loopstate, toponames[fcp->isp_topo]);
-
        /*
-        * Announce ourselves, too. This involves synthesizing an entry.
+        * Init the firmware
         */
-       if (fcp->isp_iid_set == 0) {
-               fcp->isp_iid_set = 1;
-               fcp->isp_iid = fcp->isp_loopid;
-               lp = &fcp->portdb[fcp->isp_iid];
+       MBSINIT(&mbs, 0, MBLOGALL, 30000000);
+       if (isp->isp_nchan > 1) {
+               mbs.param[0] = MBOX_INIT_FIRMWARE_MULTI_ID;
        } else {
-               lp = &fcp->portdb[fcp->isp_iid];
-               if (fcp->isp_portid != lp->portid ||
-                   fcp->isp_loopid != lp->loopid ||
-                   fcp->isp_nodewwn != ISP_NODEWWN(isp) ||
-                   fcp->isp_portwwn != ISP_PORTWWN(isp)) {
-                       lp->valid = 0;
-                       count = fcp->isp_iid;
-                       (void) isp_async(isp, ISPASYNC_PROMENADE, &count);
-               }
-       }
-       lp->loopid = fcp->isp_loopid;
-       lp->portid = fcp->isp_portid;
-       lp->node_wwn = ISP_NODEWWN(isp);
-       lp->port_wwn = ISP_PORTWWN(isp);
-       switch (isp->isp_role) {
-       case ISP_ROLE_NONE:
-               lp->roles = 0;
+               mbs.param[0] = MBOX_INIT_FIRMWARE;
+       }
+       mbs.param[2] = DMA_WD1(fcp->isp_scdma);
+       mbs.param[3] = DMA_WD0(fcp->isp_scdma);
+       mbs.param[6] = DMA_WD3(fcp->isp_scdma);
+       mbs.param[7] = DMA_WD2(fcp->isp_scdma);
+       isp_prt(isp, ISP_LOGDEBUG0, "INIT F/W from %04x%04x%04x%04x", DMA_WD3(fcp->isp_scdma), DMA_WD2(fcp->isp_scdma), DMA_WD1(fcp->isp_scdma), DMA_WD0(fcp->isp_scdma));
+       MEMORYBARRIER(isp, SYNC_SFORDEV, 0, sizeof (*icbp), 0);
+       isp_mboxcmd(isp, &mbs);
+       FC_SCRATCH_RELEASE(isp, 0);
+
+       if (mbs.param[0] != MBOX_COMMAND_COMPLETE) {
+               return;
+       }
+       isp->isp_reqidx = 0;
+       isp->isp_reqodx = 0;
+       isp->isp_residx = 0;
+
+       /*
+        * Whatever happens, we're now committed to being here.
+        */
+       isp->isp_state = ISP_INITSTATE;
+}
+
+static void
+isp_mark_portdb(ispsoftc_t *isp, int chan, int disposition)
+{
+       fcparam *fcp = FCPARAM(isp, chan);
+       int i;
+
+       if (chan < 0 || chan >= isp->isp_nchan) {
+               isp_prt(isp, ISP_LOGWARN, "isp_mark_portdb: bad channel %d", chan);
+               return;
+       }
+       for (i = 0; i < MAX_FC_TARG; i++) {
+               if (fcp->portdb[i].target_mode) {
+                       if (disposition < 0) {
+                               isp_prt(isp, ISP_LOGTINFO, "isp_mark_portdb: Chan %d zeroing handle 0x" "%04x port 0x%06x", chan,
+                                   fcp->portdb[i].handle, fcp->portdb[i].portid);
+                               ISP_MEMZERO(&fcp->portdb[i], sizeof (fcportdb_t));
+                       }
+                       continue;
+               }
+               if (disposition == 0) {
+                       ISP_MEMZERO(&fcp->portdb[i], sizeof (fcportdb_t));
+               } else {
+                       switch (fcp->portdb[i].state) {
+                       case FC_PORTDB_STATE_CHANGED:
+                       case FC_PORTDB_STATE_PENDING_VALID:
+                       case FC_PORTDB_STATE_VALID:
+                       case FC_PORTDB_STATE_PROBATIONAL:
+                               fcp->portdb[i].state = FC_PORTDB_STATE_PROBATIONAL;
+                               break;
+                       case FC_PORTDB_STATE_ZOMBIE:
+                               break;
+                       case FC_PORTDB_STATE_NIL:
+                       default:
+                               ISP_MEMZERO(&fcp->portdb[i], sizeof (fcportdb_t));
+                               fcp->portdb[i].state = FC_PORTDB_STATE_NIL;
+                               break;
+                       }
+               }
+       }
+}
+
+/*
+ * Perform an IOCB PLOGI or LOGO via EXECUTE IOCB A64 for 24XX cards
+ * or via FABRIC LOGIN/FABRIC LOGOUT for other cards.
+ */
+static int
+isp_plogx(ispsoftc_t *isp, int chan, uint16_t handle, uint32_t portid, int flags, int gs)
+{
+       mbreg_t mbs;
+       uint8_t q[QENTRY_LEN];
+       isp_plogx_t *plp;
+       fcparam *fcp;
+       uint8_t *scp;
+       uint32_t sst, parm1;
+       int rval, lev;
+       const char *msg;
+       char buf[64];
+
+       if (!IS_24XX(isp)) {
+               int action = flags & PLOGX_FLG_CMD_MASK;
+               if (action == PLOGX_FLG_CMD_PLOGI) {
+                       return (isp_port_login(isp, handle, portid));
+               } else if (action == PLOGX_FLG_CMD_LOGO) {
+                       return (isp_port_logout(isp, handle, portid));
+               } else {
+                       return (MBOX_INVALID_COMMAND);
+               }
+       }
+
+       ISP_MEMZERO(q, QENTRY_LEN);
+       plp = (isp_plogx_t *) q;
+       plp->plogx_header.rqs_entry_count = 1;
+       plp->plogx_header.rqs_entry_type = RQSTYPE_LOGIN;
+       plp->plogx_handle = 0xffffffff;
+       plp->plogx_nphdl = handle;
+       plp->plogx_vphdl = chan;
+       plp->plogx_portlo = portid;
+       plp->plogx_rspsz_porthi = (portid >> 16) & 0xff;
+       plp->plogx_flags = flags;
+
+       if (isp->isp_dblev & ISP_LOGDEBUG1) {
+               isp_print_bytes(isp, "IOCB LOGX", QENTRY_LEN, plp);
+       }
+
+       if (gs == 0) {
+               if (FC_SCRATCH_ACQUIRE(isp, chan)) {
+                       isp_prt(isp, ISP_LOGERR, sacq);
+                       return (-1);
+               }
+       }
+       fcp = FCPARAM(isp, chan);
+       scp = fcp->isp_scratch;
+       isp_put_plogx(isp, plp, (isp_plogx_t *) scp);
+
+       MBSINIT(&mbs, MBOX_EXEC_COMMAND_IOCB_A64, MBLOGALL, 500000);
+       mbs.param[1] = QENTRY_LEN;
+       mbs.param[2] = DMA_WD1(fcp->isp_scdma);
+       mbs.param[3] = DMA_WD0(fcp->isp_scdma);
+       mbs.param[6] = DMA_WD3(fcp->isp_scdma);
+       mbs.param[7] = DMA_WD2(fcp->isp_scdma);
+       MEMORYBARRIER(isp, SYNC_SFORDEV, 0, QENTRY_LEN, chan);
+       isp_mboxcmd(isp, &mbs);
+       if (mbs.param[0] != MBOX_COMMAND_COMPLETE) {
+               rval = mbs.param[0];
+               goto out;
+       }
+       MEMORYBARRIER(isp, SYNC_SFORCPU, QENTRY_LEN, QENTRY_LEN, chan);
+       scp += QENTRY_LEN;
+       isp_get_plogx(isp, (isp_plogx_t *) scp, plp);
+       if (isp->isp_dblev & ISP_LOGDEBUG1) {
+               isp_print_bytes(isp, "IOCB LOGX response", QENTRY_LEN, plp);
+       }
+
+       if (plp->plogx_status == PLOGX_STATUS_OK) {
+               rval = 0;
+               goto out;
+       } else if (plp->plogx_status != PLOGX_STATUS_IOCBERR) {
+               isp_prt(isp, ISP_LOGWARN,
+                   "status 0x%x on port login IOCB chanel %d",
+                   plp->plogx_status, chan);
+               rval = -1;
+               goto out;
+       }
+
+       sst = plp->plogx_ioparm[0].lo16 | (plp->plogx_ioparm[0].hi16 << 16);
+       parm1 = plp->plogx_ioparm[1].lo16 | (plp->plogx_ioparm[1].hi16 << 16);
+
+       rval = -1;
+       lev = ISP_LOGERR;
+       msg = NULL;
+
+       switch (sst) {
+       case PLOGX_IOCBERR_NOLINK:
+               msg = "no link";
+               break;
+       case PLOGX_IOCBERR_NOIOCB:
+               msg = "no IOCB buffer";
+               break;
+       case PLOGX_IOCBERR_NOXGHG:
+               msg = "no Exchange Control Block";
+               break;
+       case PLOGX_IOCBERR_FAILED:
+               ISP_SNPRINTF(buf, sizeof (buf), "reason 0x%x (last LOGIN state 0x%x)", parm1 & 0xff, (parm1 >> 8) & 0xff);
+               msg = buf;
+               break;
+       case PLOGX_IOCBERR_NOFABRIC:
+               msg = "no fabric";
                break;
-       case ISP_ROLE_TARGET:
-               lp->roles = SVC3_TGT_ROLE >> SVC3_ROLE_SHIFT;
+       case PLOGX_IOCBERR_NOTREADY:
+               msg = "firmware not ready";
                break;
-       case ISP_ROLE_INITIATOR:
-               lp->roles = SVC3_INI_ROLE >> SVC3_ROLE_SHIFT;
+       case PLOGX_IOCBERR_NOLOGIN:
+               ISP_SNPRINTF(buf, sizeof (buf), "not logged in (last state 0x%x)", parm1);
+               msg = buf;
+               rval = MBOX_NOT_LOGGED_IN;
                break;
-       case ISP_ROLE_BOTH:
-               lp->roles = (SVC3_INI_ROLE|SVC3_TGT_ROLE) >> SVC3_ROLE_SHIFT;
+       case PLOGX_IOCBERR_REJECT:
+               ISP_SNPRINTF(buf, sizeof (buf), "LS_RJT = 0x%x", parm1);
+               msg = buf;
                break;
+       case PLOGX_IOCBERR_NOPCB:
+               msg = "no PCB allocated";
+               break;
+       case PLOGX_IOCBERR_EINVAL:
+               ISP_SNPRINTF(buf, sizeof (buf), "invalid parameter at offset 0x%x", parm1);
+               msg = buf;
+               break;
+       case PLOGX_IOCBERR_PORTUSED:
+               lev = ISP_LOGSANCFG|ISP_LOGDEBUG0;
+               ISP_SNPRINTF(buf, sizeof (buf), "already logged in with N-Port handle 0x%x", parm1);
+               msg = buf;
+               rval = MBOX_PORT_ID_USED | (parm1 << 16);
+               break;
+       case PLOGX_IOCBERR_HNDLUSED:
+               lev = ISP_LOGSANCFG|ISP_LOGDEBUG0;
+               ISP_SNPRINTF(buf, sizeof (buf), "handle already used for PortID 0x%06x", parm1);
+               msg = buf;
+               rval = MBOX_LOOP_ID_USED;
+               break;
+       case PLOGX_IOCBERR_NOHANDLE:
+               msg = "no handle allocated";
+               break;
+       case PLOGX_IOCBERR_NOFLOGI:
+               msg = "no FLOGI_ACC";
+               break;
+       default:
+               ISP_SNPRINTF(buf, sizeof (buf), "status %x from %x", plp->plogx_status, flags);
+               msg = buf;
+               break;
+       }
+       if (msg) {
+               isp_prt(isp, ISP_LOGERR, "Chan %d PLOGX PortID 0x%06x to N-Port handle 0x%x: %s", chan, portid, handle, msg);
+       }
+out:
+       if (gs == 0) {
+               FC_SCRATCH_RELEASE(isp, chan);
+       }
+       return (rval);
+}
+
+static int
+isp_port_login(ispsoftc_t *isp, uint16_t handle, uint32_t portid)
+{
+       mbreg_t mbs;
+
+       MBSINIT(&mbs, MBOX_FABRIC_LOGIN, MBLOGNONE, 500000);
+       if (ISP_CAP_2KLOGIN(isp)) {
+               mbs.param[1] = handle;
+               mbs.ibits = (1 << 10);
+       } else {
+               mbs.param[1] = handle << 8;
+       }
+       mbs.param[2] = portid >> 16;
+       mbs.param[3] = portid;
+       mbs.logval = MBLOGNONE;
+       mbs.timeout = 500000;
+       isp_mboxcmd(isp, &mbs);
+
+       switch (mbs.param[0]) {
+       case MBOX_PORT_ID_USED:
+               isp_prt(isp, ISP_LOGDEBUG0,
+                   "isp_port_login: portid 0x%06x already logged in as %u",
+                   portid, mbs.param[1]);
+               return (MBOX_PORT_ID_USED | (mbs.param[1] << 16));
+
+       case MBOX_LOOP_ID_USED:
+               isp_prt(isp, ISP_LOGDEBUG0,
+                   "isp_port_login: handle 0x%04x in use for port id 0x%02xXXXX",
+                   handle, mbs.param[1] & 0xff);
+               return (MBOX_LOOP_ID_USED);
+
+       case MBOX_COMMAND_COMPLETE:
+               return (0);
+
+       case MBOX_COMMAND_ERROR:
+               isp_prt(isp, ISP_LOGINFO,
+                   "isp_port_login: error 0x%x in PLOGI to port 0x%06x",
+                   mbs.param[1], portid);
+               return (MBOX_COMMAND_ERROR);
+
+       case MBOX_ALL_IDS_USED:
+               isp_prt(isp, ISP_LOGINFO,
+                   "isp_port_login: all IDs used for fabric login");
+               return (MBOX_ALL_IDS_USED);
+
+       default:
+               isp_prt(isp, ISP_LOGINFO,
+                   "isp_port_login: error 0x%x on port login of 0x%06x@0x%0x",
+                   mbs.param[0], portid, handle);
+               return (mbs.param[0]);
+       }
+}
+
+static int
+isp_port_logout(ispsoftc_t *isp, uint16_t handle, uint32_t portid)
+{
+       mbreg_t mbs;
+
+       MBSINIT(&mbs, MBOX_FABRIC_LOGOUT, MBLOGNONE, 500000);
+       if (ISP_CAP_2KLOGIN(isp)) {
+               mbs.param[1] = handle;
+               mbs.ibits = (1 << 10);
+       } else {
+               mbs.param[1] = handle << 8;
+       }
+       isp_mboxcmd(isp, &mbs);
+       return (mbs.param[0] == MBOX_COMMAND_COMPLETE? 0 : mbs.param[0]);
+}
+
+static int
+isp_getpdb(ispsoftc_t *isp, int chan, uint16_t id, isp_pdb_t *pdb, int dolock)
+{
+       fcparam *fcp = FCPARAM(isp, chan);
+       mbreg_t mbs;
+       union {
+               isp_pdb_21xx_t fred;
+               isp_pdb_24xx_t bill;
+       } un;
+
+       MBSINIT(&mbs, MBOX_GET_PORT_DB, MBLOGALL & ~MBOX_COMMAND_PARAM_ERROR, 250000);
+       if (IS_24XX(isp)) {
+               mbs.ibits = (1 << 9)|(1 << 10);
+               mbs.param[1] = id;
+               mbs.param[9] = chan;
+       } else if (ISP_CAP_2KLOGIN(isp)) {
+               mbs.param[1] = id;
+       } else {
+               mbs.param[1] = id << 8;
+       }
+       mbs.param[2] = DMA_WD1(fcp->isp_scdma);
+       mbs.param[3] = DMA_WD0(fcp->isp_scdma);
+       mbs.param[6] = DMA_WD3(fcp->isp_scdma);
+       mbs.param[7] = DMA_WD2(fcp->isp_scdma);
+       if (dolock) {
+               if (FC_SCRATCH_ACQUIRE(isp, chan)) {
+                       isp_prt(isp, ISP_LOGERR, sacq);
+                       return (-1);
+               }
+       }
+       MEMORYBARRIER(isp, SYNC_SFORDEV, 0, sizeof (un), chan);
+       isp_mboxcmd(isp, &mbs);
+       if (mbs.param[0] != MBOX_COMMAND_COMPLETE) {
+               if (dolock) {
+                       FC_SCRATCH_RELEASE(isp, chan);
+               }
+               return (mbs.param[0]);
+       }
+       if (IS_24XX(isp)) {
+               isp_get_pdb_24xx(isp, fcp->isp_scratch, &un.bill);
+               pdb->handle = un.bill.pdb_handle;
+               pdb->s3_role = un.bill.pdb_prli_svc3;
+               pdb->portid = BITS2WORD_24XX(un.bill.pdb_portid_bits);
+               ISP_MEMCPY(pdb->portname, un.bill.pdb_portname, 8);
+               ISP_MEMCPY(pdb->nodename, un.bill.pdb_nodename, 8);
+               isp_prt(isp, ISP_LOGSANCFG|ISP_LOGDEBUG0,
+                   "Chan %d Port 0x%06x flags 0x%x curstate %x",
+                   chan, pdb->portid, un.bill.pdb_flags,
+                   un.bill.pdb_curstate);
+               if (un.bill.pdb_curstate < PDB2400_STATE_PLOGI_DONE ||
+                   un.bill.pdb_curstate > PDB2400_STATE_LOGGED_IN) {
+                       mbs.param[0] = MBOX_NOT_LOGGED_IN;
+                       if (dolock) {
+                               FC_SCRATCH_RELEASE(isp, chan);
+                       }
+                       return (mbs.param[0]);
+               }
+       } else {
+               isp_get_pdb_21xx(isp, fcp->isp_scratch, &un.fred);
+               pdb->handle = un.fred.pdb_loopid;
+               pdb->s3_role = un.fred.pdb_prli_svc3;
+               pdb->portid = BITS2WORD(un.fred.pdb_portid_bits);
+               ISP_MEMCPY(pdb->portname, un.fred.pdb_portname, 8);
+               ISP_MEMCPY(pdb->nodename, un.fred.pdb_nodename, 8);
+       }
+       if (dolock) {
+               FC_SCRATCH_RELEASE(isp, chan);
+       }
+       return (0);
+}
+
+static void
+isp_dump_chip_portdb(ispsoftc_t *isp, int chan, int dolock)
+{
+       isp_pdb_t pdb;
+       int lim, loopid;
+
+       if (ISP_CAP_2KLOGIN(isp)) {
+               lim = NPH_MAX_2K;
+       } else {
+               lim = NPH_MAX;
+       }
+       for (loopid = 0; loopid != lim; loopid++) {
+               if (isp_getpdb(isp, chan, loopid, &pdb, dolock)) {
+                       continue;
+               }
+               isp_prt(isp, ISP_LOGSANCFG|ISP_LOGINFO, "Chan %d Loopid 0x%04x "
+                   "PortID 0x%06x WWPN 0x%02x%02x%02x%02x%02x%02x%02x%02x",
+                   chan, loopid, pdb.portid, pdb.portname[0], pdb.portname[1],
+                   pdb.portname[2], pdb.portname[3], pdb.portname[4],
+                   pdb.portname[5], pdb.portname[6], pdb.portname[7]);
+       }
+}
+
+static uint64_t
+isp_get_wwn(ispsoftc_t *isp, int chan, int loopid, int nodename)
+{
+       uint64_t wwn = INI_NONE;
+       fcparam *fcp = FCPARAM(isp, chan);
+       mbreg_t mbs;
+
+       if (fcp->isp_fwstate < FW_READY ||
+           fcp->isp_loopstate < LOOP_PDB_RCVD) {
+               return (wwn);
+       }
+       MBSINIT(&mbs, MBOX_GET_PORT_NAME, MBLOGALL & ~MBOX_COMMAND_PARAM_ERROR, 500000);
+       if (ISP_CAP_2KLOGIN(isp)) {
+               mbs.param[1] = loopid;
+               mbs.ibits = (1 << 10);
+               if (nodename) {
+                       mbs.param[10] = 1;
+               }
+               if (ISP_CAP_MULTI_ID(isp)) {
+                       mbs.ibits |= (1 << 9);
+                       mbs.param[9] = chan;
+               }
+       } else {
+               mbs.param[1] = loopid << 8;
+               if (nodename) {
+                       mbs.param[1] |= 1;
+               }
+       }
+       isp_mboxcmd(isp, &mbs);
+       if (mbs.param[0] != MBOX_COMMAND_COMPLETE) {
+               return (wwn);
+       }
+       if (IS_24XX(isp)) {
+               wwn =
+                   (((uint64_t)(mbs.param[2] >> 8))    << 56) |
+                   (((uint64_t)(mbs.param[2] & 0xff))  << 48) |
+                   (((uint64_t)(mbs.param[3] >> 8))    << 40) |
+                   (((uint64_t)(mbs.param[3] & 0xff))  << 32) |
+                   (((uint64_t)(mbs.param[6] >> 8))    << 24) |
+                   (((uint64_t)(mbs.param[6] & 0xff))  << 16) |
+                   (((uint64_t)(mbs.param[7] >> 8))    <<  8) |
+                   (((uint64_t)(mbs.param[7] & 0xff)));
+       } else {
+               wwn =
+                   (((uint64_t)(mbs.param[2] & 0xff))  << 56) |
+                   (((uint64_t)(mbs.param[2] >> 8))    << 48) |
+                   (((uint64_t)(mbs.param[3] & 0xff))  << 40) |
+                   (((uint64_t)(mbs.param[3] >> 8))    << 32) |
+                   (((uint64_t)(mbs.param[6] & 0xff))  << 24) |
+                   (((uint64_t)(mbs.param[6] >> 8))    << 16) |
+                   (((uint64_t)(mbs.param[7] & 0xff))  <<  8) |
+                   (((uint64_t)(mbs.param[7] >> 8)));
+       }
+       return (wwn);
+}
+
+/*
+ * Make sure we have good FC link.
+ */
+
+static int
+isp_fclink_test(ispsoftc_t *isp, int chan, int usdelay)
+{
+       mbreg_t mbs;
+       int count, check_for_fabric, r;
+       uint8_t lwfs;
+       int loopid;
+       fcparam *fcp;
+       fcportdb_t *lp;
+       isp_pdb_t pdb;
+
+       fcp = FCPARAM(isp, chan);
+
+       isp_prt(isp, ISP_LOGSANCFG|ISP_LOGDEBUG0, "Chan %d FC Link Test Entry", chan);
+       ISP_MARK_PORTDB(isp, chan, 1);
+
+       /*
+        * Wait up to N microseconds for F/W to go to a ready state.
+        */
+       lwfs = FW_CONFIG_WAIT;
+       count = 0;
+       while (count < usdelay) {
+               uint64_t enano;
+               uint32_t wrk;
+               NANOTIME_T hra, hrb;
+
+               GET_NANOTIME(&hra);
+               isp_fw_state(isp, chan);
+               if (lwfs != fcp->isp_fwstate) {
+                       isp_prt(isp, ISP_LOGCONFIG|ISP_LOGSANCFG, "Chan %d Firmware State <%s->%s>", chan, isp_fc_fw_statename((int)lwfs), isp_fc_fw_statename((int)fcp->isp_fwstate));
+                       lwfs = fcp->isp_fwstate;
+               }
+               if (fcp->isp_fwstate == FW_READY) {
+                       break;
+               }
+               GET_NANOTIME(&hrb);
+
+               /*
+                * Get the elapsed time in nanoseconds.
+                * Always guaranteed to be non-zero.
+                */
+               enano = NANOTIME_SUB(&hrb, &hra);
+
+               isp_prt(isp, ISP_LOGDEBUG1, "usec%d: 0x%lx->0x%lx enano 0x%x%08x", count, (long) GET_NANOSEC(&hra), (long) GET_NANOSEC(&hrb), (uint32_t)(enano >> 32), (uint32_t)(enano));
+
+               /*
+                * If the elapsed time is less than 1 millisecond,
+                * delay a period of time up to that millisecond of
+                * waiting.
+                *
+                * This peculiar code is an attempt to try and avoid
+                * invoking uint64_t math support functions for some
+                * platforms where linkage is a problem.
+                */
+               if (enano < (1000 * 1000)) {
+                       count += 1000;
+                       enano = (1000 * 1000) - enano;
+                       while (enano > (uint64_t) 4000000000U) {
+                               ISP_SLEEP(isp, 4000000);
+                               enano -= (uint64_t) 4000000000U;
+                       }
+                       wrk = enano;
+                       wrk /= 1000;
+                       ISP_SLEEP(isp, wrk);
+               } else {
+                       while (enano > (uint64_t) 4000000000U) {
+                               count += 4000000;
+                               enano -= (uint64_t) 4000000000U;
+                       }
+                       wrk = enano;
+                       count += (wrk / 1000);
+               }
+       }
+
+
+
+       /*
+        * If we haven't gone to 'ready' state, return.
+        */
+       if (fcp->isp_fwstate != FW_READY) {
+               isp_prt(isp, ISP_LOGSANCFG, "%s: chan %d not at FW_READY state", __func__, chan);
+               return (-1);
+       }
+
+       /*
+        * Get our Loop ID and Port ID.
+        */
+       MBSINIT(&mbs, MBOX_GET_LOOP_ID, MBLOGALL, 0);
+       if (ISP_CAP_MULTI_ID(isp)) {
+               mbs.param[9] = chan;
+               mbs.ibits = (1 << 9);
+               mbs.obits = (1 << 7);
+       }
+       isp_mboxcmd(isp, &mbs);
+       if (mbs.param[0] != MBOX_COMMAND_COMPLETE) {
+               return (-1);
+       }
+
+       if (ISP_CAP_2KLOGIN(isp)) {
+               fcp->isp_loopid = mbs.param[1];
+       } else {
+               fcp->isp_loopid = mbs.param[1] & 0xff;
+       }
+
+       if (IS_2100(isp)) {
+               fcp->isp_topo = TOPO_NL_PORT;
+       } else {
+               int topo = (int) mbs.param[6];
+               if (topo < TOPO_NL_PORT || topo > TOPO_PTP_STUB) {
+                       topo = TOPO_PTP_STUB;
+               }
+               fcp->isp_topo = topo;
+       }
+       fcp->isp_portid = mbs.param[2] | (mbs.param[3] << 16);
+
+       if (IS_2100(isp)) {
+               /*
+                * Don't bother with fabric if we are using really old
+                * 2100 firmware. It's just not worth it.
+                */
+               if (ISP_FW_NEWER_THAN(isp, 1, 15, 37)) {
+                       check_for_fabric = 1;
+               } else {
+                       check_for_fabric = 0;
+               }
+       } else if (fcp->isp_topo == TOPO_FL_PORT || fcp->isp_topo == TOPO_F_PORT) {
+               check_for_fabric = 1;
+       } else {
+               check_for_fabric = 0;
+       }
+
+       /*
+        * Check to make sure we got a valid loopid
+        * The 24XX seems to mess this up for multiple channels.
+        */
+       if (fcp->isp_topo == TOPO_FL_PORT || fcp->isp_topo == TOPO_NL_PORT) {
+               uint8_t alpa = fcp->isp_portid;
+
+               if (alpa == 0) {
+                       /* "Cannot Happen" */
+                       isp_prt(isp, ISP_LOGWARN, "Zero AL_PA for Loop Topology?");
+               } else {
+                       int i;
+                       for (i = 0; alpa_map[i]; i++) {
+                               if (alpa_map[i] == alpa) {
+                                       break;
+                               }
+                       }
+                       if (alpa_map[i] && fcp->isp_loopid != i) {
+                               isp_prt(isp, ISP_LOGSANCFG|ISP_LOGDEBUG0, "Chan %d deriving loopid %d from AL_PA map  (AL_PA 0x%x) and ignoring returned value %d (AL_PA 0x%x)", chan, i, alpa_map[i], fcp->isp_loopid, alpa);
+                               fcp->isp_loopid = i;
+                       }
+               }
+       }
+
+
+       if (IS_24XX(isp)) { /* XXX SHOULDN'T THIS BE FOR 2K F/W? XXX */
+               loopid = NPH_FL_ID;
+       } else {
+               loopid = FL_ID;
+       }
+       if (check_for_fabric) {
+               r = isp_getpdb(isp, chan, loopid, &pdb, 1);
+               if (r && (fcp->isp_topo == TOPO_F_PORT || fcp->isp_topo == TOPO_FL_PORT)) {
+                       isp_prt(isp, ISP_LOGWARN, "fabric topology but cannot get info about fabric controller (0x%x)", r);
+                       fcp->isp_topo = TOPO_PTP_STUB;
+               }
+       } else {
+               r = -1;
+       }
+       if (r == 0) {
+               if (IS_2100(isp)) {
+                       fcp->isp_topo = TOPO_FL_PORT;
+               }
+               if (pdb.portid == 0) {
+                       /*
+                        * Crock.
+                        */
+                       fcp->isp_topo = TOPO_NL_PORT;
+                       goto not_on_fabric;
+               }
+
+               /*
+                * Save the Fabric controller's port database entry.
+                */
+               lp = &fcp->portdb[FL_ID];
+               lp->state = FC_PORTDB_STATE_PENDING_VALID;
+               MAKE_WWN_FROM_NODE_NAME(lp->node_wwn, pdb.nodename);
+               MAKE_WWN_FROM_NODE_NAME(lp->port_wwn, pdb.portname);
+               lp->roles = (pdb.s3_role & SVC3_ROLE_MASK) >> SVC3_ROLE_SHIFT;
+               lp->portid = pdb.portid;
+               lp->handle = pdb.handle;
+               lp->new_portid = lp->portid;
+               lp->new_roles = lp->roles;
+               if (IS_24XX(isp)) {
+                       fcp->inorder = (mbs.param[7] & ISP24XX_INORDER) != 0;
+                       if (ISP_FW_NEWER_THAN(isp, 4, 0, 27)) {
+                               fcp->npiv_fabric = (mbs.param[7] & ISP24XX_NPIV_SAN) != 0;
+                               if (fcp->npiv_fabric) {
+                                       isp_prt(isp, ISP_LOGCONFIG, "fabric supports NP-IV");
+                               }
+                       }
+                       if (chan) {
+                               fcp->isp_sns_hdl = NPH_SNS_HDLBASE + chan;
+                               r = isp_plogx(isp, chan, fcp->isp_sns_hdl, SNS_PORT_ID, PLOGX_FLG_CMD_PLOGI | PLOGX_FLG_COND_PLOGI | PLOGX_FLG_SKIP_PRLI, 0);
+                               if (r) {
+                                       isp_prt(isp, ISP_LOGWARN, "%s: Chan %d cannot log into SNS", __func__, chan);
+                                       return (-1);
+                               }
+                       } else {
+                               fcp->isp_sns_hdl = NPH_SNS_ID;
+                       }
+                       r = isp_register_fc4_type_24xx(isp, chan);
+               } else {
+                       fcp->isp_sns_hdl = SNS_ID;
+                       r = isp_register_fc4_type(isp, chan);
+               }
+               if (r) {
+                       isp_prt(isp, ISP_LOGWARN|ISP_LOGSANCFG, "%s: register fc4 type failed", __func__);
+                       return (-1);
+               }
+       } else {
+not_on_fabric:
+               fcp->portdb[FL_ID].state = FC_PORTDB_STATE_NIL;
+       }
+
+       fcp->isp_gbspeed = 1;
+       if (IS_23XX(isp) || IS_24XX(isp)) {
+               MBSINIT(&mbs, MBOX_GET_SET_DATA_RATE, MBLOGALL, 3000000);
+               mbs.param[1] = MBGSD_GET_RATE;
+               /* mbs.param[2] undefined if we're just getting rate */
+               isp_mboxcmd(isp, &mbs);
+               if (mbs.param[0] == MBOX_COMMAND_COMPLETE) {
+                       if (mbs.param[1] == MBGSD_EIGHTGB) {
+                               isp_prt(isp, ISP_LOGINFO, "Chan %d 8Gb link speed", chan);
+                               fcp->isp_gbspeed = 8;
+                       } else if (mbs.param[1] == MBGSD_FOURGB) {
+                               isp_prt(isp, ISP_LOGINFO, "Chan %d 4Gb link speed", chan);
+                               fcp->isp_gbspeed = 4;
+                       } else if (mbs.param[1] == MBGSD_TWOGB) {
+                               isp_prt(isp, ISP_LOGINFO, "Chan %d 2Gb link speed", chan);
+                               fcp->isp_gbspeed = 2;
+                       } else if (mbs.param[1] == MBGSD_ONEGB) {
+                               isp_prt(isp, ISP_LOGINFO, "Chan %d 1Gb link speed", chan);
+                               fcp->isp_gbspeed = 1;
+                       }
+               }
+       }
+
+       /*
+        * Announce ourselves, too.
+        */
+       isp_prt(isp, ISP_LOGSANCFG|ISP_LOGCONFIG, topology, chan, (uint32_t) (fcp->isp_wwpn >> 32), (uint32_t) fcp->isp_wwpn, fcp->isp_portid, fcp->isp_loopid, isp_fc_toponame(fcp));
+       isp_prt(isp, ISP_LOGSANCFG|ISP_LOGDEBUG0, "Chan %d FC Link Test Complete", chan);
+       return (0);
+}
+
+/*
+ * Complete the synchronization of our Port Database.
+ *
+ * At this point, we've scanned the local loop (if any) and the fabric
+ * and performed fabric logins on all new devices.
+ *
+ * Our task here is to go through our port database and remove any entities
+ * that are still marked probational (issuing PLOGO for ones which we had
+ * PLOGI'd into) or are dead.
+ *
+ * Our task here is to also check policy to decide whether devices which
+ * have *changed* in some way should still be kept active. For example,
+ * if a device has just changed PortID, we can either elect to treat it
+ * as an old device or as a newly arrived device (and notify the outer
+ * layer appropriately).
+ *
+ * We also do initiator map target id assignment here for new initiator
+ * devices and refresh old ones ot make sure that they point to the corret
+ * entities.
+ */
+static int
+isp_pdb_sync(ispsoftc_t *isp, int chan)
+{
+       fcparam *fcp = FCPARAM(isp, chan);
+       fcportdb_t *lp;
+       uint16_t dbidx;
+
+       if (fcp->isp_loopstate == LOOP_READY) {
+               return (0);
+       }
+
+       /*
+        * Make sure we're okay for doing this right now.
+        */
+       if (fcp->isp_loopstate != LOOP_PDB_RCVD &&
+           fcp->isp_loopstate != LOOP_FSCAN_DONE &&
+           fcp->isp_loopstate != LOOP_LSCAN_DONE) {
+               isp_prt(isp, ISP_LOGWARN, "isp_pdb_sync: bad loopstate %d",
+                   fcp->isp_loopstate);
+               return (-1);
+       }
+
+       if (fcp->isp_topo == TOPO_FL_PORT ||
+           fcp->isp_topo == TOPO_NL_PORT ||
+           fcp->isp_topo == TOPO_N_PORT) {
+               if (fcp->isp_loopstate < LOOP_LSCAN_DONE) {
+                       if (isp_scan_loop(isp, chan) != 0) {
+                               isp_prt(isp, ISP_LOGWARN,
+                                   "isp_pdb_sync: isp_scan_loop failed");
+                               return (-1);
+                       }
+               }
+       }
+
+       if (fcp->isp_topo == TOPO_F_PORT || fcp->isp_topo == TOPO_FL_PORT) {
+               if (fcp->isp_loopstate < LOOP_FSCAN_DONE) {
+                       if (isp_scan_fabric(isp, chan) != 0) {
+                               isp_prt(isp, ISP_LOGWARN,
+                                   "isp_pdb_sync: isp_scan_fabric failed");
+                               return (-1);
+                       }
+               }
+       }
+
+       isp_prt(isp, ISP_LOGSANCFG|ISP_LOGDEBUG0,
+           "Chan %d Synchronizing PDBs", chan);
+
+       fcp->isp_loopstate = LOOP_SYNCING_PDB;
+
+       for (dbidx = 0; dbidx < MAX_FC_TARG; dbidx++) {
+               lp = &fcp->portdb[dbidx];
+
+               if (lp->state == FC_PORTDB_STATE_NIL || lp->target_mode) {
+                       continue;
+               }
+
+               if (lp->state == FC_PORTDB_STATE_VALID) {
+                       if (dbidx != FL_ID) {
+                               isp_prt(isp,
+                                   ISP_LOGERR, "portdb idx %d already valid",
+                                   dbidx);
+                       }
+                       continue;
+               }
+
+               switch (lp->state) {
+               case FC_PORTDB_STATE_PROBATIONAL:
+               case FC_PORTDB_STATE_DEAD:
+                       /*
+                        * It's up to the outer layers to clear isp_dev_map.
+                        */
+                       lp->state = FC_PORTDB_STATE_NIL;
+                       isp_async(isp, ISPASYNC_DEV_GONE, chan, lp);
+                       if (lp->autologin == 0) {
+                               (void) isp_plogx(isp, chan, lp->handle,
+                                   lp->portid,
+                                   PLOGX_FLG_CMD_LOGO |
+                                   PLOGX_FLG_IMPLICIT |
+                                   PLOGX_FLG_FREE_NPHDL, 0);
+                       } else {
+                               lp->autologin = 0;
+                       }
+                       lp->new_roles = 0;
+                       lp->new_portid = 0;
+                       /*
+                        * Note that we might come out of this with our state
+                        * set to FC_PORTDB_STATE_ZOMBIE.
+                        */
+                       break;
+               case FC_PORTDB_STATE_NEW:
+                       /*
+                        * It's up to the outer layers to assign a virtual
+                        * target id in isp_dev_map (if any).
+                        */
+                       lp->portid = lp->new_portid;
+                       lp->roles = lp->new_roles;
+                       lp->state = FC_PORTDB_STATE_VALID;
+                       isp_async(isp, ISPASYNC_DEV_ARRIVED, chan, lp);
+                       lp->new_roles = 0;
+                       lp->new_portid = 0;
+                       lp->reserved = 0;
+                       lp->new_reserved = 0;
+                       break;
+               case FC_PORTDB_STATE_CHANGED:
+/*
+ * XXXX FIX THIS
+ */
+                       lp->state = FC_PORTDB_STATE_VALID;
+                       isp_async(isp, ISPASYNC_DEV_CHANGED, chan, lp);
+                       lp->new_roles = 0;
+                       lp->new_portid = 0;
+                       lp->reserved = 0;
+                       lp->new_reserved = 0;
+                       break;
+               case FC_PORTDB_STATE_PENDING_VALID:
+                       lp->portid = lp->new_portid;
+                       lp->roles = lp->new_roles;
+                       if (lp->dev_map_idx) {
+                               int t = lp->dev_map_idx - 1;
+                               fcp->isp_dev_map[t] = dbidx + 1;
+                       }
+                       lp->state = FC_PORTDB_STATE_VALID;
+                       isp_async(isp, ISPASYNC_DEV_STAYED, chan, lp);
+                       if (dbidx != FL_ID) {
+                               lp->new_roles = 0;
+                               lp->new_portid = 0;
+                       }
+                       lp->reserved = 0;
+                       lp->new_reserved = 0;
+                       break;
+               case FC_PORTDB_STATE_ZOMBIE:
+                       break;
+               default:
+                       isp_prt(isp, ISP_LOGWARN,
+                           "isp_scan_loop: state %d for idx %d",
+                           lp->state, dbidx);
+                       isp_dump_portdb(isp, chan);
+               }
        }
-       lp->loggedin = lp->valid = 1;
-       count = fcp->isp_iid;
-       (void) isp_async(isp, ISPASYNC_PROMENADE, &count);
-       return (0);
-}
 
-static char *
-isp2100_fw_statename(int state)
-{
-       switch(state) {
-       case FW_CONFIG_WAIT:    return "Config Wait";
-       case FW_WAIT_AL_PA:     return "Waiting for AL_PA";
-       case FW_WAIT_LOGIN:     return "Wait Login";
-       case FW_READY:          return "Ready";
-       case FW_LOSS_OF_SYNC:   return "Loss Of Sync";
-       case FW_ERROR:          return "Error";
-       case FW_REINIT:         return "Re-Init";
-       case FW_NON_PART:       return "Nonparticipating";
-       default:                return "?????";
-       }
+       /*
+        * If we get here, we've for sure seen not only a valid loop
+        * but know what is or isn't on it, so mark this for usage
+        * in isp_start.
+        */
+       fcp->loop_seen_once = 1;
+       fcp->isp_loopstate = LOOP_READY;
+       return (0);
 }
 
 /*
- * Synchronize our soft copy of the port database with what the f/w thinks
- * (with a view toward possibly for a specific target....)
+ * Scan local loop for devices.
  */
-
 static int
-isp_pdb_sync(struct ispsoftc *isp)
+isp_scan_loop(ispsoftc_t *isp, int chan)
 {
-       struct lportdb *lp;
-       fcparam *fcp = isp->isp_param;
+       fcportdb_t *lp, tmp;
+       fcparam *fcp = FCPARAM(isp, chan);
+       int i;
        isp_pdb_t pdb;
-       int loopid, base, lim;
+       uint16_t handle, lim = 0;
 
-       /*
-        * Make sure we're okay for doing this right now.
-        */
-       if (fcp->isp_loopstate != LOOP_PDB_RCVD &&
-           fcp->isp_loopstate != LOOP_FSCAN_DONE &&
-           fcp->isp_loopstate != LOOP_LSCAN_DONE) {
+       if (fcp->isp_fwstate < FW_READY ||
+           fcp->isp_loopstate < LOOP_PDB_RCVD) {
                return (-1);
        }
 
-       if (fcp->isp_topo == TOPO_FL_PORT || fcp->isp_topo == TOPO_NL_PORT ||
-           fcp->isp_topo == TOPO_N_PORT) {
-               if (fcp->isp_loopstate < LOOP_LSCAN_DONE) {
-                       if (isp_scan_loop(isp) != 0) {
-                               return (-1);
-                       }
-               }
-       }
-       fcp->isp_loopstate = LOOP_SYNCING_PDB;
-
-       /*
-        * If we get this far, we've settled our differences with the f/w
-        * (for local loop device) and we can say that the loop state is ready.
-        */
-
-       if (fcp->isp_topo == TOPO_NL_PORT) {
-               fcp->loop_seen_once = 1;
-               fcp->isp_loopstate = LOOP_READY;
+       if (fcp->isp_loopstate > LOOP_SCANNING_LOOP) {
                return (0);
        }
 
        /*
-        * Find all Fabric Entities that didn't make it from one scan to the
-        * next and let the world know they went away. Scan the whole database.
+        * Check our connection topology.
+        *
+        * If we're a public or private loop, we scan 0..125 as handle values.
+        * The firmware has (typically) peformed a PLOGI for us. We skip this
+        * step if we're a ISP_24XX in NP-IV mode.
+        *
+        * If we're a N-port connection, we treat this is a short loop (0..1).
         */
-       for (lp = &fcp->portdb[0]; lp < &fcp->portdb[MAX_FC_TARG]; lp++) {
-               if (lp->was_fabric_dev && lp->fabric_dev == 0) {
-                       loopid = lp - fcp->portdb;
-                       lp->valid = 0;  /* should already be set */
-                       (void) isp_async(isp, ISPASYNC_PROMENADE, &loopid);
-                       MEMZERO((void *) lp, sizeof (*lp));
-                       continue;
+       switch (fcp->isp_topo) {
+       case TOPO_NL_PORT:
+               lim = LOCAL_LOOP_LIM;
+               break;
+       case TOPO_FL_PORT:
+               if (IS_24XX(isp) && isp->isp_nchan > 1) {
+                       isp_prt(isp, ISP_LOGSANCFG|ISP_LOGDEBUG0,
+                           "Chan %d Skipping Local Loop Scan", chan);
+                       fcp->isp_loopstate = LOOP_LSCAN_DONE;
+                       return (0);
                }
-               lp->was_fabric_dev = lp->fabric_dev;
+               lim = LOCAL_LOOP_LIM;
+               break;
+       case TOPO_N_PORT:
+               lim = 2;
+               break;
+       default:
+               isp_prt(isp, ISP_LOGSANCFG|ISP_LOGDEBUG0,
+                   "Chan %d no loop topology to scan", chan);
+               fcp->isp_loopstate = LOOP_LSCAN_DONE;
+               return (0);
        }
 
-       if (fcp->isp_topo == TOPO_FL_PORT)
-               base = FC_SNS_ID+1;
-       else
-               base = 0;
+       fcp->isp_loopstate = LOOP_SCANNING_LOOP;
+
+       isp_prt(isp, ISP_LOGSANCFG|ISP_LOGDEBUG0,
+           "Chan %d FC scan loop 0..%d", chan, lim-1);
 
-       if (fcp->isp_topo == TOPO_N_PORT)
-               lim = 1;
-       else
-               lim = MAX_FC_TARG;
 
        /*
-        * Now log in any fabric devices that the outer layer has
-        * left for us to see. This seems the most sane policy
-        * for the moment.
+        * Run through the list and get the port database info for each one.
         */
-       for (lp = &fcp->portdb[base]; lp < &fcp->portdb[lim]; lp++) {
-               u_int32_t portid;
-               mbreg_t mbs;
-
-               loopid = lp - fcp->portdb;
-               if (loopid >= FL_PORT_ID && loopid <= FC_SNS_ID) {
+       for (handle = 0; handle < lim; handle++) {
+               int r;
+               /*
+                * Don't scan "special" ids.
+                */
+               if (handle >= FL_ID && handle <= SNS_ID) {
                        continue;
                }
-
+               if (ISP_CAP_2KLOGIN(isp)) {
+                       if (handle >= NPH_RESERVED && handle <= NPH_FL_ID) {
+                               continue;
+                       }
+               }
                /*
-                * Anything here?
+                * In older cards with older f/w GET_PORT_DATABASE has been
+                * known to hang. This trick gets around that problem.
                 */
-               if (lp->port_wwn == 0) {
-                       continue;
+               if (IS_2100(isp) || IS_2200(isp)) {
+                       uint64_t node_wwn = isp_get_wwn(isp, chan, handle, 1);
+                       if (fcp->isp_loopstate < LOOP_SCANNING_LOOP) {
+                               isp_prt(isp, ISP_LOGSANCFG|ISP_LOGDEBUG0,
+                                   "Chan %d FC scan loop DONE (bad)", chan);
+                               return (-1);
+                       }
+                       if (node_wwn == INI_NONE) {
+                               continue;
+                       }
                }
 
                /*
-                * Don't try to log into yourself.
+                * Get the port database entity for this index.
                 */
-               if ((portid = lp->portid) == fcp->isp_portid) {
+               r = isp_getpdb(isp, chan, handle, &pdb, 1);
+               if (r != 0) {
+                       isp_prt(isp, ISP_LOGDEBUG1,
+                           "Chan %d FC scan loop handle %d returned %x",
+                           chan, handle, r);
+                       if (fcp->isp_loopstate < LOOP_SCANNING_LOOP) {
+                               ISP_MARK_PORTDB(isp, chan, 1);
+                               isp_prt(isp, ISP_LOGSANCFG|ISP_LOGDEBUG0,
+                                   "Chan %d FC scan loop DONE (bad)", chan);
+                               return (-1);
+                       }
                        continue;
                }
 
+               if (fcp->isp_loopstate < LOOP_SCANNING_LOOP) {
+                       ISP_MARK_PORTDB(isp, chan, 1);
+                       isp_prt(isp, ISP_LOGSANCFG|ISP_LOGDEBUG0,
+                           "Chan %d FC scan loop DONE (bad)", chan);
+                       return (-1);
+               }
 
                /*
-                * If we'd been logged in- see if we still are and we haven't
-                * changed. If so, no need to log ourselves out, etc..
-                *
-                * Unfortunately, our charming Qlogic f/w has decided to
-                * return a valid port database entry for a fabric device
-                * that has, in fact, gone away. And it hangs trying to
-                * log it out.
+                * On *very* old 2100 firmware we would end up sometimes
+                * with the firmware returning the port database entry
+                * for something else. We used to restart this, but
+                * now we just punt.
                 */
-               if (lp->loggedin && lp->force_logout == 0 &&
-                   isp_getpdb(isp, lp->loopid, &pdb) == 0) {
-                       int nrole;
-                       u_int64_t nwwnn, nwwpn;
-                       nwwnn =
-                           (((u_int64_t)pdb.pdb_nodename[0]) << 56) |
-                           (((u_int64_t)pdb.pdb_nodename[1]) << 48) |
-                           (((u_int64_t)pdb.pdb_nodename[2]) << 40) |
-                           (((u_int64_t)pdb.pdb_nodename[3]) << 32) |
-                           (((u_int64_t)pdb.pdb_nodename[4]) << 24) |
-                           (((u_int64_t)pdb.pdb_nodename[5]) << 16) |
-                           (((u_int64_t)pdb.pdb_nodename[6]) <<  8) |
-                           (((u_int64_t)pdb.pdb_nodename[7]));
-                       nwwpn =
-                           (((u_int64_t)pdb.pdb_portname[0]) << 56) |
-                           (((u_int64_t)pdb.pdb_portname[1]) << 48) |
-                           (((u_int64_t)pdb.pdb_portname[2]) << 40) |
-                           (((u_int64_t)pdb.pdb_portname[3]) << 32) |
-                           (((u_int64_t)pdb.pdb_portname[4]) << 24) |
-                           (((u_int64_t)pdb.pdb_portname[5]) << 16) |
-                           (((u_int64_t)pdb.pdb_portname[6]) <<  8) |
-                           (((u_int64_t)pdb.pdb_portname[7]));
-                       nrole = (pdb.pdb_prli_svc3 & SVC3_ROLE_MASK) >>
-                           SVC3_ROLE_SHIFT;
-                       if (pdb.pdb_loopid == lp->loopid && lp->portid ==
-                           (u_int32_t) BITS2WORD(pdb.pdb_portid_bits) &&
-                           nwwnn == lp->node_wwn && nwwpn == lp->port_wwn &&
-                           lp->roles == nrole && lp->force_logout == 0) {
-                               lp->loggedin = lp->valid = 1;
-                               isp_prt(isp, ISP_LOGCONFIG, lretained,
-                                   (int) (lp - fcp->portdb),
-                                   (int) lp->loopid, lp->portid);
-                               continue;
-                       }
-               }
-
-               if (fcp->isp_fwstate != FW_READY ||
-                   fcp->isp_loopstate != LOOP_SYNCING_PDB) {
+               if (IS_2100(isp) && pdb.handle != handle) {
+                       isp_prt(isp, ISP_LOGWARN,
+                           "Chan %d cannot synchronize port database", chan);
+                       ISP_MARK_PORTDB(isp, chan, 1);
+                       isp_prt(isp, ISP_LOGSANCFG|ISP_LOGDEBUG0,
+                           "Chan %d FC scan loop DONE (bad)", chan);
                        return (-1);
                }
 
                /*
-                * Force a logout if we were logged in.
+                * Save the pertinent info locally.
                 */
-               if (lp->loggedin) {
-                       if (lp->force_logout ||
-                           isp_getpdb(isp, lp->loopid, &pdb) == 0) {
-                               mbs.param[0] = MBOX_FABRIC_LOGOUT;
-                               mbs.param[1] = lp->loopid << 8;
-                               mbs.param[2] = 0;
-                               mbs.param[3] = 0;
-                               isp_mboxcmd(isp, &mbs, MBLOGNONE);
-                               isp_prt(isp, ISP_LOGINFO, plogout,
-                                   (int) (lp - fcp->portdb), lp->loopid,
-                                   lp->portid);
-                       }
-                       lp->force_logout = lp->loggedin = 0;
-                       if (fcp->isp_fwstate != FW_READY ||
-                           fcp->isp_loopstate != LOOP_SYNCING_PDB) {
-                               return (-1);
+               MAKE_WWN_FROM_NODE_NAME(tmp.node_wwn, pdb.nodename);
+               MAKE_WWN_FROM_NODE_NAME(tmp.port_wwn, pdb.portname);
+               tmp.roles = (pdb.s3_role & SVC3_ROLE_MASK) >> SVC3_ROLE_SHIFT;
+               tmp.portid = pdb.portid;
+               tmp.handle = pdb.handle;
+
+               /*
+                * Check to make sure it's still a valid entry. The 24XX seems
+                * to return a portid but not a WWPN/WWNN or role for devices
+                * which shift on a loop.
+                */
+               if (tmp.node_wwn == 0 || tmp.port_wwn == 0 || tmp.portid == 0) {
+                       int a, b, c;
+                       a = (tmp.node_wwn == 0);
+                       b = (tmp.port_wwn == 0);
+                       c = (tmp.portid == 0);
+                       if (a == 0 && b == 0) {
+                               tmp.node_wwn =
+                                   isp_get_wwn(isp, chan, handle, 1);
+                               tmp.port_wwn =
+                                   isp_get_wwn(isp, chan, handle, 0);
+                               if (tmp.node_wwn && tmp.port_wwn) {
+                                       isp_prt(isp, ISP_LOGINFO, "DODGED!");
+                                       goto cont;
+                               }
                        }
+                       isp_prt(isp, ISP_LOGWARN,
+                           "Chan %d bad pdb (%1d%1d%1d) @ handle 0x%x", chan,
+                           a, b, c, handle);
+                       isp_dump_portdb(isp, chan);
+                       continue;
                }
+  cont:
 
                /*
-                * And log in....
+                * Now search the entire port database
+                * for the same Port and Node WWN.
                 */
-               loopid = lp - fcp->portdb;
-               lp->loopid = FL_PORT_ID;
-               do {
-                       mbs.param[0] = MBOX_FABRIC_LOGIN;
-                       mbs.param[1] = loopid << 8;
-                       mbs.param[2] = portid >> 16;
-                       mbs.param[3] = portid & 0xffff;
-                       isp_mboxcmd(isp, &mbs, MBLOGALL & ~(MBOX_LOOP_ID_USED |
-                           MBOX_PORT_ID_USED | MBOX_COMMAND_ERROR));
-                       if (fcp->isp_fwstate != FW_READY ||
-                           fcp->isp_loopstate != LOOP_SYNCING_PDB) {
+               for (i = 0; i < MAX_FC_TARG; i++) {
+                       lp = &fcp->portdb[i];
+
+                       if (lp->state == FC_PORTDB_STATE_NIL ||
+                           lp->target_mode) {
+                               continue;
+                       }
+                       if (lp->node_wwn != tmp.node_wwn) {
+                               continue;
+                       }
+                       if (lp->port_wwn != tmp.port_wwn) {
+                               continue;
+                       }
+
+                       /*
+                        * Okay- we've found a non-nil entry that matches.
+                        * Check to make sure it's probational or a zombie.
+                        */
+                       if (lp->state != FC_PORTDB_STATE_PROBATIONAL &&
+                           lp->state != FC_PORTDB_STATE_ZOMBIE) {
+                               isp_prt(isp, ISP_LOGERR,
+                                   "Chan %d [%d] not probational/zombie (0x%x)",
+                                   chan, i, lp->state);
+                               isp_dump_portdb(isp, chan);
+                               ISP_MARK_PORTDB(isp, chan, 1);
+                               isp_prt(isp, ISP_LOGSANCFG|ISP_LOGDEBUG0,
+                                   "Chan %d FC scan loop DONE (bad)", chan);
                                return (-1);
                        }
-                       switch (mbs.param[0]) {
-                       case MBOX_LOOP_ID_USED:
-                               /*
-                                * Try the next available loop id.
-                                */
-                               loopid++;
-                               break;
-                       case MBOX_PORT_ID_USED:
-                               /*
-                                * This port is already logged in.
-                                * Snaffle the loop id it's using if it's
-                                * nonzero, otherwise we're hosed.
-                                */
-                               if (mbs.param[1] != 0) {
-                                       loopid = mbs.param[1];
-                                       isp_prt(isp, ISP_LOGINFO, retained,
-                                           loopid, (int) (lp - fcp->portdb),
-                                           lp->portid);
-                               } else {
-                                       loopid = MAX_FC_TARG;
-                                       break;
-                               }
-                               /* FALLTHROUGH */
-                       case MBOX_COMMAND_COMPLETE:
-                               lp->loggedin = 1;
-                               lp->loopid = loopid;
-                               break;
-                       case MBOX_COMMAND_ERROR:
-                               isp_prt(isp, ISP_LOGINFO, plogierr,
-                                   portid, mbs.param[1]);
-                               /* FALLTHROUGH */
-                       case MBOX_ALL_IDS_USED: /* We're outta IDs */
-                       default:
-                               loopid = MAX_FC_TARG;
+
+                       /*
+                        * Mark the device as something the f/w logs into
+                        * automatically.
+                        */
+                       lp->autologin = 1;
+
+                       /*
+                        * Check to make see if really still the same
+                        * device. If it is, we mark it pending valid.
+                        */
+                       if (lp->portid == tmp.portid &&
+                           lp->handle == tmp.handle &&
+                           lp->roles == tmp.roles) {
+                               lp->new_portid = tmp.portid;
+                               lp->new_roles = tmp.roles;
+                               lp->state = FC_PORTDB_STATE_PENDING_VALID;
+                               isp_prt(isp, ISP_LOGSANCFG|ISP_LOGDEBUG0,
+                                   "Chan %d Loop Port 0x%06x@0x%04x Pending "
+                                   "Valid", chan, tmp.portid, tmp.handle);
                                break;
                        }
-               } while (lp->loopid == FL_PORT_ID && loopid < MAX_FC_TARG);
 
-               /*
-                * If we get here and we haven't set a Loop ID,
-                * we failed to log into this device.
-                */
+                       /*
+                        * We can wipe out the old handle value
+                        * here because it's no longer valid.
+                        */
+                       lp->handle = tmp.handle;
 
-               if (lp->loopid == FL_PORT_ID) {
-                       lp->loopid = 0;
-                       continue;
+                       /*
+                        * Claim that this has changed and let somebody else
+                        * decide what to do.
+                        */
+                       isp_prt(isp, ISP_LOGSANCFG|ISP_LOGDEBUG0,
+                           "Chan %d Loop Port 0x%06x@0x%04x changed",
+                           chan, tmp.portid, tmp.handle);
+                       lp->state = FC_PORTDB_STATE_CHANGED;
+                       lp->new_portid = tmp.portid;
+                       lp->new_roles = tmp.roles;
+                       break;
                }
 
                /*
-                * Make sure we can get the approriate port information.
+                * Did we find and update an old entry?
                 */
-               if (isp_getpdb(isp, lp->loopid, &pdb) != 0) {
-                       isp_prt(isp, ISP_LOGWARN, nopdb, lp->portid);
-                       goto dump_em;
-               }
-
-               if (fcp->isp_fwstate != FW_READY ||
-                   fcp->isp_loopstate != LOOP_SYNCING_PDB) {
-                       return (-1);
+               if (i < MAX_FC_TARG) {
+                       continue;
                }
 
-               if (pdb.pdb_loopid != lp->loopid) {
-                       isp_prt(isp, ISP_LOGWARN, pdbmfail1,
-                           lp->portid, pdb.pdb_loopid);
-                       goto dump_em;
-               }
-
-               if (lp->portid != (u_int32_t) BITS2WORD(pdb.pdb_portid_bits)) {
-                       isp_prt(isp, ISP_LOGWARN, pdbmfail2,
-                           lp->portid, BITS2WORD(pdb.pdb_portid_bits));
-                       goto dump_em;
-               }
-
-               lp->roles =
-                   (pdb.pdb_prli_svc3 & SVC3_ROLE_MASK) >> SVC3_ROLE_SHIFT;
-               lp->node_wwn =
-                   (((u_int64_t)pdb.pdb_nodename[0]) << 56) |
-                   (((u_int64_t)pdb.pdb_nodename[1]) << 48) |
-                   (((u_int64_t)pdb.pdb_nodename[2]) << 40) |
-                   (((u_int64_t)pdb.pdb_nodename[3]) << 32) |
-                   (((u_int64_t)pdb.pdb_nodename[4]) << 24) |
-                   (((u_int64_t)pdb.pdb_nodename[5]) << 16) |
-                   (((u_int64_t)pdb.pdb_nodename[6]) <<  8) |
-                   (((u_int64_t)pdb.pdb_nodename[7]));
-               lp->port_wwn =
-                   (((u_int64_t)pdb.pdb_portname[0]) << 56) |
-                   (((u_int64_t)pdb.pdb_portname[1]) << 48) |
-                   (((u_int64_t)pdb.pdb_portname[2]) << 40) |
-                   (((u_int64_t)pdb.pdb_portname[3]) << 32) |
-                   (((u_int64_t)pdb.pdb_portname[4]) << 24) |
-                   (((u_int64_t)pdb.pdb_portname[5]) << 16) |
-                   (((u_int64_t)pdb.pdb_portname[6]) <<  8) |
-                   (((u_int64_t)pdb.pdb_portname[7]));
                /*
-                * Check to make sure this all makes sense.
+                * Ah. A new device entry. Find an empty slot
+                * for it and save info for later disposition.
                 */
-               if (lp->node_wwn && lp->port_wwn) {
-                       lp->valid = 1;
-                       loopid = lp - fcp->portdb;
-                       (void) isp_async(isp, ISPASYNC_PROMENADE, &loopid);
+               for (i = 0; i < MAX_FC_TARG; i++) {
+                       if (fcp->portdb[i].target_mode) {
+                               continue;
+                       }
+                       if (fcp->portdb[i].state == FC_PORTDB_STATE_NIL) {
+                               break;
+                       }
+               }
+               if (i == MAX_FC_TARG) {
+                       isp_prt(isp, ISP_LOGERR,
+                           "Chan %d out of portdb entries", chan);
                        continue;
                }
-dump_em:
-               lp->valid = 0;
-               isp_prt(isp, ISP_LOGINFO,
-                   ldumped, loopid, lp->loopid, lp->portid);
-               mbs.param[0] = MBOX_FABRIC_LOGOUT;
-               mbs.param[1] = lp->loopid << 8;
-               mbs.param[2] = 0;
-               mbs.param[3] = 0;
-               isp_mboxcmd(isp, &mbs, MBLOGNONE);
-               if (fcp->isp_fwstate != FW_READY ||
-                   fcp->isp_loopstate != LOOP_SYNCING_PDB) {
+               lp = &fcp->portdb[i];
+
+               ISP_MEMZERO(lp, sizeof (fcportdb_t));
+               lp->autologin = 1;
+               lp->state = FC_PORTDB_STATE_NEW;
+               lp->new_portid = tmp.portid;
+               lp->new_roles = tmp.roles;
+               lp->handle = tmp.handle;
+               lp->port_wwn = tmp.port_wwn;
+               lp->node_wwn = tmp.node_wwn;
+               isp_prt(isp, ISP_LOGSANCFG|ISP_LOGDEBUG0,
+                   "Chan %d Loop Port 0x%06x@0x%04x is New Entry",
+                   chan, tmp.portid, tmp.handle);
+       }
+       fcp->isp_loopstate = LOOP_LSCAN_DONE;
+       isp_prt(isp, ISP_LOGSANCFG|ISP_LOGDEBUG0,
+           "Chan %d FC scan loop DONE", chan);
+       return (0);
+}
+
+/*
+ * Scan the fabric for devices and add them to our port database.
+ *
+ * Use the GID_FT command to get all Port IDs for FC4 SCSI devices it knows.
+ *
+ * For 2100-23XX cards, we can use the SNS mailbox command to pass simple
+ * name server commands to the switch management server via the QLogic f/w.
+ *
+ * For the 24XX card, we have to use CT-Pass through run via the Execute IOCB
+ * mailbox command.
+ *
+ * The net result is to leave the list of Port IDs setting untranslated in
+ * offset IGPOFF of the FC scratch area, whereupon we'll canonicalize it to
+ * host order at OGPOFF.
+ */
+
+/*
+ * Take less than half of our scratch area to store Port IDs
+ */
+#define        GIDLEN  ((ISP_FC_SCRLEN >> 1) - 16 - SNS_GID_FT_REQ_SIZE)
+#define        NGENT   ((GIDLEN - 16) >> 2)
+
+#define        IGPOFF  (2 * QENTRY_LEN)
+#define        OGPOFF  (ISP_FC_SCRLEN >> 1)
+#define        ZTXOFF  (ISP_FC_SCRLEN - (1 * QENTRY_LEN))
+#define        CTXOFF  (ISP_FC_SCRLEN - (2 * QENTRY_LEN))
+#define        XTXOFF  (ISP_FC_SCRLEN - (3 * QENTRY_LEN))
+
+static int
+isp_gid_ft_sns(ispsoftc_t *isp, int chan)
+{
+       union {
+               sns_gid_ft_req_t _x;
+               uint8_t _y[SNS_GID_FT_REQ_SIZE];
+       } un;
+       fcparam *fcp = FCPARAM(isp, chan);
+       sns_gid_ft_req_t *rq = &un._x;
+       mbreg_t mbs;
+
+       isp_prt(isp, ISP_LOGDEBUG0,
+           "Chan %d scanning fabric (GID_FT) via SNS", chan);
+
+       ISP_MEMZERO(rq, SNS_GID_FT_REQ_SIZE);
+       rq->snscb_rblen = GIDLEN >> 1;
+       rq->snscb_addr[RQRSP_ADDR0015] = DMA_WD0(fcp->isp_scdma + IGPOFF);
+       rq->snscb_addr[RQRSP_ADDR1631] = DMA_WD1(fcp->isp_scdma + IGPOFF);
+       rq->snscb_addr[RQRSP_ADDR3247] = DMA_WD2(fcp->isp_scdma + IGPOFF);
+       rq->snscb_addr[RQRSP_ADDR4863] = DMA_WD3(fcp->isp_scdma + IGPOFF);
+       rq->snscb_sblen = 6;
+       rq->snscb_cmd = SNS_GID_FT;
+       rq->snscb_mword_div_2 = NGENT;
+       rq->snscb_fc4_type = FC4_SCSI;
+
+       isp_put_gid_ft_request(isp, rq, fcp->isp_scratch);
+       MEMORYBARRIER(isp, SYNC_SFORDEV, 0, SNS_GID_FT_REQ_SIZE, chan);
+
+       MBSINIT(&mbs, MBOX_SEND_SNS, MBLOGALL, 10000000);
+       mbs.param[0] = MBOX_SEND_SNS;
+       mbs.param[1] = SNS_GID_FT_REQ_SIZE >> 1;
+       mbs.param[2] = DMA_WD1(fcp->isp_scdma);
+       mbs.param[3] = DMA_WD0(fcp->isp_scdma);
+       mbs.param[6] = DMA_WD3(fcp->isp_scdma);
+       mbs.param[7] = DMA_WD2(fcp->isp_scdma);
+       isp_mboxcmd(isp, &mbs);
+       if (mbs.param[0] != MBOX_COMMAND_COMPLETE) {
+               if (mbs.param[0] == MBOX_INVALID_COMMAND) {
+                       return (1);
+               } else {
                        return (-1);
                }
        }
+       return (0);
+}
+
+static int
+isp_gid_ft_ct_passthru(ispsoftc_t *isp, int chan)
+{
+       mbreg_t mbs;
+       fcparam *fcp = FCPARAM(isp, chan);
+       union {
+               isp_ct_pt_t plocal;
+               ct_hdr_t clocal;
+               uint8_t q[QENTRY_LEN];
+       } un;
+       isp_ct_pt_t *pt;
+       ct_hdr_t *ct;
+       uint32_t *rp;
+       uint8_t *scp = fcp->isp_scratch;
+
+       isp_prt(isp, ISP_LOGDEBUG0,
+           "Chan %d scanning fabric (GID_FT) via CT", chan);
+
+       if (!IS_24XX(isp)) {
+               return (1);
+       }
+
        /*
-        * If we get here, we've for sure seen not only a valid loop
-        * but know what is or isn't on it, so mark this for usage
-        * in isp_start.
+        * Build a Passthrough IOCB in memory.
         */
-       fcp->loop_seen_once = 1;
-       fcp->isp_loopstate = LOOP_READY;
+       pt = &un.plocal;
+       ISP_MEMZERO(un.q, QENTRY_LEN);
+       pt->ctp_header.rqs_entry_count = 1;
+       pt->ctp_header.rqs_entry_type = RQSTYPE_CT_PASSTHRU;
+       pt->ctp_handle = 0xffffffff;
+       pt->ctp_nphdl = fcp->isp_sns_hdl;
+       pt->ctp_cmd_cnt = 1;
+       pt->ctp_vpidx = ISP_GET_VPIDX(isp, chan);
+       pt->ctp_time = 30;
+       pt->ctp_rsp_cnt = 1;
+       pt->ctp_rsp_bcnt = GIDLEN;
+       pt->ctp_cmd_bcnt = sizeof (*ct) + sizeof (uint32_t);
+       pt->ctp_dataseg[0].ds_base = DMA_LO32(fcp->isp_scdma+XTXOFF);
+       pt->ctp_dataseg[0].ds_basehi = DMA_HI32(fcp->isp_scdma+XTXOFF);
+       pt->ctp_dataseg[0].ds_count = sizeof (*ct) + sizeof (uint32_t);
+       pt->ctp_dataseg[1].ds_base = DMA_LO32(fcp->isp_scdma+IGPOFF);
+       pt->ctp_dataseg[1].ds_basehi = DMA_HI32(fcp->isp_scdma+IGPOFF);
+       pt->ctp_dataseg[1].ds_count = GIDLEN;
+       if (isp->isp_dblev & ISP_LOGDEBUG1) {
+               isp_print_bytes(isp, "ct IOCB", QENTRY_LEN, pt);
+       }
+       isp_put_ct_pt(isp, pt, (isp_ct_pt_t *) &scp[CTXOFF]);
+
+       /*
+        * Build the CT header and command in memory.
+        *
+        * Note that the CT header has to end up as Big Endian format in memory.
+        */
+       ct = &un.clocal;
+       ISP_MEMZERO(ct, sizeof (*ct));
+       ct->ct_revision = CT_REVISION;
+       ct->ct_fcs_type = CT_FC_TYPE_FC;
+       ct->ct_fcs_subtype = CT_FC_SUBTYPE_NS;
+       ct->ct_cmd_resp = SNS_GID_FT;
+       ct->ct_bcnt_resid = (GIDLEN - 16) >> 2;
+
+       isp_put_ct_hdr(isp, ct, (ct_hdr_t *) &scp[XTXOFF]);
+       rp = (uint32_t *) &scp[XTXOFF+sizeof (*ct)];
+       ISP_IOZPUT_32(isp, FC4_SCSI, rp);
+       if (isp->isp_dblev & ISP_LOGDEBUG1) {
+               isp_print_bytes(isp, "CT HDR + payload after put",
+                   sizeof (*ct) + sizeof (uint32_t), &scp[XTXOFF]);
+       }
+       ISP_MEMZERO(&scp[ZTXOFF], QENTRY_LEN);
+       MBSINIT(&mbs, MBOX_EXEC_COMMAND_IOCB_A64, MBLOGALL, 500000);
+       mbs.param[1] = QENTRY_LEN;
+       mbs.param[2] = DMA_WD1(fcp->isp_scdma + CTXOFF);
+       mbs.param[3] = DMA_WD0(fcp->isp_scdma + CTXOFF);
+       mbs.param[6] = DMA_WD3(fcp->isp_scdma + CTXOFF);
+       mbs.param[7] = DMA_WD2(fcp->isp_scdma + CTXOFF);
+       MEMORYBARRIER(isp, SYNC_SFORDEV, XTXOFF, 2 * QENTRY_LEN, chan);
+       isp_mboxcmd(isp, &mbs);
+       if (mbs.param[0] != MBOX_COMMAND_COMPLETE) {
+               return (-1);
+       }
+       MEMORYBARRIER(isp, SYNC_SFORCPU, ZTXOFF, QENTRY_LEN, chan);
+       pt = &un.plocal;
+       isp_get_ct_pt(isp, (isp_ct_pt_t *) &scp[ZTXOFF], pt);
+       if (isp->isp_dblev & ISP_LOGDEBUG1) {
+               isp_print_bytes(isp, "IOCB response", QENTRY_LEN, pt);
+       }
+
+       if (pt->ctp_status && pt->ctp_status != RQCS_DATA_UNDERRUN) {
+               isp_prt(isp, ISP_LOGWARN,
+                   "Chan %d ISP GID FT CT Passthrough returned 0x%x",
+                   chan, pt->ctp_status);
+               return (-1);
+       }
+       MEMORYBARRIER(isp, SYNC_SFORCPU, IGPOFF, GIDLEN + 16, chan);
+       if (isp->isp_dblev & ISP_LOGDEBUG1) {
+               isp_print_bytes(isp, "CT response", GIDLEN+16, &scp[IGPOFF]);
+       }
        return (0);
 }
 
 static int
-isp_scan_loop(struct ispsoftc *isp)
+isp_scan_fabric(ispsoftc_t *isp, int chan)
 {
-       struct lportdb *lp;
-       fcparam *fcp = isp->isp_param;
+       fcparam *fcp = FCPARAM(isp, chan);
+       uint32_t portid;
+       uint16_t handle, oldhandle, loopid;
        isp_pdb_t pdb;
-       int loopid, lim, hival;
+       int portidx, portlim, r;
+       sns_gid_ft_rsp_t *rs0, *rs1;
 
-       switch (fcp->isp_topo) {
-       case TOPO_NL_PORT:
-               hival = FL_PORT_ID;
-               break;
-       case TOPO_N_PORT:
-               hival = 2;
-               break;
-       case TOPO_FL_PORT:
-               hival = FC_PORT_ID;
-               break;
-       default:
-               fcp->isp_loopstate = LOOP_LSCAN_DONE;
+       isp_prt(isp, ISP_LOGSANCFG|ISP_LOGDEBUG0,
+           "Chan %d FC Scan Fabric", chan);
+       if (fcp->isp_fwstate != FW_READY ||
+           fcp->isp_loopstate < LOOP_LSCAN_DONE) {
+               return (-1);
+       }
+       if (fcp->isp_loopstate > LOOP_SCANNING_FABRIC) {
+               return (0);
+       }
+       if (fcp->isp_topo != TOPO_FL_PORT && fcp->isp_topo != TOPO_F_PORT) {
+               fcp->isp_loopstate = LOOP_FSCAN_DONE;
+               isp_prt(isp, ISP_LOGSANCFG|ISP_LOGDEBUG0,
+                   "Chan %d FC Scan Fabric Done (no fabric)", chan);
+               return (0);
+       }
+
+       fcp->isp_loopstate = LOOP_SCANNING_FABRIC;
+       if (FC_SCRATCH_ACQUIRE(isp, chan)) {
+               isp_prt(isp, ISP_LOGERR, sacq);
+               ISP_MARK_PORTDB(isp, chan, 1);
+               return (-1);
+       }
+       if (fcp->isp_loopstate < LOOP_SCANNING_FABRIC) {
+               FC_SCRATCH_RELEASE(isp, chan);
+               ISP_MARK_PORTDB(isp, chan, 1);
+               return (-1);
+       }
+
+       /*
+        * Make sure we still are logged into the fabric controller.
+        */
+       if (IS_24XX(isp)) {     /* XXX SHOULDN'T THIS BE TRUE FOR 2K F/W? XXX */
+               loopid = NPH_FL_ID;
+       } else {
+               loopid = FL_ID;
+       }
+       r = isp_getpdb(isp, chan, loopid, &pdb, 0);
+       if (r == MBOX_NOT_LOGGED_IN) {
+               isp_dump_chip_portdb(isp, chan, 0);
+       }
+       if (r) {
+               fcp->isp_loopstate = LOOP_PDB_RCVD;
+               FC_SCRATCH_RELEASE(isp, chan);
+               ISP_MARK_PORTDB(isp, chan, 1);
+               return (-1);
+       }
+
+       if (IS_24XX(isp)) {
+               r = isp_gid_ft_ct_passthru(isp, chan);
+       } else {
+               r = isp_gid_ft_sns(isp, chan);
+       }
+
+       if (fcp->isp_loopstate < LOOP_SCANNING_FABRIC) {
+               FC_SCRATCH_RELEASE(isp, chan);
+               ISP_MARK_PORTDB(isp, chan, 1);
+               return (-1);
+       }
+
+       if (r > 0) {
+               fcp->isp_loopstate = LOOP_FSCAN_DONE;
+               FC_SCRATCH_RELEASE(isp, chan);
+               return (0);
+       } else if (r < 0) {
+               fcp->isp_loopstate = LOOP_PDB_RCVD;     /* try again */
+               FC_SCRATCH_RELEASE(isp, chan);
+               return (0);
+       }
+
+       MEMORYBARRIER(isp, SYNC_SFORCPU, IGPOFF, GIDLEN, chan);
+       rs0 = (sns_gid_ft_rsp_t *) ((uint8_t *)fcp->isp_scratch+IGPOFF);
+       rs1 = (sns_gid_ft_rsp_t *) ((uint8_t *)fcp->isp_scratch+OGPOFF);
+       isp_get_gid_ft_response(isp, rs0, rs1, NGENT);
+       if (fcp->isp_loopstate < LOOP_SCANNING_FABRIC) {
+               FC_SCRATCH_RELEASE(isp, chan);
+               ISP_MARK_PORTDB(isp, chan, 1);
+               return (-1);
+       }
+       if (rs1->snscb_cthdr.ct_cmd_resp != LS_ACC) {
+               int level;
+               if (rs1->snscb_cthdr.ct_reason == 9 &&
+                   rs1->snscb_cthdr.ct_explanation == 7) {
+                       level = ISP_LOGSANCFG|ISP_LOGDEBUG0;
+               } else {
+                       level = ISP_LOGWARN;
+               }
+               isp_prt(isp, level, "Chan %d Fabric Nameserver rejected GID_FT"
+                   " (Reason=0x%x Expl=0x%x)", chan,
+                   rs1->snscb_cthdr.ct_reason,
+                   rs1->snscb_cthdr.ct_explanation);
+               FC_SCRATCH_RELEASE(isp, chan);
+               fcp->isp_loopstate = LOOP_FSCAN_DONE;
                return (0);
        }
-       fcp->isp_loopstate = LOOP_SCANNING_LOOP;
+
 
        /*
-        * make sure the temp port database is clean...
+        * If we get this far, we certainly still have the fabric controller.
         */
-       MEMZERO((void *)fcp->tport, sizeof (fcp->tport));
+       fcp->portdb[FL_ID].state = FC_PORTDB_STATE_PENDING_VALID;
 
        /*
-        * Run through the local loop ports and get port database info
-        * for each loop ID.
-        *
-        * There's a somewhat unexplained situation where the f/w passes back
-        * the wrong database entity- if that happens, just restart (up to
-        * FL_PORT_ID times).
+        * Prime the handle we will start using.
         */
-       for (lim = loopid = 0; loopid < hival; loopid++) {
-               lp = &fcp->tport[loopid];
+       oldhandle = FCPARAM(isp, 0)->isp_lasthdl;
 
-               /*
-                * Don't even try for ourselves...
-                */
-               if (loopid == fcp->isp_loopid)
-                       continue;
+       /*
+        * Go through the list and remove duplicate port ids.
+        */
 
-               lp->node_wwn = isp_get_portname(isp, loopid, 1);
-               if (fcp->isp_loopstate < LOOP_SCANNING_LOOP)
-                       return (-1);
-               if (lp->node_wwn == 0)
-                       continue;
-               lp->port_wwn = isp_get_portname(isp, loopid, 0);
-               if (fcp->isp_loopstate < LOOP_SCANNING_LOOP)
-                       return (-1);
-               if (lp->port_wwn == 0) {
-                       lp->node_wwn = 0;
-                       continue;
+       portlim = 0;
+       portidx = 0;
+       for (portidx = 0; portidx < NGENT-1; portidx++) {
+               if (rs1->snscb_ports[portidx].control & 0x80) {
+                       break;
                }
+       }
 
-               /*
-                * Get an entry....
-                */
-               if (isp_getpdb(isp, loopid, &pdb) != 0) {
-                       if (fcp->isp_loopstate < LOOP_SCANNING_LOOP)
-                               return (-1);
-                       continue;
-               }
-               if (fcp->isp_loopstate < LOOP_SCANNING_LOOP) {
-                       return (-1);
-               }
+       /*
+        * If we're not at the last entry, our list wasn't big enough.
+        */
+       if ((rs1->snscb_ports[portidx].control & 0x80) == 0) {
+               isp_prt(isp, ISP_LOGWARN,
+                   "fabric too big for scratch area: increase ISP_FC_SCRLEN");
+       }
+       portlim = portidx + 1;
+       isp_prt(isp, ISP_LOGSANCFG,
+           "Chan %d got %d ports back from name server", chan, portlim);
 
-               /*
-                * If the returned database element doesn't match what we
-                * asked for, restart the process entirely (up to a point...).
-                */
-               if (pdb.pdb_loopid != loopid) {
-                       loopid = 0;
-                       if (lim++ < hival) {
-                               continue;
+       for (portidx = 0; portidx < portlim; portidx++) {
+               int npidx;
+
+               portid =
+                   ((rs1->snscb_ports[portidx].portid[0]) << 16) |
+                   ((rs1->snscb_ports[portidx].portid[1]) << 8) |
+                   ((rs1->snscb_ports[portidx].portid[2]));
+
+               for (npidx = portidx + 1; npidx < portlim; npidx++) {
+                       uint32_t new_portid =
+                           ((rs1->snscb_ports[npidx].portid[0]) << 16) |
+                           ((rs1->snscb_ports[npidx].portid[1]) << 8) |
+                           ((rs1->snscb_ports[npidx].portid[2]));
+                       if (new_portid == portid) {
+                               break;
                        }
-                       isp_prt(isp, ISP_LOGWARN,
-                           "giving up on synchronizing the port database");
-                       return (-1);
                }
 
-               /*
-                * Save the pertinent info locally.
-                */
-               lp->node_wwn =
-                   (((u_int64_t)pdb.pdb_nodename[0]) << 56) |
-                   (((u_int64_t)pdb.pdb_nodename[1]) << 48) |
-                   (((u_int64_t)pdb.pdb_nodename[2]) << 40) |
-                   (((u_int64_t)pdb.pdb_nodename[3]) << 32) |
-                   (((u_int64_t)pdb.pdb_nodename[4]) << 24) |
-                   (((u_int64_t)pdb.pdb_nodename[5]) << 16) |
-                   (((u_int64_t)pdb.pdb_nodename[6]) <<  8) |
-                   (((u_int64_t)pdb.pdb_nodename[7]));
-               lp->port_wwn =
-                   (((u_int64_t)pdb.pdb_portname[0]) << 56) |
-                   (((u_int64_t)pdb.pdb_portname[1]) << 48) |
-                   (((u_int64_t)pdb.pdb_portname[2]) << 40) |
-                   (((u_int64_t)pdb.pdb_portname[3]) << 32) |
-                   (((u_int64_t)pdb.pdb_portname[4]) << 24) |
-                   (((u_int64_t)pdb.pdb_portname[5]) << 16) |
-                   (((u_int64_t)pdb.pdb_portname[6]) <<  8) |
-                   (((u_int64_t)pdb.pdb_portname[7]));
-               lp->roles =
-                   (pdb.pdb_prli_svc3 & SVC3_ROLE_MASK) >> SVC3_ROLE_SHIFT;
-               lp->portid = BITS2WORD(pdb.pdb_portid_bits);
-               lp->loopid = pdb.pdb_loopid;
-       }
-
-       /*
-        * Mark all of the permanent local loop database entries as invalid
-        * (except our own entry).
-        */
-       for (loopid = 0; loopid < hival; loopid++) {
-               if (loopid == fcp->isp_iid) {
-                       fcp->portdb[loopid].valid = 1;
-                       fcp->portdb[loopid].loopid = fcp->isp_loopid;
-                       continue;
+               if (npidx < portlim) {
+                       rs1->snscb_ports[npidx].portid[0] = 0;
+                       rs1->snscb_ports[npidx].portid[1] = 0;
+                       rs1->snscb_ports[npidx].portid[2] = 0;
+                       isp_prt(isp, ISP_LOGSANCFG|ISP_LOGDEBUG0,
+                           "Chan %d removing duplicate PortID 0x%06x"
+                           " entry from list", chan, portid);
                }
-               fcp->portdb[loopid].valid = 0;
        }
 
        /*
-        * Now merge our local copy of the port database into our saved copy.
-        * Notify the outer layers of new devices arriving.
+        * We now have a list of Port IDs for all FC4 SCSI devices
+        * that the Fabric Name server knows about.
+        *
+        * For each entry on this list go through our port database looking
+        * for probational entries- if we find one, then an old entry is
+        * maybe still this one. We get some information to find out.
+        *
+        * Otherwise, it's a new fabric device, and we log into it
+        * (unconditionally). After searching the entire database
+        * again to make sure that we never ever ever ever have more
+        * than one entry that has the same PortID or the same
+        * WWNN/WWPN duple, we enter the device into our database.
         */
-       for (loopid = 0; loopid < hival; loopid++) {
-               int i;
 
-               /*
-                * If we don't have a non-zero Port WWN, we're not here.
-                */
-               if (fcp->tport[loopid].port_wwn == 0) {
-                       continue;
-               }
+       for (portidx = 0; portidx < portlim; portidx++) {
+               fcportdb_t *lp;
+               uint64_t wwnn, wwpn;
+               int dbidx, nr;
 
-               /*
-                * Skip ourselves.
-                */
-               if (loopid == fcp->isp_iid) {
+               portid =
+                   ((rs1->snscb_ports[portidx].portid[0]) << 16) |
+                   ((rs1->snscb_ports[portidx].portid[1]) << 8) |
+                   ((rs1->snscb_ports[portidx].portid[2]));
+
+               if (portid == 0) {
+                       isp_prt(isp, ISP_LOGSANCFG,
+                           "Chan %d skipping null PortID at idx %d",
+                           chan, portidx);
                        continue;
                }
 
                /*
-                * For the purposes of deciding whether this is the
-                * 'same' device or not, we only search for an identical
-                * Port WWN. Node WWNs may or may not be the same as
-                * the Port WWN, and there may be multiple different
-                * Port WWNs with the same Node WWN. It would be chaos
-                * to have multiple identical Port WWNs, so we don't
-                * allow that.
+                * Skip ourselves here and on other channels. If we're
+                * multi-id, we can't check the portids in other FCPARAM
+                * arenas because the resolutions here aren't synchronized.
+                * The best way to do this is to exclude looking at portids
+                * that have the same domain and area code as our own
+                * portid.
                 */
-
-               for (i = 0; i < hival; i++) {
-                       int j;
-                       if (fcp->portdb[i].port_wwn == 0)
+               if (ISP_CAP_MULTI_ID(isp)) {
+                       if ((portid >> 8) == (fcp->isp_portid >> 8)) {
+                               isp_prt(isp, ISP_LOGSANCFG,
+                                   "Chan %d skip PortID 0x%06x",
+                                   chan, portid);
                                continue;
-                       if (fcp->portdb[i].port_wwn !=
-                           fcp->tport[loopid].port_wwn)
-                               continue;
-                       /*
-                        * We found this WWN elsewhere- it's changed
-                        * loopids then. We don't change it's actual
-                        * position in our cached port database- we
-                        * just change the actual loop ID we'd use.
-                        */
-                       if (fcp->portdb[i].loopid != loopid) {
-                               isp_prt(isp, ISP_LOGINFO, portshift, i,
-                                   fcp->portdb[i].loopid,
-                                   fcp->portdb[i].portid, loopid,
-                                   fcp->tport[loopid].portid);
-                       }
-                       fcp->portdb[i].portid = fcp->tport[loopid].portid;
-                       fcp->portdb[i].loopid = loopid;
-                       fcp->portdb[i].valid = 1;
-                       fcp->portdb[i].roles = fcp->tport[loopid].roles;
-
-                       /*
-                        * Now make sure this Port WWN doesn't exist elsewhere
-                        * in the port database.
-                        */
-                       for (j = i+1; j < hival; j++) {
-                               if (fcp->portdb[i].port_wwn !=
-                                   fcp->portdb[j].port_wwn) {
-                                       continue;
-                               }
-                               isp_prt(isp, ISP_LOGWARN, portdup, j, i);
-                               /*
-                                * Invalidate the 'old' *and* 'new' ones.
-                                * This is really harsh and not quite right,
-                                * but if this happens, we really don't know
-                                * who is what at this point.
-                                */
-                               fcp->portdb[i].valid = 0;
-                               fcp->portdb[j].valid = 0;
                        }
-                       break;
-               }
-
-               /*
-                * If we didn't traverse the entire port database,
-                * then we found (and remapped) an existing entry.
-                * No need to notify anyone- go for the next one.
-                */
-               if (i < hival) {
-                       isp_prt(isp, ISP_LOGINFO, retained,
-                           fcp->portdb[i].loopid, i, fcp->portdb[i].portid);
+               } else if (portid == fcp->isp_portid) {
+                       isp_prt(isp, ISP_LOGSANCFG,
+                           "Chan %d skip ourselves on @ PortID 0x%06x",
+                           chan, portid);
                        continue;
                }
 
+               isp_prt(isp, ISP_LOGSANCFG,
+                   "Chan %d Checking Fabric Port 0x%06x", chan, portid);
+
                /*
-                * We've not found this Port WWN anywhere. It's a new entry.
-                * See if we can leave it where it is (with target == loopid).
+                * We now search our Port Database for any
+                * probational entries with this PortID. We don't
+                * look for zombies here- only probational
+                * entries (we've already logged out of zombies).
                 */
-               if (fcp->portdb[loopid].port_wwn != 0) {
-                       for (lim = 0; lim < hival; lim++) {
-                               if (fcp->portdb[lim].port_wwn == 0)
-                                       break;
-                       }
-                       /* "Cannot Happen" */
-                       if (lim == hival) {
-                               isp_prt(isp, ISP_LOGWARN, "Remap Overflow");
+               for (dbidx = 0; dbidx < MAX_FC_TARG; dbidx++) {
+                       lp = &fcp->portdb[dbidx];
+
+                       if (lp->state != FC_PORTDB_STATE_PROBATIONAL ||
+                           lp->target_mode) {
                                continue;
                        }
-                       i = lim;
-               } else {
-                       i = loopid;
+                       if (lp->portid == portid) {
+                               break;
+                       }
                }
 
                /*
-                * NB:  The actual loopid we use here is loopid- we may
-                *      in fact be at a completely different index (target).
-                */
-               fcp->portdb[i].loopid = loopid;
-               fcp->portdb[i].port_wwn = fcp->tport[loopid].port_wwn;
-               fcp->portdb[i].node_wwn = fcp->tport[loopid].node_wwn;
-               fcp->portdb[i].roles = fcp->tport[loopid].roles;
-               fcp->portdb[i].portid = fcp->tport[loopid].portid;
-               fcp->portdb[i].valid = 1;
-
-               /*
-                * Tell the outside world we've arrived.
+                * We found a probational entry with this Port ID.
                 */
-               (void) isp_async(isp, ISPASYNC_PROMENADE, &i);
-       }
-
-       /*
-        * Now find all previously used targets that are now invalid and
-        * notify the outer layers that they're gone.
-        */
-       for (lp = &fcp->portdb[0]; lp < &fcp->portdb[hival]; lp++) {
-               if (lp->valid || lp->port_wwn == 0) {
-                       continue;
-               }
+               if (dbidx < MAX_FC_TARG) {
+                       int handle_changed = 0;
 
-               /*
-                * Tell the outside world we've gone
-                * away and erase our pdb entry.
-                *
-                */
-               loopid = lp - fcp->portdb;
-               (void) isp_async(isp, ISPASYNC_PROMENADE, &loopid);
-               MEMZERO((void *) lp, sizeof (*lp));
-       }
-       fcp->isp_loopstate = LOOP_LSCAN_DONE;
-       return (0);
-}
+                       lp = &fcp->portdb[dbidx];
 
+                       /*
+                        * See if we're still logged into it.
+                        *
+                        * If we aren't, mark it as a dead device and
+                        * leave the new portid in the database entry
+                        * for somebody further along to decide what to
+                        * do (policy choice).
+                        *
+                        * If we are, check to see if it's the same
+                        * device still (it should be). If for some
+                        * reason it isn't, mark it as a changed device
+                        * and leave the new portid and role in the
+                        * database entry for somebody further along to
+                        * decide what to do (policy choice).
+                        *
+                        */
 
-static int
-isp_fabric_mbox_cmd(struct ispsoftc *isp, mbreg_t *mbp)
-{
-       isp_mboxcmd(isp, mbp, MBLOGNONE);
-       if (mbp->param[0] != MBOX_COMMAND_COMPLETE) {
-               if (FCPARAM(isp)->isp_loopstate == LOOP_SCANNING_FABRIC) {
-                       FCPARAM(isp)->isp_loopstate = LOOP_PDB_RCVD;
-               }
-               if (mbp->param[0] == MBOX_COMMAND_ERROR) {
-                       char tbuf[16];
-                       char *m;
-                       switch (mbp->param[1]) {
-                       case 1:
-                               m = "No Loop";
-                               break;
-                       case 2:
-                               m = "Failed to allocate IOCB buffer";
-                               break;
-                       case 3:
-                               m = "Failed to allocate XCB buffer";
-                               break;
-                       case 4:
-                               m = "timeout or transmit failed";
-                               break;
-                       case 5:
-                               m = "no fabric loop";
-                               break;
-                       case 6:
-                               m = "remote device not a target";
-                               break;
-                       default:
-                               SNPRINTF(tbuf, sizeof tbuf, "%x",
-                                   mbp->param[1]);
-                               m = tbuf;
-                               break;
+                       r = isp_getpdb(isp, chan, lp->handle, &pdb, 0);
+                       if (fcp->isp_loopstate != LOOP_SCANNING_FABRIC) {
+                               FC_SCRATCH_RELEASE(isp, chan);
+                               ISP_MARK_PORTDB(isp, chan, 1);
+                               return (-1);
+                       }
+                       if (r != 0) {
+                               lp->new_portid = portid;
+                               lp->state = FC_PORTDB_STATE_DEAD;
+                               isp_prt(isp, ISP_LOGSANCFG|ISP_LOGDEBUG0,
+                                   "Chan %d Fabric Port 0x%06x is dead",
+                                   chan, portid);
+                               continue;
                        }
-                       isp_prt(isp, ISP_LOGERR, "SNS Failed- %s", m);
-               }
-               return (-1);
-       }
 
-       if (FCPARAM(isp)->isp_fwstate != FW_READY ||
-           FCPARAM(isp)->isp_loopstate < LOOP_SCANNING_FABRIC) {
-               return (-1);
-       }
-       return(0);
-}
 
-#ifdef ISP_USE_GA_NXT
-static int
-isp_scan_fabric(struct ispsoftc *isp, int ftype)
-{
-       fcparam *fcp = isp->isp_param;
-       u_int32_t portid, first_portid, last_portid;
-       int hicap, last_port_same;
+                       /*
+                        * Check to make sure that handle, portid, WWPN and
+                        * WWNN agree. If they don't, then the association
+                        * between this PortID and the stated handle has been
+                        * broken by the firmware.
+                        */
+                       MAKE_WWN_FROM_NODE_NAME(wwnn, pdb.nodename);
+                       MAKE_WWN_FROM_NODE_NAME(wwpn, pdb.portname);
+                       if (pdb.handle != lp->handle ||
+                           pdb.portid != portid ||
+                           wwpn != lp->port_wwn ||
+                           wwnn != lp->node_wwn) {
+                               isp_prt(isp, ISP_LOGSANCFG|ISP_LOGDEBUG0,
+                                   fconf, chan, dbidx, pdb.handle, pdb.portid,
+                                   (uint32_t) (wwnn >> 32), (uint32_t) wwnn,
+                                   (uint32_t) (wwpn >> 32), (uint32_t) wwpn,
+                                   lp->handle, portid,
+                                   (uint32_t) (lp->node_wwn >> 32),
+                                   (uint32_t) lp->node_wwn,
+                                   (uint32_t) (lp->port_wwn >> 32),
+                                   (uint32_t) lp->port_wwn);
+                               /*
+                                * Try to re-login to this device using a
+                                * new handle. If that fails, mark it dead.
+                                *
+                                * isp_login_device will check for handle and
+                                * portid consistency after re-login.
+                                *
+                                */
+                               if (isp_login_device(isp, chan, portid, &pdb,
+                                   &oldhandle)) {
+                                       lp->new_portid = portid;
+                                       lp->state = FC_PORTDB_STATE_DEAD;
+                                       if (fcp->isp_loopstate !=
+                                           LOOP_SCANNING_FABRIC) {
+                                               FC_SCRATCH_RELEASE(isp, chan);
+                                               ISP_MARK_PORTDB(isp, chan, 1);
+                                               return (-1);
+                                       }
+                                       continue;
+                               }
+                               if (fcp->isp_loopstate !=
+                                   LOOP_SCANNING_FABRIC) {
+                                       FC_SCRATCH_RELEASE(isp, chan);
+                                       ISP_MARK_PORTDB(isp, chan, 1);
+                                       return (-1);
+                               }
+                               FCPARAM(isp, 0)->isp_lasthdl = oldhandle;
+                               MAKE_WWN_FROM_NODE_NAME(wwnn, pdb.nodename);
+                               MAKE_WWN_FROM_NODE_NAME(wwpn, pdb.portname);
+                               if (wwpn != lp->port_wwn ||
+                                   wwnn != lp->node_wwn) {
+                                       isp_prt(isp, ISP_LOGWARN, "changed WWN"
+                                           " after relogin");
+                                       lp->new_portid = portid;
+                                       lp->state = FC_PORTDB_STATE_DEAD;
+                                       continue;
+                               }
 
-       if (fcp->isp_onfabric == 0) {
-               fcp->isp_loopstate = LOOP_FSCAN_DONE;
-               return (0);
-       }
+                               lp->handle = pdb.handle;
+                               handle_changed++;
+                       }
 
-       FC_SCRATCH_ACQUIRE(isp);
+                       nr = (pdb.s3_role & SVC3_ROLE_MASK) >> SVC3_ROLE_SHIFT;
 
-       /*
-        * Since Port IDs are 24 bits, we can check against having seen
-        * anything yet with this value.
-        */
-       last_port_same = 0;
-       last_portid = 0xffffffff;       /* not a port */
-       first_portid = portid = fcp->isp_portid;
-       fcp->isp_loopstate = LOOP_SCANNING_FABRIC;
+                       /*
+                        * Check to see whether the portid and roles have
+                        * stayed the same. If they have stayed the same,
+                        * we believe that this is the same device and it
+                        * hasn't become disconnected and reconnected, so
+                        * mark it as pending valid.
+                        *
+                        * If they aren't the same, mark the device as a
+                        * changed device and save the new port id and role
+                        * and let somebody else decide.
+                        */
 
-       for (hicap = 0; hicap < GA_NXT_MAX; hicap++) {
-               mbreg_t mbs;
-               sns_screq_t *rq;
-               sns_ga_nxt_rsp_t *rs0, *rs1;
-               struct lportdb lcl;
-               u_int8_t sc[SNS_GA_NXT_RESP_SIZE];
-
-               rq = (sns_screq_t *)sc;
-               MEMZERO((void *) rq, SNS_GA_NXT_REQ_SIZE);
-               rq->snscb_rblen = SNS_GA_NXT_RESP_SIZE >> 1;
-               rq->snscb_addr[RQRSP_ADDR0015] = DMA_WD0(fcp->isp_scdma+0x100);
-               rq->snscb_addr[RQRSP_ADDR1631] = DMA_WD1(fcp->isp_scdma+0x100);
-               rq->snscb_addr[RQRSP_ADDR3247] = DMA_WD2(fcp->isp_scdma+0x100);
-               rq->snscb_addr[RQRSP_ADDR4863] = DMA_WD3(fcp->isp_scdma+0x100);
-               rq->snscb_sblen = 6;
-               rq->snscb_data[0] = SNS_GA_NXT;
-               rq->snscb_data[4] = portid & 0xffff;
-               rq->snscb_data[5] = (portid >> 16) & 0xff;
-               isp_put_sns_request(isp, rq, (sns_screq_t *) fcp->isp_scratch);
-               MEMORYBARRIER(isp, SYNC_SFORDEV, 0, SNS_GA_NXT_REQ_SIZE);
-               mbs.param[0] = MBOX_SEND_SNS;
-               mbs.param[1] = SNS_GA_NXT_REQ_SIZE >> 1;
-               mbs.param[2] = DMA_WD1(fcp->isp_scdma);
-               mbs.param[3] = DMA_WD0(fcp->isp_scdma);
-               /*
-                * Leave 4 and 5 alone
-                */
-               mbs.param[6] = DMA_WD3(fcp->isp_scdma);
-               mbs.param[7] = DMA_WD2(fcp->isp_scdma);
-               if (isp_fabric_mbox_cmd(isp, &mbs)) {
-                       if (fcp->isp_loopstate >= LOOP_SCANNING_FABRIC) {
-                               fcp->isp_loopstate = LOOP_PDB_RCVD;
+                       lp->new_portid = portid;
+                       lp->new_roles = nr;
+                       if (pdb.portid != lp->portid || nr != lp->roles ||
+                           handle_changed) {
+                               isp_prt(isp, ISP_LOGSANCFG,
+                                   "Chan %d Fabric Port 0x%06x changed",
+                                   chan, portid);
+                               lp->state = FC_PORTDB_STATE_CHANGED;
+                       } else {
+                               isp_prt(isp, ISP_LOGSANCFG,
+                                   "Chan %d Fabric Port 0x%06x "
+                                   "Now Pending Valid", chan, portid);
+                               lp->state = FC_PORTDB_STATE_PENDING_VALID;
                        }
-                       FC_SCRATCH_RELEASE(isp);
-                       return (-1);
-               }
-               MEMORYBARRIER(isp, SYNC_SFORCPU, 0x100, SNS_GA_NXT_RESP_SIZE);
-               rs1 = (sns_ga_nxt_rsp_t *) sc;
-               rs0 = (sns_ga_nxt_rsp_t *) ((u_int8_t *)fcp->isp_scratch+0x100);
-               isp_get_ga_nxt_response(isp, rs0, rs1);
-               if (rs1->snscb_cthdr.ct_response != FS_ACC) {
-                       int level;
-                       if (rs1->snscb_cthdr.ct_reason == 9 &&
-                           rs1->snscb_cthdr.ct_explanation == 7)
-                               level = ISP_LOGDEBUG0;
-                       else
-                               level = ISP_LOGWARN;
-                       isp_prt(isp, level, swrej, "GA_NXT",
-                           rs1->snscb_cthdr.ct_reason,
-                           rs1->snscb_cthdr.ct_explanation, portid);
-                       FC_SCRATCH_RELEASE(isp);
-                       fcp->isp_loopstate = LOOP_FSCAN_DONE;
-                       return (0);
+                       continue;
                }
-               portid =
-                   (((u_int32_t) rs1->snscb_port_id[0]) << 16) |
-                   (((u_int32_t) rs1->snscb_port_id[1]) << 8) |
-                   (((u_int32_t) rs1->snscb_port_id[2]));
 
                /*
-                * XXX: We should check to make sure that this entry
-                * XXX: supports the type(s) we are interested in.
-                */
-               /*
-                * Okay, we now have information about a fabric object.
-                * If it is the type we're interested in, tell the outer layers
-                * about it. The outer layer needs to  know: Port ID, WWNN,
-                * WWPN, FC4 type, and port type.
-                *
-                * The lportdb structure is adequate for this.
+                * Ah- a new entry. Search the database again for all non-NIL
+                * entries to make sure we never ever make a new database entry
+                * with the same port id. While we're at it, mark where the
+                * last free entry was.
                 */
-               MEMZERO(&lcl, sizeof (lcl));
-               lcl.port_type = rs1->snscb_port_type;
-               lcl.fc4_type = ftype;
-               lcl.portid = portid;
-               lcl.node_wwn =
-                   (((u_int64_t)rs1->snscb_nodename[0]) << 56) |
-                   (((u_int64_t)rs1->snscb_nodename[1]) << 48) |
-                   (((u_int64_t)rs1->snscb_nodename[2]) << 40) |
-                   (((u_int64_t)rs1->snscb_nodename[3]) << 32) |
-                   (((u_int64_t)rs1->snscb_nodename[4]) << 24) |
-                   (((u_int64_t)rs1->snscb_nodename[5]) << 16) |
-                   (((u_int64_t)rs1->snscb_nodename[6]) <<  8) |
-                   (((u_int64_t)rs1->snscb_nodename[7]));
-               lcl.port_wwn =
-                   (((u_int64_t)rs1->snscb_portname[0]) << 56) |
-                   (((u_int64_t)rs1->snscb_portname[1]) << 48) |
-                   (((u_int64_t)rs1->snscb_portname[2]) << 40) |
-                   (((u_int64_t)rs1->snscb_portname[3]) << 32) |
-                   (((u_int64_t)rs1->snscb_portname[4]) << 24) |
-                   (((u_int64_t)rs1->snscb_portname[5]) << 16) |
-                   (((u_int64_t)rs1->snscb_portname[6]) <<  8) |
-                   (((u_int64_t)rs1->snscb_portname[7]));
 
-               /*
-                * Does this fabric object support the type we want?
-                * If not, skip it.
-                */
-               if (rs1->snscb_fc4_types[ftype >> 5] & (1 << (ftype & 0x1f))) {
-                       if (first_portid == portid) {
-                               lcl.last_fabric_dev = 1;
-                       } else {
-                               lcl.last_fabric_dev = 0;
+               dbidx = MAX_FC_TARG;
+               for (lp = fcp->portdb; lp < &fcp->portdb[MAX_FC_TARG]; lp++) {
+                       if (lp >= &fcp->portdb[FL_ID] &&
+                           lp <= &fcp->portdb[SNS_ID]) {
+                               continue;
                        }
-                       (void) isp_async(isp, ISPASYNC_FABRIC_DEV, &lcl);
-               } else {
-                       isp_prt(isp, ISP_LOGDEBUG0,
-                           "PortID 0x%x doesn't support FC4 type 0x%x",
-                           portid, ftype);
-               }
-               if (first_portid == portid) {
-                       fcp->isp_loopstate = LOOP_FSCAN_DONE;
-                       FC_SCRATCH_RELEASE(isp);
-                       return (0);
-               }
-               if (portid == last_portid) {
-                       if (last_port_same++ > 20) {
-                               isp_prt(isp, ISP_LOGWARN,
-                                   "tangled fabric database detected");
+                       /*
+                        * Skip any target mode entries.
+                        */
+                       if (lp->target_mode) {
+                               continue;
+                       }
+                       if (lp->state == FC_PORTDB_STATE_NIL) {
+                               if (dbidx == MAX_FC_TARG) {
+                                       dbidx = lp - fcp->portdb;
+                               }
+                               continue;
+                       }
+                       if (lp->state == FC_PORTDB_STATE_ZOMBIE) {
+                               continue;
+                       }
+                       if (lp->portid == portid) {
                                break;
                        }
-               } else {
-                       last_port_same = 0 ;
-                       last_portid = portid;
                }
-       }
-       FC_SCRATCH_RELEASE(isp);
-       if (hicap >= GA_NXT_MAX) {
-               isp_prt(isp, ISP_LOGWARN, "fabric too big (> %d)", GA_NXT_MAX);
-       }
-       fcp->isp_loopstate = LOOP_FSCAN_DONE;
-       return (0);
-}
-#else
-#define        GIDLEN  ((ISP2100_SCRLEN >> 1) + 16)
-#define        NGENT   ((GIDLEN - 16) >> 2)
-
-#define        IGPOFF  (ISP2100_SCRLEN - GIDLEN)
-#define        GXOFF   (256)
-
-static int
-isp_scan_fabric(struct ispsoftc *isp, int ftype)
-{
-       fcparam *fcp = FCPARAM(isp);
-       mbreg_t mbs;
-       int i;
-       sns_gid_ft_req_t *rq;
-       sns_gid_ft_rsp_t *rs0, *rs1;
-
-       if (fcp->isp_onfabric == 0) {
-               fcp->isp_loopstate = LOOP_FSCAN_DONE;
-               return (0);
-       }
-
-       FC_SCRATCH_ACQUIRE(isp);
-       fcp->isp_loopstate = LOOP_SCANNING_FABRIC;
 
-       rq = (sns_gid_ft_req_t *)fcp->tport;
-       MEMZERO((void *) rq, SNS_GID_FT_REQ_SIZE);
-       rq->snscb_rblen = GIDLEN >> 1;
-       rq->snscb_addr[RQRSP_ADDR0015] = DMA_WD0(fcp->isp_scdma+IGPOFF);
-       rq->snscb_addr[RQRSP_ADDR1631] = DMA_WD1(fcp->isp_scdma+IGPOFF);
-       rq->snscb_addr[RQRSP_ADDR3247] = DMA_WD2(fcp->isp_scdma+IGPOFF);
-       rq->snscb_addr[RQRSP_ADDR4863] = DMA_WD3(fcp->isp_scdma+IGPOFF);
-       rq->snscb_sblen = 6;
-       rq->snscb_cmd = SNS_GID_FT;
-       rq->snscb_mword_div_2 = NGENT;
-       rq->snscb_fc4_type = ftype;
-       isp_put_gid_ft_request(isp, rq, (sns_gid_ft_req_t *) fcp->isp_scratch);
-       MEMORYBARRIER(isp, SYNC_SFORDEV, 0, SNS_GID_FT_REQ_SIZE);
-       mbs.param[0] = MBOX_SEND_SNS;
-       mbs.param[1] = SNS_GID_FT_REQ_SIZE >> 1;
-       mbs.param[2] = DMA_WD1(fcp->isp_scdma);
-       mbs.param[3] = DMA_WD0(fcp->isp_scdma);
+               if (lp < &fcp->portdb[MAX_FC_TARG]) {
+                       isp_prt(isp, ISP_LOGWARN, "Chan %d PortID 0x%06x "
+                           "already at %d handle %d state %d",
+                           chan, portid, dbidx, lp->handle, lp->state);
+                       continue;
+               }
 
-       /*
-        * Leave 4 and 5 alone
-        */
-       mbs.param[6] = DMA_WD3(fcp->isp_scdma);
-       mbs.param[7] = DMA_WD2(fcp->isp_scdma);
-       if (isp_fabric_mbox_cmd(isp, &mbs)) {
-               if (fcp->isp_loopstate >= LOOP_SCANNING_FABRIC) {
-                       fcp->isp_loopstate = LOOP_PDB_RCVD;
+               /*
+                * We should have the index of the first free entry seen.
+                */
+               if (dbidx == MAX_FC_TARG) {
+                       isp_prt(isp, ISP_LOGERR,
+                           "port database too small to login PortID 0x%06x"
+                           "- increase MAX_FC_TARG", portid);
+                       continue;
                }
-               FC_SCRATCH_RELEASE(isp);
-               return (-1);
-       }
-       if (fcp->isp_loopstate != LOOP_SCANNING_FABRIC) {
-               FC_SCRATCH_RELEASE(isp);
-               return (-1);
-       }
-       MEMORYBARRIER(isp, SYNC_SFORCPU, IGPOFF, GIDLEN);
-       rs1 = (sns_gid_ft_rsp_t *) fcp->tport;
-       rs0 = (sns_gid_ft_rsp_t *) ((u_int8_t *)fcp->isp_scratch+IGPOFF);
-       isp_get_gid_ft_response(isp, rs0, rs1, NGENT);
-       if (rs1->snscb_cthdr.ct_response != FS_ACC) {
-               int level;
-               if (rs1->snscb_cthdr.ct_reason == 9 &&
-                   rs1->snscb_cthdr.ct_explanation == 7)
-                       level = ISP_LOGDEBUG0;
-               else
-                       level = ISP_LOGWARN;
-               isp_prt(isp, level, swrej, "GID_FT",
-                   rs1->snscb_cthdr.ct_reason,
-                   rs1->snscb_cthdr.ct_explanation, 0);
-               FC_SCRATCH_RELEASE(isp);
-               fcp->isp_loopstate = LOOP_FSCAN_DONE;
-               return (0);
-       }
 
-       /*
-        * Okay, we now have a list of Port IDs for this class of device.
-        * Go through the list and for each one get the WWPN/WWNN for it
-        * and tell the outer layers about it. The outer layer needs to
-        * know: Port ID, WWNN, WWPN, FC4 type, and (possibly) port type.
-        *
-        * The lportdb structure is adequate for this.
-        */
-       i = -1;
-       do {
-               sns_gxn_id_req_t grqbuf, *gq = &grqbuf;
-               sns_gxn_id_rsp_t *gs0, grsbuf, *gs1 = &grsbuf;
-               struct lportdb lcl;
-#if    0
-               sns_gff_id_rsp_t *fs0, ffsbuf, *fs1 = &ffsbuf;
-#endif
+               /*
+                * Otherwise, point to our new home.
+                */
+               lp = &fcp->portdb[dbidx];
 
-               i++;
-               MEMZERO(&lcl, sizeof (lcl));
-               lcl.fc4_type = ftype;
-               lcl.portid =
-                   (((u_int32_t) rs1->snscb_ports[i].portid[0]) << 16) |
-                   (((u_int32_t) rs1->snscb_ports[i].portid[1]) << 8) |
-                   (((u_int32_t) rs1->snscb_ports[i].portid[2]));
-
-               MEMZERO((void *) gq, sizeof (sns_gxn_id_req_t));
-               gq->snscb_rblen = SNS_GXN_ID_RESP_SIZE >> 1;
-               gq->snscb_addr[RQRSP_ADDR0015] = DMA_WD0(fcp->isp_scdma+GXOFF);
-               gq->snscb_addr[RQRSP_ADDR1631] = DMA_WD1(fcp->isp_scdma+GXOFF);
-               gq->snscb_addr[RQRSP_ADDR3247] = DMA_WD2(fcp->isp_scdma+GXOFF);
-               gq->snscb_addr[RQRSP_ADDR4863] = DMA_WD3(fcp->isp_scdma+GXOFF);
-               gq->snscb_sblen = 6;
-               gq->snscb_cmd = SNS_GPN_ID;
-               gq->snscb_portid = lcl.portid;
-               isp_put_gxn_id_request(isp, gq,
-                   (sns_gxn_id_req_t *) fcp->isp_scratch);
-               MEMORYBARRIER(isp, SYNC_SFORDEV, 0, SNS_GXN_ID_REQ_SIZE);
-               mbs.param[0] = MBOX_SEND_SNS;
-               mbs.param[1] = SNS_GXN_ID_REQ_SIZE >> 1;
-               mbs.param[2] = DMA_WD1(fcp->isp_scdma);
-               mbs.param[3] = DMA_WD0(fcp->isp_scdma);
                /*
-                * Leave 4 and 5 alone
+                * Try to see if we are logged into this device,
+                * and maybe log into it.
+                *
+                * isp_login_device will check for handle and
+                * portid consistency after login.
                 */
-               mbs.param[6] = DMA_WD3(fcp->isp_scdma);
-               mbs.param[7] = DMA_WD2(fcp->isp_scdma);
-               if (isp_fabric_mbox_cmd(isp, &mbs)) {
-                       if (fcp->isp_loopstate >= LOOP_SCANNING_FABRIC) {
-                               fcp->isp_loopstate = LOOP_PDB_RCVD;
-                       }
-                       FC_SCRATCH_RELEASE(isp);
-                       return (-1);
-               }
-               if (fcp->isp_loopstate != LOOP_SCANNING_FABRIC) {
-                       FC_SCRATCH_RELEASE(isp);
-                       return (-1);
-               }
-               MEMORYBARRIER(isp, SYNC_SFORCPU, GXOFF, SNS_GXN_ID_RESP_SIZE);
-               gs0 = (sns_gxn_id_rsp_t *) ((u_int8_t *)fcp->isp_scratch+GXOFF);
-               isp_get_gxn_id_response(isp, gs0, gs1);
-               if (gs1->snscb_cthdr.ct_response != FS_ACC) {
-                       isp_prt(isp, ISP_LOGWARN, swrej, "GPN_ID",
-                           gs1->snscb_cthdr.ct_reason,
-                           gs1->snscb_cthdr.ct_explanation, lcl.portid);
+               if (isp_login_device(isp, chan, portid, &pdb, &oldhandle)) {
                        if (fcp->isp_loopstate != LOOP_SCANNING_FABRIC) {
-                               FC_SCRATCH_RELEASE(isp);
+                               FC_SCRATCH_RELEASE(isp, chan);
+                               ISP_MARK_PORTDB(isp, chan, 1);
                                return (-1);
                        }
                        continue;
                }
-               lcl.port_wwn = 
-                   (((u_int64_t)gs1->snscb_wwn[0]) << 56) |
-                   (((u_int64_t)gs1->snscb_wwn[1]) << 48) |
-                   (((u_int64_t)gs1->snscb_wwn[2]) << 40) |
-                   (((u_int64_t)gs1->snscb_wwn[3]) << 32) |
-                   (((u_int64_t)gs1->snscb_wwn[4]) << 24) |
-                   (((u_int64_t)gs1->snscb_wwn[5]) << 16) |
-                   (((u_int64_t)gs1->snscb_wwn[6]) <<  8) |
-                   (((u_int64_t)gs1->snscb_wwn[7]));
-
-               MEMZERO((void *) gq, sizeof (sns_gxn_id_req_t));
-               gq->snscb_rblen = SNS_GXN_ID_RESP_SIZE >> 1;
-               gq->snscb_addr[RQRSP_ADDR0015] = DMA_WD0(fcp->isp_scdma+GXOFF);
-               gq->snscb_addr[RQRSP_ADDR1631] = DMA_WD1(fcp->isp_scdma+GXOFF);
-               gq->snscb_addr[RQRSP_ADDR3247] = DMA_WD2(fcp->isp_scdma+GXOFF);
-               gq->snscb_addr[RQRSP_ADDR4863] = DMA_WD3(fcp->isp_scdma+GXOFF);
-               gq->snscb_sblen = 6;
-               gq->snscb_cmd = SNS_GNN_ID;
-               gq->snscb_portid = lcl.portid;
-               isp_put_gxn_id_request(isp, gq,
-                   (sns_gxn_id_req_t *) fcp->isp_scratch);
-               MEMORYBARRIER(isp, SYNC_SFORDEV, 0, SNS_GXN_ID_REQ_SIZE);
-               mbs.param[0] = MBOX_SEND_SNS;
-               mbs.param[1] = SNS_GXN_ID_REQ_SIZE >> 1;
-               mbs.param[2] = DMA_WD1(fcp->isp_scdma);
-               mbs.param[3] = DMA_WD0(fcp->isp_scdma);
+               if (fcp->isp_loopstate != LOOP_SCANNING_FABRIC) {
+                       FC_SCRATCH_RELEASE(isp, chan);
+                       ISP_MARK_PORTDB(isp, chan, 1);
+                       return (-1);
+               }
+               FCPARAM(isp, 0)->isp_lasthdl = oldhandle;
+
+               handle = pdb.handle;
+               MAKE_WWN_FROM_NODE_NAME(wwnn, pdb.nodename);
+               MAKE_WWN_FROM_NODE_NAME(wwpn, pdb.portname);
+               nr = (pdb.s3_role & SVC3_ROLE_MASK) >> SVC3_ROLE_SHIFT;
+
                /*
-                * Leave 4 and 5 alone
+                * And go through the database *one* more time to make sure
+                * that we do not make more than one entry that has the same
+                * WWNN/WWPN duple
                 */
-               mbs.param[6] = DMA_WD3(fcp->isp_scdma);
-               mbs.param[7] = DMA_WD2(fcp->isp_scdma);
-               if (isp_fabric_mbox_cmd(isp, &mbs)) {
-                       if (fcp->isp_loopstate >= LOOP_SCANNING_FABRIC) {
-                               fcp->isp_loopstate = LOOP_PDB_RCVD;
+               for (dbidx = 0; dbidx < MAX_FC_TARG; dbidx++) {
+                       if (dbidx >= FL_ID && dbidx <= SNS_ID) {
+                               continue;
+                       }
+                       if (fcp->portdb[dbidx].target_mode) {
+                               continue;
+                       }
+                       if (fcp->portdb[dbidx].node_wwn == wwnn &&
+                           fcp->portdb[dbidx].port_wwn == wwpn) {
+                               break;
                        }
-                       FC_SCRATCH_RELEASE(isp);
-                       return (-1);
                }
-               if (fcp->isp_loopstate != LOOP_SCANNING_FABRIC) {
-                       FC_SCRATCH_RELEASE(isp);
-                       return (-1);
+
+               if (dbidx == MAX_FC_TARG) {
+                       ISP_MEMZERO(lp, sizeof (fcportdb_t));
+                       lp->handle = handle;
+                       lp->node_wwn = wwnn;
+                       lp->port_wwn = wwpn;
+                       lp->new_portid = portid;
+                       lp->new_roles = nr;
+                       lp->state = FC_PORTDB_STATE_NEW;
+                       isp_prt(isp, ISP_LOGSANCFG,
+                           "Chan %d Fabric Port 0x%06x is a New Entry",
+                           chan, portid);
+                       continue;
                }
-               MEMORYBARRIER(isp, SYNC_SFORCPU, GXOFF, SNS_GXN_ID_RESP_SIZE);
-               gs0 = (sns_gxn_id_rsp_t *) ((u_int8_t *)fcp->isp_scratch+GXOFF);
-               isp_get_gxn_id_response(isp, gs0, gs1);
-               if (gs1->snscb_cthdr.ct_response != FS_ACC) {
-                       isp_prt(isp, ISP_LOGWARN, swrej, "GNN_ID",
-                           gs1->snscb_cthdr.ct_reason,
-                           gs1->snscb_cthdr.ct_explanation, lcl.portid);
-                       if (fcp->isp_loopstate != LOOP_SCANNING_FABRIC) {
-                               FC_SCRATCH_RELEASE(isp);
-                               return (-1);
-                       }
+
+               if (fcp->portdb[dbidx].state != FC_PORTDB_STATE_ZOMBIE) {
+                       isp_prt(isp, ISP_LOGWARN,
+                           "Chan %d PortID 0x%x 0x%08x%08x/0x%08x%08x %ld "
+                           "already at idx %d, state 0x%x", chan, portid,
+                           (uint32_t) (wwnn >> 32), (uint32_t) wwnn,
+                           (uint32_t) (wwpn >> 32), (uint32_t) wwpn,
+                           (long) (lp - fcp->portdb), dbidx,
+                           fcp->portdb[dbidx].state);
                        continue;
                }
-               lcl.node_wwn = 
-                   (((u_int64_t)gs1->snscb_wwn[0]) << 56) |
-                   (((u_int64_t)gs1->snscb_wwn[1]) << 48) |
-                   (((u_int64_t)gs1->snscb_wwn[2]) << 40) |
-                   (((u_int64_t)gs1->snscb_wwn[3]) << 32) |
-                   (((u_int64_t)gs1->snscb_wwn[4]) << 24) |
-                   (((u_int64_t)gs1->snscb_wwn[5]) << 16) |
-                   (((u_int64_t)gs1->snscb_wwn[6]) <<  8) |
-                   (((u_int64_t)gs1->snscb_wwn[7]));
 
                /*
-                * The QLogic f/w is bouncing this with a parameter error.
+                * We found a zombie entry that matches us.
+                * Revive it. We know that WWN and WWPN
+                * are the same. For fabric devices, we
+                * don't care that handle is different
+                * as we assign that. If role or portid
+                * are different, it maybe a changed device.
                 */
-#if    0
-               /*
-                * Try and get FC4 Features (FC-GS-3 only).
-                * We can use the sns_gxn_id_req_t for this request.
-                */
-               MEMZERO((void *) gq, sizeof (sns_gxn_id_req_t));
-               gq->snscb_rblen = SNS_GFF_ID_RESP_SIZE >> 1;
-               gq->snscb_addr[RQRSP_ADDR0015] = DMA_WD0(fcp->isp_scdma+GXOFF);
-               gq->snscb_addr[RQRSP_ADDR1631] = DMA_WD1(fcp->isp_scdma+GXOFF);
-               gq->snscb_addr[RQRSP_ADDR3247] = DMA_WD2(fcp->isp_scdma+GXOFF);
-               gq->snscb_addr[RQRSP_ADDR4863] = DMA_WD3(fcp->isp_scdma+GXOFF);
-               gq->snscb_sblen = 6;
-               gq->snscb_cmd = SNS_GFF_ID;
-               gq->snscb_portid = lcl.portid;
-               isp_put_gxn_id_request(isp, gq,
-                   (sns_gxn_id_req_t *) fcp->isp_scratch);
-               MEMORYBARRIER(isp, SYNC_SFORDEV, 0, SNS_GXN_ID_REQ_SIZE);
-               mbs.param[0] = MBOX_SEND_SNS;
-               mbs.param[1] = SNS_GXN_ID_REQ_SIZE >> 1;
-               mbs.param[2] = DMA_WD1(fcp->isp_scdma);
-               mbs.param[3] = DMA_WD0(fcp->isp_scdma);
+               lp = &fcp->portdb[dbidx];
+               lp->handle = handle;
+               lp->new_portid = portid;
+               lp->new_roles = nr;
+               if (lp->portid != portid || lp->roles != nr) {
+                       isp_prt(isp, ISP_LOGSANCFG|ISP_LOGDEBUG0,
+                           "Chan %d Zombie Fabric Port 0x%06x Now Changed",
+                           chan, portid);
+                       lp->state = FC_PORTDB_STATE_CHANGED;
+               } else {
+                       isp_prt(isp, ISP_LOGSANCFG|ISP_LOGDEBUG0,
+                           "Chan %d Zombie Fabric Port 0x%06x "
+                           "Now Pending Valid", chan, portid);
+                       lp->state = FC_PORTDB_STATE_PENDING_VALID;
+               }
+       }
+
+       FC_SCRATCH_RELEASE(isp, chan);
+       if (fcp->isp_loopstate != LOOP_SCANNING_FABRIC) {
+               ISP_MARK_PORTDB(isp, chan, 1);
+               return (-1);
+       }
+       fcp->isp_loopstate = LOOP_FSCAN_DONE;
+       isp_prt(isp, ISP_LOGSANCFG|ISP_LOGDEBUG0,
+           "Chan %d FC Scan Fabric Done", chan);
+       return (0);
+}
+
+/*
+ * Find an unused handle and try and use to login to a port.
+ */
+static int
+isp_login_device(ispsoftc_t *isp, int chan, uint32_t portid, isp_pdb_t *p, uint16_t *ohp)
+{
+       int lim, i, r;
+       uint16_t handle;
+
+       if (ISP_CAP_2KLOGIN(isp)) {
+               lim = NPH_MAX_2K;
+       } else {
+               lim = NPH_MAX;
+       }
+
+       handle = isp_nxt_handle(isp, chan, *ohp);
+       for (i = 0; i < lim; i++) {
                /*
-                * Leave 4 and 5 alone
+                * See if we're still logged into something with
+                * this handle and that something agrees with this
+                * port id.
                 */
-               mbs.param[6] = DMA_WD3(fcp->isp_scdma);
-               mbs.param[7] = DMA_WD2(fcp->isp_scdma);
-               if (isp_fabric_mbox_cmd(isp, &mbs)) {
-                       if (fcp->isp_loopstate >= LOOP_SCANNING_FABRIC) {
-                               fcp->isp_loopstate = LOOP_PDB_RCVD;
-                       }
-                       FC_SCRATCH_RELEASE(isp);
+               r = isp_getpdb(isp, chan, handle, p, 0);
+               if (r == 0 && p->portid != portid) {
+                       (void) isp_plogx(isp, chan, handle, portid, PLOGX_FLG_CMD_LOGO | PLOGX_FLG_IMPLICIT | PLOGX_FLG_FREE_NPHDL, 1);
+               } else if (r == 0) {
+                       break;
+               }
+               if (FCPARAM(isp, chan)->isp_loopstate != LOOP_SCANNING_FABRIC) {
                        return (-1);
                }
-               if (fcp->isp_loopstate != LOOP_SCANNING_FABRIC) {
-                       FC_SCRATCH_RELEASE(isp);
+               /*
+                * Now try and log into the device
+                */
+               r = isp_plogx(isp, chan, handle, portid, PLOGX_FLG_CMD_PLOGI, 1);
+               if (FCPARAM(isp, chan)->isp_loopstate != LOOP_SCANNING_FABRIC) {
                        return (-1);
                }
-               MEMORYBARRIER(isp, SYNC_SFORCPU, GXOFF, SNS_GFF_ID_RESP_SIZE);
-               fs0 = (sns_gff_id_rsp_t *) ((u_int8_t *)fcp->isp_scratch+GXOFF);
-               isp_get_gff_id_response(isp, fs0, fs1);
-               if (fs1->snscb_cthdr.ct_response != FS_ACC) {
-                       isp_prt(isp, /* ISP_LOGDEBUG0 */ ISP_LOGWARN,
-                           swrej, "GFF_ID",
-                           fs1->snscb_cthdr.ct_reason,
-                           fs1->snscb_cthdr.ct_explanation, lcl.portid);
-                       if (fcp->isp_loopstate != LOOP_SCANNING_FABRIC) {
-                               FC_SCRATCH_RELEASE(isp);
+               if (r == 0) {
+                       *ohp = handle;
+                       break;
+               } else if ((r & 0xffff) == MBOX_PORT_ID_USED) {
+                       /*
+                        * If we get here, then the firmwware still thinks we're logged into this device, but with a different
+                        * handle. We need to break that association. We used to try and just substitute the handle, but then
+                        * failed to get any data via isp_getpdb (below).
+                        */
+                       if (isp_plogx(isp, chan, r >> 16, portid, PLOGX_FLG_CMD_LOGO | PLOGX_FLG_IMPLICIT | PLOGX_FLG_FREE_NPHDL, 1)) {
+                               isp_prt(isp, ISP_LOGERR, "baw... logout of %x failed", r >> 16);
+                       }
+                       if (FCPARAM(isp, chan)->isp_loopstate != LOOP_SCANNING_FABRIC) {
                                return (-1);