summaryrefslogtreecommitdiff
path: root/sys/dev/isp
diff options
context:
space:
mode:
Diffstat (limited to 'sys/dev/isp')
-rw-r--r--sys/dev/isp/DriverManual.txt634
-rw-r--r--sys/dev/isp/Hardware.txt304
-rw-r--r--sys/dev/isp/isp.c7012
-rw-r--r--sys/dev/isp/isp_freebsd.c3257
-rw-r--r--sys/dev/isp/isp_freebsd.h521
-rw-r--r--sys/dev/isp/isp_ioctl.h195
-rw-r--r--sys/dev/isp/isp_library.c1725
-rw-r--r--sys/dev/isp/isp_library.h164
-rw-r--r--sys/dev/isp/isp_pci.c2506
-rw-r--r--sys/dev/isp/isp_sbus.c874
-rw-r--r--sys/dev/isp/isp_target.c1315
-rw-r--r--sys/dev/isp/isp_target.h703
-rw-r--r--sys/dev/isp/isp_tpublic.h372
-rw-r--r--sys/dev/isp/ispmbox.h986
-rw-r--r--sys/dev/isp/ispreg.h1035
-rw-r--r--sys/dev/isp/ispvar.h892
16 files changed, 22495 insertions, 0 deletions
diff --git a/sys/dev/isp/DriverManual.txt b/sys/dev/isp/DriverManual.txt
new file mode 100644
index 000000000000..ea1968f9f883
--- /dev/null
+++ b/sys/dev/isp/DriverManual.txt
@@ -0,0 +1,634 @@
+/* $FreeBSD$ */
+
+ 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 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 the 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/isp/Hardware.txt b/sys/dev/isp/Hardware.txt
new file mode 100644
index 000000000000..75f01bf7dd36
--- /dev/null
+++ b/sys/dev/isp/Hardware.txt
@@ -0,0 +1,304 @@
+/* $FreeBSD$ */
+
+ 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/isp/isp.c b/sys/dev/isp/isp.c
new file mode 100644
index 000000000000..b880a45dec30
--- /dev/null
+++ b/sys/dev/isp/isp.c
@@ -0,0 +1,7012 @@
+/*-
+ * Copyright (c) 1997-2006 by Matthew Jacob
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice 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.
+ *
+ * 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.
+ */
+
+/*
+ * Machine and OS Independent (well, as best as possible)
+ * code for the Qlogic ISP SCSI adapters.
+ */
+/*
+ * Inspiration and ideas about this driver are from Erik Moe's Linux driver
+ * (qlogicisp.c) and Dave Miller's SBus version of same (qlogicisp.c). Some
+ * ideas dredged from the Solaris driver.
+ */
+
+/*
+ * Include header file appropriate for platform we're building on.
+ */
+#ifdef __NetBSD__
+#include <dev/ic/isp_netbsd.h>
+#endif
+#ifdef __FreeBSD__
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+#include <dev/isp/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
+
+/*
+ * General defines
+ */
+
+#define MBOX_DELAY_COUNT 1000000 / 100
+
+/*
+ * 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, 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 sc4[] = "NVRAM";
+static const char bun[] =
+ "bad underrun for %d.%d (count %d, resid %d, status %s)";
+
+/*
+ * Local function prototypes.
+ */
+static int isp_parse_async(ispsoftc_t *, uint16_t);
+static int isp_handle_other_response(ispsoftc_t *, int, isphdr_t *,
+ uint16_t *);
+static void
+isp_parse_status(ispsoftc_t *, ispstatusreq_t *, XS_T *);
+static void isp_fastpost_complete(ispsoftc_t *, uint16_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_mark_getpdb_all(ispsoftc_t *);
+static int isp_getmap(ispsoftc_t *, fcpos_map_t *);
+static int isp_getpdb(ispsoftc_t *, int, isp_pdb_t *);
+static uint64_t isp_get_portname(ispsoftc_t *, int, int);
+static int isp_fclink_test(ispsoftc_t *, int);
+static const char *isp2100_fw_statename(int);
+static int isp_pdb_sync(ispsoftc_t *);
+static int isp_scan_loop(ispsoftc_t *);
+static int isp_fabric_mbox_cmd(ispsoftc_t *, mbreg_t *);
+static int isp_scan_fabric(ispsoftc_t *, int);
+static void isp_register_fc4_type(ispsoftc_t *);
+static void isp_fw_state(ispsoftc_t *);
+static void isp_mboxcmd_qnw(ispsoftc_t *, mbreg_t *, int);
+static void isp_mboxcmd(ispsoftc_t *, mbreg_t *, int);
+
+static void isp_update(ispsoftc_t *);
+static void isp_update_bus(ispsoftc_t *, int);
+static void isp_setdfltparm(ispsoftc_t *, int);
+static int isp_read_nvram(ispsoftc_t *);
+static void isp_rdnvram_word(ispsoftc_t *, int, uint16_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 *);
+
+/*
+ * Reset Hardware.
+ *
+ * Hit the chip over the head, download new f/w if available and set it running.
+ *
+ * Locking done elsewhere.
+ */
+
+void
+isp_reset(ispsoftc_t *isp)
+{
+ mbreg_t mbs;
+ uint32_t code_org;
+ int loops, i, dodnld = 1;
+ char *btype = "????";
+
+ isp->isp_state = ISP_NILSTATE;
+
+ /*
+ * Basic types (SCSI, FibreChannel and PCI or SBus)
+ * have been set in the MD code. We figure out more
+ * here. Possibly more refined types based upon PCI
+ * identification. Chip revision has been gathered.
+ *
+ * After we've fired this chip up, zero out the conf1 register
+ * for SCSI adapters and do other settings for the 2100.
+ */
+
+ /*
+ * 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.
+ */
+ 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);
+ MEMZERO(&mbs, sizeof (mbs));
+ 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;
+ }
+
+ DISABLE_INTS(isp);
+
+ /*
+ * Set up default request/response queue in-pointer/out-pointer
+ * register indices.
+ */
+ if (IS_23XX(isp)) {
+ isp->isp_rqstinrp = BIU_REQINP;
+ isp->isp_rqstoutrp = BIU_REQOUTP;
+ isp->isp_respinrp = BIU_RSPINP;
+ isp->isp_respoutrp = BIU_RSPOUTP;
+ 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);
+ } else {
+ isp->isp_rqstinrp = INMAILBOX4;
+ isp->isp_rqstoutrp = OUTMAILBOX4;
+ isp->isp_respinrp = OUTMAILBOX5;
+ isp->isp_respoutrp = INMAILBOX5;
+ }
+
+ /*
+ * 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_FC(isp)) {
+ switch (isp->isp_type) {
+ case ISP_HA_FC_2100:
+ btype = "2100";
+ break;
+ case ISP_HA_FC_2200:
+ btype = "2200";
+ break;
+ case ISP_HA_FC_2300:
+ btype = "2300";
+ break;
+ case ISP_HA_FC_2312:
+ btype = "2312";
+ break;
+ case ISP_HA_FC_2322:
+ btype = "2322";
+ break;
+ case ISP_HA_FC_2422:
+ btype = "2422";
+ 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);
+ } else if (IS_1240(isp)) {
+ sdparam *sdp = isp->isp_param;
+ btype = "1240";
+ isp->isp_clock = 60;
+ sdp->isp_ultramode = 1;
+ sdp++;
+ sdp->isp_ultramode = 1;
+ /*
+ * XXX: Should probably do some bus sensing.
+ */
+ } else if (IS_ULTRA2(isp)) {
+ static const char m[] = "bus %d is in %s Mode";
+ uint16_t l;
+ sdparam *sdp = isp->isp_param;
+
+ isp->isp_clock = 100;
+
+ if (IS_1280(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>";
+
+ l = ISP_READ(isp, SXP_PINS_DIFF) & ISP1080_MODE_MASK;
+ switch (l) {
+ case ISP1080_LVD_MODE:
+ sdp->isp_lvdmode = 1;
+ isp_prt(isp, ISP_LOGCONFIG, m, 0, "LVD");
+ break;
+ case ISP1080_HVD_MODE:
+ sdp->isp_diffmode = 1;
+ isp_prt(isp, ISP_LOGCONFIG, m, 0, "Differential");
+ break;
+ case ISP1080_SE_MODE:
+ sdp->isp_ultramode = 1;
+ isp_prt(isp, ISP_LOGCONFIG, m, 0, "Single-Ended");
+ break;
+ default:
+ isp_prt(isp, ISP_LOGERR,
+ "unknown mode on bus %d (0x%x)", 0, l);
+ break;
+ }
+
+ if (IS_DUALBUS(isp)) {
+ sdp++;
+ l = ISP_READ(isp, SXP_PINS_DIFF|SXP_BANK1_SELECT);
+ l &= ISP1080_MODE_MASK;
+ switch(l) {
+ case ISP1080_LVD_MODE:
+ sdp->isp_lvdmode = 1;
+ isp_prt(isp, ISP_LOGCONFIG, m, 1, "LVD");
+ break;
+ case ISP1080_HVD_MODE:
+ sdp->isp_diffmode = 1;
+ isp_prt(isp, ISP_LOGCONFIG,
+ m, 1, "Differential");
+ break;
+ case ISP1080_SE_MODE:
+ sdp->isp_ultramode = 1;
+ isp_prt(isp, ISP_LOGCONFIG,
+ m, 1, "Single-Ended");
+ break;
+ default:
+ isp_prt(isp, ISP_LOGERR,
+ "unknown mode on bus %d (0x%x)", 1, l);
+ break;
+ }
+ }
+ } else {
+ sdparam *sdp = isp->isp_param;
+ i = ISP_READ(isp, BIU_CONF0) & BIU_CONF0_HW_MASK;
+ switch (i) {
+ default:
+ isp_prt(isp, ISP_LOGALL, "Unknown Chip Type 0x%x", i);
+ /* FALLTHROUGH */
+ case 1:
+ btype = "1020";
+ isp->isp_type = ISP_HA_SCSI_1020;
+ isp->isp_clock = 40;
+ break;
+ case 2:
+ /*
+ * Some 1020A chips are Ultra Capable, but don't
+ * run the clock rate up for that unless told to
+ * do so by the Ultra Capable bits being set.
+ */
+ btype = "1020A";
+ isp->isp_type = ISP_HA_SCSI_1020A;
+ isp->isp_clock = 40;
+ break;
+ case 3:
+ btype = "1040";
+ isp->isp_type = ISP_HA_SCSI_1040;
+ isp->isp_clock = 60;
+ break;
+ case 4:
+ btype = "1040A";
+ isp->isp_type = ISP_HA_SCSI_1040A;
+ isp->isp_clock = 60;
+ break;
+ case 5:
+ btype = "1040B";
+ isp->isp_type = ISP_HA_SCSI_1040B;
+ isp->isp_clock = 60;
+ break;
+ case 6:
+ btype = "1040C";
+ isp->isp_type = ISP_HA_SCSI_1040C;
+ isp->isp_clock = 60;
+ break;
+ }
+ /*
+ * Now, while we're at it, gather info about ultra
+ * and/or differential mode.
+ */
+ if (ISP_READ(isp, SXP_PINS_DIFF) & SXP_PINS_DIFF_MODE) {
+ isp_prt(isp, ISP_LOGCONFIG, "Differential Mode");
+ sdp->isp_diffmode = 1;
+ } else {
+ sdp->isp_diffmode = 0;
+ }
+ i = ISP_READ(isp, RISC_PSR);
+ if (isp->isp_bustype == ISP_BT_SBUS) {
+ i &= RISC_PSR_SBUS_ULTRA;
+ } else {
+ i &= RISC_PSR_PCI_ULTRA;
+ }
+ if (i != 0) {
+ isp_prt(isp, ISP_LOGCONFIG, "Ultra Mode Capable");
+ sdp->isp_ultramode = 1;
+ /*
+ * If we're in Ultra Mode, we have to be 60MHz clock-
+ * even for the SBus version.
+ */
+ isp->isp_clock = 60;
+ } else {
+ sdp->isp_ultramode = 0;
+ /*
+ * Clock is known. Gronk.
+ */
+ }
+
+ /*
+ * Machine dependent clock (if set) overrides
+ * our generic determinations.
+ */
+ if (isp->isp_mdvec->dv_clock) {
+ if (isp->isp_mdvec->dv_clock < isp->isp_clock) {
+ isp->isp_clock = isp->isp_mdvec->dv_clock;
+ }
+ }
+
+ }
+
+ /*
+ * Clear instrumentation
+ */
+ isp->isp_intcnt = isp->isp_intbogus = 0;
+
+ /*
+ * Do MD specific pre initialization
+ */
+ ISP_RESET0(isp);
+
+again:
+
+ /*
+ * Hit the chip over the head with hammer,
+ * and give the ISP a chance to recover.
+ */
+
+ if (IS_SCSI(isp)) {
+ ISP_WRITE(isp, BIU_ICR, BIU_ICR_SOFT_RESET);
+ /*
+ * A slight delay...
+ */
+ USEC_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);
+
+
+ } else {
+ ISP_WRITE(isp, BIU2100_CSR, BIU2100_SOFT_RESET);
+ /*
+ * A slight delay...
+ */
+ USEC_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);
+ }
+
+ /*
+ * Wait for ISP to be ready to go...
+ */
+ loops = MBOX_DELAY_COUNT;
+ for (;;) {
+ if (IS_SCSI(isp)) {
+ if (!(ISP_READ(isp, BIU_ICR) & BIU_ICR_SOFT_RESET))
+ break;
+ } else {
+ if (!(ISP_READ(isp, BIU2100_CSR) & BIU2100_SOFT_RESET))
+ break;
+ }
+ USEC_DELAY(100);
+ if (--loops < 0) {
+ ISP_DUMPREGS(isp, "chip reset timed out");
+ return;
+ }
+ }
+
+ /*
+ * After we've fired this chip up, zero out the conf1 register
+ * for SCSI adapters and other settings for the 2100.
+ */
+
+ if (IS_SCSI(isp)) {
+ ISP_WRITE(isp, BIU_CONF1, 0);
+ } else {
+ 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);
+
+ /*
+ * Establish some initial burst rate stuff.
+ * (only for the 1XX0 boards). This really should
+ * be done later after fetching from NVRAM.
+ */
+ if (IS_SCSI(isp)) {
+ uint16_t tmp = isp->isp_mdvec->dv_conf1;
+ /*
+ * Busted FIFO. Turn off all but burst enables.
+ */
+ if (isp->isp_type == ISP_HA_SCSI_1040A) {
+ tmp &= BIU_BURST_ENABLE;
+ }
+ ISP_SETBITS(isp, BIU_CONF1, tmp);
+ if (tmp & BIU_BURST_ENABLE) {
+ ISP_SETBITS(isp, CDMA_CONF, DMA_ENABLE_BURST);
+ ISP_SETBITS(isp, DDMA_CONF, DMA_ENABLE_BURST);
+ }
+ ISP_WRITE(isp, RISC_MTR, 0x1212);
+ } 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); /* release paused processor */
+
+ /*
+ * Do MD specific post initialization
+ */
+ ISP_RESET1(isp);
+
+ /*
+ * Wait for everything to finish firing up.
+ *
+ * Avoid doing this on the 2312 because you can generate a PCI
+ * parity error (chip breakage).
+ */
+ if (IS_23XX(isp)) {
+ USEC_DELAY(5);
+ } else {
+ loops = MBOX_DELAY_COUNT;
+ while (ISP_READ(isp, OUTMAILBOX0) == MBOX_BUSY) {
+ USEC_DELAY(100);
+ if (--loops < 0) {
+ isp_prt(isp, ISP_LOGERR,
+ "MBOX_BUSY never cleared on reset");
+ return;
+ }
+ }
+ }
+
+ /*
+ * Up until this point we've done everything by just reading or
+ * setting registers. From this point on we rely on at least *some*
+ * kind of firmware running in the card.
+ */
+
+ /*
+ * Do some sanity checking.
+ */
+ MEMZERO(&mbs, sizeof (mbs));
+ mbs.param[0] = MBOX_NO_OP;
+ isp_mboxcmd(isp, &mbs, MBLOGALL);
+ if (mbs.param[0] != MBOX_COMMAND_COMPLETE) {
+ return;
+ }
+
+ if (IS_SCSI(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);
+ if (mbs.param[0] != MBOX_COMMAND_COMPLETE) {
+ 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]);
+ return;
+ }
+
+ }
+
+ /*
+ * Download new Firmware, unless requested not to do so.
+ * This is made slightly trickier in some cases where the
+ * firmware of the ROM revision is newer than the revision
+ * compiled into the driver. So, where we used to compare
+ * versions of our f/w and the ROM f/w, now we just see
+ * 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)) {
+ dodnld = 0;
+ }
+
+ if (IS_23XX(isp)) {
+ code_org = ISP_CODE_ORG_2300;
+ } else {
+ code_org = ISP_CODE_ORG;
+ }
+ if (dodnld) {
+ uint16_t *ptr = isp->isp_mdvec->dv_ispfw;
+
+ isp->isp_mbxworkp = &ptr[1];
+ isp->isp_mbxwrk0 = ptr[3] - 1;
+ isp->isp_mbxwrk1 = code_org + 1;
+ MEMZERO(&mbs, sizeof (mbs));
+ mbs.param[0] = MBOX_WRITE_RAM_WORD;
+ mbs.param[1] = code_org;
+ mbs.param[2] = ptr[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;
+ }
+
+ /*
+ * 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.
+ */
+ if (IS_2322(isp)) {
+ uint32_t nxtaddr;
+ uint32_t offset;
+
+ nxtaddr = ptr[3];
+ ptr = &ptr[nxtaddr];
+ offset = ptr[5] | (((uint32_t)(ptr[4] & 0x3f)) << 16);
+ isp->isp_mbxworkp = &ptr[1];
+ isp->isp_mbxwrk0 = ptr[3] - 1;
+ isp->isp_mbxwrk1 = offset + 1;
+ isp->isp_mbxwrk8 = (offset + 1) >> 16;
+ MEMZERO(&mbs, sizeof (mbs));
+ mbs.param[0] = MBOX_WRITE_RAM_WORD_EXTENDED;
+ mbs.param[1] = offset;
+ mbs.param[2] = ptr[0];
+ mbs.param[8] = offset >> 16;
+ isp_mboxcmd(isp, &mbs, MBLOGNONE);
+ if (mbs.param[0] != MBOX_COMMAND_COMPLETE) {
+ isp_prt(isp, ISP_LOGERR,
+ "Receive Sequencer F/W Load Failed");
+ return;
+ }
+
+ nxtaddr = ptr[3];
+ ptr = &ptr[nxtaddr];
+ offset = ptr[5] | (((uint32_t)(ptr[4] & 0x3f)) << 16);
+ isp->isp_mbxworkp = &ptr[1];
+ isp->isp_mbxwrk0 = ptr[3] - 1;
+ isp->isp_mbxwrk1 = (offset + 1);
+ isp->isp_mbxwrk8 = (offset + 1) >> 16;
+ MEMZERO(&mbs, sizeof (mbs));
+ mbs.param[0] = MBOX_WRITE_RAM_WORD_EXTENDED;
+ mbs.param[1] = offset;
+ mbs.param[2] = ptr[0];
+ mbs.param[8] = offset >> 16;
+ isp_mboxcmd(isp, &mbs, MBLOGNONE);
+ if (mbs.param[0] != MBOX_COMMAND_COMPLETE) {
+ isp_prt(isp, ISP_LOGERR,
+ "Transmit Sequencer F/W Load Failed");
+ return;
+ }
+ } else {
+ /*
+ * Verify that it downloaded correctly.
+ */
+ MEMZERO(&mbs, sizeof (mbs));
+ mbs.param[0] = MBOX_VERIFY_CHECKSUM;
+ mbs.param[1] = code_org;
+ isp_mboxcmd(isp, &mbs, MBLOGNONE);
+ if (mbs.param[0] != MBOX_COMMAND_COMPLETE) {
+ isp_prt(isp, ISP_LOGERR,
+ "Downloaded RISC Code Checksum Failure");
+ return;
+ }
+
+ }
+ isp->isp_loaded_fw = 1;
+ } else {
+ isp->isp_loaded_fw = 0;
+ isp_prt(isp, ISP_LOGDEBUG2, "skipping f/w download");
+ }
+
+ /*
+ * Now start it rolling.
+ *
+ * If we didn't actually download f/w,
+ * we still need to (re)start it.
+ */
+
+
+ MEMZERO(&mbs, sizeof (mbs));
+ mbs.param[0] = MBOX_EXEC_FIRMWARE;
+ mbs.param[1] = code_org;
+ if (IS_2322(isp) || IS_24XX(isp)) {
+ if (isp->isp_loaded_fw) {
+ mbs.param[2] = 0;
+ } else {
+ mbs.param[2] = 1;
+ }
+ }
+ isp_mboxcmd(isp, &mbs, MBLOGNONE);
+ if (IS_2322(isp) || IS_24XX(isp)) {
+ if (mbs.param[0] != MBOX_COMMAND_COMPLETE) {
+ isp_prt(isp, ISP_LOGERR, "EXEC F/W failed: 0x%x",
+ mbs.param[0]);
+ return;
+ }
+ }
+
+ /*
+ * Give it a chance to start.
+ */
+ USEC_DELAY(500);
+
+ 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;
+ isp_mboxcmd(isp, &mbs, MBLOGALL);
+ /* we will try not to care if this fails */
+ }
+ }
+
+ MEMZERO(&mbs, sizeof (mbs));
+ mbs.param[0] = MBOX_ABOUT_FIRMWARE;
+ isp_mboxcmd(isp, &mbs, MBLOGALL);
+ if (mbs.param[0] != MBOX_COMMAND_COMPLETE) {
+ return;
+ }
+
+ /*
+ * The SBus firmware that we are using apparently does not return
+ * major, minor, micro revisions in the mailbox registers, which
+ * is really, really, annoying.
+ */
+ if (ISP_SBUS_SUPPORTED && isp->isp_bustype == ISP_BT_SBUS) {
+ if (dodnld) {
+#ifdef ISP_TARGET_MODE
+ isp->isp_fwrev[0] = 7;
+ isp->isp_fwrev[1] = 55;
+#else
+ isp->isp_fwrev[0] = 1;
+ 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]);
+
+ if (IS_FC(isp)) {
+ /*
+ * We do not believe firmware attributes for 2100 code less
+ * 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.
+ */
+ if (!(ISP_FW_NEWER_THAN(isp, 1, 17, 0))) {
+#ifdef USE_SMALLER_2100_FIRMWARE
+ FCPARAM(isp)->isp_fwattr = ISP_FW_ATTR_SCCLUN;
+#else
+ FCPARAM(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 (IS_2KLOGIN(isp)) {
+ isp_prt(isp, ISP_LOGCONFIG, "2K Logins Supported");
+ }
+ }
+
+ 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]);
+ }
+
+ MEMZERO(&mbs, sizeof (mbs));
+ mbs.param[0] = MBOX_GET_FIRMWARE_STATUS;
+ isp_mboxcmd(isp, &mbs, MBLOGALL);
+ if (mbs.param[0] != MBOX_COMMAND_COMPLETE) {
+ return;
+ }
+ isp->isp_maxcmds = mbs.param[2];
+ isp_prt(isp, ISP_LOGINFO,
+ "%d max I/O commands supported", mbs.param[2]);
+ isp_fw_state(isp);
+
+ /*
+ * Set up DMA for the request and result mailboxes.
+ */
+ if (ISP_MBOXDMASETUP(isp) != 0) {
+ isp_prt(isp, ISP_LOGERR, "Cannot setup DMA");
+ return;
+ }
+ isp->isp_state = ISP_RESETSTATE;
+
+ /*
+ * Okay- now that we have new firmware running, we now (re)set our
+ * notion of how many luns we support. This is somewhat tricky because
+ * if we haven't loaded firmware, we sometimes do not have an easy way
+ * of knowing how many luns we support.
+ *
+ * Expanded lun firmware gives you 32 luns for SCSI cards and
+ * 16384 luns for Fibre Channel cards.
+ *
+ * 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
+ * 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.
+ */
+ if (IS_SCSI(isp)) {
+ if (dodnld) {
+ if (IS_ULTRA2(isp) || IS_ULTRA3(isp)) {
+ isp->isp_maxluns = 32;
+ } else {
+ isp->isp_maxluns = 8;
+ }
+ } else {
+ isp->isp_maxluns = 8;
+ }
+ } else {
+ if (FCPARAM(isp)->isp_fwattr & ISP_FW_ATTR_SCCLUN) {
+ isp->isp_maxluns = 16384;
+ } else {
+ isp->isp_maxluns = 16;
+ }
+ }
+}
+
+/*
+ * Initialize Parameters of Hardware to a known state.
+ *
+ * Locks are held before coming here.
+ */
+
+void
+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);
+ } else {
+ isp_scsi_init(isp);
+ }
+}
+
+static void
+isp_scsi_init(ispsoftc_t *isp)
+{
+ sdparam *sdp_chan0, *sdp_chan1;
+ mbreg_t mbs;
+
+ sdp_chan0 = isp->isp_param;
+ 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;
+ }
+
+ /* First do overall per-card settings. */
+
+ /*
+ * If we have fast memory timing enabled, turn it on.
+ */
+ if (sdp_chan0->isp_fast_mttr) {
+ ISP_WRITE(isp, RISC_MTR, 0x1313);
+ }
+
+ /*
+ * Set Retry Delay and Count.
+ * You set both channels at the same time.
+ */
+ MEMZERO(&mbs, sizeof (mbs));
+ mbs.param[0] = MBOX_SET_RETRY_COUNT;
+ 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);
+ if (mbs.param[0] != MBOX_COMMAND_COMPLETE) {
+ return;
+ }
+
+ /*
+ * Set ASYNC DATA SETUP time. This is very important.
+ */
+ MEMZERO(&mbs, sizeof (mbs));
+ mbs.param[0] = MBOX_SET_ASYNC_DATA_SETUP_TIME;
+ mbs.param[1] = sdp_chan0->isp_async_data_setup;
+ mbs.param[2] = sdp_chan1->isp_async_data_setup;
+ isp_mboxcmd(isp, &mbs, MBLOGALL);
+ if (mbs.param[0] != MBOX_COMMAND_COMPLETE) {
+ return;
+ }
+
+ /*
+ * Set ACTIVE Negation State.
+ */
+ MEMZERO(&mbs, sizeof (mbs));
+ mbs.param[0] = MBOX_SET_ACT_NEG_STATE;
+ 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);
+
+ MEMZERO(&mbs, sizeof (mbs));
+ isp_mboxcmd(isp, &mbs, MBLOGNONE);
+ if (mbs.param[0] != MBOX_COMMAND_COMPLETE) {
+ isp_prt(isp, ISP_LOGERR,
+ "failed to set active negation state (%d,%d), (%d,%d)",
+ sdp_chan0->isp_req_ack_active_neg,
+ sdp_chan0->isp_data_line_active_neg,
+ sdp_chan1->isp_req_ack_active_neg,
+ sdp_chan1->isp_data_line_active_neg);
+ /*
+ * But don't return.
+ */
+ }
+
+ /*
+ * Set the Tag Aging limit
+ */
+ MEMZERO(&mbs, sizeof (mbs));
+ mbs.param[0] = MBOX_SET_TAG_AGE_LIMIT;
+ mbs.param[1] = sdp_chan0->isp_tag_aging;
+ mbs.param[2] = sdp_chan1->isp_tag_aging;
+ isp_mboxcmd(isp, &mbs, MBLOGALL);
+ 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);
+ return;
+ }
+
+ /*
+ * Set selection timeout.
+ */
+ MEMZERO(&mbs, sizeof (mbs));
+ mbs.param[0] = MBOX_SET_SELECT_TIMEOUT;
+ mbs.param[1] = sdp_chan0->isp_selection_timeout;
+ mbs.param[2] = sdp_chan1->isp_selection_timeout;
+ isp_mboxcmd(isp, &mbs, MBLOGALL);
+ if (mbs.param[0] != MBOX_COMMAND_COMPLETE) {
+ return;
+ }
+
+ /* now do per-channel settings */
+ isp_scsi_channel_init(isp, 0);
+ if (IS_DUALBUS(isp))
+ isp_scsi_channel_init(isp, 1);
+
+ /*
+ * Now enable request/response queues
+ */
+
+ if (IS_ULTRA2(isp) || IS_1240(isp)) {
+ MEMZERO(&mbs, sizeof (mbs));
+ mbs.param[0] = MBOX_INIT_RES_QUEUE_A64;
+ 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);
+ if (mbs.param[0] != MBOX_COMMAND_COMPLETE) {
+ return;
+ }
+ isp->isp_residx = mbs.param[5];
+
+ MEMZERO(&mbs, sizeof (mbs));
+ mbs.param[0] = MBOX_INIT_REQ_QUEUE_A64;
+ 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);
+ if (mbs.param[0] != MBOX_COMMAND_COMPLETE) {
+ return;
+ }
+ isp->isp_reqidx = isp->isp_reqodx = mbs.param[4];
+ } else {
+ MEMZERO(&mbs, sizeof (mbs));
+ mbs.param[0] = MBOX_INIT_RES_QUEUE;
+ 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);
+ if (mbs.param[0] != MBOX_COMMAND_COMPLETE) {
+ return;
+ }
+ isp->isp_residx = mbs.param[5];
+
+ MEMZERO(&mbs, sizeof (mbs));
+ mbs.param[0] = MBOX_INIT_REQ_QUEUE;
+ 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);
+ if (mbs.param[0] != MBOX_COMMAND_COMPLETE) {
+ return;
+ }
+ isp->isp_reqidx = isp->isp_reqodx = mbs.param[4];
+ }
+
+ /*
+ * Turn on Fast Posting, LVD transitions
+ *
+ * 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.
+ */
+
+ MEMZERO(&mbs, sizeof (mbs));
+ mbs.param[0] = MBOX_SET_FW_FEATURES;
+ mbs.param[1] = 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
+ if (IS_ULTRA2(isp) || IS_1240(isp))
+ mbs.param[1] |= FW_FEATURE_FAST_POST;
+#endif
+ if (mbs.param[1] != 0) {
+ uint16_t sfeat = mbs.param[1];
+ isp_mboxcmd(isp, &mbs, MBLOGALL);
+ 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(ispsoftc_t *isp, int channel)
+{
+ sdparam *sdp;
+ mbreg_t mbs;
+ int tgt;
+
+ sdp = isp->isp_param;
+ sdp += channel;
+
+ /*
+ * Set (possibly new) Initiator ID.
+ */
+ MEMZERO(&mbs, sizeof (mbs));
+ mbs.param[0] = MBOX_SET_INIT_SCSI_ID;
+ mbs.param[1] = (channel << 7) | sdp->isp_initiator_id;
+ isp_mboxcmd(isp, &mbs, MBLOGALL);
+ 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);
+
+
+ /*
+ * Set current per-target parameters to an initial safe minimum.
+ */
+ for (tgt = 0; tgt < MAX_TARGETS; tgt++) {
+ int lun;
+ uint16_t sdf;
+
+ if (sdp->isp_devparam[tgt].dev_enable == 0) {
+ continue;
+ }
+#ifndef ISP_TARGET_MODE
+ sdf = sdp->isp_devparam[tgt].goal_flags;
+ sdf &= DPARM_SAFE_DFLT;
+ /*
+ * It is not quite clear when this changed over so that
+ * we could force narrow and async for 1000/1020 cards,
+ * but assume that this is only the case for loaded
+ * firmware.
+ */
+ if (isp->isp_loaded_fw) {
+ sdf |= DPARM_NARROW | DPARM_ASYNC;
+ }
+#else
+ /*
+ * The !$*!)$!$)* f/w uses the same index into some
+ * internal table to decide how to respond to negotiations,
+ * so if we've said "let's be safe" for ID X, and ID X
+ * selects *us*, the negotiations will back to 'safe'
+ * (as in narrow/async). What the f/w *should* do is
+ * use the initiator id settings to decide how to respond.
+ */
+ sdp->isp_devparam[tgt].goal_flags = sdf = DPARM_DEFAULT;
+#endif
+ MEMZERO(&mbs, sizeof (mbs));
+ mbs.param[0] = MBOX_SET_TARGET_PARAMS;
+ mbs.param[1] = (channel << 15) | (tgt << 8);
+ mbs.param[2] = sdf;
+ if ((sdf & DPARM_SYNC) == 0) {
+ mbs.param[3] = 0;
+ } else {
+ mbs.param[3] =
+ (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);
+ if (mbs.param[0] != MBOX_COMMAND_COMPLETE) {
+ sdf = DPARM_SAFE_DFLT;
+ MEMZERO(&mbs, sizeof (mbs));
+ mbs.param[0] = MBOX_SET_TARGET_PARAMS;
+ mbs.param[1] = (tgt << 8) | (channel << 15);
+ mbs.param[2] = sdf;
+ mbs.param[3] = 0;
+ isp_mboxcmd(isp, &mbs, MBLOGALL);
+ if (mbs.param[0] != MBOX_COMMAND_COMPLETE) {
+ continue;
+ }
+ }
+
+ /*
+ * We don't update any information directly from the f/w
+ * because we need to run at least one command to cause a
+ * new state to be latched up. So, we just assume that we
+ * converge to the values we just had set.
+ *
+ * Ensure that we don't believe tagged queuing is enabled yet.
+ * It turns out that sometimes the ISP just ignores our
+ * attempts to set parameters for devices that it hasn't
+ * seen yet.
+ */
+ sdp->isp_devparam[tgt].actv_flags = sdf & ~DPARM_TQING;
+ for (lun = 0; lun < (int) isp->isp_maxluns; lun++) {
+ MEMZERO(&mbs, sizeof (mbs));
+ mbs.param[0] = MBOX_SET_DEV_QUEUE_PARAMS;
+ mbs.param[1] = (channel << 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);
+ if (mbs.param[0] != MBOX_COMMAND_COMPLETE) {
+ break;
+ }
+ }
+ }
+ for (tgt = 0; tgt < MAX_TARGETS; tgt++) {
+ if (sdp->isp_devparam[tgt].dev_refresh) {
+ isp->isp_sendmarker |= (1 << channel);
+ isp->isp_update |= (1 << channel);
+ break;
+ }
+ }
+}
+
+/*
+ * Fibre Channel specific initialization.
+ *
+ * Locks are held before coming here.
+ */
+static void
+isp_fibre_init(ispsoftc_t *isp)
+{
+ fcparam *fcp;
+ isp_icb_t local, *icbp = &local;
+ mbreg_t mbs;
+ int loopid;
+ uint64_t nwwn, pwwn;
+
+ fcp = isp->isp_param;
+
+ /*
+ * Do this *before* initializing the firmware.
+ */
+ 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) {
+ return;
+ }
+
+ loopid = fcp->isp_loopid;
+ MEMZERO(icbp, sizeof (*icbp));
+ icbp->icb_version = ICB_VERSION1;
+
+ /*
+ * Firmware Options are either retrieved from NVRAM or
+ * are patched elsewhere. We check them for sanity here
+ * and make changes based on board revision, but otherwise
+ * let others decide policy.
+ */
+
+ /*
+ * 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;
+ }
+
+ /*
+ * We have to use FULL LOGIN even though it resets the loop too much
+ * because otherwise port database entries don't get updated after
+ * 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;
+ }
+
+ /*
+ * Insist on Port Database Update Async notifications
+ */
+ fcp->isp_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;
+ } else {
+ fcp->isp_fwoptions &= ~ICBOPT_TGT_ENABLE;
+ }
+
+ if (isp->isp_role & ISP_ROLE_INITIATOR) {
+ fcp->isp_fwoptions &= ~ICBOPT_INI_DISABLE;
+ } else {
+ fcp->isp_fwoptions |= ICBOPT_INI_DISABLE;
+ }
+
+ /*
+ * 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);
+ 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);
+ icbp->icb_maxalloc = 16;
+ }
+ icbp->icb_execthrottle = fcp->isp_execthrottle;
+ if (icbp->icb_execthrottle < 1) {
+ isp_prt(isp, ISP_LOGERR,
+ "bad execution throttle of %d- using 16",
+ fcp->isp_execthrottle);
+ 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;
+ if (icbp->icb_hardaddr > 125) {
+ /*
+ * We end up with these Loop IDs for F-Port topologies
+ */
+ if (icbp->icb_hardaddr != 0xff && icbp->icb_hardaddr != 0x800) {
+ isp_prt(isp, ISP_LOGERR,
+ "bad hard address %u- resetting to zero",
+ icbp->icb_hardaddr);
+ }
+ icbp->icb_hardaddr = 0;
+ }
+
+ /*
+ * Our life seems so much better with 2200s and later with
+ * the latest f/w if we set Hard Address.
+ */
+ if (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)) {
+ icbp->icb_fwoptions |= ICBOPT_EXTENDED;
+ /*
+ * Prefer or force Point-To-Point instead Loop?
+ */
+ switch(isp->isp_confopts & ISP_CFG_PORT_PREF) {
+ case ISP_CFG_NPORT:
+ icbp->icb_xfwoptions |= ICBXOPT_PTP_2_LOOP;
+ break;
+ case ISP_CFG_NPORT_ONLY:
+ icbp->icb_xfwoptions |= ICBXOPT_PTP_ONLY;
+ break;
+ case ISP_CFG_LPORT_ONLY:
+ icbp->icb_xfwoptions |= ICBXOPT_LOOP_ONLY;
+ break;
+ default:
+ icbp->icb_xfwoptions |= ICBXOPT_LOOP_2_PTP;
+ break;
+ }
+ if (IS_2200(isp)) {
+ if (ISP_FW_NEWER_THAN(isp, 1, 17, 0)) {
+ icbp->icb_xfwoptions |= ICBXOPT_RIO_16BIT;
+ icbp->icb_racctimer = 4;
+ icbp->icb_idelaytimer = 8;
+ }
+ 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).
+ */
+ 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) {
+ icbp->icb_zfwoptions |= ICBZOPT_RATE_TWOGB;
+ } else {
+ icbp->icb_zfwoptions |= ICBZOPT_RATE_AUTO;
+ }
+ if (fcp->isp_zfwoptions & ICBZOPT_50_OHM) {
+ icbp->icb_zfwoptions |= ICBZOPT_50_OHM;
+ }
+ }
+ }
+
+
+ /*
+ * For 22XX > 2.1.26 && 23XX, set some options.
+ * XXX: Probably okay for newer 2100 f/w too.
+ */
+ 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)
+ */
+ MEMZERO(&mbs, sizeof (mbs));
+ mbs.param[0] = MBOX_SET_FIRMWARE_OPTIONS;
+ mbs.param[1] = 0xb;
+ mbs.param[2] = 0;
+ mbs.param[3] = 0;
+ isp_mboxcmd(isp, &mbs, MBLOGALL);
+ }
+ icbp->icb_logintime = ICB_LOGIN_TOV;
+ icbp->icb_lunetimeout = ICB_LUN_ENABLE_TOV;
+
+ nwwn = ISP_NODEWWN(isp);
+ pwwn = ISP_PORTWWN(isp);
+ if (nwwn && pwwn) {
+ icbp->icb_fwoptions |= ICBOPT_BOTH_WWNS;
+ MAKE_NODE_NAME_FROM_WWN(icbp->icb_nodename, nwwn);
+ MAKE_NODE_NAME_FROM_WWN(icbp->icb_portname, pwwn);
+ isp_prt(isp, ISP_LOGDEBUG1,
+ "Setting ICB Node 0x%08x%08x Port 0x%08x%08x",
+ ((uint32_t) (nwwn >> 32)),
+ ((uint32_t) (nwwn & 0xffffffff)),
+ ((uint32_t) (pwwn >> 32)),
+ ((uint32_t) (pwwn & 0xffffffff)));
+ } else {
+ isp_prt(isp, ISP_LOGDEBUG1, "Not using any WWNs");
+ icbp->icb_fwoptions &= ~(ICBOPT_BOTH_WWNS|ICBOPT_FULL_LOGIN);
+ }
+ 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);
+ icbp->icb_rqstaddr[RQRSP_ADDR4863] = DMA_WD3(isp->isp_rquest_dma);
+ 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);
+
+ 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
+ */
+ MEMZERO(&mbs, sizeof (mbs));
+ 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 %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));
+ isp_mboxcmd(isp, &mbs, MBLOGALL);
+ FC_SCRATCH_RELEASE(isp);
+ if (mbs.param[0] != MBOX_COMMAND_COMPLETE) {
+ return;
+ }
+ isp->isp_reqidx = isp->isp_reqodx = 0;
+ isp->isp_residx = 0;
+ isp->isp_sendmarker = 1;
+
+ /*
+ * Whatever happens, we're now committed to being here.
+ */
+ 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(ispsoftc_t *isp, fcpos_map_t *map)
+{
+ fcparam *fcp = (fcparam *) isp->isp_param;
+ mbreg_t mbs;
+
+ MEMZERO(&mbs, sizeof (mbs));
+ mbs.param[0] = MBOX_GET_FC_AL_POSITION_MAP;
+ 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);
+ FC_SCRATCH_ACQUIRE(isp);
+ MEMORYBARRIER(isp, SYNC_SFORDEV, 0, sizeof (fcpos_map_t));
+ 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);
+ }
+ FC_SCRATCH_RELEASE(isp);
+ return (-1);
+}
+
+static void
+isp_mark_getpdb_all(ispsoftc_t *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;
+ }
+}
+
+static int
+isp_getpdb(ispsoftc_t *isp, int id, isp_pdb_t *pdbp)
+{
+ fcparam *fcp = (fcparam *) isp->isp_param;
+ mbreg_t mbs;
+
+ MEMZERO(&mbs, sizeof (mbs));
+ mbs.param[0] = MBOX_GET_PORT_DB;
+ if (IS_2KLOGIN(isp)) {
+ mbs.param[1] = id;
+ mbs.ibits |= (1 << 10);
+ } 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);
+ FC_SCRATCH_ACQUIRE(isp);
+ MEMORYBARRIER(isp, SYNC_SFORDEV, 0, sizeof (isp_pdb_t));
+ 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);
+ }
+ FC_SCRATCH_RELEASE(isp);
+ return (-1);
+}
+
+static uint64_t
+isp_get_portname(ispsoftc_t *isp, int loopid, int nodename)
+{
+ uint64_t wwn = 0;
+ mbreg_t mbs;
+
+ MEMZERO(&mbs, sizeof (mbs));
+ mbs.param[0] = MBOX_GET_PORT_NAME;
+ if (IS_2KLOGIN(isp)) {
+ mbs.param[1] = loopid;
+ mbs.ibits |= (1 << 10);
+ if (nodename) {
+ mbs.param[10] = 1;
+ }
+ } else {
+ 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 =
+ (((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 and know our Loop ID.
+ */
+
+static int
+isp_fclink_test(ispsoftc_t *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;
+ uint8_t lwfs;
+ fcparam *fcp;
+ struct lportdb *lp;
+ isp_pdb_t pdb;
+
+ fcp = isp->isp_param;
+
+ isp_prt(isp, ISP_LOGDEBUG0, "FC Link Test Entry");
+
+ /*
+ * XXX: Here is where we would start a 'loop dead' timeout
+ */
+
+ /*
+ * 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);
+ 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);
+
+ /*
+ * 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 & 0xffffffff));
+
+ /*
+ * 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) {
+ USEC_SLEEP(isp, 4000000);
+ enano -= (uint64_t) 4000000000U;
+ }
+ wrk = enano;
+ wrk /= 1000;
+ USEC_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) {
+ return (-1);
+ }
+
+ /*
+ * Get our Loop ID (if possible). We really need to have it.
+ */
+ MEMZERO(&mbs, sizeof (mbs));
+ mbs.param[0] = MBOX_GET_LOOP_ID;
+ isp_mboxcmd(isp, &mbs, MBLOGALL);
+ if (mbs.param[0] != MBOX_COMMAND_COMPLETE) {
+ return (-1);
+ }
+ if (IS_2KLOGIN(isp)) {
+ fcp->isp_loopid = mbs.param[1];
+ } else {
+ fcp->isp_loopid = mbs.param[1] & 0xff;
+ }
+ if (fcp->isp_loopid == 0xffff) { /* happens with 2k login f/w */
+ fcp->isp_loopid = MAX_FC_TARG-1;
+ } else if (fcp->isp_loopid >= MAX_FC_TARG) {
+ isp_prt(isp, ISP_LOGWARN, "bad initiator loopid (0x%x)", fcp->isp_loopid);
+ fcp->isp_loopid = MAX_FC_TARG-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;
+ } else {
+ fcp->isp_topo = TOPO_NL_PORT;
+ }
+ /*
+ * Get the port id.
+ */
+ fcp->isp_portid = mbs.param[2] | (mbs.param[3] << 16);
+
+ /*
+ * 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 a server doing aliasing).
+ */
+ fcp->isp_onfabric = 0;
+
+ 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;
+
+ 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;
+ }
+
+ 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);
+
+ /*
+ * Save the Fabric controller's port database entry.
+ */
+ lp = &fcp->portdb[loopid];
+ lp->node_wwn =
+ (((uint64_t)pdb.pdb_nodename[0]) << 56) |
+ (((uint64_t)pdb.pdb_nodename[1]) << 48) |
+ (((uint64_t)pdb.pdb_nodename[2]) << 40) |
+ (((uint64_t)pdb.pdb_nodename[3]) << 32) |
+ (((uint64_t)pdb.pdb_nodename[4]) << 24) |
+ (((uint64_t)pdb.pdb_nodename[5]) << 16) |
+ (((uint64_t)pdb.pdb_nodename[6]) << 8) |
+ (((uint64_t)pdb.pdb_nodename[7]));
+ lp->port_wwn =
+ (((uint64_t)pdb.pdb_portname[0]) << 56) |
+ (((uint64_t)pdb.pdb_portname[1]) << 48) |
+ (((uint64_t)pdb.pdb_portname[2]) << 40) |
+ (((uint64_t)pdb.pdb_portname[3]) << 32) |
+ (((uint64_t)pdb.pdb_portname[4]) << 24) |
+ (((uint64_t)pdb.pdb_portname[5]) << 16) |
+ (((uint64_t)pdb.pdb_portname[6]) << 8) |
+ (((uint64_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;
+ }
+
+ fcp->isp_gbspeed = 1;
+ if (IS_23XX(isp)) {
+ MEMZERO(&mbs, sizeof (mbs));
+ 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;
+ }
+ }
+ }
+
+ isp_prt(isp, ISP_LOGCONFIG, topology, fcp->isp_loopid,
+ fcp->isp_portid, fcp->isp_loopstate, toponames[fcp->isp_topo]);
+
+ /*
+ * Announce ourselves, too. This involves synthesizing an entry.
+ */
+ if (fcp->isp_iid_set == 0) {
+ fcp->isp_iid_set = 1;
+ fcp->isp_iid = fcp->isp_loopid;
+ lp = &fcp->portdb[fcp->isp_iid];
+ } 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;
+ break;
+ case ISP_ROLE_TARGET:
+ lp->roles = SVC3_TGT_ROLE >> SVC3_ROLE_SHIFT;
+ break;
+ case ISP_ROLE_INITIATOR:
+ lp->roles = SVC3_INI_ROLE >> SVC3_ROLE_SHIFT;
+ break;
+ case ISP_ROLE_BOTH:
+ lp->roles = (SVC3_INI_ROLE|SVC3_TGT_ROLE) >> SVC3_ROLE_SHIFT;
+ break;
+ }
+ lp->loggedin = lp->valid = 1;
+ count = fcp->isp_iid;
+ (void) isp_async(isp, ISPASYNC_PROMENADE, &count);
+ isp_prt(isp, ISP_LOGDEBUG0, "FC Link Test Complete");
+ return (0);
+}
+
+static const 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 "?????";
+ }
+}
+
+/*
+ * Synchronize our soft copy of the port database with what the f/w thinks
+ * (with a view toward possibly for a specific target....)
+ */
+
+static int
+isp_pdb_sync(ispsoftc_t *isp)
+{
+ struct lportdb *lp;
+ fcparam *fcp = isp->isp_param;
+ isp_pdb_t pdb;
+ int loopid, base, lim;
+
+ /*
+ * 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) {
+ 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;
+ 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.
+ */
+ 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;
+ }
+ lp->was_fabric_dev = lp->fabric_dev;
+ }
+
+ if (fcp->isp_topo == TOPO_FL_PORT)
+ base = FC_SNS_ID+1;
+ else
+ base = 0;
+
+ 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.
+ */
+ for (lp = &fcp->portdb[base]; lp < &fcp->portdb[lim]; lp++) {
+ uint32_t portid;
+ mbreg_t mbs;
+
+ loopid = lp - fcp->portdb;
+ if (loopid >= FL_PORT_ID && loopid <= FC_SNS_ID) {
+ continue;
+ }
+
+ /*
+ * Anything here?
+ */
+ if (lp->port_wwn == 0) {
+ continue;
+ }
+
+ /*
+ * Don't try to log into yourself.
+ */
+ if ((portid = lp->portid) == fcp->isp_portid) {
+ continue;
+ }
+
+
+ /*
+ * 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.
+ */
+ if (lp->loggedin && lp->force_logout == 0 &&
+ isp_getpdb(isp, lp->loopid, &pdb) == 0) {
+ int nrole;
+ uint64_t nwwnn, nwwpn;
+ nwwnn =
+ (((uint64_t)pdb.pdb_nodename[0]) << 56) |
+ (((uint64_t)pdb.pdb_nodename[1]) << 48) |
+ (((uint64_t)pdb.pdb_nodename[2]) << 40) |
+ (((uint64_t)pdb.pdb_nodename[3]) << 32) |
+ (((uint64_t)pdb.pdb_nodename[4]) << 24) |
+ (((uint64_t)pdb.pdb_nodename[5]) << 16) |
+ (((uint64_t)pdb.pdb_nodename[6]) << 8) |
+ (((uint64_t)pdb.pdb_nodename[7]));
+ nwwpn =
+ (((uint64_t)pdb.pdb_portname[0]) << 56) |
+ (((uint64_t)pdb.pdb_portname[1]) << 48) |
+ (((uint64_t)pdb.pdb_portname[2]) << 40) |
+ (((uint64_t)pdb.pdb_portname[3]) << 32) |
+ (((uint64_t)pdb.pdb_portname[4]) << 24) |
+ (((uint64_t)pdb.pdb_portname[5]) << 16) |
+ (((uint64_t)pdb.pdb_portname[6]) << 8) |
+ (((uint64_t)pdb.pdb_portname[7]));
+ nrole = (pdb.pdb_prli_svc3 & SVC3_ROLE_MASK) >>
+ SVC3_ROLE_SHIFT;
+ if (pdb.pdb_loopid == lp->loopid && lp->portid ==
+ (uint32_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) {
+ return (-1);
+ }
+
+ /*
+ * Force a logout if we were logged in.
+ */
+ if (lp->loggedin) {
+ if (lp->force_logout ||
+ isp_getpdb(isp, lp->loopid, &pdb) == 0) {
+ MEMZERO(&mbs, sizeof (mbs));
+ mbs.param[0] = MBOX_FABRIC_LOGOUT;
+ if (IS_2KLOGIN(isp)) {
+ mbs.param[1] = lp->loopid;
+ mbs.ibits |= (1 << 10);
+ } else {
+ mbs.param[1] = lp->loopid << 8;
+ }
+ 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);
+ }
+ }
+
+ /*
+ * And log in....
+ */
+ loopid = lp - fcp->portdb;
+ lp->loopid = FL_PORT_ID;
+ do {
+ MEMZERO(&mbs, sizeof (mbs));
+ mbs.param[0] = MBOX_FABRIC_LOGIN;
+ if (IS_2KLOGIN(isp)) {
+ mbs.param[1] = loopid;
+ mbs.ibits |= (1 << 10);
+ } else {
+ 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) {
+ 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];
+ if (loopid >= MAX_FC_TARG) {
+ loopid = MAX_FC_TARG;
+ break;
+ }
+ 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;
+ 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.
+ */
+
+ if (lp->loopid == FL_PORT_ID) {
+ lp->loopid = 0;
+ continue;
+ }
+
+ /*
+ * Make sure we can get the approriate port information.
+ */
+ 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 (pdb.pdb_loopid != lp->loopid) {
+ isp_prt(isp, ISP_LOGWARN, pdbmfail1,
+ lp->portid, pdb.pdb_loopid);
+ goto dump_em;
+ }
+
+ if (lp->portid != (uint32_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 =
+ (((uint64_t)pdb.pdb_nodename[0]) << 56) |
+ (((uint64_t)pdb.pdb_nodename[1]) << 48) |
+ (((uint64_t)pdb.pdb_nodename[2]) << 40) |
+ (((uint64_t)pdb.pdb_nodename[3]) << 32) |
+ (((uint64_t)pdb.pdb_nodename[4]) << 24) |
+ (((uint64_t)pdb.pdb_nodename[5]) << 16) |
+ (((uint64_t)pdb.pdb_nodename[6]) << 8) |
+ (((uint64_t)pdb.pdb_nodename[7]));
+ lp->port_wwn =
+ (((uint64_t)pdb.pdb_portname[0]) << 56) |
+ (((uint64_t)pdb.pdb_portname[1]) << 48) |
+ (((uint64_t)pdb.pdb_portname[2]) << 40) |
+ (((uint64_t)pdb.pdb_portname[3]) << 32) |
+ (((uint64_t)pdb.pdb_portname[4]) << 24) |
+ (((uint64_t)pdb.pdb_portname[5]) << 16) |
+ (((uint64_t)pdb.pdb_portname[6]) << 8) |
+ (((uint64_t)pdb.pdb_portname[7]));
+ /*
+ * Check to make sure this all makes sense.
+ */
+ if (lp->node_wwn && lp->port_wwn) {
+ lp->valid = 1;
+ loopid = lp - fcp->portdb;
+ (void) isp_async(isp, ISPASYNC_PROMENADE, &loopid);
+ continue;
+ }
+dump_em:
+ lp->valid = 0;
+ isp_prt(isp, ISP_LOGINFO,
+ ldumped, loopid, lp->loopid, lp->portid);
+ MEMZERO(&mbs, sizeof (mbs));
+ mbs.param[0] = MBOX_FABRIC_LOGOUT;
+ if (IS_2KLOGIN(isp)) {
+ mbs.param[1] = lp->loopid;
+ mbs.ibits |= (1 << 10);
+ } else {
+ mbs.param[1] = lp->loopid << 8;
+ }
+ isp_mboxcmd(isp, &mbs, MBLOGNONE);
+ if (fcp->isp_fwstate != FW_READY ||
+ fcp->isp_loopstate != LOOP_SYNCING_PDB) {
+ 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.
+ */
+ fcp->loop_seen_once = 1;
+ fcp->isp_loopstate = LOOP_READY;
+ return (0);
+}
+
+static int
+isp_scan_loop(ispsoftc_t *isp)
+{
+ struct lportdb *lp;
+ fcparam *fcp = isp->isp_param;
+ isp_pdb_t pdb;
+ int loopid, lim, hival;
+
+ 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:
+ isp_prt(isp, ISP_LOGDEBUG0, "no loop topology to scan");
+ fcp->isp_loopstate = LOOP_LSCAN_DONE;
+ return (0);
+ }
+ fcp->isp_loopstate = LOOP_SCANNING_LOOP;
+
+ isp_prt(isp, ISP_LOGDEBUG0, "scanning local loop 0..%d", hival);
+
+
+ /*
+ * make sure the temp port database is clean...
+ */
+ MEMZERO((void *)fcp->tport, sizeof (fcp->tport));
+
+ /*
+ * 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).
+ */
+ for (lim = loopid = 0; loopid < hival; loopid++) {
+ lp = &fcp->tport[loopid];
+
+ /*
+ * Don't even try for ourselves...
+ */
+ if (loopid == fcp->isp_loopid) {
+ continue;
+ }
+
+ if (IS_2100(isp) || IS_2200(isp)) {
+ lp->node_wwn = isp_get_portname(isp, loopid, 1);
+ if (fcp->isp_loopstate < LOOP_SCANNING_LOOP) {
+ return (-1);
+ }
+ if (lp->node_wwn == 0) {
+ continue;
+ }
+ }
+
+ /*
+ * 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 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;
+ }
+ isp_prt(isp, ISP_LOGWARN,
+ "giving up on synchronizing the port database");
+ return (-1);
+ }
+
+ /*
+ * Save the pertinent info locally.
+ */
+ lp->node_wwn =
+ (((uint64_t)pdb.pdb_nodename[0]) << 56) |
+ (((uint64_t)pdb.pdb_nodename[1]) << 48) |
+ (((uint64_t)pdb.pdb_nodename[2]) << 40) |
+ (((uint64_t)pdb.pdb_nodename[3]) << 32) |
+ (((uint64_t)pdb.pdb_nodename[4]) << 24) |
+ (((uint64_t)pdb.pdb_nodename[5]) << 16) |
+ (((uint64_t)pdb.pdb_nodename[6]) << 8) |
+ (((uint64_t)pdb.pdb_nodename[7]));
+ lp->port_wwn =
+ (((uint64_t)pdb.pdb_portname[0]) << 56) |
+ (((uint64_t)pdb.pdb_portname[1]) << 48) |
+ (((uint64_t)pdb.pdb_portname[2]) << 40) |
+ (((uint64_t)pdb.pdb_portname[3]) << 32) |
+ (((uint64_t)pdb.pdb_portname[4]) << 24) |
+ (((uint64_t)pdb.pdb_portname[5]) << 16) |
+ (((uint64_t)pdb.pdb_portname[6]) << 8) |
+ (((uint64_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;
+ }
+ 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.
+ */
+ 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;
+ }
+
+ /*
+ * Skip ourselves.
+ */
+ if (loopid == fcp->isp_iid) {
+ 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.
+ */
+
+ for (i = 0; i < hival; i++) {
+ int j;
+ if (fcp->portdb[i].port_wwn == 0)
+ 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);
+ continue;
+ }
+
+ /*
+ * 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).
+ */
+ 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");
+ continue;
+ }
+ i = lim;
+ } else {
+ i = loopid;
+ }
+
+ /*
+ * 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.
+ */
+ (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;
+ }
+
+ /*
+ * 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);
+}
+
+
+static int
+isp_fabric_mbox_cmd(ispsoftc_t *isp, mbreg_t *mbp)
+{
+ /* the caller sets up the mailbox */
+ 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;
+ }
+ 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(ispsoftc_t *isp, int ftype)
+{
+ fcparam *fcp = isp->isp_param;
+ uint32_t portid, first_portid, last_portid;
+ int hicap, last_port_same;
+
+ if (fcp->isp_onfabric == 0) {
+ fcp->isp_loopstate = LOOP_FSCAN_DONE;
+ return (0);
+ }
+
+ isp_prt(isp, ISP_LOGDEBUG0, "scanning fabric (GA_NXT)");
+
+ FC_SCRATCH_ACQUIRE(isp);
+
+ /*
+ * 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;
+
+ 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;
+ uint8_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);
+ MEMZERO(&mbs, sizeof (mbs));
+ 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;
+ }
+ 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 *) ((uint8_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);
+ }
+ portid =
+ (((uint32_t) rs1->snscb_port_id[0]) << 16) |
+ (((uint32_t) rs1->snscb_port_id[1]) << 8) |
+ (((uint32_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.
+ */
+ MEMZERO(&lcl, sizeof (lcl));
+ lcl.port_type = rs1->snscb_port_type;
+ lcl.fc4_type = ftype;
+ lcl.portid = portid;
+ lcl.node_wwn =
+ (((uint64_t)rs1->snscb_nodename[0]) << 56) |
+ (((uint64_t)rs1->snscb_nodename[1]) << 48) |
+ (((uint64_t)rs1->snscb_nodename[2]) << 40) |
+ (((uint64_t)rs1->snscb_nodename[3]) << 32) |
+ (((uint64_t)rs1->snscb_nodename[4]) << 24) |
+ (((uint64_t)rs1->snscb_nodename[5]) << 16) |
+ (((uint64_t)rs1->snscb_nodename[6]) << 8) |
+ (((uint64_t)rs1->snscb_nodename[7]));
+ lcl.port_wwn =
+ (((uint64_t)rs1->snscb_portname[0]) << 56) |
+ (((uint64_t)rs1->snscb_portname[1]) << 48) |
+ (((uint64_t)rs1->snscb_portname[2]) << 40) |
+ (((uint64_t)rs1->snscb_portname[3]) << 32) |
+ (((uint64_t)rs1->snscb_portname[4]) << 24) |
+ (((uint64_t)rs1->snscb_portname[5]) << 16) |
+ (((uint64_t)rs1->snscb_portname[6]) << 8) |
+ (((uint64_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;
+ }
+ (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");
+ 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(ispsoftc_t *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);
+ }
+
+ isp_prt(isp, ISP_LOGDEBUG0, "scanning fabric (GID_FT)");
+
+ 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);
+ MEMZERO(&mbs, sizeof (mbs));
+ 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);
+
+ /*
+ * 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;
+ }
+ 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 *) ((uint8_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
+
+ i++;
+ MEMZERO(&lcl, sizeof (lcl));
+ lcl.fc4_type = ftype;
+ lcl.portid =
+ (((uint32_t) rs1->snscb_ports[i].portid[0]) << 16) |
+ (((uint32_t) rs1->snscb_ports[i].portid[1]) << 8) |
+ (((uint32_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);
+ MEMZERO(&mbs, sizeof (mbs));
+ 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
+ */
+ 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 *) ((uint8_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 (fcp->isp_loopstate != LOOP_SCANNING_FABRIC) {
+ FC_SCRATCH_RELEASE(isp);
+ return (-1);
+ }
+ continue;
+ }
+ lcl.port_wwn =
+ (((uint64_t)gs1->snscb_wwn[0]) << 56) |
+ (((uint64_t)gs1->snscb_wwn[1]) << 48) |
+ (((uint64_t)gs1->snscb_wwn[2]) << 40) |
+ (((uint64_t)gs1->snscb_wwn[3]) << 32) |
+ (((uint64_t)gs1->snscb_wwn[4]) << 24) |
+ (((uint64_t)gs1->snscb_wwn[5]) << 16) |
+ (((uint64_t)gs1->snscb_wwn[6]) << 8) |
+ (((uint64_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);
+ MEMZERO(&mbs, sizeof (mbs));
+ 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
+ */
+ 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 *) ((uint8_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);
+ }
+ continue;
+ }
+ lcl.node_wwn =
+ (((uint64_t)gs1->snscb_wwn[0]) << 56) |
+ (((uint64_t)gs1->snscb_wwn[1]) << 48) |
+ (((uint64_t)gs1->snscb_wwn[2]) << 40) |
+ (((uint64_t)gs1->snscb_wwn[3]) << 32) |
+ (((uint64_t)gs1->snscb_wwn[4]) << 24) |
+ (((uint64_t)gs1->snscb_wwn[5]) << 16) |
+ (((uint64_t)gs1->snscb_wwn[6]) << 8) |
+ (((uint64_t)gs1->snscb_wwn[7]));
+
+ /*
+ * The QLogic f/w is bouncing this with a parameter error.
+ */
+#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);
+ MEMZERO(&mbs, sizeof (mbs));
+ 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
+ */
+ 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_GFF_ID_RESP_SIZE);
+ fs0 = (sns_gff_id_rsp_t *) ((uint8_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);
+ return (-1);
+ }
+ } else {
+ int index = (ftype >> 3);
+ int bshft = (ftype & 0x7) * 4;
+ int fc4_fval =
+ (fs1->snscb_fc4_features[index] >> bshft) & 0xf;
+ if (fc4_fval & 0x1) {
+ lcl.roles |=
+ (SVC3_INI_ROLE >> SVC3_ROLE_SHIFT);
+ }
+ if (fc4_fval & 0x2) {
+ lcl.roles |=
+ (SVC3_TGT_ROLE >> SVC3_ROLE_SHIFT);
+ }
+ }
+#endif
+
+ /*
+ * If we really want to know what kind of port type this is,
+ * we have to run another CT command. Otherwise, we'll leave
+ * it as undefined.
+ *
+ lcl.port_type = 0;
+ */
+ if (rs1->snscb_ports[i].control & 0x80) {
+ lcl.last_fabric_dev = 1;
+ } else {
+ lcl.last_fabric_dev = 0;
+ }
+ (void) isp_async(isp, ISPASYNC_FABRIC_DEV, &lcl);
+
+ } while ((rs1->snscb_ports[i].control & 0x80) == 0 && i < NGENT-1);
+
+ /*
+ * If we're not at the last entry, our list isn't big enough.
+ */
+ if ((rs1->snscb_ports[i].control & 0x80) == 0) {
+ isp_prt(isp, ISP_LOGWARN, "fabric too big for scratch area");
+ }
+
+ FC_SCRATCH_RELEASE(isp);
+ fcp->isp_loopstate = LOOP_FSCAN_DONE;
+ return (0);
+}
+#endif
+
+static void
+isp_register_fc4_type(ispsoftc_t *isp)
+{
+ fcparam *fcp = isp->isp_param;
+ uint8_t local[SNS_RFT_ID_REQ_SIZE];
+ sns_screq_t *reqp = (sns_screq_t *) local;
+ mbreg_t mbs;
+
+ MEMZERO((void *) reqp, SNS_RFT_ID_REQ_SIZE);
+ reqp->snscb_rblen = SNS_RFT_ID_RESP_SIZE >> 1;
+ reqp->snscb_addr[RQRSP_ADDR0015] = DMA_WD0(fcp->isp_scdma + 0x100);
+ reqp->snscb_addr[RQRSP_ADDR1631] = DMA_WD1(fcp->isp_scdma + 0x100);
+ reqp->snscb_addr[RQRSP_ADDR3247] = DMA_WD2(fcp->isp_scdma + 0x100);
+ reqp->snscb_addr[RQRSP_ADDR4863] = DMA_WD3(fcp->isp_scdma + 0x100);
+ reqp->snscb_sblen = 22;
+ reqp->snscb_data[0] = SNS_RFT_ID;
+ reqp->snscb_data[4] = fcp->isp_portid & 0xffff;
+ reqp->snscb_data[5] = (fcp->isp_portid >> 16) & 0xff;
+ reqp->snscb_data[6] = (1 << FC4_SCSI);
+#if 0
+ reqp->snscb_data[6] |= (1 << FC4_IP); /* ISO/IEC 8802-2 LLC/SNAP */
+#endif
+ FC_SCRATCH_ACQUIRE(isp);
+ isp_put_sns_request(isp, reqp, (sns_screq_t *) fcp->isp_scratch);
+ MEMZERO(&mbs, sizeof (mbs));
+ mbs.param[0] = MBOX_SEND_SNS;
+ mbs.param[1] = SNS_RFT_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
+ */
+ mbs.param[6] = DMA_WD3(fcp->isp_scdma);
+ mbs.param[7] = DMA_WD2(fcp->isp_scdma);
+ MEMORYBARRIER(isp, SYNC_SFORDEV, 0, SNS_RFT_ID_REQ_SIZE);
+ isp_mboxcmd(isp, &mbs, MBLOGALL);
+ FC_SCRATCH_RELEASE(isp);
+ if (mbs.param[0] == MBOX_COMMAND_COMPLETE) {
+ isp_prt(isp, ISP_LOGDEBUG0, "Register FC4 types succeeded");
+ }
+}
+
+/*
+ * Start a command. Locking is assumed done in the caller.
+ */
+
+int
+isp_start(XS_T *xs)
+{
+ ispsoftc_t *isp;
+ uint16_t nxti, optr, handle;
+ uint8_t local[QENTRY_LEN];
+ ispreq_t *reqp, *qep;
+ int target, i;
+
+ XS_INITERR(xs);
+ isp = XS_ISP(xs);
+
+ /*
+ * Check to make sure we're supporting initiator role.
+ */
+ if ((isp->isp_role & ISP_ROLE_INITIATOR) == 0) {
+ XS_SETERR(xs, HBA_SELTIMEOUT);
+ return (CMD_COMPLETE);
+ }
+
+ /*
+ * Now make sure we're running.
+ */
+
+ if (isp->isp_state != ISP_RUNSTATE) {
+ isp_prt(isp, ISP_LOGERR, "Adapter not at RUNSTATE");
+ XS_SETERR(xs, HBA_BOTCH);
+ return (CMD_COMPLETE);
+ }
+
+ /*
+ * Check command CDB length, etc.. We really are limited to 16 bytes
+ * for Fibre Channel, but can do up to 44 bytes in parallel SCSI,
+ * but probably only if we're running fairly new firmware (we'll
+ * let the old f/w choke on an extended command queue entry).
+ */
+
+ if (XS_CDBLEN(xs) > (IS_FC(isp)? 16 : 44) || XS_CDBLEN(xs) == 0) {
+ isp_prt(isp, ISP_LOGERR,
+ "unsupported cdb length (%d, CDB[0]=0x%x)",
+ XS_CDBLEN(xs), XS_CDBP(xs)[0] & 0xff);
+ XS_SETERR(xs, HBA_BOTCH);
+ return (CMD_COMPLETE);
+ }
+
+ /*
+ * Check to see whether we have good firmware state still or
+ * need to refresh our port database for this target.
+ */
+ target = XS_TGT(xs);
+ if (IS_FC(isp)) {
+ fcparam *fcp = isp->isp_param;
+ struct lportdb *lp;
+#ifdef HANDLE_LOOPSTATE_IN_OUTER_LAYERS
+ if (fcp->isp_fwstate != FW_READY ||
+ fcp->isp_loopstate != LOOP_READY) {
+ return (CMD_RQLATER);
+ }
+
+ /*
+ * If we're not on a Fabric, we can't have a target
+ * above FL_PORT_ID-1.
+ *
+ * If we're on a fabric and *not* connected as an F-port,
+ * we can't have a target less than FC_SNS_ID+1. This
+ * keeps us from having to sort out the difference between
+ * local public loop devices and those which we might get
+ * from a switch's database.
+ */
+ if (fcp->isp_onfabric == 0) {
+ if (target >= FL_PORT_ID) {
+ XS_SETERR(xs, HBA_SELTIMEOUT);
+ return (CMD_COMPLETE);
+ }
+ } else {
+ if (target >= FL_PORT_ID && target <= FC_SNS_ID) {
+ XS_SETERR(xs, HBA_SELTIMEOUT);
+ return (CMD_COMPLETE);
+ }
+ /*
+ * We used to exclude having local loop ports
+ * at the same time that we have fabric ports.
+ * That is, we used to exclude having ports
+ * at < FL_PORT_ID if we're FL-port.
+ *
+ * That's wrong. The only thing that could be
+ * dicey is if the switch you're connected to
+ * has these local loop ports appear on the
+ * fabric and we somehow attach them twice.
+ */
+ }
+#else
+ /*
+ * Check for f/w being in ready state. If the f/w
+ * isn't in ready state, then we don't know our
+ * loop ID and the f/w hasn't completed logging
+ * into all targets on the loop. If this is the
+ * case, then bounce the command. We pretend this is
+ * a SELECTION TIMEOUT error if we've never gone to
+ * FW_READY state at all- in this case we may not
+ * be hooked to a loop at all and we shouldn't hang
+ * the machine for this. Otherwise, defer this command
+ * until later.
+ */
+ if (fcp->isp_fwstate != FW_READY) {
+ /*
+ * Give ourselves at most a 250ms delay.
+ */
+ if (isp_fclink_test(isp, 250000)) {
+ XS_SETERR(xs, HBA_SELTIMEOUT);
+ if (fcp->loop_seen_once) {
+ return (CMD_RQLATER);
+ } else {
+ return (CMD_COMPLETE);
+ }
+ }
+ }
+
+ /*
+ * If we're not on a Fabric, we can't have a target
+ * above FL_PORT_ID-1.
+ *
+ * If we're on a fabric and *not* connected as an F-port,
+ * we can't have a target less than FC_SNS_ID+1. This
+ * keeps us from having to sort out the difference between
+ * local public loop devices and those which we might get
+ * from a switch's database.
+ */
+ if (fcp->isp_onfabric == 0) {
+ if (target >= FL_PORT_ID) {
+ XS_SETERR(xs, HBA_SELTIMEOUT);
+ return (CMD_COMPLETE);
+ }
+ } else {
+ if (target >= FL_PORT_ID && target <= FC_SNS_ID) {
+ XS_SETERR(xs, HBA_SELTIMEOUT);
+ return (CMD_COMPLETE);
+ }
+ if (fcp->isp_topo != TOPO_F_PORT &&
+ target < FL_PORT_ID) {
+ XS_SETERR(xs, HBA_SELTIMEOUT);
+ return (CMD_COMPLETE);
+ }
+ }
+
+ /*
+ * If our loop state is such that we haven't yet received
+ * a "Port Database Changed" notification (after a LIP or
+ * a Loop Reset or firmware initialization), then defer
+ * sending commands for a little while, but only if we've
+ * seen a valid loop at one point (otherwise we can get
+ * stuck at initialization time).
+ */
+ if (fcp->isp_loopstate < LOOP_PDB_RCVD) {
+ XS_SETERR(xs, HBA_SELTIMEOUT);
+ if (fcp->loop_seen_once) {
+ return (CMD_RQLATER);
+ } else {
+ return (CMD_COMPLETE);
+ }
+ }
+
+ /*
+ * If we're in the middle of loop or fabric scanning
+ * or merging the port databases, retry this command later.
+ */
+ if (fcp->isp_loopstate == LOOP_SCANNING_FABRIC ||
+ fcp->isp_loopstate == LOOP_SCANNING_LOOP ||
+ fcp->isp_loopstate == LOOP_SYNCING_PDB) {
+ return (CMD_RQLATER);
+ }
+
+ /*
+ * If our loop state is now such that we've just now
+ * received a Port Database Change notification, then
+ * we have to go off and (re)scan the fabric. We back
+ * out and try again later if this doesn't work.
+ */
+ if (fcp->isp_loopstate == LOOP_PDB_RCVD && fcp->isp_onfabric) {
+ if (isp_scan_fabric(isp, FC4_SCSI)) {
+ return (CMD_RQLATER);
+ }
+ if (fcp->isp_fwstate != FW_READY ||
+ fcp->isp_loopstate < LOOP_FSCAN_DONE) {
+ return (CMD_RQLATER);
+ }
+ }
+
+ /*
+ * If our loop state is now such that we've just now
+ * received a Port Database Change notification, then
+ * we have to go off and (re)synchronize our port
+ * database.
+ */
+ if (fcp->isp_loopstate < LOOP_READY) {
+ if (isp_pdb_sync(isp)) {
+ return (CMD_RQLATER);
+ }
+ if (fcp->isp_fwstate != FW_READY ||
+ fcp->isp_loopstate != LOOP_READY) {
+ return (CMD_RQLATER);
+ }
+ }
+
+ /*
+ * XXX: Here's were we would cancel any loop_dead flag
+ * XXX: also cancel in dead_loop timeout that's running
+ */
+#endif
+
+ /*
+ * Now check whether we should even think about pursuing this.
+ */
+ lp = &fcp->portdb[target];
+ if (lp->valid == 0) {
+ XS_SETERR(xs, HBA_SELTIMEOUT);
+ return (CMD_COMPLETE);
+ }
+ if ((lp->roles & (SVC3_TGT_ROLE >> SVC3_ROLE_SHIFT)) == 0) {
+ isp_prt(isp, ISP_LOGDEBUG2,
+ "Target %d does not have target service", target);
+ XS_SETERR(xs, HBA_SELTIMEOUT);
+ return (CMD_COMPLETE);
+ }
+ /*
+ * Now turn target into what the actual Loop ID is.
+ */
+ target = lp->loopid;
+ }
+
+ /*
+ * Next check to see if any HBA or Device
+ * parameters need to be updated.
+ */
+ if (isp->isp_update != 0) {
+ isp_update(isp);
+ }
+
+ if (isp_getrqentry(isp, &nxti, &optr, (void *)&qep)) {
+ isp_prt(isp, ISP_LOGDEBUG0, "Request Queue Overflow");
+ XS_SETERR(xs, HBA_BOTCH);
+ return (CMD_EAGAIN);
+ }
+
+ /*
+ * Now see if we need to synchronize the ISP with respect to anything.
+ * We do dual duty here (cough) for synchronizing for busses other
+ * than which we got here to send a command to.
+ */
+ reqp = (ispreq_t *) local;
+ if (isp->isp_sendmarker) {
+ uint8_t n = (IS_DUALBUS(isp)? 2: 1);
+ /*
+ * Check ports to send markers for...
+ */
+ for (i = 0; i < n; i++) {
+ if ((isp->isp_sendmarker & (1 << i)) == 0) {
+ continue;
+ }
+ MEMZERO((void *) reqp, QENTRY_LEN);
+ reqp->req_header.rqs_entry_count = 1;
+ reqp->req_header.rqs_entry_type = RQSTYPE_MARKER;
+ reqp->req_modifier = SYNC_ALL;
+ reqp->req_target = i << 7; /* insert bus number */
+ isp_put_request(isp, reqp, qep);
+ ISP_ADD_REQUEST(isp, nxti);
+ isp->isp_sendmarker &= ~(1 << i);
+ if (isp_getrqentry(isp, &nxti, &optr, (void *) &qep)) {
+ isp_prt(isp, ISP_LOGDEBUG0,
+ "Request Queue Overflow+");
+ XS_SETERR(xs, HBA_BOTCH);
+ return (CMD_EAGAIN);
+ }
+ }
+ }
+
+ MEMZERO((void *)reqp, QENTRY_LEN);
+ reqp->req_header.rqs_entry_count = 1;
+ if (IS_FC(isp)) {
+ reqp->req_header.rqs_entry_type = RQSTYPE_T2RQS;
+ } else {
+ if (XS_CDBLEN(xs) > 12)
+ reqp->req_header.rqs_entry_type = RQSTYPE_CMDONLY;
+ else
+ reqp->req_header.rqs_entry_type = RQSTYPE_REQUEST;
+ }
+ /* reqp->req_header.rqs_flags = 0; */
+ /* reqp->req_header.rqs_seqno = 0; */
+ if (IS_FC(isp)) {
+ /*
+ * See comment in isp_intr
+ */
+ /* XS_RESID(xs) = 0; */
+
+ /*
+ * Fibre Channel always requires some kind of tag.
+ * The Qlogic drivers seem be happy not to use a tag,
+ * but this breaks for some devices (IBM drives).
+ */
+ if (XS_TAG_P(xs)) {
+ ((ispreqt2_t *)reqp)->req_flags = XS_TAG_TYPE(xs);
+ } else {
+ /*
+ * If we don't know what tag to use, use HEAD OF QUEUE
+ * for Request Sense or Simple.
+ */
+ if (XS_CDBP(xs)[0] == 0x3) /* REQUEST SENSE */
+ ((ispreqt2_t *)reqp)->req_flags = REQFLAG_HTAG;
+ else
+ ((ispreqt2_t *)reqp)->req_flags = REQFLAG_STAG;
+ }
+ } else {
+ sdparam *sdp = (sdparam *)isp->isp_param;
+ sdp += XS_CHANNEL(xs);
+ if ((sdp->isp_devparam[target].actv_flags & DPARM_TQING) &&
+ XS_TAG_P(xs)) {
+ reqp->req_flags = XS_TAG_TYPE(xs);
+ }
+ }
+ if (IS_SCSI(isp)) {
+ reqp->req_target = target | (XS_CHANNEL(xs) << 7);
+ reqp->req_lun_trn = XS_LUN(xs);
+ reqp->req_cdblen = XS_CDBLEN(xs);
+ } else if (IS_2KLOGIN(isp)) {
+ ((ispreqt2e_t *)reqp)->req_target = target;
+ ((ispreqt2e_t *)reqp)->req_scclun = XS_LUN(xs);
+ } else if (FCPARAM(isp)->isp_fwattr & ISP_FW_ATTR_SCCLUN) {
+ ((ispreqt2_t *)reqp)->req_target = target;
+ ((ispreqt2_t *)reqp)->req_scclun = XS_LUN(xs);
+ } else {
+ ((ispreqt2_t *)reqp)->req_target = target;
+ ((ispreqt2_t *)reqp)->req_lun_trn = XS_LUN(xs);
+ }
+ MEMCPY(reqp->req_cdb, XS_CDBP(xs), XS_CDBLEN(xs));
+
+ reqp->req_time = XS_TIME(xs) / 1000;
+ if (reqp->req_time == 0 && XS_TIME(xs)) {
+ reqp->req_time = 1;
+ }
+
+ if (isp_save_xs(isp, xs, &handle)) {
+ isp_prt(isp, ISP_LOGDEBUG0, "out of xflist pointers");
+ XS_SETERR(xs, HBA_BOTCH);
+ return (CMD_EAGAIN);
+ }
+ reqp->req_handle = handle;
+
+ /*
+ * Set up DMA and/or do any bus swizzling of the request entry
+ * so that the Qlogic F/W understands what is being asked of it.
+ */
+ i = ISP_DMASETUP(isp, xs, reqp, &nxti, optr);
+ if (i != CMD_QUEUED) {
+ isp_destroy_handle(isp, handle);
+ /*
+ * dmasetup sets actual error in packet, and
+ * return what we were given to return.
+ */
+ return (i);
+ }
+ XS_SETERR(xs, HBA_NOERROR);
+ isp_prt(isp, ISP_LOGDEBUG2,
+ "START cmd for %d.%d.%d cmd 0x%x datalen %ld",
+ XS_CHANNEL(xs), XS_TGT(xs), XS_LUN(xs), XS_CDBP(xs)[0],
+ (long) XS_XFRLEN(xs));
+ ISP_ADD_REQUEST(isp, nxti);
+ isp->isp_nactive++;
+ return (CMD_QUEUED);
+}
+
+/*
+ * isp control
+ * Locks (ints blocked) assumed held.
+ */
+
+int
+isp_control(ispsoftc_t *isp, ispctl_t ctl, void *arg)
+{
+ XS_T *xs;
+ mbreg_t mbs;
+ int bus, tgt;
+ uint16_t handle;
+
+ MEMZERO(&mbs, sizeof (mbs));
+
+ switch (ctl) {
+ default:
+ isp_prt(isp, ISP_LOGERR, "Unknown Control Opcode 0x%x", ctl);
+ break;
+
+ case ISPCTL_RESET_BUS:
+ /*
+ * Issue a bus reset.
+ */
+ mbs.param[0] = MBOX_BUS_RESET;
+ if (IS_SCSI(isp)) {
+ mbs.param[1] =
+ ((sdparam *) isp->isp_param)->isp_bus_reset_delay;
+ if (mbs.param[1] < 2) {
+ mbs.param[1] = 2;
+ }
+ bus = *((int *) arg);
+ if (IS_DUALBUS(isp)) {
+ mbs.param[2] = bus;
+ }
+ } else {
+ mbs.param[1] = 10;
+ bus = 0;
+ }
+ isp->isp_sendmarker |= (1 << bus);
+ isp_mboxcmd(isp, &mbs, MBLOGALL);
+ if (mbs.param[0] != MBOX_COMMAND_COMPLETE) {
+ break;
+ }
+ isp_prt(isp, ISP_LOGINFO,
+ "driver initiated bus reset of bus %d", bus);
+ return (0);
+
+ case ISPCTL_RESET_DEV:
+ tgt = (*((int *) arg)) & 0xffff;
+ bus = (*((int *) arg)) >> 16;
+ mbs.param[0] = MBOX_ABORT_TARGET;
+ if (IS_SCSI(isp)) {
+ mbs.param[1] = (tgt << 8) | (bus << 15);
+ } else {
+ if (IS_2KLOGIN(isp)) {
+ mbs.param[1] = tgt;
+ mbs.ibits |= (1 << 10);
+ } else {
+ mbs.param[1] = (tgt << 8);
+ }
+ }
+ mbs.param[2] = 3; /* 'delay', in seconds */
+ isp_mboxcmd(isp, &mbs, MBLOGALL);
+ if (mbs.param[0] != MBOX_COMMAND_COMPLETE) {
+ break;
+ }
+ isp_prt(isp, ISP_LOGINFO,
+ "Target %d on Bus %d Reset Succeeded", tgt, bus);
+ isp->isp_sendmarker |= (1 << bus);
+ return (0);
+
+ case ISPCTL_ABORT_CMD:
+ xs = (XS_T *) arg;
+ tgt = XS_TGT(xs);
+ handle = isp_find_handle(isp, xs);
+ if (handle == 0) {
+ isp_prt(isp, ISP_LOGWARN,
+ "cannot find handle for command to abort");
+ break;
+ }
+ bus = XS_CHANNEL(xs);
+ mbs.param[0] = MBOX_ABORT;
+ if (IS_FC(isp)) {
+ if (FCPARAM(isp)->isp_fwattr & ISP_FW_ATTR_SCCLUN) {
+ if (IS_2KLOGIN(isp)) {
+ mbs.param[1] = tgt;
+ } else {
+ mbs.param[1] = tgt << 8;
+ }
+ mbs.param[6] = XS_LUN(xs);
+ } else {
+ mbs.param[1] = tgt << 8 | XS_LUN(xs);
+ }
+ } else {
+ mbs.param[1] =
+ (bus << 15) | (XS_TGT(xs) << 8) | XS_LUN(xs);
+ }
+ mbs.param[2] = handle;
+ isp_mboxcmd(isp, &mbs, MBLOGALL & ~MBOX_COMMAND_ERROR);
+ if (mbs.param[0] == MBOX_COMMAND_COMPLETE) {
+ return (0);
+ }
+ /*
+ * XXX: Look for command in the REQUEST QUEUE. That is,
+ * XXX: It hasen't been picked up by firmware yet.
+ */
+ break;
+
+ case ISPCTL_UPDATE_PARAMS:
+
+ isp_update(isp);
+ return (0);
+
+ case ISPCTL_FCLINK_TEST:
+
+ if (IS_FC(isp)) {
+ int usdelay = *((int *) arg);
+ if (usdelay == 0) {
+ usdelay = 250000;
+ }
+ return (isp_fclink_test(isp, usdelay));
+ }
+ break;
+
+ case ISPCTL_SCAN_FABRIC:
+
+ if (IS_FC(isp)) {
+ int ftype = (arg)? *((int *) arg) : FC4_SCSI;
+ return (isp_scan_fabric(isp, ftype));
+ }
+ break;
+
+ case ISPCTL_SCAN_LOOP:
+
+ if (IS_FC(isp)) {
+ return (isp_scan_loop(isp));
+ }
+ break;
+
+ case ISPCTL_PDB_SYNC:
+
+ if (IS_FC(isp)) {
+ return (isp_pdb_sync(isp));
+ }
+ break;
+
+ case ISPCTL_SEND_LIP:
+
+ if (IS_FC(isp)) {
+ mbs.param[0] = MBOX_INIT_LIP;
+ if (IS_2KLOGIN(isp)) {
+ mbs.ibits |= (1 << 10);
+ }
+ isp_mboxcmd(isp, &mbs, MBLOGALL);
+ if (mbs.param[0] == MBOX_COMMAND_COMPLETE) {
+ return (0);
+ }
+ }
+ break;
+
+ case ISPCTL_GET_POSMAP:
+
+ if (IS_FC(isp) && arg) {
+ return (isp_getmap(isp, arg));
+ }
+ break;
+
+
+ case ISPCTL_GET_PDB:
+ if (IS_FC(isp) && arg) {
+ int id = *((int *)arg);
+ isp_pdb_t *pdb = arg;
+ return (isp_getpdb(isp, id, pdb));
+ }
+ break;
+
+ case ISPCTL_RUN_MBOXCMD:
+
+ isp_mboxcmd(isp, arg, MBLOGALL);
+ return(0);
+
+#ifdef ISP_TARGET_MODE
+ case ISPCTL_TOGGLE_TMODE:
+ {
+
+ /*
+ * We don't check/set against role here- that's the
+ * responsibility for the outer layer to coordinate.
+ */
+ if (IS_SCSI(isp)) {
+ int param = *(int *)arg;
+ mbs.param[0] = MBOX_ENABLE_TARGET_MODE;
+ mbs.param[1] = param & 0xffff;
+ mbs.param[2] = param >> 16;
+ isp_mboxcmd(isp, &mbs, MBLOGALL);
+ if (mbs.param[0] != MBOX_COMMAND_COMPLETE) {
+ break;
+ }
+ }
+ return (0);
+ }
+#endif
+ }
+ return (-1);
+}
+
+/*
+ * Interrupt Service Routine(s).
+ *
+ * External (OS) framework has done the appropriate locking,
+ * and the locking will be held throughout this function.
+ */
+
+/*
+ * Limit our stack depth by sticking with the max likely number
+ * of completions on a request queue at any one time.
+ */
+#ifndef MAX_REQUESTQ_COMPLETIONS
+#define MAX_REQUESTQ_COMPLETIONS 64
+#endif
+
+void
+isp_intr(ispsoftc_t *isp, uint16_t isr, uint16_t sema, uint16_t mbox)
+{
+ XS_T *complist[MAX_REQUESTQ_COMPLETIONS], *xs;
+ uint16_t iptr, optr, junk;
+ int i, nlooked = 0, ndone = 0;
+
+again:
+ /*
+ * Is this a mailbox related interrupt?
+ * The mailbox semaphore will be nonzero if so.
+ */
+ if (sema) {
+ if (mbox & 0x4000) {
+ isp->isp_intmboxc++;
+ if (isp->isp_mboxbsy) {
+ int i = 0, obits = isp->isp_obits;
+ isp->isp_mboxtmp[i++] = mbox;
+ for (i = 1; i < MAX_MAILBOX(isp); i++) {
+ if ((obits & (1 << i)) == 0) {
+ continue;
+ }
+ isp->isp_mboxtmp[i] =
+ ISP_READ(isp, MBOX_OFF(i));
+ }
+ if (isp->isp_mbxwrk0) {
+ if (isp_mbox_continue(isp) == 0) {
+ return;
+ }
+ }
+ MBOX_NOTIFY_COMPLETE(isp);
+ } else {
+ isp_prt(isp, ISP_LOGWARN,
+ "Mbox Command Async (0x%x) with no waiters",
+ mbox);
+ }
+ } else if (isp_parse_async(isp, mbox) < 0) {
+ return;
+ }
+ if ((IS_FC(isp) && mbox != ASYNC_RIO_RESP) ||
+ isp->isp_state != ISP_RUNSTATE) {
+ ISP_WRITE(isp, HCCR, HCCR_CMD_CLEAR_RISC_INT);
+ ISP_WRITE(isp, BIU_SEMA, 0);
+ return;
+ }
+ }
+
+ /*
+ * We can't be getting this now.
+ */
+ if (isp->isp_state != ISP_RUNSTATE) {
+ isp_prt(isp, ISP_LOGWARN,
+ "interrupt (ISR=%x SEMA=%x) when not ready", isr, sema);
+ /*
+ * Thank you very much! *Burrrp*!
+ */
+ WRITE_RESPONSE_QUEUE_OUT_POINTER(isp,
+ READ_RESPONSE_QUEUE_IN_POINTER(isp));
+
+ ISP_WRITE(isp, HCCR, HCCR_CMD_CLEAR_RISC_INT);
+ ISP_WRITE(isp, BIU_SEMA, 0);
+ return;
+ }
+
+ /*
+ * Get the current Response Queue Out Pointer.
+ *
+ * If we're a 2300, we can ask what hardware what it thinks.
+ */
+ if (IS_23XX(isp)) {
+ optr = ISP_READ(isp, isp->isp_respoutrp);
+ /*
+ * Debug: to be taken out eventually
+ */
+ if (isp->isp_residx != optr) {
+ isp_prt(isp, ISP_LOGWARN, "optr %x soft optr %x",
+ optr, isp->isp_residx);
+ }
+ } else {
+ optr = isp->isp_residx;
+ }
+
+ /*
+ * You *must* read the Response Queue In Pointer
+ * prior to clearing the RISC interrupt.
+ *
+ * Debounce the 2300 if revision less than 2.
+ */
+ if (IS_2100(isp) || (IS_2300(isp) && isp->isp_revision < 2)) {
+ i = 0;
+ do {
+ iptr = READ_RESPONSE_QUEUE_IN_POINTER(isp);
+ junk = READ_RESPONSE_QUEUE_IN_POINTER(isp);
+ } while (junk != iptr && ++i < 1000);
+
+ if (iptr != junk) {
+ ISP_WRITE(isp, HCCR, HCCR_CMD_CLEAR_RISC_INT);
+ isp_prt(isp, ISP_LOGWARN,
+ "Response Queue Out Pointer Unstable (%x, %x)",
+ iptr, junk);
+ return;
+ }
+ } else {
+ iptr = READ_RESPONSE_QUEUE_IN_POINTER(isp);
+ }
+ isp->isp_resodx = iptr;
+
+
+ if (optr == iptr && sema == 0) {
+ /*
+ * There are a lot of these- reasons unknown- mostly on
+ * faster Alpha machines.
+ *
+ * I tried delaying after writing HCCR_CMD_CLEAR_RISC_INT to
+ * make sure the old interrupt went away (to avoid 'ringing'
+ * effects), but that didn't stop this from occurring.
+ */
+ if (IS_23XX(isp)) {
+ USEC_DELAY(100);
+ iptr = READ_RESPONSE_QUEUE_IN_POINTER(isp);
+ junk = ISP_READ(isp, BIU_R2HSTSLO);
+ } else {
+ junk = ISP_READ(isp, BIU_ISR);
+ }
+ if (optr == iptr) {
+ if (IS_23XX(isp)) {
+ ;
+ } else {
+ sema = ISP_READ(isp, BIU_SEMA);
+ mbox = ISP_READ(isp, OUTMAILBOX0);
+ if ((sema & 0x3) && (mbox & 0x8000)) {
+ goto again;
+ }
+ }
+ isp->isp_intbogus++;
+ isp_prt(isp, ISP_LOGDEBUG1,
+ "bogus intr- isr %x (%x) iptr %x optr %x",
+ isr, junk, iptr, optr);
+ }
+ }
+ isp->isp_resodx = iptr;
+ ISP_WRITE(isp, HCCR, HCCR_CMD_CLEAR_RISC_INT);
+ ISP_WRITE(isp, BIU_SEMA, 0);
+
+ if (isp->isp_rspbsy) {
+ return;
+ }
+ isp->isp_rspbsy = 1;
+
+ while (optr != iptr) {
+ ispstatusreq_t local, *sp = &local;
+ isphdr_t *hp;
+ int type;
+ uint16_t oop;
+ int buddaboom = 0;
+
+ hp = (isphdr_t *) ISP_QUEUE_ENTRY(isp->isp_result, optr);
+ oop = optr;
+ optr = ISP_NXT_QENTRY(optr, RESULT_QUEUE_LEN(isp));
+ nlooked++;
+ read_again:
+ /*
+ * Synchronize our view of this response queue entry.
+ */
+ MEMORYBARRIER(isp, SYNC_RESULT, oop, QENTRY_LEN);
+
+ type = isp_get_response_type(isp, hp);
+
+ if (type == RQSTYPE_RESPONSE) {
+ isp_get_response(isp, (ispstatusreq_t *) hp, sp);
+ } else if (type == RQSTYPE_RIO2) {
+ isp_rio2_t rio;
+ isp_get_rio2(isp, (isp_rio2_t *) hp, &rio);
+ for (i = 0; i < rio.req_header.rqs_seqno; i++) {
+ isp_fastpost_complete(isp, rio.req_handles[i]);
+ }
+ if (isp->isp_fpcchiwater < rio.req_header.rqs_seqno)
+ isp->isp_fpcchiwater = rio.req_header.rqs_seqno;
+ MEMZERO(hp, QENTRY_LEN); /* PERF */
+ continue;
+ } else {
+ /*
+ * Somebody reachable via isp_handle_other_response
+ * may have updated the response queue pointers for
+ * us, so we reload our goal index.
+ */
+ int i = isp_handle_other_response(isp, type, hp, &optr);
+ if (i < 0) {
+ goto read_again;
+ } else if (i > 0) {
+ iptr = isp->isp_resodx;
+ MEMZERO(hp, QENTRY_LEN); /* PERF */
+ continue;
+ }
+
+ /*
+ * After this point, we'll just look at the header as
+ * we don't know how to deal with the rest of the
+ * response.
+ */
+ isp_get_response(isp, (ispstatusreq_t *) hp, sp);
+
+ /*
+ * It really has to be a bounced request just copied
+ * from the request queue to the response queue. If
+ * not, something bad has happened.
+ */
+ if (sp->req_header.rqs_entry_type != RQSTYPE_REQUEST) {
+ isp_prt(isp, ISP_LOGERR, notresp,
+ sp->req_header.rqs_entry_type, oop, optr,
+ nlooked);
+ if (isp->isp_dblev & ISP_LOGDEBUG0) {
+ isp_print_bytes(isp, "Queue Entry",
+ QENTRY_LEN, sp);
+ }
+ MEMZERO(hp, QENTRY_LEN); /* PERF */
+ continue;
+ }
+ buddaboom = 1;
+ }
+
+ if (sp->req_header.rqs_flags & 0xf) {
+#define _RQS_OFLAGS \
+ ~(RQSFLAG_CONTINUATION|RQSFLAG_FULL|RQSFLAG_BADHEADER|RQSFLAG_BADPACKET)
+ if (sp->req_header.rqs_flags & RQSFLAG_CONTINUATION) {
+ isp_prt(isp, ISP_LOGWARN,
+ "continuation segment");
+ WRITE_RESPONSE_QUEUE_OUT_POINTER(isp, optr);
+ continue;
+ }
+ if (sp->req_header.rqs_flags & RQSFLAG_FULL) {
+ isp_prt(isp, ISP_LOGDEBUG1,
+ "internal queues full");
+ /*
+ * We'll synthesize a QUEUE FULL message below.
+ */
+ }
+ if (sp->req_header.rqs_flags & RQSFLAG_BADHEADER) {
+ isp_prt(isp, ISP_LOGERR, "bad header flag");
+ buddaboom++;
+ }
+ if (sp->req_header.rqs_flags & RQSFLAG_BADPACKET) {
+ isp_prt(isp, ISP_LOGERR, "bad request packet");
+ buddaboom++;
+ }
+ if (sp->req_header.rqs_flags & _RQS_OFLAGS) {
+ isp_prt(isp, ISP_LOGERR,
+ "unknown flags (0x%x) in response",
+ sp->req_header.rqs_flags);
+ buddaboom++;
+ }
+#undef _RQS_OFLAGS
+ }
+ if (sp->req_handle > isp->isp_maxcmds || sp->req_handle < 1) {
+ MEMZERO(hp, QENTRY_LEN); /* PERF */
+ isp_prt(isp, ISP_LOGERR,
+ "bad request handle %d (type 0x%x, flags 0x%x)",
+ sp->req_handle, sp->req_header.rqs_entry_type,
+ sp->req_header.rqs_flags);
+ WRITE_RESPONSE_QUEUE_OUT_POINTER(isp, optr);
+ continue;
+ }
+ xs = isp_find_xs(isp, sp->req_handle);
+ if (xs == NULL) {
+ uint8_t ts = sp->req_completion_status & 0xff;
+ MEMZERO(hp, QENTRY_LEN); /* PERF */
+ /*
+ * Only whine if this isn't the expected fallout of
+ * aborting the command.
+ */
+ if (sp->req_header.rqs_entry_type != RQSTYPE_RESPONSE) {
+ isp_prt(isp, ISP_LOGERR,
+ "cannot find handle 0x%x (type 0x%x)",
+ sp->req_handle,
+ sp->req_header.rqs_entry_type);
+ } else if (ts != RQCS_ABORTED) {
+ isp_prt(isp, ISP_LOGERR,
+ "cannot find handle 0x%x (status 0x%x)",
+ sp->req_handle, ts);
+ }
+ WRITE_RESPONSE_QUEUE_OUT_POINTER(isp, optr);
+ continue;
+ }
+ isp_destroy_handle(isp, sp->req_handle);
+ if (sp->req_status_flags & RQSTF_BUS_RESET) {
+ XS_SETERR(xs, HBA_BUSRESET);
+ isp->isp_sendmarker |= (1 << XS_CHANNEL(xs));
+ }
+ if (buddaboom) {
+ XS_SETERR(xs, HBA_BOTCH);
+ }
+
+ if (IS_FC(isp) && (sp->req_scsi_status & RQCS_SV)) {
+ /*
+ * Fibre Channel F/W doesn't say we got status
+ * if there's Sense Data instead. I guess they
+ * think it goes w/o saying.
+ */
+ sp->req_state_flags |= RQSF_GOT_STATUS;
+ }
+ if (sp->req_state_flags & RQSF_GOT_STATUS) {
+ *XS_STSP(xs) = sp->req_scsi_status & 0xff;
+ }
+
+ switch (sp->req_header.rqs_entry_type) {
+ case RQSTYPE_RESPONSE:
+ XS_SET_STATE_STAT(isp, xs, sp);
+ isp_parse_status(isp, sp, xs);
+ if ((XS_NOERR(xs) || XS_ERR(xs) == HBA_NOERROR) &&
+ (*XS_STSP(xs) == SCSI_BUSY)) {
+ XS_SETERR(xs, HBA_TGTBSY);
+ }
+ if (IS_SCSI(isp)) {
+ XS_RESID(xs) = sp->req_resid;
+ if ((sp->req_state_flags & RQSF_GOT_STATUS) &&
+ (*XS_STSP(xs) == SCSI_CHECK) &&
+ (sp->req_state_flags & RQSF_GOT_SENSE)) {
+ XS_SAVE_SENSE(xs, sp);
+ }
+ /*
+ * A new synchronous rate was negotiated for
+ * this target. Mark state such that we'll go
+ * look up that which has changed later.
+ */
+ if (sp->req_status_flags & RQSTF_NEGOTIATION) {
+ int t = XS_TGT(xs);
+ sdparam *sdp = isp->isp_param;
+ sdp += XS_CHANNEL(xs);
+ sdp->isp_devparam[t].dev_refresh = 1;
+ isp->isp_update |=
+ (1 << XS_CHANNEL(xs));
+ }
+ } else {
+ if (sp->req_status_flags & RQSF_XFER_COMPLETE) {
+ XS_RESID(xs) = 0;
+ } else if (sp->req_scsi_status & RQCS_RESID) {
+ XS_RESID(xs) = sp->req_resid;
+ } else {
+ XS_RESID(xs) = 0;
+ }
+ if ((sp->req_state_flags & RQSF_GOT_STATUS) &&
+ (*XS_STSP(xs) == SCSI_CHECK) &&
+ (sp->req_scsi_status & RQCS_SV)) {
+ XS_SAVE_SENSE(xs, sp);
+ /* solely for the benefit of debug */
+ sp->req_state_flags |= RQSF_GOT_SENSE;
+ }
+ }
+ isp_prt(isp, ISP_LOGDEBUG2,
+ "asked for %ld got raw resid %ld settled for %ld",
+ (long) XS_XFRLEN(xs), (long) sp->req_resid,
+ (long) XS_RESID(xs));
+ break;
+ case RQSTYPE_REQUEST:
+ if (sp->req_header.rqs_flags & RQSFLAG_FULL) {
+ /*
+ * Force Queue Full status.
+ */
+ *XS_STSP(xs) = SCSI_QFULL;
+ XS_SETERR(xs, HBA_NOERROR);
+ } else if (XS_NOERR(xs)) {
+ /*
+ * ????
+ */
+ isp_prt(isp, ISP_LOGDEBUG0,
+ "Request Queue Entry bounced back");
+ XS_SETERR(xs, HBA_BOTCH);
+ }
+ XS_RESID(xs) = XS_XFRLEN(xs);
+ break;
+ default:
+ isp_prt(isp, ISP_LOGWARN,
+ "unhandled response queue type 0x%x",
+ sp->req_header.rqs_entry_type);
+ if (XS_NOERR(xs)) {
+ XS_SETERR(xs, HBA_BOTCH);
+ }
+ break;
+ }
+
+ /*
+ * Free any DMA resources. As a side effect, this may
+ * also do any cache flushing necessary for data coherence. */
+ if (XS_XFRLEN(xs)) {
+ ISP_DMAFREE(isp, xs, sp->req_handle);
+ }
+
+ if (((isp->isp_dblev & (ISP_LOGDEBUG2|ISP_LOGDEBUG3))) ||
+ ((isp->isp_dblev & ISP_LOGDEBUG1) && ((!XS_NOERR(xs)) ||
+ (*XS_STSP(xs) != SCSI_GOOD)))) {
+ char skey;
+ if (sp->req_state_flags & RQSF_GOT_SENSE) {
+ skey = XS_SNSKEY(xs) & 0xf;
+ if (skey < 10)
+ skey += '0';
+ else
+ skey += 'a' - 10;
+ } else if (*XS_STSP(xs) == SCSI_CHECK) {
+ skey = '?';
+ } else {
+ skey = '.';
+ }
+ isp_prt(isp, ISP_LOGALL, finmsg, XS_CHANNEL(xs),
+ XS_TGT(xs), XS_LUN(xs), XS_XFRLEN(xs), XS_RESID(xs),
+ *XS_STSP(xs), skey, XS_ERR(xs));
+ }
+
+ if (isp->isp_nactive > 0)
+ isp->isp_nactive--;
+ complist[ndone++] = xs; /* defer completion call until later */
+ MEMZERO(hp, QENTRY_LEN); /* PERF */
+ if (ndone == MAX_REQUESTQ_COMPLETIONS) {
+ break;
+ }
+ }
+
+ /*
+ * If we looked at any commands, then it's valid to find out
+ * what the outpointer is. It also is a trigger to update the
+ * ISP's notion of what we've seen so far.
+ */
+ if (nlooked) {
+ WRITE_RESPONSE_QUEUE_OUT_POINTER(isp, optr);
+ /*
+ * While we're at it, read the requst queue out pointer.
+ */
+ isp->isp_reqodx = READ_REQUEST_QUEUE_OUT_POINTER(isp);
+ if (isp->isp_rscchiwater < ndone)
+ isp->isp_rscchiwater = ndone;
+ }
+
+ isp->isp_residx = optr;
+ isp->isp_rspbsy = 0;
+ for (i = 0; i < ndone; i++) {
+ xs = complist[i];
+ if (xs) {
+ isp->isp_rsltccmplt++;
+ isp_done(xs);
+ }
+ }
+}
+
+/*
+ * Support routines.
+ */
+
+static int
+isp_parse_async(ispsoftc_t *isp, uint16_t mbox)
+{
+ int rval = 0;
+ int bus;
+
+ if (IS_DUALBUS(isp)) {
+ bus = ISP_READ(isp, OUTMAILBOX6);
+ } else {
+ bus = 0;
+ }
+ isp_prt(isp, ISP_LOGDEBUG2, "Async Mbox 0x%x", mbox);
+
+ switch (mbox) {
+ case ASYNC_BUS_RESET:
+ isp->isp_sendmarker |= (1 << bus);
+#ifdef ISP_TARGET_MODE
+ if (isp_target_async(isp, bus, mbox))
+ rval = -1;
+#endif
+ isp_async(isp, ISPASYNC_BUS_RESET, &bus);
+ break;
+ case ASYNC_SYSTEM_ERROR:
+ isp->isp_state = ISP_CRASHED;
+ if (IS_FC(isp)) {
+ FCPARAM(isp)->isp_loopstate = LOOP_NIL;
+ FCPARAM(isp)->isp_fwstate = FW_CONFIG_WAIT;
+ }
+ /*
+ * Were we waiting for a mailbox command to complete?
+ * If so, it's dead, so wake up the waiter.
+ */
+ if (isp->isp_mboxbsy) {
+ isp->isp_obits = 1;
+ isp->isp_mboxtmp[0] = MBOX_HOST_INTERFACE_ERROR;
+ MBOX_NOTIFY_COMPLETE(isp);
+ }
+#ifdef ISP_FW_CRASH_DUMP
+ /*
+ * If we have crash dumps enabled, it's up to the handler
+ * for isp_async to reinit stuff and restart the firmware
+ * after performing the crash dump. The reason we do things
+ * this way is that we may need to activate a kernel thread
+ * to do all the crash dump goop.
+ */
+ isp_async(isp, ISPASYNC_FW_CRASH, NULL);
+#else
+ isp_async(isp, ISPASYNC_FW_CRASH, NULL);
+ isp_reinit(isp);
+ isp_async(isp, ISPASYNC_FW_RESTARTED, NULL);
+#endif
+ rval = -1;
+ break;
+
+ case ASYNC_RQS_XFER_ERR:
+ isp_prt(isp, ISP_LOGERR, "Request Queue Transfer Error");
+ break;
+
+ case ASYNC_RSP_XFER_ERR:
+ isp_prt(isp, ISP_LOGERR, "Response Queue Transfer Error");
+ break;
+
+ case ASYNC_QWAKEUP:
+ /*
+ * We've just been notified that the Queue has woken up.
+ * We don't need to be chatty about this- just unlatch things
+ * and move on.
+ */
+ mbox = READ_REQUEST_QUEUE_OUT_POINTER(isp);
+ break;
+
+ case ASYNC_TIMEOUT_RESET:
+ isp_prt(isp, ISP_LOGWARN,
+ "timeout initiated SCSI bus reset of bus %d", bus);
+ isp->isp_sendmarker |= (1 << bus);
+#ifdef ISP_TARGET_MODE
+ if (isp_target_async(isp, bus, mbox))
+ rval = -1;
+#endif
+ break;
+
+ case ASYNC_DEVICE_RESET:
+ isp_prt(isp, ISP_LOGINFO, "device reset on bus %d", bus);
+ isp->isp_sendmarker |= (1 << bus);
+#ifdef ISP_TARGET_MODE
+ if (isp_target_async(isp, bus, mbox))
+ rval = -1;
+#endif
+ break;
+
+ case ASYNC_EXTMSG_UNDERRUN:
+ isp_prt(isp, ISP_LOGWARN, "extended message underrun");
+ break;
+
+ case ASYNC_SCAM_INT:
+ isp_prt(isp, ISP_LOGINFO, "SCAM interrupt");
+ break;
+
+ case ASYNC_HUNG_SCSI:
+ isp_prt(isp, ISP_LOGERR,
+ "stalled SCSI Bus after DATA Overrun");
+ /* XXX: Need to issue SCSI reset at this point */
+ break;
+
+ case ASYNC_KILLED_BUS:
+ isp_prt(isp, ISP_LOGERR, "SCSI Bus reset after DATA Overrun");
+ break;
+
+ case ASYNC_BUS_TRANSIT:
+ mbox = ISP_READ(isp, OUTMAILBOX2);
+ switch (mbox & 0x1c00) {
+ case SXP_PINS_LVD_MODE:
+ isp_prt(isp, ISP_LOGINFO, "Transition to LVD mode");
+ SDPARAM(isp)->isp_diffmode = 0;
+ SDPARAM(isp)->isp_ultramode = 0;
+ SDPARAM(isp)->isp_lvdmode = 1;
+ break;
+ case SXP_PINS_HVD_MODE:
+ isp_prt(isp, ISP_LOGINFO,
+ "Transition to Differential mode");
+ SDPARAM(isp)->isp_diffmode = 1;
+ SDPARAM(isp)->isp_ultramode = 0;
+ SDPARAM(isp)->isp_lvdmode = 0;
+ break;
+ case SXP_PINS_SE_MODE:
+ isp_prt(isp, ISP_LOGINFO,
+ "Transition to Single Ended mode");
+ SDPARAM(isp)->isp_diffmode = 0;
+ SDPARAM(isp)->isp_ultramode = 1;
+ SDPARAM(isp)->isp_lvdmode = 0;
+ break;
+ default:
+ isp_prt(isp, ISP_LOGWARN,
+ "Transition to Unknown Mode 0x%x", mbox);
+ break;
+ }
+ /*
+ * XXX: Set up to renegotiate again!
+ */
+ /* Can only be for a 1080... */
+ isp->isp_sendmarker |= (1 << bus);
+ break;
+
+ /*
+ * We can use bus, which will always be zero for FC cards,
+ * as a mailbox pattern accumulator to be checked below.
+ */
+ case ASYNC_RIO5:
+ bus = 0x1ce; /* outgoing mailbox regs 1-3, 6-7 */
+ break;
+
+ case ASYNC_RIO4:
+ bus = 0x14e; /* outgoing mailbox regs 1-3, 6 */
+ break;
+
+ case ASYNC_RIO3:
+ bus = 0x10e; /* outgoing mailbox regs 1-3 */
+ break;
+
+ case ASYNC_RIO2:
+ bus = 0x106; /* outgoing mailbox regs 1-2 */
+ break;
+
+ case ASYNC_RIO1:
+ case ASYNC_CMD_CMPLT:
+ bus = 0x102; /* outgoing mailbox regs 1 */
+ break;
+
+ case ASYNC_RIO_RESP:
+ return (rval);
+
+ case ASYNC_CTIO_DONE:
+ {
+#ifdef ISP_TARGET_MODE
+ int handle =
+ (ISP_READ(isp, OUTMAILBOX2) << 16) |
+ (ISP_READ(isp, OUTMAILBOX1));
+ if (isp_target_async(isp, handle, mbox)) {
+ rval = -1;
+ } else {
+ /* count it as a fast posting intr */
+ isp->isp_fphccmplt++;
+ }
+#else
+ isp_prt(isp, ISP_LOGINFO, "Fast Posting CTIO done");
+ isp->isp_fphccmplt++; /* count it as a fast posting intr */
+#endif
+ break;
+ }
+ case ASYNC_LIP_F8:
+ case ASYNC_LIP_OCCURRED:
+ FCPARAM(isp)->isp_fwstate = FW_CONFIG_WAIT;
+ FCPARAM(isp)->isp_loopstate = LOOP_LIP_RCVD;
+ isp->isp_sendmarker = 1;
+ isp_mark_getpdb_all(isp);
+ isp_async(isp, ISPASYNC_LIP, NULL);
+#ifdef ISP_TARGET_MODE
+ if (isp_target_async(isp, bus, mbox))
+ rval = -1;
+#endif
+ /*
+ * We've had problems with data corruption occuring on
+ * commands that complete (with no apparent error) after
+ * we receive a LIP. This has been observed mostly on
+ * Local Loop topologies. To be safe, let's just mark
+ * all active commands as dead.
+ */
+ if (FCPARAM(isp)->isp_topo == TOPO_NL_PORT ||
+ FCPARAM(isp)->isp_topo == TOPO_FL_PORT) {
+ int i, j;
+ for (i = j = 0; i < isp->isp_maxcmds; i++) {
+ XS_T *xs;
+ xs = isp->isp_xflist[i];
+ if (xs != NULL) {
+ j++;
+ XS_SETERR(xs, HBA_BUSRESET);
+ }
+ }
+ if (j) {
+ isp_prt(isp, ISP_LOGERR,
+ "LIP destroyed %d active commands", j);
+ }
+ }
+ break;
+
+ case ASYNC_LOOP_UP:
+ isp->isp_sendmarker = 1;
+ FCPARAM(isp)->isp_fwstate = FW_CONFIG_WAIT;
+ FCPARAM(isp)->isp_loopstate = LOOP_LIP_RCVD;
+ isp_mark_getpdb_all(isp);
+ isp_async(isp, ISPASYNC_LOOP_UP, NULL);
+#ifdef ISP_TARGET_MODE
+ if (isp_target_async(isp, bus, mbox)) {
+ rval = -1;
+ }
+#endif
+ break;
+
+ case ASYNC_LOOP_DOWN:
+ isp->isp_sendmarker = 1;
+ FCPARAM(isp)->isp_fwstate = FW_CONFIG_WAIT;
+ FCPARAM(isp)->isp_loopstate = LOOP_NIL;
+ isp_mark_getpdb_all(isp);
+ isp_async(isp, ISPASYNC_LOOP_DOWN, NULL);
+#ifdef ISP_TARGET_MODE
+ if (isp_target_async(isp, bus, mbox)) {
+ rval = -1;
+ }
+#endif
+ break;
+
+ case ASYNC_LOOP_RESET:
+ isp->isp_sendmarker = 1;
+ FCPARAM(isp)->isp_fwstate = FW_CONFIG_WAIT;
+ FCPARAM(isp)->isp_loopstate = LOOP_NIL;
+ isp_mark_getpdb_all(isp);
+ isp_async(isp, ISPASYNC_LOOP_RESET, NULL);
+#ifdef ISP_TARGET_MODE
+ if (isp_target_async(isp, bus, mbox)) {
+ rval = -1;
+ }
+#endif
+ break;
+
+ case ASYNC_PDB_CHANGED:
+ isp->isp_sendmarker = 1;
+ FCPARAM(isp)->isp_loopstate = LOOP_PDB_RCVD;
+ isp_mark_getpdb_all(isp);
+ isp_async(isp, ISPASYNC_CHANGE_NOTIFY, ISPASYNC_CHANGE_PDB);
+ break;
+
+ case ASYNC_CHANGE_NOTIFY:
+ /*
+ * Not correct, but it will force us to rescan the loop.
+ */
+ FCPARAM(isp)->isp_loopstate = LOOP_PDB_RCVD;
+ isp_mark_getpdb_all(isp);
+ isp_async(isp, ISPASYNC_CHANGE_NOTIFY, ISPASYNC_CHANGE_SNS);
+ break;
+
+ case ASYNC_PTPMODE:
+ if (FCPARAM(isp)->isp_onfabric)
+ FCPARAM(isp)->isp_topo = TOPO_F_PORT;
+ else
+ FCPARAM(isp)->isp_topo = TOPO_N_PORT;
+ isp_mark_getpdb_all(isp);
+ isp->isp_sendmarker = 1;
+ FCPARAM(isp)->isp_fwstate = FW_CONFIG_WAIT;
+ FCPARAM(isp)->isp_loopstate = LOOP_LIP_RCVD;
+ isp_async(isp, ISPASYNC_CHANGE_NOTIFY, ISPASYNC_CHANGE_OTHER);
+#ifdef ISP_TARGET_MODE
+ if (isp_target_async(isp, bus, mbox))
+ rval = -1;
+#endif
+ isp_prt(isp, ISP_LOGINFO, "Point-to-Point mode");
+ break;
+
+ case ASYNC_CONNMODE:
+ mbox = ISP_READ(isp, OUTMAILBOX1);
+ isp_mark_getpdb_all(isp);
+ switch (mbox) {
+ case ISP_CONN_LOOP:
+ isp_prt(isp, ISP_LOGINFO,
+ "Point-to-Point -> Loop mode");
+ break;
+ case ISP_CONN_PTP:
+ isp_prt(isp, ISP_LOGINFO,
+ "Loop -> Point-to-Point mode");
+ break;
+ case ISP_CONN_BADLIP:
+ isp_prt(isp, ISP_LOGWARN,
+ "Point-to-Point -> Loop mode (BAD LIP)");
+ break;
+ case ISP_CONN_FATAL:
+ isp_prt(isp, ISP_LOGERR, "FATAL CONNECTION ERROR");
+#ifdef ISP_FW_CRASH_DUMP
+ isp_async(isp, ISPASYNC_FW_CRASH, NULL);
+#else
+ isp_async(isp, ISPASYNC_FW_CRASH, NULL);
+ isp_reinit(isp);
+ isp_async(isp, ISPASYNC_FW_RESTARTED, NULL);
+#endif
+ return (-1);
+ case ISP_CONN_LOOPBACK:
+ isp_prt(isp, ISP_LOGWARN,
+ "Looped Back in Point-to-Point mode");
+ break;
+ default:
+ isp_prt(isp, ISP_LOGWARN,
+ "Unknown connection mode (0x%x)", mbox);
+ break;
+ }
+ isp_async(isp, ISPASYNC_CHANGE_NOTIFY, ISPASYNC_CHANGE_OTHER);
+ isp->isp_sendmarker = 1;
+ FCPARAM(isp)->isp_fwstate = FW_CONFIG_WAIT;
+ FCPARAM(isp)->isp_loopstate = LOOP_LIP_RCVD;
+ break;
+
+ default:
+ isp_prt(isp, ISP_LOGWARN, "Unknown Async Code 0x%x", mbox);
+ break;
+ }
+
+ if (bus & 0x100) {
+ int i, nh;
+ uint16_t handles[16];
+
+ for (nh = 0, i = 1; i < MAX_MAILBOX(isp); i++) {
+ if ((bus & (1 << i)) == 0) {
+ continue;
+ }
+ handles[nh++] = ISP_READ(isp, MBOX_OFF(i));
+ }
+ for (i = 0; i < nh; i++) {
+ isp_fastpost_complete(isp, handles[i]);
+ isp_prt(isp, ISP_LOGDEBUG3,
+ "fast post completion of %u", handles[i]);
+ }
+ if (isp->isp_fpcchiwater < nh)
+ isp->isp_fpcchiwater = nh;
+ } else {
+ isp->isp_intoasync++;
+ }
+ return (rval);
+}
+
+/*
+ * Handle other response entries. A pointer to the request queue output
+ * index is here in case we want to eat several entries at once, although
+ * this is not used currently.
+ */
+
+static int
+isp_handle_other_response(ispsoftc_t *isp, int type,
+ isphdr_t *hp, uint16_t *optrp)
+{
+ switch (type) {
+ case RQSTYPE_STATUS_CONT:
+ isp_prt(isp, ISP_LOGINFO, "Ignored Continuation Response");
+ return (1);
+ case RQSTYPE_ATIO:
+ case RQSTYPE_CTIO:
+ case RQSTYPE_ENABLE_LUN:
+ case RQSTYPE_MODIFY_LUN:
+ case RQSTYPE_NOTIFY:
+ case RQSTYPE_NOTIFY_ACK:
+ case RQSTYPE_CTIO1:
+ case RQSTYPE_ATIO2:
+ case RQSTYPE_CTIO2:
+ case RQSTYPE_CTIO3:
+ isp->isp_rsltccmplt++; /* count as a response completion */
+#ifdef ISP_TARGET_MODE
+ if (isp_target_notify(isp, (ispstatusreq_t *) hp, optrp)) {
+ return (1);
+ }
+#endif
+ /* FALLTHROUGH */
+ case RQSTYPE_REQUEST:
+ default:
+ USEC_DELAY(100);
+ if (type != isp_get_response_type(isp, hp)) {
+ /*
+ * This is questionable- we're just papering over
+ * something we've seen on SMP linux in target
+ * mode- we don't really know what's happening
+ * here that causes us to think we've gotten
+ * an entry, but that either the entry isn't
+ * filled out yet or our CPU read data is stale.
+ */
+ isp_prt(isp, ISP_LOGINFO,
+ "unstable type in response queue");
+ return (-1);
+ }
+ isp_prt(isp, ISP_LOGWARN, "Unhandled Response Type 0x%x",
+ isp_get_response_type(isp, hp));
+ if (isp_async(isp, ISPASYNC_UNHANDLED_RESPONSE, hp)) {
+ return (1);
+ }
+ return (0);
+ }
+}
+
+static void
+isp_parse_status(ispsoftc_t *isp, ispstatusreq_t *sp, XS_T *xs)
+{
+ switch (sp->req_completion_status & 0xff) {
+ case RQCS_COMPLETE:
+ if (XS_NOERR(xs)) {
+ XS_SETERR(xs, HBA_NOERROR);
+ }
+ return;
+
+ case RQCS_INCOMPLETE:
+ if ((sp->req_state_flags & RQSF_GOT_TARGET) == 0) {
+ isp_prt(isp, ISP_LOGDEBUG1,
+ "Selection Timeout for %d.%d.%d",
+ XS_CHANNEL(xs), XS_TGT(xs), XS_LUN(xs));
+ if (XS_NOERR(xs)) {
+ XS_SETERR(xs, HBA_SELTIMEOUT);
+ }
+ return;
+ }
+ isp_prt(isp, ISP_LOGERR,
+ "command incomplete for %d.%d.%d, state 0x%x",
+ XS_CHANNEL(xs), XS_TGT(xs), XS_LUN(xs),
+ sp->req_state_flags);
+ break;
+
+ case RQCS_DMA_ERROR:
+ isp_prt(isp, ISP_LOGERR, "DMA error for command on %d.%d.%d",
+ XS_CHANNEL(xs), XS_TGT(xs), XS_LUN(xs));
+ break;
+
+ case RQCS_TRANSPORT_ERROR:
+ {
+ char buf[172];
+ SNPRINTF(buf, sizeof (buf), "states=>");
+ if (sp->req_state_flags & RQSF_GOT_BUS) {
+ SNPRINTF(buf, sizeof (buf), "%s GOT_BUS", buf);
+ }
+ if (sp->req_state_flags & RQSF_GOT_TARGET) {
+ SNPRINTF(buf, sizeof (buf), "%s GOT_TGT", buf);
+ }
+ if (sp->req_state_flags & RQSF_SENT_CDB) {
+ SNPRINTF(buf, sizeof (buf), "%s SENT_CDB", buf);
+ }
+ if (sp->req_state_flags & RQSF_XFRD_DATA) {
+ SNPRINTF(buf, sizeof (buf), "%s XFRD_DATA", buf);
+ }
+ if (sp->req_state_flags & RQSF_GOT_STATUS) {
+ SNPRINTF(buf, sizeof (buf), "%s GOT_STS", buf);
+ }
+ if (sp->req_state_flags & RQSF_GOT_SENSE) {
+ SNPRINTF(buf, sizeof (buf), "%s GOT_SNS", buf);
+ }
+ if (sp->req_state_flags & RQSF_XFER_COMPLETE) {
+ SNPRINTF(buf, sizeof (buf), "%s XFR_CMPLT", buf);
+ }
+ SNPRINTF(buf, sizeof (buf), "%s\nstatus=>", buf);
+ if (sp->req_status_flags & RQSTF_DISCONNECT) {
+ SNPRINTF(buf, sizeof (buf), "%s Disconnect", buf);
+ }
+ if (sp->req_status_flags & RQSTF_SYNCHRONOUS) {
+ SNPRINTF(buf, sizeof (buf), "%s Sync_xfr", buf);
+ }
+ if (sp->req_status_flags & RQSTF_PARITY_ERROR) {
+ SNPRINTF(buf, sizeof (buf), "%s Parity", buf);
+ }
+ if (sp->req_status_flags & RQSTF_BUS_RESET) {
+ SNPRINTF(buf, sizeof (buf), "%s Bus_Reset", buf);
+ }
+ if (sp->req_status_flags & RQSTF_DEVICE_RESET) {
+ SNPRINTF(buf, sizeof (buf), "%s Device_Reset", buf);
+ }
+ if (sp->req_status_flags & RQSTF_ABORTED) {
+ SNPRINTF(buf, sizeof (buf), "%s Aborted", buf);
+ }
+ if (sp->req_status_flags & RQSTF_TIMEOUT) {
+ SNPRINTF(buf, sizeof (buf), "%s Timeout", buf);
+ }
+ if (sp->req_status_flags & RQSTF_NEGOTIATION) {
+ SNPRINTF(buf, sizeof (buf), "%s Negotiation", buf);
+ }
+ isp_prt(isp, ISP_LOGERR, "%s", buf);
+ isp_prt(isp, ISP_LOGERR, "transport error for %d.%d.%d:\n%s",
+ XS_CHANNEL(xs), XS_TGT(xs), XS_LUN(xs), buf);
+ break;
+ }
+ case RQCS_RESET_OCCURRED:
+ isp_prt(isp, ISP_LOGWARN,
+ "bus reset destroyed command for %d.%d.%d",
+ XS_CHANNEL(xs), XS_TGT(xs), XS_LUN(xs));
+ isp->isp_sendmarker |= (1 << XS_CHANNEL(xs));
+ if (XS_NOERR(xs)) {
+ XS_SETERR(xs, HBA_BUSRESET);
+ }
+ return;
+
+ case RQCS_ABORTED:
+ isp_prt(isp, ISP_LOGERR, "command aborted for %d.%d.%d",
+ XS_CHANNEL(xs), XS_TGT(xs), XS_LUN(xs));
+ isp->isp_sendmarker |= (1 << XS_CHANNEL(xs));
+ if (XS_NOERR(xs)) {
+ XS_SETERR(xs, HBA_ABORTED);
+ }
+ return;
+
+ case RQCS_TIMEOUT:
+ isp_prt(isp, ISP_LOGWARN, "command timed out for %d.%d.%d",
+ XS_CHANNEL(xs), XS_TGT(xs), XS_LUN(xs));
+ /*
+ * Check to see if we logged out the device.
+ */
+ if (IS_FC(isp)) {
+ if ((sp->req_completion_status & RQSTF_LOGOUT) &&
+ FCPARAM(isp)->portdb[XS_TGT(xs)].valid &&
+ FCPARAM(isp)->portdb[XS_TGT(xs)].fabric_dev) {
+ FCPARAM(isp)->portdb[XS_TGT(xs)].relogin = 1;
+ }
+ }
+ if (XS_NOERR(xs)) {
+ XS_SETERR(xs, HBA_CMDTIMEOUT);
+ }
+ return;
+
+ case RQCS_DATA_OVERRUN:
+ XS_RESID(xs) = sp->req_resid;
+ isp_prt(isp, ISP_LOGERR, "data overrun for command on %d.%d.%d",
+ XS_CHANNEL(xs), XS_TGT(xs), XS_LUN(xs));
+ if (XS_NOERR(xs)) {
+ XS_SETERR(xs, HBA_DATAOVR);
+ }
+ return;
+
+ case RQCS_COMMAND_OVERRUN:
+ isp_prt(isp, ISP_LOGERR,
+ "command overrun for command on %d.%d.%d",
+ XS_CHANNEL(xs), XS_TGT(xs), XS_LUN(xs));
+ break;
+
+ case RQCS_STATUS_OVERRUN:
+ isp_prt(isp, ISP_LOGERR,
+ "status overrun for command on %d.%d.%d",
+ XS_CHANNEL(xs), XS_TGT(xs), XS_LUN(xs));
+ break;
+
+ case RQCS_BAD_MESSAGE:
+ isp_prt(isp, ISP_LOGERR,
+ "msg not COMMAND COMPLETE after status %d.%d.%d",
+ XS_CHANNEL(xs), XS_TGT(xs), XS_LUN(xs));
+ break;
+
+ case RQCS_NO_MESSAGE_OUT:
+ isp_prt(isp, ISP_LOGERR,
+ "No MESSAGE OUT phase after selection on %d.%d.%d",
+ XS_CHANNEL(xs), XS_TGT(xs), XS_LUN(xs));
+ break;
+
+ case RQCS_EXT_ID_FAILED:
+ isp_prt(isp, ISP_LOGERR, "EXTENDED IDENTIFY failed %d.%d.%d",
+ XS_CHANNEL(xs), XS_TGT(xs), XS_LUN(xs));
+ break;
+
+ case RQCS_IDE_MSG_FAILED:
+ isp_prt(isp, ISP_LOGERR,
+ "INITIATOR DETECTED ERROR rejected by %d.%d.%d",
+ XS_CHANNEL(xs), XS_TGT(xs), XS_LUN(xs));
+ break;
+
+ case RQCS_ABORT_MSG_FAILED:
+ isp_prt(isp, ISP_LOGERR, "ABORT OPERATION rejected by %d.%d.%d",
+ XS_CHANNEL(xs), XS_TGT(xs), XS_LUN(xs));
+ break;
+
+ case RQCS_REJECT_MSG_FAILED:
+ isp_prt(isp, ISP_LOGERR, "MESSAGE REJECT rejected by %d.%d.%d",
+ XS_CHANNEL(xs), XS_TGT(xs), XS_LUN(xs));
+ break;
+
+ case RQCS_NOP_MSG_FAILED:
+ isp_prt(isp, ISP_LOGERR, "NOP rejected by %d.%d.%d",
+ XS_CHANNEL(xs), XS_TGT(xs), XS_LUN(xs));
+ break;
+
+ case RQCS_PARITY_ERROR_MSG_FAILED:
+ isp_prt(isp, ISP_LOGERR,
+ "MESSAGE PARITY ERROR rejected by %d.%d.%d",
+ XS_CHANNEL(xs), XS_TGT(xs), XS_LUN(xs));
+ break;
+
+ case RQCS_DEVICE_RESET_MSG_FAILED:
+ isp_prt(isp, ISP_LOGWARN,
+ "BUS DEVICE RESET rejected by %d.%d.%d",
+ XS_CHANNEL(xs), XS_TGT(xs), XS_LUN(xs));
+ break;
+
+ case RQCS_ID_MSG_FAILED:
+ isp_prt(isp, ISP_LOGERR, "IDENTIFY rejected by %d.%d.%d",
+ XS_CHANNEL(xs), XS_TGT(xs), XS_LUN(xs));
+ break;
+
+ case RQCS_UNEXP_BUS_FREE:
+ isp_prt(isp, ISP_LOGERR, "%d.%d.%d had an unexpected bus free",
+ XS_CHANNEL(xs), XS_TGT(xs), XS_LUN(xs));
+ break;
+
+ case RQCS_DATA_UNDERRUN:
+ {
+ if (IS_FC(isp)) {
+ int ru_marked = (sp->req_scsi_status & RQCS_RU) != 0;
+ if (!ru_marked || sp->req_resid > XS_XFRLEN(xs)) {
+ isp_prt(isp, ISP_LOGWARN, bun, XS_TGT(xs),
+ XS_LUN(xs), XS_XFRLEN(xs), sp->req_resid,
+ (ru_marked)? "marked" : "not marked");
+ if (XS_NOERR(xs)) {
+ XS_SETERR(xs, HBA_BOTCH);
+ }
+ return;
+ }
+ }
+ XS_RESID(xs) = sp->req_resid;
+ if (XS_NOERR(xs)) {
+ XS_SETERR(xs, HBA_NOERROR);
+ }
+ return;
+ }
+
+ case RQCS_XACT_ERR1:
+ isp_prt(isp, ISP_LOGERR, xact1, XS_CHANNEL(xs),
+ XS_TGT(xs), XS_LUN(xs));
+ break;
+
+ case RQCS_XACT_ERR2:
+ isp_prt(isp, ISP_LOGERR, xact2,
+ XS_LUN(xs), XS_TGT(xs), XS_CHANNEL(xs));
+ break;
+
+ case RQCS_XACT_ERR3:
+ isp_prt(isp, ISP_LOGERR, xact3,
+ XS_CHANNEL(xs), XS_TGT(xs), XS_LUN(xs));
+ break;
+
+ case RQCS_BAD_ENTRY:
+ isp_prt(isp, ISP_LOGERR, "Invalid IOCB entry type detected");
+ break;
+
+ case RQCS_QUEUE_FULL:
+ isp_prt(isp, ISP_LOGDEBUG0,
+ "internal queues full for %d.%d.%d status 0x%x",
+ XS_CHANNEL(xs), XS_TGT(xs), XS_LUN(xs), *XS_STSP(xs));
+
+ /*
+ * If QFULL or some other status byte is set, then this
+ * isn't an error, per se.
+ *
+ * Unfortunately, some QLogic f/w writers have, in
+ * some cases, ommitted to *set* status to QFULL.
+ *
+
+ if (*XS_STSP(xs) != SCSI_GOOD && XS_NOERR(xs)) {
+ XS_SETERR(xs, HBA_NOERROR);
+ return;
+ }
+
+ *
+ *
+ */
+
+ *XS_STSP(xs) = SCSI_QFULL;
+ XS_SETERR(xs, HBA_NOERROR);
+ return;
+
+ case RQCS_PHASE_SKIPPED:
+ isp_prt(isp, ISP_LOGERR, pskip, XS_CHANNEL(xs),
+ XS_TGT(xs), XS_LUN(xs));
+ break;
+
+ case RQCS_ARQS_FAILED:
+ isp_prt(isp, ISP_LOGERR,
+ "Auto Request Sense failed for %d.%d.%d",
+ XS_CHANNEL(xs), XS_TGT(xs), XS_LUN(xs));
+ if (XS_NOERR(xs)) {
+ XS_SETERR(xs, HBA_ARQFAIL);
+ }
+ return;
+
+ case RQCS_WIDE_FAILED:
+ isp_prt(isp, ISP_LOGERR,
+ "Wide Negotiation failed for %d.%d.%d",
+ XS_TGT(xs), XS_LUN(xs), XS_CHANNEL(xs));
+ if (IS_SCSI(isp)) {
+ sdparam *sdp = isp->isp_param;
+ sdp += XS_CHANNEL(xs);
+ sdp->isp_devparam[XS_TGT(xs)].goal_flags &= ~DPARM_WIDE;
+ sdp->isp_devparam[XS_TGT(xs)].dev_update = 1;
+ isp->isp_update |= (1 << XS_CHANNEL(xs));
+ }
+ if (XS_NOERR(xs)) {
+ XS_SETERR(xs, HBA_NOERROR);
+ }
+ return;
+
+ case RQCS_SYNCXFER_FAILED:
+ isp_prt(isp, ISP_LOGERR,
+ "SDTR Message failed for target %d.%d.%d",
+ XS_TGT(xs), XS_LUN(xs), XS_CHANNEL(xs));
+ if (IS_SCSI(isp)) {
+ sdparam *sdp = isp->isp_param;
+ sdp += XS_CHANNEL(xs);
+ sdp->isp_devparam[XS_TGT(xs)].goal_flags &= ~DPARM_SYNC;
+ sdp->isp_devparam[XS_TGT(xs)].dev_update = 1;
+ isp->isp_update |= (1 << XS_CHANNEL(xs));
+ }
+ break;
+
+ case RQCS_LVD_BUSERR:
+ isp_prt(isp, ISP_LOGERR,
+ "Bad LVD condition while talking to %d.%d.%d",
+ XS_TGT(xs), XS_LUN(xs), XS_CHANNEL(xs));
+ break;
+
+ case RQCS_PORT_UNAVAILABLE:
+ /*
+ * No such port on the loop. Moral equivalent of SELTIMEO
+ */
+ case RQCS_PORT_LOGGED_OUT:
+ /*
+ * It was there (maybe)- treat as a selection timeout.
+ */
+ if ((sp->req_completion_status & 0xff) == RQCS_PORT_UNAVAILABLE)
+ isp_prt(isp, ISP_LOGINFO,
+ "port unavailable for target %d", XS_TGT(xs));
+ else
+ isp_prt(isp, ISP_LOGINFO,
+ "port logout for target %d", XS_TGT(xs));
+ /*
+ * If we're on a local loop, force a LIP (which is overkill)
+ * to force a re-login of this unit. If we're on fabric,
+ * then we'll have to relogin as a matter of course.
+ */
+ if (FCPARAM(isp)->isp_topo == TOPO_NL_PORT ||
+ FCPARAM(isp)->isp_topo == TOPO_FL_PORT) {
+ mbreg_t mbs;
+ MEMZERO(&mbs, sizeof (mbs));
+ mbs.param[0] = MBOX_INIT_LIP;
+ if (IS_2KLOGIN(isp)) {
+ mbs.ibits |= (1 << 10);
+ }
+ isp_mboxcmd_qnw(isp, &mbs, 1);
+ }
+
+ /*
+ * Probably overkill.
+ */
+ isp->isp_sendmarker = 1;
+ FCPARAM(isp)->isp_loopstate = LOOP_PDB_RCVD;
+ isp_mark_getpdb_all(isp);
+ isp_async(isp, ISPASYNC_CHANGE_NOTIFY, ISPASYNC_CHANGE_OTHER);
+ if (XS_NOERR(xs)) {
+ XS_SETERR(xs, HBA_SELTIMEOUT);
+ }
+ return;
+
+ case RQCS_PORT_CHANGED:
+ isp_prt(isp, ISP_LOGWARN,
+ "port changed for target %d", XS_TGT(xs));
+ if (XS_NOERR(xs)) {
+ XS_SETERR(xs, HBA_SELTIMEOUT);
+ }
+ return;
+
+ case RQCS_PORT_BUSY:
+ isp_prt(isp, ISP_LOGWARN,
+ "port busy for target %d", XS_TGT(xs));
+ if (XS_NOERR(xs)) {
+ XS_SETERR(xs, HBA_TGTBSY);
+ }
+ return;
+
+ default:
+ isp_prt(isp, ISP_LOGERR, "Unknown Completion Status 0x%x",
+ sp->req_completion_status);
+ break;
+ }
+ if (XS_NOERR(xs)) {
+ XS_SETERR(xs, HBA_BOTCH);
+ }
+}
+
+static void
+isp_fastpost_complete(ispsoftc_t *isp, uint16_t fph)
+{
+ XS_T *xs;
+
+ if (fph == 0) {
+ return;
+ }
+ xs = isp_find_xs(isp, fph);
+ if (xs == NULL) {
+ isp_prt(isp, ISP_LOGWARN,
+ "Command for fast post handle 0x%x not found", fph);
+ return;
+ }
+ isp_destroy_handle(isp, fph);
+
+ /*
+ * Since we don't have a result queue entry item,
+ * we must believe that SCSI status is zero and
+ * that all data transferred.
+ */
+ XS_SET_STATE_STAT(isp, xs, NULL);
+ XS_RESID(xs) = 0;
+ *XS_STSP(xs) = SCSI_GOOD;
+ if (XS_XFRLEN(xs)) {
+ ISP_DMAFREE(isp, xs, fph);
+ }
+ if (isp->isp_nactive)
+ isp->isp_nactive--;
+ isp->isp_fphccmplt++;
+ isp_done(xs);
+}
+
+static int
+isp_mbox_continue(ispsoftc_t *isp)
+{
+ mbreg_t mbs;
+ uint16_t *ptr;
+ uint32_t offset;
+
+ switch (isp->isp_lastmbxcmd) {
+ case MBOX_WRITE_RAM_WORD:
+ case MBOX_READ_RAM_WORD:
+ case MBOX_WRITE_RAM_WORD_EXTENDED:
+ case MBOX_READ_RAM_WORD_EXTENDED:
+ break;
+ default:
+ return (1);
+ }
+ if (isp->isp_mboxtmp[0] != MBOX_COMMAND_COMPLETE) {
+ isp->isp_mbxwrk0 = 0;
+ return (-1);
+ }
+
+ /*
+ * Clear the previous interrupt.
+ */
+ ISP_WRITE(isp, HCCR, HCCR_CMD_CLEAR_RISC_INT);
+ ISP_WRITE(isp, BIU_SEMA, 0);
+
+ /*
+ * Continue with next word.
+ */
+ MEMZERO(&mbs, sizeof (mbs));
+ ptr = isp->isp_mbxworkp;
+ switch (isp->isp_lastmbxcmd) {
+ case MBOX_WRITE_RAM_WORD:
+ mbs.param[1] = isp->isp_mbxwrk1++;;
+ mbs.param[2] = *ptr++;;
+ break;
+ case MBOX_READ_RAM_WORD:
+ *ptr++ = isp->isp_mboxtmp[2];
+ mbs.param[1] = isp->isp_mbxwrk1++;
+ break;
+ case MBOX_WRITE_RAM_WORD_EXTENDED:
+ offset = isp->isp_mbxwrk1;
+ offset |= ((uint32_t) isp->isp_mbxwrk8 << 16);
+
+ mbs.param[2] = *ptr++;;
+ mbs.param[1] = offset;
+ mbs.param[8] = offset >> 16;
+ isp->isp_mbxwrk1 = ++offset;
+ isp->isp_mbxwrk8 = offset >> 16;
+ break;
+ case MBOX_READ_RAM_WORD_EXTENDED:
+ offset = isp->isp_mbxwrk1;
+ offset |= ((uint32_t) isp->isp_mbxwrk8 << 16);
+
+ *ptr++ = isp->isp_mboxtmp[2];
+ mbs.param[1] = offset;
+ mbs.param[8] = offset >> 16;
+ isp->isp_mbxwrk1 = ++offset;
+ isp->isp_mbxwrk8 = offset >> 16;
+ break;
+ }
+ isp->isp_mbxworkp = ptr;
+ isp->isp_mbxwrk0--;
+ mbs.param[0] = isp->isp_lastmbxcmd;
+ isp_mboxcmd_qnw(isp, &mbs, 0);
+ return (0);
+}
+
+
+#define HIWRD(x) ((x) >> 16)
+#define LOWRD(x) ((x) & 0xffff)
+#define ISPOPMAP(a, b) (((a) << 16) | (b))
+static const uint32_t mbpscsi[] = {
+ ISPOPMAP(0x01, 0x01), /* 0x00: MBOX_NO_OP */
+ ISPOPMAP(0x1f, 0x01), /* 0x01: MBOX_LOAD_RAM */
+ ISPOPMAP(0x03, 0x01), /* 0x02: MBOX_EXEC_FIRMWARE */
+ ISPOPMAP(0x1f, 0x01), /* 0x03: MBOX_DUMP_RAM */
+ ISPOPMAP(0x07, 0x07), /* 0x04: MBOX_WRITE_RAM_WORD */
+ ISPOPMAP(0x03, 0x07), /* 0x05: MBOX_READ_RAM_WORD */
+ ISPOPMAP(0x3f, 0x3f), /* 0x06: MBOX_MAILBOX_REG_TEST */
+ ISPOPMAP(0x07, 0x07), /* 0x07: MBOX_VERIFY_CHECKSUM */
+ ISPOPMAP(0x01, 0x0f), /* 0x08: MBOX_ABOUT_FIRMWARE */
+ ISPOPMAP(0x00, 0x00), /* 0x09: */
+ ISPOPMAP(0x00, 0x00), /* 0x0a: */
+ ISPOPMAP(0x00, 0x00), /* 0x0b: */
+ ISPOPMAP(0x00, 0x00), /* 0x0c: */
+ ISPOPMAP(0x00, 0x00), /* 0x0d: */
+ ISPOPMAP(0x01, 0x05), /* 0x0e: MBOX_CHECK_FIRMWARE */
+ ISPOPMAP(0x00, 0x00), /* 0x0f: */
+ ISPOPMAP(0x1f, 0x1f), /* 0x10: MBOX_INIT_REQ_QUEUE */
+ ISPOPMAP(0x3f, 0x3f), /* 0x11: MBOX_INIT_RES_QUEUE */
+ ISPOPMAP(0x0f, 0x0f), /* 0x12: MBOX_EXECUTE_IOCB */
+ ISPOPMAP(0x03, 0x03), /* 0x13: MBOX_WAKE_UP */
+ ISPOPMAP(0x01, 0x3f), /* 0x14: MBOX_STOP_FIRMWARE */
+ ISPOPMAP(0x0f, 0x0f), /* 0x15: MBOX_ABORT */
+ ISPOPMAP(0x03, 0x03), /* 0x16: MBOX_ABORT_DEVICE */
+ ISPOPMAP(0x07, 0x07), /* 0x17: MBOX_ABORT_TARGET */
+ ISPOPMAP(0x07, 0x07), /* 0x18: MBOX_BUS_RESET */
+ ISPOPMAP(0x03, 0x07), /* 0x19: MBOX_STOP_QUEUE */
+ ISPOPMAP(0x03, 0x07), /* 0x1a: MBOX_START_QUEUE */
+ ISPOPMAP(0x03, 0x07), /* 0x1b: MBOX_SINGLE_STEP_QUEUE */
+ ISPOPMAP(0x03, 0x07), /* 0x1c: MBOX_ABORT_QUEUE */
+ ISPOPMAP(0x03, 0x4f), /* 0x1d: MBOX_GET_DEV_QUEUE_STATUS */
+ ISPOPMAP(0x00, 0x00), /* 0x1e: */
+ ISPOPMAP(0x01, 0x07), /* 0x1f: MBOX_GET_FIRMWARE_STATUS */
+ ISPOPMAP(0x01, 0x07), /* 0x20: MBOX_GET_INIT_SCSI_ID */
+ ISPOPMAP(0x01, 0x07), /* 0x21: MBOX_GET_SELECT_TIMEOUT */
+ ISPOPMAP(0x01, 0xc7), /* 0x22: MBOX_GET_RETRY_COUNT */
+ ISPOPMAP(0x01, 0x07), /* 0x23: MBOX_GET_TAG_AGE_LIMIT */
+ ISPOPMAP(0x01, 0x03), /* 0x24: MBOX_GET_CLOCK_RATE */
+ ISPOPMAP(0x01, 0x07), /* 0x25: MBOX_GET_ACT_NEG_STATE */
+ ISPOPMAP(0x01, 0x07), /* 0x26: MBOX_GET_ASYNC_DATA_SETUP_TIME */
+ ISPOPMAP(0x01, 0x07), /* 0x27: MBOX_GET_PCI_PARAMS */
+ ISPOPMAP(0x03, 0x4f), /* 0x28: MBOX_GET_TARGET_PARAMS */
+ ISPOPMAP(0x03, 0x0f), /* 0x29: MBOX_GET_DEV_QUEUE_PARAMS */
+ ISPOPMAP(0x01, 0x07), /* 0x2a: MBOX_GET_RESET_DELAY_PARAMS */
+ ISPOPMAP(0x00, 0x00), /* 0x2b: */
+ ISPOPMAP(0x00, 0x00), /* 0x2c: */
+ ISPOPMAP(0x00, 0x00), /* 0x2d: */
+ ISPOPMAP(0x00, 0x00), /* 0x2e: */
+ ISPOPMAP(0x00, 0x00), /* 0x2f: */
+ ISPOPMAP(0x03, 0x03), /* 0x30: MBOX_SET_INIT_SCSI_ID */
+ ISPOPMAP(0x07, 0x07), /* 0x31: MBOX_SET_SELECT_TIMEOUT */
+ ISPOPMAP(0xc7, 0xc7), /* 0x32: MBOX_SET_RETRY_COUNT */
+ ISPOPMAP(0x07, 0x07), /* 0x33: MBOX_SET_TAG_AGE_LIMIT */
+ ISPOPMAP(0x03, 0x03), /* 0x34: MBOX_SET_CLOCK_RATE */
+ ISPOPMAP(0x07, 0x07), /* 0x35: MBOX_SET_ACT_NEG_STATE */
+ ISPOPMAP(0x07, 0x07), /* 0x36: MBOX_SET_ASYNC_DATA_SETUP_TIME */
+ ISPOPMAP(0x07, 0x07), /* 0x37: MBOX_SET_PCI_CONTROL_PARAMS */
+ ISPOPMAP(0x4f, 0x4f), /* 0x38: MBOX_SET_TARGET_PARAMS */
+ ISPOPMAP(0x0f, 0x0f), /* 0x39: MBOX_SET_DEV_QUEUE_PARAMS */
+ ISPOPMAP(0x07, 0x07), /* 0x3a: MBOX_SET_RESET_DELAY_PARAMS */
+ ISPOPMAP(0x00, 0x00), /* 0x3b: */
+ ISPOPMAP(0x00, 0x00), /* 0x3c: */
+ ISPOPMAP(0x00, 0x00), /* 0x3d: */
+ ISPOPMAP(0x00, 0x00), /* 0x3e: */
+ ISPOPMAP(0x00, 0x00), /* 0x3f: */
+ ISPOPMAP(0x01, 0x03), /* 0x40: MBOX_RETURN_BIOS_BLOCK_ADDR */
+ ISPOPMAP(0x3f, 0x01), /* 0x41: MBOX_WRITE_FOUR_RAM_WORDS */
+ ISPOPMAP(0x03, 0x07), /* 0x42: MBOX_EXEC_BIOS_IOCB */
+ ISPOPMAP(0x00, 0x00), /* 0x43: */
+ ISPOPMAP(0x00, 0x00), /* 0x44: */
+ ISPOPMAP(0x03, 0x03), /* 0x45: SET SYSTEM PARAMETER */
+ ISPOPMAP(0x01, 0x03), /* 0x46: GET SYSTEM PARAMETER */
+ ISPOPMAP(0x00, 0x00), /* 0x47: */
+ ISPOPMAP(0x01, 0xcf), /* 0x48: GET SCAM CONFIGURATION */
+ ISPOPMAP(0xcf, 0xcf), /* 0x49: SET SCAM CONFIGURATION */
+ ISPOPMAP(0x03, 0x03), /* 0x4a: MBOX_SET_FIRMWARE_FEATURES */
+ ISPOPMAP(0x01, 0x03), /* 0x4b: MBOX_GET_FIRMWARE_FEATURES */
+ ISPOPMAP(0x00, 0x00), /* 0x4c: */
+ ISPOPMAP(0x00, 0x00), /* 0x4d: */
+ ISPOPMAP(0x00, 0x00), /* 0x4e: */
+ ISPOPMAP(0x00, 0x00), /* 0x4f: */
+ ISPOPMAP(0xdf, 0xdf), /* 0x50: LOAD RAM A64 */
+ ISPOPMAP(0xdf, 0xdf), /* 0x51: DUMP RAM A64 */
+ ISPOPMAP(0xdf, 0xff), /* 0x52: INITIALIZE REQUEST QUEUE A64 */
+ ISPOPMAP(0xef, 0xff), /* 0x53: INITIALIZE RESPONSE QUEUE A64 */
+ ISPOPMAP(0xcf, 0x01), /* 0x54: EXECUTE IOCB A64 */
+ ISPOPMAP(0x07, 0x01), /* 0x55: ENABLE TARGET MODE */
+ ISPOPMAP(0x03, 0x0f), /* 0x56: GET TARGET STATUS */
+ ISPOPMAP(0x00, 0x00), /* 0x57: */
+ ISPOPMAP(0x00, 0x00), /* 0x58: */
+ ISPOPMAP(0x00, 0x00), /* 0x59: */
+ ISPOPMAP(0x03, 0x03), /* 0x5a: SET DATA OVERRUN RECOVERY MODE */
+ ISPOPMAP(0x01, 0x03), /* 0x5b: GET DATA OVERRUN RECOVERY MODE */
+ ISPOPMAP(0x0f, 0x0f), /* 0x5c: SET HOST DATA */
+ ISPOPMAP(0x01, 0x01) /* 0x5d: GET NOST DATA */
+};
+
+#ifndef ISP_STRIPPED
+static char *scsi_mbcmd_names[] = {
+ "NO-OP",
+ "LOAD RAM",
+ "EXEC FIRMWARE",
+ "DUMP RAM",
+ "WRITE RAM WORD",
+ "READ RAM WORD",
+ "MAILBOX REG TEST",
+ "VERIFY CHECKSUM",
+ "ABOUT FIRMWARE",
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ "CHECK FIRMWARE",
+ NULL,
+ "INIT REQUEST QUEUE",
+ "INIT RESULT QUEUE",
+ "EXECUTE IOCB",
+ "WAKE UP",
+ "STOP FIRMWARE",
+ "ABORT",
+ "ABORT DEVICE",
+ "ABORT TARGET",
+ "BUS RESET",
+ "STOP QUEUE",
+ "START QUEUE",
+ "SINGLE STEP QUEUE",
+ "ABORT QUEUE",
+ "GET DEV QUEUE STATUS",
+ NULL,
+ "GET FIRMWARE STATUS",
+ "GET INIT SCSI ID",
+ "GET SELECT TIMEOUT",
+ "GET RETRY COUNT",
+ "GET TAG AGE LIMIT",
+ "GET CLOCK RATE",
+ "GET ACT NEG STATE",
+ "GET ASYNC DATA SETUP TIME",
+ "GET PCI PARAMS",
+ "GET TARGET PARAMS",
+ "GET DEV QUEUE PARAMS",
+ "GET RESET DELAY PARAMS",
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ "SET INIT SCSI ID",
+ "SET SELECT TIMEOUT",
+ "SET RETRY COUNT",
+ "SET TAG AGE LIMIT",
+ "SET CLOCK RATE",
+ "SET ACT NEG STATE",
+ "SET ASYNC DATA SETUP TIME",
+ "SET PCI CONTROL PARAMS",
+ "SET TARGET PARAMS",
+ "SET DEV QUEUE PARAMS",
+ "SET RESET DELAY PARAMS",
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ "RETURN BIOS BLOCK ADDR",
+ "WRITE FOUR RAM WORDS",
+ "EXEC BIOS IOCB",
+ NULL,
+ NULL,
+ "SET SYSTEM PARAMETER",
+ "GET SYSTEM PARAMETER",
+ NULL,
+ "GET SCAM CONFIGURATION",
+ "SET SCAM CONFIGURATION",
+ "SET FIRMWARE FEATURES",
+ "GET FIRMWARE FEATURES",
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ "LOAD RAM A64",
+ "DUMP RAM A64",
+ "INITIALIZE REQUEST QUEUE A64",
+ "INITIALIZE RESPONSE QUEUE A64",
+ "EXECUTE IOCB A64",
+ "ENABLE TARGET MODE",
+ "GET TARGET MODE STATE",
+ NULL,
+ NULL,
+ NULL,
+ "SET DATA OVERRUN RECOVERY MODE",
+ "GET DATA OVERRUN RECOVERY MODE",
+ "SET HOST DATA",
+ "GET NOST DATA",
+};
+#endif
+
+static const uint32_t mbpfc[] = {
+ ISPOPMAP(0x01, 0x01), /* 0x00: MBOX_NO_OP */
+ ISPOPMAP(0x1f, 0x01), /* 0x01: MBOX_LOAD_RAM */
+ ISPOPMAP(0x07, 0x01), /* 0x02: MBOX_EXEC_FIRMWARE */
+ ISPOPMAP(0xdf, 0x01), /* 0x03: MBOX_DUMP_RAM */
+ ISPOPMAP(0x07, 0x07), /* 0x04: MBOX_WRITE_RAM_WORD */
+ ISPOPMAP(0x03, 0x07), /* 0x05: MBOX_READ_RAM_WORD */
+ ISPOPMAP(0xff, 0xff), /* 0x06: MBOX_MAILBOX_REG_TEST */
+ ISPOPMAP(0x03, 0x05), /* 0x07: MBOX_VERIFY_CHECKSUM */
+ ISPOPMAP(0x01, 0x4f), /* 0x08: MBOX_ABOUT_FIRMWARE */
+ ISPOPMAP(0xdf, 0x01), /* 0x09: LOAD RAM */
+ ISPOPMAP(0xdf, 0x01), /* 0x0a: DUMP RAM */
+ ISPOPMAP(0x00, 0x00), /* 0x0b: */
+ ISPOPMAP(0x00, 0x00), /* 0x0c: */
+ ISPOPMAP(0x10f, 0x01), /* 0x0d: MBOX_WRITE_RAM_WORD_EXTENDED) */
+ ISPOPMAP(0x01, 0x05), /* 0x0e: MBOX_CHECK_FIRMWARE */
+ ISPOPMAP(0x10f, 0x05), /* 0x0f: MBOX_READ_RAM_WORD_EXTENDED */
+ ISPOPMAP(0x1f, 0x11), /* 0x10: MBOX_INIT_REQ_QUEUE */
+ ISPOPMAP(0x2f, 0x21), /* 0x11: MBOX_INIT_RES_QUEUE */
+ ISPOPMAP(0x0f, 0x01), /* 0x12: MBOX_EXECUTE_IOCB */
+ ISPOPMAP(0x03, 0x03), /* 0x13: MBOX_WAKE_UP */
+ ISPOPMAP(0x01, 0xff), /* 0x14: MBOX_STOP_FIRMWARE */
+ ISPOPMAP(0x4f, 0x01), /* 0x15: MBOX_ABORT */
+ ISPOPMAP(0x07, 0x01), /* 0x16: MBOX_ABORT_DEVICE */
+ ISPOPMAP(0x07, 0x01), /* 0x17: MBOX_ABORT_TARGET */
+ ISPOPMAP(0x03, 0x03), /* 0x18: MBOX_BUS_RESET */
+ ISPOPMAP(0x07, 0x05), /* 0x19: MBOX_STOP_QUEUE */
+ ISPOPMAP(0x07, 0x05), /* 0x1a: MBOX_START_QUEUE */
+ ISPOPMAP(0x07, 0x05), /* 0x1b: MBOX_SINGLE_STEP_QUEUE */
+ ISPOPMAP(0x07, 0x05), /* 0x1c: MBOX_ABORT_QUEUE */
+ ISPOPMAP(0x07, 0x03), /* 0x1d: MBOX_GET_DEV_QUEUE_STATUS */
+ ISPOPMAP(0x00, 0x00), /* 0x1e: */
+ ISPOPMAP(0x01, 0x07), /* 0x1f: MBOX_GET_FIRMWARE_STATUS */
+ ISPOPMAP(0x01, 0x4f), /* 0x20: MBOX_GET_LOOP_ID */
+ ISPOPMAP(0x00, 0x00), /* 0x21: */
+ ISPOPMAP(0x01, 0x07), /* 0x22: MBOX_GET_RETRY_COUNT */
+ ISPOPMAP(0x00, 0x00), /* 0x23: */
+ ISPOPMAP(0x00, 0x00), /* 0x24: */
+ ISPOPMAP(0x00, 0x00), /* 0x25: */
+ ISPOPMAP(0x00, 0x00), /* 0x26: */
+ ISPOPMAP(0x00, 0x00), /* 0x27: */
+ ISPOPMAP(0x01, 0x03), /* 0x28: MBOX_GET_FIRMWARE_OPTIONS */
+ ISPOPMAP(0x03, 0x07), /* 0x29: MBOX_GET_PORT_QUEUE_PARAMS */
+ ISPOPMAP(0x00, 0x00), /* 0x2a: */
+ ISPOPMAP(0x00, 0x00), /* 0x2b: */
+ ISPOPMAP(0x00, 0x00), /* 0x2c: */
+ ISPOPMAP(0x00, 0x00), /* 0x2d: */
+ ISPOPMAP(0x00, 0x00), /* 0x2e: */
+ ISPOPMAP(0x00, 0x00), /* 0x2f: */
+ ISPOPMAP(0x00, 0x00), /* 0x30: */
+ ISPOPMAP(0x00, 0x00), /* 0x31: */
+ ISPOPMAP(0x07, 0x07), /* 0x32: MBOX_SET_RETRY_COUNT */
+ ISPOPMAP(0x00, 0x00), /* 0x33: */
+ ISPOPMAP(0x00, 0x00), /* 0x34: */
+ ISPOPMAP(0x00, 0x00), /* 0x35: */
+ ISPOPMAP(0x00, 0x00), /* 0x36: */
+ ISPOPMAP(0x00, 0x00), /* 0x37: */
+ ISPOPMAP(0x0f, 0x01), /* 0x38: MBOX_SET_FIRMWARE_OPTIONS */
+ ISPOPMAP(0x0f, 0x07), /* 0x39: MBOX_SET_PORT_QUEUE_PARAMS */
+ ISPOPMAP(0x00, 0x00), /* 0x3a: */
+ ISPOPMAP(0x00, 0x00), /* 0x3b: */
+ ISPOPMAP(0x00, 0x00), /* 0x3c: */
+ ISPOPMAP(0x00, 0x00), /* 0x3d: */
+ ISPOPMAP(0x00, 0x00), /* 0x3e: */
+ ISPOPMAP(0x00, 0x00), /* 0x3f: */
+ ISPOPMAP(0x03, 0x01), /* 0x40: MBOX_LOOP_PORT_BYPASS */
+ ISPOPMAP(0x03, 0x01), /* 0x41: MBOX_LOOP_PORT_ENABLE */
+ ISPOPMAP(0x03, 0x07), /* 0x42: MBOX_GET_RESOURCE_COUNTS */
+ ISPOPMAP(0x01, 0x01), /* 0x43: MBOX_REQUEST_NON_PARTICIPATING_MODE */
+ ISPOPMAP(0x00, 0x00), /* 0x44: */
+ ISPOPMAP(0x00, 0x00), /* 0x45: */
+ ISPOPMAP(0x00, 0x00), /* 0x46: */
+ ISPOPMAP(0xcf, 0x03), /* 0x47: GET PORT_DATABASE ENHANCED */
+ ISPOPMAP(0x00, 0x00), /* 0x48: */
+ ISPOPMAP(0x00, 0x00), /* 0x49: */
+ ISPOPMAP(0x00, 0x00), /* 0x4a: */
+ ISPOPMAP(0x00, 0x00), /* 0x4b: */
+ ISPOPMAP(0x00, 0x00), /* 0x4c: */
+ ISPOPMAP(0x00, 0x00), /* 0x4d: */
+ ISPOPMAP(0x00, 0x00), /* 0x4e: */
+ ISPOPMAP(0x00, 0x00), /* 0x4f: */
+ ISPOPMAP(0x00, 0x00), /* 0x50: */
+ ISPOPMAP(0x00, 0x00), /* 0x51: */
+ ISPOPMAP(0x00, 0x00), /* 0x52: */
+ ISPOPMAP(0x00, 0x00), /* 0x53: */
+ ISPOPMAP(0xcf, 0x01), /* 0x54: EXECUTE IOCB A64 */
+ ISPOPMAP(0x00, 0x00), /* 0x55: */
+ ISPOPMAP(0x00, 0x00), /* 0x56: */
+ ISPOPMAP(0x00, 0x00), /* 0x57: */
+ ISPOPMAP(0x00, 0x00), /* 0x58: */
+ ISPOPMAP(0x00, 0x00), /* 0x59: */
+ ISPOPMAP(0x00, 0x00), /* 0x5a: */
+ ISPOPMAP(0x03, 0x01), /* 0x5b: MBOX_DRIVER_HEARTBEAT */
+ ISPOPMAP(0xcf, 0x01), /* 0x5c: MBOX_FW_HEARTBEAT */
+ ISPOPMAP(0x07, 0x03), /* 0x5d: MBOX_GET_SET_DATA_RATE */
+ ISPOPMAP(0x00, 0x00), /* 0x5e: */
+ ISPOPMAP(0x00, 0x00), /* 0x5f: */
+ ISPOPMAP(0xcd, 0x31), /* 0x60: MBOX_INIT_FIRMWARE */
+ ISPOPMAP(0x00, 0x00), /* 0x61: */
+ ISPOPMAP(0x01, 0x01), /* 0x62: MBOX_INIT_LIP */
+ ISPOPMAP(0xcd, 0x03), /* 0x63: MBOX_GET_FC_AL_POSITION_MAP */
+ ISPOPMAP(0xcf, 0x01), /* 0x64: MBOX_GET_PORT_DB */
+ ISPOPMAP(0x07, 0x01), /* 0x65: MBOX_CLEAR_ACA */
+ ISPOPMAP(0x07, 0x01), /* 0x66: MBOX_TARGET_RESET */
+ ISPOPMAP(0x07, 0x01), /* 0x67: MBOX_CLEAR_TASK_SET */
+ ISPOPMAP(0x07, 0x01), /* 0x68: MBOX_ABORT_TASK_SET */
+ ISPOPMAP(0x01, 0x07), /* 0x69: MBOX_GET_FW_STATE */
+ ISPOPMAP(0x03, 0xcf), /* 0x6a: MBOX_GET_PORT_NAME */
+ ISPOPMAP(0xcf, 0x01), /* 0x6b: MBOX_GET_LINK_STATUS */
+ ISPOPMAP(0x0f, 0x01), /* 0x6c: MBOX_INIT_LIP_RESET */
+ ISPOPMAP(0x00, 0x00), /* 0x6d: */
+ ISPOPMAP(0xcf, 0x03), /* 0x6e: MBOX_SEND_SNS */
+ ISPOPMAP(0x0f, 0x07), /* 0x6f: MBOX_FABRIC_LOGIN */
+ ISPOPMAP(0x03, 0x01), /* 0x70: MBOX_SEND_CHANGE_REQUEST */
+ ISPOPMAP(0x03, 0x03), /* 0x71: MBOX_FABRIC_LOGOUT */
+ ISPOPMAP(0x0f, 0x0f), /* 0x72: MBOX_INIT_LIP_LOGIN */
+ ISPOPMAP(0x00, 0x00), /* 0x73: */
+ ISPOPMAP(0x07, 0x01), /* 0x74: LOGIN LOOP PORT */
+ ISPOPMAP(0xcf, 0x03), /* 0x75: GET PORT/NODE NAME LIST */
+ ISPOPMAP(0x4f, 0x01), /* 0x76: SET VENDOR ID */
+ ISPOPMAP(0xcd, 0x01), /* 0x77: INITIALIZE IP MAILBOX */
+ ISPOPMAP(0x00, 0x00), /* 0x78: */
+ ISPOPMAP(0x00, 0x00), /* 0x79: */
+ ISPOPMAP(0x00, 0x00), /* 0x7a: */
+ ISPOPMAP(0x00, 0x00), /* 0x7b: */
+ ISPOPMAP(0x4f, 0x03), /* 0x7c: Get ID List */
+ ISPOPMAP(0xcf, 0x01), /* 0x7d: SEND LFA */
+ ISPOPMAP(0x0f, 0x01) /* 0x7e: LUN RESET */
+};
+/*
+ * Footnotes
+ *
+ * (1): this sets bits 21..16 in mailbox register #8, which we nominally
+ * do not access at this time in the core driver. The caller is
+ * responsible for setting this register first (Gross!). The assumption
+ * is that we won't overflow.
+ */
+
+#ifndef ISP_STRIPPED
+static char *fc_mbcmd_names[] = {
+ "NO-OP",
+ "LOAD RAM",
+ "EXEC FIRMWARE",
+ "DUMP RAM",
+ "WRITE RAM WORD",
+ "READ RAM WORD",
+ "MAILBOX REG TEST",
+ "VERIFY CHECKSUM",
+ "ABOUT FIRMWARE",
+ "LOAD RAM",
+ "DUMP RAM",
+ "WRITE RAM WORD EXTENDED",
+ NULL,
+ "READ RAM WORD EXTENDED",
+ "CHECK FIRMWARE",
+ NULL,
+ "INIT REQUEST QUEUE",
+ "INIT RESULT QUEUE",
+ "EXECUTE IOCB",
+ "WAKE UP",
+ "STOP FIRMWARE",
+ "ABORT",
+ "ABORT DEVICE",
+ "ABORT TARGET",
+ "BUS RESET",
+ "STOP QUEUE",
+ "START QUEUE",
+ "SINGLE STEP QUEUE",
+ "ABORT QUEUE",
+ "GET DEV QUEUE STATUS",
+ NULL,
+ "GET FIRMWARE STATUS",
+ "GET LOOP ID",
+ NULL,
+ "GET RETRY COUNT",
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ "GET FIRMWARE OPTIONS",
+ "GET PORT QUEUE PARAMS",
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ "SET RETRY COUNT",
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ "SET FIRMWARE OPTIONS",
+ "SET PORT QUEUE PARAMS",
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ "LOOP PORT BYPASS",
+ "LOOP PORT ENABLE",
+ "GET RESOURCE COUNTS",
+ "REQUEST NON PARTICIPATING MODE",
+ NULL,
+ NULL,
+ NULL,
+ "GET PORT DATABASE ENHANCED",
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ "EXECUTE IOCB A64",
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ "DRIVER HEARTBEAT",
+ NULL,
+ "GET/SET DATA RATE",
+ NULL,
+ NULL,
+ "INIT FIRMWARE",
+ NULL,
+ "INIT LIP",
+ "GET FC-AL POSITION MAP",
+ "GET PORT DATABASE",
+ "CLEAR ACA",
+ "TARGET RESET",
+ "CLEAR TASK SET",
+ "ABORT TASK SET",
+ "GET FW STATE",
+ "GET PORT NAME",
+ "GET LINK STATUS",
+ "INIT LIP RESET",
+ NULL,
+ "SEND SNS",
+ "FABRIC LOGIN",
+ "SEND CHANGE REQUEST",
+ "FABRIC LOGOUT",
+ "INIT LIP LOGIN",
+ NULL,
+ "LOGIN LOOP PORT",
+ "GET PORT/NODE NAME LIST",
+ "SET VENDOR ID",
+ "INITIALIZE IP MAILBOX",
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ "Get ID List",
+ "SEND LFA",
+ "Lun RESET"
+};
+#endif
+
+static void
+isp_mboxcmd_qnw(ispsoftc_t *isp, mbreg_t *mbp, int nodelay)
+{
+ unsigned int ibits, obits, box, opcode;
+ const uint32_t *mcp;
+
+ if (IS_FC(isp)) {
+ mcp = mbpfc;
+ } else {
+ mcp = mbpscsi;
+ }
+ opcode = mbp->param[0];
+ ibits = HIWRD(mcp[opcode]) & NMBOX_BMASK(isp);
+ obits = LOWRD(mcp[opcode]) & NMBOX_BMASK(isp);
+ ibits |= mbp->ibits;
+ obits |= mbp->obits;
+ for (box = 0; box < MAX_MAILBOX(isp); box++) {
+ if (ibits & (1 << box)) {
+ ISP_WRITE(isp, MBOX_OFF(box), mbp->param[box]);
+ }
+ if (nodelay == 0) {
+ isp->isp_mboxtmp[box] = mbp->param[box] = 0;
+ }
+ }
+ if (nodelay == 0) {
+ isp->isp_lastmbxcmd = opcode;
+ isp->isp_obits = obits;
+ isp->isp_mboxbsy = 1;
+ }
+ ISP_WRITE(isp, HCCR, HCCR_CMD_SET_HOST_INT);
+ /*
+ * Oddly enough, if we're not delaying for an answer,
+ * delay a bit to give the f/w a chance to pick up the
+ * command.
+ */
+ if (nodelay) {
+ USEC_DELAY(100);
+ }
+}
+
+static void
+isp_mboxcmd(ispsoftc_t *isp, mbreg_t *mbp, int logmask)
+{
+ char *cname, *xname, tname[16], mname[16];
+ unsigned int lim, ibits, obits, box, opcode;
+ const uint32_t *mcp;
+
+ if (IS_FC(isp)) {
+ mcp = mbpfc;
+ lim = (sizeof (mbpfc) / sizeof (mbpfc[0]));
+ } else {
+ mcp = mbpscsi;
+ lim = (sizeof (mbpscsi) / sizeof (mbpscsi[0]));
+ }
+
+ if ((opcode = mbp->param[0]) >= lim) {
+ mbp->param[0] = MBOX_INVALID_COMMAND;
+ isp_prt(isp, ISP_LOGERR, "Unknown Command 0x%x", opcode);
+ return;
+ }
+
+ ibits = HIWRD(mcp[opcode]) & NMBOX_BMASK(isp);
+ obits = LOWRD(mcp[opcode]) & NMBOX_BMASK(isp);
+
+ /*
+ * Pick up any additional bits that the caller might have set.
+ */
+ ibits |= mbp->ibits;
+ obits |= mbp->obits;
+
+ if (ibits == 0 && obits == 0) {
+ mbp->param[0] = MBOX_COMMAND_PARAM_ERROR;
+ isp_prt(isp, ISP_LOGERR, "no parameters for 0x%x", opcode);
+ return;
+ }
+
+ /*
+ * Get exclusive usage of mailbox registers.
+ */
+ MBOX_ACQUIRE(isp);
+
+ for (box = 0; box < MAX_MAILBOX(isp); box++) {
+ if (ibits & (1 << box)) {
+ isp_prt(isp, ISP_LOGDEBUG1, "IN mbox %d = 0x%x", box,
+ mbp->param[box]);
+ ISP_WRITE(isp, MBOX_OFF(box), mbp->param[box]);
+ }
+ isp->isp_mboxtmp[box] = mbp->param[box] = 0;
+ }
+
+ isp->isp_lastmbxcmd = opcode;
+
+ /*
+ * We assume that we can't overwrite a previous command.
+ */
+ isp->isp_obits = obits;
+ isp->isp_mboxbsy = 1;
+
+ /*
+ * Set Host Interrupt condition so that RISC will pick up mailbox regs.
+ */
+ ISP_WRITE(isp, HCCR, HCCR_CMD_SET_HOST_INT);
+
+ /*
+ * While we haven't finished the command, spin our wheels here.
+ */
+ MBOX_WAIT_COMPLETE(isp);
+
+ /*
+ * Did the command time out?
+ */
+ if (isp->isp_mboxbsy) {
+ isp->isp_mboxbsy = 0;
+ MBOX_RELEASE(isp);
+ return;
+ }
+
+ /*
+ * Copy back output registers.
+ */
+ for (box = 0; box < MAX_MAILBOX(isp); box++) {
+ if (obits & (1 << box)) {
+ mbp->param[box] = isp->isp_mboxtmp[box];
+ isp_prt(isp, ISP_LOGDEBUG1, "OUT mbox %d = 0x%x", box,
+ mbp->param[box]);
+ }
+ }
+
+ MBOX_RELEASE(isp);
+
+ if (logmask == 0 || opcode == MBOX_EXEC_FIRMWARE) {
+ return;
+ }
+#ifdef ISP_STRIPPED
+ cname = NULL;
+#else
+ cname = (IS_FC(isp))? fc_mbcmd_names[opcode] : scsi_mbcmd_names[opcode];
+#endif
+ if (cname == NULL) {
+ cname = tname;
+ SNPRINTF(tname, sizeof tname, "opcode %x", opcode);
+ }
+
+ /*
+ * Just to be chatty here...
+ */
+ xname = NULL;
+ switch (mbp->param[0]) {
+ case MBOX_COMMAND_COMPLETE:
+ break;
+ case MBOX_INVALID_COMMAND:
+ if (logmask & MBLOGMASK(MBOX_COMMAND_COMPLETE)) {
+ xname = "INVALID COMMAND";
+ }
+ break;
+ case MBOX_HOST_INTERFACE_ERROR:
+ if (logmask & MBLOGMASK(MBOX_HOST_INTERFACE_ERROR)) {
+ xname = "HOST INTERFACE ERROR";
+ }
+ break;
+ case MBOX_TEST_FAILED:
+ if (logmask & MBLOGMASK(MBOX_TEST_FAILED)) {
+ xname = "TEST FAILED";
+ }
+ break;
+ case MBOX_COMMAND_ERROR:
+ if (logmask & MBLOGMASK(MBOX_COMMAND_ERROR)) {
+ xname = "COMMAND ERROR";
+ }
+ break;
+ case MBOX_COMMAND_PARAM_ERROR:
+ if (logmask & MBLOGMASK(MBOX_COMMAND_PARAM_ERROR)) {
+ xname = "COMMAND PARAMETER ERROR";
+ }
+ break;
+ case MBOX_LOOP_ID_USED:
+ if (logmask & MBLOGMASK(MBOX_LOOP_ID_USED)) {
+ xname = "LOOP ID ALREADY IN USE";
+ }
+ break;
+ case MBOX_PORT_ID_USED:
+ if (logmask & MBLOGMASK(MBOX_PORT_ID_USED)) {
+ xname = "PORT ID ALREADY IN USE";
+ }
+ break;
+ case MBOX_ALL_IDS_USED:
+ if (logmask & MBLOGMASK(MBOX_ALL_IDS_USED)) {
+ xname = "ALL LOOP IDS IN USE";
+ }
+ break;
+ case 0: /* special case */
+ xname = "TIMEOUT";
+ break;
+ default:
+ SNPRINTF(mname, sizeof mname, "error 0x%x", mbp->param[0]);
+ xname = mname;
+ break;
+ }
+ if (xname) {
+ isp_prt(isp, ISP_LOGALL, "Mailbox Command '%s' failed (%s)",
+ cname, xname);
+ }
+}
+
+static void
+isp_fw_state(ispsoftc_t *isp)
+{
+ if (IS_FC(isp)) {
+ mbreg_t mbs;
+ fcparam *fcp = isp->isp_param;
+
+ MEMZERO(&mbs, sizeof (mbs));
+ mbs.param[0] = MBOX_GET_FW_STATE;
+ isp_mboxcmd(isp, &mbs, MBLOGALL);
+ if (mbs.param[0] == MBOX_COMMAND_COMPLETE) {
+ fcp->isp_fwstate = mbs.param[1];
+ }
+ }
+}
+
+static void
+isp_update(ispsoftc_t *isp)
+{
+ int bus, upmask;
+
+ for (bus = 0, upmask = isp->isp_update; upmask != 0; bus++) {
+ if (upmask & (1 << bus)) {
+ isp_update_bus(isp, bus);
+ }
+ upmask &= ~(1 << bus);
+ }
+}
+
+static void
+isp_update_bus(ispsoftc_t *isp, int bus)
+{
+ int tgt;
+ mbreg_t mbs;
+ sdparam *sdp;
+
+ isp->isp_update &= ~(1 << bus);
+ if (IS_FC(isp)) {
+ /*
+ * There are no 'per-bus' settings for Fibre Channel.
+ */
+ return;
+ }
+ sdp = isp->isp_param;
+ sdp += bus;
+
+ for (tgt = 0; tgt < MAX_TARGETS; tgt++) {
+ uint16_t flags, period, offset;
+ int get;
+
+ if (sdp->isp_devparam[tgt].dev_enable == 0) {
+ sdp->isp_devparam[tgt].dev_update = 0;
+ sdp->isp_devparam[tgt].dev_refresh = 0;
+ isp_prt(isp, ISP_LOGDEBUG0,
+ "skipping target %d bus %d update", tgt, bus);
+ continue;
+ }
+ /*
+ * If the goal is to update the status of the device,
+ * take what's in goal_flags and try and set the device
+ * toward that. Otherwise, if we're just refreshing the
+ * current device state, get the current parameters.
+ */
+
+ MEMZERO(&mbs, sizeof (mbs));
+
+ /*
+ * Refresh overrides set
+ */
+ if (sdp->isp_devparam[tgt].dev_refresh) {
+ mbs.param[0] = MBOX_GET_TARGET_PARAMS;
+ sdp->isp_devparam[tgt].dev_refresh = 0;
+ get = 1;
+ } else if (sdp->isp_devparam[tgt].dev_update) {
+ mbs.param[0] = MBOX_SET_TARGET_PARAMS;
+ /*
+ * Make sure goal_flags has "Renegotiate on Error"
+ * on and "Freeze Queue on Error" off.
+ */
+ sdp->isp_devparam[tgt].goal_flags |= DPARM_RENEG;
+ sdp->isp_devparam[tgt].goal_flags &= ~DPARM_QFRZ;
+
+ mbs.param[2] = sdp->isp_devparam[tgt].goal_flags;
+
+ /*
+ * Insist that PARITY must be enabled
+ * if SYNC or WIDE is enabled.
+ */
+ if ((mbs.param[2] & (DPARM_SYNC|DPARM_WIDE)) != 0) {
+ mbs.param[2] |= DPARM_PARITY;
+ }
+
+ if (mbs.param[2] & DPARM_SYNC) {
+ mbs.param[3] =
+ (sdp->isp_devparam[tgt].goal_offset << 8) |
+ (sdp->isp_devparam[tgt].goal_period);
+ }
+ /*
+ * A command completion later that has
+ * RQSTF_NEGOTIATION set can cause
+ * the dev_refresh/announce cycle also.
+ *
+ * Note: It is really important to update our current
+ * flags with at least the state of TAG capabilities-
+ * otherwise we might try and send a tagged command
+ * when we have it all turned off. So change it here
+ * to say that current already matches goal.
+ */
+ sdp->isp_devparam[tgt].actv_flags &= ~DPARM_TQING;
+ sdp->isp_devparam[tgt].actv_flags |=
+ (sdp->isp_devparam[tgt].goal_flags & DPARM_TQING);
+ isp_prt(isp, ISP_LOGDEBUG0,
+ "bus %d set tgt %d flags 0x%x off 0x%x period 0x%x",
+ bus, tgt, mbs.param[2], mbs.param[3] >> 8,
+ mbs.param[3] & 0xff);
+ sdp->isp_devparam[tgt].dev_update = 0;
+ sdp->isp_devparam[tgt].dev_refresh = 1;
+ get = 0;
+ } else {
+ continue;
+ }
+ mbs.param[1] = (bus << 15) | (tgt << 8);
+ isp_mboxcmd(isp, &mbs, MBLOGALL);
+ if (get == 0) {
+ isp->isp_sendmarker |= (1 << bus);
+ continue;
+ }
+ flags = mbs.param[2];
+ period = mbs.param[3] & 0xff;
+ offset = mbs.param[3] >> 8;
+ sdp->isp_devparam[tgt].actv_flags = flags;
+ sdp->isp_devparam[tgt].actv_period = period;
+ sdp->isp_devparam[tgt].actv_offset = offset;
+ get = (bus << 16) | tgt;
+ (void) isp_async(isp, ISPASYNC_NEW_TGT_PARAMS, &get);
+ }
+
+ for (tgt = 0; tgt < MAX_TARGETS; tgt++) {
+ if (sdp->isp_devparam[tgt].dev_update ||
+ sdp->isp_devparam[tgt].dev_refresh) {
+ isp->isp_update |= (1 << bus);
+ break;
+ }
+ }
+}
+
+#ifndef DEFAULT_FRAMESIZE
+#define DEFAULT_FRAMESIZE(isp) ICB_DFLT_FRMLEN
+#endif
+#ifndef DEFAULT_EXEC_THROTTLE
+#define DEFAULT_EXEC_THROTTLE(isp) ISP_EXEC_THROTTLE
+#endif
+
+static void
+isp_setdfltparm(ispsoftc_t *isp, int channel)
+{
+ int tgt;
+ sdparam *sdp;
+
+ if (IS_FC(isp)) {
+ fcparam *fcp = (fcparam *) isp->isp_param;
+ int nvfail;
+
+ fcp += channel;
+ if (fcp->isp_gotdparms) {
+ return;
+ }
+ fcp->isp_gotdparms = 1;
+ fcp->isp_maxfrmlen = DEFAULT_FRAMESIZE(isp);
+ fcp->isp_maxalloc = ICB_DFLT_ALLOC;
+ fcp->isp_execthrottle = DEFAULT_EXEC_THROTTLE(isp);
+ fcp->isp_retry_delay = ICB_DFLT_RDELAY;
+ fcp->isp_retry_count = ICB_DFLT_RCOUNT;
+ /* Platform specific.... */
+ fcp->isp_loopid = DEFAULT_LOOPID(isp);
+ fcp->isp_nodewwn = DEFAULT_NODEWWN(isp);
+ fcp->isp_portwwn = DEFAULT_PORTWWN(isp);
+ fcp->isp_fwoptions = 0;
+ fcp->isp_fwoptions |= ICBOPT_FAIRNESS;
+ fcp->isp_fwoptions |= ICBOPT_PDBCHANGE_AE;
+ fcp->isp_fwoptions |= ICBOPT_HARD_ADDRESS;
+ fcp->isp_fwoptions |= ICBOPT_FAST_POST;
+ if (isp->isp_confopts & ISP_CFG_FULL_DUPLEX)
+ fcp->isp_fwoptions |= ICBOPT_FULL_DUPLEX;
+
+ /*
+ * Make sure this is turned off now until we get
+ * extended options from NVRAM
+ */
+ fcp->isp_fwoptions &= ~ICBOPT_EXTENDED;
+
+ /*
+ * Now try and read NVRAM unless told to not do so.
+ * This will set fcparam's isp_nodewwn && isp_portwwn.
+ */
+ if ((isp->isp_confopts & ISP_CFG_NONVRAM) == 0) {
+ nvfail = isp_read_nvram(isp);
+ if (nvfail) {
+ isp->isp_confopts |= ISP_CFG_NONVRAM;
+ }
+ } else {
+ nvfail = 1;
+ }
+ /*
+ * Set node && port to override platform set defaults
+ * unless the nvram read failed (or none was done),
+ * or the platform code wants to use what had been
+ * set in the defaults.
+ */
+ if (nvfail) {
+ isp->isp_confopts |= ISP_CFG_OWNWWPN|ISP_CFG_OWNWWNN;
+ }
+ if (isp->isp_confopts & ISP_CFG_OWNWWNN) {
+ isp_prt(isp, ISP_LOGCONFIG, "Using Node WWN 0x%08x%08x",
+ (uint32_t) (DEFAULT_NODEWWN(isp) >> 32),
+ (uint32_t) (DEFAULT_NODEWWN(isp) & 0xffffffff));
+ ISP_NODEWWN(isp) = DEFAULT_NODEWWN(isp);
+ } else {
+ /*
+ * We always start out with values derived
+ * from NVRAM or our platform default.
+ */
+ ISP_NODEWWN(isp) = fcp->isp_nodewwn;
+ if (fcp->isp_nodewwn == 0) {
+ isp_prt(isp, ISP_LOGCONFIG,
+ "bad WWNN- using default\n");
+ ISP_NODEWWN(isp) = DEFAULT_NODEWWN(isp);
+ }
+ }
+ if (isp->isp_confopts & ISP_CFG_OWNWWPN) {
+ isp_prt(isp, ISP_LOGCONFIG, "Using Port WWN 0x%08x%08x",
+ (uint32_t) (DEFAULT_PORTWWN(isp) >> 32),
+ (uint32_t) (DEFAULT_PORTWWN(isp) & 0xffffffff));
+ ISP_PORTWWN(isp) = DEFAULT_PORTWWN(isp);
+ } else {
+ /*
+ * We always start out with values derived
+ * from NVRAM or our platform default.
+ */
+ ISP_PORTWWN(isp) = fcp->isp_portwwn;
+ if (fcp->isp_portwwn == 0) {
+ isp_prt(isp, ISP_LOGCONFIG,
+ "bad WWPN- using default\n");
+ ISP_PORTWWN(isp) = DEFAULT_PORTWWN(isp);
+ }
+ }
+ return;
+ }
+
+ sdp = (sdparam *) isp->isp_param;
+ sdp += channel;
+
+ /*
+ * Been there, done that, got the T-shirt...
+ */
+ if (sdp->isp_gotdparms) {
+ return;
+ }
+ sdp->isp_gotdparms = 1;
+
+ /*
+ * Establish some default parameters.
+ */
+ sdp->isp_cmd_dma_burst_enable = 0;
+ sdp->isp_data_dma_burst_enabl = 1;
+ sdp->isp_fifo_threshold = 0;
+ sdp->isp_initiator_id = DEFAULT_IID(isp);
+ if (isp->isp_type >= ISP_HA_SCSI_1040) {
+ sdp->isp_async_data_setup = 9;
+ } else {
+ sdp->isp_async_data_setup = 6;
+ }
+ sdp->isp_selection_timeout = 250;
+ sdp->isp_max_queue_depth = MAXISPREQUEST(isp);
+ sdp->isp_tag_aging = 8;
+ sdp->isp_bus_reset_delay = 5;
+ /*
+ * Don't retry selection, busy or queue full automatically- reflect
+ * these back to us.
+ */
+ sdp->isp_retry_count = 0;
+ sdp->isp_retry_delay = 0;
+
+ for (tgt = 0; tgt < MAX_TARGETS; tgt++) {
+ sdp->isp_devparam[tgt].exc_throttle = ISP_EXEC_THROTTLE;
+ sdp->isp_devparam[tgt].dev_enable = 1;
+ }
+
+ /*
+ * If we've not been told to avoid reading NVRAM, try and read it.
+ * If we're successful reading it, we can then return because NVRAM
+ * will tell us what the desired settings are. Otherwise, we establish
+ * some reasonable 'fake' nvram and goal defaults.
+ */
+
+ if ((isp->isp_confopts & ISP_CFG_NONVRAM) == 0) {
+ if (isp_read_nvram(isp) == 0) {
+ return;
+ }
+ }
+
+ /*
+ * Now try and see whether we have specific values for them.
+ */
+ if ((isp->isp_confopts & ISP_CFG_NONVRAM) == 0) {
+ mbreg_t mbs;
+
+ MEMZERO(&mbs, sizeof (mbs));
+ mbs.param[0] = MBOX_GET_ACT_NEG_STATE;
+ isp_mboxcmd(isp, &mbs, MBLOGNONE);
+ if (mbs.param[0] != MBOX_COMMAND_COMPLETE) {
+ sdp->isp_req_ack_active_neg = 1;
+ sdp->isp_data_line_active_neg = 1;
+ } else {
+ sdp->isp_req_ack_active_neg =
+ (mbs.param[1+channel] >> 4) & 0x1;
+ sdp->isp_data_line_active_neg =
+ (mbs.param[1+channel] >> 5) & 0x1;
+ }
+ }
+
+ isp_prt(isp, ISP_LOGDEBUG0, sc0, sc3,
+ 0, sdp->isp_fifo_threshold, sdp->isp_initiator_id,
+ sdp->isp_bus_reset_delay, sdp->isp_retry_count,
+ sdp->isp_retry_delay, sdp->isp_async_data_setup);
+ isp_prt(isp, ISP_LOGDEBUG0, sc1, sc3,
+ sdp->isp_req_ack_active_neg, sdp->isp_data_line_active_neg,
+ sdp->isp_data_dma_burst_enabl, sdp->isp_cmd_dma_burst_enable,
+ sdp->isp_selection_timeout, sdp->isp_max_queue_depth);
+
+ /*
+ * The trick here is to establish a default for the default (honk!)
+ * state (goal_flags). Then try and get the current status from
+ * the card to fill in the current state. We don't, in fact, set
+ * the default to the SAFE default state- that's not the goal state.
+ */
+ for (tgt = 0; tgt < MAX_TARGETS; tgt++) {
+ uint8_t off, per;
+ sdp->isp_devparam[tgt].actv_offset = 0;
+ sdp->isp_devparam[tgt].actv_period = 0;
+ sdp->isp_devparam[tgt].actv_flags = 0;
+
+ sdp->isp_devparam[tgt].goal_flags =
+ sdp->isp_devparam[tgt].nvrm_flags = DPARM_DEFAULT;
+
+ /*
+ * We default to Wide/Fast for versions less than a 1040
+ * (unless it's SBus).
+ */
+ if (IS_ULTRA3(isp)) {
+ off = ISP_80M_SYNCPARMS >> 8;
+ per = ISP_80M_SYNCPARMS & 0xff;
+ } else if (IS_ULTRA2(isp)) {
+ off = ISP_40M_SYNCPARMS >> 8;
+ per = ISP_40M_SYNCPARMS & 0xff;
+ } else if (IS_1240(isp)) {
+ off = ISP_20M_SYNCPARMS >> 8;
+ per = ISP_20M_SYNCPARMS & 0xff;
+ } else if ((isp->isp_bustype == ISP_BT_SBUS &&
+ isp->isp_type < ISP_HA_SCSI_1020A) ||
+ (isp->isp_bustype == ISP_BT_PCI &&
+ isp->isp_type < ISP_HA_SCSI_1040) ||
+ (isp->isp_clock && isp->isp_clock < 60) ||
+ (sdp->isp_ultramode == 0)) {
+ off = ISP_10M_SYNCPARMS >> 8;
+ per = ISP_10M_SYNCPARMS & 0xff;
+ } else {
+ off = ISP_20M_SYNCPARMS_1040 >> 8;
+ per = ISP_20M_SYNCPARMS_1040 & 0xff;
+ }
+ sdp->isp_devparam[tgt].goal_offset =
+ sdp->isp_devparam[tgt].nvrm_offset = off;
+ sdp->isp_devparam[tgt].goal_period =
+ sdp->isp_devparam[tgt].nvrm_period = per;
+
+ isp_prt(isp, ISP_LOGDEBUG0, sc2, sc3,
+ channel, tgt, sdp->isp_devparam[tgt].nvrm_flags,
+ sdp->isp_devparam[tgt].nvrm_offset,
+ sdp->isp_devparam[tgt].nvrm_period);
+ }
+}
+
+/*
+ * Re-initialize the ISP and complete all orphaned commands
+ * with a 'botched' notice. The reset/init routines should
+ * not disturb an already active list of commands.
+ *
+ * Locks held prior to coming here.
+ */
+
+void
+isp_reinit(ispsoftc_t *isp)
+{
+ XS_T *xs;
+ int i;
+
+ if (IS_FC(isp)) {
+ isp_mark_getpdb_all(isp);
+ }
+ isp_reset(isp);
+ if (isp->isp_state != ISP_RESETSTATE) {
+ isp_prt(isp, ISP_LOGERR, "isp_reinit cannot reset card");
+ } else if (isp->isp_role != ISP_ROLE_NONE) {
+ isp_init(isp);
+ if (isp->isp_state == ISP_INITSTATE) {
+ isp->isp_state = ISP_RUNSTATE;
+ }
+ if (isp->isp_state != ISP_RUNSTATE) {
+ isp_prt(isp, ISP_LOGERR,
+ "isp_reinit cannot restart card");
+ }
+ }
+ isp->isp_nactive = 0;
+
+ for (i = 0; i < isp->isp_maxcmds; i++) {
+ uint16_t handle;
+ xs = isp->isp_xflist[i];
+ if (xs == NULL) {
+ continue;
+ }
+ handle = isp_index_handle(i);
+ isp_destroy_handle(isp, handle);
+ if (XS_XFRLEN(xs)) {
+ ISP_DMAFREE(isp, xs, handle);
+ XS_RESID(xs) = XS_XFRLEN(xs);
+ } else {
+ XS_RESID(xs) = 0;
+ }
+ XS_SETERR(xs, HBA_BUSRESET);
+ isp_done(xs);
+ }
+}
+
+/*
+ * NVRAM Routines
+ */
+static int
+isp_read_nvram(ispsoftc_t *isp)
+{
+ int i, amt, retval;
+ uint8_t csum, minversion;
+ union {
+ uint8_t _x[ISP2100_NVRAM_SIZE];
+ uint16_t _s[ISP2100_NVRAM_SIZE>>1];
+ } _n;
+#define nvram_data _n._x
+#define nvram_words _n._s
+
+ if (IS_FC(isp)) {
+ amt = ISP2100_NVRAM_SIZE;
+ minversion = 1;
+ } else if (IS_ULTRA2(isp)) {
+ amt = ISP1080_NVRAM_SIZE;
+ minversion = 0;
+ } else {
+ amt = ISP_NVRAM_SIZE;
+ minversion = 2;
+ }
+
+ /*
+ * Just read the first two words first to see if we have a valid
+ * NVRAM to continue reading the rest with.
+ */
+ for (i = 0; i < 2; i++) {
+ isp_rdnvram_word(isp, i, &nvram_words[i]);
+ }
+ if (nvram_data[0] != 'I' || nvram_data[1] != 'S' ||
+ nvram_data[2] != 'P') {
+ if (isp->isp_bustype != ISP_BT_SBUS) {
+ isp_prt(isp, ISP_LOGWARN, "invalid NVRAM header");
+ isp_prt(isp, ISP_LOGDEBUG0, "%x %x %x",
+ nvram_data[0], nvram_data[1], nvram_data[2]);
+ }
+ retval = -1;
+ goto out;
+ }
+ for (i = 2; i < amt>>1; i++) {
+ isp_rdnvram_word(isp, i, &nvram_words[i]);
+ }
+ for (csum = 0, i = 0; i < amt; i++) {
+ csum += nvram_data[i];
+ }
+ if (csum != 0) {
+ isp_prt(isp, ISP_LOGWARN, "invalid NVRAM checksum");
+ retval = -1;
+ goto out;
+ }
+ if (ISP_NVRAM_VERSION(nvram_data) < minversion) {
+ isp_prt(isp, ISP_LOGWARN, "version %d NVRAM not understood",
+ ISP_NVRAM_VERSION(nvram_data));
+ retval = -1;
+ goto out;
+ }
+
+ if (IS_ULTRA3(isp)) {
+ isp_parse_nvram_12160(isp, 0, nvram_data);
+ if (IS_12160(isp))
+ isp_parse_nvram_12160(isp, 1, nvram_data);
+ } else if (IS_1080(isp)) {
+ isp_parse_nvram_1080(isp, 0, nvram_data);
+ } else if (IS_1280(isp) || IS_1240(isp)) {
+ isp_parse_nvram_1080(isp, 0, nvram_data);
+ isp_parse_nvram_1080(isp, 1, nvram_data);
+ } else if (IS_SCSI(isp)) {
+ isp_parse_nvram_1020(isp, nvram_data);
+ } else {
+ isp_parse_nvram_2100(isp, nvram_data);
+ }
+ retval = 0;
+out:
+ return (retval);
+#undef nvram_data
+#undef nvram_words
+}
+
+static void
+isp_rdnvram_word(ispsoftc_t *isp, int wo, uint16_t *rp)
+{
+ int i, cbits;
+ uint16_t bit, rqst, junk;
+
+ ISP_WRITE(isp, BIU_NVRAM, BIU_NVRAM_SELECT);
+ USEC_DELAY(10);
+ ISP_WRITE(isp, BIU_NVRAM, BIU_NVRAM_SELECT|BIU_NVRAM_CLOCK);
+ USEC_DELAY(10);
+
+ if (IS_FC(isp)) {
+ wo &= ((ISP2100_NVRAM_SIZE >> 1) - 1);
+ if (IS_2312(isp) && isp->isp_port) {
+ wo += 128;
+ }
+ rqst = (ISP_NVRAM_READ << 8) | wo;
+ cbits = 10;
+ } else if (IS_ULTRA2(isp)) {
+ wo &= ((ISP1080_NVRAM_SIZE >> 1) - 1);
+ rqst = (ISP_NVRAM_READ << 8) | wo;
+ cbits = 10;
+ } else {
+ wo &= ((ISP_NVRAM_SIZE >> 1) - 1);
+ rqst = (ISP_NVRAM_READ << 6) | wo;
+ cbits = 8;
+ }
+
+ /*
+ * Clock the word select request out...
+ */
+ for (i = cbits; i >= 0; i--) {
+ if ((rqst >> i) & 1) {
+ bit = BIU_NVRAM_SELECT | BIU_NVRAM_DATAOUT;
+ } else {
+ bit = BIU_NVRAM_SELECT;
+ }
+ ISP_WRITE(isp, BIU_NVRAM, bit);
+ USEC_DELAY(10);
+ junk = ISP_READ(isp, BIU_NVRAM); /* force PCI flush */
+ ISP_WRITE(isp, BIU_NVRAM, bit | BIU_NVRAM_CLOCK);
+ USEC_DELAY(10);
+ junk = ISP_READ(isp, BIU_NVRAM); /* force PCI flush */
+ ISP_WRITE(isp, BIU_NVRAM, bit);
+ USEC_DELAY(10);
+ junk = ISP_READ(isp, BIU_NVRAM); /* force PCI flush */
+ }
+ /*
+ * Now read the result back in (bits come back in MSB format).
+ */
+ *rp = 0;
+ for (i = 0; i < 16; i++) {
+ uint16_t rv;
+ *rp <<= 1;
+ ISP_WRITE(isp, BIU_NVRAM, BIU_NVRAM_SELECT|BIU_NVRAM_CLOCK);
+ USEC_DELAY(10);
+ rv = ISP_READ(isp, BIU_NVRAM);
+ if (rv & BIU_NVRAM_DATAIN) {
+ *rp |= 1;
+ }
+ USEC_DELAY(10);
+ ISP_WRITE(isp, BIU_NVRAM, BIU_NVRAM_SELECT);
+ USEC_DELAY(10);
+ junk = ISP_READ(isp, BIU_NVRAM); /* force PCI flush */
+ }
+ ISP_WRITE(isp, BIU_NVRAM, 0);
+ USEC_DELAY(10);
+ junk = ISP_READ(isp, BIU_NVRAM); /* force PCI flush */
+ ISP_SWIZZLE_NVRAM_WORD(isp, rp);
+}
+
+static void
+isp_parse_nvram_1020(ispsoftc_t *isp, uint8_t *nvram_data)
+{
+ sdparam *sdp = (sdparam *) isp->isp_param;
+ int tgt;
+
+ sdp->isp_fifo_threshold =
+ ISP_NVRAM_FIFO_THRESHOLD(nvram_data) |
+ (ISP_NVRAM_FIFO_THRESHOLD_128(nvram_data) << 2);
+
+ if ((isp->isp_confopts & ISP_CFG_OWNLOOPID) == 0)
+ sdp->isp_initiator_id =
+ ISP_NVRAM_INITIATOR_ID(nvram_data);
+
+ sdp->isp_bus_reset_delay =
+ ISP_NVRAM_BUS_RESET_DELAY(nvram_data);
+
+ sdp->isp_retry_count =
+ ISP_NVRAM_BUS_RETRY_COUNT(nvram_data);
+
+ sdp->isp_retry_delay =
+ ISP_NVRAM_BUS_RETRY_DELAY(nvram_data);
+
+ sdp->isp_async_data_setup =
+ ISP_NVRAM_ASYNC_DATA_SETUP_TIME(nvram_data);
+
+ if (isp->isp_type >= ISP_HA_SCSI_1040) {
+ if (sdp->isp_async_data_setup < 9) {
+ sdp->isp_async_data_setup = 9;
+ }
+ } else {
+ if (sdp->isp_async_data_setup != 6) {
+ sdp->isp_async_data_setup = 6;
+ }
+ }
+
+ sdp->isp_req_ack_active_neg =
+ ISP_NVRAM_REQ_ACK_ACTIVE_NEGATION(nvram_data);
+
+ sdp->isp_data_line_active_neg =
+ ISP_NVRAM_DATA_LINE_ACTIVE_NEGATION(nvram_data);
+
+ sdp->isp_data_dma_burst_enabl =
+ ISP_NVRAM_DATA_DMA_BURST_ENABLE(nvram_data);
+
+ sdp->isp_cmd_dma_burst_enable =
+ ISP_NVRAM_CMD_DMA_BURST_ENABLE(nvram_data);
+
+ sdp->isp_tag_aging =
+ ISP_NVRAM_TAG_AGE_LIMIT(nvram_data);
+
+ sdp->isp_selection_timeout =
+ ISP_NVRAM_SELECTION_TIMEOUT(nvram_data);
+
+ sdp->isp_max_queue_depth =
+ ISP_NVRAM_MAX_QUEUE_DEPTH(nvram_data);
+
+ sdp->isp_fast_mttr = ISP_NVRAM_FAST_MTTR_ENABLE(nvram_data);
+
+ isp_prt(isp, ISP_LOGDEBUG0, sc0, sc4,
+ 0, sdp->isp_fifo_threshold, sdp->isp_initiator_id,
+ sdp->isp_bus_reset_delay, sdp->isp_retry_count,
+ sdp->isp_retry_delay, sdp->isp_async_data_setup);
+ isp_prt(isp, ISP_LOGDEBUG0, sc1, sc4,
+ sdp->isp_req_ack_active_neg, sdp->isp_data_line_active_neg,
+ sdp->isp_data_dma_burst_enabl, sdp->isp_cmd_dma_burst_enable,
+ sdp->isp_selection_timeout, sdp->isp_max_queue_depth);
+
+ for (tgt = 0; tgt < MAX_TARGETS; tgt++) {
+ sdp->isp_devparam[tgt].dev_enable =
+ ISP_NVRAM_TGT_DEVICE_ENABLE(nvram_data, tgt);
+ sdp->isp_devparam[tgt].exc_throttle =
+ ISP_NVRAM_TGT_EXEC_THROTTLE(nvram_data, tgt);
+ sdp->isp_devparam[tgt].nvrm_offset =
+ ISP_NVRAM_TGT_SYNC_OFFSET(nvram_data, tgt);
+ sdp->isp_devparam[tgt].nvrm_period =
+ ISP_NVRAM_TGT_SYNC_PERIOD(nvram_data, tgt);
+ /*
+ * We probably shouldn't lie about this, but it
+ * it makes it much safer if we limit NVRAM values
+ * to sanity.
+ */
+ if (isp->isp_type < ISP_HA_SCSI_1040) {
+ /*
+ * If we're not ultra, we can't possibly
+ * be a shorter period than this.
+ */
+ if (sdp->isp_devparam[tgt].nvrm_period < 0x19) {
+ sdp->isp_devparam[tgt].nvrm_period = 0x19;
+ }
+ if (sdp->isp_devparam[tgt].nvrm_offset > 0xc) {
+ sdp->isp_devparam[tgt].nvrm_offset = 0x0c;
+ }
+ } else {
+ if (sdp->isp_devparam[tgt].nvrm_offset > 0x8) {
+ sdp->isp_devparam[tgt].nvrm_offset = 0x8;
+ }
+ }
+ sdp->isp_devparam[tgt].nvrm_flags = 0;
+ if (ISP_NVRAM_TGT_RENEG(nvram_data, tgt))
+ sdp->isp_devparam[tgt].nvrm_flags |= DPARM_RENEG;
+ sdp->isp_devparam[tgt].nvrm_flags |= DPARM_ARQ;
+ if (ISP_NVRAM_TGT_TQING(nvram_data, tgt))
+ sdp->isp_devparam[tgt].nvrm_flags |= DPARM_TQING;
+ if (ISP_NVRAM_TGT_SYNC(nvram_data, tgt))
+ sdp->isp_devparam[tgt].nvrm_flags |= DPARM_SYNC;
+ if (ISP_NVRAM_TGT_WIDE(nvram_data, tgt))
+ sdp->isp_devparam[tgt].nvrm_flags |= DPARM_WIDE;
+ if (ISP_NVRAM_TGT_PARITY(nvram_data, tgt))
+ sdp->isp_devparam[tgt].nvrm_flags |= DPARM_PARITY;
+ if (ISP_NVRAM_TGT_DISC(nvram_data, tgt))
+ sdp->isp_devparam[tgt].nvrm_flags |= DPARM_DISC;
+ sdp->isp_devparam[tgt].actv_flags = 0; /* we don't know */
+ isp_prt(isp, ISP_LOGDEBUG0, sc2, sc4,
+ 0, tgt, sdp->isp_devparam[tgt].nvrm_flags,
+ sdp->isp_devparam[tgt].nvrm_offset,
+ sdp->isp_devparam[tgt].nvrm_period);
+ sdp->isp_devparam[tgt].goal_offset =
+ sdp->isp_devparam[tgt].nvrm_offset;
+ sdp->isp_devparam[tgt].goal_period =
+ sdp->isp_devparam[tgt].nvrm_period;
+ sdp->isp_devparam[tgt].goal_flags =
+ sdp->isp_devparam[tgt].nvrm_flags;
+ }
+}
+
+static void
+isp_parse_nvram_1080(ispsoftc_t *isp, int bus, uint8_t *nvram_data)
+{
+ sdparam *sdp = (sdparam *) isp->isp_param;
+ int tgt;
+
+ sdp += bus;
+
+ sdp->isp_fifo_threshold =
+ ISP1080_NVRAM_FIFO_THRESHOLD(nvram_data);
+
+ if ((isp->isp_confopts & ISP_CFG_OWNLOOPID) == 0)
+ sdp->isp_initiator_id =
+ ISP1080_NVRAM_INITIATOR_ID(nvram_data, bus);
+
+ sdp->isp_bus_reset_delay =
+ ISP1080_NVRAM_BUS_RESET_DELAY(nvram_data, bus);
+
+ sdp->isp_retry_count =
+ ISP1080_NVRAM_BUS_RETRY_COUNT(nvram_data, bus);
+
+ sdp->isp_retry_delay =
+ ISP1080_NVRAM_BUS_RETRY_DELAY(nvram_data, bus);
+
+ sdp->isp_async_data_setup =
+ ISP1080_NVRAM_ASYNC_DATA_SETUP_TIME(nvram_data, bus);
+
+ sdp->isp_req_ack_active_neg =
+ ISP1080_NVRAM_REQ_ACK_ACTIVE_NEGATION(nvram_data, bus);
+
+ sdp->isp_data_line_active_neg =
+ ISP1080_NVRAM_DATA_LINE_ACTIVE_NEGATION(nvram_data, bus);
+
+ sdp->isp_data_dma_burst_enabl =
+ ISP1080_NVRAM_BURST_ENABLE(nvram_data);
+
+ sdp->isp_cmd_dma_burst_enable =
+ ISP1080_NVRAM_BURST_ENABLE(nvram_data);
+
+ sdp->isp_selection_timeout =
+ ISP1080_NVRAM_SELECTION_TIMEOUT(nvram_data, bus);
+
+ sdp->isp_max_queue_depth =
+ ISP1080_NVRAM_MAX_QUEUE_DEPTH(nvram_data, bus);
+
+ isp_prt(isp, ISP_LOGDEBUG0, sc0, sc4,
+ bus, sdp->isp_fifo_threshold, sdp->isp_initiator_id,
+ sdp->isp_bus_reset_delay, sdp->isp_retry_count,
+ sdp->isp_retry_delay, sdp->isp_async_data_setup);
+ isp_prt(isp, ISP_LOGDEBUG0, sc1, sc4,
+ sdp->isp_req_ack_active_neg, sdp->isp_data_line_active_neg,
+ sdp->isp_data_dma_burst_enabl, sdp->isp_cmd_dma_burst_enable,
+ sdp->isp_selection_timeout, sdp->isp_max_queue_depth);
+
+
+ for (tgt = 0; tgt < MAX_TARGETS; tgt++) {
+ sdp->isp_devparam[tgt].dev_enable =
+ ISP1080_NVRAM_TGT_DEVICE_ENABLE(nvram_data, tgt, bus);
+ sdp->isp_devparam[tgt].exc_throttle =
+ ISP1080_NVRAM_TGT_EXEC_THROTTLE(nvram_data, tgt, bus);
+ sdp->isp_devparam[tgt].nvrm_offset =
+ ISP1080_NVRAM_TGT_SYNC_OFFSET(nvram_data, tgt, bus);
+ sdp->isp_devparam[tgt].nvrm_period =
+ ISP1080_NVRAM_TGT_SYNC_PERIOD(nvram_data, tgt, bus);
+ sdp->isp_devparam[tgt].nvrm_flags = 0;
+ if (ISP1080_NVRAM_TGT_RENEG(nvram_data, tgt, bus))
+ sdp->isp_devparam[tgt].nvrm_flags |= DPARM_RENEG;
+ sdp->isp_devparam[tgt].nvrm_flags |= DPARM_ARQ;
+ if (ISP1080_NVRAM_TGT_TQING(nvram_data, tgt, bus))
+ sdp->isp_devparam[tgt].nvrm_flags |= DPARM_TQING;
+ if (ISP1080_NVRAM_TGT_SYNC(nvram_data, tgt, bus))
+ sdp->isp_devparam[tgt].nvrm_flags |= DPARM_SYNC;
+ if (ISP1080_NVRAM_TGT_WIDE(nvram_data, tgt, bus))
+ sdp->isp_devparam[tgt].nvrm_flags |= DPARM_WIDE;
+ if (ISP1080_NVRAM_TGT_PARITY(nvram_data, tgt, bus))
+ sdp->isp_devparam[tgt].nvrm_flags |= DPARM_PARITY;
+ if (ISP1080_NVRAM_TGT_DISC(nvram_data, tgt, bus))
+ sdp->isp_devparam[tgt].nvrm_flags |= DPARM_DISC;
+ sdp->isp_devparam[tgt].actv_flags = 0;
+ isp_prt(isp, ISP_LOGDEBUG0, sc2, sc4,
+ bus, tgt, sdp->isp_devparam[tgt].nvrm_flags,
+ sdp->isp_devparam[tgt].nvrm_offset,
+ sdp->isp_devparam[tgt].nvrm_period);
+ sdp->isp_devparam[tgt].goal_offset =
+ sdp->isp_devparam[tgt].nvrm_offset;
+ sdp->isp_devparam[tgt].goal_period =
+ sdp->isp_devparam[tgt].nvrm_period;
+ sdp->isp_devparam[tgt].goal_flags =
+ sdp->isp_devparam[tgt].nvrm_flags;
+ }
+}
+
+static void
+isp_parse_nvram_12160(ispsoftc_t *isp, int bus, uint8_t *nvram_data)
+{
+ sdparam *sdp = (sdparam *) isp->isp_param;
+ int tgt;
+
+ sdp += bus;
+
+ sdp->isp_fifo_threshold =
+ ISP12160_NVRAM_FIFO_THRESHOLD(nvram_data);
+
+ if ((isp->isp_confopts & ISP_CFG_OWNLOOPID) == 0)
+ sdp->isp_initiator_id =
+ ISP12160_NVRAM_INITIATOR_ID(nvram_data, bus);
+
+ sdp->isp_bus_reset_delay =
+ ISP12160_NVRAM_BUS_RESET_DELAY(nvram_data, bus);
+
+ sdp->isp_retry_count =
+ ISP12160_NVRAM_BUS_RETRY_COUNT(nvram_data, bus);
+
+ sdp->isp_retry_delay =
+ ISP12160_NVRAM_BUS_RETRY_DELAY(nvram_data, bus);
+
+ sdp->isp_async_data_setup =
+ ISP12160_NVRAM_ASYNC_DATA_SETUP_TIME(nvram_data, bus);
+
+ sdp->isp_req_ack_active_neg =
+ ISP12160_NVRAM_REQ_ACK_ACTIVE_NEGATION(nvram_data, bus);
+
+ sdp->isp_data_line_active_neg =
+ ISP12160_NVRAM_DATA_LINE_ACTIVE_NEGATION(nvram_data, bus);
+
+ sdp->isp_data_dma_burst_enabl =
+ ISP12160_NVRAM_BURST_ENABLE(nvram_data);
+
+ sdp->isp_cmd_dma_burst_enable =
+ ISP12160_NVRAM_BURST_ENABLE(nvram_data);
+
+ sdp->isp_selection_timeout =
+ ISP12160_NVRAM_SELECTION_TIMEOUT(nvram_data, bus);
+
+ sdp->isp_max_queue_depth =
+ ISP12160_NVRAM_MAX_QUEUE_DEPTH(nvram_data, bus);
+
+ isp_prt(isp, ISP_LOGDEBUG0, sc0, sc4,
+ bus, sdp->isp_fifo_threshold, sdp->isp_initiator_id,
+ sdp->isp_bus_reset_delay, sdp->isp_retry_count,
+ sdp->isp_retry_delay, sdp->isp_async_data_setup);
+ isp_prt(isp, ISP_LOGDEBUG0, sc1, sc4,
+ sdp->isp_req_ack_active_neg, sdp->isp_data_line_active_neg,
+ sdp->isp_data_dma_burst_enabl, sdp->isp_cmd_dma_burst_enable,
+ sdp->isp_selection_timeout, sdp->isp_max_queue_depth);
+
+ for (tgt = 0; tgt < MAX_TARGETS; tgt++) {
+ sdp->isp_devparam[tgt].dev_enable =
+ ISP12160_NVRAM_TGT_DEVICE_ENABLE(nvram_data, tgt, bus);
+ sdp->isp_devparam[tgt].exc_throttle =
+ ISP12160_NVRAM_TGT_EXEC_THROTTLE(nvram_data, tgt, bus);
+ sdp->isp_devparam[tgt].nvrm_offset =
+ ISP12160_NVRAM_TGT_SYNC_OFFSET(nvram_data, tgt, bus);
+ sdp->isp_devparam[tgt].nvrm_period =
+ ISP12160_NVRAM_TGT_SYNC_PERIOD(nvram_data, tgt, bus);
+ sdp->isp_devparam[tgt].nvrm_flags = 0;
+ if (ISP12160_NVRAM_TGT_RENEG(nvram_data, tgt, bus))
+ sdp->isp_devparam[tgt].nvrm_flags |= DPARM_RENEG;
+ sdp->isp_devparam[tgt].nvrm_flags |= DPARM_ARQ;
+ if (ISP12160_NVRAM_TGT_TQING(nvram_data, tgt, bus))
+ sdp->isp_devparam[tgt].nvrm_flags |= DPARM_TQING;
+ if (ISP12160_NVRAM_TGT_SYNC(nvram_data, tgt, bus))
+ sdp->isp_devparam[tgt].nvrm_flags |= DPARM_SYNC;
+ if (ISP12160_NVRAM_TGT_WIDE(nvram_data, tgt, bus))
+ sdp->isp_devparam[tgt].nvrm_flags |= DPARM_WIDE;
+ if (ISP12160_NVRAM_TGT_PARITY(nvram_data, tgt, bus))
+ sdp->isp_devparam[tgt].nvrm_flags |= DPARM_PARITY;
+ if (ISP12160_NVRAM_TGT_DISC(nvram_data, tgt, bus))
+ sdp->isp_devparam[tgt].nvrm_flags |= DPARM_DISC;
+ sdp->isp_devparam[tgt].actv_flags = 0;
+ isp_prt(isp, ISP_LOGDEBUG0, sc2, sc4,
+ bus, tgt, sdp->isp_devparam[tgt].nvrm_flags,
+ sdp->isp_devparam[tgt].nvrm_offset,
+ sdp->isp_devparam[tgt].nvrm_period);
+ sdp->isp_devparam[tgt].goal_offset =
+ sdp->isp_devparam[tgt].nvrm_offset;
+ sdp->isp_devparam[tgt].goal_period =
+ sdp->isp_devparam[tgt].nvrm_period;
+ sdp->isp_devparam[tgt].goal_flags =
+ sdp->isp_devparam[tgt].nvrm_flags;
+ }
+}
+
+static void
+isp_parse_nvram_2100(ispsoftc_t *isp, uint8_t *nvram_data)
+{
+ fcparam *fcp = (fcparam *) isp->isp_param;
+ uint64_t wwn;
+
+ /*
+ * There is NVRAM storage for both Port and Node entities-
+ * but the Node entity appears to be unused on all the cards
+ * I can find. However, we should account for this being set
+ * at some point in the future.
+ *
+ * Qlogic WWNs have an NAA of 2, but usually nothing shows up in
+ * bits 48..60. In the case of the 2202, it appears that they do
+ * use bit 48 to distinguish between the two instances on the card.
+ * The 2204, which I've never seen, *probably* extends this method.
+ */
+ wwn = ISP2100_NVRAM_PORT_NAME(nvram_data);
+ if (wwn) {
+ isp_prt(isp, ISP_LOGCONFIG, "NVRAM Port WWN 0x%08x%08x",
+ (uint32_t) (wwn >> 32), (uint32_t) (wwn & 0xffffffff));
+ if ((wwn >> 60) == 0) {
+ wwn |= (((uint64_t) 2)<< 60);
+ }
+ }
+ fcp->isp_portwwn = wwn;
+ if (IS_2200(isp) || IS_23XX(isp)) {
+ wwn = ISP2100_NVRAM_NODE_NAME(nvram_data);
+ if (wwn) {
+ isp_prt(isp, ISP_LOGCONFIG, "NVRAM Node WWN 0x%08x%08x",
+ (uint32_t) (wwn >> 32),
+ (uint32_t) (wwn & 0xffffffff));
+ if ((wwn >> 60) == 0) {
+ wwn |= (((uint64_t) 2)<< 60);
+ }
+ }
+ } else {
+ wwn &= ~((uint64_t) 0xfff << 48);
+ }
+ fcp->isp_nodewwn = wwn;
+
+ /*
+ * Make sure we have both Node and Port as non-zero values.
+ */
+ if (fcp->isp_nodewwn != 0 && fcp->isp_portwwn == 0) {
+ fcp->isp_portwwn = fcp->isp_nodewwn;
+ } else if (fcp->isp_nodewwn == 0 && fcp->isp_portwwn != 0) {
+ fcp->isp_nodewwn = fcp->isp_portwwn;
+ }
+
+ /*
+ * Make the Node and Port values sane if they're NAA == 2.
+ * This means to clear bits 48..56 for the Node WWN and
+ * make sure that there's some non-zero value in 48..56
+ * for the Port WWN.
+ */
+ if (fcp->isp_nodewwn && fcp->isp_portwwn) {
+ if ((fcp->isp_nodewwn & (((uint64_t) 0xfff) << 48)) != 0 &&
+ (fcp->isp_nodewwn >> 60) == 2) {
+ fcp->isp_nodewwn &= ~((uint64_t) 0xfff << 48);
+ }
+ if ((fcp->isp_portwwn & (((uint64_t) 0xfff) << 48)) == 0 &&
+ (fcp->isp_portwwn >> 60) == 2) {
+ fcp->isp_portwwn |= ((uint64_t) 1 << 56);
+ }
+ }
+
+ fcp->isp_maxalloc =
+ ISP2100_NVRAM_MAXIOCBALLOCATION(nvram_data);
+ if ((isp->isp_confopts & ISP_CFG_OWNFSZ) == 0)
+ fcp->isp_maxfrmlen =
+ ISP2100_NVRAM_MAXFRAMELENGTH(nvram_data);
+ fcp->isp_retry_delay =
+ ISP2100_NVRAM_RETRY_DELAY(nvram_data);
+ fcp->isp_retry_count =
+ ISP2100_NVRAM_RETRY_COUNT(nvram_data);
+ if ((isp->isp_confopts & ISP_CFG_OWNLOOPID) == 0)
+ fcp->isp_loopid =
+ ISP2100_NVRAM_HARDLOOPID(nvram_data);
+ if ((isp->isp_confopts & ISP_CFG_OWNEXCTHROTTLE) == 0)
+ fcp->isp_execthrottle =
+ ISP2100_NVRAM_EXECUTION_THROTTLE(nvram_data);
+ fcp->isp_fwoptions = ISP2100_NVRAM_OPTIONS(nvram_data);
+ isp_prt(isp, ISP_LOGDEBUG0,
+ "NVRAM 0x%08x%08x 0x%08x%08x maxalloc %d maxframelen %d",
+ (uint32_t) (fcp->isp_nodewwn >> 32), (uint32_t) fcp->isp_nodewwn,
+ (uint32_t) (fcp->isp_portwwn >> 32), (uint32_t) fcp->isp_portwwn,
+ ISP2100_NVRAM_MAXIOCBALLOCATION(nvram_data),
+ ISP2100_NVRAM_MAXFRAMELENGTH(nvram_data));
+ isp_prt(isp, ISP_LOGDEBUG0,
+ "execthrottle %d fwoptions 0x%x hardloop %d tov %d",
+ ISP2100_NVRAM_EXECUTION_THROTTLE(nvram_data),
+ ISP2100_NVRAM_OPTIONS(nvram_data),
+ ISP2100_NVRAM_HARDLOOPID(nvram_data),
+ ISP2100_NVRAM_TOV(nvram_data));
+ fcp->isp_xfwoptions = ISP2100_XFW_OPTIONS(nvram_data);
+ fcp->isp_zfwoptions = ISP2100_ZFW_OPTIONS(nvram_data);
+ isp_prt(isp, ISP_LOGDEBUG0,
+ "xfwoptions 0x%x zfw options 0x%x",
+ ISP2100_XFW_OPTIONS(nvram_data), ISP2100_ZFW_OPTIONS(nvram_data));
+}
+
+#ifdef ISP_FW_CRASH_DUMP
+static void isp2200_fw_dump(ispsoftc_t *);
+static void isp2300_fw_dump(ispsoftc_t *);
+
+static void
+isp2200_fw_dump(ispsoftc_t *isp)
+{
+ int i, j;
+ mbreg_t mbs;
+ uint16_t *ptr;
+
+ MEMZERO(&mbs, sizeof (mbs));
+ ptr = FCPARAM(isp)->isp_dump_data;
+ if (ptr == NULL) {
+ isp_prt(isp, ISP_LOGERR,
+ "No place to dump RISC registers and SRAM");
+ return;
+ }
+ if (*ptr++) {
+ isp_prt(isp, ISP_LOGERR,
+ "dump area for RISC registers and SRAM already used");
+ return;
+ }
+ ISP_WRITE(isp, HCCR, HCCR_CMD_PAUSE);
+ for (i = 0; i < 100; i++) {
+ USEC_DELAY(100);
+ if (ISP_READ(isp, HCCR) & HCCR_PAUSE) {
+ break;
+ }
+ }
+ if (ISP_READ(isp, HCCR) & HCCR_PAUSE) {
+ /*
+ * PBIU Registers
+ */
+ for (i = 0; i < 8; i++) {
+ *ptr++ = ISP_READ(isp, BIU_BLOCK + (i << 1));
+ }
+
+ /*
+ * Mailbox Registers
+ */
+ for (i = 0; i < 8; i++) {
+ *ptr++ = ISP_READ(isp, MBOX_BLOCK + (i << 1));
+ }
+
+ /*
+ * DMA Registers
+ */
+ for (i = 0; i < 48; i++) {
+ *ptr++ = ISP_READ(isp, DMA_BLOCK + 0x20 + (i << 1));
+ }
+
+ /*
+ * RISC H/W Registers
+ */
+ ISP_WRITE(isp, BIU2100_CSR, 0);
+ for (i = 0; i < 16; i++) {
+ *ptr++ = ISP_READ(isp, BIU_BLOCK + 0xA0 + (i << 1));
+ }
+
+ /*
+ * RISC GP Registers
+ */
+ for (j = 0; j < 8; j++) {
+ ISP_WRITE(isp, BIU_BLOCK + 0xA4, 0x2000 + (j << 8));
+ for (i = 0; i < 16; i++) {
+ *ptr++ =
+ ISP_READ(isp, BIU_BLOCK + 0x80 + (i << 1));
+ }
+ }
+
+ /*
+ * Frame Buffer Hardware Registers
+ */
+ ISP_WRITE(isp, BIU2100_CSR, 0x10);
+ for (i = 0; i < 16; i++) {
+ *ptr++ = ISP_READ(isp, BIU_BLOCK + 0x80 + (i << 1));
+ }
+
+ /*
+ * Fibre Protocol Module 0 Hardware Registers
+ */
+ ISP_WRITE(isp, BIU2100_CSR, 0x20);
+ for (i = 0; i < 64; i++) {
+ *ptr++ = ISP_READ(isp, BIU_BLOCK + 0x80 + (i << 1));
+ }
+
+ /*
+ * Fibre Protocol Module 1 Hardware Registers
+ */
+ ISP_WRITE(isp, BIU2100_CSR, 0x30);
+ for (i = 0; i < 64; i++) {
+ *ptr++ = ISP_READ(isp, BIU_BLOCK + 0x80 + (i << 1));
+ }
+ } else {
+ isp_prt(isp, ISP_LOGERR, "RISC Would Not Pause");
+ return;
+ }
+ isp_prt(isp, ISP_LOGALL,
+ "isp_fw_dump: RISC registers dumped successfully");
+ ISP_WRITE(isp, BIU2100_CSR, BIU2100_SOFT_RESET);
+ for (i = 0; i < 100; i++) {
+ USEC_DELAY(100);
+ if (ISP_READ(isp, OUTMAILBOX0) == 0) {
+ break;
+ }
+ }
+ if (ISP_READ(isp, OUTMAILBOX0) != 0) {
+ isp_prt(isp, ISP_LOGERR, "Board Would Not Reset");
+ return;
+ }
+ ISP_WRITE(isp, HCCR, HCCR_CMD_PAUSE);
+ for (i = 0; i < 100; i++) {
+ USEC_DELAY(100);
+ if (ISP_READ(isp, HCCR) & HCCR_PAUSE) {
+ break;
+ }
+ }
+ if ((ISP_READ(isp, HCCR) & HCCR_PAUSE) == 0) {
+ isp_prt(isp, ISP_LOGERR, "RISC Would Not Pause After Reset");
+ return;
+ }
+ ISP_WRITE(isp, RISC_EMB, 0xf2);
+ ISP_WRITE(isp, HCCR, HCCR_CMD_RELEASE);
+ for (i = 0; i < 100; i++) {
+ USEC_DELAY(100);
+ if ((ISP_READ(isp, HCCR) & HCCR_PAUSE) == 0) {
+ break;
+ }
+ }
+ ENABLE_INTS(isp);
+ mbs.param[0] = MBOX_READ_RAM_WORD;
+ mbs.param[1] = 0x1000;
+ isp->isp_mbxworkp = (void *) ptr;
+ isp->isp_mbxwrk0 = 0xefff; /* continuation count */
+ isp->isp_mbxwrk1 = 0x1001; /* next SRAM address */
+ isp_control(isp, ISPCTL_RUN_MBOXCMD, &mbs);
+ if (mbs.param[0] != MBOX_COMMAND_COMPLETE) {
+ isp_prt(isp, ISP_LOGWARN,
+ "RAM DUMP FAILED @ WORD %x", isp->isp_mbxwrk1);
+ return;
+ }
+ ptr = isp->isp_mbxworkp; /* finish fetch of final word */
+ *ptr++ = isp->isp_mboxtmp[2];
+ isp_prt(isp, ISP_LOGALL, "isp_fw_dump: SRAM dumped successfully");
+ FCPARAM(isp)->isp_dump_data[0] = isp->isp_type; /* now used */
+ (void) isp_async(isp, ISPASYNC_FW_DUMPED, 0);
+}
+
+static void
+isp2300_fw_dump(ispsoftc_t *isp)
+{
+ int i, j;
+ mbreg_t mbs;
+ uint16_t *ptr;
+
+ MEMZERO(&mbs, sizeof (mbs));
+ ptr = FCPARAM(isp)->isp_dump_data;
+ if (ptr == NULL) {
+ isp_prt(isp, ISP_LOGERR,
+ "No place to dump RISC registers and SRAM");
+ return;
+ }
+ if (*ptr++) {
+ isp_prt(isp, ISP_LOGERR,
+ "dump area for RISC registers and SRAM already used");
+ return;
+ }
+ ISP_WRITE(isp, HCCR, HCCR_CMD_PAUSE);
+ for (i = 0; i < 100; i++) {
+ USEC_DELAY(100);
+ if (ISP_READ(isp, HCCR) & HCCR_PAUSE) {
+ break;
+ }
+ }
+ if (ISP_READ(isp, HCCR) & HCCR_PAUSE) {
+ /*
+ * PBIU registers
+ */
+ for (i = 0; i < 8; i++) {
+ *ptr++ = ISP_READ(isp, BIU_BLOCK + (i << 1));
+ }
+
+ /*
+ * ReqQ-RspQ-Risc2Host Status registers
+ */
+ for (i = 0; i < 8; i++) {
+ *ptr++ = ISP_READ(isp, BIU_BLOCK + 0x10 + (i << 1));
+ }
+
+ /*
+ * Mailbox Registers
+ */
+ for (i = 0; i < 32; i++) {
+ *ptr++ =
+ ISP_READ(isp, PCI_MBOX_REGS2300_OFF + (i << 1));
+ }
+
+ /*
+ * Auto Request Response DMA registers
+ */
+ ISP_WRITE(isp, BIU2100_CSR, 0x40);
+ for (i = 0; i < 32; i++) {
+ *ptr++ = ISP_READ(isp, BIU_BLOCK + 0x80 + (i << 1));
+ }
+
+ /*
+ * DMA registers
+ */
+ ISP_WRITE(isp, BIU2100_CSR, 0x50);
+ for (i = 0; i < 48; i++) {
+ *ptr++ = ISP_READ(isp, BIU_BLOCK + 0x80 + (i << 1));
+ }
+
+ /*
+ * RISC hardware registers
+ */
+ ISP_WRITE(isp, BIU2100_CSR, 0);
+ for (i = 0; i < 16; i++) {
+ *ptr++ = ISP_READ(isp, BIU_BLOCK + 0xA0 + (i << 1));
+ }
+
+ /*
+ * RISC GP? registers
+ */
+ for (j = 0; j < 8; j++) {
+ ISP_WRITE(isp, BIU_BLOCK + 0xA4, 0x2000 + (j << 9));
+ for (i = 0; i < 16; i++) {
+ *ptr++ =
+ ISP_READ(isp, BIU_BLOCK + 0x80 + (i << 1));
+ }
+ }
+
+ /*
+ * frame buffer hardware registers
+ */
+ ISP_WRITE(isp, BIU2100_CSR, 0x10);
+ for (i = 0; i < 64; i++) {
+ *ptr++ = ISP_READ(isp, BIU_BLOCK + 0x80 + (i << 1));
+ }
+
+ /*
+ * FPM B0 hardware registers
+ */
+ ISP_WRITE(isp, BIU2100_CSR, 0x20);
+ for (i = 0; i < 64; i++) {
+ *ptr++ = ISP_READ(isp, BIU_BLOCK + 0x80 + (i << 1));
+ }
+
+ /*
+ * FPM B1 hardware registers
+ */
+ ISP_WRITE(isp, BIU2100_CSR, 0x30);
+ for (i = 0; i < 64; i++) {
+ *ptr++ = ISP_READ(isp, BIU_BLOCK + 0x80 + (i << 1));
+ }
+ } else {
+ isp_prt(isp, ISP_LOGERR, "RISC Would Not Pause");
+ return;
+ }
+ isp_prt(isp, ISP_LOGALL,
+ "isp_fw_dump: RISC registers dumped successfully");
+ ISP_WRITE(isp, BIU2100_CSR, BIU2100_SOFT_RESET);
+ for (i = 0; i < 100; i++) {
+ USEC_DELAY(100);
+ if (ISP_READ(isp, OUTMAILBOX0) == 0) {
+ break;
+ }
+ }
+ if (ISP_READ(isp, OUTMAILBOX0) != 0) {
+ isp_prt(isp, ISP_LOGERR, "Board Would Not Reset");
+ return;
+ }
+ ENABLE_INTS(isp);
+ MEMZERO(&mbs, sizeof (mbs));
+ mbs.param[0] = MBOX_READ_RAM_WORD;
+ mbs.param[1] = 0x800;
+ isp->isp_mbxworkp = (void *) ptr;
+ isp->isp_mbxwrk0 = 0xf7ff; /* continuation count */
+ isp->isp_mbxwrk1 = 0x801; /* next SRAM address */
+ isp_control(isp, ISPCTL_RUN_MBOXCMD, &mbs);
+ if (mbs.param[0] != MBOX_COMMAND_COMPLETE) {
+ isp_prt(isp, ISP_LOGWARN,
+ "RAM DUMP FAILED @ WORD %x", isp->isp_mbxwrk1);
+ return;
+ }
+ ptr = isp->isp_mbxworkp; /* finish fetch of final word */
+ *ptr++ = isp->isp_mboxtmp[2];
+ MEMZERO(&mbs, sizeof (mbs));
+ mbs.param[0] = MBOX_READ_RAM_WORD_EXTENDED;
+ mbs.param[8] = 1;
+ isp->isp_mbxworkp = (void *) ptr;
+ isp->isp_mbxwrk0 = 0xffff; /* continuation count */
+ isp->isp_mbxwrk1 = 0x1; /* next SRAM address */
+ isp->isp_mbxwrk8 = 0x1;
+ isp_control(isp, ISPCTL_RUN_MBOXCMD, &mbs);
+ if (mbs.param[0] != MBOX_COMMAND_COMPLETE) {
+ isp_prt(isp, ISP_LOGWARN,
+ "RAM DUMP FAILED @ WORD %x", 0x10000 + isp->isp_mbxwrk1);
+ return;
+ }
+ ptr = isp->isp_mbxworkp; /* finish final word */
+ *ptr++ = mbs.param[2];
+ isp_prt(isp, ISP_LOGALL, "isp_fw_dump: SRAM dumped successfully");
+ FCPARAM(isp)->isp_dump_data[0] = isp->isp_type; /* now used */
+ (void) isp_async(isp, ISPASYNC_FW_DUMPED, 0);
+}
+
+void
+isp_fw_dump(ispsoftc_t *isp)
+{
+ if (IS_2200(isp))
+ isp2200_fw_dump(isp);
+ else if (IS_23XX(isp))
+ isp2300_fw_dump(isp);
+}
+#endif
diff --git a/sys/dev/isp/isp_freebsd.c b/sys/dev/isp/isp_freebsd.c
new file mode 100644
index 000000000000..af81b135acaa
--- /dev/null
+++ b/sys/dev/isp/isp_freebsd.c
@@ -0,0 +1,3257 @@
+/*-
+ *
+ * Copyright (c) 1997-2006 by Matthew Jacob
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice 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.
+ *
+ * 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.
+ */
+
+/*
+ * Platform (FreeBSD) dependent common attachment code for Qlogic adapters.
+ */
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+#include <dev/isp/isp_freebsd.h>
+#include <sys/unistd.h>
+#include <sys/kthread.h>
+#include <machine/stdarg.h> /* for use by isp_prt below */
+#include <sys/conf.h>
+#include <sys/module.h>
+#include <sys/ioccom.h>
+#include <dev/isp/isp_ioctl.h>
+
+
+MODULE_VERSION(isp, 1);
+MODULE_DEPEND(isp, cam, 1, 1, 1);
+int isp_announced = 0;
+
+static d_ioctl_t ispioctl;
+static void isp_intr_enable(void *);
+static void isp_cam_async(void *, uint32_t, struct cam_path *, void *);
+static void isp_poll(struct cam_sim *);
+static timeout_t isp_watchdog;
+static void isp_kthread(void *);
+static void isp_action(struct cam_sim *, union ccb *);
+
+#if __FreeBSD_version < 700000
+ispfwfunc *isp_get_firmware_p = NULL;
+#endif
+
+#if __FreeBSD_version < 500000
+#define ISP_CDEV_MAJOR 248
+static struct cdevsw isp_cdevsw = {
+ /* open */ nullopen,
+ /* close */ nullclose,
+ /* read */ noread,
+ /* write */ nowrite,
+ /* ioctl */ ispioctl,
+ /* poll */ nopoll,
+ /* mmap */ nommap,
+ /* strategy */ nostrategy,
+ /* name */ "isp",
+ /* maj */ ISP_CDEV_MAJOR,
+ /* dump */ nodump,
+ /* psize */ nopsize,
+ /* flags */ D_TAPE,
+};
+#else
+static struct cdevsw isp_cdevsw = {
+ .d_version = D_VERSION,
+ .d_flags = D_NEEDGIANT,
+ .d_ioctl = ispioctl,
+ .d_name = "isp",
+};
+#endif
+
+static ispsoftc_t *isplist = NULL;
+
+void
+isp_attach(ispsoftc_t *isp)
+{
+ int primary, secondary;
+ struct ccb_setasync csa;
+ struct cam_devq *devq;
+ struct cam_sim *sim;
+ struct cam_path *path;
+
+ /*
+ * Establish (in case of 12X0) which bus is the primary.
+ */
+
+ primary = 0;
+ secondary = 1;
+
+ /*
+ * Create the device queue for our SIM(s).
+ */
+ devq = cam_simq_alloc(isp->isp_maxcmds);
+ if (devq == NULL) {
+ return;
+ }
+
+ /*
+ * Construct our SIM entry.
+ */
+ ISPLOCK_2_CAMLOCK(isp);
+ sim = cam_sim_alloc(isp_action, isp_poll, "isp", isp,
+ device_get_unit(isp->isp_dev), 1, isp->isp_maxcmds, devq);
+ if (sim == NULL) {
+ cam_simq_free(devq);
+ CAMLOCK_2_ISPLOCK(isp);
+ return;
+ }
+ CAMLOCK_2_ISPLOCK(isp);
+
+ isp->isp_osinfo.ehook.ich_func = isp_intr_enable;
+ isp->isp_osinfo.ehook.ich_arg = isp;
+ ISPLOCK_2_CAMLOCK(isp);
+ if (config_intrhook_establish(&isp->isp_osinfo.ehook) != 0) {
+ cam_sim_free(sim, TRUE);
+ CAMLOCK_2_ISPLOCK(isp);
+ isp_prt(isp, ISP_LOGERR,
+ "could not establish interrupt enable hook");
+ return;
+ }
+
+ if (xpt_bus_register(sim, primary) != CAM_SUCCESS) {
+ cam_sim_free(sim, TRUE);
+ CAMLOCK_2_ISPLOCK(isp);
+ return;
+ }
+
+ if (xpt_create_path(&path, NULL, cam_sim_path(sim),
+ CAM_TARGET_WILDCARD, CAM_LUN_WILDCARD) != CAM_REQ_CMP) {
+ xpt_bus_deregister(cam_sim_path(sim));
+ cam_sim_free(sim, TRUE);
+ config_intrhook_disestablish(&isp->isp_osinfo.ehook);
+ CAMLOCK_2_ISPLOCK(isp);
+ return;
+ }
+
+ xpt_setup_ccb(&csa.ccb_h, path, 5);
+ csa.ccb_h.func_code = XPT_SASYNC_CB;
+ csa.event_enable = AC_LOST_DEVICE;
+ csa.callback = isp_cam_async;
+ csa.callback_arg = sim;
+ xpt_action((union ccb *)&csa);
+ CAMLOCK_2_ISPLOCK(isp);
+ isp->isp_sim = sim;
+ isp->isp_path = path;
+ /*
+ * Create a kernel thread for fibre channel instances. We
+ * don't have dual channel FC cards.
+ */
+ if (IS_FC(isp)) {
+ ISPLOCK_2_CAMLOCK(isp);
+#if __FreeBSD_version >= 500000
+ /* XXX: LOCK VIOLATION */
+ cv_init(&isp->isp_osinfo.kthread_cv, "isp_kthread_cv");
+ if (kthread_create(isp_kthread, isp, &isp->isp_osinfo.kproc,
+ RFHIGHPID, 0, "%s: fc_thrd",
+ device_get_nameunit(isp->isp_dev)))
+#else
+ if (kthread_create(isp_kthread, isp, &isp->isp_osinfo.kproc,
+ "%s: fc_thrd", device_get_nameunit(isp->isp_dev)))
+#endif
+ {
+ xpt_bus_deregister(cam_sim_path(sim));
+ cam_sim_free(sim, TRUE);
+ config_intrhook_disestablish(&isp->isp_osinfo.ehook);
+ CAMLOCK_2_ISPLOCK(isp);
+ isp_prt(isp, ISP_LOGERR, "could not create kthread");
+ return;
+ }
+ CAMLOCK_2_ISPLOCK(isp);
+ }
+
+
+ /*
+ * If we have a second channel, construct SIM entry for that.
+ */
+ if (IS_DUALBUS(isp)) {
+ ISPLOCK_2_CAMLOCK(isp);
+ sim = cam_sim_alloc(isp_action, isp_poll, "isp", isp,
+ device_get_unit(isp->isp_dev), 1, isp->isp_maxcmds, devq);
+ if (sim == NULL) {
+ xpt_bus_deregister(cam_sim_path(isp->isp_sim));
+ xpt_free_path(isp->isp_path);
+ cam_simq_free(devq);
+ config_intrhook_disestablish(&isp->isp_osinfo.ehook);
+ return;
+ }
+ if (xpt_bus_register(sim, secondary) != CAM_SUCCESS) {
+ xpt_bus_deregister(cam_sim_path(isp->isp_sim));
+ xpt_free_path(isp->isp_path);
+ cam_sim_free(sim, TRUE);
+ config_intrhook_disestablish(&isp->isp_osinfo.ehook);
+ CAMLOCK_2_ISPLOCK(isp);
+ return;
+ }
+
+ if (xpt_create_path(&path, NULL, cam_sim_path(sim),
+ CAM_TARGET_WILDCARD, CAM_LUN_WILDCARD) != CAM_REQ_CMP) {
+ xpt_bus_deregister(cam_sim_path(isp->isp_sim));
+ xpt_free_path(isp->isp_path);
+ xpt_bus_deregister(cam_sim_path(sim));
+ cam_sim_free(sim, TRUE);
+ config_intrhook_disestablish(&isp->isp_osinfo.ehook);
+ CAMLOCK_2_ISPLOCK(isp);
+ return;
+ }
+
+ xpt_setup_ccb(&csa.ccb_h, path, 5);
+ csa.ccb_h.func_code = XPT_SASYNC_CB;
+ csa.event_enable = AC_LOST_DEVICE;
+ csa.callback = isp_cam_async;
+ csa.callback_arg = sim;
+ xpt_action((union ccb *)&csa);
+ CAMLOCK_2_ISPLOCK(isp);
+ isp->isp_sim2 = sim;
+ isp->isp_path2 = path;
+ }
+
+ /*
+ * Create device nodes
+ */
+ (void) make_dev(&isp_cdevsw, device_get_unit(isp->isp_dev), UID_ROOT,
+ GID_OPERATOR, 0600, "%s", device_get_nameunit(isp->isp_dev));
+
+ if (isp->isp_role != ISP_ROLE_NONE) {
+ isp->isp_state = ISP_RUNSTATE;
+ ENABLE_INTS(isp);
+ }
+ if (isplist == NULL) {
+ isplist = isp;
+ } else {
+ ispsoftc_t *tmp = isplist;
+ while (tmp->isp_osinfo.next) {
+ tmp = tmp->isp_osinfo.next;
+ }
+ tmp->isp_osinfo.next = isp;
+ }
+
+}
+
+static __inline void
+isp_freeze_loopdown(ispsoftc_t *isp, char *msg)
+{
+ if (isp->isp_osinfo.simqfrozen == 0) {
+ isp_prt(isp, ISP_LOGDEBUG0, "%s: freeze simq (loopdown)", msg);
+ isp->isp_osinfo.simqfrozen |= SIMQFRZ_LOOPDOWN;
+ ISPLOCK_2_CAMLOCK(isp);
+ xpt_freeze_simq(isp->isp_sim, 1);
+ CAMLOCK_2_ISPLOCK(isp);
+ } else {
+ isp_prt(isp, ISP_LOGDEBUG0, "%s: mark frozen (loopdown)", msg);
+ isp->isp_osinfo.simqfrozen |= SIMQFRZ_LOOPDOWN;
+ }
+}
+
+
+#if __FreeBSD_version < 500000
+#define _DEV dev_t
+#define _IOP struct proc
+#else
+#define _IOP struct thread
+#define _DEV struct cdev *
+#endif
+
+static int
+ispioctl(_DEV dev, u_long c, caddr_t addr, int flags, _IOP *td)
+{
+ ispsoftc_t *isp;
+ int nr, retval = ENOTTY;
+
+ isp = isplist;
+ while (isp) {
+ if (minor(dev) == device_get_unit(isp->isp_dev)) {
+ break;
+ }
+ isp = isp->isp_osinfo.next;
+ }
+ if (isp == NULL)
+ return (ENXIO);
+
+ switch (c) {
+#ifdef ISP_FW_CRASH_DUMP
+ case ISP_GET_FW_CRASH_DUMP:
+ if (IS_FC(isp)) {
+ uint16_t *ptr = FCPARAM(isp)->isp_dump_data;
+ size_t sz;
+
+ retval = 0;
+ if (IS_2200(isp)) {
+ sz = QLA2200_RISC_IMAGE_DUMP_SIZE;
+ } else {
+ sz = QLA2300_RISC_IMAGE_DUMP_SIZE;
+ }
+ ISP_LOCK(isp);
+ if (ptr && *ptr) {
+ void *uaddr = *((void **) addr);
+ if (copyout(ptr, uaddr, sz)) {
+ retval = EFAULT;
+ } else {
+ *ptr = 0;
+ }
+ } else {
+ retval = ENXIO;
+ }
+ ISP_UNLOCK(isp);
+ }
+ break;
+ case ISP_FORCE_CRASH_DUMP:
+ if (IS_FC(isp)) {
+ ISP_LOCK(isp);
+ isp_freeze_loopdown(isp,
+ "ispioctl(ISP_FORCE_CRASH_DUMP)");
+ isp_fw_dump(isp);
+ isp_reinit(isp);
+ ISP_UNLOCK(isp);
+ retval = 0;
+ }
+ break;
+#endif
+ case ISP_SDBLEV:
+ {
+ int olddblev = isp->isp_dblev;
+ isp->isp_dblev = *(int *)addr;
+ *(int *)addr = olddblev;
+ retval = 0;
+ break;
+ }
+ case ISP_GETROLE:
+ *(int *)addr = isp->isp_role;
+ retval = 0;
+ break;
+ case ISP_SETROLE:
+ nr = *(int *)addr;
+ if (nr & ~(ISP_ROLE_INITIATOR|ISP_ROLE_TARGET)) {
+ retval = EINVAL;
+ break;
+ }
+ *(int *)addr = isp->isp_role;
+ isp->isp_role = nr;
+ /* FALLTHROUGH */
+ case ISP_RESETHBA:
+ ISP_LOCK(isp);
+ isp_reinit(isp);
+ ISP_UNLOCK(isp);
+ retval = 0;
+ break;
+ case ISP_RESCAN:
+ if (IS_FC(isp)) {
+ ISP_LOCK(isp);
+ if (isp_fc_runstate(isp, 5 * 1000000)) {
+ retval = EIO;
+ } else {
+ retval = 0;
+ }
+ ISP_UNLOCK(isp);
+ }
+ break;
+ case ISP_FC_LIP:
+ if (IS_FC(isp)) {
+ ISP_LOCK(isp);
+ if (isp_control(isp, ISPCTL_SEND_LIP, 0)) {
+ retval = EIO;
+ } else {
+ retval = 0;
+ }
+ ISP_UNLOCK(isp);
+ }
+ break;
+ case ISP_FC_GETDINFO:
+ {
+ struct isp_fc_device *ifc = (struct isp_fc_device *) addr;
+ struct lportdb *lp;
+
+ if (IS_SCSI(isp)) {
+ break;
+ }
+ if (ifc->loopid < 0 || ifc->loopid >= MAX_FC_TARG) {
+ retval = EINVAL;
+ break;
+ }
+ ISP_LOCK(isp);
+ lp = &FCPARAM(isp)->portdb[ifc->loopid];
+ if (lp->valid) {
+ ifc->role = lp->roles;
+ ifc->loopid = lp->loopid;
+ ifc->portid = lp->portid;
+ ifc->node_wwn = lp->node_wwn;
+ ifc->port_wwn = lp->port_wwn;
+ retval = 0;
+ } else {
+ retval = ENODEV;
+ }
+ ISP_UNLOCK(isp);
+ break;
+ }
+ case ISP_GET_STATS:
+ {
+ isp_stats_t *sp = (isp_stats_t *) addr;
+
+ MEMZERO(sp, sizeof (*sp));
+ sp->isp_stat_version = ISP_STATS_VERSION;
+ sp->isp_type = isp->isp_type;
+ sp->isp_revision = isp->isp_revision;
+ ISP_LOCK(isp);
+ sp->isp_stats[ISP_INTCNT] = isp->isp_intcnt;
+ sp->isp_stats[ISP_INTBOGUS] = isp->isp_intbogus;
+ sp->isp_stats[ISP_INTMBOXC] = isp->isp_intmboxc;
+ sp->isp_stats[ISP_INGOASYNC] = isp->isp_intoasync;
+ sp->isp_stats[ISP_RSLTCCMPLT] = isp->isp_rsltccmplt;
+ sp->isp_stats[ISP_FPHCCMCPLT] = isp->isp_fphccmplt;
+ sp->isp_stats[ISP_RSCCHIWAT] = isp->isp_rscchiwater;
+ sp->isp_stats[ISP_FPCCHIWAT] = isp->isp_fpcchiwater;
+ ISP_UNLOCK(isp);
+ retval = 0;
+ break;
+ }
+ case ISP_CLR_STATS:
+ ISP_LOCK(isp);
+ isp->isp_intcnt = 0;
+ isp->isp_intbogus = 0;
+ isp->isp_intmboxc = 0;
+ isp->isp_intoasync = 0;
+ isp->isp_rsltccmplt = 0;
+ isp->isp_fphccmplt = 0;
+ isp->isp_rscchiwater = 0;
+ isp->isp_fpcchiwater = 0;
+ ISP_UNLOCK(isp);
+ retval = 0;
+ break;
+ case ISP_FC_GETHINFO:
+ {
+ struct isp_hba_device *hba = (struct isp_hba_device *) addr;
+ MEMZERO(hba, sizeof (*hba));
+
+ hba->fc_fw_major = ISP_FW_MAJORX(isp->isp_fwrev);
+ hba->fc_fw_minor = ISP_FW_MINORX(isp->isp_fwrev);
+ hba->fc_fw_micro = ISP_FW_MICROX(isp->isp_fwrev);
+ if (IS_FC(isp)) {
+ hba->fc_speed = FCPARAM(isp)->isp_gbspeed;
+ hba->fc_scsi_supported = 1;
+ hba->fc_topology = FCPARAM(isp)->isp_topo + 1;
+ hba->fc_loopid = FCPARAM(isp)->isp_loopid;
+ hba->nvram_node_wwn = FCPARAM(isp)->isp_nodewwn;
+ hba->nvram_port_wwn = FCPARAM(isp)->isp_portwwn;
+ hba->active_node_wwn = ISP_NODEWWN(isp);
+ hba->active_port_wwn = ISP_PORTWWN(isp);
+ }
+ retval = 0;
+ break;
+ }
+ case ISP_GET_FC_PARAM:
+ {
+ struct isp_fc_param *f = (struct isp_fc_param *) addr;
+
+ if (IS_SCSI(isp)) {
+ break;
+ }
+ f->parameter = 0;
+ if (strcmp(f->param_name, "framelength") == 0) {
+ f->parameter = FCPARAM(isp)->isp_maxfrmlen;
+ retval = 0;
+ break;
+ }
+ if (strcmp(f->param_name, "exec_throttle") == 0) {
+ f->parameter = FCPARAM(isp)->isp_execthrottle;
+ retval = 0;
+ break;
+ }
+ if (strcmp(f->param_name, "fullduplex") == 0) {
+ if (FCPARAM(isp)->isp_fwoptions & ICBOPT_FULL_DUPLEX)
+ f->parameter = 1;
+ retval = 0;
+ break;
+ }
+ if (strcmp(f->param_name, "loopid") == 0) {
+ f->parameter = FCPARAM(isp)->isp_loopid;
+ retval = 0;
+ break;
+ }
+ retval = EINVAL;
+ break;
+ }
+ case ISP_SET_FC_PARAM:
+ {
+ struct isp_fc_param *f = (struct isp_fc_param *) addr;
+ uint32_t param = f->parameter;
+
+ if (IS_SCSI(isp)) {
+ break;
+ }
+ f->parameter = 0;
+ if (strcmp(f->param_name, "framelength") == 0) {
+ if (param != 512 && param != 1024 && param != 1024) {
+ retval = EINVAL;
+ break;
+ }
+ FCPARAM(isp)->isp_maxfrmlen = param;
+ retval = 0;
+ break;
+ }
+ if (strcmp(f->param_name, "exec_throttle") == 0) {
+ if (param < 16 || param > 255) {
+ retval = EINVAL;
+ break;
+ }
+ FCPARAM(isp)->isp_execthrottle = param;
+ retval = 0;
+ break;
+ }
+ if (strcmp(f->param_name, "fullduplex") == 0) {
+ if (param != 0 && param != 1) {
+ retval = EINVAL;
+ break;
+ }
+ if (param) {
+ FCPARAM(isp)->isp_fwoptions |=
+ ICBOPT_FULL_DUPLEX;
+ } else {
+ FCPARAM(isp)->isp_fwoptions &=
+ ~ICBOPT_FULL_DUPLEX;
+ }
+ retval = 0;
+ break;
+ }
+ if (strcmp(f->param_name, "loopid") == 0) {
+ if (param < 0 || param > 125) {
+ retval = EINVAL;
+ break;
+ }
+ FCPARAM(isp)->isp_loopid = param;
+ retval = 0;
+ break;
+ }
+ retval = EINVAL;
+ break;
+ }
+ case ISP_TSK_MGMT:
+ {
+ int needmarker;
+ struct isp_fc_tsk_mgmt *fct = (struct isp_fc_tsk_mgmt *) addr;
+ uint16_t loopid;
+ mbreg_t mbs;
+
+ if (IS_SCSI(isp)) {
+ break;
+ }
+
+ memset(&mbs, 0, sizeof (mbs));
+ needmarker = retval = 0;
+ loopid = fct->loopid;
+ if (IS_2KLOGIN(isp) == 0) {
+ loopid <<= 8;
+ }
+ switch (fct->action) {
+ case IPT_CLEAR_ACA:
+ mbs.param[0] = MBOX_CLEAR_ACA;
+ mbs.param[1] = loopid;
+ mbs.param[2] = fct->lun;
+ break;
+ case IPT_TARGET_RESET:
+ mbs.param[0] = MBOX_TARGET_RESET;
+ mbs.param[1] = loopid;
+ needmarker = 1;
+ break;
+ case IPT_LUN_RESET:
+ mbs.param[0] = MBOX_LUN_RESET;
+ mbs.param[1] = loopid;
+ mbs.param[2] = fct->lun;
+ needmarker = 1;
+ break;
+ case IPT_CLEAR_TASK_SET:
+ mbs.param[0] = MBOX_CLEAR_TASK_SET;
+ mbs.param[1] = loopid;
+ mbs.param[2] = fct->lun;
+ needmarker = 1;
+ break;
+ case IPT_ABORT_TASK_SET:
+ mbs.param[0] = MBOX_ABORT_TASK_SET;
+ mbs.param[1] = loopid;
+ mbs.param[2] = fct->lun;
+ needmarker = 1;
+ break;
+ default:
+ retval = EINVAL;
+ break;
+ }
+ if (retval == 0) {
+ ISP_LOCK(isp);
+ if (needmarker) {
+ isp->isp_sendmarker |= 1;
+ }
+ retval = isp_control(isp, ISPCTL_RUN_MBOXCMD, &mbs);
+ ISP_UNLOCK(isp);
+ if (retval)
+ retval = EIO;
+ }
+ break;
+ }
+ default:
+ break;
+ }
+ return (retval);
+}
+
+static void
+isp_intr_enable(void *arg)
+{
+ ispsoftc_t *isp = arg;
+ if (isp->isp_role != ISP_ROLE_NONE) {
+ ENABLE_INTS(isp);
+#if 0
+ isp->isp_osinfo.intsok = 1;
+#endif
+ }
+ /* Release our hook so that the boot can continue. */
+ config_intrhook_disestablish(&isp->isp_osinfo.ehook);
+}
+
+/*
+ * Put the target mode functions here, because some are inlines
+ */
+
+#ifdef ISP_TARGET_MODE
+
+static __inline int is_lun_enabled(ispsoftc_t *, int, lun_id_t);
+static __inline int are_any_luns_enabled(ispsoftc_t *, int);
+static __inline tstate_t *get_lun_statep(ispsoftc_t *, int, lun_id_t);
+static __inline void rls_lun_statep(ispsoftc_t *, tstate_t *);
+static __inline atio_private_data_t *isp_get_atpd(ispsoftc_t *, int);
+static cam_status
+create_lun_state(ispsoftc_t *, int, struct cam_path *, tstate_t **);
+static void destroy_lun_state(ispsoftc_t *, tstate_t *);
+static int isp_en_lun(ispsoftc_t *, union ccb *);
+static void isp_ledone(ispsoftc_t *, lun_entry_t *);
+static cam_status isp_abort_tgt_ccb(ispsoftc_t *, union ccb *);
+static timeout_t isp_refire_putback_atio;
+static void isp_complete_ctio(union ccb *);
+static void isp_target_putback_atio(union ccb *);
+static void isp_target_start_ctio(ispsoftc_t *, union ccb *);
+static int isp_handle_platform_atio(ispsoftc_t *, at_entry_t *);
+static int isp_handle_platform_atio2(ispsoftc_t *, at2_entry_t *);
+static int isp_handle_platform_ctio(ispsoftc_t *, void *);
+static int isp_handle_platform_notify_scsi(ispsoftc_t *, in_entry_t *);
+static int isp_handle_platform_notify_fc(ispsoftc_t *, in_fcentry_t *);
+
+static __inline int
+is_lun_enabled(ispsoftc_t *isp, int bus, lun_id_t lun)
+{
+ tstate_t *tptr;
+ tptr = isp->isp_osinfo.lun_hash[LUN_HASH_FUNC(isp, bus, lun)];
+ if (tptr == NULL) {
+ return (0);
+ }
+ do {
+ if (tptr->lun == (lun_id_t) lun && tptr->bus == bus) {
+ return (1);
+ }
+ } while ((tptr = tptr->next) != NULL);
+ return (0);
+}
+
+static __inline int
+are_any_luns_enabled(ispsoftc_t *isp, int port)
+{
+ int lo, hi;
+ if (IS_DUALBUS(isp)) {
+ lo = (port * (LUN_HASH_SIZE >> 1));
+ hi = lo + (LUN_HASH_SIZE >> 1);
+ } else {
+ lo = 0;
+ hi = LUN_HASH_SIZE;
+ }
+ for (lo = 0; lo < hi; lo++) {
+ if (isp->isp_osinfo.lun_hash[lo]) {
+ return (1);
+ }
+ }
+ return (0);
+}
+
+static __inline tstate_t *
+get_lun_statep(ispsoftc_t *isp, int bus, lun_id_t lun)
+{
+ tstate_t *tptr = NULL;
+
+ if (lun == CAM_LUN_WILDCARD) {
+ if (isp->isp_osinfo.tmflags[bus] & TM_WILDCARD_ENABLED) {
+ tptr = &isp->isp_osinfo.tsdflt[bus];
+ tptr->hold++;
+ return (tptr);
+ }
+ return (NULL);
+ } else {
+ tptr = isp->isp_osinfo.lun_hash[LUN_HASH_FUNC(isp, bus, lun)];
+ if (tptr == NULL) {
+ return (NULL);
+ }
+ }
+
+ do {
+ if (tptr->lun == lun && tptr->bus == bus) {
+ tptr->hold++;
+ return (tptr);
+ }
+ } while ((tptr = tptr->next) != NULL);
+ return (tptr);
+}
+
+static __inline void
+rls_lun_statep(ispsoftc_t *isp, tstate_t *tptr)
+{
+ if (tptr->hold)
+ tptr->hold--;
+}
+
+static __inline atio_private_data_t *
+isp_get_atpd(ispsoftc_t *isp, int tag)
+{
+ atio_private_data_t *atp;
+ for (atp = isp->isp_osinfo.atpdp;
+ atp < &isp->isp_osinfo.atpdp[ATPDPSIZE]; atp++) {
+ if (atp->tag == tag)
+ return (atp);
+ }
+ return (NULL);
+}
+
+static cam_status
+create_lun_state(ispsoftc_t *isp, int bus,
+ struct cam_path *path, tstate_t **rslt)
+{
+ cam_status status;
+ lun_id_t lun;
+ int hfx;
+ tstate_t *tptr, *new;
+
+ lun = xpt_path_lun_id(path);
+ if (lun < 0) {
+ return (CAM_LUN_INVALID);
+ }
+ if (is_lun_enabled(isp, bus, lun)) {
+ return (CAM_LUN_ALRDY_ENA);
+ }
+ new = (tstate_t *) malloc(sizeof (tstate_t), M_DEVBUF, M_NOWAIT|M_ZERO);
+ if (new == NULL) {
+ return (CAM_RESRC_UNAVAIL);
+ }
+
+ status = xpt_create_path(&new->owner, NULL, xpt_path_path_id(path),
+ xpt_path_target_id(path), xpt_path_lun_id(path));
+ if (status != CAM_REQ_CMP) {
+ free(new, M_DEVBUF);
+ return (status);
+ }
+ new->bus = bus;
+ new->lun = lun;
+ SLIST_INIT(&new->atios);
+ SLIST_INIT(&new->inots);
+ new->hold = 1;
+
+ hfx = LUN_HASH_FUNC(isp, new->bus, new->lun);
+ tptr = isp->isp_osinfo.lun_hash[hfx];
+ if (tptr == NULL) {
+ isp->isp_osinfo.lun_hash[hfx] = new;
+ } else {
+ while (tptr->next)
+ tptr = tptr->next;
+ tptr->next = new;
+ }
+ *rslt = new;
+ return (CAM_REQ_CMP);
+}
+
+static __inline void
+destroy_lun_state(ispsoftc_t *isp, tstate_t *tptr)
+{
+ int hfx;
+ tstate_t *lw, *pw;
+
+ if (tptr->hold) {
+ return;
+ }
+ hfx = LUN_HASH_FUNC(isp, tptr->bus, tptr->lun);
+ pw = isp->isp_osinfo.lun_hash[hfx];
+ if (pw == NULL) {
+ return;
+ } else if (pw->lun == tptr->lun && pw->bus == tptr->bus) {
+ isp->isp_osinfo.lun_hash[hfx] = pw->next;
+ } else {
+ lw = pw;
+ pw = lw->next;
+ while (pw) {
+ if (pw->lun == tptr->lun && pw->bus == tptr->bus) {
+ lw->next = pw->next;
+ break;
+ }
+ lw = pw;
+ pw = pw->next;
+ }
+ if (pw == NULL) {
+ return;
+ }
+ }
+ free(tptr, M_DEVBUF);
+}
+
+/*
+ * Enable luns.
+ */
+static int
+isp_en_lun(ispsoftc_t *isp, union ccb *ccb)
+{
+ struct ccb_en_lun *cel = &ccb->cel;
+ tstate_t *tptr;
+ uint32_t seq;
+ int bus, cmd, av, wildcard, tm_on;
+ lun_id_t lun;
+ target_id_t tgt;
+
+ bus = XS_CHANNEL(ccb);
+ if (bus > 1) {
+ xpt_print_path(ccb->ccb_h.path);
+ printf("illegal bus %d\n", bus);
+ ccb->ccb_h.status = CAM_PATH_INVALID;
+ return (-1);
+ }
+ tgt = ccb->ccb_h.target_id;
+ lun = ccb->ccb_h.target_lun;
+
+ isp_prt(isp, ISP_LOGTDEBUG0,
+ "isp_en_lun: %sabling lun 0x%x on channel %d",
+ cel->enable? "en" : "dis", lun, bus);
+
+
+ if ((lun != CAM_LUN_WILDCARD) &&
+ (lun < 0 || lun >= (lun_id_t) isp->isp_maxluns)) {
+ ccb->ccb_h.status = CAM_LUN_INVALID;
+ return (-1);
+ }
+
+ if (IS_SCSI(isp)) {
+ sdparam *sdp = isp->isp_param;
+ sdp += bus;
+ if (tgt != CAM_TARGET_WILDCARD &&
+ tgt != sdp->isp_initiator_id) {
+ ccb->ccb_h.status = CAM_TID_INVALID;
+ return (-1);
+ }
+ } else {
+ /*
+ * There's really no point in doing this yet w/o multi-tid
+ * capability. Even then, it's problematic.
+ */
+#if 0
+ if (tgt != CAM_TARGET_WILDCARD &&
+ tgt != FCPARAM(isp)->isp_iid) {
+ ccb->ccb_h.status = CAM_TID_INVALID;
+ return (-1);
+ }
+#endif
+ /*
+ * This is as a good a place as any to check f/w capabilities.
+ */
+ if ((FCPARAM(isp)->isp_fwattr & ISP_FW_ATTR_TMODE) == 0) {
+ isp_prt(isp, ISP_LOGERR,
+ "firmware does not support target mode");
+ ccb->ccb_h.status = CAM_FUNC_NOTAVAIL;
+ return (-1);
+ }
+ /*
+ * XXX: We *could* handle non-SCCLUN f/w, but we'd have to
+ * XXX: dorks with our already fragile enable/disable code.
+ */
+ if ((FCPARAM(isp)->isp_fwattr & ISP_FW_ATTR_SCCLUN) == 0) {
+ isp_prt(isp, ISP_LOGERR,
+ "firmware not SCCLUN capable");
+ ccb->ccb_h.status = CAM_FUNC_NOTAVAIL;
+ return (-1);
+ }
+ }
+
+ if (tgt == CAM_TARGET_WILDCARD) {
+ if (lun == CAM_LUN_WILDCARD) {
+ wildcard = 1;
+ } else {
+ ccb->ccb_h.status = CAM_LUN_INVALID;
+ return (-1);
+ }
+ } else {
+ wildcard = 0;
+ }
+
+ tm_on = (isp->isp_osinfo.tmflags[bus] & TM_TMODE_ENABLED) != 0;
+
+ /*
+ * Next check to see whether this is a target/lun wildcard action.
+ *
+ * If so, we know that we can accept commands for luns that haven't
+ * been enabled yet and send them upstream. Otherwise, we have to
+ * handle them locally (if we see them at all).
+ */
+
+ if (wildcard) {
+ tptr = &isp->isp_osinfo.tsdflt[bus];
+ if (cel->enable) {
+ if (tm_on) {
+ ccb->ccb_h.status = CAM_LUN_ALRDY_ENA;
+ return (-1);
+ }
+ ccb->ccb_h.status =
+ xpt_create_path(&tptr->owner, NULL,
+ xpt_path_path_id(ccb->ccb_h.path),
+ xpt_path_target_id(ccb->ccb_h.path),
+ xpt_path_lun_id(ccb->ccb_h.path));
+ if (ccb->ccb_h.status != CAM_REQ_CMP) {
+ return (-1);
+ }
+ SLIST_INIT(&tptr->atios);
+ SLIST_INIT(&tptr->inots);
+ isp->isp_osinfo.tmflags[bus] |= TM_WILDCARD_ENABLED;
+ } else {
+ if (tm_on == 0) {
+ ccb->ccb_h.status = CAM_REQ_CMP;
+ return (-1);
+ }
+ if (tptr->hold) {
+ ccb->ccb_h.status = CAM_SCSI_BUSY;
+ return (-1);
+ }
+ xpt_free_path(tptr->owner);
+ isp->isp_osinfo.tmflags[bus] &= ~TM_WILDCARD_ENABLED;
+ }
+ }
+
+ /*
+ * Now check to see whether this bus needs to be
+ * enabled/disabled with respect to target mode.
+ */
+ av = bus << 31;
+ if (cel->enable && tm_on == 0) {
+ av |= ENABLE_TARGET_FLAG;
+ av = isp_control(isp, ISPCTL_TOGGLE_TMODE, &av);
+ if (av) {
+ ccb->ccb_h.status = CAM_FUNC_NOTAVAIL;
+ if (wildcard) {
+ isp->isp_osinfo.tmflags[bus] &=
+ ~TM_WILDCARD_ENABLED;
+ xpt_free_path(tptr->owner);
+ }
+ return (-1);
+ }
+ isp->isp_osinfo.tmflags[bus] |= TM_TMODE_ENABLED;
+ isp_prt(isp, ISP_LOGINFO,
+ "Target Mode enabled on channel %d", bus);
+ } else if (cel->enable == 0 && tm_on && wildcard) {
+ if (are_any_luns_enabled(isp, bus)) {
+ ccb->ccb_h.status = CAM_SCSI_BUSY;
+ return (-1);
+ }
+ av = isp_control(isp, ISPCTL_TOGGLE_TMODE, &av);
+ if (av) {
+ ccb->ccb_h.status = CAM_FUNC_NOTAVAIL;
+ return (-1);
+ }
+ isp->isp_osinfo.tmflags[bus] &= ~TM_TMODE_ENABLED;
+ isp_prt(isp, ISP_LOGINFO,
+ "Target Mode disabled on channel %d", bus);
+ }
+
+ if (wildcard) {
+ ccb->ccb_h.status = CAM_REQ_CMP;
+ return (-1);
+ }
+
+ /*
+ * Find an empty slot
+ */
+ for (seq = 0; seq < NLEACT; seq++) {
+ if (isp->isp_osinfo.leact[seq] == 0) {
+ break;
+ }
+ }
+ if (seq >= NLEACT) {
+ ccb->ccb_h.status = CAM_RESRC_UNAVAIL;
+ return (-1);
+
+ }
+ isp->isp_osinfo.leact[seq] = ccb;
+
+ if (cel->enable) {
+ ccb->ccb_h.status =
+ create_lun_state(isp, bus, ccb->ccb_h.path, &tptr);
+ if (ccb->ccb_h.status != CAM_REQ_CMP) {
+ isp->isp_osinfo.leact[seq] = 0;
+ return (-1);
+ }
+ } else {
+ tptr = get_lun_statep(isp, bus, lun);
+ if (tptr == NULL) {
+ ccb->ccb_h.status = CAM_LUN_INVALID;
+ return (-1);
+ }
+ }
+
+ if (cel->enable) {
+ int c, n, ulun = lun;
+
+ cmd = RQSTYPE_ENABLE_LUN;
+ c = DFLT_CMND_CNT;
+ n = DFLT_INOT_CNT;
+ if (IS_FC(isp) && lun != 0) {
+ cmd = RQSTYPE_MODIFY_LUN;
+ n = 0;
+ /*
+ * For SCC firmware, we only deal with setting
+ * (enabling or modifying) lun 0.
+ */
+ ulun = 0;
+ }
+ if (isp_lun_cmd(isp, cmd, bus, tgt, ulun, c, n, seq+1) == 0) {
+ rls_lun_statep(isp, tptr);
+ ccb->ccb_h.status = CAM_REQ_INPROG;
+ return (seq);
+ }
+ } else {
+ int c, n, ulun = lun;
+
+ cmd = -RQSTYPE_MODIFY_LUN;
+ c = DFLT_CMND_CNT;
+ n = DFLT_INOT_CNT;
+ if (IS_FC(isp) && lun != 0) {
+ n = 0;
+ /*
+ * For SCC firmware, we only deal with setting
+ * (enabling or modifying) lun 0.
+ */
+ ulun = 0;
+ }
+ if (isp_lun_cmd(isp, cmd, bus, tgt, ulun, c, n, seq+1) == 0) {
+ rls_lun_statep(isp, tptr);
+ ccb->ccb_h.status = CAM_REQ_INPROG;
+ return (seq);
+ }
+ }
+ rls_lun_statep(isp, tptr);
+ xpt_print_path(ccb->ccb_h.path);
+ printf("isp_lun_cmd failed\n");
+ isp->isp_osinfo.leact[seq] = 0;
+ ccb->ccb_h.status = CAM_REQ_CMP_ERR;
+ return (-1);
+}
+
+static void
+isp_ledone(ispsoftc_t *isp, lun_entry_t *lep)
+{
+ const char lfmt[] = "lun %d now %sabled for target mode on channel %d";
+ union ccb *ccb;
+ uint32_t seq;
+ tstate_t *tptr;
+ int av;
+ struct ccb_en_lun *cel;
+
+ seq = lep->le_reserved - 1;
+ if (seq >= NLEACT) {
+ isp_prt(isp, ISP_LOGERR,
+ "seq out of range (%u) in isp_ledone", seq);
+ return;
+ }
+ ccb = isp->isp_osinfo.leact[seq];
+ if (ccb == 0) {
+ isp_prt(isp, ISP_LOGERR,
+ "no ccb for seq %u in isp_ledone", seq);
+ return;
+ }
+ cel = &ccb->cel;
+ tptr = get_lun_statep(isp, XS_CHANNEL(ccb), XS_LUN(ccb));
+ if (tptr == NULL) {
+ xpt_print_path(ccb->ccb_h.path);
+ printf("null tptr in isp_ledone\n");
+ isp->isp_osinfo.leact[seq] = 0;
+ return;
+ }
+
+ if (lep->le_status != LUN_OK) {
+ xpt_print_path(ccb->ccb_h.path);
+ printf("ENABLE/MODIFY LUN returned 0x%x\n", lep->le_status);
+err:
+ ccb->ccb_h.status = CAM_REQ_CMP_ERR;
+ xpt_print_path(ccb->ccb_h.path);
+ rls_lun_statep(isp, tptr);
+ isp->isp_osinfo.leact[seq] = 0;
+ ISPLOCK_2_CAMLOCK(isp);
+ xpt_done(ccb);
+ CAMLOCK_2_ISPLOCK(isp);
+ return;
+ } else {
+ isp_prt(isp, ISP_LOGTDEBUG0,
+ "isp_ledone: ENABLE/MODIFY done okay");
+ }
+
+
+ if (cel->enable) {
+ ccb->ccb_h.status = CAM_REQ_CMP;
+ isp_prt(isp, ISP_LOGINFO, lfmt,
+ XS_LUN(ccb), "en", XS_CHANNEL(ccb));
+ rls_lun_statep(isp, tptr);
+ isp->isp_osinfo.leact[seq] = 0;
+ ISPLOCK_2_CAMLOCK(isp);
+ xpt_done(ccb);
+ CAMLOCK_2_ISPLOCK(isp);
+ return;
+ }
+
+ if (lep->le_header.rqs_entry_type == RQSTYPE_MODIFY_LUN) {
+ if (isp_lun_cmd(isp, -RQSTYPE_ENABLE_LUN, XS_CHANNEL(ccb),
+ XS_TGT(ccb), XS_LUN(ccb), 0, 0, seq+1)) {
+ xpt_print_path(ccb->ccb_h.path);
+ printf("isp_ledone: isp_lun_cmd failed\n");
+ goto err;
+ }
+ rls_lun_statep(isp, tptr);
+ return;
+ }
+
+ isp_prt(isp, ISP_LOGINFO, lfmt, XS_LUN(ccb), "dis", XS_CHANNEL(ccb));
+ rls_lun_statep(isp, tptr);
+ destroy_lun_state(isp, tptr);
+ ccb->ccb_h.status = CAM_REQ_CMP;
+ isp->isp_osinfo.leact[seq] = 0;
+ ISPLOCK_2_CAMLOCK(isp);
+ xpt_done(ccb);
+ CAMLOCK_2_ISPLOCK(isp);
+ if (are_any_luns_enabled(isp, XS_CHANNEL(ccb)) == 0) {
+ int bus = XS_CHANNEL(ccb);
+ av = bus << 31;
+ av = isp_control(isp, ISPCTL_TOGGLE_TMODE, &av);
+ if (av) {
+ isp_prt(isp, ISP_LOGWARN,
+ "disable target mode on channel %d failed", bus);
+ } else {
+ isp_prt(isp, ISP_LOGINFO,
+ "Target Mode disabled on channel %d", bus);
+ }
+ isp->isp_osinfo.tmflags[bus] &= ~TM_TMODE_ENABLED;
+ }
+}
+
+
+static cam_status
+isp_abort_tgt_ccb(ispsoftc_t *isp, union ccb *ccb)
+{
+ tstate_t *tptr;
+ struct ccb_hdr_slist *lp;
+ struct ccb_hdr *curelm;
+ int found, *ctr;
+ union ccb *accb = ccb->cab.abort_ccb;
+
+ isp_prt(isp, ISP_LOGTDEBUG0, "aborting ccb %p", accb);
+ if (accb->ccb_h.target_id != CAM_TARGET_WILDCARD) {
+ int badpath = 0;
+ if (IS_FC(isp) && (accb->ccb_h.target_id !=
+ ((fcparam *) isp->isp_param)->isp_loopid)) {
+ badpath = 1;
+ } else if (IS_SCSI(isp) && (accb->ccb_h.target_id !=
+ ((sdparam *) isp->isp_param)->isp_initiator_id)) {
+ badpath = 1;
+ }
+ if (badpath) {
+ /*
+ * Being restrictive about target ids is really about
+ * making sure we're aborting for the right multi-tid
+ * path. This doesn't really make much sense at present.
+ */
+#if 0
+ return (CAM_PATH_INVALID);
+#endif
+ }
+ }
+ tptr = get_lun_statep(isp, XS_CHANNEL(ccb), accb->ccb_h.target_lun);
+ if (tptr == NULL) {
+ isp_prt(isp, ISP_LOGTDEBUG0,
+ "isp_abort_tgt_ccb: can't get statep");
+ return (CAM_PATH_INVALID);
+ }
+ if (accb->ccb_h.func_code == XPT_ACCEPT_TARGET_IO) {
+ lp = &tptr->atios;
+ ctr = &tptr->atio_count;
+ } else if (accb->ccb_h.func_code == XPT_IMMED_NOTIFY) {
+ lp = &tptr->inots;
+ ctr = &tptr->inot_count;
+ } else {
+ rls_lun_statep(isp, tptr);
+ isp_prt(isp, ISP_LOGTDEBUG0,
+ "isp_abort_tgt_ccb: bad func %d\n", accb->ccb_h.func_code);
+ return (CAM_UA_ABORT);
+ }
+ curelm = SLIST_FIRST(lp);
+ found = 0;
+ if (curelm == &accb->ccb_h) {
+ found = 1;
+ SLIST_REMOVE_HEAD(lp, sim_links.sle);
+ } else {
+ while(curelm != NULL) {
+ struct ccb_hdr *nextelm;
+
+ nextelm = SLIST_NEXT(curelm, sim_links.sle);
+ if (nextelm == &accb->ccb_h) {
+ found = 1;
+ SLIST_NEXT(curelm, sim_links.sle) =
+ SLIST_NEXT(nextelm, sim_links.sle);
+ break;
+ }
+ curelm = nextelm;
+ }
+ }
+ rls_lun_statep(isp, tptr);
+ if (found) {
+ (*ctr)--;
+ accb->ccb_h.status = CAM_REQ_ABORTED;
+ xpt_done(accb);
+ return (CAM_REQ_CMP);
+ }
+ isp_prt(isp, ISP_LOGTDEBUG0,
+ "isp_abort_tgt_ccb: CCB %p not found\n", ccb);
+ return (CAM_PATH_INVALID);
+}
+
+static void
+isp_target_start_ctio(ispsoftc_t *isp, union ccb *ccb)
+{
+ void *qe;
+ struct ccb_scsiio *cso = &ccb->csio;
+ uint16_t *hp, save_handle;
+ uint16_t nxti, optr;
+ uint8_t local[QENTRY_LEN];
+
+
+ if (isp_getrqentry(isp, &nxti, &optr, &qe)) {
+ xpt_print_path(ccb->ccb_h.path);
+ printf("Request Queue Overflow in isp_target_start_ctio\n");
+ XS_SETERR(ccb, CAM_REQUEUE_REQ);
+ goto out;
+ }
+ memset(local, 0, QENTRY_LEN);
+
+ /*
+ * We're either moving data or completing a command here.
+ */
+
+ if (IS_FC(isp)) {
+ atio_private_data_t *atp;
+ ct2_entry_t *cto = (ct2_entry_t *) local;
+
+ cto->ct_header.rqs_entry_type = RQSTYPE_CTIO2;
+ cto->ct_header.rqs_entry_count = 1;
+ if (IS_2KLOGIN(isp)) {
+ ((ct2e_entry_t *)cto)->ct_iid = cso->init_id;
+ } else {
+ cto->ct_iid = cso->init_id;
+ if (!(FCPARAM(isp)->isp_fwattr & ISP_FW_ATTR_SCCLUN)) {
+ cto->ct_lun = ccb->ccb_h.target_lun;
+ }
+ }
+
+ atp = isp_get_atpd(isp, cso->tag_id);
+ if (atp == NULL) {
+ isp_prt(isp, ISP_LOGERR,
+ "cannot find private data adjunct for tag %x",
+ cso->tag_id);
+ XS_SETERR(ccb, CAM_REQ_CMP_ERR);
+ goto out;
+ }
+
+ cto->ct_rxid = cso->tag_id;
+ if (cso->dxfer_len == 0) {
+ cto->ct_flags |= CT2_FLAG_MODE1 | CT2_NO_DATA;
+ if (ccb->ccb_h.flags & CAM_SEND_STATUS) {
+ cto->ct_flags |= CT2_SENDSTATUS;
+ cto->rsp.m1.ct_scsi_status = cso->scsi_status;
+ cto->ct_resid =
+ atp->orig_datalen - atp->bytes_xfered;
+ if (cto->ct_resid < 0) {
+ cto->rsp.m1.ct_scsi_status |=
+ CT2_DATA_OVER;
+ } else if (cto->ct_resid > 0) {
+ cto->rsp.m1.ct_scsi_status |=
+ CT2_DATA_UNDER;
+ }
+ }
+ if ((ccb->ccb_h.flags & CAM_SEND_SENSE) != 0) {
+ int m = min(cso->sense_len, MAXRESPLEN);
+ memcpy(cto->rsp.m1.ct_resp,
+ &cso->sense_data, m);
+ cto->rsp.m1.ct_senselen = m;
+ cto->rsp.m1.ct_scsi_status |= CT2_SNSLEN_VALID;
+ }
+ } else {
+ cto->ct_flags |= CT2_FLAG_MODE0;
+ if ((cso->ccb_h.flags & CAM_DIR_MASK) == CAM_DIR_IN) {
+ cto->ct_flags |= CT2_DATA_IN;
+ } else {
+ cto->ct_flags |= CT2_DATA_OUT;
+ }
+ cto->ct_reloff = atp->bytes_xfered;
+ if ((ccb->ccb_h.flags & CAM_SEND_STATUS) != 0) {
+ cto->ct_flags |= CT2_SENDSTATUS;
+ cto->rsp.m0.ct_scsi_status = cso->scsi_status;
+ cto->ct_resid =
+ atp->orig_datalen -
+ (atp->bytes_xfered + cso->dxfer_len);
+ if (cto->ct_resid < 0) {
+ cto->rsp.m0.ct_scsi_status |=
+ CT2_DATA_OVER;
+ } else if (cto->ct_resid > 0) {
+ cto->rsp.m0.ct_scsi_status |=
+ CT2_DATA_UNDER;
+ }
+ } else {
+ atp->last_xframt = cso->dxfer_len;
+ }
+ /*
+ * If we're sending data and status back together,
+ * we can't also send back sense data as well.
+ */
+ ccb->ccb_h.flags &= ~CAM_SEND_SENSE;
+ }
+
+ if (cto->ct_flags & CT2_SENDSTATUS) {
+ isp_prt(isp, ISP_LOGTDEBUG0,
+ "CTIO2[%x] STATUS %x origd %u curd %u resid %u",
+ cto->ct_rxid, cso->scsi_status, atp->orig_datalen,
+ cso->dxfer_len, cto->ct_resid);
+ cto->ct_flags |= CT2_CCINCR;
+ atp->state = ATPD_STATE_LAST_CTIO;
+ } else {
+ atp->state = ATPD_STATE_CTIO;
+ }
+ cto->ct_timeout = 10;
+ hp = &cto->ct_syshandle;
+ } else {
+ ct_entry_t *cto = (ct_entry_t *) local;
+
+ cto->ct_header.rqs_entry_type = RQSTYPE_CTIO;
+ cto->ct_header.rqs_entry_count = 1;
+ cto->ct_iid = cso->init_id;
+ cto->ct_iid |= XS_CHANNEL(ccb) << 7;
+ cto->ct_tgt = ccb->ccb_h.target_id;
+ cto->ct_lun = ccb->ccb_h.target_lun;
+ cto->ct_fwhandle = AT_GET_HANDLE(cso->tag_id);
+ if (AT_HAS_TAG(cso->tag_id)) {
+ cto->ct_tag_val = (uint8_t) AT_GET_TAG(cso->tag_id);
+ cto->ct_flags |= CT_TQAE;
+ }
+ if (ccb->ccb_h.flags & CAM_DIS_DISCONNECT) {
+ cto->ct_flags |= CT_NODISC;
+ }
+ if (cso->dxfer_len == 0) {
+ cto->ct_flags |= CT_NO_DATA;
+ } else if ((cso->ccb_h.flags & CAM_DIR_MASK) == CAM_DIR_IN) {
+ cto->ct_flags |= CT_DATA_IN;
+ } else {
+ cto->ct_flags |= CT_DATA_OUT;
+ }
+ if (ccb->ccb_h.flags & CAM_SEND_STATUS) {
+ cto->ct_flags |= CT_SENDSTATUS|CT_CCINCR;
+ cto->ct_scsi_status = cso->scsi_status;
+ cto->ct_resid = cso->resid;
+ isp_prt(isp, ISP_LOGTDEBUG0,
+ "CTIO[%x] SCSI STATUS 0x%x resid %d tag_id %x",
+ cto->ct_fwhandle, cso->scsi_status, cso->resid,
+ cso->tag_id);
+ }
+ ccb->ccb_h.flags &= ~CAM_SEND_SENSE;
+ cto->ct_timeout = 10;
+ hp = &cto->ct_syshandle;
+ }
+
+ if (isp_save_xs_tgt(isp, ccb, hp)) {
+ xpt_print_path(ccb->ccb_h.path);
+ printf("No XFLIST pointers for isp_target_start_ctio\n");
+ XS_SETERR(ccb, CAM_REQUEUE_REQ);
+ goto out;
+ }
+
+
+ /*
+ * Call the dma setup routines for this entry (and any subsequent
+ * CTIOs) if there's data to move, and then tell the f/w it's got
+ * new things to play with. As with isp_start's usage of DMA setup,
+ * any swizzling is done in the machine dependent layer. Because
+ * of this, we put the request onto the queue area first in native
+ * format.
+ */
+
+ save_handle = *hp;
+
+ switch (ISP_DMASETUP(isp, cso, (ispreq_t *) local, &nxti, optr)) {
+ case CMD_QUEUED:
+ ISP_ADD_REQUEST(isp, nxti);
+ ccb->ccb_h.status |= CAM_SIM_QUEUED;
+ return;
+
+ case CMD_EAGAIN:
+ XS_SETERR(ccb, CAM_REQUEUE_REQ);
+ break;
+
+ default:
+ break;
+ }
+ isp_destroy_tgt_handle(isp, save_handle);
+
+out:
+ ISPLOCK_2_CAMLOCK(isp);
+ xpt_done(ccb);
+ CAMLOCK_2_ISPLOCK(isp);
+}
+
+static void
+isp_refire_putback_atio(void *arg)
+{
+ int s = splcam();
+ isp_target_putback_atio(arg);
+ splx(s);
+}
+
+static void
+isp_target_putback_atio(union ccb *ccb)
+{
+ ispsoftc_t *isp;
+ struct ccb_scsiio *cso;
+ uint16_t nxti, optr;
+ void *qe;
+
+ isp = XS_ISP(ccb);
+
+ if (isp_getrqentry(isp, &nxti, &optr, &qe)) {
+ (void) timeout(isp_refire_putback_atio, ccb, 10);
+ isp_prt(isp, ISP_LOGWARN,
+ "isp_target_putback_atio: Request Queue Overflow");
+ return;
+ }
+ memset(qe, 0, QENTRY_LEN);
+ cso = &ccb->csio;
+ if (IS_FC(isp)) {
+ at2_entry_t local, *at = &local;
+ MEMZERO(at, sizeof (at2_entry_t));
+ at->at_header.rqs_entry_type = RQSTYPE_ATIO2;
+ at->at_header.rqs_entry_count = 1;
+ if ((FCPARAM(isp)->isp_fwattr & ISP_FW_ATTR_SCCLUN) != 0) {
+ at->at_scclun = (uint16_t) ccb->ccb_h.target_lun;
+ } else {
+ at->at_lun = (uint8_t) ccb->ccb_h.target_lun;
+ }
+ at->at_status = CT_OK;
+ at->at_rxid = cso->tag_id;
+ at->at_iid = cso->ccb_h.target_id;
+ isp_put_atio2(isp, at, qe);
+ } else {
+ at_entry_t local, *at = &local;
+ MEMZERO(at, sizeof (at_entry_t));
+ at->at_header.rqs_entry_type = RQSTYPE_ATIO;
+ at->at_header.rqs_entry_count = 1;
+ at->at_iid = cso->init_id;
+ at->at_iid |= XS_CHANNEL(ccb) << 7;
+ at->at_tgt = cso->ccb_h.target_id;
+ at->at_lun = cso->ccb_h.target_lun;
+ at->at_status = CT_OK;
+ at->at_tag_val = AT_GET_TAG(cso->tag_id);
+ at->at_handle = AT_GET_HANDLE(cso->tag_id);
+ isp_put_atio(isp, at, qe);
+ }
+ ISP_TDQE(isp, "isp_target_putback_atio", (int) optr, qe);
+ ISP_ADD_REQUEST(isp, nxti);
+ isp_complete_ctio(ccb);
+}
+
+static void
+isp_complete_ctio(union ccb *ccb)
+{
+ ISPLOCK_2_CAMLOCK(isp);
+ if ((ccb->ccb_h.status & CAM_STATUS_MASK) == CAM_REQ_INPROG) {
+ ccb->ccb_h.status |= CAM_REQ_CMP;
+ }
+ ccb->ccb_h.status &= ~CAM_SIM_QUEUED;
+ xpt_done(ccb);
+ CAMLOCK_2_ISPLOCK(isp);
+}
+
+/*
+ * Handle ATIO stuff that the generic code can't.
+ * This means handling CDBs.
+ */
+
+static int
+isp_handle_platform_atio(ispsoftc_t *isp, at_entry_t *aep)
+{
+ tstate_t *tptr;
+ int status, bus, iswildcard;
+ struct ccb_accept_tio *atiop;
+
+ /*
+ * The firmware status (except for the QLTM_SVALID bit)
+ * indicates why this ATIO was sent to us.
+ *
+ * If QLTM_SVALID is set, the firware has recommended Sense Data.
+ *
+ * If the DISCONNECTS DISABLED bit is set in the flags field,
+ * we're still connected on the SCSI bus.
+ */
+ status = aep->at_status;
+ if ((status & ~QLTM_SVALID) == AT_PHASE_ERROR) {
+ /*
+ * Bus Phase Sequence error. We should have sense data
+ * suggested by the f/w. I'm not sure quite yet what
+ * to do about this for CAM.
+ */
+ isp_prt(isp, ISP_LOGWARN, "PHASE ERROR");
+ isp_endcmd(isp, aep, SCSI_STATUS_BUSY, 0);
+ return (0);
+ }
+ if ((status & ~QLTM_SVALID) != AT_CDB) {
+ isp_prt(isp, ISP_LOGWARN, "bad atio (0x%x) leaked to platform",
+ status);
+ isp_endcmd(isp, aep, SCSI_STATUS_BUSY, 0);
+ return (0);
+ }
+
+ bus = GET_BUS_VAL(aep->at_iid);
+ tptr = get_lun_statep(isp, bus, aep->at_lun);
+ if (tptr == NULL) {
+ tptr = get_lun_statep(isp, bus, CAM_LUN_WILDCARD);
+ if (tptr == NULL) {
+ /*
+ * Because we can't autofeed sense data back with
+ * a command for parallel SCSI, we can't give back
+ * a CHECK CONDITION. We'll give back a BUSY status
+ * instead. This works out okay because the only
+ * time we should, in fact, get this, is in the
+ * case that somebody configured us without the
+ * blackhole driver, so they get what they deserve.
+ */
+ isp_endcmd(isp, aep, SCSI_STATUS_BUSY, 0);
+ return (0);
+ }
+ iswildcard = 1;
+ } else {
+ iswildcard = 0;
+ }
+
+ atiop = (struct ccb_accept_tio *) SLIST_FIRST(&tptr->atios);
+ if (atiop == NULL) {
+ /*
+ * Because we can't autofeed sense data back with
+ * a command for parallel SCSI, we can't give back
+ * a CHECK CONDITION. We'll give back a QUEUE FULL status
+ * instead. This works out okay because the only time we
+ * should, in fact, get this, is in the case that we've
+ * run out of ATIOS.
+ */
+ xpt_print_path(tptr->owner);
+ isp_prt(isp, ISP_LOGWARN,
+ "no ATIOS for lun %d from initiator %d on channel %d",
+ aep->at_lun, GET_IID_VAL(aep->at_iid), bus);
+ if (aep->at_flags & AT_TQAE)
+ isp_endcmd(isp, aep, SCSI_STATUS_QUEUE_FULL, 0);
+ else
+ isp_endcmd(isp, aep, SCSI_STATUS_BUSY, 0);
+ rls_lun_statep(isp, tptr);
+ return (0);
+ }
+ SLIST_REMOVE_HEAD(&tptr->atios, sim_links.sle);
+ tptr->atio_count--;
+ isp_prt(isp, ISP_LOGTDEBUG0, "Take FREE ATIO lun %d, count now %d",
+ aep->at_lun, tptr->atio_count);
+ if (iswildcard) {
+ atiop->ccb_h.target_id = aep->at_tgt;
+ atiop->ccb_h.target_lun = aep->at_lun;
+ }
+ if (aep->at_flags & AT_NODISC) {
+ atiop->ccb_h.flags = CAM_DIS_DISCONNECT;
+ } else {
+ atiop->ccb_h.flags = 0;
+ }
+
+ if (status & QLTM_SVALID) {
+ size_t amt = imin(QLTM_SENSELEN, sizeof (atiop->sense_data));
+ atiop->sense_len = amt;
+ MEMCPY(&atiop->sense_data, aep->at_sense, amt);
+ } else {
+ atiop->sense_len = 0;
+ }
+
+ atiop->init_id = GET_IID_VAL(aep->at_iid);
+ atiop->cdb_len = aep->at_cdblen;
+ MEMCPY(atiop->cdb_io.cdb_bytes, aep->at_cdb, aep->at_cdblen);
+ atiop->ccb_h.status = CAM_CDB_RECVD;
+ /*
+ * Construct a tag 'id' based upon tag value (which may be 0..255)
+ * and the handle (which we have to preserve).
+ */
+ AT_MAKE_TAGID(atiop->tag_id, device_get_unit(isp->isp_dev), aep);
+ if (aep->at_flags & AT_TQAE) {
+ atiop->tag_action = aep->at_tag_type;
+ atiop->ccb_h.status |= CAM_TAG_ACTION_VALID;
+ }
+ xpt_done((union ccb*)atiop);
+ isp_prt(isp, ISP_LOGTDEBUG0,
+ "ATIO[%x] CDB=0x%x bus %d iid%d->lun%d tag 0x%x ttype 0x%x %s",
+ aep->at_handle, aep->at_cdb[0] & 0xff, GET_BUS_VAL(aep->at_iid),
+ GET_IID_VAL(aep->at_iid), aep->at_lun, aep->at_tag_val & 0xff,
+ aep->at_tag_type, (aep->at_flags & AT_NODISC)?
+ "nondisc" : "disconnecting");
+ rls_lun_statep(isp, tptr);
+ return (0);
+}
+
+static int
+isp_handle_platform_atio2(ispsoftc_t *isp, at2_entry_t *aep)
+{
+ lun_id_t lun;
+ tstate_t *tptr;
+ struct ccb_accept_tio *atiop;
+ atio_private_data_t *atp;
+
+ /*
+ * The firmware status (except for the QLTM_SVALID bit)
+ * indicates why this ATIO was sent to us.
+ *
+ * If QLTM_SVALID is set, the firware has recommended Sense Data.
+ */
+ if ((aep->at_status & ~QLTM_SVALID) != AT_CDB) {
+ isp_prt(isp, ISP_LOGWARN,
+ "bogus atio (0x%x) leaked to platform", aep->at_status);
+ isp_endcmd(isp, aep, SCSI_STATUS_BUSY, 0);
+ return (0);
+ }
+
+ if ((FCPARAM(isp)->isp_fwattr & ISP_FW_ATTR_SCCLUN) != 0) {
+ lun = aep->at_scclun;
+ } else {
+ lun = aep->at_lun;
+ }
+ tptr = get_lun_statep(isp, 0, lun);
+ if (tptr == NULL) {
+ isp_prt(isp, ISP_LOGTDEBUG0,
+ "[0x%x] no state pointer for lun %d", aep->at_rxid, lun);
+ tptr = get_lun_statep(isp, 0, CAM_LUN_WILDCARD);
+ if (tptr == NULL) {
+ isp_endcmd(isp, aep,
+ SCSI_STATUS_CHECK_COND | ECMD_SVALID |
+ (0x5 << 12) | (0x25 << 16), 0);
+ return (0);
+ }
+ }
+
+ atp = isp_get_atpd(isp, 0);
+ atiop = (struct ccb_accept_tio *) SLIST_FIRST(&tptr->atios);
+ if (atiop == NULL || atp == NULL) {
+
+ /*
+ * Because we can't autofeed sense data back with
+ * a command for parallel SCSI, we can't give back
+ * a CHECK CONDITION. We'll give back a QUEUE FULL status
+ * instead. This works out okay because the only time we
+ * should, in fact, get this, is in the case that we've
+ * run out of ATIOS.
+ */
+ xpt_print_path(tptr->owner);
+ isp_prt(isp, ISP_LOGWARN,
+ "no %s for lun %d from initiator %d",
+ (atp == NULL && atiop == NULL)? "ATIO2s *or* ATPS" :
+ ((atp == NULL)? "ATPs" : "ATIO2s"), lun, aep->at_iid);
+ rls_lun_statep(isp, tptr);
+ isp_endcmd(isp, aep, SCSI_STATUS_QUEUE_FULL, 0);
+ return (0);
+ }
+ atp->state = ATPD_STATE_ATIO;
+ SLIST_REMOVE_HEAD(&tptr->atios, sim_links.sle);
+ tptr->atio_count--;
+ isp_prt(isp, ISP_LOGTDEBUG0, "Take FREE ATIO lun %d, count now %d",
+ lun, tptr->atio_count);
+
+ if (tptr == &isp->isp_osinfo.tsdflt[0]) {
+ atiop->ccb_h.target_id =
+ ((fcparam *)isp->isp_param)->isp_loopid;
+ atiop->ccb_h.target_lun = lun;
+ }
+ /*
+ * We don't get 'suggested' sense data as we do with SCSI cards.
+ */
+ atiop->sense_len = 0;
+
+ atiop->init_id = aep->at_iid;
+ atiop->cdb_len = ATIO2_CDBLEN;
+ MEMCPY(atiop->cdb_io.cdb_bytes, aep->at_cdb, ATIO2_CDBLEN);
+ atiop->ccb_h.status = CAM_CDB_RECVD;
+ atiop->tag_id = aep->at_rxid;
+ switch (aep->at_taskflags & ATIO2_TC_ATTR_MASK) {
+ case ATIO2_TC_ATTR_SIMPLEQ:
+ atiop->tag_action = MSG_SIMPLE_Q_TAG;
+ break;
+ case ATIO2_TC_ATTR_HEADOFQ:
+ atiop->tag_action = MSG_HEAD_OF_Q_TAG;
+ break;
+ case ATIO2_TC_ATTR_ORDERED:
+ atiop->tag_action = MSG_ORDERED_Q_TAG;
+ break;
+ case ATIO2_TC_ATTR_ACAQ: /* ?? */
+ case ATIO2_TC_ATTR_UNTAGGED:
+ default:
+ atiop->tag_action = 0;
+ break;
+ }
+ atiop->ccb_h.flags = CAM_TAG_ACTION_VALID;
+
+ atp->tag = atiop->tag_id;
+ atp->lun = lun;
+ atp->orig_datalen = aep->at_datalen;
+ atp->last_xframt = 0;
+ atp->bytes_xfered = 0;
+ atp->state = ATPD_STATE_CAM;
+ ISPLOCK_2_CAMLOCK(siP);
+ xpt_done((union ccb*)atiop);
+
+ isp_prt(isp, ISP_LOGTDEBUG0,
+ "ATIO2[%x] CDB=0x%x iid%d->lun%d tattr 0x%x datalen %u",
+ aep->at_rxid, aep->at_cdb[0] & 0xff, aep->at_iid,
+ lun, aep->at_taskflags, aep->at_datalen);
+ rls_lun_statep(isp, tptr);
+ return (0);
+}
+
+static int
+isp_handle_platform_ctio(ispsoftc_t *isp, void *arg)
+{
+ union ccb *ccb;
+ int sentstatus, ok, notify_cam, resid = 0;
+ uint16_t tval;
+
+ /*
+ * CTIO and CTIO2 are close enough....
+ */
+
+ ccb = isp_find_xs_tgt(isp, ((ct_entry_t *)arg)->ct_syshandle);
+ KASSERT((ccb != NULL), ("null ccb in isp_handle_platform_ctio"));
+ isp_destroy_tgt_handle(isp, ((ct_entry_t *)arg)->ct_syshandle);
+
+ if (IS_FC(isp)) {
+ ct2_entry_t *ct = arg;
+ atio_private_data_t *atp = isp_get_atpd(isp, ct->ct_rxid);
+ if (atp == NULL) {
+ isp_prt(isp, ISP_LOGERR,
+ "cannot find adjunct for %x after I/O",
+ ct->ct_rxid);
+ return (0);
+ }
+ sentstatus = ct->ct_flags & CT2_SENDSTATUS;
+ ok = (ct->ct_status & ~QLTM_SVALID) == CT_OK;
+ if (ok && sentstatus && (ccb->ccb_h.flags & CAM_SEND_SENSE)) {
+ ccb->ccb_h.status |= CAM_SENT_SENSE;
+ }
+ notify_cam = ct->ct_header.rqs_seqno & 0x1;
+ if ((ct->ct_flags & CT2_DATAMASK) != CT2_NO_DATA) {
+ resid = ct->ct_resid;
+ atp->bytes_xfered += (atp->last_xframt - resid);
+ atp->last_xframt = 0;
+ }
+ if (sentstatus || !ok) {
+ atp->tag = 0;
+ }
+ isp_prt(isp, ok? ISP_LOGTDEBUG0 : ISP_LOGWARN,
+ "CTIO2[%x] sts 0x%x flg 0x%x sns %d resid %d %s",
+ ct->ct_rxid, ct->ct_status, ct->ct_flags,
+ (ccb->ccb_h.status & CAM_SENT_SENSE) != 0,
+ resid, sentstatus? "FIN" : "MID");
+ tval = ct->ct_rxid;
+
+ /* XXX: should really come after isp_complete_ctio */
+ atp->state = ATPD_STATE_PDON;
+ } else {
+ ct_entry_t *ct = arg;
+ sentstatus = ct->ct_flags & CT_SENDSTATUS;
+ ok = (ct->ct_status & ~QLTM_SVALID) == CT_OK;
+ /*
+ * We *ought* to be able to get back to the original ATIO
+ * here, but for some reason this gets lost. It's just as
+ * well because it's squirrelled away as part of periph
+ * private data.
+ *
+ * We can live without it as long as we continue to use
+ * the auto-replenish feature for CTIOs.
+ */
+ notify_cam = ct->ct_header.rqs_seqno & 0x1;
+ if (ct->ct_status & QLTM_SVALID) {
+ char *sp = (char *)ct;
+ sp += CTIO_SENSE_OFFSET;
+ ccb->csio.sense_len =
+ min(sizeof (ccb->csio.sense_data), QLTM_SENSELEN);
+ MEMCPY(&ccb->csio.sense_data, sp, ccb->csio.sense_len);
+ ccb->ccb_h.status |= CAM_AUTOSNS_VALID;
+ }
+ if ((ct->ct_flags & CT_DATAMASK) != CT_NO_DATA) {
+ resid = ct->ct_resid;
+ }
+ isp_prt(isp, ISP_LOGTDEBUG0,
+ "CTIO[%x] tag %x iid %d lun %d sts %x flg %x resid %d %s",
+ ct->ct_fwhandle, ct->ct_tag_val, ct->ct_iid, ct->ct_lun,
+ ct->ct_status, ct->ct_flags, resid,
+ sentstatus? "FIN" : "MID");
+ tval = ct->ct_fwhandle;
+ }
+ ccb->csio.resid += resid;
+
+ /*
+ * We're here either because intermediate data transfers are done
+ * and/or the final status CTIO (which may have joined with a
+ * Data Transfer) is done.
+ *
+ * In any case, for this platform, the upper layers figure out
+ * what to do next, so all we do here is collect status and
+ * pass information along. Any DMA handles have already been
+ * freed.
+ */
+ if (notify_cam == 0) {
+ isp_prt(isp, ISP_LOGTDEBUG0, " INTER CTIO[0x%x] done", tval);
+ return (0);
+ }
+
+ isp_prt(isp, ISP_LOGTDEBUG0, "%s CTIO[0x%x] done",
+ (sentstatus)? " FINAL " : "MIDTERM ", tval);
+
+ if (!ok) {
+ isp_target_putback_atio(ccb);
+ } else {
+ isp_complete_ctio(ccb);
+
+ }
+ return (0);
+}
+
+static int
+isp_handle_platform_notify_scsi(ispsoftc_t *isp, in_entry_t *inp)
+{
+ return (0); /* XXXX */
+}
+
+static int
+isp_handle_platform_notify_fc(ispsoftc_t *isp, in_fcentry_t *inp)
+{
+
+ switch (inp->in_status) {
+ case IN_PORT_LOGOUT:
+ isp_prt(isp, ISP_LOGWARN, "port logout of iid %d",
+ inp->in_iid);
+ break;
+ case IN_PORT_CHANGED:
+ isp_prt(isp, ISP_LOGWARN, "port changed for iid %d",
+ inp->in_iid);
+ break;
+ case IN_GLOBAL_LOGO:
+ isp_prt(isp, ISP_LOGINFO, "all ports logged out");
+ break;
+ case IN_ABORT_TASK:
+ {
+ atio_private_data_t *atp = isp_get_atpd(isp, inp->in_seqid);
+ struct ccb_immed_notify *inot = NULL;
+
+ if (atp) {
+ tstate_t *tptr = get_lun_statep(isp, 0, atp->lun);
+ if (tptr) {
+ inot = (struct ccb_immed_notify *)
+ SLIST_FIRST(&tptr->inots);
+ if (inot) {
+ tptr->inot_count--;
+ SLIST_REMOVE_HEAD(&tptr->inots,
+ sim_links.sle);
+ isp_prt(isp, ISP_LOGTDEBUG0,
+ "Take FREE INOT count now %d",
+ tptr->inot_count);
+ }
+ }
+ isp_prt(isp, ISP_LOGWARN,
+ "abort task RX_ID %x IID %d state %d",
+ inp->in_seqid, inp->in_iid, atp->state);
+ } else {
+ isp_prt(isp, ISP_LOGWARN,
+ "abort task RX_ID %x from iid %d, state unknown",
+ inp->in_seqid, inp->in_iid);
+ }
+ if (inot) {
+ inot->initiator_id = inp->in_iid;
+ inot->sense_len = 0;
+ inot->message_args[0] = MSG_ABORT_TAG;
+ inot->message_args[1] = inp->in_seqid & 0xff;
+ inot->message_args[2] = (inp->in_seqid >> 8) & 0xff;
+ inot->ccb_h.status = CAM_MESSAGE_RECV;
+ xpt_done((union ccb *)inot);
+ }
+ break;
+ }
+ default:
+ break;
+ }
+ return (0);
+}
+#endif
+
+static void
+isp_cam_async(void *cbarg, uint32_t code, struct cam_path *path, void *arg)
+{
+ struct cam_sim *sim;
+ ispsoftc_t *isp;
+
+ sim = (struct cam_sim *)cbarg;
+ isp = (ispsoftc_t *) cam_sim_softc(sim);
+ switch (code) {
+ case AC_LOST_DEVICE:
+ if (IS_SCSI(isp)) {
+ uint16_t oflags, nflags;
+ sdparam *sdp = isp->isp_param;
+ int tgt;
+
+ tgt = xpt_path_target_id(path);
+ if (tgt >= 0) {
+ sdp += cam_sim_bus(sim);
+ ISP_LOCK(isp);
+ nflags = sdp->isp_devparam[tgt].nvrm_flags;
+#ifndef ISP_TARGET_MODE
+ nflags &= DPARM_SAFE_DFLT;
+ if (isp->isp_loaded_fw) {
+ nflags |= DPARM_NARROW | DPARM_ASYNC;
+ }
+#else
+ nflags = DPARM_DEFAULT;
+#endif
+ oflags = sdp->isp_devparam[tgt].goal_flags;
+ sdp->isp_devparam[tgt].goal_flags = nflags;
+ sdp->isp_devparam[tgt].dev_update = 1;
+ isp->isp_update |= (1 << cam_sim_bus(sim));
+ (void) isp_control(isp,
+ ISPCTL_UPDATE_PARAMS, NULL);
+ sdp->isp_devparam[tgt].goal_flags = oflags;
+ ISP_UNLOCK(isp);
+ }
+ }
+ break;
+ default:
+ isp_prt(isp, ISP_LOGWARN, "isp_cam_async: Code 0x%x", code);
+ break;
+ }
+}
+
+static void
+isp_poll(struct cam_sim *sim)
+{
+ ispsoftc_t *isp = cam_sim_softc(sim);
+ uint16_t isr, sema, mbox;
+
+ ISP_LOCK(isp);
+ if (ISP_READ_ISR(isp, &isr, &sema, &mbox)) {
+ isp_intr(isp, isr, sema, mbox);
+ }
+ ISP_UNLOCK(isp);
+}
+
+
+static void
+isp_watchdog(void *arg)
+{
+ XS_T *xs = arg;
+ ispsoftc_t *isp = XS_ISP(xs);
+ uint32_t handle;
+ int iok;
+
+ /*
+ * We've decided this command is dead. Make sure we're not trying
+ * to kill a command that's already dead by getting it's handle and
+ * and seeing whether it's still alive.
+ */
+ ISP_LOCK(isp);
+ iok = isp->isp_osinfo.intsok;
+ isp->isp_osinfo.intsok = 0;
+ handle = isp_find_handle(isp, xs);
+ if (handle) {
+ uint16_t isr, sema, mbox;
+
+ if (XS_CMD_DONE_P(xs)) {
+ isp_prt(isp, ISP_LOGDEBUG1,
+ "watchdog found done cmd (handle 0x%x)", handle);
+ ISP_UNLOCK(isp);
+ return;
+ }
+
+ if (XS_CMD_WDOG_P(xs)) {
+ isp_prt(isp, ISP_LOGDEBUG2,
+ "recursive watchdog (handle 0x%x)", handle);
+ ISP_UNLOCK(isp);
+ return;
+ }
+
+ XS_CMD_S_WDOG(xs);
+ if (ISP_READ_ISR(isp, &isr, &sema, &mbox)) {
+ isp_intr(isp, isr, sema, mbox);
+ }
+ if (XS_CMD_DONE_P(xs)) {
+ isp_prt(isp, ISP_LOGDEBUG2,
+ "watchdog cleanup for handle 0x%x", handle);
+ xpt_done((union ccb *) xs);
+ } else if (XS_CMD_GRACE_P(xs)) {
+ /*
+ * Make sure the command is *really* dead before we
+ * release the handle (and DMA resources) for reuse.
+ */
+ (void) isp_control(isp, ISPCTL_ABORT_CMD, arg);
+
+ /*
+ * After this point, the comamnd is really dead.
+ */
+ if (XS_XFRLEN(xs)) {
+ ISP_DMAFREE(isp, xs, handle);
+ }
+ isp_destroy_handle(isp, handle);
+ xpt_print_path(xs->ccb_h.path);
+ isp_prt(isp, ISP_LOGWARN,
+ "watchdog timeout for handle 0x%x", handle);
+ XS_SETERR(xs, CAM_CMD_TIMEOUT);
+ XS_CMD_C_WDOG(xs);
+ isp_done(xs);
+ } else {
+ uint16_t nxti, optr;
+ ispreq_t local, *mp= &local, *qe;
+
+ XS_CMD_C_WDOG(xs);
+ xs->ccb_h.timeout_ch = timeout(isp_watchdog, xs, hz);
+ if (isp_getrqentry(isp, &nxti, &optr, (void **) &qe)) {
+ ISP_UNLOCK(isp);
+ return;
+ }
+ XS_CMD_S_GRACE(xs);
+ MEMZERO((void *) mp, sizeof (*mp));
+ mp->req_header.rqs_entry_count = 1;
+ mp->req_header.rqs_entry_type = RQSTYPE_MARKER;
+ mp->req_modifier = SYNC_ALL;
+ mp->req_target = XS_CHANNEL(xs) << 7;
+ isp_put_request(isp, mp, qe);
+ ISP_ADD_REQUEST(isp, nxti);
+ }
+ } else {
+ isp_prt(isp, ISP_LOGDEBUG2, "watchdog with no command");
+ }
+ isp->isp_osinfo.intsok = iok;
+ ISP_UNLOCK(isp);
+}
+
+static void
+isp_kthread(void *arg)
+{
+ ispsoftc_t *isp = arg;
+
+
+#if __FreeBSD_version < 500000
+ int s;
+
+ s = splcam();
+ isp->isp_osinfo.intsok = 1;
+#else
+#ifdef ISP_SMPLOCK
+ mtx_lock(&isp->isp_lock);
+#else
+ mtx_lock(&Giant);
+#endif
+#endif
+ /*
+ * The first loop is for our usage where we have yet to have
+ * gotten good fibre channel state.
+ */
+ for (;;) {
+ int wasfrozen;
+
+ isp_prt(isp, ISP_LOGDEBUG0, "kthread: checking FC state");
+ while (isp_fc_runstate(isp, 2 * 1000000) != 0) {
+ isp_prt(isp, ISP_LOGDEBUG0, "kthread: FC state ungood");
+ if (FCPARAM(isp)->isp_fwstate != FW_READY ||
+ FCPARAM(isp)->isp_loopstate < LOOP_PDB_RCVD) {
+ if (FCPARAM(isp)->loop_seen_once == 0 ||
+ isp->isp_osinfo.ktmature == 0) {
+ break;
+ }
+ }
+#ifdef ISP_SMPLOCK
+ msleep(isp_kthread, &isp->isp_lock,
+ PRIBIO, "isp_fcthrd", hz);
+#else
+ (void) tsleep(isp_kthread, PRIBIO, "isp_fcthrd", hz);
+#endif
+ }
+
+ /*
+ * Even if we didn't get good loop state we may be
+ * unfreezing the SIMQ so that we can kill off
+ * commands (if we've never seen loop before, for example).
+ */
+ isp->isp_osinfo.ktmature = 1;
+ wasfrozen = isp->isp_osinfo.simqfrozen & SIMQFRZ_LOOPDOWN;
+ isp->isp_osinfo.simqfrozen &= ~SIMQFRZ_LOOPDOWN;
+ if (wasfrozen && isp->isp_osinfo.simqfrozen == 0) {
+ isp_prt(isp, ISP_LOGDEBUG0, "kthread: releasing simq");
+ ISPLOCK_2_CAMLOCK(isp);
+ xpt_release_simq(isp->isp_sim, 1);
+ CAMLOCK_2_ISPLOCK(isp);
+ }
+ isp_prt(isp, ISP_LOGDEBUG0, "kthread: waiting until called");
+#if __FreeBSD_version < 500000
+ tsleep(&isp->isp_osinfo.kproc, PRIBIO, "isp_fc_worker", 0);
+#else
+#ifdef ISP_SMPLOCK
+ cv_wait(&isp->isp_osinfo.kthread_cv, &isp->isp_lock);
+#else
+ (void) tsleep(&isp->isp_osinfo.kthread_cv, PRIBIO, "fc_cv", 0);
+#endif
+#endif
+ }
+}
+
+static void
+isp_action(struct cam_sim *sim, union ccb *ccb)
+{
+ int bus, tgt, error;
+ ispsoftc_t *isp;
+ struct ccb_trans_settings *cts;
+
+ CAM_DEBUG(ccb->ccb_h.path, CAM_DEBUG_TRACE, ("isp_action\n"));
+
+ isp = (ispsoftc_t *)cam_sim_softc(sim);
+ ccb->ccb_h.sim_priv.entries[0].field = 0;
+ ccb->ccb_h.sim_priv.entries[1].ptr = isp;
+ if (isp->isp_state != ISP_RUNSTATE &&
+ ccb->ccb_h.func_code == XPT_SCSI_IO) {
+ CAMLOCK_2_ISPLOCK(isp);
+ isp_init(isp);
+ if (isp->isp_state != ISP_INITSTATE) {
+ ISP_UNLOCK(isp);
+ /*
+ * Lie. Say it was a selection timeout.
+ */
+ ccb->ccb_h.status = CAM_SEL_TIMEOUT | CAM_DEV_QFRZN;
+ xpt_freeze_devq(ccb->ccb_h.path, 1);
+ xpt_done(ccb);
+ return;
+ }
+ isp->isp_state = ISP_RUNSTATE;
+ ISPLOCK_2_CAMLOCK(isp);
+ }
+ isp_prt(isp, ISP_LOGDEBUG2, "isp_action code %x", ccb->ccb_h.func_code);
+
+
+ switch (ccb->ccb_h.func_code) {
+ case XPT_SCSI_IO: /* Execute the requested I/O operation */
+ /*
+ * Do a couple of preliminary checks...
+ */
+ if ((ccb->ccb_h.flags & CAM_CDB_POINTER) != 0) {
+ if ((ccb->ccb_h.flags & CAM_CDB_PHYS) != 0) {
+ ccb->ccb_h.status = CAM_REQ_INVALID;
+ xpt_done(ccb);
+ break;
+ }
+ }
+#ifdef DIAGNOSTIC
+ if (ccb->ccb_h.target_id > (ISP_MAX_TARGETS(isp) - 1)) {
+ ccb->ccb_h.status = CAM_PATH_INVALID;
+ } else if (ccb->ccb_h.target_lun > (ISP_MAX_LUNS(isp) - 1)) {
+ ccb->ccb_h.status = CAM_PATH_INVALID;
+ }
+ if (ccb->ccb_h.status == CAM_PATH_INVALID) {
+ isp_prt(isp, ISP_LOGERR,
+ "invalid tgt/lun (%d.%d) in XPT_SCSI_IO",
+ ccb->ccb_h.target_id, ccb->ccb_h.target_lun);
+ xpt_done(ccb);
+ break;
+ }
+#endif
+ ((struct ccb_scsiio *) ccb)->scsi_status = SCSI_STATUS_OK;
+ CAMLOCK_2_ISPLOCK(isp);
+ error = isp_start((XS_T *) ccb);
+ switch (error) {
+ case CMD_QUEUED:
+ ccb->ccb_h.status |= CAM_SIM_QUEUED;
+ if (ccb->ccb_h.timeout != CAM_TIME_INFINITY) {
+ uint64_t ticks = (uint64_t) hz;
+ if (ccb->ccb_h.timeout == CAM_TIME_DEFAULT)
+ ticks = 60 * 1000 * ticks;
+ else
+ ticks = ccb->ccb_h.timeout * hz;
+ ticks = ((ticks + 999) / 1000) + hz + hz;
+ if (ticks >= 0x80000000) {
+ isp_prt(isp, ISP_LOGERR,
+ "timeout overflow");
+ ticks = 0x7fffffff;
+ }
+ ccb->ccb_h.timeout_ch = timeout(isp_watchdog,
+ (caddr_t)ccb, (int)ticks);
+ } else {
+ callout_handle_init(&ccb->ccb_h.timeout_ch);
+ }
+ ISPLOCK_2_CAMLOCK(isp);
+ break;
+ case CMD_RQLATER:
+ /*
+ * This can only happen for Fibre Channel
+ */
+ KASSERT((IS_FC(isp)), ("CMD_RQLATER for FC only"));
+ if (FCPARAM(isp)->loop_seen_once == 0 &&
+ isp->isp_osinfo.ktmature) {
+ ISPLOCK_2_CAMLOCK(isp);
+ XS_SETERR(ccb, CAM_SEL_TIMEOUT);
+ xpt_done(ccb);
+ break;
+ }
+#if __FreeBSD_version < 500000
+ wakeup(&isp->isp_osinfo.kproc);
+#else
+#ifdef ISP_SMPLOCK
+ cv_signal(&isp->isp_osinfo.kthread_cv);
+#else
+ wakeup(&isp->isp_osinfo.kthread_cv);
+#endif
+#endif
+ isp_freeze_loopdown(isp, "isp_action(RQLATER)");
+ XS_SETERR(ccb, CAM_REQUEUE_REQ);
+ ISPLOCK_2_CAMLOCK(isp);
+ xpt_done(ccb);
+ break;
+ case CMD_EAGAIN:
+ XS_SETERR(ccb, CAM_REQUEUE_REQ);
+ ISPLOCK_2_CAMLOCK(isp);
+ xpt_done(ccb);
+ break;
+ case CMD_COMPLETE:
+ isp_done((struct ccb_scsiio *) ccb);
+ ISPLOCK_2_CAMLOCK(isp);
+ break;
+ default:
+ isp_prt(isp, ISP_LOGERR,
+ "What's this? 0x%x at %d in file %s",
+ error, __LINE__, __FILE__);
+ XS_SETERR(ccb, CAM_REQ_CMP_ERR);
+ xpt_done(ccb);
+ ISPLOCK_2_CAMLOCK(isp);
+ }
+ break;
+
+#ifdef ISP_TARGET_MODE
+ case XPT_EN_LUN: /* Enable LUN as a target */
+ {
+ int seq, iok, i;
+ CAMLOCK_2_ISPLOCK(isp);
+ iok = isp->isp_osinfo.intsok;
+ isp->isp_osinfo.intsok = 0;
+ seq = isp_en_lun(isp, ccb);
+ if (seq < 0) {
+ isp->isp_osinfo.intsok = iok;
+ ISPLOCK_2_CAMLOCK(isp);
+ xpt_done(ccb);
+ break;
+ }
+ for (i = 0; isp->isp_osinfo.leact[seq] && i < 30 * 1000; i++) {
+ uint16_t isr, sema, mbox;
+ if (ISP_READ_ISR(isp, &isr, &sema, &mbox)) {
+ isp_intr(isp, isr, sema, mbox);
+ }
+ DELAY(1000);
+ }
+ isp->isp_osinfo.intsok = iok;
+ ISPLOCK_2_CAMLOCK(isp);
+ break;
+ }
+ case XPT_NOTIFY_ACK: /* recycle notify ack */
+ case XPT_IMMED_NOTIFY: /* Add Immediate Notify Resource */
+ case XPT_ACCEPT_TARGET_IO: /* Add Accept Target IO Resource */
+ {
+ tstate_t *tptr =
+ get_lun_statep(isp, XS_CHANNEL(ccb), ccb->ccb_h.target_lun);
+ if (tptr == NULL) {
+ ccb->ccb_h.status = CAM_LUN_INVALID;
+ xpt_done(ccb);
+ break;
+ }
+ ccb->ccb_h.sim_priv.entries[0].field = 0;
+ ccb->ccb_h.sim_priv.entries[1].ptr = isp;
+ ccb->ccb_h.flags = 0;
+
+ CAMLOCK_2_ISPLOCK(isp);
+ if (ccb->ccb_h.func_code == XPT_ACCEPT_TARGET_IO) {
+ /*
+ * Note that the command itself may not be done-
+ * it may not even have had the first CTIO sent.
+ */
+ tptr->atio_count++;
+ isp_prt(isp, ISP_LOGTDEBUG0,
+ "Put FREE ATIO, lun %d, count now %d",
+ ccb->ccb_h.target_lun, tptr->atio_count);
+ SLIST_INSERT_HEAD(&tptr->atios, &ccb->ccb_h,
+ sim_links.sle);
+ } else if (ccb->ccb_h.func_code == XPT_IMMED_NOTIFY) {
+ tptr->inot_count++;
+ isp_prt(isp, ISP_LOGTDEBUG0,
+ "Put FREE INOT, lun %d, count now %d",
+ ccb->ccb_h.target_lun, tptr->inot_count);
+ SLIST_INSERT_HEAD(&tptr->inots, &ccb->ccb_h,
+ sim_links.sle);
+ } else {
+ isp_prt(isp, ISP_LOGWARN, "Got Notify ACK");;
+ }
+ rls_lun_statep(isp, tptr);
+ ccb->ccb_h.status = CAM_REQ_INPROG;
+ ISPLOCK_2_CAMLOCK(isp);
+ break;
+ }
+ case XPT_CONT_TARGET_IO:
+ {
+ CAMLOCK_2_ISPLOCK(isp);
+ isp_target_start_ctio(isp, ccb);
+ ISPLOCK_2_CAMLOCK(isp);
+ break;
+ }
+#endif
+ case XPT_RESET_DEV: /* BDR the specified SCSI device */
+
+ bus = cam_sim_bus(xpt_path_sim(ccb->ccb_h.path));
+ tgt = ccb->ccb_h.target_id;
+ tgt |= (bus << 16);
+
+ CAMLOCK_2_ISPLOCK(isp);
+ error = isp_control(isp, ISPCTL_RESET_DEV, &tgt);
+ ISPLOCK_2_CAMLOCK(isp);
+ if (error) {
+ ccb->ccb_h.status = CAM_REQ_CMP_ERR;
+ } else {
+ ccb->ccb_h.status = CAM_REQ_CMP;
+ }
+ xpt_done(ccb);
+ break;
+ case XPT_ABORT: /* Abort the specified CCB */
+ {
+ union ccb *accb = ccb->cab.abort_ccb;
+ CAMLOCK_2_ISPLOCK(isp);
+ switch (accb->ccb_h.func_code) {
+#ifdef ISP_TARGET_MODE
+ case XPT_ACCEPT_TARGET_IO:
+ case XPT_IMMED_NOTIFY:
+ ccb->ccb_h.status = isp_abort_tgt_ccb(isp, ccb);
+ break;
+ case XPT_CONT_TARGET_IO:
+ isp_prt(isp, ISP_LOGERR, "cannot abort CTIOs yet");
+ ccb->ccb_h.status = CAM_UA_ABORT;
+ break;
+#endif
+ case XPT_SCSI_IO:
+ error = isp_control(isp, ISPCTL_ABORT_CMD, ccb);
+ if (error) {
+ ccb->ccb_h.status = CAM_UA_ABORT;
+ } else {
+ ccb->ccb_h.status = CAM_REQ_CMP;
+ }
+ break;
+ default:
+ ccb->ccb_h.status = CAM_REQ_INVALID;
+ break;
+ }
+ ISPLOCK_2_CAMLOCK(isp);
+ xpt_done(ccb);
+ break;
+ }
+#ifdef CAM_NEW_TRAN_CODE
+#define IS_CURRENT_SETTINGS(c) (c->type == CTS_TYPE_CURRENT_SETTINGS)
+#else
+#define IS_CURRENT_SETTINGS(c) (c->flags & CCB_TRANS_CURRENT_SETTINGS)
+#endif
+ case XPT_SET_TRAN_SETTINGS: /* Nexus Settings */
+ cts = &ccb->cts;
+ if (!IS_CURRENT_SETTINGS(cts)) {
+ ccb->ccb_h.status = CAM_REQ_INVALID;
+ xpt_done(ccb);
+ break;
+ }
+ tgt = cts->ccb_h.target_id;
+ CAMLOCK_2_ISPLOCK(isp);
+ if (IS_SCSI(isp)) {
+#ifndef CAM_NEW_TRAN_CODE
+ sdparam *sdp = isp->isp_param;
+ uint16_t *dptr;
+
+ bus = cam_sim_bus(xpt_path_sim(cts->ccb_h.path));
+
+ sdp += bus;
+ /*
+ * We always update (internally) from goal_flags
+ * so any request to change settings just gets
+ * vectored to that location.
+ */
+ dptr = &sdp->isp_devparam[tgt].goal_flags;
+
+ /*
+ * Note that these operations affect the
+ * the goal flags (goal_flags)- not
+ * the current state flags. Then we mark
+ * things so that the next operation to
+ * this HBA will cause the update to occur.
+ */
+ if (cts->valid & CCB_TRANS_DISC_VALID) {
+ if ((cts->flags & CCB_TRANS_DISC_ENB) != 0) {
+ *dptr |= DPARM_DISC;
+ } else {
+ *dptr &= ~DPARM_DISC;
+ }
+ }
+ if (cts->valid & CCB_TRANS_TQ_VALID) {
+ if ((cts->flags & CCB_TRANS_TAG_ENB) != 0) {
+ *dptr |= DPARM_TQING;
+ } else {
+ *dptr &= ~DPARM_TQING;
+ }
+ }
+ if (cts->valid & CCB_TRANS_BUS_WIDTH_VALID) {
+ switch (cts->bus_width) {
+ case MSG_EXT_WDTR_BUS_16_BIT:
+ *dptr |= DPARM_WIDE;
+ break;
+ default:
+ *dptr &= ~DPARM_WIDE;
+ }
+ }
+ /*
+ * Any SYNC RATE of nonzero and SYNC_OFFSET
+ * of nonzero will cause us to go to the
+ * selected (from NVRAM) maximum value for
+ * this device. At a later point, we'll
+ * allow finer control.
+ */
+ if ((cts->valid & CCB_TRANS_SYNC_RATE_VALID) &&
+ (cts->valid & CCB_TRANS_SYNC_OFFSET_VALID) &&
+ (cts->sync_offset > 0)) {
+ *dptr |= DPARM_SYNC;
+ } else {
+ *dptr &= ~DPARM_SYNC;
+ }
+ *dptr |= DPARM_SAFE_DFLT;
+#else
+ struct ccb_trans_settings_scsi *scsi =
+ &cts->proto_specific.scsi;
+ struct ccb_trans_settings_spi *spi =
+ &cts->xport_specific.spi;
+ sdparam *sdp = isp->isp_param;
+ uint16_t *dptr;
+
+ bus = cam_sim_bus(xpt_path_sim(cts->ccb_h.path));
+ sdp += bus;
+ /*
+ * We always update (internally) from goal_flags
+ * so any request to change settings just gets
+ * vectored to that location.
+ */
+ dptr = &sdp->isp_devparam[tgt].goal_flags;
+
+ if ((spi->valid & CTS_SPI_VALID_DISC) != 0) {
+ if ((spi->flags & CTS_SPI_FLAGS_DISC_ENB) != 0)
+ *dptr |= DPARM_DISC;
+ else
+ *dptr &= ~DPARM_DISC;
+ }
+
+ if ((scsi->valid & CTS_SCSI_VALID_TQ) != 0) {
+ if ((scsi->flags & CTS_SCSI_FLAGS_TAG_ENB) != 0)
+ *dptr |= DPARM_TQING;
+ else
+ *dptr &= ~DPARM_TQING;
+ }
+
+ if ((spi->valid & CTS_SPI_VALID_BUS_WIDTH) != 0) {
+ if (spi->bus_width == MSG_EXT_WDTR_BUS_16_BIT)
+ *dptr |= DPARM_WIDE;
+ else
+ *dptr &= ~DPARM_WIDE;
+ }
+
+ /*
+ * XXX: FIX ME
+ */
+ if ((spi->valid & CTS_SPI_VALID_SYNC_OFFSET) &&
+ (spi->valid & CTS_SPI_VALID_SYNC_RATE) &&
+ (spi->sync_period && spi->sync_offset)) {
+ *dptr |= DPARM_SYNC;
+ /*
+ * XXX: CHECK FOR LEGALITY
+ */
+ sdp->isp_devparam[tgt].goal_period =
+ spi->sync_period;
+ sdp->isp_devparam[tgt].goal_offset =
+ spi->sync_offset;
+ } else {
+ *dptr &= ~DPARM_SYNC;
+ }
+#endif
+ isp_prt(isp, ISP_LOGDEBUG0,
+ "SET bus %d targ %d to flags %x off %x per %x",
+ bus, tgt, sdp->isp_devparam[tgt].goal_flags,
+ sdp->isp_devparam[tgt].goal_offset,
+ sdp->isp_devparam[tgt].goal_period);
+ sdp->isp_devparam[tgt].dev_update = 1;
+ isp->isp_update |= (1 << bus);
+ }
+ ISPLOCK_2_CAMLOCK(isp);
+ ccb->ccb_h.status = CAM_REQ_CMP;
+ xpt_done(ccb);
+ break;
+ case XPT_GET_TRAN_SETTINGS:
+ cts = &ccb->cts;
+ tgt = cts->ccb_h.target_id;
+ CAMLOCK_2_ISPLOCK(isp);
+ if (IS_FC(isp)) {
+#ifndef CAM_NEW_TRAN_CODE
+ /*
+ * a lot of normal SCSI things don't make sense.
+ */
+ cts->flags = CCB_TRANS_TAG_ENB | CCB_TRANS_DISC_ENB;
+ cts->valid = CCB_TRANS_DISC_VALID | CCB_TRANS_TQ_VALID;
+ /*
+ * How do you measure the width of a high
+ * speed serial bus? Well, in bytes.
+ *
+ * Offset and period make no sense, though, so we set
+ * (above) a 'base' transfer speed to be gigabit.
+ */
+ cts->bus_width = MSG_EXT_WDTR_BUS_8_BIT;
+#else
+ fcparam *fcp = isp->isp_param;
+ struct ccb_trans_settings_fc *fc =
+ &cts->xport_specific.fc;
+
+ cts->protocol = PROTO_SCSI;
+ cts->protocol_version = SCSI_REV_2;
+ cts->transport = XPORT_FC;
+ cts->transport_version = 0;
+
+ fc->valid = CTS_FC_VALID_SPEED;
+ if (fcp->isp_gbspeed == 2)
+ fc->bitrate = 200000;
+ else
+ fc->bitrate = 100000;
+ if (tgt > 0 && tgt < MAX_FC_TARG) {
+ struct lportdb *lp = &fcp->portdb[tgt];
+ fc->wwnn = lp->node_wwn;
+ fc->wwpn = lp->port_wwn;
+ fc->port = lp->portid;
+ fc->valid |= CTS_FC_VALID_WWNN |
+ CTS_FC_VALID_WWPN | CTS_FC_VALID_PORT;
+ }
+#endif
+ } else {
+#ifdef CAM_NEW_TRAN_CODE
+ struct ccb_trans_settings_scsi *scsi =
+ &cts->proto_specific.scsi;
+ struct ccb_trans_settings_spi *spi =
+ &cts->xport_specific.spi;
+#endif
+ sdparam *sdp = isp->isp_param;
+ int bus = cam_sim_bus(xpt_path_sim(cts->ccb_h.path));
+ uint16_t dval, pval, oval;
+
+ sdp += bus;
+
+ if (IS_CURRENT_SETTINGS(cts)) {
+ sdp->isp_devparam[tgt].dev_refresh = 1;
+ isp->isp_update |= (1 << bus);
+ (void) isp_control(isp, ISPCTL_UPDATE_PARAMS,
+ NULL);
+ dval = sdp->isp_devparam[tgt].actv_flags;
+ oval = sdp->isp_devparam[tgt].actv_offset;
+ pval = sdp->isp_devparam[tgt].actv_period;
+ } else {
+ dval = sdp->isp_devparam[tgt].nvrm_flags;
+ oval = sdp->isp_devparam[tgt].nvrm_offset;
+ pval = sdp->isp_devparam[tgt].nvrm_period;
+ }
+
+#ifndef CAM_NEW_TRAN_CODE
+ cts->flags &= ~(CCB_TRANS_DISC_ENB|CCB_TRANS_TAG_ENB);
+
+ if (dval & DPARM_DISC) {
+ cts->flags |= CCB_TRANS_DISC_ENB;
+ }
+ if (dval & DPARM_TQING) {
+ cts->flags |= CCB_TRANS_TAG_ENB;
+ }
+ if (dval & DPARM_WIDE) {
+ cts->bus_width = MSG_EXT_WDTR_BUS_16_BIT;
+ } else {
+ cts->bus_width = MSG_EXT_WDTR_BUS_8_BIT;
+ }
+ cts->valid = CCB_TRANS_BUS_WIDTH_VALID |
+ CCB_TRANS_DISC_VALID | CCB_TRANS_TQ_VALID;
+
+ if ((dval & DPARM_SYNC) && oval != 0) {
+ cts->sync_period = pval;
+ cts->sync_offset = oval;
+ cts->valid |=
+ CCB_TRANS_SYNC_RATE_VALID |
+ CCB_TRANS_SYNC_OFFSET_VALID;
+ }
+#else
+ cts->protocol = PROTO_SCSI;
+ cts->protocol_version = SCSI_REV_2;
+ cts->transport = XPORT_SPI;
+ cts->transport_version = 2;
+
+ scsi->flags &= ~CTS_SCSI_FLAGS_TAG_ENB;
+ spi->flags &= ~CTS_SPI_FLAGS_DISC_ENB;
+ if (dval & DPARM_DISC) {
+ spi->flags |= CTS_SPI_FLAGS_DISC_ENB;
+ }
+ if (dval & DPARM_TQING) {
+ scsi->flags |= CTS_SCSI_FLAGS_TAG_ENB;
+ }
+ if ((dval & DPARM_SYNC) && oval && pval) {
+ spi->sync_offset = oval;
+ spi->sync_period = pval;
+ spi->valid |= CTS_SPI_VALID_SYNC_OFFSET;
+ spi->valid |= CTS_SPI_VALID_SYNC_RATE;
+ }
+ spi->valid |= CTS_SPI_VALID_BUS_WIDTH;
+ if (dval & DPARM_WIDE) {
+ spi->bus_width = MSG_EXT_WDTR_BUS_16_BIT;
+ } else {
+ spi->bus_width = MSG_EXT_WDTR_BUS_8_BIT;
+ }
+ if (cts->ccb_h.target_lun != CAM_LUN_WILDCARD) {
+ scsi->valid = CTS_SCSI_VALID_TQ;
+ spi->valid |= CTS_SPI_VALID_DISC;
+ } else {
+ scsi->valid = 0;
+ }
+#endif
+ isp_prt(isp, ISP_LOGDEBUG0,
+ "GET %s bus %d targ %d to flags %x off %x per %x",
+ IS_CURRENT_SETTINGS(cts)? "ACTIVE" : "NVRAM",
+ bus, tgt, dval, oval, pval);
+ }
+ ISPLOCK_2_CAMLOCK(isp);
+ ccb->ccb_h.status = CAM_REQ_CMP;
+ xpt_done(ccb);
+ break;
+
+ case XPT_CALC_GEOMETRY:
+#if __FreeBSD_version < 500000
+ {
+ struct ccb_calc_geometry *ccg;
+ u_int32_t secs_per_cylinder;
+ u_int32_t size_mb;
+
+ ccg = &ccb->ccg;
+ if (ccg->block_size == 0) {
+ ccb->ccb_h.status = CAM_REQ_INVALID;
+ xpt_done(ccb);
+ break;
+ }
+ size_mb = ccg->volume_size /((1024L * 1024L) / ccg->block_size);
+ if (size_mb > 1024) {
+ ccg->heads = 255;
+ ccg->secs_per_track = 63;
+ } else {
+ ccg->heads = 64;
+ ccg->secs_per_track = 32;
+ }
+ secs_per_cylinder = ccg->heads * ccg->secs_per_track;
+ ccg->cylinders = ccg->volume_size / secs_per_cylinder;
+ ccb->ccb_h.status = CAM_REQ_CMP;
+ xpt_done(ccb);
+ break;
+ }
+#else
+ {
+ cam_calc_geometry(&ccb->ccg, /*extended*/1);
+ xpt_done(ccb);
+ break;
+ }
+#endif
+ case XPT_RESET_BUS: /* Reset the specified bus */
+ bus = cam_sim_bus(sim);
+ CAMLOCK_2_ISPLOCK(isp);
+ error = isp_control(isp, ISPCTL_RESET_BUS, &bus);
+ ISPLOCK_2_CAMLOCK(isp);
+ if (error)
+ ccb->ccb_h.status = CAM_REQ_CMP_ERR;
+ else {
+ if (cam_sim_bus(sim) && isp->isp_path2 != NULL)
+ xpt_async(AC_BUS_RESET, isp->isp_path2, NULL);
+ else if (isp->isp_path != NULL)
+ xpt_async(AC_BUS_RESET, isp->isp_path, NULL);
+ ccb->ccb_h.status = CAM_REQ_CMP;
+ }
+ xpt_done(ccb);
+ break;
+
+ case XPT_TERM_IO: /* Terminate the I/O process */
+ ccb->ccb_h.status = CAM_REQ_INVALID;
+ xpt_done(ccb);
+ break;
+
+ case XPT_PATH_INQ: /* Path routing inquiry */
+ {
+ struct ccb_pathinq *cpi = &ccb->cpi;
+
+ cpi->version_num = 1;
+#ifdef ISP_TARGET_MODE
+ cpi->target_sprt = PIT_PROCESSOR | PIT_DISCONNECT | PIT_TERM_IO;
+#else
+ cpi->target_sprt = 0;
+#endif
+ cpi->hba_eng_cnt = 0;
+ cpi->max_target = ISP_MAX_TARGETS(isp) - 1;
+ cpi->max_lun = ISP_MAX_LUNS(isp) - 1;
+ cpi->bus_id = cam_sim_bus(sim);
+ if (IS_FC(isp)) {
+ cpi->hba_misc = PIM_NOBUSRESET;
+ /*
+ * Because our loop ID can shift from time to time,
+ * make our initiator ID out of range of our bus.
+ */
+ cpi->initiator_id = cpi->max_target + 1;
+
+ /*
+ * Set base transfer capabilities for Fibre Channel.
+ * Technically not correct because we don't know
+ * what media we're running on top of- but we'll
+ * look good if we always say 100MB/s.
+ */
+ if (FCPARAM(isp)->isp_gbspeed == 2)
+ cpi->base_transfer_speed = 200000;
+ else
+ cpi->base_transfer_speed = 100000;
+ cpi->hba_inquiry = PI_TAG_ABLE;
+#ifdef CAM_NEW_TRAN_CODE
+ cpi->transport = XPORT_FC;
+ cpi->transport_version = 0; /* WHAT'S THIS FOR? */
+#endif
+ } else {
+ sdparam *sdp = isp->isp_param;
+ sdp += cam_sim_bus(xpt_path_sim(cpi->ccb_h.path));
+ cpi->hba_inquiry = PI_SDTR_ABLE|PI_TAG_ABLE|PI_WIDE_16;
+ cpi->hba_misc = 0;
+ cpi->initiator_id = sdp->isp_initiator_id;
+ cpi->base_transfer_speed = 3300;
+#ifdef CAM_NEW_TRAN_CODE
+ cpi->transport = XPORT_SPI;
+ cpi->transport_version = 2; /* WHAT'S THIS FOR? */
+#endif
+ }
+#ifdef CAM_NEW_TRAN_CODE
+ cpi->protocol = PROTO_SCSI;
+ cpi->protocol_version = SCSI_REV_2;
+#endif
+ strncpy(cpi->sim_vid, "FreeBSD", SIM_IDLEN);
+ strncpy(cpi->hba_vid, "Qlogic", HBA_IDLEN);
+ strncpy(cpi->dev_name, cam_sim_name(sim), DEV_IDLEN);
+ cpi->unit_number = cam_sim_unit(sim);
+ cpi->ccb_h.status = CAM_REQ_CMP;
+ xpt_done(ccb);
+ break;
+ }
+ default:
+ ccb->ccb_h.status = CAM_REQ_INVALID;
+ xpt_done(ccb);
+ break;
+ }
+}
+
+#define ISPDDB (CAM_DEBUG_INFO|CAM_DEBUG_TRACE|CAM_DEBUG_CDB)
+void
+isp_done(struct ccb_scsiio *sccb)
+{
+ ispsoftc_t *isp = XS_ISP(sccb);
+
+ if (XS_NOERR(sccb))
+ XS_SETERR(sccb, CAM_REQ_CMP);
+
+ if ((sccb->ccb_h.status & CAM_STATUS_MASK) == CAM_REQ_CMP &&
+ (sccb->scsi_status != SCSI_STATUS_OK)) {
+ sccb->ccb_h.status &= ~CAM_STATUS_MASK;
+ if ((sccb->scsi_status == SCSI_STATUS_CHECK_COND) &&
+ (sccb->ccb_h.status & CAM_AUTOSNS_VALID) == 0) {
+ sccb->ccb_h.status |= CAM_AUTOSENSE_FAIL;
+ } else {
+ sccb->ccb_h.status |= CAM_SCSI_STATUS_ERROR;
+ }
+ }
+
+ sccb->ccb_h.status &= ~CAM_SIM_QUEUED;
+ if ((sccb->ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP) {
+ if ((sccb->ccb_h.status & CAM_DEV_QFRZN) == 0) {
+ sccb->ccb_h.status |= CAM_DEV_QFRZN;
+ xpt_freeze_devq(sccb->ccb_h.path, 1);
+ isp_prt(isp, ISP_LOGDEBUG0,
+ "freeze devq %d.%d cam sts %x scsi sts %x",
+ sccb->ccb_h.target_id, sccb->ccb_h.target_lun,
+ sccb->ccb_h.status, sccb->scsi_status);
+ }
+ }
+
+ if ((CAM_DEBUGGED(sccb->ccb_h.path, ISPDDB)) &&
+ (sccb->ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP) {
+ xpt_print_path(sccb->ccb_h.path);
+ isp_prt(isp, ISP_LOGINFO,
+ "cam completion status 0x%x", sccb->ccb_h.status);
+ }
+
+ XS_CMD_S_DONE(sccb);
+ if (XS_CMD_WDOG_P(sccb) == 0) {
+ untimeout(isp_watchdog, (caddr_t)sccb, sccb->ccb_h.timeout_ch);
+ if (XS_CMD_GRACE_P(sccb)) {
+ isp_prt(isp, ISP_LOGDEBUG2,
+ "finished command on borrowed time");
+ }
+ XS_CMD_S_CLEAR(sccb);
+ ISPLOCK_2_CAMLOCK(isp);
+ xpt_done((union ccb *) sccb);
+ CAMLOCK_2_ISPLOCK(isp);
+ }
+}
+
+int
+isp_async(ispsoftc_t *isp, ispasync_t cmd, void *arg)
+{
+ int bus, rv = 0;
+ switch (cmd) {
+ case ISPASYNC_NEW_TGT_PARAMS:
+ {
+#ifdef CAM_NEW_TRAN_CODE
+ struct ccb_trans_settings_scsi *scsi;
+ struct ccb_trans_settings_spi *spi;
+#endif
+ int flags, tgt;
+ sdparam *sdp = isp->isp_param;
+ struct ccb_trans_settings cts;
+ struct cam_path *tmppath;
+
+ memset(&cts, 0, sizeof (struct ccb_trans_settings));
+
+ tgt = *((int *)arg);
+ bus = (tgt >> 16) & 0xffff;
+ tgt &= 0xffff;
+ sdp += bus;
+ ISPLOCK_2_CAMLOCK(isp);
+ if (xpt_create_path(&tmppath, NULL,
+ cam_sim_path(bus? isp->isp_sim2 : isp->isp_sim),
+ tgt, CAM_LUN_WILDCARD) != CAM_REQ_CMP) {
+ CAMLOCK_2_ISPLOCK(isp);
+ isp_prt(isp, ISP_LOGWARN,
+ "isp_async cannot make temp path for %d.%d",
+ tgt, bus);
+ rv = -1;
+ break;
+ }
+ CAMLOCK_2_ISPLOCK(isp);
+ flags = sdp->isp_devparam[tgt].actv_flags;
+#ifdef CAM_NEW_TRAN_CODE
+ cts.type = CTS_TYPE_CURRENT_SETTINGS;
+ cts.protocol = PROTO_SCSI;
+ cts.transport = XPORT_SPI;
+
+ scsi = &cts.proto_specific.scsi;
+ spi = &cts.xport_specific.spi;
+
+ if (flags & DPARM_TQING) {
+ scsi->valid |= CTS_SCSI_VALID_TQ;
+ scsi->flags |= CTS_SCSI_FLAGS_TAG_ENB;
+ spi->flags |= CTS_SPI_FLAGS_TAG_ENB;
+ }
+
+ if (flags & DPARM_DISC) {
+ spi->valid |= CTS_SPI_VALID_DISC;
+ spi->flags |= CTS_SPI_FLAGS_DISC_ENB;
+ }
+ spi->flags |= CTS_SPI_VALID_BUS_WIDTH;
+ if (flags & DPARM_WIDE) {
+ spi->bus_width = MSG_EXT_WDTR_BUS_16_BIT;
+ } else {
+ spi->bus_width = MSG_EXT_WDTR_BUS_8_BIT;
+ }
+ if (flags & DPARM_SYNC) {
+ spi->valid |= CTS_SPI_VALID_SYNC_RATE;
+ spi->valid |= CTS_SPI_VALID_SYNC_OFFSET;
+ spi->sync_period = sdp->isp_devparam[tgt].actv_period;
+ spi->sync_offset = sdp->isp_devparam[tgt].actv_offset;
+ }
+#else
+ cts.flags = CCB_TRANS_CURRENT_SETTINGS;
+ cts.valid = CCB_TRANS_DISC_VALID | CCB_TRANS_TQ_VALID;
+ if (flags & DPARM_DISC) {
+ cts.flags |= CCB_TRANS_DISC_ENB;
+ }
+ if (flags & DPARM_TQING) {
+ cts.flags |= CCB_TRANS_TAG_ENB;
+ }
+ cts.valid |= CCB_TRANS_BUS_WIDTH_VALID;
+ cts.bus_width = (flags & DPARM_WIDE)?
+ MSG_EXT_WDTR_BUS_8_BIT : MSG_EXT_WDTR_BUS_16_BIT;
+ cts.sync_period = sdp->isp_devparam[tgt].actv_period;
+ cts.sync_offset = sdp->isp_devparam[tgt].actv_offset;
+ if (flags & DPARM_SYNC) {
+ cts.valid |=
+ CCB_TRANS_SYNC_RATE_VALID |
+ CCB_TRANS_SYNC_OFFSET_VALID;
+ }
+#endif
+ isp_prt(isp, ISP_LOGDEBUG2,
+ "NEW_TGT_PARAMS bus %d tgt %d period %x offset %x flags %x",
+ bus, tgt, sdp->isp_devparam[tgt].actv_period,
+ sdp->isp_devparam[tgt].actv_offset, flags);
+ xpt_setup_ccb(&cts.ccb_h, tmppath, 1);
+ ISPLOCK_2_CAMLOCK(isp);
+ xpt_async(AC_TRANSFER_NEG, tmppath, &cts);
+ xpt_free_path(tmppath);
+ CAMLOCK_2_ISPLOCK(isp);
+ break;
+ }
+ case ISPASYNC_BUS_RESET:
+ bus = *((int *)arg);
+ isp_prt(isp, ISP_LOGINFO, "SCSI bus reset on bus %d detected",
+ bus);
+ if (bus > 0 && isp->isp_path2) {
+ ISPLOCK_2_CAMLOCK(isp);
+ xpt_async(AC_BUS_RESET, isp->isp_path2, NULL);
+ CAMLOCK_2_ISPLOCK(isp);
+ } else if (isp->isp_path) {
+ ISPLOCK_2_CAMLOCK(isp);
+ xpt_async(AC_BUS_RESET, isp->isp_path, NULL);
+ CAMLOCK_2_ISPLOCK(isp);
+ }
+ break;
+ case ISPASYNC_LIP:
+ if (isp->isp_path) {
+ isp_freeze_loopdown(isp, "ISPASYNC_LIP");
+ }
+ isp_prt(isp, ISP_LOGINFO, "LIP Received");
+ break;
+ case ISPASYNC_LOOP_RESET:
+ if (isp->isp_path) {
+ isp_freeze_loopdown(isp, "ISPASYNC_LOOP_RESET");
+ }
+ isp_prt(isp, ISP_LOGINFO, "Loop Reset Received");
+ break;
+ case ISPASYNC_LOOP_DOWN:
+ if (isp->isp_path) {
+ isp_freeze_loopdown(isp, "ISPASYNC_LOOP_DOWN");
+ }
+ isp_prt(isp, ISP_LOGINFO, "Loop DOWN");
+ break;
+ case ISPASYNC_LOOP_UP:
+ /*
+ * Now we just note that Loop has come up. We don't
+ * actually do anything because we're waiting for a
+ * Change Notify before activating the FC cleanup
+ * thread to look at the state of the loop again.
+ */
+ isp_prt(isp, ISP_LOGINFO, "Loop UP");
+ break;
+ case ISPASYNC_PROMENADE:
+ {
+ const char *fmt = "Target %d (Loop 0x%x) Port ID 0x%x "
+ "(role %s) %s\n Port WWN 0x%08x%08x\n Node WWN 0x%08x%08x";
+ static const char *roles[4] = {
+ "(none)", "Target", "Initiator", "Target/Initiator"
+ };
+ fcparam *fcp = isp->isp_param;
+ int tgt = *((int *) arg);
+#if __FreeBSD_version >= 500000
+ int is_tgt_mask = (SVC3_TGT_ROLE >> SVC3_ROLE_SHIFT);
+ struct cam_path *tmppath;
+#endif
+ struct lportdb *lp = &fcp->portdb[tgt];
+
+ isp_prt(isp, ISP_LOGINFO, fmt, tgt, lp->loopid, lp->portid,
+ roles[lp->roles & 0x3],
+ (lp->valid)? "Arrived" : "Departed",
+ (uint32_t) (lp->port_wwn >> 32),
+ (uint32_t) (lp->port_wwn & 0xffffffffLL),
+ (uint32_t) (lp->node_wwn >> 32),
+ (uint32_t) (lp->node_wwn & 0xffffffffLL));
+
+ ISPLOCK_2_CAMLOCK(isp);
+#if __FreeBSD_version >= 500000
+ if (xpt_create_path(&tmppath, NULL, cam_sim_path(isp->isp_sim),
+ (target_id_t)tgt, CAM_LUN_WILDCARD) != CAM_REQ_CMP) {
+ CAMLOCK_2_ISPLOCK(isp);
+ break;
+ }
+ /*
+ * Policy: only announce targets.
+ */
+ if (lp->roles & is_tgt_mask) {
+ if (lp->valid) {
+ xpt_async(AC_FOUND_DEVICE, tmppath, NULL);
+ } else {
+ xpt_async(AC_LOST_DEVICE, tmppath, NULL);
+ }
+ }
+ xpt_free_path(tmppath);
+#endif
+ CAMLOCK_2_ISPLOCK(isp);
+ break;
+ }
+ case ISPASYNC_CHANGE_NOTIFY:
+ if (arg == ISPASYNC_CHANGE_PDB) {
+ isp_prt(isp, ISP_LOGINFO,
+ "Port Database Changed");
+ } else if (arg == ISPASYNC_CHANGE_SNS) {
+ isp_prt(isp, ISP_LOGINFO,
+ "Name Server Database Changed");
+ }
+#if __FreeBSD_version < 500000
+ wakeup(&isp->isp_osinfo.kproc);
+#else
+#ifdef ISP_SMPLOCK
+ cv_signal(&isp->isp_osinfo.kthread_cv);
+#else
+ wakeup(&isp->isp_osinfo.kthread_cv);
+#endif
+#endif
+ break;
+ case ISPASYNC_FABRIC_DEV:
+ {
+ int target, base, lim;
+ fcparam *fcp = isp->isp_param;
+ struct lportdb *lp = NULL;
+ struct lportdb *clp = (struct lportdb *) arg;
+ char *pt;
+
+ switch (clp->port_type) {
+ case 1:
+ pt = " N_Port";
+ break;
+ case 2:
+ pt = " NL_Port";
+ break;
+ case 3:
+ pt = "F/NL_Port";
+ break;
+ case 0x7f:
+ pt = " Nx_Port";
+ break;
+ case 0x81:
+ pt = " F_port";
+ break;
+ case 0x82:
+ pt = " FL_Port";
+ break;
+ case 0x84:
+ pt = " E_port";
+ break;
+ default:
+ pt = " ";
+ break;
+ }
+
+ isp_prt(isp, ISP_LOGINFO,
+ "%s Fabric Device @ PortID 0x%x", pt, clp->portid);
+
+ /*
+ * If we don't have an initiator role we bail.
+ *
+ * We just use ISPASYNC_FABRIC_DEV for announcement purposes.
+ */
+
+ if ((isp->isp_role & ISP_ROLE_INITIATOR) == 0) {
+ break;
+ }
+
+ /*
+ * Is this entry for us? If so, we bail.
+ */
+
+ if (fcp->isp_portid == clp->portid) {
+ break;
+ }
+
+ /*
+ * Else, the default policy is to find room for it in
+ * our local port database. Later, when we execute
+ * the call to isp_pdb_sync either this newly arrived
+ * or already logged in device will be (re)announced.
+ */
+
+ if (fcp->isp_topo == TOPO_FL_PORT)
+ base = FC_SNS_ID+1;
+ else
+ base = 0;
+
+ if (fcp->isp_topo == TOPO_N_PORT)
+ lim = 1;
+ else
+ lim = MAX_FC_TARG;
+
+ /*
+ * Is it already in our list?
+ */
+ for (target = base; target < lim; target++) {
+ if (target >= FL_PORT_ID && target <= FC_SNS_ID) {
+ continue;
+ }
+ lp = &fcp->portdb[target];
+ if (lp->port_wwn == clp->port_wwn &&
+ lp->node_wwn == clp->node_wwn) {
+ lp->fabric_dev = 1;
+ break;
+ }
+ }
+ if (target < lim) {
+ break;
+ }
+ for (target = base; target < lim; target++) {
+ if (target >= FL_PORT_ID && target <= FC_SNS_ID) {
+ continue;
+ }
+ lp = &fcp->portdb[target];
+ if (lp->port_wwn == 0) {
+ break;
+ }
+ }
+ if (target == lim) {
+ isp_prt(isp, ISP_LOGWARN,
+ "out of space for fabric devices");
+ break;
+ }
+ lp->port_type = clp->port_type;
+ lp->fc4_type = clp->fc4_type;
+ lp->node_wwn = clp->node_wwn;
+ lp->port_wwn = clp->port_wwn;
+ lp->portid = clp->portid;
+ lp->fabric_dev = 1;
+ break;
+ }
+#ifdef ISP_TARGET_MODE
+ case ISPASYNC_TARGET_NOTIFY:
+ {
+ tmd_notify_t *nt = arg;
+ isp_prt(isp, ISP_LOGALL,
+ "target notify code 0x%x", nt->nt_ncode);
+ break;
+ }
+ case ISPASYNC_TARGET_ACTION:
+ switch (((isphdr_t *)arg)->rqs_entry_type) {
+ default:
+ isp_prt(isp, ISP_LOGWARN,
+ "event 0x%x for unhandled target action",
+ ((isphdr_t *)arg)->rqs_entry_type);
+ break;
+ case RQSTYPE_NOTIFY:
+ if (IS_SCSI(isp)) {
+ rv = isp_handle_platform_notify_scsi(isp,
+ (in_entry_t *) arg);
+ } else {
+ rv = isp_handle_platform_notify_fc(isp,
+ (in_fcentry_t *) arg);
+ }
+ break;
+ case RQSTYPE_ATIO:
+ rv = isp_handle_platform_atio(isp, (at_entry_t *) arg);
+ break;
+ case RQSTYPE_ATIO2:
+ rv = isp_handle_platform_atio2(isp, (at2_entry_t *)arg);
+ break;
+ case RQSTYPE_CTIO3:
+ case RQSTYPE_CTIO2:
+ case RQSTYPE_CTIO:
+ rv = isp_handle_platform_ctio(isp, arg);
+ break;
+ case RQSTYPE_ENABLE_LUN:
+ case RQSTYPE_MODIFY_LUN:
+ isp_ledone(isp, (lun_entry_t *) arg);
+ break;
+ }
+ break;
+#endif
+ case ISPASYNC_FW_CRASH:
+ {
+ uint16_t mbox1, mbox6;
+ mbox1 = ISP_READ(isp, OUTMAILBOX1);
+ if (IS_DUALBUS(isp)) {
+ mbox6 = ISP_READ(isp, OUTMAILBOX6);
+ } else {
+ mbox6 = 0;
+ }
+ isp_prt(isp, ISP_LOGERR,
+ "Internal Firmware Error on bus %d @ RISC Address 0x%x",
+ mbox6, mbox1);
+#ifdef ISP_FW_CRASH_DUMP
+ /*
+ * XXX: really need a thread to do this right.
+ */
+ if (IS_FC(isp)) {
+ FCPARAM(isp)->isp_fwstate = FW_CONFIG_WAIT;
+ FCPARAM(isp)->isp_loopstate = LOOP_NIL;
+ isp_freeze_loopdown(isp, "f/w crash");
+ isp_fw_dump(isp);
+ }
+ isp_reinit(isp);
+ isp_async(isp, ISPASYNC_FW_RESTARTED, NULL);
+#endif
+ break;
+ }
+ case ISPASYNC_UNHANDLED_RESPONSE:
+ break;
+ default:
+ isp_prt(isp, ISP_LOGERR, "unknown isp_async event %d", cmd);
+ break;
+ }
+ return (rv);
+}
+
+
+/*
+ * Locks are held before coming here.
+ */
+void
+isp_uninit(ispsoftc_t *isp)
+{
+ ISP_WRITE(isp, HCCR, HCCR_CMD_RESET);
+ DISABLE_INTS(isp);
+}
+
+void
+isp_prt(ispsoftc_t *isp, int level, const char *fmt, ...)
+{
+ va_list ap;
+ if (level != ISP_LOGALL && (level & isp->isp_dblev) == 0) {
+ return;
+ }
+ printf("%s: ", device_get_nameunit(isp->isp_dev));
+ va_start(ap, fmt);
+ vprintf(fmt, ap);
+ va_end(ap);
+ printf("\n");
+}
diff --git a/sys/dev/isp/isp_freebsd.h b/sys/dev/isp/isp_freebsd.h
new file mode 100644
index 000000000000..6502dea00844
--- /dev/null
+++ b/sys/dev/isp/isp_freebsd.h
@@ -0,0 +1,521 @@
+/* $FreeBSD$ */
+/*-
+ * Qlogic ISP SCSI Host Adapter FreeBSD Wrapper Definitions
+ *
+ * Copyright (c) 1997-2006 by Matthew Jacob
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice 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.
+ *
+ * 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.
+ */
+#ifndef _ISP_FREEBSD_H
+#define _ISP_FREEBSD_H
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/endian.h>
+#if __FreeBSD_version < 500000
+#include <sys/kernel.h>
+#include <sys/queue.h>
+#include <sys/malloc.h>
+#else
+#include <sys/lock.h>
+#include <sys/kernel.h>
+#include <sys/queue.h>
+#include <sys/malloc.h>
+#include <sys/mutex.h>
+#include <sys/condvar.h>
+#endif
+
+#include <sys/proc.h>
+#include <sys/bus.h>
+
+#include <machine/bus.h>
+#if __FreeBSD_version < 500000
+#include <machine/clock.h>
+#endif
+#include <machine/cpu.h>
+
+#include <cam/cam.h>
+#include <cam/cam_debug.h>
+#include <cam/cam_ccb.h>
+#include <cam/cam_sim.h>
+#include <cam/cam_xpt.h>
+#include <cam/cam_xpt_sim.h>
+#include <cam/cam_debug.h>
+#include <cam/scsi/scsi_all.h>
+#include <cam/scsi/scsi_message.h>
+
+#include "opt_ddb.h"
+#include "opt_isp.h"
+
+#if __FreeBSD_version < 500000
+#define ISP_PLATFORM_VERSION_MAJOR 4
+#define ISP_PLATFORM_VERSION_MINOR 17
+#else
+#define ISP_PLATFORM_VERSION_MAJOR 5
+#define ISP_PLATFORM_VERSION_MINOR 9
+#endif
+
+/*
+ * Efficiency- get rid of SBus code && tests unless we need them.
+ */
+#ifdef __sparc64__
+#define ISP_SBUS_SUPPORTED 1
+#else
+#define ISP_SBUS_SUPPORTED 0
+#endif
+
+#define HANDLE_LOOPSTATE_IN_OUTER_LAYERS 1
+/* #define ISP_SMPLOCK 1 */
+
+#if __FreeBSD_version < 500000
+#define ISP_IFLAGS INTR_TYPE_CAM
+#else
+#ifdef ISP_SMPLOCK
+#define ISP_IFLAGS INTR_TYPE_CAM | INTR_ENTROPY | INTR_MPSAFE
+#else
+#define ISP_IFLAGS INTR_TYPE_CAM | INTR_ENTROPY
+#endif
+#endif
+
+#if __FreeBSD_version < 700000
+typedef void ispfwfunc(int, int, int, uint16_t **);
+#endif
+
+#ifdef ISP_TARGET_MODE
+#define ISP_TARGET_FUNCTIONS 1
+#define ATPDPSIZE 256
+typedef struct {
+ uint32_t orig_datalen;
+ uint32_t bytes_xfered;
+ uint32_t last_xframt;
+ uint32_t tag : 16,
+ lun : 13, /* not enough */
+ state : 3;
+} atio_private_data_t;
+#define ATPD_STATE_FREE 0
+#define ATPD_STATE_ATIO 1
+#define ATPD_STATE_CAM 2
+#define ATPD_STATE_CTIO 3
+#define ATPD_STATE_LAST_CTIO 4
+#define ATPD_STATE_PDON 5
+
+typedef struct tstate {
+ struct tstate *next;
+ struct cam_path *owner;
+ struct ccb_hdr_slist atios;
+ struct ccb_hdr_slist inots;
+ lun_id_t lun;
+ int bus;
+ uint32_t hold;
+ int atio_count;
+ int inot_count;
+} tstate_t;
+
+#define LUN_HASH_SIZE 32
+#define LUN_HASH_FUNC(isp, port, lun) \
+ ((IS_DUALBUS(isp)) ? \
+ (((lun) & ((LUN_HASH_SIZE >> 1) - 1)) << (port)) : \
+ ((lun) & (LUN_HASH_SIZE - 1)))
+#endif
+
+struct isposinfo {
+ struct ispsoftc * next;
+ uint64_t default_port_wwn;
+ uint64_t default_node_wwn;
+ uint32_t default_id;
+ device_t dev;
+ struct cam_sim *sim;
+ struct cam_path *path;
+ struct cam_sim *sim2;
+ struct cam_path *path2;
+ struct intr_config_hook ehook;
+ uint8_t
+ disabled : 1,
+ fcbsy : 1,
+ ktmature : 1,
+ mboxwaiting : 1,
+ intsok : 1,
+ simqfrozen : 3;
+#if __FreeBSD_version >= 500000
+ struct firmware * fw;
+ struct mtx lock;
+ struct cv kthread_cv;
+#endif
+ struct proc *kproc;
+ bus_dma_tag_t cdmat;
+ bus_dmamap_t cdmap;
+#define isp_cdmat isp_osinfo.cdmat
+#define isp_cdmap isp_osinfo.cdmap
+#ifdef ISP_TARGET_MODE
+#define TM_WILDCARD_ENABLED 0x02
+#define TM_TMODE_ENABLED 0x01
+ uint8_t tmflags[2]; /* two busses */
+#define NLEACT 4
+ union ccb * leact[NLEACT];
+ tstate_t tsdflt[2]; /* two busses */
+ tstate_t *lun_hash[LUN_HASH_SIZE];
+ atio_private_data_t atpdp[ATPDPSIZE];
+#endif
+};
+
+#define isp_lock isp_osinfo.lock
+
+/*
+ * Locking macros...
+ */
+
+#ifdef ISP_SMPLOCK
+#define ISP_LOCK(x) mtx_lock(&(x)->isp_lock)
+#define ISP_UNLOCK(x) mtx_unlock(&(x)->isp_lock)
+#define ISPLOCK_2_CAMLOCK(isp) \
+ mtx_unlock(&(isp)->isp_lock); mtx_lock(&Giant)
+#define CAMLOCK_2_ISPLOCK(isp) \
+ mtx_unlock(&Giant); mtx_lock(&(isp)->isp_lock)
+#else
+#define ISP_LOCK(x) do { } while (0)
+#define ISP_UNLOCK(x) do { } while (0)
+#define ISPLOCK_2_CAMLOCK(isp) do { } while (0)
+#define CAMLOCK_2_ISPLOCK(isp) do { } while (0)
+#endif
+
+/*
+ * Required Macros/Defines
+ */
+
+#define ISP2100_SCRLEN 0x800
+
+#define MEMZERO(a, b) memset(a, 0, b)
+#define MEMCPY memcpy
+#define SNPRINTF snprintf
+#define USEC_DELAY DELAY
+#define USEC_SLEEP(isp, x) \
+ if (isp->isp_osinfo.intsok) \
+ ISP_UNLOCK(isp); \
+ DELAY(x); \
+ if (isp->isp_osinfo.intsok) \
+ ISP_LOCK(isp)
+
+#define NANOTIME_T struct timespec
+#define GET_NANOTIME nanotime
+#define GET_NANOSEC(x) ((x)->tv_sec * 1000000000 + (x)->tv_nsec)
+#define NANOTIME_SUB nanotime_sub
+
+#define MAXISPREQUEST(isp) ((IS_FC(isp) || IS_ULTRA2(isp))? 1024 : 256)
+
+#define MEMORYBARRIER(isp, type, offset, size) \
+switch (type) { \
+case SYNC_SFORDEV: \
+case SYNC_REQUEST: \
+ bus_dmamap_sync(isp->isp_cdmat, isp->isp_cdmap, \
+ BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE); \
+ break; \
+case SYNC_SFORCPU: \
+case SYNC_RESULT: \
+ bus_dmamap_sync(isp->isp_cdmat, isp->isp_cdmap, \
+ BUS_DMASYNC_POSTREAD | BUS_DMASYNC_POSTWRITE); \
+ break; \
+default: \
+ break; \
+}
+
+#define MBOX_ACQUIRE(isp)
+#define MBOX_WAIT_COMPLETE isp_mbox_wait_complete
+#define MBOX_NOTIFY_COMPLETE(isp) \
+ if (isp->isp_osinfo.mboxwaiting) { \
+ isp->isp_osinfo.mboxwaiting = 0; \
+ wakeup(&isp->isp_mbxworkp); \
+ } \
+ isp->isp_mboxbsy = 0
+#define MBOX_RELEASE(isp)
+
+#define FC_SCRATCH_ACQUIRE(isp) \
+ if (isp->isp_osinfo.fcbsy) { \
+ isp_prt(isp, ISP_LOGWARN, \
+ "FC scratch area busy (line %d)!", __LINE__); \
+ } else \
+ isp->isp_osinfo.fcbsy = 1
+#define FC_SCRATCH_RELEASE(isp) isp->isp_osinfo.fcbsy = 0
+
+#ifndef SCSI_GOOD
+#define SCSI_GOOD SCSI_STATUS_OK
+#endif
+#ifndef SCSI_CHECK
+#define SCSI_CHECK SCSI_STATUS_CHECK_COND
+#endif
+#ifndef SCSI_BUSY
+#define SCSI_BUSY SCSI_STATUS_BUSY
+#endif
+#ifndef SCSI_QFULL
+#define SCSI_QFULL SCSI_STATUS_QUEUE_FULL
+#endif
+
+#define XS_T struct ccb_scsiio
+#define XS_DMA_ADDR_T bus_addr_t
+#define XS_ISP(ccb) ((ispsoftc_t *) (ccb)->ccb_h.spriv_ptr1)
+#define XS_CHANNEL(ccb) cam_sim_bus(xpt_path_sim((ccb)->ccb_h.path))
+#define XS_TGT(ccb) (ccb)->ccb_h.target_id
+#define XS_LUN(ccb) (ccb)->ccb_h.target_lun
+
+#define XS_CDBP(ccb) \
+ (((ccb)->ccb_h.flags & CAM_CDB_POINTER)? \
+ (ccb)->cdb_io.cdb_ptr : (ccb)->cdb_io.cdb_bytes)
+
+#define XS_CDBLEN(ccb) (ccb)->cdb_len
+#define XS_XFRLEN(ccb) (ccb)->dxfer_len
+#define XS_TIME(ccb) (ccb)->ccb_h.timeout
+#define XS_RESID(ccb) (ccb)->resid
+#define XS_STSP(ccb) (&(ccb)->scsi_status)
+#define XS_SNSP(ccb) (&(ccb)->sense_data)
+
+#define XS_SNSLEN(ccb) \
+ imin((sizeof((ccb)->sense_data)), ccb->sense_len)
+
+#define XS_SNSKEY(ccb) ((ccb)->sense_data.flags & 0xf)
+#define XS_TAG_P(ccb) \
+ (((ccb)->ccb_h.flags & CAM_TAG_ACTION_VALID) && \
+ (ccb)->tag_action != CAM_TAG_ACTION_NONE)
+
+#define XS_TAG_TYPE(ccb) \
+ ((ccb->tag_action == MSG_SIMPLE_Q_TAG)? REQFLAG_STAG : \
+ ((ccb->tag_action == MSG_HEAD_OF_Q_TAG)? REQFLAG_HTAG : REQFLAG_OTAG))
+
+
+#define XS_SETERR(ccb, v) (ccb)->ccb_h.status &= ~CAM_STATUS_MASK, \
+ (ccb)->ccb_h.status |= v, \
+ (ccb)->ccb_h.spriv_field0 |= ISP_SPRIV_ERRSET
+
+# define HBA_NOERROR CAM_REQ_INPROG
+# define HBA_BOTCH CAM_UNREC_HBA_ERROR
+# define HBA_CMDTIMEOUT CAM_CMD_TIMEOUT
+# define HBA_SELTIMEOUT CAM_SEL_TIMEOUT
+# define HBA_TGTBSY CAM_SCSI_STATUS_ERROR
+# define HBA_BUSRESET CAM_SCSI_BUS_RESET
+# define HBA_ABORTED CAM_REQ_ABORTED
+# define HBA_DATAOVR CAM_DATA_RUN_ERR
+# define HBA_ARQFAIL CAM_AUTOSENSE_FAIL
+
+
+#define XS_ERR(ccb) ((ccb)->ccb_h.status & CAM_STATUS_MASK)
+
+#define XS_NOERR(ccb) \
+ (((ccb)->ccb_h.spriv_field0 & ISP_SPRIV_ERRSET) == 0 || \
+ ((ccb)->ccb_h.status & CAM_STATUS_MASK) == CAM_REQ_INPROG)
+
+#define XS_INITERR(ccb) \
+ XS_SETERR(ccb, CAM_REQ_INPROG), (ccb)->ccb_h.spriv_field0 = 0
+
+#define XS_SAVE_SENSE(xs, sp) \
+ (xs)->ccb_h.status |= CAM_AUTOSNS_VALID, \
+ memcpy(&(xs)->sense_data, sp->req_sense_data, \
+ imin(XS_SNSLEN(xs), sp->req_sense_len))
+
+#define XS_SET_STATE_STAT(a, b, c)
+
+#define DEFAULT_IID(x) (isp)->isp_osinfo.default_id
+#define DEFAULT_LOOPID(x) (isp)->isp_osinfo.default_id
+#define DEFAULT_NODEWWN(isp) (isp)->isp_osinfo.default_node_wwn
+#define DEFAULT_PORTWWN(isp) (isp)->isp_osinfo.default_port_wwn
+#define ISP_NODEWWN(isp) FCPARAM(isp)->isp_nodewwn
+#define ISP_PORTWWN(isp) FCPARAM(isp)->isp_portwwn
+
+#if BYTE_ORDER == BIG_ENDIAN
+#ifdef ISP_SBUS_SUPPORTED
+#define ISP_IOXPUT_8(isp, s, d) *(d) = s
+#define ISP_IOXPUT_16(isp, s, d) \
+ *(d) = (isp->isp_bustype == ISP_BT_SBUS)? s : bswap16(s)
+#define ISP_IOXPUT_32(isp, s, d) \
+ *(d) = (isp->isp_bustype == ISP_BT_SBUS)? s : bswap32(s)
+#define ISP_IOXGET_8(isp, s, d) d = (*((uint8_t *)s))
+#define ISP_IOXGET_16(isp, s, d) \
+ d = (isp->isp_bustype == ISP_BT_SBUS)? \
+ *((uint16_t *)s) : bswap16(*((uint16_t *)s))
+#define ISP_IOXGET_32(isp, s, d) \
+ d = (isp->isp_bustype == ISP_BT_SBUS)? \
+ *((uint32_t *)s) : bswap32(*((uint32_t *)s))
+#else
+#define ISP_IOXPUT_8(isp, s, d) *(d) = s
+#define ISP_IOXPUT_16(isp, s, d) *(d) = bswap16(s)
+#define ISP_IOXPUT_32(isp, s, d) *(d) = bswap32(s)
+#define ISP_IOXGET_8(isp, s, d) d = (*((uint8_t *)s))
+#define ISP_IOXGET_16(isp, s, d) d = bswap16(*((uint16_t *)s))
+#define ISP_IOXGET_32(isp, s, d) d = bswap32(*((uint32_t *)s))
+#endif
+#define ISP_SWIZZLE_NVRAM_WORD(isp, rp) *rp = bswap16(*rp)
+#else
+#define ISP_IOXPUT_8(isp, s, d) *(d) = s
+#define ISP_IOXPUT_16(isp, s, d) *(d) = s
+#define ISP_IOXPUT_32(isp, s, d) *(d) = s
+#define ISP_IOXGET_8(isp, s, d) d = *(s)
+#define ISP_IOXGET_16(isp, s, d) d = *(s)
+#define ISP_IOXGET_32(isp, s, d) d = *(s)
+#define ISP_SWIZZLE_NVRAM_WORD(isp, rp)
+#endif
+
+/*
+ * Includes of common header files
+ */
+
+#include <dev/isp/ispreg.h>
+#include <dev/isp/ispvar.h>
+#include <dev/isp/ispmbox.h>
+
+#ifdef ISP_TARGET_MODE
+#include <dev/isp/isp_tpublic.h>
+#endif
+
+void isp_prt(ispsoftc_t *, int level, const char *, ...)
+ __printflike(3, 4);
+/*
+ * isp_osinfo definiitions && shorthand
+ */
+#define SIMQFRZ_RESOURCE 0x1
+#define SIMQFRZ_LOOPDOWN 0x2
+#define SIMQFRZ_TIMED 0x4
+
+#define isp_sim isp_osinfo.sim
+#define isp_path isp_osinfo.path
+#define isp_sim2 isp_osinfo.sim2
+#define isp_path2 isp_osinfo.path2
+#define isp_dev isp_osinfo.dev
+
+/*
+ * prototypes for isp_pci && isp_freebsd to share
+ */
+extern void isp_attach(ispsoftc_t *);
+extern void isp_uninit(ispsoftc_t *);
+
+/*
+ * driver global data
+ */
+extern int isp_announced;
+
+/*
+ * Platform private flags
+ */
+#define ISP_SPRIV_ERRSET 0x1
+#define ISP_SPRIV_INWDOG 0x2
+#define ISP_SPRIV_GRACE 0x4
+#define ISP_SPRIV_DONE 0x8
+
+#define XS_CMD_S_WDOG(sccb) (sccb)->ccb_h.spriv_field0 |= ISP_SPRIV_INWDOG
+#define XS_CMD_C_WDOG(sccb) (sccb)->ccb_h.spriv_field0 &= ~ISP_SPRIV_INWDOG
+#define XS_CMD_WDOG_P(sccb) ((sccb)->ccb_h.spriv_field0 & ISP_SPRIV_INWDOG)
+
+#define XS_CMD_S_GRACE(sccb) (sccb)->ccb_h.spriv_field0 |= ISP_SPRIV_GRACE
+#define XS_CMD_C_GRACE(sccb) (sccb)->ccb_h.spriv_field0 &= ~ISP_SPRIV_GRACE
+#define XS_CMD_GRACE_P(sccb) ((sccb)->ccb_h.spriv_field0 & ISP_SPRIV_GRACE)
+
+#define XS_CMD_S_DONE(sccb) (sccb)->ccb_h.spriv_field0 |= ISP_SPRIV_DONE
+#define XS_CMD_C_DONE(sccb) (sccb)->ccb_h.spriv_field0 &= ~ISP_SPRIV_DONE
+#define XS_CMD_DONE_P(sccb) ((sccb)->ccb_h.spriv_field0 & ISP_SPRIV_DONE)
+
+#define XS_CMD_S_CLEAR(sccb) (sccb)->ccb_h.spriv_field0 = 0
+
+/*
+ * Platform specific inline functions
+ */
+
+static __inline void isp_mbox_wait_complete(ispsoftc_t *);
+static __inline void
+isp_mbox_wait_complete(ispsoftc_t *isp)
+{
+ if (isp->isp_osinfo.intsok) {
+ int lim = ((isp->isp_mbxwrk0)? 120 : 20) * hz;
+ isp->isp_osinfo.mboxwaiting = 1;
+#ifdef ISP_SMPLOCK
+ (void) msleep(&isp->isp_mbxworkp,
+ &isp->isp_lock, PRIBIO, "isp_mboxwaiting", lim);
+#else
+ (void) tsleep(&isp->isp_mbxworkp,
+ PRIBIO, "isp_mboxwaiting", lim);
+#endif
+ if (isp->isp_mboxbsy != 0) {
+ isp_prt(isp, ISP_LOGWARN,
+ "Interrupting Mailbox Command (0x%x) Timeout",
+ isp->isp_lastmbxcmd);
+ isp->isp_mboxbsy = 0;
+ }
+ isp->isp_osinfo.mboxwaiting = 0;
+ } else {
+ int lim = ((isp->isp_mbxwrk0)? 240 : 60) * 10000;
+ int j;
+ for (j = 0; j < lim; j++) {
+ uint16_t isr, sema, mbox;
+ if (isp->isp_mboxbsy == 0) {
+ break;
+ }
+ if (ISP_READ_ISR(isp, &isr, &sema, &mbox)) {
+ isp_intr(isp, isr, sema, mbox);
+ if (isp->isp_mboxbsy == 0) {
+ break;
+ }
+ }
+ USEC_DELAY(500);
+ }
+ if (isp->isp_mboxbsy != 0) {
+ isp_prt(isp, ISP_LOGWARN,
+ "Polled Mailbox Command (0x%x) Timeout",
+ isp->isp_lastmbxcmd);
+ }
+ }
+}
+
+static __inline uint64_t nanotime_sub(struct timespec *, struct timespec *);
+static __inline uint64_t
+nanotime_sub(struct timespec *b, struct timespec *a)
+{
+ uint64_t elapsed;
+ struct timespec x = *b;
+ timespecsub(&x, a);
+ elapsed = GET_NANOSEC(&x);
+ if (elapsed == 0)
+ elapsed++;
+ return (elapsed);
+}
+
+static __inline char *strncat(char *, const char *, size_t);
+static __inline char *
+strncat(char *d, const char *s, size_t c)
+{
+ char *t = d;
+
+ if (c) {
+ while (*d)
+ d++;
+ while ((*d++ = *s++)) {
+ if (--c == 0) {
+ *d = '\0';
+ break;
+ }
+ }
+ }
+ return (t);
+}
+
+/*
+ * ISP Library functions
+ */
+
+#include <dev/isp/isp_library.h>
+
+#endif /* _ISP_FREEBSD_H */
diff --git a/sys/dev/isp/isp_ioctl.h b/sys/dev/isp/isp_ioctl.h
new file mode 100644
index 000000000000..4171466e29d2
--- /dev/null
+++ b/sys/dev/isp/isp_ioctl.h
@@ -0,0 +1,195 @@
+/* $FreeBSD$ */
+/*-
+ *
+ * Copyright (c) 1997-2006 by Matthew Jacob
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice 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.
+ *
+ * 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.
+ */
+/*
+ * ioctl definitions for Qlogic FC/SCSI HBA driver
+ */
+#define ISP_IOC (021) /* 'Ctrl-Q' */
+
+/*
+ * This ioctl sets/retrieves the debugging level for this hba instance.
+ * Note that this is not a simple integer level- see ispvar.h for definitions.
+ *
+ * The arguments is a pointer to an integer with the new debugging level.
+ * The old value is written into this argument.
+ */
+
+#define ISP_SDBLEV _IOWR(ISP_IOC, 1, int)
+
+/*
+ * This ioctl resets the HBA. Use with caution.
+ */
+#define ISP_RESETHBA _IO(ISP_IOC, 2)
+
+/*
+ * This ioctl performs a fibre channel rescan.
+ */
+#define ISP_RESCAN _IO(ISP_IOC, 3)
+
+/*
+ * This ioctl performs a reset and then will set the adapter to the
+ * role that was passed in (the old role will be returned). It almost
+ * goes w/o saying: use with caution.
+ */
+#define ISP_SETROLE _IOWR(ISP_IOC, 4, int)
+
+#define ISP_ROLE_NONE 0x0
+#define ISP_ROLE_TARGET 0x1
+#define ISP_ROLE_INITIATOR 0x2
+#define ISP_ROLE_BOTH (ISP_ROLE_TARGET|ISP_ROLE_INITIATOR)
+
+/*
+ * Get the current adapter role
+ */
+#define ISP_GETROLE _IOR(ISP_IOC, 5, int)
+
+/*
+ * Get/Clear Stats
+ */
+#define ISP_STATS_VERSION 0
+typedef struct {
+ uint8_t isp_stat_version;
+ uint8_t isp_type; /* (ro) reflects chip type */
+ uint8_t isp_revision; /* (ro) reflects chip version */
+ uint8_t unused1;
+ uint32_t unused2;
+ /*
+ * Statistics Counters
+ */
+#define ISP_NSTATS 16
+#define ISP_INTCNT 0
+#define ISP_INTBOGUS 1
+#define ISP_INTMBOXC 2
+#define ISP_INGOASYNC 3
+#define ISP_RSLTCCMPLT 4
+#define ISP_FPHCCMCPLT 5
+#define ISP_RSCCHIWAT 6
+#define ISP_FPCCHIWAT 7
+ uint64_t isp_stats[ISP_NSTATS];
+} isp_stats_t;
+
+#define ISP_GET_STATS _IOR(ISP_IOC, 6, isp_stats_t)
+#define ISP_CLR_STATS _IO(ISP_IOC, 7)
+
+/*
+ * Initiate a LIP
+ */
+#define ISP_FC_LIP _IO(ISP_IOC, 8)
+
+/*
+ * Return the Port Database structure for the named device, or ENODEV if none.
+ * Caller fills in virtual loopid (0..255), aka 'target'. The driver returns
+ * ENODEV (if nothing valid there) or the actual loopid (for local loop devices
+ * only), 24 bit Port ID and Node and Port WWNs.
+ */
+struct isp_fc_device {
+ uint32_t loopid; /* 0..255 */
+ uint32_t : 6,
+ role : 2,
+ portid : 24; /* 24 bit Port ID */
+ uint64_t node_wwn;
+ uint64_t port_wwn;
+};
+#define ISP_FC_GETDINFO _IOWR(ISP_IOC, 9, struct isp_fc_device)
+
+/*
+ * Get F/W crash dump
+ */
+#define ISP_GET_FW_CRASH_DUMP _IO(ISP_IOC, 10)
+#define ISP_FORCE_CRASH_DUMP _IO(ISP_IOC, 11)
+
+/*
+ * Get information about this Host Adapter, including current connection
+ * topology and capabilities.
+ */
+struct isp_hba_device {
+ uint32_t
+ : 8,
+ : 4,
+ fc_speed : 4, /* Gbps */
+ : 2,
+ fc_class2 : 1,
+ fc_ip_supported : 1,
+ fc_scsi_supported : 1,
+ fc_topology : 3,
+ fc_loopid : 8;
+ uint8_t fc_fw_major;
+ uint8_t fc_fw_minor;
+ uint8_t fc_fw_micro;
+ uint8_t reserved;
+ uint64_t nvram_node_wwn;
+ uint64_t nvram_port_wwn;
+ uint64_t active_node_wwn;
+ uint64_t active_port_wwn;
+};
+
+#define ISP_TOPO_UNKNOWN 0 /* connection topology unknown */
+#define ISP_TOPO_FCAL 1 /* private or PL_DA */
+#define ISP_TOPO_LPORT 2 /* public loop */
+#define ISP_TOPO_NPORT 3 /* N-port */
+#define ISP_TOPO_FPORT 4 /* F-port */
+
+#define ISP_FC_GETHINFO _IOR(ISP_IOC, 12, struct isp_hba_device)
+/*
+ * Set some internal parameters. This doesn't take effect until
+ * the chip is reset.
+ *
+ * Each parameter is generalized to be a name string with an integer value.
+ *
+ * Known parameters are:
+ *
+ * Name Value Range
+ *
+ * "framelength" 512,1024,2048
+ * "exec_throttle" 16..255
+ * "fullduplex" 0,1
+ * "loopid" 0..125
+ */
+
+struct isp_fc_param {
+ char param_name[16]; /* null terminated */
+ uint32_t parameter;
+};
+
+#define ISP_GET_FC_PARAM _IOWR(ISP_IOC, 98, struct isp_fc_param)
+#define ISP_SET_FC_PARAM _IOWR(ISP_IOC, 99, struct isp_fc_param)
+
+/*
+ * Various Reset Goodies
+ */
+struct isp_fc_tsk_mgmt {
+ uint32_t loopid; /* 0..255 */
+ uint32_t lun;
+ enum {
+ IPT_CLEAR_ACA,
+ IPT_TARGET_RESET,
+ IPT_LUN_RESET,
+ IPT_CLEAR_TASK_SET,
+ IPT_ABORT_TASK_SET
+ } action;
+};
+#define ISP_TSK_MGMT _IOWR(ISP_IOC, 97, struct isp_fc_tsk_mgmt)
diff --git a/sys/dev/isp/isp_library.c b/sys/dev/isp/isp_library.c
new file mode 100644
index 000000000000..298197f25b8a
--- /dev/null
+++ b/sys/dev/isp/isp_library.c
@@ -0,0 +1,1725 @@
+/*-
+ * Copyright (c) 1999-2006 by Matthew Jacob
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice 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.
+ *
+ * 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.
+ *
+ */
+/*
+ * Qlogic Host Adapter Internal Library Functions
+ */
+#ifdef __NetBSD__
+#include <dev/ic/isp_netbsd.h>
+#endif
+#ifdef __FreeBSD__
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+#include <dev/isp/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
+
+int
+isp_save_xs(ispsoftc_t *isp, XS_T *xs, uint16_t *handlep)
+{
+ int i, j;
+
+ for (j = isp->isp_lasthdls, i = 0; i < (int) isp->isp_maxcmds; i++) {
+ if (isp->isp_xflist[j] == NULL) {
+ break;
+ }
+ if (++j == isp->isp_maxcmds) {
+ j = 0;
+ }
+ }
+ if (i == isp->isp_maxcmds) {
+ return (-1);
+ }
+ isp->isp_xflist[j] = xs;
+ *handlep = j+1;
+ if (++j == isp->isp_maxcmds)
+ j = 0;
+ isp->isp_lasthdls = (uint16_t)j;
+ return (0);
+}
+
+XS_T *
+isp_find_xs(ispsoftc_t *isp, uint16_t handle)
+{
+ if (handle < 1 || handle > (uint16_t) isp->isp_maxcmds) {
+ return (NULL);
+ } else {
+ return (isp->isp_xflist[handle - 1]);
+ }
+}
+
+uint16_t
+isp_find_handle(ispsoftc_t *isp, XS_T *xs)
+{
+ int i;
+ if (xs != NULL) {
+ for (i = 0; i < isp->isp_maxcmds; i++) {
+ if (isp->isp_xflist[i] == xs) {
+ return ((uint16_t) i+1);
+ }
+ }
+ }
+ return (0);
+}
+
+int
+isp_handle_index(uint16_t handle)
+{
+ return (handle-1);
+}
+
+uint16_t
+isp_index_handle(int index)
+{
+ return (index+1);
+}
+
+void
+isp_destroy_handle(ispsoftc_t *isp, uint16_t handle)
+{
+ if (handle > 0 && handle <= (uint16_t) isp->isp_maxcmds) {
+ isp->isp_xflist[handle - 1] = NULL;
+ }
+}
+
+int
+isp_getrqentry(ispsoftc_t *isp, uint16_t *iptrp,
+ uint16_t *optrp, void **resultp)
+{
+ volatile uint16_t iptr, optr;
+
+ optr = isp->isp_reqodx = READ_REQUEST_QUEUE_OUT_POINTER(isp);
+ iptr = isp->isp_reqidx;
+ *resultp = ISP_QUEUE_ENTRY(isp->isp_rquest, iptr);
+ iptr = ISP_NXT_QENTRY(iptr, RQUEST_QUEUE_LEN(isp));
+ if (iptr == optr) {
+ return (1);
+ }
+ if (optrp)
+ *optrp = optr;
+ if (iptrp)
+ *iptrp = iptr;
+ return (0);
+}
+
+#define TBA (4 * (((QENTRY_LEN >> 2) * 3) + 1) + 1)
+void
+isp_print_qentry(ispsoftc_t *isp, char *msg, int idx, void *arg)
+{
+ char buf[TBA];
+ int amt, i, j;
+ uint8_t *ptr = arg;
+
+ isp_prt(isp, ISP_LOGALL, "%s index %d=>", msg, idx);
+ for (buf[0] = 0, amt = i = 0; i < 4; i++) {
+ buf[0] = 0;
+ SNPRINTF(buf, TBA, " ");
+ for (j = 0; j < (QENTRY_LEN >> 2); j++) {
+ SNPRINTF(buf, TBA, "%s %02x", buf, ptr[amt++] & 0xff);
+ }
+ isp_prt(isp, ISP_LOGALL, buf);
+ }
+}
+
+void
+isp_print_bytes(ispsoftc_t *isp, char *msg, int amt, void *arg)
+{
+ char buf[128];
+ uint8_t *ptr = arg;
+ int off;
+
+ if (msg)
+ isp_prt(isp, ISP_LOGALL, "%s:", msg);
+ off = 0;
+ buf[0] = 0;
+ while (off < amt) {
+ int j, to;
+ to = off;
+ for (j = 0; j < 16; j++) {
+ SNPRINTF(buf, 128, "%s %02x", buf, ptr[off++] & 0xff);
+ if (off == amt)
+ break;
+ }
+ isp_prt(isp, ISP_LOGALL, "0x%08x:%s", to, buf);
+ buf[0] = 0;
+ }
+}
+
+/*
+ * Do the common path to try and ensure that link is up, we've scanned
+ * the fabric (if we're on a fabric), and that we've synchronized this
+ * all with our own database and done the appropriate logins.
+ *
+ * We repeatedly check for firmware state and loop state after each
+ * action because things may have changed while we were doing this.
+ * Any failure or change of state causes us to return a nonzero value.
+ *
+ * We honor HBA roles in that if we're not in Initiator mode, we don't
+ * attempt to sync up the database (that's for somebody else to do,
+ * if ever).
+ *
+ * We assume we enter here with any locks held.
+ */
+
+int
+isp_fc_runstate(ispsoftc_t *isp, int tval)
+{
+ fcparam *fcp;
+ int *tptr;
+
+ if (IS_SCSI(isp))
+ return (0);
+
+ tptr = &tval;
+ if (isp_control(isp, ISPCTL_FCLINK_TEST, tptr) != 0) {
+ return (-1);
+ }
+ fcp = FCPARAM(isp);
+ if (fcp->isp_fwstate != FW_READY || fcp->isp_loopstate < LOOP_PDB_RCVD)
+ return (-1);
+ if (isp_control(isp, ISPCTL_SCAN_FABRIC, NULL) != 0) {
+ return (-1);
+ }
+ if (isp_control(isp, ISPCTL_SCAN_LOOP, NULL) != 0) {
+ return (-1);
+ }
+ if ((isp->isp_role & ISP_ROLE_INITIATOR) == 0) {
+ return (0);
+ }
+ if (isp_control(isp, ISPCTL_PDB_SYNC, NULL) != 0) {
+ return (-1);
+ }
+ if (fcp->isp_fwstate != FW_READY || fcp->isp_loopstate != LOOP_READY) {
+ return (-1);
+ }
+ return (0);
+}
+
+/*
+ * Functions to move stuff to a form that the QLogic RISC engine understands
+ * and functions to move stuff back to a form the processor understands.
+ *
+ * Each platform is required to provide the 8, 16 and 32 bit
+ * swizzle and unswizzle macros (ISP_IOX{PUT|GET}_{8,16,32})
+ *
+ * The assumption is that swizzling and unswizzling is mostly done 'in place'
+ * (with a few exceptions for efficiency).
+ */
+
+#define ISP_IS_SBUS(isp) \
+ (ISP_SBUS_SUPPORTED && (isp)->isp_bustype == ISP_BT_SBUS)
+
+/*
+ * Swizzle/Copy Functions
+ */
+void
+isp_copy_out_hdr(ispsoftc_t *isp, isphdr_t *hpsrc, isphdr_t *hpdst)
+{
+ if (ISP_IS_SBUS(isp)) {
+ ISP_IOXPUT_8(isp, hpsrc->rqs_entry_type,
+ &hpdst->rqs_entry_count);
+ ISP_IOXPUT_8(isp, hpsrc->rqs_entry_count,
+ &hpdst->rqs_entry_type);
+ ISP_IOXPUT_8(isp, hpsrc->rqs_seqno,
+ &hpdst->rqs_flags);
+ ISP_IOXPUT_8(isp, hpsrc->rqs_flags,
+ &hpdst->rqs_seqno);
+ } else {
+ ISP_IOXPUT_8(isp, hpsrc->rqs_entry_type,
+ &hpdst->rqs_entry_type);
+ ISP_IOXPUT_8(isp, hpsrc->rqs_entry_count,
+ &hpdst->rqs_entry_count);
+ ISP_IOXPUT_8(isp, hpsrc->rqs_seqno,
+ &hpdst->rqs_seqno);
+ ISP_IOXPUT_8(isp, hpsrc->rqs_flags,
+ &hpdst->rqs_flags);
+ }
+}
+
+void
+isp_copy_in_hdr(ispsoftc_t *isp, isphdr_t *hpsrc, isphdr_t *hpdst)
+{
+ if (ISP_IS_SBUS(isp)) {
+ ISP_IOXGET_8(isp, &hpsrc->rqs_entry_type,
+ hpdst->rqs_entry_count);
+ ISP_IOXGET_8(isp, &hpsrc->rqs_entry_count,
+ hpdst->rqs_entry_type);
+ ISP_IOXGET_8(isp, &hpsrc->rqs_seqno,
+ hpdst->rqs_flags);
+ ISP_IOXGET_8(isp, &hpsrc->rqs_flags,
+ hpdst->rqs_seqno);
+ } else {
+ ISP_IOXGET_8(isp, &hpsrc->rqs_entry_type,
+ hpdst->rqs_entry_type);
+ ISP_IOXGET_8(isp, &hpsrc->rqs_entry_count,
+ hpdst->rqs_entry_count);
+ ISP_IOXGET_8(isp, &hpsrc->rqs_seqno,
+ hpdst->rqs_seqno);
+ ISP_IOXGET_8(isp, &hpsrc->rqs_flags,
+ hpdst->rqs_flags);
+ }
+}
+
+int
+isp_get_response_type(ispsoftc_t *isp, isphdr_t *hp)
+{
+ uint8_t type;
+ if (ISP_IS_SBUS(isp)) {
+ ISP_IOXGET_8(isp, &hp->rqs_entry_count, type);
+ } else {
+ ISP_IOXGET_8(isp, &hp->rqs_entry_type, type);
+ }
+ return ((int)type);
+}
+
+void
+isp_put_request(ispsoftc_t *isp, ispreq_t *rqsrc, ispreq_t *rqdst)
+{
+ int i;
+ isp_copy_out_hdr(isp, &rqsrc->req_header, &rqdst->req_header);
+ ISP_IOXPUT_32(isp, rqsrc->req_handle, &rqdst->req_handle);
+ if (ISP_IS_SBUS(isp)) {
+ ISP_IOXPUT_8(isp, rqsrc->req_lun_trn, &rqdst->req_target);
+ ISP_IOXPUT_8(isp, rqsrc->req_target, &rqdst->req_lun_trn);
+ } else {
+ ISP_IOXPUT_8(isp, rqsrc->req_lun_trn, &rqdst->req_lun_trn);
+ ISP_IOXPUT_8(isp, rqsrc->req_target, &rqdst->req_target);
+ }
+ ISP_IOXPUT_16(isp, rqsrc->req_cdblen, &rqdst->req_cdblen);
+ ISP_IOXPUT_16(isp, rqsrc->req_flags, &rqdst->req_flags);
+ ISP_IOXPUT_16(isp, rqsrc->req_time, &rqdst->req_time);
+ ISP_IOXPUT_16(isp, rqsrc->req_seg_count, &rqdst->req_seg_count);
+ for (i = 0; i < 12; i++) {
+ ISP_IOXPUT_8(isp, rqsrc->req_cdb[i], &rqdst->req_cdb[i]);
+ }
+ for (i = 0; i < ISP_RQDSEG; i++) {
+ ISP_IOXPUT_32(isp, rqsrc->req_dataseg[i].ds_base,
+ &rqdst->req_dataseg[i].ds_base);
+ ISP_IOXPUT_32(isp, rqsrc->req_dataseg[i].ds_count,
+ &rqdst->req_dataseg[i].ds_count);
+ }
+}
+
+void
+isp_put_request_t2(ispsoftc_t *isp, ispreqt2_t *tqsrc, ispreqt2_t *tqdst)
+{
+ int i;
+ isp_copy_out_hdr(isp, &tqsrc->req_header, &tqdst->req_header);
+ ISP_IOXPUT_32(isp, tqsrc->req_handle, &tqdst->req_handle);
+ ISP_IOXPUT_8(isp, tqsrc->req_lun_trn, &tqdst->req_lun_trn);
+ ISP_IOXPUT_8(isp, tqsrc->req_target, &tqdst->req_target);
+ ISP_IOXPUT_16(isp, tqsrc->req_scclun, &tqdst->req_scclun);
+ ISP_IOXPUT_16(isp, tqsrc->req_flags, &tqdst->req_flags);
+ ISP_IOXPUT_16(isp, tqsrc->_res2, &tqdst->_res2);
+ ISP_IOXPUT_16(isp, tqsrc->req_time, &tqdst->req_time);
+ ISP_IOXPUT_16(isp, tqsrc->req_seg_count, &tqdst->req_seg_count);
+ for (i = 0; i < 16; i++) {
+ ISP_IOXPUT_8(isp, tqsrc->req_cdb[i], &tqdst->req_cdb[i]);
+ }
+ ISP_IOXPUT_32(isp, tqsrc->req_totalcnt, &tqdst->req_totalcnt);
+ for (i = 0; i < ISP_RQDSEG_T2; i++) {
+ ISP_IOXPUT_32(isp, tqsrc->req_dataseg[i].ds_base,
+ &tqdst->req_dataseg[i].ds_base);
+ ISP_IOXPUT_32(isp, tqsrc->req_dataseg[i].ds_count,
+ &tqdst->req_dataseg[i].ds_count);
+ }
+}
+
+void
+isp_put_request_t2e(ispsoftc_t *isp, ispreqt2e_t *tqsrc, ispreqt2e_t *tqdst)
+{
+ int i;
+ isp_copy_out_hdr(isp, &tqsrc->req_header, &tqdst->req_header);
+ ISP_IOXPUT_32(isp, tqsrc->req_handle, &tqdst->req_handle);
+ ISP_IOXPUT_16(isp, tqsrc->req_target, &tqdst->req_target);
+ ISP_IOXPUT_16(isp, tqsrc->req_scclun, &tqdst->req_scclun);
+ ISP_IOXPUT_16(isp, tqsrc->req_flags, &tqdst->req_flags);
+ ISP_IOXPUT_16(isp, tqsrc->_res2, &tqdst->_res2);
+ ISP_IOXPUT_16(isp, tqsrc->req_time, &tqdst->req_time);
+ ISP_IOXPUT_16(isp, tqsrc->req_seg_count, &tqdst->req_seg_count);
+ for (i = 0; i < 16; i++) {
+ ISP_IOXPUT_8(isp, tqsrc->req_cdb[i], &tqdst->req_cdb[i]);
+ }
+ ISP_IOXPUT_32(isp, tqsrc->req_totalcnt, &tqdst->req_totalcnt);
+ for (i = 0; i < ISP_RQDSEG_T2; i++) {
+ ISP_IOXPUT_32(isp, tqsrc->req_dataseg[i].ds_base,
+ &tqdst->req_dataseg[i].ds_base);
+ ISP_IOXPUT_32(isp, tqsrc->req_dataseg[i].ds_count,
+ &tqdst->req_dataseg[i].ds_count);
+ }
+}
+
+void
+isp_put_request_t3(ispsoftc_t *isp, ispreqt3_t *tqsrc, ispreqt3_t *tqdst)
+{
+ int i;
+ isp_copy_out_hdr(isp, &tqsrc->req_header, &tqdst->req_header);
+ ISP_IOXPUT_32(isp, tqsrc->req_handle, &tqdst->req_handle);
+ ISP_IOXPUT_8(isp, tqsrc->req_lun_trn, &tqdst->req_lun_trn);
+ ISP_IOXPUT_8(isp, tqsrc->req_target, &tqdst->req_target);
+ ISP_IOXPUT_16(isp, tqsrc->req_scclun, &tqdst->req_scclun);
+ ISP_IOXPUT_16(isp, tqsrc->req_flags, &tqdst->req_flags);
+ ISP_IOXPUT_16(isp, tqsrc->_res2, &tqdst->_res2);
+ ISP_IOXPUT_16(isp, tqsrc->req_time, &tqdst->req_time);
+ ISP_IOXPUT_16(isp, tqsrc->req_seg_count, &tqdst->req_seg_count);
+ for (i = 0; i < 16; i++) {
+ ISP_IOXPUT_8(isp, tqsrc->req_cdb[i], &tqdst->req_cdb[i]);
+ }
+ ISP_IOXPUT_32(isp, tqsrc->req_totalcnt, &tqdst->req_totalcnt);
+ for (i = 0; i < ISP_RQDSEG_T3; i++) {
+ ISP_IOXPUT_32(isp, tqsrc->req_dataseg[i].ds_base,
+ &tqdst->req_dataseg[i].ds_base);
+ ISP_IOXPUT_32(isp, tqsrc->req_dataseg[i].ds_basehi,
+ &tqdst->req_dataseg[i].ds_basehi);
+ ISP_IOXPUT_32(isp, tqsrc->req_dataseg[i].ds_count,
+ &tqdst->req_dataseg[i].ds_count);
+ }
+}
+
+void
+isp_put_request_t3e(ispsoftc_t *isp, ispreqt3e_t *tqsrc, ispreqt3e_t *tqdst)
+{
+ int i;
+ isp_copy_out_hdr(isp, &tqsrc->req_header, &tqdst->req_header);
+ ISP_IOXPUT_32(isp, tqsrc->req_handle, &tqdst->req_handle);
+ ISP_IOXPUT_16(isp, tqsrc->req_target, &tqdst->req_target);
+ ISP_IOXPUT_16(isp, tqsrc->req_scclun, &tqdst->req_scclun);
+ ISP_IOXPUT_16(isp, tqsrc->req_flags, &tqdst->req_flags);
+ ISP_IOXPUT_16(isp, tqsrc->_res2, &tqdst->_res2);
+ ISP_IOXPUT_16(isp, tqsrc->req_time, &tqdst->req_time);
+ ISP_IOXPUT_16(isp, tqsrc->req_seg_count, &tqdst->req_seg_count);
+ for (i = 0; i < 16; i++) {
+ ISP_IOXPUT_8(isp, tqsrc->req_cdb[i], &tqdst->req_cdb[i]);
+ }
+ ISP_IOXPUT_32(isp, tqsrc->req_totalcnt, &tqdst->req_totalcnt);
+ for (i = 0; i < ISP_RQDSEG_T3; i++) {
+ ISP_IOXPUT_32(isp, tqsrc->req_dataseg[i].ds_base,
+ &tqdst->req_dataseg[i].ds_base);
+ ISP_IOXPUT_32(isp, tqsrc->req_dataseg[i].ds_basehi,
+ &tqdst->req_dataseg[i].ds_basehi);
+ ISP_IOXPUT_32(isp, tqsrc->req_dataseg[i].ds_count,
+ &tqdst->req_dataseg[i].ds_count);
+ }
+}
+
+void
+isp_put_extended_request(ispsoftc_t *isp, ispextreq_t *xqsrc,
+ ispextreq_t *xqdst)
+{
+ int i;
+ isp_copy_out_hdr(isp, &xqsrc->req_header, &xqdst->req_header);
+ ISP_IOXPUT_32(isp, xqsrc->req_handle, &xqdst->req_handle);
+ if (ISP_IS_SBUS(isp)) {
+ ISP_IOXPUT_8(isp, xqsrc->req_lun_trn, &xqdst->req_target);
+ ISP_IOXPUT_8(isp, xqsrc->req_target, &xqdst->req_lun_trn);
+ } else {
+ ISP_IOXPUT_8(isp, xqsrc->req_lun_trn, &xqdst->req_lun_trn);
+ ISP_IOXPUT_8(isp, xqsrc->req_target, &xqdst->req_target);
+ }
+ ISP_IOXPUT_16(isp, xqsrc->req_cdblen, &xqdst->req_cdblen);
+ ISP_IOXPUT_16(isp, xqsrc->req_flags, &xqdst->req_flags);
+ ISP_IOXPUT_16(isp, xqsrc->req_time, &xqdst->req_time);
+ ISP_IOXPUT_16(isp, xqsrc->req_seg_count, &xqdst->req_seg_count);
+ for (i = 0; i < 44; i++) {
+ ISP_IOXPUT_8(isp, xqsrc->req_cdb[i], &xqdst->req_cdb[i]);
+ }
+}
+
+void
+isp_put_cont_req(ispsoftc_t *isp, ispcontreq_t *cqsrc, ispcontreq_t *cqdst)
+{
+ int i;
+ isp_copy_out_hdr(isp, &cqsrc->req_header, &cqdst->req_header);
+ for (i = 0; i < ISP_CDSEG; i++) {
+ ISP_IOXPUT_32(isp, cqsrc->req_dataseg[i].ds_base,
+ &cqdst->req_dataseg[i].ds_base);
+ ISP_IOXPUT_32(isp, cqsrc->req_dataseg[i].ds_count,
+ &cqdst->req_dataseg[i].ds_count);
+ }
+}
+
+void
+isp_put_cont64_req(ispsoftc_t *isp, ispcontreq64_t *cqsrc,
+ ispcontreq64_t *cqdst)
+{
+ int i;
+ isp_copy_out_hdr(isp, &cqsrc->req_header, &cqdst->req_header);
+ for (i = 0; i < ISP_CDSEG64; i++) {
+ ISP_IOXPUT_32(isp, cqsrc->req_dataseg[i].ds_base,
+ &cqdst->req_dataseg[i].ds_base);
+ ISP_IOXPUT_32(isp, cqsrc->req_dataseg[i].ds_basehi,
+ &cqdst->req_dataseg[i].ds_basehi);
+ ISP_IOXPUT_32(isp, cqsrc->req_dataseg[i].ds_count,
+ &cqdst->req_dataseg[i].ds_count);
+ }
+}
+
+void
+isp_get_response(ispsoftc_t *isp, ispstatusreq_t *spsrc,
+ ispstatusreq_t *spdst)
+{
+ int i;
+ isp_copy_in_hdr(isp, &spsrc->req_header, &spdst->req_header);
+ ISP_IOXGET_32(isp, &spsrc->req_handle, spdst->req_handle);
+ ISP_IOXGET_16(isp, &spsrc->req_scsi_status, spdst->req_scsi_status);
+ ISP_IOXGET_16(isp, &spsrc->req_completion_status,
+ spdst->req_completion_status);
+ ISP_IOXGET_16(isp, &spsrc->req_state_flags, spdst->req_state_flags);
+ ISP_IOXGET_16(isp, &spsrc->req_status_flags, spdst->req_status_flags);
+ ISP_IOXGET_16(isp, &spsrc->req_time, spdst->req_time);
+ ISP_IOXGET_16(isp, &spsrc->req_sense_len, spdst->req_sense_len);
+ ISP_IOXGET_32(isp, &spsrc->req_resid, spdst->req_resid);
+ for (i = 0; i < 8; i++) {
+ ISP_IOXGET_8(isp, &spsrc->req_response[i],
+ spdst->req_response[i]);
+ }
+ for (i = 0; i < 32; i++) {
+ ISP_IOXGET_8(isp, &spsrc->req_sense_data[i],
+ spdst->req_sense_data[i]);
+ }
+}
+
+void
+isp_get_response_x(ispsoftc_t *isp, ispstatus_cont_t *cpsrc,
+ ispstatus_cont_t *cpdst)
+{
+ int i;
+ isp_copy_in_hdr(isp, &cpsrc->req_header, &cpdst->req_header);
+ for (i = 0; i < 60; i++) {
+ ISP_IOXGET_8(isp, &cpsrc->req_sense_data[i],
+ cpdst->req_sense_data[i]);
+ }
+}
+
+void
+isp_get_rio2(ispsoftc_t *isp, isp_rio2_t *r2src, isp_rio2_t *r2dst)
+{
+ int i;
+ isp_copy_in_hdr(isp, &r2src->req_header, &r2dst->req_header);
+ if (r2dst->req_header.rqs_seqno > 30)
+ r2dst->req_header.rqs_seqno = 30;
+ for (i = 0; i < r2dst->req_header.rqs_seqno; i++) {
+ ISP_IOXGET_16(isp, &r2src->req_handles[i],
+ r2dst->req_handles[i]);
+ }
+ while (i < 30) {
+ r2dst->req_handles[i++] = 0;
+ }
+}
+
+void
+isp_put_icb(ispsoftc_t *isp, isp_icb_t *Is, isp_icb_t *Id)
+{
+ int i;
+ if (ISP_IS_SBUS(isp)) {
+ ISP_IOXPUT_8(isp, Is->icb_version, &Id->_reserved0);
+ ISP_IOXPUT_8(isp, Is->_reserved0, &Id->icb_version);
+ } else {
+ ISP_IOXPUT_8(isp, Is->icb_version, &Id->icb_version);
+ ISP_IOXPUT_8(isp, Is->_reserved0, &Id->_reserved0);
+ }
+ ISP_IOXPUT_16(isp, Is->icb_fwoptions, &Id->icb_fwoptions);
+ ISP_IOXPUT_16(isp, Is->icb_maxfrmlen, &Id->icb_maxfrmlen);
+ ISP_IOXPUT_16(isp, Is->icb_maxalloc, &Id->icb_maxalloc);
+ ISP_IOXPUT_16(isp, Is->icb_execthrottle, &Id->icb_execthrottle);
+ if (ISP_IS_SBUS(isp)) {
+ ISP_IOXPUT_8(isp, Is->icb_retry_count, &Id->icb_retry_delay);
+ ISP_IOXPUT_8(isp, Is->icb_retry_delay, &Id->icb_retry_count);
+ } else {
+ ISP_IOXPUT_8(isp, Is->icb_retry_count, &Id->icb_retry_count);
+ ISP_IOXPUT_8(isp, Is->icb_retry_delay, &Id->icb_retry_delay);
+ }
+ for (i = 0; i < 8; i++) {
+ ISP_IOXPUT_8(isp, Is->icb_portname[i], &Id->icb_portname[i]);
+ }
+ ISP_IOXPUT_16(isp, Is->icb_hardaddr, &Id->icb_hardaddr);
+ if (ISP_IS_SBUS(isp)) {
+ ISP_IOXPUT_8(isp, Is->icb_iqdevtype, &Id->icb_logintime);
+ ISP_IOXPUT_8(isp, Is->icb_logintime, &Id->icb_iqdevtype);
+ } else {
+ ISP_IOXPUT_8(isp, Is->icb_iqdevtype, &Id->icb_iqdevtype);
+ ISP_IOXPUT_8(isp, Is->icb_logintime, &Id->icb_logintime);
+ }
+ for (i = 0; i < 8; i++) {
+ ISP_IOXPUT_8(isp, Is->icb_nodename[i], &Id->icb_nodename[i]);
+ }
+ ISP_IOXPUT_16(isp, Is->icb_rqstout, &Id->icb_rqstout);
+ ISP_IOXPUT_16(isp, Is->icb_rspnsin, &Id->icb_rspnsin);
+ ISP_IOXPUT_16(isp, Is->icb_rqstqlen, &Id->icb_rqstqlen);
+ ISP_IOXPUT_16(isp, Is->icb_rsltqlen, &Id->icb_rsltqlen);
+ for (i = 0; i < 4; i++) {
+ ISP_IOXPUT_16(isp, Is->icb_rqstaddr[i], &Id->icb_rqstaddr[i]);
+ }
+ for (i = 0; i < 4; i++) {
+ ISP_IOXPUT_16(isp, Is->icb_respaddr[i], &Id->icb_respaddr[i]);
+ }
+ ISP_IOXPUT_16(isp, Is->icb_lunenables, &Id->icb_lunenables);
+ if (ISP_IS_SBUS(isp)) {
+ ISP_IOXPUT_8(isp, Is->icb_ccnt, &Id->icb_icnt);
+ ISP_IOXPUT_8(isp, Is->icb_icnt, &Id->icb_ccnt);
+ } else {
+ ISP_IOXPUT_8(isp, Is->icb_ccnt, &Id->icb_ccnt);
+ ISP_IOXPUT_8(isp, Is->icb_icnt, &Id->icb_icnt);
+ }
+ ISP_IOXPUT_16(isp, Is->icb_lunetimeout, &Id->icb_lunetimeout);
+ ISP_IOXPUT_16(isp, Is->icb_xfwoptions, &Id->icb_xfwoptions);
+ if (ISP_IS_SBUS(isp)) {
+ ISP_IOXPUT_8(isp, Is->icb_racctimer, &Id->icb_idelaytimer);
+ ISP_IOXPUT_8(isp, Is->icb_idelaytimer, &Id->icb_racctimer);
+ } else {
+ ISP_IOXPUT_8(isp, Is->icb_racctimer, &Id->icb_racctimer);
+ ISP_IOXPUT_8(isp, Is->icb_idelaytimer, &Id->icb_idelaytimer);
+ }
+ ISP_IOXPUT_16(isp, Is->icb_zfwoptions, &Id->icb_zfwoptions);
+}
+
+void
+isp_get_pdb(ispsoftc_t *isp, isp_pdb_t *src, isp_pdb_t *dst)
+{
+ int i;
+ ISP_IOXGET_16(isp, &src->pdb_options, dst->pdb_options);
+ ISP_IOXGET_8(isp, &src->pdb_mstate, dst->pdb_mstate);
+ ISP_IOXGET_8(isp, &src->pdb_sstate, dst->pdb_sstate);
+ for (i = 0; i < 4; i++) {
+ ISP_IOXGET_8(isp, &src->pdb_hardaddr_bits[i],
+ dst->pdb_hardaddr_bits[i]);
+ }
+ for (i = 0; i < 4; i++) {
+ ISP_IOXGET_8(isp, &src->pdb_portid_bits[i],
+ dst->pdb_portid_bits[i]);
+ }
+ for (i = 0; i < 8; i++) {
+ ISP_IOXGET_8(isp, &src->pdb_nodename[i], dst->pdb_nodename[i]);
+ }
+ for (i = 0; i < 8; i++) {
+ ISP_IOXGET_8(isp, &src->pdb_portname[i], dst->pdb_portname[i]);
+ }
+ ISP_IOXGET_16(isp, &src->pdb_execthrottle, dst->pdb_execthrottle);
+ ISP_IOXGET_16(isp, &src->pdb_exec_count, dst->pdb_exec_count);
+ ISP_IOXGET_8(isp, &src->pdb_retry_count, dst->pdb_retry_count);
+ ISP_IOXGET_8(isp, &src->pdb_retry_delay, dst->pdb_retry_delay);
+ ISP_IOXGET_16(isp, &src->pdb_resalloc, dst->pdb_resalloc);
+ ISP_IOXGET_16(isp, &src->pdb_curalloc, dst->pdb_curalloc);
+ ISP_IOXGET_16(isp, &src->pdb_qhead, dst->pdb_qhead);
+ ISP_IOXGET_16(isp, &src->pdb_qtail, dst->pdb_qtail);
+ ISP_IOXGET_16(isp, &src->pdb_tl_next, dst->pdb_tl_next);
+ ISP_IOXGET_16(isp, &src->pdb_tl_last, dst->pdb_tl_last);
+ ISP_IOXGET_16(isp, &src->pdb_features, dst->pdb_features);
+ ISP_IOXGET_16(isp, &src->pdb_pconcurrnt, dst->pdb_pconcurrnt);
+ ISP_IOXGET_16(isp, &src->pdb_roi, dst->pdb_roi);
+ ISP_IOXGET_8(isp, &src->pdb_target, dst->pdb_target);
+ ISP_IOXGET_8(isp, &src->pdb_initiator, dst->pdb_initiator);
+ ISP_IOXGET_16(isp, &src->pdb_rdsiz, dst->pdb_rdsiz);
+ ISP_IOXGET_16(isp, &src->pdb_ncseq, dst->pdb_ncseq);
+ ISP_IOXGET_16(isp, &src->pdb_noseq, dst->pdb_noseq);
+ ISP_IOXGET_16(isp, &src->pdb_labrtflg, dst->pdb_labrtflg);
+ ISP_IOXGET_16(isp, &src->pdb_lstopflg, dst->pdb_lstopflg);
+ ISP_IOXGET_16(isp, &src->pdb_sqhead, dst->pdb_sqhead);
+ ISP_IOXGET_16(isp, &src->pdb_sqtail, dst->pdb_sqtail);
+ ISP_IOXGET_16(isp, &src->pdb_ptimer, dst->pdb_ptimer);
+ ISP_IOXGET_16(isp, &src->pdb_nxt_seqid, dst->pdb_nxt_seqid);
+ ISP_IOXGET_16(isp, &src->pdb_fcount, dst->pdb_fcount);
+ ISP_IOXGET_16(isp, &src->pdb_prli_len, dst->pdb_prli_len);
+ ISP_IOXGET_16(isp, &src->pdb_prli_svc0, dst->pdb_prli_svc0);
+ ISP_IOXGET_16(isp, &src->pdb_prli_svc3, dst->pdb_prli_svc3);
+ ISP_IOXGET_16(isp, &src->pdb_loopid, dst->pdb_loopid);
+ ISP_IOXGET_16(isp, &src->pdb_il_ptr, dst->pdb_il_ptr);
+ ISP_IOXGET_16(isp, &src->pdb_sl_ptr, dst->pdb_sl_ptr);
+}
+
+
+/*
+ * CT_HDR canonicalization- only needed for SNS responses
+ */
+void
+isp_get_ct_hdr(ispsoftc_t *isp, ct_hdr_t *src, ct_hdr_t *dst)
+{
+ ISP_IOXGET_8(isp, &src->ct_revision, dst->ct_revision);
+ ISP_IOXGET_8(isp, &src->ct_portid[0], dst->ct_portid[0]);
+ ISP_IOXGET_8(isp, &src->ct_portid[1], dst->ct_portid[1]);
+ ISP_IOXGET_8(isp, &src->ct_portid[2], dst->ct_portid[2]);
+ ISP_IOXGET_8(isp, &src->ct_fcs_type, dst->ct_fcs_type);
+ ISP_IOXGET_8(isp, &src->ct_fcs_subtype, dst->ct_fcs_subtype);
+ ISP_IOXGET_8(isp, &src->ct_options, dst->ct_options);
+ ISP_IOXGET_8(isp, &src->ct_res0, dst->ct_res0);
+ ISP_IOXGET_16(isp, &src->ct_response, dst->ct_response);
+ dst->ct_response = (dst->ct_response << 8) | (dst->ct_response >> 8);
+ ISP_IOXGET_16(isp, &src->ct_resid, dst->ct_resid);
+ dst->ct_resid = (dst->ct_resid << 8) | (dst->ct_resid >> 8);
+ ISP_IOXGET_8(isp, &src->ct_res1, dst->ct_res1);
+ ISP_IOXGET_8(isp, &src->ct_reason, dst->ct_reason);
+ ISP_IOXGET_8(isp, &src->ct_explanation, dst->ct_explanation);
+ ISP_IOXGET_8(isp, &src->ct_vunique, dst->ct_vunique);
+}
+
+/*
+ * Generic SNS request - not particularly useful since the per-command data
+ * isn't always 16 bit words.
+ */
+void
+isp_put_sns_request(ispsoftc_t *isp, sns_screq_t *src, sns_screq_t *dst)
+{
+ int i, nw = (int) src->snscb_sblen;
+ ISP_IOXPUT_16(isp, src->snscb_rblen, &dst->snscb_rblen);
+ for (i = 0; i < 4; i++) {
+ ISP_IOXPUT_16(isp, src->snscb_addr[i], &dst->snscb_addr[i]);
+ }
+ ISP_IOXPUT_16(isp, src->snscb_sblen, &dst->snscb_sblen);
+ for (i = 0; i < nw; i++) {
+ ISP_IOXPUT_16(isp, src->snscb_data[i], &dst->snscb_data[i]);
+ }
+
+}
+
+void
+isp_put_gid_ft_request(ispsoftc_t *isp, sns_gid_ft_req_t *src,
+ sns_gid_ft_req_t *dst)
+{
+ ISP_IOXPUT_16(isp, src->snscb_rblen, &dst->snscb_rblen);
+ ISP_IOXPUT_16(isp, src->snscb_res0, &dst->snscb_res0);
+ ISP_IOXPUT_16(isp, src->snscb_addr[0], &dst->snscb_addr[0]);
+ ISP_IOXPUT_16(isp, src->snscb_addr[1], &dst->snscb_addr[1]);
+ ISP_IOXPUT_16(isp, src->snscb_addr[2], &dst->snscb_addr[2]);
+ ISP_IOXPUT_16(isp, src->snscb_addr[3], &dst->snscb_addr[3]);
+ ISP_IOXPUT_16(isp, src->snscb_sblen, &dst->snscb_sblen);
+ ISP_IOXPUT_16(isp, src->snscb_res1, &dst->snscb_res1);
+ ISP_IOXPUT_16(isp, src->snscb_cmd, &dst->snscb_cmd);
+ ISP_IOXPUT_16(isp, src->snscb_mword_div_2, &dst->snscb_mword_div_2);
+ ISP_IOXPUT_32(isp, src->snscb_res3, &dst->snscb_res3);
+ ISP_IOXPUT_32(isp, src->snscb_fc4_type, &dst->snscb_fc4_type);
+}
+
+void
+isp_put_gxn_id_request(ispsoftc_t *isp, sns_gxn_id_req_t *src,
+ sns_gxn_id_req_t *dst)
+{
+ ISP_IOXPUT_16(isp, src->snscb_rblen, &dst->snscb_rblen);
+ ISP_IOXPUT_16(isp, src->snscb_res0, &dst->snscb_res0);
+ ISP_IOXPUT_16(isp, src->snscb_addr[0], &dst->snscb_addr[0]);
+ ISP_IOXPUT_16(isp, src->snscb_addr[1], &dst->snscb_addr[1]);
+ ISP_IOXPUT_16(isp, src->snscb_addr[2], &dst->snscb_addr[2]);
+ ISP_IOXPUT_16(isp, src->snscb_addr[3], &dst->snscb_addr[3]);
+ ISP_IOXPUT_16(isp, src->snscb_sblen, &dst->snscb_sblen);
+ ISP_IOXPUT_16(isp, src->snscb_res1, &dst->snscb_res1);
+ ISP_IOXPUT_16(isp, src->snscb_cmd, &dst->snscb_cmd);
+ ISP_IOXPUT_16(isp, src->snscb_res2, &dst->snscb_res2);
+ ISP_IOXPUT_32(isp, src->snscb_res3, &dst->snscb_res3);
+ ISP_IOXPUT_32(isp, src->snscb_portid, &dst->snscb_portid);
+}
+
+/*
+ * Generic SNS response - not particularly useful since the per-command data
+ * isn't always 16 bit words.
+ */
+void
+isp_get_sns_response(ispsoftc_t *isp, sns_scrsp_t *src,
+ sns_scrsp_t *dst, int nwords)
+{
+ int i;
+ isp_get_ct_hdr(isp, &src->snscb_cthdr, &dst->snscb_cthdr);
+ ISP_IOXGET_8(isp, &src->snscb_port_type, dst->snscb_port_type);
+ for (i = 0; i < 3; i++) {
+ ISP_IOXGET_8(isp, &src->snscb_port_id[i],
+ dst->snscb_port_id[i]);
+ }
+ for (i = 0; i < 8; i++) {
+ ISP_IOXGET_8(isp, &src->snscb_portname[i],
+ dst->snscb_portname[i]);
+ }
+ for (i = 0; i < nwords; i++) {
+ ISP_IOXGET_16(isp, &src->snscb_data[i], dst->snscb_data[i]);
+ }
+}
+
+void
+isp_get_gid_ft_response(ispsoftc_t *isp, sns_gid_ft_rsp_t *src,
+ sns_gid_ft_rsp_t *dst, int nwords)
+{
+ int i;
+ isp_get_ct_hdr(isp, &src->snscb_cthdr, &dst->snscb_cthdr);
+ for (i = 0; i < nwords; i++) {
+ int j;
+ ISP_IOXGET_8(isp,
+ &src->snscb_ports[i].control,
+ dst->snscb_ports[i].control);
+ for (j = 0; j < 3; j++) {
+ ISP_IOXGET_8(isp,
+ &src->snscb_ports[i].portid[j],
+ dst->snscb_ports[i].portid[j]);
+ }
+ if (dst->snscb_ports[i].control & 0x80) {
+ break;
+ }
+ }
+}
+
+void
+isp_get_gxn_id_response(ispsoftc_t *isp, sns_gxn_id_rsp_t *src,
+ sns_gxn_id_rsp_t *dst)
+{
+ int i;
+ isp_get_ct_hdr(isp, &src->snscb_cthdr, &dst->snscb_cthdr);
+ for (i = 0; i < 8; i++)
+ ISP_IOXGET_8(isp, &src->snscb_wwn[i], dst->snscb_wwn[i]);
+}
+
+void
+isp_get_gff_id_response(ispsoftc_t *isp, sns_gff_id_rsp_t *src,
+ sns_gff_id_rsp_t *dst)
+{
+ int i;
+ isp_get_ct_hdr(isp, &src->snscb_cthdr, &dst->snscb_cthdr);
+ for (i = 0; i < 32; i++) {
+ ISP_IOXGET_32(isp, &src->snscb_fc4_features[i],
+ dst->snscb_fc4_features[i]);
+ }
+}
+
+void
+isp_get_ga_nxt_response(ispsoftc_t *isp, sns_ga_nxt_rsp_t *src,
+ sns_ga_nxt_rsp_t *dst)
+{
+ int i;
+ isp_get_ct_hdr(isp, &src->snscb_cthdr, &dst->snscb_cthdr);
+ ISP_IOXGET_8(isp, &src->snscb_port_type, dst->snscb_port_type);
+ for (i = 0; i < 3; i++) {
+ ISP_IOXGET_8(isp, &src->snscb_port_id[i],
+ dst->snscb_port_id[i]);
+ }
+ for (i = 0; i < 8; i++) {
+ ISP_IOXGET_8(isp, &src->snscb_portname[i],
+ dst->snscb_portname[i]);
+ }
+ ISP_IOXGET_8(isp, &src->snscb_pnlen, dst->snscb_pnlen);
+ for (i = 0; i < 255; i++) {
+ ISP_IOXGET_8(isp, &src->snscb_pname[i], dst->snscb_pname[i]);
+ }
+ for (i = 0; i < 8; i++) {
+ ISP_IOXGET_8(isp, &src->snscb_nodename[i],
+ dst->snscb_nodename[i]);
+ }
+ ISP_IOXGET_8(isp, &src->snscb_nnlen, dst->snscb_nnlen);
+ for (i = 0; i < 255; i++) {
+ ISP_IOXGET_8(isp, &src->snscb_nname[i], dst->snscb_nname[i]);
+ }
+ for (i = 0; i < 8; i++) {
+ ISP_IOXGET_8(isp, &src->snscb_ipassoc[i],
+ dst->snscb_ipassoc[i]);
+ }
+ for (i = 0; i < 16; i++) {
+ ISP_IOXGET_8(isp, &src->snscb_ipaddr[i], dst->snscb_ipaddr[i]);
+ }
+ for (i = 0; i < 4; i++) {
+ ISP_IOXGET_8(isp, &src->snscb_svc_class[i],
+ dst->snscb_svc_class[i]);
+ }
+ for (i = 0; i < 32; i++) {
+ ISP_IOXGET_8(isp, &src->snscb_fc4_types[i],
+ dst->snscb_fc4_types[i]);
+ }
+ for (i = 0; i < 8; i++) {
+ ISP_IOXGET_8(isp, &src->snscb_fpname[i], dst->snscb_fpname[i]);
+ }
+ ISP_IOXGET_8(isp, &src->snscb_reserved, dst->snscb_reserved);
+ for (i = 0; i < 3; i++) {
+ ISP_IOXGET_8(isp, &src->snscb_hardaddr[i],
+ dst->snscb_hardaddr[i]);
+ }
+}
+
+#ifdef ISP_TARGET_MODE
+
+int
+isp_save_xs_tgt(ispsoftc_t *isp, void *xs, uint16_t *handlep)
+{
+ int i;
+
+ for (i = 0; i < (int) isp->isp_maxcmds; i++) {
+ if (isp->isp_tgtlist[i] == NULL) {
+ break;
+ }
+ }
+ if (i == isp->isp_maxcmds) {
+ return (-1);
+ }
+ isp->isp_tgtlist[i] = xs;
+ *handlep = i+1;
+ return (0);
+}
+
+void *
+isp_find_xs_tgt(ispsoftc_t *isp, uint16_t handle)
+{
+ if (handle < 1 || handle > (uint16_t) isp->isp_maxcmds) {
+ return (NULL);
+ } else {
+ return (isp->isp_tgtlist[handle - 1]);
+ }
+}
+
+uint16_t
+isp_find_tgt_handle(ispsoftc_t *isp, void *xs)
+{
+ int i;
+ if (xs != NULL) {
+ for (i = 0; i < isp->isp_maxcmds; i++) {
+ if (isp->isp_tgtlist[i] == xs) {
+ return ((uint16_t) i+1);
+ }
+ }
+ }
+ return (0);
+}
+
+void
+isp_destroy_tgt_handle(ispsoftc_t *isp, uint16_t handle)
+{
+ if (handle > 0 && handle <= (uint16_t) isp->isp_maxcmds) {
+ isp->isp_tgtlist[handle - 1] = NULL;
+ }
+}
+void
+isp_put_atio(ispsoftc_t *isp, at_entry_t *atsrc, at_entry_t *atdst)
+{
+ int i;
+ isp_copy_out_hdr(isp, &atsrc->at_header, &atdst->at_header);
+ ISP_IOXPUT_16(isp, atsrc->at_reserved, &atdst->at_reserved);
+ ISP_IOXPUT_16(isp, atsrc->at_handle, &atdst->at_handle);
+ if (ISP_IS_SBUS(isp)) {
+ ISP_IOXPUT_8(isp, atsrc->at_lun, &atdst->at_iid);
+ ISP_IOXPUT_8(isp, atsrc->at_iid, &atdst->at_lun);
+ ISP_IOXPUT_8(isp, atsrc->at_cdblen, &atdst->at_tgt);
+ ISP_IOXPUT_8(isp, atsrc->at_tgt, &atdst->at_cdblen);
+ ISP_IOXPUT_8(isp, atsrc->at_status, &atdst->at_scsi_status);
+ ISP_IOXPUT_8(isp, atsrc->at_scsi_status, &atdst->at_status);
+ ISP_IOXPUT_8(isp, atsrc->at_tag_val, &atdst->at_tag_type);
+ ISP_IOXPUT_8(isp, atsrc->at_tag_type, &atdst->at_tag_val);
+ } else {
+ ISP_IOXPUT_8(isp, atsrc->at_lun, &atdst->at_lun);
+ ISP_IOXPUT_8(isp, atsrc->at_iid, &atdst->at_iid);
+ ISP_IOXPUT_8(isp, atsrc->at_cdblen, &atdst->at_cdblen);
+ ISP_IOXPUT_8(isp, atsrc->at_tgt, &atdst->at_tgt);
+ ISP_IOXPUT_8(isp, atsrc->at_status, &atdst->at_status);
+ ISP_IOXPUT_8(isp, atsrc->at_scsi_status,
+ &atdst->at_scsi_status);
+ ISP_IOXPUT_8(isp, atsrc->at_tag_val, &atdst->at_tag_val);
+ ISP_IOXPUT_8(isp, atsrc->at_tag_type, &atdst->at_tag_type);
+ }
+ ISP_IOXPUT_32(isp, atsrc->at_flags, &atdst->at_flags);
+ for (i = 0; i < ATIO_CDBLEN; i++) {
+ ISP_IOXPUT_8(isp, atsrc->at_cdb[i], &atdst->at_cdb[i]);
+ }
+ for (i = 0; i < QLTM_SENSELEN; i++) {
+ ISP_IOXPUT_8(isp, atsrc->at_sense[i], &atdst->at_sense[i]);
+ }
+}
+
+void
+isp_get_atio(ispsoftc_t *isp, at_entry_t *atsrc, at_entry_t *atdst)
+{
+ int i;
+ isp_copy_in_hdr(isp, &atsrc->at_header, &atdst->at_header);
+ ISP_IOXGET_16(isp, &atsrc->at_reserved, atdst->at_reserved);
+ ISP_IOXGET_16(isp, &atsrc->at_handle, atdst->at_handle);
+ if (ISP_IS_SBUS(isp)) {
+ ISP_IOXGET_8(isp, &atsrc->at_lun, atdst->at_iid);
+ ISP_IOXGET_8(isp, &atsrc->at_iid, atdst->at_lun);
+ ISP_IOXGET_8(isp, &atsrc->at_cdblen, atdst->at_tgt);
+ ISP_IOXGET_8(isp, &atsrc->at_tgt, atdst->at_cdblen);
+ ISP_IOXGET_8(isp, &atsrc->at_status, atdst->at_scsi_status);
+ ISP_IOXGET_8(isp, &atsrc->at_scsi_status, atdst->at_status);
+ ISP_IOXGET_8(isp, &atsrc->at_tag_val, atdst->at_tag_type);
+ ISP_IOXGET_8(isp, &atsrc->at_tag_type, atdst->at_tag_val);
+ } else {
+ ISP_IOXGET_8(isp, &atsrc->at_lun, atdst->at_lun);
+ ISP_IOXGET_8(isp, &atsrc->at_iid, atdst->at_iid);
+ ISP_IOXGET_8(isp, &atsrc->at_cdblen, atdst->at_cdblen);
+ ISP_IOXGET_8(isp, &atsrc->at_tgt, atdst->at_tgt);
+ ISP_IOXGET_8(isp, &atsrc->at_status, atdst->at_status);
+ ISP_IOXGET_8(isp, &atsrc->at_scsi_status,
+ atdst->at_scsi_status);
+ ISP_IOXGET_8(isp, &atsrc->at_tag_val, atdst->at_tag_val);
+ ISP_IOXGET_8(isp, &atsrc->at_tag_type, atdst->at_tag_type);
+ }
+ ISP_IOXGET_32(isp, &atsrc->at_flags, atdst->at_flags);
+ for (i = 0; i < ATIO_CDBLEN; i++) {
+ ISP_IOXGET_8(isp, &atsrc->at_cdb[i], atdst->at_cdb[i]);
+ }
+ for (i = 0; i < QLTM_SENSELEN; i++) {
+ ISP_IOXGET_8(isp, &atsrc->at_sense[i], atdst->at_sense[i]);
+ }
+}
+
+void
+isp_put_atio2(ispsoftc_t *isp, at2_entry_t *atsrc, at2_entry_t *atdst)
+{
+ int i;
+ isp_copy_out_hdr(isp, &atsrc->at_header, &atdst->at_header);
+ ISP_IOXPUT_32(isp, atsrc->at_reserved, &atdst->at_reserved);
+ ISP_IOXPUT_8(isp, atsrc->at_lun, &atdst->at_lun);
+ ISP_IOXPUT_8(isp, atsrc->at_iid, &atdst->at_iid);
+ ISP_IOXPUT_16(isp, atsrc->at_rxid, &atdst->at_rxid);
+ ISP_IOXPUT_16(isp, atsrc->at_flags, &atdst->at_flags);
+ ISP_IOXPUT_16(isp, atsrc->at_status, &atdst->at_status);
+ ISP_IOXPUT_8(isp, atsrc->at_crn, &atdst->at_crn);
+ ISP_IOXPUT_8(isp, atsrc->at_taskcodes, &atdst->at_taskcodes);
+ ISP_IOXPUT_8(isp, atsrc->at_taskflags, &atdst->at_taskflags);
+ ISP_IOXPUT_8(isp, atsrc->at_execodes, &atdst->at_execodes);
+ for (i = 0; i < ATIO2_CDBLEN; i++) {
+ ISP_IOXPUT_8(isp, atsrc->at_cdb[i], &atdst->at_cdb[i]);
+ }
+ ISP_IOXPUT_32(isp, atsrc->at_datalen, &atdst->at_datalen);
+ ISP_IOXPUT_16(isp, atsrc->at_scclun, &atdst->at_scclun);
+ for (i = 0; i < 4; i++) {
+ ISP_IOXPUT_16(isp, atsrc->at_wwpn[i], &atdst->at_wwpn[i]);
+ }
+ for (i = 0; i < 6; i++) {
+ ISP_IOXPUT_16(isp, atsrc->at_reserved2[i],
+ &atdst->at_reserved2[i]);
+ }
+ ISP_IOXPUT_16(isp, atsrc->at_oxid, &atdst->at_oxid);
+}
+
+void
+isp_put_atio2e(ispsoftc_t *isp, at2e_entry_t *atsrc, at2e_entry_t *atdst)
+{
+ int i;
+ isp_copy_out_hdr(isp, &atsrc->at_header, &atdst->at_header);
+ ISP_IOXPUT_32(isp, atsrc->at_reserved, &atdst->at_reserved);
+ ISP_IOXPUT_16(isp, atsrc->at_iid, &atdst->at_iid);
+ ISP_IOXPUT_16(isp, atsrc->at_rxid, &atdst->at_rxid);
+ ISP_IOXPUT_16(isp, atsrc->at_flags, &atdst->at_flags);
+ ISP_IOXPUT_16(isp, atsrc->at_status, &atdst->at_status);
+ ISP_IOXPUT_8(isp, atsrc->at_crn, &atdst->at_crn);
+ ISP_IOXPUT_8(isp, atsrc->at_taskcodes, &atdst->at_taskcodes);
+ ISP_IOXPUT_8(isp, atsrc->at_taskflags, &atdst->at_taskflags);
+ ISP_IOXPUT_8(isp, atsrc->at_execodes, &atdst->at_execodes);
+ for (i = 0; i < ATIO2_CDBLEN; i++) {
+ ISP_IOXPUT_8(isp, atsrc->at_cdb[i], &atdst->at_cdb[i]);
+ }
+ ISP_IOXPUT_32(isp, atsrc->at_datalen, &atdst->at_datalen);
+ ISP_IOXPUT_16(isp, atsrc->at_scclun, &atdst->at_scclun);
+ for (i = 0; i < 4; i++) {
+ ISP_IOXPUT_16(isp, atsrc->at_wwpn[i], &atdst->at_wwpn[i]);
+ }
+ for (i = 0; i < 6; i++) {
+ ISP_IOXPUT_16(isp, atsrc->at_reserved2[i],
+ &atdst->at_reserved2[i]);
+ }
+ ISP_IOXPUT_16(isp, atsrc->at_oxid, &atdst->at_oxid);
+}
+
+void
+isp_get_atio2(ispsoftc_t *isp, at2_entry_t *atsrc, at2_entry_t *atdst)
+{
+ int i;
+ isp_copy_in_hdr(isp, &atsrc->at_header, &atdst->at_header);
+ ISP_IOXGET_32(isp, &atsrc->at_reserved, atdst->at_reserved);
+ ISP_IOXGET_8(isp, &atsrc->at_lun, atdst->at_lun);
+ ISP_IOXGET_8(isp, &atsrc->at_iid, atdst->at_iid);
+ ISP_IOXGET_16(isp, &atsrc->at_rxid, atdst->at_rxid);
+ ISP_IOXGET_16(isp, &atsrc->at_flags, atdst->at_flags);
+ ISP_IOXGET_16(isp, &atsrc->at_status, atdst->at_status);
+ ISP_IOXGET_8(isp, &atsrc->at_crn, atdst->at_crn);
+ ISP_IOXGET_8(isp, &atsrc->at_taskcodes, atdst->at_taskcodes);
+ ISP_IOXGET_8(isp, &atsrc->at_taskflags, atdst->at_taskflags);
+ ISP_IOXGET_8(isp, &atsrc->at_execodes, atdst->at_execodes);
+ for (i = 0; i < ATIO2_CDBLEN; i++) {
+ ISP_IOXGET_8(isp, &atsrc->at_cdb[i], atdst->at_cdb[i]);
+ }
+ ISP_IOXGET_32(isp, &atsrc->at_datalen, atdst->at_datalen);
+ ISP_IOXGET_16(isp, &atsrc->at_scclun, atdst->at_scclun);
+ for (i = 0; i < 4; i++) {
+ ISP_IOXGET_16(isp, &atsrc->at_wwpn[i], atdst->at_wwpn[i]);
+ }
+ for (i = 0; i < 6; i++) {
+ ISP_IOXGET_16(isp, &atsrc->at_reserved2[i],
+ atdst->at_reserved2[i]);
+ }
+ ISP_IOXGET_16(isp, &atsrc->at_oxid, atdst->at_oxid);
+}
+
+void
+isp_get_atio2e(ispsoftc_t *isp, at2e_entry_t *atsrc, at2e_entry_t *atdst)
+{
+ int i;
+ isp_copy_in_hdr(isp, &atsrc->at_header, &atdst->at_header);
+ ISP_IOXGET_32(isp, &atsrc->at_reserved, atdst->at_reserved);
+ ISP_IOXGET_16(isp, &atsrc->at_iid, atdst->at_iid);
+ ISP_IOXGET_16(isp, &atsrc->at_rxid, atdst->at_rxid);
+ ISP_IOXGET_16(isp, &atsrc->at_flags, atdst->at_flags);
+ ISP_IOXGET_16(isp, &atsrc->at_status, atdst->at_status);
+ ISP_IOXGET_8(isp, &atsrc->at_crn, atdst->at_crn);
+ ISP_IOXGET_8(isp, &atsrc->at_taskcodes, atdst->at_taskcodes);
+ ISP_IOXGET_8(isp, &atsrc->at_taskflags, atdst->at_taskflags);
+ ISP_IOXGET_8(isp, &atsrc->at_execodes, atdst->at_execodes);
+ for (i = 0; i < ATIO2_CDBLEN; i++) {
+ ISP_IOXGET_8(isp, &atsrc->at_cdb[i], atdst->at_cdb[i]);
+ }
+ ISP_IOXGET_32(isp, &atsrc->at_datalen, atdst->at_datalen);
+ ISP_IOXGET_16(isp, &atsrc->at_scclun, atdst->at_scclun);
+ for (i = 0; i < 4; i++) {
+ ISP_IOXGET_16(isp, &atsrc->at_wwpn[i], atdst->at_wwpn[i]);
+ }
+ for (i = 0; i < 6; i++) {
+ ISP_IOXGET_16(isp, &atsrc->at_reserved2[i],
+ atdst->at_reserved2[i]);
+ }
+ ISP_IOXGET_16(isp, &atsrc->at_oxid, atdst->at_oxid);
+}
+
+void
+isp_put_ctio(ispsoftc_t *isp, ct_entry_t *ctsrc, ct_entry_t *ctdst)
+{
+ int i;
+ isp_copy_out_hdr(isp, &ctsrc->ct_header, &ctdst->ct_header);
+ ISP_IOXPUT_16(isp, ctsrc->ct_reserved, &ctdst->ct_reserved);
+ ISP_IOXPUT_16(isp, ctsrc->ct_fwhandle, &ctdst->ct_fwhandle);
+ if (ISP_IS_SBUS(isp)) {
+ ISP_IOXPUT_8(isp, ctsrc->ct_iid, &ctdst->ct_lun);
+ ISP_IOXPUT_8(isp, ctsrc->ct_lun, &ctdst->ct_iid);
+ ISP_IOXPUT_8(isp, ctsrc->ct_tgt, &ctdst->ct_reserved2);
+ ISP_IOXPUT_8(isp, ctsrc->ct_reserved2, &ctdst->ct_tgt);
+ ISP_IOXPUT_8(isp, ctsrc->ct_status, &ctdst->ct_scsi_status);
+ ISP_IOXPUT_8(isp, ctsrc->ct_scsi_status, &ctdst->ct_status);
+ ISP_IOXPUT_8(isp, ctsrc->ct_tag_type, &ctdst->ct_tag_val);
+ ISP_IOXPUT_8(isp, ctsrc->ct_tag_val, &ctdst->ct_tag_type);
+ } else {
+ ISP_IOXPUT_8(isp, ctsrc->ct_iid, &ctdst->ct_iid);
+ ISP_IOXPUT_8(isp, ctsrc->ct_lun, &ctdst->ct_lun);
+ ISP_IOXPUT_8(isp, ctsrc->ct_tgt, &ctdst->ct_tgt);
+ ISP_IOXPUT_8(isp, ctsrc->ct_reserved2, &ctdst->ct_reserved2);
+ ISP_IOXPUT_8(isp, ctsrc->ct_scsi_status,
+ &ctdst->ct_scsi_status);
+ ISP_IOXPUT_8(isp, ctsrc->ct_status, &ctdst->ct_status);
+ ISP_IOXPUT_8(isp, ctsrc->ct_tag_type, &ctdst->ct_tag_type);
+ ISP_IOXPUT_8(isp, ctsrc->ct_tag_val, &ctdst->ct_tag_val);
+ }
+ ISP_IOXPUT_32(isp, ctsrc->ct_flags, &ctdst->ct_flags);
+ ISP_IOXPUT_32(isp, ctsrc->ct_xfrlen, &ctdst->ct_xfrlen);
+ ISP_IOXPUT_32(isp, ctsrc->ct_resid, &ctdst->ct_resid);
+ ISP_IOXPUT_16(isp, ctsrc->ct_timeout, &ctdst->ct_timeout);
+ ISP_IOXPUT_16(isp, ctsrc->ct_seg_count, &ctdst->ct_seg_count);
+ for (i = 0; i < ISP_RQDSEG; i++) {
+ ISP_IOXPUT_32(isp, ctsrc->ct_dataseg[i].ds_base,
+ &ctdst->ct_dataseg[i].ds_base);
+ ISP_IOXPUT_32(isp, ctsrc->ct_dataseg[i].ds_count,
+ &ctdst->ct_dataseg[i].ds_count);
+ }
+}
+
+void
+isp_get_ctio(ispsoftc_t *isp, ct_entry_t *ctsrc, ct_entry_t *ctdst)
+{
+ int i;
+ isp_copy_in_hdr(isp, &ctsrc->ct_header, &ctdst->ct_header);
+ ISP_IOXGET_16(isp, &ctsrc->ct_reserved, ctdst->ct_reserved);
+ ISP_IOXGET_16(isp, &ctsrc->ct_fwhandle, ctdst->ct_fwhandle);
+ if (ISP_IS_SBUS(isp)) {
+ ISP_IOXGET_8(isp, &ctsrc->ct_lun, ctdst->ct_iid);
+ ISP_IOXGET_8(isp, &ctsrc->ct_iid, ctdst->ct_lun);
+ ISP_IOXGET_8(isp, &ctsrc->ct_reserved2, ctdst->ct_tgt);
+ ISP_IOXGET_8(isp, &ctsrc->ct_tgt, ctdst->ct_reserved2);
+ ISP_IOXGET_8(isp, &ctsrc->ct_status, ctdst->ct_scsi_status);
+ ISP_IOXGET_8(isp, &ctsrc->ct_scsi_status, ctdst->ct_status);
+ ISP_IOXGET_8(isp, &ctsrc->ct_tag_val, ctdst->ct_tag_type);
+ ISP_IOXGET_8(isp, &ctsrc->ct_tag_type, ctdst->ct_tag_val);
+ } else {
+ ISP_IOXGET_8(isp, &ctsrc->ct_lun, ctdst->ct_lun);
+ ISP_IOXGET_8(isp, &ctsrc->ct_iid, ctdst->ct_iid);
+ ISP_IOXGET_8(isp, &ctsrc->ct_reserved2, ctdst->ct_reserved2);
+ ISP_IOXGET_8(isp, &ctsrc->ct_tgt, ctdst->ct_tgt);
+ ISP_IOXGET_8(isp, &ctsrc->ct_status, ctdst->ct_status);
+ ISP_IOXGET_8(isp, &ctsrc->ct_scsi_status,
+ ctdst->ct_scsi_status);
+ ISP_IOXGET_8(isp, &ctsrc->ct_tag_val, ctdst->ct_tag_val);
+ ISP_IOXGET_8(isp, &ctsrc->ct_tag_type, ctdst->ct_tag_type);
+ }
+ ISP_IOXGET_32(isp, &ctsrc->ct_flags, ctdst->ct_flags);
+ ISP_IOXGET_32(isp, &ctsrc->ct_xfrlen, ctdst->ct_xfrlen);
+ ISP_IOXGET_32(isp, &ctsrc->ct_resid, ctdst->ct_resid);
+ ISP_IOXGET_16(isp, &ctsrc->ct_timeout, ctdst->ct_timeout);
+ ISP_IOXGET_16(isp, &ctsrc->ct_seg_count, ctdst->ct_seg_count);
+ for (i = 0; i < ISP_RQDSEG; i++) {
+ ISP_IOXGET_32(isp,
+ &ctsrc->ct_dataseg[i].ds_base,
+ ctdst->ct_dataseg[i].ds_base);
+ ISP_IOXGET_32(isp,
+ &ctsrc->ct_dataseg[i].ds_count,
+ ctdst->ct_dataseg[i].ds_count);
+ }
+}
+
+void
+isp_put_ctio2(ispsoftc_t *isp, ct2_entry_t *ctsrc, ct2_entry_t *ctdst)
+{
+ int i;
+ isp_copy_out_hdr(isp, &ctsrc->ct_header, &ctdst->ct_header);
+ ISP_IOXPUT_16(isp, ctsrc->ct_reserved, &ctdst->ct_reserved);
+ ISP_IOXPUT_16(isp, ctsrc->ct_fwhandle, &ctdst->ct_fwhandle);
+ ISP_IOXPUT_8(isp, ctsrc->ct_lun, &ctdst->ct_lun);
+ ISP_IOXPUT_8(isp, ctsrc->ct_iid, &ctdst->ct_iid);
+ ISP_IOXPUT_16(isp, ctsrc->ct_rxid, &ctdst->ct_rxid);
+ ISP_IOXPUT_16(isp, ctsrc->ct_flags, &ctdst->ct_flags);
+ ISP_IOXPUT_16(isp, ctsrc->ct_timeout, &ctdst->ct_timeout);
+ ISP_IOXPUT_16(isp, ctsrc->ct_seg_count, &ctdst->ct_seg_count);
+ ISP_IOXPUT_32(isp, ctsrc->ct_resid, &ctdst->ct_resid);
+ ISP_IOXPUT_32(isp, ctsrc->ct_reloff, &ctdst->ct_reloff);
+ if ((ctsrc->ct_flags & CT2_FLAG_MMASK) == CT2_FLAG_MODE0) {
+ ISP_IOXPUT_32(isp, ctsrc->rsp.m0._reserved,
+ &ctdst->rsp.m0._reserved);
+ ISP_IOXPUT_16(isp, ctsrc->rsp.m0._reserved2,
+ &ctdst->rsp.m0._reserved2);
+ ISP_IOXPUT_16(isp, ctsrc->rsp.m0.ct_scsi_status,
+ &ctdst->rsp.m0.ct_scsi_status);
+ ISP_IOXPUT_32(isp, ctsrc->rsp.m0.ct_xfrlen,
+ &ctdst->rsp.m0.ct_xfrlen);
+ if (ctsrc->ct_header.rqs_entry_type == RQSTYPE_CTIO2) {
+ for (i = 0; i < ISP_RQDSEG_T2; i++) {
+ ISP_IOXPUT_32(isp,
+ ctsrc->rsp.m0.ct_dataseg[i].ds_base,
+ &ctdst->rsp.m0.ct_dataseg[i].ds_base);
+ ISP_IOXPUT_32(isp,
+ ctsrc->rsp.m0.ct_dataseg[i].ds_count,
+ &ctdst->rsp.m0.ct_dataseg[i].ds_count);
+ }
+ } else if (ctsrc->ct_header.rqs_entry_type == RQSTYPE_CTIO3) {
+ for (i = 0; i < ISP_RQDSEG_T3; i++) {
+ ISP_IOXPUT_32(isp,
+ ctsrc->rsp.m0.ct_dataseg64[i].ds_base,
+ &ctdst->rsp.m0.ct_dataseg64[i].ds_base);
+ ISP_IOXPUT_32(isp,
+ ctsrc->rsp.m0.ct_dataseg64[i].ds_basehi,
+ &ctdst->rsp.m0.ct_dataseg64[i].ds_basehi);
+ ISP_IOXPUT_32(isp,
+ ctsrc->rsp.m0.ct_dataseg64[i].ds_count,
+ &ctdst->rsp.m0.ct_dataseg64[i].ds_count);
+ }
+ } else if (ctsrc->ct_header.rqs_entry_type == RQSTYPE_CTIO4) {
+ ISP_IOXPUT_16(isp, ctsrc->rsp.m0.ct_dslist.ds_type,
+ &ctdst->rsp.m0.ct_dslist.ds_type);
+ ISP_IOXPUT_32(isp, ctsrc->rsp.m0.ct_dslist.ds_segment,
+ &ctdst->rsp.m0.ct_dslist.ds_segment);
+ ISP_IOXPUT_32(isp, ctsrc->rsp.m0.ct_dslist.ds_base,
+ &ctdst->rsp.m0.ct_dslist.ds_base);
+ }
+ } else if ((ctsrc->ct_flags & CT2_FLAG_MMASK) == CT2_FLAG_MODE1) {
+ ISP_IOXPUT_16(isp, ctsrc->rsp.m1._reserved,
+ &ctdst->rsp.m1._reserved);
+ ISP_IOXPUT_16(isp, ctsrc->rsp.m1._reserved2,
+ &ctdst->rsp.m1._reserved2);
+ ISP_IOXPUT_16(isp, ctsrc->rsp.m1.ct_senselen,
+ &ctdst->rsp.m1.ct_senselen);
+ ISP_IOXPUT_16(isp, ctsrc->rsp.m1.ct_scsi_status,
+ &ctdst->rsp.m1.ct_scsi_status);
+ ISP_IOXPUT_16(isp, ctsrc->rsp.m1.ct_resplen,
+ &ctdst->rsp.m1.ct_resplen);
+ for (i = 0; i < MAXRESPLEN; i++) {
+ ISP_IOXPUT_8(isp, ctsrc->rsp.m1.ct_resp[i],
+ &ctdst->rsp.m1.ct_resp[i]);
+ }
+ } else {
+ ISP_IOXPUT_32(isp, ctsrc->rsp.m2._reserved,
+ &ctdst->rsp.m2._reserved);
+ ISP_IOXPUT_16(isp, ctsrc->rsp.m2._reserved2,
+ &ctdst->rsp.m2._reserved2);
+ ISP_IOXPUT_16(isp, ctsrc->rsp.m2._reserved3,
+ &ctdst->rsp.m2._reserved3);
+ ISP_IOXPUT_32(isp, ctsrc->rsp.m2.ct_datalen,
+ &ctdst->rsp.m2.ct_datalen);
+ ISP_IOXPUT_32(isp, ctsrc->rsp.m2.ct_fcp_rsp_iudata.ds_base,
+ &ctdst->rsp.m2.ct_fcp_rsp_iudata.ds_base);
+ ISP_IOXPUT_32(isp, ctsrc->rsp.m2.ct_fcp_rsp_iudata.ds_count,
+ &ctdst->rsp.m2.ct_fcp_rsp_iudata.ds_count);
+ }
+}
+
+void
+isp_put_ctio2e(ispsoftc_t *isp, ct2e_entry_t *ctsrc, ct2e_entry_t *ctdst)
+{
+ int i;
+ isp_copy_out_hdr(isp, &ctsrc->ct_header, &ctdst->ct_header);
+ ISP_IOXPUT_16(isp, ctsrc->ct_reserved, &ctdst->ct_reserved);
+ ISP_IOXPUT_16(isp, ctsrc->ct_fwhandle, &ctdst->ct_fwhandle);
+ ISP_IOXPUT_16(isp, ctsrc->ct_iid, &ctdst->ct_iid);
+ ISP_IOXPUT_16(isp, ctsrc->ct_rxid, &ctdst->ct_rxid);
+ ISP_IOXPUT_16(isp, ctsrc->ct_flags, &ctdst->ct_flags);
+ ISP_IOXPUT_16(isp, ctsrc->ct_timeout, &ctdst->ct_timeout);
+ ISP_IOXPUT_16(isp, ctsrc->ct_seg_count, &ctdst->ct_seg_count);
+ ISP_IOXPUT_32(isp, ctsrc->ct_resid, &ctdst->ct_resid);
+ ISP_IOXPUT_32(isp, ctsrc->ct_reloff, &ctdst->ct_reloff);
+ if ((ctsrc->ct_flags & CT2_FLAG_MMASK) == CT2_FLAG_MODE0) {
+ ISP_IOXPUT_32(isp, ctsrc->rsp.m0._reserved,
+ &ctdst->rsp.m0._reserved);
+ ISP_IOXPUT_16(isp, ctsrc->rsp.m0._reserved2,
+ &ctdst->rsp.m0._reserved2);
+ ISP_IOXPUT_16(isp, ctsrc->rsp.m0.ct_scsi_status,
+ &ctdst->rsp.m0.ct_scsi_status);
+ ISP_IOXPUT_32(isp, ctsrc->rsp.m0.ct_xfrlen,
+ &ctdst->rsp.m0.ct_xfrlen);
+ if (ctsrc->ct_header.rqs_entry_type == RQSTYPE_CTIO2) {
+ for (i = 0; i < ISP_RQDSEG_T2; i++) {
+ ISP_IOXPUT_32(isp,
+ ctsrc->rsp.m0.ct_dataseg[i].ds_base,
+ &ctdst->rsp.m0.ct_dataseg[i].ds_base);
+ ISP_IOXPUT_32(isp,
+ ctsrc->rsp.m0.ct_dataseg[i].ds_count,
+ &ctdst->rsp.m0.ct_dataseg[i].ds_count);
+ }
+ } else if (ctsrc->ct_header.rqs_entry_type == RQSTYPE_CTIO3) {
+ for (i = 0; i < ISP_RQDSEG_T3; i++) {
+ ISP_IOXPUT_32(isp,
+ ctsrc->rsp.m0.ct_dataseg64[i].ds_base,
+ &ctdst->rsp.m0.ct_dataseg64[i].ds_base);
+ ISP_IOXPUT_32(isp,
+ ctsrc->rsp.m0.ct_dataseg64[i].ds_basehi,
+ &ctdst->rsp.m0.ct_dataseg64[i].ds_basehi);
+ ISP_IOXPUT_32(isp,
+ ctsrc->rsp.m0.ct_dataseg64[i].ds_count,
+ &ctdst->rsp.m0.ct_dataseg64[i].ds_count);
+ }
+ } else if (ctsrc->ct_header.rqs_entry_type == RQSTYPE_CTIO4) {
+ ISP_IOXPUT_16(isp, ctsrc->rsp.m0.ct_dslist.ds_type,
+ &ctdst->rsp.m0.ct_dslist.ds_type);
+ ISP_IOXPUT_32(isp, ctsrc->rsp.m0.ct_dslist.ds_segment,
+ &ctdst->rsp.m0.ct_dslist.ds_segment);
+ ISP_IOXPUT_32(isp, ctsrc->rsp.m0.ct_dslist.ds_base,
+ &ctdst->rsp.m0.ct_dslist.ds_base);
+ }
+ } else if ((ctsrc->ct_flags & CT2_FLAG_MMASK) == CT2_FLAG_MODE1) {
+ ISP_IOXPUT_16(isp, ctsrc->rsp.m1._reserved,
+ &ctdst->rsp.m1._reserved);
+ ISP_IOXPUT_16(isp, ctsrc->rsp.m1._reserved2,
+ &ctdst->rsp.m1._reserved2);
+ ISP_IOXPUT_16(isp, ctsrc->rsp.m1.ct_senselen,
+ &ctdst->rsp.m1.ct_senselen);
+ ISP_IOXPUT_16(isp, ctsrc->rsp.m1.ct_scsi_status,
+ &ctdst->rsp.m1.ct_scsi_status);
+ ISP_IOXPUT_16(isp, ctsrc->rsp.m1.ct_resplen,
+ &ctdst->rsp.m1.ct_resplen);
+ for (i = 0; i < MAXRESPLEN; i++) {
+ ISP_IOXPUT_8(isp, ctsrc->rsp.m1.ct_resp[i],
+ &ctdst->rsp.m1.ct_resp[i]);
+ }
+ } else {
+ ISP_IOXPUT_32(isp, ctsrc->rsp.m2._reserved,
+ &ctdst->rsp.m2._reserved);
+ ISP_IOXPUT_16(isp, ctsrc->rsp.m2._reserved2,
+ &ctdst->rsp.m2._reserved2);
+ ISP_IOXPUT_16(isp, ctsrc->rsp.m2._reserved3,
+ &ctdst->rsp.m2._reserved3);
+ ISP_IOXPUT_32(isp, ctsrc->rsp.m2.ct_datalen,
+ &ctdst->rsp.m2.ct_datalen);
+ ISP_IOXPUT_32(isp, ctsrc->rsp.m2.ct_fcp_rsp_iudata.ds_base,
+ &ctdst->rsp.m2.ct_fcp_rsp_iudata.ds_base);
+ ISP_IOXPUT_32(isp, ctsrc->rsp.m2.ct_fcp_rsp_iudata.ds_count,
+ &ctdst->rsp.m2.ct_fcp_rsp_iudata.ds_count);
+ }
+}
+
+void
+isp_get_ctio2(ispsoftc_t *isp, ct2_entry_t *ctsrc, ct2_entry_t *ctdst)
+{
+ isp_copy_in_hdr(isp, &ctsrc->ct_header, &ctdst->ct_header);
+ ISP_IOXGET_16(isp, &ctsrc->ct_reserved, ctdst->ct_reserved);
+ ISP_IOXGET_16(isp, &ctsrc->ct_fwhandle, ctdst->ct_fwhandle);
+ ISP_IOXGET_8(isp, &ctsrc->ct_lun, ctdst->ct_lun);
+ ISP_IOXGET_8(isp, &ctsrc->ct_iid, ctdst->ct_iid);
+ ISP_IOXGET_16(isp, &ctsrc->ct_rxid, ctdst->ct_rxid);
+ ISP_IOXGET_16(isp, &ctsrc->ct_flags, ctdst->ct_flags);
+ ISP_IOXGET_16(isp, &ctsrc->ct_status, ctdst->ct_status);
+ ISP_IOXGET_16(isp, &ctsrc->ct_timeout, ctdst->ct_timeout);
+ ISP_IOXGET_16(isp, &ctsrc->ct_seg_count, ctdst->ct_seg_count);
+ ISP_IOXGET_32(isp, &ctsrc->ct_reloff, ctdst->ct_reloff);
+ ISP_IOXGET_32(isp, &ctsrc->ct_resid, ctdst->ct_resid);
+}
+
+void
+isp_get_ctio2e(ispsoftc_t *isp, ct2e_entry_t *ctsrc, ct2e_entry_t *ctdst)
+{
+ isp_copy_in_hdr(isp, &ctsrc->ct_header, &ctdst->ct_header);
+ ISP_IOXGET_16(isp, &ctsrc->ct_reserved, ctdst->ct_reserved);
+ ISP_IOXGET_16(isp, &ctsrc->ct_fwhandle, ctdst->ct_fwhandle);
+ ISP_IOXGET_16(isp, &ctsrc->ct_iid, ctdst->ct_iid);
+ ISP_IOXGET_16(isp, &ctsrc->ct_rxid, ctdst->ct_rxid);
+ ISP_IOXGET_16(isp, &ctsrc->ct_flags, ctdst->ct_flags);
+ ISP_IOXGET_16(isp, &ctsrc->ct_status, ctdst->ct_status);
+ ISP_IOXGET_16(isp, &ctsrc->ct_timeout, ctdst->ct_timeout);
+ ISP_IOXGET_16(isp, &ctsrc->ct_seg_count, ctdst->ct_seg_count);
+ ISP_IOXGET_32(isp, &ctsrc->ct_reloff, ctdst->ct_reloff);
+ ISP_IOXGET_32(isp, &ctsrc->ct_resid, ctdst->ct_resid);
+}
+
+void
+isp_put_enable_lun(ispsoftc_t *isp, lun_entry_t *lesrc, lun_entry_t *ledst)
+{
+ int i;
+ isp_copy_out_hdr(isp, &lesrc->le_header, &ledst->le_header);
+ ISP_IOXPUT_32(isp, lesrc->le_reserved, &ledst->le_reserved);
+ if (ISP_IS_SBUS(isp)) {
+ ISP_IOXPUT_8(isp, lesrc->le_lun, &ledst->le_rsvd);
+ ISP_IOXPUT_8(isp, lesrc->le_rsvd, &ledst->le_lun);
+ ISP_IOXPUT_8(isp, lesrc->le_ops, &ledst->le_tgt);
+ ISP_IOXPUT_8(isp, lesrc->le_tgt, &ledst->le_ops);
+ ISP_IOXPUT_8(isp, lesrc->le_status, &ledst->le_reserved2);
+ ISP_IOXPUT_8(isp, lesrc->le_reserved2, &ledst->le_status);
+ ISP_IOXPUT_8(isp, lesrc->le_cmd_count, &ledst->le_in_count);
+ ISP_IOXPUT_8(isp, lesrc->le_in_count, &ledst->le_cmd_count);
+ ISP_IOXPUT_8(isp, lesrc->le_cdb6len, &ledst->le_cdb7len);
+ ISP_IOXPUT_8(isp, lesrc->le_cdb7len, &ledst->le_cdb6len);
+ } else {
+ ISP_IOXPUT_8(isp, lesrc->le_lun, &ledst->le_lun);
+ ISP_IOXPUT_8(isp, lesrc->le_rsvd, &ledst->le_rsvd);
+ ISP_IOXPUT_8(isp, lesrc->le_ops, &ledst->le_ops);
+ ISP_IOXPUT_8(isp, lesrc->le_tgt, &ledst->le_tgt);
+ ISP_IOXPUT_8(isp, lesrc->le_status, &ledst->le_status);
+ ISP_IOXPUT_8(isp, lesrc->le_reserved2, &ledst->le_reserved2);
+ ISP_IOXPUT_8(isp, lesrc->le_cmd_count, &ledst->le_cmd_count);
+ ISP_IOXPUT_8(isp, lesrc->le_in_count, &ledst->le_in_count);
+ ISP_IOXPUT_8(isp, lesrc->le_cdb6len, &ledst->le_cdb6len);
+ ISP_IOXPUT_8(isp, lesrc->le_cdb7len, &ledst->le_cdb7len);
+ }
+ ISP_IOXPUT_32(isp, lesrc->le_flags, &ledst->le_flags);
+ ISP_IOXPUT_16(isp, lesrc->le_timeout, &ledst->le_timeout);
+ for (i = 0; i < 20; i++) {
+ ISP_IOXPUT_8(isp, lesrc->le_reserved3[i],
+ &ledst->le_reserved3[i]);
+ }
+}
+
+void
+isp_get_enable_lun(ispsoftc_t *isp, lun_entry_t *lesrc, lun_entry_t *ledst)
+{
+ int i;
+ isp_copy_in_hdr(isp, &lesrc->le_header, &ledst->le_header);
+ ISP_IOXGET_32(isp, &lesrc->le_reserved, ledst->le_reserved);
+ if (ISP_IS_SBUS(isp)) {
+ ISP_IOXGET_8(isp, &lesrc->le_lun, ledst->le_rsvd);
+ ISP_IOXGET_8(isp, &lesrc->le_rsvd, ledst->le_lun);
+ ISP_IOXGET_8(isp, &lesrc->le_ops, ledst->le_tgt);
+ ISP_IOXGET_8(isp, &lesrc->le_tgt, ledst->le_ops);
+ ISP_IOXGET_8(isp, &lesrc->le_status, ledst->le_reserved2);
+ ISP_IOXGET_8(isp, &lesrc->le_reserved2, ledst->le_status);
+ ISP_IOXGET_8(isp, &lesrc->le_cmd_count, ledst->le_in_count);
+ ISP_IOXGET_8(isp, &lesrc->le_in_count, ledst->le_cmd_count);
+ ISP_IOXGET_8(isp, &lesrc->le_cdb6len, ledst->le_cdb7len);
+ ISP_IOXGET_8(isp, &lesrc->le_cdb7len, ledst->le_cdb6len);
+ } else {
+ ISP_IOXGET_8(isp, &lesrc->le_lun, ledst->le_lun);
+ ISP_IOXGET_8(isp, &lesrc->le_rsvd, ledst->le_rsvd);
+ ISP_IOXGET_8(isp, &lesrc->le_ops, ledst->le_ops);
+ ISP_IOXGET_8(isp, &lesrc->le_tgt, ledst->le_tgt);
+ ISP_IOXGET_8(isp, &lesrc->le_status, ledst->le_status);
+ ISP_IOXGET_8(isp, &lesrc->le_reserved2, ledst->le_reserved2);
+ ISP_IOXGET_8(isp, &lesrc->le_cmd_count, ledst->le_cmd_count);
+ ISP_IOXGET_8(isp, &lesrc->le_in_count, ledst->le_in_count);
+ ISP_IOXGET_8(isp, &lesrc->le_cdb6len, ledst->le_cdb6len);
+ ISP_IOXGET_8(isp, &lesrc->le_cdb7len, ledst->le_cdb7len);
+ }
+ ISP_IOXGET_32(isp, &lesrc->le_flags, ledst->le_flags);
+ ISP_IOXGET_16(isp, &lesrc->le_timeout, ledst->le_timeout);
+ for (i = 0; i < 20; i++) {
+ ISP_IOXGET_8(isp, &lesrc->le_reserved3[i],
+ ledst->le_reserved3[i]);
+ }
+}
+
+void
+isp_put_notify(ispsoftc_t *isp, in_entry_t *insrc, in_entry_t *indst)
+{
+ int i;
+ isp_copy_out_hdr(isp, &insrc->in_header, &indst->in_header);
+ ISP_IOXPUT_32(isp, insrc->in_reserved, &indst->in_reserved);
+ if (ISP_IS_SBUS(isp)) {
+ ISP_IOXPUT_8(isp, insrc->in_lun, &indst->in_iid);
+ ISP_IOXPUT_8(isp, insrc->in_iid, &indst->in_lun);
+ ISP_IOXPUT_8(isp, insrc->in_reserved2, &indst->in_tgt);
+ ISP_IOXPUT_8(isp, insrc->in_tgt, &indst->in_reserved2);
+ ISP_IOXPUT_8(isp, insrc->in_status, &indst->in_rsvd2);
+ ISP_IOXPUT_8(isp, insrc->in_rsvd2, &indst->in_status);
+ ISP_IOXPUT_8(isp, insrc->in_tag_val, &indst->in_tag_type);
+ ISP_IOXPUT_8(isp, insrc->in_tag_type, &indst->in_tag_val);
+ } else {
+ ISP_IOXPUT_8(isp, insrc->in_lun, &indst->in_lun);
+ ISP_IOXPUT_8(isp, insrc->in_iid, &indst->in_iid);
+ ISP_IOXPUT_8(isp, insrc->in_reserved2, &indst->in_reserved2);
+ ISP_IOXPUT_8(isp, insrc->in_tgt, &indst->in_tgt);
+ ISP_IOXPUT_8(isp, insrc->in_status, &indst->in_status);
+ ISP_IOXPUT_8(isp, insrc->in_rsvd2, &indst->in_rsvd2);
+ ISP_IOXPUT_8(isp, insrc->in_tag_val, &indst->in_tag_val);
+ ISP_IOXPUT_8(isp, insrc->in_tag_type, &indst->in_tag_type);
+ }
+ ISP_IOXPUT_32(isp, insrc->in_flags, &indst->in_flags);
+ ISP_IOXPUT_16(isp, insrc->in_seqid, &indst->in_seqid);
+ for (i = 0; i < IN_MSGLEN; i++) {
+ ISP_IOXPUT_8(isp, insrc->in_msg[i], &indst->in_msg[i]);
+ }
+ for (i = 0; i < IN_RSVDLEN; i++) {
+ ISP_IOXPUT_8(isp, insrc->in_reserved3[i],
+ &indst->in_reserved3[i]);
+ }
+ for (i = 0; i < QLTM_SENSELEN; i++) {
+ ISP_IOXPUT_8(isp, insrc->in_sense[i],
+ &indst->in_sense[i]);
+ }
+}
+
+void
+isp_get_notify(ispsoftc_t *isp, in_entry_t *insrc, in_entry_t *indst)
+{
+ int i;
+ isp_copy_in_hdr(isp, &insrc->in_header, &indst->in_header);
+ ISP_IOXGET_32(isp, &insrc->in_reserved, indst->in_reserved);
+ if (ISP_IS_SBUS(isp)) {
+ ISP_IOXGET_8(isp, &insrc->in_lun, indst->in_iid);
+ ISP_IOXGET_8(isp, &insrc->in_iid, indst->in_lun);
+ ISP_IOXGET_8(isp, &insrc->in_reserved2, indst->in_tgt);
+ ISP_IOXGET_8(isp, &insrc->in_tgt, indst->in_reserved2);
+ ISP_IOXGET_8(isp, &insrc->in_status, indst->in_rsvd2);
+ ISP_IOXGET_8(isp, &insrc->in_rsvd2, indst->in_status);
+ ISP_IOXGET_8(isp, &insrc->in_tag_val, indst->in_tag_type);
+ ISP_IOXGET_8(isp, &insrc->in_tag_type, indst->in_tag_val);
+ } else {
+ ISP_IOXGET_8(isp, &insrc->in_lun, indst->in_lun);
+ ISP_IOXGET_8(isp, &insrc->in_iid, indst->in_iid);
+ ISP_IOXGET_8(isp, &insrc->in_reserved2, indst->in_reserved2);
+ ISP_IOXGET_8(isp, &insrc->in_tgt, indst->in_tgt);
+ ISP_IOXGET_8(isp, &insrc->in_status, indst->in_status);
+ ISP_IOXGET_8(isp, &insrc->in_rsvd2, indst->in_rsvd2);
+ ISP_IOXGET_8(isp, &insrc->in_tag_val, indst->in_tag_val);
+ ISP_IOXGET_8(isp, &insrc->in_tag_type, indst->in_tag_type);
+ }
+ ISP_IOXGET_32(isp, &insrc->in_flags, indst->in_flags);
+ ISP_IOXGET_16(isp, &insrc->in_seqid, indst->in_seqid);
+ for (i = 0; i < IN_MSGLEN; i++) {
+ ISP_IOXGET_8(isp, &insrc->in_msg[i], indst->in_msg[i]);
+ }
+ for (i = 0; i < IN_RSVDLEN; i++) {
+ ISP_IOXGET_8(isp, &insrc->in_reserved3[i],
+ indst->in_reserved3[i]);
+ }
+ for (i = 0; i < QLTM_SENSELEN; i++) {
+ ISP_IOXGET_8(isp, &insrc->in_sense[i],
+ indst->in_sense[i]);
+ }
+}
+
+void
+isp_put_notify_fc(ispsoftc_t *isp, in_fcentry_t *insrc,
+ in_fcentry_t *indst)
+{
+ isp_copy_out_hdr(isp, &insrc->in_header, &indst->in_header);
+ ISP_IOXPUT_32(isp, insrc->in_reserved, &indst->in_reserved);
+ ISP_IOXPUT_8(isp, insrc->in_lun, &indst->in_lun);
+ ISP_IOXPUT_8(isp, insrc->in_iid, &indst->in_iid);
+ ISP_IOXPUT_16(isp, insrc->in_scclun, &indst->in_scclun);
+ ISP_IOXPUT_32(isp, insrc->in_reserved2, &indst->in_reserved2);
+ ISP_IOXPUT_16(isp, insrc->in_status, &indst->in_status);
+ ISP_IOXPUT_16(isp, insrc->in_task_flags, &indst->in_task_flags);
+ ISP_IOXPUT_16(isp, insrc->in_seqid, &indst->in_seqid);
+}
+
+void
+isp_put_notify_fc_e(ispsoftc_t *isp, in_fcentry_e_t *insrc,
+ in_fcentry_e_t *indst)
+{
+ isp_copy_out_hdr(isp, &insrc->in_header, &indst->in_header);
+ ISP_IOXPUT_32(isp, insrc->in_reserved, &indst->in_reserved);
+ ISP_IOXPUT_16(isp, insrc->in_iid, &indst->in_iid);
+ ISP_IOXPUT_16(isp, insrc->in_scclun, &indst->in_scclun);
+ ISP_IOXPUT_32(isp, insrc->in_reserved2, &indst->in_reserved2);
+ ISP_IOXPUT_16(isp, insrc->in_status, &indst->in_status);
+ ISP_IOXPUT_16(isp, insrc->in_task_flags, &indst->in_task_flags);
+ ISP_IOXPUT_16(isp, insrc->in_seqid, &indst->in_seqid);
+}
+
+void
+isp_get_notify_fc(ispsoftc_t *isp, in_fcentry_t *insrc,
+ in_fcentry_t *indst)
+{
+ isp_copy_in_hdr(isp, &insrc->in_header, &indst->in_header);
+ ISP_IOXGET_32(isp, &insrc->in_reserved, indst->in_reserved);
+ ISP_IOXGET_8(isp, &insrc->in_lun, indst->in_lun);
+ ISP_IOXGET_8(isp, &insrc->in_iid, indst->in_iid);
+ ISP_IOXGET_16(isp, &insrc->in_scclun, indst->in_scclun);
+ ISP_IOXGET_32(isp, &insrc->in_reserved2, indst->in_reserved2);
+ ISP_IOXGET_16(isp, &insrc->in_status, indst->in_status);
+ ISP_IOXGET_16(isp, &insrc->in_task_flags, indst->in_task_flags);
+ ISP_IOXGET_16(isp, &insrc->in_seqid, indst->in_seqid);
+}
+
+void
+isp_get_notify_fc_e(ispsoftc_t *isp, in_fcentry_e_t *insrc,
+ in_fcentry_e_t *indst)
+{
+ isp_copy_in_hdr(isp, &insrc->in_header, &indst->in_header);
+ ISP_IOXGET_32(isp, &insrc->in_reserved, indst->in_reserved);
+ ISP_IOXGET_16(isp, &insrc->in_iid, indst->in_iid);
+ ISP_IOXGET_16(isp, &insrc->in_scclun, indst->in_scclun);
+ ISP_IOXGET_32(isp, &insrc->in_reserved2, indst->in_reserved2);
+ ISP_IOXGET_16(isp, &insrc->in_status, indst->in_status);
+ ISP_IOXGET_16(isp, &insrc->in_task_flags, indst->in_task_flags);
+ ISP_IOXGET_16(isp, &insrc->in_seqid, indst->in_seqid);
+}
+
+void
+isp_put_notify_ack(ispsoftc_t *isp, na_entry_t *nasrc, na_entry_t *nadst)
+{
+ int i;
+ isp_copy_out_hdr(isp, &nasrc->na_header, &nadst->na_header);
+ ISP_IOXPUT_32(isp, nasrc->na_reserved, &nadst->na_reserved);
+ if (ISP_IS_SBUS(isp)) {
+ ISP_IOXPUT_8(isp, nasrc->na_lun, &nadst->na_iid);
+ ISP_IOXPUT_8(isp, nasrc->na_iid, &nadst->na_lun);
+ ISP_IOXPUT_8(isp, nasrc->na_status, &nadst->na_event);
+ ISP_IOXPUT_8(isp, nasrc->na_event, &nadst->na_status);
+ } else {
+ ISP_IOXPUT_8(isp, nasrc->na_lun, &nadst->na_lun);
+ ISP_IOXPUT_8(isp, nasrc->na_iid, &nadst->na_iid);
+ ISP_IOXPUT_8(isp, nasrc->na_status, &nadst->na_status);
+ ISP_IOXPUT_8(isp, nasrc->na_event, &nadst->na_event);
+ }
+ ISP_IOXPUT_32(isp, nasrc->na_flags, &nadst->na_flags);
+ for (i = 0; i < NA_RSVDLEN; i++) {
+ ISP_IOXPUT_16(isp, nasrc->na_reserved3[i],
+ &nadst->na_reserved3[i]);
+ }
+}
+
+void
+isp_get_notify_ack(ispsoftc_t *isp, na_entry_t *nasrc, na_entry_t *nadst)
+{
+ int i;
+ isp_copy_in_hdr(isp, &nasrc->na_header, &nadst->na_header);
+ ISP_IOXGET_32(isp, &nasrc->na_reserved, nadst->na_reserved);
+ if (ISP_IS_SBUS(isp)) {
+ ISP_IOXGET_8(isp, &nasrc->na_lun, nadst->na_iid);
+ ISP_IOXGET_8(isp, &nasrc->na_iid, nadst->na_lun);
+ ISP_IOXGET_8(isp, &nasrc->na_status, nadst->na_event);
+ ISP_IOXGET_8(isp, &nasrc->na_event, nadst->na_status);
+ } else {
+ ISP_IOXGET_8(isp, &nasrc->na_lun, nadst->na_lun);
+ ISP_IOXGET_8(isp, &nasrc->na_iid, nadst->na_iid);
+ ISP_IOXGET_8(isp, &nasrc->na_status, nadst->na_status);
+ ISP_IOXGET_8(isp, &nasrc->na_event, nadst->na_event);
+ }
+ ISP_IOXGET_32(isp, &nasrc->na_flags, nadst->na_flags);
+ for (i = 0; i < NA_RSVDLEN; i++) {
+ ISP_IOXGET_16(isp, &nasrc->na_reserved3[i],
+ nadst->na_reserved3[i]);
+ }
+}
+
+void
+isp_put_notify_ack_fc(ispsoftc_t *isp, na_fcentry_t *nasrc,
+ na_fcentry_t *nadst)
+{
+ int i;
+ isp_copy_out_hdr(isp, &nasrc->na_header, &nadst->na_header);
+ ISP_IOXPUT_32(isp, nasrc->na_reserved, &nadst->na_reserved);
+ ISP_IOXPUT_8(isp, nasrc->na_reserved1, &nadst->na_reserved1);
+ ISP_IOXPUT_8(isp, nasrc->na_iid, &nadst->na_iid);
+ ISP_IOXPUT_16(isp, nasrc->na_response, &nadst->na_response);
+ ISP_IOXPUT_16(isp, nasrc->na_flags, &nadst->na_flags);
+ ISP_IOXPUT_16(isp, nasrc->na_reserved2, &nadst->na_reserved2);
+ ISP_IOXPUT_16(isp, nasrc->na_status, &nadst->na_status);
+ ISP_IOXPUT_16(isp, nasrc->na_task_flags, &nadst->na_task_flags);
+ ISP_IOXPUT_16(isp, nasrc->na_seqid, &nadst->na_seqid);
+ for (i = 0; i < NA2_RSVDLEN; i++) {
+ ISP_IOXPUT_16(isp, nasrc->na_reserved3[i],
+ &nadst->na_reserved3[i]);
+ }
+}
+
+void
+isp_put_notify_ack_fc_e(ispsoftc_t *isp, na_fcentry_e_t *nasrc,
+ na_fcentry_e_t *nadst)
+{
+ int i;
+ isp_copy_out_hdr(isp, &nasrc->na_header, &nadst->na_header);
+ ISP_IOXPUT_32(isp, nasrc->na_reserved, &nadst->na_reserved);
+ ISP_IOXPUT_16(isp, nasrc->na_iid, &nadst->na_iid);
+ ISP_IOXPUT_16(isp, nasrc->na_response, &nadst->na_response);
+ ISP_IOXPUT_16(isp, nasrc->na_flags, &nadst->na_flags);
+ ISP_IOXPUT_16(isp, nasrc->na_reserved2, &nadst->na_reserved2);
+ ISP_IOXPUT_16(isp, nasrc->na_status, &nadst->na_status);
+ ISP_IOXPUT_16(isp, nasrc->na_task_flags, &nadst->na_task_flags);
+ ISP_IOXPUT_16(isp, nasrc->na_seqid, &nadst->na_seqid);
+ for (i = 0; i < NA2_RSVDLEN; i++) {
+ ISP_IOXPUT_16(isp, nasrc->na_reserved3[i],
+ &nadst->na_reserved3[i]);
+ }
+}
+
+void
+isp_get_notify_ack_fc(ispsoftc_t *isp, na_fcentry_t *nasrc,
+ na_fcentry_t *nadst)
+{
+ int i;
+ isp_copy_in_hdr(isp, &nasrc->na_header, &nadst->na_header);
+ ISP_IOXGET_32(isp, &nasrc->na_reserved, nadst->na_reserved);
+ ISP_IOXGET_8(isp, &nasrc->na_reserved1, nadst->na_reserved1);
+ ISP_IOXGET_8(isp, &nasrc->na_iid, nadst->na_iid);
+ ISP_IOXGET_16(isp, &nasrc->na_response, nadst->na_response);
+ ISP_IOXGET_16(isp, &nasrc->na_flags, nadst->na_flags);
+ ISP_IOXGET_16(isp, &nasrc->na_reserved2, nadst->na_reserved2);
+ ISP_IOXGET_16(isp, &nasrc->na_status, nadst->na_status);
+ ISP_IOXGET_16(isp, &nasrc->na_task_flags, nadst->na_task_flags);
+ ISP_IOXGET_16(isp, &nasrc->na_seqid, nadst->na_seqid);
+ for (i = 0; i < NA2_RSVDLEN; i++) {
+ ISP_IOXGET_16(isp, &nasrc->na_reserved3[i],
+ nadst->na_reserved3[i]);
+ }
+}
+
+void
+isp_get_notify_ack_fc_e(ispsoftc_t *isp, na_fcentry_e_t *nasrc,
+ na_fcentry_e_t *nadst)
+{
+ int i;
+ isp_copy_in_hdr(isp, &nasrc->na_header, &nadst->na_header);
+ ISP_IOXGET_32(isp, &nasrc->na_reserved, nadst->na_reserved);
+ ISP_IOXGET_16(isp, &nasrc->na_iid, nadst->na_iid);
+ ISP_IOXGET_16(isp, &nasrc->na_response, nadst->na_response);
+ ISP_IOXGET_16(isp, &nasrc->na_flags, nadst->na_flags);
+ ISP_IOXGET_16(isp, &nasrc->na_reserved2, nadst->na_reserved2);
+ ISP_IOXGET_16(isp, &nasrc->na_status, nadst->na_status);
+ ISP_IOXGET_16(isp, &nasrc->na_task_flags, nadst->na_task_flags);
+ ISP_IOXGET_16(isp, &nasrc->na_seqid, nadst->na_seqid);
+ for (i = 0; i < NA2_RSVDLEN; i++) {
+ ISP_IOXGET_16(isp, &nasrc->na_reserved3[i],
+ nadst->na_reserved3[i]);
+ }
+}
+#endif /* ISP_TARGET_MODE */
diff --git a/sys/dev/isp/isp_library.h b/sys/dev/isp/isp_library.h
new file mode 100644
index 000000000000..4faa320e3bd6
--- /dev/null
+++ b/sys/dev/isp/isp_library.h
@@ -0,0 +1,164 @@
+/* $FreeBSD$ */
+/*-
+ * Qlogic Host Adapter Library Functions
+ *
+ * Copyright (c) 1999-2006 by Matthew Jacob
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice 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.
+ *
+ * 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.
+ *
+ */
+#ifndef _ISP_LIBRARY_H
+#define _ISP_LIBRARY_H
+
+extern int isp_save_xs(ispsoftc_t *, XS_T *, uint16_t *);
+extern XS_T *isp_find_xs(ispsoftc_t *, uint16_t);
+extern uint16_t isp_find_handle(ispsoftc_t *, XS_T *);
+extern int isp_handle_index(uint16_t);
+extern uint16_t isp_index_handle(int);
+extern void isp_destroy_handle(ispsoftc_t *, uint16_t);
+extern int isp_getrqentry(ispsoftc_t *, uint16_t *, uint16_t *, void **);
+extern void isp_print_qentry (ispsoftc_t *, char *, int, void *);
+extern void isp_print_bytes(ispsoftc_t *, char *, int, void *);
+extern int isp_fc_runstate(ispsoftc_t *, int);
+extern void isp_copy_out_hdr(ispsoftc_t *, isphdr_t *, isphdr_t *);
+extern void isp_copy_in_hdr(ispsoftc_t *, isphdr_t *, isphdr_t *);
+extern int isp_get_response_type(ispsoftc_t *, isphdr_t *);
+extern void
+isp_put_request(ispsoftc_t *, ispreq_t *, ispreq_t *);
+extern void
+isp_put_request_t2(ispsoftc_t *, ispreqt2_t *, ispreqt2_t *);
+extern void
+isp_put_request_t2e(ispsoftc_t *, ispreqt2e_t *, ispreqt2e_t *);
+extern void
+isp_put_request_t3(ispsoftc_t *, ispreqt3_t *, ispreqt3_t *);
+extern void
+isp_put_request_t3e(ispsoftc_t *, ispreqt3e_t *, ispreqt3e_t *);
+extern void
+isp_put_extended_request(ispsoftc_t *, ispextreq_t *, ispextreq_t *);
+extern void
+isp_put_cont_req(ispsoftc_t *, ispcontreq_t *, ispcontreq_t *);
+extern void
+isp_put_cont64_req(ispsoftc_t *, ispcontreq64_t *, ispcontreq64_t *);
+extern void
+isp_get_response(ispsoftc_t *, ispstatusreq_t *, ispstatusreq_t *);
+extern void
+isp_get_response_x(ispsoftc_t *, ispstatus_cont_t *, ispstatus_cont_t *);
+extern void
+isp_get_rio2(ispsoftc_t *, isp_rio2_t *, isp_rio2_t *);
+extern void
+isp_put_icb(ispsoftc_t *, isp_icb_t *, isp_icb_t *);
+extern void
+isp_get_pdb(ispsoftc_t *, isp_pdb_t *, isp_pdb_t *);
+extern void
+isp_get_ct_hdr(ispsoftc_t *isp, ct_hdr_t *, ct_hdr_t *);
+extern void
+isp_put_sns_request(ispsoftc_t *, sns_screq_t *, sns_screq_t *);
+extern void
+isp_put_gid_ft_request(ispsoftc_t *, sns_gid_ft_req_t *,
+ sns_gid_ft_req_t *);
+extern void
+isp_put_gxn_id_request(ispsoftc_t *, sns_gxn_id_req_t *,
+ sns_gxn_id_req_t *);
+extern void
+isp_get_sns_response(ispsoftc_t *, sns_scrsp_t *, sns_scrsp_t *, int);
+extern void
+isp_get_gid_ft_response(ispsoftc_t *, sns_gid_ft_rsp_t *,
+ sns_gid_ft_rsp_t *, int);
+extern void
+isp_get_gxn_id_response(ispsoftc_t *, sns_gxn_id_rsp_t *,
+ sns_gxn_id_rsp_t *);
+extern void
+isp_get_gff_id_response(ispsoftc_t *, sns_gff_id_rsp_t *,
+ sns_gff_id_rsp_t *);
+extern void
+isp_get_ga_nxt_response(ispsoftc_t *, sns_ga_nxt_rsp_t *,
+ sns_ga_nxt_rsp_t *);
+
+#ifdef ISP_TARGET_MODE
+#if defined(__NetBSD__) || defined(__OpenBSD__)
+#include <dev/ic/isp_target.h>
+#elif defined(__FreeBSD__)
+#include <dev/isp/isp_target.h>
+#else
+#include "isp_target.h"
+#endif
+
+extern int isp_save_xs_tgt(ispsoftc_t *, void *, uint16_t *);
+extern void *isp_find_xs_tgt(ispsoftc_t *, uint16_t);
+extern uint16_t isp_find_tgt_handle(ispsoftc_t *, void *);
+extern void isp_destroy_tgt_handle(ispsoftc_t *, uint16_t);
+
+extern void
+isp_put_atio(ispsoftc_t *, at_entry_t *, at_entry_t *);
+extern void
+isp_get_atio(ispsoftc_t *, at_entry_t *, at_entry_t *);
+extern void
+isp_put_atio2(ispsoftc_t *, at2_entry_t *, at2_entry_t *);
+extern void
+isp_put_atio2e(ispsoftc_t *, at2e_entry_t *, at2e_entry_t *);
+extern void
+isp_get_atio2(ispsoftc_t *, at2_entry_t *, at2_entry_t *);
+extern void
+isp_get_atio2e(ispsoftc_t *, at2e_entry_t *, at2e_entry_t *);
+extern void
+isp_put_ctio(ispsoftc_t *, ct_entry_t *, ct_entry_t *);
+extern void
+isp_get_ctio(ispsoftc_t *, ct_entry_t *, ct_entry_t *);
+extern void
+isp_put_ctio2(ispsoftc_t *, ct2_entry_t *, ct2_entry_t *);
+extern void
+isp_put_ctio2e(ispsoftc_t *, ct2e_entry_t *, ct2e_entry_t *);
+extern void
+isp_get_ctio2(ispsoftc_t *, ct2_entry_t *, ct2_entry_t *);
+extern void
+isp_get_ctio2e(ispsoftc_t *, ct2e_entry_t *, ct2e_entry_t *);
+extern void
+isp_put_enable_lun(ispsoftc_t *, lun_entry_t *, lun_entry_t *);
+extern void
+isp_get_enable_lun(ispsoftc_t *, lun_entry_t *, lun_entry_t *);
+extern void
+isp_put_notify(ispsoftc_t *, in_entry_t *, in_entry_t *);
+extern void
+isp_get_notify(ispsoftc_t *, in_entry_t *, in_entry_t *);
+extern void
+isp_put_notify_fc(ispsoftc_t *, in_fcentry_t *, in_fcentry_t *);
+extern void
+isp_put_notify_fc_e(ispsoftc_t *, in_fcentry_e_t *, in_fcentry_e_t *);
+extern void
+isp_get_notify_fc(ispsoftc_t *, in_fcentry_t *, in_fcentry_t *);
+extern void
+isp_get_notify_fc_e(ispsoftc_t *, in_fcentry_e_t *, in_fcentry_e_t *);
+extern void
+isp_put_notify_ack(ispsoftc_t *, na_entry_t *, na_entry_t *);
+extern void
+isp_get_notify_ack(ispsoftc_t *, na_entry_t *, na_entry_t *);
+extern void
+isp_put_notify_ack_fc(ispsoftc_t *, na_fcentry_t *, na_fcentry_t *);
+extern void
+isp_put_notify_ack_fc_e(ispsoftc_t *, na_fcentry_e_t *, na_fcentry_e_t *);
+extern void
+isp_get_notify_ack_fc(ispsoftc_t *, na_fcentry_t *, na_fcentry_t *);
+extern void
+isp_get_notify_ack_fc_e(ispsoftc_t *, na_fcentry_e_t *, na_fcentry_e_t *);
+#endif /* ISP_TARGET_MODE */
+#endif /* _ISP_LIBRARY_H */
diff --git a/sys/dev/isp/isp_pci.c b/sys/dev/isp/isp_pci.c
new file mode 100644
index 000000000000..7ea7dbf5bc7c
--- /dev/null
+++ b/sys/dev/isp/isp_pci.c
@@ -0,0 +1,2506 @@
+/*-
+ *
+ * Copyright (c) 1997-2006 by Matthew Jacob
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice 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.
+ *
+ * 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.
+ *
+ */
+/*
+ * PCI specific probe and attach routines for Qlogic ISP SCSI adapters.
+ * FreeBSD Version.
+ */
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <sys/module.h>
+#if __FreeBSD_version >= 700000
+#include <sys/linker.h>
+#include <sys/firmware.h>
+#endif
+#include <sys/bus.h>
+#if __FreeBSD_version < 500000
+#include <pci/pcireg.h>
+#include <pci/pcivar.h>
+#include <machine/bus_memio.h>
+#include <machine/bus_pio.h>
+#else
+#include <sys/stdint.h>
+#include <dev/pci/pcireg.h>
+#include <dev/pci/pcivar.h>
+#endif
+#include <machine/bus.h>
+#include <machine/resource.h>
+#include <sys/rman.h>
+#include <sys/malloc.h>
+
+#include <dev/isp/isp_freebsd.h>
+
+#if __FreeBSD_version < 500000
+#define BUS_PROBE_DEFAULT 0
+#endif
+
+static uint16_t isp_pci_rd_reg(ispsoftc_t *, int);
+static void isp_pci_wr_reg(ispsoftc_t *, int, uint16_t);
+static uint16_t isp_pci_rd_reg_1080(ispsoftc_t *, int);
+static void isp_pci_wr_reg_1080(ispsoftc_t *, int, uint16_t);
+static int
+isp_pci_rd_isr(ispsoftc_t *, uint16_t *, uint16_t *, uint16_t *);
+static int
+isp_pci_rd_isr_2300(ispsoftc_t *, uint16_t *, uint16_t *, uint16_t *);
+static int isp_pci_mbxdma(ispsoftc_t *);
+static int
+isp_pci_dmasetup(ispsoftc_t *, XS_T *, ispreq_t *, uint16_t *, uint16_t);
+static void
+isp_pci_dmateardown(ispsoftc_t *, XS_T *, uint16_t);
+
+
+static void isp_pci_reset1(ispsoftc_t *);
+static void isp_pci_dumpregs(ispsoftc_t *, const char *);
+
+static struct ispmdvec mdvec = {
+ isp_pci_rd_isr,
+ isp_pci_rd_reg,
+ isp_pci_wr_reg,
+ isp_pci_mbxdma,
+ isp_pci_dmasetup,
+ isp_pci_dmateardown,
+ NULL,
+ isp_pci_reset1,
+ isp_pci_dumpregs,
+ NULL,
+ BIU_BURST_ENABLE|BIU_PCI_CONF1_FIFO_64
+};
+
+static struct ispmdvec mdvec_1080 = {
+ isp_pci_rd_isr,
+ isp_pci_rd_reg_1080,
+ isp_pci_wr_reg_1080,
+ isp_pci_mbxdma,
+ isp_pci_dmasetup,
+ isp_pci_dmateardown,
+ NULL,
+ isp_pci_reset1,
+ isp_pci_dumpregs,
+ NULL,
+ BIU_BURST_ENABLE|BIU_PCI_CONF1_FIFO_64
+};
+
+static struct ispmdvec mdvec_12160 = {
+ isp_pci_rd_isr,
+ isp_pci_rd_reg_1080,
+ isp_pci_wr_reg_1080,
+ isp_pci_mbxdma,
+ isp_pci_dmasetup,
+ isp_pci_dmateardown,
+ NULL,
+ isp_pci_reset1,
+ isp_pci_dumpregs,
+ NULL,
+ BIU_BURST_ENABLE|BIU_PCI_CONF1_FIFO_64
+};
+
+static struct ispmdvec mdvec_2100 = {
+ isp_pci_rd_isr,
+ isp_pci_rd_reg,
+ isp_pci_wr_reg,
+ isp_pci_mbxdma,
+ isp_pci_dmasetup,
+ isp_pci_dmateardown,
+ NULL,
+ isp_pci_reset1,
+ isp_pci_dumpregs
+};
+
+static struct ispmdvec mdvec_2200 = {
+ isp_pci_rd_isr,
+ isp_pci_rd_reg,
+ isp_pci_wr_reg,
+ isp_pci_mbxdma,
+ isp_pci_dmasetup,
+ isp_pci_dmateardown,
+ NULL,
+ isp_pci_reset1,
+ isp_pci_dumpregs
+};
+
+static struct ispmdvec mdvec_2300 = {
+ isp_pci_rd_isr_2300,
+ isp_pci_rd_reg,
+ isp_pci_wr_reg,
+ isp_pci_mbxdma,
+ isp_pci_dmasetup,
+ isp_pci_dmateardown,
+ NULL,
+ isp_pci_reset1,
+ isp_pci_dumpregs
+};
+
+#ifndef PCIM_CMD_INVEN
+#define PCIM_CMD_INVEN 0x10
+#endif
+#ifndef PCIM_CMD_BUSMASTEREN
+#define PCIM_CMD_BUSMASTEREN 0x0004
+#endif
+#ifndef PCIM_CMD_PERRESPEN
+#define PCIM_CMD_PERRESPEN 0x0040
+#endif
+#ifndef PCIM_CMD_SEREN
+#define PCIM_CMD_SEREN 0x0100
+#endif
+#ifndef PCIM_CMD_INTX_DISABLE
+#define PCIM_CMD_INTX_DISABLE 0x0400
+#endif
+
+#ifndef PCIR_COMMAND
+#define PCIR_COMMAND 0x04
+#endif
+
+#ifndef PCIR_CACHELNSZ
+#define PCIR_CACHELNSZ 0x0c
+#endif
+
+#ifndef PCIR_LATTIMER
+#define PCIR_LATTIMER 0x0d
+#endif
+
+#ifndef PCIR_ROMADDR
+#define PCIR_ROMADDR 0x30
+#endif
+
+#ifndef PCI_VENDOR_QLOGIC
+#define PCI_VENDOR_QLOGIC 0x1077
+#endif
+
+#ifndef PCI_PRODUCT_QLOGIC_ISP1020
+#define PCI_PRODUCT_QLOGIC_ISP1020 0x1020
+#endif
+
+#ifndef PCI_PRODUCT_QLOGIC_ISP1080
+#define PCI_PRODUCT_QLOGIC_ISP1080 0x1080
+#endif
+
+#ifndef PCI_PRODUCT_QLOGIC_ISP10160
+#define PCI_PRODUCT_QLOGIC_ISP10160 0x1016
+#endif
+
+#ifndef PCI_PRODUCT_QLOGIC_ISP12160
+#define PCI_PRODUCT_QLOGIC_ISP12160 0x1216
+#endif
+
+#ifndef PCI_PRODUCT_QLOGIC_ISP1240
+#define PCI_PRODUCT_QLOGIC_ISP1240 0x1240
+#endif
+
+#ifndef PCI_PRODUCT_QLOGIC_ISP1280
+#define PCI_PRODUCT_QLOGIC_ISP1280 0x1280
+#endif
+
+#ifndef PCI_PRODUCT_QLOGIC_ISP2100
+#define PCI_PRODUCT_QLOGIC_ISP2100 0x2100
+#endif
+
+#ifndef PCI_PRODUCT_QLOGIC_ISP2200
+#define PCI_PRODUCT_QLOGIC_ISP2200 0x2200
+#endif
+
+#ifndef PCI_PRODUCT_QLOGIC_ISP2300
+#define PCI_PRODUCT_QLOGIC_ISP2300 0x2300
+#endif
+
+#ifndef PCI_PRODUCT_QLOGIC_ISP2312
+#define PCI_PRODUCT_QLOGIC_ISP2312 0x2312
+#endif
+
+#ifndef PCI_PRODUCT_QLOGIC_ISP2322
+#define PCI_PRODUCT_QLOGIC_ISP2322 0x2322
+#endif
+
+#ifndef PCI_PRODUCT_QLOGIC_ISP2422
+#define PCI_PRODUCT_QLOGIC_ISP2422 0x2422
+#endif
+
+#ifndef PCI_PRODUCT_QLOGIC_ISP6312
+#define PCI_PRODUCT_QLOGIC_ISP6312 0x6312
+#endif
+
+#ifndef PCI_PRODUCT_QLOGIC_ISP6322
+#define PCI_PRODUCT_QLOGIC_ISP6322 0x6322
+#endif
+
+
+#define PCI_QLOGIC_ISP1020 \
+ ((PCI_PRODUCT_QLOGIC_ISP1020 << 16) | PCI_VENDOR_QLOGIC)
+
+#define PCI_QLOGIC_ISP1080 \
+ ((PCI_PRODUCT_QLOGIC_ISP1080 << 16) | PCI_VENDOR_QLOGIC)
+
+#define PCI_QLOGIC_ISP10160 \
+ ((PCI_PRODUCT_QLOGIC_ISP10160 << 16) | PCI_VENDOR_QLOGIC)
+
+#define PCI_QLOGIC_ISP12160 \
+ ((PCI_PRODUCT_QLOGIC_ISP12160 << 16) | PCI_VENDOR_QLOGIC)
+
+#define PCI_QLOGIC_ISP1240 \
+ ((PCI_PRODUCT_QLOGIC_ISP1240 << 16) | PCI_VENDOR_QLOGIC)
+
+#define PCI_QLOGIC_ISP1280 \
+ ((PCI_PRODUCT_QLOGIC_ISP1280 << 16) | PCI_VENDOR_QLOGIC)
+
+#define PCI_QLOGIC_ISP2100 \
+ ((PCI_PRODUCT_QLOGIC_ISP2100 << 16) | PCI_VENDOR_QLOGIC)
+
+#define PCI_QLOGIC_ISP2200 \
+ ((PCI_PRODUCT_QLOGIC_ISP2200 << 16) | PCI_VENDOR_QLOGIC)
+
+#define PCI_QLOGIC_ISP2300 \
+ ((PCI_PRODUCT_QLOGIC_ISP2300 << 16) | PCI_VENDOR_QLOGIC)
+
+#define PCI_QLOGIC_ISP2312 \
+ ((PCI_PRODUCT_QLOGIC_ISP2312 << 16) | PCI_VENDOR_QLOGIC)
+
+#define PCI_QLOGIC_ISP2322 \
+ ((PCI_PRODUCT_QLOGIC_ISP2322 << 16) | PCI_VENDOR_QLOGIC)
+
+#define PCI_QLOGIC_ISP2422 \
+ ((PCI_PRODUCT_QLOGIC_ISP2422 << 16) | PCI_VENDOR_QLOGIC)
+
+#define PCI_QLOGIC_ISP6312 \
+ ((PCI_PRODUCT_QLOGIC_ISP6312 << 16) | PCI_VENDOR_QLOGIC)
+
+#define PCI_QLOGIC_ISP6322 \
+ ((PCI_PRODUCT_QLOGIC_ISP6322 << 16) | PCI_VENDOR_QLOGIC)
+
+/*
+ * Odd case for some AMI raid cards... We need to *not* attach to this.
+ */
+#define AMI_RAID_SUBVENDOR_ID 0x101e
+
+#define IO_MAP_REG 0x10
+#define MEM_MAP_REG 0x14
+
+#define PCI_DFLT_LTNCY 0x40
+#define PCI_DFLT_LNSZ 0x10
+
+static int isp_pci_probe (device_t);
+static int isp_pci_attach (device_t);
+
+
+struct isp_pcisoftc {
+ ispsoftc_t pci_isp;
+ device_t pci_dev;
+ struct resource * pci_reg;
+ bus_space_tag_t pci_st;
+ bus_space_handle_t pci_sh;
+ void * ih;
+ int16_t pci_poff[_NREG_BLKS];
+ bus_dma_tag_t dmat;
+ bus_dmamap_t *dmaps;
+};
+
+static device_method_t isp_pci_methods[] = {
+ /* Device interface */
+ DEVMETHOD(device_probe, isp_pci_probe),
+ DEVMETHOD(device_attach, isp_pci_attach),
+ { 0, 0 }
+};
+static void isp_pci_intr(void *);
+
+static driver_t isp_pci_driver = {
+ "isp", isp_pci_methods, sizeof (struct isp_pcisoftc)
+};
+static devclass_t isp_devclass;
+DRIVER_MODULE(isp, pci, isp_pci_driver, isp_devclass, 0, 0);
+#if __FreeBSD_version >= 700000
+MODULE_DEPEND(isp, ispfw, 1, 1, 1);
+MODULE_DEPEND(isp, firmware, 1, 1, 1);
+#else
+extern ispfwfunc *isp_get_firmware_p;
+#endif
+
+static int
+isp_pci_probe(device_t dev)
+{
+ switch ((pci_get_device(dev) << 16) | (pci_get_vendor(dev))) {
+ case PCI_QLOGIC_ISP1020:
+ device_set_desc(dev, "Qlogic ISP 1020/1040 PCI SCSI Adapter");
+ break;
+ case PCI_QLOGIC_ISP1080:
+ device_set_desc(dev, "Qlogic ISP 1080 PCI SCSI Adapter");
+ break;
+ case PCI_QLOGIC_ISP1240:
+ device_set_desc(dev, "Qlogic ISP 1240 PCI SCSI Adapter");
+ break;
+ case PCI_QLOGIC_ISP1280:
+ device_set_desc(dev, "Qlogic ISP 1280 PCI SCSI Adapter");
+ break;
+ case PCI_QLOGIC_ISP10160:
+ device_set_desc(dev, "Qlogic ISP 10160 PCI SCSI Adapter");
+ break;
+ case PCI_QLOGIC_ISP12160:
+ if (pci_get_subvendor(dev) == AMI_RAID_SUBVENDOR_ID) {
+ return (ENXIO);
+ }
+ device_set_desc(dev, "Qlogic ISP 12160 PCI SCSI Adapter");
+ break;
+ case PCI_QLOGIC_ISP2100:
+ device_set_desc(dev, "Qlogic ISP 2100 PCI FC-AL Adapter");
+ break;
+ case PCI_QLOGIC_ISP2200:
+ device_set_desc(dev, "Qlogic ISP 2200 PCI FC-AL Adapter");
+ break;
+ case PCI_QLOGIC_ISP2300:
+ device_set_desc(dev, "Qlogic ISP 2300 PCI FC-AL Adapter");
+ break;
+ case PCI_QLOGIC_ISP2312:
+ device_set_desc(dev, "Qlogic ISP 2312 PCI FC-AL Adapter");
+ break;
+ case PCI_QLOGIC_ISP2322:
+ device_set_desc(dev, "Qlogic ISP 2322 PCI FC-AL Adapter");
+ break;
+#if 0
+ case PCI_QLOGIC_ISP2422:
+ device_set_desc(dev, "Qlogic ISP 2422 PCI FC-AL Adapter");
+ break;
+#endif
+ case PCI_QLOGIC_ISP6312:
+ device_set_desc(dev, "Qlogic ISP 6312 PCI FC-AL Adapter");
+ break;
+ case PCI_QLOGIC_ISP6322:
+ device_set_desc(dev, "Qlogic ISP 6322 PCI FC-AL Adapter");
+ break;
+ default:
+ return (ENXIO);
+ }
+ if (isp_announced == 0 && bootverbose) {
+ printf("Qlogic ISP Driver, FreeBSD Version %d.%d, "
+ "Core Version %d.%d\n",
+ ISP_PLATFORM_VERSION_MAJOR, ISP_PLATFORM_VERSION_MINOR,
+ ISP_CORE_VERSION_MAJOR, ISP_CORE_VERSION_MINOR);
+ isp_announced++;
+ }
+ /*
+ * XXXX: Here is where we might load the f/w module
+ * XXXX: (or increase a reference count to it).
+ */
+ return (BUS_PROBE_DEFAULT);
+}
+
+#if __FreeBSD_version < 500000
+static void
+isp_get_options(device_t dev, ispsoftc_t *isp)
+{
+ uint64_t wwn;
+ int bitmap, unit;
+
+ unit = device_get_unit(dev);
+ if (getenv_int("isp_disable", &bitmap)) {
+ if (bitmap & (1 << unit)) {
+ isp->isp_osinfo.disabled = 1;
+ return;
+ }
+ }
+
+ if (getenv_int("isp_no_fwload", &bitmap)) {
+ if (bitmap & (1 << unit))
+ isp->isp_confopts |= ISP_CFG_NORELOAD;
+ }
+ if (getenv_int("isp_fwload", &bitmap)) {
+ if (bitmap & (1 << unit))
+ isp->isp_confopts &= ~ISP_CFG_NORELOAD;
+ }
+ if (getenv_int("isp_no_nvram", &bitmap)) {
+ if (bitmap & (1 << unit))
+ isp->isp_confopts |= ISP_CFG_NONVRAM;
+ }
+ if (getenv_int("isp_nvram", &bitmap)) {
+ if (bitmap & (1 << unit))
+ isp->isp_confopts &= ~ISP_CFG_NONVRAM;
+ }
+ if (getenv_int("isp_fcduplex", &bitmap)) {
+ if (bitmap & (1 << unit))
+ isp->isp_confopts |= ISP_CFG_FULL_DUPLEX;
+ }
+ if (getenv_int("isp_no_fcduplex", &bitmap)) {
+ if (bitmap & (1 << unit))
+ isp->isp_confopts &= ~ISP_CFG_FULL_DUPLEX;
+ }
+ if (getenv_int("isp_nport", &bitmap)) {
+ if (bitmap & (1 << unit))
+ isp->isp_confopts |= ISP_CFG_NPORT;
+ }
+
+ /*
+ * Because the resource_*_value functions can neither return
+ * 64 bit integer values, nor can they be directly coerced
+ * to interpret the right hand side of the assignment as
+ * you want them to interpret it, we have to force WWN
+ * hint replacement to specify WWN strings with a leading
+ * 'w' (e..g w50000000aaaa0001). Sigh.
+ */
+ if (getenv_quad("isp_portwwn", &wwn)) {
+ isp->isp_osinfo.default_port_wwn = wwn;
+ isp->isp_confopts |= ISP_CFG_OWNWWPN;
+ }
+ if (isp->isp_osinfo.default_port_wwn == 0) {
+ isp->isp_osinfo.default_port_wwn = 0x400000007F000009ull;
+ }
+
+ if (getenv_quad("isp_nodewwn", &wwn)) {
+ isp->isp_osinfo.default_node_wwn = wwn;
+ isp->isp_confopts |= ISP_CFG_OWNWWNN;
+ }
+ if (isp->isp_osinfo.default_node_wwn == 0) {
+ isp->isp_osinfo.default_node_wwn = 0x400000007F000009ull;
+ }
+
+ bitmap = 0;
+ (void) getenv_int("isp_debug", &bitmap);
+ if (bitmap) {
+ isp->isp_dblev = bitmap;
+ } else {
+ isp->isp_dblev = ISP_LOGWARN|ISP_LOGERR;
+ }
+ if (bootverbose) {
+ isp->isp_dblev |= ISP_LOGCONFIG|ISP_LOGINFO;
+ }
+
+#ifdef ISP_FW_CRASH_DUMP
+ bitmap = 0;
+ if (getenv_int("isp_fw_dump_enable", &bitmap)) {
+ if (bitmap & (1 << unit) {
+ size_t amt = 0;
+ if (IS_2200(isp)) {
+ amt = QLA2200_RISC_IMAGE_DUMP_SIZE;
+ } else if (IS_23XX(isp)) {
+ amt = QLA2300_RISC_IMAGE_DUMP_SIZE;
+ }
+ if (amt) {
+ FCPARAM(isp)->isp_dump_data =
+ malloc(amt, M_DEVBUF, M_WAITOK);
+ memset(FCPARAM(isp)->isp_dump_data, 0, amt);
+ } else {
+ device_printf(dev,
+ "f/w crash dumps not supported for card\n");
+ }
+ }
+ }
+#endif
+ bitmap = 0;
+ if (getenv_int("role", &bitmap)) {
+ isp->isp_role = bitmap;
+ } else {
+ isp->isp_role = ISP_DEFAULT_ROLES;
+ }
+}
+
+static void
+isp_get_pci_options(device_t dev, int *m1, int *m2)
+{
+ int bitmap;
+ int unit = device_get_unit(dev);
+
+ *m1 = PCIM_CMD_MEMEN;
+ *m2 = PCIM_CMD_PORTEN;
+ if (getenv_int("isp_mem_map", &bitmap)) {
+ if (bitmap & (1 << unit)) {
+ *m1 = PCIM_CMD_MEMEN;
+ *m2 = PCIM_CMD_PORTEN;
+ }
+ }
+ bitmap = 0;
+ if (getenv_int("isp_io_map", &bitmap)) {
+ if (bitmap & (1 << unit)) {
+ *m1 = PCIM_CMD_PORTEN;
+ *m2 = PCIM_CMD_MEMEN;
+ }
+ }
+}
+#else
+static void
+isp_get_options(device_t dev, ispsoftc_t *isp)
+{
+ int tval;
+ const char *sptr;
+ /*
+ * Figure out if we're supposed to skip this one.
+ */
+
+ tval = 0;
+ if (resource_int_value(device_get_name(dev), device_get_unit(dev),
+ "disable", &tval) == 0 && tval) {
+ device_printf(dev, "disabled at user request\n");
+ isp->isp_osinfo.disabled = 1;
+ return;
+ }
+
+ tval = -1;
+ if (resource_int_value(device_get_name(dev), device_get_unit(dev),
+ "role", &tval) == 0 && tval != -1) {
+ tval &= (ISP_ROLE_INITIATOR|ISP_ROLE_TARGET);
+ isp->isp_role = tval;
+ device_printf(dev, "setting role to 0x%x\n", isp->isp_role);
+ } else {
+#ifdef ISP_TARGET_MODE
+ isp->isp_role = ISP_ROLE_TARGET;
+#else
+ isp->isp_role = ISP_DEFAULT_ROLES;
+#endif
+ }
+
+ tval = 0;
+ if (resource_int_value(device_get_name(dev), device_get_unit(dev),
+ "fwload_disable", &tval) == 0 && tval != 0) {
+ isp->isp_confopts |= ISP_CFG_NORELOAD;
+ }
+ tval = 0;
+ if (resource_int_value(device_get_name(dev), device_get_unit(dev),
+ "ignore_nvram", &tval) == 0 && tval != 0) {
+ isp->isp_confopts |= ISP_CFG_NONVRAM;
+ }
+ tval = 0;
+ if (resource_int_value(device_get_name(dev), device_get_unit(dev),
+ "fullduplex", &tval) == 0 && tval != 0) {
+ isp->isp_confopts |= ISP_CFG_FULL_DUPLEX;
+ }
+#ifdef ISP_FW_CRASH_DUMP
+ tval = 0;
+ if (resource_int_value(device_get_name(dev), device_get_unit(dev),
+ "fw_dump_enable", &tval) == 0 && tval != 0) {
+ size_t amt = 0;
+ if (IS_2200(isp)) {
+ amt = QLA2200_RISC_IMAGE_DUMP_SIZE;
+ } else if (IS_23XX(isp)) {
+ amt = QLA2300_RISC_IMAGE_DUMP_SIZE;
+ }
+ if (amt) {
+ FCPARAM(isp)->isp_dump_data =
+ malloc(amt, M_DEVBUF, M_WAITOK | M_ZERO);
+ } else {
+ device_printf(dev,
+ "f/w crash dumps not supported for this model\n");
+ }
+ }
+#endif
+
+ sptr = 0;
+ if (resource_string_value(device_get_name(dev), device_get_unit(dev),
+ "topology", (const char **) &sptr) == 0 && sptr != 0) {
+ if (strcmp(sptr, "lport") == 0) {
+ isp->isp_confopts |= ISP_CFG_LPORT;
+ } else if (strcmp(sptr, "nport") == 0) {
+ isp->isp_confopts |= ISP_CFG_NPORT;
+ } else if (strcmp(sptr, "lport-only") == 0) {
+ isp->isp_confopts |= ISP_CFG_LPORT_ONLY;
+ } else if (strcmp(sptr, "nport-only") == 0) {
+ isp->isp_confopts |= ISP_CFG_NPORT_ONLY;
+ }
+ }
+
+ /*
+ * Because the resource_*_value functions can neither return
+ * 64 bit integer values, nor can they be directly coerced
+ * to interpret the right hand side of the assignment as
+ * you want them to interpret it, we have to force WWN
+ * hint replacement to specify WWN strings with a leading
+ * 'w' (e..g w50000000aaaa0001). Sigh.
+ */
+ sptr = 0;
+ tval = resource_string_value(device_get_name(dev), device_get_unit(dev),
+ "portwwn", (const char **) &sptr);
+ if (tval == 0 && sptr != 0 && *sptr++ == 'w') {
+ char *eptr = 0;
+ isp->isp_osinfo.default_port_wwn = strtouq(sptr, &eptr, 16);
+ if (eptr < sptr + 16 || isp->isp_osinfo.default_port_wwn == 0) {
+ device_printf(dev, "mangled portwwn hint '%s'\n", sptr);
+ isp->isp_osinfo.default_port_wwn = 0;
+ } else {
+ isp->isp_confopts |= ISP_CFG_OWNWWPN;
+ }
+ }
+ if (isp->isp_osinfo.default_port_wwn == 0) {
+ isp->isp_osinfo.default_port_wwn = 0x400000007F000009ull;
+ }
+
+ sptr = 0;
+ tval = resource_string_value(device_get_name(dev), device_get_unit(dev),
+ "nodewwn", (const char **) &sptr);
+ if (tval == 0 && sptr != 0 && *sptr++ == 'w') {
+ char *eptr = 0;
+ isp->isp_osinfo.default_node_wwn = strtouq(sptr, &eptr, 16);
+ if (eptr < sptr + 16 || isp->isp_osinfo.default_node_wwn == 0) {
+ device_printf(dev, "mangled nodewwn hint '%s'\n", sptr);
+ isp->isp_osinfo.default_node_wwn = 0;
+ } else {
+ isp->isp_confopts |= ISP_CFG_OWNWWNN;
+ }
+ }
+ if (isp->isp_osinfo.default_node_wwn == 0) {
+ isp->isp_osinfo.default_node_wwn = 0x400000007F000009ull;
+ }
+
+ isp->isp_osinfo.default_id = -1;
+ if (resource_int_value(device_get_name(dev), device_get_unit(dev),
+ "iid", &tval) == 0) {
+ isp->isp_osinfo.default_id = tval;
+ isp->isp_confopts |= ISP_CFG_OWNLOOPID;
+ }
+ if (isp->isp_osinfo.default_id == -1) {
+ if (IS_FC(isp)) {
+ isp->isp_osinfo.default_id = 109;
+ } else {
+ isp->isp_osinfo.default_id = 7;
+ }
+ }
+
+ /*
+ * Set up logging levels.
+ */
+ tval = 0;
+ (void) resource_int_value(device_get_name(dev), device_get_unit(dev),
+ "debug", &tval);
+ if (tval) {
+ isp->isp_dblev = tval;
+ } else {
+ isp->isp_dblev = ISP_LOGWARN|ISP_LOGERR;
+ }
+ if (bootverbose) {
+ isp->isp_dblev |= ISP_LOGCONFIG|ISP_LOGINFO;
+ }
+
+}
+
+static void
+isp_get_pci_options(device_t dev, int *m1, int *m2)
+{
+ int tval;
+ /*
+ * Which we should try first - memory mapping or i/o mapping?
+ *
+ * We used to try memory first followed by i/o on alpha, otherwise
+ * the reverse, but we should just try memory first all the time now.
+ */
+ *m1 = PCIM_CMD_MEMEN;
+ *m2 = PCIM_CMD_PORTEN;
+
+ tval = 0;
+ if (resource_int_value(device_get_name(dev), device_get_unit(dev),
+ "prefer_iomap", &tval) == 0 && tval != 0) {
+ *m1 = PCIM_CMD_PORTEN;
+ *m2 = PCIM_CMD_MEMEN;
+ }
+ tval = 0;
+ if (resource_int_value(device_get_name(dev), device_get_unit(dev),
+ "prefer_memmap", &tval) == 0 && tval != 0) {
+ *m1 = PCIM_CMD_MEMEN;
+ *m2 = PCIM_CMD_PORTEN;
+ }
+}
+#endif
+
+static int
+isp_pci_attach(device_t dev)
+{
+ struct resource *regs, *irq;
+ int rtp, rgd, iqd, m1, m2;
+ uint32_t data, cmd, linesz, psize, basetype;
+ struct isp_pcisoftc *pcs;
+ ispsoftc_t *isp = NULL;
+ struct ispmdvec *mdvp;
+#if __FreeBSD_version >= 500000
+ int locksetup = 0;
+#endif
+
+ pcs = device_get_softc(dev);
+ if (pcs == NULL) {
+ device_printf(dev, "cannot get softc\n");
+ return (ENOMEM);
+ }
+ memset(pcs, 0, sizeof (*pcs));
+ pcs->pci_dev = dev;
+ isp = &pcs->pci_isp;
+
+ /*
+ * Get Generic Options
+ */
+ isp_get_options(dev, isp);
+
+ /*
+ * Check to see if options have us disabled
+ */
+ if (isp->isp_osinfo.disabled) {
+ /*
+ * But return zero to preserve unit numbering
+ */
+ return (0);
+ }
+
+ /*
+ * Get PCI options- which in this case are just mapping preferences.
+ */
+ isp_get_pci_options(dev, &m1, &m2);
+
+
+ linesz = PCI_DFLT_LNSZ;
+ irq = regs = NULL;
+ rgd = rtp = iqd = 0;
+
+ cmd = pci_read_config(dev, PCIR_COMMAND, 2);
+ if (cmd & m1) {
+ rtp = (m1 == PCIM_CMD_MEMEN)? SYS_RES_MEMORY : SYS_RES_IOPORT;
+ rgd = (m1 == PCIM_CMD_MEMEN)? MEM_MAP_REG : IO_MAP_REG;
+ regs = bus_alloc_resource_any(dev, rtp, &rgd, RF_ACTIVE);
+ }
+ if (regs == NULL && (cmd & m2)) {
+ rtp = (m2 == PCIM_CMD_MEMEN)? SYS_RES_MEMORY : SYS_RES_IOPORT;
+ rgd = (m2 == PCIM_CMD_MEMEN)? MEM_MAP_REG : IO_MAP_REG;
+ regs = bus_alloc_resource_any(dev, rtp, &rgd, RF_ACTIVE);
+ }
+ if (regs == NULL) {
+ device_printf(dev, "unable to map any ports\n");
+ goto bad;
+ }
+ if (bootverbose) {
+ device_printf(dev, "using %s space register mapping\n",
+ (rgd == IO_MAP_REG)? "I/O" : "Memory");
+ }
+ pcs->pci_dev = dev;
+ pcs->pci_reg = regs;
+ pcs->pci_st = rman_get_bustag(regs);
+ pcs->pci_sh = rman_get_bushandle(regs);
+
+ pcs->pci_poff[BIU_BLOCK >> _BLK_REG_SHFT] = BIU_REGS_OFF;
+ pcs->pci_poff[MBOX_BLOCK >> _BLK_REG_SHFT] = PCI_MBOX_REGS_OFF;
+ pcs->pci_poff[SXP_BLOCK >> _BLK_REG_SHFT] = PCI_SXP_REGS_OFF;
+ pcs->pci_poff[RISC_BLOCK >> _BLK_REG_SHFT] = PCI_RISC_REGS_OFF;
+ pcs->pci_poff[DMA_BLOCK >> _BLK_REG_SHFT] = DMA_REGS_OFF;
+ mdvp = &mdvec;
+ basetype = ISP_HA_SCSI_UNKNOWN;
+ psize = sizeof (sdparam);
+ if (pci_get_devid(dev) == PCI_QLOGIC_ISP1020) {
+ mdvp = &mdvec;
+ basetype = ISP_HA_SCSI_UNKNOWN;
+ psize = sizeof (sdparam);
+ }
+ if (pci_get_devid(dev) == PCI_QLOGIC_ISP1080) {
+ mdvp = &mdvec_1080;
+ basetype = ISP_HA_SCSI_1080;
+ psize = sizeof (sdparam);
+ pcs->pci_poff[DMA_BLOCK >> _BLK_REG_SHFT] =
+ ISP1080_DMA_REGS_OFF;
+ }
+ if (pci_get_devid(dev) == PCI_QLOGIC_ISP1240) {
+ mdvp = &mdvec_1080;
+ basetype = ISP_HA_SCSI_1240;
+ psize = 2 * sizeof (sdparam);
+ pcs->pci_poff[DMA_BLOCK >> _BLK_REG_SHFT] =
+ ISP1080_DMA_REGS_OFF;
+ }
+ if (pci_get_devid(dev) == PCI_QLOGIC_ISP1280) {
+ mdvp = &mdvec_1080;
+ basetype = ISP_HA_SCSI_1280;
+ psize = 2 * sizeof (sdparam);
+ pcs->pci_poff[DMA_BLOCK >> _BLK_REG_SHFT] =
+ ISP1080_DMA_REGS_OFF;
+ }
+ if (pci_get_devid(dev) == PCI_QLOGIC_ISP10160) {
+ mdvp = &mdvec_12160;
+ basetype = ISP_HA_SCSI_10160;
+ psize = sizeof (sdparam);
+ pcs->pci_poff[DMA_BLOCK >> _BLK_REG_SHFT] =
+ ISP1080_DMA_REGS_OFF;
+ }
+ if (pci_get_devid(dev) == PCI_QLOGIC_ISP12160) {
+ mdvp = &mdvec_12160;
+ basetype = ISP_HA_SCSI_12160;
+ psize = 2 * sizeof (sdparam);
+ pcs->pci_poff[DMA_BLOCK >> _BLK_REG_SHFT] =
+ ISP1080_DMA_REGS_OFF;
+ }
+ if (pci_get_devid(dev) == PCI_QLOGIC_ISP2100) {
+ mdvp = &mdvec_2100;
+ basetype = ISP_HA_FC_2100;
+ psize = sizeof (fcparam);
+ pcs->pci_poff[MBOX_BLOCK >> _BLK_REG_SHFT] =
+ PCI_MBOX_REGS2100_OFF;
+ if (pci_get_revid(dev) < 3) {
+ /*
+ * XXX: Need to get the actual revision
+ * XXX: number of the 2100 FB. At any rate,
+ * XXX: lower cache line size for early revision
+ * XXX; boards.
+ */
+ linesz = 1;
+ }
+ }
+ if (pci_get_devid(dev) == PCI_QLOGIC_ISP2200) {
+ mdvp = &mdvec_2200;
+ basetype = ISP_HA_FC_2200;
+ psize = sizeof (fcparam);
+ pcs->pci_poff[MBOX_BLOCK >> _BLK_REG_SHFT] =
+ PCI_MBOX_REGS2100_OFF;
+ }
+ if (pci_get_devid(dev) == PCI_QLOGIC_ISP2300) {
+ mdvp = &mdvec_2300;
+ basetype = ISP_HA_FC_2300;
+ psize = sizeof (fcparam);
+ pcs->pci_poff[MBOX_BLOCK >> _BLK_REG_SHFT] =
+ PCI_MBOX_REGS2300_OFF;
+ }
+ if (pci_get_devid(dev) == PCI_QLOGIC_ISP2312 ||
+ pci_get_devid(dev) == PCI_QLOGIC_ISP6312) {
+ mdvp = &mdvec_2300;
+ basetype = ISP_HA_FC_2312;
+ psize = sizeof (fcparam);
+ pcs->pci_poff[MBOX_BLOCK >> _BLK_REG_SHFT] =
+ PCI_MBOX_REGS2300_OFF;
+ }
+ if (pci_get_devid(dev) == PCI_QLOGIC_ISP2322 ||
+ pci_get_devid(dev) == PCI_QLOGIC_ISP6322) {
+ mdvp = &mdvec_2300;
+ basetype = ISP_HA_FC_2322;
+ psize = sizeof (fcparam);
+ pcs->pci_poff[MBOX_BLOCK >> _BLK_REG_SHFT] =
+ PCI_MBOX_REGS2300_OFF;
+ }
+ if (pci_get_devid(dev) == PCI_QLOGIC_ISP2422) {
+ mdvp = &mdvec_2300;
+ basetype = ISP_HA_FC_2422;
+ psize = sizeof (fcparam);
+ pcs->pci_poff[MBOX_BLOCK >> _BLK_REG_SHFT] =
+ PCI_MBOX_REGS2300_OFF;
+ }
+ isp = &pcs->pci_isp;
+ isp->isp_param = malloc(psize, M_DEVBUF, M_NOWAIT | M_ZERO);
+ if (isp->isp_param == NULL) {
+ device_printf(dev, "cannot allocate parameter data\n");
+ goto bad;
+ }
+ isp->isp_mdvec = mdvp;
+ isp->isp_type = basetype;
+ isp->isp_revision = pci_get_revid(dev);
+ isp->isp_dev = dev;
+
+#if __FreeBSD_version >= 700000
+ /*
+ * Try and find firmware for this device.
+ */
+ {
+ char fwname[32];
+ unsigned int did = pci_get_device(dev);
+
+ /*
+ * Map a few pci ids to fw names
+ */
+ switch (did) {
+ case PCI_PRODUCT_QLOGIC_ISP1020:
+ did = 0x1040;
+ break;
+ case PCI_PRODUCT_QLOGIC_ISP1240:
+ did = 0x1080;
+ break;
+ case PCI_PRODUCT_QLOGIC_ISP10160:
+ case PCI_PRODUCT_QLOGIC_ISP12160:
+ did = 0x12160;
+ break;
+ case PCI_PRODUCT_QLOGIC_ISP6312:
+ case PCI_PRODUCT_QLOGIC_ISP2312:
+ did = 0x2300;
+ break;
+ case PCI_PRODUCT_QLOGIC_ISP6322:
+ did = 0x2322;
+ break;
+ default:
+ break;
+ }
+
+ isp->isp_osinfo.fw = NULL;
+ if (isp->isp_role & ISP_ROLE_TARGET) {
+ snprintf(fwname, sizeof (fwname), "isp_%04x_it", did);
+ isp->isp_osinfo.fw = firmware_get(fwname);
+ }
+ if (isp->isp_osinfo.fw == NULL) {
+ snprintf(fwname, sizeof (fwname), "isp_%04x", did);
+ isp->isp_osinfo.fw = firmware_get(fwname);
+ }
+ if (isp->isp_osinfo.fw != NULL) {
+ union {
+ const void *fred;
+ uint16_t *bob;
+ } u;
+ u.fred = isp->isp_osinfo.fw->data;
+ isp->isp_mdvec->dv_ispfw = u.bob;
+ }
+ }
+#else
+ if (isp_get_firmware_p) {
+ int device = (int) pci_get_device(dev);
+#ifdef ISP_TARGET_MODE
+ (*isp_get_firmware_p)(0, 1, device, &mdvp->dv_ispfw);
+#else
+ (*isp_get_firmware_p)(0, 0, device, &mdvp->dv_ispfw);
+#endif
+ }
+#endif
+
+ /*
+ * Make sure that SERR, PERR, WRITE INVALIDATE and BUSMASTER
+ * are set.
+ */
+ cmd |= PCIM_CMD_SEREN | PCIM_CMD_PERRESPEN |
+ PCIM_CMD_BUSMASTEREN | PCIM_CMD_INVEN;
+
+ if (IS_2300(isp)) { /* per QLogic errata */
+ cmd &= ~PCIM_CMD_INVEN;
+ }
+
+ if (IS_23XX(isp)) {
+ /*
+ * Can't tell if ROM will hang on 'ABOUT FIRMWARE' command.
+ */
+ isp->isp_touched = 1;
+
+ }
+
+ if (IS_2322(isp) || pci_get_devid(dev) == PCI_QLOGIC_ISP6312) {
+ cmd &= ~PCIM_CMD_INTX_DISABLE;
+ }
+
+ pci_write_config(dev, PCIR_COMMAND, cmd, 2);
+
+ /*
+ * Make sure the Cache Line Size register is set sensibly.
+ */
+ data = pci_read_config(dev, PCIR_CACHELNSZ, 1);
+ if (data != linesz) {
+ data = PCI_DFLT_LNSZ;
+ isp_prt(isp, ISP_LOGCONFIG, "set PCI line size to %d", data);
+ pci_write_config(dev, PCIR_CACHELNSZ, data, 1);
+ }
+
+ /*
+ * Make sure the Latency Timer is sane.
+ */
+ data = pci_read_config(dev, PCIR_LATTIMER, 1);
+ if (data < PCI_DFLT_LTNCY) {
+ data = PCI_DFLT_LTNCY;
+ isp_prt(isp, ISP_LOGCONFIG, "set PCI latency to %d", data);
+ pci_write_config(dev, PCIR_LATTIMER, data, 1);
+ }
+
+ /*
+ * Make sure we've disabled the ROM.
+ */
+ data = pci_read_config(dev, PCIR_ROMADDR, 4);
+ data &= ~1;
+ pci_write_config(dev, PCIR_ROMADDR, data, 4);
+
+ iqd = 0;
+ irq = bus_alloc_resource_any(dev, SYS_RES_IRQ, &iqd,
+ RF_ACTIVE | RF_SHAREABLE);
+ if (irq == NULL) {
+ device_printf(dev, "could not allocate interrupt\n");
+ goto bad;
+ }
+
+#if __FreeBSD_version >= 500000
+ /* Make sure the lock is set up. */
+ mtx_init(&isp->isp_osinfo.lock, "isp", NULL, MTX_DEF);
+ locksetup++;
+#endif
+
+ if (bus_setup_intr(dev, irq, ISP_IFLAGS, isp_pci_intr, isp, &pcs->ih)) {
+ device_printf(dev, "could not setup interrupt\n");
+ goto bad;
+ }
+
+ /*
+ * Last minute checks...
+ */
+ if (IS_23XX(isp)) {
+ isp->isp_port = pci_get_function(dev);
+ }
+
+ /*
+ * Make sure we're in reset state.
+ */
+ ISP_LOCK(isp);
+ isp_reset(isp);
+ if (isp->isp_state != ISP_RESETSTATE) {
+ ISP_UNLOCK(isp);
+ goto bad;
+ }
+ isp_init(isp);
+ if (isp->isp_role != ISP_ROLE_NONE && isp->isp_state != ISP_INITSTATE) {
+ isp_uninit(isp);
+ ISP_UNLOCK(isp);
+ goto bad;
+ }
+ isp_attach(isp);
+ if (isp->isp_role != ISP_ROLE_NONE && isp->isp_state != ISP_RUNSTATE) {
+ isp_uninit(isp);
+ ISP_UNLOCK(isp);
+ goto bad;
+ }
+ /*
+ * XXXX: Here is where we might unload the f/w module
+ * XXXX: (or decrease the reference count to it).
+ */
+ ISP_UNLOCK(isp);
+ return (0);
+
+bad:
+
+ if (pcs && pcs->ih) {
+ (void) bus_teardown_intr(dev, irq, pcs->ih);
+ }
+
+#if __FreeBSD_version >= 500000
+ if (locksetup && isp) {
+ mtx_destroy(&isp->isp_osinfo.lock);
+ }
+#endif
+
+ if (irq) {
+ (void) bus_release_resource(dev, SYS_RES_IRQ, iqd, irq);
+ }
+
+
+ if (regs) {
+ (void) bus_release_resource(dev, rtp, rgd, regs);
+ }
+
+ if (pcs) {
+ if (pcs->pci_isp.isp_param) {
+#ifdef ISP_FW_CRASH_DUMP
+ if (IS_FC(isp) && FCPARAM(isp)->isp_dump_data) {
+ free(FCPARAM(isp)->isp_dump_data, M_DEVBUF);
+ }
+#endif
+ free(pcs->pci_isp.isp_param, M_DEVBUF);
+ }
+ }
+
+ /*
+ * XXXX: Here is where we might unload the f/w module
+ * XXXX: (or decrease the reference count to it).
+ */
+ return (ENXIO);
+}
+
+static void
+isp_pci_intr(void *arg)
+{
+ ispsoftc_t *isp = arg;
+ uint16_t isr, sema, mbox;
+
+ ISP_LOCK(isp);
+ isp->isp_intcnt++;
+ if (ISP_READ_ISR(isp, &isr, &sema, &mbox) == 0) {
+ isp->isp_intbogus++;
+ } else {
+ int iok = isp->isp_osinfo.intsok;
+ isp->isp_osinfo.intsok = 0;
+ isp_intr(isp, isr, sema, mbox);
+ isp->isp_osinfo.intsok = iok;
+ }
+ ISP_UNLOCK(isp);
+}
+
+
+#define IspVirt2Off(a, x) \
+ (((struct isp_pcisoftc *)a)->pci_poff[((x) & _BLK_REG_MASK) >> \
+ _BLK_REG_SHFT] + ((x) & 0xfff))
+
+#define BXR2(pcs, off) \
+ bus_space_read_2(pcs->pci_st, pcs->pci_sh, off)
+#define BXW2(pcs, off, v) \
+ bus_space_write_2(pcs->pci_st, pcs->pci_sh, off, v)
+
+
+static __inline int
+isp_pci_rd_debounced(ispsoftc_t *isp, int off, uint16_t *rp)
+{
+ struct isp_pcisoftc *pcs = (struct isp_pcisoftc *) isp;
+ uint16_t val0, val1;
+ int i = 0;
+
+ do {
+ val0 = BXR2(pcs, IspVirt2Off(isp, off));
+ val1 = BXR2(pcs, IspVirt2Off(isp, off));
+ } while (val0 != val1 && ++i < 1000);
+ if (val0 != val1) {
+ return (1);
+ }
+ *rp = val0;
+ return (0);
+}
+
+static int
+isp_pci_rd_isr(ispsoftc_t *isp, uint16_t *isrp,
+ uint16_t *semap, uint16_t *mbp)
+{
+ struct isp_pcisoftc *pcs = (struct isp_pcisoftc *) isp;
+ uint16_t isr, sema;
+
+ if (IS_2100(isp)) {
+ if (isp_pci_rd_debounced(isp, BIU_ISR, &isr)) {
+ return (0);
+ }
+ if (isp_pci_rd_debounced(isp, BIU_SEMA, &sema)) {
+ return (0);
+ }
+ } else {
+ isr = BXR2(pcs, IspVirt2Off(isp, BIU_ISR));
+ sema = BXR2(pcs, IspVirt2Off(isp, BIU_SEMA));
+ }
+ isp_prt(isp, ISP_LOGDEBUG3, "ISR 0x%x SEMA 0x%x", isr, sema);
+ isr &= INT_PENDING_MASK(isp);
+ sema &= BIU_SEMA_LOCK;
+ if (isr == 0 && sema == 0) {
+ return (0);
+ }
+ *isrp = isr;
+ if ((*semap = sema) != 0) {
+ if (IS_2100(isp)) {
+ if (isp_pci_rd_debounced(isp, OUTMAILBOX0, mbp)) {
+ return (0);
+ }
+ } else {
+ *mbp = BXR2(pcs, IspVirt2Off(isp, OUTMAILBOX0));
+ }
+ }
+ return (1);
+}
+
+static int
+isp_pci_rd_isr_2300(ispsoftc_t *isp, uint16_t *isrp,
+ uint16_t *semap, uint16_t *mbox0p)
+{
+ struct isp_pcisoftc *pcs = (struct isp_pcisoftc *) isp;
+ uint16_t hccr;
+ uint32_t r2hisr;
+
+ if (!(BXR2(pcs, IspVirt2Off(isp, BIU_ISR) & BIU2100_ISR_RISC_INT))) {
+ *isrp = 0;
+ return (0);
+ }
+ r2hisr = bus_space_read_4(pcs->pci_st, pcs->pci_sh,
+ IspVirt2Off(pcs, BIU_R2HSTSLO));
+ isp_prt(isp, ISP_LOGDEBUG3, "RISC2HOST ISR 0x%x", r2hisr);
+ if ((r2hisr & BIU_R2HST_INTR) == 0) {
+ *isrp = 0;
+ return (0);
+ }
+ switch (r2hisr & BIU_R2HST_ISTAT_MASK) {
+ case ISPR2HST_ROM_MBX_OK:
+ case ISPR2HST_ROM_MBX_FAIL:
+ case ISPR2HST_MBX_OK:
+ case ISPR2HST_MBX_FAIL:
+ case ISPR2HST_ASYNC_EVENT:
+ *isrp = r2hisr & 0xffff;
+ *mbox0p = (r2hisr >> 16);
+ *semap = 1;
+ return (1);
+ case ISPR2HST_RIO_16:
+ *isrp = r2hisr & 0xffff;
+ *mbox0p = ASYNC_RIO1;
+ *semap = 1;
+ return (1);
+ case ISPR2HST_FPOST:
+ *isrp = r2hisr & 0xffff;
+ *mbox0p = ASYNC_CMD_CMPLT;
+ *semap = 1;
+ return (1);
+ case ISPR2HST_FPOST_CTIO:
+ *isrp = r2hisr & 0xffff;
+ *mbox0p = ASYNC_CTIO_DONE;
+ *semap = 1;
+ return (1);
+ case ISPR2HST_RSPQ_UPDATE:
+ *isrp = r2hisr & 0xffff;
+ *mbox0p = 0;
+ *semap = 0;
+ return (1);
+ default:
+ hccr = ISP_READ(isp, HCCR);
+ if (hccr & HCCR_PAUSE) {
+ ISP_WRITE(isp, HCCR, HCCR_RESET);
+ isp_prt(isp, ISP_LOGERR,
+ "RISC paused at interrupt (%x->%x\n", hccr,
+ ISP_READ(isp, HCCR));
+ } else {
+ isp_prt(isp, ISP_LOGERR, "unknown interrerupt 0x%x\n",
+ r2hisr);
+ }
+ return (0);
+ }
+}
+
+static uint16_t
+isp_pci_rd_reg(ispsoftc_t *isp, int regoff)
+{
+ uint16_t rv;
+ struct isp_pcisoftc *pcs = (struct isp_pcisoftc *) isp;
+ int oldconf = 0;
+
+ if ((regoff & _BLK_REG_MASK) == SXP_BLOCK) {
+ /*
+ * We will assume that someone has paused the RISC processor.
+ */
+ oldconf = BXR2(pcs, IspVirt2Off(isp, BIU_CONF1));
+ BXW2(pcs, IspVirt2Off(isp, BIU_CONF1),
+ oldconf | BIU_PCI_CONF1_SXP);
+ }
+ rv = BXR2(pcs, IspVirt2Off(isp, regoff));
+ if ((regoff & _BLK_REG_MASK) == SXP_BLOCK) {
+ BXW2(pcs, IspVirt2Off(isp, BIU_CONF1), oldconf);
+ }
+ return (rv);
+}
+
+static void
+isp_pci_wr_reg(ispsoftc_t *isp, int regoff, uint16_t val)
+{
+ struct isp_pcisoftc *pcs = (struct isp_pcisoftc *) isp;
+ int oldconf = 0;
+
+ if ((regoff & _BLK_REG_MASK) == SXP_BLOCK) {
+ /*
+ * We will assume that someone has paused the RISC processor.
+ */
+ oldconf = BXR2(pcs, IspVirt2Off(isp, BIU_CONF1));
+ BXW2(pcs, IspVirt2Off(isp, BIU_CONF1),
+ oldconf | BIU_PCI_CONF1_SXP);
+ }
+ BXW2(pcs, IspVirt2Off(isp, regoff), val);
+ if ((regoff & _BLK_REG_MASK) == SXP_BLOCK) {
+ BXW2(pcs, IspVirt2Off(isp, BIU_CONF1), oldconf);
+ }
+}
+
+static uint16_t
+isp_pci_rd_reg_1080(ispsoftc_t *isp, int regoff)
+{
+ uint16_t rv, oc = 0;
+ struct isp_pcisoftc *pcs = (struct isp_pcisoftc *) isp;
+
+ if ((regoff & _BLK_REG_MASK) == SXP_BLOCK ||
+ (regoff & _BLK_REG_MASK) == (SXP_BLOCK|SXP_BANK1_SELECT)) {
+ uint16_t tc;
+ /*
+ * We will assume that someone has paused the RISC processor.
+ */
+ oc = BXR2(pcs, IspVirt2Off(isp, BIU_CONF1));
+ tc = oc & ~BIU_PCI1080_CONF1_DMA;
+ if (regoff & SXP_BANK1_SELECT)
+ tc |= BIU_PCI1080_CONF1_SXP1;
+ else
+ tc |= BIU_PCI1080_CONF1_SXP0;
+ BXW2(pcs, IspVirt2Off(isp, BIU_CONF1), tc);
+ } else if ((regoff & _BLK_REG_MASK) == DMA_BLOCK) {
+ oc = BXR2(pcs, IspVirt2Off(isp, BIU_CONF1));
+ BXW2(pcs, IspVirt2Off(isp, BIU_CONF1),
+ oc | BIU_PCI1080_CONF1_DMA);
+ }
+ rv = BXR2(pcs, IspVirt2Off(isp, regoff));
+ if (oc) {
+ BXW2(pcs, IspVirt2Off(isp, BIU_CONF1), oc);
+ }
+ return (rv);
+}
+
+static void
+isp_pci_wr_reg_1080(ispsoftc_t *isp, int regoff, uint16_t val)
+{
+ struct isp_pcisoftc *pcs = (struct isp_pcisoftc *) isp;
+ int oc = 0;
+
+ if ((regoff & _BLK_REG_MASK) == SXP_BLOCK ||
+ (regoff & _BLK_REG_MASK) == (SXP_BLOCK|SXP_BANK1_SELECT)) {
+ uint16_t tc;
+ /*
+ * We will assume that someone has paused the RISC processor.
+ */
+ oc = BXR2(pcs, IspVirt2Off(isp, BIU_CONF1));
+ tc = oc & ~BIU_PCI1080_CONF1_DMA;
+ if (regoff & SXP_BANK1_SELECT)
+ tc |= BIU_PCI1080_CONF1_SXP1;
+ else
+ tc |= BIU_PCI1080_CONF1_SXP0;
+ BXW2(pcs, IspVirt2Off(isp, BIU_CONF1), tc);
+ } else if ((regoff & _BLK_REG_MASK) == DMA_BLOCK) {
+ oc = BXR2(pcs, IspVirt2Off(isp, BIU_CONF1));
+ BXW2(pcs, IspVirt2Off(isp, BIU_CONF1),
+ oc | BIU_PCI1080_CONF1_DMA);
+ }
+ BXW2(pcs, IspVirt2Off(isp, regoff), val);
+ if (oc) {
+ BXW2(pcs, IspVirt2Off(isp, BIU_CONF1), oc);
+ }
+}
+
+
+struct imush {
+ ispsoftc_t *isp;
+ int error;
+};
+
+static void imc(void *, bus_dma_segment_t *, int, int);
+
+static void
+imc(void *arg, bus_dma_segment_t *segs, int nseg, int error)
+{
+ struct imush *imushp = (struct imush *) arg;
+ if (error) {
+ imushp->error = error;
+ } else {
+ ispsoftc_t *isp =imushp->isp;
+ bus_addr_t addr = segs->ds_addr;
+
+ isp->isp_rquest_dma = addr;
+ addr += ISP_QUEUE_SIZE(RQUEST_QUEUE_LEN(isp));
+ isp->isp_result_dma = addr;
+ if (IS_FC(isp)) {
+ addr += ISP_QUEUE_SIZE(RESULT_QUEUE_LEN(isp));
+ FCPARAM(isp)->isp_scdma = addr;
+ }
+ }
+}
+
+/*
+ * Should be BUS_SPACE_MAXSIZE, but MAXPHYS is larger than BUS_SPACE_MAXSIZE
+ */
+#define ISP_NSEGS ((MAXPHYS / PAGE_SIZE) + 1)
+
+#if __FreeBSD_version < 500000
+#define isp_dma_tag_create bus_dma_tag_create
+#else
+#define isp_dma_tag_create(a, b, c, d, e, f, g, h, i, j, k, z) \
+ bus_dma_tag_create(a, b, c, d, e, f, g, h, i, j, k, \
+ busdma_lock_mutex, &Giant, z)
+#endif
+
+static int
+isp_pci_mbxdma(ispsoftc_t *isp)
+{
+ struct isp_pcisoftc *pcs = (struct isp_pcisoftc *)isp;
+ caddr_t base;
+ uint32_t len;
+ int i, error, ns;
+ bus_size_t slim; /* segment size */
+ bus_addr_t llim; /* low limit of unavailable dma */
+ bus_addr_t hlim; /* high limit of unavailable dma */
+ struct imush im;
+
+ /*
+ * Already been here? If so, leave...
+ */
+ if (isp->isp_rquest) {
+ return (0);
+ }
+
+ hlim = BUS_SPACE_MAXADDR;
+ if (IS_ULTRA2(isp) || IS_FC(isp) || IS_1240(isp)) {
+ slim = (bus_size_t) (1ULL << 32);
+ llim = BUS_SPACE_MAXADDR;
+ } else {
+ llim = BUS_SPACE_MAXADDR_32BIT;
+ slim = (1 << 24);
+ }
+
+ /*
+ * XXX: We don't really support 64 bit target mode for parallel scsi yet
+ */
+#ifdef ISP_TARGET_MODE
+ if (IS_SCSI(isp) && sizeof (bus_addr_t) > 4) {
+ isp_prt(isp, ISP_LOGERR, "we cannot do DAC for SPI cards yet");
+ return (1);
+ }
+#endif
+
+ ISP_UNLOCK(isp);
+ if (isp_dma_tag_create(NULL, 1, slim, llim, hlim,
+ NULL, NULL, BUS_SPACE_MAXSIZE, ISP_NSEGS, slim, 0, &pcs->dmat)) {
+ isp_prt(isp, ISP_LOGERR, "could not create master dma tag");
+ ISP_LOCK(isp);
+ return (1);
+ }
+
+
+ len = sizeof (XS_T **) * isp->isp_maxcmds;
+ isp->isp_xflist = (XS_T **) malloc(len, M_DEVBUF, M_WAITOK | M_ZERO);
+ if (isp->isp_xflist == NULL) {
+ isp_prt(isp, ISP_LOGERR, "cannot alloc xflist array");
+ ISP_LOCK(isp);
+ return (1);
+ }
+#ifdef ISP_TARGET_MODE
+ len = sizeof (void **) * isp->isp_maxcmds;
+ isp->isp_tgtlist = (void **) malloc(len, M_DEVBUF, M_WAITOK | M_ZERO);
+ if (isp->isp_tgtlist == NULL) {
+ isp_prt(isp, ISP_LOGERR, "cannot alloc tgtlist array");
+ ISP_LOCK(isp);
+ return (1);
+ }
+#endif
+ len = sizeof (bus_dmamap_t) * isp->isp_maxcmds;
+ pcs->dmaps = (bus_dmamap_t *) malloc(len, M_DEVBUF, M_WAITOK);
+ if (pcs->dmaps == NULL) {
+ isp_prt(isp, ISP_LOGERR, "can't alloc dma map storage");
+ free(isp->isp_xflist, M_DEVBUF);
+#ifdef ISP_TARGET_MODE
+ free(isp->isp_tgtlist, M_DEVBUF);
+#endif
+ ISP_LOCK(isp);
+ return (1);
+ }
+
+ /*
+ * Allocate and map the request, result queues, plus FC scratch area.
+ */
+ len = ISP_QUEUE_SIZE(RQUEST_QUEUE_LEN(isp));
+ len += ISP_QUEUE_SIZE(RESULT_QUEUE_LEN(isp));
+ if (IS_FC(isp)) {
+ len += ISP2100_SCRLEN;
+ }
+
+ ns = (len / PAGE_SIZE) + 1;
+ /*
+ * Create a tag for the control spaces- force it to within 32 bits.
+ */
+ if (isp_dma_tag_create(pcs->dmat, QENTRY_LEN, slim,
+ BUS_SPACE_MAXADDR_32BIT, BUS_SPACE_MAXADDR,
+ NULL, NULL, len, ns, slim, 0, &isp->isp_cdmat)) {
+ isp_prt(isp, ISP_LOGERR,
+ "cannot create a dma tag for control spaces");
+ free(pcs->dmaps, M_DEVBUF);
+ free(isp->isp_xflist, M_DEVBUF);
+#ifdef ISP_TARGET_MODE
+ free(isp->isp_tgtlist, M_DEVBUF);
+#endif
+ ISP_LOCK(isp);
+ return (1);
+ }
+
+ if (bus_dmamem_alloc(isp->isp_cdmat, (void **)&base, BUS_DMA_NOWAIT,
+ &isp->isp_cdmap) != 0) {
+ isp_prt(isp, ISP_LOGERR,
+ "cannot allocate %d bytes of CCB memory", len);
+ bus_dma_tag_destroy(isp->isp_cdmat);
+ free(isp->isp_xflist, M_DEVBUF);
+#ifdef ISP_TARGET_MODE
+ free(isp->isp_tgtlist, M_DEVBUF);
+#endif
+ free(pcs->dmaps, M_DEVBUF);
+ ISP_LOCK(isp);
+ return (1);
+ }
+
+ for (i = 0; i < isp->isp_maxcmds; i++) {
+ error = bus_dmamap_create(pcs->dmat, 0, &pcs->dmaps[i]);
+ if (error) {
+ isp_prt(isp, ISP_LOGERR,
+ "error %d creating per-cmd DMA maps", error);
+ while (--i >= 0) {
+ bus_dmamap_destroy(pcs->dmat, pcs->dmaps[i]);
+ }
+ goto bad;
+ }
+ }
+
+ im.isp = isp;
+ im.error = 0;
+ bus_dmamap_load(isp->isp_cdmat, isp->isp_cdmap, base, len, imc, &im, 0);
+ if (im.error) {
+ isp_prt(isp, ISP_LOGERR,
+ "error %d loading dma map for control areas", im.error);
+ goto bad;
+ }
+
+ isp->isp_rquest = base;
+ base += ISP_QUEUE_SIZE(RQUEST_QUEUE_LEN(isp));
+ isp->isp_result = base;
+ if (IS_FC(isp)) {
+ base += ISP_QUEUE_SIZE(RESULT_QUEUE_LEN(isp));
+ FCPARAM(isp)->isp_scratch = base;
+ }
+ ISP_LOCK(isp);
+ return (0);
+
+bad:
+ bus_dmamem_free(isp->isp_cdmat, base, isp->isp_cdmap);
+ bus_dma_tag_destroy(isp->isp_cdmat);
+ free(isp->isp_xflist, M_DEVBUF);
+#ifdef ISP_TARGET_MODE
+ free(isp->isp_tgtlist, M_DEVBUF);
+#endif
+ free(pcs->dmaps, M_DEVBUF);
+ ISP_LOCK(isp);
+ isp->isp_rquest = NULL;
+ return (1);
+}
+
+typedef struct {
+ ispsoftc_t *isp;
+ void *cmd_token;
+ void *rq;
+ uint16_t *nxtip;
+ uint16_t optr;
+ int error;
+} mush_t;
+
+#define MUSHERR_NOQENTRIES -2
+
+#ifdef ISP_TARGET_MODE
+/*
+ * We need to handle DMA for target mode differently from initiator mode.
+ *
+ * DMA mapping and construction and submission of CTIO Request Entries
+ * and rendevous for completion are very tightly coupled because we start
+ * out by knowing (per platform) how much data we have to move, but we
+ * don't know, up front, how many DMA mapping segments will have to be used
+ * cover that data, so we don't know how many CTIO Request Entries we
+ * will end up using. Further, for performance reasons we may want to
+ * (on the last CTIO for Fibre Channel), send status too (if all went well).
+ *
+ * The standard vector still goes through isp_pci_dmasetup, but the callback
+ * for the DMA mapping routines comes here instead with the whole transfer
+ * mapped and a pointer to a partially filled in already allocated request
+ * queue entry. We finish the job.
+ */
+static void tdma_mk(void *, bus_dma_segment_t *, int, int);
+static void tdma_mkfc(void *, bus_dma_segment_t *, int, int);
+
+#define STATUS_WITH_DATA 1
+
+static void
+tdma_mk(void *arg, bus_dma_segment_t *dm_segs, int nseg, int error)
+{
+ mush_t *mp;
+ struct ccb_scsiio *csio;
+ ispsoftc_t *isp;
+ struct isp_pcisoftc *pcs;
+ bus_dmamap_t *dp;
+ ct_entry_t *cto, *qe;
+ uint8_t scsi_status;
+ uint16_t curi, nxti, handle;
+ uint32_t sflags;
+ int32_t resid;
+ int nth_ctio, nctios, send_status;
+
+ mp = (mush_t *) arg;
+ if (error) {
+ mp->error = error;
+ return;
+ }
+
+ isp = mp->isp;
+ csio = mp->cmd_token;
+ cto = mp->rq;
+ curi = isp->isp_reqidx;
+ qe = (ct_entry_t *) ISP_QUEUE_ENTRY(isp->isp_rquest, curi);
+
+ cto->ct_xfrlen = 0;
+ cto->ct_seg_count = 0;
+ cto->ct_header.rqs_entry_count = 1;
+ MEMZERO(cto->ct_dataseg, sizeof(cto->ct_dataseg));
+
+ if (nseg == 0) {
+ cto->ct_header.rqs_seqno = 1;
+ isp_prt(isp, ISP_LOGTDEBUG1,
+ "CTIO[%x] lun%d iid%d tag %x flgs %x sts %x ssts %x res %d",
+ cto->ct_fwhandle, csio->ccb_h.target_lun, cto->ct_iid,
+ cto->ct_tag_val, cto->ct_flags, cto->ct_status,
+ cto->ct_scsi_status, cto->ct_resid);
+ ISP_TDQE(isp, "tdma_mk[no data]", curi, cto);
+ isp_put_ctio(isp, cto, qe);
+ return;
+ }
+
+ nctios = nseg / ISP_RQDSEG;
+ if (nseg % ISP_RQDSEG) {
+ nctios++;
+ }
+
+ /*
+ * Save syshandle, and potentially any SCSI status, which we'll
+ * reinsert on the last CTIO we're going to send.
+ */
+
+ handle = cto->ct_syshandle;
+ cto->ct_syshandle = 0;
+ cto->ct_header.rqs_seqno = 0;
+ send_status = (cto->ct_flags & CT_SENDSTATUS) != 0;
+
+ if (send_status) {
+ sflags = cto->ct_flags & (CT_SENDSTATUS | CT_CCINCR);
+ cto->ct_flags &= ~(CT_SENDSTATUS | CT_CCINCR);
+ /*
+ * Preserve residual.
+ */
+ resid = cto->ct_resid;
+
+ /*
+ * Save actual SCSI status.
+ */
+ scsi_status = cto->ct_scsi_status;
+
+#ifndef STATUS_WITH_DATA
+ sflags |= CT_NO_DATA;
+ /*
+ * We can't do a status at the same time as a data CTIO, so
+ * we need to synthesize an extra CTIO at this level.
+ */
+ nctios++;
+#endif
+ } else {
+ sflags = scsi_status = resid = 0;
+ }
+
+ cto->ct_resid = 0;
+ cto->ct_scsi_status = 0;
+
+ pcs = (struct isp_pcisoftc *)isp;
+ dp = &pcs->dmaps[isp_handle_index(handle)];
+ if ((csio->ccb_h.flags & CAM_DIR_MASK) == CAM_DIR_IN) {
+ bus_dmamap_sync(pcs->dmat, *dp, BUS_DMASYNC_PREREAD);
+ } else {
+ bus_dmamap_sync(pcs->dmat, *dp, BUS_DMASYNC_PREWRITE);
+ }
+
+ nxti = *mp->nxtip;
+
+ for (nth_ctio = 0; nth_ctio < nctios; nth_ctio++) {
+ int seglim;
+
+ seglim = nseg;
+ if (seglim) {
+ int seg;
+
+ if (seglim > ISP_RQDSEG)
+ seglim = ISP_RQDSEG;
+
+ for (seg = 0; seg < seglim; seg++, nseg--) {
+ /*
+ * Unlike normal initiator commands, we don't
+ * do any swizzling here.
+ */
+ cto->ct_dataseg[seg].ds_count = dm_segs->ds_len;
+ cto->ct_dataseg[seg].ds_base = dm_segs->ds_addr;
+ cto->ct_xfrlen += dm_segs->ds_len;
+ dm_segs++;
+ }
+ cto->ct_seg_count = seg;
+ } else {
+ /*
+ * This case should only happen when we're sending an
+ * extra CTIO with final status.
+ */
+ if (send_status == 0) {
+ isp_prt(isp, ISP_LOGWARN,
+ "tdma_mk ran out of segments");
+ mp->error = EINVAL;
+ return;
+ }
+ }
+
+ /*
+ * At this point, the fields ct_lun, ct_iid, ct_tagval,
+ * ct_tagtype, and ct_timeout have been carried over
+ * unchanged from what our caller had set.
+ *
+ * The dataseg fields and the seg_count fields we just got
+ * through setting. The data direction we've preserved all
+ * along and only clear it if we're now sending status.
+ */
+
+ if (nth_ctio == nctios - 1) {
+ /*
+ * We're the last in a sequence of CTIOs, so mark
+ * this CTIO and save the handle to the CCB such that
+ * when this CTIO completes we can free dma resources
+ * and do whatever else we need to do to finish the
+ * rest of the command. We *don't* give this to the
+ * firmware to work on- the caller will do that.
+ */
+
+ cto->ct_syshandle = handle;
+ cto->ct_header.rqs_seqno = 1;
+
+ if (send_status) {
+ cto->ct_scsi_status = scsi_status;
+ cto->ct_flags |= sflags;
+ cto->ct_resid = resid;
+ }
+ if (send_status) {
+ isp_prt(isp, ISP_LOGTDEBUG1,
+ "CTIO[%x] lun%d iid %d tag %x ct_flags %x "
+ "scsi status %x resid %d",
+ cto->ct_fwhandle, csio->ccb_h.target_lun,
+ cto->ct_iid, cto->ct_tag_val, cto->ct_flags,
+ cto->ct_scsi_status, cto->ct_resid);
+ } else {
+ isp_prt(isp, ISP_LOGTDEBUG1,
+ "CTIO[%x] lun%d iid%d tag %x ct_flags 0x%x",
+ cto->ct_fwhandle, csio->ccb_h.target_lun,
+ cto->ct_iid, cto->ct_tag_val,
+ cto->ct_flags);
+ }
+ isp_put_ctio(isp, cto, qe);
+ ISP_TDQE(isp, "last tdma_mk", curi, cto);
+ if (nctios > 1) {
+ MEMORYBARRIER(isp, SYNC_REQUEST,
+ curi, QENTRY_LEN);
+ }
+ } else {
+ ct_entry_t *oqe = qe;
+
+ /*
+ * Make sure syshandle fields are clean
+ */
+ cto->ct_syshandle = 0;
+ cto->ct_header.rqs_seqno = 0;
+
+ isp_prt(isp, ISP_LOGTDEBUG1,
+ "CTIO[%x] lun%d for ID%d ct_flags 0x%x",
+ cto->ct_fwhandle, csio->ccb_h.target_lun,
+ cto->ct_iid, cto->ct_flags);
+
+ /*
+ * Get a new CTIO
+ */
+ qe = (ct_entry_t *)
+ ISP_QUEUE_ENTRY(isp->isp_rquest, nxti);
+ nxti = ISP_NXT_QENTRY(nxti, RQUEST_QUEUE_LEN(isp));
+ if (nxti == mp->optr) {
+ isp_prt(isp, ISP_LOGTDEBUG0,
+ "Queue Overflow in tdma_mk");
+ mp->error = MUSHERR_NOQENTRIES;
+ return;
+ }
+
+ /*
+ * Now that we're done with the old CTIO,
+ * flush it out to the request queue.
+ */
+ ISP_TDQE(isp, "dma_tgt_fc", curi, cto);
+ isp_put_ctio(isp, cto, oqe);
+ if (nth_ctio != 0) {
+ MEMORYBARRIER(isp, SYNC_REQUEST, curi,
+ QENTRY_LEN);
+ }
+ curi = ISP_NXT_QENTRY(curi, RQUEST_QUEUE_LEN(isp));
+
+ /*
+ * Reset some fields in the CTIO so we can reuse
+ * for the next one we'll flush to the request
+ * queue.
+ */
+ cto->ct_header.rqs_entry_type = RQSTYPE_CTIO;
+ cto->ct_header.rqs_entry_count = 1;
+ cto->ct_header.rqs_flags = 0;
+ cto->ct_status = 0;
+ cto->ct_scsi_status = 0;
+ cto->ct_xfrlen = 0;
+ cto->ct_resid = 0;
+ cto->ct_seg_count = 0;
+ MEMZERO(cto->ct_dataseg, sizeof(cto->ct_dataseg));
+ }
+ }
+ *mp->nxtip = nxti;
+}
+
+/*
+ * We don't have to do multiple CTIOs here. Instead, we can just do
+ * continuation segments as needed. This greatly simplifies the code
+ * improves performance.
+ */
+
+static void
+tdma_mkfc(void *arg, bus_dma_segment_t *dm_segs, int nseg, int error)
+{
+ mush_t *mp;
+ struct ccb_scsiio *csio;
+ ispsoftc_t *isp;
+ ct2_entry_t *cto, *qe;
+ uint16_t curi, nxti;
+ ispds_t *ds;
+ ispds64_t *ds64;
+ int segcnt, seglim;
+
+ mp = (mush_t *) arg;
+ if (error) {
+ mp->error = error;
+ return;
+ }
+
+ isp = mp->isp;
+ csio = mp->cmd_token;
+ cto = mp->rq;
+
+ curi = isp->isp_reqidx;
+ qe = (ct2_entry_t *) ISP_QUEUE_ENTRY(isp->isp_rquest, curi);
+
+ if (nseg == 0) {
+ if ((cto->ct_flags & CT2_FLAG_MMASK) != CT2_FLAG_MODE1) {
+ isp_prt(isp, ISP_LOGWARN,
+ "dma2_tgt_fc, a status CTIO2 without MODE1 "
+ "set (0x%x)", cto->ct_flags);
+ mp->error = EINVAL;
+ return;
+ }
+ /*
+ * We preserve ct_lun, ct_iid, ct_rxid. We set the data
+ * flags to NO DATA and clear relative offset flags.
+ * We preserve the ct_resid and the response area.
+ */
+ cto->ct_header.rqs_seqno = 1;
+ cto->ct_seg_count = 0;
+ cto->ct_reloff = 0;
+ isp_prt(isp, ISP_LOGTDEBUG1,
+ "CTIO2[%x] lun %d->iid%d flgs 0x%x sts 0x%x ssts "
+ "0x%x res %d", cto->ct_rxid, csio->ccb_h.target_lun,
+ cto->ct_iid, cto->ct_flags, cto->ct_status,
+ cto->rsp.m1.ct_scsi_status, cto->ct_resid);
+ if (IS_2KLOGIN(isp)) {
+ isp_put_ctio2e(isp,
+ (ct2e_entry_t *)cto, (ct2e_entry_t *)qe);
+ } else {
+ isp_put_ctio2(isp, cto, qe);
+ }
+ ISP_TDQE(isp, "dma2_tgt_fc[no data]", curi, qe);
+ return;
+ }
+
+ if ((cto->ct_flags & CT2_FLAG_MMASK) != CT2_FLAG_MODE0) {
+ isp_prt(isp, ISP_LOGERR,
+ "dma2_tgt_fc, a data CTIO2 without MODE0 set "
+ "(0x%x)", cto->ct_flags);
+ mp->error = EINVAL;
+ return;
+ }
+
+
+ nxti = *mp->nxtip;
+
+ /*
+ * Check to see if we need to DAC addressing or not.
+ *
+ * Any address that's over the 4GB boundary causes this
+ * to happen.
+ */
+ segcnt = nseg;
+ if (sizeof (bus_addr_t) > 4) {
+ for (segcnt = 0; segcnt < nseg; segcnt++) {
+ uint64_t addr = dm_segs[segcnt].ds_addr;
+ if (addr >= 0x100000000LL) {
+ break;
+ }
+ }
+ }
+ if (segcnt != nseg) {
+ cto->ct_header.rqs_entry_type = RQSTYPE_CTIO3;
+ seglim = ISP_RQDSEG_T3;
+ ds64 = &cto->rsp.m0.ct_dataseg64[0];
+ ds = NULL;
+ } else {
+ seglim = ISP_RQDSEG_T2;
+ ds64 = NULL;
+ ds = &cto->rsp.m0.ct_dataseg[0];
+ }
+ cto->ct_seg_count = 0;
+
+ /*
+ * Set up the CTIO2 data segments.
+ */
+ for (segcnt = 0; cto->ct_seg_count < seglim && segcnt < nseg;
+ cto->ct_seg_count++, segcnt++) {
+ if (ds64) {
+ ds64->ds_basehi =
+ ((uint64_t) (dm_segs[segcnt].ds_addr) >> 32);
+ ds64->ds_base = dm_segs[segcnt].ds_addr;
+ ds64->ds_count = dm_segs[segcnt].ds_len;
+ ds64++;
+ } else {
+ ds->ds_base = dm_segs[segcnt].ds_addr;
+ ds->ds_count = dm_segs[segcnt].ds_len;
+ ds++;
+ }
+ cto->rsp.m0.ct_xfrlen += dm_segs[segcnt].ds_len;
+#if __FreeBSD_version < 500000
+ isp_prt(isp, ISP_LOGTDEBUG1,
+ "isp_send_ctio2: ent0[%d]0x%llx:%llu",
+ cto->ct_seg_count, (uint64_t)dm_segs[segcnt].ds_addr,
+ (uint64_t)dm_segs[segcnt].ds_len);
+#else
+ isp_prt(isp, ISP_LOGTDEBUG1,
+ "isp_send_ctio2: ent0[%d]0x%jx:%ju",
+ cto->ct_seg_count, (uintmax_t)dm_segs[segcnt].ds_addr,
+ (uintmax_t)dm_segs[segcnt].ds_len);
+#endif
+ }
+
+ while (segcnt < nseg) {
+ uint16_t curip;
+ int seg;
+ ispcontreq_t local, *crq = &local, *qep;
+
+ qep = (ispcontreq_t *) ISP_QUEUE_ENTRY(isp->isp_rquest, nxti);
+ curip = nxti;
+ nxti = ISP_NXT_QENTRY(curip, RQUEST_QUEUE_LEN(isp));
+ if (nxti == mp->optr) {
+ ISP_UNLOCK(isp);
+ isp_prt(isp, ISP_LOGTDEBUG0,
+ "tdma_mkfc: request queue overflow");
+ mp->error = MUSHERR_NOQENTRIES;
+ return;
+ }
+ cto->ct_header.rqs_entry_count++;
+ MEMZERO((void *)crq, sizeof (*crq));
+ crq->req_header.rqs_entry_count = 1;
+ if (cto->ct_header.rqs_entry_type == RQSTYPE_CTIO3) {
+ seglim = ISP_CDSEG64;
+ ds = NULL;
+ ds64 = &((ispcontreq64_t *)crq)->req_dataseg[0];
+ crq->req_header.rqs_entry_type = RQSTYPE_A64_CONT;
+ } else {
+ seglim = ISP_CDSEG;
+ ds = &crq->req_dataseg[0];
+ ds64 = NULL;
+ crq->req_header.rqs_entry_type = RQSTYPE_DATASEG;
+ }
+ for (seg = 0; segcnt < nseg && seg < seglim;
+ segcnt++, seg++) {
+ if (ds64) {
+ ds64->ds_basehi =
+ ((uint64_t) (dm_segs[segcnt].ds_addr) >> 32);
+ ds64->ds_base = dm_segs[segcnt].ds_addr;
+ ds64->ds_count = dm_segs[segcnt].ds_len;
+ ds64++;
+ } else {
+ ds->ds_base = dm_segs[segcnt].ds_addr;
+ ds->ds_count = dm_segs[segcnt].ds_len;
+ ds++;
+ }
+#if __FreeBSD_version < 500000
+ isp_prt(isp, ISP_LOGTDEBUG1,
+ "isp_send_ctio2: ent%d[%d]%llx:%llu",
+ cto->ct_header.rqs_entry_count-1, seg,
+ (uint64_t)dm_segs[segcnt].ds_addr,
+ (uint64_t)dm_segs[segcnt].ds_len);
+#else
+ isp_prt(isp, ISP_LOGTDEBUG1,
+ "isp_send_ctio2: ent%d[%d]%jx:%ju",
+ cto->ct_header.rqs_entry_count-1, seg,
+ (uintmax_t)dm_segs[segcnt].ds_addr,
+ (uintmax_t)dm_segs[segcnt].ds_len);
+#endif
+ cto->rsp.m0.ct_xfrlen += dm_segs[segcnt].ds_len;
+ cto->ct_seg_count++;
+ }
+ MEMORYBARRIER(isp, SYNC_REQUEST, curip, QENTRY_LEN);
+ isp_put_cont_req(isp, crq, qep);
+ ISP_TDQE(isp, "cont entry", curi, qep);
+ }
+
+ /*
+ * No do final twiddling for the CTIO itself.
+ */
+ cto->ct_header.rqs_seqno = 1;
+ isp_prt(isp, ISP_LOGTDEBUG1,
+ "CTIO2[%x] lun %d->iid%d flgs 0x%x sts 0x%x ssts 0x%x resid %d",
+ cto->ct_rxid, csio->ccb_h.target_lun, (int) cto->ct_iid,
+ cto->ct_flags, cto->ct_status, cto->rsp.m1.ct_scsi_status,
+ cto->ct_resid);
+ if (IS_2KLOGIN(isp))
+ isp_put_ctio2e(isp, (ct2e_entry_t *)cto, (ct2e_entry_t *)qe);
+ else
+ isp_put_ctio2(isp, cto, qe);
+ ISP_TDQE(isp, "last dma2_tgt_fc", curi, qe);
+ *mp->nxtip = nxti;
+}
+#endif
+
+static void dma2_a64(void *, bus_dma_segment_t *, int, int);
+static void dma2(void *, bus_dma_segment_t *, int, int);
+
+static void
+dma2_a64(void *arg, bus_dma_segment_t *dm_segs, int nseg, int error)
+{
+ mush_t *mp;
+ ispsoftc_t *isp;
+ struct ccb_scsiio *csio;
+ struct isp_pcisoftc *pcs;
+ bus_dmamap_t *dp;
+ bus_dma_segment_t *eseg;
+ ispreq64_t *rq;
+ int seglim, datalen;
+ uint16_t nxti;
+
+ mp = (mush_t *) arg;
+ if (error) {
+ mp->error = error;
+ return;
+ }
+
+ if (nseg < 1) {
+ isp_prt(mp->isp, ISP_LOGERR, "bad segment count (%d)", nseg);
+ mp->error = EFAULT;
+ return;
+ }
+ csio = mp->cmd_token;
+ isp = mp->isp;
+ rq = mp->rq;
+ pcs = (struct isp_pcisoftc *)mp->isp;
+ dp = &pcs->dmaps[isp_handle_index(rq->req_handle)];
+ nxti = *mp->nxtip;
+
+ if ((csio->ccb_h.flags & CAM_DIR_MASK) == CAM_DIR_IN) {
+ bus_dmamap_sync(pcs->dmat, *dp, BUS_DMASYNC_PREREAD);
+ } else {
+ bus_dmamap_sync(pcs->dmat, *dp, BUS_DMASYNC_PREWRITE);
+ }
+ datalen = XS_XFRLEN(csio);
+
+ /*
+ * We're passed an initial partially filled in entry that
+ * has most fields filled in except for data transfer
+ * related values.
+ *
+ * Our job is to fill in the initial request queue entry and
+ * then to start allocating and filling in continuation entries
+ * until we've covered the entire transfer.
+ */
+
+ if (IS_FC(isp)) {
+ rq->req_header.rqs_entry_type = RQSTYPE_T3RQS;
+ seglim = ISP_RQDSEG_T3;
+ ((ispreqt3_t *)rq)->req_totalcnt = datalen;
+ if ((csio->ccb_h.flags & CAM_DIR_MASK) == CAM_DIR_IN) {
+ ((ispreqt3_t *)rq)->req_flags |= REQFLAG_DATA_IN;
+ } else {
+ ((ispreqt3_t *)rq)->req_flags |= REQFLAG_DATA_OUT;
+ }
+ } else {
+ rq->req_header.rqs_entry_type = RQSTYPE_A64;
+ if (csio->cdb_len > 12) {
+ seglim = 0;
+ } else {
+ seglim = ISP_RQDSEG_A64;
+ }
+ if ((csio->ccb_h.flags & CAM_DIR_MASK) == CAM_DIR_IN) {
+ rq->req_flags |= REQFLAG_DATA_IN;
+ } else {
+ rq->req_flags |= REQFLAG_DATA_OUT;
+ }
+ }
+
+ eseg = dm_segs + nseg;
+
+ while (datalen != 0 && rq->req_seg_count < seglim && dm_segs != eseg) {
+ if (IS_FC(isp)) {
+ ispreqt3_t *rq3 = (ispreqt3_t *)rq;
+ rq3->req_dataseg[rq3->req_seg_count].ds_base =
+ DMA_LO32(dm_segs->ds_addr);
+ rq3->req_dataseg[rq3->req_seg_count].ds_basehi =
+ DMA_HI32(dm_segs->ds_addr);
+ rq3->req_dataseg[rq3->req_seg_count].ds_count =
+ dm_segs->ds_len;
+ } else {
+ rq->req_dataseg[rq->req_seg_count].ds_base =
+ DMA_LO32(dm_segs->ds_addr);
+ rq->req_dataseg[rq->req_seg_count].ds_basehi =
+ DMA_HI32(dm_segs->ds_addr);
+ rq->req_dataseg[rq->req_seg_count].ds_count =
+ dm_segs->ds_len;
+ }
+ datalen -= dm_segs->ds_len;
+ rq->req_seg_count++;
+ dm_segs++;
+ }
+
+ while (datalen > 0 && dm_segs != eseg) {
+ uint16_t onxti;
+ ispcontreq64_t local, *crq = &local, *cqe;
+
+ cqe = (ispcontreq64_t *) ISP_QUEUE_ENTRY(isp->isp_rquest, nxti);
+ onxti = nxti;
+ nxti = ISP_NXT_QENTRY(onxti, RQUEST_QUEUE_LEN(isp));
+ if (nxti == mp->optr) {
+ isp_prt(isp, ISP_LOGDEBUG0, "Request Queue Overflow++");
+ mp->error = MUSHERR_NOQENTRIES;
+ return;
+ }
+ rq->req_header.rqs_entry_count++;
+ MEMZERO((void *)crq, sizeof (*crq));
+ crq->req_header.rqs_entry_count = 1;
+ crq->req_header.rqs_entry_type = RQSTYPE_A64_CONT;
+
+ seglim = 0;
+ while (datalen > 0 && seglim < ISP_CDSEG64 && dm_segs != eseg) {
+ crq->req_dataseg[seglim].ds_base =
+ DMA_LO32(dm_segs->ds_addr);
+ crq->req_dataseg[seglim].ds_basehi =
+ DMA_HI32(dm_segs->ds_addr);
+ crq->req_dataseg[seglim].ds_count =
+ dm_segs->ds_len;
+ rq->req_seg_count++;
+ dm_segs++;
+ seglim++;
+ datalen -= dm_segs->ds_len;
+ }
+ isp_put_cont64_req(isp, crq, cqe);
+ MEMORYBARRIER(isp, SYNC_REQUEST, onxti, QENTRY_LEN);
+ }
+ *mp->nxtip = nxti;
+}
+
+static void
+dma2(void *arg, bus_dma_segment_t *dm_segs, int nseg, int error)
+{
+ mush_t *mp;
+ ispsoftc_t *isp;
+ struct ccb_scsiio *csio;
+ struct isp_pcisoftc *pcs;
+ bus_dmamap_t *dp;
+ bus_dma_segment_t *eseg;
+ ispreq_t *rq;
+ int seglim, datalen;
+ uint16_t nxti;
+
+ mp = (mush_t *) arg;
+ if (error) {
+ mp->error = error;
+ return;
+ }
+
+ if (nseg < 1) {
+ isp_prt(mp->isp, ISP_LOGERR, "bad segment count (%d)", nseg);
+ mp->error = EFAULT;
+ return;
+ }
+ csio = mp->cmd_token;
+ isp = mp->isp;
+ rq = mp->rq;
+ pcs = (struct isp_pcisoftc *)mp->isp;
+ dp = &pcs->dmaps[isp_handle_index(rq->req_handle)];
+ nxti = *mp->nxtip;
+
+ if ((csio->ccb_h.flags & CAM_DIR_MASK) == CAM_DIR_IN) {
+ bus_dmamap_sync(pcs->dmat, *dp, BUS_DMASYNC_PREREAD);
+ } else {
+ bus_dmamap_sync(pcs->dmat, *dp, BUS_DMASYNC_PREWRITE);
+ }
+
+ datalen = XS_XFRLEN(csio);
+
+ /*
+ * We're passed an initial partially filled in entry that
+ * has most fields filled in except for data transfer
+ * related values.
+ *
+ * Our job is to fill in the initial request queue entry and
+ * then to start allocating and filling in continuation entries
+ * until we've covered the entire transfer.
+ */
+
+ if (IS_FC(isp)) {
+ seglim = ISP_RQDSEG_T2;
+ ((ispreqt2_t *)rq)->req_totalcnt = datalen;
+ if ((csio->ccb_h.flags & CAM_DIR_MASK) == CAM_DIR_IN) {
+ ((ispreqt2_t *)rq)->req_flags |= REQFLAG_DATA_IN;
+ } else {
+ ((ispreqt2_t *)rq)->req_flags |= REQFLAG_DATA_OUT;
+ }
+ } else {
+ if (csio->cdb_len > 12) {
+ seglim = 0;
+ } else {
+ seglim = ISP_RQDSEG;
+ }
+ if ((csio->ccb_h.flags & CAM_DIR_MASK) == CAM_DIR_IN) {
+ rq->req_flags |= REQFLAG_DATA_IN;
+ } else {
+ rq->req_flags |= REQFLAG_DATA_OUT;
+ }
+ }
+
+ eseg = dm_segs + nseg;
+
+ while (datalen != 0 && rq->req_seg_count < seglim && dm_segs != eseg) {
+ if (IS_FC(isp)) {
+ ispreqt2_t *rq2 = (ispreqt2_t *)rq;
+ rq2->req_dataseg[rq2->req_seg_count].ds_base =
+ DMA_LO32(dm_segs->ds_addr);
+ rq2->req_dataseg[rq2->req_seg_count].ds_count =
+ dm_segs->ds_len;
+ } else {
+ rq->req_dataseg[rq->req_seg_count].ds_base =
+ DMA_LO32(dm_segs->ds_addr);
+ rq->req_dataseg[rq->req_seg_count].ds_count =
+ dm_segs->ds_len;
+ }
+ datalen -= dm_segs->ds_len;
+ rq->req_seg_count++;
+ dm_segs++;
+ }
+
+ while (datalen > 0 && dm_segs != eseg) {
+ uint16_t onxti;
+ ispcontreq_t local, *crq = &local, *cqe;
+
+ cqe = (ispcontreq_t *) ISP_QUEUE_ENTRY(isp->isp_rquest, nxti);
+ onxti = nxti;
+ nxti = ISP_NXT_QENTRY(onxti, RQUEST_QUEUE_LEN(isp));
+ if (nxti == mp->optr) {
+ isp_prt(isp, ISP_LOGDEBUG0, "Request Queue Overflow++");
+ mp->error = MUSHERR_NOQENTRIES;
+ return;
+ }
+ rq->req_header.rqs_entry_count++;
+ MEMZERO((void *)crq, sizeof (*crq));
+ crq->req_header.rqs_entry_count = 1;
+ crq->req_header.rqs_entry_type = RQSTYPE_DATASEG;
+
+ seglim = 0;
+ while (datalen > 0 && seglim < ISP_CDSEG && dm_segs != eseg) {
+ crq->req_dataseg[seglim].ds_base =
+ DMA_LO32(dm_segs->ds_addr);
+ crq->req_dataseg[seglim].ds_count =
+ dm_segs->ds_len;
+ rq->req_seg_count++;
+ dm_segs++;
+ seglim++;
+ datalen -= dm_segs->ds_len;
+ }
+ isp_put_cont_req(isp, crq, cqe);
+ MEMORYBARRIER(isp, SYNC_REQUEST, onxti, QENTRY_LEN);
+ }
+ *mp->nxtip = nxti;
+}
+
+/*
+ * We enter with ISP_LOCK held
+ */
+static int
+isp_pci_dmasetup(ispsoftc_t *isp, struct ccb_scsiio *csio, ispreq_t *rq,
+ uint16_t *nxtip, uint16_t optr)
+{
+ struct isp_pcisoftc *pcs = (struct isp_pcisoftc *)isp;
+ ispreq_t *qep;
+ bus_dmamap_t *dp = NULL;
+ mush_t mush, *mp;
+ void (*eptr)(void *, bus_dma_segment_t *, int, int);
+
+ qep = (ispreq_t *) ISP_QUEUE_ENTRY(isp->isp_rquest, isp->isp_reqidx);
+#ifdef ISP_TARGET_MODE
+ if (csio->ccb_h.func_code == XPT_CONT_TARGET_IO) {
+ if (IS_FC(isp)) {
+ eptr = tdma_mkfc;
+ } else {
+ eptr = tdma_mk;
+ }
+ if ((csio->ccb_h.flags & CAM_DIR_MASK) == CAM_DIR_NONE ||
+ (csio->dxfer_len == 0)) {
+ mp = &mush;
+ mp->isp = isp;
+ mp->cmd_token = csio;
+ mp->rq = rq; /* really a ct_entry_t or ct2_entry_t */
+ mp->nxtip = nxtip;
+ mp->optr = optr;
+ mp->error = 0;
+ ISPLOCK_2_CAMLOCK(isp);
+ (*eptr)(mp, NULL, 0, 0);
+ CAMLOCK_2_ISPLOCK(isp);
+ goto mbxsync;
+ }
+ } else
+#endif
+ if (sizeof (bus_addr_t) > 4) {
+ eptr = dma2_a64;
+ } else {
+ eptr = dma2;
+ }
+
+
+ if ((csio->ccb_h.flags & CAM_DIR_MASK) == CAM_DIR_NONE ||
+ (csio->dxfer_len == 0)) {
+ rq->req_seg_count = 1;
+ goto mbxsync;
+ }
+
+ /*
+ * Do a virtual grapevine step to collect info for
+ * the callback dma allocation that we have to use...
+ */
+ mp = &mush;
+ mp->isp = isp;
+ mp->cmd_token = csio;
+ mp->rq = rq;
+ mp->nxtip = nxtip;
+ mp->optr = optr;
+ mp->error = 0;
+
+ ISPLOCK_2_CAMLOCK(isp);
+ if ((csio->ccb_h.flags & CAM_SCATTER_VALID) == 0) {
+ if ((csio->ccb_h.flags & CAM_DATA_PHYS) == 0) {
+ int error, s;
+ dp = &pcs->dmaps[isp_handle_index(rq->req_handle)];
+ s = splsoftvm();
+ error = bus_dmamap_load(pcs->dmat, *dp,
+ csio->data_ptr, csio->dxfer_len, eptr, mp, 0);
+ if (error == EINPROGRESS) {
+ bus_dmamap_unload(pcs->dmat, *dp);
+ mp->error = EINVAL;
+ isp_prt(isp, ISP_LOGERR,
+ "deferred dma allocation not supported");
+ } else if (error && mp->error == 0) {
+#ifdef DIAGNOSTIC
+ isp_prt(isp, ISP_LOGERR,
+ "error %d in dma mapping code", error);
+#endif
+ mp->error = error;
+ }
+ splx(s);
+ } else {
+ /* Pointer to physical buffer */
+ struct bus_dma_segment seg;
+ seg.ds_addr = (bus_addr_t)(vm_offset_t)csio->data_ptr;
+ seg.ds_len = csio->dxfer_len;
+ (*eptr)(mp, &seg, 1, 0);
+ }
+ } else {
+ struct bus_dma_segment *segs;
+
+ if ((csio->ccb_h.flags & CAM_DATA_PHYS) != 0) {
+ isp_prt(isp, ISP_LOGERR,
+ "Physical segment pointers unsupported");
+ mp->error = EINVAL;
+ } else if ((csio->ccb_h.flags & CAM_SG_LIST_PHYS) == 0) {
+ isp_prt(isp, ISP_LOGERR,
+ "Virtual segment addresses unsupported");
+ mp->error = EINVAL;
+ } else {
+ /* Just use the segments provided */
+ segs = (struct bus_dma_segment *) csio->data_ptr;
+ (*eptr)(mp, segs, csio->sglist_cnt, 0);
+ }
+ }
+ CAMLOCK_2_ISPLOCK(isp);
+ if (mp->error) {
+ int retval = CMD_COMPLETE;
+ if (mp->error == MUSHERR_NOQENTRIES) {
+ retval = CMD_EAGAIN;
+ } else if (mp->error == EFBIG) {
+ XS_SETERR(csio, CAM_REQ_TOO_BIG);
+ } else if (mp->error == EINVAL) {
+ XS_SETERR(csio, CAM_REQ_INVALID);
+ } else {
+ XS_SETERR(csio, CAM_UNREC_HBA_ERROR);
+ }
+ return (retval);
+ }
+mbxsync:
+ switch (rq->req_header.rqs_entry_type) {
+ case RQSTYPE_REQUEST:
+ isp_put_request(isp, rq, qep);
+ break;
+ case RQSTYPE_CMDONLY:
+ isp_put_extended_request(isp, (ispextreq_t *)rq,
+ (ispextreq_t *)qep);
+ break;
+ case RQSTYPE_T2RQS:
+ isp_put_request_t2(isp, (ispreqt2_t *) rq, (ispreqt2_t *) qep);
+ break;
+ case RQSTYPE_A64:
+ case RQSTYPE_T3RQS:
+ isp_put_request_t3(isp, (ispreqt3_t *) rq, (ispreqt3_t *) qep);
+ break;
+ }
+ return (CMD_QUEUED);
+}
+
+static void
+isp_pci_dmateardown(ispsoftc_t *isp, XS_T *xs, uint16_t handle)
+{
+ struct isp_pcisoftc *pcs = (struct isp_pcisoftc *)isp;
+ bus_dmamap_t *dp = &pcs->dmaps[isp_handle_index(handle)];
+ if ((xs->ccb_h.flags & CAM_DIR_MASK) == CAM_DIR_IN) {
+ bus_dmamap_sync(pcs->dmat, *dp, BUS_DMASYNC_POSTREAD);
+ } else {
+ bus_dmamap_sync(pcs->dmat, *dp, BUS_DMASYNC_POSTWRITE);
+ }
+ bus_dmamap_unload(pcs->dmat, *dp);
+}
+
+
+static void
+isp_pci_reset1(ispsoftc_t *isp)
+{
+ /* Make sure the BIOS is disabled */
+ isp_pci_wr_reg(isp, HCCR, PCI_HCCR_CMD_BIOS);
+ /* and enable interrupts */
+ ENABLE_INTS(isp);
+}
+
+static void
+isp_pci_dumpregs(ispsoftc_t *isp, const char *msg)
+{
+ struct isp_pcisoftc *pcs = (struct isp_pcisoftc *)isp;
+ if (msg)
+ printf("%s: %s\n", device_get_nameunit(isp->isp_dev), msg);
+ else
+ printf("%s:\n", device_get_nameunit(isp->isp_dev));
+ if (IS_SCSI(isp))
+ printf(" biu_conf1=%x", ISP_READ(isp, BIU_CONF1));
+ else
+ printf(" biu_csr=%x", ISP_READ(isp, BIU2100_CSR));
+ printf(" biu_icr=%x biu_isr=%x biu_sema=%x ", ISP_READ(isp, BIU_ICR),
+ ISP_READ(isp, BIU_ISR), ISP_READ(isp, BIU_SEMA));
+ printf("risc_hccr=%x\n", ISP_READ(isp, HCCR));
+
+
+ if (IS_SCSI(isp)) {
+ ISP_WRITE(isp, HCCR, HCCR_CMD_PAUSE);
+ printf(" cdma_conf=%x cdma_sts=%x cdma_fifostat=%x\n",
+ ISP_READ(isp, CDMA_CONF), ISP_READ(isp, CDMA_STATUS),
+ ISP_READ(isp, CDMA_FIFO_STS));
+ printf(" ddma_conf=%x ddma_sts=%x ddma_fifostat=%x\n",
+ ISP_READ(isp, DDMA_CONF), ISP_READ(isp, DDMA_STATUS),
+ ISP_READ(isp, DDMA_FIFO_STS));
+ printf(" sxp_int=%x sxp_gross=%x sxp(scsi_ctrl)=%x\n",
+ ISP_READ(isp, SXP_INTERRUPT),
+ ISP_READ(isp, SXP_GROSS_ERR),
+ ISP_READ(isp, SXP_PINS_CTRL));
+ ISP_WRITE(isp, HCCR, HCCR_CMD_RELEASE);
+ }
+ printf(" mbox regs: %x %x %x %x %x\n",
+ ISP_READ(isp, OUTMAILBOX0), ISP_READ(isp, OUTMAILBOX1),
+ ISP_READ(isp, OUTMAILBOX2), ISP_READ(isp, OUTMAILBOX3),
+ ISP_READ(isp, OUTMAILBOX4));
+ printf(" PCI Status Command/Status=%x\n",
+ pci_read_config(pcs->pci_dev, PCIR_COMMAND, 1));
+}
diff --git a/sys/dev/isp/isp_sbus.c b/sys/dev/isp/isp_sbus.c
new file mode 100644
index 000000000000..c8b88fc61b0a
--- /dev/null
+++ b/sys/dev/isp/isp_sbus.c
@@ -0,0 +1,874 @@
+/*-
+ * Copyright (c) 1997-2006 by Matthew Jacob
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice 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.
+ *
+ * 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.
+ */
+/*
+ * SBus specific probe and attach routines for Qlogic ISP SCSI adapters.
+ * FreeBSD Version.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#if __FreeBSD_version >= 700000
+#include <sys/linker.h>
+#include <sys/firmware.h>
+#endif
+#include <sys/bus.h>
+#include <sys/kernel.h>
+#include <sys/module.h>
+#include <sys/resource.h>
+
+#include <dev/ofw/ofw_bus.h>
+
+#include <machine/bus.h>
+#include <machine/resource.h>
+#include <sys/rman.h>
+#include <sparc64/sbus/sbusvar.h>
+
+#include <dev/isp/isp_freebsd.h>
+
+static uint16_t isp_sbus_rd_reg(ispsoftc_t *, int);
+static void isp_sbus_wr_reg(ispsoftc_t *, int, uint16_t);
+static int
+isp_sbus_rd_isr(ispsoftc_t *, uint16_t *, uint16_t *, uint16_t *);
+static int isp_sbus_mbxdma(ispsoftc_t *);
+static int
+isp_sbus_dmasetup(ispsoftc_t *, XS_T *, ispreq_t *, uint16_t *, uint16_t);
+static void
+isp_sbus_dmateardown(ispsoftc_t *, XS_T *, uint16_t);
+
+static void isp_sbus_reset1(ispsoftc_t *);
+static void isp_sbus_dumpregs(ispsoftc_t *, const char *);
+
+static struct ispmdvec mdvec = {
+ isp_sbus_rd_isr,
+ isp_sbus_rd_reg,
+ isp_sbus_wr_reg,
+ isp_sbus_mbxdma,
+ isp_sbus_dmasetup,
+ isp_sbus_dmateardown,
+ NULL,
+ isp_sbus_reset1,
+ isp_sbus_dumpregs,
+ NULL,
+ BIU_BURST_ENABLE|BIU_PCI_CONF1_FIFO_64
+};
+
+static int isp_sbus_probe (device_t);
+static int isp_sbus_attach (device_t);
+
+
+struct isp_sbussoftc {
+ ispsoftc_t sbus_isp;
+ device_t sbus_dev;
+ struct resource * sbus_reg;
+ bus_space_tag_t sbus_st;
+ bus_space_handle_t sbus_sh;
+ void * ih;
+ int16_t sbus_poff[_NREG_BLKS];
+ bus_dma_tag_t dmat;
+ bus_dmamap_t *dmaps;
+ sdparam sbus_param;
+ struct ispmdvec sbus_mdvec;
+ struct resource * sbus_ires;
+};
+
+
+static device_method_t isp_sbus_methods[] = {
+ /* Device interface */
+ DEVMETHOD(device_probe, isp_sbus_probe),
+ DEVMETHOD(device_attach, isp_sbus_attach),
+ { 0, 0 }
+};
+static void isp_sbus_intr(void *);
+
+static driver_t isp_sbus_driver = {
+ "isp", isp_sbus_methods, sizeof (struct isp_sbussoftc)
+};
+static devclass_t isp_devclass;
+DRIVER_MODULE(isp, sbus, isp_sbus_driver, isp_devclass, 0, 0);
+#if __FreeBSD_version >= 700000
+MODULE_DEPEND(isp, firmware, 1, 1, 1);
+#else
+extern ispfwfunc *isp_get_firmware_p;
+#endif
+
+static int
+isp_sbus_probe(device_t dev)
+{
+ int found = 0;
+ const char *name = ofw_bus_get_name(dev);
+ if (strcmp(name, "SUNW,isp") == 0 ||
+ strcmp(name, "QLGC,isp") == 0 ||
+ strcmp(name, "ptisp") == 0 ||
+ strcmp(name, "PTI,ptisp") == 0) {
+ found++;
+ }
+ if (!found)
+ return (ENXIO);
+
+ if (isp_announced == 0 && bootverbose) {
+ printf("Qlogic ISP Driver, FreeBSD Version %d.%d, "
+ "Core Version %d.%d\n",
+ ISP_PLATFORM_VERSION_MAJOR, ISP_PLATFORM_VERSION_MINOR,
+ ISP_CORE_VERSION_MAJOR, ISP_CORE_VERSION_MINOR);
+ isp_announced++;
+ }
+ return (0);
+}
+
+static int
+isp_sbus_attach(device_t dev)
+{
+ struct resource *regs;
+ int tval, iqd, isp_debug, role, rid, ispburst;
+ struct isp_sbussoftc *sbs;
+ ispsoftc_t *isp = NULL;
+ int locksetup = 0;
+
+ /*
+ * Figure out if we're supposed to skip this one.
+ * If we are, we actually go to ISP_ROLE_NONE.
+ */
+
+ tval = 0;
+ if (resource_int_value(device_get_name(dev), device_get_unit(dev),
+ "disable", &tval) == 0 && tval) {
+ device_printf(dev, "device is disabled\n");
+ /* but return 0 so the !$)$)*!$*) unit isn't reused */
+ return (0);
+ }
+
+ role = 0;
+ if (resource_int_value(device_get_name(dev), device_get_unit(dev),
+ "role", &role) == 0 &&
+ ((role & ~(ISP_ROLE_INITIATOR|ISP_ROLE_TARGET)) == 0)) {
+ device_printf(dev, "setting role to 0x%x\n", role);
+ } else {
+#ifdef ISP_TARGET_MODE
+ role = ISP_ROLE_INITIATOR|ISP_ROLE_TARGET;
+#else
+ role = ISP_DEFAULT_ROLES;
+#endif
+ }
+
+ sbs = malloc(sizeof (*sbs), M_DEVBUF, M_NOWAIT | M_ZERO);
+ if (sbs == NULL) {
+ device_printf(dev, "cannot allocate softc\n");
+ return (ENOMEM);
+ }
+
+ regs = NULL;
+ iqd = 0;
+
+ rid = 0;
+ regs =
+ bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, RF_ACTIVE);
+ if (regs == 0) {
+ device_printf(dev, "unable to map registers\n");
+ goto bad;
+ }
+ sbs->sbus_dev = dev;
+ sbs->sbus_reg = regs;
+ sbs->sbus_st = rman_get_bustag(regs);
+ sbs->sbus_sh = rman_get_bushandle(regs);
+ sbs->sbus_mdvec = mdvec;
+
+ sbs->sbus_poff[BIU_BLOCK >> _BLK_REG_SHFT] = BIU_REGS_OFF;
+ sbs->sbus_poff[MBOX_BLOCK >> _BLK_REG_SHFT] = SBUS_MBOX_REGS_OFF;
+ sbs->sbus_poff[SXP_BLOCK >> _BLK_REG_SHFT] = SBUS_SXP_REGS_OFF;
+ sbs->sbus_poff[RISC_BLOCK >> _BLK_REG_SHFT] = SBUS_RISC_REGS_OFF;
+ sbs->sbus_poff[DMA_BLOCK >> _BLK_REG_SHFT] = DMA_REGS_OFF;
+ isp = &sbs->sbus_isp;
+ isp->isp_mdvec = &sbs->sbus_mdvec;
+ isp->isp_bustype = ISP_BT_SBUS;
+ isp->isp_type = ISP_HA_SCSI_UNKNOWN;
+ isp->isp_param = &sbs->sbus_param;
+ isp->isp_revision = 0; /* XXX */
+ isp->isp_role = role;
+ isp->isp_dev = dev;
+
+ /*
+ * Get the clock frequency and convert it from HZ to MHz,
+ * rounding up. This defaults to 25MHz if there isn't a
+ * device specific one in the OFW device tree.
+ */
+ sbs->sbus_mdvec.dv_clock = (sbus_get_clockfreq(dev) + 500000)/1000000;
+
+ /*
+ * Now figure out what the proper burst sizes, etc., to use.
+ * Unfortunately, there is no ddi_dma_burstsizes here which
+ * walks up the tree finding the limiting burst size node (if
+ * any). We just use what's here for isp.
+ */
+ ispburst = sbus_get_burstsz(dev);
+ if (ispburst == 0) {
+ ispburst = SBUS_BURST_32 - 1;
+ }
+ sbs->sbus_mdvec.dv_conf1 = 0;
+ if (ispburst & (1 << 5)) {
+ sbs->sbus_mdvec.dv_conf1 = BIU_SBUS_CONF1_FIFO_32;
+ } else if (ispburst & (1 << 4)) {
+ sbs->sbus_mdvec.dv_conf1 = BIU_SBUS_CONF1_FIFO_16;
+ } else if (ispburst & (1 << 3)) {
+ sbs->sbus_mdvec.dv_conf1 =
+ BIU_SBUS_CONF1_BURST8 | BIU_SBUS_CONF1_FIFO_8;
+ }
+ if (sbs->sbus_mdvec.dv_conf1) {
+ sbs->sbus_mdvec.dv_conf1 |= BIU_BURST_ENABLE;
+ }
+
+ /*
+ * Some early versions of the PTI SBus adapter
+ * would fail in trying to download (via poking)
+ * FW. We give up on them.
+ */
+ if (strcmp("PTI,ptisp", ofw_bus_get_name(dev)) == 0 ||
+ strcmp("ptisp", ofw_bus_get_name(dev)) == 0) {
+ isp->isp_confopts |= ISP_CFG_NORELOAD;
+ }
+
+ /*
+ * We don't trust NVRAM on SBus cards
+ */
+ isp->isp_confopts |= ISP_CFG_NONVRAM;
+
+
+#if __FreeBSD_version >= 700000
+ isp->isp_osinfo.fw = firmware_get("isp_1000");
+ if (isp->isp_osinfo.fw) {
+ union {
+ const void *cp;
+ uint16_t *sp;
+ } stupid;
+ stupid.cp = isp->isp_osinfo.fw->data;
+ isp->isp_mdvec->dv_ispfw = stupid.sp;
+ }
+#else
+ /*
+ * Try and find firmware for this device.
+ */
+ if (isp_get_firmware_p) {
+ (*isp_get_firmware_p)(0, 0, 0x1000, &sbs->sbus_mdvec.dv_ispfw);
+ }
+#endif
+
+ iqd = 0;
+ sbs->sbus_ires = bus_alloc_resource_any(dev, SYS_RES_IRQ, &iqd,
+ RF_ACTIVE | RF_SHAREABLE);
+ if (sbs->sbus_ires == NULL) {
+ device_printf(dev, "could not allocate interrupt\n");
+ goto bad;
+ }
+
+ tval = 0;
+ if (resource_int_value(device_get_name(dev), device_get_unit(dev),
+ "fwload_disable", &tval) == 0 && tval != 0) {
+ isp->isp_confopts |= ISP_CFG_NORELOAD;
+ }
+
+ isp->isp_osinfo.default_id = -1;
+ if (resource_int_value(device_get_name(dev), device_get_unit(dev),
+ "iid", &tval) == 0) {
+ isp->isp_osinfo.default_id = tval;
+ isp->isp_confopts |= ISP_CFG_OWNLOOPID;
+ }
+ if (isp->isp_osinfo.default_id == -1) {
+ /*
+ * XXX: should be a way to get properties w/o having
+ * XXX: to call OF_xxx functions
+ */
+ isp->isp_osinfo.default_id = 7;
+ }
+
+ isp_debug = 0;
+ (void) resource_int_value(device_get_name(dev), device_get_unit(dev),
+ "debug", &isp_debug);
+
+ /* Make sure the lock is set up. */
+ mtx_init(&isp->isp_osinfo.lock, "isp", NULL, MTX_DEF);
+ locksetup++;
+
+ if (bus_setup_intr(dev, sbs->sbus_ires, ISP_IFLAGS,
+ isp_sbus_intr, isp, &sbs->ih)) {
+ device_printf(dev, "could not setup interrupt\n");
+ goto bad;
+ }
+
+ /*
+ * Set up logging levels.
+ */
+ if (isp_debug) {
+ isp->isp_dblev = isp_debug;
+ } else {
+ isp->isp_dblev = ISP_LOGWARN|ISP_LOGERR;
+ }
+ if (bootverbose)
+ isp->isp_dblev |= ISP_LOGCONFIG|ISP_LOGINFO;
+
+ /*
+ * Make sure we're in reset state.
+ */
+ ISP_LOCK(isp);
+ isp_reset(isp);
+ if (isp->isp_state != ISP_RESETSTATE) {
+ ISP_UNLOCK(isp);
+ goto bad;
+ }
+ isp_init(isp);
+ if (isp->isp_role != ISP_ROLE_NONE && isp->isp_state != ISP_INITSTATE) {
+ isp_uninit(isp);
+ ISP_UNLOCK(isp);
+ goto bad;
+ }
+ isp_attach(isp);
+ if (isp->isp_role != ISP_ROLE_NONE && isp->isp_state != ISP_RUNSTATE) {
+ isp_uninit(isp);
+ ISP_UNLOCK(isp);
+ goto bad;
+ }
+ /*
+ * XXXX: Here is where we might unload the f/w module
+ * XXXX: (or decrease the reference count to it).
+ */
+ ISP_UNLOCK(isp);
+ return (0);
+
+bad:
+
+ if (sbs && sbs->ih) {
+ (void) bus_teardown_intr(dev, sbs->sbus_ires, sbs->ih);
+ }
+
+ if (locksetup && isp) {
+ mtx_destroy(&isp->isp_osinfo.lock);
+ }
+
+ if (sbs && sbs->sbus_ires) {
+ bus_release_resource(dev, SYS_RES_IRQ, iqd, sbs->sbus_ires);
+ }
+
+
+ if (regs) {
+ (void) bus_release_resource(dev, 0, 0, regs);
+ }
+
+ if (sbs) {
+ if (sbs->sbus_isp.isp_param)
+ free(sbs->sbus_isp.isp_param, M_DEVBUF);
+ free(sbs, M_DEVBUF);
+ }
+
+ /*
+ * XXXX: Here is where we might unload the f/w module
+ * XXXX: (or decrease the reference count to it).
+ */
+ return (ENXIO);
+}
+
+static void
+isp_sbus_intr(void *arg)
+{
+ ispsoftc_t *isp = arg;
+ uint16_t isr, sema, mbox;
+
+ ISP_LOCK(isp);
+ isp->isp_intcnt++;
+ if (ISP_READ_ISR(isp, &isr, &sema, &mbox) == 0) {
+ isp->isp_intbogus++;
+ } else {
+ int iok = isp->isp_osinfo.intsok;
+ isp->isp_osinfo.intsok = 0;
+ isp_intr(isp, isr, sema, mbox);
+ isp->isp_osinfo.intsok = iok;
+ }
+ ISP_UNLOCK(isp);
+}
+
+#define IspVirt2Off(a, x) \
+ (((struct isp_sbussoftc *)a)->sbus_poff[((x) & _BLK_REG_MASK) >> \
+ _BLK_REG_SHFT] + ((x) & 0xff))
+
+#define BXR2(sbc, off) \
+ bus_space_read_2(sbc->sbus_st, sbc->sbus_sh, off)
+
+static int
+isp_sbus_rd_isr(ispsoftc_t *isp, uint16_t *isrp,
+ uint16_t *semap, uint16_t *mbp)
+{
+ struct isp_sbussoftc *sbc = (struct isp_sbussoftc *) isp;
+ uint16_t isr, sema;
+
+ isr = BXR2(sbc, IspVirt2Off(isp, BIU_ISR));
+ sema = BXR2(sbc, IspVirt2Off(isp, BIU_SEMA));
+ isp_prt(isp, ISP_LOGDEBUG3, "ISR 0x%x SEMA 0x%x", isr, sema);
+ isr &= INT_PENDING_MASK(isp);
+ sema &= BIU_SEMA_LOCK;
+ if (isr == 0 && sema == 0) {
+ return (0);
+ }
+ *isrp = isr;
+ if ((*semap = sema) != 0) {
+ *mbp = BXR2(sbc, IspVirt2Off(isp, OUTMAILBOX0));
+ }
+ return (1);
+}
+
+static uint16_t
+isp_sbus_rd_reg(ispsoftc_t *isp, int regoff)
+{
+ uint16_t rval;
+ struct isp_sbussoftc *sbs = (struct isp_sbussoftc *) isp;
+ int offset = sbs->sbus_poff[(regoff & _BLK_REG_MASK) >> _BLK_REG_SHFT];
+ offset += (regoff & 0xff);
+ rval = bus_space_read_2(sbs->sbus_st, sbs->sbus_sh, offset);
+ isp_prt(isp, ISP_LOGDEBUG3,
+ "isp_sbus_rd_reg(off %x) = %x", regoff, rval);
+ return (rval);
+}
+
+static void
+isp_sbus_wr_reg(ispsoftc_t *isp, int regoff, uint16_t val)
+{
+ struct isp_sbussoftc *sbs = (struct isp_sbussoftc *) isp;
+ int offset = sbs->sbus_poff[(regoff & _BLK_REG_MASK) >> _BLK_REG_SHFT];
+ offset += (regoff & 0xff);
+ isp_prt(isp, ISP_LOGDEBUG3,
+ "isp_sbus_wr_reg(off %x) = %x", regoff, val);
+ bus_space_write_2(sbs->sbus_st, sbs->sbus_sh, offset, val);
+}
+
+struct imush {
+ ispsoftc_t *isp;
+ int error;
+};
+
+static void imc(void *, bus_dma_segment_t *, int, int);
+
+static void
+imc(void *arg, bus_dma_segment_t *segs, int nseg, int error)
+{
+ struct imush *imushp = (struct imush *) arg;
+ if (error) {
+ imushp->error = error;
+ } else {
+ ispsoftc_t *isp =imushp->isp;
+ bus_addr_t addr = segs->ds_addr;
+
+ isp->isp_rquest_dma = addr;
+ addr += ISP_QUEUE_SIZE(RQUEST_QUEUE_LEN(isp));
+ isp->isp_result_dma = addr;
+ }
+}
+
+/*
+ * Should be BUS_SPACE_MAXSIZE, but MAXPHYS is larger than BUS_SPACE_MAXSIZE
+ */
+#define ISP_NSEGS ((MAXPHYS / PAGE_SIZE) + 1)
+
+static int
+isp_sbus_mbxdma(ispsoftc_t *isp)
+{
+ struct isp_sbussoftc *sbs = (struct isp_sbussoftc *)isp;
+ caddr_t base;
+ uint32_t len;
+ int i, error, ns;
+ struct imush im;
+
+ /*
+ * Already been here? If so, leave...
+ */
+ if (isp->isp_rquest) {
+ return (0);
+ }
+
+ ISP_UNLOCK(isp);
+
+ if (bus_dma_tag_create(NULL, 1, BUS_SPACE_MAXADDR_24BIT+1,
+ BUS_SPACE_MAXADDR_32BIT, BUS_SPACE_MAXADDR_32BIT,
+ NULL, NULL, BUS_SPACE_MAXSIZE_32BIT, ISP_NSEGS,
+ BUS_SPACE_MAXADDR_24BIT, 0, busdma_lock_mutex, &Giant,
+ &sbs->dmat)) {
+ isp_prt(isp, ISP_LOGERR, "could not create master dma tag");
+ ISP_LOCK(isp);
+ return(1);
+ }
+
+ len = sizeof (XS_T **) * isp->isp_maxcmds;
+ isp->isp_xflist = (XS_T **) malloc(len, M_DEVBUF, M_WAITOK | M_ZERO);
+ if (isp->isp_xflist == NULL) {
+ isp_prt(isp, ISP_LOGERR, "cannot alloc xflist array");
+ ISP_LOCK(isp);
+ return (1);
+ }
+ len = sizeof (bus_dmamap_t) * isp->isp_maxcmds;
+ sbs->dmaps = (bus_dmamap_t *) malloc(len, M_DEVBUF, M_WAITOK);
+ if (sbs->dmaps == NULL) {
+ isp_prt(isp, ISP_LOGERR, "can't alloc dma map storage");
+ free(isp->isp_xflist, M_DEVBUF);
+ ISP_LOCK(isp);
+ return (1);
+ }
+
+ /*
+ * Allocate and map the request, result queues, plus FC scratch area.
+ */
+ len = ISP_QUEUE_SIZE(RQUEST_QUEUE_LEN(isp));
+ len += ISP_QUEUE_SIZE(RESULT_QUEUE_LEN(isp));
+
+ ns = (len / PAGE_SIZE) + 1;
+ if (bus_dma_tag_create(sbs->dmat, QENTRY_LEN, BUS_SPACE_MAXADDR_24BIT+1,
+ BUS_SPACE_MAXADDR_32BIT, BUS_SPACE_MAXADDR_32BIT, NULL, NULL,
+ len, ns, BUS_SPACE_MAXADDR_24BIT, 0, busdma_lock_mutex, &Giant,
+ &isp->isp_cdmat)) {
+ isp_prt(isp, ISP_LOGERR,
+ "cannot create a dma tag for control spaces");
+ free(sbs->dmaps, M_DEVBUF);
+ free(isp->isp_xflist, M_DEVBUF);
+ ISP_LOCK(isp);
+ return (1);
+ }
+
+ if (bus_dmamem_alloc(isp->isp_cdmat, (void **)&base, BUS_DMA_NOWAIT,
+ &isp->isp_cdmap) != 0) {
+ isp_prt(isp, ISP_LOGERR,
+ "cannot allocate %d bytes of CCB memory", len);
+ bus_dma_tag_destroy(isp->isp_cdmat);
+ free(isp->isp_xflist, M_DEVBUF);
+ free(sbs->dmaps, M_DEVBUF);
+ ISP_LOCK(isp);
+ return (1);
+ }
+
+ for (i = 0; i < isp->isp_maxcmds; i++) {
+ error = bus_dmamap_create(sbs->dmat, 0, &sbs->dmaps[i]);
+ if (error) {
+ isp_prt(isp, ISP_LOGERR,
+ "error %d creating per-cmd DMA maps", error);
+ while (--i >= 0) {
+ bus_dmamap_destroy(sbs->dmat, sbs->dmaps[i]);
+ }
+ goto bad;
+ }
+ }
+
+ im.isp = isp;
+ im.error = 0;
+ bus_dmamap_load(isp->isp_cdmat, isp->isp_cdmap, base, len, imc, &im, 0);
+ if (im.error) {
+ isp_prt(isp, ISP_LOGERR,
+ "error %d loading dma map for control areas", im.error);
+ goto bad;
+ }
+
+ isp->isp_rquest = base;
+ base += ISP_QUEUE_SIZE(RQUEST_QUEUE_LEN(isp));
+ ISP_LOCK(isp);
+ isp->isp_result = base;
+ return (0);
+
+bad:
+ bus_dmamem_free(isp->isp_cdmat, base, isp->isp_cdmap);
+ bus_dma_tag_destroy(isp->isp_cdmat);
+ free(isp->isp_xflist, M_DEVBUF);
+ free(sbs->dmaps, M_DEVBUF);
+ ISP_LOCK(isp);
+ isp->isp_rquest = NULL;
+ return (1);
+}
+
+typedef struct {
+ ispsoftc_t *isp;
+ void *cmd_token;
+ void *rq;
+ uint16_t *nxtip;
+ uint16_t optr;
+ int error;
+} mush_t;
+
+#define MUSHERR_NOQENTRIES -2
+
+
+static void dma2(void *, bus_dma_segment_t *, int, int);
+
+static void
+dma2(void *arg, bus_dma_segment_t *dm_segs, int nseg, int error)
+{
+ mush_t *mp;
+ ispsoftc_t *isp;
+ struct ccb_scsiio *csio;
+ struct isp_sbussoftc *sbs;
+ bus_dmamap_t *dp;
+ bus_dma_segment_t *eseg;
+ ispreq_t *rq;
+ int seglim, datalen;
+ uint16_t nxti;
+
+ mp = (mush_t *) arg;
+ if (error) {
+ mp->error = error;
+ return;
+ }
+
+ if (nseg < 1) {
+ isp_prt(mp->isp, ISP_LOGERR, "bad segment count (%d)", nseg);
+ mp->error = EFAULT;
+ return;
+ }
+ csio = mp->cmd_token;
+ isp = mp->isp;
+ rq = mp->rq;
+ sbs = (struct isp_sbussoftc *)mp->isp;
+ dp = &sbs->dmaps[isp_handle_index(rq->req_handle)];
+ nxti = *mp->nxtip;
+
+ if ((csio->ccb_h.flags & CAM_DIR_MASK) == CAM_DIR_IN) {
+ bus_dmamap_sync(sbs->dmat, *dp, BUS_DMASYNC_PREREAD);
+ } else {
+ bus_dmamap_sync(sbs->dmat, *dp, BUS_DMASYNC_PREWRITE);
+ }
+
+ datalen = XS_XFRLEN(csio);
+
+ /*
+ * We're passed an initial partially filled in entry that
+ * has most fields filled in except for data transfer
+ * related values.
+ *
+ * Our job is to fill in the initial request queue entry and
+ * then to start allocating and filling in continuation entries
+ * until we've covered the entire transfer.
+ */
+
+ if (csio->cdb_len > 12) {
+ seglim = 0;
+ } else {
+ seglim = ISP_RQDSEG;
+ }
+ if ((csio->ccb_h.flags & CAM_DIR_MASK) == CAM_DIR_IN) {
+ rq->req_flags |= REQFLAG_DATA_IN;
+ } else {
+ rq->req_flags |= REQFLAG_DATA_OUT;
+ }
+
+ eseg = dm_segs + nseg;
+
+ while (datalen != 0 && rq->req_seg_count < seglim && dm_segs != eseg) {
+ rq->req_dataseg[rq->req_seg_count].ds_base = dm_segs->ds_addr;
+ rq->req_dataseg[rq->req_seg_count].ds_count = dm_segs->ds_len;
+ datalen -= dm_segs->ds_len;
+ rq->req_seg_count++;
+ dm_segs++;
+ }
+
+ while (datalen > 0 && dm_segs != eseg) {
+ uint16_t onxti;
+ ispcontreq_t local, *crq = &local, *cqe;
+
+ cqe = (ispcontreq_t *) ISP_QUEUE_ENTRY(isp->isp_rquest, nxti);
+ onxti = nxti;
+ nxti = ISP_NXT_QENTRY(onxti, RQUEST_QUEUE_LEN(isp));
+ if (nxti == mp->optr) {
+ isp_prt(isp, ISP_LOGDEBUG0, "Request Queue Overflow++");
+ mp->error = MUSHERR_NOQENTRIES;
+ return;
+ }
+ rq->req_header.rqs_entry_count++;
+ MEMZERO((void *)crq, sizeof (*crq));
+ crq->req_header.rqs_entry_count = 1;
+ crq->req_header.rqs_entry_type = RQSTYPE_DATASEG;
+
+ seglim = 0;
+ while (datalen > 0 && seglim < ISP_CDSEG && dm_segs != eseg) {
+ crq->req_dataseg[seglim].ds_base =
+ dm_segs->ds_addr;
+ crq->req_dataseg[seglim].ds_count =
+ dm_segs->ds_len;
+ rq->req_seg_count++;
+ dm_segs++;
+ seglim++;
+ datalen -= dm_segs->ds_len;
+ }
+ isp_put_cont_req(isp, crq, cqe);
+ MEMORYBARRIER(isp, SYNC_REQUEST, onxti, QENTRY_LEN);
+ }
+ *mp->nxtip = nxti;
+}
+
+static int
+isp_sbus_dmasetup(ispsoftc_t *isp, struct ccb_scsiio *csio, ispreq_t *rq,
+ uint16_t *nxtip, uint16_t optr)
+{
+ struct isp_sbussoftc *sbs = (struct isp_sbussoftc *)isp;
+ ispreq_t *qep;
+ bus_dmamap_t *dp = NULL;
+ mush_t mush, *mp;
+ void (*eptr)(void *, bus_dma_segment_t *, int, int);
+
+ qep = (ispreq_t *) ISP_QUEUE_ENTRY(isp->isp_rquest, isp->isp_reqidx);
+ eptr = dma2;
+
+
+ if ((csio->ccb_h.flags & CAM_DIR_MASK) == CAM_DIR_NONE ||
+ (csio->dxfer_len == 0)) {
+ rq->req_seg_count = 1;
+ goto mbxsync;
+ }
+
+ /*
+ * Do a virtual grapevine step to collect info for
+ * the callback dma allocation that we have to use...
+ */
+ mp = &mush;
+ mp->isp = isp;
+ mp->cmd_token = csio;
+ mp->rq = rq;
+ mp->nxtip = nxtip;
+ mp->optr = optr;
+ mp->error = 0;
+
+ if ((csio->ccb_h.flags & CAM_SCATTER_VALID) == 0) {
+ if ((csio->ccb_h.flags & CAM_DATA_PHYS) == 0) {
+ int error, s;
+ dp = &sbs->dmaps[isp_handle_index(rq->req_handle)];
+ s = splsoftvm();
+ error = bus_dmamap_load(sbs->dmat, *dp,
+ csio->data_ptr, csio->dxfer_len, eptr, mp, 0);
+ if (error == EINPROGRESS) {
+ bus_dmamap_unload(sbs->dmat, *dp);
+ mp->error = EINVAL;
+ isp_prt(isp, ISP_LOGERR,
+ "deferred dma allocation not supported");
+ } else if (error && mp->error == 0) {
+#ifdef DIAGNOSTIC
+ isp_prt(isp, ISP_LOGERR,
+ "error %d in dma mapping code", error);
+#endif
+ mp->error = error;
+ }
+ splx(s);
+ } else {
+ /* Pointer to physical buffer */
+ struct bus_dma_segment seg;
+ seg.ds_addr = (bus_addr_t)csio->data_ptr;
+ seg.ds_len = csio->dxfer_len;
+ (*eptr)(mp, &seg, 1, 0);
+ }
+ } else {
+ struct bus_dma_segment *segs;
+
+ if ((csio->ccb_h.flags & CAM_DATA_PHYS) != 0) {
+ isp_prt(isp, ISP_LOGERR,
+ "Physical segment pointers unsupported");
+ mp->error = EINVAL;
+ } else if ((csio->ccb_h.flags & CAM_SG_LIST_PHYS) == 0) {
+ isp_prt(isp, ISP_LOGERR,
+ "Virtual segment addresses unsupported");
+ mp->error = EINVAL;
+ } else {
+ /* Just use the segments provided */
+ segs = (struct bus_dma_segment *) csio->data_ptr;
+ (*eptr)(mp, segs, csio->sglist_cnt, 0);
+ }
+ }
+ if (mp->error) {
+ int retval = CMD_COMPLETE;
+ if (mp->error == MUSHERR_NOQENTRIES) {
+ retval = CMD_EAGAIN;
+ } else if (mp->error == EFBIG) {
+ XS_SETERR(csio, CAM_REQ_TOO_BIG);
+ } else if (mp->error == EINVAL) {
+ XS_SETERR(csio, CAM_REQ_INVALID);
+ } else {
+ XS_SETERR(csio, CAM_UNREC_HBA_ERROR);
+ }
+ return (retval);
+ }
+mbxsync:
+ switch (rq->req_header.rqs_entry_type) {
+ case RQSTYPE_REQUEST:
+ isp_put_request(isp, rq, qep);
+ break;
+ case RQSTYPE_CMDONLY:
+ isp_put_extended_request(isp, (ispextreq_t *)rq,
+ (ispextreq_t *)qep);
+ break;
+ }
+ return (CMD_QUEUED);
+}
+
+static void
+isp_sbus_dmateardown(ispsoftc_t *isp, XS_T *xs, uint16_t handle)
+{
+ struct isp_sbussoftc *sbs = (struct isp_sbussoftc *)isp;
+ bus_dmamap_t *dp = &sbs->dmaps[isp_handle_index(handle)];
+ if ((xs->ccb_h.flags & CAM_DIR_MASK) == CAM_DIR_IN) {
+ bus_dmamap_sync(sbs->dmat, *dp, BUS_DMASYNC_POSTREAD);
+ } else {
+ bus_dmamap_sync(sbs->dmat, *dp, BUS_DMASYNC_POSTWRITE);
+ }
+ bus_dmamap_unload(sbs->dmat, *dp);
+}
+
+
+static void
+isp_sbus_reset1(ispsoftc_t *isp)
+{
+ /* enable interrupts */
+ ENABLE_INTS(isp);
+}
+
+static void
+isp_sbus_dumpregs(ispsoftc_t *isp, const char *msg)
+{
+ if (msg)
+ printf("%s: %s\n", device_get_nameunit(isp->isp_dev), msg);
+ else
+ printf("%s:\n", device_get_nameunit(isp->isp_dev));
+ printf(" biu_conf1=%x", ISP_READ(isp, BIU_CONF1));
+ printf(" biu_icr=%x biu_isr=%x biu_sema=%x ", ISP_READ(isp, BIU_ICR),
+ ISP_READ(isp, BIU_ISR), ISP_READ(isp, BIU_SEMA));
+ printf("risc_hccr=%x\n", ISP_READ(isp, HCCR));
+
+
+ ISP_WRITE(isp, HCCR, HCCR_CMD_PAUSE);
+ printf(" cdma_conf=%x cdma_sts=%x cdma_fifostat=%x\n",
+ ISP_READ(isp, CDMA_CONF), ISP_READ(isp, CDMA_STATUS),
+ ISP_READ(isp, CDMA_FIFO_STS));
+ printf(" ddma_conf=%x ddma_sts=%x ddma_fifostat=%x\n",
+ ISP_READ(isp, DDMA_CONF), ISP_READ(isp, DDMA_STATUS),
+ ISP_READ(isp, DDMA_FIFO_STS));
+ printf(" sxp_int=%x sxp_gross=%x sxp(scsi_ctrl)=%x\n",
+ ISP_READ(isp, SXP_INTERRUPT),
+ ISP_READ(isp, SXP_GROSS_ERR),
+ ISP_READ(isp, SXP_PINS_CTRL));
+ ISP_WRITE(isp, HCCR, HCCR_CMD_RELEASE);
+ printf(" mbox regs: %x %x %x %x %x\n",
+ ISP_READ(isp, OUTMAILBOX0), ISP_READ(isp, OUTMAILBOX1),
+ ISP_READ(isp, OUTMAILBOX2), ISP_READ(isp, OUTMAILBOX3),
+ ISP_READ(isp, OUTMAILBOX4));
+}
diff --git a/sys/dev/isp/isp_target.c b/sys/dev/isp/isp_target.c
new file mode 100644
index 000000000000..ebbd80b71114
--- /dev/null
+++ b/sys/dev/isp/isp_target.c
@@ -0,0 +1,1315 @@
+/*-
+ * Machine and OS Independent Target Mode Code for the Qlogic SCSI/FC adapters.
+ *
+ * Copyright (c) 1997-2006 by Matthew Jacob
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice 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.
+ *
+ * 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.
+ */
+/*
+ * Bug fixes gratefully acknowledged from:
+ * Oded Kedem <oded@kashya.com>
+ */
+/*
+ * Include header file appropriate for platform we're building on.
+ */
+
+#ifdef __NetBSD__
+#include <dev/ic/isp_netbsd.h>
+#endif
+#ifdef __FreeBSD__
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+#include <dev/isp/isp_freebsd.h>
+#endif
+#ifdef __OpenBSD__
+#include <dev/ic/isp_openbsd.h>
+#endif
+#ifdef __linux__
+#include "isp_linux.h"
+#endif
+
+#ifdef ISP_TARGET_MODE
+static const char atiocope[] =
+ "ATIO returned for lun %d because it was in the middle of Bus Device Reset "
+ "on bus %d";
+static const char atior[] =
+ "ATIO returned on for lun %d on from IID %d because a Bus Reset occurred "
+ "on bus %d";
+
+static void isp_got_msg(ispsoftc_t *, in_entry_t *);
+static void isp_got_msg_fc(ispsoftc_t *, in_fcentry_t *);
+static void isp_handle_atio(ispsoftc_t *, at_entry_t *);
+static void isp_handle_atio2(ispsoftc_t *, at2_entry_t *);
+static void isp_handle_ctio(ispsoftc_t *, ct_entry_t *);
+static void isp_handle_ctio2(ispsoftc_t *, ct2_entry_t *);
+
+/*
+ * The Qlogic driver gets an interrupt to look at response queue entries.
+ * Some of these are status completions for initiatior mode commands, but
+ * if target mode is enabled, we get a whole wad of response queue entries
+ * to be handled here.
+ *
+ * Basically the split into 3 main groups: Lun Enable/Modification responses,
+ * SCSI Command processing, and Immediate Notification events.
+ *
+ * You start by writing a request queue entry to enable target mode (and
+ * establish some resource limitations which you can modify later).
+ * The f/w responds with a LUN ENABLE or LUN MODIFY response with
+ * the status of this action. If the enable was successful, you can expect...
+ *
+ * Response queue entries with SCSI commands encapsulate show up in an ATIO
+ * (Accept Target IO) type- sometimes with enough info to stop the command at
+ * this level. Ultimately the driver has to feed back to the f/w's request
+ * queue a sequence of CTIOs (continue target I/O) that describe data to
+ * be moved and/or status to be sent) and finally finishing with sending
+ * to the f/w's response queue an ATIO which then completes the handshake
+ * with the f/w for that command. There's a lot of variations on this theme,
+ * including flags you can set in the CTIO for the Qlogic 2X00 fibre channel
+ * cards that 'auto-replenish' the f/w's ATIO count, but this is the basic
+ * gist of it.
+ *
+ * The third group that can show up in the response queue are Immediate
+ * Notification events. These include things like notifications of SCSI bus
+ * resets, or Bus Device Reset messages or other messages received. This
+ * a classic oddbins area. It can get a little weird because you then turn
+ * around and acknowledge the Immediate Notify by writing an entry onto the
+ * request queue and then the f/w turns around and gives you an acknowledgement
+ * to *your* acknowledgement on the response queue (the idea being to let
+ * the f/w tell you when the event is *really* over I guess).
+ *
+ */
+
+
+/*
+ * A new response queue entry has arrived. The interrupt service code
+ * has already swizzled it into the platform dependent from canonical form.
+ *
+ * Because of the way this driver is designed, unfortunately most of the
+ * actual synchronization work has to be done in the platform specific
+ * code- we have no synchroniation primitives in the common code.
+ */
+
+int
+isp_target_notify(ispsoftc_t *isp, void *vptr, uint16_t *optrp)
+{
+ uint16_t status, seqid;
+ union {
+ at_entry_t *atiop;
+ at2_entry_t *at2iop;
+ at2e_entry_t *at2eiop;
+ ct_entry_t *ctiop;
+ ct2_entry_t *ct2iop;
+ ct2e_entry_t *ct2eiop;
+ lun_entry_t *lunenp;
+ in_entry_t *inotp;
+ in_fcentry_t *inot_fcp;
+ in_fcentry_e_t *inote_fcp;
+ na_entry_t *nackp;
+ na_fcentry_t *nack_fcp;
+ na_fcentry_e_t *nacke_fcp;
+ isphdr_t *hp;
+ void * *vp;
+#define atiop unp.atiop
+#define at2iop unp.at2iop
+#define at2eiop unp.at2eiop
+#define ctiop unp.ctiop
+#define ct2iop unp.ct2iop
+#define ct2eiop unp.ct2eiop
+#define lunenp unp.lunenp
+#define inotp unp.inotp
+#define inot_fcp unp.inot_fcp
+#define inote_fcp unp.inote_fcp
+#define nackp unp.nackp
+#define nack_fcp unp.nack_fcp
+#define nacke_fcp unp.nacke_fcp
+#define hdrp unp.hp
+ } unp;
+ uint8_t local[QENTRY_LEN];
+ int bus, type, rval = 1;
+
+ type = isp_get_response_type(isp, (isphdr_t *)vptr);
+ unp.vp = vptr;
+
+ ISP_TDQE(isp, "isp_target_notify", (int) *optrp, vptr);
+
+ switch(type) {
+ case RQSTYPE_ATIO:
+ isp_get_atio(isp, atiop, (at_entry_t *) local);
+ isp_handle_atio(isp, (at_entry_t *) local);
+ break;
+ case RQSTYPE_CTIO:
+ isp_get_ctio(isp, ctiop, (ct_entry_t *) local);
+ isp_handle_ctio(isp, (ct_entry_t *) local);
+ break;
+ case RQSTYPE_ATIO2:
+ if (IS_2KLOGIN(isp)) {
+ isp_get_atio2e(isp, at2eiop, (at2e_entry_t *) local);
+ } else {
+ isp_get_atio2(isp, at2iop, (at2_entry_t *) local);
+ }
+ isp_handle_atio2(isp, (at2_entry_t *) local);
+ break;
+ case RQSTYPE_CTIO3:
+ case RQSTYPE_CTIO2:
+ if (IS_2KLOGIN(isp)) {
+ isp_get_ctio2e(isp, ct2eiop, (ct2e_entry_t *) local);
+ } else {
+ isp_get_ctio2(isp, ct2iop, (ct2_entry_t *) local);
+ }
+ isp_handle_ctio2(isp, (ct2_entry_t *) local);
+ break;
+ case RQSTYPE_ENABLE_LUN:
+ case RQSTYPE_MODIFY_LUN:
+ isp_get_enable_lun(isp, lunenp, (lun_entry_t *) local);
+ (void) isp_async(isp, ISPASYNC_TARGET_ACTION, local);
+ break;
+
+ case RQSTYPE_NOTIFY:
+ /*
+ * Either the ISP received a SCSI message it can't
+ * handle, or it's returning an Immed. Notify entry
+ * we sent. We can send Immed. Notify entries to
+ * increment the firmware's resource count for them
+ * (we set this initially in the Enable Lun entry).
+ */
+ bus = 0;
+ if (IS_FC(isp)) {
+ if (IS_2KLOGIN(isp)) {
+ isp_get_notify_fc_e(isp, inote_fcp,
+ (in_fcentry_e_t *)local);
+ } else {
+ isp_get_notify_fc(isp, inot_fcp,
+ (in_fcentry_t *)local);
+ }
+ inot_fcp = (in_fcentry_t *) local;
+ status = inot_fcp->in_status;
+ seqid = inot_fcp->in_seqid;
+ } else {
+ isp_get_notify(isp, inotp, (in_entry_t *)local);
+ inotp = (in_entry_t *) local;
+ status = inotp->in_status & 0xff;
+ seqid = inotp->in_seqid;
+ if (IS_DUALBUS(isp)) {
+ bus = GET_BUS_VAL(inotp->in_iid);
+ SET_BUS_VAL(inotp->in_iid, 0);
+ }
+ }
+
+ isp_prt(isp, ISP_LOGTDEBUG0,
+ "Immediate Notify On Bus %d, status=0x%x seqid=0x%x",
+ bus, status, seqid);
+
+ switch (status) {
+ case IN_MSG_RECEIVED:
+ case IN_IDE_RECEIVED:
+ if (IS_FC(isp)) {
+ isp_got_msg_fc(isp, (in_fcentry_t *)local);
+ } else {
+ isp_got_msg(isp, (in_entry_t *)local);
+ }
+ break;
+ case IN_RSRC_UNAVAIL:
+ isp_prt(isp, ISP_LOGWARN, "Firmware out of ATIOs");
+ isp_notify_ack(isp, local);
+ break;
+ case IN_RESET:
+ (void) isp_target_async(isp, 0, ASYNC_BUS_RESET);
+ break;
+ case IN_PORT_LOGOUT:
+ case IN_ABORT_TASK:
+ case IN_PORT_CHANGED:
+ case IN_GLOBAL_LOGO:
+ (void) isp_async(isp, ISPASYNC_TARGET_ACTION, &local);
+ break;
+ default:
+ isp_prt(isp, ISP_LOGERR,
+ "bad status (0x%x) in isp_target_notify", status);
+ isp_notify_ack(isp, local);
+ break;
+ }
+ break;
+
+ case RQSTYPE_NOTIFY_ACK:
+ /*
+ * The ISP is acknowledging our acknowledgement of an
+ * Immediate Notify entry for some asynchronous event.
+ */
+ if (IS_FC(isp)) {
+ if (IS_2KLOGIN(isp)) {
+ isp_get_notify_ack_fc_e(isp, nacke_fcp,
+ (na_fcentry_e_t *)local);
+ } else {
+ isp_get_notify_ack_fc(isp, nack_fcp,
+ (na_fcentry_t *)local);
+ }
+ nack_fcp = (na_fcentry_t *)local;
+ isp_prt(isp, ISP_LOGTDEBUG1,
+ "Notify Ack status=0x%x seqid 0x%x",
+ nack_fcp->na_status, nack_fcp->na_seqid);
+ } else {
+ isp_get_notify_ack(isp, nackp, (na_entry_t *)local);
+ nackp = (na_entry_t *)local;
+ isp_prt(isp, ISP_LOGTDEBUG1,
+ "Notify Ack event 0x%x status=0x%x seqid 0x%x",
+ nackp->na_event, nackp->na_status, nackp->na_seqid);
+ }
+ break;
+ default:
+ isp_prt(isp, ISP_LOGERR,
+ "Unknown entry type 0x%x in isp_target_notify", type);
+ rval = 0;
+ break;
+ }
+#undef atiop
+#undef at2iop
+#undef at2eiop
+#undef ctiop
+#undef ct2iop
+#undef ct2eiop
+#undef lunenp
+#undef inotp
+#undef inot_fcp
+#undef inote_fcp
+#undef nackp
+#undef nack_fcp
+#undef nacke_fcp
+#undef hdrp
+ return (rval);
+}
+
+
+/*
+ * Toggle (on/off) target mode for bus/target/lun
+ *
+ * The caller has checked for overlap and legality.
+ *
+ * Note that not all of bus, target or lun can be paid attention to.
+ * Note also that this action will not be complete until the f/w writes
+ * response entry. The caller is responsible for synchronizing this.
+ */
+int
+isp_lun_cmd(ispsoftc_t *isp, int cmd, int bus, int tgt, int lun,
+ int cmd_cnt, int inot_cnt, uint32_t opaque)
+{
+ lun_entry_t el;
+ uint16_t nxti, optr;
+ void *outp;
+
+
+ MEMZERO(&el, sizeof (el));
+ if (IS_DUALBUS(isp)) {
+ el.le_rsvd = (bus & 0x1) << 7;
+ }
+ el.le_cmd_count = cmd_cnt;
+ el.le_in_count = inot_cnt;
+ if (cmd == RQSTYPE_ENABLE_LUN) {
+ if (IS_SCSI(isp)) {
+ el.le_flags = LUN_TQAE|LUN_DISAD;
+ el.le_cdb6len = 12;
+ el.le_cdb7len = 12;
+ }
+ } else if (cmd == -RQSTYPE_ENABLE_LUN) {
+ cmd = RQSTYPE_ENABLE_LUN;
+ el.le_cmd_count = 0;
+ el.le_in_count = 0;
+ } else if (cmd == -RQSTYPE_MODIFY_LUN) {
+ cmd = RQSTYPE_MODIFY_LUN;
+ el.le_ops = LUN_CCDECR | LUN_INDECR;
+ } else {
+ el.le_ops = LUN_CCINCR | LUN_ININCR;
+ }
+ el.le_header.rqs_entry_type = cmd;
+ el.le_header.rqs_entry_count = 1;
+ el.le_reserved = opaque;
+ if (IS_SCSI(isp)) {
+ el.le_tgt = tgt;
+ el.le_lun = lun;
+ } else if ((FCPARAM(isp)->isp_fwattr & ISP_FW_ATTR_SCCLUN) == 0) {
+ el.le_lun = lun;
+ }
+
+ if (isp_getrqentry(isp, &nxti, &optr, &outp)) {
+ isp_prt(isp, ISP_LOGERR,
+ "Request Queue Overflow in isp_lun_cmd");
+ return (-1);
+ }
+ ISP_TDQE(isp, "isp_lun_cmd", (int) optr, &el);
+ isp_put_enable_lun(isp, &el, outp);
+ ISP_ADD_REQUEST(isp, nxti);
+ return (0);
+}
+
+
+int
+isp_target_put_entry(ispsoftc_t *isp, void *ap)
+{
+ void *outp;
+ uint16_t nxti, optr;
+ uint8_t etype = ((isphdr_t *) ap)->rqs_entry_type;
+
+ if (isp_getrqentry(isp, &nxti, &optr, &outp)) {
+ isp_prt(isp, ISP_LOGWARN,
+ "Request Queue Overflow in isp_target_put_entry");
+ return (-1);
+ }
+ switch (etype) {
+ case RQSTYPE_ATIO:
+ isp_put_atio(isp, (at_entry_t *) ap, (at_entry_t *) outp);
+ break;
+ case RQSTYPE_ATIO2:
+ if (IS_2KLOGIN(isp)) {
+ isp_put_atio2e(isp, (at2e_entry_t *) ap, (at2e_entry_t *) outp);
+ } else {
+ isp_put_atio2(isp, (at2_entry_t *) ap, (at2_entry_t *) outp);
+ }
+ break;
+ case RQSTYPE_CTIO:
+ isp_put_ctio(isp, (ct_entry_t *) ap, (ct_entry_t *) outp);
+ break;
+ case RQSTYPE_CTIO2:
+ if (IS_2KLOGIN(isp)) {
+ isp_put_ctio2e(isp, (ct2e_entry_t *) ap, (ct2e_entry_t *) outp);
+ } else {
+ isp_put_ctio2(isp, (ct2_entry_t *) ap, (ct2_entry_t *) outp);
+ }
+ break;
+ default:
+ isp_prt(isp, ISP_LOGERR,
+ "Unknown type 0x%x in isp_put_entry", etype);
+ return (-1);
+ }
+
+ ISP_TDQE(isp, "isp_target_put_entry", (int) optr, ap);
+ ISP_ADD_REQUEST(isp, nxti);
+ return (0);
+}
+
+int
+isp_target_put_atio(ispsoftc_t *isp, void *arg)
+{
+ union {
+ at_entry_t _atio;
+ at2_entry_t _atio2;
+ at2e_entry_t _atio2e;
+ } atun;
+
+ MEMZERO(&atun, sizeof atun);
+ if (IS_FC(isp)) {
+ at2_entry_t *aep = arg;
+ atun._atio2.at_header.rqs_entry_type = RQSTYPE_ATIO2;
+ atun._atio2.at_header.rqs_entry_count = 1;
+ if (FCPARAM(isp)->isp_fwattr & ISP_FW_ATTR_SCCLUN) {
+ atun._atio2.at_scclun = (uint16_t) aep->at_scclun;
+ } else {
+ atun._atio2.at_lun = (uint8_t) aep->at_lun;
+ }
+ if (IS_2KLOGIN(isp)) {
+ atun._atio2e.at_iid = ((at2e_entry_t *)aep)->at_iid;
+ } else {
+ atun._atio2.at_iid = aep->at_iid;
+ }
+ atun._atio2.at_rxid = aep->at_rxid;
+ atun._atio2.at_status = CT_OK;
+ } else {
+ at_entry_t *aep = arg;
+ atun._atio.at_header.rqs_entry_type = RQSTYPE_ATIO;
+ atun._atio.at_header.rqs_entry_count = 1;
+ atun._atio.at_handle = aep->at_handle;
+ atun._atio.at_iid = aep->at_iid;
+ atun._atio.at_tgt = aep->at_tgt;
+ atun._atio.at_lun = aep->at_lun;
+ atun._atio.at_tag_type = aep->at_tag_type;
+ atun._atio.at_tag_val = aep->at_tag_val;
+ atun._atio.at_status = (aep->at_flags & AT_TQAE);
+ atun._atio.at_status |= CT_OK;
+ }
+ return (isp_target_put_entry(isp, &atun));
+}
+
+/*
+ * Command completion- both for handling cases of no resources or
+ * no blackhole driver, or other cases where we have to, inline,
+ * finish the command sanely, or for normal command completion.
+ *
+ * The 'completion' code value has the scsi status byte in the low 8 bits.
+ * If status is a CHECK CONDITION and bit 8 is nonzero, then bits 12..15 have
+ * the sense key and bits 16..23 have the ASCQ and bits 24..31 have the ASC
+ * values.
+ *
+ * NB: the key, asc, ascq, cannot be used for parallel SCSI as it doesn't
+ * NB: inline SCSI sense reporting. As such, we lose this information. XXX.
+ *
+ * For both parallel && fibre channel, we use the feature that does
+ * an automatic resource autoreplenish so we don't have then later do
+ * put of an atio to replenish the f/w's resource count.
+ */
+
+int
+isp_endcmd(ispsoftc_t *isp, void *arg, uint32_t code, uint16_t hdl)
+{
+ int sts;
+ union {
+ ct_entry_t _ctio;
+ ct2_entry_t _ctio2;
+ ct2e_entry_t _ctio2e;
+ } un;
+
+ MEMZERO(&un, sizeof un);
+ sts = code & 0xff;
+
+ if (IS_FC(isp)) {
+ at2_entry_t *aep = arg;
+ ct2_entry_t *cto = &un._ctio2;
+
+ cto->ct_header.rqs_entry_type = RQSTYPE_CTIO2;
+ cto->ct_header.rqs_entry_count = 1;
+ if ((FCPARAM(isp)->isp_fwattr & ISP_FW_ATTR_SCCLUN) == 0) {
+ cto->ct_lun = aep->at_lun;
+ }
+ if (IS_2KLOGIN(isp)) {
+ un._ctio2e.ct_iid = ((at2e_entry_t *)aep)->at_iid;
+ } else {
+ cto->ct_iid = aep->at_iid;
+ }
+ cto->ct_rxid = aep->at_rxid;
+ cto->rsp.m1.ct_scsi_status = sts;
+ cto->ct_flags = CT2_SENDSTATUS | CT2_NO_DATA | CT2_FLAG_MODE1;
+ if (hdl == 0) {
+ cto->ct_flags |= CT2_CCINCR;
+ }
+ if (aep->at_datalen) {
+ cto->ct_resid = aep->at_datalen;
+ cto->rsp.m1.ct_scsi_status |= CT2_DATA_UNDER;
+ }
+ if (sts == SCSI_CHECK && (code & ECMD_SVALID)) {
+ cto->rsp.m1.ct_resp[0] = 0xf0;
+ cto->rsp.m1.ct_resp[2] = (code >> 12) & 0xf;
+ cto->rsp.m1.ct_resp[7] = 8;
+ cto->rsp.m1.ct_resp[12] = (code >> 24) & 0xff;
+ cto->rsp.m1.ct_resp[13] = (code >> 16) & 0xff;
+ cto->rsp.m1.ct_senselen = 16;
+ cto->rsp.m1.ct_scsi_status |= CT2_SNSLEN_VALID;
+ }
+ cto->ct_syshandle = hdl;
+ } else {
+ at_entry_t *aep = arg;
+ ct_entry_t *cto = &un._ctio;
+
+ cto->ct_header.rqs_entry_type = RQSTYPE_CTIO;
+ cto->ct_header.rqs_entry_count = 1;
+ cto->ct_fwhandle = aep->at_handle;
+ cto->ct_iid = aep->at_iid;
+ cto->ct_tgt = aep->at_tgt;
+ cto->ct_lun = aep->at_lun;
+ cto->ct_tag_type = aep->at_tag_type;
+ cto->ct_tag_val = aep->at_tag_val;
+ if (aep->at_flags & AT_TQAE) {
+ cto->ct_flags |= CT_TQAE;
+ }
+ cto->ct_flags = CT_SENDSTATUS | CT_NO_DATA;
+ if (hdl == 0) {
+ cto->ct_flags |= CT_CCINCR;
+ }
+ cto->ct_scsi_status = sts;
+ cto->ct_syshandle = hdl;
+ }
+ return (isp_target_put_entry(isp, &un));
+}
+
+/*
+ * These are either broadcast events or specifically CTIO fast completion
+ */
+int
+isp_target_async(ispsoftc_t *isp, int bus, int event)
+{
+ tmd_notify_t notify;
+
+ MEMZERO(&notify, sizeof (tmd_notify_t));
+ notify.nt_hba = isp;
+ notify.nt_iid = INI_ANY;
+ /* nt_tgt set in outer layers */
+ notify.nt_lun = LUN_ANY;
+ notify.nt_tagval = TAG_ANY;
+
+ if (IS_SCSI(isp)) {
+ TAG_INSERT_BUS(notify.nt_tagval, bus);
+ }
+
+ switch (event) {
+ case ASYNC_LOOP_UP:
+ case ASYNC_PTPMODE:
+ notify.nt_ncode = NT_LINK_UP;
+ (void) isp_async(isp, ISPASYNC_TARGET_NOTIFY, &notify);
+ break;
+ case ASYNC_LOOP_DOWN:
+ notify.nt_ncode = NT_LINK_DOWN;
+ (void) isp_async(isp, ISPASYNC_TARGET_NOTIFY, &notify);
+ break;
+ case ASYNC_LIP_F8:
+ case ASYNC_LIP_OCCURRED:
+ case ASYNC_LOOP_RESET:
+ notify.nt_ncode = NT_LIP_RESET;
+ (void) isp_async(isp, ISPASYNC_TARGET_NOTIFY, &notify);
+ break;
+ case ASYNC_BUS_RESET:
+ case ASYNC_TIMEOUT_RESET: /* XXX: where does this come from ? */
+ notify.nt_ncode = NT_BUS_RESET;
+ (void) isp_async(isp, ISPASYNC_TARGET_NOTIFY, &notify);
+ break;
+ case ASYNC_DEVICE_RESET:
+ notify.nt_ncode = NT_TARGET_RESET;
+ (void) isp_async(isp, ISPASYNC_TARGET_NOTIFY, &notify);
+ break;
+ case ASYNC_CTIO_DONE:
+ {
+ uint8_t storage[QENTRY_LEN];
+ memset(storage, 0, QENTRY_LEN);
+ if (IS_FC(isp)) {
+ /* This should also suffice for 2K login code */
+ ct2_entry_t *ct = (ct2_entry_t *) storage;
+ ct->ct_header.rqs_entry_type = RQSTYPE_CTIO2;
+ ct->ct_status = CT_OK;
+ ct->ct_syshandle = bus;
+ ct->ct_flags = CT2_SENDSTATUS|CT2_FASTPOST;
+ } else {
+ ct_entry_t *ct = (ct_entry_t *) storage;
+ ct->ct_header.rqs_entry_type = RQSTYPE_CTIO;
+ ct->ct_status = CT_OK;
+ ct->ct_fwhandle = bus;
+ ct->ct_flags = CT_SENDSTATUS;
+ }
+ (void) isp_async(isp, ISPASYNC_TARGET_ACTION, storage);
+ return (0);
+ }
+ default:
+ isp_prt(isp, ISP_LOGERR,
+ "isp_target_async: unknown event 0x%x", event);
+ if (isp->isp_state == ISP_RUNSTATE) {
+ isp_notify_ack(isp, NULL);
+ }
+ break;
+ }
+ return (0);
+}
+
+
+/*
+ * Process a received message.
+ * The ISP firmware can handle most messages, there are only
+ * a few that we need to deal with:
+ * - abort: clean up the current command
+ * - abort tag and clear queue
+ */
+
+static void
+isp_got_msg(ispsoftc_t *isp, in_entry_t *inp)
+{
+ tmd_notify_t nt;
+ uint8_t status = inp->in_status & ~QLTM_SVALID;
+
+ MEMZERO(&nt, sizeof (nt));
+ nt.nt_hba = isp;
+ nt.nt_iid = GET_IID_VAL(inp->in_iid);
+ nt.nt_tgt = inp->in_tgt;
+ nt.nt_lun = inp->in_lun;
+ IN_MAKE_TAGID(nt.nt_tagval, 0, inp);
+ nt.nt_lreserved = inp;
+
+ if (status == IN_IDE_RECEIVED || status == IN_MSG_RECEIVED) {
+ switch (inp->in_msg[0]) {
+ case MSG_ABORT:
+ nt.nt_ncode = NT_ABORT_TASK_SET;
+ break;
+ case MSG_BUS_DEV_RESET:
+ nt.nt_ncode = NT_TARGET_RESET;
+ break;
+ case MSG_ABORT_TAG:
+ nt.nt_ncode = NT_ABORT_TASK;
+ break;
+ case MSG_CLEAR_QUEUE:
+ nt.nt_ncode = NT_CLEAR_TASK_SET;
+ break;
+ case MSG_REL_RECOVERY:
+ nt.nt_ncode = NT_CLEAR_ACA;
+ break;
+ case MSG_TERM_IO_PROC:
+ nt.nt_ncode = NT_ABORT_TASK;
+ break;
+ case MSG_LUN_RESET:
+ nt.nt_ncode = NT_LUN_RESET;
+ break;
+ default:
+ isp_prt(isp, ISP_LOGERR,
+ "unhandled message 0x%x", inp->in_msg[0]);
+ isp_notify_ack(isp, inp);
+ return;
+ }
+ (void) isp_async(isp, ISPASYNC_TARGET_NOTIFY, &nt);
+ } else {
+ isp_prt(isp, ISP_LOGERR,
+ "unknown immediate notify status 0x%x", inp->in_status);
+ isp_notify_ack(isp, inp);
+ }
+}
+
+/*
+ * Synthesize a message from the task management flags in a FCP_CMND_IU.
+ */
+static void
+isp_got_msg_fc(ispsoftc_t *isp, in_fcentry_t *inp)
+{
+ tmd_notify_t nt;
+ static const char f1[] = "%s from loop id %d lun %d seq 0x%x";
+ static const char f2[] =
+ "unknown %s 0x%x lun %d loop id %d task flags 0x%x seq 0x%x\n";
+ uint16_t seqid, loopid;
+
+ MEMZERO(&nt, sizeof (tmd_notify_t));
+ nt.nt_hba = isp;
+ if (IS_2KLOGIN(isp)) {
+ nt.nt_iid = ((in_fcentry_e_t *)inp)->in_iid;
+ loopid = ((in_fcentry_e_t *)inp)->in_iid;
+ seqid = ((in_fcentry_e_t *)inp)->in_seqid;
+ } else {
+ nt.nt_iid = inp->in_iid;
+ loopid = inp->in_iid;
+ seqid = inp->in_seqid;
+ }
+ /* nt_tgt set in outer layers */
+ if (FCPARAM(isp)->isp_fwattr & ISP_FW_ATTR_SCCLUN) {
+ nt.nt_lun = inp->in_scclun;
+ } else {
+ nt.nt_lun = inp->in_lun;
+ }
+ IN_FC_MAKE_TAGID(nt.nt_tagval, 0, seqid);
+ nt.nt_lreserved = inp;
+
+ if (inp->in_status != IN_MSG_RECEIVED) {
+ isp_prt(isp, ISP_LOGINFO, f2, "immediate notify status",
+ inp->in_status, nt.nt_lun, loopid, inp->in_task_flags,
+ inp->in_seqid);
+ isp_notify_ack(isp, inp);
+ return;
+ }
+
+ if (inp->in_task_flags & TASK_FLAGS_ABORT_TASK_SET) {
+ isp_prt(isp, ISP_LOGINFO, f1, "ABORT TASK SET",
+ loopid, nt.nt_lun, inp->in_seqid);
+ nt.nt_ncode = NT_ABORT_TASK_SET;
+ } else if (inp->in_task_flags & TASK_FLAGS_CLEAR_TASK_SET) {
+ isp_prt(isp, ISP_LOGINFO, f1, "CLEAR TASK SET",
+ loopid, nt.nt_lun, inp->in_seqid);
+ nt.nt_ncode = NT_CLEAR_TASK_SET;
+ } else if (inp->in_task_flags & TASK_FLAGS_LUN_RESET) {
+ isp_prt(isp, ISP_LOGINFO, f1, "LUN RESET",
+ loopid, nt.nt_lun, inp->in_seqid);
+ nt.nt_ncode = NT_LUN_RESET;
+ } else if (inp->in_task_flags & TASK_FLAGS_TARGET_RESET) {
+ isp_prt(isp, ISP_LOGINFO, f1, "TARGET RESET",
+ loopid, nt.nt_lun, inp->in_seqid);
+ nt.nt_ncode = NT_TARGET_RESET;
+ } else if (inp->in_task_flags & TASK_FLAGS_CLEAR_ACA) {
+ isp_prt(isp, ISP_LOGINFO, f1, "CLEAR ACA",
+ loopid, nt.nt_lun, inp->in_seqid);
+ nt.nt_ncode = NT_CLEAR_ACA;
+ } else {
+ isp_prt(isp, ISP_LOGWARN, f2, "task flag", inp->in_status,
+ nt.nt_lun, loopid, inp->in_task_flags, inp->in_seqid);
+ isp_notify_ack(isp, inp);
+ return;
+ }
+ (void) isp_async(isp, ISPASYNC_TARGET_NOTIFY, &nt);
+}
+
+void
+isp_notify_ack(ispsoftc_t *isp, void *arg)
+{
+ char storage[QENTRY_LEN];
+ uint16_t nxti, optr;
+ void *outp;
+
+ if (isp_getrqentry(isp, &nxti, &optr, &outp)) {
+ isp_prt(isp, ISP_LOGWARN,
+ "Request Queue Overflow For isp_notify_ack");
+ return;
+ }
+
+ MEMZERO(storage, QENTRY_LEN);
+
+ if (IS_FC(isp)) {
+ na_fcentry_t *na = (na_fcentry_t *) storage;
+ int iid = 0;
+
+ if (arg) {
+ in_fcentry_t *inp = arg;
+ MEMCPY(storage, arg, sizeof (isphdr_t));
+ if (IS_2KLOGIN(isp)) {
+ ((na_fcentry_e_t *)na)->na_iid =
+ ((in_fcentry_e_t *)inp)->in_iid;
+ iid = ((na_fcentry_e_t *)na)->na_iid;
+ } else {
+ na->na_iid = inp->in_iid;
+ iid = na->na_iid;
+ }
+ na->na_task_flags =
+ inp->in_task_flags & TASK_FLAGS_RESERVED_MASK;
+ na->na_seqid = inp->in_seqid;
+ na->na_flags = NAFC_RCOUNT;
+ na->na_status = inp->in_status;
+ if (inp->in_status == IN_RESET) {
+ na->na_flags |= NAFC_RST_CLRD;
+ }
+ if (inp->in_status == IN_MSG_RECEIVED) {
+ na->na_flags |= NAFC_TVALID;
+ na->na_response = 0; /* XXX SUCCEEDED XXX */
+ }
+ } else {
+ na->na_flags = NAFC_RST_CLRD;
+ }
+ na->na_header.rqs_entry_type = RQSTYPE_NOTIFY_ACK;
+ na->na_header.rqs_entry_count = 1;
+ if (IS_2KLOGIN(isp)) {
+ isp_put_notify_ack_fc_e(isp, (na_fcentry_e_t *) na,
+ (na_fcentry_e_t *)outp);
+ } else {
+ isp_put_notify_ack_fc(isp, na, (na_fcentry_t *)outp);
+ }
+ isp_prt(isp, ISP_LOGTDEBUG0, "notify ack iid %u seqid %x flags "
+ "%x tflags %x response %x", iid, na->na_seqid,
+ na->na_flags, na->na_task_flags, na->na_response);
+ } else {
+ na_entry_t *na = (na_entry_t *) storage;
+ if (arg) {
+ in_entry_t *inp = arg;
+ MEMCPY(storage, arg, sizeof (isphdr_t));
+ na->na_iid = inp->in_iid;
+ na->na_lun = inp->in_lun;
+ na->na_tgt = inp->in_tgt;
+ na->na_seqid = inp->in_seqid;
+ if (inp->in_status == IN_RESET) {
+ na->na_event = NA_RST_CLRD;
+ }
+ } else {
+ na->na_event = NA_RST_CLRD;
+ }
+ na->na_header.rqs_entry_type = RQSTYPE_NOTIFY_ACK;
+ na->na_header.rqs_entry_count = 1;
+ isp_put_notify_ack(isp, na, (na_entry_t *)outp);
+ isp_prt(isp, ISP_LOGTDEBUG0, "notify ack iid %u lun %u tgt %u "
+ "seqid %x event %x", na->na_iid, na->na_lun, na->na_tgt,
+ na->na_seqid, na->na_event);
+ }
+ ISP_TDQE(isp, "isp_notify_ack", (int) optr, storage);
+ ISP_ADD_REQUEST(isp, nxti);
+}
+
+static void
+isp_handle_atio(ispsoftc_t *isp, at_entry_t *aep)
+{
+ int lun;
+ lun = aep->at_lun;
+ /*
+ * The firmware status (except for the QLTM_SVALID bit) indicates
+ * why this ATIO was sent to us.
+ *
+ * If QLTM_SVALID is set, the firware has recommended Sense Data.
+ *
+ * If the DISCONNECTS DISABLED bit is set in the flags field,
+ * we're still connected on the SCSI bus - i.e. the initiator
+ * did not set DiscPriv in the identify message. We don't care
+ * about this so it's ignored.
+ */
+
+ switch(aep->at_status & ~QLTM_SVALID) {
+ case AT_PATH_INVALID:
+ /*
+ * ATIO rejected by the firmware due to disabled lun.
+ */
+ isp_prt(isp, ISP_LOGERR,
+ "rejected ATIO for disabled lun %d", lun);
+ break;
+ case AT_NOCAP:
+ /*
+ * Requested Capability not available
+ * We sent an ATIO that overflowed the firmware's
+ * command resource count.
+ */
+ isp_prt(isp, ISP_LOGERR,
+ "rejected ATIO for lun %d because of command count"
+ " overflow", lun);
+ break;
+
+ case AT_BDR_MSG:
+ /*
+ * If we send an ATIO to the firmware to increment
+ * its command resource count, and the firmware is
+ * recovering from a Bus Device Reset, it returns
+ * the ATIO with this status. We set the command
+ * resource count in the Enable Lun entry and do
+ * not increment it. Therefore we should never get
+ * this status here.
+ */
+ isp_prt(isp, ISP_LOGERR, atiocope, lun,
+ GET_BUS_VAL(aep->at_iid));
+ break;
+
+ case AT_CDB: /* Got a CDB */
+ case AT_PHASE_ERROR: /* Bus Phase Sequence Error */
+ /*
+ * Punt to platform specific layer.
+ */
+ (void) isp_async(isp, ISPASYNC_TARGET_ACTION, aep);
+ break;
+
+ case AT_RESET:
+ /*
+ * A bus reset came along and blew away this command. Why
+ * they do this in addition the async event code stuff,
+ * I dunno.
+ *
+ * Ignore it because the async event will clear things
+ * up for us.
+ */
+ isp_prt(isp, ISP_LOGWARN, atior, lun,
+ GET_IID_VAL(aep->at_iid), GET_BUS_VAL(aep->at_iid));
+ break;
+
+
+ default:
+ isp_prt(isp, ISP_LOGERR,
+ "Unknown ATIO status 0x%x from initiator %d for lun %d",
+ aep->at_status, aep->at_iid, lun);
+ (void) isp_target_put_atio(isp, aep);
+ break;
+ }
+}
+
+static void
+isp_handle_atio2(ispsoftc_t *isp, at2_entry_t *aep)
+{
+ int lun, iid;
+
+ if (FCPARAM(isp)->isp_fwattr & ISP_FW_ATTR_SCCLUN) {
+ lun = aep->at_scclun;
+ } else {
+ lun = aep->at_lun;
+ }
+
+ if (IS_2KLOGIN(isp)) {
+ iid = ((at2e_entry_t *)aep)->at_iid;
+ } else {
+ iid = aep->at_iid;
+ }
+
+ /*
+ * The firmware status (except for the QLTM_SVALID bit) indicates
+ * why this ATIO was sent to us.
+ *
+ * If QLTM_SVALID is set, the firware has recommended Sense Data.
+ *
+ * If the DISCONNECTS DISABLED bit is set in the flags field,
+ * we're still connected on the SCSI bus - i.e. the initiator
+ * did not set DiscPriv in the identify message. We don't care
+ * about this so it's ignored.
+ */
+
+ switch(aep->at_status & ~QLTM_SVALID) {
+ case AT_PATH_INVALID:
+ /*
+ * ATIO rejected by the firmware due to disabled lun.
+ */
+ isp_prt(isp, ISP_LOGERR,
+ "rejected ATIO2 for disabled lun %d", lun);
+ break;
+ case AT_NOCAP:
+ /*
+ * Requested Capability not available
+ * We sent an ATIO that overflowed the firmware's
+ * command resource count.
+ */
+ isp_prt(isp, ISP_LOGERR,
+ "rejected ATIO2 for lun %d- command count overflow", lun);
+ break;
+
+ case AT_BDR_MSG:
+ /*
+ * If we send an ATIO to the firmware to increment
+ * its command resource count, and the firmware is
+ * recovering from a Bus Device Reset, it returns
+ * the ATIO with this status. We set the command
+ * resource count in the Enable Lun entry and no
+ * not increment it. Therefore we should never get
+ * this status here.
+ */
+ isp_prt(isp, ISP_LOGERR, atiocope, lun, 0);
+ break;
+
+ case AT_CDB: /* Got a CDB */
+ /*
+ * Punt to platform specific layer.
+ */
+ (void) isp_async(isp, ISPASYNC_TARGET_ACTION, aep);
+ break;
+
+ case AT_RESET:
+ /*
+ * A bus reset came along an blew away this command. Why
+ * they do this in addition the async event code stuff,
+ * I dunno.
+ *
+ * Ignore it because the async event will clear things
+ * up for us.
+ */
+ isp_prt(isp, ISP_LOGERR, atior, lun, iid, 0);
+ break;
+
+
+ default:
+ isp_prt(isp, ISP_LOGERR,
+ "Unknown ATIO2 status 0x%x from initiator %d for lun %d",
+ aep->at_status, iid, lun);
+ (void) isp_target_put_atio(isp, aep);
+ break;
+ }
+}
+
+static void
+isp_handle_ctio(ispsoftc_t *isp, ct_entry_t *ct)
+{
+ void *xs;
+ int pl = ISP_LOGTDEBUG2;
+ char *fmsg = NULL;
+
+ if (ct->ct_syshandle) {
+ xs = isp_find_xs_tgt(isp, ct->ct_syshandle);
+ if (xs == NULL)
+ pl = ISP_LOGALL;
+ } else {
+ xs = NULL;
+ }
+
+ switch(ct->ct_status & ~QLTM_SVALID) {
+ case CT_OK:
+ /*
+ * There are generally 3 possibilities as to why we'd get
+ * this condition:
+ * We disconnected after receiving a CDB.
+ * We sent or received data.
+ * We sent status & command complete.
+ */
+
+ if (ct->ct_flags & CT_SENDSTATUS) {
+ break;
+ } else if ((ct->ct_flags & CT_DATAMASK) == CT_NO_DATA) {
+ /*
+ * Nothing to do in this case.
+ */
+ isp_prt(isp, pl, "CTIO- iid %d disconnected OK",
+ ct->ct_iid);
+ return;
+ }
+ break;
+
+ case CT_BDR_MSG:
+ /*
+ * Bus Device Reset message received or the SCSI Bus has
+ * been Reset; the firmware has gone to Bus Free.
+ *
+ * The firmware generates an async mailbox interupt to
+ * notify us of this and returns outstanding CTIOs with this
+ * status. These CTIOs are handled in that same way as
+ * CT_ABORTED ones, so just fall through here.
+ */
+ fmsg = "Bus Device Reset";
+ /*FALLTHROUGH*/
+ case CT_RESET:
+ if (fmsg == NULL)
+ fmsg = "Bus Reset";
+ /*FALLTHROUGH*/
+ case CT_ABORTED:
+ /*
+ * When an Abort message is received the firmware goes to
+ * Bus Free and returns all outstanding CTIOs with the status
+ * set, then sends us an Immediate Notify entry.
+ */
+ if (fmsg == NULL)
+ fmsg = "ABORT TAG message sent by Initiator";
+
+ isp_prt(isp, ISP_LOGWARN, "CTIO destroyed by %s", fmsg);
+ break;
+
+ case CT_INVAL:
+ /*
+ * CTIO rejected by the firmware due to disabled lun.
+ * "Cannot Happen".
+ */
+ isp_prt(isp, ISP_LOGERR,
+ "Firmware rejected CTIO for disabled lun %d",
+ ct->ct_lun);
+ break;
+
+ case CT_NOPATH:
+ /*
+ * CTIO rejected by the firmware due "no path for the
+ * nondisconnecting nexus specified". This means that
+ * we tried to access the bus while a non-disconnecting
+ * command is in process.
+ */
+ isp_prt(isp, ISP_LOGERR,
+ "Firmware rejected CTIO for bad nexus %d/%d/%d",
+ ct->ct_iid, ct->ct_tgt, ct->ct_lun);
+ break;
+
+ case CT_RSELTMO:
+ fmsg = "Reselection";
+ /*FALLTHROUGH*/
+ case CT_TIMEOUT:
+ if (fmsg == NULL)
+ fmsg = "Command";
+ isp_prt(isp, ISP_LOGERR, "Firmware timed out on %s", fmsg);
+ break;
+
+ case CT_PANIC:
+ if (fmsg == NULL)
+ fmsg = "Unrecoverable Error";
+ /*FALLTHROUGH*/
+ case CT_ERR:
+ if (fmsg == NULL)
+ fmsg = "Completed with Error";
+ /*FALLTHROUGH*/
+ case CT_PHASE_ERROR:
+ if (fmsg == NULL)
+ fmsg = "Phase Sequence Error";
+ /*FALLTHROUGH*/
+ case CT_TERMINATED:
+ if (fmsg == NULL)
+ fmsg = "terminated by TERMINATE TRANSFER";
+ /*FALLTHROUGH*/
+ case CT_NOACK:
+ if (fmsg == NULL)
+ fmsg = "unacknowledged Immediate Notify pending";
+ isp_prt(isp, ISP_LOGERR, "CTIO returned by f/w- %s", fmsg);
+ break;
+ default:
+ isp_prt(isp, ISP_LOGERR, "Unknown CTIO status 0x%x",
+ ct->ct_status & ~QLTM_SVALID);
+ break;
+ }
+
+ if (xs == NULL) {
+ /*
+ * There may be more than one CTIO for a data transfer,
+ * or this may be a status CTIO we're not monitoring.
+ *
+ * The assumption is that they'll all be returned in the
+ * order we got them.
+ */
+ if (ct->ct_syshandle == 0) {
+ if ((ct->ct_flags & CT_SENDSTATUS) == 0) {
+ isp_prt(isp, pl,
+ "intermediate CTIO completed ok");
+ } else {
+ isp_prt(isp, pl,
+ "unmonitored CTIO completed ok");
+ }
+ } else {
+ isp_prt(isp, pl,
+ "NO xs for CTIO (handle 0x%x) status 0x%x",
+ ct->ct_syshandle, ct->ct_status & ~QLTM_SVALID);
+ }
+ } else {
+ /*
+ * Final CTIO completed. Release DMA resources and
+ * notify platform dependent layers.
+ */
+ if ((ct->ct_flags & CT_DATAMASK) != CT_NO_DATA) {
+ ISP_DMAFREE(isp, xs, ct->ct_syshandle);
+ }
+ isp_prt(isp, pl, "final CTIO complete");
+ /*
+ * The platform layer will destroy the handle if appropriate.
+ */
+ (void) isp_async(isp, ISPASYNC_TARGET_ACTION, ct);
+ }
+}
+
+static void
+isp_handle_ctio2(ispsoftc_t *isp, ct2_entry_t *ct)
+{
+ XS_T *xs;
+ int pl = ISP_LOGTDEBUG2;
+ char *fmsg = NULL;
+
+ if (ct->ct_syshandle) {
+ xs = isp_find_xs_tgt(isp, ct->ct_syshandle);
+ if (xs == NULL)
+ pl = ISP_LOGALL;
+ } else {
+ xs = NULL;
+ }
+
+ switch(ct->ct_status & ~QLTM_SVALID) {
+ case CT_BUS_ERROR:
+ isp_prt(isp, ISP_LOGERR, "PCI DMA Bus Error");
+ /* FALL Through */
+ case CT_DATA_OVER:
+ case CT_DATA_UNDER:
+ case CT_OK:
+ /*
+ * There are generally 2 possibilities as to why we'd get
+ * this condition:
+ * We sent or received data.
+ * We sent status & command complete.
+ */
+
+ break;
+
+ case CT_BDR_MSG:
+ /*
+ * Target Reset function received.
+ *
+ * The firmware generates an async mailbox interupt to
+ * notify us of this and returns outstanding CTIOs with this
+ * status. These CTIOs are handled in that same way as
+ * CT_ABORTED ones, so just fall through here.
+ */
+ fmsg = "TARGET RESET Task Management Function Received";
+ /*FALLTHROUGH*/
+ case CT_RESET:
+ if (fmsg == NULL)
+ fmsg = "LIP Reset";
+ /*FALLTHROUGH*/
+ case CT_ABORTED:
+ /*
+ * When an Abort message is received the firmware goes to
+ * Bus Free and returns all outstanding CTIOs with the status
+ * set, then sends us an Immediate Notify entry.
+ */
+ if (fmsg == NULL)
+ fmsg = "ABORT Task Management Function Received";
+
+ isp_prt(isp, ISP_LOGERR, "CTIO2 destroyed by %s: RX_ID=0x%x",
+ fmsg, ct->ct_rxid);
+ break;
+
+ case CT_INVAL:
+ /*
+ * CTIO rejected by the firmware - invalid data direction.
+ */
+ isp_prt(isp, ISP_LOGERR, "CTIO2 had wrong data direction");
+ break;
+
+ case CT_RSELTMO:
+ fmsg = "failure to reconnect to initiator";
+ /*FALLTHROUGH*/
+ case CT_TIMEOUT:
+ if (fmsg == NULL)
+ fmsg = "command";
+ isp_prt(isp, ISP_LOGERR, "Firmware timed out on %s", fmsg);
+ break;
+
+ case CT_ERR:
+ fmsg = "Completed with Error";
+ /*FALLTHROUGH*/
+ case CT_LOGOUT:
+ if (fmsg == NULL)
+ fmsg = "Port Logout";
+ /*FALLTHROUGH*/
+ case CT_PORTNOTAVAIL:
+ if (fmsg == NULL)
+ fmsg = "Port not available";
+ /*FALLTHROUGH*/
+ case CT_PORTCHANGED:
+ if (fmsg == NULL)
+ fmsg = "Port Changed";
+ /*FALLTHROUGH*/
+ case CT_NOACK:
+ if (fmsg == NULL)
+ fmsg = "unacknowledged Immediate Notify pending";
+ isp_prt(isp, ISP_LOGERR, "CTIO returned by f/w- %s", fmsg);
+ break;
+
+ case CT_INVRXID:
+ /*
+ * CTIO rejected by the firmware because an invalid RX_ID.
+ * Just print a message.
+ */
+ isp_prt(isp, ISP_LOGERR,
+ "CTIO2 completed with Invalid RX_ID 0x%x", ct->ct_rxid);
+ break;
+
+ default:
+ isp_prt(isp, ISP_LOGERR, "Unknown CTIO2 status 0x%x",
+ ct->ct_status & ~QLTM_SVALID);
+ break;
+ }
+
+ if (xs == NULL) {
+ /*
+ * There may be more than one CTIO for a data transfer,
+ * or this may be a status CTIO we're not monitoring.
+ *
+ * The assumption is that they'll all be returned in the
+ * order we got them.
+ */
+ if (ct->ct_syshandle == 0) {
+ if ((ct->ct_flags & CT2_SENDSTATUS) == 0) {
+ isp_prt(isp, pl,
+ "intermediate CTIO completed ok");
+ } else {
+ isp_prt(isp, pl,
+ "unmonitored CTIO completed ok");
+ }
+ } else {
+ isp_prt(isp, pl,
+ "NO xs for CTIO (handle 0x%x) status 0x%x",
+ ct->ct_syshandle, ct->ct_status & ~QLTM_SVALID);
+ }
+ } else {
+ if ((ct->ct_flags & CT2_DATAMASK) != CT2_NO_DATA) {
+ ISP_DMAFREE(isp, xs, ct->ct_syshandle);
+ }
+ if (ct->ct_flags & CT2_SENDSTATUS) {
+ /*
+ * Sent status and command complete.
+ *
+ * We're now really done with this command, so we
+ * punt to the platform dependent layers because
+ * only there can we do the appropriate command
+ * complete thread synchronization.
+ */
+ isp_prt(isp, pl, "status CTIO complete");
+ } else {
+ /*
+ * Final CTIO completed. Release DMA resources and
+ * notify platform dependent layers.
+ */
+ isp_prt(isp, pl, "data CTIO complete");
+ }
+ (void) isp_async(isp, ISPASYNC_TARGET_ACTION, ct);
+ /*
+ * The platform layer will destroy the handle if appropriate.
+ */
+ }
+}
+#endif
diff --git a/sys/dev/isp/isp_target.h b/sys/dev/isp/isp_target.h
new file mode 100644
index 000000000000..be0387760c6d
--- /dev/null
+++ b/sys/dev/isp/isp_target.h
@@ -0,0 +1,703 @@
+/* $FreeBSD$ */
+/*-
+ * Qlogic Target Mode Structure and Flag Definitions
+ *
+ * Copyright (c) 1997, 1998
+ * Patrick Stirling
+ * pms@psconsult.com
+ * All rights reserved.
+ *
+ * Additonal Copyright (c) 1997-2006 by Matthew Jacob
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice 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.
+ *
+ * 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.
+ *
+ */
+#ifndef _ISP_TARGET_H
+#define _ISP_TARGET_H
+
+#define QLTM_SENSELEN 18 /* non-FC cards only */
+#define QLTM_SVALID 0x80
+
+/*
+ * Structure for Enable Lun and Modify Lun queue entries
+ */
+typedef struct {
+ isphdr_t le_header;
+ uint32_t le_reserved;
+ uint8_t le_lun;
+ uint8_t le_rsvd;
+ uint8_t le_ops; /* Modify LUN only */
+ uint8_t le_tgt; /* Not for FC */
+ uint32_t le_flags; /* Not for FC */
+ uint8_t le_status;
+ uint8_t le_reserved2;
+ uint8_t le_cmd_count;
+ uint8_t le_in_count;
+ uint8_t le_cdb6len; /* Not for FC */
+ uint8_t le_cdb7len; /* Not for FC */
+ uint16_t le_timeout;
+ uint16_t le_reserved3[20];
+} lun_entry_t;
+
+/*
+ * le_flags values
+ */
+#define LUN_TQAE 0x00000002 /* bit1 Tagged Queue Action Enable */
+#define LUN_DSSM 0x01000000 /* bit24 Disable Sending SDP Message */
+#define LUN_DISAD 0x02000000 /* bit25 Disable autodisconnect */
+#define LUN_DM 0x40000000 /* bit30 Disconnects Mandatory */
+
+/*
+ * le_ops values
+ */
+#define LUN_CCINCR 0x01 /* increment command count */
+#define LUN_CCDECR 0x02 /* decrement command count */
+#define LUN_ININCR 0x40 /* increment immed. notify count */
+#define LUN_INDECR 0x80 /* decrement immed. notify count */
+
+/*
+ * le_status values
+ */
+#define LUN_OK 0x01 /* we be rockin' */
+#define LUN_ERR 0x04 /* request completed with error */
+#define LUN_INVAL 0x06 /* invalid request */
+#define LUN_NOCAP 0x16 /* can't provide requested capability */
+#define LUN_ENABLED 0x3E /* LUN already enabled */
+
+/*
+ * Immediate Notify Entry structure
+ */
+#define IN_MSGLEN 8 /* 8 bytes */
+#define IN_RSVDLEN 8 /* 8 words */
+typedef struct {
+ isphdr_t in_header;
+ uint32_t in_reserved;
+ uint8_t in_lun; /* lun */
+ uint8_t in_iid; /* initiator */
+ uint8_t in_reserved2;
+ uint8_t in_tgt; /* target */
+ uint32_t in_flags;
+ uint8_t in_status;
+ uint8_t in_rsvd2;
+ uint8_t in_tag_val; /* tag value */
+ uint8_t in_tag_type; /* tag type */
+ uint16_t in_seqid; /* sequence id */
+ uint8_t in_msg[IN_MSGLEN]; /* SCSI message bytes */
+ uint16_t in_reserved3[IN_RSVDLEN];
+ uint8_t in_sense[QLTM_SENSELEN];/* suggested sense data */
+} in_entry_t;
+
+typedef struct {
+ isphdr_t in_header;
+ uint32_t in_reserved;
+ uint8_t in_lun; /* lun */
+ uint8_t in_iid; /* initiator */
+ uint16_t in_scclun;
+ uint32_t in_reserved2;
+ uint16_t in_status;
+ uint16_t in_task_flags;
+ uint16_t in_seqid; /* sequence id */
+} in_fcentry_t;
+
+typedef struct {
+ isphdr_t in_header;
+ uint32_t in_reserved;
+ uint16_t in_iid; /* initiator */
+ uint16_t in_scclun;
+ uint32_t in_reserved2;
+ uint16_t in_status;
+ uint16_t in_task_flags;
+ uint16_t in_seqid; /* sequence id */
+} in_fcentry_e_t;
+
+/*
+ * Values for the in_status field
+ */
+#define IN_REJECT 0x0D /* Message Reject message received */
+#define IN_RESET 0x0E /* Bus Reset occurred */
+#define IN_NO_RCAP 0x16 /* requested capability not available */
+#define IN_IDE_RECEIVED 0x33 /* Initiator Detected Error msg received */
+#define IN_RSRC_UNAVAIL 0x34 /* resource unavailable */
+#define IN_MSG_RECEIVED 0x36 /* SCSI message received */
+#define IN_ABORT_TASK 0x20 /* task named in RX_ID is being aborted (FC) */
+#define IN_PORT_LOGOUT 0x29 /* port has logged out (FC) */
+#define IN_PORT_CHANGED 0x2A /* port changed */
+#define IN_GLOBAL_LOGO 0x2E /* all ports logged out */
+#define IN_NO_NEXUS 0x3B /* Nexus not established */
+
+/*
+ * Values for the in_task_flags field- should only get one at a time!
+ */
+#define TASK_FLAGS_RESERVED_MASK (0xe700)
+#define TASK_FLAGS_CLEAR_ACA (1<<14)
+#define TASK_FLAGS_TARGET_RESET (1<<13)
+#define TASK_FLAGS_LUN_RESET (1<<12)
+#define TASK_FLAGS_CLEAR_TASK_SET (1<<10)
+#define TASK_FLAGS_ABORT_TASK_SET (1<<9)
+
+#ifndef MSG_ABORT
+#define MSG_ABORT 0x06
+#endif
+#ifndef MSG_BUS_DEV_RESET
+#define MSG_BUS_DEV_RESET 0x0c
+#endif
+#ifndef MSG_ABORT_TAG
+#define MSG_ABORT_TAG 0x0d
+#endif
+#ifndef MSG_CLEAR_QUEUE
+#define MSG_CLEAR_QUEUE 0x0e
+#endif
+#ifndef MSG_REL_RECOVERY
+#define MSG_REL_RECOVERY 0x10
+#endif
+#ifndef MSG_TERM_IO_PROC
+#define MSG_TERM_IO_PROC 0x11
+#endif
+#ifndef MSG_LUN_RESET
+#define MSG_LUN_RESET 0x17
+#endif
+
+/*
+ * Notify Acknowledge Entry structure
+ */
+#define NA_RSVDLEN 22
+typedef struct {
+ isphdr_t na_header;
+ uint32_t na_reserved;
+ uint8_t na_lun; /* lun */
+ uint8_t na_iid; /* initiator */
+ uint8_t na_reserved2;
+ uint8_t na_tgt; /* target */
+ uint32_t na_flags;
+ uint8_t na_status;
+ uint8_t na_event;
+ uint16_t na_seqid; /* sequence id */
+ uint16_t na_reserved3[NA_RSVDLEN];
+} na_entry_t;
+
+/*
+ * Value for the na_event field
+ */
+#define NA_RST_CLRD 0x80 /* Clear an async event notification */
+#define NA_OK 0x01 /* Notify Acknowledge Succeeded */
+#define NA_INVALID 0x06 /* Invalid Notify Acknowledge */
+
+#define NA2_RSVDLEN 21
+typedef struct {
+ isphdr_t na_header;
+ uint32_t na_reserved;
+ uint8_t na_reserved1;
+ uint8_t na_iid; /* initiator loop id */
+ uint16_t na_response;
+ uint16_t na_flags;
+ uint16_t na_reserved2;
+ uint16_t na_status;
+ uint16_t na_task_flags;
+ uint16_t na_seqid; /* sequence id */
+ uint16_t na_reserved3[NA2_RSVDLEN];
+} na_fcentry_t;
+
+typedef struct {
+ isphdr_t na_header;
+ uint32_t na_reserved;
+ uint16_t na_iid; /* initiator loop id */
+ uint16_t na_response; /* response code */
+ uint16_t na_flags;
+ uint16_t na_reserved2;
+ uint16_t na_status;
+ uint16_t na_task_flags;
+ uint16_t na_seqid; /* sequence id */
+ uint16_t na_reserved3[NA2_RSVDLEN];
+} na_fcentry_e_t;
+
+#define NAFC_RCOUNT 0x80 /* increment resource count */
+#define NAFC_RST_CLRD 0x20 /* Clear LIP Reset */
+#define NAFC_TVALID 0x10 /* task mangement response code is valid */
+
+/*
+ * Accept Target I/O Entry structure
+ */
+#define ATIO_CDBLEN 26
+
+typedef struct {
+ isphdr_t at_header;
+ uint16_t at_reserved;
+ uint16_t at_handle;
+ uint8_t at_lun; /* lun */
+ uint8_t at_iid; /* initiator */
+ uint8_t at_cdblen; /* cdb length */
+ uint8_t at_tgt; /* target */
+ uint32_t at_flags;
+ uint8_t at_status; /* firmware status */
+ uint8_t at_scsi_status; /* scsi status */
+ uint8_t at_tag_val; /* tag value */
+ uint8_t at_tag_type; /* tag type */
+ uint8_t at_cdb[ATIO_CDBLEN]; /* received CDB */
+ uint8_t at_sense[QLTM_SENSELEN];/* suggested sense data */
+} at_entry_t;
+
+/*
+ * at_flags values
+ */
+#define AT_NODISC 0x00008000 /* disconnect disabled */
+#define AT_TQAE 0x00000002 /* Tagged Queue Action enabled */
+
+/*
+ * at_status values
+ */
+#define AT_PATH_INVALID 0x07 /* ATIO sent to firmware for disabled lun */
+#define AT_RESET 0x0E /* SCSI Bus Reset Occurred */
+#define AT_PHASE_ERROR 0x14 /* Bus phase sequence error */
+#define AT_NOCAP 0x16 /* Requested capability not available */
+#define AT_BDR_MSG 0x17 /* Bus Device Reset msg received */
+#define AT_CDB 0x3D /* CDB received */
+/*
+ * Macros to create and fetch and test concatenated handle and tag value macros
+ */
+
+#define AT_MAKE_TAGID(tid, inst, aep) \
+ tid = aep->at_handle; \
+ if (aep->at_flags & AT_TQAE) { \
+ tid |= (aep->at_tag_val << 16); \
+ tid |= (1 << 24); \
+ } \
+ tid |= (GET_BUS_VAL(aep->at_iid) << 25); \
+ tid |= (inst << 26)
+
+#define CT_MAKE_TAGID(tid, bus, inst, ct) \
+ tid = ct->ct_fwhandle; \
+ if (ct->ct_flags & CT_TQAE) { \
+ tid |= (ct->ct_tag_val << 16); \
+ tid |= (1 << 24); \
+ } \
+ tid |= ((bus & 0x1) << 25); \
+ tid |= (inst << 26)
+
+#define AT_HAS_TAG(val) ((val) & (1 << 24))
+#define AT_GET_TAG(val) (((val) >> 16) & 0xff)
+#define AT_GET_INST(val) (((val) >> 26) & 0x3f)
+#define AT_GET_BUS(val) (((val) >> 25) & 0x1)
+#define AT_GET_HANDLE(val) ((val) & 0xffff)
+
+#define IN_MAKE_TAGID(tid, inst, inp) \
+ tid = inp->in_seqid; \
+ tid |= (inp->in_tag_val << 16); \
+ tid |= (1 << 24); \
+ tid |= (GET_BUS_VAL(inp->in_iid) << 25); \
+ tid |= (inst << 26)
+
+#define TAG_INSERT_INST(tid, inst) \
+ tid &= ~(0x3ffffff); \
+ tid |= (inst << 26)
+
+#define TAG_INSERT_BUS(tid, bus) \
+ tid &= ~(1 << 25); \
+ tid |= (bus << 25)
+
+/*
+ * Accept Target I/O Entry structure, Type 2
+ */
+#define ATIO2_CDBLEN 16
+
+typedef struct {
+ isphdr_t at_header;
+ uint32_t at_reserved;
+ uint8_t at_lun; /* lun or reserved */
+ uint8_t at_iid; /* initiator */
+ uint16_t at_rxid; /* response ID */
+ uint16_t at_flags;
+ uint16_t at_status; /* firmware status */
+ uint8_t at_crn; /* command reference number */
+ uint8_t at_taskcodes;
+ uint8_t at_taskflags;
+ uint8_t at_execodes;
+ uint8_t at_cdb[ATIO2_CDBLEN]; /* received CDB */
+ uint32_t at_datalen; /* allocated data len */
+ uint16_t at_scclun; /* SCC Lun or reserved */
+ uint16_t at_wwpn[4]; /* WWPN of initiator */
+ uint16_t at_reserved2[6];
+ uint16_t at_oxid;
+} at2_entry_t;
+
+typedef struct {
+ isphdr_t at_header;
+ uint32_t at_reserved;
+ uint16_t at_iid; /* initiator */
+ uint16_t at_rxid; /* response ID */
+ uint16_t at_flags;
+ uint16_t at_status; /* firmware status */
+ uint8_t at_crn; /* command reference number */
+ uint8_t at_taskcodes;
+ uint8_t at_taskflags;
+ uint8_t at_execodes;
+ uint8_t at_cdb[ATIO2_CDBLEN]; /* received CDB */
+ uint32_t at_datalen; /* allocated data len */
+ uint16_t at_scclun; /* SCC Lun or reserved */
+ uint16_t at_wwpn[4]; /* WWPN of initiator */
+ uint16_t at_reserved2[6];
+ uint16_t at_oxid;
+} at2e_entry_t;
+
+#define ATIO2_WWPN_OFFSET 0x2A
+#define ATIO2_OXID_OFFSET 0x3E
+
+#define ATIO2_TC_ATTR_MASK 0x7
+#define ATIO2_TC_ATTR_SIMPLEQ 0
+#define ATIO2_TC_ATTR_HEADOFQ 1
+#define ATIO2_TC_ATTR_ORDERED 2
+#define ATIO2_TC_ATTR_ACAQ 4
+#define ATIO2_TC_ATTR_UNTAGGED 5
+
+#define ATIO2_EX_WRITE 0x1
+#define ATIO2_EX_READ 0x2
+/*
+ * Macros to create and fetch and test concatenated handle and tag value macros
+ */
+#define AT2_MAKE_TAGID(tid, inst, aep) \
+ tid = aep->at_rxid; \
+ tid |= (inst << 16)
+
+#define CT2_MAKE_TAGID(tid, inst, ct) \
+ tid = ct->ct_rxid; \
+ tid |= (inst << 16)
+
+#define AT2_HAS_TAG(val) 1
+#define AT2_GET_TAG(val) ((val) & 0xffff)
+#define AT2_GET_INST(val) ((val) >> 16)
+#define AT2_GET_HANDLE AT2_GET_TAG
+
+#define FC_HAS_TAG AT2_HAS_TAG
+#define FC_GET_TAG AT2_GET_TAG
+#define FC_GET_INST AT2_GET_INST
+#define FC_GET_HANDLE AT2_GET_HANDLE
+
+#define IN_FC_MAKE_TAGID(tid, inst, seqid) \
+ tid = seqid; \
+ tid |= (inst << 16)
+
+#define FC_TAG_INSERT_INST(tid, inst) \
+ tid &= ~0xffff; \
+ tid |= (inst << 16)
+
+
+/*
+ * Continue Target I/O Entry structure
+ * Request from driver. The response from the
+ * ISP firmware is the same except that the last 18
+ * bytes are overwritten by suggested sense data if
+ * the 'autosense valid' bit is set in the status byte.
+ */
+typedef struct {
+ isphdr_t ct_header;
+ uint16_t ct_reserved;
+#define ct_syshandle ct_reserved /* we use this */
+ uint16_t ct_fwhandle; /* required by f/w */
+ uint8_t ct_lun; /* lun */
+ uint8_t ct_iid; /* initiator id */
+ uint8_t ct_reserved2;
+ uint8_t ct_tgt; /* our target id */
+ uint32_t ct_flags;
+ uint8_t ct_status; /* isp status */
+ uint8_t ct_scsi_status; /* scsi status */
+ uint8_t ct_tag_val; /* tag value */
+ uint8_t ct_tag_type; /* tag type */
+ uint32_t ct_xfrlen; /* transfer length */
+ uint32_t ct_resid; /* residual length */
+ uint16_t ct_timeout;
+ uint16_t ct_seg_count;
+ /*
+ * This is so we can share tag name space with
+ * CTIO{2,3,4} with the minimum of pain.
+ */
+ union {
+ ispds_t ct_a[ISP_RQDSEG];
+ } _u;
+#define ct_dataseg _u.ct_a
+} ct_entry_t;
+
+/*
+ * For some of the dual port SCSI adapters, port (bus #) is reported
+ * in the MSbit of ct_iid. Bit fields are a bit too awkward here.
+ *
+ * Note that this does not apply to FC adapters at all which can and
+ * do report IIDs between 0x81 && 0xfe (or 0x7ff) which represent devices
+ * that have logged in across a SCSI fabric.
+ */
+#define GET_IID_VAL(x) (x & 0x3f)
+#define GET_BUS_VAL(x) ((x >> 7) & 0x1)
+#define SET_IID_VAL(y, x) y = ((y & ~0x3f) | (x & 0x3f))
+#define SET_BUS_VAL(y, x) y = ((y & 0x3f) | ((x & 0x1) << 7))
+
+/*
+ * ct_flags values
+ */
+#define CT_TQAE 0x00000002 /* bit 1, Tagged Queue Action enable */
+#define CT_DATA_IN 0x00000040 /* bits 6&7, Data direction */
+#define CT_DATA_OUT 0x00000080 /* bits 6&7, Data direction */
+#define CT_NO_DATA 0x000000C0 /* bits 6&7, Data direction */
+#define CT_CCINCR 0x00000100 /* bit 8, autoincrement atio count */
+#define CT_DATAMASK 0x000000C0 /* bits 6&7, Data direction */
+#define CT_INISYNCWIDE 0x00004000 /* bit 14, Do Sync/Wide Negotiation */
+#define CT_NODISC 0x00008000 /* bit 15, Disconnects disabled */
+#define CT_DSDP 0x01000000 /* bit 24, Disable Save Data Pointers */
+#define CT_SENDRDP 0x04000000 /* bit 26, Send Restore Pointers msg */
+#define CT_SENDSTATUS 0x80000000 /* bit 31, Send SCSI status byte */
+
+/*
+ * ct_status values
+ * - set by the firmware when it returns the CTIO
+ */
+#define CT_OK 0x01 /* completed without error */
+#define CT_ABORTED 0x02 /* aborted by host */
+#define CT_ERR 0x04 /* see sense data for error */
+#define CT_INVAL 0x06 /* request for disabled lun */
+#define CT_NOPATH 0x07 /* invalid ITL nexus */
+#define CT_INVRXID 0x08 /* (FC only) Invalid RX_ID */
+#define CT_DATA_OVER 0x09 /* (FC only) Data Overrun */
+#define CT_RSELTMO 0x0A /* reselection timeout after 2 tries */
+#define CT_TIMEOUT 0x0B /* timed out */
+#define CT_RESET 0x0E /* SCSI Bus Reset occurred */
+#define CT_PARITY 0x0F /* Uncorrectable Parity Error */
+#define CT_BUS_ERROR 0x10 /* (FC Only) DMA PCI Error */
+#define CT_PANIC 0x13 /* Unrecoverable Error */
+#define CT_PHASE_ERROR 0x14 /* Bus phase sequence error */
+#define CT_BDR_MSG 0x17 /* Bus Device Reset msg received */
+#define CT_DATA_UNDER 0x15 /* (FC only) Data Underrun */
+#define CT_TERMINATED 0x19 /* due to Terminate Transfer mbox cmd */
+#define CT_PORTNOTAVAIL 0x28 /* port not available */
+#define CT_LOGOUT 0x29 /* port logout */
+#define CT_PORTCHANGED 0x2A /* port changed */
+#define CT_IDE 0x33 /* Initiator Detected Error */
+#define CT_NOACK 0x35 /* Outstanding Immed. Notify. entry */
+#define CT_SRR 0x45 /* SRR Received */
+#define CT_LUN_RESET 0x48 /* Lun Reset Received */
+
+/*
+ * When the firmware returns a CTIO entry, it may overwrite the last
+ * part of the structure with sense data. This starts at offset 0x2E
+ * into the entry, which is in the middle of ct_dataseg[1]. Rather
+ * than define a new struct for this, I'm just using the sense data
+ * offset.
+ */
+#define CTIO_SENSE_OFFSET 0x2E
+
+/*
+ * Entry length in u_longs. All entries are the same size so
+ * any one will do as the numerator.
+ */
+#define UINT32_ENTRY_SIZE (sizeof(at_entry_t)/sizeof(uint32_t))
+
+/*
+ * QLA2100 CTIO (type 2) entry
+ */
+#define MAXRESPLEN 26
+typedef struct {
+ isphdr_t ct_header;
+ uint16_t ct_reserved;
+ uint16_t ct_fwhandle; /* just to match CTIO */
+ uint8_t ct_lun; /* lun */
+ uint8_t ct_iid; /* initiator id */
+ uint16_t ct_rxid; /* response ID */
+ uint16_t ct_flags;
+ uint16_t ct_status; /* isp status */
+ uint16_t ct_timeout;
+ uint16_t ct_seg_count;
+ uint32_t ct_reloff; /* relative offset */
+ int32_t ct_resid; /* residual length */
+ union {
+ /*
+ * The three different modes that the target driver
+ * can set the CTIO{2,3,4} up as.
+ *
+ * The first is for sending FCP_DATA_IUs as well as
+ * (optionally) sending a terminal SCSI status FCP_RSP_IU.
+ *
+ * The second is for sending SCSI sense data in an FCP_RSP_IU.
+ * Note that no FCP_DATA_IUs will be sent.
+ *
+ * The third is for sending FCP_RSP_IUs as built specifically
+ * in system memory as located by the isp_dataseg.
+ */
+ struct {
+ uint32_t _reserved;
+ uint16_t _reserved2;
+ uint16_t ct_scsi_status;
+ uint32_t ct_xfrlen;
+ union {
+ ispds_t ct_a[ISP_RQDSEG_T2]; /* CTIO2 */
+ ispds64_t ct_b[ISP_RQDSEG_T3]; /* CTIO3 */
+ ispdslist_t ct_c; /* CTIO4 */
+ } _u;
+#define ct_dataseg _u.ct_a
+#define ct_dataseg64 _u.ct_b
+#define ct_dslist _u.ct_c
+ } m0;
+ struct {
+ uint16_t _reserved;
+ uint16_t _reserved2;
+ uint16_t ct_senselen;
+ uint16_t ct_scsi_status;
+ uint16_t ct_resplen;
+ uint8_t ct_resp[MAXRESPLEN];
+ } m1;
+ struct {
+ uint32_t _reserved;
+ uint16_t _reserved2;
+ uint16_t _reserved3;
+ uint32_t ct_datalen;
+ ispds_t ct_fcp_rsp_iudata;
+ } m2;
+ } rsp;
+} ct2_entry_t;
+
+typedef struct {
+ isphdr_t ct_header;
+ uint16_t ct_reserved;
+ uint16_t ct_fwhandle; /* just to match CTIO */
+ uint16_t ct_iid; /* initiator id */
+ uint16_t ct_rxid; /* response ID */
+ uint16_t ct_flags;
+ uint16_t ct_status; /* isp status */
+ uint16_t ct_timeout;
+ uint16_t ct_seg_count;
+ uint32_t ct_reloff; /* relative offset */
+ int32_t ct_resid; /* residual length */
+ union {
+ struct {
+ uint32_t _reserved;
+ uint16_t _reserved2;
+ uint16_t ct_scsi_status;
+ uint32_t ct_xfrlen;
+ union {
+ ispds_t ct_a[ISP_RQDSEG_T2]; /* CTIO2 */
+ ispds64_t ct_b[ISP_RQDSEG_T3]; /* CTIO3 */
+ ispdslist_t ct_c; /* CTIO4 */
+ } _u;
+ } m0;
+ struct {
+ uint16_t _reserved;
+ uint16_t _reserved2;
+ uint16_t ct_senselen;
+ uint16_t ct_scsi_status;
+ uint16_t ct_resplen;
+ uint8_t ct_resp[MAXRESPLEN];
+ } m1;
+ struct {
+ uint32_t _reserved;
+ uint16_t _reserved2;
+ uint16_t _reserved3;
+ uint32_t ct_datalen;
+ ispds_t ct_fcp_rsp_iudata;
+ } m2;
+ } rsp;
+} ct2e_entry_t;
+
+/*
+ * ct_flags values for CTIO2
+ */
+#define CT2_FLAG_MMASK 0x0003
+#define CT2_FLAG_MODE0 0x0000
+#define CT2_FLAG_MODE1 0x0001
+#define CT2_FLAG_MODE2 0x0002
+#define CT2_DATA_IN CT_DATA_IN
+#define CT2_DATA_OUT CT_DATA_OUT
+#define CT2_NO_DATA CT_NO_DATA
+#define CT2_DATAMASK CT_DATAMASK
+#define CT2_CCINCR 0x0100
+#define CT2_FASTPOST 0x0200
+#define CT2_TERMINATE 0x4000
+#define CT2_SENDSTATUS 0x8000
+
+/*
+ * ct_status values are (mostly) the same as that for ct_entry.
+ */
+
+/*
+ * ct_scsi_status values- the low 8 bits are the normal SCSI status
+ * we know and love. The upper 8 bits are validity markers for FCP_RSP_IU
+ * fields.
+ */
+#define CT2_RSPLEN_VALID 0x0100
+#define CT2_SNSLEN_VALID 0x0200
+#define CT2_DATA_OVER 0x0400
+#define CT2_DATA_UNDER 0x0800
+
+/*
+ * Debug macros
+ */
+
+#define ISP_TDQE(isp, msg, idx, arg) \
+ if (isp->isp_dblev & ISP_LOGTDEBUG2) isp_print_qentry(isp, msg, idx, arg)
+
+/*
+ * The functions below are for the publicly available
+ * target mode functions that are internal to the Qlogic driver.
+ */
+
+/*
+ * This function handles new response queue entry appropriate for target mode.
+ */
+int isp_target_notify(ispsoftc_t *, void *, uint16_t *);
+
+/*
+ * This function externalizes the ability to acknowledge an Immediate Notify
+ * request.
+ */
+void isp_notify_ack(ispsoftc_t *, void *);
+
+/*
+ * Enable/Disable/Modify a logical unit.
+ * (softc, cmd, bus, tgt, lun, cmd_cnt, inotify_cnt, opaque)
+ */
+#define DFLT_CMND_CNT 0xfe /* unmonitored */
+#define DFLT_INOT_CNT 0xfe /* unmonitored */
+int isp_lun_cmd(ispsoftc_t *, int, int, int, int, int, int, uint32_t);
+
+/*
+ * General request queue 'put' routine for target mode entries.
+ */
+int isp_target_put_entry(ispsoftc_t *isp, void *);
+
+/*
+ * General routine to put back an ATIO entry-
+ * used for replenishing f/w resource counts.
+ * The argument is a pointer to a source ATIO
+ * or ATIO2.
+ */
+int isp_target_put_atio(ispsoftc_t *, void *);
+
+/*
+ * General routine to send a final CTIO for a command- used mostly for
+ * local responses.
+ */
+int isp_endcmd(ispsoftc_t *, void *, uint32_t, uint16_t);
+#define ECMD_SVALID 0x100
+
+/*
+ * Handle an asynchronous event
+ *
+ * Return nonzero if the interrupt that generated this event has been dismissed.
+ */
+int isp_target_async(ispsoftc_t *, int, int);
+
+#endif /* _ISP_TARGET_H */
diff --git a/sys/dev/isp/isp_tpublic.h b/sys/dev/isp/isp_tpublic.h
new file mode 100644
index 000000000000..d1a0b3d2ca67
--- /dev/null
+++ b/sys/dev/isp/isp_tpublic.h
@@ -0,0 +1,372 @@
+/* $FreeBSD$ */
+/*-
+ * Qlogic ISP Host Adapter Public Target Interface Structures && Routines
+ *
+ * Copyright (c) 1997-2006 by Matthew Jacob
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice 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.
+ *
+ * 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.
+ */
+/*
+ * Host Adapter Public Target Interface Structures && Routines
+ */
+
+#ifndef _ISP_TPUBLIC_H
+#define _ISP_TPUBLIC_H 1
+
+/*
+ * Action codes set by the MD target driver for
+ * the external layer to figure out what to do with.
+ */
+typedef enum {
+ QOUT_HBA_REG=0, /* the argument is a pointer to a hba_register_t */
+ QOUT_ENABLE, /* the argument is a pointer to a enadis_t */
+ QOUT_DISABLE, /* the argument is a pointer to a enadis_t */
+ QOUT_TMD_START, /* the argument is a pointer to a tmd_cmd_t */
+ QOUT_TMD_DONE, /* the argument is a pointer to a tmd_cmd_t */
+ QOUT_NOTIFY, /* the argument is a pointer to a tmd_notify_t */
+ QOUT_HBA_UNREG /* the argument is a pointer to a hba_register_t */
+} tact_e;
+
+/*
+ * Action codes set by the external layer for the
+ * MD driver to figure out what to do with.
+ */
+typedef enum {
+ QIN_HBA_REG=99, /* the argument is a pointer to a hba_register_t */
+ QIN_ENABLE, /* the argument is a pointer to a enadis_t */
+ QIN_DISABLE, /* the argument is a pointer to a enadis_t */
+ QIN_TMD_CONT, /* the argument is a pointer to a tmd_cmd_t */
+ QIN_TMD_FIN, /* the argument is a pointer to a tmd_cmd_t */
+ QIN_NOTIFY_ACK, /* the argument is a pointer to a tmd_notify_t */
+ QIN_HBA_UNREG, /* the argument is a pointer to a hba_register_t */
+} qact_e;
+
+/*
+ * This structure is used to register to other software modules the
+ * binding of an HBA identifier, driver name and instance and the
+ * lun width capapbilities of this target driver. It's up to each
+ * platform to figure out how it wants to do this, but a typical
+ * sequence would be for the MD layer to find some external module's
+ * entry point and start by sending a QOUT_HBA_REG with info filled
+ * in, and the external module to call back with a QIN_HBA_REG that
+ * passes back the corresponding information.
+ */
+#define QR_VERSION 10
+typedef struct {
+ void * r_identity;
+ void (*r_action)(qact_e, void *);
+ char r_name[8];
+ int r_inst;
+ int r_version;
+ enum { R_FC, R_SCSI } r_type;
+} hba_register_t;
+
+/*
+ * Notify structure
+ */
+typedef enum {
+ NT_ABORT_TASK=0x1000,
+ NT_ABORT_TASK_SET,
+ NT_CLEAR_ACA,
+ NT_CLEAR_TASK_SET,
+ NT_LUN_RESET,
+ NT_TARGET_RESET,
+ NT_BUS_RESET,
+ NT_LIP_RESET,
+ NT_LINK_UP,
+ NT_LINK_DOWN,
+ NT_LOGOUT,
+ NT_HBA_RESET
+} tmd_ncode_t;
+
+typedef struct tmd_notify {
+ void * nt_hba; /* HBA tag */
+ uint64_t nt_iid; /* inititator id */
+ uint64_t nt_tgt; /* target id */
+ uint16_t nt_lun; /* logical unit */
+ uint16_t nt_padding; /* padding */
+ uint32_t nt_tagval; /* tag value */
+ tmd_ncode_t nt_ncode; /* action */
+ void * nt_lreserved;
+ void * nt_hreserved;
+} tmd_notify_t;
+#define LUN_ANY 0xffff
+#define TGT_ANY ((uint64_t) -1)
+#define INI_ANY ((uint64_t) -1)
+#define TAG_ANY 0
+#define MATCH_TMD(tmd, iid, lun, tag) \
+ ( \
+ (tmd) && \
+ (iid == INI_ANY || iid == tmd->cd_iid) && \
+ (lun == LUN_ANY || lun == tmd->cd_lun) && \
+ (tag == TAG_ANY || tag == tmd->cd_tagval) \
+ )
+
+/*
+ * A word about ENABLE/DISABLE: the argument is a pointer to a enadis_t
+ * with en_hba, en_iid, en_chan, en_tgt and en_lun filled out.
+ *
+ * If an error occurs in either enabling or disabling the described lun
+ * cd_error is set with an appropriate non-zero value.
+ */
+typedef struct {
+ void * en_private; /* for outer layer usage */
+ void * en_hba; /* HBA tag */
+ uint64_t en_iid; /* initiator ID */
+ uint64_t en_tgt; /* target id */
+ uint16_t en_lun; /* logical unit */
+ uint16_t en_chan; /* channel on card */
+ int en_error;
+} enadis_t;
+
+/*
+ * Suggested Software Target Mode Command Handling structure.
+ *
+ * A note about terminology:
+ *
+ * MD stands for "Machine Dependent".
+ *
+ * This driver is structured in three layers: Outer MD, core, and inner MD.
+ * The latter also is bus dependent (i.e., is cognizant of PCI bus issues
+ * as well as platform issues).
+ *
+ *
+ * "Outer Layer" means "Other Module"
+ *
+ * Some additional module that actually implements SCSI target command
+ * policy is the recipient of incoming commands and the source of the
+ * disposition for them.
+ *
+ * The command structure below is one suggested possible MD command structure,
+ * but since the handling of thbis is entirely in the MD layer, there is
+ * no explicit or implicit requirement that it be used.
+ *
+ * The cd_private tag should be used by the MD layer to keep a free list
+ * of these structures. Code outside of this driver can then use this
+ * to identify it's own unit structures. That is, when not on the MD
+ * layer's freelist, the MD layer should shove into it the identifier
+ * that the outer layer has for it- passed in on an initial QIN_HBA_REG
+ * call (see below).
+ *
+ * The cd_hba tag is a tag that uniquely identifies the HBA this target
+ * mode command is coming from. The outer layer has to pass this back
+ * unchanged to avoid chaos.
+ *
+ * The cd_iid, cd_tgt, cd_lun and cd_port tags are used to identify the
+ * id of the initiator who sent us a command, the target claim to be, the
+ * lun on the target we claim to be, and the port instance (for multiple
+ * port host adapters) that this applies to (consider it an extra port
+ * parameter). The iid, tgt and lun values are deliberately chosen to be
+ * fat so that, for example, World Wide Names can be used instead of
+ * the units that the firmware uses (in the case where the MD
+ * layer maintains a port database, for example).
+ *
+ * The cd_tagtype field specifies what kind of command tag type, if
+ * any, has been sent with the command. Note that the Outer Layer
+ * still needs to pass the tag handle through unchanged even
+ * if the tag type is CD_UNTAGGED.
+ *
+ * The cd_cdb contains storage for the passed in command descriptor block.
+ * There is no need to define length as the callee should be able to
+ * figure this out.
+ *
+ * The tag cd_lflags are the flags set by the MD driver when it gets
+ * command incoming or when it needs to inform any outside entities
+ * that the last requested action failed.
+ *
+ * The tag cd_hflags should be set by any outside software to indicate
+ * the validity of sense and status fields (defined below) and to indicate
+ * the direction data is expected to move. It is an error to have both
+ * CDFH_DATA_IN and CDFH_DATA_OUT set.
+ *
+ * If the CDFH_STSVALID flag is set, the command should be completed (after
+ * sending any data and/or status). If CDFH_SNSVALID is set and the MD layer
+ * can also handle sending the associated sense data (either back with an
+ * FCP RESPONSE IU for Fibre Channel or otherwise automatically handling a
+ * REQUEST SENSE from the initator for this target/lun), the MD layer will
+ * set the CDFL_SENTSENSE flag on successful transmission of the sense data.
+ * It is an error for the CDFH_SNSVALID bit to be set and CDFH_STSVALID not
+ * to be set. It is an error for the CDFH_SNSVALID be set and the associated
+ * SCSI status (cd_scsi_status) not be set to CHECK CONDITON.
+ *
+ * The tag cd_data points to a data segment to either be filled or
+ * read from depending on the direction of data movement. The tag
+ * is undefined if no data direction is set. The MD layer and outer
+ * layers must agree on the meaning of cd_data and it is specifically
+ * not defined here.
+ *
+ * The tag cd_totlen is the total data amount expected to be moved
+ * over the life of the command. It may be set by the MD layer, possibly
+ * from the datalen field of an FCP CMND IU unit. If it shows up in the outer
+ * layers set to zero and the CDB indicates data should be moved, the outer
+ * layer should set it to the amount expected to be moved.
+ *
+ * The tag cd_resid should be the total residual of data not transferred.
+ * The outer layers need to set this at the begining of command processing
+ * to equal cd_totlen. As data is successfully moved, this value is decreased.
+ * At the end of a command, any nonzero residual indicates the number of bytes
+ * requested by the command but not moved.
+ *
+ * The tag cd_xfrlen is the length of the currently active data transfer.
+ * This allows several interations between any outside software and the
+ * MD layer to move data.
+ *
+ * The reason that total length and total residual have to be tracked
+ * is to keep track of relative offset.
+ *
+ * The tags cd_sense and cd_scsi_status are pretty obvious.
+ *
+ * The tag cd_error is to communicate between the MD layer and outer software
+ * the current error conditions.
+ *
+ * The tag cd_lreserved, cd_hreserved are scratch areas for use for the MD
+ * and outer layers respectively.
+ *
+ */
+
+#ifndef TMD_CDBLEN
+#define TMD_CDBLEN 16
+#endif
+#ifndef TMD_SENSELEN
+#define TMD_SENSELEN 18
+#endif
+#ifndef QCDS
+#define QCDS 8
+#endif
+
+typedef struct tmd_cmd {
+ void * cd_private; /* private data pointer */
+ void * cd_hba; /* HBA tag */
+ void * cd_data; /* 'pointer' to data */
+ uint64_t cd_iid; /* initiator ID */
+ uint64_t cd_tgt; /* target id */
+ uint64_t cd_lun; /* logical unit */
+ uint32_t cd_tagval; /* tag value */
+ uint32_t cd_lflags; /* flags lower level sets */
+ uint32_t cd_hflags; /* flags higher level sets */
+ uint32_t cd_totlen; /* total data load */
+ uint32_t cd_resid; /* total data residual */
+ uint32_t cd_xfrlen; /* current data load */
+ int32_t cd_error; /* current error */
+ uint8_t cd_tagtype : 4,
+ cd_port : 4; /* port number on HBA */
+ uint8_t cd_scsi_status;
+ uint8_t cd_sense[TMD_SENSELEN];
+ uint8_t cd_cdb[TMD_CDBLEN];
+ union {
+ void * ptrs[QCDS / sizeof (void *)];
+ uint64_t llongs[QCDS / sizeof (uint64_t)];
+ uint32_t longs[QCDS / sizeof (uint32_t)];
+ uint16_t shorts[QCDS / sizeof (uint16_t)];
+ uint8_t bytes[QCDS];
+ } cd_lreserved[3], cd_hreserved[3];
+} tmd_cmd_t;
+
+/* defined tags */
+#define CD_UNTAGGED 0
+#define CD_SIMPLE_TAG 1
+#define CD_ORDERED_TAG 2
+#define CD_HEAD_TAG 3
+#define CD_ACA_TAG 4
+
+#ifndef TMD_SIZE
+#define TMD_SIZE (sizeof (tmd_cmd_t))
+#endif
+
+/*
+ * Note that NODISC (obviously) doesn't apply to non-SPI transport.
+ *
+ * Note that knowing the data direction and lengh at the time of receipt of
+ * a command from the initiator is a feature only of Fibre Channel.
+ *
+ * The CDFL_BIDIR is in anticipation of the adoption of some newer
+ * features required by OSD.
+ *
+ * The principle selector for MD layer to know whether data is to
+ * be transferred in any QOUT_TMD_CONT call is cd_xfrlen- the
+ * flags CDFH_DATA_IN and CDFH_DATA_OUT define which direction.
+ */
+#define CDFL_SNSVALID 0x01 /* sense data (from f/w) good */
+#define CDFL_SENTSTATUS 0x02 /* last action sent status */
+#define CDFL_DATA_IN 0x04 /* target (us) -> initiator (them) */
+#define CDFL_DATA_OUT 0x08 /* initiator (them) -> target (us) */
+#define CDFL_BIDIR 0x0C /* bidirectional data */
+#define CDFL_ERROR 0x10 /* last action ended in error */
+#define CDFL_NODISC 0x20 /* disconnects disabled */
+#define CDFL_SENTSENSE 0x40 /* last action sent sense data */
+#define CDFL_BUSY 0x80 /* this command is not on a free list */
+#define CDFL_PRIVATE 0xFF000000 /* private layer flags */
+
+#define CDFH_SNSVALID 0x01 /* sense data (from outer layer) good */
+#define CDFH_STSVALID 0x02 /* status valid */
+#define CDFH_DATA_IN 0x04 /* target (us) -> initiator (them) */
+#define CDFH_DATA_OUT 0x08 /* initiator (them) -> target (us) */
+#define CDFH_DATA_MASK 0x0C /* mask to cover data direction */
+#define CDFH_PRIVATE 0xFF000000 /* private layer flags */
+
+
+/*
+ * A word about the START/CONT/DONE/FIN dance:
+ *
+ * When the HBA is enabled for receiving commands, one may show up
+ * without notice. When that happens, the MD target mode driver
+ * gets a tmd_cmd_t, fills it with the info that just arrived, and
+ * calls the outer layer with a QOUT_TMD_START code and pointer to
+ * the tmd_cmd_t.
+ *
+ * The outer layer decodes the command, fetches data, prepares stuff,
+ * whatever, and starts by passing back the pointer with a QIN_TMD_CONT
+ * code which causes the MD target mode driver to generate CTIOs to
+ * satisfy whatever action needs to be taken. When those CTIOs complete,
+ * the MD target driver sends the pointer to the cmd_tmd_t back with
+ * a QOUT_TMD_DONE code. This repeats for as long as necessary. These
+ * may not be done in parallel- they are sequential operations.
+ *
+ * The outer layer signals it wants to end the command by settings within
+ * the tmd_cmd_t itself. When the final QIN_TMD_CONT is reported completed,
+ * the outer layer frees the tmd_cmd_t by sending the pointer to it
+ * back with a QIN_TMD_FIN code.
+ *
+ * The graph looks like:
+ *
+ * QOUT_TMD_START -> [ QIN_TMD_CONT -> QOUT_TMD_DONE ] * -> QIN_TMD_FIN.
+ *
+ */
+
+/*
+ * Target handler functions.
+ *
+ * The MD target handler function (the outer layer calls this)
+ * should be be prototyped like:
+ *
+ * void target_action(qact_e, void *arg)
+ *
+ * The outer layer target handler function (the MD layer calls this)
+ * should be be prototyped like:
+ *
+ * void scsi_target_handler(tact_e, void *arg)
+ */
+#endif /* _ISP_TPUBLIC_H */
+/*
+ * vim:ts=4:sw=4:expandtab
+ */
diff --git a/sys/dev/isp/ispmbox.h b/sys/dev/isp/ispmbox.h
new file mode 100644
index 000000000000..2ef8d51e42d7
--- /dev/null
+++ b/sys/dev/isp/ispmbox.h
@@ -0,0 +1,986 @@
+/* $FreeBSD$ */
+/*-
+ * Mailbox and Queue Entry Definitions for for Qlogic ISP SCSI adapters.
+ *
+ * Copyright (c) 1997-2006 by Matthew Jacob
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice 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.
+ *
+ * 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.
+ *
+ */
+#ifndef _ISPMBOX_H
+#define _ISPMBOX_H
+
+/*
+ * Mailbox Command Opcodes
+ */
+#define MBOX_NO_OP 0x0000
+#define MBOX_LOAD_RAM 0x0001
+#define MBOX_EXEC_FIRMWARE 0x0002
+#define MBOX_DUMP_RAM 0x0003
+#define MBOX_WRITE_RAM_WORD 0x0004
+#define MBOX_READ_RAM_WORD 0x0005
+#define MBOX_MAILBOX_REG_TEST 0x0006
+#define MBOX_VERIFY_CHECKSUM 0x0007
+#define MBOX_ABOUT_FIRMWARE 0x0008
+ /* 9 */
+ /* a */
+ /* b */
+ /* c */
+#define MBOX_WRITE_RAM_WORD_EXTENDED 0x000d
+#define MBOX_CHECK_FIRMWARE 0x000e
+#define MBOX_READ_RAM_WORD_EXTENDED 0x000f
+#define MBOX_INIT_REQ_QUEUE 0x0010
+#define MBOX_INIT_RES_QUEUE 0x0011
+#define MBOX_EXECUTE_IOCB 0x0012
+#define MBOX_WAKE_UP 0x0013
+#define MBOX_STOP_FIRMWARE 0x0014
+#define MBOX_ABORT 0x0015
+#define MBOX_ABORT_DEVICE 0x0016
+#define MBOX_ABORT_TARGET 0x0017
+#define MBOX_BUS_RESET 0x0018
+#define MBOX_STOP_QUEUE 0x0019
+#define MBOX_START_QUEUE 0x001a
+#define MBOX_SINGLE_STEP_QUEUE 0x001b
+#define MBOX_ABORT_QUEUE 0x001c
+#define MBOX_GET_DEV_QUEUE_STATUS 0x001d
+ /* 1e */
+#define MBOX_GET_FIRMWARE_STATUS 0x001f
+#define MBOX_GET_INIT_SCSI_ID 0x0020
+#define MBOX_GET_SELECT_TIMEOUT 0x0021
+#define MBOX_GET_RETRY_COUNT 0x0022
+#define MBOX_GET_TAG_AGE_LIMIT 0x0023
+#define MBOX_GET_CLOCK_RATE 0x0024
+#define MBOX_GET_ACT_NEG_STATE 0x0025
+#define MBOX_GET_ASYNC_DATA_SETUP_TIME 0x0026
+#define MBOX_GET_SBUS_PARAMS 0x0027
+#define MBOX_GET_PCI_PARAMS MBOX_GET_SBUS_PARAMS
+#define MBOX_GET_TARGET_PARAMS 0x0028
+#define MBOX_GET_DEV_QUEUE_PARAMS 0x0029
+#define MBOX_GET_RESET_DELAY_PARAMS 0x002a
+ /* 2b */
+ /* 2c */
+ /* 2d */
+ /* 2e */
+ /* 2f */
+#define MBOX_SET_INIT_SCSI_ID 0x0030
+#define MBOX_SET_SELECT_TIMEOUT 0x0031
+#define MBOX_SET_RETRY_COUNT 0x0032
+#define MBOX_SET_TAG_AGE_LIMIT 0x0033
+#define MBOX_SET_CLOCK_RATE 0x0034
+#define MBOX_SET_ACT_NEG_STATE 0x0035
+#define MBOX_SET_ASYNC_DATA_SETUP_TIME 0x0036
+#define MBOX_SET_SBUS_CONTROL_PARAMS 0x0037
+#define MBOX_SET_PCI_PARAMETERS 0x0037
+#define MBOX_SET_TARGET_PARAMS 0x0038
+#define MBOX_SET_DEV_QUEUE_PARAMS 0x0039
+#define MBOX_SET_RESET_DELAY_PARAMS 0x003a
+ /* 3b */
+ /* 3c */
+ /* 3d */
+ /* 3e */
+ /* 3f */
+#define MBOX_RETURN_BIOS_BLOCK_ADDR 0x0040
+#define MBOX_WRITE_FOUR_RAM_WORDS 0x0041
+#define MBOX_EXEC_BIOS_IOCB 0x0042
+#define MBOX_SET_FW_FEATURES 0x004a
+#define MBOX_GET_FW_FEATURES 0x004b
+#define FW_FEATURE_FAST_POST 0x1
+#define FW_FEATURE_LVD_NOTIFY 0x2
+#define FW_FEATURE_RIO_32BIT 0x4
+#define FW_FEATURE_RIO_16BIT 0x8
+
+#define MBOX_INIT_REQ_QUEUE_A64 0x0052
+#define MBOX_INIT_RES_QUEUE_A64 0x0053
+
+#define MBOX_ENABLE_TARGET_MODE 0x0055
+#define ENABLE_TARGET_FLAG 0x8000
+#define ENABLE_TQING_FLAG 0x0004
+#define ENABLE_MANDATORY_DISC 0x0002
+#define MBOX_GET_TARGET_STATUS 0x0056
+
+/* These are for the ISP2X00 FC cards */
+#define MBOX_GET_LOOP_ID 0x0020
+#define MBOX_GET_FIRMWARE_OPTIONS 0x0028
+#define MBOX_SET_FIRMWARE_OPTIONS 0x0038
+#define MBOX_GET_RESOURCE_COUNT 0x0042
+#define MBOX_ENHANCED_GET_PDB 0x0047
+#define MBOX_EXEC_COMMAND_IOCB_A64 0x0054
+#define MBOX_INIT_FIRMWARE 0x0060
+#define MBOX_GET_INIT_CONTROL_BLOCK 0x0061
+#define MBOX_INIT_LIP 0x0062
+#define MBOX_GET_FC_AL_POSITION_MAP 0x0063
+#define MBOX_GET_PORT_DB 0x0064
+#define MBOX_CLEAR_ACA 0x0065
+#define MBOX_TARGET_RESET 0x0066
+#define MBOX_CLEAR_TASK_SET 0x0067
+#define MBOX_ABORT_TASK_SET 0x0068
+#define MBOX_GET_FW_STATE 0x0069
+#define MBOX_GET_PORT_NAME 0x006A
+#define MBOX_GET_LINK_STATUS 0x006B
+#define MBOX_INIT_LIP_RESET 0x006C
+#define MBOX_SEND_SNS 0x006E
+#define MBOX_FABRIC_LOGIN 0x006F
+#define MBOX_SEND_CHANGE_REQUEST 0x0070
+#define MBOX_FABRIC_LOGOUT 0x0071
+#define MBOX_INIT_LIP_LOGIN 0x0072
+#define MBOX_LUN_RESET 0x007E
+
+#define MBOX_DRIVER_HEARTBEAT 0x005B
+#define MBOX_FW_HEARTBEAT 0x005C
+
+#define MBOX_GET_SET_DATA_RATE 0x005D /* 23XX only */
+#define MBGSD_GET_RATE 0
+#define MBGSD_SET_RATE 1
+#define MBGSD_ONEGB 0
+#define MBGSD_TWOGB 1
+#define MBGSD_AUTO 2
+
+
+#define ISP2100_SET_PCI_PARAM 0x00ff
+
+#define MBOX_BUSY 0x04
+
+/*
+ * Mailbox Command Complete Status Codes
+ */
+#define MBOX_COMMAND_COMPLETE 0x4000
+#define MBOX_INVALID_COMMAND 0x4001
+#define MBOX_HOST_INTERFACE_ERROR 0x4002
+#define MBOX_TEST_FAILED 0x4003
+#define MBOX_COMMAND_ERROR 0x4005
+#define MBOX_COMMAND_PARAM_ERROR 0x4006
+#define MBOX_PORT_ID_USED 0x4007
+#define MBOX_LOOP_ID_USED 0x4008
+#define MBOX_ALL_IDS_USED 0x4009
+#define MBOX_NOT_LOGGED_IN 0x400A
+#define MBLOGALL 0x000f
+#define MBLOGNONE 0x0000
+#define MBLOGMASK(x) ((x) & 0xf)
+
+/*
+ * Asynchronous event status codes
+ */
+#define ASYNC_BUS_RESET 0x8001
+#define ASYNC_SYSTEM_ERROR 0x8002
+#define ASYNC_RQS_XFER_ERR 0x8003
+#define ASYNC_RSP_XFER_ERR 0x8004
+#define ASYNC_QWAKEUP 0x8005
+#define ASYNC_TIMEOUT_RESET 0x8006
+#define ASYNC_DEVICE_RESET 0x8007
+#define ASYNC_EXTMSG_UNDERRUN 0x800A
+#define ASYNC_SCAM_INT 0x800B
+#define ASYNC_HUNG_SCSI 0x800C
+#define ASYNC_KILLED_BUS 0x800D
+#define ASYNC_BUS_TRANSIT 0x800E /* LVD -> HVD, eg. */
+#define ASYNC_LIP_OCCURRED 0x8010
+#define ASYNC_LOOP_UP 0x8011
+#define ASYNC_LOOP_DOWN 0x8012
+#define ASYNC_LOOP_RESET 0x8013
+#define ASYNC_PDB_CHANGED 0x8014
+#define ASYNC_CHANGE_NOTIFY 0x8015
+#define ASYNC_LIP_F8 0x8016
+#define ASYNC_CMD_CMPLT 0x8020
+#define ASYNC_CTIO_DONE 0x8021
+#define ASYNC_IP_XMIT_DONE 0x8022
+#define ASYNC_IP_RECV_DONE 0x8023
+#define ASYNC_IP_BROADCAST 0x8024
+#define ASYNC_IP_RCVQ_LOW 0x8025
+#define ASYNC_IP_RCVQ_EMPTY 0x8026
+#define ASYNC_IP_RECV_DONE_ALIGNED 0x8027
+#define ASYNC_PTPMODE 0x8030
+#define ASYNC_RIO1 0x8031
+#define ASYNC_RIO2 0x8032
+#define ASYNC_RIO3 0x8033
+#define ASYNC_RIO4 0x8034
+#define ASYNC_RIO5 0x8035
+#define ASYNC_CONNMODE 0x8036
+#define ISP_CONN_LOOP 1
+#define ISP_CONN_PTP 2
+#define ISP_CONN_BADLIP 3
+#define ISP_CONN_FATAL 4
+#define ISP_CONN_LOOPBACK 5
+#define ASYNC_RIO_RESP 0x8040
+#define ASYNC_RIO_COMP 0x8042
+/*
+ * 2.01.31 2200 Only. Need Bit 13 in Mailbox 1 for Set Firmware Options
+ * mailbox command to enable this.
+ */
+#define ASYNC_QFULL_SENT 0x8049
+
+/*
+ * Mailbox Usages
+ */
+
+#define WRITE_REQUEST_QUEUE_IN_POINTER(isp, value) \
+ ISP_WRITE(isp, isp->isp_rqstinrp, value)
+
+#define READ_REQUEST_QUEUE_OUT_POINTER(isp) \
+ ISP_READ(isp, isp->isp_rqstoutrp)
+
+#define READ_RESPONSE_QUEUE_IN_POINTER(isp) \
+ ISP_READ(isp, isp->isp_respinrp)
+
+#define WRITE_RESPONSE_QUEUE_OUT_POINTER(isp, value) \
+ ISP_WRITE(isp, isp->isp_respoutrp, value)
+
+/*
+ * Command Structure Definitions
+ */
+
+typedef struct {
+ uint32_t ds_base;
+ uint32_t ds_count;
+} ispds_t;
+
+typedef struct {
+ uint32_t ds_base;
+ uint32_t ds_basehi;
+ uint32_t ds_count;
+} ispds64_t;
+
+#define DSTYPE_32BIT 0
+#define DSTYPE_64BIT 1
+typedef struct {
+ uint16_t ds_type; /* 0-> ispds_t, 1-> ispds64_t */
+ uint32_t ds_segment; /* unused */
+ uint32_t ds_base; /* 32 bit address of DSD list */
+} ispdslist_t;
+
+
+/*
+ * These elements get swizzled around for SBus instances.
+ */
+#define ISP_SWAP8(a, b) { \
+ uint8_t tmp; \
+ tmp = a; \
+ a = b; \
+ b = tmp; \
+}
+typedef struct {
+ uint8_t rqs_entry_type;
+ uint8_t rqs_entry_count;
+ uint8_t rqs_seqno;
+ uint8_t rqs_flags;
+} isphdr_t;
+
+/* RQS Flag definitions */
+#define RQSFLAG_CONTINUATION 0x01
+#define RQSFLAG_FULL 0x02
+#define RQSFLAG_BADHEADER 0x04
+#define RQSFLAG_BADPACKET 0x08
+
+/* RQS entry_type definitions */
+#define RQSTYPE_REQUEST 0x01
+#define RQSTYPE_DATASEG 0x02
+#define RQSTYPE_RESPONSE 0x03
+#define RQSTYPE_MARKER 0x04
+#define RQSTYPE_CMDONLY 0x05
+#define RQSTYPE_ATIO 0x06 /* Target Mode */
+#define RQSTYPE_CTIO 0x07 /* Target Mode */
+#define RQSTYPE_SCAM 0x08
+#define RQSTYPE_A64 0x09
+#define RQSTYPE_A64_CONT 0x0a
+#define RQSTYPE_ENABLE_LUN 0x0b /* Target Mode */
+#define RQSTYPE_MODIFY_LUN 0x0c /* Target Mode */
+#define RQSTYPE_NOTIFY 0x0d /* Target Mode */
+#define RQSTYPE_NOTIFY_ACK 0x0e /* Target Mode */
+#define RQSTYPE_CTIO1 0x0f /* Target Mode */
+#define RQSTYPE_STATUS_CONT 0x10
+#define RQSTYPE_T2RQS 0x11
+#define RQSTYPE_IP_XMIT 0x13
+#define RQSTYPE_T4RQS 0x15
+#define RQSTYPE_ATIO2 0x16 /* Target Mode */
+#define RQSTYPE_CTIO2 0x17 /* Target Mode */
+#define RQSTYPE_CSET0 0x18
+#define RQSTYPE_T3RQS 0x19
+#define RQSTYPE_IP_XMIT_64 0x1b
+#define RQSTYPE_CTIO4 0x1e /* Target Mode */
+#define RQSTYPE_CTIO3 0x1f /* Target Mode */
+#define RQSTYPE_RIO1 0x21
+#define RQSTYPE_RIO2 0x22
+#define RQSTYPE_IP_RECV 0x23
+#define RQSTYPE_IP_RECV_CONT 0x24
+
+
+#define ISP_RQDSEG 4
+typedef struct {
+ isphdr_t req_header;
+ uint32_t req_handle;
+ uint8_t req_lun_trn;
+ uint8_t req_target;
+ uint16_t req_cdblen;
+#define req_modifier req_cdblen /* marker packet */
+ uint16_t req_flags;
+ uint16_t req_reserved;
+ uint16_t req_time;
+ uint16_t req_seg_count;
+ uint8_t req_cdb[12];
+ ispds_t req_dataseg[ISP_RQDSEG];
+} ispreq_t;
+
+#define ispreq64_t ispreqt3_t /* same as.... */
+#define ISP_RQDSEG_A64 2
+
+/*
+ * A request packet can also be a marker packet.
+ */
+#define SYNC_DEVICE 0
+#define SYNC_TARGET 1
+#define SYNC_ALL 2
+#define SYNC_LIP 3
+
+#define ISP_RQDSEG_T2 3
+typedef struct {
+ isphdr_t req_header;
+ uint32_t req_handle;
+ uint8_t req_lun_trn;
+ uint8_t req_target;
+ uint16_t req_scclun;
+ uint16_t req_flags;
+ uint16_t _res2;
+ uint16_t req_time;
+ uint16_t req_seg_count;
+ uint8_t req_cdb[16];
+ uint32_t req_totalcnt;
+ ispds_t req_dataseg[ISP_RQDSEG_T2];
+} ispreqt2_t;
+
+typedef struct {
+ isphdr_t req_header;
+ uint32_t req_handle;
+ uint16_t req_target;
+ uint16_t req_scclun;
+ uint16_t req_flags;
+ uint16_t _res2;
+ uint16_t req_time;
+ uint16_t req_seg_count;
+ uint8_t req_cdb[16];
+ uint32_t req_totalcnt;
+ ispds_t req_dataseg[ISP_RQDSEG_T2];
+} ispreqt2e_t;
+
+#define ISP_RQDSEG_T3 2
+typedef struct {
+ isphdr_t req_header;
+ uint32_t req_handle;
+ uint8_t req_lun_trn;
+ uint8_t req_target;
+ uint16_t req_scclun;
+ uint16_t req_flags;
+ uint16_t _res2;
+ uint16_t req_time;
+ uint16_t req_seg_count;
+ uint8_t req_cdb[16];
+ uint32_t req_totalcnt;
+ ispds64_t req_dataseg[ISP_RQDSEG_T3];
+} ispreqt3_t;
+
+typedef struct {
+ isphdr_t req_header;
+ uint32_t req_handle;
+ uint16_t req_target;
+ uint16_t req_scclun;
+ uint16_t req_flags;
+ uint16_t _res2;
+ uint16_t req_time;
+ uint16_t req_seg_count;
+ uint8_t req_cdb[16];
+ uint32_t req_totalcnt;
+ ispds64_t req_dataseg[ISP_RQDSEG_T3];
+} ispreqt3e_t;
+
+/* req_flag values */
+#define REQFLAG_NODISCON 0x0001
+#define REQFLAG_HTAG 0x0002
+#define REQFLAG_OTAG 0x0004
+#define REQFLAG_STAG 0x0008
+#define REQFLAG_TARGET_RTN 0x0010
+
+#define REQFLAG_NODATA 0x0000
+#define REQFLAG_DATA_IN 0x0020
+#define REQFLAG_DATA_OUT 0x0040
+#define REQFLAG_DATA_UNKNOWN 0x0060
+
+#define REQFLAG_DISARQ 0x0100
+#define REQFLAG_FRC_ASYNC 0x0200
+#define REQFLAG_FRC_SYNC 0x0400
+#define REQFLAG_FRC_WIDE 0x0800
+#define REQFLAG_NOPARITY 0x1000
+#define REQFLAG_STOPQ 0x2000
+#define REQFLAG_XTRASNS 0x4000
+#define REQFLAG_PRIORITY 0x8000
+
+typedef struct {
+ isphdr_t req_header;
+ uint32_t req_handle;
+ uint8_t req_lun_trn;
+ uint8_t req_target;
+ uint16_t req_cdblen;
+ uint16_t req_flags;
+ uint16_t _res1;
+ uint16_t req_time;
+ uint16_t req_seg_count;
+ uint8_t req_cdb[44];
+} ispextreq_t;
+
+#define ISP_CDSEG 7
+typedef struct {
+ isphdr_t req_header;
+ uint32_t _res1;
+ ispds_t req_dataseg[ISP_CDSEG];
+} ispcontreq_t;
+
+#define ISP_CDSEG64 5
+typedef struct {
+ isphdr_t req_header;
+ ispds64_t req_dataseg[ISP_CDSEG64];
+} ispcontreq64_t;
+
+typedef struct {
+ isphdr_t req_header;
+ uint32_t req_handle;
+ uint16_t req_scsi_status;
+ uint16_t req_completion_status;
+ uint16_t req_state_flags;
+ uint16_t req_status_flags;
+ uint16_t req_time;
+#define req_response_len req_time /* FC only */
+ uint16_t req_sense_len;
+ uint32_t req_resid;
+ uint8_t req_response[8]; /* FC only */
+ uint8_t req_sense_data[32];
+} ispstatusreq_t;
+
+typedef struct {
+ isphdr_t req_header;
+ uint8_t req_sense_data[60];
+} ispstatus_cont_t;
+
+/*
+ * For Qlogic 2X00, the high order byte of SCSI status has
+ * additional meaning.
+ */
+#define RQCS_RU 0x800 /* Residual Under */
+#define RQCS_RO 0x400 /* Residual Over */
+#define RQCS_RESID (RQCS_RU|RQCS_RO)
+#define RQCS_SV 0x200 /* Sense Length Valid */
+#define RQCS_RV 0x100 /* FCP Response Length Valid */
+
+/*
+ * Completion Status Codes.
+ */
+#define RQCS_COMPLETE 0x0000
+#define RQCS_DMA_ERROR 0x0002
+#define RQCS_RESET_OCCURRED 0x0004
+#define RQCS_ABORTED 0x0005
+#define RQCS_TIMEOUT 0x0006
+#define RQCS_DATA_OVERRUN 0x0007
+#define RQCS_DATA_UNDERRUN 0x0015
+#define RQCS_QUEUE_FULL 0x001C
+
+/* 1X00 Only Completion Codes */
+#define RQCS_INCOMPLETE 0x0001
+#define RQCS_TRANSPORT_ERROR 0x0003
+#define RQCS_COMMAND_OVERRUN 0x0008
+#define RQCS_STATUS_OVERRUN 0x0009
+#define RQCS_BAD_MESSAGE 0x000a
+#define RQCS_NO_MESSAGE_OUT 0x000b
+#define RQCS_EXT_ID_FAILED 0x000c
+#define RQCS_IDE_MSG_FAILED 0x000d
+#define RQCS_ABORT_MSG_FAILED 0x000e
+#define RQCS_REJECT_MSG_FAILED 0x000f
+#define RQCS_NOP_MSG_FAILED 0x0010
+#define RQCS_PARITY_ERROR_MSG_FAILED 0x0011
+#define RQCS_DEVICE_RESET_MSG_FAILED 0x0012
+#define RQCS_ID_MSG_FAILED 0x0013
+#define RQCS_UNEXP_BUS_FREE 0x0014
+#define RQCS_XACT_ERR1 0x0018
+#define RQCS_XACT_ERR2 0x0019
+#define RQCS_XACT_ERR3 0x001A
+#define RQCS_BAD_ENTRY 0x001B
+#define RQCS_PHASE_SKIPPED 0x001D
+#define RQCS_ARQS_FAILED 0x001E
+#define RQCS_WIDE_FAILED 0x001F
+#define RQCS_SYNCXFER_FAILED 0x0020
+#define RQCS_LVD_BUSERR 0x0021
+
+/* 2X00 Only Completion Codes */
+#define RQCS_PORT_UNAVAILABLE 0x0028
+#define RQCS_PORT_LOGGED_OUT 0x0029
+#define RQCS_PORT_CHANGED 0x002A
+#define RQCS_PORT_BUSY 0x002B
+
+/*
+ * 1X00 specific State Flags
+ */
+#define RQSF_GOT_BUS 0x0100
+#define RQSF_GOT_TARGET 0x0200
+#define RQSF_SENT_CDB 0x0400
+#define RQSF_XFRD_DATA 0x0800
+#define RQSF_GOT_STATUS 0x1000
+#define RQSF_GOT_SENSE 0x2000
+#define RQSF_XFER_COMPLETE 0x4000
+
+/*
+ * 2X00 specific State Flags
+ * (same as 1X00 except RQSF_GOT_BUS/RQSF_GOT_TARGET are not available)
+ */
+#define RQSF_DATA_IN 0x0020
+#define RQSF_DATA_OUT 0x0040
+#define RQSF_STAG 0x0008
+#define RQSF_OTAG 0x0004
+#define RQSF_HTAG 0x0002
+/*
+ * 1X00 Status Flags
+ */
+#define RQSTF_DISCONNECT 0x0001
+#define RQSTF_SYNCHRONOUS 0x0002
+#define RQSTF_PARITY_ERROR 0x0004
+#define RQSTF_BUS_RESET 0x0008
+#define RQSTF_DEVICE_RESET 0x0010
+#define RQSTF_ABORTED 0x0020
+#define RQSTF_TIMEOUT 0x0040
+#define RQSTF_NEGOTIATION 0x0080
+
+/*
+ * 2X00 specific state flags
+ */
+/* RQSF_SENT_CDB */
+/* RQSF_XFRD_DATA */
+/* RQSF_GOT_STATUS */
+/* RQSF_XFER_COMPLETE */
+
+/*
+ * 2X00 specific status flags
+ */
+/* RQSTF_ABORTED */
+/* RQSTF_TIMEOUT */
+#define RQSTF_DMA_ERROR 0x0080
+#define RQSTF_LOGOUT 0x2000
+
+/*
+ * Miscellaneous
+ */
+#ifndef ISP_EXEC_THROTTLE
+#define ISP_EXEC_THROTTLE 16
+#endif
+
+/*
+ * About Firmware returns an 'attribute' word in mailbox 6.
+ */
+#define ISP_FW_ATTR_TMODE 0x01
+#define ISP_FW_ATTR_SCCLUN 0x02
+#define ISP_FW_ATTR_FABRIC 0x04
+#define ISP_FW_ATTR_CLASS2 0x08
+#define ISP_FW_ATTR_FCTAPE 0x10
+#define ISP_FW_ATTR_IP 0x20
+#define ISP_FW_ATTR_VI 0x40
+#define ISP_FW_ATTR_VI_SOLARIS 0x80
+#define ISP_FW_ATTR_2KLOGINS 0x100 /* XXX: just a guess */
+
+#define IS_2KLOGIN(isp) \
+ (IS_FC(isp) && (FCPARAM(isp)->isp_fwattr & ISP_FW_ATTR_2KLOGINS))
+
+/*
+ * Reduced Interrupt Operation Response Queue Entreis
+ */
+
+typedef struct {
+ isphdr_t req_header;
+ uint32_t req_handles[15];
+} isp_rio1_t;
+
+typedef struct {
+ isphdr_t req_header;
+ uint16_t req_handles[30];
+} isp_rio2_t;
+
+/*
+ * FC (ISP2100) specific data structures
+ */
+
+/*
+ * Initialization Control Block
+ *
+ * Version One (prime) format.
+ */
+typedef struct isp_icb {
+ uint8_t icb_version;
+ uint8_t _reserved0;
+ uint16_t icb_fwoptions;
+ uint16_t icb_maxfrmlen;
+ uint16_t icb_maxalloc;
+ uint16_t icb_execthrottle;
+ uint8_t icb_retry_count;
+ uint8_t icb_retry_delay;
+ uint8_t icb_portname[8];
+ uint16_t icb_hardaddr;
+ uint8_t icb_iqdevtype;
+ uint8_t icb_logintime;
+ uint8_t icb_nodename[8];
+ uint16_t icb_rqstout;
+ uint16_t icb_rspnsin;
+ uint16_t icb_rqstqlen;
+ uint16_t icb_rsltqlen;
+ uint16_t icb_rqstaddr[4];
+ uint16_t icb_respaddr[4];
+ uint16_t icb_lunenables;
+ uint8_t icb_ccnt;
+ uint8_t icb_icnt;
+ uint16_t icb_lunetimeout;
+ uint16_t _reserved1;
+ uint16_t icb_xfwoptions;
+ uint8_t icb_racctimer;
+ uint8_t icb_idelaytimer;
+ uint16_t icb_zfwoptions;
+ uint16_t _reserved2[13];
+} isp_icb_t;
+#define ICB_VERSION1 1
+
+#define ICBOPT_HARD_ADDRESS 0x0001
+#define ICBOPT_FAIRNESS 0x0002
+#define ICBOPT_FULL_DUPLEX 0x0004
+#define ICBOPT_FAST_POST 0x0008
+#define ICBOPT_TGT_ENABLE 0x0010
+#define ICBOPT_INI_DISABLE 0x0020
+#define ICBOPT_INI_ADISC 0x0040
+#define ICBOPT_INI_TGTTYPE 0x0080
+#define ICBOPT_PDBCHANGE_AE 0x0100
+#define ICBOPT_NOLIP 0x0200
+#define ICBOPT_SRCHDOWN 0x0400
+#define ICBOPT_PREVLOOP 0x0800
+#define ICBOPT_STOP_ON_QFULL 0x1000
+#define ICBOPT_FULL_LOGIN 0x2000
+#define ICBOPT_BOTH_WWNS 0x4000
+#define ICBOPT_EXTENDED 0x8000
+
+#define ICBXOPT_CLASS2_ACK0 0x0200
+#define ICBXOPT_CLASS2 0x0100
+#define ICBXOPT_LOOP_ONLY (0 << 4)
+#define ICBXOPT_PTP_ONLY (1 << 4)
+#define ICBXOPT_LOOP_2_PTP (2 << 4)
+#define ICBXOPT_PTP_2_LOOP (3 << 4)
+
+/*
+ * The lower 4 bits of the xfwoptions field are the OPERATION MODE bits.
+ * RIO is not defined for the 23XX cards
+ */
+#define ICBXOPT_RIO_OFF 0
+#define ICBXOPT_RIO_16BIT 1
+#define ICBXOPT_RIO_32BIT 2
+#define ICBXOPT_RIO_16BIT_IOCB 3
+#define ICBXOPT_RIO_32BIT_IOCB 4
+#define ICBXOPT_ZIO 5
+#define ICBXOPT_TIMER_MASK 0x7
+
+#define ICBZOPT_ENA_RDXFR_RDY 0x01
+#define ICBZOPT_ENA_OOF (1 << 6) /* out of order frame handling */
+#define ICBZOPT_50_OHM 0x0200
+/* These 3 only apply to the 2300 */
+#define ICBZOPT_RATE_ONEGB (MBGSD_ONEGB << 14)
+#define ICBZOPT_RATE_TWOGB (MBGSD_TWOGB << 14)
+#define ICBZOPT_RATE_AUTO (MBGSD_AUTO << 14)
+
+
+#define ICB_MIN_FRMLEN 256
+#define ICB_MAX_FRMLEN 2112
+#define ICB_DFLT_FRMLEN 1024
+#define ICB_DFLT_ALLOC 256
+#define ICB_DFLT_THROTTLE 16
+#define ICB_DFLT_RDELAY 5
+#define ICB_DFLT_RCOUNT 3
+
+#define ICB_LOGIN_TOV 30
+#define ICB_LUN_ENABLE_TOV 180
+
+
+
+#define RQRSP_ADDR0015 0
+#define RQRSP_ADDR1631 1
+#define RQRSP_ADDR3247 2
+#define RQRSP_ADDR4863 3
+
+
+#define ICB_NNM0 7
+#define ICB_NNM1 6
+#define ICB_NNM2 5
+#define ICB_NNM3 4
+#define ICB_NNM4 3
+#define ICB_NNM5 2
+#define ICB_NNM6 1
+#define ICB_NNM7 0
+
+#define MAKE_NODE_NAME_FROM_WWN(array, wwn) \
+ array[ICB_NNM0] = (uint8_t) ((wwn >> 0) & 0xff), \
+ array[ICB_NNM1] = (uint8_t) ((wwn >> 8) & 0xff), \
+ array[ICB_NNM2] = (uint8_t) ((wwn >> 16) & 0xff), \
+ array[ICB_NNM3] = (uint8_t) ((wwn >> 24) & 0xff), \
+ array[ICB_NNM4] = (uint8_t) ((wwn >> 32) & 0xff), \
+ array[ICB_NNM5] = (uint8_t) ((wwn >> 40) & 0xff), \
+ array[ICB_NNM6] = (uint8_t) ((wwn >> 48) & 0xff), \
+ array[ICB_NNM7] = (uint8_t) ((wwn >> 56) & 0xff)
+
+#define MAKE_WWN_FROM_NODE_NAME(wwn, array) \
+ wwn = ((uint64_t) array[ICB_NNM0]) | \
+ ((uint64_t) array[ICB_NNM1] << 8) | \
+ ((uint64_t) array[ICB_NNM2] << 16) | \
+ ((uint64_t) array[ICB_NNM3] << 24) | \
+ ((uint64_t) array[ICB_NNM4] << 32) | \
+ ((uint64_t) array[ICB_NNM5] << 40) | \
+ ((uint64_t) array[ICB_NNM6] << 48) | \
+ ((uint64_t) array[ICB_NNM7] << 56)
+
+/*
+ * FC-AL Position Map
+ *
+ * This is an at most 128 byte map that returns either
+ * the LILP or Firmware generated list of ports.
+ *
+ * We deviate a bit from the returned qlogic format to
+ * use an extra bit to say whether this was a LILP or
+ * f/w generated map.
+ */
+typedef struct {
+ uint8_t fwmap : 1,
+ count : 7;
+ uint8_t map[127];
+} fcpos_map_t;
+
+/*
+ * Port Data Base Element
+ */
+
+typedef struct {
+ uint16_t pdb_options;
+ uint8_t pdb_mstate;
+ uint8_t pdb_sstate;
+#define BITS2WORD(x) ((x)[0] << 16 | (x)[3] << 8 | (x)[2])
+ uint8_t pdb_hardaddr_bits[4];
+ uint8_t pdb_portid_bits[4];
+ uint8_t pdb_nodename[8];
+ uint8_t pdb_portname[8];
+ uint16_t pdb_execthrottle;
+ uint16_t pdb_exec_count;
+ uint8_t pdb_retry_count;
+ uint8_t pdb_retry_delay;
+ uint16_t pdb_resalloc;
+ uint16_t pdb_curalloc;
+ uint16_t pdb_qhead;
+ uint16_t pdb_qtail;
+ uint16_t pdb_tl_next;
+ uint16_t pdb_tl_last;
+ uint16_t pdb_features; /* PLOGI, Common Service */
+ uint16_t pdb_pconcurrnt; /* PLOGI, Common Service */
+ uint16_t pdb_roi; /* PLOGI, Common Service */
+ uint8_t pdb_target;
+ uint8_t pdb_initiator; /* PLOGI, Class 3 Control Flags */
+ uint16_t pdb_rdsiz; /* PLOGI, Class 3 */
+ uint16_t pdb_ncseq; /* PLOGI, Class 3 */
+ uint16_t pdb_noseq; /* PLOGI, Class 3 */
+ uint16_t pdb_labrtflg;
+ uint16_t pdb_lstopflg;
+ uint16_t pdb_sqhead;
+ uint16_t pdb_sqtail;
+ uint16_t pdb_ptimer;
+ uint16_t pdb_nxt_seqid;
+ uint16_t pdb_fcount;
+ uint16_t pdb_prli_len;
+ uint16_t pdb_prli_svc0;
+ uint16_t pdb_prli_svc3;
+ uint16_t pdb_loopid;
+ uint16_t pdb_il_ptr;
+ uint16_t pdb_sl_ptr;
+} isp_pdb_t;
+
+#define PDB_OPTIONS_XMITTING (1<<11)
+#define PDB_OPTIONS_LNKXMIT (1<<10)
+#define PDB_OPTIONS_ABORTED (1<<9)
+#define PDB_OPTIONS_ADISC (1<<1)
+
+#define PDB_STATE_DISCOVERY 0
+#define PDB_STATE_WDISC_ACK 1
+#define PDB_STATE_PLOGI 2
+#define PDB_STATE_PLOGI_ACK 3
+#define PDB_STATE_PRLI 4
+#define PDB_STATE_PRLI_ACK 5
+#define PDB_STATE_LOGGED_IN 6
+#define PDB_STATE_PORT_UNAVAIL 7
+#define PDB_STATE_PRLO 8
+#define PDB_STATE_PRLO_ACK 9
+#define PDB_STATE_PLOGO 10
+#define PDB_STATE_PLOG_ACK 11
+
+#define SVC3_TGT_ROLE 0x10
+#define SVC3_INI_ROLE 0x20
+#define SVC3_ROLE_MASK 0x30
+#define SVC3_ROLE_SHIFT 4
+
+/*
+ * CT definition
+ *
+ * This is as the QLogic f/w documentations defines it- which is just opposite,
+ * bit wise, from what the specification defines it as. Additionally, the
+ * ct_response and ct_resid (really from FC-GS-2) need to be byte swapped.
+ */
+
+typedef struct {
+ uint8_t ct_revision;
+ uint8_t ct_portid[3];
+ uint8_t ct_fcs_type;
+ uint8_t ct_fcs_subtype;
+ uint8_t ct_options;
+ uint8_t ct_res0;
+ uint16_t ct_response;
+ uint16_t ct_resid;
+ uint8_t ct_res1;
+ uint8_t ct_reason;
+ uint8_t ct_explanation;
+ uint8_t ct_vunique;
+} ct_hdr_t;
+#define FS_ACC 0x8002
+#define FS_RJT 0x8001
+
+#define FC4_IP 5 /* ISO/EEC 8802-2 LLC/SNAP "Out of Order Delivery" */
+#define FC4_SCSI 8 /* SCSI-3 via Fivre Channel Protocol (FCP) */
+#define FC4_FC_SVC 0x20 /* Fibre Channel Services */
+
+#define SNS_GA_NXT 0x100
+#define SNS_GPN_ID 0x112
+#define SNS_GNN_ID 0x113
+#define SNS_GFF_ID 0x11F
+#define SNS_GID_FT 0x171
+#define SNS_RFT_ID 0x217
+typedef struct {
+ uint16_t snscb_rblen; /* response buffer length (words) */
+ uint16_t snscb_res0;
+ uint16_t snscb_addr[4]; /* response buffer address */
+ uint16_t snscb_sblen; /* subcommand buffer length (words) */
+ uint16_t snscb_res1;
+ uint16_t snscb_data[1]; /* variable data */
+} sns_screq_t; /* Subcommand Request Structure */
+
+typedef struct {
+ uint16_t snscb_rblen; /* response buffer length (words) */
+ uint16_t snscb_res0;
+ uint16_t snscb_addr[4]; /* response buffer address */
+ uint16_t snscb_sblen; /* subcommand buffer length (words) */
+ uint16_t snscb_res1;
+ uint16_t snscb_cmd;
+ uint16_t snscb_res2;
+ uint32_t snscb_res3;
+ uint32_t snscb_port;
+} sns_ga_nxt_req_t;
+#define SNS_GA_NXT_REQ_SIZE (sizeof (sns_ga_nxt_req_t))
+
+typedef struct {
+ uint16_t snscb_rblen; /* response buffer length (words) */
+ uint16_t snscb_res0;
+ uint16_t snscb_addr[4]; /* response buffer address */
+ uint16_t snscb_sblen; /* subcommand buffer length (words) */
+ uint16_t snscb_res1;
+ uint16_t snscb_cmd;
+ uint16_t snscb_res2;
+ uint32_t snscb_res3;
+ uint32_t snscb_portid;
+} sns_gxn_id_req_t;
+#define SNS_GXN_ID_REQ_SIZE (sizeof (sns_gxn_id_req_t))
+
+typedef struct {
+ uint16_t snscb_rblen; /* response buffer length (words) */
+ uint16_t snscb_res0;
+ uint16_t snscb_addr[4]; /* response buffer address */
+ uint16_t snscb_sblen; /* subcommand buffer length (words) */
+ uint16_t snscb_res1;
+ uint16_t snscb_cmd;
+ uint16_t snscb_mword_div_2;
+ uint32_t snscb_res3;
+ uint32_t snscb_fc4_type;
+} sns_gid_ft_req_t;
+#define SNS_GID_FT_REQ_SIZE (sizeof (sns_gid_ft_req_t))
+
+typedef struct {
+ uint16_t snscb_rblen; /* response buffer length (words) */
+ uint16_t snscb_res0;
+ uint16_t snscb_addr[4]; /* response buffer address */
+ uint16_t snscb_sblen; /* subcommand buffer length (words) */
+ uint16_t snscb_res1;
+ uint16_t snscb_cmd;
+ uint16_t snscb_res2;
+ uint32_t snscb_res3;
+ uint32_t snscb_port;
+ uint32_t snscb_fc4_types[8];
+} sns_rft_id_req_t;
+#define SNS_RFT_ID_REQ_SIZE (sizeof (sns_rft_id_req_t))
+
+typedef struct {
+ ct_hdr_t snscb_cthdr;
+ uint8_t snscb_port_type;
+ uint8_t snscb_port_id[3];
+ uint8_t snscb_portname[8];
+ uint16_t snscb_data[1]; /* variable data */
+} sns_scrsp_t; /* Subcommand Response Structure */
+
+typedef struct {
+ ct_hdr_t snscb_cthdr;
+ uint8_t snscb_port_type;
+ uint8_t snscb_port_id[3];
+ uint8_t snscb_portname[8];
+ uint8_t snscb_pnlen; /* symbolic port name length */
+ uint8_t snscb_pname[255]; /* symbolic port name */
+ uint8_t snscb_nodename[8];
+ uint8_t snscb_nnlen; /* symbolic node name length */
+ uint8_t snscb_nname[255]; /* symbolic node name */
+ uint8_t snscb_ipassoc[8];
+ uint8_t snscb_ipaddr[16];
+ uint8_t snscb_svc_class[4];
+ uint8_t snscb_fc4_types[32];
+ uint8_t snscb_fpname[8];
+ uint8_t snscb_reserved;
+ uint8_t snscb_hardaddr[3];
+} sns_ga_nxt_rsp_t; /* Subcommand Response Structure */
+#define SNS_GA_NXT_RESP_SIZE (sizeof (sns_ga_nxt_rsp_t))
+
+typedef struct {
+ ct_hdr_t snscb_cthdr;
+ uint8_t snscb_wwn[8];
+} sns_gxn_id_rsp_t;
+#define SNS_GXN_ID_RESP_SIZE (sizeof (sns_gxn_id_rsp_t))
+
+typedef struct {
+ ct_hdr_t snscb_cthdr;
+ uint32_t snscb_fc4_features[32];
+} sns_gff_id_rsp_t;
+#define SNS_GFF_ID_RESP_SIZE (sizeof (sns_gff_id_rsp_t))
+
+typedef struct {
+ ct_hdr_t snscb_cthdr;
+ struct {
+ uint8_t control;
+ uint8_t portid[3];
+ } snscb_ports[1];
+} sns_gid_ft_rsp_t;
+#define SNS_GID_FT_RESP_SIZE(x) ((sizeof (sns_gid_ft_rsp_t)) + ((x - 1) << 2))
+
+#define SNS_RFT_ID_RESP_SIZE (sizeof (ct_hdr_t))
+
+#endif /* _ISPMBOX_H */
diff --git a/sys/dev/isp/ispreg.h b/sys/dev/isp/ispreg.h
new file mode 100644
index 000000000000..8c5e5d413ee2
--- /dev/null
+++ b/sys/dev/isp/ispreg.h
@@ -0,0 +1,1035 @@
+/* $FreeBSD$ */
+/*-
+ * Machine Independent (well, as best as possible) register
+ * definitions for Qlogic ISP SCSI adapters.
+ *
+ * Copyright (c) 1997-2006 by Matthew Jacob
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice 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.
+ *
+ * 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.
+ */
+#ifndef _ISPREG_H
+#define _ISPREG_H
+
+/*
+ * Hardware definitions for the Qlogic ISP registers.
+ */
+
+/*
+ * This defines types of access to various registers.
+ *
+ * R: Read Only
+ * W: Write Only
+ * RW: Read/Write
+ *
+ * R*, W*, RW*: Read Only, Write Only, Read/Write, but only
+ * if RISC processor in ISP is paused.
+ */
+
+/*
+ * Offsets for various register blocks.
+ *
+ * Sad but true, different architectures have different offsets.
+ *
+ * Don't be alarmed if none of this makes sense. The original register
+ * layout set some defines in a certain pattern. Everything else has been
+ * grafted on since. For example, the ISP1080 manual will state that DMA
+ * registers start at 0x80 from the base of the register address space.
+ * That's true, but for our purposes, we define DMA_REGS_OFF for the 1080
+ * to start at offset 0x60 because the DMA registers are all defined to
+ * be DMA_BLOCK+0x20 and so on. Clear?
+ */
+
+#define BIU_REGS_OFF 0x00
+
+#define PCI_MBOX_REGS_OFF 0x70
+#define PCI_MBOX_REGS2100_OFF 0x10
+#define PCI_MBOX_REGS2300_OFF 0x40
+#define SBUS_MBOX_REGS_OFF 0x80
+
+#define PCI_SXP_REGS_OFF 0x80
+#define SBUS_SXP_REGS_OFF 0x200
+
+#define PCI_RISC_REGS_OFF 0x80
+#define SBUS_RISC_REGS_OFF 0x400
+
+/* Bless me! Chip designers have putzed it again! */
+#define ISP1080_DMA_REGS_OFF 0x60
+#define DMA_REGS_OFF 0x00 /* same as BIU block */
+
+#define SBUS_REGSIZE 0x450
+#define PCI_REGSIZE 0x100
+
+/*
+ * NB: The *_BLOCK definitions have no specific hardware meaning.
+ * They serve simply to note to the MD layer which block of
+ * registers offsets are being accessed.
+ */
+#define _NREG_BLKS 5
+#define _BLK_REG_SHFT 13
+#define _BLK_REG_MASK (7 << _BLK_REG_SHFT)
+#define BIU_BLOCK (0 << _BLK_REG_SHFT)
+#define MBOX_BLOCK (1 << _BLK_REG_SHFT)
+#define SXP_BLOCK (2 << _BLK_REG_SHFT)
+#define RISC_BLOCK (3 << _BLK_REG_SHFT)
+#define DMA_BLOCK (4 << _BLK_REG_SHFT)
+
+/*
+ * Bus Interface Block Register Offsets
+ */
+
+#define BIU_ID_LO (BIU_BLOCK+0x0) /* R : Bus ID, Low */
+#define BIU2100_FLASH_ADDR (BIU_BLOCK+0x0)
+#define BIU_ID_HI (BIU_BLOCK+0x2) /* R : Bus ID, High */
+#define BIU2100_FLASH_DATA (BIU_BLOCK+0x2)
+#define BIU_CONF0 (BIU_BLOCK+0x4) /* R : Bus Configuration #0 */
+#define BIU_CONF1 (BIU_BLOCK+0x6) /* R : Bus Configuration #1 */
+#define BIU2100_CSR (BIU_BLOCK+0x6)
+#define BIU_ICR (BIU_BLOCK+0x8) /* RW : Bus Interface Ctrl */
+#define BIU_ISR (BIU_BLOCK+0xA) /* R : Bus Interface Status */
+#define BIU_SEMA (BIU_BLOCK+0xC) /* RW : Bus Semaphore */
+#define BIU_NVRAM (BIU_BLOCK+0xE) /* RW : Bus NVRAM */
+/*
+ * These are specific to the 2300.
+ *
+ * They *claim* you can read BIU_R2HSTSLO with a full 32 bit access
+ * and get both registers, but I'm a bit dubious about that. But the
+ * point here is that the top 16 bits are firmware defined bits that
+ * the RISC processor uses to inform the host about something- usually
+ * something which was nominally in a mailbox register.
+ */
+#define BIU_REQINP (BIU_BLOCK+0x10) /* Request Queue In */
+#define BIU_REQOUTP (BIU_BLOCK+0x12) /* Request Queue Out */
+#define BIU_RSPINP (BIU_BLOCK+0x14) /* Response Queue In */
+#define BIU_RSPOUTP (BIU_BLOCK+0x16) /* Response Queue Out */
+
+#define BIU_R2HSTSLO (BIU_BLOCK+0x18)
+#define BIU_R2HSTSHI (BIU_BLOCK+0x1A)
+
+#define BIU_R2HST_INTR (1 << 15) /* RISC to Host Interrupt */
+#define BIU_R2HST_PAUSED (1 << 8) /* RISC paused */
+#define BIU_R2HST_ISTAT_MASK 0x3f /* intr information && status */
+#define ISPR2HST_ROM_MBX_OK 0x1 /* ROM mailbox cmd done ok */
+#define ISPR2HST_ROM_MBX_FAIL 0x2 /* ROM mailbox cmd done fail */
+#define ISPR2HST_MBX_OK 0x10 /* mailbox cmd done ok */
+#define ISPR2HST_MBX_FAIL 0x11 /* mailbox cmd done fail */
+#define ISPR2HST_ASYNC_EVENT 0x12 /* Async Event */
+#define ISPR2HST_RSPQ_UPDATE 0x13 /* Response Queue Update */
+#define ISPR2HST_RQST_UPDATE 0x14 /* Resquest Queue Update */
+#define ISPR2HST_RIO_16 0x15 /* RIO 1-16 */
+#define ISPR2HST_FPOST 0x16 /* Low 16 bits fast post */
+#define ISPR2HST_FPOST_CTIO 0x17 /* Low 16 bits fast post ctio */
+
+#define DFIFO_COMMAND (BIU_BLOCK+0x60) /* RW : Command FIFO Port */
+#define RDMA2100_CONTROL DFIFO_COMMAND
+#define DFIFO_DATA (BIU_BLOCK+0x62) /* RW : Data FIFO Port */
+
+/*
+ * Putzed DMA register layouts.
+ */
+#define CDMA_CONF (DMA_BLOCK+0x20) /* RW*: DMA Configuration */
+#define CDMA2100_CONTROL CDMA_CONF
+#define CDMA_CONTROL (DMA_BLOCK+0x22) /* RW*: DMA Control */
+#define CDMA_STATUS (DMA_BLOCK+0x24) /* R : DMA Status */
+#define CDMA_FIFO_STS (DMA_BLOCK+0x26) /* R : DMA FIFO Status */
+#define CDMA_COUNT (DMA_BLOCK+0x28) /* RW*: DMA Transfer Count */
+#define CDMA_ADDR0 (DMA_BLOCK+0x2C) /* RW*: DMA Address, Word 0 */
+#define CDMA_ADDR1 (DMA_BLOCK+0x2E) /* RW*: DMA Address, Word 1 */
+#define CDMA_ADDR2 (DMA_BLOCK+0x30) /* RW*: DMA Address, Word 2 */
+#define CDMA_ADDR3 (DMA_BLOCK+0x32) /* RW*: DMA Address, Word 3 */
+
+#define DDMA_CONF (DMA_BLOCK+0x40) /* RW*: DMA Configuration */
+#define TDMA2100_CONTROL DDMA_CONF
+#define DDMA_CONTROL (DMA_BLOCK+0x42) /* RW*: DMA Control */
+#define DDMA_STATUS (DMA_BLOCK+0x44) /* R : DMA Status */
+#define DDMA_FIFO_STS (DMA_BLOCK+0x46) /* R : DMA FIFO Status */
+#define DDMA_COUNT_LO (DMA_BLOCK+0x48) /* RW*: DMA Xfer Count, Low */
+#define DDMA_COUNT_HI (DMA_BLOCK+0x4A) /* RW*: DMA Xfer Count, High */
+#define DDMA_ADDR0 (DMA_BLOCK+0x4C) /* RW*: DMA Address, Word 0 */
+#define DDMA_ADDR1 (DMA_BLOCK+0x4E) /* RW*: DMA Address, Word 1 */
+/* these are for the 1040A cards */
+#define DDMA_ADDR2 (DMA_BLOCK+0x50) /* RW*: DMA Address, Word 2 */
+#define DDMA_ADDR3 (DMA_BLOCK+0x52) /* RW*: DMA Address, Word 3 */
+
+
+/*
+ * Bus Interface Block Register Definitions
+ */
+/* BUS CONFIGURATION REGISTER #0 */
+#define BIU_CONF0_HW_MASK 0x000F /* Hardware revision mask */
+/* BUS CONFIGURATION REGISTER #1 */
+
+#define BIU_SBUS_CONF1_PARITY 0x0100 /* Enable parity checking */
+#define BIU_SBUS_CONF1_FCODE_MASK 0x00F0 /* Fcode cycle mask */
+
+#define BIU_PCI_CONF1_FIFO_128 0x0040 /* 128 bytes FIFO threshold */
+#define BIU_PCI_CONF1_FIFO_64 0x0030 /* 64 bytes FIFO threshold */
+#define BIU_PCI_CONF1_FIFO_32 0x0020 /* 32 bytes FIFO threshold */
+#define BIU_PCI_CONF1_FIFO_16 0x0010 /* 16 bytes FIFO threshold */
+#define BIU_BURST_ENABLE 0x0004 /* Global enable Bus bursts */
+#define BIU_SBUS_CONF1_FIFO_64 0x0003 /* 64 bytes FIFO threshold */
+#define BIU_SBUS_CONF1_FIFO_32 0x0002 /* 32 bytes FIFO threshold */
+#define BIU_SBUS_CONF1_FIFO_16 0x0001 /* 16 bytes FIFO threshold */
+#define BIU_SBUS_CONF1_FIFO_8 0x0000 /* 8 bytes FIFO threshold */
+#define BIU_SBUS_CONF1_BURST8 0x0008 /* Enable 8-byte bursts */
+#define BIU_PCI_CONF1_SXP 0x0008 /* SXP register select */
+
+#define BIU_PCI1080_CONF1_SXP0 0x0100 /* SXP bank #1 select */
+#define BIU_PCI1080_CONF1_SXP1 0x0200 /* SXP bank #2 select */
+#define BIU_PCI1080_CONF1_DMA 0x0300 /* DMA bank select */
+
+/* ISP2100 Bus Control/Status Register */
+
+#define BIU2100_ICSR_REGBSEL 0x30 /* RW: register bank select */
+#define BIU2100_RISC_REGS (0 << 4) /* RISC Regs */
+#define BIU2100_FB_REGS (1 << 4) /* FrameBuffer Regs */
+#define BIU2100_FPM0_REGS (2 << 4) /* FPM 0 Regs */
+#define BIU2100_FPM1_REGS (3 << 4) /* FPM 1 Regs */
+#define BIU2100_NVRAM_OFFSET (1 << 14)
+#define BIU2100_FLASH_UPPER_64K 0x04 /* RW: Upper 64K Bank Select */
+#define BIU2100_FLASH_ENABLE 0x02 /* RW: Enable Flash RAM */
+#define BIU2100_SOFT_RESET 0x01
+/* SOFT RESET FOR ISP2100 is same bit, but in this register, not ICR */
+
+
+/* BUS CONTROL REGISTER */
+#define BIU_ICR_ENABLE_DMA_INT 0x0020 /* Enable DMA interrupts */
+#define BIU_ICR_ENABLE_CDMA_INT 0x0010 /* Enable CDMA interrupts */
+#define BIU_ICR_ENABLE_SXP_INT 0x0008 /* Enable SXP interrupts */
+#define BIU_ICR_ENABLE_RISC_INT 0x0004 /* Enable Risc interrupts */
+#define BIU_ICR_ENABLE_ALL_INTS 0x0002 /* Global enable all inter */
+#define BIU_ICR_SOFT_RESET 0x0001 /* Soft Reset of ISP */
+
+#define BIU2100_ICR_ENABLE_ALL_INTS 0x8000
+#define BIU2100_ICR_ENA_FPM_INT 0x0020
+#define BIU2100_ICR_ENA_FB_INT 0x0010
+#define BIU2100_ICR_ENA_RISC_INT 0x0008
+#define BIU2100_ICR_ENA_CDMA_INT 0x0004
+#define BIU2100_ICR_ENABLE_RXDMA_INT 0x0002
+#define BIU2100_ICR_ENABLE_TXDMA_INT 0x0001
+#define BIU2100_ICR_DISABLE_ALL_INTS 0x0000
+
+#define ENABLE_INTS(isp) (IS_SCSI(isp))? \
+ ISP_WRITE(isp, BIU_ICR, BIU_ICR_ENABLE_RISC_INT | BIU_ICR_ENABLE_ALL_INTS) : \
+ ISP_WRITE(isp, BIU_ICR, BIU2100_ICR_ENA_RISC_INT | BIU2100_ICR_ENABLE_ALL_INTS)
+
+#define INTS_ENABLED(isp) ((IS_SCSI(isp))? \
+ (ISP_READ(isp, BIU_ICR) & (BIU_ICR_ENABLE_RISC_INT|BIU_ICR_ENABLE_ALL_INTS)) :\
+ (ISP_READ(isp, BIU_ICR) & \
+ (BIU2100_ICR_ENA_RISC_INT|BIU2100_ICR_ENABLE_ALL_INTS)))
+
+#define DISABLE_INTS(isp) ISP_WRITE(isp, BIU_ICR, 0)
+
+/* BUS STATUS REGISTER */
+#define BIU_ISR_DMA_INT 0x0020 /* DMA interrupt pending */
+#define BIU_ISR_CDMA_INT 0x0010 /* CDMA interrupt pending */
+#define BIU_ISR_SXP_INT 0x0008 /* SXP interrupt pending */
+#define BIU_ISR_RISC_INT 0x0004 /* Risc interrupt pending */
+#define BIU_ISR_IPEND 0x0002 /* Global interrupt pending */
+
+#define BIU2100_ISR_INT_PENDING 0x8000 /* Global interrupt pending */
+#define BIU2100_ISR_FPM_INT 0x0020 /* FPM interrupt pending */
+#define BIU2100_ISR_FB_INT 0x0010 /* FB interrupt pending */
+#define BIU2100_ISR_RISC_INT 0x0008 /* Risc interrupt pending */
+#define BIU2100_ISR_CDMA_INT 0x0004 /* CDMA interrupt pending */
+#define BIU2100_ISR_RXDMA_INT_PENDING 0x0002 /* Global interrupt pending */
+#define BIU2100_ISR_TXDMA_INT_PENDING 0x0001 /* Global interrupt pending */
+
+#define INT_PENDING(isp, isr) (IS_FC(isp)? \
+ ((isr & BIU2100_ISR_RISC_INT) != 0) : ((isr & BIU_ISR_RISC_INT) != 0))
+
+#define INT_PENDING_MASK(isp) \
+ (IS_FC(isp)? BIU2100_ISR_RISC_INT: BIU_ISR_RISC_INT)
+
+/* BUS SEMAPHORE REGISTER */
+#define BIU_SEMA_STATUS 0x0002 /* Semaphore Status Bit */
+#define BIU_SEMA_LOCK 0x0001 /* Semaphore Lock Bit */
+
+/* NVRAM SEMAPHORE REGISTER */
+#define BIU_NVRAM_CLOCK 0x0001
+#define BIU_NVRAM_SELECT 0x0002
+#define BIU_NVRAM_DATAOUT 0x0004
+#define BIU_NVRAM_DATAIN 0x0008
+#define BIU_NVRAM_BUSY 0x0080 /* 2322/24xx only */
+#define ISP_NVRAM_READ 6
+
+/* COMNMAND && DATA DMA CONFIGURATION REGISTER */
+#define DMA_ENABLE_SXP_DMA 0x0008 /* Enable SXP to DMA Data */
+#define DMA_ENABLE_INTS 0x0004 /* Enable interrupts to RISC */
+#define DMA_ENABLE_BURST 0x0002 /* Enable Bus burst trans */
+#define DMA_DMA_DIRECTION 0x0001 /*
+ * Set DMA direction:
+ * 0 - DMA FIFO to host
+ * 1 - Host to DMA FIFO
+ */
+
+/* COMMAND && DATA DMA CONTROL REGISTER */
+#define DMA_CNTRL_SUSPEND_CHAN 0x0010 /* Suspend DMA transfer */
+#define DMA_CNTRL_CLEAR_CHAN 0x0008 /*
+ * Clear FIFO and DMA Channel,
+ * reset DMA registers
+ */
+#define DMA_CNTRL_CLEAR_FIFO 0x0004 /* Clear DMA FIFO */
+#define DMA_CNTRL_RESET_INT 0x0002 /* Clear DMA interrupt */
+#define DMA_CNTRL_STROBE 0x0001 /* Start DMA transfer */
+
+/*
+ * Variants of same for 2100
+ */
+#define DMA_CNTRL2100_CLEAR_CHAN 0x0004
+#define DMA_CNTRL2100_RESET_INT 0x0002
+
+
+
+/* DMA STATUS REGISTER */
+#define DMA_SBUS_STATUS_PIPE_MASK 0x00C0 /* DMA Pipeline status mask */
+#define DMA_SBUS_STATUS_CHAN_MASK 0x0030 /* Channel status mask */
+#define DMA_SBUS_STATUS_BUS_PARITY 0x0008 /* Parity Error on bus */
+#define DMA_SBUS_STATUS_BUS_ERR 0x0004 /* Error Detected on bus */
+#define DMA_SBUS_STATUS_TERM_COUNT 0x0002 /* DMA Transfer Completed */
+#define DMA_SBUS_STATUS_INTERRUPT 0x0001 /* Enable DMA channel inter */
+
+#define DMA_PCI_STATUS_INTERRUPT 0x8000 /* Enable DMA channel inter */
+#define DMA_PCI_STATUS_RETRY_STAT 0x4000 /* Retry status */
+#define DMA_PCI_STATUS_CHAN_MASK 0x3000 /* Channel status mask */
+#define DMA_PCI_STATUS_FIFO_OVR 0x0100 /* DMA FIFO overrun cond */
+#define DMA_PCI_STATUS_FIFO_UDR 0x0080 /* DMA FIFO underrun cond */
+#define DMA_PCI_STATUS_BUS_ERR 0x0040 /* Error Detected on bus */
+#define DMA_PCI_STATUS_BUS_PARITY 0x0020 /* Parity Error on bus */
+#define DMA_PCI_STATUS_CLR_PEND 0x0010 /* DMA clear pending */
+#define DMA_PCI_STATUS_TERM_COUNT 0x0008 /* DMA Transfer Completed */
+#define DMA_PCI_STATUS_DMA_SUSP 0x0004 /* DMA suspended */
+#define DMA_PCI_STATUS_PIPE_MASK 0x0003 /* DMA Pipeline status mask */
+
+/* DMA Status Register, pipeline status bits */
+#define DMA_SBUS_PIPE_FULL 0x00C0 /* Both pipeline stages full */
+#define DMA_SBUS_PIPE_OVERRUN 0x0080 /* Pipeline overrun */
+#define DMA_SBUS_PIPE_STAGE1 0x0040 /*
+ * Pipeline stage 1 Loaded,
+ * stage 2 empty
+ */
+#define DMA_PCI_PIPE_FULL 0x0003 /* Both pipeline stages full */
+#define DMA_PCI_PIPE_OVERRUN 0x0002 /* Pipeline overrun */
+#define DMA_PCI_PIPE_STAGE1 0x0001 /*
+ * Pipeline stage 1 Loaded,
+ * stage 2 empty
+ */
+#define DMA_PIPE_EMPTY 0x0000 /* All pipeline stages empty */
+
+/* DMA Status Register, channel status bits */
+#define DMA_SBUS_CHAN_SUSPEND 0x0030 /* Channel error or suspended */
+#define DMA_SBUS_CHAN_TRANSFER 0x0020 /* Chan transfer in progress */
+#define DMA_SBUS_CHAN_ACTIVE 0x0010 /* Chan trans to host active */
+#define DMA_PCI_CHAN_TRANSFER 0x3000 /* Chan transfer in progress */
+#define DMA_PCI_CHAN_SUSPEND 0x2000 /* Channel error or suspended */
+#define DMA_PCI_CHAN_ACTIVE 0x1000 /* Chan trans to host active */
+#define ISP_DMA_CHAN_IDLE 0x0000 /* Chan idle (normal comp) */
+
+
+/* DMA FIFO STATUS REGISTER */
+#define DMA_FIFO_STATUS_OVERRUN 0x0200 /* FIFO Overrun Condition */
+#define DMA_FIFO_STATUS_UNDERRUN 0x0100 /* FIFO Underrun Condition */
+#define DMA_FIFO_SBUS_COUNT_MASK 0x007F /* FIFO Byte count mask */
+#define DMA_FIFO_PCI_COUNT_MASK 0x00FF /* FIFO Byte count mask */
+
+/*
+ * Mailbox Block Register Offsets
+ */
+
+#define INMAILBOX0 (MBOX_BLOCK+0x0)
+#define INMAILBOX1 (MBOX_BLOCK+0x2)
+#define INMAILBOX2 (MBOX_BLOCK+0x4)
+#define INMAILBOX3 (MBOX_BLOCK+0x6)
+#define INMAILBOX4 (MBOX_BLOCK+0x8)
+#define INMAILBOX5 (MBOX_BLOCK+0xA)
+#define INMAILBOX6 (MBOX_BLOCK+0xC)
+#define INMAILBOX7 (MBOX_BLOCK+0xE)
+
+#define OUTMAILBOX0 (MBOX_BLOCK+0x0)
+#define OUTMAILBOX1 (MBOX_BLOCK+0x2)
+#define OUTMAILBOX2 (MBOX_BLOCK+0x4)
+#define OUTMAILBOX3 (MBOX_BLOCK+0x6)
+#define OUTMAILBOX4 (MBOX_BLOCK+0x8)
+#define OUTMAILBOX5 (MBOX_BLOCK+0xA)
+#define OUTMAILBOX6 (MBOX_BLOCK+0xC)
+#define OUTMAILBOX7 (MBOX_BLOCK+0xE)
+
+/*
+ * Strictly speaking, it's
+ * SCSI && 2100 : 8 MBOX registers
+ * 2200: 24 MBOX registers
+ * 2300: 32 MBOX registers
+ */
+#define MBOX_OFF(n) (MBOX_BLOCK + ((n) << 1))
+#define NMBOX(isp) \
+ (((((isp)->isp_type & ISP_HA_SCSI) >= ISP_HA_SCSI_1040A) || \
+ ((isp)->isp_type & ISP_HA_FC))? 12 : 6)
+#define NMBOX_BMASK(isp) \
+ (((((isp)->isp_type & ISP_HA_SCSI) >= ISP_HA_SCSI_1040A) || \
+ ((isp)->isp_type & ISP_HA_FC))? 0xfff : 0x3f)
+
+#define MAX_MAILBOX(isp) ((IS_FC(isp))? 12 : 8)
+#define MAILBOX_STORAGE 12
+typedef struct {
+ uint16_t param[MAILBOX_STORAGE];
+ uint16_t ibits, obits;
+} mbreg_t;
+
+/*
+ * Fibre Protocol Module and Frame Buffer Register Offsets/Definitions (2X00).
+ * NB: The RISC processor must be paused and the appropriate register
+ * bank selected via BIU2100_CSR bits.
+ */
+
+#define FPM_DIAG_CONFIG (BIU_BLOCK + 0x96)
+#define FPM_SOFT_RESET 0x0100
+
+#define FBM_CMD (BIU_BLOCK + 0xB8)
+#define FBMCMD_FIFO_RESET_ALL 0xA000
+
+
+/*
+ * SXP Block Register Offsets
+ */
+#define SXP_PART_ID (SXP_BLOCK+0x0) /* R : Part ID Code */
+#define SXP_CONFIG1 (SXP_BLOCK+0x2) /* RW*: Configuration Reg #1 */
+#define SXP_CONFIG2 (SXP_BLOCK+0x4) /* RW*: Configuration Reg #2 */
+#define SXP_CONFIG3 (SXP_BLOCK+0x6) /* RW*: Configuration Reg #2 */
+#define SXP_INSTRUCTION (SXP_BLOCK+0xC) /* RW*: Instruction Pointer */
+#define SXP_RETURN_ADDR (SXP_BLOCK+0x10) /* RW*: Return Address */
+#define SXP_COMMAND (SXP_BLOCK+0x14) /* RW*: Command */
+#define SXP_INTERRUPT (SXP_BLOCK+0x18) /* R : Interrupt */
+#define SXP_SEQUENCE (SXP_BLOCK+0x1C) /* RW*: Sequence */
+#define SXP_GROSS_ERR (SXP_BLOCK+0x1E) /* R : Gross Error */
+#define SXP_EXCEPTION (SXP_BLOCK+0x20) /* RW*: Exception Enable */
+#define SXP_OVERRIDE (SXP_BLOCK+0x24) /* RW*: Override */
+#define SXP_LIT_BASE (SXP_BLOCK+0x28) /* RW*: Literal Base */
+#define SXP_USER_FLAGS (SXP_BLOCK+0x2C) /* RW*: User Flags */
+#define SXP_USER_EXCEPT (SXP_BLOCK+0x30) /* RW*: User Exception */
+#define SXP_BREAKPOINT (SXP_BLOCK+0x34) /* RW*: Breakpoint */
+#define SXP_SCSI_ID (SXP_BLOCK+0x40) /* RW*: SCSI ID */
+#define SXP_DEV_CONFIG1 (SXP_BLOCK+0x42) /* RW*: Device Config Reg #1 */
+#define SXP_DEV_CONFIG2 (SXP_BLOCK+0x44) /* RW*: Device Config Reg #2 */
+#define SXP_PHASE_PTR (SXP_BLOCK+0x48) /* RW*: SCSI Phase Pointer */
+#define SXP_BUF_PTR (SXP_BLOCK+0x4C) /* RW*: SCSI Buffer Pointer */
+#define SXP_BUF_CTR (SXP_BLOCK+0x50) /* RW*: SCSI Buffer Counter */
+#define SXP_BUFFER (SXP_BLOCK+0x52) /* RW*: SCSI Buffer */
+#define SXP_BUF_BYTE (SXP_BLOCK+0x54) /* RW*: SCSI Buffer Byte */
+#define SXP_BUF_WD (SXP_BLOCK+0x56) /* RW*: SCSI Buffer Word */
+#define SXP_BUF_WD_TRAN (SXP_BLOCK+0x58) /* RW*: SCSI Buffer Wd xlate */
+#define SXP_FIFO (SXP_BLOCK+0x5A) /* RW*: SCSI FIFO */
+#define SXP_FIFO_STATUS (SXP_BLOCK+0x5C) /* RW*: SCSI FIFO Status */
+#define SXP_FIFO_TOP (SXP_BLOCK+0x5E) /* RW*: SCSI FIFO Top Resid */
+#define SXP_FIFO_BOTTOM (SXP_BLOCK+0x60) /* RW*: SCSI FIFO Bot Resid */
+#define SXP_TRAN_REG (SXP_BLOCK+0x64) /* RW*: SCSI Transferr Reg */
+#define SXP_TRAN_CNT_LO (SXP_BLOCK+0x68) /* RW*: SCSI Trans Count */
+#define SXP_TRAN_CNT_HI (SXP_BLOCK+0x6A) /* RW*: SCSI Trans Count */
+#define SXP_TRAN_CTR_LO (SXP_BLOCK+0x6C) /* RW*: SCSI Trans Counter */
+#define SXP_TRAN_CTR_HI (SXP_BLOCK+0x6E) /* RW*: SCSI Trans Counter */
+#define SXP_ARB_DATA (SXP_BLOCK+0x70) /* R : SCSI Arb Data */
+#define SXP_PINS_CTRL (SXP_BLOCK+0x72) /* RW*: SCSI Control Pins */
+#define SXP_PINS_DATA (SXP_BLOCK+0x74) /* RW*: SCSI Data Pins */
+#define SXP_PINS_DIFF (SXP_BLOCK+0x76) /* RW*: SCSI Diff Pins */
+
+/* for 1080/1280/1240 only */
+#define SXP_BANK1_SELECT 0x100
+
+
+/* SXP CONF1 REGISTER */
+#define SXP_CONF1_ASYNCH_SETUP 0xF000 /* Asynchronous setup time */
+#define SXP_CONF1_SELECTION_UNIT 0x0000 /* Selection time unit */
+#define SXP_CONF1_SELECTION_TIMEOUT 0x0600 /* Selection timeout */
+#define SXP_CONF1_CLOCK_FACTOR 0x00E0 /* Clock factor */
+#define SXP_CONF1_SCSI_ID 0x000F /* SCSI id */
+
+/* SXP CONF2 REGISTER */
+#define SXP_CONF2_DISABLE_FILTER 0x0040 /* Disable SCSI rec filters */
+#define SXP_CONF2_REQ_ACK_PULLUPS 0x0020 /* Enable req/ack pullups */
+#define SXP_CONF2_DATA_PULLUPS 0x0010 /* Enable data pullups */
+#define SXP_CONF2_CONFIG_AUTOLOAD 0x0008 /* Enable dev conf auto-load */
+#define SXP_CONF2_RESELECT 0x0002 /* Enable reselection */
+#define SXP_CONF2_SELECT 0x0001 /* Enable selection */
+
+/* SXP INTERRUPT REGISTER */
+#define SXP_INT_PARITY_ERR 0x8000 /* Parity error detected */
+#define SXP_INT_GROSS_ERR 0x4000 /* Gross error detected */
+#define SXP_INT_FUNCTION_ABORT 0x2000 /* Last cmd aborted */
+#define SXP_INT_CONDITION_FAILED 0x1000 /* Last cond failed test */
+#define SXP_INT_FIFO_EMPTY 0x0800 /* SCSI FIFO is empty */
+#define SXP_INT_BUF_COUNTER_ZERO 0x0400 /* SCSI buf count == zero */
+#define SXP_INT_XFER_ZERO 0x0200 /* SCSI trans count == zero */
+#define SXP_INT_INT_PENDING 0x0080 /* SXP interrupt pending */
+#define SXP_INT_CMD_RUNNING 0x0040 /* SXP is running a command */
+#define SXP_INT_INT_RETURN_CODE 0x000F /* Interrupt return code */
+
+
+/* SXP GROSS ERROR REGISTER */
+#define SXP_GROSS_OFFSET_RESID 0x0040 /* Req/Ack offset not zero */
+#define SXP_GROSS_OFFSET_UNDERFLOW 0x0020 /* Req/Ack offset underflow */
+#define SXP_GROSS_OFFSET_OVERFLOW 0x0010 /* Req/Ack offset overflow */
+#define SXP_GROSS_FIFO_UNDERFLOW 0x0008 /* SCSI FIFO underflow */
+#define SXP_GROSS_FIFO_OVERFLOW 0x0004 /* SCSI FIFO overflow */
+#define SXP_GROSS_WRITE_ERR 0x0002 /* SXP and RISC wrote to reg */
+#define SXP_GROSS_ILLEGAL_INST 0x0001 /* Bad inst loaded into SXP */
+
+/* SXP EXCEPTION REGISTER */
+#define SXP_EXCEPT_USER_0 0x8000 /* Enable user exception #0 */
+#define SXP_EXCEPT_USER_1 0x4000 /* Enable user exception #1 */
+#define PCI_SXP_EXCEPT_SCAM 0x0400 /* SCAM Selection enable */
+#define SXP_EXCEPT_BUS_FREE 0x0200 /* Enable Bus Free det */
+#define SXP_EXCEPT_TARGET_ATN 0x0100 /* Enable TGT mode atten det */
+#define SXP_EXCEPT_RESELECTED 0x0080 /* Enable ReSEL exc handling */
+#define SXP_EXCEPT_SELECTED 0x0040 /* Enable SEL exc handling */
+#define SXP_EXCEPT_ARBITRATION 0x0020 /* Enable ARB exc handling */
+#define SXP_EXCEPT_GROSS_ERR 0x0010 /* Enable gross error except */
+#define SXP_EXCEPT_BUS_RESET 0x0008 /* Enable Bus Reset except */
+
+ /* SXP OVERRIDE REGISTER */
+#define SXP_ORIDE_EXT_TRIGGER 0x8000 /* Enable external trigger */
+#define SXP_ORIDE_STEP 0x4000 /* Enable single step mode */
+#define SXP_ORIDE_BREAKPOINT 0x2000 /* Enable breakpoint reg */
+#define SXP_ORIDE_PIN_WRITE 0x1000 /* Enable write to SCSI pins */
+#define SXP_ORIDE_FORCE_OUTPUTS 0x0800 /* Force SCSI outputs on */
+#define SXP_ORIDE_LOOPBACK 0x0400 /* Enable SCSI loopback mode */
+#define SXP_ORIDE_PARITY_TEST 0x0200 /* Enable parity test mode */
+#define SXP_ORIDE_TRISTATE_ENA_PINS 0x0100 /* Tristate SCSI enable pins */
+#define SXP_ORIDE_TRISTATE_PINS 0x0080 /* Tristate SCSI pins */
+#define SXP_ORIDE_FIFO_RESET 0x0008 /* Reset SCSI FIFO */
+#define SXP_ORIDE_CMD_TERMINATE 0x0004 /* Terminate cur SXP com */
+#define SXP_ORIDE_RESET_REG 0x0002 /* Reset SXP registers */
+#define SXP_ORIDE_RESET_MODULE 0x0001 /* Reset SXP module */
+
+/* SXP COMMANDS */
+#define SXP_RESET_BUS_CMD 0x300b
+
+/* SXP SCSI ID REGISTER */
+#define SXP_SELECTING_ID 0x0F00 /* (Re)Selecting id */
+#define SXP_SELECT_ID 0x000F /* Select id */
+
+/* SXP DEV CONFIG1 REGISTER */
+#define SXP_DCONF1_SYNC_HOLD 0x7000 /* Synchronous data hold */
+#define SXP_DCONF1_SYNC_SETUP 0x0F00 /* Synchronous data setup */
+#define SXP_DCONF1_SYNC_OFFSET 0x000F /* Synchronous data offset */
+
+
+/* SXP DEV CONFIG2 REGISTER */
+#define SXP_DCONF2_FLAGS_MASK 0xF000 /* Device flags */
+#define SXP_DCONF2_WIDE 0x0400 /* Enable wide SCSI */
+#define SXP_DCONF2_PARITY 0x0200 /* Enable parity checking */
+#define SXP_DCONF2_BLOCK_MODE 0x0100 /* Enable blk mode xfr count */
+#define SXP_DCONF2_ASSERTION_MASK 0x0007 /* Assersion period mask */
+
+
+/* SXP PHASE POINTER REGISTER */
+#define SXP_PHASE_STATUS_PTR 0x1000 /* Status buffer offset */
+#define SXP_PHASE_MSG_IN_PTR 0x0700 /* Msg in buffer offset */
+#define SXP_PHASE_COM_PTR 0x00F0 /* Command buffer offset */
+#define SXP_PHASE_MSG_OUT_PTR 0x0007 /* Msg out buffer offset */
+
+
+/* SXP FIFO STATUS REGISTER */
+#define SXP_FIFO_TOP_RESID 0x8000 /* Top residue reg full */
+#define SXP_FIFO_ACK_RESID 0x4000 /* Wide transfers odd resid */
+#define SXP_FIFO_COUNT_MASK 0x001C /* Words in SXP FIFO */
+#define SXP_FIFO_BOTTOM_RESID 0x0001 /* Bottom residue reg full */
+
+
+/* SXP CONTROL PINS REGISTER */
+#define SXP_PINS_CON_PHASE 0x8000 /* Scsi phase valid */
+#define SXP_PINS_CON_PARITY_HI 0x0400 /* Parity pin */
+#define SXP_PINS_CON_PARITY_LO 0x0200 /* Parity pin */
+#define SXP_PINS_CON_REQ 0x0100 /* SCSI bus REQUEST */
+#define SXP_PINS_CON_ACK 0x0080 /* SCSI bus ACKNOWLEDGE */
+#define SXP_PINS_CON_RST 0x0040 /* SCSI bus RESET */
+#define SXP_PINS_CON_BSY 0x0020 /* SCSI bus BUSY */
+#define SXP_PINS_CON_SEL 0x0010 /* SCSI bus SELECT */
+#define SXP_PINS_CON_ATN 0x0008 /* SCSI bus ATTENTION */
+#define SXP_PINS_CON_MSG 0x0004 /* SCSI bus MESSAGE */
+#define SXP_PINS_CON_CD 0x0002 /* SCSI bus COMMAND */
+#define SXP_PINS_CON_IO 0x0001 /* SCSI bus INPUT */
+
+/*
+ * Set the hold time for the SCSI Bus Reset to be 250 ms
+ */
+#define SXP_SCSI_BUS_RESET_HOLD_TIME 250
+
+/* SXP DIFF PINS REGISTER */
+#define SXP_PINS_DIFF_SENSE 0x0200 /* DIFFSENS sig on SCSI bus */
+#define SXP_PINS_DIFF_MODE 0x0100 /* DIFFM signal */
+#define SXP_PINS_DIFF_ENABLE_OUTPUT 0x0080 /* Enable SXP SCSI data drv */
+#define SXP_PINS_DIFF_PINS_MASK 0x007C /* Differential control pins */
+#define SXP_PINS_DIFF_TARGET 0x0002 /* Enable SXP target mode */
+#define SXP_PINS_DIFF_INITIATOR 0x0001 /* Enable SXP initiator mode */
+
+/* Ultra2 only */
+#define SXP_PINS_LVD_MODE 0x1000
+#define SXP_PINS_HVD_MODE 0x0800
+#define SXP_PINS_SE_MODE 0x0400
+
+/* The above have to be put together with the DIFFM pin to make sense */
+#define ISP1080_LVD_MODE (SXP_PINS_LVD_MODE)
+#define ISP1080_HVD_MODE (SXP_PINS_HVD_MODE|SXP_PINS_DIFF_MODE)
+#define ISP1080_SE_MODE (SXP_PINS_SE_MODE)
+#define ISP1080_MODE_MASK \
+ (SXP_PINS_LVD_MODE|SXP_PINS_HVD_MODE|SXP_PINS_SE_MODE|SXP_PINS_DIFF_MODE)
+
+/*
+ * RISC and Host Command and Control Block Register Offsets
+ */
+
+#define RISC_ACC RISC_BLOCK+0x0 /* RW*: Accumulator */
+#define RISC_R1 RISC_BLOCK+0x2 /* RW*: GP Reg R1 */
+#define RISC_R2 RISC_BLOCK+0x4 /* RW*: GP Reg R2 */
+#define RISC_R3 RISC_BLOCK+0x6 /* RW*: GP Reg R3 */
+#define RISC_R4 RISC_BLOCK+0x8 /* RW*: GP Reg R4 */
+#define RISC_R5 RISC_BLOCK+0xA /* RW*: GP Reg R5 */
+#define RISC_R6 RISC_BLOCK+0xC /* RW*: GP Reg R6 */
+#define RISC_R7 RISC_BLOCK+0xE /* RW*: GP Reg R7 */
+#define RISC_R8 RISC_BLOCK+0x10 /* RW*: GP Reg R8 */
+#define RISC_R9 RISC_BLOCK+0x12 /* RW*: GP Reg R9 */
+#define RISC_R10 RISC_BLOCK+0x14 /* RW*: GP Reg R10 */
+#define RISC_R11 RISC_BLOCK+0x16 /* RW*: GP Reg R11 */
+#define RISC_R12 RISC_BLOCK+0x18 /* RW*: GP Reg R12 */
+#define RISC_R13 RISC_BLOCK+0x1a /* RW*: GP Reg R13 */
+#define RISC_R14 RISC_BLOCK+0x1c /* RW*: GP Reg R14 */
+#define RISC_R15 RISC_BLOCK+0x1e /* RW*: GP Reg R15 */
+#define RISC_PSR RISC_BLOCK+0x20 /* RW*: Processor Status */
+#define RISC_IVR RISC_BLOCK+0x22 /* RW*: Interrupt Vector */
+#define RISC_PCR RISC_BLOCK+0x24 /* RW*: Processor Ctrl */
+#define RISC_RAR0 RISC_BLOCK+0x26 /* RW*: Ram Address #0 */
+#define RISC_RAR1 RISC_BLOCK+0x28 /* RW*: Ram Address #1 */
+#define RISC_LCR RISC_BLOCK+0x2a /* RW*: Loop Counter */
+#define RISC_PC RISC_BLOCK+0x2c /* R : Program Counter */
+#define RISC_MTR RISC_BLOCK+0x2e /* RW*: Memory Timing */
+#define RISC_MTR2100 RISC_BLOCK+0x30
+
+#define RISC_EMB RISC_BLOCK+0x30 /* RW*: Ext Mem Boundary */
+#define DUAL_BANK 8
+#define RISC_SP RISC_BLOCK+0x32 /* RW*: Stack Pointer */
+#define RISC_HRL RISC_BLOCK+0x3e /* R *: Hardware Rev Level */
+#define HCCR RISC_BLOCK+0x40 /* RW : Host Command & Ctrl */
+#define BP0 RISC_BLOCK+0x42 /* RW : Processor Brkpt #0 */
+#define BP1 RISC_BLOCK+0x44 /* RW : Processor Brkpt #1 */
+#define TCR RISC_BLOCK+0x46 /* W : Test Control */
+#define TMR RISC_BLOCK+0x48 /* W : Test Mode */
+
+
+/* PROCESSOR STATUS REGISTER */
+#define RISC_PSR_FORCE_TRUE 0x8000
+#define RISC_PSR_LOOP_COUNT_DONE 0x4000
+#define RISC_PSR_RISC_INT 0x2000
+#define RISC_PSR_TIMER_ROLLOVER 0x1000
+#define RISC_PSR_ALU_OVERFLOW 0x0800
+#define RISC_PSR_ALU_MSB 0x0400
+#define RISC_PSR_ALU_CARRY 0x0200
+#define RISC_PSR_ALU_ZERO 0x0100
+
+#define RISC_PSR_PCI_ULTRA 0x0080
+#define RISC_PSR_SBUS_ULTRA 0x0020
+
+#define RISC_PSR_DMA_INT 0x0010
+#define RISC_PSR_SXP_INT 0x0008
+#define RISC_PSR_HOST_INT 0x0004
+#define RISC_PSR_INT_PENDING 0x0002
+#define RISC_PSR_FORCE_FALSE 0x0001
+
+
+/* Host Command and Control */
+#define HCCR_CMD_NOP 0x0000 /* NOP */
+#define HCCR_CMD_RESET 0x1000 /* Reset RISC */
+#define HCCR_CMD_PAUSE 0x2000 /* Pause RISC */
+#define HCCR_CMD_RELEASE 0x3000 /* Release Paused RISC */
+#define HCCR_CMD_STEP 0x4000 /* Single Step RISC */
+#define HCCR_2X00_DISABLE_PARITY_PAUSE 0x4001 /*
+ * Disable RISC pause on FPM
+ * parity error.
+ */
+#define HCCR_CMD_SET_HOST_INT 0x5000 /* Set Host Interrupt */
+#define HCCR_CMD_CLEAR_HOST_INT 0x6000 /* Clear Host Interrupt */
+#define HCCR_CMD_CLEAR_RISC_INT 0x7000 /* Clear RISC interrupt */
+#define HCCR_CMD_BREAKPOINT 0x8000 /* Change breakpoint enables */
+#define PCI_HCCR_CMD_BIOS 0x9000 /* Write BIOS (disable) */
+#define PCI_HCCR_CMD_PARITY 0xA000 /* Write parity enable */
+#define PCI_HCCR_CMD_PARITY_ERR 0xE000 /* Generate parity error */
+#define HCCR_CMD_TEST_MODE 0xF000 /* Set Test Mode */
+
+#define ISP2100_HCCR_PARITY_ENABLE_2 0x0400
+#define ISP2100_HCCR_PARITY_ENABLE_1 0x0200
+#define ISP2100_HCCR_PARITY_ENABLE_0 0x0100
+#define ISP2100_HCCR_PARITY 0x0001
+
+#define PCI_HCCR_PARITY 0x0400 /* Parity error flag */
+#define PCI_HCCR_PARITY_ENABLE_1 0x0200 /* Parity enable bank 1 */
+#define PCI_HCCR_PARITY_ENABLE_0 0x0100 /* Parity enable bank 0 */
+
+#define HCCR_HOST_INT 0x0080 /* R : Host interrupt set */
+#define HCCR_RESET 0x0040 /* R : reset in progress */
+#define HCCR_PAUSE 0x0020 /* R : RISC paused */
+
+#define PCI_HCCR_BIOS 0x0001 /* W : BIOS enable */
+
+/*
+ * NVRAM Definitions (PCI cards only)
+ */
+
+#define ISPBSMX(c, byte, shift, mask) \
+ (((c)[(byte)] >> (shift)) & (mask))
+/*
+ * Qlogic 1020/1040 NVRAM is an array of 128 bytes.
+ *
+ * Some portion of the front of this is for general host adapter properties
+ * This is followed by an array of per-target parameters, and is tailed off
+ * with a checksum xor byte at offset 127. For non-byte entities data is
+ * stored in Little Endian order.
+ */
+
+#define ISP_NVRAM_SIZE 128
+
+#define ISP_NVRAM_VERSION(c) (c)[4]
+#define ISP_NVRAM_FIFO_THRESHOLD(c) ISPBSMX(c, 5, 0, 0x03)
+#define ISP_NVRAM_BIOS_DISABLE(c) ISPBSMX(c, 5, 2, 0x01)
+#define ISP_NVRAM_HBA_ENABLE(c) ISPBSMX(c, 5, 3, 0x01)
+#define ISP_NVRAM_INITIATOR_ID(c) ISPBSMX(c, 5, 4, 0x0f)
+#define ISP_NVRAM_BUS_RESET_DELAY(c) (c)[6]
+#define ISP_NVRAM_BUS_RETRY_COUNT(c) (c)[7]
+#define ISP_NVRAM_BUS_RETRY_DELAY(c) (c)[8]
+#define ISP_NVRAM_ASYNC_DATA_SETUP_TIME(c) ISPBSMX(c, 9, 0, 0x0f)
+#define ISP_NVRAM_REQ_ACK_ACTIVE_NEGATION(c) ISPBSMX(c, 9, 4, 0x01)
+#define ISP_NVRAM_DATA_LINE_ACTIVE_NEGATION(c) ISPBSMX(c, 9, 5, 0x01)
+#define ISP_NVRAM_DATA_DMA_BURST_ENABLE(c) ISPBSMX(c, 9, 6, 0x01)
+#define ISP_NVRAM_CMD_DMA_BURST_ENABLE(c) ISPBSMX(c, 9, 7, 0x01)
+#define ISP_NVRAM_TAG_AGE_LIMIT(c) (c)[10]
+#define ISP_NVRAM_LOWTRM_ENABLE(c) ISPBSMX(c, 11, 0, 0x01)
+#define ISP_NVRAM_HITRM_ENABLE(c) ISPBSMX(c, 11, 1, 0x01)
+#define ISP_NVRAM_PCMC_BURST_ENABLE(c) ISPBSMX(c, 11, 2, 0x01)
+#define ISP_NVRAM_ENABLE_60_MHZ(c) ISPBSMX(c, 11, 3, 0x01)
+#define ISP_NVRAM_SCSI_RESET_DISABLE(c) ISPBSMX(c, 11, 4, 0x01)
+#define ISP_NVRAM_ENABLE_AUTO_TERM(c) ISPBSMX(c, 11, 5, 0x01)
+#define ISP_NVRAM_FIFO_THRESHOLD_128(c) ISPBSMX(c, 11, 6, 0x01)
+#define ISP_NVRAM_AUTO_TERM_SUPPORT(c) ISPBSMX(c, 11, 7, 0x01)
+#define ISP_NVRAM_SELECTION_TIMEOUT(c) (((c)[12]) | ((c)[13] << 8))
+#define ISP_NVRAM_MAX_QUEUE_DEPTH(c) (((c)[14]) | ((c)[15] << 8))
+#define ISP_NVRAM_SCSI_BUS_SIZE(c) ISPBSMX(c, 16, 0, 0x01)
+#define ISP_NVRAM_SCSI_BUS_TYPE(c) ISPBSMX(c, 16, 1, 0x01)
+#define ISP_NVRAM_ADAPTER_CLK_SPEED(c) ISPBSMX(c, 16, 2, 0x01)
+#define ISP_NVRAM_SOFT_TERM_SUPPORT(c) ISPBSMX(c, 16, 3, 0x01)
+#define ISP_NVRAM_FLASH_ONBOARD(c) ISPBSMX(c, 16, 4, 0x01)
+#define ISP_NVRAM_FAST_MTTR_ENABLE(c) ISPBSMX(c, 22, 0, 0x01)
+
+#define ISP_NVRAM_TARGOFF 28
+#define ISP_NVARM_TARGSIZE 6
+#define _IxT(tgt, tidx) \
+ (ISP_NVRAM_TARGOFF + (ISP_NVARM_TARGSIZE * (tgt)) + (tidx))
+#define ISP_NVRAM_TGT_RENEG(c, t) ISPBSMX(c, _IxT(t, 0), 0, 0x01)
+#define ISP_NVRAM_TGT_QFRZ(c, t) ISPBSMX(c, _IxT(t, 0), 1, 0x01)
+#define ISP_NVRAM_TGT_ARQ(c, t) ISPBSMX(c, _IxT(t, 0), 2, 0x01)
+#define ISP_NVRAM_TGT_TQING(c, t) ISPBSMX(c, _IxT(t, 0), 3, 0x01)
+#define ISP_NVRAM_TGT_SYNC(c, t) ISPBSMX(c, _IxT(t, 0), 4, 0x01)
+#define ISP_NVRAM_TGT_WIDE(c, t) ISPBSMX(c, _IxT(t, 0), 5, 0x01)
+#define ISP_NVRAM_TGT_PARITY(c, t) ISPBSMX(c, _IxT(t, 0), 6, 0x01)
+#define ISP_NVRAM_TGT_DISC(c, t) ISPBSMX(c, _IxT(t, 0), 7, 0x01)
+#define ISP_NVRAM_TGT_EXEC_THROTTLE(c, t) ISPBSMX(c, _IxT(t, 1), 0, 0xff)
+#define ISP_NVRAM_TGT_SYNC_PERIOD(c, t) ISPBSMX(c, _IxT(t, 2), 0, 0xff)
+#define ISP_NVRAM_TGT_SYNC_OFFSET(c, t) ISPBSMX(c, _IxT(t, 3), 0, 0x0f)
+#define ISP_NVRAM_TGT_DEVICE_ENABLE(c, t) ISPBSMX(c, _IxT(t, 3), 4, 0x01)
+#define ISP_NVRAM_TGT_LUN_DISABLE(c, t) ISPBSMX(c, _IxT(t, 3), 5, 0x01)
+
+/*
+ * Qlogic 1080/1240 NVRAM is an array of 256 bytes.
+ *
+ * Some portion of the front of this is for general host adapter properties
+ * This is followed by an array of per-target parameters, and is tailed off
+ * with a checksum xor byte at offset 256. For non-byte entities data is
+ * stored in Little Endian order.
+ */
+
+#define ISP1080_NVRAM_SIZE 256
+
+#define ISP1080_NVRAM_VERSION(c) ISP_NVRAM_VERSION(c)
+
+/* Offset 5 */
+/*
+ uint8_t bios_configuration_mode :2;
+ uint8_t bios_disable :1;
+ uint8_t selectable_scsi_boot_enable :1;
+ uint8_t cd_rom_boot_enable :1;
+ uint8_t disable_loading_risc_code :1;
+ uint8_t enable_64bit_addressing :1;
+ uint8_t unused_7 :1;
+ */
+
+/* Offsets 6, 7 */
+/*
+ uint8_t boot_lun_number :5;
+ uint8_t scsi_bus_number :1;
+ uint8_t unused_6 :1;
+ uint8_t unused_7 :1;
+ uint8_t boot_target_number :4;
+ uint8_t unused_12 :1;
+ uint8_t unused_13 :1;
+ uint8_t unused_14 :1;
+ uint8_t unused_15 :1;
+ */
+
+#define ISP1080_NVRAM_HBA_ENABLE(c) ISPBSMX(c, 16, 3, 0x01)
+
+#define ISP1080_NVRAM_BURST_ENABLE(c) ISPBSMX(c, 16, 1, 0x01)
+#define ISP1080_NVRAM_FIFO_THRESHOLD(c) ISPBSMX(c, 16, 4, 0x0f)
+
+#define ISP1080_NVRAM_AUTO_TERM_SUPPORT(c) ISPBSMX(c, 17, 7, 0x01)
+#define ISP1080_NVRAM_BUS0_TERM_MODE(c) ISPBSMX(c, 17, 0, 0x03)
+#define ISP1080_NVRAM_BUS1_TERM_MODE(c) ISPBSMX(c, 17, 2, 0x03)
+
+#define ISP1080_ISP_PARAMETER(c) \
+ (((c)[18]) | ((c)[19] << 8))
+
+#define ISP1080_FAST_POST(c) ISPBSMX(c, 20, 0, 0x01)
+#define ISP1080_REPORT_LVD_TRANSITION(c) ISPBSMX(c, 20, 1, 0x01)
+
+#define ISP1080_BUS1_OFF 112
+
+#define ISP1080_NVRAM_INITIATOR_ID(c, b) \
+ ISPBSMX(c, ((b == 0)? 0 : ISP1080_BUS1_OFF) + 24, 0, 0x0f)
+#define ISP1080_NVRAM_BUS_RESET_DELAY(c, b) \
+ (c)[((b == 0)? 0 : ISP1080_BUS1_OFF) + 25]
+#define ISP1080_NVRAM_BUS_RETRY_COUNT(c, b) \
+ (c)[((b == 0)? 0 : ISP1080_BUS1_OFF) + 26]
+#define ISP1080_NVRAM_BUS_RETRY_DELAY(c, b) \
+ (c)[((b == 0)? 0 : ISP1080_BUS1_OFF) + 27]
+
+#define ISP1080_NVRAM_ASYNC_DATA_SETUP_TIME(c, b) \
+ ISPBSMX(c, ((b == 0)? 0 : ISP1080_BUS1_OFF) + 28, 0, 0x0f)
+#define ISP1080_NVRAM_REQ_ACK_ACTIVE_NEGATION(c, b) \
+ ISPBSMX(c, ((b == 0)? 0 : ISP1080_BUS1_OFF) + 28, 4, 0x01)
+#define ISP1080_NVRAM_DATA_LINE_ACTIVE_NEGATION(c, b) \
+ ISPBSMX(c, ((b == 0)? 0 : ISP1080_BUS1_OFF) + 28, 5, 0x01)
+#define ISP1080_NVRAM_SELECTION_TIMEOUT(c, b) \
+ (((c)[((b == 0)? 0 : ISP1080_BUS1_OFF) + 30]) | \
+ ((c)[((b == 0)? 0 : ISP1080_BUS1_OFF) + 31] << 8))
+#define ISP1080_NVRAM_MAX_QUEUE_DEPTH(c, b) \
+ (((c)[((b == 0)? 0 : ISP1080_BUS1_OFF) + 32]) | \
+ ((c)[((b == 0)? 0 : ISP1080_BUS1_OFF) + 33] << 8))
+
+#define ISP1080_NVRAM_TARGOFF(b) \
+ ((b == 0)? 40: (40 + ISP1080_BUS1_OFF))
+#define ISP1080_NVRAM_TARGSIZE 6
+#define _IxT8(tgt, tidx, b) \
+ (ISP1080_NVRAM_TARGOFF((b)) + (ISP1080_NVRAM_TARGSIZE * (tgt)) + (tidx))
+
+#define ISP1080_NVRAM_TGT_RENEG(c, t, b) \
+ ISPBSMX(c, _IxT8(t, 0, (b)), 0, 0x01)
+#define ISP1080_NVRAM_TGT_QFRZ(c, t, b) \
+ ISPBSMX(c, _IxT8(t, 0, (b)), 1, 0x01)
+#define ISP1080_NVRAM_TGT_ARQ(c, t, b) \
+ ISPBSMX(c, _IxT8(t, 0, (b)), 2, 0x01)
+#define ISP1080_NVRAM_TGT_TQING(c, t, b) \
+ ISPBSMX(c, _IxT8(t, 0, (b)), 3, 0x01)
+#define ISP1080_NVRAM_TGT_SYNC(c, t, b) \
+ ISPBSMX(c, _IxT8(t, 0, (b)), 4, 0x01)
+#define ISP1080_NVRAM_TGT_WIDE(c, t, b) \
+ ISPBSMX(c, _IxT8(t, 0, (b)), 5, 0x01)
+#define ISP1080_NVRAM_TGT_PARITY(c, t, b) \
+ ISPBSMX(c, _IxT8(t, 0, (b)), 6, 0x01)
+#define ISP1080_NVRAM_TGT_DISC(c, t, b) \
+ ISPBSMX(c, _IxT8(t, 0, (b)), 7, 0x01)
+#define ISP1080_NVRAM_TGT_EXEC_THROTTLE(c, t, b) \
+ ISPBSMX(c, _IxT8(t, 1, (b)), 0, 0xff)
+#define ISP1080_NVRAM_TGT_SYNC_PERIOD(c, t, b) \
+ ISPBSMX(c, _IxT8(t, 2, (b)), 0, 0xff)
+#define ISP1080_NVRAM_TGT_SYNC_OFFSET(c, t, b) \
+ ISPBSMX(c, _IxT8(t, 3, (b)), 0, 0x0f)
+#define ISP1080_NVRAM_TGT_DEVICE_ENABLE(c, t, b) \
+ ISPBSMX(c, _IxT8(t, 3, (b)), 4, 0x01)
+#define ISP1080_NVRAM_TGT_LUN_DISABLE(c, t, b) \
+ ISPBSMX(c, _IxT8(t, 3, (b)), 5, 0x01)
+
+#define ISP12160_NVRAM_HBA_ENABLE ISP1080_NVRAM_HBA_ENABLE
+#define ISP12160_NVRAM_BURST_ENABLE ISP1080_NVRAM_BURST_ENABLE
+#define ISP12160_NVRAM_FIFO_THRESHOLD ISP1080_NVRAM_FIFO_THRESHOLD
+#define ISP12160_NVRAM_AUTO_TERM_SUPPORT ISP1080_NVRAM_AUTO_TERM_SUPPORT
+#define ISP12160_NVRAM_BUS0_TERM_MODE ISP1080_NVRAM_BUS0_TERM_MODE
+#define ISP12160_NVRAM_BUS1_TERM_MODE ISP1080_NVRAM_BUS1_TERM_MODE
+#define ISP12160_ISP_PARAMETER ISP12160_ISP_PARAMETER
+#define ISP12160_FAST_POST ISP1080_FAST_POST
+#define ISP12160_REPORT_LVD_TRANSITION ISP1080_REPORT_LVD_TRANSTION
+
+#define ISP12160_NVRAM_INITIATOR_ID \
+ ISP1080_NVRAM_INITIATOR_ID
+#define ISP12160_NVRAM_BUS_RESET_DELAY \
+ ISP1080_NVRAM_BUS_RESET_DELAY
+#define ISP12160_NVRAM_BUS_RETRY_COUNT \
+ ISP1080_NVRAM_BUS_RETRY_COUNT
+#define ISP12160_NVRAM_BUS_RETRY_DELAY \
+ ISP1080_NVRAM_BUS_RETRY_DELAY
+#define ISP12160_NVRAM_ASYNC_DATA_SETUP_TIME \
+ ISP1080_NVRAM_ASYNC_DATA_SETUP_TIME
+#define ISP12160_NVRAM_REQ_ACK_ACTIVE_NEGATION \
+ ISP1080_NVRAM_REQ_ACK_ACTIVE_NEGATION
+#define ISP12160_NVRAM_DATA_LINE_ACTIVE_NEGATION \
+ ISP1080_NVRAM_DATA_LINE_ACTIVE_NEGATION
+#define ISP12160_NVRAM_SELECTION_TIMEOUT \
+ ISP1080_NVRAM_SELECTION_TIMEOUT
+#define ISP12160_NVRAM_MAX_QUEUE_DEPTH \
+ ISP1080_NVRAM_MAX_QUEUE_DEPTH
+
+
+#define ISP12160_BUS0_OFF 24
+#define ISP12160_BUS1_OFF 136
+
+#define ISP12160_NVRAM_TARGOFF(b) \
+ (((b == 0)? ISP12160_BUS0_OFF : ISP12160_BUS1_OFF) + 16)
+
+#define ISP12160_NVRAM_TARGSIZE 6
+#define _IxT16(tgt, tidx, b) \
+ (ISP12160_NVRAM_TARGOFF((b))+(ISP12160_NVRAM_TARGSIZE * (tgt))+(tidx))
+
+#define ISP12160_NVRAM_TGT_RENEG(c, t, b) \
+ ISPBSMX(c, _IxT16(t, 0, (b)), 0, 0x01)
+#define ISP12160_NVRAM_TGT_QFRZ(c, t, b) \
+ ISPBSMX(c, _IxT16(t, 0, (b)), 1, 0x01)
+#define ISP12160_NVRAM_TGT_ARQ(c, t, b) \
+ ISPBSMX(c, _IxT16(t, 0, (b)), 2, 0x01)
+#define ISP12160_NVRAM_TGT_TQING(c, t, b) \
+ ISPBSMX(c, _IxT16(t, 0, (b)), 3, 0x01)
+#define ISP12160_NVRAM_TGT_SYNC(c, t, b) \
+ ISPBSMX(c, _IxT16(t, 0, (b)), 4, 0x01)
+#define ISP12160_NVRAM_TGT_WIDE(c, t, b) \
+ ISPBSMX(c, _IxT16(t, 0, (b)), 5, 0x01)
+#define ISP12160_NVRAM_TGT_PARITY(c, t, b) \
+ ISPBSMX(c, _IxT16(t, 0, (b)), 6, 0x01)
+#define ISP12160_NVRAM_TGT_DISC(c, t, b) \
+ ISPBSMX(c, _IxT16(t, 0, (b)), 7, 0x01)
+
+#define ISP12160_NVRAM_TGT_EXEC_THROTTLE(c, t, b) \
+ ISPBSMX(c, _IxT16(t, 1, (b)), 0, 0xff)
+#define ISP12160_NVRAM_TGT_SYNC_PERIOD(c, t, b) \
+ ISPBSMX(c, _IxT16(t, 2, (b)), 0, 0xff)
+
+#define ISP12160_NVRAM_TGT_SYNC_OFFSET(c, t, b) \
+ ISPBSMX(c, _IxT16(t, 3, (b)), 0, 0x1f)
+#define ISP12160_NVRAM_TGT_DEVICE_ENABLE(c, t, b) \
+ ISPBSMX(c, _IxT16(t, 3, (b)), 5, 0x01)
+
+#define ISP12160_NVRAM_PPR_OPTIONS(c, t, b) \
+ ISPBSMX(c, _IxT16(t, 4, (b)), 0, 0x0f)
+#define ISP12160_NVRAM_PPR_WIDTH(c, t, b) \
+ ISPBSMX(c, _IxT16(t, 4, (b)), 4, 0x03)
+#define ISP12160_NVRAM_PPR_ENABLE(c, t, b) \
+ ISPBSMX(c, _IxT16(t, 4, (b)), 7, 0x01)
+
+/*
+ * Qlogic 2XXX NVRAM is an array of 256 bytes.
+ *
+ * Some portion of the front of this is for general RISC engine parameters,
+ * mostly reflecting the state of the last INITIALIZE FIRMWARE mailbox command.
+ *
+ * This is followed by some general host adapter parameters, and ends with
+ * a checksum xor byte at offset 255. For non-byte entities data is stored
+ * in Little Endian order.
+ */
+#define ISP2100_NVRAM_SIZE 256
+/* ISP_NVRAM_VERSION is in same overall place */
+#define ISP2100_NVRAM_RISCVER(c) (c)[6]
+#define ISP2100_NVRAM_OPTIONS(c) ((c)[8] | ((c)[9] << 8))
+#define ISP2100_NVRAM_MAXFRAMELENGTH(c) (((c)[10]) | ((c)[11] << 8))
+#define ISP2100_NVRAM_MAXIOCBALLOCATION(c) (((c)[12]) | ((c)[13] << 8))
+#define ISP2100_NVRAM_EXECUTION_THROTTLE(c) (((c)[14]) | ((c)[15] << 8))
+#define ISP2100_NVRAM_RETRY_COUNT(c) (c)[16]
+#define ISP2100_NVRAM_RETRY_DELAY(c) (c)[17]
+
+#define ISP2100_NVRAM_PORT_NAME(c) (\
+ (((uint64_t)(c)[18]) << 56) | \
+ (((uint64_t)(c)[19]) << 48) | \
+ (((uint64_t)(c)[20]) << 40) | \
+ (((uint64_t)(c)[21]) << 32) | \
+ (((uint64_t)(c)[22]) << 24) | \
+ (((uint64_t)(c)[23]) << 16) | \
+ (((uint64_t)(c)[24]) << 8) | \
+ (((uint64_t)(c)[25]) << 0))
+
+#define ISP2100_NVRAM_HARDLOOPID(c) ((c)[26] | ((c)[27] << 8))
+#define ISP2100_NVRAM_TOV(c) ((c)[29])
+
+#define ISP2100_NVRAM_NODE_NAME(c) (\
+ (((uint64_t)(c)[30]) << 56) | \
+ (((uint64_t)(c)[31]) << 48) | \
+ (((uint64_t)(c)[32]) << 40) | \
+ (((uint64_t)(c)[33]) << 32) | \
+ (((uint64_t)(c)[34]) << 24) | \
+ (((uint64_t)(c)[35]) << 16) | \
+ (((uint64_t)(c)[36]) << 8) | \
+ (((uint64_t)(c)[37]) << 0))
+
+#define ISP2100_XFW_OPTIONS(c) ((c)[38] | ((c)[39] << 8))
+
+#define ISP2100_RACC_TIMER(c) (c)[40]
+#define ISP2100_IDELAY_TIMER(c) (c)[41]
+
+#define ISP2100_ZFW_OPTIONS(c) ((c)[42] | ((c)[43] << 8))
+
+#define ISP2100_SERIAL_LINK(c) ((c)[68] | ((c)[69] << 8))
+
+#define ISP2100_NVRAM_HBA_OPTIONS(c) ((c)[70] | ((c)[71] << 8))
+#define ISP2100_NVRAM_HBA_DISABLE(c) ISPBSMX(c, 70, 0, 0x01)
+#define ISP2100_NVRAM_BIOS_DISABLE(c) ISPBSMX(c, 70, 1, 0x01)
+#define ISP2100_NVRAM_LUN_DISABLE(c) ISPBSMX(c, 70, 2, 0x01)
+#define ISP2100_NVRAM_ENABLE_SELECT_BOOT(c) ISPBSMX(c, 70, 3, 0x01)
+#define ISP2100_NVRAM_DISABLE_CODELOAD(c) ISPBSMX(c, 70, 4, 0x01)
+#define ISP2100_NVRAM_SET_CACHELINESZ(c) ISPBSMX(c, 70, 5, 0x01)
+
+#define ISP2100_NVRAM_BOOT_NODE_NAME(c) (\
+ (((uint64_t)(c)[72]) << 56) | \
+ (((uint64_t)(c)[73]) << 48) | \
+ (((uint64_t)(c)[74]) << 40) | \
+ (((uint64_t)(c)[75]) << 32) | \
+ (((uint64_t)(c)[76]) << 24) | \
+ (((uint64_t)(c)[77]) << 16) | \
+ (((uint64_t)(c)[78]) << 8) | \
+ (((uint64_t)(c)[79]) << 0))
+
+#define ISP2100_NVRAM_BOOT_LUN(c) (c)[80]
+#define ISP2100_RESET_DELAY(c) (c)[81]
+
+#define ISP2100_HBA_FEATURES(c) ((c)[232] | ((c)[233] << 8))
+
+/*
+ * Firmware Crash Dump
+ *
+ * QLogic needs specific information format when they look at firmware crashes.
+ *
+ * This is incredibly kernel memory consumptive (to say the least), so this
+ * code is only compiled in when needed.
+ */
+
+#define QLA2200_RISC_IMAGE_DUMP_SIZE \
+ (1 * sizeof (uint16_t)) + /* 'used' flag (also HBA type) */ \
+ (352 * sizeof (uint16_t)) + /* RISC registers */ \
+ (61440 * sizeof (uint16_t)) /* RISC SRAM (offset 0x1000..0xffff) */
+#define QLA2300_RISC_IMAGE_DUMP_SIZE \
+ (1 * sizeof (uint16_t)) + /* 'used' flag (also HBA type) */ \
+ (464 * sizeof (uint16_t)) + /* RISC registers */ \
+ (63488 * sizeof (uint16_t)) + /* RISC SRAM (0x0800..0xffff) */ \
+ (4096 * sizeof (uint16_t)) + /* RISC SRAM (0x10000..0x10FFF) */ \
+ (61440 * sizeof (uint16_t)) /* RISC SRAM (0x11000..0x1FFFF) */
+/* the larger of the two */
+#define ISP_CRASH_IMAGE_SIZE QLA2300_RISC_IMAGE_DUMP_SIZE
+#endif /* _ISPREG_H */
diff --git a/sys/dev/isp/ispvar.h b/sys/dev/isp/ispvar.h
new file mode 100644
index 000000000000..5ea8e8a7c415
--- /dev/null
+++ b/sys/dev/isp/ispvar.h
@@ -0,0 +1,892 @@
+/* $FreeBSD$ */
+/*-
+ * Soft Definitions for for Qlogic ISP SCSI adapters.
+ *
+ * Copyright (c) 1997-2006 by Matthew Jacob
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice 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.
+ *
+ * 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.
+ */
+
+#ifndef _ISPVAR_H
+#define _ISPVAR_H
+
+#if defined(__NetBSD__) || defined(__OpenBSD__)
+#include <dev/ic/ispmbox.h>
+#endif
+#ifdef __FreeBSD__
+#include <dev/isp/ispmbox.h>
+#endif
+#ifdef __linux__
+#include "ispmbox.h"
+#endif
+#ifdef __svr4__
+#include "ispmbox.h"
+#endif
+
+#define ISP_CORE_VERSION_MAJOR 2
+#define ISP_CORE_VERSION_MINOR 11
+
+/*
+ * Vector for bus specific code to provide specific services.
+ */
+typedef struct ispsoftc ispsoftc_t;
+struct ispmdvec {
+ int (*dv_rd_isr)
+ (ispsoftc_t *, uint16_t *, uint16_t *, uint16_t *);
+ uint16_t (*dv_rd_reg) (ispsoftc_t *, int);
+ void (*dv_wr_reg) (ispsoftc_t *, int, uint16_t);
+ int (*dv_mbxdma) (ispsoftc_t *);
+ int (*dv_dmaset)
+ (ispsoftc_t *, XS_T *, ispreq_t *, uint16_t *, uint16_t);
+ void (*dv_dmaclr) (ispsoftc_t *, XS_T *, uint16_t);
+ void (*dv_reset0) (ispsoftc_t *);
+ void (*dv_reset1) (ispsoftc_t *);
+ void (*dv_dregs) (ispsoftc_t *, const char *);
+ uint16_t *dv_ispfw; /* ptr to f/w */
+ uint16_t dv_conf1;
+ uint16_t dv_clock; /* clock frequency */
+};
+
+/*
+ * Overall parameters
+ */
+#define MAX_TARGETS 16
+#define MAX_FC_TARG 256
+#define ISP_MAX_TARGETS(isp) (IS_FC(isp)? MAX_FC_TARG : MAX_TARGETS)
+#define ISP_MAX_LUNS(isp) (isp)->isp_maxluns
+
+/*
+ * Macros to access ISP registers through bus specific layers-
+ * mostly wrappers to vector through the mdvec structure.
+ */
+#define ISP_READ_ISR(isp, isrp, semap, mbox0p) \
+ (*(isp)->isp_mdvec->dv_rd_isr)(isp, isrp, semap, mbox0p)
+
+#define ISP_READ(isp, reg) \
+ (*(isp)->isp_mdvec->dv_rd_reg)((isp), (reg))
+
+#define ISP_WRITE(isp, reg, val) \
+ (*(isp)->isp_mdvec->dv_wr_reg)((isp), (reg), (val))
+
+#define ISP_MBOXDMASETUP(isp) \
+ (*(isp)->isp_mdvec->dv_mbxdma)((isp))
+
+#define ISP_DMASETUP(isp, xs, req, iptrp, optr) \
+ (*(isp)->isp_mdvec->dv_dmaset)((isp), (xs), (req), (iptrp), (optr))
+
+#define ISP_DMAFREE(isp, xs, hndl) \
+ if ((isp)->isp_mdvec->dv_dmaclr) \
+ (*(isp)->isp_mdvec->dv_dmaclr)((isp), (xs), (hndl))
+
+#define ISP_RESET0(isp) \
+ if ((isp)->isp_mdvec->dv_reset0) (*(isp)->isp_mdvec->dv_reset0)((isp))
+#define ISP_RESET1(isp) \
+ if ((isp)->isp_mdvec->dv_reset1) (*(isp)->isp_mdvec->dv_reset1)((isp))
+#define ISP_DUMPREGS(isp, m) \
+ if ((isp)->isp_mdvec->dv_dregs) (*(isp)->isp_mdvec->dv_dregs)((isp),(m))
+
+#define ISP_SETBITS(isp, reg, val) \
+ (*(isp)->isp_mdvec->dv_wr_reg)((isp), (reg), ISP_READ((isp), (reg)) | (val))
+
+#define ISP_CLRBITS(isp, reg, val) \
+ (*(isp)->isp_mdvec->dv_wr_reg)((isp), (reg), ISP_READ((isp), (reg)) & ~(val))
+
+/*
+ * The MEMORYBARRIER macro is defined per platform (to provide synchronization
+ * on Request and Response Queues, Scratch DMA areas, and Registers)
+ *
+ * Defined Memory Barrier Synchronization Types
+ */
+#define SYNC_REQUEST 0 /* request queue synchronization */
+#define SYNC_RESULT 1 /* result queue synchronization */
+#define SYNC_SFORDEV 2 /* scratch, sync for ISP */
+#define SYNC_SFORCPU 3 /* scratch, sync for CPU */
+#define SYNC_REG 4 /* for registers */
+
+/*
+ * Request/Response Queue defines and macros.
+ * The maximum is defined per platform (and can be based on board type).
+ */
+/* This is the size of a queue entry (request and response) */
+#define QENTRY_LEN 64
+/* Both request and result queue length must be a power of two */
+#define RQUEST_QUEUE_LEN(x) MAXISPREQUEST(x)
+#ifdef ISP_TARGET_MODE
+#define RESULT_QUEUE_LEN(x) MAXISPREQUEST(x)
+#else
+#define RESULT_QUEUE_LEN(x) \
+ (((MAXISPREQUEST(x) >> 2) < 64)? 64 : MAXISPREQUEST(x) >> 2)
+#endif
+#define ISP_QUEUE_ENTRY(q, idx) (((uint8_t *)q) + ((idx) * QENTRY_LEN))
+#define ISP_QUEUE_SIZE(n) ((n) * QENTRY_LEN)
+#define ISP_NXT_QENTRY(idx, qlen) (((idx) + 1) & ((qlen)-1))
+#define ISP_QFREE(in, out, qlen) \
+ ((in == out)? (qlen - 1) : ((in > out)? \
+ ((qlen - 1) - (in - out)) : (out - in - 1)))
+#define ISP_QAVAIL(isp) \
+ ISP_QFREE(isp->isp_reqidx, isp->isp_reqodx, RQUEST_QUEUE_LEN(isp))
+
+#define ISP_ADD_REQUEST(isp, nxti) \
+ MEMORYBARRIER(isp, SYNC_REQUEST, isp->isp_reqidx, QENTRY_LEN); \
+ WRITE_REQUEST_QUEUE_IN_POINTER(isp, nxti); \
+ isp->isp_reqidx = nxti
+
+/*
+ * SCSI Specific Host Adapter Parameters- per bus, per target
+ */
+typedef struct {
+ uint32_t isp_gotdparms : 1,
+ isp_req_ack_active_neg : 1,
+ isp_data_line_active_neg: 1,
+ isp_cmd_dma_burst_enable: 1,
+ isp_data_dma_burst_enabl: 1,
+ isp_fifo_threshold : 3,
+ isp_ultramode : 1,
+ isp_diffmode : 1,
+ isp_lvdmode : 1,
+ isp_fast_mttr : 1, /* fast sram */
+ isp_initiator_id : 4,
+ isp_async_data_setup : 4;
+ uint16_t isp_selection_timeout;
+ uint16_t isp_max_queue_depth;
+ uint8_t isp_tag_aging;
+ uint8_t isp_bus_reset_delay;
+ uint8_t isp_retry_count;
+ uint8_t isp_retry_delay;
+ struct {
+ uint32_t
+ exc_throttle : 8,
+ : 1,
+ dev_enable : 1, /* ignored */
+ dev_update : 1,
+ dev_refresh : 1,
+ actv_offset : 4,
+ goal_offset : 4,
+ nvrm_offset : 4;
+ uint8_t actv_period; /* current sync period */
+ uint8_t goal_period; /* goal sync period */
+ uint8_t nvrm_period; /* nvram sync period */
+ uint16_t actv_flags; /* current device flags */
+ uint16_t goal_flags; /* goal device flags */
+ uint16_t nvrm_flags; /* nvram device flags */
+ } isp_devparam[MAX_TARGETS];
+} sdparam;
+
+/*
+ * Device Flags
+ */
+#define DPARM_DISC 0x8000
+#define DPARM_PARITY 0x4000
+#define DPARM_WIDE 0x2000
+#define DPARM_SYNC 0x1000
+#define DPARM_TQING 0x0800
+#define DPARM_ARQ 0x0400
+#define DPARM_QFRZ 0x0200
+#define DPARM_RENEG 0x0100
+#define DPARM_NARROW 0x0080
+#define DPARM_ASYNC 0x0040
+#define DPARM_PPR 0x0020
+#define DPARM_DEFAULT (0xFF00 & ~DPARM_QFRZ)
+#define DPARM_SAFE_DFLT (DPARM_DEFAULT & ~(DPARM_WIDE|DPARM_SYNC|DPARM_TQING))
+
+/* technically, not really correct, as they need to be rated based upon clock */
+#define ISP_80M_SYNCPARMS 0x0c09
+#define ISP_40M_SYNCPARMS 0x0c0a
+#define ISP_20M_SYNCPARMS 0x0c0c
+#define ISP_20M_SYNCPARMS_1040 0x080c
+#define ISP_10M_SYNCPARMS 0x0c19
+#define ISP_08M_SYNCPARMS 0x0c25
+#define ISP_05M_SYNCPARMS 0x0c32
+#define ISP_04M_SYNCPARMS 0x0c41
+
+/*
+ * Fibre Channel Specifics
+ */
+#define FL_PORT_ID 0x7e /* FL_Port Special ID */
+#define FC_PORT_ID 0x7f /* Fabric Controller Special ID */
+#define FC_SNS_ID 0x80 /* SNS Server Special ID */
+
+/* #define ISP_USE_GA_NXT 1 */ /* Use GA_NXT with switches */
+#ifndef GA_NXT_MAX
+#define GA_NXT_MAX 256
+#endif
+
+typedef struct {
+ uint32_t : 13,
+ isp_gbspeed : 3,
+ : 1,
+ isp_iid_set : 1,
+ loop_seen_once : 1,
+ isp_loopstate : 4, /* Current Loop State */
+ isp_fwstate : 4, /* ISP F/W state */
+ isp_gotdparms : 1,
+ isp_topo : 3,
+ isp_onfabric : 1;
+ uint32_t : 8,
+ isp_portid : 24; /* S_ID */
+ uint16_t isp_fwoptions;
+ uint16_t isp_xfwoptions;
+ uint16_t isp_zfwoptions;
+ uint16_t isp_iid; /* 'initiator' id */
+ uint16_t isp_loopid; /* hard loop id */
+ uint16_t isp_fwattr; /* firmware attributes */
+ uint16_t isp_execthrottle;
+ uint8_t isp_retry_delay;
+ uint8_t isp_retry_count;
+ uint8_t isp_reserved;
+ uint16_t isp_maxalloc;
+ uint16_t isp_maxfrmlen;
+ uint64_t isp_nodewwn;
+ uint64_t isp_portwwn;
+ /*
+ * Port Data Base. This is indexed by 'target', which is invariate.
+ * However, elements within can move around due to loop changes,
+ * so the actual loop ID passed to the F/W is in this structure.
+ * The first time the loop is seen up, loopid will match the index
+ * (except for fabric nodes which are above mapped above FC_SNS_ID
+ * and are completely virtual), but subsequent LIPs can cause things
+ * to move around.
+ */
+ struct lportdb {
+ uint32_t loopid : 16,
+ : 2,
+ fc4_type : 4,
+ last_fabric_dev : 1,
+ relogin : 1,
+ force_logout : 1,
+ was_fabric_dev : 1,
+ fabric_dev : 1,
+ loggedin : 1,
+ roles : 2,
+ tvalid : 1,
+ valid : 1;
+ uint32_t port_type : 8,
+ portid : 24;
+ uint64_t node_wwn;
+ uint64_t port_wwn;
+ } portdb[MAX_FC_TARG], tport[FC_PORT_ID];
+
+ /*
+ * Scratch DMA mapped in area to fetch Port Database stuff, etc.
+ */
+ void * isp_scratch;
+ XS_DMA_ADDR_T isp_scdma;
+#ifdef ISP_FW_CRASH_DUMP
+ uint16_t * isp_dump_data;
+#endif
+} fcparam;
+
+#define FW_CONFIG_WAIT 0
+#define FW_WAIT_AL_PA 1
+#define FW_WAIT_LOGIN 2
+#define FW_READY 3
+#define FW_LOSS_OF_SYNC 4
+#define FW_ERROR 5
+#define FW_REINIT 6
+#define FW_NON_PART 7
+
+#define LOOP_NIL 0
+#define LOOP_LIP_RCVD 1
+#define LOOP_PDB_RCVD 2
+#define LOOP_SCANNING_FABRIC 3
+#define LOOP_FSCAN_DONE 4
+#define LOOP_SCANNING_LOOP 5
+#define LOOP_LSCAN_DONE 6
+#define LOOP_SYNCING_PDB 7
+#define LOOP_READY 8
+
+#define TOPO_NL_PORT 0
+#define TOPO_FL_PORT 1
+#define TOPO_N_PORT 2
+#define TOPO_F_PORT 3
+#define TOPO_PTP_STUB 4
+
+/*
+ * Soft Structure per host adapter
+ */
+struct ispsoftc {
+ /*
+ * Platform (OS) specific data
+ */
+ struct isposinfo isp_osinfo;
+
+ /*
+ * Pointer to bus specific functions and data
+ */
+ struct ispmdvec * isp_mdvec;
+
+ /*
+ * (Mostly) nonvolatile state. Board specific parameters
+ * may contain some volatile state (e.g., current loop state).
+ */
+
+ void * isp_param; /* type specific */
+ uint16_t isp_fwrev[3]; /* Loaded F/W revision */
+ uint16_t isp_romfw_rev[3]; /* PROM F/W revision */
+ uint16_t isp_maxcmds; /* max possible I/O cmds */
+ uint8_t isp_type; /* HBA Chip Type */
+ uint8_t isp_revision; /* HBA Chip H/W Revision */
+ uint32_t isp_maxluns; /* maximum luns supported */
+
+ uint32_t isp_clock : 8, /* input clock */
+ : 4,
+ isp_port : 1, /* 23XX only */
+ isp_failed : 1, /* board failed */
+ isp_open : 1, /* opened (ioctl) */
+ isp_touched : 1, /* board ever seen? */
+ isp_bustype : 1, /* SBus or PCI */
+ isp_loaded_fw : 1, /* loaded firmware */
+ isp_role : 2, /* roles supported */
+ isp_dblev : 12; /* debug log mask */
+
+ uint32_t isp_confopts; /* config options */
+
+ uint16_t isp_rqstinrp; /* register for REQINP */
+ uint16_t isp_rqstoutrp; /* register for REQOUTP */
+ uint16_t isp_respinrp; /* register for RESINP */
+ uint16_t isp_respoutrp; /* register for RESOUTP */
+
+ /*
+ * Instrumentation
+ */
+ uint64_t isp_intcnt; /* total int count */
+ uint64_t isp_intbogus; /* spurious int count */
+ uint64_t isp_intmboxc; /* mbox completions */
+ uint64_t isp_intoasync; /* other async */
+ uint64_t isp_rsltccmplt; /* CMDs on result q */
+ uint64_t isp_fphccmplt; /* CMDs via fastpost */
+ uint16_t isp_rscchiwater;
+ uint16_t isp_fpcchiwater;
+
+ /*
+ * Volatile state
+ */
+
+ volatile uint32_t : 8,
+ isp_mboxbsy : 1, /* mailbox command active */
+ isp_state : 3,
+ isp_sendmarker : 2, /* send a marker entry */
+ isp_update : 2, /* update parameters */
+ isp_nactive : 16; /* how many commands active */
+ volatile uint16_t isp_reqodx; /* index of last ISP pickup */
+ volatile uint16_t isp_reqidx; /* index of next request */
+ volatile uint16_t isp_residx; /* index of next result */
+ volatile uint16_t isp_resodx; /* index of next result */
+ volatile uint16_t isp_rspbsy;
+ volatile uint16_t isp_lasthdls; /* last handle seed */
+ volatile uint16_t isp_obits; /* mailbox command output */
+ volatile uint16_t isp_mboxtmp[MAILBOX_STORAGE];
+ volatile uint16_t isp_lastmbxcmd; /* last mbox command sent */
+ volatile uint16_t isp_mbxwrk0;
+ volatile uint16_t isp_mbxwrk1;
+ volatile uint16_t isp_mbxwrk2;
+ volatile uint16_t isp_mbxwrk8;
+ void * isp_mbxworkp;
+
+ /*
+ * Active commands are stored here, indexed by handle functions.
+ */
+ XS_T **isp_xflist;
+
+#ifdef ISP_TARGET_MODE
+ /*
+ * Active target commands are stored here, indexed by handle function.
+ */
+ void **isp_tgtlist;
+#endif
+
+ /*
+ * request/result queue pointers and DMA handles for them.
+ */
+ void * isp_rquest;
+ void * isp_result;
+ XS_DMA_ADDR_T isp_rquest_dma;
+ XS_DMA_ADDR_T isp_result_dma;
+};
+
+#define SDPARAM(isp) ((sdparam *) (isp)->isp_param)
+#define FCPARAM(isp) ((fcparam *) (isp)->isp_param)
+
+/*
+ * ISP Driver Run States
+ */
+#define ISP_NILSTATE 0
+#define ISP_CRASHED 1
+#define ISP_RESETSTATE 2
+#define ISP_INITSTATE 3
+#define ISP_RUNSTATE 4
+
+/*
+ * ISP Configuration Options
+ */
+#define ISP_CFG_NORELOAD 0x80 /* don't download f/w */
+#define ISP_CFG_NONVRAM 0x40 /* ignore NVRAM */
+#define ISP_CFG_TWOGB 0x20 /* force 2GB connection (23XX only) */
+#define ISP_CFG_ONEGB 0x10 /* force 1GB connection (23XX only) */
+#define ISP_CFG_FULL_DUPLEX 0x01 /* Full Duplex (Fibre Channel only) */
+#define ISP_CFG_PORT_PREF 0x0C /* Mask for Port Prefs (2200 only) */
+#define ISP_CFG_LPORT 0x00 /* prefer {N/F}L-Port connection */
+#define ISP_CFG_NPORT 0x04 /* prefer {N/F}-Port connection */
+#define ISP_CFG_NPORT_ONLY 0x08 /* insist on {N/F}-Port connection */
+#define ISP_CFG_LPORT_ONLY 0x0C /* insist on {N/F}L-Port connection */
+#define ISP_CFG_OWNWWPN 0x100 /* override NVRAM wwpn */
+#define ISP_CFG_OWNWWNN 0x200 /* override NVRAM wwnn */
+#define ISP_CFG_OWNFSZ 0x400 /* override NVRAM frame size */
+#define ISP_CFG_OWNLOOPID 0x800 /* override NVRAM loopid */
+#define ISP_CFG_OWNEXCTHROTTLE 0x1000 /* override NVRAM execution throttle */
+
+/*
+ * Prior to calling isp_reset for the first time, the outer layer
+ * should set isp_role to one of NONE, INITIATOR, TARGET, BOTH.
+ *
+ * If you set ISP_ROLE_NONE, the cards will be reset, new firmware loaded,
+ * NVRAM read, and defaults set, but any further initialization (e.g.
+ * INITIALIZE CONTROL BLOCK commands for 2X00 cards) won't be done.
+ *
+ * If INITIATOR MODE isn't set, attempts to run commands will be stopped
+ * at isp_start and completed with the moral equivalent of SELECTION TIMEOUT.
+ *
+ * If TARGET MODE is set, it doesn't mean that the rest of target mode support
+ * needs to be enabled, or will even work. What happens with the 2X00 cards
+ * here is that if you have enabled it with TARGET MODE as part of the ICB
+ * options, but you haven't given the f/w any ram resources for ATIOs or
+ * Immediate Notifies, the f/w just handles what it can and you never see
+ * anything. Basically, it sends a single byte of data (the first byte,
+ * which you can set as part of the INITIALIZE CONTROL BLOCK command) for
+ * INQUIRY, and sends back QUEUE FULL status for any other command.
+ *
+ */
+#define ISP_ROLE_NONE 0x0
+#define ISP_ROLE_TARGET 0x1
+#define ISP_ROLE_INITIATOR 0x2
+#define ISP_ROLE_BOTH (ISP_ROLE_TARGET|ISP_ROLE_INITIATOR)
+#define ISP_ROLE_EITHER ISP_ROLE_BOTH
+#ifndef ISP_DEFAULT_ROLES
+#define ISP_DEFAULT_ROLES ISP_ROLE_INITIATOR
+#endif
+
+
+/*
+ * Firmware related defines
+ */
+#define ISP_CODE_ORG 0x1000 /* default f/w code start */
+#define ISP_CODE_ORG_2300 0x0800 /* ..except for 2300s */
+#define ISP_FW_REV(maj, min, mic) ((maj << 24) | (min << 16) | mic)
+#define ISP_FW_MAJOR(code) ((code >> 24) & 0xff)
+#define ISP_FW_MINOR(code) ((code >> 16) & 0xff)
+#define ISP_FW_MICRO(code) ((code >> 8) & 0xff)
+#define ISP_FW_REVX(xp) ((xp[0]<<24) | (xp[1] << 16) | xp[2])
+#define ISP_FW_MAJORX(xp) (xp[0])
+#define ISP_FW_MINORX(xp) (xp[1])
+#define ISP_FW_MICROX(xp) (xp[2])
+#define ISP_FW_NEWER_THAN(i, major, minor, micro) \
+ (ISP_FW_REVX((i)->isp_fwrev) > ISP_FW_REV(major, minor, micro))
+
+/*
+ * Bus (implementation) types
+ */
+#define ISP_BT_PCI 0 /* PCI Implementations */
+#define ISP_BT_SBUS 1 /* SBus Implementations */
+
+/*
+ * If we have not otherwise defined SBus support away make sure
+ * it is defined here such that the code is included as default
+ */
+#ifndef ISP_SBUS_SUPPORTED
+#define ISP_SBUS_SUPPORTED 1
+#endif
+
+/*
+ * Chip Types
+ */
+#define ISP_HA_SCSI 0xf
+#define ISP_HA_SCSI_UNKNOWN 0x1
+#define ISP_HA_SCSI_1020 0x2
+#define ISP_HA_SCSI_1020A 0x3
+#define ISP_HA_SCSI_1040 0x4
+#define ISP_HA_SCSI_1040A 0x5
+#define ISP_HA_SCSI_1040B 0x6
+#define ISP_HA_SCSI_1040C 0x7
+#define ISP_HA_SCSI_1240 0x8
+#define ISP_HA_SCSI_1080 0x9
+#define ISP_HA_SCSI_1280 0xa
+#define ISP_HA_SCSI_10160 0xb
+#define ISP_HA_SCSI_12160 0xc
+#define ISP_HA_FC 0xf0
+#define ISP_HA_FC_2100 0x10
+#define ISP_HA_FC_2200 0x20
+#define ISP_HA_FC_2300 0x30
+#define ISP_HA_FC_2312 0x40
+#define ISP_HA_FC_2322 0x50
+#define ISP_HA_FC_2400 0x60
+#define ISP_HA_FC_2422 0x61
+
+#define IS_SCSI(isp) (isp->isp_type & ISP_HA_SCSI)
+#define IS_1240(isp) (isp->isp_type == ISP_HA_SCSI_1240)
+#define IS_1080(isp) (isp->isp_type == ISP_HA_SCSI_1080)
+#define IS_1280(isp) (isp->isp_type == ISP_HA_SCSI_1280)
+#define IS_10160(isp) (isp->isp_type == ISP_HA_SCSI_10160)
+#define IS_12160(isp) (isp->isp_type == ISP_HA_SCSI_12160)
+
+#define IS_12X0(isp) (IS_1240(isp) || IS_1280(isp))
+#define IS_1X160(isp) (IS_10160(isp) || IS_12160(isp))
+#define IS_DUALBUS(isp) (IS_12X0(isp) || IS_12160(isp))
+#define IS_ULTRA2(isp) (IS_1080(isp) || IS_1280(isp) || IS_1X160(isp))
+#define IS_ULTRA3(isp) (IS_1X160(isp))
+
+#define IS_FC(isp) ((isp)->isp_type & ISP_HA_FC)
+#define IS_2100(isp) ((isp)->isp_type == ISP_HA_FC_2100)
+#define IS_2200(isp) ((isp)->isp_type == ISP_HA_FC_2200)
+#define IS_23XX(isp) \
+ ((isp)->isp_type >= ISP_HA_FC_2300 && (isp)->isp_type < ISP_HA_FC_2400)
+#define IS_2300(isp) ((isp)->isp_type == ISP_HA_FC_2300)
+#define IS_2312(isp) ((isp)->isp_type == ISP_HA_FC_2312)
+#define IS_2322(isp) ((isp)->isp_type == ISP_HA_FC_2322)
+#define IS_24XX(isp) ((isp)->isp_type >= ISP_HA_FC_2400)
+
+/*
+ * DMA related macros
+ */
+#define DMA_WD3(x) ((((uint64_t)x) >> 48) & 0xffff)
+#define DMA_WD2(x) ((((uint64_t)x) >> 32) & 0xffff)
+#define DMA_WD1(x) (((x) >> 16) & 0xffff)
+#define DMA_WD0(x) (((x) & 0xffff))
+
+#define DMA_LO32(x) ((uint32_t) (x))
+#define DMA_HI32(x) ((uint32_t)(((uint64_t)x) >> 32))
+
+/*
+ * Core System Function Prototypes
+ */
+
+/*
+ * Reset Hardware. Totally. Assumes that you'll follow this with
+ * a call to isp_init.
+ */
+void isp_reset(ispsoftc_t *);
+
+/*
+ * Initialize Hardware to known state
+ */
+void isp_init(ispsoftc_t *);
+
+/*
+ * Reset the ISP and call completion for any orphaned commands.
+ */
+void isp_reinit(ispsoftc_t *);
+
+#ifdef ISP_FW_CRASH_DUMP
+/*
+ * Dump firmware entry point.
+ */
+void isp_fw_dump(ispsoftc_t *isp);
+#endif
+
+/*
+ * Internal Interrupt Service Routine
+ *
+ * The outer layers do the spade work to get the appropriate status register,
+ * semaphore register and first mailbox register (if appropriate). This also
+ * means that most spurious/bogus interrupts not for us can be filtered first.
+ */
+void isp_intr(ispsoftc_t *, uint16_t, uint16_t, uint16_t);
+
+
+/*
+ * Command Entry Point- Platform Dependent layers call into this
+ */
+int isp_start(XS_T *);
+
+/* these values are what isp_start returns */
+#define CMD_COMPLETE 101 /* command completed */
+#define CMD_EAGAIN 102 /* busy- maybe retry later */
+#define CMD_QUEUED 103 /* command has been queued for execution */
+#define CMD_RQLATER 104 /* requeue this command later */
+
+/*
+ * Command Completion Point- Core layers call out from this with completed cmds
+ */
+void isp_done(XS_T *);
+
+/*
+ * Platform Dependent to External to Internal Control Function
+ *
+ * Assumes locks are held on entry. You should note that with many of
+ * these commands and locks may be released while this is occurring.
+ *
+ * A few notes about some of these functions:
+ *
+ * ISPCTL_FCLINK_TEST tests to make sure we have good fibre channel link.
+ * The argument is a pointer to an integer which is the time, in microseconds,
+ * we should wait to see whether we have good link. This test, if successful,
+ * lets us know our connection topology and our Loop ID/AL_PA and so on.
+ * You can't get anywhere without this.
+ *
+ * ISPCTL_SCAN_FABRIC queries the name server (if we're on a fabric) for
+ * all entities using the FC Generic Services subcommand GET ALL NEXT.
+ * For each found entity, an ISPASYNC_FABRICDEV event is generated (see
+ * below).
+ *
+ * ISPCTL_SCAN_LOOP does a local loop scan. This is only done if the connection
+ * topology is NL or FL port (private or public loop). Since the Qlogic f/w
+ * 'automatically' manages local loop connections, this function essentially
+ * notes the arrival, departure, and possible shuffling around of local loop
+ * entities. Thus for each arrival and departure this generates an isp_async
+ * event of ISPASYNC_PROMENADE (see below).
+ *
+ * ISPCTL_PDB_SYNC is somewhat misnamed. It actually is the final step, in
+ * order, of ISPCTL_FCLINK_TEST, ISPCTL_SCAN_FABRIC, and ISPCTL_SCAN_LOOP.
+ * The main purpose of ISPCTL_PDB_SYNC is to complete management of logging
+ * and logging out of fabric devices (if one is on a fabric) and then marking
+ * the 'loop state' as being ready to now be used for sending commands to
+ * devices. Originally fabric name server and local loop scanning were
+ * part of this function. It's now been separated to allow for finer control.
+ */
+typedef enum {
+ ISPCTL_RESET_BUS, /* Reset Bus */
+ ISPCTL_RESET_DEV, /* Reset Device */
+ ISPCTL_ABORT_CMD, /* Abort Command */
+ ISPCTL_UPDATE_PARAMS, /* Update Operating Parameters (SCSI) */
+ ISPCTL_FCLINK_TEST, /* Test FC Link Status */
+ ISPCTL_SCAN_FABRIC, /* (Re)scan Fabric Name Server */
+ ISPCTL_SCAN_LOOP, /* (Re)scan Local Loop */
+ ISPCTL_PDB_SYNC, /* Synchronize Port Database */
+ ISPCTL_SEND_LIP, /* Send a LIP */
+ ISPCTL_GET_POSMAP, /* Get FC-AL position map */
+ ISPCTL_RUN_MBOXCMD, /* run a mailbox command */
+ ISPCTL_TOGGLE_TMODE, /* toggle target mode */
+ ISPCTL_GET_PDB /* get a single port database entry */
+} ispctl_t;
+int isp_control(ispsoftc_t *, ispctl_t, void *);
+
+
+/*
+ * Platform Dependent to Internal to External Control Function
+ * (each platform must provide such a function)
+ *
+ * Assumes locks are held.
+ *
+ * A few notes about some of these functions:
+ *
+ * ISPASYNC_CHANGE_NOTIFY notifies the outer layer that a change has
+ * occurred that invalidates the list of fabric devices known and/or
+ * the list of known loop devices. The argument passed is a pointer
+ * whose values are defined below (local loop change, name server
+ * change, other). 'Other' may simply be a LIP, or a change in
+ * connection topology.
+ *
+ * ISPASYNC_FABRIC_DEV announces the next element in a list of
+ * fabric device names we're getting out of the name server. The
+ * argument points to a GET ALL NEXT response structure. The list
+ * is known to terminate with an entry that refers to ourselves.
+ * One of the main purposes of this function is to allow outer
+ * layers, which are OS dependent, to set policy as to which fabric
+ * devices might actually be logged into (and made visible) later
+ * at ISPCTL_PDB_SYNC time. Since there's a finite number of fabric
+ * devices that we can log into (256 less 3 'reserved' for F-port
+ * topologies), and fabrics can grow up to 8 million or so entries
+ * (24 bits of Port Address, less a wad of reserved spaces), clearly
+ * we had better let the OS determine login policy.
+ *
+ * ISPASYNC_PROMENADE has an argument that is a pointer to an integer which
+ * is an index into the portdb in the softc ('target'). Whether that entry's
+ * valid tag is set or not says whether something has arrived or departed.
+ * The name refers to a favorite pastime of many city dwellers- watching
+ * people come and go, talking of Michaelangelo, and so on..
+ *
+ * ISPASYNC_UNHANDLED_RESPONSE gives outer layers a chance to parse a
+ * response queue entry not otherwise handled. The outer layer should
+ * return non-zero if it handled it. The 'arg' points to an unmassaged
+ * response queue entry.
+ */
+
+typedef enum {
+ ISPASYNC_NEW_TGT_PARAMS, /* New Target Parameters Negotiated */
+ ISPASYNC_BUS_RESET, /* Bus Was Reset */
+ ISPASYNC_LOOP_DOWN, /* FC Loop Down */
+ ISPASYNC_LOOP_UP, /* FC Loop Up */
+ ISPASYNC_LIP, /* LIP Received */
+ ISPASYNC_LOOP_RESET, /* Loop Reset Received */
+ ISPASYNC_CHANGE_NOTIFY, /* FC Change Notification */
+ ISPASYNC_FABRIC_DEV, /* FC Fabric Device Arrival */
+ ISPASYNC_PROMENADE, /* FC Objects coming && going */
+ ISPASYNC_TARGET_NOTIFY, /* target asynchronous notification event */
+ ISPASYNC_TARGET_ACTION, /* target action requested */
+ ISPASYNC_CONF_CHANGE, /* Platform Configuration Change */
+ ISPASYNC_UNHANDLED_RESPONSE, /* Unhandled Response Entry */
+ ISPASYNC_FW_CRASH, /* Firmware has crashed */
+ ISPASYNC_FW_DUMPED, /* Firmware crashdump taken */
+ ISPASYNC_FW_RESTARTED /* Firmware has been restarted */
+} ispasync_t;
+int isp_async(ispsoftc_t *, ispasync_t, void *);
+
+#define ISPASYNC_CHANGE_PDB ((void *) 0)
+#define ISPASYNC_CHANGE_SNS ((void *) 1)
+#define ISPASYNC_CHANGE_OTHER ((void *) 2)
+
+/*
+ * Platform Dependent Error and Debug Printout
+ *
+ * Generally this is:
+ *
+ * void isp_prt(ispsoftc_t *, int level, const char *, ...)
+ *
+ * but due to compiler differences on different platforms this won't be
+ * formally done here. Instead, it goes in each platform definition file.
+ */
+
+#define ISP_LOGALL 0x0 /* log always */
+#define ISP_LOGCONFIG 0x1 /* log configuration messages */
+#define ISP_LOGINFO 0x2 /* log informational messages */
+#define ISP_LOGWARN 0x4 /* log warning messages */
+#define ISP_LOGERR 0x8 /* log error messages */
+#define ISP_LOGDEBUG0 0x10 /* log simple debug messages */
+#define ISP_LOGDEBUG1 0x20 /* log intermediate debug messages */
+#define ISP_LOGDEBUG2 0x40 /* log most debug messages */
+#define ISP_LOGDEBUG3 0x80 /* log high frequency debug messages */
+#define ISP_LOGDEBUG4 0x100 /* log high frequency debug messages */
+#define ISP_LOGTDEBUG0 0x200 /* log simple debug messages (target mode) */
+#define ISP_LOGTDEBUG1 0x400 /* log intermediate debug messages (target) */
+#define ISP_LOGTDEBUG2 0x800 /* log all debug messages (target) */
+
+/*
+ * Each Platform provides it's own isposinfo substructure of the ispsoftc
+ * defined above.
+ *
+ * Each platform must also provide the following macros/defines:
+ *
+ *
+ * ISP2100_SCRLEN - length for the Fibre Channel scratch DMA area
+ *
+ * MEMZERO(dst, src) platform zeroing function
+ * MEMCPY(dst, src, count) platform copying function
+ * SNPRINTF(buf, bufsize, fmt, ...) snprintf
+ * USEC_DELAY(usecs) microsecond spindelay function
+ * USEC_SLEEP(isp, usecs) microsecond sleep function
+ *
+ * NANOTIME_T nanosecond time type
+ *
+ * GET_NANOTIME(NANOTIME_T *) get current nanotime.
+ *
+ * GET_NANOSEC(NANOTIME_T *) get uint64_t from NANOTIME_T
+ *
+ * NANOTIME_SUB(NANOTIME_T *, NANOTIME_T *)
+ * subtract two NANOTIME_T values
+ *
+ *
+ * MAXISPREQUEST(ispsoftc_t *) maximum request queue size
+ * for this particular board type
+ *
+ * MEMORYBARRIER(ispsoftc_t *, barrier_type, offset, size)
+ *
+ * Function/Macro the provides memory synchronization on
+ * various objects so that the ISP's and the system's view
+ * of the same object is consistent.
+ *
+ * MBOX_ACQUIRE(ispsoftc_t *) acquire lock on mailbox regs
+ * MBOX_WAIT_COMPLETE(ispsoftc_t *) wait for mailbox cmd to be done
+ * MBOX_NOTIFY_COMPLETE(ispsoftc_t *) notification of mbox cmd donee
+ * MBOX_RELEASE(ispsoftc_t *) release lock on mailbox regs
+ *
+ * FC_SCRATCH_ACQUIRE(ispsoftc_t *) acquire lock on FC scratch area
+ * FC_SCRATCH_RELEASE(ispsoftc_t *) acquire lock on FC scratch area
+ *
+ * SCSI_GOOD SCSI 'Good' Status
+ * SCSI_CHECK SCSI 'Check Condition' Status
+ * SCSI_BUSY SCSI 'Busy' Status
+ * SCSI_QFULL SCSI 'Queue Full' Status
+ *
+ * XS_T Platform SCSI transaction type (i.e., command for HBA)
+ * XS_DMA_ADDR_T Platform PCI DMA Address Type
+ * XS_ISP(xs) gets an instance out of an XS_T
+ * XS_CHANNEL(xs) gets the channel (bus # for DUALBUS cards) ""
+ * XS_TGT(xs) gets the target ""
+ * XS_LUN(xs) gets the lun ""
+ * XS_CDBP(xs) gets a pointer to the scsi CDB ""
+ * XS_CDBLEN(xs) gets the CDB's length ""
+ * XS_XFRLEN(xs) gets the associated data transfer length ""
+ * XS_TIME(xs) gets the time (in milliseconds) for this command
+ * XS_RESID(xs) gets the current residual count
+ * XS_STSP(xs) gets a pointer to the SCSI status byte ""
+ * XS_SNSP(xs) gets a pointer to the associate sense data
+ * XS_SNSLEN(xs) gets the length of sense data storage
+ * XS_SNSKEY(xs) dereferences XS_SNSP to get the current stored Sense Key
+ * XS_TAG_P(xs) predicate of whether this command should be tagged
+ * XS_TAG_TYPE(xs) which type of tag to use
+ * XS_SETERR(xs) set error state
+ *
+ * HBA_NOERROR command has no erros
+ * HBA_BOTCH hba botched something
+ * HBA_CMDTIMEOUT command timed out
+ * HBA_SELTIMEOUT selection timed out (also port logouts for FC)
+ * HBA_TGTBSY target returned a BUSY status
+ * HBA_BUSRESET bus reset destroyed command
+ * HBA_ABORTED command was aborted (by request)
+ * HBA_DATAOVR a data overrun was detected
+ * HBA_ARQFAIL Automatic Request Sense failed
+ *
+ * XS_ERR(xs) return current error state
+ * XS_NOERR(xs) there is no error currently set
+ * XS_INITERR(xs) initialize error state
+ *
+ * XS_SAVE_SENSE(xs, sp) save sense data
+ *
+ * XS_SET_STATE_STAT(isp, sp, xs) platform dependent interpreter of
+ * response queue entry status bits
+ *
+ *
+ * DEFAULT_IID(ispsoftc_t *) Default SCSI initiator ID
+ * DEFAULT_LOOPID(ispsoftc_t *) Default FC Loop ID
+ * DEFAULT_NODEWWN(ispsoftc_t *) Default Node WWN
+ * DEFAULT_PORTWWN(ispsoftc_t *) Default Port WWN
+ * DEFAULT_FRAMESIZE(ispsoftc_t *) Default Frame Size
+ * DEFAULT_EXEC_THROTTLE(ispsoftc_t *) Default Execution Throttle
+ * These establish reasonable defaults for each platform.
+ * These must be available independent of card NVRAM and are
+ * to be used should NVRAM not be readable.
+ *
+ * ISP_NODEWWN(ispsoftc_t *) FC Node WWN to use
+ * ISP_PORTWWN(ispsoftc_t *) FC Port WWN to use
+ *
+ * These are to be used after NVRAM is read. The tags
+ * in fcparam.isp_{node,port}wwn reflect the values
+ * read from NVRAM (possibly corrected for card botches).
+ * Each platform can take that information and override
+ * it or ignore and return the Node and Port WWNs to be
+ * used when sending the Qlogic f/w the Initialization Control
+ * Block.
+ *
+ * (XXX these do endian specific transformations- in transition XXX)
+ *
+ * ISP_IOXPUT_8(ispsoftc_t *, uint8_t srcval, uint8_t *dstptr)
+ * ISP_IOXPUT_16(ispsoftc_t *, uint16_t srcval, uint16_t *dstptr)
+ * ISP_IOXPUT_32(ispsoftc_t *, uint32_t srcval, uint32_t *dstptr)
+ *
+ * ISP_IOXGET_8(ispsoftc_t *, uint8_t *srcptr, uint8_t dstrval)
+ * ISP_IOXGET_16(ispsoftc_t *, uint16_t *srcptr, uint16_t dstrval)
+ * ISP_IOXGET_32(ispsoftc_t *, uint32_t *srcptr, uint32_t dstrval)
+ *
+ * ISP_SWIZZLE_NVRAM_WORD(ispsoftc_t *, uint16_t *)
+ */
+
+#endif /* _ISPVAR_H */