;
; This is the ALPHA (previously called "EVAX") version of ARCH_DEFS.MAR,
; which contains architectural definitions for compiling VMS sources
; for VAX and ALPHA systems.
;
EVAX = 1
ALPHA = 1
BIGPAGE = 1
ADDRESSBITS = 32
	.IF DEFINED DEBUG$LOG
        .TITLE  LOGGING_DUDRIVER - LOGGING DISK CLASS DRIVER
        .IFF
 	.TITLE	DUDRIVER - DISK CLASS DRIVER
        .ENDC
	.IDENT	'X-69'
;
;****************************************************************************
;*									    *
;*  COPYRIGHT (c) 1978, 1980, 1982, 1984, 1986, 1987, 1988, 1991, 1992 BY   *
;*  DIGITAL EQUIPMENT CORPORATION, MAYNARD, MASSACHUSETTS.		    *
;*  ALL RIGHTS RESERVED.						    *
;* 									    *
;*  THIS SOFTWARE IS FURNISHED UNDER A LICENSE AND MAY BE USED AND COPIED   *
;*  ONLY IN  ACCORDANCE WITH  THE  TERMS  OF  SUCH  LICENSE  AND WITH THE   *
;*  INCLUSION OF THE ABOVE COPYRIGHT NOTICE. THIS SOFTWARE OR  ANY  OTHER   *
;*  COPIES THEREOF MAY NOT BE PROVIDED OR OTHERWISE MADE AVAILABLE TO ANY   *
;*  OTHER PERSON.  NO TITLE TO AND OWNERSHIP OF  THE  SOFTWARE IS  HEREBY   *
;*  TRANSFERRED.							    *
;* 									    *
;*  THE INFORMATION IN THIS SOFTWARE IS  SUBJECT TO CHANGE WITHOUT NOTICE   *
;*  AND  SHOULD  NOT  BE  CONSTRUED AS  A COMMITMENT BY DIGITAL EQUIPMENT   *
;*  CORPORATION.							    *
;* 									    *
;*  DIGITAL ASSUMES NO RESPONSIBILITY FOR THE USE  OR  RELIABILITY OF ITS   *
;*  SOFTWARE ON EQUIPMENT WHICH IS NOT SUPPLIED BY DIGITAL.		    *
;* 									    *
;*									    *
;****************************************************************************
;
; Robert Rappaport  11-May-1981
;
;  DISK CLASS DRIVER
;
; MODIFIED BY
;
;
;	X-69	KMJ0141A	Kevin M. Jenkins		30-OCT-1995
;		Change CDDB$W_STATUS to CDDB$L_STATUS for ALPHA
;
;	X-68	KMJ0141		Kevin M. Jenkins		23-OCT-1995
;               In IO_STALLED if we have a SHDIO check for CDDB$V_RECONNECT
;		along with CDDB$V_NOCONN and if either is set go to
;		SHDIO_REJECT
;
;
;
;       X-67    DVE             Dennis Van Eron                 16-Oct-1995
;               Add call to errorlog routine ERL$LOGSTATUS in
;               DU$RE_SYNCH to capture CDRP information about the
;               command that has timed out ( GCS in DU$TMR ).
;
;       X-66    DVE             Dennis Van Eron                 28-Sep-1995
;               Changed PDT$B_TYPE to PDT$B_PDT_TYPE in DU$TMR allowing
;               for correct value to be loaded in CDDB$W_LOAD_AVAIL.
;
;	X-65	PJH		Paul J. Houlihan		26-Jun-1995
;		Add Pending I/O routine for Fast Path.
;		- Make sure that the start I/O routine saves the correct registers.
;		- Remove MSCP_STALL and MSCP_UNSTALL
;
;	X-64	TRC1010		Terry Cassidy			15-May-1995
;		Change MAXBCNT for VMS MSCP server to 63.5K
;
;	X-63	JCH703	John C. Hallyburton, Jr.		11-May-1995
;		Add FAST_FDT dispatch address
;
;	X-62	PJH		Paul J. Houlihan		12-NOV-1994
;		Add Fast Path support to I/O mainline.
;
;	X-61	MJN		Marc Noel			07-Mar-1995
;		Remove du$transfer_cntlr_err and all supporting code.
;
;	X-59	WDB64B5		Walter D. Blaschuk, Jr.		23-Feb-1995
;               64-bit Virtual Addressing/64BCLUS changes: The following
;		modifications must be conditionalized in order to prevent 
;		bifurcation of source modules into ZETA and THETA.
;		1) Define 64-bit functions in FDT,
;		2) Enable 64-bit Diagnostic buffers in DDT,
;		3) Fix ASSUMES for new data structure fields,
;
;	X-58	MJN		Marc J. Noel			10-Feb-1995
;		Insert Paul Destefano's changes which were removed
;		by mistake.  Here is the change description for Paul's
;		Change.  Also added another assume statment
;		for WLG flags.
;		PRD		Paul R. DeStefano		01-Feb-1995
;		The write logging status bits in the IRP/CDRP are referenced
;		by both DUDRIVER and SHDRIVER.  As part of the process of
;		moving the SHD_FLAGS field so that it no longer overlays the
;		_PRI field, the write logging status bits have been moved
;		to a separate WLG_FLAGS field (the WLG_FLAGS field does not
;		overlay the _PRI field either).  Changed all CDRP$B_SHD_FLAGS
;		references to CDRP$B_WLG_FLAGS.
;
;	X-57	MJN		Marc J. Noel			06-Feb-1995
;		Pass UCB to error log status not the cddb
;		Also correct ksp invalid crashes.
;				
;
;	X-56	MJN		Marc J. Noel			02-Feb-1995
;		Pass UCB to error log status not the cddb
;		Also correct du$msg_err_hndlr so controller errors 
;		are not resynched there.		
;
;	X-54U1	MJN		Marc J. Noel			02-Feb-1995
;		Correct du_msg_err_hndlr so that KSP invalid crashes
;		do not occur.
;
;	X-54	MJN		Marc J. Noel			09-Jan-1995
;		Add new subroutine du$transfer_cntlr_err and add hooks
;		in dispatch routines to call this subroutine when 
;		controller errors are returned from drives.
;
;	X-53	TRC1003		Terry Cassidy			09-Dec-1994
;		Remove DU$SHAD_WCHECK_FDT once again.
;
;	X-52			Brian Porter			02-DEC-1994
;		Add an explicit clear of the remote DCD when controller flags
;		are copied to the CDDB.
;
;	X-51	GH		Gary Hughes			14-Sep-1994
;		Replace code that manipulates server structures with macros
;		that call global routines in the server.
;
;	X-50	GH		Gary Hughes			21-Jun-1994
;		Fold of X-48U2, X-48U1
;		Modify move_server to wait for the available to complete.
;		Modify the status returned by the MVRESTART code to
;		one that shdriver will accept.
;
;	X-49	NJB		Nancy Jean Burkholder		1-Jun-1994
;		Return zero data transferred when an SS$_ILLCDTST is
;		returned from a SEND_MSCP_MSG in START_DISK_COPY_DATA.
;
;	X-48	GH		Gary Hughes			12-May-1994
;		Add MSCP_MVRESTART test to FUNCTION_EXIT.
;		Modify VC error routines to post SRVIOs immediately rather than
;		queue them for restart.
;		Have IO_STALLED change the HRB state when stalling a server I/O
;		on the UCB pending queue. Have SEND_IO restore the normal
;		driver wait state in HRBs for server I/Os.
;
;       X-47    MAS0T01         Michael A. Stams              18-Apr-1994
;               Move check in RECONN_COMMON for RESYNCH to CONNECT_ERR where
;               it really belongs (porting error). Correctly clear CDRP$L_CDT
;	        in RECONN_COMMON just befor the DISCONNECT.
;
;	X-46			Brian Porter			22-APR-1994
;		Correctly handle INVALID command for write history management
;		commands.
;
;	X-45	PJH		Paul J. Houlihan		7-Apr-1994
;               Use a new PRMBSY_CLEANUP_PERMITTED bit to avoid a deadlock
;               in connection cleanup where the PRMCDRP must be allocated 
;		but DUTU$GET_DEVTYPE has already allocated PRMCDRP and issued
;		a SEND_MSCP_MSG after which the connection fails.
;
;	X-44			Brian Porter			16-MAR-1994
;		Correctly handle INVALID command for write logging transfer
;		commands.
;
;	X-43			Brian Porter			09-MAR-1994
;		Correctly handle OFFLINE and AVAILABLE status for transfer
;		commands.
;	X-42			Brian Porter			02-MAR-1994
;		Add V55-2 (X-19A18) change to RECORD_UNIT_STATUS.
;
;	X-41			Brian Porter			28-FEB-1994
;		Correct syntax of EXTZV in TRANSFER_DATA_ERROR:.
;
;	X-40			Brian Porter			28-FEB-1994
;		Persuade the GEM compiler to schedule instructions correctly
;		in TRANSFER_AVLBL: and TRANSFER_DATA_ERROR:.
;
;	X-39	MAS0E06		Michael A. Stams		15-Feb-1994
;		Change M_RESYNCH to V_RESYNCH in DUTU$CONNECT_ERR.
;
;	X-38	RFB001		Ray Boucher			 7-Jan-1994
;		1. Bring DU$TIMER up to parity with CORAL. Set IMPEND as
;		   required (it is currently not set anywhere); add special
;		   DAPBSY processing; move IMPEND check to beginning.
;		2. Replace CANCEL_WAIT macro with IDLE_CDRP macro for
;                  the case where CDRP FQFL queue links contain
;                  non-paged pool addresses and crash the system.
;		3. Remove unused RET instruction in DU$SHAD_WCHECK_FDT:
;		   routine added in X-36.
;
;	X-37	PJH		Paul J. Houlihan		19-Nov-1993
;		Set UCB$V_EXFUNC_SUPP.
;
;       X-36    MAS0E05		Michael A. Stams	        8-Nov-1993
;		Check-in paul's fix.
;		PJH		Paul J. Houlihan	       01-Nov-1993
;		- Fix bug where legal function mask went away in Step 2 drivers
;		leaving IO$_DISPLAY not declared as buffered I/O.
;		- Re-instate DU$SHAD_WCHECK_FDT.
;		- Add commented out callout to Faulty Towers.
;
;	X-35	TPF005A		Tom Frederick		       25-Oct-1993
;		Step 2.  Correct problem with call interface to SHDRIVER
;		in DU$CRESHAD_FDT, DU$REMSHAD_FDT.
;
;	X-34	MAS0E04		Michael A. Stam			8-Oct-1993
;		C2 support, blade parity.
;
;	X-33	MAS0E03		Michael A. Stams	       14-Sep-1993
;		More step2.
;		Remove obsolete routine(s).
;
;	X-32	MAS0E02		Michael A. Stams	       13-Sep-1993
;		Change setup_io from jsb to call (Step2).
;               Really do X-30.
;
;	X-31	MAS0E01		Michael	A. Stams	       19-Aug-1993
;		Step2 conversion.
;
;	X-30	MAS0P04		Michael A. Stams	       21-Jul-1993
;		Remove debug code nolonger needed.	
;
;	X-29	MAS0P03		Michael A. Stams               15-Jul-1993
;		Update media table for RX26 DVDRIVER Case (Jensen).
;
;	X-28	MAS0P02		Michael Stams                   1-Jul-1993
;		final blade/amber parity changes.			  
;		
;		Removed all code modification comments prior to "initial port to
;		EVMS" to aid in readability 
;
;		However Lest we forget those who's hard work provided us with 
;		the foundation on which to build SYS$DUDRIVER.
;
;			Ellen M. Batbouta, Cheryl J. Bulmer, Scott H. Davis, 
;			Paul R. DeStefano, Rod Gamache, William Goleman, 
;			Keith F. Griffin, Gary Hughes, Jim Klumpp, 
;			Lee Leahy, Jeff McLeman, Stephen F. Shirron, 
;			Mark A. Stiles, Leonard S. Szubowicz, Ward C. Travis, 
;			Ralph O. Weber, Wai C. Yim.
;
;
;	X-27	MAS0P01		Michael Stams			28-May-1993
;       	Step One of Blade parity.
;		Incorporate VMS V5.5-1 change V-19B1.
;
;       	    V-19B1  JJA92014        John J. Andruszkiewicz  26-Jan-1992
;                   Increase controller timeout in MAKE_CONNECTION 53$ 
;		    to 10 seconds.
;
;		Port MSCP packet logging.
;	
;	X-26	WLG0D26		William Goleman			 9-Mar-1993
;		Clear out CDRP$L_CDT field when connection will not reform.
;		Include prefered path fix from Blade release:
;		GH039           Gary Hughes                     1-Apr-1993
;               Modify start_packack to skip the preferred path check if
;               the CDRP is flagged as a server I/O request.
;
;	X-25	BJU0020		Barbara Upham  			02-Apr-1993
;		Update media table for new devices.
;
;	X-24	WLG0D24		William Goleman			26-Mar-1993
;		Change interface to DUTU$LOOKUP_UCB, R0 is changed to 
;		contain the address of the UCB to start with when searching
;		for a "known" device. Preserve R3 as well as R5 in the 
;		STARTIO routine. 
;
;	X-23	WLG0D23		William Goleman			 9-Mar-1993
;		. Fix the setting of SS$_CTRLERR status for transfer 
;		  commands that are in the midst of invalid command 
;		  processing.
;		. Fix comments around SEND_MSCP_MSG macro.
;		. Restore call to DUTU$KILL_THIS_THREAD in SEND_IO.
;		. Remove unconditional bit clear of IMPEND and DAPBSY bits
;		  to indicate that the DAP permanent CDRP is not in use.
;
;	X-22	PJH		Paul J. Houlihan		16-Dec-1992
;		Optimize mainline DUDRIVER code for the following three
;		cases in order of priority:
;		1. Case of READPBLK
;		2. Case of WRITEPBLK, Both cases 1 & 2 assume that
;		   all resources are available.
;		3. Optimze for BACKUP which normally consumes all
;		   Send credits (ie. optimize send credit stall case)
;		Changes largely consist of .BRANCH_LIKELY and replacing
;		the queue instructions with macros.
;
;	X-21	PJH		Paul J. Houlihan		30-Nov-1992
;		Add ACP NAME field for DUTUSUBS init of a new DDB.
;
;	X-20	PJH		Paul J. Houlihan		18-Nov-1992
;		Have X-19 apply only to remote system disks. For local disks,
;		the controller's pseudo-System Block, which must be used in
;		the class driver controller init, has not yet been created.
;		Thus for local devices, normal process polling is desired.
;
;	X-19	SFS0594		Stephen F. Shirron		06-Oct-1992
;		Make sure that the system disk gets found before any other
;		disks are found.  To do this, explicitly connect to the system
;		disk server instead of waiting for it to be found by the
;		process poller.  Don't start the process poller until the
;		system disk has been found.  This guarantees that the original
;		UCB for the system disk will be used if multiple paths to the
;		system disk are eventually found.  Also fix a porting bug in
;		TRANSFER_IVCMD.
;		
;	X-18	WLG0D17		William Goleman			 1-Oct-1992
;		. Add changes from code inspection of LOCATE_UNIT, CONN_WALK,
;		  and PACKACK routines. 
;		. Change calls to RE_SYNCH to use direct branches.
;		. Changes to JSB_ENTRY masks in many places that were incorrect.
;		. Remove useage of STALL_CALL_FORK macro when calling 
;		  LOCATE_UNIT. Also add comment headers to describe inputs and 
;		  outputs.
;		. Within PACKACK and LOCATE_UNIT routines, restore code to be
;		  more like the VAX code now that the branching across 
;		  subroutine boundaries have been liberalized.
;		. Change entry mask in send_io entry point.
;
;	X-17	GH038A		Gary Hughes			14-Oct-1992
;		Add HSX00/01 device types.
;
;	X-16	WLG0D16		William Goleman			28-Sep-1992
;		Add additional check to X-11 to make sure the permanent CDRP 
;		is not in use before issuing the disconnect when a connection 
;		is reestablished to a controller with a changed allocation 
;		class. Also, change remque of permanent CDRP to adhere to new 
;		SCS interface: (use CANCEL_WAIT) before issuing disconnect on 
;		newly formed circuit.
;
;	X-15	GH001A		Gary Hughes			21-Aug-1992
;		Fold fixes from V5.4-3 to existing routines. Add
;		DU$MSG_ERR_HNDLR.
;
;	X-14	SWA		Scott W. Apgar			05-Aug-1992
;		Changing references to the AUXSTRUC offset in the CRB to use
;		the new field SCS_STRUC.  The AUXSTRUC offset was overloaded.
;
;	X-13	PJH		Paul J. Houlihan		13-Jul-1992
;		Fold 11U1 from FT3 into FT4 and beyond. Also fix a bug where
;		the IMPEND bit is left set in DU$TIMER when it was cleared
;		in VAX/VMS.
;
;		X-11U1	PJH	Paul J. Houlihan		08-Jul-1992
;		Rework DU$TIMER routine to remove stature of COMMON_PREP
;		as subroutine which causes threads to be dropped when
;		when we stall for a SCS resource. DU$TIMER is now a single
;		routine as was done in VAX/VMS rather than 2 level of routines.
;
;	X-12	SFS0562		Stephen F. Shirron		06-Jul-1992
;		Update media ID translation table to exactly match
;		that found in the current Blade generation (X-45)
;		on the VMS master pack.
;
;	X-11	WLG0A11		William Goleman			18-Jun-1992
;		. Add a disconnect if a SCC command for a new connection 
;		  discovers that a connection has been reestablished to a
;		  server that has changed allocation class.
;		. Change constant used for disconnect reason from a local 
;		  variable to a SCS defined constant.
;
;	X-10	PJH		Paul J. Houlihan		30-Mar-1992
;		- Merge in a VAX fix for problem encountered on our testing:
;		X-82U2	GH027		Gary Hughes		3-Oct-1990
;		Correct race condition in make_connection with the prmcdrp.
;               - Re-synch CMS version number.
;
;	X-23	WLG0A22		William Goleman			12-Feb-1992
;		. Fix missing PACKACK return state in PACKACK_DO_ONLINE.
;		. Remove code that set up status in PACKACK_UNKNO_INOPR. 
;		  Some callers expected R0 to remain unchanged.
;		. Fix driver name and BT_ORDER.
;
;	X-22	PJH		Paul J. Houlihan		11-Feb-1992
;		Return success from controller init routine.
;
;	X-21	WLG0A21		William Goleman			31-Jan-1992
;		. Fix wrong register useage in MAKE_CONNECTION.
;		. Restore remote SYSAP name constants to 16 bytes.
;		. Change driver name in DPTAB to SYS$DUDRIVER.
;
;	X-20	WLG0A20		William Goleman			31-Jan-1992
;		Remove unnecessary $LDBDEF, and change MAXUNITS parameter
;		in DPT.
;
;	X-19	WLG0A19		William Goleman			27-Jan-1992
;		. Move device configuration subroutines into DUTUSUBS so that
;		  they can be called by TUDRIVER.
;		. Remove READRCT from the list of legal functions.
;		
;	X-18	WLG0A18		William Goleman			22-Jan-1992
;		. Fix bad reference to promoted variable (UCB$W_DEVSTS).
;
;	X-17	WLG0A17		William Goleman			22-Jan-1992
;		. Add BT_ORDER calue to the driver prologue table.
;		. Restore the label FUNCTION_EXIT to global status.
;
;	X-16	WLG0A16		William Goleman			17-Jan-1992
;		. Fix entry masks for return from SEND_MSCP_MSG macro.
;		. Remove shifted bit instructions.
;		. Fix entry masks for several routines called during
;		  configuration of new devices.
;		. Remove refrences to PERMCDRP_TO_CDDB macro.
;		. Rewrite controller init routine for clarity, separating
;		  into two main parts.
;		. Routines added to be called when a new MSCP$DISK process
;		  is located (eliminate need for CONFIGURE process).
;		. Start changes for LOCK_IODB. This function does not work 
;		  as a macro anymore. It has been made into a subroutine 
;		  to aviod a possible fork in-line.
;		
;	X-15	PJH		Paul J. Houlihan		20-Dec-1991
;		Final LSER DSA Fixes.
;		- Fix interface to DUTU$DEALLOC_ALL routine. Skip indicator
;		was not being initialized for calls to main entry point.
;		- Overcome MAXUNITS checking by using NO_IDB_DISPATCH.
;		- Fix bug in COMMON_PREP .JSB_ENTRY where R4 not
;		returned as an output which is used by later SEND_MSCP_MSG.
;		- Fix KILL_THIS_THREAD in SEND_IO where no status is being
;		returned when one is expected. Just skip it for now.
;
;	X-14	BJT284		Benjamin J. Thomas III	 4-Dec-1991
;		Fix W_DEVSTS reference to be L_DEVSTS
;
;	X-13	BJT277		Benjamin J. Thomas III	21-Nov-1991
;		Use IRP for passing of QIO arguments P1 - P6
;
;	X-12	WLG0A12		William Goleman			19-NOV-1991
;		Reflect promotions made to byte and word fields in the CDDB.
;
;	X-11	BJT271		Benjamin J. Thomas III	15-Nov-1991
;		Promote FUNC fields
;
;	X-10	BJT260		Benjamin J. Thomas III	31-Oct-1991
;		Promote more IRP and UCB fields to longwords.
;
;	X-9	WLG0A09	 William Goleman/Paul Houlihan	13-Sep-1991
;		Changes to use a new SCS macro that removes CDRP from
;		resource wait queues in SCS.
;		
;	X-8	BJT247		Benjamin J. Thomas III	27-Sep-1991
;		Promote some IRP fields to longwords
;
;	X-7	JSSDU		John S. Simakauskas	29-Aug-1991
;		Change DU$SEND_IO to SEND_IO in DUDRIVER and DUTUSUBS.
;		This label is also used in TUDRIVER.
;
;	X-6	WLG0A06		William Goleman		 4-May-1991
;		Remove byte refrences to word field CDRP$W_DUTUCNTR. The
;		high byte of this field was used for HIRT processing which
;		is obsolete. Remove branch barriers added for debugging.
;
;	X-5	GHJ051		Gregory H. Jordan	 8-Apr-1991
;		Fix up PSECTS, establish the initial PSECT for the Prologue.
;		Remove use of the DUTU$END in the DDTAB macro for Alpha.  
;		The end label is not used on Alpha and we won't need to
;		link with DUTUEND.MAR.  Also, for Step 1 drivers, the
;		controller init is setup in the DDTAB macro.
;
;	X-4	WLG0A04		William Goleman		 5-APR-1991
;               Remove executable attribute from data psects. Get driver
;               to assemble with the Alpha compiler.
;
;	X-2Z1	WLG0AZ1		William Goleman		26-Mar-1991
;		Conditionalize code for the port simulator, and V5.4. Change 
;		calls to DUTU$RESTORE_CREDIT to use new SCS macro of the 
;		same name. Utilize new macro STALL_CALL_FORK to call 
;		subroutines that do not return inline. Change invalid cmd
;		processing to work in ported version while remaining 
;		backward compatable. Modification numbering changes are
;		due to the rebuild of the master pack.
;
;	X-82K4	WLG0AK4		William Goleman		15-Feb-1991
;		Change calls to altreqcom. Change names of STARTIO and 
;		RESTARTIO to SETUP_IO and SEND_IO. Make all calls to 
;		SEND_IO use BSBW. Break all branches across SEND_MSCP_MSG
;		macro to enforce routine distinction; then repair all 
;		broken branches making routines more modular.
;
;	X-82K3	WLG0AK3		William Goleman		28-Jan-1991
;		Initial port for EVMS. Consolidate and document entry 
;		points to DU$CONNECT_ERR. Remove all uses of DO_ACTION	
;		and ACTION_ENTRY macros, and replace them with the 
;		DISPATCH macro. Add JSB_ENTRY macro calls to all entry
;		points. Comment out code added in 82K2 to allow continued 
;		testing in the VMS environment.
;


;
; MACRO LIBRARY CALLS
;

	$ARBDEF				; ARB offsets
	$CDTDEF				; CDT offsets
;MS
	$CLUBDEF
	$CRBDEF				; CRB offsets
	$CSBDEF
	$DCDEF				; Device Classes and Types
	$DDBDEF				; DDB offsets
	$DDTDEF				; DDT offsets
	$DEVDEF				; DEVICE CHARACTERISTICS bits
	$DPTDEF				; DPT offsets
	$DYNDEF				; DYN symbols
	$EMBLTDEF			; EMB Log Message Types

	$IDBDEF				; IDB offsets
	$IODEF				; I/O FUNCTION codes
	$IPLDEF				; IPL levels
	$IRPDEF

.IF DEFINED,IRP$Q_QIO_P1
	$BUFIODEF			;Define the Diag/Buff I/O offsets
.ENDC


	$MSCPDEF			; Mass Storage Control Protocol
	$MSLGDEF			; MSCP Error Log offsets
	$MTXDEF				; MUTEX offsets
	$ORBDEF				; ORB offsets
	$PAGEDEF			; Page and block size values
	$PBDEF				; Path Block offsets
	$PCBDEF				; Process Control Block offsets
	$PDTDEF				; Port Descriptor Table offsets
	$PRDEF				; Processor Registers
	$PRVDEF				; Process Privilege Bits
	$RCTDEF				; Revector Cache Table offsets
	$RDDEF				; RDTE offsets
	$RDTDEF				; RDT offsets
	$SBDEF				; System Block Offsets
	$SCSDEF				; Systems Communication Services 
	$SCSCMGDEF			; SCS Connect Message offsets
;MS
	$SHADDEF
	$SSDEF				; System Status values
	$UCBDEF				; Unit Control Block offsets
	$VADEF				; Virtual Address offsets
	$VECDEF				; Interrupt Dispatch Vector offsets
	$WCBDEF				; Window Control Block offsets

	$DUTUDEF			; Common class driver CDDB
					; extensions and other common symbols


; Constants

INIT_IMMED_DELTA=30		; During Controller Initialization, 
				;  timeout DELTA for immediate MSCP commands.
CONNECT_DELTA=10		; During Controller Initialization, the
				;  time interval for retrying failed
				;  CONNECT attempts.
HOST_TIMEOUT=30			; Host timeout value.

INITIAL_CREDIT=10
INITIAL_DG_COUNT=1
MAX_RETRY==2
MIN_SEND_CREDIT=2
INITIAL_LOAD=4			; Initial load rating for port load balancing

CHECK_CREDIT=1			; Define to enable testing for connection
				;  credit stalls .

DAP_LIMIT=3                     ; Load DAP_LIMIT

	.IF NDF UCB$V_MSCP_MVRESTART
UCB$V_MSCP_MVRESTART=^xf
UCB$M_MSCP_MVRESTART=^x8000
	.ENDC



	.SBTTL	ASSUMES

; The following set of ASSUME statements will all be true as long as
;	the IRP and CDRP definitions remain consistent.

	ASSUME	CDRP$L_IOQFL-CDRP$L_IOQFL	EQ	IRP$L_IOQFL
	ASSUME	CDRP$L_IOQBL-CDRP$L_IOQFL	EQ	IRP$L_IOQBL
	ASSUME	CDRP$W_IRP_SIZE-CDRP$L_IOQFL	EQ	IRP$W_SIZE
	ASSUME	CDRP$B_IRP_TYPE-CDRP$L_IOQFL	EQ	IRP$B_TYPE
	ASSUME	CDRP$B_RMOD-CDRP$L_IOQFL	EQ	IRP$B_RMOD
	ASSUME	CDRP$L_PID-CDRP$L_IOQFL		EQ	IRP$L_PID
	.IF	DEFINED,CDRP$L_AST
	ASSUME	CDRP$L_AST-CDRP$L_IOQFL		EQ	IRP$L_AST
	ASSUME	CDRP$L_ASTPRM-CDRP$L_IOQFL	EQ	IRP$L_ASTPRM
        .ENDC
	.IF	DEFINED,CDRP$PQ_ACB64_AST
	ASSUME	CDRP$PQ_ACB64_AST-CDRP$L_IOQFL	  EQ	IRP$PQ_ACB64_AST
	ASSUME	CDRP$Q_ACB64_ASTPRM-CDRP$L_IOQFL  EQ	IRP$Q_ACB64_ASTPRM
	ASSUME  CDRP$L_ACB_FLAGS-CDRP$L_IOQFL     EQ	IRP$L_ACB_FLAGS
	ASSUME  CDRP$L_ACB64X_OFFSET-CDRP$L_IOQFL EQ	IRP$L_ACB64X_OFFSET
        .ENDC
	ASSUME	CDRP$L_WIND-CDRP$L_IOQFL	EQ	IRP$L_WIND
	ASSUME	CDRP$L_UCB-CDRP$L_IOQFL		EQ	IRP$L_UCB
	ASSUME	CDRP$L_FUNC-CDRP$L_IOQFL	EQ	IRP$L_FUNC
	ASSUME	CDRP$B_EFN-CDRP$L_IOQFL		EQ	IRP$B_EFN
	ASSUME	CDRP$B_PRI-CDRP$L_IOQFL		EQ	IRP$B_PRI
	ASSUME  CDRP$B_WLG_FLAGS-CDRP$L_IOQFL 	EQ 	IRP$B_WLG_FLAGS
	.IF	DEFINED,CRDP$L_IOSB
	ASSUME	CDRP$L_IOSB-CDRP$L_IOQFL	EQ	IRP$L_IOSB
        .ENDC
	.IF	DEFINED,CRDP$PQ_IOSB
	ASSUME	CDRP$PQ_IOSB-CDRP$L_IOQFL	EPQ	IRP$PQ_IOSB
        .ENDC	
	ASSUME	CDRP$L_CHAN-CDRP$L_IOQFL	EQ	IRP$L_CHAN
	ASSUME	CDRP$L_STS-CDRP$L_IOQFL		EQ	IRP$L_STS
	ASSUME	CDRP$L_SVAPTE-CDRP$L_IOQFL	EQ	IRP$L_SVAPTE
	ASSUME	CDRP$L_BOFF-CDRP$L_IOQFL	EQ	IRP$L_BOFF
	ASSUME	CDRP$L_BCNT-CDRP$L_IOQFL	EQ	IRP$L_BCNT
	ASSUME	CDRP$L_BCNT-CDRP$L_IOQFL	EQ	IRP$L_BCNT
	ASSUME	CDRP$L_IOST1-CDRP$L_IOQFL	EQ	IRP$L_IOST1
	ASSUME	CDRP$L_MEDIA-CDRP$L_IOQFL	EQ	IRP$L_MEDIA
	ASSUME	CDRP$L_IOST2-CDRP$L_IOQFL	EQ	IRP$L_IOST2
	ASSUME	CDRP$L_TT_TERM-CDRP$L_IOQFL	EQ	IRP$L_TT_TERM
	ASSUME	CDRP$B_CARCON-CDRP$L_IOQFL	EQ	IRP$B_CARCON
	ASSUME	CDRP$Q_NT_PRVMSK-CDRP$L_IOQFL	EQ	IRP$Q_NT_PRVMSK
;	ASSUME	CDRP$L_ABCNT-CDRP$L_IOQFL	EQ	IRP$L_ABCNT
	ASSUME	CDRP$L_ABCNT-CDRP$L_IOQFL	EQ	IRP$L_ABCNT
;	ASSUME	CDRP$L_OBCNT-CDRP$L_IOQFL	EQ	IRP$L_OBCNT
	ASSUME	CDRP$L_OBCNT-CDRP$L_IOQFL	EQ	IRP$L_OBCNT
	ASSUME	CDRP$L_SEGVBN-CDRP$L_IOQFL	EQ	IRP$L_SEGVBN
	ASSUME	CDRP$L_DIAGBUF-CDRP$L_IOQFL	EQ	IRP$L_DIAGBUF
	ASSUME	CDRP$L_SEQNUM-CDRP$L_IOQFL	EQ	IRP$L_SEQNUM
	ASSUME	CDRP$L_EXTEND-CDRP$L_IOQFL	EQ	IRP$L_EXTEND
	ASSUME	CDRP$L_ARB-CDRP$L_IOQFL		EQ	IRP$L_ARB
;
; The following assume statements are to allow the flexibility of specifying
; the Write Log Entry flags by their IRP names or CDRP names depending on
; whether the IRP$B_WLG_FLAGS field or the CDRP$B_WLG_FLAGS field is being
; referenced.  These flags are defined in both IRPDEF and CDRPDEF.
;
	ASSUME IRP$V_WLE_REUSE  EQ CDRP$V_WLE_REUSE
	ASSUME IRP$M_WLE_REUSE  EQ CDRP$M_WLE_REUSE
	ASSUME IRP$V_WLE_SUPWL  EQ CDRP$V_WLE_SUPWL
	ASSUME IRP$M_WLE_SUPWL  EQ CDRP$M_WLE_SUPWL
	ASSUME	UCB$K_DU_LENGTH		LE	UCB$K_LENGTH+160

;
; If the preceeding ASSUME macro breaks it is a signal that the Disk Class
; Driver UCB has grown larger than the space allocated to the Boot Device UCB 
; in [SYS.SRC]DEVICEDAT.MAR.  The Boot Device UCB allocated in DEVICEDAT is 40 
; longwords longer than the nominal UCB (whose length is known symbolically as 
; UCB$K_LENGTH).  This additional length is meant to accomodate all possible 
; idiosyncracies that individual disk drivers might have.  To correct such an 
; overgrowth in UCB size entails enlarging the expansion area in the Boot 
; Device UCB (beyond the 40 longwords) and the relevant correction to the above 
; ASSUME macro.
;


	.SBTTL	Allocate Space for Template UCB

; Allocate zeroed space for template UCB and ORB

	INIT_UCB size=UCB$K_DU_LENGTH



	.SBTTL	Driver Prologue and Dispatch Tables (and UCB Initialization)

	DRIVER_DATA	$$$105_PROLOGUE 
DU$DPT::
DPT::
	DPTAB	-				; Driver Prologue Table
		ADAPTER=NULL,-			; No Adapter
		FLAGS=<DPT$M_SCS -		; SCS must also be loaded,
		      !DPT$M_NOUNLOAD -		; Don't unload this driver,
		      !DPT$M_NO_IDB_DISPATCH>,-	;  and keep the IDB small.
		UCBSIZE=UCB$K_DU_LENGTH,-	; Length of UCB to be created
		MAXUNITS=1,-			; Sysgen insists on one UCB
		BT_ORDER=5000,-			; Load after port and SCS
		STEP=2,-			; Step 2 driver
		SMP=YES,-			; SMP Supported
		NAME=SYS$DUDRIVER		; Driver name
	DPT_STORE INIT				; Control block init values
	DPT_STORE DDB,DDB$L_ACPD,L,<^A\F11\>	; Default ACP name
	DPT_STORE DDB,DDB$L_ACPD+3,B,1		; ACP class


	; The following UCB initialization requests alter the template UCB
	; as well as producing equivalent DPT_STORE entries.  Thus both 
	; structures reflect the required initial UCB state and the UCBs 
	; initially processed by this driver are identical whether they are
	; produced by SYSGEN or by IOC$COPY_UCB.

	INIT_UCB	W_SIZE,WORD,UCB$K_DU_LENGTH
	INIT_UCB	B_TYPE,BYTE,DYN$C_UCB
	INIT_UCB	B_FLCK,BYTE,SPL$C_SCS
	INIT_UCB	L_DEVCHAR,LONG,<<DEV$M_FOD!-
					 DEV$M_DIR!-
					 DEV$M_AVL!-
					 DEV$M_ELG!-
					 DEV$M_SHR!-
					 DEV$M_IDV!-
					 DEV$M_ODV!-
					 DEV$M_RND>>
	INIT_UCB	L_DEVCHAR2,LONG,<<DEV$M_CLU!-
					  DEV$M_MSCP!-
					  DEV$M_NNM>>
	INIT_UCB	B_DEVCLASS,BYTE,DC$_DISK
	INIT_UCB	W_DEVBUFSIZ,WORD,512
	INIT_UCB	W_RWAITCNT,WORD,1
	INIT_UCB	B_DIPL,BYTE,IPL$_SCS
	INIT_UCB	L_STS,LONG,<<UCB$M_NO_ASSIGN!UCB$M_EXFUNC_SUPP!UCB$M_ONLINE>>
	INIT_UCB	L_DEVSTS,LONG, <<UCB$M_NOCNVRT -
				      	!UCB$M_MSCP_INITING -
					!UCB$M_MSCP_WAITBMP>>
	INIT_UCB	L_AFFINITY,LONG,-1

	; Initially the disk is made to look huge.  This prevents comparison 
	; errors and the MSCP server will catch any real problems.  Once the 
	; correct size is known or it has been determined that the size need 
	; not be known immediately, this value will be reduced.
	INIT_UCB	L_MAXBLOCK,LONG,<<^x7F000000>>


	DPT_STORE REINIT		; Control block re-initialization values

	; N.B. Causing the following values to be setup during re-initializa-
	; tion is not significant because this driver cannot be reloaded.  
	; However, were the driver to be reloadable the following values would 
	; need to be re-initialized upon each driver reload.

	DPT_STORE DDB,DDB$L_DDT,D,DU$DDT	; DDT address.

	DPT_STORE END
;
; Driver Dispatch Table
;
;
; The size of the Diagnostic buffer header is different for 32-bit buffer
; addresses and 64-bit buffer addresses.  For the Zeta release the 32-bit is
; used for the Theta release the 64-bit buffer address is used.
;

.IF DEFINED,IRP$Q_QIO_P1 
		DIAG_BUF_SIZE =	-               ; 64-bit DIAGNOSTIC BUFFER SIZE=
			MSCP$K_MXCMDLEN+  -     ; MSCP command +
			MSCP$K_LEN+       -     ; MSCP length +
			20+               -     ; 20 +
			<BUFIO$K_HDRLEN64-BUFIO$K_HDRLEN32>+ -
			DDT$M_DIAGBUF64  	; Indicate 64-bit diag buff.
.IFF
		DIAG_BUF_SIZE = -               ; 32-bit DIAGNOSTIC BUFFER SIZE=
			MSCP$K_MXCMDLEN+  -     ; MSCP command +
			MSCP$K_LEN+	  -     ; MSCP length +
			20		  
.ENDC

	DDTAB	DEVNAM=DU,-			;Driver Dispatch Table
		START=DU$SETUP_IO,-		;Start I/O Operation
		FUNCTB=DU$FUNCTABLE,-		;Function Decision Table
		CANCEL=DUTU$CANCEL,-		;Cancel I/O Entry Point
		DIAGBF=DIAG_BUF_SIZE, -		; Diag Buff Size.
		CTRLINIT=DU$CONTROLLER_INIT,-	; Controller init routine.
		UNITINIT=DUTU$UNITINIT,-	; Unit initialization routine.
		MNTVER=DUTU$MOUNTVER, -		; Mount Verification routine.
		AUX_ROUTINE=DUTU$REVALIDATE, -	; Quorum lost processing.
		FAST_FDT=ACP_STD$FASTIO_BLOCK,-	; Fast-IO FDT routine
		PENDING_IO=DU$PENDING_IO	; Pending I/O Routine


	.SBTTL	DISK CLASS DRIVER FUNCTION DECISION TABLE
;++
;
; DISK CLASS DRIVER FUNCTION DECISION TABLE
;
;--
 
FDT_INI	DU$FUNCTABLE				;Function Decision Table
	FDT_BUF	-			; BUFFERED I/O FUNCTIONS
		<NOP,-			;  No Operation
		 UNLOAD,-		;  Unload (make available + spindown)
		 AVAILABLE,-		;  Available (no spindown)
		 PACKACK,-		;  Pack Acknowledge
		 DSE,-			;  Data Security Erase
		 SENSECHAR,-		;  Sense Characteristics
		 SETCHAR,-		;  Set   Characteristics
		 SENSEMODE,-		;  Sense Mode
		 SETMODE,-		;  Set   Mode
		 ACCESS,-		;  Access file and/or find directory entry
		 ACPCONTROL,-		;  ACP Control Function
		 CREATE,-		;  Create file and/or create directory entry
		 DEACCESS,-		;  Deaccess file
		 DELETE,-		;  Delete file and/or directory entry
		 MODIFY,-		;  Modify file attributes
		 MOUNT, -		;  Mount volume
		 CRESHAD, -		;  Create a shadow set virtual unit
		 REMSHAD, -		;  Remove a shadow set member
;		 DISPLAY, -		;  Display text string
 		 FORMAT>		;  Format media command
.IF DEFINED,IRP$Q_QIO_P1
	FDT_64 -				; 64-BIT I/O FUNCTIONS
		<NOP,-				;  No Operation
		 UNLOAD,-		;  Unload (make available + spindown)
		 AVAILABLE,-		;  Available (no spindown)
		 PACKACK,-		;  Pack Acknowledge
		 READLBLK,-		;  Read LOGICAL Block
		 READPBLK,-		;  Read PHYSICAL Block
		 READVBLK,-		;  Read VIRTUAL Block
		 SENSECHAR,-		;  Sense Characteristics
		 SETCHAR,-		;  Set   Characteristics
		 SENSEMODE,-		;  Sense Mode
		 SETMODE,-		;  Set   Mode
		 REMSHAD,-		;  Remove a shadow set member
		 WRITECHECK,-		;  Write Check
		 WRITELBLK,-		;  Write LOGICAL Block
		 WRITEVBLK>		;  Write VIRTUAL Block
.ENDC
	FDT_ACT	ACP_STD$READBLK,-		; READ FUNCTIONS
		<READLBLK,-		;  Read LOGICAL Block
		 READPBLK,-		;  Read PHYSICAL Block
		 READVBLK>		;  Read VIRTUAL Block
	FDT_ACT	ACP_STD$WRITEBLK,-		; WRITE FUNCTIONS
		<WRITECHECK,-		;  Write Check
		 WRITELBLK,-		;  Write LOGICAL Block
		 WRITEVBLK>		;  Write VIRTUAL Block
	FDT_ACT	ACP_STD$ACCESS,-		;
		<ACCESS,CREATE>		; ACCESS AND CREATE FILE OR DIRECTORY
	FDT_ACT	ACP_STD$DEACCESS,<DEACCESS>; DEACCESS FILE
	FDT_ACT	ACP_STD$MODIFY,-		;
		<ACPCONTROL,-		;  ACP Control Function
		 DELETE,-		;  Delete file or directory entry
		 MODIFY>		;  Modify File Attributes
	FDT_ACT	DU$DSE_FDT,<DSE>	; Data Security Erase
	FDT_ACT	ACP_STD$MOUNT,<MOUNT>	; Mount Volume
	FDT_ACT	EXE_STD$LCLDSKVALID, -	; Functions effecting UCB$V_VALID
		<PACKACK, -		;  Pack Acknowledge
		 AVAILABLE, -		;  Available (no spin-down)
		 UNLOAD>		;  Unload
	FDT_ACT	EXE_STD$ZEROPARM, -	; Zero parameter functions
		<NOP>			;  No Operation
	FDT_ACT	EXE_STD$SENSEMODE,-	;
		<SENSECHAR,-		;  Sense Characteristics
		 SENSEMODE>		;  Sense Mode
	FDT_ACT	EXE_STD$SETCHAR,-		;
		<SETCHAR,-		;  Set Characteristics
		 SETMODE>		;  Set Mode
	FDT_ACT	DU$CRESHAD_FDT, -	;  Create a shadow set virtual unit
		<CRESHAD>
	FDT_ACT	DU$REMSHAD_FDT, -	;  Remove a shadow set member
		<REMSHAD>
	FDT_ACT	EXE_STD$ONEPARM,-		; One parameter functions
		<FORMAT>		;  FORMAT
;	FDT_ACT DUTU$DISPLAY,-		; Display text strings.
;		<DISPLAY>
	FDT_ACT	DU$SETPATH,-		; Specify a preferred path.
		<SETPRFPATH>


	.SBTTL	Static Storage
	.SBTTL	- Data Area Shared With Common Subroutines Module
;++
;
; Data Area Shared With Common Subroutines Module
;
; Functional Description:
;
;	This PSECT contains those constant (link-time) values which would 
;	otherwise be passed as arguments to the disk and tape class driver 
;	common routines in module DUTUSUBS.
;
;--

	.SAVE

	DRIVER_DATA $$$220_DUTU_DATA_01

;base + DUTU$L_CDDB_LISTHEAD			; Location containing the
						; address of the CDDB listhead
	.ADDRESS IOC$GL_DU_CDDB			; for CDDBs belonging to the 
						; disk device type
	.ADDRESS .				; Initialize listhead for LDB
	.ADDRESS .-4				;  structures.

INIT_FKB:		.BLKB	FKB$K_LENGTH
CLASS_DRVR_NAME:	.ASCII	/VMS$DISK_CL_DRVR/
SERVER_NAME:		.ASCII	/MSCP$DISK       /
CONNECT_DATA:		.ASCII	/V5.0            /
DEVICE_NAME::		.ASCII	/DUA/
ACP_NAME::		; Must be 4 bytes as used to init a longword field
                                                                    
			.ASCII  /F11/		; ACP default name
			.ASCII  /B/		; ACP class 

	.RESTORE


	.SBTTL	- Media-id to Device Type Conversion Table
;++
;
; Media-id to Device Type Conversion Table
;
; Functional Description:
;
;	This table is used by DUTU$GET_DEVTYPE to convert a MSCP media 
;	identifier to a VMS device type.
;
;	Entries are made here in order of expected frequency of use.  This
;	speeds lookup for the more common cases.
;
;--
;GH media IDs for X-82U5,6,7,8 already checked in
	MEDIA	<DU>, <RA81>
	MEDIA	<DJ>, <RA60>
	MEDIA	<DU>, <RA82>
	MEDIA	<DU>, <RA80>
	MEDIA	<DU>, <RD54>
	MEDIA	<DU>, <RD53>
	MEDIA	<DU>, <RD52>
	MEDIA	<DU>, <RD51>
	MEDIA	<DU>, <RX50>
	MEDIA	<DU>, <RX33>
	MEDIA	<DU>, <RX23>
	MEDIA	<DU>, <RD33>
	MEDIA	<DU>, <RD32>
	MEDIA	<DU>, <RD31>
	MEDIA	<DU>, <RA70>
	MEDIA	<DU>, <RA90>
	MEDIA	<DU>, <RA92>
	MEDIA	<DI>, <RF72>
	MEDIA	<DI>, <RF71>
	MEDIA	<DI>, <RF31>
	MEDIA	<DI>, <RF30>
	MEDIA	<DU>, <ESE25>
        MEDIA   <DU>, <ESE52>
        MEDIA   <DU>, <ESE56>
        MEDIA   <DU>, <ESE58>
        MEDIA   <ST>, <SST00>, DT$_SSTRIPE
	MEDIA	<DI>, <RFH31>
	MEDIA	<DI>, <RFH72>
	MEDIA	<DI>, <EF51>
	MEDIA	<DI>, <EF52>
	MEDIA	<DI>, <EF53>
	MEDIA	<DI>, <EF54>
	MEDIA	<DI>, <EF58>
	MEDIA	<DK>, <EZ51>
	MEDIA	<DK>, <EZ52>
	MEDIA	<DK>, <EZ53>
	MEDIA	<DK>, <EZ54>
	MEDIA	<DK>, <EZ58>
	MEDIA	<DI>, <RF73>
	MEDIA	<DI>, <RF74>
	MEDIA	<DI>, <RF75>
	MEDIA	<DI>, <RFH73>
	MEDIA	<DU>, <RA72>
	MEDIA	<DU>, <RA71>
	MEDIA	<DU>, <RAH72>, DT$_RA71
	MEDIA	<DU>, <RA73>, DT$_RA73
	MEDIA	<DI>, <RF35>
	MEDIA	<DI>, <RF36>
	MEDIA	<DI>, <RF37>
	MEDIA	<DI>, <RFH35>
	MEDIA	<DI>, <RFF31>
	MEDIA	<DU>, <RX35>
	MEDIA	<DU>, <RX18>
	MEDIA	<DA>, <RC25>
	MEDIA	<DA>, <RCF25>
	MEDIA	<DU>, <RRD40>
	MEDIA	<DU>, <RRD50>
	MEDIA	<DU>, <ESE20>
	MEDIA	<DE>, <SVS00>, DT$_DISK9
	MEDIA	<DU>, <DUX00>, DT$_Generic_DU
	MEDIA	<DU>, <HSX00>
	MEDIA	<DU>, <HSX01>
	MEDIA	<DB>, <RP06>
	MEDIA	<DR>, <RM05>
	MEDIA	<DR>, <RP07>
	MEDIA	<DR>, <RM80>
	MEDIA	<DR>, <RM03>
	MEDIA	<DR>, <RP08>, DT$_RP07HT
	MEDIA	<DB>, <RP05>
	MEDIA	<DB>, <RP04>
	MEDIA	<DM>, <RK07>
	MEDIA	<DM>, <RK06>
	MEDIA	<DL>, <RL02>
	MEDIA	<DL>, <RL01>
	MEDIA	<DY>, <RX04>
	MEDIA	<DY>, <RX02>
	MEDIA	<DX>, <RX01>
	MEDIA	<DD>, <TU58>
	MEDIA	<DQ>, <RB02>
	MEDIA	<DQ>, <RB80>
	MEDIA	<ML>, <ML11>
	MEDIA	<DT>, <TU56>
	MEDIA	<DU>, <RD26>
	MEDIA	<DK>, <RZ22>
	MEDIA	<DK>, <RZ23>
	MEDIA	<DK>, <RZL23>, DT$_RZ23L
	MEDIA	<DK>, <RZ24>
	MEDIA	<DK>, <RZL24>, DT$_RZ24L
	MEDIA	<DK>, <RZ25>
	MEDIA	<DK>, <RZL25>, DT$_RZ25L
	MEDIA	<DK>, <HSZ10>
	MEDIA	<DK>, <RZ26>
	MEDIA	<DK>, <RZ27>
	MEDIA   <DK>, <RZB27>, DT$_RZ27B
	MEDIA	<DK>, <RZL27>, DT$_RZ27L
	MEDIA	<DK>, <RZ28>
	MEDIA   <DK>, <RZB28>, DT$_RZ28B
	MEDIA	<DK>, <RZ29>
	MEDIA   <DK>, <RZB29>, DT$_RZ29B
	MEDIA	<DK>, <RZ31>
	MEDIA	<DK>, <RZL34>, DT$_RZ34L
	MEDIA	<DK>, <RZ35> 
	MEDIA	<DK>, <RZL35>, DT$_RZ35L
	MEDIA	<DK>, <RZ36>
	MEDIA	<DK>, <RZL36>, DT$_RZ36L
        MEDIA   <DK>, <RZ13>
        MEDIA   <DK>, <RZ14>
        MEDIA   <DK>, <RZ15>
        MEDIA   <DK>, <RZ16>
        MEDIA   <DK>, <RZ17>
        MEDIA   <DK>, <RZ18>
        MEDIA   <DK>, <RZ37>
        MEDIA   <DK>, <RZ38>
	MEDIA	<DK>, <RZ55>
	MEDIA	<DK>, <RZL55>, DT$_RZ55L
	MEDIA	<DK>, <RZ56>
	MEDIA	<DK>, <RZL56>, DT$_RZ56L
	MEDIA	<DK>, <RZ57>
	MEDIA	<DK>, <RZL57>, DT$_RZ57L
	MEDIA	<DK>, <RZI57>, DT$_RZ57I
	MEDIA	<DK>, <RZ58>
	MEDIA	<DK>, <RZ59>
	MEDIA	<DK>, <RZ72>
	MEDIA	<DK>, <RZ73>
	MEDIA	<DK>, <RZ74>
	MEDIA	<DK>, <RZ75>
	MEDIA	<DK>, <RWZ01>
	MEDIA	<DK>, <RRD40>, DT$_RRD40S
	MEDIA	<DK>, <RRD42>, DT$_RRD42
	MEDIA	<DK>, <DKX00>, DT$_Generic_DK
	MEDIA	<DK>, <RX23>,  DT$_RX23S
	MEDIA	<DK>, <RX26>,  DT$_RX26
	MEDIA	<DK>, <RX33>,  DT$_RX33S
	MEDIA	<DK>, <RZL26>, DT$_RZ26L
	MEDIA	<DK>, <RRD43>, DT$_RRD43
	MEDIA	<DK>, <RRD44>, DT$_RRD44
	MEDIA 	<DK>, <HSZ15>
        MEDIA 	<DK>, <RZM26>, DT$_RZ26M
	MEDIA 	<DK>, <RW504>
	MEDIA 	<DK>, <RW510>
	MEDIA 	<DK>, <RW514>
	MEDIA 	<DK>, <RW516>
	MEDIA 	<DK>, <RWZ52>
	MEDIA 	<DK>, <RWZ53>
	MEDIA 	<DK>, <RWZ54>
	MEDIA 	<DK>, <RWZ31>
	MEDIA 	<DK>, <EZ31>
	MEDIA 	<DK>, <EZ32>
	MEDIA 	<DK>, <EZ33>
	MEDIA 	<DK>, <EZ34>
	MEDIA 	<DK>, <EZ35>
        MEDIA 	<DK>, <EZL31>, DT$_EZ31L
        MEDIA 	<DK>, <EZL32>, DT$_EZ32L
        MEDIA 	<DK>, <EZL33>, DT$_EZ33L
        MEDIA	<DK>, <HSZ20>
	MEDIA	<DK>, <HSZ40>
	MEDIA	<DK>, <RZB26>, DT$_RZ26B
	MEDIA	<DV>, <RX26>,  DT$_RX26

	.SBTTL	Controller Initialization Routine
;++
;
; MSCP speaking intelligent controller initialization routine. This routine is
; called at IPL$POWER.
;
; Inputs:
; 
;	R4	IDB address  (with System ID of intelligent controller 
;	R5	IDB address	in the CSR field)
;	R6	DDB address
;	R8	CRB address for intelligent controller.
;
; Inputs to Continuation Routine:
;
;	R5	UCB address
;
; Outputs from Continuation Routine:
;
;	None
;	
; Outputs:
;
;	None
;
;--

	DRIVER_CODE

DU$CONTROLLER_INIT::
	$DRIVER_CTRLINIT_ENTRY FETCH=YES
;	.JSB_ENTRY INPUT=   <            R4,R5,R6,R8>,-
;		   OUTPUT=  <>,-
;		   SCRATCH= <>,-
;		   PRESERVE=<>

;
; Check for CDDB already present.  If a CDDB is present, this call results
; from a power failure.  This driver performs power failure recovery as a
; result of virtual circuit closure notification.  No action need be taken
; here.
;
	TSTL	CRB$L_SCS_STRUC(R8)		; Is there a CDDB present?
	BNEQ	30$				; Exit if CDDB is present.

;
; The UCB that is linked onto the DDB list could be the boot device UCB.
; Therefore, make the UCB online so that I/O may be performed to it.  All other
; initialization of the UCB is performed as the result of DPT_STORE entries
; placed in the INIT section of the DPT by the INIT_UCB macro.
;
	MOVL	DDB$L_UCB(R6), R5		; Get first UCB on DDB list
	BISL	#<UCB$M_ONLINE -		; This may be a boot UCB, set
		 !UCB$M_VALID>, UCB$L_STS(R5)	; online and valid just in case.
	MOVL	#1, CRB$L_SCS_STRUC(R8)		; Prevent powerfail entry from
						;  doing anything.

	FORK	ROUTINE=DU$CONTINUE_INIT, -	; Create fork thread.
		CONTINUE=30$			

30$:	MOVZWL	#SS$_NORMAL,R0			;  Always return success.
	RET


	.SBTTL	DU$CONTINUE_INIT - Continue Initialization at IPL SCS
;++
;
;	DU$CONTINUE_INIT - Continue Initialization at IPL SCS
;
; Functional Description:
;
;	This routine is forked to from Controller Initialization
;	routine. If the system disk is a shadow set, this routine stalls
;	waiting for SHDRIVER to complete its controller initialization routine.
;	Then this routine waits for all I/O structures for any remote system
;	disk to be setup before general process polling is enabled. This remote
;	system disk processing is required as there maybe a direct and served
;	path to the system disk and the direct path must be setup first for the
;	I/O database to be setup correctly. A problem can arise if general
;	process polling is immediately enabled as the served path may be seen
;	before the direct path which can result in two UCBs being created for
;	the same disk.
;	
;	The process poller is called to enable DUDRIVER polling for servers on
;	other nodes of the cluster.
;	
; Inputs:
;
;	R5	UCB address
; 	IPL	IPL$SCS
;
;--

DU$CONTINUE_INIT:
	.JSB_ENTRY INPUT=   <               R5>,-
		   OUTPUT=  <                 >,-
		   SCRATCH= <R0,R1,R2,R3,R4,R5>,-
		   PRESERVE=<>

5$:	BLBC	G^EXE$GL_SHADOW_SYS_DISK,20$	; LBC not booting hbs. Continue.
	CMPL	G^SYS$AR_BOOTUCB,-		; Has the system disk pointer 
		G^EXE$GL_SYSUCB			;  been updated?
	BNEQ	20$				; NEQ means updated, so skip wait
	FORK_WAIT				; Wait for a second
	BRB	5$				;  and try again
.                                                         

20$:	CMPL	R5, G^SYS$AR_BOOTUCB		; Is this the boot device?
	BNEQ	25$				; Nope
	MOVL	UCB$L_DDB(R5), R2		; Get the DDB
	MOVL	DDB$L_SB(R2), R2		;  and then the SB
	CMPL	G^SCS$AR_LOCALSB,R2		; Is this this the local SB?
	BEQL	25$				; Yes, so skip remote system disk
						;  processing
;
; Call NEW_SERVER directly to setup I/O structures for the remote system disk.
; Wait until BOOTUCB is linked to a CDDB whose ORGUCB field = 0 (ie. CDDB has
; completed initialization).
;
	MOVAB	SB$B_SYSTEMID(R2), R2		; Point to the system ID
	PUSHL	R5				; Save UCB
	JSB	DUTU$NEW_SERVER			; Create data structures for
						;  the boot device
	POPL	R4				; Restore UCB, move to R4

	MOVAB	INIT_FKB, R5			; Get a fork block (we can't
	MOVW	#FKB$C_LENGTH, FKB$W_SIZE(R5)	;  use the UCB's), and set
	MOVB	#DYN$C_FRK, FKB$B_TYPE(R5)	;  it up so we can FORK_WAIT
	MOVB	#SPL$C_SCS, FKB$B_FLCK(R5)	;

21$:	MOVL	UCB$L_CDDB(R4), R3		; Get CDDB
	BEQL	22$				; Branch if not created yet
	TSTL	CDDB$L_ORIGUCB(R3)		; Found boot UCB yet?
	BEQL	25$				; Yep
22$:	FORK_WAIT				; Wait for a second
	BRB	21$				;  and try again
.                                                         
;
; Enable DUDRIVER process polling for MSCP$DISK servers.
;
25$:	MOVAB	DUTU$NEW_SERVER, R0		; Address of new server routine
	MOVAB	SERVER_NAME, R2			; Address of process name
	JSB	G^SCS$POLL_PROC			; Declare a process to be polled
	BLBS	R0, 27$				; Branch on success
	FORK_WAIT				; Wait for a second
	BRB	25$				;  and try again
.                                                         
;
; The only reason this routine returns failure status is if it was unable to
; allocate a System Poller Process Block (low pool). If successful, registers
; returned are:
;
;	 R0 is odd - enable polling
;	 R1 is address of SPPB
;	 
27$:	CLRL	R2				; All systems, forever
	JSB	G^SCS$POLL_MODE			; Enable polling

;	MOVAB	<DUTU$DATA+DUTU$L_LDB_BLINK>,-	; Initialize LDB listhead
;		<DUTU$DATA+DUTU$L_LDB_FLINK>	; 
;	MOVAB	<DUTU$DATA+DUTU$L_LDB_FLINK>,-	; Initialize LDB listhead
;		<DUTU$DATA+DUTU$L_LDB_BLINK>	; 

30$:	RSB	
	

	.SBTTL	DU$MAKE_CONNECTION
;++
;
;	DU$MAKE_CONNECTION 
;
; Functional Description:
;
;	Internal subroutine, called from DUTU$INIT_LOCAL_STRUCTURES and
;	DU$CONNECT_ERR, that establishes a connection to the MSCP server 
;	in the intelligent controller.
;
; Inputs:
;
;	R2	Completion Routine address
;	R3	CDDB address 
;	R5	permanent CDRP address
;
; Outputs:
;
;	None
;
;--

MAKE_CONNECTION::
           
DU$MAKE_CONNECTION::

	.JSB_ENTRY INPUT=   <      R2,R3,   R5>,-
		   OUTPUT=  <>,-
		   SCRATCH= <R0,R1,R2,R3,R4,R5>

	MOVL	R2, CDDB$L_SAVED_PC(R3)	; Save return address in CDDB
	BISL	#CDDB$M_DISABLED, -	; Set disabled state in CDDB until
		CDDB$L_STATUS(R3)	;  controller is verified good.

	BICL	#CDDB$M_CRNSET,-        ; We need to send another CRN if this
		CDDB$L_STATUS(R3)	; controller understands WLG.
10$:	CONNECT	DU$IDR,-		; Entry point of Input Dispatcher Routine.
		DU$DGDR,-		; Entry point of Datagram Dispatcher.
	    	DU$CONNECT_ERR,-	; Error entry point.
		CDDB$B_SYSTEMID(R3),-	; Destination SYSTEM ID.
		,-			; Remote station address.
		SERVER_NAME,-		; MSCP server name.
		CLASS_DRVR_NAME,-	; Ascii of class driver name.
		#INITIAL_CREDIT,-	;  Needs definition
		#MIN_SEND_CREDIT,-	; Minimum send credit
		#INITIAL_DG_COUNT,-	; Initial DataGram count
		,-			; Block transfer priority
		CONNECT_DATA,-		; Connect data
		(R3),-			; Also pass CDDB address to CDT$L_AUXSTRUC
		,-			; Bad Response packet address
                DU$PATH_MOVE,-		; Path move routine
                #INITIAL_LOAD,-		; Initial load value
		DU$FAST_RECVMSG_REQ,-	; Request Fast Receive Message routine
		DU$FAST_RECVMSG_PM,- 	; Fast Receive Message routine
		DUTU$CHANGE_AFFINITY	; Change affinity routine


	BLBS	R0,30$			; LBS means we now have a working conn

	MOVL	R0, CDRP$L_IOST1(R5)	; Save failure status from connect call.

	MOVL	CDRP$L_UBARSRCE(R5), R3	; Get the CDDB address.
;
; This may be a reconnect attempt in response to a move path request from SCS 
; that has failed. For that case the move path status bit is cleared in the
; CDDB. This permits the failover code called from PACKACK search for an
; alternate path.
;
	BICL	#<CDDB$M_PATHMOVE-	; Clear the bit indicating a move path
		!CDDB$M_PRMBSY>,-	;  request was active and release the
		CDDB$L_STATUS(R3)	;  PRMCDRP.

	MOVL	CDDB$L_CRB(R3), R3	; Get CRB address.
	MOVAB	B^20$,CRB$L_TOUTROUT(R3); Establish place to call,
					;  for periodic wakeups.
	ADDL3	#CONNECT_DELTA,-	; Establish Due time as a little in
		G^EXE$GL_ABSTIM,-	;  the future.
		CRB$L_DUETIME(R3)
	CMPW	#SS$_ABORT, R0		; Is the connect permanently bad?
	BNEQ	15$			; Wait a bit if connect might work.
	MNEGL	#1, CRB$L_DUETIME(R3)	; Else, wait forever...
15$:	RSB				;  and exit this thread.

; Continue at IPL = IPL$_SCS, with fork spinlock held.

20$:	.JSB_ENTRY INPUT=   <         R3      >,-
		   SCRATCH= <R0,R1,R2,R3,R4,R5>

	MOVL	CRB$L_SCS_STRUC(R3),R3	; Get the CDDB address
	.MACRO	ALLOC_PRMCDRP ?start, ?ok, ?exit
.                                         
	ALLOC_PRMCDRP			; Gain exclusive access to the PRMCDRP
	MOVAB	CDDB$A_PRMCDRP(R3),R5	; Get permanent CDRP address.
	BRW	10$			; Loop back and try CONNECT again.
.                                                                   

; A connection has been established

30$:	CLRL	CDRP$L_IOST1(R5)	; Clear any previous connection status.
	MOVL	CDRP$L_UBARSRCE(R5), R1	; Get the CDDB address.
;
; Here we release the PRMCDRP even though we continue to use it for the next
; few messages. If everything goes well, we will be the only thread trying to
; use this CDRP. If something fails, the error thread will take over and break
; the connection and it needs the PRMCDRP to do so. Clearing it's busy bit
; here allows the error thread to run.
;
;GH X-82U2 (already checked, placeholder)
	BICL	#CDDB$M_PRMBSY,-	; Release the PRMCDRP
		CDDB$L_STATUS(R1)

	MOVL	R3, CDDB$L_CDT(R1)	; Save CDT address (in perm CDRP).
	MOVL	R4, CDDB$L_PDT(R1)	; Save PDT address.
	MOVL	R3, CDDB$L_DAPCDT(R1)	; Save CDT address in DAP CDRP too.
	MOVL	R1, R3			; Now that CDT is saved, move CDDB addr.

	MOVL	CDDB$L_CRB(R3), R1	; Get CRB address.
	MNEGL	#1, CRB$L_DUETIME(R1)	; Infinite time till next timeout, now.
	MOVAB	DU$INIT_TIMEOUT, -	; Establish timeout routine that will
		CRB$L_TOUTROUT(R1)	; serve for rest of controller init.

;
; Here a SET CONTROLLER CHARACTERISTICS MSCP Packet is sent to the intelligent
; controller over the connection just established.
;
	.MACRO	ALLOC_RSPID,?L0,?L1,?L2,?L3
.                                    
	ALLOC_RSPID			; ALLOCate a ReSPonse ID.
	.MACRO	ALLOC_MSG_BUF,?L0,?L1,?L2
.                                  
	ALLOC_MSG_BUF			; Allocate an MSCP buffer (and also
					;  allocate a unit of flow control).
	BLBS	R0, 40$			; Branch if successful.
	MOVZWL	#EMB$K_CTLRES_INIT, R0	; Failed to allocate Set error type.
	BSBW	DU$RE_SYNCH		; Break connection, and log the event
	RSB

40$:	CLRL	R1			; First set Controller Characteristics
					;  with zero (i.e. infinite) host timeout.
	BICW	#MSCP$M_MD_CRNPR,-	; Clear the CRN present modifier to 
		MSCP$W_MODIFIER(R2)	; make sure PRP_STCON_MSG does not
					; specify a CRN.
	BSBW	PRP_STCON_MSG		; Call to prepare MSCP command.
;MS????
	MOVL	CDRP$L_UBARSRCE(R5), R1	; Get the CDDB address. 
        MOVL	CDDB$L_PDT(R1),R4	; 
	;First Send MSCP Message
	SEND_MSCP_MSG			; Returns with end-message addr. in R2.


;
; Inputs:
;
;	R0	Status of send operation
;	R1	Size of command packet sent
;	R2	MSCP message buffer address
;	R3	CDDB  address
;	R4	PDT  address
;	R5	CDRP address
;

	.JSB_ENTRY INPUT=  <R0,R1,R2,R3,R4,R5>,-
		   SCRATCH=<R0,R1,R2,R3,R4,R5>

	BSBW	RECORD_STCON		; Record Controller Characteristics.
	BLBC	R0, 45$			; Break the conn if RECORD_STCON aborted
	.MACRO	RECYCH_MSG_BUF,?L2,?L1
.                               
	RECYCH_MSG_BUF			; We recycle the END PACKET and
					;  thereby allocate a new send credit.
	BLBS	R0, 50$			; Branch if send credit allocated.
.                                                                   
	MOVZWL	#EMB$K_CTLRES_INIT, R0	; Set error type code.
	BSBW	DU$RE_SYNCH		; Break connection, and log the event
	RSB

45$:	; A new connection has been formed to a KNOWN MSCP server with an
	; allocation class that is different from the one that was used
	; the last time a connection was established to it. To avoid a 
	; data corruption scenario, this class driver will disconnect, from
	; the new incarnation of the server and never retry.

	MOVL	CDDB$L_CRB(R3),R0	; R0 => CRB.
	MNEGL	#1, CRB$L_DUETIME(R0)	; Else, wait forever...
	MOVL	CDDB$L_PDT(R3),R4	; Get the PDT address.
	MOVAB	CDDB$A_PRMCDRP(R3), R5	; Get permanent CDRP address.
	MOVL	CDDB$L_CDT(R3), R3	; Get the CDT address.
        .MACRO  IDLE_CDRP  	,RWCPTR_UPD=YES,CDRP_CDT_CLEAR=YES,?L1,?L2
                                                                    
	IDLE_CDRP			; Remove the CDRP from its queue
	MOVL	#1, R0			; Do not deallocate map registers
	BSBW	DUTU$DEALLOC_ALL	; Deallocate its RSPID & msg. buf.

	DISCONNECT	#SCS$C_STNORMAL
	RSB

50$:	RECYCL_RSPID			; We also recycle the RSPID.

;
;BEGIN CONTROLLER SPECIFIC SETUPS
;

; if this is the VMS MSCP server, check for minium revision.
; if it is not at the correct revision level for this class
; driver the host timeout is left at zero, and addtionally the CDDB
; is left DISABLED to prevent unit creation of failover attempts.
; (however, the controller timeout routine is left intact, since the
; remote system may reboot with the correct version later).
;
	MOVZBL	CDDB$B_CNTRLMDL(R3),R1	; Get controller model type.
	CMPB	#MSCP$K_CM_EMULA, R1	; Is this the VMS MSCPserver?
	BNEQ	51$			; Branch if not.
	CMPB	#5, CDDB$B_CHVRSN(R3)	; Is this emulator at minimum revision?
	BGTRU	52$			; Branch if not at minimum.

; Determine the correct host timeout interval.  For controllers which have dual
; path capability, this is the larger of HOST_TIMEOUT (30 seconds) and the
; controller timeout interval returned by the just completed Set Controller
; Characteristics.  If the control does not support dual path devices of
; interest, then the host timeout value should be zero (which represents an
; infinite timeout) regardless of the controller timeout value.
;
; The controller already believes the host timeout interval to be infinite, as
; a result of the previous Set Controller Characteristics command. Therefore,
; no further action is needed for that case, and a second SCS message is not
; necessary.  This same technique is used for a VMS MSCPserver which is not at
; the correct revision level for this class driver -- the host timeout is left
; at zero,, and additionally the CDDB is left DISABLED to prevent unit creation
; or failover attempts. (However, the controller timeout routine is left intact,
; since the remote system may reboot with the correct version later).


51$:	BBS	#MSCP$V_CF_MLTHS, -		; If multihost, need to set
		CDDB$W_CNTRLFLGS(R3), 53$	;  a real host timeout.

	CMPB	#MSCP$K_CM_UDA50, R1	; Now check all the possible
	BEQL	53$			;  non-multihost controllers that
	CMPB	#MSCP$K_CM_UDA50A, R1	;  can have dual path devices,
	BEQL	53$			;  and set the same host timeout
	CMPB	#MSCP$K_CM_KDA50, R1	;  for them in case of actual
	BEQL	53$			;  dual path configurations.
	CMPB	#MSCP$K_CM_KDB50, R1	;  All other controllers can stay
	BEQL	53$			;  with the host timeout interval
	CMPB	#MSCP$K_CM_KDM70, R1	;  as infinite.
	BEQL	53$			;
	CMPB	#MSCP$K_CM_KSB50, R1	;
	BEQL	53$			; Finish connection setup if other
	BICL	#CDDB$M_DISABLED, -	; Clear disabled state for controller
		CDDB$L_STATUS(R3)	;  since it meets our criteria.
52$:	JMP	@CDDB$L_SAVED_PC(R3)	; Return to caller.
                                                     
					;  controller type.

53$:	MOVL	#HOST_TIMEOUT, R1	; Assume 30 seconds as value.
	MOVZWL	CDDB$W_CNTRLTMO(R3), R0	; Get controller timeout interval.
	CMPL	R0, R1			; Compare with HOST_TIMEOUT value.
	BLEQU	55$			; Branch if HOST_TIMEOUT is larger.

;
; The host timeout is set to a value equal to the controller timeout, plus 2
; seconds to allow some slack for other operations that are driven by the
; timeout routines that may push the interval out slightly, and to compensate
; for controllers that have a slightly different view of time (notably the
; 'turbo' KDA50). Host timeout is a byte field in the set controller
; characteristics packet, but the MSCP spec states that no controller will ever
; return a host timeout value >255 and will treat any value >255 as 255. 
;
	ADDL3	#10, R0, R1		; Else, use controller timeout plus 10
					;  seconds as host timeout interval.
55$:
	BBC	#1,G^EXE$GL_SHADOWING,- ; Branch if no HBS.
		57$
	BBC	#MSCP$V_CF_WHL,-	; Branch if controller doesn't support
		CDDB$W_CNTRLFLGS(R3),57$;  write history logging.
	IFNOCLSTR 57$			; Skip if we are not in a Cluster
56$:	BISW	#MSCP$M_MD_CRNPR,-	; Set the CRN present modifier
		MSCP$W_MODIFIER(R2)	;					
	BISL	#CDDB$M_CRNSET,-	; Indicate that we have set a CRN.
		CDDB$L_STATUS(R3)

57$:	BSBB	PRP_STCON_MSG		; Must reset controller characteristics.
;MS????
	MOVL	CDRP$L_UBARSRCE(R5), R1	; Get the CDDB address.
	MOVL CDDB$L_PDT(R3),R4		; Get PDT.
	SEND_MSCP_MSG			; Returns with end-message addr. in R2.

;
; Inputs:
;
;	R0	Status of send operation
;	R1	Size of command packet sent
;	R2	MSCP message buffer address
;	R3	CDDB  address
;	R4	PDT  address
;	R5	CDRP address
;

	.JSB_ENTRY INPUT=  <R0,R1,R2,R3,R4,R5>,-
		   SCRATCH=<R0,R1,R2,R3,R4,R5>

	BSBW	RECORD_STCON		; Record Controller Characteristics.
	BLBC	R0, 70$			; Exit if RECORD_STCON was aborted
	RECYCH_MSG_BUF			; Again we recycle the END PACKET and
.                                                                      
					;  thereby allocate a new send credit.
	BLBC	R0, 65$			; Branch if failure.
.                                                     
	RECYCL_RSPID			; We also recycle the RSPID.

	BICL	#CDDB$M_DISABLED, -	; Clear disabled state 
		CDDB$L_STATUS(R3)	;  since it meets our criteria.

	BBC	#1,G^EXE$GL_SHADOWING,- ; Branch if no HBS.
.                                                    
		59$
	BBC	#MSCP$V_CF_WHL,-	; Branch if controller doesn't support
.                                                                       
		CDDB$W_CNTRLFLGS(R3),59$;  write history logging.
	BBS	#CDDB$V_CRNSET,-
.                         
		CDDB$L_STATUS(R3),59$
	IFNOCLSTR 59$			; Skip if we are not in a Cluster
.                                                                  
	ALLOC_PRMCDRP			; Gain exclusive access to the PRMCDRP
.                                                                       
	MOVAB	CDDB$A_PRMCDRP(R3),R5	; Get permanent CDRP address.
	BSBW	STCON_CNID		; Start routine to set CRN
	BRB	59$


; Re-enable DAP processing for all non-emulators in this allocation class on
; the assumption that we may need to detect the presence of another (new)
; non-emulated path to the device.

59$:    SUBL3   #CDDB$L_CDDBLINK, -     ; Get listhead
                 W^<DUTU$DATA -         
                 +DUTU$L_CDDB_LISTHEAD>, R1    

60$:    MOVL    CDDB$L_CDDBLINK(R1), R1 ; Get next CDDB
        BEQL    63$                     ;
        CMPB    #MSCP$K_CM_EMULA, -     ; Ignore Emulators (we don't
                CDDB$B_CNTRLMDL(R1)     ;  send them DAPS)
        BEQL    60$                     ;
        CMPL    CDDB$L_ALLOCLS(R3), -   ; Ignore if not same ALLOCLS
                CDDB$L_ALLOCLS(R1)      ;
        BNEQ    60$                     ;
        MOVL    #DAP_LIMIT, -           ;
                CDDB$L_DAP_LIMIT(R1)        ;
        BRB     60$
        
63$:	JMP	@CDDB$L_SAVED_PC(R3)	; Return to caller.
                                                     

65$:	MOVZWL	#EMB$K_CTLRES_INIT, R0	; Set error type code.
	BSBW	DU$RE_SYNCH		; Break connection, and log the event
	RSB

70$:	; A new connection has been formed to a KNOWN MSCP server with an
	; allocation class that is different from the one that was used
	; the last time a connection was established to it. To avoid a 
	; data corruption scenario, this class driver will disconnect, from
	; the new incarnation of the server and never retry.

	MOVL	CDDB$L_CRB(R3),R0	; R0 => CRB.
	MNEGL	#1, CRB$L_DUETIME(R0)	; Else, wait forever...
	MOVL	CDDB$L_PDT(R3),R4	; Get the PDT address.
	MOVAB	CDDB$A_PRMCDRP(R3), R5	; Get permanent CDRP address.
	MOVL	CDDB$L_CDT(R3), R3	; Get the CDT address.
	IDLE_CDRP			; Remove the CDRP from its queue
.                                                                 
	MOVL	#1, R0			; Do not deallocate map registers
	BSBW	DUTU$DEALLOC_ALL	; Deallocate its RSPID & msg. buf.

	DISCONNECT	#SCS$C_STNORMAL
	RSB


	.SBTTL	DU$INIT_TIMEOUT - Class Driver Init Timeout Routine
;++
;
;	DU$INIT_TIMEOUT - Class driver init timeout routine
;
; Functional Description:
;
;	This routine is specified during controller initialization as the
;	timeout routine that is to be used until that process has completed.
;
; Inputs:
;
;	R3	CRB address
;
; Implicit Output:
; 
;	The connection represented by this CRB is broken (disconnected).
;
;--

DU$INIT_TIMEOUT::			; Controller Init Timeout handler.

	.JSB_ENTRY INPUT=  <         R3      >,-
		   SCRATCH=<R0,R1,R2,   R4,R5>

	MOVL	CRB$L_SCS_STRUC(R3), R3		; Get CDDB address in R3.
	MOVZWL	#EMB$K_CTLRES_INIT, R0		; Set error type code.
	BSBW	DU$RE_SYNCH			; Break connection, log event
	RSB

;++
;
; PRP_STCON_MSG - Prepare a Set Controller Characteristics Command Message.
;
; Inputs:
;
;	R1	Host Timeout Value
;	R2	MSCP buffer to fill
;	R3	CDDB address 
;	R5	CDRP address
;
;--

PRP_STCON_MSG:

	.JSB_ENTRY INPUT=   <   R1,R2,R3,   R5>,-
		   SCRATCH= <R0,R1            >,-
		   PRESERVE=<      R2,R3,R4,R5>


	PUSHL	R1			; Save important register.
	PUSHL	R4			; Save what in here.
	MOVZWL	MSCP$W_MODIFIER(R2),R4  ; Save modifier field.
	INIT_MSCP_MSG			; Initialize buffer for MSCP message.
	POPL	R1			;  modifier
	BBC	#MSCP$V_MD_CRNPR,R4,10$	; Branch if no CRN Present Modifier.
	BISW	#MSCP$M_MD_CRNPR,-	; Turn modifier
                MSCP$W_MODIFIER(R2)     ;   on.
10$:	POPL	R4			; Restore R4.
	MOVB	#MSCP$K_OP_STCON,-	; Insert SET CONTROLLER CHARACTERISTICS
		MSCP$B_OPCODE(R2)	;  opcode with NO modifiers.
	MOVW	CDDB$W_CNTRLFLGS(R3),-	; Set host settable characteristics
		MSCP$W_CNT_FLGS(R2)	;  bits into MSCP command message.
	MOVW	R1,MSCP$W_HST_TMO(R2)	; Set host timeout into MSCP packet.
	.MACRO	READ_SYSTIME DST
.                         
	READ_SYSTIME	MSCP$Q_TIME(R2)	; Transmit time of century in clunks.

	MOVL	CDDB$L_CRB(R3),R0	; R0 => CRB.
	MOVZWL	CDDB$W_CNTRLTMO(R3),-(SP); Pickup controller delta.
	BNEQ	70$			; NEQ implies this controller has been
					;  init'ed at least once before.
	MOVL	#INIT_IMMED_DELTA,(SP)	; Else use compiled in timeout.
70$:
	ADDL3	(SP)+,-			; Establish delta time for time out
		G^EXE$GL_ABSTIM,-	;  to prevent against controller never
		CRB$L_DUETIME(R0)	;  responding.
;MS beg
75$:	BBC	#MSCP$V_MD_CRNPR,-	; Set the CRN present modifier
		MSCP$W_MODIFIER(R2),100$;
	MOVL	G^CLU$GL_CLUB,R0	; Get CLUB.
        MOVL	CLUB$L_LOCAL_CSB(R0),R0 ; Get the CSB
	MOVW	CSB$W_CNID(R0),-	; Get cnid.
		MSCP$W_HRN(R2)		;
	BNEQ	80$			; Branch if not zero.
	MOVW	#-1,MSCP$W_HRN(R2)	; Fold to -1.
80$:	CMPW	#-1,CSB$W_CNID(R0)
	BEQL	900$
100$:	RSB				; Return to caller.
900$:	BUG_CHECK SHADBOOTFAIL, FATAL	; The node having CNID of
					; -1 must reboot. Illegal
					; in MSCP spec for command assists.


;++-- STCON_CNID - Send a Command Assisted Controller a CNID (HRN)
;
; Start a fork thread that waits until this node is a cluster
; member. Get the CNID. Read controller charateristics and
; then write then with the CNID. Convert a CNID to a CRB of -1.
;
; Explicit Inputs:
;
;	R2=MSCP PACKET
;	R3=CDDB
;	R5=CDRP
;
; Explicit outputs.
;
; None.


STCON_CNID::
	.JSB_ENTRY INPUT=  <      R2,R3   ,R5>,-
		   SCRATCH=<>

10$:	MOVL	G^CLU$GL_CLUB,R0		; Get CLUB.
        MOVL	CLUB$L_LOCAL_CSB(R0),R0		; Get the CSB
	BBS	#CSB$V_MEMBER,-			; Branch if a
		CSB$L_STATUS(R0),20$		;  member.
	FORK_WAIT				; Wait a second
	BRB	10$				; Check again.
.                                                       

; When we get here we are a member.

20$:
	MOVL	CDRP$L_MSG_BUF(R5),R2		; Get MSCP Packet.
	MOVZWL	CDDB$W_CNTRLTMO(R3),R0		; Set up Correct timeout.
	ADDL3	#10,R0,R1			; Correct timeout.
	BISW	#MSCP$M_MD_CRNPR,-		; Set the CRN present modifier
		MSCP$W_MODIFIER(R2)		;
	BSBW	PRP_STCON_MSG			; Set up again.
	BISL	#CDDB$M_CRNSET,-		; Indicate that we have set a
		CDDB$L_STATUS(R3)			;  CRN.
	MOVL CDDB$L_PDT(R3),R4			; Get PDT.
	SEND_MSCP_MSG				; Send message to the MSCP server.

	.JSB_ENTRY INPUT=  <R0,R1,R2,R3,R4,R5>,-
		   SCRATCH=<R0,R1,R2,R3,R4,R5>

	RECYCH_MSG_BUF				; We recycle the END PACKET and
.                                                                        
						;  thereby allocate a new send credit.
	BLBC	R0, 99$				; Branch if failure.

	RECYCL_RSPID				; We also recycle the RSPID.


	MOVL	G^CLU$GL_CLUB,R0		; Get CLUB.
        MOVL	CLUB$L_LOCAL_CSB(R0),R0		; Get the CSB
	MOVZWL	CSB$W_CNID(R0),-		; Use DSDRIVER field
		CDDB$L_CPYSEQNUM(R3)		;  to store CNID if DUDRIVER.
	BICL	#CDDB$M_PRMBSY,-		; Release the PRMCDRP
		CDDB$L_STATUS(R3)		; 
	RSB

99$:	MOVZWL	#EMB$K_CTLRES_INIT, R0		; Set error type code.
	BICL	#CDDB$M_CRNSET,-		; Show this thread
		CDDB$L_STATUS(R3)			;  failed.
	BICL	#CDDB$M_PRMBSY,-		; Release the PRMCDRP
		CDDB$L_STATUS(R3)		; 
	BRW	DU$RE_SYNCH			; Failure here means		we
.                                                                           
						;  must re-CONNECT.


;MS end 


	.SBTTL	RECORD_STCON - Record data from a SCC end message.
;++
;
; RECORD_STCON - Record data from a Set Controller Characteristics end message
;		in the CDDB.
;
; Inputs:
;
;	R2	MSCP End Message
;	R3	CDDB
;
; Outputs:
;
;	R0,R1 are clobbered.
;
;--

RECORD_STCON:

	.JSB_ENTRY INPUT=  <      R2,R3      >,-
		   SCRATCH=<   R1>,-
		   OUTPUT= <R0>

	MOVZWL	<CDDB$A_PRMCDRP -	; Get STCON end message size.
		+CDRP$W_ENDMSGSIZ>(R3), R1
	BSBW	DUTU$FILL_MSCP_MSG	; Zero fill STCON end message.
	MOVW	MSCP$W_CNT_FLGS(R2),-	; Pickup NON-host settable characteristics
		CDDB$W_CNTRLFLGS(R3)	;  from END PACKET and save in CDDB.
     	BICW2	#<1@MSCP$V_CF_RDCD>,CDDB$W_CNTRLFLGS(R3) ; Explicit clear of remote DCD
	MOVW	MSCP$W_CNT_TMO(R2),-	; Likewise with controller timeout.
		CDDB$W_CNTRLTMO(R3)
	BNEQ	10$			; Check for infinite (zero) value.
	DECB	CDDB$W_CNTRLTMO(R3)	; Disallow zero, set to 255 seconds.

10$:	MOVQ	MSCP$Q_CNT_ID(R2),-	; Also save controller unique ID.
		CDDB$Q_CNTRLID(R3)
.                           

	ASSUME	MSCP$B_CNT_HVR EQ MSCP$B_CNT_SVR+1
	ASSUME	CDDB$B_CHVRSN  EQ CDDB$B_CSVRSN+1

	MOVW	MSCP$B_CNT_SVR(R2),-	; Save controller hardware and 
		CDDB$B_CSVRSN(R3)	; software version.

; The following code will determine if the controller is an RQDX3 with
; earlier than 3.10 firmware. If it is, a value of 15 seconds will be 
; used as the timeout value. This is to fix a potential cluster hang.

	CMPB	CDDB$B_CNTRLMDL(R3), -	; Is this an RQDX3 ?
		#MSCP$K_CM_RQDX3	;
	BNEQ	15$			; If NEQ no
	CMPB	CDDB$B_CSVRSN(R3), #2	; Microcode rev. level
					;  less than 3.10?
	BGTRU	15$			; If GTR no
	MOVW	#15,-			; Use 15 second timeout for RQDX3
		CDDB$W_CNTRLTMO(R3)	;  with old firmware

15$:
;
; Check for controller type in order to perform version checks for RF73 and
; RF35 controllers. If they are older than the specified version, disable
; Write History Logging by clearing the flag in the CDDB. The Packack routines
; will test for this flag before setting the unit write logging flag.
;
	MOVZBL	CDDB$B_CNTRLMDL(R3),R1	; Get controller model type.
	CMPB	#MSCP$K_CM_RF73,R1
	BEQL	40$
	CMPB	#MSCP$K_CM_RF35,R1
	BNEQ	50$

40$:	CMPB	CDDB$B_CSVRSN(R3),#87	; Check for minimum controller 
	BGEQ	50$			;  software version
	BICW	#MSCP$M_CF_WHL,-	; If below minimum rev, clear
		CDDB$W_CNTRLFLGS(R3)	;  WHL support flag



50$:	MOVL	MSCP$L_MAXBCNT(R2), -	; Store controller max byte count
		CDDB$L_MAXBCNT(R3)	; in CDDB.
	BNEQ	75$			; Max byte count supplied?
					;  Yes - use it
	CMPB	#MSCP$K_CM_EMULA,R1	; VMS MSCP Emulator?
	BEQL	60$			;  Yes - supply appropriate default
	ASHL	#24, #1, -		;  No  - use 2**24 as max byte count
		CDDB$L_MAXBCNT(R3)
	BRB	75$			; Done setting default
60$:	MOVL	#<127*MMG$C_VAX_PAGE_SIZE>, -
		CDDB$L_MAXBCNT(R3)	;  Use default of 127 VAX pages

75$:	BBSS	#CDDB$V_ALCLS_SET, -	; Branch if allocation class already
		CDDB$L_STATUS(R3), 90$	; set, and indicate it is now set.

	; The allocation class is about to be set for this device.  The object
	; is to give every reasonable chance for the value to be non-zero.

	MOVL	G^CLU$GL_ALLOCLS, -	; Assume a local, single host 
		CDDB$L_ALLOCLS(R3)	; controller.
	BBC	#MSCP$V_CF_MLTHS, -	; Branch if a single host controller.
		CDDB$W_CNTRLFLGS(R3), -
		80$
	MOVZBL	MSCP$B_CNT_ALCS(R2), -	; Get set controller characteristics
		CDDB$L_ALLOCLS(R3)	; allocation class.
80$:	MOVAB	<CDDB$L_DDB -		; Init loop through all DDBs.
		-DDB$L_CONLINK>(R3), R0
82$:	MOVL	DDB$L_CONLINK(R0), R0	; Link to next DDB.
	BEQL	100$			; Branch if no more DDBs.
	MOVL	CDDB$L_ALLOCLS(R3), -	; Copy allocation class to this
		DDB$L_ALLOCLS(R0)	; DDB.
	BRB	82$			; Loop till no more DDBs.

90$:	BBC	#MSCP$V_CF_MLTHS, -	; Branch if a single host controller.
		CDDB$W_CNTRLFLGS(R3), -	; 
		100$
	MOVZBL	MSCP$B_CNT_ALCS(R2), R1	; Get set controller characteristics
	CMPL	R1, CDDB$L_ALLOCLS(R3)	; allocation class.
	BEQL	100$			; Reconnect with same allo class

	MOVL	#SS$_ABORT, R0		; Return status of ABORT
	RSB

100$:	MOVL	#SS$_NORMAL, R0		; Return status of success
	RSB



	.SBTTL	DU$DSE_FDT - Data Security Erase FDT Routine
;++
;
; DU$DSE_FDT - Data Security Erase FDT Routine
;
; Functional Description:
;
;	This is the FDT routine for the Data Security Erase operation 
;	supported by MSCP speaking devices.  The byte count (P2) is stored in 
;	IRP$L_BCNT.  The starting logical block (P3) is stored in IRP$L_MEDIA.
;	Control is transfered to EXE$QIODRVPKT, thus queueing the I/O request
;	to the driver's start I/O routine.
;
; Inputs:
;
;	R3		IRP address
;	IRP$L_QIO_P2	byte count
;	IRP$L_QIO_P3	starting logical block
;
; Outputs:
;
;	IRP$L_BCNT(R3)  <== IRP$L_QIO_P2 <byte count>
;	IRP$L_MEDIA(R3) <== IRP$L_QIO_P3 <starting logical block>
;
; Side effects:
;
;	Control is transfered to EXE$QIODRVPKT, thus queueing the I/O request
;	to the driver's start I/O routine.
;
;--

DU$DSE_FDT:
	$DRIVER_FDT_ENTRY FETCH=YES 
;	.JSB_ENTRY INPUT= <         R3,   R5>,-
;		   OUTPUT=<         R3,   R5>

	MOVL	IRP$L_QIO_P2(R3),IRP$L_BCNT(R3)	; Setup erase byte count.
	MOVL	IRP$L_QIO_P3(R3),IRP$L_MEDIA(R3); Setup erase starting LBN
	CALL_QIODRVPKT


	.SBTTL	DU$SETPATH - FDT routine to specify a preferred access path
;++
;
; DU$SETPATH - Set preferred path FDT routine
;
; Functional Description:
;
;	This routine takes as its inputs a UCB adress and an ASCII string 
;	containing an SCS node name. If a CDDB is found for the specified 
;	node, it's address is loaded into the UCB preferred path field for 
;	later use by DUTU$LOCATE_UNIT.
;
; Inputs:
;
;	R3		address of IRP
;	R5		address of UCB
;	IRP$L_QIO_P1	address of P1 (counted ASCII string)
;
; Outputs:
;
;	R0	completion status
;
;	R0-R2, R9-R11 may be changed
;
;--

DU$SETPATH::
	$DRIVER_FDT_ENTRY FETCH=YES
;	.JSB_ENTRY INPUT=  <         R3,   R5>,-
;		   OUTPUT= <R0,      R3,R4,R5>,-
;		   SCRATCH=<R0,R1,R2,         R9,R10,R11>

	BBS	#IO$V_FORCEPATH,-		; Branch if FORCEPATH to trigger
		IRP$L_FUNC(R3),70$		;  failover now.
	BICL3	#IO$M_FCODE,IRP$L_FUNC(R3),R0	; Get all modifier bits
	BNEQ	93$				; Branch if any other modifier bit set
	MOVL	IRP$L_QIO_P1(R3),R11		; Get address of descriptor
;GH X-88 (excluding CBS related fixes)
	IFNORD	#1,(R11),90$			; Test access of length byte
	MOVZBL	(R11),R0			; Get name length
	IFNORD	R0,1(R11),90$			; Test access of name string
	PUSHR	#^M<R3,R4>			; Save valuable registers
	MOVZBL	(R11),R4			; Pick up name length
	MOVAL	G^SCS$GQ_CONFIG,R10		; Get head of SB queue.
	MOVL	R10,R9				; And preload the pointer.
	ASSUME SB$L_FLINK EQ 0
10$:	MOVL	SB$L_FLINK(R9),R9		; Get the next SB.
	CMPL	R10,R9				; If we are at the end of the
	BEQL	91$				;  queue, exit with error.
	CMPB	R4,SB$T_NODENAME(R9)		; Do the nodename lengths match?
	BNEQ	10$				; No. Go find another SB
	CMPC3	R4,1(R11),SB$T_NODENAME+1(R9)	; Do the names match?
	BNEQ	10$				; No.

;
; We found a match for the nodename. Since the local controller, if any,
; would be the default preferred path, clear thepreferred path cell if
; the local node wasspecified. Otherwise, proceed to look for a CDDB for
; the specified node.
;
	POPR	#^M<R3,R4>			; Found a match.
;GH X-82U3
	CMPL	R9,G^SCS$AR_LOCALSB		; Is this the local system?
	BNEQ	30$				; No, go find a CDDB.
	CLRL	R9				; For the local system, clear
	BRB	50$				;  the preferred path.
;GH end
30$:	MOVL	SB$B_SYSTEMID(R9),R0		; Remember the system id.
	MOVZWL	SB$B_SYSTEMID+4(R9),R1

	MOVL	G^IOC$GL_DU_CDDB,R9		; Get the CDDB list
	BEQL	92$				; Just in case there aren't any.
40$:	CMPL	CDDB$B_SYSTEMID(R9),R0		; Compare the system ids.
	BNEQ	48$
	CMPW	CDDB$B_SYSTEMID+4(R9),R1
	BEQL	50$				; We have a match.
48$:	MOVL	CDDB$L_CDDBLINK(R9),R9		; Get another CDDB.
	BEQL	92$				; None left. Exit with error.
	BRB	40$				; Go back and check again.

50$:	MOVL	R9,UCB$L_PREF_CDDB(R5)		; Store the address of this
	MOVZWL	#SS$_NORMAL,R0			;  CDDB and complete normally.
	CALL_FINISHIOC

;
; We arrive here if the forcepath modifier is set. Assume that a preferred
; path has been defined if needed and send the packet on to start_io.
;
70$:
	CALL_QIODRVPKT	

90$:	MOVZWL	#SS$_ACCVIO,R0			; Could not access argument
	BRB	99$
91$:	POPR	#^M<R3,R4>
	MOVZWL	#SS$_NOSUCHNODE,R0		; Could not find SB or CDDB.
	BRB	99$
92$:	MOVZWL	#SS$_CTRLERR,R0
	BRB	99$
93$:	MOVL	#SS$_ILLIOFUNC,R0       	; Return illegal function error

99$:	
	CALL_ABORTIO



	.SBTTL	CRESHAD/REMSHAD FDT Routines
;++
;
; Functional Description:
;
;	The following are the FDT routines used by volume shadowing.
;
; Inputs:
;
;	R3	IRP address
;	R5	UCB address
;
; Implicit Inputs:
;
;	IRP$L_FUNC(R3)		I/O function code and modifiers
;	UCB$W_MSCPUNIT(R5)	MSCP$V_SHADOW set for shadow set virtual units
;
; Outputs:
;
;	All registers preserved.
;
;--

DU$CRESHAD_FDT::				; ----> IO$_CRESHAD
DU$REMSHAD_FDT::				; ----> IO$_REMSHAD
                                                             

        $DRIVER_FDT_ENTRY FETCH=YES

        $FDTARGDEF
        $OFFDEF FDT$ARG,<IRP,PCB,UCB,CCB>
        MOVL    G^EXE$GL_HBS_PTR,R0     ; Shadow dispatcher
        BGEQ    10$                     ; Illegal if not filled in
        PUSHL   R6                      ; Push CCB address,
        PUSHL   R5                      ; Push UCB address,
        PUSHL   R4                      ; Push PCB address,
        PUSHL   R3                      ; Push IRP address.
        CALLS   #4,(R0)                 ; Jump to shadow dispatcher
        RET                             ; return to caller.
10$:    MOVZBL  #SS$_ILLIOFUNC,R0       ; Set error status
        CALL_FINISHIOC DO_RET=YES       ; Complete I/O request


	.SBTTL	START I/O
	.SBTTL	- Out of main line code
;++
;
; Handle device which is in mount verification.
;
; Inputs:
;
;	R3	UCB address
;	R5	CDRP address
;
;--

START_MOUNT_VER:

	; In most cases, mount verification guarantees that the volume is 
	; valid before presenting requests to the drivers.  The single 
	; exception is IO$_CRESHAD for which the state of UCB$V_VALID must 
	; correctly represent the online state of the unit with respect this 
	; host.  UCB$V_VALID being clear is cause for immediately terminating 
	; the request with SS$_VOLINV (unless the request is an IO$_CRESHAD).  
	; Presumably, mount verification will pickup the pieces and retry the 
	; mount verification operation from the top.

	CMPL	#IO$_CRESHAD, -		; Is this an IO$_CRESHAD function?
		CDRP$L_FUNC(R5)
	BEQL	5$			; Skip VALID test for IO$_CRESHAD.
	BBS	#UCB$V_VALID, -		; In mount verification, valid bit
		UCB$L_STS(R3), 5$	; should always be set.
	MOVZWL	#SS$_VOLINV,R0		; Indicate error status.
	CLRL	R1			; Clear second word of I/O status.
	BSBW	FUNCTION_EXIT		; GOTO common exit.
	RSB

5$:	; To avoid unnecessary hangs, the conditions which would prevent the 
	; IRP from being processed are tested.  If any such condition exists, 
	; an attempt will be made to failover non-shadow-set devices to their 
	; alternate path.  If that succeeds, the request will be processed on 
	; the alternate path.  If the failover fails, the IRP will be 
	; immediately completed with a SS$_DEVOFFLINE status.  For shadow 
	; sets and their members, the absense of a connection to a working 
	; controller will cause control to be passed to DU$MVIRP_NOCONN.

	CLRL	CDRP$L_RWCPTR(R5)	; Clear wait count pointer.

	MOVL	UCB$L_CDDB(R3), R0	; Get CDDB address.

; If the CDDB status indicates that a path move operation is under way, in
; response to a request from SCS, we complete this mount verification request
; now with a devoffline status. This will cause mount verification to wait
; for a time and retry, by which time the move path will have either
; completed or failed.

	BBS	#CDDB$V_PATHMOVE, -	; If an SCS requested path move is in
		CDDB$L_STATUS(R0), 80$	; progress, complete this mv request
					; now with devoffline. 

; Do we have a valid connection? If not, we can safely trigger failover here
; unless this is a shadow set virtual unit.

	BBS	#CDDB$V_NOCONN, -		; Is there a connection?
		CDDB$L_STATUS(R0), 90$		; Yes, we have no connection.

; We are here if we have a connection. Compare our idea of RWAITCNT with
; reality. If it matches, we are stalled for reasons under control of the
; class driver so we can call RESTART_IO and queue the request. If path
; failover is required, it will be correctly handled in the packack routines.
; If RWAITCNT does not match, we are stalled by SCS and we will return this
; IRP back to MOUNTVER for it to retry later.

	EXTZV	#UCB$V_MSCP_MNTVERIP, -	; Get mount verification in progress
		#1, UCB$L_DEVSTS(R3), R0; bit.
	BBC	#UCB$V_MSCP_PKACK, -	; Is packack in progress?
		UCB$L_DEVSTS(R3), 8$	; If yes, then
	INCW	R0			; increment expected wait count.
8$:	BBC	#UCB$V_MSCP_WAITBMP, -	; Is wait count bumped?
		UCB$L_DEVSTS(R3), 10$	; Branch if wait count not bumped.
	INCW	R0			; Increment expected wait count.
10$:
	CMPW	R0, UCB$W_RWAITCNT(R3)	; Is wait count as expected?
	BNEQ	20$			; If yes, restart this I/O request
	BSBW	SEND_IO			; Send the I/O again.
	RSB

20$:	MOVZWL	#SS$_DEVOFFLINE, R0	; Else, change status to SS$_DEVOFFLINE.
	CLRL	R1			; Init second IOSB longword.

	ALT_REQCOM 		   ; Complete mount ver. request now.
				   ; Mount ver will retry later

80$:	ASSUME	UCB$V_CLUTRAN GE 16	; We are going to send this back to
;GH fix register reference (X-82U1)
	BISL	#UCB$M_CLUTRAN, -	;  MV because the path move connect has
		UCB$L_STS(R3)		;  not completed. Set the clutran bit
					;  to request minimal MV and to support
					;  foreign disks.
	MOVZWL	#SS$_DEVOFFLINE, R0	; Change status to SS$_DEVOFFLINE.
	CLRL	R1			; Init second IOSB longword.


	ALT_REQCOM                      ; Complete mount ver. request now.

; There is no connection. 

; Unless this is a shadow set virtual unit, connection walking is in order.
; Since there is no conection, no I/O requests that have already been given 
; to the port need to be cleaned up. The connection error routine takes care
; of this.

90$:	MOVL	UCB$L_CDT(R3), -	; Copy current CDT address into CDRP.
		CDRP$L_CDT(R5)
	MOVL	UCB$L_PDT(R3), R4	; Get current PDT address.
	MOVAB	MV_AFTER_LOCATE_UNIT,R2	; Set up the return address
	BSBW	DUTU$LOCATE_UNIT	; Find the failed unit.
	RSB

;
;	Upon completion, the UCB and CDRP input to DUTU_LOACTE_UNIT have 
;	the same connection relationships they had when the call was made.
;	In particular, the following registers and fields are unchanged:
;
;		R4 (PDT address)
;		UCB$L_PDT
;		UCB$L_CDT
;		CDRP$L_CDT
;
;	If the status returned by DUTU$LOCATE_UNIT is SS$_NORMAL, the 
;	(possibly) new MSCP unit number (for MSCP served devices) is now 
;	in CDRP$W_ENDMSGSIZ(R5) for use in subsequent failover processing.
;
;	Any SCS resources held by the CDRP on input to DUTU$LOCATE_UNIT 
;	are now deallocated.
;
; Inputs:
;
;	R0	Status
;		   SS$_NORMAL means unit was located
;		   SS$_MEDOFL means unit was not located
;		   SS$_ABORT  means the request has been canceled
;	R2	CDDB address of connection on which unit was located (or zero)
;	R3	UCB address
;	R4	PDT address (same as input)
;	R5	CDRP address
;

MV_AFTER_LOCATE_UNIT:

	.JSB_ENTRY INPUT=   <R0,   R2,R3,R4,R5>,-
		   OUTPUT=  <>,-
		   SCRATCH= <R0,R1,R2,R3,R4,R5>,-
		   PRESERVE=<>

	BLBC	R0, 95$			; Branch if attempt succeeded
	BSBW	DUTU$MOVE_UNIT		; Move unit to place just located.
	BLBS	R0, 150$		; Branch if move succeded.

95$:	MOVZWL	#SS$_DEVOFFLINE, R0	; Else, change status to SS$_DEVOFFLINE.
	CLRL	R1			; Init second IOSB longword.
	ALT_REQCOM                      ; Complete mount ver. request now.

150$:	BSBW	SEND_IO			; Allow function to proceed.
	RSB


	.SBTTL	DU$PENDING_IO - Init and queue MSCP I/O
;++
; DU$PENDING_IO - Init and queue MSCP I/O
;
; Functional Description:
;
; This routine is invoked when the I/O Exec wishes an I/O to be placed
; on the UCB$L_IOQFL queue. The I/O Exec is responsible for trying to 
; initiate the I/O on the port CPU sometime later. 
;
; Initial CDRP initialization is performed and the busy bit is cleared.
; Then the CDRP is either queued to the UCB$L_IOQFL queue or, not queued
; but instead posted with an error. The return status reflects the
; disposition of the I/O.
;
; Inputs:
;	R1		Queue List Head address
;	R3		IRP address
;	IRP$L_UCB(IRP)	UCB address
;
; Outputs:
;       None
;	All registers preserved except R0,R1
;
; RETURN VALUE:
;       R0 = -1 If IRP was not queued (ie. posted back with error instead)
;          =  0 If IRP was placed on empty queue
;          =  1 If other IRPs were already in the queue
;
;--
FLINK = 0
BLINK = 4

DU$PENDING_IO:
	$DRIVER_PENDING_IO_ENTRY PRESERVE=<R2,R3,R4,R5>, FETCH=YES
;
; Init CDRP fields
;
	; DUDRIVER CDRP initialization at the beginning of this routine must
	; be kept synchronized with the DUDRIVER Start I/O routine, DU$SETUP_IO.

	MOVL	IRP$L_UCB(R3),R5	; Get the UCB
	BICL	#UCB$M_BSY,UCB$L_STS(R5) ; Undo bit setting so that multiple
					; IRP's can be started.
	MOVAB	IRP$L_FQFL(R3), R0	; Get address of CDRP portion of IRP.
	MOVL	R5, R3			; Move UCB address.
	MOVL	R0, R5			; Move CDRP address.
	ASSUME  CDRP$B_CD_TYPE EQ CDRP$W_CDRPSIZE+2
	ASSUME  CDRP$B_FLCK    EQ CDRP$W_CDRPSIZE+3
	MOVL	#< <SPL$C_SCS@24> -	; Initialize CDRP size, type and fork
		! <DYN$C_CDRP@16> -	; IPL fields.
		! <CDRP$L_IOQFL&^xFFFF> >, -
		CDRP$W_CDRPSIZE(R5)
	ASSUME	CDRP$W_DUTUCNTR  EQ <CDRP$L_DUTUFLAGS+4>
	ASSUME	CDRP$W_ENDMSGSIZ EQ <CDRP$L_DUTUFLAGS+6>
	CLRQ	CDRP$L_DUTUFLAGS(R5)	; Clear flags, counter, & msg. size.
	MOVAB	UCB$W_RWAITCNT(R3), -	; Point CDRP field to wait counter
.                                                                   
		CDRP$L_RWCPTR(R5)	; in the UCB.
	SCS_INIT_CDRP			; Init SCS state and wait state.
	CLRL	CDRP$L_MSG_BUF(R5)	; Prevent spurious DEALLOC_MSG_BUF calls
	CLRL	CDRP$L_RSPID(R5)	; Prevent spurious DEALLOC_RSPID calls
	CLRL	CDRP$L_LBUFH_AD(R5)	; Prevent spurious UNMAP calls.
;
; Now decide how to dispose of the IRP.
; If RWAITCNT is non-zero then dispatch to the IO_STALLED routine where the
; IRP is either stalled on the UCB$L_IOQFL queue or posted back with an error.
; Otherwise just queue IRP to the end of the UCB$L_IOQFL queue.
;
	TSTW	UCB$W_RWAITCNT(R3)	; Are "normal" I/O requests stalled?
	.BRANCH_LIKELY
	BEQL	40$			; Branch if no
	PUSHR	#^M<R1,R5>		; Save registers.
	BSBW	IO_STALLED		; Call routine to decide if I/O should stall
	POPR	#^M<R1,R5>		; Restore registers.
	MOVAL	CDRP$L_IOQFL(R5),R5	; Get IRP address of CDRP
	CMPL	BLINK(R1),R5		; Was IRP queued to tail of queue?
	BEQL	45$			; Yes, go select correct return status
	MOVL	#-1,R0			; IRP not queued, Return R0 = -1
	RET				; Return to caller.
;
; Now queue IRP to the UCB$L_IOQFL queue.
;
40$:	INSERT_EL -			; Queue IRP to tail of UCB IOQ
		EL	= CDRP$L_IOQFL(R5),-;
		QUE	= @BLINK(R1),-	;
		SCRATCH = R0		;
45$:	CMPL	FLINK(R1),BLINK(R1)	; After insert, is only one entry on Q?
	BNEQ	50$			; Branch if >1
	CLRL	R0			; QUEUE WAS EMPTY, RETURN R0 = 0
	RET				; Return to caller.
50$:	MOVL	#1,R0			; QUEUE WAS NOT EMPTY, RETURN R0 = 1
	RET				; Return to caller.

                                                                                                                

	.SBTTL	IO_STALLED - Handle device with I/O requests stalled.
;++
;
;	IO_STALLED - Handle device with I/O requests stalled.
;
; Functional Description:
;
;	For any one of several reasons requests on this UCB are stalled. If 
;	this is a mount verification, presumably mount verification is in 
;	progress which would stall this UCB.  So go process the mount 
;	verfication IRP.
;
;	Otherwise, determine whether or not there is any hope that this IRP 
;	will work when the UCB becomes unstalled.  If mount verification is in 
;	progress, the volume is still valid, or the request if a physical 
;	function, then assume there is hope.  If the function is IO$_UNLOAD or 
;	IO$_AVAILABLE and there is no connection to the remote server, perform 
;	the request-specific completion processing anyway.  This ensures that 
;	driver-specific cleanup is always performed for these two functions, 
;	which is important because they are often issued only once. If 
;	function is IO$_PACKACK, shadowing is not involved, and unit can be 
;	failed over to a working controller, then do failover and continue 
;	with IO$_PACKACK.  Otherwise, terminate the request now with SS$_VOLINV.
;
; Inputs:
;
;	R3	UCB address
;	R5	CDRP address
;
;--

IO_STALLED:

	.JSB_ENTRY INPUT=   <         R3,   R5>,-
		   OUTPUT=  <>,-
		   SCRATCH= <R0,R1,R2,R3,R4,R5>,-
		   PRESERVE=<>

	.BRANCH_LIKELY
	BBC	#IRP$V_MVIRP, -		; Is this a mount verification IRP?
		CDRP$L_STS(R5), 5$	; Skip if this is NOT a mount ver IRP.
	BRW	START_MOUNT_VER		; Branch if mount verification IRP

5$:	CDRP$L_STS2 = CDRP$L_IOQFL + IRP$L_STS2
	.BRANCH_LIKELY
	BBC	#IRP$V_SHDIO,-		; If this I/O is from the HBS driver,
		CDRP$L_STS2(R5),-	; check for a connection. Note that 
		10$			; HBS will set the MV bit when 
					; rebuilding the VU.
	MOVL	UCB$L_CDDB(R3), R0	; Get CDDB address.
	BITW	#<CDDB$M_RECONNECT -    ; If either is set
		!CDDB$M_NOCONN>, -	;  reject IO.
		CDDB$L_STATUS(R0)
	BEQL	10$			; Continue.
	MOVZWL	#SS$_DEVOFFLINE,R0	; Return this I/O to the shadowing code
	CLRL	R1			; with device offline. 
	ALT_REQCOM 

10$:	BBS	#UCB$V_MSCP_MNTVERIP, -	; Is mount verification in progress?
		UCB$L_DEVSTS(R3), 20$	; Branch if mount ver is in progress.
	.BRANCH_LIKELY
	BBS	#UCB$V_VALID, -		; Is the volume still valid?
		UCB$L_STS(R3), 20$	; Branch if volume is valid.
	BBS	#UCB$V_MSCP_PKACK, -	; Branch if a PACKACK is in progress.
		UCB$L_DEVSTS(R3), 20$	; 
	BBC	#IRP$V_PHYSIO, -	; Volume not valid:  branch if this 
		CDRP$L_STS(R5), 60$	; request is not a physical function.
	MOVL	UCB$L_CDDB(R3), R0	; Get CDDB address.
	BBS	#CDDB$V_PATHMOVE, -	; Is an SCS requested path move in
		CDDB$L_STATUS(R0), 20$	; progress? Branch if so.
	BBS	#CDDB$V_NOCONN, -	; Is there a connection?
		CDDB$L_STATUS(R0), 30$	; Branch if NO connection present.
20$:
	MOVAL	CDRP$L_IOQFL(R5),R1	; Get IRP address of CDRP
	INSERT_EL -			; Queue IRP   
		EL 	= (R1),-        
		QUE 	= @UCB$L_IOQBL(R3),- 
		SCRATCH = R0            ; Then discontinue processing on this  
	RSB				; request.
					
30$:	EXTZV	#IO$V_FCODE,#IO$S_FCODE,-; Extract I/O function code.
	    	CDRP$L_FUNC(R5), R0
	ASSUME	IO$S_FCODE LT 8
	CMPB	#IO$_AVAILABLE, R0	; IO$_AVAILABLE function?
	BEQL	40$			; Branch if IO$_AVAILABLE function.
	CMPB	#IO$_UNLOAD, R0		; IO$_UNLOAD function?
	BNEQ	50$			; Branch if IO$_UNLOAD function.
40$:	MOVZBL	#SS$_DEVOFFLINE, R0	; Complete AVAILABLE or UNLOAD with
	BICL	#UCB$M_MSCP_WRTP,-	;  and clear class driver write 
		UCB$L_DEVSTS(R3)	;  protect flag.
	BRB	65$			

50$:	CMPB	#IO$_PACKACK, R0	; IO$_PACKACK function?
	BNEQ	60$			; Branch if not IO$_PACKACK.
	EXTZV	#UCB$V_MSCP_WAITBMP, -	; Get wait count bumped bit.
		#1, UCB$L_DEVSTS(R3), R0
	CMPW	R0, UCB$W_RWAITCNT(R3)	; Is wait count as expected?
	BEQL	70$			; Branch if not as expected.
60$:	MOVZWL	#SS$_VOLINV,R0		; Indicate error status.
65$:	CLRL	R1			; Clear second word of I/O status.
	BSBW	FUNCTION_EXIT		; GOTO common exit.
	RSB

70$:	MOVL	UCB$L_CDT(R3), -	; Copy current CDT address into CDRP.
		CDRP$L_CDT(R5)
	MOVL	UCB$L_PDT(R3), R4	; Get current PDT address.
	MOVAB	IS_AFTER_LOCATE_UNIT,R2	; Set up return address
	BSBW	DUTU$LOCATE_UNIT	; Find the failed unit.
	RSB

;
;	Upon completion, the UCB and CDRP input to DUTU_LOACTE_UNIT have 
;	the same connection relationships they had when the call was made.
;	In particular, the following registers and fields are unchanged:
;
;		R4 (PDT address)
;		UCB$L_PDT
;		UCB$L_CDT
;		CDRP$L_CDT
;
;	If the status returned by DUTU$LOCATE_UNIT is SS$_NORMAL, the 
;	(possibly) new MSCP unit number (for MSCP served devices) is now 
;	in CDRP$W_ENDMSGSIZ(R5) for use in subsequent failover processing.
;
;	Any SCS resources held by the CDRP on input to DUTU$LOCATE_UNIT 
;	are now deallocated.
;
; Inputs:
;
;	R0	Status
;		   SS$_NORMAL means unit was located
;		   SS$_MEDOFL means unit was not located
;		   SS$_ABORT  means the request has been canceled
;	R2	CDDB address of connection on which unit was located (or zero)
;	R3	UCB address
;	R4	PDT address (same as input)
;	R5	CDRP address
;

IS_AFTER_LOCATE_UNIT:

	.JSB_ENTRY INPUT=   <R0,   R2,R3,R4,R5>,-
		   OUTPUT=  <>,-
		   SCRATCH= <R0,R1,R2,R3,R4,R5>,-
		   PRESERVE=<>

	BLBC	R0, 90$			; Branch if attempt failed
	BSBW	DUTU$MOVE_UNIT		; Move unit to place just located.
	BLBC	R0, 90$			; Branch if move failed.
	BSBW	SEND_IO			; Send the I/O on the new connection.
	RSB

90$:	MOVL	#SS$_VOLINV,R0		; Indicate error status.
	CLRL	R1			; Clear second word of I/O status.
	BSBW	FUNCTION_EXIT		; GOTO common exit.
	RSB



	.SBTTL	- Setup I/O - External Request Entry Point
;++
;
;	SETUP_IO - Entry Point for All External I/Os
;
; Functional Description:
;
;	This is the "start" entry point specified in the driver dispatch 
;	table. All requests coming into this driver through the "front 
;	door" come in here.
;	
; Inputs:
;
;	R3	IRP address
;	R5	UCB address
;
; Outputs: None.
;
;	The IRP is either inserted to the I/O queue (if the device is 
;	local) or an MSCP packet is created, and that packet is passed
;	on to the SEND_IO routine.
;
;--


DU$SETUP_IO:
	$DRIVER_START_ENTRY PRESERVE=<R2,R3,R4,R5>, FETCH=YES

	; DUDRIVER CDRP initialization at the beginning of this routine must
	; be kept synchronized with the DU$PENDING_IO routine.

	BICL	#<UCB$M_BSY>,	 -	; Undo bit setting so that multiple
		UCB$L_STS(R5)		; IRP's can be started.

	; If this UCB indicates that the device is a local (non-MSCP) device 
	; that has also been made available to us via 1) dual porting and 
	; 2) an MSCP server on the node to which it is dual ported, then 
	; shunt this IRP to the local driver.

	BITL	#DEV$M_CDP, -		; Is this a class driver path to a
		UCB$L_DEVCHAR2(R5)	; local device?
	.BRANCH_LIKELY
	BEQL	10$			; Branch if remote device.
	MOVL	UCB$L_2P_ALTUCB(R5), R5	; Get local UCB address.
	MOVL	IRP$L_MEDIA(R3), R0	; Get logical block address.
	CALL_CVTLOGPHY                  ; Convert it to a physical address.
	CALL_INSIOQC                    ; Go hand this IRP to local driver.
	RET                             ;  and that's all for a local device!
	; Initialize CDRP fields.

10$:	MOVAB	IRP$L_FQFL(R3), R0	; Get address of CDRP portion of IRP.
	MOVL	R5, R3			; Move UCB address.
	MOVL	R0, R5			; Move CDRP address.
	ASSUME	CDRP$B_CD_TYPE EQ CDRP$W_CDRPSIZE+2
	ASSUME	CDRP$B_FLCK    EQ CDRP$W_CDRPSIZE+3
	MOVL	#< <SPL$C_SCS@24> -	; Initialize CDRP size, type and fork
		 ! <DYN$C_CDRP@16> -	; IPL fields.
		 ! <CDRP$L_IOQFL&^xFFFF> >, -
		CDRP$W_CDRPSIZE(R5)
	ASSUME	CDRP$W_DUTUCNTR  EQ <CDRP$L_DUTUFLAGS+4>
	ASSUME	CDRP$W_ENDMSGSIZ EQ <CDRP$L_DUTUFLAGS+6>
	CLRQ	CDRP$L_DUTUFLAGS(R5)	; Clear flags, counter, & msg. size.
	MOVAB	UCB$W_RWAITCNT(R3), -	; Point CDRP field to wait counter
.                                                                   
		CDRP$L_RWCPTR(R5)	; in the UCB.
	SCS_INIT_CDRP			; Init SCS state and wait state.

 
;
; Determine if we can use Fast Path. Perform all DUDRIVER checks that would
; prevent the use of Fast Path.
; If everything looks OK then Request permission from Fast Path from SCS and port.
; If all layers grant permission then branch to perform a Fast Path I/O
; otherwise just do traditional I/O.
;
	;
	; First test if Fast Path is enabled on this disk. Also check if unit 
	; is valid. This check is done here to accomplish all UCB$L_STS
	; checks in one instruction sequence.
	;
	BICL3	#^C<UCB$M_FAST_PATH!UCB$M_VALID>,- ; Get bits of interest
		UCB$L_STS(R3),R0	
	CMPL	R0,#UCB$M_FAST_PATH!UCB$M_VALID ; Is Fast Path enabled on this unit
					; AND is the volume software valid?
	BNEQ	81$			; No, so can't use Fast Path
	;
	; Now check if we are called in circumstances that would allow the
	; SCS lock to be dropped in favor of the port-specific PM spinlock.
	; If running on a unipirocessor then there is no SCS lock to be concerned
	; about so always try for Fast Path in this case. For SMP systems, 
	; try for Fast Path only if the IRP$V_LOCK_RELEASEABLE bit is set as 
	; this implies caller can tolerate dropping the SCS lock.
	;
	ASSUME	SMP$V_ENABLED EQ 0
	BLBC	SMP$GL_FLAGS,17$	; Branch if uniprocessor to always try Fast Path
	BBC	#IRP$V_LOCK_RELEASEABLE,-; Branch if SCS lock NOT releaseable
		CDRP$L_STS(R5), 80$	

17$:	TSTW	UCB$W_RWAITCNT(R3)	; Are "normal" I/O requests stalled?
	BNEQ	80$			; If stalled then can't use Fast Path

	; Test for CDRP$V_ERLIP not needed as this is part of the IVCMD 
	; Processing which is internal DUDRIVER command processing. It is
	; not possible on a call of the start I/O routine.

	;
	; Test for UCB$V_VALID being set was done above.
	;

	;
	; Get requested function, only IO$_READPBLK, IO$_WRITEPBLK  and IO$_WRITECHECK can use Fast Path
	;
	MOVL	CDRP$L_FUNC(R5), R1	; Get function code & modifiers.
	ASSUME	IO$V_FCODE EQ 0
	BICL3	#^cIO$M_FCODE, R1, R0	; Extract just the I/O function code.

	CMPL	#IO$_READPBLK, R0	; IO$_READPBLK function?
	.BRANCH_LIKELY
	BEQL	20$			; If IO$_READPBLK then keep doing Fast Path checks
	CMPL	#IO$_WRITEPBLK, R0	; IO$_WRITEPBLK function?
	.BRANCH_LIKELY
	BEQL	19$			; If IO$_WRITEPBLK then keep doing Fast Path checks
	CMPL	#IO$_WRITECHECK, R0	; IO$_WRITECHECK function?
	BNEQ	80$			; If not IO$_WRITECHECK then can't use Fast Path
	BRW	20$			; Continue Fast Path checks
	;
	; For writes, WLE processing disables Fast Path (at least for V1 of Fast Path)
	;
19$:	BBS	#IRP$V_WLE,-		; Branch if write logging involved.
		CDRP$L_STS2(R5),80$

20$:	;
	; Several tests for CDRP$L_STS are done here.
	;
	BITL	#<IRP$M_PHYSIO!IRP$M_MVIRP>,- ; Is this a physical request or a MVIRP?
		CDRP$L_STS(R5)		; 
	BNEQ	80$			; If so then can't use Fast Path
        ;
	; If any esoteric or obsolete modifiers are present, then can't use Fast Path.
	; Note IO$V_MSCPMODIFS and IO$M_DATACHECK are supported in Fast Path.
	; 
	BITL	#<IO$M_INHRETRY -	;    inhibit retries
		 !^x180>, R1		;    old modifiers that should be clear
	BNEQ	80$			; Branch if any modifier set as can't use Fast Path

	;
	; Diagnostic buffers are ignored for Fast Path.
	;

	;
	; Do the SEND_MSCP_MSG sanity checks. Connection being an "open" state
	; is checked by SCS when we request a Fast Sendmsg in a moment.
	;
	MOVL	UCB$L_CDDB(R3), R0	; Get the CDDB address
	CMPL	UCB$L_CDT(R3),CDDB$L_CDT(R0) ; Make sure all CDT structures 
	BNEQ	80$			;  involved agree
;
; Now make any checks from the I/O completion path that present complexities
; that Fast Path can't handle.
;
	;
	; Test for IRP$V_MVIRP bit being set was done above
	;

;
; DUDRIVER has no objection to using Fast Path. Now ask SCS and the port for
; permission. If permission granted then we can branch to perform the I/O via Fast Path.
;
30$:	MOVL	UCB$L_CDT(R3),-		; Place CDT pointer into CDRP for handy
		CDRP$L_CDT(R5)		;  reference by SCS routines.
	MOVL	UCB$L_PDT(R3),R4	; R4 => port's PDT.
        MOVAL   CDRP$L_SVAPTE(R5),R1    ; Get addr in IRP of SVAPTE
	FAST_SENDMSG_REQUEST		; Ask SCS/port if can use Fast Path
	BLBS	R0,FAST_PATH_IO		; Branch if request approved and do Fast Path I/O
	BRW	81$			; Else can't use Fast Path
;
; 	Fast Path cannot be used, do traditional I/O. Increment debug counter if
;	DUDRIVER vetoed used of Fast Path.
;
80$:	
;	INCREMENT_IOCNT	COUNTER=FP_SYSAP_NOSEND,- ; (DEBUG)Increment counter saying SYSAP vetoes Fast Path
;			UCB=R3

81$:	CLRL	CDRP$L_MSG_BUF(R5)	; Prevent spurious DEALLOC_MSG_BUF calls
	CLRL	CDRP$L_RSPID(R5)	; Prevent spurious DEALLOC_RSPID calls
	CLRL	CDRP$L_LBUFH_AD(R5)	; Prevent spurious UNMAP calls.

	TSTW	UCB$W_RWAITCNT(R3)	; Are "normal" I/O requests stalled?
	BNEQ	100$			; Continue if not stalled
	BSBW	SEND_IO			; Issue the I/O to the port
	RET	
100$:	BSBW	IO_STALLED		; Branch if requests are stalled.
	RET	


	.SBTTL	FAST_PATH_IO - Perform a Fast Path I/O 
;++
;
;	FAST_PATH_IO - Perform a Fast Path I/O 
;
; Functional Description:
;
;	All layers have granted permission to use Fast Path and all resources
;	are available. On successful return from the FAST_SENDMSG_REQUEST the 
;	Port Mainline (PM) spinlock is now held. The first order of business is to drop
;	the SCS spinlock as soon as possible. DUDRIVER first makes any SCS database updates
;	that are needed and then releases the SCS spinlock leaving only PM spinlock protection.
;	The reserved resources are then linked to this I/O, the MSCP message is setup
;	and then the message is sent on it's way. 
;
; Inputs:
;
;	R3	UCB  address
;	R4	PDT  address
;	R5	CDRP address
;	CDRP$L_RBUN - contains a pointer to the resource bundle
;
; Outputs: 
;
;	None.
;
;--
FAST_PATH_IO:

;
; SCS database updates made before SCS lock is dropped.
;
	MOVL	UCB$L_CDDB(R3), R1	; Get the CDDB address, and 
	INSERT_EL -			; Link CDRP onto CDDB active Q
		EL 	= (R5),-
		QUE 	= @CDDB$L_CDRPQBL(R1),- 
		SCRATCH = R0
;
; All updates have been made, now release SCS spinlock which leaves us
; with the PM spinlock at IPL 8 for synchronization. Note that for the
; logging driver we hold unto the SCS spinlock until the message is built
; and about to be sent out over the port, at which point more logging occurs.
;
	UNLOCK	LOCKNAME=SCS,-		; Unlock SCS access
		PRESERVE=NO		; Don't preserve R0
					; Don't change IPL
;
; Only PM spinlock sychronization for the remainder of this routine.
;
;	INCREMENT_IOCNT	COUNTER=FP_SYSAP_SENDS,- ; (DEBUG)Increment counter of FP Sends
;			UCB=R3

;
; Associate allocated SCS resources to this I/O and then map the user data buffer.
;
	MOVAB	CDRP$T_LBUFHNDL(R5),-	; Put address of Local BUFfer HaNDLe
		CDRP$L_LBUFH_AD(R5)	;  field into field that points to it.
        MOVAL   CDRP$L_SVAPTE(R5),R1    ; Get addr in IRP of SVAPTE
	FAST_SENDMSG_ASSOCIATE_PM	; Associate resources to I/O and then MAP user buffe
r
;
; Outputs:
;  	R2,CDRP$L_MSG_BUF - contains pointer to message buffer
;	CDRP$L_RSPID   - contains a RSPID
;  	CDRP$L_BD_ADDR - contains pointer to buffer descriptor
;	CDRP$L_LBUFH_AD(R5) - contains pointer to completely filled in buffer handle
;
; Now create the MSCP message
;
	;
	; All longwords of MSCP message are written in the message setup below
	; except the last longword. Instead of zeroing the entire message
	; just zero the ninth longword.
	;
	ASSUME MSCP$K_MXCMDLEN EQ 36
	CLRL	MSCP$K_MXCMDLEN-4(R2)	; Zero unwritten portion of command buffer
	
	MOVL	CDRP$L_RSPID(R5),-	; Use RSPID as command reference
		MSCP$L_CMD_REF(R2)	;  number for all commands.

	ASSUME	MSCP$W_SEQ_NUM EQ MSCP$W_UNIT+2
	MOVZWL	UCB$W_MSCPUNIT(R3),-	; Indicate UNIT number in MSCP
		MSCP$W_UNIT(R2)		;  packet.

	MOVL	CDRP$L_FUNC(R5),R1	; Get I/O function code and modifiers
	ASSUME 	IO$V_FCODE EQ 0
	ASSUME 	IO$_WRITECHECK EQ 10
	ASSUME 	IO$_WRITEPBLK  EQ 11
	ASSUME 	IO$_READPBLK   EQ 12
	.BRANCH_LIKELY
	BBS	#2,R1,50$		; Branch if IO$_READPBLK
	BLBC	R1,45$			; Branch if IO$_WRITECHECK
	ASSUME	MSCP$W_MODIFIER EQ MSCP$B_OPCODE+2
	MOVL	#MSCP$K_OP_WRITE, -	; Setup write MSCP opcode.
		MSCP$B_OPCODE(R2)
	BRW	55$ 			; Skip over read setup.
	;
	; Checks for transfers beyond end of disk into RCT not needed as
	; physical requests don't use Fast Path.
	;
45$:	MOVL	#MSCP$K_OP_COMP, -	; Setup Compare host data opcode.
		MSCP$B_OPCODE(R2)
	BRW	60$ 			; Only MSCPMODIFS qualifier applies to WRITECHECK


50$:	MOVL	#MSCP$K_OP_READ, -	; Setup read MSCP opcode.
		MSCP$B_OPCODE(R2)

55$:	BITL	#<IO$M_DATACHECK -	; Any supported Fast Path
		 !IO$M_MSCPMODIFS>, R1	;  modifiers present? 
	.BRANCH_LIKELY
	BEQL	100$ 			; Branch if all modifiers are clear.

	BBC	#IO$V_DATACHECK, R1, 60$; Branch if data check not specified.
	ASSUME	MSCP$W_MODIFIER EQ MSCP$B_OPCODE+2
	BISL	#MSCP$M_MD_COMP@16, -	; Else, set the read/write with
		MSCP$B_OPCODE(R2)	; data compare modifier.

60$:	BBC	#IO$V_MSCPMODIFS,R1,100$; Branch if no MSCP modifiers specified.
	BISW	CDRP$L_MEDIA+6(R5), - 	; Set specified modifiers in packet.
		MSCP$W_MODIFIER(R2)

100$:	MOVQ	CDRP$T_LBUFHNDL(R5),-	; Copy contents of buffer handle to
		MSCP$B_BUFFER(R2)	;  MSCP buffer descriptor field.
	MOVL	CDRP$T_LBUFHNDL+8(R5),-	; Buffer handle is 96 bits (12 bytes)
		MSCP$B_BUFFER+8(R2)	;  in length.

	MOVL	CDRP$L_BCNT(R5),-
		MSCP$L_BYTE_CNT(R2)	; Copy byte count of transfer.
	MOVL	CDRP$L_MEDIA(R5),-
		MSCP$L_LBN(R2)		; And also the Logical Block Number.
;
; For logging driver the SCS spinlock is held until the message is built and 
; we have logged it. The SCS spinlock is released earlier for non-logging driver.
;
;
; Now send the message. The return address is setup to be label FAST_PATH_RET
; which is the traditional I/O transfer return point. Fast Path is also attempted
; on I/O completion but once again a layer could veto the attempt. In that case
; traditional I/O completion is performed and the return address must be
; setup correctly.
;
	MOVL	#MSCP$K_MXCMDLEN, R1	; Pass longest command length
	FAST_SENDMSG_PM  FAST_PATH_RET	; Specify standard I/O return in case
					; Fast Path is not used on I/O completion
	RET				; Return to start I/O routine caller


	.SBTTL	SEND_IO - Create MSCP Packet and Dispatch to Function Routines
;++
;
;	SEND_IO - Create MSCP Packet and Dispatch to Function Specific Routines
;
; Functional Description:
;
;	This routine is used in the "normal" I/O path, by being called from
;	the SETUP_IO routine above, as well as other routines within the 
;	driver that are resubmitting I/O requests that have been removed from
;	other queues (such as: restart, resource wait, stalled queues etc.)
;
;	An MSCP message packet is created for this I/O request. The parts
;	of the packet that are common to all I/O requests are filled in, and
;	the packet is then dispatched to function specific processing.
;
; Inputs:
;
;	R3	UCB  address
;	R5	CDRP address
;
; Outputs: 
;
;	None.
;
;--

SEND_IO::

	.JSB_ENTRY INPUT=   <         R3,   R5>,-
		   OUTPUT=  <>,-
		   SCRATCH= <>,-
		   PRESERVE=<>

	MOVL	UCB$L_CDT(R3),-		;MAINL; Place CDT pointer into CDRP for handy
		CDRP$L_CDT(R5)		;MAINL;  reference by SCS routines. Note we
					;MAINL;  do this after label SEND_IO so
					;MAINL;  that it is refreshed upon restart.
	MOVL	UCB$L_PDT(R3),R4	;MAINL; R4 => port's PDT.

	BITL	#CDRP$M_ERLIP,	-	;MAINL; When set, this bit means an operation
		CDRP$L_DUTUFLAGS(R5)	;MAINL; which replaced a block is being 
	BNEQ	10$			;MAINL; restarted and that the previously
					;MAINL; allocated RSPID should continue to
					;MAINL; be used.
	CLRL	CDRP$L_RSPID(R5)	;MAINL; Prevent spurious DEALLOC_RSPID.
	ALLOC_RSPID			;MAINL; ALLOCate a ReSPonse ID.
.                                                                

10$:	BITL	#UCB$M_VALID, -		;MAINL; Is volume software valid?
		UCB$L_STS(R3)            
	.BRANCH_LIKELY
	BNEQ	30$			;MAINL; Branch if volume is software valid.

; Handle volume which is not software valid.
;
; When a volume is not software valid, only physical opeations are legal.  All 
; other operations must produce a SS$_VOLINV status.  The only exception is a 
; small race window where the volume can become invalid while a thread is 
; waiting for a RSPID.  In this case, a check for a packack which might make 
; things better is required.

	BBS	#IRP$V_PHYSIO,-		; If PHYSICAL I/O is requested,
		CDRP$L_STS(R5), 30$	;  continue even tho VOLINV.
	TSTW	UCB$W_RWAITCNT(R3)	; Is the wait count raised?
	BEQL	20$			; Branch if wait count not raised.
	DEALLOC_RSPID			; Else, release RSPID.
	BRW	IO_STALLED		; Join the I/O is stalled code path.
.                                                                     

20$:	MOVZWL	#SS$_VOLINV,R0		; Indicate error status.
	CLRL	R1			; Clear second word of I/O status.
	BSBW	FUNCTION_EXIT		; GOTO common exit.
	RSB

30$:	ALLOC_MSG_BUF			;MAINL; Allocate an MSCP buffer (and also
                                                                           
	.BRANCH_LIKELY
	BLBS	R0,40$			;MAINL;  allocate a unit of flow control).

; Control gets here whenever there is a message buffer allocation failure.  
; This implies that the connection to the MSCP server is broken.  The action
; to be taken is to kill this thread of execution by placing the CDRP at the 
; tail of the outstanding CDRP queue.  Because of the connection failure, a
; thread exists that is currently executing that is gathering all CDRP's 
; associated with this connection for a future restart attempt.  Therefore, 
; control is pased to DUTU$KILL_THIS_THREAD which will handle this error.

	JSB	DUTU$KILL_THIS_THREAD	; Branch to where we collect all active
	RSB				;  CDRP's prior to re-CONNECTION.

; Here a little common MSCP packet initialization.

40$:	MOVL	R2, R0			;MAINL; Copy message buffer address.
	.REPEAT	MSCP$K_MXCMDLEN / 8
	CLRQ	(R0)+			;MAINL; Zero entire message buffer.
	.ENDR
.      

	MOVL	CDRP$L_RSPID(R5),-	;MAINL; Use RSPID as command reference
		MSCP$L_CMD_REF(R2)	;MAINL;  number for all commands.

	MOVW	UCB$W_MSCPUNIT(R3),-	;MAINL; Indicate UNIT number in MSCP
		MSCP$W_UNIT(R2)		;MAINL;  packet.

DU$DISPATCH_CMD:

	MOVL	CDRP$L_FUNC(R5), R1	;MAINL; Get function code & modifiers.
	ASSUME	IO$V_FCODE EQ 0
	BICL3	#^cIO$M_FCODE, R1, R0	;MAINL; Extract just the I/O function code.

	CMPL	#IO$_READPBLK, R0	;MAINL; IO$_READPBLK function?
	.BRANCH_LIKELY
	BEQL	START_READ		;MAINL; Branch if IO$_READPBLK function.
.                                                                         
	CMPL	#IO$_WRITEPBLK, R0	;MAINL; IO$_WRITEPBLK function?
	.BRANCH_LIKELY
	BEQL	START_WRITE		;MAINL; Branch if IO$_WRITEPBLK function.
.                                                                          

	DISPATCH R0, type=B, prefix=IO$_, < -		; Dispatch to correct 
		<NOP,		START_NOP>, -		; function processing.
		<UNLOAD,	START_UNLOAD>, -
		<PACKACK,	START_PACKACK>, -
		<WRITECHECK,	START_WRITECHECK>, -    ;MAINL;
		<AVAILABLE,	START_AVAILABLE>, -
		<DSE,		START_DSE>, -
		<FORMAT,	START_FORMAT>, -
		<DISPLAY,	DUTU$START_DISPLAY>, -
		<SETPRFPATH,	START_SETPATH>, -
                <DISK_COPY_DATA,START_DISK_COPY_DATA> -
                <WHM,           START_WHM> -
 		>
           

	RESTORE_CREDIT
	MOVZWL	#SS$_ILLIOFUNC,R0
	CLRL	R1			; Clear second word of I/O status.
	BSBW	FUNCTION_EXIT		; GOTO common exit.
	RSB



	.SBTTL	START_NOP - Sequential NOP Function
;++
;
;	START_NOP - Prepare an MSCP packet to do a SET UNIT CHARACTERISTICS.
;	N.B. SUC is a sequential MSCP command.
;
; Inputs:
;
;	R1	I/O function code & modifiers
;	R2	MSCP buffer
;	R3	UCB address
;	R4	PDT address
;	R5	CDRP address
;
;	The MSCP packet contains zeros except for:
;
;	MSCP$L_CMD_REF(R2)	<== CDRP$L_RSPID(R5)
;	MSCP$W_UNIT(R2)		<== UCB$W_MSCPUNIT(R3)
;
;--

START_NOP:

	MOVB	#MSCP$K_OP_STUNT, -	; Make that command a set unit 
		MSCP$B_OPCODE(R2)	; characteristics command.
	MOVW	UCB$W_UNIT_FLAGS(R3), -	; Must provide current unit flags
		MSCP$W_UNT_FLGS(R2)	; for SUC.
	MOVL	UCB$L_MSCPDEVPARAM(R3),-; Must also provide device dependent
		MSCP$L_DEV_PARM(R2)	; parameters for SUC.
	MOVL	UCB$L_CDDB(R3), R1	; Get CDDB of intelligent controller.
	SEND_MSCP_MSG			; Send SUC command

;
; Inputs:
;
;	R0	Status of send operation
;	R1	Size of command packet sent
;	R2	MSCP message buffer address
;	R3	CDDB  address
;	R4	PDT  address
;	R5	CDRP address
;

	.JSB_ENTRY INPUT=  <R0,R1,R2,R3,R4,R5>,-
		   SCRATCH=<R0,R1,R2,R3,R4,R5>

        CMPW	#SS$_ILLCDTST,R0
	BNEQ	10$			; Branch if no CDT error detected

	MOVZWL	#SS$_DEVOFFLINE,R0	; CDT error was detected, report error
	CLRL	R1			; Clear for I/O status block.
	BSBW	FUNCTION_EXIT		; Branch to common exit.
	RSB

10$:	ASSUME	MSCP$V_ST_MASK EQ 0
	BICW3	#^cMSCP$M_ST_MASK, -	; Extract major MSCP status.
		MSCP$W_STATUS(R2), R0
	DISPATCH R0, type=W, prefix=MSCP$K_ST_, < -
		<SUCC,	NOP_SUCC>, -
		<OFFLN,	NOP_OFFLINE>, -
		<AVLBL,	NOP_AVAIL>, -
		<DRIVE,	NOP_DRVERR>, -
		<CNTLR,	NOP_CTRLERR>, -
		<ICMD,	NOP_IVCMD>, -
		<FMTER,	NOP_CTRLERR> -
		>
	BSBW	DU$INVALID_STS		; Unexpected MSCP end status.
	RSB

NOP_OFFLINE:
	MOVL	#SS$_DEVOFFLINE, R0	; Set the status
	BRW	NOP_EXIT		;  and branch out.

NOP_AVAIL:
	MOVL	#SS$_MEDOFL, R0		; Set the status
	BRW	NOP_EXIT		;  and branch out.

NOP_DRVERR:
	MOVL	#SS$_DRVERR, R0		; Set the status
	BRW	NOP_EXIT		;  and branch out.

NOP_CTRLERR:
	MOVL	#SS$_CTRLERR, R0	; Set the status
	BRW	NOP_EXIT		;  and branch out.

NOP_IVCMD:
	MOVL	#SS$_CTRLERR, R0	; Set the status
	.MACRO	IVCMD_BEGIN	?L1,?L2
.                                
	IVCMD_BEGIN			; Begin invalid command processing.

;
; Restore MSCP command packet to its original state before it was
; sent out to the controller.
;
	MOVB	#MSCP$K_OP_STUNT, -	; Make that command a set unit 
		MSCP$B_OPCODE(R2)	; characteristics command.
	MOVW	UCB$W_UNIT_FLAGS(R3), -	; Must provide current unit flags
		MSCP$W_UNT_FLGS(R2)	; for SUC.
	MOVL	UCB$L_MSCPDEVPARAM(R3),-; Must also provide device dependent
		MSCP$L_DEV_PARM(R2)	; parameters for SUC.
	IVCMD_END			; Complete invalid command processing.
	BRB	NOP_EXIT

NOP_SUCC:
	MOVL	#SS$_NORMAL, R0		; Set the status

NOP_EXIT:
	CLRL	R1			; Clear for I/O status block.
	BSBW	FUNCTION_EXIT		; Branch to common exit.
	RSB



	.SBTTL	START_FORMAT - Prepare an MSCP FORMAT command packet.
;+
;
; START_FORMAT - Prepare an MSCP command packet to do a FORMAT command
;		 for a unit that has been previously brought online 
;		 with the IO$M_MSCP_FORMAT modifier.
;
; Inputs:
;
;	R1 => I/O function code & modifiers
;	R2 => MSCP buffer
;	R3 => UCB
;	R4 => PDT
;	R5 => CDRP
;
;	The MSCP packet contains zeros except for:
;
;	MSCP$L_CMD_REF(R2)	<== CDRP$L_RSPID(R5)
;	MSCP$W_UNIT(R2)		<== UCB$W_MSCPUNIT(R3)
;
;-

START_FORMAT:

	MOVB	#MSCP$K_OP_FMT, -	; Make the command a format
		MSCP$B_OPCODE(R2)	;  media.
	MOVL	CDRP$L_MEDIA(R5), -	; Set format function from P1
		MSCP$L_FMT_FUNC(R2) 	;  parameter.
	MOVL	UCB$L_CDDB(R3), R0	; Get CDDB address.
	CMPB	#MSCP$K_CM_RQDX3, -	; RQDX3 Controller?
		CDDB$B_CNTRLMDL(R0)	;
	BNEQ	5$			; If NEQ no
	CMPB	#DT$_RX33, -		; RX33 Device ?
		UCB$B_DEVTYPE(R3)	;
	BNEQ	5$			; If NEQ no
	CMPB	#MSCP$K_FMT_DOUB, -	; Double density?
		MSCP$L_FMT_FUNC(R2) 	; 
	BNEQ	5$			; If NEQ no
	MOVZWL	#MSCP$K_FMT_RX33, -	; Set format function for RX33
		MSCP$L_FMT_FUNC(R2) 	; 

5$:	MOVL	UCB$L_CDDB(R3), R1	; Get CDDB of intelligent controller.
	SEND_MSCP_MSG			; Send the FORMAT command

;
; Inputs:
;
;	R0	Status of send operation
;	R1	Size of command packet sent
;	R2	MSCP message buffer address
;	R3	CDDB  address
;	R4	PDT  address
;	R5	CDRP address
;

	.JSB_ENTRY INPUT=  <R0,R1,R2,R3,R4,R5>,-
		   SCRATCH=<R0,R1,R2,R3,R4,R5>

        CMPW	#SS$_ILLCDTST,R0
	BNEQ	7$			; Branch if no CDT error detected

	MOVZWL	#SS$_DEVOFFLINE,R0
	CLRL	R1			; Clear for I/O status block.
	BSBW	FUNCTION_EXIT		; Branch to common exit.
	RSB

; Format command performs an implicit AVAILABLE.
; Update the UCB to reflect an AVAILABLE command.

7$:	BICL	#<UCB$M_VALID>, -	; Clear software volume valid.
		UCB$L_STS(R3)
	BBCC	#UCB$V_LCL_VALID, -	; First FORMAT or AVAILABLE?
		UCB$L_STS(R3), 10$	; If CC not first, "Can't happen"
	DECB	UCB$B_ONLCNT(R3)	; Decrement online count.

10$:	ASSUME	MSCP$V_ST_MASK EQ 0
	BICW3	#^cMSCP$M_ST_MASK, -	; Extract major MSCP status.
		MSCP$W_STATUS(R2), R0
	DISPATCH R0, type=W, prefix=MSCP$K_ST_, < -
		<SUCC,	FORMAT_SUCC>, -
		<OFFLN,	FORMAT_OFFLINE>, -
		<AVLBL,	FORMAT_OFFLINE>, -
		<DRIVE,	FORMAT_DRVERR>, -
		<CNTLR,	FORMAT_CTRLERR>, -
		<ICMD,	FORMAT_IVCMD>, -
		<IPARM,	FORMAT_IPARM>, -
		<ABRTD,	FORMAT_ABORT>, -
		<MFMTE,	FORMAT_MFMTE>, -
		<HSTBF,	FORMAT_HSTBF>, -
		<WRTPR,	FORMAT_WRITELCK> -
		>
	BSBW	DU$INVALID_STS		; Unexpected MSCP end status.
	RSB

FORMAT_OFFLINE:
	MOVL	#SS$_MEDOFL, R0		; Set the status
	BRW	FORMAT_EXIT		;  and branch out.

FORMAT_DRVERR:
	MOVL	#SS$_DRVERR, R0		; Set the status
	BRW	FORMAT_EXIT		;  and branch out.

FORMAT_CTRLERR:
	MOVL	#SS$_CTRLERR, R0	; Set the status
	BRW	FORMAT_EXIT		;  and branch out.

FORMAT_IPARM:
	MOVL	#SS$_ILLIOFUNC, R0	; Set the status
	BRW	FORMAT_EXIT		;  and branch out.

FORMAT_ABORT:
	MOVL	#SS$_ABORT, R0		; Set the status
	BRW	FORMAT_EXIT		;  and branch out.

FORMAT_MFMTE:
	MOVL	#SS$_FORMAT, R0		; Set the status
	BRW	FORMAT_EXIT		;  and branch out.

FORMAT_HSTBF:
	MOVL	#SS$_IVBUFLEN, R0	; Set the status
	BRW	FORMAT_EXIT		;  and branch out.

FORMAT_WRITELCK:
	MOVL	#SS$_WRITLCK, R0	; Set the status
	BRW	FORMAT_EXIT		;  and branch out.

FORMAT_IVCMD:
	MOVL	#SS$_CTRLERR, R0	; Set the status
	IVCMD_BEGIN			; Begin invalid command processing.
.                                                                    
	MOVB	#MSCP$K_OP_FMT, -	; Make the command a format
		MSCP$B_OPCODE(R2)	;  media.
	MOVL	CDRP$L_MEDIA(R5), -	; Set format function from P1
		MSCP$L_FMT_FUNC(R2) 	;  parameter.
	IVCMD_END			; Complete invalid command processing.
	BRB	FORMAT_EXIT

FORMAT_SUCC:
	MOVL	#SS$_NORMAL, R0		; Set the status

FORMAT_EXIT:
	CLRL	R1			; Clear for I/O status block.
	BSBW	FUNCTION_EXIT		; Branch to common exit.
	RSB



	.SBTTL	PACKACK MSCPserver failover code

;  Out of line code to move unit from MSCPserver to a local controller
;  if conditions warrant.  PACKACK is the method used for most failover
;  cases since it interlocks the volume (via RWAITCNT) and often
;  indicates implicit (or explicit) validation to follow.

PACKACK_MOVE_SERVER:

;  DUTUCNTR is used for the retry count for trips through PACKACK.  

	INCW	CDRP$W_DUTUCNTR(R5)	; Show that we've been here.

;  If the SLUN_RSVP magic unit number is present, it means the unit is
;  not currently online from this host.  Therefore, failover can be done
;  regardless of how we got into PACKACK.  DUTU$LOCATE_UNIT provides
;  an appropriate path (disallowing served paths for SRVIO) if one exists.

	CMPW	#MSCP$K_SLUN_RSVP, -	; Is the reserved slun unit the current
		UCB$W_MSCPUNIT(R3)	;  MSCPUNIT for this device?
	BNEQ	10$			; If not, check options carefully.
	BICL	#<UCB$M_VALID>, -	; Clobber the valid bit to prevent
		UCB$L_STS(R3)		;  other MVIRP threads from "helping".
	BRW	PACKACK_FAILOVER	; And go try to find a better path.
.                                                                    

;  If the unit number was correctly set, then the device may have been in use
;  by parties unknown.  So, ability and method for failover is dependent on
;  what type IRP we have:  MVIRP, SRVIO, or a local PACKACK function.

10$:	BBS	#IRP$V_MVIRP, -		; If MVIRP, failover allowed directly
		CDRP$L_STS(R5), 60$	;  so issue AVAILABLE first.
	BBC	#DEV$V_MNT, -		; For SRVIO or local request, try to
		UCB$L_DEVCHAR(R3), 60$	;  failover if disk not mounted.
	BBC	#IRP$V_SRVIO, -		; If not SRVIO, must be an actual local
		CDRP$L_STS(R5), 70$	;  PACKACK function to a served disk.

	MOVAB	15$,R2			; Set up return address
	BSBW	DUTU$LOCATE_UNIT	; Find the failed unit.
	RSB

;
;	Upon completion, the UCB and CDRP input to DUTU_LOACTE_UNIT have 
;	the same connection relationships they had when the call was made.
;	In particular, the following registers and fields are unchanged:
;
;		R4 (PDT address)
;		UCB$L_PDT
;		UCB$L_CDT
;		CDRP$L_CDT
;
;	If the status returned by DUTU$LOCATE_UNIT is SS$_NORMAL, the 
;	(possibly) new MSCP unit number (for MSCP served devices) is now 
;	in CDRP$W_ENDMSGSIZ(R5) for use in subsequent failover processing.
;
;	Any SCS resources held by the CDRP on input to DUTU$LOCATE_UNIT 
;	are now deallocated.
;
; Inputs:
;
;	R0	Status
;		   SS$_NORMAL means unit was located
;		   SS$_MEDOFL means unit was not located
;		   SS$_ABORT  means the request has been canceled
;	R2	CDDB address of connection on which unit was located (or zero)
;	R3	UCB address
;	R4	PDT address (same as input)
;	R5	CDRP address
;

15$:	.JSB_ENTRY INPUT=   <R0,   R2,R3,R4,R5>,-
		   OUTPUT=  <>,-
		   SCRATCH= <R0,R1,R2,R3,R4,R5>,-
		   PRESERVE=<>

	BLBC	R0, 30$			; Failure means no local paths useable.
	MOVL	UCB$L_CDDB(R3), R0	; Get CDDB address.
	CMPB	#MSCP$K_CM_EMULA, -	; Is the controller for this device
		CDDB$B_CNTRLMDL(R0)	;  still the MSCPserver?
	BNEQ	40$			; If not server, exit without mountver.
	MOVZWL	#SS$_MEDOFL, R0		; Setup a status that will invoke mv
	BRW	END_PACKACK		;  and let it do the work.
.                                                           

30$:	CMPW	#SS$_ABORT, R0		; If request was cancelled, leave the
	BEQL	END_PACKACK		;  status alone.  Otherwise, return
.                                                                    

40$:	MOVZWL	#SS$_NOSUCHDEV, R0	;  failure without starting mv, since
	BRW	END_PACKACK		;  the local host can still use server.
.                                                                        
	
; And now, keep your eye on the ball:
;
;   If we're here, we can attempt to failover to a better (non-emulated)
;   path willy-nilly.  The basic assumptions governing entrance to this
;   section were:
;     a. The current primary path for the disk is a VMS MSCPserver.
;     b. There is a possibility of a better, direct (local) path available
;        because the DEV$V_LOC bit was set in UCB$L_DEVCHAR2.
;     c. This is the first time into PACKACK for this IRP.
;
;   The possible reasons we are now here are:
;     1. This is an MVIRP.  All validation required if we change paths will
;        automatically be done by mount verification, so we are free to mung
;        as desired.
;     2. This is the first local PACKACK for the device, so either MOUNT will
;        validate the volume directly afterwards, or a privileged user
;        has access.  In either case failover is no more dangerous than
;        bringing the current path online.  Mount verification is unnecessary
;        for the local host since the disk isn't yet mounted, and also
;        unnecessary for the server since the current path was already served,
;        thus the server should have been offline already.
;     3. Finally, if this is a SRVIO and the disk is not mounted locally, then
;        we can change the path for similar reasons as #2.

60$:	BICL	#<UCB$M_VALID>, -	; Make sure that nothing can get to the
		UCB$L_STS(R3)		;  device without effort.
;
; At this point we are going to send an MSCP AVAILABLE.
;

	MOVB	#MSCP$K_OP_AVAIL, -	; Unit number not SLUN magic value, so
		MSCP$B_OPCODE(R2)	;  send an available to this server.
	MOVL	UCB$L_CDDB(R3), R1	; Get CDDB of intelligent controller.

	SEND_MSCP_MSG			; Send the AVAIL command

;
; Inputs:
;
;	R0	Status of send operation
;	R1	Size of command packet sent
;	R2	MSCP message buffer address
;	R3	CDDB  address
;	R4	PDT  address
;	R5	CDRP address
;

	.JSB_ENTRY INPUT=  <R0,R1,R2,R3,R4,R5>,-
		   SCRATCH=<R0,R1,R2,R3,R4,R5>

	MOVL	CDRP$L_UCB(R5),R3
	MOVW	#MSCP$K_SLUN_RSVP, -	; Prevent MVIRPs from doing more
		UCB$W_MSCPUNIT(R3)	;  AVAILs if the disk stays offline.
	ASSUME	CDRP$V_CAND EQ 0
	BLBC	CDRP$L_DUTUFLAGS(R5),65$; If this CDRP was not canceled,
					;  skip down to continue.
	MOVZWL	#SS$_ABORT, R0		; Set status for aborted command,
	BRW	END_PACKACK		;  drop back and punt.
.                                                       

65$:	BRW	PACKACK_FAILOVER_MSG	; Else, go try to find a better path.
                                                                       

;
;  Local PACKACK function, with device already mounted.  Continue to use the
;  current (served) path.  Random local PACKACKs cannot to modify paths
;  without the necessary mount verification, although EXE$LCLDSKVALID
;  will essentially screen them all out anyway...
;    (Also used as a branch assist)

70$:	BRW	PACKACK_DO_ONLINE	;  Continue using current controller...
                                                                         



	.SBTTL	START_PACKACK - Prepare an MSCP packet to do an ONLINE command.
;++
;
;	START_PACKACK - Prepare an MSCP packet to do an ONLINE command.
;
; Functional Description:
;
;	PACKACK is all things to all people.
;
; Inputs:
;
;	R1	I/O function code & modifiers
;	R2	MSCP buffer
;	R3	UCB
;	R4	PDT
;	R5	CDRP
;
;	The MSCP packet contains zeros except for:
;
;	MSCP$L_CMD_REF(R2)	<== CDRP$L_RSPID(R5)
;	MSCP$W_UNIT(R2)		<== UCB$W_MSCPUNIT(R3)
;
;--

START_PACKACK:

	BBSS	#UCB$V_MSCP_PKACK, -	; Branch if PACKACK in progress is
		UCB$L_DEVSTS(R3), 80$	; set and set it.
	INCW	UCB$W_RWAITCNT(R3)	; Bump wait count to stall I/O.

80$:	MOVL	UCB$L_CDDB(R3), R0	; Get CDDB address.
;
; Look for a preferred path and test to see if we are currently using it.
; unless this i/o came from a server.
;
	BBS	#IRP$V_SRVIO,-		; Ignore the prefered path for
		CDRP$L_STS(R5), 83$	;  served I/O
	TSTL	UCB$L_PREF_CDDB(R3)	; Has a preferred path been specified?
	BEQL	83$			; No, proceed with normal checks.
	CMPL	UCB$L_PREF_CDDB(R3),R0	; Is the current path the preferred
	BNEQ	85$			;  path? If not, begin conn walk. It
					;  will use the preferred path.

83$:	CMPB	#MSCP$K_CM_EMULA, -	; Is the controller for this device
		CDDB$B_CNTRLMDL(R0)	;  the MSCPserver?
	BEQL	85$			; Branch if this is a server.
	BRW	PACKACK_DO_ONLINE	; Continue using current controller...
.                                                                       


;  Processing a request for a served path or a device with a specified
;  preferred path. Attempt failover to a local  or specified controller
;  if the correct circumstances exist.

85$:	TSTW	CDRP$W_DUTUCNTR(R5)	; Have we attempted a failover already?
	BNEQ	90$			; Branch if yes, use the server.
	BBC	#DEV$V_2P,-		; Do not try to failover or load balance
		UCB$L_DEVCHAR2(R3),90$	;  if we have only ever seen one path.
	BRW	PACKACK_MOVE_SERVER	; Else try to find the best path,
					;  either local or least busy server.
90$:	BBC	#IRP$V_SRVIO, -		; Make sure this isn't a server I/O,
		CDRP$L_STS(R5), 100$	;  to avoid multiple hop serving.
	RESTORE_CREDIT
	MOVZWL	#SS$_NOSUCHDEV, R0	;  failure without starting MV.
	BRW	END_PACKACK
.                    

100$:	CMPW	#MSCP$K_SLUN_RSVP, -	; Is the reserved slun unit the current
		UCB$W_MSCPUNIT(R3)	;  MSCPUNIT for this device?
	BEQL	110$			; Yes, request SLUN using GUS command
	BRW	PACKACK_DO_ONLINE	; Otherwise send the ONLINE; unit OK.
.                                                                      

;  Before an ONLINE command can be sent, we must update the unit number by
;  requesting the Server Local Unit Number with a Get Unit Status command.

110$:	BICL	#<UCB$M_VALID>, -	; Clobber the valid bit since we're
		UCB$L_STS(R3)		;  about to modify the unit number.
	MOVB	#MSCP$K_OP_GTUNT, -	; Set up opcode for a Get Unit Status
		MSCP$B_OPCODE(R2)	;  command to determine MSCPUNIT.
	MOVW	UCB$W_UNIT(R3), -	; Get unit number in SLUN area for
		MSCP$W_SLUN_UNIT(R2)	;  server to use.
	MOVL	CDDB$L_ALLOCLS(R0), -	; Put allocation class in SLUN area,
		MSCP$L_SLUN_ALLOCLS(R2)	;  in the same fashion.
	MOVL	UCB$L_DDB(R3), R0	; Get DDB address for this device.

	ASSUME	MSCP$V_SLUN_C EQ 0
	ASSUME	MSCP$S_SLUN_C LE 8

	SUBB3	#^a/A/-1, -		; Put controller letter offset (1-26)
		DDB$T_NAME+3(R0), -	;  corresponding to (A-Z) in SLUN
		MSCP$W_SLUN_DEVNAME(R2)	;  area low order bits.

	ASSUME	<MSCP$V_MTYP_D1 + MSCP$S_MTYP_D1> EQ MSCP$V_MTYP_D0

	EXTZV	#MSCP$V_MTYP_D1, -	; Get D0/D1 from Media Id in UCB,
		#MSCP$S_MTYP_D1  -	;  corresponding to DDcu letters
		+MSCP$S_MTYP_D0, -	;  of device name.
		UCB$L_MEDIA_ID(R3), R0	; 

	ASSUME	<MSCP$V_SLUN_D1 + MSCP$S_SLUN_D1> EQ MSCP$V_SLUN_D0

	INSV	R0, #MSCP$V_SLUN_D1, -	; Put D0/D1 device name letters
		#MSCP$S_SLUN_D1  -	;  in SLUN devname field for
		+MSCP$S_SLUN_D0, -	;  server to use in looking up
		MSCP$W_SLUN_DEVNAME(R2)	;  the disk.

	MOVL	UCB$L_CDDB(R3), R1	; Get CDDB of intelligent controller.
	SEND_MSCP_MSG			; Send the GUS command

;
; Inputs:
;
;	R0	Status of send operation
;	R1	Size of command packet sent
;	R2	MSCP message buffer address
;	R3	CDDB  address
;	R4	PDT  address
;	R5	CDRP address
;

	.JSB_ENTRY INPUT=  <R0,R1,R2,R3,R4,R5>,-
		   SCRATCH=<R0,R1,R2,R3,R4,R5>

        CMPW	#SS$_ILLCDTST,R0
	BNEQ	115$			; Branch out if CDT error detected
	BRW	PACKACK_UNKNO_INOPR	; Branch assist for bad CDT
.                                                            

115$:	ASSUME	CDRP$V_CAND EQ 0
	BLBC	CDRP$L_DUTUFLAGS(R5), -	; If this command has not been canned,
		120$			;  check the status returned.
	BRW	PACKACK_ABORT		; If we got canned, return abort status
					;   regardless of command outcome.

120$:	ASSUME	MSCP$V_ST_MASK EQ 0
	BICW3	#^cMSCP$M_ST_MASK, -	; Extract major MSCP status.
		MSCP$W_STATUS(R2), R0
	DISPATCH R0, type=W, prefix=MSCP$K_ST_, < -
		<AVLBL,	125$>, -
		<SUCC,	128$>, -
		<ABRTD,	123$>, -
		<OFFLN,	PACKACK_OFFLINE> -
		>
.          
	BSBW	DU$INVALID_STS		; Unexpected MSCP end status.
	RSB

123$:	MOVL	#SS$_ABORT, R0		; Set aborted status
	BRW	END_PACKACK		;  and use the common exit.
.                                                            

125$:	MOVL	#SS$_MEDOFL, R0		; Set offline status
	BRB	130$			;  and join common code.

128$:	MOVL	#SS$_NORMAL, R0		; Set normal status and fall through.

;  We have the end message from the SLUN RSVP request.  Validate the fields
;  and update the unit number if everything's in order.

130$:	CMPW	#MSCP$K_SLUN_RSVP, -	; Does the end message still have the
		MSCP$W_UNIT(R2)		;  magic SLUN RSVP unit number?
	BNEQ	140$			; It isn't supposed to.
135$:	BUG_CHECK DISKCLASS, FATAL	; Bogus end message from MSCP emulator.

140$:	BBC	#MSCP$V_SLUN, -		; The SLUN bit had better be set in
		MSCP$W_UNIT(R2), 135$	;  the end message.
	MOVW	MSCP$W_UNIT(R2), -	; Update the MSCP unit number field
		UCB$W_MSCPUNIT(R3)	;  according to emulator end msg.
	MOVW	MSCP$W_UNIT(R2), -	; Update the SRV_MSCPUNIT field
		UCB$W_SRV_MSCPUNIT(R3)	;  as well.

	STALL_CALL_FORK,-			; Reset MSCP command packet
		ROUTINE = DUTU$RESET_MSCP_MSG,-	; 
		RES_LOC = R2

;	.DISABLE LSB

;  Do the ONLINE command for the PACKACK function.  The correct
;  unit number for this controller is in the command message regardless
;  of the method we arrived here.  Now attempt to actually bring the
;  device online to this host.

PACKACK_DO_ONLINE:

	MOVB	#MSCP$K_OP_ONLIN, -	; Transfer ONLINE opcode
		MSCP$B_OPCODE(R2)	;  to MSCP command packet.
	MOVW	UCB$W_UNIT_FLAGS(R3),-	; Copy unit flags from UCB
		MSCP$W_UNT_FLGS(R2)	;  to MSCP packet.
	MOVL	UCB$L_MSCPDEVPARAM(R3),-; Copy Device dependent parameters
		MSCP$L_DEV_PARM(R2)	;  into packet as well.

;  Check for MSCP format command sequence.

	BITL	#IO$M_MSCP_FORMAT, -	; Online preceeding a format?
		CDRP$L_FUNC(R5)		; 
	BEQL	10$			; If eql no
	BISW	#<MSCP$M_MD_IGNMF -	; Ignore media format errors
		!MSCP$M_MD_EXCAC>, -	;  and set device for exclusive access
		MSCP$W_MODIFIER(R2)	; An IO$_FORMAT command will follow

10$:	MOVL	UCB$L_CDDB(R3), R1	; Get CDDB of intelligent controller.
	SEND_MSCP_MSG			; Send the FORMAT command.

;
; Inputs:
;
;	R0	Status of send operation
;	R1	Size of command packet sent
;	R2	MSCP message buffer address
;	R3	CDDB  address
;	R4	PDT  address
;	R5	CDRP address
;

	.JSB_ENTRY INPUT=  <R0,R1,R2,R3,R4,R5>,-
		   SCRATCH=<R0,R1,R2,R3,R4,R5>

        CMPW	#SS$_ILLCDTST,R0
	BNEQ	20$			; If a CDT error is detected, there is
	BRW	PACKACK_UNKNO_INOPR	; no need to check the status returned
.                                                                       

20$:	BICL	#<UCB$M_VALID>, -	; Clear software volume valid bit
		UCB$L_STS(R3)

	ASSUME	MSCP$V_ST_MASK EQ 0
	BICW3	#^cMSCP$M_ST_MASK, -	; Extract major MSCP status.
		MSCP$W_STATUS(R2), R0
	DISPATCH R0, type=W, prefix=MSCP$K_ST_, < -
		<SUCC,	PACKACK_SUCC>, -
		<AVLBL,	PACKACK_AVLBL>, -
		<OFFLN,	PACKACK_OFFLINE>, -
		<ABRTD,	PACKACK_ABORT>, -
		<DRIVE,	PACKACK_DRVERR>, -
		<ICMD,	PACKACK_IVCMD>, -
		<MFMTE,	PACKACK_MFMTE>, -
		<DATA,	PACKACK_MFMTE>, -
		<CNTLR,	PACKACK_CTRLERR> -
		>
.          
	BSBW	DU$INVALID_STS		; Unexpected MSCP end status.
	RSB

PACKACK_SUCC:

;  The online has completed successfully.  Now test
;  for otherwise undetected errors during the ONLINE command.

	BBC	#MSCP$V_UF_576, -	; Is disk 576 bytes/sector?
		MSCP$W_UNT_FLGS(R2), 10$; Branch if not.
					; Encountered 576 bytes/sector disk.
	MOVZWL	#SS$_FORMAT, R0		; Get bad disk format error.
	BRW	PACKACK_UNDO_ONLINE	; Negate the successful ONLINE command.

10$:	EXTZV	#MSCP$V_MTYP_D1, -	; Get UCB's D0/D1 media id fields.
		#<MSCP$S_MTYP_D1 -
		 +MSCP$S_MTYP_D0>,-
		UCB$L_MEDIA_ID(R3), R0
	CMPZV	#MSCP$V_MTYP_D1, -	; Do the UCB's D0/D1 media id fields
		#<MSCP$S_MTYP_D1 -	; match those in the end message?
		 +MSCP$S_MTYP_D0>,-
		MSCP$L_MEDIA_ID(R2), R0
	BEQL	20$			; Continue if D0/D1 fields match.
	MOVZWL	#SS$_MEDOFL, R0		; Treat this like disk not online.
	BRW	PACKACK_UNDO_ONLINE	; Negate the successful ONLINE command.

20$:	ASSUME	CDRP$V_CAND EQ 0
	BLBC	CDRP$L_DUTUFLAGS(R5), -	; Check to make sure this request
		30$			;  was not canceled
	MOVZWL	#SS$_ABORT, R0		; Set canceled status.
	BRW	PACKACK_UNDO_ONLINE	; Negate the successful ONLINE command.

30$:	BSBW	RECORD_ONLINE		; Record ONLINE data in UCB.
	STALL_CALL_FORK,-			; Reset MSCP command packet
		ROUTINE = DUTU$RESET_MSCP_MSG,-	; 
		RES_LOC = R2

	MOVB	#MSCP$K_OP_GTUNT,-	; Opcode is for GET UNIT STATUS.
		MSCP$B_OPCODE(R2)	; 
	MOVL	UCB$L_CDDB(R3), R1	; Get CDDB of intelligent controller.
	SEND_MSCP_MSG			; Send the GUS command.

;
; Inputs:
;
;	R0	Status of send operation
;	R1	Size of command packet sent
;	R2	MSCP message buffer address
;	R3	CDDB  address
;	R4	PDT  address
;	R5	CDRP address
;

	.JSB_ENTRY INPUT=  <R0,R1,R2,R3,R4,R5>,-
		   SCRATCH=<R0,R1,R2,R3,R4,R5>

        CMPW	#SS$_ILLCDTST,R0
	BEQL	40$			; Branch out if CDT error detected

	IF_MSCP	SUCCESS then=PACKACK_GTUNT_SUCC  ; Branch if GTUNT successful.
40$:	ASSUME	CDRP$V_CAND EQ 0	; Else, ...
	BLBC	CDRP$L_DUTUFLAGS(R5), -	; Was I/O request canceled?
		50$			; Branch if request was canceled.
	MOVZWL	#SS$_ABORT, R0		; Set canceled status.
	BRW	PACKACK_UNDO_ONLINE	; Negate the successful ONLINE command.
.                                                                        

50$:	MOVZWL	#SS$_MEDOFL, R0		; Assume failure.
	INCW	CDRP$W_DUTUCNTR(R5)	; Bump count of retries.
	CMPW	#4, CDRP$W_DUTUCNTR(R5)	; Reached the limit?
	BGTRU	60$			; If not, continue.
	BRW	END_PACKACK		; If so, exit with error.
.                                                          

60$:	MOVAB	70$, R2			; Pass the return address
	BSBW	DUTU$RESET_MSCP_MSG	; Reset MSCP command packet
	RSB

;
; Inputs:
;
;	R2	MSCP message buffer address
;	R3	UCB  address
;	R5	CDDB address
;

70$:	.JSB_ENTRY INPUT=  <      R2,R3,   R5>,-
		   SCRATCH=<R0,R1,R2,R3,R4,R5>

	BRW	START_PACKACK		; Go try again.
.                                                

PACKACK_GTUNT_SUCC:

	BSBW	RECORD_UNIT_STATUS	; Record UNIT STATUS data in UCB.
	MOVL	#SS$_NORMAL, R0		; The PACKACK has been successful.

VALID_PACKACK:
	BISL	#<UCB$M_VALID>, -	; Set software volume valid.
.                                                             
		UCB$L_STS(R3)

END_PACKACK:

	.JSB_ENTRY INPUT=  <R0,      R3,R4,R5>,-
		   SCRATCH=<R0,R1,R2,R3,R4,R5>

	BBCC	#UCB$V_MSCP_PKACK, -	; Branch if PACKACK in progress is
		UCB$L_DEVSTS(R3), 10$	; set and set it.
	DECW	UCB$W_RWAITCNT(R3)	; Unbump wait count.
10$:	PUSHR	#^M<R0,R3,R4,R5>	; Save important registers.

	MOVL	R3, R5			; Setup UCB address.
	JSB	G^SCS$UNSTALLUCB	; Unstall I/O requests.
	POPR	#^M<R0,R3,R4,R5>	; Restore important registers.
	BLBS	R0, 20$			; Branch if PACKACK successful.
	TSTL	CDRP$L_PID(R5)		; Check for internal IRP.
	BLSS	20$			; Skip LCL_VALID cleanup if int. IRP.
	BBCC	#UCB$V_LCL_VALID, -	; Else, clear local valid bit and
		UCB$L_STS(R3), 20$	; branch if its already clear.
	DECB	UCB$B_ONLCNT(R3)	; Else, decrement the online count.
20$:	CLRL	R1			; Clear R1, for I/O status block.
	BSBW	FUNCTION_EXIT		; Packack function is done.
	RSB



	.SBTTL	PACKACK Errors

PACKACK_DRVERR:
	MOVL	#SS$_DRVERR, R0		; Show drive error status.
	BRW	END_PACKACK		; Exit PACKACK processing.

PACKACK_IVCMD:
	MOVL	#SS$_CTRLERR, R0	; Set the status.
	IVCMD_BEGIN			; Begin invalid command processing.
.                                                                    
	MOVB	#MSCP$K_OP_ONLIN, -	; Transfer ONLINE opcode
		MSCP$B_OPCODE(R2)	;  to MSCP command packet.
	MOVW	UCB$W_UNIT_FLAGS(R3),-	; Copy unit flags from UCB
		MSCP$W_UNT_FLGS(R2)	;  to MSCP packet.
	MOVL	UCB$L_MSCPDEVPARAM(R3),-; Copy Device dependent parameters
		MSCP$L_DEV_PARM(R2)	;  into packet as well.
	BITL	#IO$M_MSCP_FORMAT, -	; Online preceeding a format?
		CDRP$L_FUNC(R5)
	BEQL	20$			; If eql no
	BISW	#<MSCP$M_MD_IGNMF -	; Ignore media format errors and
		!MSCP$M_MD_EXCAC>, -	;  set device for exclusive access
		MSCP$W_MODIFIER(R2)	; An IO$_FORMAT command will follow
20$:	IVCMD_END			; Complete invalid command processing.
	BRW	END_PACKACK		; Complete PACKACK operation.
.                                                              

PACKACK_CTRLERR:
	MOVL	#SS$_CTRLERR, R0	; Show controller error status.
	BRW	END_PACKACK		; Complete PACKACK operation.
.                                                              

PACKACK_MFMTE:
	MOVL	#SS$_FORMAT, R0		; Set error status.
	BRW	END_PACKACK		; Exit PACKACK processing.
.                                                           

PACKACK_ABORT:
	MOVZWL	#SS$_ABORT, R0		; Show cancelled status.
	BRW	END_PACKACK		; Exit PACKACK processing.
.                                                           

PACKACK_UNDO_ONLINE:			; Must undo the effect of a successful 
					; MSCP online command.
	MOVL	R0, CDRP$L_MEDIA(R5)	; Save function error status.
	BICL	#CDRP$M_CAND, -		; Clear cancel flag to prohibit
		CDRP$L_DUTUFLAGS(R5)	;  second round of cancel processing.
	MOVAB	5$, R2			; Pass the return address
	BSBW	DUTU$RESET_MSCP_MSG	; Reset MSCP command packet
	RSB

;
; Inputs:
;
;	R2	MSCP message buffer address
;	R3	UCB  address
;	R5	CDDB address
;

5$:	.JSB_ENTRY INPUT=  <      R2,R3,   R5>,-
		   SCRATCH=<R0,R1,R2,R3,R4,R5>

	MOVB	#MSCP$K_OP_AVAIL, -	; Undo online with available command.
		MSCP$B_OPCODE(R2)	; 
	MOVL	UCB$L_CDDB(R3), R1	; Get CDDB of intelligent controller.
	SEND_MSCP_MSG			; Send the AVAIL command.

;
; Inputs:
;
;	R0	Status of send operation
;	R1	Size of command packet sent
;	R2	MSCP message buffer address
;	R3	CDDB  address
;	R4	PDT  address
;	R5	CDRP address
;

	.JSB_ENTRY INPUT=  <R0,R1,R2,R3,R4,R5>,-
		   SCRATCH=<R0,R1,R2,R3,R4,R5>

        CMPW	#SS$_ILLCDTST,R0	; No CDT error, continue to process
	BNEQ	10$			;  whatever error was returned.
	BRW	PACKACK_UNKNO_INOPR	; Branch out if CDT error detected

10$:	MOVL	CDRP$L_MEDIA(R5), R0	; Restore function error status
	BRW	END_PACKACK		;  and branch out.
.                                                   

PACKACK_OFFLINE:

	EXTZV	#MSCP$V_ST_SBCOD,-	; Extract MSCP end-packet status
		#MSCP$S_ST_SBCOD,-	;  subcode value.
		MSCP$W_STATUS(R2), R0
	DISPATCH R0, type=W, prefix=MSCP$K_SC_, < -
		<UNKNO,	PACKACK_MEDOFL>, -
		<NOVOL,	PACKACK_NOVOL>, -
		<INOPR,	PACKACK_MEDOFL>, -
		<DUPUN,	PACKACK_DUPUN>, -
		<UDSBL,	PACKACK_UNAVAILABLE>, -
		<EXUSE,	PACKACK_UNAVAILABLE> -
		>
.          
	MOVL	#SS$_DRVERR, R0		; Return drive error for anything else
	BRW	END_PACKACK		;  and branch out.
.                                                   

PACKACK_NOVOL:

	MOVL	#SS$_MEDOFL, R0		; Set the status
	BRW	END_PACKACK		;  and branch out.
.                                                   

PACKACK_MEDOFL:

	MOVL	#SS$_MEDOFL, R0		; Set the status add fall through.

PACKACK_UNKNO_INOPR:

	ASSUME	CDRP$V_CAND EQ 0
	BLBC	CDRP$L_DUTUFLAGS(R5),10$; If not cancelled, continue.
	BRW	PACKACK_ABORT		;  Otherwise, get out of here now.
.                                                                   
10$:	INCW	CDRP$W_DUTUCNTR(R5)	; Count this retry attempt.
	CMPW	#4, CDRP$W_DUTUCNTR(R5)	; Are the retries exhausted?
	BGTRU	PACKACK_FAILOVER_MSG	; Branch if retries remain.
	BRW	END_PACKACK		; Exit PACKACK processing.
.                                                           

PACKACK_DUPUN:

	MOVL	#SS$_DUPUNIT, R0	; Set the status
	BSBW	DUTU$SEND_DUPLICATE_UNIT; Send a message to the operator.
	BRW	END_PACKACK		; Exit PACKACK processing.
.                                                           

PACKACK_FAILOVER_MSG:

	MOVL	#1, R0			; Don't unmap port buffers.
	BSBW	DUTU$DEALLOC_ALL	; Deallocate RSPID and message buf.

PACKACK_FAILOVER:

	MOVAB	PF_AFTER_LOCATE_UNIT,R2	; Set up return address
	BSBW	DUTU$LOCATE_UNIT	; Find the failed unit.
	RSB

;
;	Upon completion, the UCB and CDRP input to DUTU_LOACTE_UNIT have 
;	the same connection relationships they had when the call was made.
;	In particular, the following registers and fields are unchanged:
;
;		R4 (PDT address)
;		UCB$L_PDT
;		UCB$L_CDT
;		CDRP$L_CDT
;
;	If the status returned by DUTU$LOCATE_UNIT is SS$_NORMAL, the 
;	(possibly) new MSCP unit number (for MSCP served devices) is now 
;	in CDRP$W_ENDMSGSIZ(R5) for use in subsequent failover processing.
;
;	Any SCS resources held by the CDRP on input to DUTU$LOCATE_UNIT 
;	are now deallocated.
;
; Inputs:
;
;	R0	Status
;		   SS$_NORMAL means unit was located
;		   SS$_MEDOFL means unit was not located
;		   SS$_ABORT  means the request has been canceled
;	R2	CDDB address of connection on which unit was located (or zero)
;	R3	UCB address
;	R4	PDT address (same as input)
;	R5	CDRP address
;

PF_AFTER_LOCATE_UNIT:

	.JSB_ENTRY INPUT=   <R0,   R2,R3,R4,R5>,-
		   OUTPUT=  <>,-
		   SCRATCH= <R0,R1,R2,R3,R4,R5>,-
		   PRESERVE=<>

	BLBC	R0, END_PACKACK		; Exit PACKACK processing
.                                                          
	BSBW	DUTU$MOVE_UNIT		; Move unit to place just located.
	BLBC	R0, END_PACKACK		; Exit PACKACK processing
.                                                          
	BSBW	SEND_IO			; Otherwise, retry the PACKACK.
	RSB

;
; Somebody else has the unit in use; either diagnostics or exclusive 
; access.  An available attention message is sent when they 
; release the unit.  So make it offline, for now.
;
PACKACK_UNAVAILABLE:
	MOVL	#SS$_DEVNOTSHR, R0	; Set status to "not shareable".
	BICL	#DEV$M_AVL, -		; Clear unit available for use flag
		UCB$L_DEVCHAR(R3)	; in the UCB.
	BRW	END_PACKACK		; Exit PACKACK processing.
.                                                           

PACKACK_AVLBL:

	BBS	#MSCP$V_SC_ALUSE,-	; Return "device not shareable" error
.                                                                      
		MSCP$W_STATUS(R2),-	;  if it is already marked as "in use".
		PACKACK_UNAVAILABLE
	MOVL	#SS$_MEDOFL, R0		; Otherwise return "media offline" 
	BRW	END_PACKACK		;  status and branch out.
.                                                          



	.SBTTL	RECORD_ONLINE - copy data form ONLINE end message to UCB.
;++
;
; RECORD_ONLINE - copy data form ONLINE end message to UCB.
;
; Inputs:
;
;	R2	End Message
;	R3	UCB
;
; Outputs:
;
;	R0 is destroyed
;	UCB fields set
;	Load available field in the CDDB is decremented. This will cause
;	connection walking to round robin between equally loaded servers.
;
;--

DU$RECORD_ONLINE::
            
RECORD_ONLINE:

	.JSB_ENTRY INPUT=   <      R2,R3      >,-
		   SCRATCH= <R0               >,-
		   PRESERVE=<>

	MOVQ	MSCP$Q_UNIT_ID(R2),-	; In the event of success, copy unit
		UCB$Q_UNIT_ID(R3)	;  characteristics data to UCB.
.                                                                
	MOVL	MSCP$L_MEDIA_ID(R2),-	; Starting with the UNIT ID, followed
		UCB$L_MEDIA_ID(R3)	;  by the media identifier and
	BSBW	DUTU$GET_DEVTYPE	;  device type and 
	MOVL	MSCP$L_UNT_SIZE(R2),-	; Then followed by the unit size and
		UCB$L_MAXBLOCK(R3)
	MOVL	MSCP$L_VOL_SER(R2),-	; Then by the volume serial number.
		UCB$L_DU_VOLSER(R3)
	MOVW	MSCP$W_UNT_FLGS(R2),-	; Copy new unit flags from end packet.
		UCB$W_UNIT_FLAGS(R3)
	MOVL	UCB$L_CDDB(R3), R0	; Pick up CDDB.
	DECW	CDDB$W_LOAD_AVAIL(R0)	; Decrement current load available.
	RSB				; Return to caller.



	.SBTTL	RECORD_UNIT_STATUS - copy data from GUS end message to UCB.
;++
;	RECORD_UNIT_STATUS - copy data from GET UNIT STATUS end message to UCB.
;
; Functional Description:
;
;	The supplied MSCP end message is analyzed and appropriate fields in 
;	the UCB are filled in with information contained in the end message.
;
;	If the end message is shorter than MSCP$K_LEN, it is zero filled 
;	to that length.  This compensates for controllers possibly passing 
;	some fields back as zeros by returning short messages.  Then various 
;	geometry parameters are copied or calculated from the geometry 
;	information in the end message.  If the basic cylinders/tracks/sectors 
;	information produced by these calulations contains any zeros, a class 
;	driver bugcheck is declared.  Finally, the two write-locked bits are 
;	tested.  If either is set, the DEV$M_SWL bit is set.  Otherwise, the 
;	bit is not set.
;
; Inputs:
;
;	R2	address of MSCP get unit status end message
;	R3	UCB address
;	R5	CDRP address
;
;	CDRP$W_ENDMSGSIZ(R5) size of MSCP end message
;
; Outputs:
;
;	R0 & R1	destroyed
;
;	The following UCB fields are altered:
;
;	UCB$W_DU_LBNPTRK	LBNs per track
;	UCB$W_DU_TRKPGRP	tracks per group
;	UCB$W_DU_GRPPCYL	groups per cylinder
;	UCB$W_DU_RCTSIZE	LBNs in the RCT
;	UCB$B_DU_RBNPTRK	RBNs per track
;	UCB$B_DU_RCTCPYS	number of RCT copies
;	UCB$L_DU_TOTSIZ		LBNs plus RCT blocks
;	UCB$B_SECTORS		sectors per track (must not be zero)
;	UCB$B_TRACKS		tracks per cylinder (must not be zero)
;	UCB$W_CYLINDERS		cylinders per volume (must not be zero)
;	UCB$W_DU_MUNTC		multi unit code
;	UCB$B_DU_USVR		unit software version
;	UCB$B_DU_UHVR		unit hardware version
;
;--

DU$RECORD_UNIT_STATUS::
                 
RECORD_UNIT_STATUS:

	.JSB_ENTRY INPUT=   <      R2,R3,   R5>,-
		   SCRATCH= <R0,R1            >,-
		   PRESERVE=<>

	MOVZWL	CDRP$W_ENDMSGSIZ(R5), R1; Get size of end message received.
	BSBW	DUTU$FILL_MSCP_MSG	; Zero fill the MSCP message.

10$:	MOVW	MSCP$W_TRACK(R2),-	; Copy date from end message to UCB.
		UCB$W_DU_LBNPTRK(R3)	; LBN's per track.
	MOVW	MSCP$W_GROUP(R2),-	; Then tracks per group.
		UCB$W_DU_TRKPGRP(R3)
	MOVW	MSCP$W_CYLINDER(R2),-	; Groups per cylinder.
		UCB$W_DU_GRPPCYL(R3)
	MOVL	UCB$L_MAXBLOCK(R3),-	; Unit size in LBNs.
		UCB$L_DU_USIZE(R3)
	MOVW	MSCP$W_RCT_SIZE(R2),-	; Size of the RCT in blocks.
		UCB$W_DU_RCTSIZE(R3)
	MOVB	MSCP$B_RBNS(R2),-	; RBN's per track.
		UCB$B_DU_RBNPTRK(R3)
	MOVW	MSCP$W_MULT_UNT(R2),-	; Multi unit code
		UCB$W_DU_MUNTC(R3)

	ASSUME	MSCP$B_UNIT_HVR EQ MSCP$B_UNIT_SVR+1
	ASSUME	UCB$B_DU_UHVR   EQ UCB$B_DU_USVR+1
	MOVW	MSCP$B_UNIT_SVR(R2),-	; Unit software and hardware
		UCB$B_DU_USVR(R3)	; version

	BISL	#DEV$M_RCT, -		; Assume this unit has an RCT.
		UCB$L_DEVCHAR(R3)
	MOVZBL	MSCP$B_RCT_CPYS(R2), R0	; R0 = # of RCT copies on the unit.
	BNEQ	15$			; Branch if unit has an RCT.
	BICL	#DEV$M_RCT, -		; Else, signal that unit has no RCT.
		UCB$L_DEVCHAR(R3)
15$:	MOVB	R0, UCB$B_DU_RCTCPYS(R3); Save number of RCT copies on unit.

	MULW2	MSCP$W_RCT_SIZE(R2),R0	; R0 = size of RCT area.
	ADDL3	UCB$L_MAXBLOCK(R3),R0,-	; Size of user visible area plus size
		UCB$L_DU_TOTSZ(R3)	;  of RCT area is total size.

	MOVB	MSCP$W_TRACK(R2),-	; Init UCB devdepend fields.
		UCB$B_SECTORS(R3)	; Tracksize is number of sectors.
	BEQL	999$			; Branch to bugcheck if zero.
	CLRL	R0			; Clear Product.
	MULW3	MSCP$W_GROUP(R2),-	; Group size * cylinder size =
		MSCP$W_CYLINDER(R2),R0	;  # of tracks in a cylinder.
	MOVB	R0,UCB$B_TRACKS(R3)
	BEQL	999$			; Branch to bugcheck if zero.
	MULW2	MSCP$W_TRACK(R2),R0	; * #sectors/track = #sectors/cyl
	CLRL	-(SP)			; Put UCB$L_MAXBLOCK into low order
	PUSHL	UCB$L_MAXBLOCK(R3)	;  longword of workarea on stack.
	EDIV	R0,(SP)+,R0,R1		; UCB$L_MAXBLOCK/R0 ... quotient -> R0,
					;  remainder -> R1, workarea popped off
					;  stack.
	TSTL	R1			; Test for any remainder.
	BEQL	25$			; EQL means no remainder.
	INCL	R0			; If remainder, round quotient up.
25$:	MOVW	R0,UCB$W_CYLINDERS(R3)	; Store number of cylinders on disk.
	BEQL	999$			; Branch to bugcheck if zero.

	BICL	#UCB$M_MSCP_WRTP, -	; Clear class driver write protected
		UCB$L_DEVSTS(R3)	; flag.
	BITW	#<MSCP$M_UF_WRTPD -	; Is the unit data loss,
		 !MSCP$M_UF_WRTPH -	; hardware, or
		 !MSCP$M_UF_WRTPS>,-	; software write protected?
		MSCP$W_UNT_FLGS(R2)
	BEQL	60$			; Branch if not write protected.
	BISL	#UCB$M_MSCP_WRTP, -	; Else, set the class driver write
		UCB$L_DEVSTS(R3)	; protected flag.

60$:
;
; Check SCSI parameters.
;
        BICL    #DEV$M_NOFE,-           ; Assume success.
                UCB$L_DEVCHAR2(R3)      ;
        BBC     #DEV$V_SCSI,-           ; If not scsi disk, skip check.
                UCB$L_DEVCHAR2(R3),70$  ;
        BBSS    #MSCP$V_UF_REPLC,-      ; Does it support Forced Error?
                UCB$W_UNIT_FLAGS(R3),70$;
        BISL    #DEV$M_NOFE,-           ; Set NO Forced Error bit in UCB.
                UCB$L_DEVCHAR2(R3)      ;
70$:
;
; Check HBVS write history logging parameters.
;
        BICL    #DEV$M_WLG, -           ; Init the bit first.
                UCB$L_DEVCHAR2(R3)      ;
	BICW	#MSCP$M_UF_WHL,-	; And again...
		UCB$W_UNIT_FLAGS(R3)	;
        BBCC	#MSCP$V_UF_WHL,-        ; Branch if unit doesn't support
                MSCP$W_UNT_FLGS(R2), 80$;  write history logging.
	MOVL	UCB$L_CDDB(R3),R0	; Pick up CDDB address
	CMPB	#MSCP$K_CM_EMULA,-	; If this unit had the wlg flag set
		CDDB$B_CNTRLMDL(R0)	;  in an end message from the emulator,
	BEQL	75$			;  set the UCB wlg bits.
	BBC	#MSCP$V_CF_WHL,-	; Branch if controller does not
		CDDB$W_CNTRLFLGS(R0),80$;  support WHL.
75$:	BISL    #DEV$M_WLG, -           ; Set the bit.
                UCB$L_DEVCHAR2(R3)      ;
        BISW    #MSCP$M_UF_WHL,-        ; And again, again...
                UCB$W_UNIT_FLAGS(R3)    ;
;
; Exit
;
80$:	RSB				; Return to caller

999$:	BUG_CHECK DISKCLASS, FATAL	; This bugcheck is here to catch MSCP
					; servers which return invalid
					; geometry information from a 
					; Get Unit Status to an ONLINE drive
					; red-handed.



	.SBTTL	START_UNLOAD, START_AVAILABLE and START_SETPATH
;++
;
;	START_AVAILABLE - Prepare an MSCP packet to do an AVAILABLE command 
;			  without the spindown modifier.
;
;	START_UNLOAD -	Prepare an MSCP packet to do an AVAILABLE command with
;			spindown specified.
;
;	START_SETPATH - Prepare and MSCP packet to do an AVAILABLE command with
;			the all-hosts modifier, and not clear the valid bit.
;
; Inputs:
;
;	R1	I/O function code & modifiers
;	R2	MSCP buffer
;	R3	UCB address
;	R4	PDT address
;	R5	CDRP address
;
;	The MSCP packet contains zeros except for:
;
;	MSCP$L_CMD_REF(R2)	<== CDRP$L_RSPID(R5)
;	MSCP$W_UNIT(R2)		<== UCB$W_MSCPUNIT(R3)
;
;--

START_SETPATH:

	BISL	#IO$M_ALLHOSTS, R1	; Set the all hosts modifier,
	BRB	START_AVAILABLE		;  and join the common code.

START_UNLOAD:

	MOVW	#MSCP$M_MD_SPNDW,-	; Specify the SPINDOWN bit in the
		MSCP$W_MODIFIER(R2)	;  modifier word.

START_AVAILABLE:

	MOVB	#MSCP$K_OP_AVAIL,-	; Transfer AVAILABLE opcode
		MSCP$B_OPCODE(R2)	;  to packet.
	BBC	#IO$V_ALLHOSTS, R1,-	; Branch if all hosts modifier absent.
		SEND_AVAILABLE
	BISW	#MSCP$M_MD_ALLCD, -	; Else, set the all class drivers
		MSCP$W_MODIFIER(R2)	; modifier.

SEND_AVAILABLE:

	MOVL	UCB$L_CDDB(R3), R1	; Get CDDB of intelligent controller.
	SEND_MSCP_MSG			; Send the AVAIL command.

;
; Inputs:
;
;	R0	Status of send operation
;	R1	Size of command packet sent
;	R2	MSCP message buffer address
;	R3	CDDB  address
;	R4	PDT  address
;	R5	CDRP address
;

	.JSB_ENTRY INPUT=  <R0,R1,R2,R3,R4,R5>,-
		   SCRATCH=<R0,R1,R2,R3,R4,R5>

        CMPW	#SS$_ILLCDTST,R0
	BEQL	AVAILABLE_CDTERR	; Branch out if CDT error detected

	ASSUME	IO$_UNLOAD EQ 1		; Odd
	ASSUME	IO$_AVAILABLE EQ 17	; Odd
	ASSUME	IO$_SETPRFPATH EQ 18	; Even (for BLBC...)
;GH X-91 (already checked in, placeholder)
	MOVL	CDRP$L_FUNC(R5), R1	; If this is SETPRFPATH, leave volume
	BLBC	R1,60$			;  valid so that mount ver will trigger
					;  failover.
	BICL	#UCB$M_VALID,-		; Clear software volume valid.
		UCB$L_STS(R3)

60$:	ASSUME	MSCP$V_ST_MASK EQ 0
	BICW3	#^cMSCP$M_ST_MASK, -	; Extract major MSCP status.
		MSCP$W_STATUS(R2), R0
	DISPATCH R0, type=W, prefix=MSCP$K_ST_, < -
		<SUCC,	AVAILABLE_SUCC>, -
		<AVLBL,	AVAILABLE_OFFLINE>, -
		<OFFLN,	AVAILABLE_OFFLINE>, -
		<ABRTD,	AVAILABLE_ABORT>, -
		<DRIVE,	AVAILABLE_DRVERR>, -
		<CNTLR,	AVAILABLE_CTRLERR>, -
		<ICMD,	AVAILABLE_IVCMD> -
		>
	BSBW	DU$INVALID_STS		; Unexpected MSCP end status.
	RSB

AVAILABLE_CDTERR:
	MOVZWL	#SS$_DEVOFFLINE,R0
	CLRL	R1			; Clear for I/O status block.
	BSBW	FUNCTION_EXIT		; Branch to common exit.
	RSB

AVAILABLE_OFFLINE:
	MOVL	#SS$_MEDOFL, R0		; Set the status
	BRW	AVAILABLE_EXIT		;  and branch out.

AVAILABLE_ABORT:
	MOVL	#SS$_ABORT, R0		; Set the status
	BRW	AVAILABLE_EXIT		;  and branch out.

AVAILABLE_DRVERR:
	MOVL	#SS$_DRVERR, R0		; Set the status
	BRW	AVAILABLE_EXIT		;  and branch out.

AVAILABLE_CTRLERR:
	MOVL	#SS$_CTRLERR, R0	; Set the status
	BRW	AVAILABLE_EXIT		;  and branch out.

AVAILABLE_IVCMD:
	MOVL	#SS$_CTRLERR, R0	; Default status is controller error
	IVCMD_BEGIN			; Begin invalid command processing.
.                                                                    
	MOVL	CDRP$L_FUNC(R5), R1	; If this is SETPRFPATH, leave volume
	ASSUME	IO$V_FCODE EQ 0
	BICW3	#^cIO$M_FCODE, R1, R0	; Extract just the I/O function code.
	MOVB	#MSCP$K_OP_AVAIL,-	; Transfer AVAILABLE opcode
		MSCP$B_OPCODE(R2)	;  to packet.
	CMPW	R0, #IO$_SETPRFPATH	; If this was a perfered path request,
	BEQL	10$			;  always set the ALLCD modifier.
	BBS	#IO$V_ALLHOSTS, R1, 20$	; Branch if all hosts modifier present.
10$:	BISW	#MSCP$M_MD_ALLCD, -	; Else, set the all class drivers
		MSCP$W_MODIFIER(R2)	; modifier.
20$:	IVCMD_END			; Complete invalid command processing.
	BRB	AVAILABLE_EXIT

AVAILABLE_SUCC:				; Action routine for MSCP$K_ST_SUCC.
;GH X-92, X-95
	BLBS	CDRP$L_FUNC(R5),-	; If not a forcepath request, we are
		AVAILABLE_DONE		;  done.
	BBS	#UCB$V_MSCP_IGNSRV,-	; We will wait for an incoming AVATN
		UCB$L_DEVSTS(R3),-	;  if there are two direct paths.
		AVAILABLE_DONE
	MOVL	R5,R4			; Otherwise, juggle registers to
	MOVL	R3,R5			;  TRIGGER_MV's liking and go start
	BSBW	DUTU$TRIGGER_MV		;  mount ver on this node
	MOVL	R5,R3			; Unjuggle registers and exit.
	MOVL	R4,R5
	MOVL	UCB$L_PDT(R3),R4
AVAILABLE_DONE:
;GH end
	MOVL	#SS$_NORMAL, R0		; 

AVAILABLE_EXIT:
	BICL	#UCB$M_MSCP_WRTP, -	; Also clear class driver write 
		UCB$L_DEVSTS(R3)	; protected flag.
	CLRL	R1			; Clear R1, for I/O status block.
	BSBW	FUNCTION_EXIT
	RSB


	.SBTTL	START_DISK_COPY_DATA - Start a Disk Copy Data Command
;
; START_DISK_COPY_DATA - Prepare an MSCP packet to do a Disk Copy Data command.
;
; INPUTS:
;	R1 => I/O function code & modifiers
;	R2 => MSCP buffer
;	R3 => UCB
;	R4 => PDT
;	R5 => CDRP
;
;	The MSCP packet contains zeros except for:
;
;	MSCP$L_CMD_REF(R2)	<== CDRP$L_RSPID(R5)
;	MSCP$W_UNIT(R2)		<== UCB$W_MSCPUNIT(R3)
;
START_DISK_COPY_DATA:
;
; Initialize opcode and modifier field.
;
	MOVB	#MSCP$K_OP_DCD,-	; Transfer DISK COPY DATA opcode
		MSCP$B_OPCODE(R2)	;  to packet.
	CLRW	MSCP$W_MODIFIER(R2)	; Clear all modifiers.
;
; Check modifiers.
;
	; Establish Communication Path
	BBC	#IO$V_EST_COM_PATH,R1,10$
	BISW	#MSCP$M_MD_ESTCP,MSCP$W_MODIFIER(R2)
10$:
	; Local Source Unit
	BBC	#IO$V_LCL_SRC_UNIT,R1,20$
	BISW	#MSCP$M_MD_LOCSU,MSCP$W_MODIFIER(R2)
20$:
	; Retain Communication Path
	BBC	#IO$V_RTN_COM_PATH,R1,30$
	BISW	#MSCP$M_MD_RETCP,MSCP$W_MODIFIER(R2)
30$:
;
; Best effort consistency check.
;
	; Check source ucb.
	TSTL	CDRP$L_DCD_SRC_UCB(R5)		; Is there a ucb?
	BEQL	50$				;

	; Local Copy Controller Check
	BBC	#IO$V_LCL_SRC_UNIT,R1,40$	; Skip if not a local copy.

	MOVL	CDRP$L_DCD_SRC_UCB(R5),R0	; Load source device UCB.
	CMPL	UCB$L_CDDB(R0),UCB$L_CDDB(R3)	; Make sure the source and
	BNEQ	50$				;  target has the same controller.

	MOVL	UCB$L_CDDB(R3),R0		; Get the target controller cddb.
	BITW	#CDDB$M_NOCONN,-		; Is there a connection?
		CDDB$L_STATUS(R0)		;
	BNEQ	50$				; NOCONN set, get out of here.
 	BITW	#MSCP$M_CF_LDCD,-		; Check the controller's
		CDDB$W_CNTRLFLGS(R0)		;  local copy flag.
	BEQL	50$				; EQL not capable.

	BRB	60$
40$:

	; Remote Copy Controller Check

	MOVL	UCB$L_CDDB(R3),R0		; Get the target controller cddb.
	BITW	#CDDB$M_NOCONN,-		; Is there a connection?
		CDDB$L_STATUS(R0)		;
	BNEQ	50$				; NOCONN set, get out of here.
 	BITW	#MSCP$M_CF_RDCD,-		; Check the controller's
		CDDB$W_CNTRLFLGS(R0)		;  remote copy flag.
	BEQL	50$				; EQL not capable.

	MOVL	CDRP$L_DCD_SRC_UCB(R5),R0	; Load source device UCB.
	MOVL	UCB$L_CDDB(R0),R0		; Get CDDB of source controller.
	BITW	#CDDB$M_NOCONN,-		; Is there a connection?
		CDDB$L_STATUS(R0)		;
	BNEQ	50$				; NOCONN set, get out of here.

	BRB	60$

	; Branch Assist to DCD_ERROR
50$:	BRW	DCD_ERROR
                   
60$:
;
; Fill in MSCP command.
;
	; Logical Block Count
	ASHL	#-9,CDRP$L_BCNT(R5),MSCP$L_LBCOUNT(R2)

	; LBN
	MOVL	CDRP$L_MEDIA(R5),MSCP$L_SRC_LBN(R2)
	MOVL	CDRP$L_MEDIA(R5),MSCP$L_DEST_LBN(R2)

	; Source Device Unit Number
	MOVL	CDRP$L_DCD_SRC_UCB(R5),R0
	MOVW	UCB$W_UNIT(R0),MSCP$W_SRC_UNUM(R2)

	; Source Device Unit Identification
	MOVQ	UCB$Q_UNIT_ID(R0),MSCP$Q_SRC_UID(R2)
.                                             

	; Source Device Port Address
	MOVL	UCB$L_CDT(R0),R0
	CLRQ	MSCP$Q_PORT_ADR(R2)
.                            
	MOVB	CDT$B_RSTATION(R0),MSCP$Q_PORT_ADR+2(R2)

	; Source Device SCA System Address
	MOVL	CDT$L_PB(R0),R0
	MOVL	PB$L_SBLINK(R0),R0
	MOVL	SB$B_SYSTEMID(R0),  MSCP$Q_SYS_ADR(R2)
	MOVZWL	SB$B_SYSTEMID+4(R0),MSCP$Q_SYS_ADR+4(R2)
;
; Check for invalid command processing.
;
	.MACRO	IF_IVCMD then, ?skip
.                             
	IF_IVCMD then=DCD_IVCMD_END	; Branch if invalid command processing.
;
; Send the MSCP message.
;
	.MACRO	SEND_DCD_MSG, ?main, ?cdterr, ?nolog
.                                             
	SEND_DCD_MSG			; Send message to the MSCP server.


	.JSB_ENTRY INPUT=  <R0,R1,R2,R3,R4,R5>,-
		   SCRATCH=<R0,R1,R2,R3,R4,R5>
;
; Special case CDT errors.
;
        CMPW	#SS$_ILLCDTST,R0
	BEQL	DCD_ERROR		; Branch out if CDT error detected
;
; Process communication status flag first.
;
	BBS	#MSCP$V_EF_CPRET,-	; Check end flag for communication
		MSCP$B_FLAGS(R2),70$	;  path end flag.
	BICW	#IO$M_RTN_COM_PATH,-	; Clear CDRP modifier to notify caller.
		CDRP$L_FUNC(R5)		;
70$:
;
; Process status.
;
	MOVL	#SS$_NORMAL, R0		; Set success status.
	EXTZV	#MSCP$V_ST_MASK,#MSCP$S_ST_MASK,- ; Extract the status-code only.
		MSCP$W_STATUS(R2),R1
	CMPW	#MSCP$K_ST_SUCC,R1	; Is the command successful?
	BEQL	DCD_SUCC		; Yes, setup status and exit.

	CMPW	#MSCP$K_ST_SBCERR,R1	; Subcommand error?
	BNEQ	DCD_ERROR		; No, exit with device offline.

	; Is the subcommand error a forced-error?

	CMPW	#<MSCP$K_ST_DATA+MSCP$K_SC_FRCER>,-	
		<MSCP$Z_SBC_STS+MSCP$W_STATUS-MSCP$W_UNIT>(R2)
	BNEQ	DCD_ERROR

	MOVL	#SS$_FORCEDERROR,R0	; Return forced-error
	BRB	DCD_SUCC		;

DCD_ERROR:
	MOVL	#SS$_DEVOFFLINE,R0	; Anything else return
					;  device offline.
	CLRL	MSCP$L_LBCOUNT(R2)	; Zero bytes transferred

DCD_SUCC:
	ASHL	#16,R0,R0		; Shift status one word left.
	ASHL	#9,MSCP$L_LBCOUNT(R2),R1; Get # bytes actually transferred.
	ASHQ	#-16,R0,R0		; Shift into proper position for IOSB.

	BRW	FUNCTION_EXIT
.                      

;DCD_IVCMD:
;	IVCMD_BEGIN			; Begin invalid command processing.
;	BRW	DU$DISPATCH_CMD		; Rebuild failing MSCP packet.

DCD_IVCMD_END:
	IVCMD_END			; Complete invalid command processing.
	BRB	DCD_SUCC		; Fall through to complete disk copy
					; data operation.

	.SBTTL	START_WHM - Start a Write History Management Command
;
; START_WHM - Start a Write History Management Command
;
;	The entry handles all whm operations plus the break connection
;	command.  The break connection command is signaled by setting
;	the break_conn I/O modifier.
;
; INPUTS:
;	R1 => I/O function code & modifiers
;	R2 => MSCP buffer
;	R3 => UCB
;	R4 => PDT
;	R5 => CDRP
;
;	The MSCP packet contains zeros except for:
;
;	MSCP$L_CMD_REF(R2)	<== CDRP$L_RSPID(R5)
;	MSCP$W_UNIT(R2)		<== UCB$W_MSCPUNIT(R3)
;
START_WHM:

;
; Initialize opcode and modifier field.
;
	MOVB	#MSCP$K_OP_WRHIM,-	; Transfer WRITE HISTORY MANAGEMENT opcode
		MSCP$B_OPCODE(R2)	;  to packet.
	CLRW	MSCP$W_MODIFIER(R2)	; Clear all modifiers.
;
; Determine operation.
;
	; Deallocate All Operation
	BBC	#IO$V_DEALC_ALL,R1,10$
	MOVW	#MSCP$K_WHM_DALL,MSCP$W_OPER(R2)
	BRB	100$
10$:
	; Deallocate by Host Reference Number Operation
	BBC	#IO$V_DEALC_HRN,R1,20$
	MOVW	#MSCP$K_WHM_DHRN,MSCP$W_OPER(R2)
	BRB	100$
20$:
	; Deallocate by Entry Locator Operation
	BBC	#IO$V_DEALC_ENTLOC,R1,30$
	MOVW	#MSCP$K_WHM_DELO,MSCP$W_OPER(R2)
	BRB	100$
30$:
	; Decrement Allocation Failure Count
	BBC	#IO$V_DECR_AFC,R1,40$
	MOVW	#MSCP$K_WHM_DAFC,MSCP$W_OPER(R2)
	BRB	100$
40$:
	; Read All
	BBC	#IO$V_READ_ALL,R1,50$
	MOVW	#MSCP$K_WHM_RALL,MSCP$W_OPER(R2)
	BRB	90$
50$:
	; Read by HRN
	BBC	#IO$V_READ_HRN,R1,60$
	MOVW	#MSCP$K_WHM_RHRN,MSCP$W_OPER(R2)
	BRB	90$
60$:
	; Break Connection
	BBC	#IO$V_BREAK_CONN,R1,70$
	MOVB	#MSCP$K_OP_TERCO,-	; Terminate Connection.
		MSCP$B_OPCODE(R2)	;
	CLRW	MSCP$W_MODIFIER(R2)	; Clear all modifiers.
	BRB	100$
70$:
;
; Skip buffer setup.
;
	BRB	100$
;
; Set up mscp command.
;
90$:    ;Check for zero bcnt transfers.
	TSTL	CDRP$L_BCNT(R5)		; is it zero?
	BEQL	100$			; don't bother mapping.
	; Map buffer for transfer.
	MOVAB	CDRP$T_LBUFHNDL(R5),-	; Put address of Local BUFfer HaNDLe
		CDRP$L_LBUFH_AD(R5)	;  field into field that points to it.
	.MACRO	MAP_IRP
.                
	MAP_IRP				; Allocate mapping resources and load
					;  them with data from SVAPTE, BOFF,
					;  and BCNT derived from IRP within
					;  CDRP.
	MOVL	CDRP$L_MSG_BUF(R5),R2	; Refresh R2 => MSCP packet.
	MOVQ	CDRP$T_LBUFHNDL(R5),-	; Copy contents of buffer handle to
		MSCP$B_BUFFER(R2)	;  MSCP buffer descriptor field.
	MOVL	CDRP$T_LBUFHNDL+8(R5),-	; Buffer handle is 96 bits (12 bytes)
		MSCP$B_BUFFER+8(R2)	;  in length.

	; Load byte count and offset.
	MOVL	CDRP$L_BCNT(R5),-	; Copy byte count of transfer
		MSCP$L_BYTE_CNT(R2)	;  and offset.
	MOVW	CDRP$L_MEDIA(R5),-	;
		MSCP$W_OFFSET(R2)	;


100$:	; Transfer entry id/ hrn into packet
	MOVL	CDRP$L_CLN_WLE(R5),-	; Move the write log entry.
		MSCP$W_HRN(R2)		;

;
; Check for invalid command processing.
;
	IF_IVCMD then=WHM_IVCMD_END	; Branch if invalid command processing.
.                                                                        
;
; Send the MSCP message.
;
	MOVL	UCB$L_CDDB(R3),R1	; CDDB for DUTU$SEND_MSCP_MSG
	SEND_MSCP_MSG			; Send message to the MSCP server.


	.JSB_ENTRY INPUT=  <R0,R1,R2,R3,R4,R5>,-
		   SCRATCH=<R0,R1,R2,R3,R4,R5>

;
; Special case CDT errors.
;
        CMPW	#SS$_ILLCDTST,R0
	BEQL	WHM_CDTERR		; Branch out if CDT error detected
;
; Dispatch on status.
;
	CLRQ	R0			; Init registers

	ASSUME	MSCP$V_ST_MASK EQ 0
	BICW3	#^cMSCP$M_ST_MASK, -	; Extract major MSCP status.
		MSCP$W_STATUS(R2), R0
	DISPATCH R0, type=W, prefix=MSCP$K_ST_, < -
		<SUCC, WHM_SUCC>, -
		<ICMD, WHM_IVCMD>, -
		<OFFLN, WHM_OFFLN>, -
		<HSTBF, WHM_HSTBF>, -
		<WHEAE, WHM_ENTRY_ACCESS_ERROR>, -
		<ABRTD, WHM_ABORTED> -
		>

	BRW	DU$INVALID_STS		; Unexpected MSCP end status.
.                                                              

WHM_IVCMD:
	MOVZBL	MSCP$W_STATUS+1(R2), R0	; Extract MSCP end-packet status
					;  subcode value (high byte only).
	DISPATCH R0, type=W, prefix=MSCP$, < -
		<L_LBN,		WHM_IVCMD_LBN>, -
		<L_BYTE_CNT,	WHM_IVCMD_BCNT>, -
		<W_MODIFIER,	WHM_IVCMD_MODIFIER>, -
		<W_HRN,	    	WHM_IVCMD_WLOG>,-
		<W_ENT_ID,	WHM_IVCMD_WLOG> -
		>
	MOVZWL	#SS$_CTRLERR, R0	; Default status is controller error
	BRB	WHM_IVCMD_BEGIN		; If no match.

WHM_IVCMD_BCNT:
	MOVZWL	#SS$_IVBUFLEN, R0	; Set up status to return.
	BRB	WHM_IVCMD_BEGIN		; Merge

WHM_IVCMD_LBN:
	MOVZWL	#SS$_IVADDR, R0		; Set up status to return.
	BRB	WHM_IVCMD_BEGIN		; Merge

WHM_IVCMD_WLOG:
	MOVZWL	#SS$_RESET, R0		; Set up status to return.
	BRB	WHM_IVCMD_COMMON	; Merge
.                                        

WHM_IVCMD_MODIFIER:
	MOVZWL	#SS$_ILLIOFUNC, R0	; Default status is invalid bufer length

WHM_IVCMD_BEGIN:

	IVCMD_BEGIN			; Begin invalid command processing.
.                                                                    
	BRW	DU$DISPATCH_CMD		; Rebuild failing MSCP packet.
.                                                               

WHM_IVCMD_END:

	IVCMD_END			; Complete invalid command processing.

WHM_IVCMD_COMMON:
	CLRL	MSCP$L_BYTE_CNT(R2)	; Zero bytes transferred
	CLRW	MSCP$W_COUNT(R2)	; Zero count field
	BRB	WHM_COMMON		; Merge

WHM_ABORTED:
	MOVZWL	#SS$_ABORT,R0		; Set status
	CLRL	R1			; Clear for I/O status block.
	BRB	WHM_COMMON		; Branch to common exit.
.                                                         

WHM_CDTERR:
	MOVZWL	#SS$_DEVOFFLINE,R0	; Set status
	CLRL	R1			; Clear for I/O status block.
	BRB	WHM_COMMON		; Branch to common exit.
.                                                         

WHM_ENTRY_ACCESS_ERROR:
	MOVL	#SS$_NOENTRY,R0		; Set status
	CMPW	#<MSCP$K_ST_WHEAE+-	; Is it a previous allocation
		  MSCP$K_SC_ALLOF>,-	;  failure?
		MSCP$W_STATUS(R2)	;
	BNEQ	WHM_COMMON		; If NEQ no
.                                            
	MOVL	#SS$_IVLOGTAB,R0	; Return "invalid log table"
	BRW	WHM_COMMON		; Merge
.                                        

WHM_OFFLN:
	MOVL	#SS$_DEVOFFLINE,R0	; Set status
	BRW	WHM_COMMON		; Merge
.                                        

WHM_HSTBF:
	MOVL	#SS$_HSTBUFACC,R0	; Set status
        BRW	WHM_COMMON		; Merge
                                         

WHM_SUCC:
	MOVL	#SS$_NORMAL,R0		; Set status
.                                             

WHM_COMMON:
	ASHL	#16,R0,R0		; Shift status one word left.

	MOVL	MSCP$L_BYTE_CNT(R2),R1	; Get # bytes actually transferred.
	BITL	#<IO$M_READ_ALL!-	; Check for read operations.
		  IO$M_READ_HRN>,-	; If this is a read operation
		CDRP$L_FUNC(R5)		;  and the byte count is zero,
	BEQL	10$			;  then it is a request for how
	TSTL	R1			;  many write logs are in the
	BNEQ	10$			;  controller.
	MOVZWL	MSCP$W_COUNT(R2),R1	; Report the count instead.
10$:
	ASHQ	#-16,R0,R0		; Shift into proper position for IOSB.
	BRW	FUNCTION_EXIT
.                      

WLE_ERROR_EXIT::
	.JSB_ENTRY INPUT=  <      R2,R3,R4,R5>


        RESTORE_CREDIT     			; Restore allocated send credit
	MOVW	#-1,CDRP$L_CLN_WLE(R5)
	MOVL	#SS$_NOENTRY,R0
        CLRL	R1
	BRW	FUNCTION_EXIT
.                      


	.SBTTL	START_DSE - Start data security erase
	.SBTTL	START_WRITEPBLK & START_WRITECHECK - Start write opertions
;++
;
;	START_WRITECHECK - Prepare an MSCP packet to do a COMPARE HOST DATA 
;			   command.
;
;	START_DSE - Prepare an MSCP packet to do a ERASE command.
;
; Inputs:
;
;	R1	I/O function code & modifiers
;	R2	MSCP buffer
;	R3	UCB address
;	R4	PDT address
;	R5	CDRP address
;
;	The MSCP packet contains zeros except for:
;
;	MSCP$L_CMD_REF(R2)	<== CDRP$L_RSPID(R5)
;	MSCP$W_UNIT(R2)		<== UCB$W_MSCPUNIT(R3)
;
;--

START_WRITECHECK:

	MOVB	#MSCP$K_OP_COMP, -	;MAINL; Compare host data opcode to packet.
		MSCP$B_OPCODE(R2)
	BRB	START_WCHK_DSE		;MAINL; Go set "ignore IO$V_DATACHECK" bit.

START_DSE:

	MOVB	#MSCP$K_OP_ERASE,-	; Transfer ERASE opcode to packet.
		MSCP$B_OPCODE(R2)
;MS
	BBC	#IRP$V_WLE,-		; Check for write logging.
		CDRP$L_STS2(R5),-	;
		START_WCHK_DSE

	BBS	#IRP$V_SRVIO, -		; If IO is from mscp server,
		CDRP$L_STS(R5), 10$	;  just pass it through.

	MOVL	R5,R0			; Save CDRP
	MOVL	R3,R5			; Move the UCB
	MOVAB	CDRP$L_IOQFL(R0),R3	; R3 => IRP section of CDRP. This is
	MOVL	IRP$L_SHAD(R3),R2	; R2 => SHDRIVER's SHAD
	MOVL	SHAD$L_VU_UCB(R2),R0	; R0 => HBS VU.
	MOVL	UCB$L_DDT(R0),R0	; R0 => DDT.
	CLRL	R4			; R4 = SHDRIVER input argument; clear
	JSB	@DDT$L_AUX_STORAGE(R0)	; Go get a write log entry.
	MOVAB	IRP$L_FQFL(R3), R5	; Get address of CDRP portion of IRP.
	MOVL	CDRP$L_UCB(R5),R3	; R3 => UCB.	
	MOVL	UCB$L_PDT(R3),R4	; Get the PDT.
	MOVL	CDRP$L_MSG_BUF(R5),R2	; R2 => Message buffer.
	MOVZWL	CDRP$L_FUNC(R5),R1	; Restore function and modifiers.
	BLBC	R0,5$
	BBS	#DEV$V_WLG,-		; Branch if
		UCB$L_DEVCHAR2(R3),10$	;  still write-logging device.
	MOVZWL	#SS$_MEDOFL,R0		; Signal error for HBVS
	CLRL	R1			; Clear for I/O status block.
	RESTORE_CREDIT
	BSBW	FUNCTION_EXIT		; And goto common function exit.
	RSB
5$:	BRW	WLE_ERROR_EXIT          ; We didn't get an entry - Return to
					; SHD river
10$:	MOVL	CDRP$L_CLN_WLE(R5),-	; Move the write log entry.
		MSCP$W_HRN(R2)		;
	BISW	#MSCP$M_MD_HISLO,-	; Set history log modifier.
		MSCP$W_MODIFIER(R2)	;

  	BBC	#CDRP$V_WLE_REUSE,-	; Check for reusing log entry
  		CDRP$B_WLG_FLAGS(R5),-	;  modifier.
		START_WCHK_DSE
	BISW	#MSCP$M_MD_REUSE,-	;
		MSCP$W_MODIFIER(R2)	;

  	BBC	#CDRP$V_WLE_SUPWL,-	; Check supplementary write log
  		CDRP$B_WLG_FLAGS(R5),-	;  modifier.
		START_WCHK_DSE
	BISW	#MSCP$M_MD_SUPWL,-	;
		MSCP$W_MODIFIER(R2)	;

;
;	This code section receives control to process all physical requests.
;	It validates the starting LBN and transfer size.  If the transfer 
;	request begins in the RCT, it must be exactly one block long.  If the 
;	transfer begins outside the RCT it cannot cross into the RCT.  In 
;	addition, some functions cannot access the RCT at all.
;-

START_WCHK_DSE:

	CMPL	CDRP$L_MEDIA(R5), -	; Check for RCT access.
		UCB$L_MAXBLOCK(R3)
	BLSSU	20$			; Branch if xfer doesn't start in RCT.

10$:	MOVZWL	#SS$_IVADDR,R0		; Signal bad disk address.
	CLRL	R1			; Clear for I/O status block.
	RESTORE_CREDIT
	BSBW	FUNCTION_EXIT		; And goto common function exit.
	RSB

20$:	SUBL3	#1, CDRP$L_BCNT(R5), R0	; Make sure xfer doesn't spill into RCT.
	ASHL	#-9, R0, R0		; Compute rounded blocks transfered.
	ADDL	CDRP$L_MEDIA(R5), R0	; Compute last block accessed.
	CMPL	R0, UCB$L_MAXBLOCK(R3)	; Transfer must end before RCT.
	BGEQU	10$			; Branch if byte count is too big.

	BBC	#IO$V_INHRETRY, R1, 30$	; Branch if retries not inhibited.
	ASSUME	MSCP$V_MD_SEREC GE 8	; Else, set the suppress error
	BISW	#MSCP$M_MD_SEREC, -	; modifier.
		MSCP$W_MODIFIER(R2)

30$:	BBC	#IO$V_MSCPMODIFS, R1, 40$;Branch if no MSCP modifiers specified.
	BISW	CDRP$L_MEDIA+6(R5), -	; Set specified modifiers in packet.
		MSCP$W_MODIFIER(R2)

40$:	BITB	#^x80, R1		; Check for obsolete modifier (EXPRESS).
	BEQL	70$			; Continue processing if none set.
	MOVZWL	#SS$_ILLIOFUNC, R0	; Else, set illegal I/O function error,
	CLRL	R1			; Clear for I/O status block.
	RESTORE_CREDIT
	BSBW	FUNCTION_EXIT		; And goto common function exit.
	RSB

70$:	BICW3	#IO$M_FMODIFIERS, R1, R0; Extract just the function code.
	CMPW	#IO$_DSE, R0		; Is this a DSE request?
	BEQL	80$			; If so, check for invalid command.
	BRW	DO_MAP			; Branch if not a DSE request.
80$:	IF_IVCMD then=DSE_IVCMD_END	; Branch if invalid command DSE proc'ng.
                                                                          
	BRW	AFTER_MAP		; Else, to DSE request.
.                                                        


	.SBTTL	START_READWRITE - Start READ or WRITE operation
;++
;
;	START_READWRITE - Prepare an MSCP packet to do a READ or WRITE command.
;
; Inputs:
;
;	R0	IRP function code without modifiers
;	R1	I/O function code & modifiers
;	R2	MSCP buffer address
;	R3	UCB address
;	R4	PDT address
;	R5	CDRP address
;
;--

START_WRITE:
	MOVB	#MSCP$K_OP_WRITE, -	;MAINL; Setup write MSCP opcode.
		MSCP$B_OPCODE(R2)
;MS - beg
	BBC	#IRP$V_WLE,-		;MAINL; Check for write logging.
		CDRP$L_STS2(R5),-	;
		START_RW_COMMON		;

; We are handling write log entries (WLE's). Since these are scarce resources
; and shadowing has now flow control, we wait until flow control has been
; exerted before allocating a WLE. This is accomplished by a callback to
; 

	BBS	#IRP$V_SRVIO, -		; If IO is from mscp server,
		CDRP$L_STS(R5), 10$	;  just pass it through.

	MOVL	R5,R0			; Save CDRP
	MOVL	R3,R5			; Move the UCB
	MOVAB	CDRP$L_IOQFL(R0),R3	; R3 => IRP section of CDRP. This is
	MOVL	IRP$L_SHAD(R3),R2	; R2 => SHDRIVER's SHAD
	MOVL	SHAD$L_VU_UCB(R2),R0	; R0 => HBS VU.
	MOVL	UCB$L_DDT(R0),R0	; R0 => DDT.
	CLRL	R4			; R4 = SHDRIVER input argumnet; clear.
	JSB	@DDT$L_AUX_STORAGE(R0)	; Go get a write log entry.
	MOVAB	IRP$L_FQFL(R3), R5	; Get address of CDRP portion of IRP.
	MOVL	CDRP$L_UCB(R5),R3	; R3 => UCB.	
	MOVL	UCB$L_PDT(R3),R4	; Get the PDT.
	MOVL	CDRP$L_MSG_BUF(R5),R2	; R2 => Message buffer.
	MOVZWL	CDRP$L_FUNC(R5),R1	; Restore function and modifiers.
        BLBC	R0,5$
	BBS	#DEV$V_WLG,-		;Branch if
		UCB$L_DEVCHAR2(R3),10$  ; still write-logging device.
	MOVZWL	#SS$_MEDOFL,R0		; Signal error for HBVS
	CLRL	R1			; Clear for I/O status block.
	RESTORE_CREDIT
	BSBW	FUNCTION_EXIT		; And goto common function exit.
	RSB
5$:	BRW	WLE_ERROR_EXIT		;We didn't get an entry - Return to
					; SHDRIVER
10$:	MOVL	CDRP$L_CLN_WLE(R5),-	; Move the write log entry.
		MSCP$W_HRN(R2)		;
	BISW	#MSCP$M_MD_HISLO,-	; Set history log modifier.
		MSCP$W_MODIFIER(R2)	;

  	BBC	#CDRP$V_WLE_REUSE,-	; Check for reusing log entry
  		CDRP$B_WLG_FLAGS(R5),-	;  modifier.
		START_RW_COMMON		;
	BISW	#MSCP$M_MD_REUSE,-	;
		MSCP$W_MODIFIER(R2)	;

  	BBC	#CDRP$V_WLE_SUPWL,-	; Check supplementary write log
  		CDRP$B_WLG_FLAGS(R5),-	;  modifier.
		START_RW_COMMON		;
	BISW	#MSCP$M_MD_SUPWL,-	;
		MSCP$W_MODIFIER(R2)	;

;MS - End
	BRB	START_RW_COMMON		; Skip over read setup.

START_READ:
	MOVB	#MSCP$K_OP_READ, -	;MAINL; Setup read MSCP opcode.
		MSCP$B_OPCODE(R2)

START_RW_COMMON:
	BITL	#IRP$M_PHYSIO,-
		CDRP$L_STS(R5)		;MAINL; Is this a physical request ?
	.BRANCH_LIKELY 
	BEQL	70$			;MAINL;  If not, skip physical processing

	CMPL	CDRP$L_MEDIA(R5), -	; Check for RCT access.
		UCB$L_MAXBLOCK(R3)
	.BRANCH_LIKELY
	BLSSU	60$			; Branch if xfer doesn't start in RCT
	CMPL	CDRP$L_MEDIA(R5), -	; Check for legal RCT block.
		UCB$L_DU_TOTSZ(R3)
	BGEQU	40$			; Branch if LBN is beyond RCT.
	CMPL	#512, CDRP$L_BCNT(R5)	; Reads of RCT must be 1 block.
	BGEQ	70$			; If byte count ok, check modifiers.

30$:	MOVZWL	#SS$_IVBUFLEN, R0	; Error in byte count field.
	BRB	50$

40$:	MOVZWL	#SS$_IVADDR,R0		; Signal bad disk address.

50$:	CLRL	R1			; Clear for I/O status block.
	RESTORE_CREDIT
	BSBW	FUNCTION_EXIT		; And goto common function exit.
	RSB

60$:	SUBL3	#1, CDRP$L_BCNT(R5), R0	; Make sure xfer doesn't spill into RCT.
	ASHL	#-9, R0, R0		; Compute rounded blocks transfered.
	ADDL	CDRP$L_MEDIA(R5), R0	; Compute last block accessed.
	CMPL	R0, UCB$L_MAXBLOCK(R3)	; Transfer must end before RCT.
	BGEQU	30$			; Branch if byte count is too big.

70$:	BITL	#<IO$M_DATACHECK -	;MAINL;    data check
		 !IO$M_INHRETRY -	;MAINL;    inhibit retries
		 !IO$M_MSCPMODIFS -	;MAINL;    MSCP modifiers present
		 !^x180>, R1		;MAINL;    old modifiers that should be clear
	.BRANCH_LIKELY
	BEQL	DO_MAP			;MAINL; Branch if all modifiers are clear.

	BBC	#IO$V_DATACHECK, R1, 80$;MAINL; Branch if data check not specified.
	BISW	#MSCP$M_MD_COMP, -	;MAINL; Else, set the read/write with
		MSCP$W_MODIFIER(R2)	;MAINL; data compare modifier.

80$:	BBC	#IO$V_INHRETRY, R1, 90$	; Branch if retries not inhibited.
	BISW	#MSCP$M_MD_SEREC, -	; Else, set the suppress error
		MSCP$W_MODIFIER(R2)	; modifier.

90$:	BBC	#IO$V_MSCPMODIFS, R1,100$;MAINL;Branch if no MSCP modifiers specified.
	BISW	CDRP$L_MEDIA+6(R5), -	;MAINL; Set specified modifiers in packet.
		MSCP$W_MODIFIER(R2)

100$:	BITB	#^x80, R1		;MAINL; Check for obsolete modifier (EXPRESS).
	BEQL	DO_MAP			;MAINL; Continue processing if none set.
	MOVZWL	#SS$_ILLIOFUNC, R0	; Else, set illegal I/O function error,
	BRB	50$			; and complete request now.

DO_MAP:
	IF_IVCMD then=TRANSFER_IVCMD_END;MAINL; Branch if invalid command processing.
.                                                                              

	MOVAB	CDRP$T_LBUFHNDL(R5),-	;MAINL; Put address of Local BUFfer HaNDLe
		CDRP$L_LBUFH_AD(R5)	;MAINL;  field into field that points to it.
	MAP_IRP				;MAINL; Allocate mapping resources and load
.                                                                            
					;MAINL;  them with data from SVAPTE, BOFF,
					;MAINL;  and BCNT derived from IRP within
					;MAINL;  CDRP.

	MOVL	CDRP$L_MSG_BUF(R5),R2	;MAINL; Refresh R2 => MSCP packet.
	MOVQ	CDRP$T_LBUFHNDL(R5),-	;MAINL; Copy contents of buffer handle to
		MSCP$B_BUFFER(R2)	;MAINL;  MSCP buffer descriptor field.
	MOVL	CDRP$T_LBUFHNDL+8(R5),-	;MAINL; Buffer handle is 96 bits (12 bytes)
		MSCP$B_BUFFER+8(R2)	;MAINL;  in length.

AFTER_MAP:
	MOVL	CDRP$L_BCNT(R5),-
		MSCP$L_BYTE_CNT(R2)	;MAINL; Copy byte count of transfer.
	MOVL	CDRP$L_MEDIA(R5),-
		MSCP$L_LBN(R2)		;MAINL; And also the Logical Block Number.
	MOVL	UCB$L_CDDB(R3), R1	;MAINL; Get the CDDB address, and 
	SEND_MSCP_MSG			;MAINL;  Send the command.
.                                                           

;
; Inputs:
;
;	R0	Status of send operation
;	R1	Size of command packet sent
;	R2	MSCP message buffer address
;	R3	CDDB  address
;	R4	PDT  address
;	R5	CDRP address
;
FAST_PATH_RET:

	.JSB_ENTRY INPUT=  <R0,R1,R2,R3,R4,R5>,-
		   SCRATCH=<R0,R1,R2,R3,R4,R5>

        CMPW	#SS$_ILLCDTST,R0	;MAINL; Check for CDT error
	.BRANCH_LIKELY
	BNEQ	10$ 			;MAINL; If none, continue...

	MOVZWL	#SS$_DEVOFFLINE,R0	; Handle any CDT error
	CLRL	R1			; Clear for I/O status block.
	BSBW	FUNCTION_EXIT		; Branch to common exit.
	RSB

; To make the normal (successful) case go faster, an explicit test for a 
; successful MSCP status is made here.  If that test fails, a dispatch based 
; on the returned MSCP status is used to determine the appropriate action.
; If it succeeds, however, we're goin' a git out o' here like a shot.

10$:	MOVL	CDRP$L_MSG_BUF(R5), R2	;MAINL; Make sure of the MSCP buffer addr.
	IF_MSCP	FAILURE then=TRANSFER_MSCP_ERROR  ;MAINL; Branch if not successful.
.                                                                            

	; Transfer was successful.
	MOVL	#SS$_NORMAL@16, R0	;MAINL; Set success status.
					;MAINL; Here R0 has SS$_ code in hi order.
	MOVL	MSCP$L_BYTE_CNT(R2), R1	;MAINL; Get # bytes actually transferred.
;MS
	BBC	#IRP$V_WLE,-		;MAINL; If not write logging, continue
		CDRP$L_STS2(R5),-	;MAINL;
		TRANSFER_SHIFT		;MAINL;
	MOVL	MSCP$W_HRN(R2),-	; Copy (entry id, entrylocator)
		CDRP$L_CLN_WLE(R5)		;
;MS
TRANSFER_SHIFT:
	ASHQ	#-16, R0, R0		;MAINL; Shift into proper position for IOSB.
	BSBW	FUNCTION_EXIT		;MAINL; Fall though to function exit.
	RSB



	.SBTTL	FUNCTION_EXIT - Common request completion processing
;++
;
; FUNCTION_EXIT - Common request completion processing
;
; Functional Description:
;
;	This routine deallocates all SCS resources held by the request.  
;	If necessary, it also performs special single stream processing.  
;
; Inputs:
;
;	R0	First  longword of I/O status
;	R1	Second longword of I/O status
;	R3	UCB address
;	R4	PDT address
;	R5	CDRP address
;
; Outputs: None.
;
;--

DU$FUNCTION_EXIT::
            
FUNCTION_EXIT::

	.JSB_ENTRY INPUT=  <R0,R1,   R3,R4,R5>,-
		   SCRATCH=<R0,R1,R2,R3,R4,R5>

	MOVL	CDRP$L_MSG_BUF(R5), R2	;MAINL; Get end message address.
	BEQL	10$			;MAINL; Branch if no end message present.

	BLBC	R0,25$			;MAINL; Skip if failure


10$:	BICL	#CDRP$M_ERLIP, -	;MAINL; Just in case clear bit.
		CDRP$L_DUTUFLAGS(R5)

	MOVQ	R0,-(SP)		;MAINL; Save final I/O status on stack.
	CLRL	R0			;MAINL; Clear skip indicator -release map regs
	BSBW	DUTU$DEALLOC_ALL	;MAINL; Free resources owned by this CDRP.
	MOVQ	(SP)+,R0		;MAINL; Restore final I/O status.

	MOVL	UCB$L_CDDB(R3), R2	;MAINL; Get CDDB address in R2.


	BITL	#CDDB$M_SNGLSTRM, -	;MAINL; Is CDDB in single stream mode?
		CDDB$L_STATUS(R2)
	BNEQ	40$			;MAINL; Branch if single stream.

20$:
	.BRANCH_LIKELY
	BBC	#IRP$V_MVIRP,-		;MAINL; If this is not an MVIRP, skip the
		CDRP$L_STS(R5),22$	;MAINL;  restart test.
	BBCC	#UCB$V_MSCP_MVRESTART,-	;MAINL; Otherwise, if restart is set, clear
		UCB$L_DEVSTS(R3),22$	;MAINL;  it and return an error to MV.
	MOVZWL	#SS$_MEDOFL,R0

22$:	ALT_REQCOM                      ;MAINL; Else, complete the request.

25$:
;GH X-90 change to call msg_err_hndlr
	BBS	#IRP$V_MVIRP,-          ; Skip if MVIRP
		CDRP$L_STS(R5),10$
	BSBW	DU$MSG_ERR_HNDLR
;GH end
	BRW	10$			; Out of line branch to improve code generation

40$:	BBS	#IRP$V_MVIRP, -		; If this is a mount verification
		CDRP$L_STS(R5), -	;  I/O request, just exit normally
		20$			;  via IOC$ALTREQCOM.
	PUSHR	#^M<R2,R4>		; Save CDDB and PDT addresses.
	BBC	#IRP$V_WLE,-		;Br if not write logging
		CDRP$L_STS2(R5),45$
	BBS	#IRP$V_SRVIO, -			; Flow control is for
		CDRP$L_STS(R5), 45$		;  local nodes.
	MOVQ	R0,CDRP$L_IOST1(R5)		; Store status.
	MOVL	CDRP$L_MIRP(R5),R4		; Get MIRP.
	DECB	IRP$B_SHD_PIO_ACT(R4)		; Decrement active Count
	BEQL	42$				; Branch if not complete.
	BRB	50$
;	RSB
42$:	MOVL	UCB$L_SHAD(R3),R4		; Get the SHAD.
	MOVL	SHAD$L_VU_UCB(R4),R4		; Get VU UCB.
	MOVL	UCB$L_DDT(R4),R4		; Get DDT

; Note that a non-zero R4 is a necessary Input argument here.

	JSB	@DDT$L_AUX_STORAGE(R4)		; Go do flow control.
;	RSB
;       BSBB	20$		        ;Do HBVS exit - single stream.
	BRB	50$
45$:	CALL_ALTREQCOM			; Complete request, but keep control.
50$:	POPR	#^M<R3,R4>		; Restore CDDB and PDT addresses.
	BSBW	DUTU$RESTART_NEXT_CDRP	; Restart next CDRP.
	RSB



	.SBTTL	READ/WRITE Error processing
;++
;
; All data transfer error cases begin their processing here.
;
; CDRP$L_MSG_BUF and R2 point to the END PACKET which was returned by the 
; server.
;
;--

TRANSFER_MSCP_ERROR::

	.JSB_ENTRY INPUT=   <      R2>,-
		   OUTPUT=  <>,-
		   SCRATCH= <>,-
		   PRESERVE=<>

	ASSUME	MSCP$V_ST_MASK EQ 0
	BICW3	#^cMSCP$M_ST_MASK, -	; Extract major MSCP status.
		MSCP$W_STATUS(R2), R0
	DISPATCH R0, type=W, prefix=MSCP$K_ST_, < -
		<AVLBL,	TRANSFER_AVLBL>, -
		<OFFLN,	TRANSFER_OFFLINE>, -
		<DATA,	TRANSFER_DATA_ERROR>, -
		<DRIVE,	TRANSFER_DRVERR>, -
		<CNTLR,	TRANSFER_CTRLERR>, -
		<WRTPR,	TRANSFER_WRITELCK>, -
		<COMP,	TRANSFER_DATACHECK>, -
		<MFMTE,	TRANSFER_FORMAT>, -
		<ABRTD,	TRANSFER_ABORT>, -
		<HSTBF,	TRANSFER_HSTBFERR>, -
		<ICMD,	TRANSFER_IVCMD> -
		>
	BSBW	DU$INVALID_STS		; Unexpected MSCP end status.
	RSB

TRANSFER_AVLBL:
	MOVL	#SS$_MEDOFL, R0		; Set the status to media offline.
	BRW	TRANSFER_EXIT		; Branch to common exit.

TRANSFER_OFFLINE:
	EXTZV	#MSCP$V_ST_SBCOD,-	; Extract MSCP end-packet status
		#MSCP$S_ST_SBCOD,-	;  subcode value.
		MSCP$W_STATUS(R2), R0

	DISPATCH R0, type=W, prefix=MSCP$K_SC_, < -
		<UNKNO,	TRANSFER_UNKNO_INOPR>, -
		<NOVOL,	TRANSFER_NOVOL>, -
		<INOPR,	TRANSFER_UNKNO_INOPR>, -
		<DUPUN,	TRANSFER_DUPUN>, -
		<UDSBL,	TRANSFER_UNAVAILABLE>, -
		<EXUSE,	TRANSFER_UNAVAILABLE> -
		>

	MOVL	#SS$_DRVERR, R0		; Return drive error for anything else
	BRW	TRANSFER_EXIT		;  and branch out.

TRANSFER_NOVOL:
	MOVL	#SS$_MEDOFL, R0		; Set the status
	BRW	TRANSFER_EXIT		;  and branch out.

TRANSFER_UNKNO_INOPR:
	MOVL	#SS$_DEVOFFLINE, R0	; Set the status
	BRW	TRANSFER_EXIT		;  and branch out.

TRANSFER_DUPUN:
	MOVL	#SS$_DUPUNIT, R0	; Set the status
	BSBW	DUTU$SEND_DUPLICATE_UNIT; Send a message to the operator.
	BRW	TRANSFER_EXIT		; Exit processing.

TRANSFER_UNAVAILABLE:
	MOVL	#SS$_DEVNOTSHR, R0	; Set status to "not shareable".
	BICL	#DEV$M_AVL, -		; Clear unit available for use flag
		UCB$L_DEVCHAR(R3)	; in the UCB.
	BRW	TRANSFER_EXIT		; Exit processing.

TRANSFER_DATA_ERROR:
	MOVL	#SS$_FORCEDERROR, R0	; Set up a default status.
	EXTZV	#MSCP$V_ST_SBCOD,#MSCP$S_ST_SBCOD,MSCP$W_STATUS(R2),R1 ; Extract sub-code
	CMPW	R1, #MSCP$K_SC_FRCER	; Forced error?
	BNEQ	10$			; If NEQ no
	BRW	TRANSFER_EXIT		; Branch to common exit
10$:	MOVL	#SS$_PARITY,R0		; If not forced error return parity err
	BRW	TRANSFER_EXIT		; Branch to common exit.

TRANSFER_DRVERR:
	MOVL	#SS$_DRVERR, R0		; Set the status
	BRW	TRANSFER_EXIT		;  and branch to common exit.

TRANSFER_CTRLERR:
	MOVL	#SS$_CTRLERR, R0	; Set the status
	BRW	TRANSFER_EXIT		;  and branch to common exit.

TRANSFER_WRITELCK:
	MOVL	#SS$_WRITLCK, R0	; Set the status
	BRW	TRANSFER_EXIT		;  and branch to common exit.

TRANSFER_DATACHECK:
	MOVL	#SS$_DATACHECK, R0	; Set the status
	BRW	TRANSFER_EXIT		;  and branch to common exit.

TRANSFER_FORMAT:
	MOVL	#SS$_FORMAT, R0		; Set the status
	BRW	TRANSFER_EXIT		;  and branch to common exit.

TRANSFER_ABORT:
	MOVL	#SS$_ABORT, R0		; Set the status
	BRW	TRANSFER_EXIT		;  and branch to common exit.

TRANSFER_HSTBFERR:
	MOVL	#SS$_IVBUFLEN, R0	; Default status is invalid bufer length
	EXTZV	#MSCP$V_ST_SBCOD,#MSCP$S_ST_SBCOD,MSCP$W_STATUS(R2),R1 ; Extract sub-code
	CMPW	R1, #MSCP$K_SC_ODDBC	; If this was an odd byte count,
	BNEQ	20$			;  the status in R0 is correct.
	BRW	TRANSFER_EXIT		;  and branch to common exit.
20$:	BSBW	DU$INVALID_STS		; Unexpected MSCP status was returned.
	RSB

TRANSFER_EXIT:
	ROTL	#16, R0, R0		; Move status to high order word.
	MOVL	MSCP$L_BYTE_CNT(R2), R1	; Get # bytes actually transferred.
	ASHQ	#-16, R0, R0		; Shift into proper position for IOSB.
	BSBW	FUNCTION_EXIT		; Now call standard function exit.
	RSB

TRANSFER_IVCMD:
	CLRL	R0			; Init
	MOVZBW	MSCP$W_STATUS+1(R2), R0	; Extract MSCP end-packet status
					;  subcode value (high byte only).
	DISPATCH R0, type=W, prefix=MSCP$, < -
		<L_LBN,		TRANSFER_IVCMD_LBN>, -
		<L_BYTE_CNT,	TRANSFER_IVCMD_BCNT>, -
		<W_MODIFIER,	TRANSFER_IVCMD_MODIFIER>, -
		<W_HRN,	    	TRANSFER_IVCMD_WLOG>,-
		<W_ENT_ID,	TRANSFER_IVCMD_WLOG> -
		>
	MOVZWL	#SS$_CTRLERR, R0	; Default status is controller error
	BRB	TRANSFER_IVCMD_BEGIN	; If no match.

TRANSFER_IVCMD_BCNT:
	MOVZWL	#SS$_IVBUFLEN, R0	; Set up status to return.
	BRB	TRANSFER_IVCMD_COMMON	; Merge
.                                        

TRANSFER_IVCMD_LBN:
	MOVZWL	#SS$_IVADDR, R0		; Set up status to return.
	BRB	TRANSFER_IVCMD_COMMON	; Merge
.                                        

TRANSFER_IVCMD_WLOG:
	MOVZWL	#SS$_NOENTRY, R0	; Set up status to return.
	BRB	TRANSFER_IVCMD_COMMON	; Merge
.                                        

TRANSFER_IVCMD_MODIFIER:
	MOVZWL	#SS$_ILLIOFUNC, R0	; Default status is invalid bufer length
	BBS	#IO$V_MSCPMODIFS, -	; Were invalid MSCP modifiers specified?
.                                                                         
		CDRP$L_FUNC(R5),TRANSFER_IVCMD_COMMON ; If BS yes

TRANSFER_IVCMD_BEGIN:

	IVCMD_BEGIN			; Begin invalid command processing.
.                                                                    

	BRW	DU$DISPATCH_CMD		; Repeat MSCP command packet setup
.                                                                   
					; upto but not including the MAP_IRP.
					; Then do everything after the 
TRANSFER_IVCMD_END:			; MAP_IRP here, by hand.
	ASSUME	CDRP$S_LBUFHNDL EQ 12
	MOVQ	CDRP$T_LBUFHNDL(R5), -	; Copy contents of buffer handle to
		MSCP$B_BUFFER(R2)	; MSCP buffer descriptor field.
	MOVL	CDRP$T_LBUFHNDL+8(R5), -
		MSCP$B_BUFFER+8(R2)
DSE_IVCMD_END:
	MOVL	CDRP$L_BCNT(R5), -	; Copy transfer byte count.
		MSCP$L_BYTE_CNT(R2)	
	MOVL	CDRP$L_MEDIA(R5), -	; Copy starting logical block number.
		MSCP$L_LBN(R2)

	IVCMD_END			; Complete invalid command processing.
.                                                                       

TRANSFER_IVCMD_COMMON:
	CLRL	MSCP$L_BYTE_CNT(R2)	; Else, clear "byte count."
	BRW	TRANSFER_EXIT		; And blow this operation away.
.                                                                


		.SBTTL  DU$MSG_ERR_HNDLR

;++
;
;Functional Description:
;
;	This routine determines if the software context for an operation 
;	should be logged and if the UCB error count should be incremented. 
;	The policy is:
;
;	o If the end message shows an error log was generated, or if the
;	CDRP shows error handling is still in progress, software context
;	is logged but the UCB error count is not incremented. The ERRCNT
;	will be incremented when the error logging datagram is processed
;	by DU$DGDR.
;
;	o If neither ERLOG nor ERLIP are set, the End Message is
;	examined to determine if the software context should be logged
;	and the error count incremented (R0 has possibly been altered by
;	the the DO_ACTION macros in the START_xxxx routines and therefore 
;	may not reflect MSLG$W_STATUS). 
;
;	Special Casing (only for ERLOG or ERLIP clear):
;
;	o If MSCP$V_EF_BBLKR is set, the DUHIRT routines have already 
;	logged the replacement attempt and (on failure) the disk transfer 
;	error through ERL$LOGMESSAGE, which increments the error count. There 
;	is one exception: DRVERR. DUHIRT returns DRVERR if the attempt to 
;	restore good data fails. In this case, we increment the
;	error count before logging status.
;
;	o TRANSFER_MEDOFL turns R0 into SS$_DRVERR if a transfer fails with 
;	AVLBL!INOPR. This is the exception in AVLBL processing, so it warrants
;	both logging and incrementing.
;
;	o Invalid command processing has already used ERL$LOGMESSAGE to log
;	software context and to increment the error count. Unfortuantely,
;	TRANSFER_INVALID_COMMAND may also return SS$_CTRLERR, so we have
;	to special case it
;
;	o We ignore SS$_IVBUFLEN, since TRANSFER_HOST_BUFFER_ERROR may have
;	already reset the controller
;
;	o We log software context for any operation that results in SS$_MEDOFL
;	where the End Msg SUBCODE shows a duplicate unit or an inoperative
;	drive
;
;	Inputs:
;
;	R2	End msg address
;	R3	UCB address
;	R4	PDT address
;	R5	CDRP address
;
;	Outputs:
;
;	None (R0, R1 preserved across macros)
;
;gh new error log message routine
DU$MSG_ERR_HNDLR:

	.JSB_ENTRY INPUT=   <R2,R3,R4,R5>,-
		   OUTPUT=  <>,-
		   SCRATCH= <>,-
		   PRESERVE=<>

	BBS	#MSCP$V_EF_ERLOG,-	; If error log entry expected, don't
		MSCP$B_FLAGS(R2),-	;  increment error count, but do
		ERM_LOGERR		;  LOGSTATUS.
	BBS	#CDRP$V_ERLIP,-
		CDRP$L_DUTUFLAGS(R5),-
		ERM_LOGERR
	
5$:						; Start error processing
  	MOVQ	R0, -(SP)			; Save real R0 across macros

	ASSUME	MSCP$V_ST_MASK EQ 0
	BICW3	#^cMSCP$M_ST_MASK, -	; Extract major MSCP status.
		MSCP$W_STATUS(R2), R0
	DISPATCH R0, type=W, prefix=MSCP$K_ST_, < -
		<ICMD,  ERM_IGNORE>, -		; Ignore
		<AVLBL,	ERM_IGNORE>, -	
		<OFFLN,	ERM_IGNORE>, -
		<CNTLR,	ERM_CTRLERR>, -		; Go check for BBR
		<DATA,	ERM_CTRLERR>, -
		<MFMTE,	ERM_CTRLERR>, -
  		<DRIVE,	ERM_DRVERR>, -		; Process
		>
; First train out for uninteresting packets:

ERM_IGNORE:
	MOVQ    (SP)+,R0	    	; Restore R0 

	RSB
	
ERM_CTRLERR:
	BBS	#MSCP$V_EF_BBLKR, -	; If BS ERRCNT bumped
		MSCP$B_FLAGS(R2), -	;
		ERM_POPNLOG		;
ERM_DRVERR:
	INCW	UCB$L_ERRCNT(R3)	; No error log generated, but error
					;  needs counting and status should
ERM_POPNLOG:				;  be logged.
	MOVQ	(SP)+,R0		; Restore R0

ERM_LOGERR:
	JSB 	G^ERL$LOGSTATUS		; Log software context and return to
					;  FUNCTION_EXIT
	RSB
;gh end	

	.SBTTL	PATH_MOVE - SCS Request for Change of Path to Device
;++
;
; PATH_MOVE - Break and Reform Connection to Device
;
; Functional Description:
;
;	The address of this routine is passed to SCS as a parameter to the
;	connect request macro. SCS uses this address to call when a path
;	to a device needs to be moved for load balancing.
;
; Inputs:
;
;	R0	SCS$C_USE_ALTERNATE_PORT
;	R1	Load rating
;	R3	CDT address
;	R4	PDT address
;
; Outputs: 
;
;	None.
;
;--

DU$PATH_MOVE:

	.JSB_ENTRY INPUT=   <R0,R1,   R3,R4   >,-
		   OUTPUT=  <>,-
		   SCRATCH= <>,-
		   PRESERVE=<>

	MOVL	CDT$L_AUXSTRUC(R3),R3	; R3 => CDDB.
	BISL	#CDDB$M_PATHMOVE,-	; Indicate that an SCS requested
		CDDB$L_STATUS(R3)	; path move is in progress.
	BRB	RECONN_COMMON		; Branch around to common code.
.                                                                



	.SBTTL	CONNECT_ERR - Error Encountered During Connect Request
;++
;
;	DU$CONNECT_ERR - Clean Up After Error Encountered During SCS CONNECT
;
; Functional Description:
;
;	This routine is called if an error is encountered during the
;	processing of a connect request. 
;
; Inputs:
;
;	R0	Error status from SCS$CONNECT, could be almost anything.
;	R1	Disconnect type copy
;	R3	CDT address
;	R4	PDT address
;
; Outputs:
;
;	None.
;
;--

DU$CONNECT_ERR::

	.JSB_ENTRY INPUT=   <R0,R1,   R3,R4   >,-
		   OUTPUT=  <>,-
		   SCRATCH= <>,-
		   PRESERVE=<>

	MOVL	CDT$L_AUXSTRUC(R3),R3		; R3 => CDDB.
	BBSC	#CDDB$V_RESYNCH,-
.                          
		CDDB$L_STATUS(R3),-
		RECONN_COMMON2
	CMPL	R1,#SCS$C_USE_ALTERNATE_PORT
	BNEQ	RECONN_COMMON
.                      
	BISL	#CDDB$M_PATHMOVE,-		; Indicate that an SCS requested
		CDDB$L_STATUS(R3)		; path move is in progress.
	BRB	RECONN_COMMON
.                      



	.SBTTL	RE_SYNCH - Break This Connection
;++
;
;	DU$RE_SYNCH - Break This Connection and Log the Event
;
; Functional Description:
;
;	This entry point is used when an event so heinous has occurred on 
;	this connection, that the class driver can't take it anymore and
;	decides to drop this connection to try and find a better one to 
;	the device.
;
; Inputs:
;
;	R0	Status
;		    #EMB$K_CTLRES_INIT, 
;			From DU$INIT_TIMEOUT Timeout routine for controller init
;			Error during DU$MAKE_CONNECTION
;		    #EMB$K_CTLRES_IMTMO,
;			From DU$TIMER Immediate command failed to complete in 
;			timeout int
;		    #EMB$K_CTLRES_TMO,
;			From DU$TIMER Oldest outstanding command is not making 
;			progress
;		    #EMB$K_CTLRES_INVMSG,
;			From INV_ATTN_MSG bad attention message received
;			From DU$INVALID_STS MSCP status received is bad
;	R3	CDDB address
;
; Outputs: 
;
;	None.
;
;--

DU$RE_SYNCH::

	.JSB_ENTRY INPUT=   <R0,      R3      >,-
		   OUTPUT=  <>,-
		   SCRATCH= <R0,R1,R2,R3,R4,R5>,-
		   PRESERVE=<>

;       Check for a GCS timeout and if so log additional errorlog information
;        about the command that actually timed out

        CMPW    R0, #EMB$K_CTLRES_TMO   ; If timing out an old command
        BEQL    10$                     ;  message buffer info already set up.
        CLRL    R1                      ; Zero message length.
        MOVL    R3, R2                  ; Use innocuous message buffer address.
        BRB     15$                     ; Skip logging status
10$:    MOVL    CDDB$L_CDRPQFL(R3),R5   ; Get address of oldest command
        MOVL    CDRP$L_UCB(R5),R3       ; Get address of UCB
        JSB     G^ERL$LOGSTATUS         ; Log information pertaining to the
                                        ;  timed out command

        MOVL    UCB$L_CDDB(R3),R3       ; CDDB -> R3
15$:	JSB	G^ERL$LOG_DMSCP		; Log reason for controller reset.

	BITL	#<CDDB$M_INITING-	; If the connection is disabled or
		!CDDB$M_DISABLED>,-	;  or initing, we are here as a result
		CDDB$L_STATUS(R3)	;  of an error in controller init or
	BNEQ	20$			;  DU$MAKE_CONNECTION. Skip the noconn
					;  test.
	BBC	#CDDB$V_NOCONN,-	; If we do not have a connection,
		CDDB$L_STATUS(R3),20$	;  another error thread is running.
	RSB				; Exit now.

20$:	MOVL	CDDB$L_PDT(R3),R4	; R4 => PDT.
	CMPB	#MSCP$K_CM_EMULA, -	; If this is the MSCP server, the right
		CDDB$B_CNTRLMDL(R3)	; resynch technique is DISCONNECT.
	BEQL	RECONN_COMMON		; So, skip the MRESET setup.
	BISL	#CDDB$M_RESYNCH,-	; Signal that we should reset
		CDDB$L_STATUS(R3)	;  intelligent controller.
	BRB	RECONN_COMMON		; Branch around to common code.



	.SBTTL	re-CONNECTION after VC error or failure
;++
;
;	RECON_COMMON - Block of code invoked when a connection is reestablished
;	to an intelligent controller following some disturbance to the logical
;	CONNECTION.  The purpose of this code is to locate all CDRPs that were
;	active on this controller, and place them in the proper order into the 
;	restart queue. Once all the CDRPs are on this queue we "execute"
;	(restart) each of these CDRP's, one by one, until the restart queue is
;	empty. When the last such CDRP has completed, normal QIO processing is
;	resumed.  This code works in cooperation with code in FUNCTION_EXIT.
;
;	This code is invoked because of an error detected by the Port Driver or
;	because y the Disk Class Driver has decided that the intelligent
;	controller has gone "insane".
;
;	The actions herein taken are the following:
;
;	0.  If the class driver is objecting to an uncooperative controller,
;	    log an errorlog message indicating why the controller is being
;	    reset.
;
;	1.  Disable the Timeout Mechanism Routine wakeups by placing a longword
;	    of all 1's in CRB$L_DUETIME.
;
;	2.  In order to prevent new CDRP's from starting up, UCB$W_RWAITCNT is
;	    incremented for each UCB associated with this controller. The
;	    RWAITCNT field is used to incicate the reasons that I/O is currently
;	    stalled to this unit. When this count is non-zero, incoming CDRP's
;	    are placed on the UCB$L_IRPQFL queue.  Incrementing RWAITCNT here
;	    insures that all new CDRP's are stalled until the connection is
;	    recovered.
;
;	3.  Resources owned by the permanent and DAP CDRPs are deallocated and 
;	    dequeued if they are on a resource wait queue.
;
;	4.  Active CDRP's can be found in one of the following places:
;
;		a) On the RDT resource wait Q.  Here also RWAITCNT has been
;		   bumped once to many times.
;
;		b) On the CDDB$L_CDRPQFL.  Here RWAITCNT is normal except for
;		   the bump given in step 2.
;
;		c) On some other resource wait Q (Flow control, message buffer,
;		   mapping resources, etc.). Here again RWAITCNT has been bumped
;
;		d) On the CDDB$L_RSTRTQ.  If here, the CONNECTION has failed
;		   while we were in the middle of cleaning up a previous
;
;		Our aim here is to gather all the active CDRP's onto the
;		CDDB$L_RSTRTQ.  To do this we search for them in the above
;		mentioned places in the order in which they were mentioned.
;		This order is important as will be explained below.
;
;	5.  At the time of the call to DU$CONNECT_ERR, that mount verification
;	    may be in porgress. If that is the case the volume being verified
;	    is marked as invalid and during re-CONNECTION the volume is not
;	    brought back online.  Also, a set of inactive (i.e. no resources
;	    allocated for them) CDRPs (IRPs), and one mount verification
;	    specific CDRP on the mount verification queue of the UCB. 
;
;	    The contents of the mount verification queue can be ignored.  The
;	    active mount verification CDRP is treated normally.  Its I/O is
;	    retried and will probably fail. Mount verification then re-submits
;	    the I/O and it winds up on the normal UCB I/O queue until the
;	    RWAITCNT goes to zero. After re-CONNECTION, the I/O will start up
;	    normally and everything should resume transparently.
;
;	6.  The RDT resource wait queue is scanned until the first entry for
;	    this UCB is found. Each time an entry is removed, the RDT is scanned
;	    from the beginning.  Only a clean scan ends the process.
;
;	7.  Each entry on CDDB$L_CDRPQFL is removed and inserted into the
;	    restart queue by calling INSERT_RSTRTQ.
;
;	    INSERT_RSTRTQ deallocates all resources allocated by a CDRP prior
;	    to inserting it in CDDB$L_RSTRTQ. Because of this, the only CDRPs
;	    that still own RSPIDs are the CDRPs on other resource wait queues.
;	    To find those CDRPs, the RDT is scanned for entries that originated
;	    on the failed connection. When one is found, it is removed from the
;	    wait queue and inserted in the restart queue. Note that this process
;	    deallocates the resources allocated to that CDRP and as a result 
;	    could cause another of the CDRPs from this connection to receive
;	    these resources and proceed up to the CDDB$L_CDRPQFL.  Therefore
;	    after a removing a CDRP from a wait queue here, we branch back to
;	    step 6 to safeguard against this possibility. A complete scan of
;	    the RDT with no hits implies that all CDRPs have been gathered for
;	    the failed connection.
;
;	 8. The dead connection is now DISCONNECTed.
;
;	 9. Any mapping resources still owned by CDRPs on the restart queue are 
;	    now deallocated. This deallocation is delayed until after the 
;	    DISCONNECT (and possible MRESET) to prevent an "insane" controller 
;	    from continuing to transfer by re-allocated mapping resources.
;	    Once these mapping resources are released, I/O requests related to
;	    "mounting" a disk are released so that $MOUNT or mount verification
;	    can take appropriate action.  This is done by releasing any request
;	    with IRP$V_MVIRP set, or requests directed to a device with
;	    UCB$V_VAILD clear. The mount verification case is obvious and
;	    experience shows that $MOUNT does not set UCB$V_VALID before
;	    issuing the IO$_PACKACK.
;
;	10  A new connection is formed to the remote server.
;	
;	11. CDRPs are now removed from the restart queue and executed one at at
;	    time until that queue is exhausted.  Care must be taken to safeguard
;	    against the possibility that a particular request could repeatedly
;	    hang a controller (i.e.cause a re-CONNECTION) and thereby prevent
;	    any requests from getting through.  To deal with this possibility,
;	    a particular request is only retried a fixed number of times
;	    (MAX_RETRY).  The algorithm which resolves this dilemma relies on
;	    several data items in the CDDB:
;
;		a) CDDB$L_RSTRTCDRP - the address of the CDRP that is currently
;		   being processed in single stream mode if we are in single
;		   stream mode.
;
;		b) CDDB$L_RETRYCNT - the number of remaining retries for the
;		   current CDRP being processes in single stream mode if we are
;		   in single stream mode.
;
;		c) CDDB$V_SNGLSTRM - bit in CDDB$L_STATUS which tells us if we
;		   are in single stream mode.
;
;	    The algorithm is as follows: If upon selecting the first CDRP on
;	    CDDB$L_RSTRTQFL, CDDB$V_SNGLSTRM is clear, the bit is set knowing
;	    that this is the first retry attempt for this request in single
;	    stream mode.  This bit being clear implies either that this is the
;	    first re-CONNECTION since the system came up or that the last
;	    re-CONNECTION ran to completion leaving the bit clear.  In this
;	    case we select this first CDRP, set CDDB$L_RETRYCNT to the maximum
;	    and establish this CDRP as the current one by storing its address
;	    in CDDB$L_RSTRTCDRP.
;
;	    If however CDDB$V_SNGLSTRM is set upon selecting a CDRP, the CDRP
;	    address selected is compared to the current value of
;	    CDDB$L_RSTRTCDRP. If they are NOT equal, this is the first retry
;	    attempt for this CDRP and the CDDB$L_RETRYCNT is set to the maximum
;	    and CDRP is stored in CDDB$L_RSTRTCDRP.  If the selected CDRP is the
;	    same as that in CDDB$L_RSTRTQFL, the retry count is decremented and
;	    if it is greater than zero attempt to process the CDRP again.
;
;	    Note this all works even though the address of a CDRP is not
;	    necessarily unique. That is, many I/O requests in the life of the
;	    system may occupy the same CDRP in virtual space.  However, once
;	    re-CONNECTION logic begins, it deals only with the CDRPs on the
;	    CDDB$L_RSTRTQFL.  This list never grows until re-CONNECTION is run
;	    to completion since all new IRPs are being backed up.  Therefore
;	    even though repeated re-CONNECTIONs may occur that do not run to
;	    completion the CDDB$L_RSTRTQFL is always monotonically decreasing
;	    and no new CDRPs are entered onto it. In a fixed list of CDRPs
;	    the address is a unique descriptor.
;
;	12. Note that CDDB$M_SNGLSTRM in CDDB$L_STATUS acts as a flag to
;	    FUNCTION_EXIT so that it can aid in the one at a time restart of
;	    the CDRPs.
;
;	13. For safety sake, all UCB's are checked to make sure that their
;	    UCB$W_RWAITCNT values are all equal to 1. Also CDDB$L_CDRPQFL is
;	    verified to be empty.
;
;	14. Remque the first CDRP on CDDB$L_RSTRTQFL and branch to SEND_IO to
;	    begin its execution.
;
;
; Inputs:
;
;	R3	CDDB address
;
;--

RECONN_COMMON:

	BBCS	#CDDB$V_DISC_PEND,-	;  then simly RSB back and 
		CDDB$L_STATUS(R3),-
		RECONN_COMMON2		;  terminate this thread.
	RSB

RECONN_COMMON2:
5$:	BISL	#<CDDB$M_RECONNECT -	; Indicate that we are re-connecting
		 !CDDB$M_NOCONN>, -	;  and have no current connection.
		CDDB$L_STATUS(R3)
	INCL	CDDB$L_RSTRTCNT(R3)	; Count number times thru here.
	BICL	#<CDDB$M_IMPEND -	; Signal: no immediate command pending
		 !CDDB$M_INITING -	; 	  out of initialization
		 !CDDB$M_DAPBSY - 	;         not in use
		 !CDDB$M_RSTRTWAIT>,-	;	  not waiting to restart CDRPs
		CDDB$L_STATUS(R3)
	MOVL	CDDB$L_CRB(R3),R0	; R0 => CRB.
	MNEGL	#1,CRB$L_DUETIME(R0)	; Prevent Timeout Mechanism wakeups.

	SUBL3	#<UCB$L_CDDB_LINK -	; Get "previous" UCB address in R1.
		 -CDDB$L_UCBCHAIN>, -
		R3, R1

10$:	MOVL	UCB$L_CDDB_LINK(R1), R1	; Chain to next UCB.
	BEQL	20$			; EQL implies no more UCB's here.
	BBSS	#UCB$V_MSCP_WAITBMP, -	; If already bumped, branch around;
		UCB$L_DEVSTS(R1), 10$	; else indicate RWAITCNT bumped.
	INCW	UCB$W_RWAITCNT(R1)	; Prevent new CDRP's from starting up.
	BRB	10$			; Go look for more UCB's.
20$:

;
; Now we are sure that no new CDRP's will start.
;

	BSBW	DUTU$DISCONNECT_CANCEL	; Perform disconnect cancel cleanup.

; Deallocate RSPID & message buffer on each of the CDDB perm. IRP/CDRP pairs.
; If either of the perm. CDRPs is on a resource wait queue, remove it now
; since the routines following this which clean up the queues still expect
; to have a valid RSPID.

	ASSUME	CDRP$L_FQFL EQ 0	; For coming REMQUEs

	MOVAB	CDDB$A_DAPCDRP(R3), R5	; Get DAP permanent CDRP address.
	IDLE_CDRP			; Remove the CDRP from its queue
.                                                                 
	MOVL	#1, R0			; Don't unmap port buffers.
	BSBW	DUTU$DEALLOC_ALL	; Deallocate its RSPID & msg. buf.

; The PRMBSY bit protects access to the PRMCDRP but there are cases where this
; can result in a deadlock. To avoid deadlock, if the PRMCDRP is busy and
; the PRMBSY_CLEANUP_PERMITTED bit is set then connection cleanup is
; permitted access to the PRMCDRP without abritration.

	BBC	#CDDB$V_PRMBSY, -	; Branch if PRMCDRP not busy
		CDDB$L_STATUS(R3), 30$	; 
	BBC	#CDDB$V_PRMBSY_CLEANUP_PERMITTED, -; PRMCDRP is busy, Branch if
		CDDB$L_STATUS(R3), 30$	; connection cleanup is not permitted
					; special access to busy PRMCDRP
	BICL	#CDDB$M_PRMBSY_CLEANUP_PERMITTED, -; Clear bit allowing conn
		CDDB$L_STATUS(R3)	; cleanup access to PRMCDRP
	BRW	31$			; Branch to skip arbitration
.                                                             

30$:	ALLOC_PRMCDRP			; Arbitrate for the perm CDRP.
                                                                
31$:	MOVAB	CDDB$A_PRMCDRP(R3), R5	; Get permanent CDRP address.
	IDLE_CDRP -			; Remove the CDRP from its queue
		CDRP_CDT_CLEAR=NO	; but don't clear CDRP$L_CDT cell
.                                                                  
	MOVL	#1, R0			; Don't unmap port buffers.
	BSBW	DUTU$DEALLOC_ALL	; Deallocate its RSPID & msg. buf.
;
;	Registers here are:
;		R3 => CDDB
;		R4 => PDT.
;		R5 => Permanent CDRP.
;
; Locate and prepare for restarting all CDRPs currently waiting for a RSPID.
; Since the class driver allocates a RSPID as the first step in any function,  
; CDRPs found with SCAN_RSPID_WAIT are not holding any resources and are not 
; active. Since these CDRPs hold no resources, their cleanup doesn't cause any 
; other waiting requests to become active.  (This fact is not currently used, 
; but it might be useful.)

	MOVL	CDDB$L_CDT(R3), R3	; Get CDT address.

	CLRL	R1			; Set SCAN_RSPID_WAIT flag.
	SCAN_RSPID_WAIT -		; Use SCS service to scan RSPID
	    action = DUTU$RECONN_LOOKUP	; wait queue.

; Remove all CDRPs on the active requests queue.  These CDRPs:
;   a. have outstanding requests in the intelligent controller, 
;   b. suffered allocation failures due to a broken connection,
;   c. represent the request during which an "insane" controller was detected.
; In any case, these CDRPs are not on any resource wait queue and do not have 
; their associated resource wait count bumped due to need for a resource.

	BSBW	DUTU$DRAIN_CDDB_CDRPQ	; Cleanup active requests.

; Now scan the entire Response-id Descriptor Table for any remaining CDRPs 
; belonging to this connection.  Presumably these CDRPs are on a resource wait 
; queue somewhere.  In addtion, releasing whatever resources such CDRPs hold 
; may cause other waiting CDRPs to become active.  Therefore, after every CDRP 
; is located and processed, the active CDRP queue must be scanned again.

	INCL	R1			; Set SCAN_RDT flag.
	SCAN_RDT -			; Use SCS service to scan RDT.
	    action = DUTU$RECONN_LOOKUP

	MOVL	CDT$L_AUXSTRUC(R3), R3	; Restore the CDDB address.

;
; All CDRP's are now in CDDB$L_RSTRTQFL. DISCONNECT the old connection.
; This requires the use of the permanent CDRP which we have allocated above.
;
	MOVAB	CDDB$A_PRMCDRP(R3), R5	; CDRP into R5 for coming BSBWs.
	MOVL	R3,R0			; R0 => CDDB.
	MOVL	CDRP$L_CDT(R5),R3	; Set R3 => CDT.
	BISL	#CDDB$M_NOCONN, -	; Set no connection active flag.
		CDDB$L_STATUS(R0)
	BBC	#CDDB$V_RESYNCH,-	; Do NOT branch around if we were called
		CDDB$L_STATUS(R0),50$	;  in order to re-synchronize.
	MOVL	CDT$L_PB(R3),R3		; R3 => Path Block for MRESET, etc.
	MRESET	PB$B_RSTATION(R3),#1	; Force controller to reset,
	MSTART	PB$B_RSTATION(R3)	;  and restart itself.
	MOVL	CDRP$L_UBARSRCE(R5), R0	; Get the CDDB address.
	BICL	#CDDB$M_PRMBSY,-	; Clear out the bit indicating the 
		CDDB$L_STATUS(R0)	;  permanent CDRP is in use, 
	RSB				;  and end this thread. The Port driver
					;  calls it's error routine as a result
					;  of MRESET to DISCONNECT.

50$:	CLRL	CDRP$L_CDT(R5)		; Clear link to CDT
	BBS	#CDDB$V_PATHMOVE, -	; Check for disconnect reason, and
		CDDB$L_STATUS(R0), 60$	; disconnect with the appropriate
					; status.

	DISCONNECT	#SCS$C_STNORMAL
	BRB	70$
.            


60$:	DISCONNECT	#SCS$C_USE_ALTERNATE_PORT


					
70$:	MOVL	CDRP$L_UBARSRCE(R5), R3	; Restore R3 => CDDB after disconnect.
	BICL	#CDDB$M_DISC_PEND,-
		CDDB$L_STATUS(R3)
;
; Deallocate mapping resources 
;

	; Any mapping resources still owned by CDRPs on the restart queue are 
	; deallocated here.  This deallocation is delayed until after the 
	; DISCONNECT (and possible MRESET) to prevent an "insane" controller 
	; from continuing to transfer via possibly re-allocated mapping 
	; resources.  Once the mapping resources are released, I/O requests 
	; related to "mounting" a disk are released so that $MOUNT or mount 
	; verification can take appropriate action.  This is done by 
	; releaseing any request with IRP$V_MVIRP or directed to a device with 
	; UCB$V_VAILD clear is released.  The mount verification case is 
	; obvious and experience shows that $MOUNT does not set UCB$V_VALID 
	; before issuing the IO$_PACKACK.

	PUSHAB	CDDB$L_RSTRTQFL(R3)	; Setup listhead address.
	PUSHL	CDDB$L_RSTRTQFL(R3)	; Setup first CDRP address.

80$:	POPL	R5			; Get next CDRP address.
	CMPL	R5, (SP)		; Is it the listhead?
	BEQL	100$			; If yes, all deallocations are done.
	CLRL	R0			; Clear skip indicator -release map regs
	BSBW	DUTU$DEALLOC_ALL	; Free SCS resources owned by this CDRP.
	PUSHL	(R5)			; Push next CDRP address.
	MOVL	CDRP$L_UCB(R5), R0	; Get UCB address.
	BBS	#IRP$V_SHDIO,-		; Post the IRP if this IRP is from HBS
		CDRP$L_STS2(R5), 90$	;  so it can recover now.
	BBS	#IRP$V_SRVIO,-		; Post the IRP if this IRP is from the
		CDRP$L_STS(R5), 90$	;  server so it can recover now.
	BBC	#UCB$V_VALID, -		; Branch to post the IRP if this
		UCB$L_STS(R0), 90$	; device is not "volume valid."

	BBC	#IRP$V_MVIRP, -		; Is this a mount verification IRP?
		CDRP$L_STS(R5), 80$	; Branch if not an MV IRP.
90$:	REMQUE	(R5), R0		; Else, remove IRP/CDRP from restart
	POST_CDRP status=SS$_DEVOFFLINE	; queue and send it to post processing.
	BRB	80$			; Loop till all restart CDRPs are done.

100$:	TSTL	(SP)+			; Clear listhead pointer from stack.

	; Deallocate mapping resources whose description is stored in the 
	; CDDB permanent CDRP.  This information was placed there by 
	; DUTU$INSERT_RESTARTQ when it discovers that the permanent CDRP 
	; owns mapping resources.  In this way, another thread is allowed to 
	; use the permanent CDRP while this connection is broken.

	MOVAB	CDDB$A_PRMCDRP(R3), R5	; Get CDRP in R5.
	CLRL	R0			; Clear skip indicator -release map regs
	BSBW	DUTU$DEALLOC_ALL	; Free MAP resources.
	BICL	#CDDB$M_PRMBSY,-
		CDDB$L_STATUS(R3)

	BSBW	DUTU$REVALIDATE		; Start mount verification for online
					; devices.  Starting MV here allows
					; MV to perform failover, if that's 
					; possible.

;
; re-CONNECT - Here we call an internal subroutine which:
;
;	1. Makes a connection to the MSCP server in the intelligent
;		controller.
;
;	2. Sends an MSCP command to SET CONTROLLER CHARACTERISTICS.
;
;	3. Allocates an MSCP buffer and RSPID for our future use in
;		connection management.
;
; Upon return R4 => PDT and R5 => CDRP.
;

	ALLOC_PRMCDRP			; Gain exclusive access to the PRMCDRP.
.                                                                        
	MOVAB	CDDB$A_PRMCDRP(R3), R5	; Get permanent CDRP address.
	MOVAB	RC_AFTER_MAKE_CONN, R2	; Set up return address
	BSBW	DU$MAKE_CONNECTION	; Make a connection to the MSCP server.
	RSB

;
;	Connection established and initial SET CONTROLLER CHARACTERISTICS
;	command is sent to controller.  Also an MSCP buffer and a RSPID are
;	allocated for the connection.
;
; Inputs:
;
;	R3	CDDB Address
;

RC_AFTER_MAKE_CONN:
	.JSB_ENTRY INPUT=   <         R3>,-
		   OUTPUT=  <>,-
		   SCRATCH= <R0,R1,R2,R3,R4,R5>,-
		   PRESERVE=<>

; All the connection dependent information regarding the newly formed 
; connection to the MSCP server is now propagated to all the UCB's in the 
; primary chain for this CDDB.  At the same time, every RWAITCNT value is 
; tested to insure that it is consistant with the reason codes in the UCB
; status fields.  This is a precautionary exercise. In END_SINGLE_STREAM,
; RWAITCNT is reduced by one and the wait count bumped flag is cleared.

	MOVAB	<CDDB$L_UCBCHAIN -	; Setup "previous" UCB address.
		-UCB$L_CDDB_LINK>(R3), -
		R5
120$:	MOVL	UCB$L_CDDB_LINK(R5), R5	; Link to next UCB.
	BEQL	130$			; Branch if no more UCBs to test.
	BSBW	DUTU$INIT_CONN_UCB	; Setup connection dep. UCB fields.
	CMPB	#MSCP$K_CM_EMULA, -	; Reconnecting to the VMS MSCPserver
		CDDB$B_CNTRLMDL(R3)	;  (emulator) ?
	BNEQ	120$			; Continue with next UCB if not.
	MOVW	#MSCP$K_SLUN_RSVP, -	; Else, reset MSCPUNIT field since it
		UCB$W_MSCPUNIT(R5)	;  needs to be recalculated.
	BRB	120$			; Loop through all UCBs.

130$:	; It is now possible to execute requests on behalf of any pending 
	; mount verification threads.  Therefore, the CDDB$V_NOCONN
	; bit is cleared.

	BICL	#<CDDB$M_NOCONN-
		!CDDB$M_PATHMOVE-
		!CDDB$M_PRMBSY>, -
		 CDDB$L_STATUS(R3)

; Restore normal CRB timeout routine and interval.

	MOVL	CDDB$L_CRB(R3),R0		; Get CRB address.
	MOVAB	DU$TIMER, -			; Establish permanent 
		CRB$L_TOUTROUT(R0)		;  timeout routine.
	MOVZWL	CDDB$W_CNTRLTMO(R3), R1		; Get cntrler timeout interval.
	ADDL3	R1, G^EXE$GL_ABSTIM, -		; Use that to set next timeout 
		CRB$L_DUETIME(R0)		; wakeup time.

	; The normal MSCP timeout mechanism is now in effect.  Henceforth,
	; no fork thread may use the CDDB permanent CDRP as a fork block.
	;
	; While the mount verification threads are being handled, also poll 
	; for any previously unknown devices handled by this server.

	BISL	#CDDB$M_DAPBSY, -		; Set DAP CDRP in use flag.
		CDDB$L_STATUS(R3)
	MOVL	CDDB$L_DAPCDRP(R3), R5		; Get DAP CDRP address.

	STALL_CALL_FORK,-			; Find all units 
		ROUTINE = DUTU$POLL_FOR_UNITS,-	;  on this controller.
		RES_LOC = R2,
.                      

RC_AFTER_UNIT_POLL:
	.JSB_ENTRY INPUT=   <         R3,R4,R5>,-
		   OUTPUT=  <>,-
		   SCRATCH= <R0,R1,R2,R3,R4,R5>,-
		   PRESERVE=<>

	BICL	#CDDB$M_DAPBSY, -		; Clear DAP CDRP in use flag.
		CDDB$L_STATUS(R3)

	; At this point, there once was a test for an empty CDDB$L_CDRPQFL.
	; Such a test is no longer valid because mount verification processing 
	; may very well have CDRPs active on the connection represented by 
	; this CDDB.

; At this point, the reconnection thread may need to be suspended (you may 
; wish to think of it as ended) until mount verification completes for all the 
; disks (or UCBs) which had mount verification started by DUTU$REVALIDATE.  
; This is necessary if and only if CDDB$L_WTUCBCTR is non-zero, indicating 
; that one or more UCBs are waiting for mount verification to complete.  When 
; this thread is "suspended," the end mount verification routine will call 
; DUTU$RESTART_NEXT_CDRP when the count of UCBs waiting for mount verification
; to complete reaches zero.  This scheme is described in excruciating detail
; in the DUTU$REVALIDATE preamble.

	TSTL	CDDB$L_WTUCBCTR(R3)	; Are any UCBs waiting for mnt. ver.?
	BNEQ	140$			; Continue if so.
	BRW	DUTU$RESTART_NEXT_CDRP	; Else, branch - no UCBs are waiting.
.                                                                      
140$:	BISL	#CDDB$M_RSTRTWAIT, -	; Signal that connection is waiting
		CDDB$L_STATUS(R3)	; to restart CDRPs and "suspend" the
	RSB				; reconnect thread.



	.SBTTL	DU$TIMER - Class Driver Timeout Mechanism Routine
;++
;
;	DU$TIMER - Time out Mechanism Routine.  
;
; Functional Description:
;
;	This routine is called periodically whenever CRB$L_DUETIME becomes due. 
;	At the time of a periodic call to DU$TIMER the Class Driver is in one 
;	of three states with respect to the intelligent mass storage controller 
;	associated with the CRB pointed at by R3.
;
;	1. State #1, the "normal" state for which this routine is optimized,
;	    is characterized by the following two conditions:
;
;		a) One or more MSCP commands are outstanding to the controller.
;		    This is determined by having a NON-empty queue of CDRP's
;		    hanging off the CDDB.
;
;		b) The oldest outstanding command was initiated since the
;		    previous invocation of DU$TIMER and is therefore not very
;		    old.  This is determined by comparing the RSPID of the
;		    currently oldest command to the RSPID of the oldest request
;		    at the time of the previous invocation.  If they are not
;		    equal then we are in State #1.
;
;	2. State #2 is characterized by having NO outstanding MSCP commands in
;	    the controller.  This is determined by finding an empty CDRP queue
;	    in the CDDB.
;
;	3. State #3 is the state where MSCP commands are outstanding and the
;	    oldest one has been outstanding for at least one previous DU$TIMER
;	    invocation.
;
;	If we determine that we are in state #1, we simply record the RSPID of 
;	the currently oldest outstanding MSCP command in CDDB$L_OLDRSPID and we 
;	initialize CDDB$L_OLDCMDSTS to all 1's.  We then calculate a new due 
;	time, place it in CRB$L_DUETIME and return to our caller, which results
;	in scheduling ourselves for the next invocation of DU$TIMER.
;
;	 States #2 and #3 share some common code.  In both cases an IMMEDIATE 
;	command is issued to the controller but for diverse reasons.  In the 
;	case of state #2 it will be an effective NOP command that is only 
;	issued to insure against the controller timing out the host (i.e. us) 
;	due to lack of activity on our part.  In the case of state #3, the 
;	IMMEDIATE command will be a "GET COMMAND STATUS" for the oldest 
;	outstanding MSCP command.
;
;	The common code they share consists of code to appropriate the pre-
;	allocated DAP CDRP pointed to by the CDDB for this controller. A RSPID 
;	and message buffer are allocated. Any error will cause the thread to 
;	terminate in the expectation that the VC error routine is or will soon 
;	run for this controller.  Also at this time a new due time is 
;	calculated prior to doing the DRIVER_SEND_MSG so that we will be able 
;	to time out the Immediate command.  Then the code for these two states 
;	diverges for a while to prepare distinct MSCP packets, do the 
;	SEND_MSG_BUF, and in the case of state #3, to do some specific 
;	processing upon receipt of the END PACKET for the IMMEDIATE command.  
;	This processing consists of insuring that the command status returned 
;	in the END PACKET indicates progress being made on the oldest 
;	outstanding command; and also of saving this received command status 
;	in the CDDB$L_OLDCMDSTS so as to have it available at the next 
;	invocation, if this oldest command is still outstanding. Following this 
;	the two code paths converge to return the message buffer and RSPID.
;
;	States #1 and #3 will return to the State #2 code if the controller 
;	provides load balancing information in order to send a GUS packet. The
;	controller will return it's current available load in the GUS end packet.
;
; Inputs:
;
;	R3	CRB of the intelligent disk controller
;
; 	IPL = IPL$_SCS, with fork spinlock held.
;
; Outputs:
;
;	Registers R0 through R5 are all possibly modified.
;
;--


WAIT_FOR_CREDIT	= 45			; How long the timer thread should
					;  wait for a send credit, in seconds.

DU$CREDIT_STALL::

	.JSB_ENTRY INPUT=  <         R3      >,-
		   SCRATCH=<R0,R1,R2,R3,R4,R5>

;
; We have timed out waiting for a send credit. Break the connection and allow
; it to reform.
;
	MOVL	CRB$L_SCS_STRUC(R3),R1	; R1 => CDDB.
	BICL	#<CDDB$M_IMPEND-	; Indicate that immediate command is
		!CDDB$M_DAPBSY>,-	;  no longer pending and we are no 
		CDDB$L_STATUS(R1)	;  longer using the DAP CDRP.
	MOVL	CDDB$L_PDT(R1),R4	; Load up PDT and CDT pointers.
	MOVL	CDDB$L_CDT(R1),R3
	CLRQ	R0			; Clear the error status.
	BRW	DU$CONNECT_ERR		; Shoot the connection.
.                                                        

TIMER::
 
DU$TIMER::

	.JSB_ENTRY INPUT=   <R3>,-
		   OUTPUT=  <>,-
		   SCRATCH= <>,-
		   PRESERVE=<>

	MOVL	CRB$L_SCS_STRUC(R3),R1	; R1 => CDDB.
	BITL	#CDDB$M_NOCONN,-	; Do we have a connection?
		CDDB$L_STATUS(R1)
	BEQL	5$			; Yes. Continue with timeout processing.
	RSB				; No. Vanish in a puff of bits.

5$:	BBC	#CDDB$V_IMPEND,-	; Branch if an immediate command is NOT
		CDDB$L_STATUS(R1),10$	;  pending.
;
; Bit set implies that an immediate "GET STATUS" type command has not
; completed in the timeout interval. So resynchronization logic is called.
;
	MOVZWL	#EMB$K_CTLRES_IMTMO, R0	; Set error type code.
	MOVL	R1, R3			; R3 => CDDB.
	BSBW	DU$RE_SYNCH		; Break connection, and log the event
	RSB

	ASSUME	CDDB$L_CDRPQFL   EQ   0
10$:	CMPL	(R1),R1			; If =, then list of CDRP's is empty
	BNEQ	12$			; EQL means empty list of CDRP's, (#2)
	CLRL	R0			; R0 flagged to indicate State #2.
	CLRL	CDDB$L_OLDRSPID(R1)	; Set to impossible value to prevent
	BRW	COMMON_PREP		;  inadvertent comparison error.

12$:	MOVL	(R1),R0			; R0 => CDRP associated with "oldest"
					;  outstanding MSCP command.
	CMPL	CDRP$L_RSPID(R0),-	; Compare RSPID of oldest request to
		CDDB$L_OLDRSPID(R1)	;  that of request current at time of
					;  previous invocation of DU$TIMER.
	BEQL	COMMON_PREP		; EQL implies State #3, i.e. current
					;  oldest has been around for awhile.

;--
; State1 - New oldest CDRP has been found, Save RSPID of this CDRP and
;  	   get load information if supported by controller.
;
; Inputs:
;	R0	Oldest outstanding CDRP
;	R1	CDDB address
;	R3	CRB  address
;
;--

STATE1:
	MOVL	CDRP$L_RSPID(R0),-	; State #1, we have a NEW oldest request
		CDDB$L_OLDRSPID(R1)	;  so record its RSPID in CDDB field.
	MNEGL	#1,CDDB$L_OLDCMDSTS(R1)	; And initialize its associated status.

;GH X-82U4 (already checked in, placeholder)
	MOVZBL	CDDB$B_CNTRLMDL(R1),R0	; Get controller model type.
	CMPB	#MSCP$K_CM_HSC40,R0	; If this controller is an HSC (any
	BEQL	GET_LOAD_INFO		;  model) proceed to code to send a
	CMPB	#MSCP$K_CM_HSC50,R0	;  GUS.
	BEQL	GET_LOAD_INFO
	CMPB	#MSCP$K_CM_HSC60,R0
	BEQL	GET_LOAD_INFO
	CMPB	#MSCP$K_CM_HSC70,R0
	BEQL	GET_LOAD_INFO
	CMPB	#MSCP$K_CM_HSC90,R0
	BEQL	GET_LOAD_INFO

	BBS	#MSCP$V_CF_LOAD,-	; If we need to get load information,
		CDDB$W_CNTRLFLGS(R1),GET_LOAD_INFO;  skip ahead to send GUS.
	MOVZWL	CDDB$W_CNTRLTMO(R1),-(SP); Pickup controller delta.
	ADDL3	(SP)+,-			; Calculate delta time for next
		G^EXE$GL_ABSTIM,-	;  periodic invocation of DU$TIMER.
		CRB$L_DUETIME(R3)
	CLRL	R0			; Indicate external call
	BSBW	DUTU$DODAP
	RSB

GET_LOAD_INFO:
	CLRL	R0			; Fall through to State #2 code below
					;  to always send a GUS to pick up load
					;  information.
;--
; COMMON_PREP - Common Setup for States 2 and 3
;
; Inputs:
;	R0	FLAG:   0 - State 2
;		      ^=0 - State 3
;	R1	CDDB address
;	R3	CRB address
;--
COMMON_PREP:
;
; For both state #2 and #3 an immediate command is issued to the controller.
; If state #2, it is a "GET UNIT STATUS", If state #3, it is a 
; "GET COMMAND STS". The setup here is common to both.
;
	BBC	#CDDB$V_DAPBSY,-	; We need the DAP CDRP to send our
		CDDB$L_STATUS(R1),40$	;  message. Exit if it is busy.
;
;       Special DAPBSY processing. If here, another thread owns the DAP CDRP
;       (DAPBSY set). If State 2, there are no outstanding commands to
;       process, so exit. If State 3, avoid possible collision in RE_SYNCH
;       by exiting if POLLING clear. If POLLING is set, the oldest outstanding
;       command is in fact a GUS so RE_SYNCH with the controller. This prevents
;       a hang in POLL_FOR_UNITS if the controller device logic is inoperative
;       but the port logic is still operating.

	CMPB    #MSCP$K_CM_EMULA, -     ; Ignore if a Server.
                CDDB$B_CNTRLMDL(R1)
        BEQL    35$
        TSTL    R0                      ;If State 2, or State 3 but not
        BEQL    35$                     ; POLLING, reload timer and RSB
        MOVL    CDDB$L_DAPCDRP(R1),R5   ; Pick up the CDRP.
        CMPL    R0,R5                   ;Is oldest CDRP the DAP CDRP?
        BNEQ    35$                     ;No. Reload timer.
        BBC     #CDDB$V_POLLING,-       ;If DAP CDRP and POLLING, a GUS has
                CDDB$L_STATUS(R1), 35$  ; timed out from POLL_FOR_UNITS
                                        ;Else, fall through.
	MOVZWL	#EMB$K_CTLRES_IMTMO, R0	; Set error type code.
	MOVL	R1, R3			; R3 => CDDB.
	BSBW	DU$RE_SYNCH		; Break connection, and log the event
	RSB

35$:
	MOVAB	DU$TIMER,-		; Restore normal timeout routine.
		CRB$L_TOUTROUT(R3)

	MOVZWL	CDDB$W_CNTRLTMO(R1),-(SP); Pickup controller delta.
	ADDL3	(SP)+,-			; Calculate delta time for next
		G^EXE$GL_ABSTIM,-	;  periodic invocation of DU$TIMER.
		CRB$L_DUETIME(R3)
	RSB

40$:	
	BISL	#<CDDB$M_IMPEND-	; Show we own DAPCDRP and an
		!CDDB$M_DAPBSY>,-	;  immediate command is in 
		CDDB$L_STATUS(R1)	;  progress.
	MOVL	CDDB$L_PDT(R1),R4	; Setup for SEND_MSG_BUF, R4=>PDT.
	MOVL	CDDB$L_DAPCDRP(R1),R5	; Pick up the DAP CDRP address.
	MOVL	R0,CDRP$L_UCB(R5)	; Save state flag.

	MOVAB	DU$CREDIT_STALL,-	; Load special timeout routine for 
		CRB$L_TOUTROUT(R3)	;  credit stalls.
	ADDL3	#WAIT_FOR_CREDIT,-	; Calculate delta time for 
		G^EXE$GL_ABSTIM,-	;  invocation of credit stall routine.
		CRB$L_DUETIME(R3)

	ALLOC_RSPID
.            
	ALLOC_MSG_BUF
.              
	BLBS	R0,41$			; Continue if successful.
	CLRL	R0			; Clear skip indicator -release map regs
	BSBW	DUTU$DEALLOC_ALL	; Return all held resources
	CLRL	CDRP$L_UCB(R5)		; To avoid possible confusion later.
	MOVL	CRB$L_SCS_STRUC(R3),R1	; R1 => CDDB.
	BICL	#<CDDB$M_IMPEND-	; Indicate that immediate command is
		!CDDB$M_DAPBSY>,-
		CDDB$L_STATUS(R1)	;  no longer pending.
	RSB				; Say goodnight to the folks Gracie.

41$:	MOVL	CDRP$L_UCB(R5),R0	; Restore state flag.
	CLRL	CDRP$L_UCB(R5)		; To avoid possible confusion later.
	MOVL	CRB$L_SCS_STRUC(R3),R1	; R1 => CDDB.
	CMPL	(R1),R0			; Does R0 still point to the oldest
	BEQL	45$			;  CDRP?   If not, clear it
	CLRL	R0			;  so we will enter state #2 code.

45$:	MOVL	CDDB$L_PDT(R1),R4
	MOVQ	R0, -(SP)		; Save valuable registers.
	INIT_MSCP_MSG			; Initalize buffer for MSCP message.
	MOVQ	(SP)+, R0		; Restore valuable registers.

	MOVAB	DU$TIMER,-		; Restore normal timeout routine.
		CRB$L_TOUTROUT(R3)

	MOVZWL	CDDB$W_CNTRLTMO(R1),-(SP); Pickup controller delta.
	ADDL3	(SP)+,-			 ; Calculate delta time for next
		G^EXE$GL_ABSTIM,-	;  periodic invocation of DU$TIMER.
		CRB$L_DUETIME(R3)

	TSTL	R0			; Test for State #2 or State #3.
	BNEQ	STATE3 			; NEQ implies State #3. 

; State #2 specific code.
; Here we prepare the MSCP packet for the "GET UNIT STATUS" command for
;	unit #0, which is an effective NOP command.  This is done to
;	maintain minimum activity so that the controller will not time
;	out the host (i.e. us). NOTE that since the MSCP buffer has been
;	cleared above, there is no need to specify unit #0 in the command
;	buffer.
;
; Inputs:
;	R1	CDDB address
;	R2	MSCP Packet
;	R3	CRB  address
;	R4	PDT  address
;	R5	CDRP address
;
; Outputs:
;
;	Registers R0 through R5 are modified.
;--

STATE2:	
	MOVB	#MSCP$K_OP_GTUNT,-	; Move in "GET UNIT STATUS" opcode.
		MSCP$B_OPCODE(R2)
	SEND_MSCP_MSG			; Here we call to send the MSCP packet
					;  to the intelligent disk controller.

;
; Control is returned here after receipt of the END PACKET corresponding to 
; the MSCP NOP sent above.  Control is regained due to a callback from our own 
; INPUT DISPATCHER ROUTINE.  The load information is stored in the 
; response, the RSPID is recycled, and the END PACKET is is recycled for use 
; as our next MSCP packet.
;
;
; Inputs:
;
;	R0	Status of send operation
;	R1	Size of command packet sent
;	R2	MSCP message buffer address
;	R3	CDDB  address
;	R4	PDT  address
;	R5	CDRP address
;

	.JSB_ENTRY INPUT=  <R0,R1,R2,R3,R4,R5>,-
		   SCRATCH=<R0,R1,R2,R3,R4,R5>

	CMPW	#SS$_ILLCDTST,R0	; Did we get an error from the send?
	BNEQ	42$			; If so, error recovery is required.
	BUG_CHECK	DISKCLASS,FATAL

42$:	MOVL	CRB$L_SCS_STRUC(R3),R1	; R1 => CDDB.
	BBC	#MSCP$V_CF_LOAD,-	; Is there anything useful in the
		CDDB$W_CNTRLFLGS(R1),-	;  load field. Copy it to the CDDB
		43$			;  if there is.
	MOVW	MSCP$W_LOAD_AVAIL(R2),-
 		CDDB$W_LOAD_AVAIL(R1)
        CMPB    #PDT$C_PE,-             ; If this port is NI, we are done.
                PDT$B_PDT_TYPE(R4)
	BEQL	43$
	MOVZWL	CDDB$W_LOAD_AVAIL(R1),R2; Otherwise, increase the load avail
	BLEQ	43$			; value, if it is gtr than zero,
	ASHL	#2,R2,R2		; to make this server appear more
	MOVW	R2,CDDB$W_LOAD_AVAIL(R1); attractive.

43$:	CLRL	R0			; Clear skip indicator -release map regs
	BSBW	DUTU$DEALLOC_ALL	; Release all SCS resources
	MOVL	CRB$L_SCS_STRUC(R3),R1	; R1 => CDDB.
	BICL	#<CDDB$M_IMPEND-	; Indicate that immediate command is
		!CDDB$M_DAPBSY>,-
		CDDB$L_STATUS(R1)	;  no longer pending.
	CLRL	R0			; Indicate external call
	BSBW	DUTU$DODAP		; Continue by doing DAP processing.
	RSB


; State #3 specific code.
; Here we prepare the MSCP packet for a "GET COMMAND STATUS" command.
;
; Note: CDRP$L_UCB may be zero.  This can occur only when DUTU$POLL_FOR_UNITS 
; has not yet located a single unit.  In such cases, the unit number in the 
; GET COMMAND STATUS is left zero.  This will most likely cause the remote 
; controller to report that this command is unknown.  Receipt of such a report 
; twice in a row causes the controller to be reinitialized.  However, this is 
; the correct action because the command in question must be a GET UNIT STATUS 
; command issued by DUTU$POLL_FOR_UNITS.  GET UNIT STATUS is a sequential 
; command and failure to complete it in a timeout interval is by definition 
; caused by a sick controller.
;
; Inputs:
;	R0	CDRP outstanding for controller timeout period
;	R1	CDDB address
;	R2	MSCP Packet
;	R3	CRB  address
;	R4	PDT  address
;	R5	CDRP address
;
; Outputs:
;
;	Registers R0 through R5 are modified.
;--

STATE3:
	MOVL	CDRP$L_UCB(R0),R0	; R0 => UCB for oldest outstanding request.
	BEQL	53$			; Branch if no UCB address.

	MOVW	UCB$W_MSCPUNIT(R0),-	; Setup UNIT field.
		MSCP$W_UNIT(R2)
	MOVL	CDDB$L_CDRPQFL(R1), R0	; Restore CDRP address.

53$:	MOVB	#MSCP$K_OP_GTCMD,-	; Setup OPCODE field.
		MSCP$B_OPCODE(R2)

	MOVL	CDDB$L_OLDRSPID(R1),-	; Setup OUTSTANDING COMMAND REFERENCE
		MSCP$L_OUT_REF(R2)	;  field.
	SEND_MSCP_MSG			; Here we call to send the MSCP packet
					;  to the intelligent disk controller.

;
; Control is returned here upon receipt of the END PACKET for the above 
; "GET COMMAND STATUS" message. First a check is made to make sure that 
; progress has indeed been made on the outstanding command, by comparing the 
; outstanding command status from the END PACKET to the previous value in 
; CDDB field CDDB$L_OLDCMDSTS. 
;
;
; Inputs:
;
;	R0	Status of send operation
;	R1	Size of command packet sent
;	R2	MSCP message buffer address
;	R3	CDDB  address
;	R4	PDT  address
;	R5	CDRP address
;

	.JSB_ENTRY INPUT=  <R0,R1,R2,R3,R4,R5>,-
		   SCRATCH=<R0,R1,R2,R3,R4,R5>

	CMPW	#SS$_ILLCDTST,R0	; Did we get an error from the send?
	BNEQ	55$			; If so, error recovery is required.
	BUG_CHECK	DISKCLASS,FATAL

55$:	MOVL	CRB$L_SCS_STRUC(R3),R1	; R1 => CDDB.
	CMPL	MSCP$L_CMD_STS(R2),-	; Compare received outstanding command
		CDDB$L_OLDCMDSTS(R1)	;  status to previous value.
	BLSSU	60$			; LSSU implies progress made so branch.

	; Anything else, implies no progress has been made.  So we goto
	;  re-synchronize with the intelligent controller and re-issue all
	;  outstanding commands.  Since we actually have an end message,
	;  we log it along with the reason for resetting the controller,
	;  since it may have useful information for debugging purposes.

	MOVL	R1, R3			; R3 => CDDB.
	MOVZWL	#EMB$K_CTLRES_TMO, R0	; Set error type code.
	MOVZWL	CDRP$W_ENDMSGSIZ(R5),R1 ; Get message length.
					; (R2 is the message buffer address).
	BSBW	DU$RE_SYNCH		; Break connection, and log the event
	RSB

60$:	MOVL	MSCP$L_CMD_STS(R2),-	; Remember this received outstanding
		CDDB$L_OLDCMDSTS(R1)	;  command status for next time.

70$:	CLRL	R0			; Clear skip indicator -release map regs
	BSBW	DUTU$DEALLOC_ALL	; Release all SCS resources
	MOVL	CRB$L_SCS_STRUC(R3),R1	; R1 => CDDB.
	BICL	#<CDDB$M_IMPEND-	; Indicate that immediate command is
		!CDDB$M_DAPBSY>,-
		CDDB$L_STATUS(R1)	;  no longer pending.

	MOVZBL	CDDB$B_CNTRLMDL(R1),R0	; Get controller model type.
	CMPB	#MSCP$K_CM_HSC40,R0	; If this controller is an HSC (any
	BEQL	80$			;  model) proceed to code to send a
	CMPB	#MSCP$K_CM_HSC50,R0	;  GUS.
	BEQL	80$
	CMPB	#MSCP$K_CM_HSC60,R0
	BEQL	80$
	CMPB	#MSCP$K_CM_HSC70,R0
	BEQL	80$
	CMPB	#MSCP$K_CM_HSC90,R0
	BEQL	80$

	BBS	#MSCP$V_CF_LOAD,-	; Do we need to go back and get load
		CDDB$W_CNTRLFLGS(R1),-	;  information? Branch if not.
		80$
	CLRL	R0			; Indicate external call
	BSBW	DUTU$DODAP		; Continue by doing DAP processing.
	RSB

80$:	BRW	GET_LOAD_INFO  		; Go back and send a GUS to pick
                                                                  
					;  up load information



	.SBTTL	DU$IDR - Class Driver Input Dispatch Routine
;++
;
;	DU$IDR - Class Driver Input Dispatching Routine.  
;
; Functional Description:
;
;	This routine is to the class driver what the Interrupt Service Routine 
;	is to a conventional driver.  We are called here by the Port Driver
;	and we are passed the address of an END PACKET or an ATTENTION
;	MESSAGE buffer.  By testing a bit in the ENDCODE field of the
;	received buffer we determine which of the two has been received.
;	For ATTENTION MESSAGES we immediately branch to ATTN_MSG.
;
;	For END PACKETs we first determine if the END PACKET is still of
;	interest.  This is done by testing whether the COMMAND REFERENCE
;	NUMBER returned in the END PACKET, interpreted as a RSPID, is
;	still valid.  If not, we merely deallocate the END PACKET and
;	return to our caller in the Port Driver, after logging the packet
;	as an error.
;
;	If the END PACKET is still of interest then before dispatching
;	to the code that originally issued the MSCP command for which we
;	have just received the END PACKET, we first remove the
;	CDRP associated with the command from the list of active CDRP's
;	defined by the listhead located at CDDB$L_CDRPQFL.
;
; Inputs:
;
;	R1	Message Length
;	R2	END PACKET or ATTENTION MESSAGE BUFFER
;	R3	Connection Descriptor Table address
;
;--

	ASSUME	OWN$V_LOG_ENABLD EQ 0
	ASSUME	OWN$M_LOG_ENABLD EQ 1

DU$IDR::

	.JSB_ENTRY INPUT=   <   R1,R2,R3       >,-
		   OUTPUT=  <>,-
		   SCRATCH= <>,-
		   PRESERVE=<>

	BITL	#MSCP$M_OP_END, -	; Is this an ATTENTION MESSAGE?
		MSCP$B_OPCODE(R2)
	.BRANCH_LIKELY
	BNEQ	11$			; Branch around if not an ATTENTION MESSAGE.
	BRW	ATTN_MSG		; Branch if its an ATTENTION MESSAGE.
11$:
;
; Process command END MESSAGES
;

;	BSBW    FT$RECIEVE_MESSAGE_FILTER ; Filter End Packet for potential error insertion

	; NOTE: The following is a duplication of the code in FIND_RSPID_RDTE.
	; The code is reproduced inline here to avoid two branches and make
	; this code path as fast as possible.

	MOVL	G^SCS$GL_RDT,R0		; R0 => base of the RDT.
	MOVZWL	MSCP$L_CMD_REF(R2),R5	; Pickup RDTE index from END PACKET.
	CMPL	R5,RDT$L_MAXRDIDX(R0)	; Bounds check on index.
	.BRANCH_LIKELY
	BLEQU	15$			; Index found within bounds
12$:	CMPW	#-1, MSCP$L_CMD_REF(R2)	; Check for sentinal CMD_REF indicating
	BEQL	14$			;  we have no interest in the response.
	CMPW	#-1, MSCP$L_CMD_REF+2(R2); Check for server poisoned end message
	BNEQ	13$			;   bug check if so.
	BUG_CHECK MSCPCLASS,FATAL	; MSCP server requested bug_check

13$:	MOVZWL	#EMB$K_BADRSPID, R0	; Set error type code.
	MOVL	CDT$L_AUXSTRUC(R3), R3	; R3 => CDDB.
	JSB	G^ERL$LOG_DMSCP		; Log message with bad or stale RSPID.
	MOVL	CDDB$L_CDT(R3), R3	; Restore CDT address.
14$:	DEALLOC_MSG_BUF_REG		; Deallocate ATTN MSG buffer.
	RSB				; Return to SCS caller.

15$:	MOVAQ	(R0)[R5],R5		; R5 => RDTE of interest.

	CMPW	RD$W_SEQNUM(R5),-	; See if RSPID is still valid by
		MSCP$L_CMD_REF+2(R2)	;  comparing the sequence number.
	BNEQ	12$			; NEQ implies invalid RSPID.
	ASSUME	RD$V_BUSY EQ 0
	BLBC	RD$W_STATE(R5), -	; Check busy bit, branch if RDTE
		12$			;  not in use.
	MOVL	RD$L_CDRP(R5),R5	; R5 => CDRP.
	MOVL	CDT$L_AUXSTRUC(R3),R0	; R0 => CDDB.
	CMPL	CDDB$L_OLDRSPID(R0),-	; See if oldest outstanding command has
		MSCP$L_CMD_REF(R2)	;  this Command Reference Number.
	BEQL	28$			; Branch if yes.
20$:	ASSUME	MSCP$K_LEN LT 32767
	MOVW	R1, CDRP$W_ENDMSGSIZ(R5); Save length of incoming packet.
	MOVL	R2, CDRP$L_MSG_BUF(R5)	; Save address of incoming packet.
	REMOVE_EL -			; Remove R5=>CDRP from list.  
		EL = (R5),-
		SCRATCH = R3 	

21$:	ASSUME	CDRP$V_CAND EQ 0
	BLBS	CDRP$L_DUTUFLAGS(R5), -	; Has request been canceled?
		30$			; If so, do cancel completion work.

23$:	BITL	#IRP$M_DIAGBUF,	-	; Was a diagnostic buffer supplied?
		CDRP$L_STS(R5)
	BNEQ	50$			; Branch if diagnostic buffer present.
25$:	MOVX	CDRP$Q_FR3(R5), R3	; Restore fork registers, R3 & R4.
	MOVX	CDRP$Q_FR4(R5), R4
	JMP	@CDRP$L_FPC(R5)		; Dispatch to issuer of MSCP command
.                                                                     
					; who will return to our caller.


; Out of mainline code for infrequent operations

28$:	CLRL	CDDB$L_OLDRSPID(R0)	; Prevent inadvertent timeouts due to
					;  reuse of RSPID in error situations.
	BRB	20$			; Branch back to normal flow.

30$:	BSBW	DUTU$TEST_CANCEL_DONE	; If this request completes a cancel
					;  operation, cleanup that operation.
	BRB	23$			; Branch back to normal flow.

50$:	BSBW	DUTU$DUMP_ENDMESSAGE	; If diagnostic buffer, record MSCP
					;  end message sent in the buffer.
	BRB	25$			; Branch back to normal flow.


;
; Process ATTENTION MESSAGES
;

ATTN_MSG:

	PUSHR	#^M<R1,R2,R3,R4>	; Save vital registers.
	MOVL	CDT$L_AUXSTRUC(R3), R3	; Get CDDB address.
	DISPATCH -			; Dispatch to attention message 
		MSCP$B_OPCODE(R2), -	;  specific processing:
		type=B, prefix=MSCP$K_OP_, < -
		<AVATN, UNIT_AVAILABLE_ATTN>, -
		<DUPUN, DUPLICATE_UNIT_ATTN>, -
		<ACPTH, ACCESS_PATH_ATTN> -
		>
INV_ATTN_MSG:				; Process invalid ATTENTION MESSAGE.
	MOVZWL	#EMB$C_INVATT, R0	; Invalid attention message type.
	JSB	G^ERL$LOG_DMSCP		; Log incorrect DISK MSCP message.
	POPR	#^M<R1,R2,R3,R4>	; Restore vital registers.
	DEALLOC_MSG_BUF_REG		; Deallocate ATTN MSG buffer.
	MOVL	CDT$L_AUXSTRUC(R3), R3	; Get CDDB again.
	MOVZWL	#EMB$K_CTLRES_INVMSG,R0	; Set error type code.
	BSBW	DU$RE_SYNCH		; Break connection, and log the event
	RSB



	.SBTTL Attention Message Processing
	.SBTTL   - Process Unit Available Attention Message
;++
;
; Functional Description:
;
;	This routine processes unit available attention messages.  If the
;	available unit is already known in the I/O database, no action is 
;	taken.  If the available unit represents a second path to an already 
;	known unit, the I/O database is altered to show the alternate path 
;	availability.  If the available unit represents a totally new device, 
;	it is added to the I/O database.
;
; Inputs:
;
;	R1	attention message size
;	R2	attention message address
;	R3	CDDB address
;
; Outputs:
;
;	R0 - R5 destroyed
;	All other registers preserved
;
;--

UNIT_AVAILABLE_ATTN:

	BSBW	DUTU$NEW_UNIT		; Process possible new unit.
	BLBC	R0, 90$			; Branch if no unit found or created.
	TSTL	R2			; Was a UCB found or created?
	BEQL	90$			; Branch if no UCB to update.
	BISL	#DEV$M_AVL, -		; Set unit available for use flag
		UCB$L_DEVCHAR(R2)	; in the UCB.
90$:	POPR	#^M<R1,R2,R3,R4>	; Restore vital registers.
	DEALLOC_MSG_BUF_REG		; Deallocate ATTN MSG buffer.
	RSB				; Return to SCS caller.



	.SBTTL   - Process Duplicate Unit Attention Message
;++
;
; Functional Description:
;
;	This routine processes duplicate unit attention messages.
;	Notification of the condition is sent to the operator's console and 
;	an entry is made in the error log.  If the unit described in the 
;	message cannot be found, an invalid MSCP message error log entry is 
;	generated.
;
; Inputs:
;
;	R1	attention message size
;	R2	attention message address
;	R3	CDDB address
;
; Outputs:
;
;	R0 - R5 destroyed
;	All other registers preserved
;
;--

DUPLICATE_UNIT_ATTN:

	BBS	#MSCP$V_SLUN, -		; If this message has the SLUN bit
		MSCP$W_UNIT(R2), 10$	;  set in MSCP unit, ignore it.
	CMPB	CDDB$B_CNTRLMDL(R3), -	; Likewise, if the controller that
		#MSCP$K_CM_EMULA	;  sent the message is an emulator,
	BEQL	10$			;  don't bother with it.
	MOVL	CDDB$L_UCBCHAIN(R3), R0	; Get address of UCB to start with
	BSBW	DUTU$LOOKUP_UCB		; Locate UCB for this message.
	MOVL	R0, R3			; Setup UCB address.
	BEQL	10$			; If no UCB found, ignore message.
	BSBW	DUTU$SEND_DUPLICATE_UNIT; Send message to operator.
	MOVZWL	#EMB$C_DUPUN, R0	; Setup duplicate unit error log code.
	JSB	G^ERL$LOGMESSAGE	; Error log attention message 
10$:	POPR	#^M<R1,R2,R3,R4>	; Restore vital registers.
	DEALLOC_MSG_BUF_REG		; Deallocate ATTN MSG buffer.
	RSB				;  and return to caller.



	.SBTTL   - Process Access Path Attention Message
;++
;
; Functional Description:
;
;	This routine processes access path attention messages.  If the access 
;	path represents a second path to an already known unit, the I/O 
;	database is altered to show the alternate path availability, and an 
;	entry is made in the error log indicating receipt of the message.
;	If the unit described in the message cannot be found, an invalid MSCP 
;	message error log entry is generated.
;
; Inputs:
;
;	R1	attention message size
;	R2	attention message address
;	R3	CDDB address
;
; Outputs:
;
;	R0 - R5 destroyed
;	All other registers preserved
;
;--

ACCESS_PATH_ATTN:

	BSBW	DUTU$SETUP_DUAL_PATH	; Process possible dual path unit.
10$:	POPR	#^M<R1,R2,R3,R4>	; Restore vital registers.
	DEALLOC_MSG_BUF_REG		; Deallocate ATTN MSG buffer.
	RSB				; Return to SCS caller.



	.SBTTL	DU$DGDR	- Data Gram Dispatch Routine
;++
;
; Inputs:
;
;	R1	length of datagram
;	R2	datagram
;	R3	CDT address
;	R4	PDT address
;
;--

DU$DGDR:

	.JSB_ENTRY INPUT=   <   R1,R2,R3,R4   >,-
		   OUTPUT=  <>,-
		   SCRATCH= <>,-
		   PRESERVE=<R0,R1,R2,R3,R4,R5>

	MOVL	CDT$L_AUXSTRUC(R3),R0	; R0 => CDDB
	MOVL	R3,R5			; Save pointer to CDT.
	CMPB	#MSLG$K_DISK_TRN,-	; Look into error log message.
		MSLG$B_FORMAT(R2)	;  See if disk transfer error.
	BEQL	5$			; EQL means yes.
	CMPB	#MSLG$K_REPLACE,-	; See if bbr attempt error.
		MSLG$B_FORMAT(R2)
	BEQL	5$			; EQL means yes.
	CMPB	#MSLG$K_SDI,-		; See if SDI error.
		MSLG$B_FORMAT(R2)
	BEQL	5$			; EQL means yes.
	CMPB	#MSLG$K_SML_DSK, -	; See if small disk error.
		MSLG$B_FORMAT(R2)
	BNEQ	30$			; NEQ means unit # field maybe invalid.

5$:					; If here, then UNIT # field valid.
	SUBL3	#<UCB$L_CDDB_LINK -	; Get "previous" UCB address in R3.
		 -CDDB$L_UCBCHAIN>, -
		R0, R3

10$:	MOVL	UCB$L_CDDB_LINK(R3), R3	; Chain to next UCB.
	BEQL	30$			; No more UCBs.
	CMPW	UCB$W_MSCPUNIT(R3),-	; See if datagram (error log packet)
		MSCP$W_UNIT(R2)		;  for this unit.
	BNEQ	10$			; If not, branch abck to try next unit.

15$:	MOVZWL	#EMB$C_DM,R0		; Put type of message into R0.
	JSB	G^ERL$LOGMESSAGE	; And call to log message.

20$:	MOVL	R5,R3			; Restore R3 => CDT.
	SUBL	PDT$L_DGOVRHD(R4),R2	; R2 => SCS header of datagram.
	QUEUE_DG_BUF			; Requeue datagram buffer.
	RSB				; Return to port.

30$:	MOVL	MSCP$L_CMD_REF(R2),R3	; Pickup RSPID from END PACKET.
	BEQL	40$			; EQL means no RSPID.
	PUSHR	#^M<R1,R5>		; Save registers.
	MOVL	R3, R5			; Copy RSPID.
	FIND_RSPID_RDTE			; Lookup RDTE for RSPID.
	MOVL	R5, R3			; Copy RDTE address.
	POPR	#^M<R1,R5>		; Restore saved registers.
	BLBC	R0,40$			; Branch if lookup error.
	MOVL	RD$L_CDRP(R3),R3	; Get CDRP address in R3.
	MOVL	CDRP$L_UCB(R3),R3	; Get UCB address in R3 (if any).
	BNEQ	15$			; If we get a UCB, use it.

40$:	MOVL	CDT$L_AUXSTRUC(R5),R3	; R3 => CDDB.
	MOVZWL	#EMB$C_NOUNIT_DG,R0	; Indicate type of message.
	JSB	G^ERL$LOG_DMSCP		; Call to log message.
	BRB	20$			; Rejoin code.



	.SBTTL	DU$INVALID_STS - Unknown error returned
;++
;
; Functional Description:
;
;	This routine is called is a status is returned from a MSCP server 
;	that is unknown to this driver. The MSCP end message is logged in the 
;	error log, and the connection is broken to the controller.
;
; Inputs:
;
;	R2	MSCP packet address
;	R3	UCB address
;	R4	PDT address
;	R5	CDRP address
;	CDRP$L_ENDMSGSIZ(R5) => length of MSCP packet with invalid status
;
;--

DU$INVALID_STS:

	.JSB_ENTRY INPUT=<R2,R3,R4,R5>

	MOVZWL	#EMB$C_INVSTS, R0	; Indicate type of record to log.
	MOVZWL	CDRP$W_ENDMSGSIZ(R5), R1; Pickup length of faulty packet.
	MOVL	UCB$L_CDDB(R3), R3	; Get CDDB address for logging error.
	JSB	G^ERL$LOG_DMSCP		; Log disk MSCP error.
	BSBW	DUTU$INSERT_RESTARTQ	; Queue CDRP for retry.
	MOVZWL	#EMB$K_CTLRES_INVMSG, R0  ; Set error type code.
	BSBW	DU$RE_SYNCH		; Break connection, and log the event
	RSB



	.SBTTL	DU$FAST_RECVMSG_REQ - Check if Fast Path I/O completion possible 
;++
;                                                          
;	DU$FAST_RECVMSG_REQ - Check if Fast Path I/O completion possible 
;
; Functional Description:
;
;	This routine is called to determine Fast Path can be used on a I/O
; 	completion. All the DUDRIVER checks involved in I/O completion are 
; 	performed and if no complications are found then we check with
;	SCS about all the SCS resources associated with this I/O. If SCS
; 	permits the use of Fast Path then we perform any updates to the SCS
;	database that must be performed before the SCS spinlock can be released.
;
;	We are called here by the Port Driver and are passed the address of
;	an END PACKET or an ATTENTION MESSAGE buffer.  By testing a bit in
;	the ENDCODE field of the received buffer we determine which of the
;	two has been received. Only END messages can use Fast Path.
;
;	For END PACKETs we first determine if the END PACKET is still of
;	interest.  This is done by testing whether the COMMAND REFERENCE
;	NUMBER returned in the END PACKET, interpreted as a RSPID, is
;	still valid.  Only valid RSPIDs can use Fast Path.
;
;	Calling sequence:
;		JSB	@CDT$FAST_RECVMSG_REQUEST(CDT)
;
; Inputs:
;	R1	Message Length
;	R2	END PACKET or ATTENTION MESSAGE BUFFER
;	R3	Connection Descriptor Table address
;	R4	PDT address
;
;Outputs:
;	R0	= 1 if Fast Path permitted for completion processing
;		= 0 if Fast Path denied for completion processing
;	R2	END PACKET or ATTENTION MESSAGE BUFFER
;	R5	CDRP address if R0 = SS$_NORMAL
;	R1,R2 destroyed
;	Other registers preserved
;--

DU$FAST_RECVMSG_REQ:
	.JSB_ENTRY INPUT=   <      R2,R3,R4   >,-
		   OUTPUT=  <R0,   R2,      R5>,-
		   SCRATCH= <   R1            >,-
		   PRESERVE=<                 >

;
; Perform all DUDRIVER check that might prevent an I/O completion via Fast Path.
;
	BITL	#MSCP$M_OP_END, -	; Is this an ATTENTION MESSAGE?
		MSCP$B_OPCODE(R2)
	BEQL	NO_FAST_COMPL1		; Yes, so can't use Fast Path
;
; Process command END MESSAGES
;

	; NOTE: The following is a duplication of the code in FIND_RSPID_RDTE.
	; The code is reproduced inline here to avoid two branches and make
	; this code path as fast as possible.

	MOVL	G^SCS$GL_RDT,R0		; R0 => base of the RDT.
	MOVZWL	MSCP$L_CMD_REF(R2),R5	; Pickup RDTE index from END PACKET.
	CMPL	R5,RDT$L_MAXRDIDX(R0)	; Bounds check on index.
	BGTRU	NO_FAST_COMPL1		; No, so can't use Fast Path

	MOVAQ	(R0)[R5],R5		; R5 => RDTE of interest.

	CMPW	RD$W_SEQNUM(R5),-	; See if RSPID is still valid by
		MSCP$L_CMD_REF+2(R2)	;  comparing the sequence number.
	BNEQ	NO_FAST_COMPL1 		; Not valid, so can't use Fast Path
	;
	; One validity check on the RSPID is enough
	;
	ASSUME	RD$V_BUSY EQ 0
	BLBC	RD$W_STATE(R5), -	; Check busy bit, branch if RDTE
	NO_FAST_COMPL1 			;  not in use as can't use Fast Path.

	MOVL	RD$L_CDRP(R5),R5	; R5 => CDRP.
	;
	; Message length only needed for logging and is saved on stack earlier
	;
;	MOVW	R1, CDRP$W_ENDMSGSIZ(R5); Save length of incoming packet.
	MOVL	R2, CDRP$L_MSG_BUF(R5)	; Save address of incoming packet.

	ASSUME	CDRP$V_CAND EQ 0
	BLBS	CDRP$L_DUTUFLAGS(R5), -	; Has request been canceled?
		NO_FAST_COMPL 		; If so, can't use Fast Path
	;
	; Diagnostic buffers are ignored for Fast Path.
	; Don't need to Restore R3 and R4 from fork block. This leaves R3=CDT and R4=PDT.
	;
	IF_MSCP	FAILURE then=NO_FAST_COMPL ; Branch if MSCP error as can't use Fast Path
;
;	I/Os with WLE are screened out on Start I/O so don't need any WLE code here.
;       Not using Fast Path for WLE I/Os means that no RBUN exists and none is wanted
;	therefore the FAST_RECVMSG_CHECK_RES service is guaranteed to fail.
;

;
;	Error log can't be set as no error occured so CDRP$M_ERLIP needn't be cleared.
;
	MOVL	CDT$L_AUXSTRUC(R3),R0	; R0 => CDDB.
	BITL	#CDDB$M_SNGLSTRM, -	; Is CDDB in single stream mode?
		CDDB$L_STATUS(R0)
	BNEQ	NO_FAST_COMPL		; Branch if single stream.
;
;	MVIRP I/Os are screened out on Start I/O so don't need to check them here.
;       Not using Fast Path for MVIRPs means that no RBUN exists and none is wanted
;	therefore the FAST_RECVMSG_CHECK_RES service is guaranteed to fail.
;

;
; Now check with SCS about any problems with SCS resources that would preclude Fast Path.
;
	FAST_RECVMSG_CHECK_RES -	; Check SCS resources
		FAST_PATH_DENIED=NO_FAST_COMPL2	; Branch if can't do Fast completion
;
; Fast Path can be used for this I/O completion! First log the pkt then
; make any DUDRIVER updates to the SCS database that must be performed before
; the SCS spinlock is dropped and then log the pkt after it has been updated.
; Logging is not done immediately on entry to this routine as we don't want the
; packet to be logged twice if Fast Path can't be used.
;

	MOVL	CDRP$L_MSG_BUF(R5),R2	; Get message buffer address
	MOVL	CDT$L_AUXSTRUC(R3),R0	; R0 => CDDB.
	CMPL	CDDB$L_OLDRSPID(R0),-	; See if oldest outstanding command has
		MSCP$L_CMD_REF(R2)	;  this Command Reference Number.
	.BRANCH_LIKELY 
	BNEQ	50$			; Branch if yes.
	CLRL	CDDB$L_OLDRSPID(R0)	; Prevent inadvertent timeouts due to
					;  reuse of RSPID in error situations.

50$:	REMOVE_EL -			; Remove R5=>CDRP from list.  
		EL = (R5),-
		SCRATCH = R1 	

;
; Fast Path can be used for this I/O completion, so return success.
;
	MOVL	#SS$_NORMAL,R0
	RSB

;
; Problem found that precludes use of Fast Path for I/O completion, so return failure.
; If we saved the message buffer in the CDRP then be sure to zero this pointer.
;
NO_FAST_COMPL:
;	MOVL	CDRP$L_UCB(R5),R1		;(DEBUG) Get device UCB
;	BEQL	NO_FAST_COMPL2			;(DEBUG) Branch if no UCB
;	INCREMENT_IOCNT COUNTER=FP_SYSAP_NORECV,- ; (DEBUG) Count number of SYSAP vetoes of Recv Msg
;			UCB=R1
NO_FAST_COMPL2:
	CLRL	CDRP$L_MSG_BUF(R5)		; Clear out message buffer ptr as Fast Path can't be used
NO_FAST_COMPL1:
	CLRL	R0				; Return failure indicating Fast Path can't be used
	RSB



	.SBTTL	DU$FAST_RECVMSG_PM - Fast I/O completion under the PM spinlock
;++
;                                                          
;	DU$FAST_RECVMSG_PM - Fast I/O completion under the PM spinlock
;
; Functional Description:
;
; This routine completes a successful I/O and posts the results via Fast Path. 
; Unsuccessful I/O have been screened out and does not use Fast Path. The
; PM spinlock is used for synchronization throughout this routine. The IOSB
; is setup and the local request completion is then invoked to post the
; I/O on the local CPU.
;
;Calling sequence:
;		JSB	@CDT$FAST_RECVMSG_PM(CDT)
;
; Inputs:
;	R2	Message buffer address
;	R3	CDT address
;	R4	PDT address
;	R5	CDRP address
;
;Outputs:
;	R0,R1,R2 destroyed
;	Other registers preserved
;--

DU$FAST_RECVMSG_PM:
	.JSB_ENTRY INPUT=   <      R2,R3,R4,R5>,-
		   OUTPUT=  <                 >,-
		   SCRATCH= <R0,R1,R2         >,-
		   PRESERVE=<                 >
;
; Complete the successful I/O and post results. Do any accounting and cleanup
; that remains.
;

;	MOVL	CDRP$L_UCB(R5),R1		;(DEBUG) Get device UCB
;	INCREMENT_IOCNT	COUNTER=FP_SYSAP_RECVS,- ; (DEBUG)Increment counter of FP Receives
;			UCB=R1
	CLRL	CDRP$L_LBUFH_AD(R5)	; Signal no local buffer handle 
 
	MOVL	#SS$_NORMAL@16, R0	; Set success status.
					; Here R0 has SS$_ code in hi order.
	MOVL	MSCP$L_BYTE_CNT(R2), R1	; Get # bytes actually transferred.
	ASHQ	#-16, R0,-		; Shift into proper position for IOSB.
		CDRP$L_IOST1(R5)

	CALL_REQCOM_LOCAL			; Perform a Fast Path request completion

	RSB

	.END
