doswstuff=0
	.IF DEFINED DEBUG$LOG
	.TITLE	LOGGING_DUTUSUBS Logging Disk/Tape Class Driver Subroutines
	.IFF
	.TITLE	DUTUSUBS Disk/Tape Class Driver Subroutines
	.ENDC
	.IDENT	'X-75'
;
;****************************************************************************
;*									    *
;*  COPYRIGHT © 1978, 1992, 1996 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.		    *
;*									    *
;*									    *
;****************************************************************************
;
;
;++
;
; FACILITY:
;
;	MSCP Disk and Tape Class Drivers
;
; ABSTRACT:
;
;	This module contains subroutines used by the MSCP disk and tape
;	class drivers.
;
; ENVIRONMENT:
;
;	This module is linked into
;		- DUDRIVER, the MSCP disk class driver,
;		- TUDRIVER, the TMSCP tape class driver.
;	The routines herein are called by those drivers as needed.
;	See individual routine descriptions for interface details.
;
;
;
; AUTHOR: Ralph O. Weber,	CREATION DATE: 7-AUG-1983
;
; MODIFIED BY:
;
;       X-75    HAR             Harold Read                    05-Dec-1996
;		Changed interface to find_ddb to expect the CDDB rather than a
;		DDB. Changed test for a served path from looking at the UCB to 
;		checking the controller model in the CDDB.
;
;       X-74    HAR             Harold Read                    08-Nov-1996
;               - Make sure init conn ucb is called to update related UCB fields
;                 when ucb$l_cdt is updated.
;               - Save MSCP buffer pointer across call to setup dual path
;
;	X-73	HAR		Harold Read			14-Oct-1996
;		Check status from call mount verify in R2 instead of R0.
;
;	X-72	HAR		Harold Read			26-Sep-1996
;		Fix typo in previous checking. Rats...
;
;	X-71	HAR		Harold Read			23-Sep-1996
;		Save R4 across call to mount verification.
;
;	X-70	HAR		Harold Read			31-May-1996
;                1. Added allocate pool macro, which zeros memory, for
;                   allocate on the fly CDRPs.
;                2. Replace calls to exe$alononpaged with allocate pool macro
;                3. Created some permanent fork blocks at the end of the CDDB.
;                4. Add initialization for permanent fork blocks.
;                5. Replaced prmcdrp usage as a fork block with new perm fork
;                   blocks for get unit name, init hirt & trigger mount verify.
;                6. Implemented allocate on the fly CDRPs for get unit
;                   name and mount verification.
;                7. Reserve the prmcdrp for connection processing only.
;                8. Init current CDT for internal IRPs where missing.
;		 9. Add a check in post cdrp to prevent posting permanent CDRPs
;		10. Add checks for an error returned from the send mscp msg macro.
;		11. Change error returned by send mscp msg from devoffln to 
;		    illcdtst, since connection failure is the only error that
;		    will be returned when a packet transmission is requested.
;               Other differences:
;                1. Add global labels to make debug easier.
;		 2. Added mode to alt reqcom macro to retain control after
;		    invocation. Re-implemented macro in post cdrp to fix a bug
;		    obtaining the SHAD from the ucb that was fixed in the macro.
;		 3. Delete code (again) that checks for ss$_abort and change
;		    ss$_abort error to ss$_illcdtst in walkerr.
;		 4. Removed check_rwaitcnt call from poll for units and subroutine.
;		 5. Used SAVD CDDB longword in FKB2P to store CDDB pointer for
;		    MOVE IODB.
;
;	X-69	RFB009		Ray Boucher			16-Aug-1996
;		Forgot to add IRPDEF so IRP$Q_QIO_P1 can be used to make
;		compile time decisions.
;
;       X-68    RFB008          Ray Boucher                      7-Aug-1996
;               Add conditional compiler code to allow building on OpenVMS
;               versions prior to V7.0
;
;	X-67	HAR		Harold Read			07-May-1996
;		Spelling, comment and format updates, replace tabs with
;		spaces where appropriate.
;
;	X-66	JCH710b John C. Hallyburton, Jr.		25-Apr-1996
;		EVMS-GRYPHON 1564: failure to pop R5 in DUTU$SETUP_DUAL_PATH.
;
;		When DUTU$MOVE_UNIT fails over to an MSCP controller,
;		be sure to set up the allocation class in UCB$Q_UNIT_ID.
;		Otherwise if we failed over from an HSC the allocation class
;		will not be set in UCB$Q_UNIT_ID and DUTU$FIND_DDB will create
;		a new DDB with the wrong allocation class, and things will
;		get very ugly very quickly.
;
;	X-65	JCH710a John C. Hallyburton, Jr.		 8-Apr-1996
;		Happy birthday, mom. DUTU$FIND_DDB_AFTER_LOCK apparently can
;		get called with other than a UCB as the fork block. Therefore,
;		decode block type to figure out where to fetch UCB pointer,
;		so we can test for MSCP served DDB. If served DDB, obtain the
;		alloclass from the UCB. Conversely, if not served DDB, we must
;		store the CDDB allocls in the DDB.
;
;	X-64	JCH710	John C. Hallyburton, Jr.		14-Mar-1996
;		STAR:: DOCD$:[EVMS.PROJECT_DOCUMENTS]FS-SCSI-NAMING.PS
;		Check for allocation class on MSCP messages, now that a
;		remote device can have an allocation class different from
;		that of its host.
;
;	X-63	DVE004		Dennis Van Eron			28-Feb-1996
;		In routine DUTU$NEW_UNIT, changed BBSS to BBS and
;		set the UF_REPL bit in the UCB unit flags field.
;
;	X-62	DVE003		Dennis Van Eron			15-Sep-1995
;		Changed PDT$B_TYPE to PDT$B_PDT_TYPE in DUTU$POLL_FOR_UNITS
;		allowing for correct value to be loaded in
;		CDDB$W_LOAD_AVAIL.
;
;	X-61	DVE002		Dennis Van Eron			13-Jun-1995
;		Add second validity check to DUTU$NEW_UNIT before decision
;		to not configure Controller Based Shadowing (DUS,DJS)
;		devices.
;
;	X-60	DVE		Dennis Van Eron			14-Apr-1995
;		Check for a DR device in LN_AFTER_FIND_DDB and set DEV$M_NLT bit
;		in DEVCHAR2 if SERVER is an ALPHA. This allows initing of
;		ASTRO/PLUTO disks on a CLIENT.
;
;	       X-57U3  RFB007	       Ray Boucher		14-Apr-1995
;		TUDRIVER not setting SCSI compaction at boot time because
;		the SCSI bit in DEVCHAR2 not valid at the time it's checked
;		in DUTU$NEW_UNIT. Use MEDIA_ID field in MSCP end message to
;		detect served SCSI new unit. Remove TLZ06 compaction
;		restriction.
;
;	X-59	RFB006		Ray Boucher			17-Mar-1995
;		Check for a tape device before setting bit 15 of
;		MSCP$W_UNT_FLGS because it's REPLC for disk and WBC for
;		tape. TMSCP server doesn't support WBC for non-MSCP tapes.
;
;	X-58	PJH		Paul J. Houlihan		12-Nov-1994
;		Fast Path Changes:
;		- Flag mainline code that is on Fast Path.
;		- Add ability to repossess CDRP on the active queue.
;		- Clone port Fast Path information to device UCB.
;		- Add Fast Path Change Affinity routine.
;
;	X-57	LSS0324		Leonard S. Szubowicz	21-Feb-1995
;		Alter the IRP/CDRP assumes to deal with forthcoming changes
;		to these structures.
;
;	X-56	TRC1007		Terry Cassidy			23-Jan-1995
;		Merge stray packet back into mainline
;		PRD		Paul R. DeStefano		06-Jan-1995
;		Modified DUTU$LOG_PKT and DUTU$LOG_IRP_PKT to test for
;		the absence of structures using BGEQ rather than BEQL.	BEQL
;		doesn't work if the pool poisoner is on.  BGEQ checks for
;		non-system address.  Also removed superfluous TSTLs.
;
;	X-55	TRC1000		Terry Cassidy			28-Oct-1994
;		Demote CMPL to CMPW for #SS$_ operands
;
;	X-54	RFB005		Ray Boucher			19-Sep-1994
;		Set DEV$V_SCSI in DEVCHAR2 if DDB is MK. Add support for
;		tmscp served non-mscp tapes.
;
;	X-53	GH		Gary Hughes			14-Sep-1994
;		Remove DUTU$SERVER_MV and replace calls to it with the MSCP_MV
;		macro, which will call the server to perform it's own
;		housekeeping.
;
;	X-52	GH		Gary Hughes			5-Aug-1994
;		Add CDT cross checking to DUTU$SEND_MSCP_MSG. Post the IRP
;		with an error if the CDT pointers do not match.
;
;	X-51	RFB004		Ray Boucher			28-Jun-1994
;		The DUTU$LOG_IRP_EXIT_PKT port to Alpha AXP in the logging
;		DU/TUDRIVER does not correctly log the IOSB.
;
;	X-50	GH		Gary Hughes			21-Jun-1994
;		Fix branch sense in test of UCB$L_FAIL_MUTEX in DUTU$NEW_UNIT.
;
;	X-49	NJB		Nancy Jean Burkholder		24-May-1994
;		Modify START_DISPLAY to return SS$_DEVOFFLINE instead of
;		SS$_ILLCDTST.
;
;	X-48	GH		Gary Hughes			12-May-1994
;		Fold of X-52A1A1.
;		Modify Setup_dual_path and Failover to set the MV_RESTART flag
;		if an event occurs that indicates a new, better path for a
;		device already in mount verification. Modify New_unit to
;		proceed to Trigger_mv if we have found a new direct path
;		and Mount Ver is in progress (Trigger_mv will check for this
;		and set the restart bit appropraitely).
;
;	X-47	GH034		Gary Hughes			8-Apr-1994
;		Modify DUTU$LOCATE_UNIT to compare the media ID in the GUS
;		return and loop to walk_next_conn if they do not match.
;		This is now consistant with VAX behaviour (previously, it
;		would overwrite the old media ID causing much confusion).
;
;	X-46	WLG0E46		William Goleman			8-Apr-1994
;		Change data structure being used as a fork block in
;		ILS_AFTER_POLL_FOR_UNITS. Use fork block in the LDB instead
;		of the one in the DAP CDRP.
;
;	X-45	PJH		Paul J. Houlihan		7-Apr-1994
;		Use a new PRMBSY_CLEANUP_PERMITTED to avoid a deadlock
;		in DUTU$GET_DEVTYPE where the PRMCDRP is allocated and the
;		connection fails after the SEND_MSCP_MSG is started. The
;		deadlock occurs in connection cleanup which must allocate
;		the PRMCDRP to cleanup.
;
;	X-44			Brian Porter			04-APR-1994
;		Remove check for WLE IRP in BEGIN_MNTVER.  If the IRP
;		is for SRVIO then just post it.
;
;	X-43	GH033A		Gary Hughes			7-Mar-1994
;		Add check in DUTU$SETUP_DUAL_PATH to ensure that we have
;		located the correct UCB for an incoming ACPTH message. If
;		the message has a media ID, use it otherwise make sure we
;		find a DU or DJ device.
;
;	X-42	MAS0E07		Michael A. Stams		9-Feb-1994
;		Restore HBS member validation support.
;		Correctly start LDB search at first queue element.
;
;	X-41	RFB003		Ray Boucher			2-Feb-1994
;		1. Add call to DUTU$FAILOVER to include the "new" second
;		path after linking it to the UCB in LINK_2P_UCB. The call
;		was removed during the port to Alpha AXP.
;		2. In dutu$failover for tapes, if the primary path is served
;		and the secondary path is not then attempt failover to
;		secondary. This allows tapes to be fairly static but yet give
;		preference to direct path over served.
;
;	X-40	RFB002		Ray Boucher			21-Jan-1994
;		1. IO$DISPLAY failed with SS$_BADPARAM, a regression during
;		   the port from VAX.
;		2. Change the driver behavior dealing with device numbers
;		   >9999 from crashing the system to ignoring them
;		   (i.e., the same as it does for device #4095).
;		3. Add "no-bounce" code to prevent tapes from failing back
;		   and forth between HSx controllers after each dismount.
;		4. Correct the date in X-39.
;
;	X-39	MAS0E06		Michael A. Stams		10-Jan-1994
;		Move BUG_CHECK 'inline', add RSB at 700$ in LINK_NEW_UCB.
;
;	X-38	PJH		Paul J. Houlihan		17-Dec-1993
;		Add setting of EXFUNC_SUPP bit to new UCBs. Override unwanted
;		COPY_UCB behavior and get STS field from template UCB. This
;		also helps the NO_ASSIGN bit which wasn't getting set.
;
;	X-37	MAS0E05		Michael A. Stams		 8-Nov-1993
;		Check-in Paul's fix.
;		PJH		Paul J. Houlihan		01-Nov-1993
;		- Place PDT into R4 while scanning for canceled CDRPs. PDT
;		is needed by CANCEL_WAIT which is called in scan callback
;		routines.
;		- Add commented out callout to Faulty Towers.
;
;	X-36	RFB0001		Ray Boucher			8-Oct-1993
;		Remove hardcoded ORB. IOC_STD$COPY_UCB will create an ORB
;		from the DEVICE.DISK template.
;
;	X-35	LSS293		Leonard S. Szubowicz	14-Sep-1993
;		HLLDD: In routine DO_ORIG_UCB_IO, invoke the driver mount
;		verification routine using the step 2 interface.
;
;	X-33	MAS0E04		Michael A. Stams		13-Sep-1993
;		Additional Step2 conversion.
;
;	X-33	MAS0E03		Michael A. Stams		19-Aug-1993
;		Step2 conversion.
;
;	X-32	MAS0E02		Michael A. Stams		12-Jul-1993
;		Remove DUTU_TRACE_STORAGE Psect
;
;	X-31	MAS0E01		Michael A. Stams		 8-JUL-1993
;		Fold V15_PLUS (WHM/DCD Blade parity) into mainline.
;		(Merge and/or verify X-27 through X-30)
;
;		X-30	RWC125		Richard W. Critz, Jr.		18-Jun-1993
;			Add DDR support.
;
;		X-29	WLG0E29		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. This allows the secondary address,
;			DUTU$LOOKUP_UCB_RESUME to be eliminated. Fix the setting
;			of the type and subtype fields for the CDDB structure.
;
;		X-28	WLG0D28		William Goleman			10-Mar-1993
;			Prohibit access to served stripe sets.
;			Fix comments around SEND_MSCP_MSG macro.
;
;		X-27	DEE0176		David E. Eiche			26-Feb-1993
;			Change EVMS$DRIVER_DPT to DRIVER$DPT to conform to Step 2
;			driver table naming conventions.
;
;	X-26U6	MAS0P02		Michael A. Stams		 1-JUL-1993
;		Integrate Final Blade/Amber 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.
;
;			John J. Andruszkiewicz ,Cheryl J. Bulmer, Wayne Cardoza
;			Scott H. Davis, Paul R. DeStefano, Stu Farnham
;			Stephen Fiorelli, Rod Gamache, William Goleman
;			Hai Huang, Gary Hughes, Kevin M. Jenkins
;			Trudy C. Matthews, Robert L. Rappaport, Mark A. Stiles
;			Leonard S. Szubowicz, Ralph O. Weber, Ward C. Travis
;
;
;	X-26U5	MAS0P01		Michael A. Stams		28-May-1993
;		Step one of Blade Parity.
;		   Port MSCP logging routines.
;
;	X-26U4	WLG0E30		William Goleman			 5-May-1993
;		Change the way controller based shadow sets are recognized
;		and avoided. Move the test from the poll for units loop to
;		new unit processing.
;
;	X-26U3	WLG0E30		William Goleman			 9-Apr-1993
;		. Restore calls to IOC$FREE_UCB.
;		. Disable access to CBS from Alpha nodes.
;
;	X-26U2	WLG0E29		William Goleman			11-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. This allows the secondary address,
;		DUTU$LOOKUP_UCB_RESUME to be eliminated.
;
;	X-26U1	WLG0E28		William Goleman			 9-Mar-1993
;		Prohibit access to served stripe sets.
;		Fix comments around SEND_MSCP_MSG macro.
;
;	X-26	PKW189		Paul K. M. Weiss		25-Jan-1993
;		Fix byte references to IRP$C_LENGTH
;
;	X-25	WLG0D25		William Goleman			19-Jan-1993
;		Be explicit about setting input flag to DUTU$FIND_DDB.
;		The low bit of R0 indicates whether or not the IODB mutex
;		is held on entry to the routine.
;
;	X-24	PJH		Paul J. Houlihan		16-Dec-1992
;		Optimize mainline DUTUSUBS 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-23	PJH		Paul J. Houlihan		30-Nov-1992
;		Add init of DDB ACPD field which may not be initialized
;		if created by IOC$CONFIGURE which doesn't call IOC$INITDRV.
;
;	X-22	PJH		Paul J. Houlihan		18-Nov-1992
;		Add comment on new caller of NEW_SERVER.
;
;	X-21	WLG0D21		William Goleman			12-Nov-1992
;		Fix register confusion caused by reorganization of MOVE_UNIT.
;
;	X-20	SFS0595		Stephen F. Shirron		06-Nov-1992
;		Fix a porting bug in LINK_NEW_UCB (failure to loop back
;		after the FORK_WAIT).
;
;	X-19	WLG0D19		William Goleman			29-Oct-1992
;		Restore code removed from LOCATE_UNIT that checked for
;		SS$_ABORT being returned as a result of the SEND_MSCP_MSG.
;		Fix register save mask conflict in END_CONN_WALK.
;
;	X-18	WLG0D18		William Goleman			21-Oct-1992
;		Code changes resulting from inspection of device failover code.
;		Many changes to locate unit, connection walking, and packack
;		subroutines.
;
;	X-17	GHJ		Gregory H. Jordan		28-Sep-1992
;		Change BISB to BISL since it's operating on a longword field.
;		Add fold from V1_SSB, listed below:
;
;		X-12U2	GHJ3240 Gregory H. Jordan		10-Sep-1992
;		Fix deallocation of CDDB structures in CREATE_CDDB when an
;		existing CDDB with the same name is found.
;
;	X-16	GH001A		Gary Hughes			15-Sep-1992
;		V5.4-3 changes merged (except IRP logging).
;		Added TRIGGER_MV entry point to DUTU$FAILOVER.
;
;	X-15	WLG0D15		William Goleman			 9-Aug-1992
;		. Fix bug in WALKERR that was using the stack to save the
;		  offset to an error entry point during connection walking.
;		. Fix problem with register restoration in DUTU$CANCEL_RDT.
;
;	x-14	SWA		Scott W. Apgar			07-Aug-1992
;		Change the CRB offset auxstruc to use scs_struc.  auxstruc was
;		overloaded.
;
;	X-13	RWC089		Richard W. Critz, Jr.		 2-Jul-1992
;		Change semantics of channel numbers back to unsigned words.
;
;	X-12	WLG0D12		William Goleman			 5-Jun-1992
;		Make sure correct CDRP address is passed to the CANCEL_WAIT
;		macro in CANCEL_RDT and CANCEL_RDTWAIT.
;
;	X-11	WLG0A11	  William Goleman/Stephen F. Shirron	26-May-1992
;		Change search for appropriate DDB in NEW_SERVER routine to
;		look for a DDB with a DDT pointer to the dispatch routine
;		for this driver, instead of searching for a particular
;		device name.
;
;	X-10	PJH		Paul J. Houlihan		27-MAR-1992
;		- Smarten up the allocation of I/O structures via the LDB
;		to cover the 4 states possible for local and remote
;		controllers. This supports multiple local controllers and
;		even a local device ported between two local controllers.
;		- Fix connection walking where return address is lost in
;		LOCATE_UNIT.
;		- Fix finding of CDDB address in MOVE_IODB where R5 is a
;		FKB2P and not a CDRP.
;		- Add lines unintentionally dropped during porting of
;		LOOKUP_UCB but which are needed for short datagram
;		messages and local controllers.
;
;	X-34	WLG0A34		William Goleman			27-Feb-1992
;		. Save register containing size of pool allocated for LDB
;		  around MOVC that zeros out the buffer.
;		. Retrieve stored CDDB address using fork block offset
;		  instead of CDRP offsets in MOVE_IODB.
;
;	X-33	PJH		Paul J. Houlihan		21-Feb-1992
;		Ignore the last message returned on a poll for units if
;		a status of Unit-Offline is involved. The problem is that
;		the normal system disk polling retries needed when a slow
;		controller doesn't promptly identify it's devices, are not
;		performed for unit 0. The confusion results from always
;		assuming the last poll sweep end message about unit 0, always
;		contains valid information. This leads to a hung system
;		that is looping in mount verification when it should be
;		looping trying to poll for the system disk.
;
;	X-32	WLG0A32		William Goleman			14-Feb-1992
;		Fix bad parameter (R4) being passed to FIND_DDB.
;
;	X-31	WLG/PJH		William Goleman/PJH		14-Feb-1992
;		Use new quadword mutex lock/unlock routines.
;
;	X-30	PJH		Paul J. Houlihan		14-Feb-1992
;		Allow booting of disks with non-zero unit numbers by initing
;		UCB MSCPUNIT field to the UCB UNIT field as was done in VAX
;		VMS. Also fix problem in cleanup of regs saved on the stack.
;
;	X-29	PJH		Paul J. Houlihan		11-Feb-1992
;		Fix incorrect register usage in BOOTUCB test and make sure
;		valid is always set for boot device UCB so that subsequent
;		PACKACK succeeds.
;
;	X-28	WLG0A28		William Goleman			10-Feb-1992
;		Move initialization of the CRB FLCK field to CREATE_CDDB
;		so that it is done before the connection is established to
;		a remote system.
;
;	X-27	WLG0A27		William Goleman/Paul J. Houlihan  7-Feb-1992
;		. Change input to LOCK_IODB to expect continuation address in
;		  R4 instead of R3.
;		. Insert code for aquisition of IODB mutex in FIND_DDB
;		  subroutine. Not enough context can be stored across fork.
;		. PJH - Add support for local controllers to NEW_SERVER
;		  where the DDB should hang off the LOCALSB but the SYSTEMID
;		  in the CDDB should be from the pseudo-system block for the
;		  local controller.
;
;	X-26	WLG0A26		William Goleman			  7-Feb-1992
;		Fix refrence to promoted field IOC$GL_MUTEX, now a quadword.
;
;	X-25	WLG0A25		William Goleman			 27-Jan-1992
;		Move device configuration routines from DUDRIVER into this
;		module so that they may be shared by TUDRIVER.
;
;	X-24	WLG0A24		William Goleman			 21-Jan-1992
;		Remove explicit refrence to DU$FUNCTION_EXIT.
;
;	X-23	WLG0A23		William Goleman			 20-Jan-1992
;		; Repair broken assume for field promotion in CDRP.
;		. Fix entry masks on return from SEND_MSCP_MSG.
;		. Bad parameter being passed to DUTU$CHECK_NOCANCEL from
;		  DUTU$MOVE_UNIT.
;
;	X-22	WLG0A22		William Goleman			 17-Jan-1992
;		. Remove shifted bit instructions.
;		. Remove refrences to LOCK_IODB macro, and add a new subroutine
;		  to do the locking (DUTU$LOCK_IODB).
;		. Remove refrences to PERMCDRP_TO_CDDB macro.
;		. Skip over controller based shadow set virtual units.
;		.Straighten up some comments.
;		.Change interface to DUTU$FIND_DDB, pass the CDDB address
;		 instead of the address of the DDB to start searching with.
;		.Make sure all routines have entry masks.
;		.Add new subroutine for acquiring the I/O database mutex.
;
;	X-21	PJH		Paul J. Houlihan		20-Dec-1991
;		Final LASER DA Fixes.
;		- Fix interface to DUTU$DEALLOC_ALL routine. Skip indicator
;		was not being initialized for calls to main entry point.
;		- System disk doesn't have valid set, test off BOOTUCB and
;		set valid
;		- Remove extra LINK_NEW_UCB from DO_ORIG_UCB
;		- Fix JSB_ENTRY for DUTU$NEW_UNIT that prevented the return
;		of a value in R2.
;
;	X-20	WLG0A20		William Goleman			22-Nov-1991
;		Fix bad symbol refrence.
;
;	X-19	WLG0A19		William Goleman			21-Nov-1991
;		Fix broken assumptions.
;
;	X-18	BJT277		Benjamin J. Thomas III		21-Nov-1991
;		Use IRP for passing of QIO arguments P1 - P6
;
;	X-17	WLG0A17		William Goleman			19-Nov-1991
;		Reflect promotions made to byte and word fields in the CDDB.
;
;	X-16	BJT271		Benjamin J. Thomas III	15-Nov-1991
;		Promote FUNC fields
;
;	X-15	BJT260		Benjamin J. Thomas III	31-Oct-1991
;		Promote even more word fields STS in IRP and UCB to longwords
;
;	X-14	BJT259		Benjamin J. Thomas III	25-Oct-1991
;		Promote word fields STS in IRP and UCB to longwords
;
;	X-13	WLG0A11		William Goleman/Paul Houlihan 13-Sep-1991
;		Changes to cleanup of old CDRPs after connection loss.
;		Employ new SCS macros.
;
;	X-12	BJT247		Benjamin J. Thomas III	27-Sep-1991
;		Promote some UCB, IRP and CDRP fields to longwords
;
;	X-11	WLG0A11		William Goleman		10-Sep-1991
;		Eliminate use of UCB$B_FEX field which is being removed
;		for alignment and obsolescence reasons. Add new field in
;		the MSCP extension of the UCB for use as a failover mutex.
;
;	X-10	LSS0223		Leonard S. Szubowicz	30-Aug-1991
;		Expand UCB$W_REFC to a longword to avoid word granularity
;		problems within the same quadword.
;
;	X-9	JSSDU	John S. Simakauskas		30-Aug-1991
;		Change DU$SEND_IO to SEND_IO in DUTU$RESTART_NEXT_CDRP,
;		and in DUDRIVER. The TUDRIVER also has this label.
;
;	X-8	WLG07a		Stephen Shirron		18-Jun-1991
;		Incorporate changes sent up from Stephen Shirron. Fix
;		entry save mask in DU$COMMON_ROUTINE.
;
;	X-7	GHJ055		Gregory H. Jordan	23-Apr-1991
;		New device routine is SCS$DISK_MSCP_NEWDEV.
;
;	X-6	GHJ053		Gregory H. Jordan	19-Apr-1991
;		Update with new MSCP interface.
;		Remove $BQODEF and $RPBDEF.
;
;	X-5	GHJ052		Gregory H. Jordan	 8-Apr-1991
;		Use DRIVER_DATA macro for all DATA psects.
;
;		Fix up the calls to EXE$DEALNONPAG at the DUPLICATE_SYSTEMID
;		label to delete the CDDB and to return properly.
;
;		Fix up the returns in the connection walking routines.
;
;		Utilize the 2PFKB$L_SAVD_CDDB field, this field is
;		needed since the CDDB saved isn't always the same
;		as in UCB$L_CDDB.
;
;		Fix a couple JSB_ENTRY masks.
;
;		Several return to caller's callers needed to be JMPs
;		instead of JSBs.
;
;		Filler space was added into the DEVTYPE table to keep all
;		references aligned.
;
;		Call DUTU$BEGIN_CONN_WALK and DUTU$WALK_NEXT_CONN with the
;		STALL_CALL_FORK macro since these routines need the return
;		address passed in.
;
;	X-A04	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
;		Remove DUTU$RESTORE_CREDIT. A new SCS routine has been
;		added to provide the needed function of deallocating a
;		local buffer without sending a message. Conditionalize
;		code so that a new driver can be built for the port
;		simulator, V5.4, or EVMS.
;
;	X-89K6	WLG0AK6		William Goleman		15-Feb-1991
;		Remove boot block and dump file updates to primitive file
;		system on system disk failover. Enforce modularity by
;		preventing branches across calls to routines that so not
;		return immediately. Delete subroutines involved in invalid
;		command processing, in accordence with changes to the
;		IVCMD_* macros.
;
;	X-89K5	JSSTU001x	John S. Simakauskas	15-Feb-1991
;		Move COPY_UNIT_FLAGS out of TUDRIER into DUTUsubs.
;
;	X-89K4	WLG0AK4		William Goleman		28-Jan-1991
;		Initial port for EVMS.
;		Remove all references to Phase I Volume Shadowing. Remove
;		Host Initiated Revectoring code. Change calls to routines
;		that fork, passing a return address using a register instead
;		of the stack. Replace DO_ACTION macro calls with DISPATCH,
;		and remove the subroutines that were used with the DO_ACTION
;		and ACTION_ENTRY macros. Comment out other ALPHA changes
;		added to allow continued testing under VMS.
;
;--



	.SBTTL	Symbol Offset Definitions
;++
;
; Included Symbol Definitions:
;
;--
	$BTDDEF				; Define boot device types
	$CDTDEF				; Define CDT offsets
	$CPUDEF				; Define per-CPU data area
	$CRBDEF				; Define CRB offsets
	$DCDEF				; Define device classes & types
	$DDBDEF				; Define Driver Data Block offsets
	$DDTDEF				; Define Driver Dispatch Table offsets
	$DEVDEF				; Define device characteristics bits
	$DPTDEF				; Devine Driver Prologue Table offsets
	$DSRVDEF			; Define MSCP server control structure
	$DYNDEF				; Define DYN symbols
	$EMBLTDEF			; Define EMB Log Message Types
	$IODEF				; Define I/O function codes
	$IPLDEF				; Define IPL levels
	$LDBDEF				; Load Driver DataBase structure
	$MSCPDEF			; Mass Storage Control Protocol offsets
	$MSGDEF				; Define system message types
	$MT2DEF				; Define Magtape Extended Characteristics
	$MTXDEF				; Define MUTEX offsets
	$PBDEF				; Define Path Block offsets
	$PCBDEF				; Define Process Control Block offsets
	$PDTDEF				; Define Port Descriptor Table offsets
	$PRDEF				; Define Processor Register numbers
	$SBDEF				; Define System Block offsets
	$SHADDEF			; SHADowing control structure offsets
	$SPLCODDEF			; Define SPINLOCK indices
	$SSDEF				; Define System Status values
	$UCBDEF				; Define Unit Control Block offsets
	$UQBDEF				; Define Unit Queue Block offsets
	$VCBDEF				; Define VCB offsets
	$VECDEF				; Interrupt Dispatch Vector offsets
	$$IO_ROUTINES_DATADEF		; Define I/O post processing offsets
	$DUTUDEF			; Define common class driver CDDB
					; extensions and other common symbols
.IF DF IRP$Q_QIO_P1                     ; If backporting prior to V7.0
        $IOCNTDEF                       ; Define I/O counters definitions
.ENDC

.IIF NDF UCB$L_DUTUFKBLINK, UCB$L_DUTUFKBLINK = <UCB$L_SHAD+4>
.IIF NDF UCB$M_MVFKBBSY, UCB$M_MVFKBBSY = <^X40>
.IIF NDF UCB$V_MVFKBBSY, UCB$V_MVFKBBSY = 6
.IIF NDF UCB$M_GTUNMBSY, UCB$M_GTUNMBSY = <^X80>
.IIF NDF UCB$V_GTUNMBSY, UCB$V_GTUNMBSY = 7

UCB$W_TU_FORMENU=UCB$L_DEVDEPND2	; For tape devices, the supported formats
					;  overlap the DEVDEPND2 field so that they
					;  can be returned by GETDVI.  This
					;  declaration also appears in TUDRIVER.
	.IF NDF UCB$V_MSCP_MVRESTART
		UCB$V_MSCP_MVRESTART=^xf
		UCB$M_MSCP_MVRESTART=^x8000
	.ENDC


;++
;
;  LOCAL DEFINITIONS
;
;--

;
; Define work area, CDRP$T_LBUFHNDL, offsets
;
ASSUME CDRP$S_LBUFHNDL EQ 12
CDRP$L_WALK_CDDB  = CDRP$T_LBUFHNDL
CDRP$L_WALK_ALCLS = CDRP$T_LBUFHNDL + 4
CDRP$L_WALK_SVPC  = CDRP$T_LBUFHNDL + 8
CDRP$L_LB_CDDB	  = CDRP$L_KEYDESC

DAP_LIMIT=3
	.if	df,doswstuff
	.EXTERNAL IOC_STD$SETUP_SWITCH
	.endc

	.SBTTL	PSECT Definitions
;++
;
; PSECT to locate the media id to device type conversion table
;
;--

	DRIVER_DATA	$$$220_DEVTYPE_TABLE_00

DUTU$DEVTYPE_TABLE::			; Locate base of conversion table
;
; Add some patch space to the end of the conversion table
;
	DRIVER_DATA	$$$220_DEVTYPE_TABLE_02

	.REPEAT 5
	.LONG	0
	.LONG	0
	.ENDR
;
; PSECT definition to locate the data region
;
	DRIVER_DATA	$$$220_DUTU_DATA_00

DUTU$DATA::				; Locate base of data region
;
; PSECT definition for own storage
;
	DRIVER_DATA

DUTU$OWN_STORAGE::			; Locate base of own storage

	.LONG	0			; Longword for compatibility with VAX.
	.WORD	OWN_STORAGE_LEN		; Size.
	.WORD	DYN$C_CLASSDRV		; Type / sub-type.
	.WORD	0			; Status.
	.WORD	0			; Reserved.
	.BLKL	4			; Buffer pointers.
	.BLKW	4			; Include/exclude ranges.

OWN_STORAGE_LEN = .-DUTU$OWN_STORAGE
;
; Main PSECT for module code
;
	.IF	DEFINED $$$V54
		.PSECT	$$$115_DRIVER RD,WRT,EXE,LONG
	.IFF
		DRIVER_CODE
	.ENDC



	.SBTTL	IRP - CDRP Consistency Check
;++
;
; 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
	.IF	DEFINED,CDRP$L_IOSB
		ASSUME	CDRP$L_IOSB-CDRP$L_IOQFL	EQ	IRP$L_IOSB
	.ENDC
	.IF	DEFINED,CDRP$PQ_IOSB
		ASSUME	CDRP$PQ_IOSB-CDRP$L_IOQFL	EQ	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_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_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



	.SBTTL	DUTU$DISPLAY
;++
;
;	DUTU$DISPLAY - DISPLAY TEXT STRING
;
; FUNCTIONAL DESCRIPTION:
;
; This routine provides information for use by device displays.
; A device display provides a means of displaying text and other
; information associated with a particular device or unit. Device
; displays are typically used to convey information to system
; operators, such as volume labels for operator mount requests.
;
; The user shall supply at a minimum, parameters P3, item code,
; and P4, mode. The user shall optionally supply parameters P1,
; which is the address of an string list. P2 is unused. The item
; list is made up of:
;	a longword of the number of following arguments in longwords
;	address of first ASCID
;	address of second ASCID (optional)
;
; Item and mode field values are defined below.
;
; ITEM = 0 MODE = any:	Item 0 shall never be implemented by any display
;			device. Hosts may use this value to determine if
;			DISPLAY command is implemented without altering
;			the display.
;
; ITEM = 1 MODE = 0:	Clears the volume display. Expects no text string.
;
; ITEM = 1 MODE = 1:	Displays the volume label currently associated with
;			the unit or the volume mounted on the unit. Expects
;			one text string, described by one ASCID buffer.
;
; ITEM = 1 MODE = 2:	Displays the volume label that an operator should
;			mount on the unit. Expects two text strings.
;			The first text string is the volume label. The
;			second text string is optional.	 If the second string
;			is non-null, then it is the command "MOUNT" in the
;			local language.
;
;
; Special use is made of the stack for local storage because all the user
; storage areas need to be checked for read acess. As the areas are verified,
; they are pushed onto the stack so the user can't change them between checking
; and using the areas. Three user areas are probed and two are kept onto the
; stack:
;
; The three user areas to be probed are:
;
;	P1 --->	 ---------------
;		|      n	|
;		 ---------------	 ----------------
;		|strng desc addr| --->	|	| # bytes|
;		 ---------------	 ----------------
;		|strng desc addr|	| string addr.	 | ---> / VMSRL5 /
;		 ---------------	 ----------------
;		|      etc	|
;
;
; What is pushed onto the stack is, in order of high to low addresses, the
; address of the string descriptors, and then the descriptors themselves.
;
;
; INPUTS:
;
;	R3 = I/O PACKET ADDRESS
;	R4 = CURRENT PCB
;	R5 = UCB ADDRESS
;	R6 = ASSIGNED CCB ADDRESS
;	IRP$L_QIO_P1(R3) = ADDRESS OF THE ITEM LIST
;	IRP$L_QIO_P2(R3) = ITEM CODE
;	IRP$L_QIO_P3(R3) = MODE
;
; OUTPUTS:
;
;	R0 = STATUS OF THE OPERATION
;
; COMPLETION CODES:
;
;	SS$_NORMAL - SUCCESSFUL
;	SS$_ACCVIO - BUFFER ACCESS VIOLATION
;
;--

DUTU$DISPLAY::

	$DRIVER_FDT_ENTRY FETCH=YES

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

	$OFFSET 0, NEGATIVE,<-
			<REG8>,-
			<REG7>,-
			<REG6>,-
			<UCB>,-
			<PCB>,-
			<IRP>,-
			<NSTRINGS>,-
			<BYTCNT>,-
			<DESCADDR>>

	MOVL	SP,R11			; Save original kernel stack pointer
	PUSHR	#^M<R3,R4,R5,R6,R7,R8>	; Save all registers we use.
	SUBL	#<IRP-DESCADDR>,SP	; Move SP to top of stack.
	MOVL	IRP$L_QIO_P1(R3),R2	; Get string list
	BNEQ	5$			; If no params, finish up FDT routine.
	BRW	60$			; BRW can't reach 60$.

5$:	IFNORD	#4,(R2),20$		; Do we have access to argument counter?
	MOVL	(R2)+,NSTRINGS(R11)	; Save number of strings.
	MOVL	NSTRINGS(R11),-		; BYTCNT(R11) is counter of how many
		BYTCNT(R11)		;  bytes to allocate for system space.
					;  Here we add the bytes of byte count.
	ASSUME MSCP$W_DMODE EQ 14
	DISPLAY_SIZE = MSCP$K_MXCMDLEN-MSCP$W_DMODE-2 ;EEE constant definition
	ASSUME DISPLAY_SIZE LE 64
	CMPL	NSTRINGS(R11),-		; More arguments than max allowed by
		#DISPLAY_SIZE		;  size of MSCP packet?
	BLEQ	10$			; Bad parameters.
	MOVL	#SS$_BADPARAM,R0	; Return bad parameter function code.
	BRW	99$

10$:	ASHL	#2,NSTRINGS(R11),R10	; Determine bytes needed for storage.
	SUBL	R10,SP			; Move SP to where the top of the stack
					;  will be after pushing string addrs.
	MOVL	SP,DESCADDR(R11)	; Initialize address pointer.
	IFNORD	R10,(R2),20$		;  so that we can check read access.
	BRW	30$			; BRW can't reach 85$.

20$:	BRW	85$

30$:	MOVC3	R10,(R2),(SP)		; Save string addresses passes by user.
	ASHL	#3,NSTRINGS(R11),R10	; Descriptors will take up two longwords
					;  each on the stack.
	SUBL	R10,SP			; Setup stack storage space for ASCID
					;  addresses.
	MOVL	DESCADDR(R11),R2	; Get address of first string.
	MOVL	SP,R8			; R8 points to where the first ASCID goes.
	MOVL	NSTRINGS(R11),R9	; Use number of strings as counter.
	BEQL	46$			; If EQL, no strings to process.

40$:	MOVL	(R2)+,R0		; Get first ASCID descriptor.
	BEQL	45$			; Let user not supply ASCID address.
	IFNORD	#8,(R0),20$		; If no read on ASCID, accvio.
	MOVQ	(R0),R0			; Move descriptor to R0, R1.
	MOVZBL	R0,R0			; Only low word valid for byte count.
	BEQL	45$			; Let user supply 0 for length.
	MOVQ	R0,(R8)+		; Move descriptor to stack storage.
	ADDL2	R0,BYTCNT(R11)		; Add to system buffer byte count.
	IFNORD	R0,(R1),20$		; Finally check string itself.

44$:	SOBGTR	R9,40$			; Any more strings passed?
	BRW	46$			; Valid arguments were passed.

45$:	CLRQ	(R8)+			; Push two longwords of 0's.
	BRW	44$			; And we're done.
;
; We have all the information we need on the stack. Allocate system
; space to accomodate exactly DISPLAY_SIZE bytes (plus buffer overhead).
; Also make sure that the user hasn't asked for more space, which would
; imply bad parameters.
;
46$:	CMPL	#DISPLAY_SIZE,BYTCNT(R11);Has the user specified too much data?
	BGEQ	48$			; LSS implies too much data.
	MOVL	#SS$_BADPARAM,R0	; Return bad parameter function code.
	BRW	99$

48$:	MOVL	#DISPLAY_SIZE+12,R1	; Ask for DISPLAY_SIZE plus 12 bytes of
					;  system buffer overhead.
	MOVL	PCB(R11),R4		; Need PCB.
					; Allocate system space buffer
	JSB	G^EXE$DEBIT_BYTCNT_ALO	; and debit byte count.
	BLBC	R0,99$			; Branch on quota exceeded.

	MOVL	IRP(R11),R3		; Restore IRP.
	MOVL	R2,IRP$L_SVAPTE(R3)	; Store address of system buffer.
	MOVL	R1,IRP$L_BOFF(R3)	; Number of bytes deducted.
	ADDL3	#12,R2,(R2)		; Store starting address of system
					;  buffer data area in buffer header.
	CLRL	4(R2)			; No user buffer.
	MOVW	R1,IRP$W_SIZE(R2)	;
;
; Initialize size field of block. Note use of IRP$W_SIZE offset with R2.  It is
; perfectly OK. Here we pre-initialize the data portion of the system buffer to zeros.
;
	PUSHR	#^M<R1,R2,R3,R4,R5>			; Save registers.
	MOVC5	#0, (SP), #0, #DISPLAY_SIZE, 12(R2)	; Zero data area.
	POPR	#^M<R1,R2,R3,R4,R5>			; Restore saved registers.
;
; Move data into system buffer.
;
	MOVL	SP,R8			; Start at top of stack.
	MOVAL	12(R2),R3		; Initialize buffer address.
	MOVL	NSTRINGS(R11),R9	; Get number of args again for counter.
	BEQL	60$			; If no strings, branch around.

50$:	MOVL	(R8)+,R10		; R10 gets ASCID's byte count.
	MOVB	R10,(R3)+		; Move byte count into system buffer.
	MOVC3	R10,@(R8)+,(R3)		; Move string into buffer operation.

55$:	SOBGTR	R9,50$			; Process next string.

60$:	MOVL	IRP(R11),R3		; R3 was destroyed by MOVC.
	MOVW	IRP$L_QIO_P4(R3), -	; Save mode in upper word of
		IRP$L_MEDIA+2(R3)	;  IRP$L_MEDIA.
	MOVW	IRP$L_QIO_P3(R3), -	; Save item codein lower word of
		IRP$L_MEDIA(R3)		;  IRP$L_MEDIA.

	MOVAB	IRP(R11),SP
	POPR	#^M<R3,R4,R5,R6,R7,R8>	; Restore registers

	CALL_QIODRVPKT		; Queue the packet.

85$:	MOVL	#SS$_ACCVIO,R0		; Return access violation.

99$:	MOVAB	IRP(R11),SP
	POPR	#^M<R3,R4,R5,R6,R7,R8>	; Restore registers

	CALL_ABORTIO		; Abort I/O request.



	.SBTTL	Start a DISPLAY function.
;++
;
; START_DISPLAY
;     Create and pass a formatted MSCP packet to the server to issue
;     a DSPLY operation.
;     The packet contains a buffer of ASCII characters to be
;     interpreted by the server.
;     The ASCII characters can be used to visually display information
;     on the device 'display', or can be used with media loaders to
;     select a specified piece of media.
;
;     Not all devices implement the DISPLAY function.
;
;
; Inputs:
;
;	R1	I/O function code & modifiers
;	R2	MSCP buffer address
;	R3	UCB address
;	R4	PDT address
;	R5	CDRP address
;	CDRP$L_MEDIA(R5)	; The high order word contains the mode
;				; and the low order word the item code.
;
;--

DUTU$START_DISPLAY::

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

	MOVB	#MSCP$K_OP_DSPLY,-		; Display opcode to packet
		MSCP$B_OPCODE(R2)		;
	CLRW	MSCP$W_MODIFIER(R2)		; VMS uses no modifiers.
	ASSUME MSCP$W_DITEM EQ 12
	ASSUME MSCP$W_DMODE EQ MSCP$W_DITEM+2

10$:	MOVL	CDRP$L_MEDIA(R5),-		; Move mode and item code.
		MSCP$W_DITEM(R2)
	MOVL	CDRP$L_SVAPTE(R5),R1		; Get address of system buffer
						; data area
	BEQL	20$				; Finished setup.
	MOVL	(R1),R1				; R1 gets address of system
						;  buffer data area.
	PUSHR	#^M<R2,R3,R4,R5>		; R0-R5 destroyed by MOVC.
	ASSUME	MSCP$T_DTEXT EQ 16
	MOVC3	#DISPLAY_SIZE,(R1),-		; Move display text into
		MSCP$T_DTEXT(R2)		;  MSCP buffer area.
	POPR	#^M<R2,R3,R4,R5>		; R0-R5 destroyed by MOVC.

20$:	MOVL	UCB$L_CDDB(R3), R1		; Get CDDB of controller.

	SEND_MSCP_MSG				; Send off the display 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>,-
		   OUTPUT=  <>,-
		   SCRATCH=<>,-
		   PRESERVE=<>

	CMPW	#SS$_ILLCDTST, R0		; Check for CDT error
	BEQL	DISPLAY_DEVOFFLINE		; branch on error
	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,	DISPLAY_SUCC>, -
		<ICMD,	DISPLAY_IVCMD>, -
		<OFFLN, DISPLAY_OFFLINE> -
		<AVLBL, DISPLAY_OFFLINE>, -
		<CNTLR, DISPLAY_CTRLERR>, -
		<FMTER, DISPLAY_CTRLERR>, -
		<DRIVE, DISPLAY_DRVERR>, -
		<LOADR, DISPLAY_LOADR>, -
		>
	BRW	DISPLAY_CTRLERR			; Return controller error
						; for anything else
DISPLAY_DEVOFFLINE:
	MOVL	#SS$_DEVOFFLINE,R0		; Return device offline
	BRW	DISPLAY_EXIT			;  and branch out.

DISPLAY_LOADR:
DISPLAY_SUCC:
	MOVL	#SS$_NORMAL, R0			; Set status
	BRW	DISPLAY_EXIT			;  and branch out.

DISPLAY_IVCMD:
	MOVL	#SS$_ILLIOFUNC, R0		; Set status
	BRW	DISPLAY_EXIT			;  and branch out.

DISPLAY_OFFLINE:
	MOVL	#SS$_MEDOFL, R0			; Set status
	BRW	DISPLAY_EXIT			;  and branch out.

DISPLAY_CTRLERR:
	MOVL	#SS$_CTRLERR, R0		; Set status
	BRW	DISPLAY_EXIT			;  and branch out.

DISPLAY_DRVERR:
	MOVL	#SS$_DRVERR, R0			; Set status
;---	BRW	DISPLAY_EXIT			;  and branch out.

DISPLAY_EXIT:
	CLRL	R1				; Clear for I/O status block.

	BSBW	FUNCTION_EXIT

	RSB



	.SBTTL	----- INITIALIZATION/REINITIALIZATION ROUTINES -----

	.SBTTL	DUTU$NEW_SERVER - New Server Has Been Sighted In The Cluster
;++
;
;	DUTU$NEW_SERVER - A New Server Has Been Sighted
;
; Functional Description:
;
;	This routine is called when the SCA Process Poller detects a possible
;	new server in the cluster or directly from DU controller init for remote
;	system disk processing. The following functions are performed:
;
;	A subroutine is callled to:
;		1. make the connection to the server just discovered.
;		2. Send out the Set Controller Charactistics command.
;		3. Allocate a command buffer and RSPID for future connection
;		   management use.
;
;	The timeout mechanism for this connection is put into place.
;
;	The poll for units processing is begun to find out what units are
;	available on this new controller.
;
;
; Inputs:
;
;	R2	address of system ID
;	IPL = IPL$_SCS, with fork spinlock held.
;
; Outputs:
;
;	R0	Status (used by SCS Process Poller only)
;			- LBS Disable polling on the system
;			- LBC Continue polling the system
;
;--

DUTU$NEW_SERVER::
	.enabl lsb
	.JSB_ENTRY INPUT=   <	   R2	      >,-
		   OUTPUT=  <R0		      >,-
		   SCRATCH= <	              >,-
		   PRESERVE=<                 >
;
; First check to see if this server has already been found. Since this routine
; forks to gain access to the I/O database, status cannot be returned to stop
; polling on the first pass through this routine. If this is a subsequent pass
; through for the same system, return a status with LBS to discontinue polling
; on this system.
;
	SUBL3	#CDDB$L_CDDBLINK, -		; Initialize previous CDDB addr
		<DUTU$DATA+DUTU$L_CDDB_LISTHEAD>,R0;

10$:	MOVL	CDDB$L_CDDBLINK(R0), R0		; Get the next CDDB in the list
	BEQL	20$				; End of the list
	CMPL	CDDB$B_SYSTEMID(R0), (R2)	; Compare first part of ID
	BNEQ	10$				; Get the next one if different
	CMPW	CDDB$B_SYSTEMID+4(R0), 4(R2)	; Compare the second part too
	BNEQ	10$				; Move to next LDB if different
	BRW	70$				; Exit and stop polling this system.
;
; If there is contention for the I/O database lock, this routine could be called
; multiple times for the same controller. To make sure a new LDB is not created
; for each one of those calls, a check is made of the LDBs in the list. If one
; is found for this system, execute an RSB with LBC so that polling for this
; system continues. This is done in case something fails later on.
;
20$:	MOVAB	<DUTU$DATA+DUTU$L_LDB_FLINK>,-	; Get addr of LDB list head
		R1				;  for comparison later
	SUBL3	#LDB$PS_FLINK,R1,R0		; Get addr of the first LDB

30$:	MOVL	LDB$PS_FLINK(R0), R0		; Get the next LDB in the list
	CMPL	R0, R1				;  and check against listhead
	BEQL	40$				; End of the list
	CMPL	LDB$Q_SYSID(R0), (R2)		; Compare first part of ID
	BNEQ	30$				; Get the next one if different
	CMPW	LDB$Q_SYSID+4(R0), 4(R2)	; Compare the second part too
	BNEQ	30$				; Move to next LDB if different
	BRW	90$				; Keep polling, but exit for now
;
; The system just found with the desired server application has not been
; processed before (or it did not complete successfully in the past). Now
; check to make sure the local structures do not already exist for this device
; type before continuing.
;
;
; Note there is a dichotomy with regard to DSA devices residing on the local
; node, in that at times we want to treat local devices as residing on a
; particular controller and at other times as devices merely addressable
; via the local node (without regard to local controller). Such devices always
; have a local system and path block created (with a system ID that has bit 47
; set and no SCS System Name). For controller functions (like controller init
; or controller failure/reset) we want to treat this controller as a distinct
; entity with a unique system ID. However for device naming, the convention is
; to merely refer to the local device as DUA0 or <local_node>$DUA0 without
; regard to the local controller the device actually resides on.
;
;  The code below adapts to this dichotomy by saving the system ID found by the
; process poller on the stack (which has bit 47 set indicating a local
; controller). If a local controller is found by CONFIG_SYS, the local node
; system block SCS$AR_LOCALSB is used instead of the local controller system
; block in the construction of the VMS device naming database (DDBs). IOGEN has
; already created data structures under the local node SB which we will use
; instead of creating a DDB,CRB,IDB off the local controller system block.
; However the system ID passed in by the SCS process poller is the one actually
; saved in the CDDB SYSTEMID field. By this means, the CDDB SYSTEMID identifies
; the units on this controller as a separate entity for controller functions
; but in terms of device naming, the device is located via the local node
; system block DDBs and thereby can be referred to as simply DUA0.
;
40$:	MOVQ	(R2),-(SP)			; Save SCS System ID on stack
	MOVL	R2, R1				; Get system ID address
	CLRL	R2				; No buffer being used

	JSB	G^SCS$CONFIG_SYS		; Return system block in R1
	BLBC	R0, 100$			; If none found, IODB is rotten

	BBC	#SB$V_LOCAL,SB$L_STATUS(R1),45$ ; Br if not local controller
	MOVL	SCS$AR_LOCALSB,R1		; Use local system block

45$:	MOVAB	SB$L_DDB(R1), R2		; Get the DDB listhead address
	MOVAB	DRIVER$DPT, R4			; Get DPT address
	ASSUME	DDB$L_LINK EQ 0

50$:	MOVL	DDB$L_LINK(R2), R0		; Get DDB to check.
	BEQL	60$				; If there are no more DDBs
;
; Check for a device DDB that uses this driver. All the other types of devices
; that use the class drivers have DDBs that are mutants of this first DDB.
;
	CMPL	DDB$PS_DPT(R0), R4		; Is this DDB for this driver?
	BEQL	60$				; If so, go on down
	MOVL	R0, R2				; Move new DDB addr to old addr
	BRW	50$				; If not, keep looking
;
; Build a load driver data structure containing all the information necessary
; to call IOC$CONFIGURE which will create all the necessary I/O data structures
; (DDB, IDB, CRB,...)
;
60$:	PUSHR	#^M<R0,R1,R2,R3>		; Save registers
	MOVL	#LDB$K_LENGTH+FKB2P$K_LENGTH,R1 ; Allocate memory for LDB and a
						;  class driver style fork block
	ALLOCATE_POOL  BUGCHECK

	MOVL	R2, R4				; Move base address to new reg
	MOVW	R1, LDB$W_SIZE(R4)		; Set up the size field,
	POPR	#^M<R0,R1,R2,R3>		; Restore registers
	INSQUE	(R4), -				; Link new LDB
		@<DUTU$DATA+DUTU$L_LDB_BLINK>	;  onto the end of the list
	BISL	#<LDB$M_NOADAP -		; NOADAPTER for
		 !LDB$M_REMOTE -		;  REMOTE DU devices.
		 !LDB$M_SCS>, -			;  remote means SCS is required.
		 LDB$L_FLAGS(R4)		;
	MOVQ	(SP)+, -			; Copy system ID found by
		LDB$Q_SYSID(R4)			; process poller to LDB.
	CLRW	LDB$Q_SYSID+6(R4)		; Clear out unused upper bytes.
	MOVL	#1, LDB$L_NUMVEC(R4)		; Only one interrupt vector
;
; If the driver was loaded using IOGEN, the data structures may already exist.
; In this case, initializing the UCB address in the LDB will cause IOC$CONFIGURE
; to return a status of ss$_devexists, and not create new structures.
;
; Since local controllers share the same local SB, a second local controller
; could already find a valid DDB and UCB or if the first controller had no
; units then only a DDB. Note only the DDB is shared among the local
; controllers. For the case of a DDB with a UCB, we must be able to distinguish
; between a proto-type UCB created by CONFIGURE and an already valid UCB for
; an existing unit on a different local controller created by DUTU$NEW_UNIT.
; This is done by using the MEDIA_ID field which is set non-zero in the
; DUTU$NEW_UNIT routine. We assume the CONFIGURE UCB MEDIA ID created by
; LOAD_DRIVER will be zero and that if the UCB is prsent, will always be the
; first entry in the DDB list of UCBs.
;
; The table below shows the 4 possible states of I/O structures depending on
; whether a local or remote controller is involved, along with the allocation
; needed:
;
; 1) No structures exists - (Remote) - Allocate DDB, CRB, IDB. No need to
;    allocate a UCB thats is deallocates shortly so set LDB UCB to -1.
; 2) DDB exists but no UCB exists - (Local) Need not allocate the UCB as we
;    are about to deallocate it in a moment. Set LDB UCB to -1 to flag this
;    case of no UCB to deallocate. Note however CRB and IDB are allocated.
; 3) DDB, and UCB exists and UCB was created by DUTU$NEW_UNIT -
;    (Local) - Handle identically to State 2.
; 4) DDB, and UCB exists and UCB was created by CONFIGURE - (Remote,Local)
;    Allocate nothing as DDB, UCB, CRB and IDB already allocated for us.
;    Filling in all the LDB fields results in no allocation.
;
	TSTL	R0				; Check for the existence of DDB
	BEQL	62$				; If there is none (State 1),
						;  skip around other checks
	MOVL	R0, LDB$PS_DDB(R4)		; Save DDB in LDB
	MOVL	DDB$L_UCB(R0), R0		; Move UCB address to reg
	BEQL	62$				; If no UCB present (State 2),
						;  then don't alloc one
	TSTL	UCB$L_MEDIA_ID(R0)		; Media ID exist (created by
						;  NEW_UNIT)?
	BNEQ	62$				; Yes, so State 3, don't allocate
	MOVL	R0, LDB$PS_UCB(R4)		; State 4, Move the UCB created
						;  by CONFIGURE to the LDB
	MOVL	UCB$L_CRB(R0), R0		; Assume a CRB exists and get it
	MOVL	R0, LDB$PS_CRB(R4)		; Move the CRB to the LDB
	MOVL	<CRB$L_INTD+VEC$L_IDB>(R0), -	; Move IDB to the LDB
		LDB$PS_IDB(R4)			;
	BRW	65$				;  and continue
;
; Make sure UCB is not allocated by setting LDB UCB to -1. Leave the CRB and
; IDB fields zero so that they are allocated.
;
62$:	MOVL	#-1, LDB$PS_UCB(R4)		; Set LDB UCB to -1 to flag

65$:	MOVAB	DPT, R0				; Get the DPT address
	MOVZWL	DPT$W_MAXUNITS(R0), -		; Create the number of UCBs
		LDB$L_MAXUNITS(R4)		;  specified in the DPT
	MOVL	R0, LDB$PS_DPT(R4)		; Save the address of the DPT
	MOVL	R1, LDB$PS_SB(R4)		; Save the system block address
	MOVL	R2, LDB$PS_LASTDDB(R4)		; Backpointer for new DDB
	MOVL	#3, LDB$IL_DDB_NAMELEN(R4)	; Length of character string
	MOVL	DEVICE_NAME, LDB$T_DEVNAM(R4)	;  and the string itself
	MOVAB	LDB$T_DEVNAM(R4), -		; Move the address of the char
		LDB$PS_DDB_NAME(R4)		;  string into the pointer
	MOVAB	LDB$K_LENGTH(R4), R5		; Get the fork block address
	MOVL	R5, LDB$PS_FORK(R4)		;  and save it in the LDB.
	MOVW	#FKB2P$K_LENGTH, FKB$W_SIZE(R5) ;  initialize it,
	ASSUME	FKB$B_FLCK EQ FKB$B_TYPE+1	;  set up the type and size,
	MOVW	#<<SPL$C_SCS@8>!DYN$C_FRK>,-	;  and make it fork at IPL SCS
		FKB$B_TYPE(R5)			;
	MOVL	R4, FKB2P$L_SAVD_UCB(R5)	; Save LDB addr (since no UCB)
	MOVAB	DUTU$INIT_LOCAL_STRUCTURES, R4	; Set up continuation address

	BSBW	DUTU$LOCK_IODB			; When write access is granted,

	CLRL	R0				;  invoke continuation routine
	RSB

70$:	MOVL	#SS$_NORMAL, R0			; Return success,  stop polling
	RSB

80$:	ADDL	#8,SP				; Clear temp data off stk
	POPR	#^M<R0,R1,R2,R3>		; Restore registers

90$:	CLRL	R0				; Leave the polling alone
	RSB
;
; Execution of this bugcheck means that the local data structures are inconsistent
; with the cluster members as seen by the process poller.
;
100$:	BUG_CHECK DISKCLASS, FATAL

	.dsabl lsb


	.SBTTL	 DUTU$INIT_LOCAL_STRUCTURES - Create and Init Local I/O Structures
;++
;
;	DUTU$INIT_LOCAL_STRUCTURES - Create and Init Local I/O Structures
;
; Functional Description:
;
;	This routine is a callback routine from DUTU$LOCK_IODB initiated in
;	DUTU$NEW_SERVER above. When the I/O database has been locked, this
;	routine is called to allocate the local I/O database structures and
;	initialize them.
;
; Inputs:
;
;	R5	Fork block address
;	IPL = IPL$_SCS, with the I/O database mutex held for write access.
;
; Implicit Input:
;
;	FKB2P$L_SAVD_UCB(R5)	LDB address
;
; Input to Called Routine (DU$MAKE_CONNECTION):
;
;	R5	Permanent CDRP
;
; Output from Called Routine:
;
;	None
;
; Outputs:
;
;	None	(return is to fork dispatcher)
;
;--

DUTU$INIT_LOCAL_STRUCTURES::

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

	MOVL	FKB2P$L_SAVD_UCB(R5), R4	; Restore LDB address
	PUSHL	R4				; Push argument on the stack

	CALLS	#1,G^IOC$CONFIGURE		; Create necessary structures
	BLBC	R0, 20$				; If failed, skip down.

	BSBW	DUTU$CREATE_CDDB		; Create the CDDB
	BLBC	R0, 30$				; Return failed status to caller

	UNLOCK_IODB				; Unlock access to the IODB

	BSBW	RE_CONNECT			; Call subroutine to make conn

	RSB					; This thread is history

;
; Failure at any point in IOC$CONFIGURE deallocates any structures successfully
; allocated. Only the LDB and associated fork block need to be deallocated at
; this point.
;
20$:	MOVL	#SS$_NORMAL, R0			; Setting a status of normal
						;  deallocates the LDB only
;
; Failure in DUTU$CREATE_CDDB leaves all the data structures created in
; IOC$CONFIGURE to be deallocated. Hold onto the IODB lock since it is needed
; in IOC$CLEANUP_DB. R0 still contains the error returned from DUTU$CREATE_CDDB.
; The low bit in R0 serves as a flag to signal that all structures need to be
; deallocated.
;
30$:	MOVAB	LDB$Q_SYSID(R4), R1		; Get address of system ID

	JSB	DUTU$NEW_SERVER_CLEANUP		; Return structures we already

	UNLOCK_IODB				;  have, Unlock the IODB,

	RSB					;  and return.



	.SBTTL	DUTU$CREATE_CDDB - Create and initialize a CDDB
;++
;
; DUTU$CREATE_CDDB - Create and initialize a CDDB
;
; Functional Description:
;
;	This routine allocates pool for a disk or tape class driver CDDB,
;	initializes that CDDB, and links it into the appropriate CDDB chain.
;	Some initialization of the CRB and DDB, previously created by SYSGEN
;	or one of its kin, is also performed.
;
; Inputs:
;
;	R4	LDB address
;
; Implicit Inputs:
;
;	LDB$PS_CRB(R4)		CRB address
;	LDB$PS_DDB(R4)		DDB address
;	LDB$Q_SYSID(R4)		system ID of remote system
;	LDB$PS_UCB(R4)		UCB  address, or -1 if no UCB
;				if UCB ^= BOOTUCB, this UCB does not
;				represent a real device and should be discarded
;				when it is no longer needed
; Outputs:
;
;	R0	Status
;		SS$_NORMAL CDDB was created and initialized
;		SS$ABORT   Duplicate system ID was found, no CDDB was created
;	R3	CDDB address, if a new CDDB was created
;	R4	LDB address
;	R1,R2,R5 - Destroyed
;
; Implicit Outputs:
;
;	The CDDB is completely initialized and ready for use by the class
;	driver. If this is the boot device, CDDB$L_ORIGUCB is set with the UCB
;	address and valid is set in the UCB. Otherwise, CDDB$L_ORIGUCB is zero
;	and the UCB is deallocated.  See the preamble for DUTU$POLL_FOR_UNITS
;	for a discussion of boot device processing and CDDB$L_ORIGUCB.
;
;	DDB initialization:
;	The DDB is initialized to have no UCBs chained to it, and it is not
;	on any CDDB chain.
;
;	CRB initialization:
;	The CRB is inserted onto the TIMELINK chain with an infinite timeout
;	interval.  CRB$L_SCS_STRUC is initialized to point to the CDDB.
;
;--

DUTU$CREATE_CDDB::

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

	MOVZWL	#CDDB$K_DUTULENGTH, R1		; Get size of a CDDB.
	.enabl lsb
	ALLOCATE_POOL  BUGCHECK
;	BLBC	R0, 100$			; Branch if allocation failed.

       	MOVL	R2,R3				; move new cddb to r3
;
; R1 - size of allocated block
; R3 - address of allocated block (CDDB)
; R4 - LDB address
;
	MOVW	R1, CDDB$W_SIZE(R3)		; Setup CDDB size.
	ASSUME	CDDB$B_SUBTYPE	EQ  CDDB$B_TYPE+1
	MOVW	#<DYN$C_CD_CDDB@8 -		; Set type and subtype fields
		 !DYN$C_CLASSDRV>, -		; with the CLASSDRV major type
		CDDB$B_TYPE(R3)			; and CDDB subtype.
	MOVQ	LDB$Q_SYSID(R4), -		; Record SYSTEM ID.
		CDDB$B_SYSTEMID(R3)
	MOVL	#<CDDB$M_INITING -		; Initialize CDDB status.
		 !CDDB$M_NOCONN>, -
		CDDB$L_STATUS(R3)
	SUBB3	#^a/A/-1,LDB$T_DEVNAM+2(R4),R0	; Get controller letter (ddCu)
	BEQL	BUGCHK				; Make sure there is one.
	CMPB	#26, R0				; Is controller letter > Z ?
	BLSSU	BUGCHK				; Better not be.
	ROTL	R0, #1, CDDB$L_CTRLTR_MASK(R3)	; Set initial controller letter.
;
; Set initial controller flags to be used on first Set Controller
; Characteristics command.  The flags set are:
;
	MOVW	#<MSCP$M_CF_ATTN -		; enable ATTENTION messages
		 !MSCP$M_CF_MISC -		; enable misc error log messages
		 !MSCP$M_CF_THIS>, -		; enable host error log messages
		CDDB$W_CNTRLFLGS(R3)
	CLRW	CDDB$W_LOAD_AVAIL(R3)		; Set load available to zero.
	MOVAB	CDDB$L_CDRPQFL(R3), -		; Initialize Queue listheads.
		CDDB$L_CDRPQFL(R3)
	MOVAB	CDDB$L_CDRPQFL(R3), -		;	"	"	"
		CDDB$L_CDRPQBL(R3)
	MOVAB	CDDB$L_RSTRTQFL(R3), -		;	"	"	"
		CDDB$L_RSTRTQFL(R3)
	MOVAB	CDDB$L_RSTRTQFL(R3), -		;	"	"	"
		CDDB$L_RSTRTQBL(R3)
;
; Note:
;   The canclqfl/bl fields correspond to the abcnt/obcnt fields in a real IRP.
;
	MOVAB	CDDB$L_CANCLQFL(R3), -		;	"	"	"
		CDDB$L_CANCLQFL(R3)
	MOVAB	CDDB$L_CANCLQFL(R3), -		;	"	"	"
		CDDB$L_CANCLQBL(R3)
        MOVL    R3, <FKB2P$L_SAVD_CDDB -        ; Save CDDB address in
                +CDDB$A_FKB2P>(R3)              ; FKB2P fork block.
        MOVW    #FKB2P$K_LENGTH, -              ; Init size of FKB2P
                <FKB$W_SIZE+CDDB$A_FKB2P>(R3)   ;  fork block.
        ASSUME  FKB$B_FLCK EQ <FKB$B_TYPE + 1>
        MOVW    #<<SPL$C_SCS@8> ! DYN$C_FRK>, - ; Init type and IPL
                <FKB$B_TYPE+CDDB$A_FKB2P>(R3)   ;  of FKB2P fork block.
	MOVAB	CDDB$A_PRMCDRP(R3), R2		; Locate permanent CDRP.

	BSBW	DUTU$CDDB_INIT_PRM_CDRP		; Initialize it.

	MOVAB	CDDB$A_DAPCDRP(R3), R2		; Locate DAP CDRP.

	BSBW	DUTU$CDDB_INIT_PRM_CDRP		; Initialize it.

	MOVL	R2, CDDB$L_DAPCDRP(R3)		; Save DAP CDRP address.
	MOVL	LDB$PS_CRB(R4), CDDB$L_CRB(R3)	; Save CRB address.
;
; Init permanent fork blocks
;
; INIHIRT
        MOVAB   CDDB$A_INIHIRT(R3), R2          ; Get pointer to fork block
        MOVW    #FKB$C_LENGTH, FKB$W_SIZE(R2)   ; Set up size, 
        MOVB    #DYN$C_FRK, FKB$B_TYPE(R2)      ;  type and 
        MOVB    #SPL$C_SCS, FKB$B_FLCK(R2)      ;  fork IPL
; STCNID
        MOVAB   CDDB$A_STCNID(R3), R2		; Get pointer to fork block
        MOVW    #FKB$C_LENGTH, FKB$W_SIZE(R2)   ; Set up size, 
        MOVB    #DYN$C_FRK, FKB$B_TYPE(R2)      ;  type and 
        MOVB    #SPL$C_SCS, FKB$B_FLCK(R2)      ;  fork IPL
;
; DDB initialization (and possibly UCB deallocation). The only UCB to
; deallocate results from connect done by CONFIGURE in which case we assume
; it's the only UCB on the DDB chain.
;
	MOVL	LDB$PS_DDB(R4), R0		; Get DDB address.
	MOVL	R0, CDDB$L_DDB(R3)		; Save it in CDDB.
;
; Move ACP name into DDB in case DDB is created by IOC$CONFIGURE
; which doesn't call IOC$INITDRV.
;
	MOVL	ACP_NAME,DDB$L_ACPD(R0)		;
	MOVL	LDB$PS_UCB(R4), R5		; Get the UCB address
	CMPL	#-1,R5				; Any UCB to deallocate?
	BEQL	50$				; No, so skip deallocate
	CMPL	G^SYS$AR_BOOTUCB, R5		; Is UCB for the boot device?
	BEQL	45$				;  Branch if yes
	CLRL	DDB$L_UCB(R0)			; If UCB is not for the boot

	JSB	G^IOC$FREE_UCB

	BRW	50$
;
; This is the boot UCB. Flag this fact by saving the UCB in ORIGUCB
; and setting the valid bit so a later packack will succeed.
;
45$:	MOVL	R5, CDDB$L_ORIGUCB(R3)		; Save boot device UCB addr.
	BISL	#UCB$M_VALID, UCB$L_STS(R5)	; Set software volume valid.
	MOVW	UCB$W_UNIT(R5), UCB$W_MSCPUNIT(R5) ; Init MSCPUNIT to unit #
	MOVL	#-1, UCB$L_AFFINITY(R5)		;
.IF DF IRP$Q_QIO_P1				; If backporting prior to V7.0
	BLBC	SGN$GL_SYSTEM_CHECK,50$		; Branch if monitoring not enabled
	MOVL	#IOCNT$C_LENGTH,R1		; Get Size of IOCNT structure

 	ALLOCATE_POOL  BUGCHECK			; Allocate an IOCNT structure

	MOVL	R2,UCB$PS_IO_COUNTERS(R5)	; Save pointer to IOCNT structure
.ENDC
;
; The new CDDB is linked at the end of a chain of CDDBs for this device type
; (disk or tape).  While searching for the end of that chain, the system-ID of
; the new CDDB is compared to the system-ID of each CDDB in the chain. The new
; system-ID should be unique. If a match is found, the new CDDB, DDB, CRB, and
; IDB just created are all unlinked (as appropriate) and deallocated.  All
; knowledge of the attempt to form two connections to the same server are
; vaporized.
;
50$:	SUBL3	#CDDB$L_CDDBLINK, -		; Initialize previous CDDB addr.
		<DUTU$DATA + DUTU$L_CDDB_LISTHEAD>,R0

60$:	MOVL	R0, R1				; Save previous CDDB address.
	MOVL	CDDB$L_CDDBLINK(R1), R0		; Link to next CDDB.
	BEQL	90$				; Branch if no more CDDBs.
	ASSUME	CDDB$S_SYSTEMID GE 6
	CMPL	CDDB$B_SYSTEMID(R0), -		; Is new system-id different
		CDDB$B_SYSTEMID(R3)		; from one's already in use?
	BNEQ	60$				; Branch if different.
	CMPW	CDDB$B_SYSTEMID+4(R0), -
		CDDB$B_SYSTEMID+4(R3)
	BNEQ	60$				; Branch if different.
;
; There already exists a CDDB with the same system ID as the one in the CDDB
; just created. Deallocate the CDDB just created and all the associated data
; structures (DDB, IDB, CRB).
;
	MOVL	CDDB$L_DDB(R3), R0		; Get DDB address.
	TSTL	DDB$L_UCB(R0)			; Check for UCBs.  None should
	BNEQ	BUGCHK				; be found because this cannot
	TSTL	DDB$L_2P_UCB(R0)		; be the system disk CDDB.
	BNEQ	BUGCHK				; If one if found, crash
	ADDL3	#<SB$L_DDB - DDB$L_LINK>, -	; Get starting DDB chain
		DDB$L_SB(R0), R1		; address.

70$:	MOVL	DDB$L_LINK(R1), R1		; Link to next DDB.
	BEQL	80$				; Branch if no more DDBs.
	CMPL	DDB$L_LINK(R1), R0		; Find DDB which points to
	BNEQ	70$				; about to vaporize DDB.
	MOVL	DDB$L_LINK(R0), DDB$L_LINK(R1)	; Unlink vaporizing DDB.

80$:	JSB	G^EXE$DEANONPAGED		; Deallocate DDB.

	MOVL	CDDB$L_CRB(R3), R4		; Get CRB address.
	MOVL	CRB$L_INTD+VEC$L_IDB(R4), R0	; Get IDB address.

	JSB	G^EXE$DEANONPAGED		; Deallocate IDB.

	MOVL	R4, R0				; Setup CRB.

	JSB	G^EXE$DEANONPAGED		; Deallocate CRB.

	MOVL	R3, R0				; Setup CDDB.

	JSB	G^EXE$DEANONPAGED		; Deallocate CDDB.

	MOVL	#SS$_ABORT, R0			; Return abort status.
	BRW	100$				; Branch to common return

90$:	MOVL	R3, CDDB$L_CDDBLINK(R1)		; Link new CDDB on end of list.
;
; CRB initialization
;
	MOVL	CDDB$L_CRB(R3), R1		; Get CRB address.
	MOVB	#SPL$C_SCS,CRB$B_FLCK(R1)	; Init fork spinlock index.
;
; Insure access violation  until a valid timeout routine is established.
;
	CLRL	CRB$L_TOUTROUT(R1)		;
	MNEGL	#1, CRB$L_DUETIME(R1)		; Set infinite due time.
	MOVL	R3, CRB$L_SCS_STRUC(R1)		; Now CDDB is aux structure.
	MOVL	R1, R3				; Move CRB to expected register

	CALL_THREADCRB				; Thread CRB on TIMELINK chain.

	MOVL	CRB$L_SCS_STRUC(R3), R3		; Restore CDDB address.
	MOVL	#SS$_NORMAL, R0			; Return status of success.

100$:	RSB

BUGCHK: BUG_CHECK MSCPCLASS, FATAL		; Bogus DDB controller letter OR
						; Duplicate system-id on CDDB
						; with units already associated.
	.dsabl lsb

	.SBTTL	DUTU$CDDB_INIT_PRM_CDRP - Initialize the permanent CDRP.
;++
;
; DUTU$CDDB_INIT_PRM_CDRP - Initialize a CDRP permanently attached to a CDDB
;
; Functional Description:
;
;	This routine initializes one of the CDRPs permanently attached to a
;	CDDB.  Because the CDRP is included in the CDDB allocation, it is
;	zeroed when the CDDB is zeroed.	 Therefore, only interesting, non-
;	zero fields are initialized here.
;
; Inputs:
;
;	R2 - CDRP address
;	R3 - CDDB address
;
; Outputs:
;
;	R0 & R1 destroyed.
;	All other registers are preserved.
;
;--

DUTU$CDDB_INIT_PRM_CDRP::

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

	MOVL	R3, CDRP$L_UBARSRCE(R2)		; Save CDDB addr in CDRP
	ASSUME	CDRP$B_FLCK EQ CDRP$B_CD_TYPE+1
	MOVW	#<DYN$C_CDRP + <SPL$C_SCS@8>>, -; Initialize type and fork
		CDRP$B_CD_TYPE(R2)		; IPL.
	CLRL	CDRP$L_RWCPTR(R2)		; Clear pointer to RWAITCNT.
	MOVL	#CDRP$M_PERM, -			; Flag this a perm. IRP/CDRP.
		CDRP$L_DUTUFLAGS(R2)

	SCS_INIT_CDRP CDRP=R2			; Perform SCS init of CDRP

	RSB



	.SBTTL	DUTU$POLL_FOR_UNITS - Poll a server for its available units
;++
;
;	DUTU$POLL_FOR_UNITS - Poll a server for its available units
;
; Functional Description:
;
;	This routine polls a MSCP server to determine what units it has
;	available.  This is done by sending GET UNIT STATUS commands with the
;	NEXT UNIT modifier until all known units have been reported.
;	Functional units which are not represented in the I/O data base have a
;	UCB added.
;
; Special Processing for the Boot Device:
;
;	When the class driver is controlling the boot device, DUTU$CREATE_CDDB
;	detects a UCB which is valid (UCB$V_VALID set in UCB$L_STS).  When
;	this is the case, the UCB address is copied to CDDB$L_ORIGUCB.	In all
;	other cases, CDDB$L_ORIGUCB is left zero and the UCB passed to the
;	driver's controller initialization routine is deallocated.
;
;	While polling for units, this routine should find the boot device.  It
;	will match the unit number to the unit number in the UCB pointed to by
;	CDDB$L_ORIGUCB and when a match occurs it will begin special boot
;	device processing.  After the special boot device processing is
;	initiated for the first time, CDDB$L_ORIGUCB will be cleared.  This
;	makes the special handling of the boot device a once per system boot
;	occurence.
;
;	This processing differs from other poll units processing in two ways.
;	The original UCB -- handed the class driver during controller
;	initialization -- is used, not a newly created UCB.  Also, a special,
;	limited form of mount verification (controlled completely by this
;	routine and its subroutines) is performed.  This limited mount
;	verification only performs a PACKACK function.	(As opposed to regular
;	mount verification which performs both a PACKACK and a volume
;	validation operation).	There is no memory stored volume information
;	against which to validate the boot volume.  Also, system requirements
;	at this stage of the booting process are limited to the boot device
;	being accessible to the boot procedures (i.e. ONLINE).
;
;	N.B. this method works equally well for disks or tapes.	 Although
;	booting from tapes is not supported, its not prohibited by this code.
;
; Inputs:
;
;	R2	Return address
;	R3	CDDB address
;	R4	PDT address
;	R5	address of the DAP CDRP
;
; Implicit Inputs:
;
;	CDDB$L_STATUS(R3)	CDDB$V_DAPBSY set
;	CDDB$L_ORIGUCB(R3)	boot device UCB address, if first init. or
;				boot device by class driver; otherwise zero
;	CDRP$L_RSPID(R5)	a valid RSPID
;	CDRP$L_MSG_BUF(R5)	address of a valid SCS message buffer
;	CDRP$W_SIZE(R5)		offset to the CDDB
;
;	The normal class driver MSCP operation timeout mechanism must be
;	enabled.
;
; Outputs:
;
;	R0	Status
;		SS$_NORMAL  -  Polling completed normally
;		SS$_ILLCDTST   -  Polling did not complete (connection broke)
;	R3	CDDB address (same as input)
;	R4	PDT address  (same as input)
;	R5	CDRP address (same as input)
;
;--

	.ENABLE LSB

DUTU$POLL_FOR_UNITS::

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


	MOVL	R2,CDDB$L_SAVED_PC(R3)	; Save caller's return in CDDB field.
	BISL	#CDDB$M_POLLING, -	; Set bit indicating polling in
		CDDB$L_STATUS(R3)	; progress.
;
; Clearing CDRP$L_MEDIA causes the search for units to begin with
; unit 1.  MSCP dictates that unit 0 is the last unit to be polled.
;
	CLRL	CDRP$L_MEDIA(R5)

	ALLOC_RSPID			; Allocate a response-id.

	ALLOC_MSG_BUF			; Allocate a message buffer.
	BLBC	R0, POLL_CONN_BROKE	; connection broken already.

POLL_LOOP:

	INIT_MSCP_MSG			; Initialize buffer for MSCP message.

	INCW	CDRP$L_MEDIA(R5)	; Proceed with next unit.
	MOVW	CDRP$L_MEDIA(R5), -	; Put next unit number in MSCP
		MSCP$W_UNIT(R2)		; command.
	MOVB	#MSCP$K_OP_GTUNT, -	; Command is GET UNIT STATUS.
		MSCP$B_OPCODE(R2)
	MOVW	#MSCP$M_MD_NXUNT, -	; Modifier is NEXT UNIT.
		MSCP$W_MODIFIER(R2)
	MOVL	CDRP$L_UBARSRCE(R5), R1 ; Get the CDDB address.

	SEND_MSCP_MSG			; Returns with END PACKET addr. in R2.
;
; Control is returned here after receipt of the END PACKET corresponding to
; the MSCP Get Unit Status command.  Control is regained via a callback from
; DU$IDR.
;
;
; 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>,-
		   OUTPUT=  <>,-
		   SCRATCH= <>,-
		   PRESERVE=<>

	CMPW	#SS$_ILLCDTST, R0	; Check for CDT error
	BEQL	POLL_CONN_BROKE		; branch on error
	MOVZWL	MSCP$W_UNIT(R2), -	; Save unit number located.
		CDRP$L_MEDIA(R5)
;
; Check for having located the original UCB, possibly the system
; device, presented to the driver's controller initialization routine.
; Of course, no end message for a device that is UNIT-OFFLINE need
; even be considered for the system disk. This can happen on the last
; polling end message when booting off unit 0 and Unit 0 can't be
; found promptly by slow controllers.
;
	BICW3	#^cMSCP$M_ST_MASK, -	; Extract major MSCP status.
		MSCP$W_STATUS(R2), R0
	CMPW	#MSCP$K_ST_OFFLN,R0	; Unit offline ?
	BEQL	30$			; Branch if so.
	MOVL	CDDB$L_ORIGUCB(R3), R0	; Get original UCB address.
	BEQL	30$			; Branch if no such UCB exists.
	BBC	#CDDB$V_DISABLED, -	; If the system device is on a disabled
		CDDB$L_STATUS(R3), 5$	;  controller during boot, give up.

	BUG_CHECK MSCPCLASS, FATAL	; System device on disabled controller.

5$:	CMPB	#MSCP$K_CM_EMULA, -	; Is the controller for the system
		CDDB$B_CNTRLMDL(R3)	;  device a VMS MSCPserver?
	BNEQ	10$			; Branch if not an emulator.
;
; System device is on VMS MSCPserver.
;
; Compare unit.
;
	CMPW	MSCP$W_SLUN_UNIT(R2), - ; Do the actual drive unit number and
		UCB$W_UNIT(R0)		;  VMS unit number match?
	BNEQ	30$			; Branch if wrong unit.
;
; Compare allocation class.
;
	MOVL	UCB$L_DDB(R0), R1	; Get DDB address.
	CMPL	MSCP$L_SLUN_ALLOCLS(R2),-  ; See if same alloclass, might
		DDB$L_ALLOCLS(R1)	   ;  be a port alloclass
	BNEQ	30$			   ; Search again if wrong A/C
;
; Compare controller letter.
;
	SUBB3	#^a/A/-1, -		; Get controller letter representation
		DDB$T_NAME+3(R1), R1	;  from DDB name (ddCu), range = 1-26.
	MOVZBL	R1, R1			; Clear out extraneous bits.
	CMPZV	#MSCP$V_SLUN_C, -	; Does the controller letter from the
		#MSCP$S_SLUN_C, -	;  SLUN field of the message match that
		MSCP$W_SLUN_DEVNAME(R2), R1  ;	of the DDB?
	BNEQ	30$			; Branch if wrong device name.
;
; Compare second letter of device type.
;
	MOVL	UCB$L_DDB(R0), R1	; Get DDB address.
	SUBB3	#^a/A/-1, -		; Get device type letter representation
		DDB$T_NAME+2(R1), R1	;  from DDB name (dDcu), range = 1-26.
	MOVZBL	R1, R1			; Clear out extraneous bits.
	CMPZV	#MSCP$V_MTYP_D1, -	; Does the device type letter from the
		#MSCP$S_MTYP_D1, -	;  media_id field of the message match
		MSCP$L_MEDIA_ID(R2), R1 ;  that of the DDB?
	BNEQ	30$			; Branch if wrong device name.
;
; Compare first letter of device type.
;
	MOVL	UCB$L_DDB(R0), R1	; Get DDB address.
	SUBB3	#^a/A/-1, -		; Get device type letter representation
		DDB$T_NAME+1(R1), R1	;  from DDB name (Ddcu), range = 1-26.
	MOVZBL	R1, R1			; Clear out extraneous bits.
	CMPZV	#MSCP$V_MTYP_D0, -	; Does the device type letter from the
		#MSCP$S_MTYP_D0, -	;  media_id field of the message match
		MSCP$L_MEDIA_ID(R2), R1 ;  that of the DDB?
	BNEQ	30$			; Branch if wrong device name.

	MOVW	MSCP$W_UNIT(R2), -	; Have correct device, update MSCPUNIT
		UCB$W_MSCPUNIT(R0)	;  in case it is necessary.
	BRW	DO_ORIG_UCB		; Go to special case code.
;
; System device on local (non-emulator) controller.
;
10$:	CMPW	MSCP$W_UNIT(R2), -	; Does UCB unit match end-message
		UCB$W_MSCPUNIT(R0)	; unit number?
	BEQL	DO_ORIG_UCB		; If match, go to special case code.
;
; Check end-message -- determine whether or not we want the unit it
; describes in our I/O data base.  We want the unit if the status is
; one of SUCC, AVLBL, DRIVE, or OFFLN (NOVOL).
;
30$:	EXTZV	#MSCP$V_MTYP_D0, -	; Does the device type letter from the
		#MSCP$S_MTYP_D0, -	;  media_id field of the message match
		MSCP$L_MEDIA_ID(R2), R1 ;  that of the DDB?
	CMPL	R1, #^a/S/-^a/@/	; Was the charater an S?
	BEQL	90$
	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,40$>, -
		<AVLBL,40$>, -
		<DRIVE,40$>, -
		<OFFLN,35$> -
		>
	BRW	90$			; If none of the above, ignore message.
;
; For offline devices, accept only those who have no volume mounted or
; are disabled via the RUN/STOP switch, subcode NOVOL, or are in
; exclusive use elsewhere
;
35$:	BITW	#<MSCP$M_SC_NOVOL!-	; Is the subcode NOVOL?
		  MSCP$M_SC_EXUSE>,-	;  or EXUSE?
		MSCP$W_STATUS(R2)	;
	BEQL	90$			; Branch if not.

40$:	BBC	#MSCP$V_CF_LOAD,-	; If this controller provides load
		CDDB$W_CNTRLFLGS(R3),-	;  information, copy it from the end
		45$			;  message to the CDDB rather than wait
	MOVW	MSCP$W_LOAD_AVAIL(R2),- ;  for the controller timeout to pick
		CDDB$W_LOAD_AVAIL(R3)	;  it up.
	CMPB	#PDT$C_PE,-		; If this port is NI, we are done.
		PDT$B_PDT_TYPE(R4)
	BEQL	45$
	MOVZWL	CDDB$W_LOAD_AVAIL(R3),R1; Otherwise, increase the load avail
	ASHL	#2,R1,R1		; rating to make this server more
	MOVW	R1,CDDB$W_LOAD_AVAIL(R3); attractive than an NI server.

45$:	MOVZWL	CDRP$W_ENDMSGSIZ(R5), R1; Restore MSCP message size.

	BSBW	DUTU$NEW_UNIT		; Make sure a UCB exists for this unit.
	BLBS	R0, 50$			; Branch on success.

	CMPW	#SS$_IVDEVNAM, R0	; Was unit number bad?	(Can ignore).
	BEQL	FINISH_ORIGUCB		; Branch if bad unit number - continue.
	BRW	RESTART_POLL		; Else, restart poll loop.

50$:	CMPB	#DC$_TAPE, -		; Is this a tape?
		UCB$B_DEVCLASS(R2)	;
	BNEQ	FINISH_ORIGUCB		; Branch if not a tape.
	MOVL	CDRP$L_MSG_BUF(R5), R0	; Get message buffer address.
	BICW3	#^cMSCP$M_ST_MASK, -	; Extract major MSCP status.
		MSCP$W_STATUS(R0), R1	;
	CMPW	#MSCP$K_ST_DRIVE,R1	; Drive error?
	BEQL	FINISH_ORIGUCB		; Branch if so.
	BICW3	#^c<MSCP$M_UF_CACH!-	; Get mask of unit flags that are valid
		    MSCP$M_UF_EWRER!-	;  if Unit-available or Unit-Offline and
		    MSCP$M_UF_VARSP>, - ;  subcode = No media mounted or
		MSCP$W_UNT_FLGS(R0), R1 ;  diabled via switch setting.
	BISW	R1, UCB$W_UNIT_FLAGS(R2); Set appropriate unit flags in UCB.

	BSBW	DUTU$COPY_UNIT_FLAGS	; Call tape class driver specific code.

FINISH_ORIGUCB:

	TSTL	R2			; Was there a UCB?
	BEQL	90$			; Branch if no UCB.
	MOVL	CDRP$L_MSG_BUF(R5), R0	; Get message buffer address.
	ASSUME	MSCP$K_ST_SUCC EQ 0	; If the unit found was online,
	BITW	#MSCP$M_ST_MASK, -	; the unit flags are valid.
		MSCP$W_STATUS(R0)
	BNEQ	80$			; Branch if unit not online.
	MOVW	MSCP$W_UNT_FLGS(R0), -	; If online, copy unit flags to UCB.
		UCB$W_UNIT_FLAGS(R2)
;
; Insure that permanent CDRP and DAP CDRP have a UCB address.
;
80$:	MOVL	R2, CDDB$L_PRMUCB(R3)	; Set permanent CDRP UCB address.
	MOVL	R2, CDDB$L_DAPUCB(R3)	; Set DAP CDRP UCB address.

90$:	TSTW	CDRP$L_MEDIA(R5)	; Was unit found zero?
	BEQL	95$			; If zero, all done:  exit.

	RECYCH_MSG_BUF			; Recycle message buffer.
	BLBC	R0, POLL_CONN_BROKE	; Branch if connection is broken.

	RECYCL_RSPID			; Recycle response-id.

	BRW	POLL_LOOP		; Loop till unit zero found.

95$:	TSTL	CDDB$L_ORIGUCB(R3)	; Have we found the system device yet?
	BNEQ	NO_ORIGUCB		; Nonzero ORIGUCB means we haven't
	CLRL	R0			; Clear skip indicator -release map regs

	BSBW	DUTU$DEALLOC_ALL	; Release all CDRP resources.

	BICL	#CDDB$M_POLLING, -	; Indicate that polling no longer is
		CDDB$L_STATUS(R3)	; in progress.
	MOVL	#SS$_NORMAL, R0		; Setup success status.
	JMP	@CDDB$L_SAVED_PC(R3)	; Return to caller.
;
; The connection has broken during a polling operation.	 Release CDRP
; resources and abort the polling thread.
;
POLL_CONN_BROKE:

	CLRL	R0			; Clear skip indicator -release map regs

	BSBW	DUTU$DEALLOC_ALL	; Release all CDRP resources and

	MOVL	#SS$_ILLCDTST, R0	; Insure failed status, it might get
					;  changed by the deallc calls.
	BICL	#CDDB$M_POLLING, -	; Indicate that polling no longer is
		CDDB$L_STATUS(R3)	; in progress.
	BICL	#CDDB$M_DAPBSY, -	; Clear dap busy
		CDDB$L_STATUS(R3)	; 
	RSB				; terminate the polling fork thread.
;
; At this point, something went wrong trying to build a UCB for a unit
; discovered in the polling process.  Most likely, there was not enough
; non-paged pool to accommodate the UCB.  The process of polling for units
; will be restarted from the beginning.	 Hopefully, by the time we return
; to the unit which could not be configured, some non-paged pool will be
; available to configure it.  Otherwise, this is an infinite loop.
;
RESTART_POLL:

	RECYCL_MSG_BUF			; Recycle MSCP message buffer.
	BLBC	R0, POLL_CONN_BROKE	; Branch if connection is broken.

	RECYCL_RSPID			; Recycle the response ID.

	CLRL	CDRP$L_MEDIA(R5)	; Restart polling at unit 1.
	BRW	POLL_LOOP		; Restart loop.
;
; The system device has still not completed initialization even though
; we've finished polling this controller.  Since failover can't occur
; at this point in booting; don't hang around forever if the situation
; looks bleak.
;
NO_ORIGUCB:

	CMPL	G^EXE$GL_ABSTIM, #45	; Have we been up for 45 seconds
					; without finding the system device?
	BLSSU	RESTART_POLL		; If not, try, try again

	BUG_CHECK MSCPCLASS, FATAL	; But don't be a damn fool about it.



	.SBTTL	DO_ORIG_UCB - Hand Process Boot Device UCB.
;++
;
;  DO_ORIG_UCB - Hand Process Boot Device UCB.
;
; Functional Description:
;
;	Boot device UCB fields are initialized from the get unit status
;	packet.	 A limited form of mount verification processing -- involving
;	only a IO$_PACKACK -- is begun.	 Finally, the UCB is properly linked
;	into the I/O database.
;
; Inputs:
;
;	R0	UCB address
;	R2	MSCP message buffer address
;	R3	CDDB address
;	R4	PDT address
;	R5	CDRP address
;
; Outputs:
;
;	R0 & R1 scratch
;	R2	UCB address
;	R3	CDDB address (unchanged)
;	R4	PDT address (unchanged)
;	R5	CDRP address (unchanged)
;
;--

DO_ORIG_UCB:

	PUSHR	#^M<R0,R3,R4,R5>	; Save registers.
	MOVZWL	CDRP$W_ENDMSGSIZ(R5), R1; Restore length of MSCP message.

	BSBW	DUTU$FILL_MSCP_MSG	; Zero fill the MSCP message.

	MOVL	MSCP$L_MEDIA_ID(R2), -	; Copy MSCP media identification.
		UCB$L_MEDIA_ID(R0)
	MOVQ	MSCP$Q_UNIT_ID(R2), -	; Copy unique identifier.
		UCB$Q_UNIT_ID(R0)
	MOVW	MSCP$W_UNIT(R2), -	; Copy MSCP unit number.
		UCB$W_MSCPUNIT(R0)
	MOVL	R0, R5			; Setup UCB address.
;
; Allocate permanent fork blocks for use when attempting to allocate memory
; for a mount verification IRP and for get unit name.
;
        MOVZWL  #<2*FKB$K_LENGTH>, R1 		; Get size of 2 fork blocks

        ALLOCATE_POOL   BUGCHECK        	; Allocate fork blocks

        MOVW    R1, FKB$W_SIZE(R2)      	; Set up size,
        MOVB    #DYN$C_FRK, FKB$B_TYPE(R2)	;  type and
        MOVB    #SPL$C_SCS, FKB$B_FLCK(R2)	;  fork IPL
        MOVL    R2, UCB$L_DUTUFKBLINK(R5)	; Save fork block pointer in UCB
	MOVAB	FKB$K_LENGTH(R2), R2		; Adjust to next fork block
        MOVW    #FKB$K_LENGTH, FKB$W_SIZE(R2)	; Set up size,
        MOVB    #DYN$C_FRK, FKB$B_TYPE(R2)	;  type and
        MOVB    #SPL$C_SCS, FKB$B_FLCK(R2)	;  fork IPL

	CALL_SEVER_UCB			; Unlink UCB.

	PREPARE_TO_FORK -
		DUTU$LINK_NEW_UCB, -	; Prepare for control to return here
		FRKBLK = (R5), -	;  when fork occurs in DUTU$FIND_DDB
		MASK = <^M<>>

	BBS	#UCB$V_MSCP_INITING, -	; Branch if UCB still initing;
		UCB$L_DEVSTS(R5), 388$	; it should not be!!!

	MOVL	UCB$L_DDT(R5), R0	; Get DDT address.
	CMPL	#IOC$RETURN, -		; Does this driver do mount
		DDT$PS_MNTVER_2(R0)	; verification?
	BEQL	320$			; If not, skip simulated mnt. ver.
	MOVZWL	#IRP$K_LENGTH, R1	; Get IRP size.

	ALLOCATE_POOL BUGCHECK

	JSB	DUTU$INIT_IRP_CDRP	; Setup size and type fields.

	MOVL	R2,R3			; Set up registers
	MOVL	R1,R2			;
	MOVL	R5, IRP$L_UCB(R3)	; Put device UCB in IRP
	MOVL	UCB$L_CDT(R5), - 	;
		IRP$L_CDT(R3)	 	; Put current CDT in IRP

	PREPARE_TO_FORK -		; Create fork thread to packack the
		DO_ORIG_UCB_IO,-	;  system disk. Control to return here.
		FRKBLK = (R5), -	; Fork on UCB
		MASK = <^M<>>		; Don't save any registers

320$:	POPR	#^M<R2,R3,R4,R5>	; Restore registers.
	CLRL	CDDB$L_ORIGUCB(R3)	; Indicate no more original UCB.
	BRW	FINISH_ORIGUCB		; Rejoin polling loop.

388$:	BUG_CHECK MSCPCLASS, FATAL	; Boot UCB initialization waited for
					; something.  This should never happen.

	.DISABLE LSB


	.SBTTL	DUTU$DO_ORIG_UCB_IO - Packack boot device
;++
;
;  DUTU$DO_ORIG_UCB_IO - Packack boot device
;
; Functional Description:
;
;	This fork thread issues any I/O requests needed to make the system
;	device accessible.  (IO$_PACKACK)
;
; Inputs:
;
;	R3	IRP address
;	R5	UCB address
;
; Outputs:
;
;	None.  This fork thread practices self-immolation.
;
;--

DUTU$DO_ORIG_UCB_IO::
DO_ORIG_UCB_IO:

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

	MOVL	R5, IRP$L_UCB(R3)		; Set UCB address in IRP.
	MOVL	#<IRP$M_PHYSIO -		; Set IRP status flags.
		 !IRP$M_MVIRP>, IRP$L_STS(R3)
	BBSS	#UCB$V_MSCP_MNTVERIP, -		; Mark the UCB mount ver. in
		UCB$L_DEVSTS(R5), 20$		; progress, and bump wait
	INCW	UCB$W_RWAITCNT(R5)		; if that's needed.

20$:	ASSUME	IO$_PHYSICAL GE IO$_PACKACK
	MOVL	#IO$_PACKACK, IRP$L_FUNC(R3)	; Set function code.

25$:	BISL	#UCB$M_VALID, -			; Make UCB valid as
		UCB$L_STS(R5)			;  MV would have done.

	STALL_CALL_FORK,-			; Begin the request.
		ROUTINE=G^EXE$MNTVERSIO		;

	BLBC	IRP$L_IOST1(R3), 90$		; Branch if PACKACK error.
	CMPB	#DC$_DISK, UCB$B_DEVCLASS(R5)	; Else, is this a disk?
	BNEQ	50$				; Branch if not a disk.
	BBSS	#UCB$V_LCL_VALID, -		; Set local valid bit for sys.
		UCB$L_STS(R5), 50$		; dev. & branch if already set.
	INCB	UCB$B_ONLCNT(R5)		; Else, increment online count.

50$:	PUSHL	R3				; Else, save IRP address.
	CLRL	R3				; Signal end mount ver.
	MOVL	UCB$L_DDT(R5), R0		; Get DDT address.

	PUSHL	R5				; P2 = UCB
	PUSHL	R3				; P1 = IRP (zero in this case)

	CALLS	#2,@DDT$PS_MNTVER_2(R0)		; Call end mount ver. rout.

	POPL	R0				; Get IRP address.
	JMP	G^EXE$DEANONPAGED		; Deallocate it and terminate
						; this fork thread.

90$:	MOVB	UCB$B_FLCK(R5), -		; Copy fork lock index to
		IRP$B_FLCK(R3)			;  CDRP at tail of IRP.
	MOVAB	IRP$L_FQFL(R3), R5		; Get CDRP address.

	FORK_WAIT				; Wait with CDRP as fork block.

	MOVL	IRP$L_UCB(R3), R5		; Restore UCB address.
	JMP	DO_ORIG_UCB_IO			; Try the PACKACK again.



	.SBTTL	DUTU$NEW_SERVER_CLEANUP - Deallocate Temporary Structures
;++
;
;	DUTU$NEW_SERVER_CLEANUP - Deallocate Temporary Structures
;
; Functional Description:
;
;
; Inputs:
;
;	R0	Flag for type of cleanup
;		LBC - cleanup all structures in the LDB
;		LBS - cleanup only the LDB and the fork block
;	R1	System ID address
;
; Implicit Outputs:
;
;	If LBS in R0, meaning that all structures in the LDB must be cleaned
;	up, the I/O database mutex is assumed to still be held. This mutex must
;	be held when calling IOC$CLEANUP_IODB.
;
;	The LDB, fork block, and possibly the associated data structures
;	(DDB,CRB,IDB,etc.) are deallocated back to pool.
;
;--

DUTU$NEW_SERVER_CLEANUP::

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

	MOVAB	<DUTU$DATA+DUTU$L_LDB_FLINK>,-	; Get addr of LDB list head
		R3				;  for comparison later
	SUBL3	#LDB$PS_FLINK, R3, R4		; Get addr of the first LDB

10$:	MOVL	LDB$PS_FLINK(R4), R4		; Get the next LDB in the list
	CMPL	R4, R3				;  and compare to listhead
	BEQL	30$				; End of the list
	CMPL	LDB$Q_SYSID(R4), (R1)		; Compare first part of ID
	BNEQ	10$				; Get the next one if different
	CMPW	LDB$Q_SYSID+4(R4), 4(R1)	; Compare the second part too
	BNEQ	10$				; Move to next LDB if different
	BLBS	R0, 20$				; Skip and only dealloc the LDB
	PUSHL	R4				; Pass the LDB address

	CALLS	#1,G^IOC$CLEANUP_IODB		; Cleanup based on LDB state

20$:	REMQUE	(R4), R4			; Remove LDB from active queue
	MOVL	R4, R0				; Then move the LDB into place

	JSB	G^EXE$DEANONPAGED		;  and deallocate that too.

	RSB

30$:	BUG_CHECK	DISKCLASS,FATAL



	.SBTTL	DUTU$UNITINIT - Class driver unit initialization routine
;++
;
;	DUTU$UNITINIT - Class driver unit initialization routine
;
; Functional Description:
;
;	The operating system calls this routine after calling the controller
;	initialization routine:
;
;		at system startup
;		during driver loading
;		during recovery from a power failure
;
;	For the class drivers, the primary purpose of this routine is deletion
;	of unnecessary UCBs created by SYSGEN.	For the purposes of determining
;	whether or not to delete a UCB that UCB can be in one of three states
;	upon entry to this routine:
;
;	    1.	The UCB can be online (i.e. UCB$V_ONLINE is set in UCB$L_STS).
;		In this case, the UCB should not be deleted.  The UCB is
;		either the system disk UCB, supplied at startup, or it is a
;		UCB built during normal system operation by the class driver
;		and this call was caused by a power failure.
;
;	    2.	The UCB can be offline after a power failure (i.e.
;		UCB$V_ONLINE is clear and UCB$V_POWER is set in UCB$L_STS).
;		Again, the UCB should not be deleted.  This call is the result
;		of a power failure and the UCB is one of the CDP UCBs for a
;		local device which has been found to be accessible via a class
;		driver path.
;
;	    3.	The UCB can be offline with no power failure having occured
;		(i.e. both UCB$V_ONLINE and UCB$V_POWER are clear in UCB$L_STS).
;		This UCB should be removed from the DDB chain and deleted.  It
;		is a bogus UCB supplied by SYSGEN.
;
;	N.B. These tests depend heavily on two facts:
;
;	    a.	The DPT_STORE initialization macros do NOT set UCB$V_ONLINE.
;
;	    b.	The class driver controller initialization does set
;		UCB$V_ONLINE in any UCB that potentially represents the system
;		device.
;
;	To make life simpler and to keep SDA device displays from looking
;	"funny," this routine always clears UCB$V_POWER.  No other part of the
;	class driver tests, resets, or cares about this bit, but leaving it on
;	eternally after a power failure looks wierd.
;
; Inputs:
;
;	R5	UCB address
;
; Outputs:
;
;	R0-R4 are destroyed.
;	All other registers are preserved.
;
;--

DUTU$UNITINIT::

	$DRIVER_UNITINIT_ENTRY FETCH=YES,PRESERVE=<R5>

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

	BBSC	#UCB$V_POWER, UCB$L_STS(R5),5$	; Clear pwr fail bit & branch if set.
	BBS	#UCB$V_ONLINE, UCB$L_STS(R5),5$ ; Branch if the online bit is set.
	BISL	#UCB$M_DELETEUCB, -		; Else, set the delete this
		UCB$L_STS(R5)			; UCB bit.

	IOFORK	ENVIRONMENT=CALL		; Fork before deleting this UCB.

	MOVAB	10$, R4				; Set up continuation address

	BSBW	DUTU$LOCK_IODB			; When write access is granted,

5$:	RET
;
; Inputs:
;
;	R3	CDDB address
;	R5	UCB  address
;
10$:	.JSB_ENTRY INPUT=   <	      R3,   R5>,-
		   OUTPUT=  <>,-
		   SCRATCH= <                 >,-
		   PRESERVE=<                 >

	CALL_DELETE_UCB				; Then, delete this UCB.

	UNLOCK_IODB				; Unlock write access to IODB.

90$:	RSB					; Return to caller.



	.SBTTL	----- CONNECTION WALKING ROUTINES -----
	.SBTTL	DUTU$BEGIN_CONN_WALK - Begin Walk Through Connections
;++
;
;	DUTU$BEGIN_CONN_WALK - Begin Walk Through Connections
;
; Functional Description:
;
;	If the specified allocation class is non-zero, this routine
;	initializes a CDRP-based fork thread for a walk through all known
;	connections of the type used by this class driver (actually only disk
;	connections at this time).  Upon normal completion, the fork thread is
;	"ready" to send messages to the first known connection.	 Switching to
;	the next known connection is accomplished using the DUTU$WALK_NEXT_CONN
;	routine.  The walk through the known connections is ended by calling
;	DUTU$END_CONN_WALK.
;
;	If there is a valid secondary path, it will be set up as the first
;	connection to be used.	Continuing on to the next connection will
;	then walk through all known connections just as if there were no
;	secondary path.	 Thus, callers do not need any special setup and
;	can use these routines independent of secondary path configuration.
;
;	If the specified allocation class is zero, this routine is basically a
;	successful NOP.	 This is also the correct thing to do because the
;	remaining connection walking routines "notice" that connection walking
;	is not in progress and do the appropriate thing.
;
;	This routine releases the message buffer owned by the input fork
;	thread by calling RESTORE_CREDIT.  This assumes that the input
;	fork thread has allocated the message buffer and not used it.  Next
;	the connection walk context is setup in the four longwords beginning
;	at CDRP$T_LBUFHNDL.  The first functional connection is located.
;	N.B. connections with any of the following CDDB$L_STATUS bits set --
;	CDDB$V_INITING, CDDB$V_RECONNECT, CDDB$V_RESYNCH, or CDDB$V_NOCONN --
;	are skipped by the connection walking logic.  Connections whose
;	allocation class do not match that input are also skipped.  Finally,
;	the environment suitable for sending messages on the selected
;	connection is established, and control is returned to the caller.
;
;	The connection walking routines are intended to be used in the
;	following way:
;
;		BSBW	DUTU$BEGIN_CONN_WALK
;		BLBC	R0, 70$
;	10$:	{setup message 1}
;		BSBW	DUTU$SEND MSCP MSG
;		{process end message 1}
;		BSBW	DUTU$RESET MSCP MSG
;		BLBC	R0, 50$
;		{setup message 2}
;		BSBW	DUTU$SEND MSCP MSG
;		{process end message 1}
;		.
;		.
;		.
;	50$:	BSBW	DUTU$WALK_NEXT CONN	; Handle Error Conditions.
;		BLBS	R0, 10$
;	70$:	BSBW	DUTU$END_CONN WALK	; Found something or ran
;						; out of connections.
;		BLBC	R0, NONE_FOUND		; Branch if not conn. found.
;
; Inputs:
;
;	R2	Caller's return address
;	R3	UCB address
;	R5	CDRP address
;
; Implicit Inputs:
;
;	UCB$W_MSCPUNIT(R3)	MSCP unit number for message
;	UCB$L_CDDB(R3)		CDDB address
;	CDDB$L_ALLOCLS(cddb)	desired allocation class
;	CDRP$L_MSG_BUF(R5)	message buffer address or zero
;	CDRP$L_RSPID(R5)	RSPID or zero
;	CDRP$T_LBUFHNDL(R5)	four longwords of scratch space
;	CDRP$L_DUTUFLAGS(R5)	CDRP$V_CONNWALK and CDRP$V_WALK_2P clear
;
; Outputs:
;
;	R0	Status
;		   SS$_NORMAL means a connection is setup for message delivery
;		   SS$_CONNECFAIL means no (more) connections to walk
;		   SS$_ABORT means the I/O request has been canceled
;	R2	message buffer address
;	R4	PDT address for selected connection
;
; Implicit Outputs:
;
;	CDRP$L_CDT(R5)		CDT address for selected connection
;	CDRP$L_MSG_BUF(R5)	message buffer address (same as R2)
;	CDRP$L_RSPID(R5)	a RSPID
;	CDRP$T_LBUFHNDL(R5)	four longwords of connection walking context
;	CDRP$L_DUTUFLAGS(R5)	CDRP$V_CONNWALK set
;
;	R0 - R2, and R4 are changed.
;	All other registers are preserved.
;
; WARNINGS:
;
;	This routine releases the message buffer owned by the input fork
;	thread by calling RESTORE_CREDIT.  This assumes that the input fork
;	thread has allocated the message buffer and not used it.  If the
;	input fork thread owns a message buffer due to receipt of an end
;	message, that message buffer should be deallocated using DEALLOC_MSG_BUF
;	before calling DUTU$BEGIN_CONN_WALK.
;
;	The connection walking context is store in the four longwords
;	beginning at CDRP$T_LBUFHNDL.  This means that block transfer
;	operations cannot be performed on any of the connections located
;	during the connection walking process.
;
;	Proper operation of these routines assumes that new connections are
;	added to the end of the CDDB list.  Currently, this is true.
;
;--

DUTU$BEGIN_CONN_WALK::

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

	MOVL	R2, CDRP$L_WALK_SVPC(R5)	; Save return address.
	MOVAB	BGXERR, CDRP$L_UBARSRCE(R5)	; Store error routine addr.
	CLRL	CDRP$L_LB_CDDB(R5)		; Init load balance field
	MOVL	UCB$L_CDDB(R3), R0		; Get initial unit CDDB addr.
	MOVL	CDDB$L_ALLOCLS(R0), -		; Save desired allocation class.
		CDRP$L_WALK_ALCLS(R5)
	BEQL	7$				; Branch if zero.
	BISL	#CDRP$M_CONNWALK, -		; Set bit indicating that con-
		CDRP$L_DUTUFLAGS(R5)		; nection walking in progress.
	TSTL	CDRP$L_MSG_BUF(R5)		; Is a message buffer owned?
	BEQL	5$				; Branch if no buffer owned.

	RESTORE_CREDIT

	BICL	#CDRP$M_ERLIP, -		; since RSPID is gone, clear
		CDRP$L_DUTUFLAGS(R5)		; error log in progress flag.

5$:	CLRL	CDRP$L_CDT(R5)			; Now, this CDRP is related to
	CLRL	R4				; no particular connection.

7$:	TSTL	CDRP$L_RSPID(R5)		; Is a RSPID owned?
	BNEQ	20$				; Branch if RSPID owned.

	ALLOC_RSPID				; Else, allocate one.

20$:	BBC	#CDRP$V_CONNWALK, -		; If not walking connections,
		CDRP$L_DUTUFLAGS(R5), 60$	; go to separate setup.
	CLRL	CDRP$L_RWCPTR(R5)		; Clear wait count pointer.
	BBC	#CDRP$V_WALK_2P,-		; If the WALK_2P bit was set by
		CDRP$L_DUTUFLAGS(R5), 25$	;  our caller, load the 2P_CDDB
	MOVL	UCB$L_2P_CDDB(R3),R0		;  and slide into connection
	BRW	DUTU$WALK_TEST_CONN		;  walking without further ado.
;
; Look for a preferred path that has been specified by some other entity. If
; this is set, use this as the first choice and disable the load balancing
; logic on the presumption that whatever set the preferred path knows what it
; it doing.
;
25$:	BBS	#IRP$V_SRVIO,-			; Ignore the prefered path for
		CDRP$L_STS(R5), 30$		;  served I/O
	MOVL	UCB$L_PREF_CDDB(R3),R0		; Pick up preferred path.
	BEQL	30$				; None? Proceed with normal
						;  connection walk.
	BICL	#CDRP$M_LOADBAL, -		; Preferred path overrides LB.
		CDRP$L_DUTUFLAGS(R5)
	BISL	#CDRP$M_WALK_2P, -		; If pref path is invalid, this
		CDRP$L_DUTUFLAGS(R5)		;  flag will cause normal conn
						;  walk to start over.
	BRW	DUTU$WALK_TEST_CONN			; Now, go try the specified
						;  path.

30$:	BBS	#CDRP$V_LOADBAL, -		; LOCATE_UNIT wants us to use
		CDRP$L_DUTUFLAGS(R5), 40$	;  load information on our
						;  first pass.
	BBC	#DEV$V_LOC, -			; Branch if no local path as
		UCB$L_DEVCHAR2(R3), 40$		;  the secondary may not be a
						;  good choice.
	BBC	#DEV$V_2P, -			; Branch if no secondary path.
		UCB$L_DEVCHAR2(R3), 40$
	TSTL	UCB$L_2P_CDDB(R3)		; Secondary path valid?
	BEQL	40$				; Branch if not present.
	MOVL	UCB$L_2P_CDDB(R3), R0		; Else, use secondary path first
	BISL	#CDRP$M_WALK_2P, -		; Indicate that secondary path
		CDRP$L_DUTUFLAGS(R5)		;  is being tried first.
	BRW	DUTU$WALK_TEST_CONN			; Slip into connection walk.
;
; No secondary path available, so walk through connections.
;
40$:	SUBL3	#CDDB$L_CDDBLINK, -		; Initialize previous CDDB addr.
		<DUTU$DATA + DUTU$L_CDDB_LISTHEAD>, -
		CDRP$L_WALK_CDDB(R5)
	BRW	DUTU$WALK_NEXT_COMMON		; Join common next CDDB code.

60$:	MOVL	UCB$L_CDDB(R3), R0		; For one-shots, get CDDB addr.
	BRW	DUTU$WALK_TEST_CONN		; Slip in the backdoor of
						; DUTU$WALK_NEXT_CONN.


	.SBTTL	DUTU$WALK_NEXT_CONN - Walk to Next Working Connection
;++
;
;	DUTU$WALK_NEXT_CONN - Walk to Next Working Connection
;
; Functional Description:
;
;	This routine uses the connection walking information stored in
;	CDRP$T_LBUFHNDL to locate the next functioning connection and setup an
;	environment which allows sending of a message on that connection.
;	Upon normal completion, the fork thread is "ready" to send messages to
;	the next known and functional connection.  If no more connections are
;	available, an error status is returned in R0.  If either the
;	UCB$V_MSCP_IGNSRV bit in UCB$L_DEVSTS or CDRP$V_LOC_ONLY bit in
;	CDRP$L_DUTUFLAGS is set, VMS MSCPserver controllers are skipped
;	over and the next available local connection is tried.	Other VMS
;	MSCPserver controllers are skipped over for requests from the MSCPserver
;	(indicated by IRP$V_SRVIO being set in CDRP$L_STS) regardless of other
;	settings, to avoid multiple server "hops".
;
;	This routine releases the message buffer owned by the input fork
;	thread (if any) by executing a DEALLOC_MSG_BUF.	 This assumes that the
;	input fork thread owns the message buffer by virtue of receiving an
;	end message.  Next the next functional connection is located.
;	N.B. connections with any of the following CDDB$L_STATUS bits set --
;	CDDB$V_INITING, CDDB$V_RECONNECT, CDDB$V_RESYNCH, or CDDB$V_NOCONN --
;	are skipped by the connection walking logic.  Connections whose
;	allocation class do not match that input are also skipped.  Finally,
;	the environment suitable for sending messages on the selected
;	connection is established, and control is returned to the caller.
;
;	The method for using the connection walking routines is documented in
;	the functional description for DUTU$BEGIN_CONN_WALK.
;
; Inputs:
;
;	R3	UCB address
;	R5	CDRP address
;
; Implicit Inputs:
;
;	UCB$W_MSCPUNIT(R3)	MSCP unit number for message
;	UCB$L_DEVSTS(R3)	UCB$V_MSCP_IGNSRV set if emulators to be skipped
;	CDRP$L_MSG_BUF(R5)	message buffer address or zero
;	CDRP$L_RSPID(R5)	RSPID
;	CDRP$T_LBUFHNDL(R5)	four longwords of connection walking context
;	CDRP$L_DUTUFLAGS(R5)	CDRP$V_CONNWALK set if walking connections,
;				else clear; CDRP$V_LOC_ONLY set if emulators
;				to be skipped
;
; WARNINGS:
;
;	This routine releases the message buffer owned by the input fork
;	thread (if any) by executing a DEALLOC_MSG_BUF.	 This assumes that the
;	input fork thread owns the message buffer by virtue of receiving an
;	end message.  If the input fork thread owns a message buffer because
;	one was allocated and not used, that message buffer should be
;	deallocated using RESTORE_CREDIT before calling DUTU$WALK_NEXT_CONN.
;
;	The connection walking context is store in the four longwords
;	beginning at CDRP$T_LBUFHNDL.  This means that block transfer
;	operations cannot be performed on any of the connections located
;	during the connection walking process.
;
;	Proper operation of these routines assumes that new connections are
;	added to the end of the CDDB list.  Currently, this is true.
;
;--

	.ENABLE LSB
;
; (Out of main line error condition code)
; The allocation class equals 0 connection does not work, or there are no
; more connections to try. Say so and exit unless a load balancing pass
; was requested. In this case, the WALK_CDDB field contains our best choice
; path.
;
80$:	BBC	#CDRP$V_LOADBAL,-		; If load balancing not active
		CDRP$L_DUTUFLAGS(R5), 85$	;  return an error.
	MOVL	CDRP$L_LB_CDDB(R5),R0		; Otherwise, pick up CDDB ptr.
	BEQL	85$				; Oops, none found.
	BITL	#<CDDB$M_SNGLSTRM -		; Check for status bits
		 !CDDB$M_INITING -		; indicating a disfunctional
		 !CDDB$M_RESYNCH -		; connection.
		 !CDDB$M_NOCONN -
		 !CDDB$M_DISABLED>, -
		CDDB$L_STATUS(R0)
	BNEQ	84$				; Branch if inoperative conn.
	MOVL	R0,CDRP$L_WALK_CDDB(R5)		; Make the two CDDB pointers
						;  consistant.
	DECW	CDDB$W_LOAD_AVAIL(R0)		; Avoid making this the choice
	BRW	30$				;  for other concurrent threads

84$:	CLRL	CDRP$L_LB_CDDB(R5)

85$:	MOVZWL	#SS$_CONNECFAIL, R0		; Signal no more connections.
	CLRL	CDRP$L_UBARSRCE(R5)		; Clear error routine addr.
	JMP	@CDRP$L_WALK_SVPC(R5)		; Return to caller.
;
; Inputs:
;
;	R2	message buffer address
;	R3	UCB address
;	R5	CDRP address
;
DUTU$WALK_NEXT_CONN::

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

	MOVL	R2,CDRP$L_WALK_SVPC(R5)		; Save return address.
	BBCC	#CDRP$V_LOADBAL,-		; If this flag is set, the path
		CDRP$L_DUTUFLAGS(R5),5$		;  found by load balancing is
	BISL	#CDRP$M_WALK_2P, -		; This will cause the conn walk
		CDRP$L_DUTUFLAGS(R5)		;  to start at the beginning.

5$:	ASSUME	CDRP$V_CAND EQ 0		; Check for canceled request.
	BLBC	CDRP$L_DUTUFLAGS(R5), -		; Branch if request not canned.
		DUTU$WALK_NEXT_COMMON		;
	MOVZWL	#SS$_ABORT, R0			; Else, set status, and
	JMP	@CDRP$L_WALK_SVPC(R5)		; Return to caller.

DUTU$WALK_NEXT_COMMON:

	BBC	#CDRP$V_CONNWALK, -		; Branch to immediate exit,
		CDRP$L_DUTUFLAGS(R5), 80$	; if not walking connections.
	MOVAB	BGXERR, CDRP$L_UBARSRCE(R5)	; Store error routine addr.
	TSTL	CDRP$L_MSG_BUF(R5)		; Is a message buffer owned?
	BEQL	10$				; Branch if no buffer owned.

	DEALLOC_MSG_BUF				; Else, deallocate buffer.

10$:	BBC	#CDRP$V_LOADBAL,-		; Is this a load balancing pass?
		CDRP$L_DUTUFLAGS(R5), 12$	; If so, init LB work areas.
	CLRL	CDRP$L_LB_CDDB(R5)		; Ensure that we return correct
						;  info if all connections are
						;  broken.
12$:	MOVL	CDRP$L_WALK_CDDB(R5), R0	; Get CDDB address.

15$:	BBC	#CDRP$V_CONNWALK, -		; Branch to immediate exit,
		CDRP$L_DUTUFLAGS(R5), 80$	; if not walking connections.
	BBCC	#CDRP$V_WALK_2P, -		; Branch if not trying sec. path
		CDRP$L_DUTUFLAGS(R5), 17$	; Else clear flag and setup for
	SUBL3	#CDDB$L_CDDBLINK, -		;  walking all connections,
		<DUTU$DATA + DUTU$L_CDDB_LISTHEAD>,R0	  ; since secondary path
							  ; didn't work.
17$:	MOVL	CDDB$L_CDDBLINK(R0), R0		; Link to next CDDB.
	BEQL	80$				; Branch if no more CDDBs.
	CMPL	CDRP$L_WALK_ALCLS(R5), -	; Check for desired allocation
		CDDB$L_ALLOCLS(R0)		; class.
	BNEQ	15$				; Branch if wrong alloc. class.

DUTU$WALK_TEST_CONN:
	BITL	#<CDDB$M_SNGLSTRM -		; Check for status bits
		 !CDDB$M_INITING -		; indicating a disfunctional
		 !CDDB$M_RESYNCH -		; connection.
		 !CDDB$M_NOCONN -
		 !CDDB$M_DISABLED>, -
		CDDB$L_STATUS(R0)
	BNEQ	15$				; Branch if inoperative conn.
	MOVL	UCB$L_DDB(R3), R1		; Get DDB address.
	MOVZBL	DDB$T_NAME+3(R1), R1		; Pickup controller letter.
	SUBB	#^a/A/-1, R1			; Subtract out ascii offset.
	BBC	R1, CDDB$L_CTRLTR_MASK(R0), 15$ ; Does controller have letter?
	CMPB	#MSCP$K_CM_EMULA, -		; Is this controller the
		CDDB$B_CNTRLMDL(R0)		;  VMS MSCPserver?
	BNEQ	20$				; Branch if not.
	BBS	#IRP$V_SRVIO, -			; Don't use another server if
		CDRP$L_STS(R5), 15$		;  this is from a server.
	BBS	#CDRP$V_LOC_ONLY, -		; If the Local Only bit is
		CDRP$L_DUTUFLAGS(R5), 15$	;  set, find next controller.
	BBS	#DEV$V_SSM, -			; Don't bother with a server
		UCB$L_DEVCHAR2(R3), 15$		;  if this is a shadow member.
	TSTW	UCB$W_MSCPUNIT(R3)		; Likewise, don't bother if
	BLSS	15$				;  this is a virtual unit.

20$:	MOVL	R0, CDRP$L_WALK_CDDB(R5)	; Save working CDDB address.
	BBC	#CDRP$V_LOADBAL,-		; Is this a load balancing pass?
		CDRP$L_DUTUFLAGS(R5), 25$
	TSTL	CDRP$L_LB_CDDB(R5)		; Do we have a CDDB yet?
	BEQL	23$				; No, then use this one.
	PUSHL	R1				; Move the address of our best
	MOVL	CDRP$L_LB_CDDB(R5),R1		;  CDDB into R1, saving R1 first.
	CMPW	CDDB$W_LOAD_AVAIL(R0),-		; Compare the load at new CDDB
		CDDB$W_LOAD_AVAIL(R1)		;  with our best value found so far.
	BGTR	22$				; If this looks better, save it.
	POPL	R1				; Otherwise, restore R1 and go
	BRW	15$				;  for more.

22$:	POPL	R1				; Restore R1 and save the new

23$:	MOVL	R0, CDRP$L_LB_CDDB(R5)		;  CDDB.
	BRW	15$				; Go look some more.

25$:	BBS	#CDRP$V_CONNWALK, -		; If connection walking, skip
		CDRP$L_DUTUFLAGS(R5), 30$	; non-walker handling.
	TSTL	CDRP$L_MSG_BUF(R5)		; Does non-walker hold message?
	BEQL	70$				; If no, then get one.
	BRW	73$				; If yes, then use it.

30$:	MOVL	CDDB$L_CDT(R0), CDRP$L_CDT(R5)	; Store new CDT address in CDRP.
	MOVL	CDDB$L_PDT(R0), R4		; Get new PDT address.
	TSTL	CDRP$L_RSPID(R5)		; Is a RSPID owned?
	BNEQ	35$				; Branch if RSPID owned.

	ALLOC_RSPID				; Else, allocate a RSPID.

	BRW	70$				; Skip RSPID owned else.

35$:	RECYCL_RSPID				; If RSPID owned, recycle it.

70$:	ALLOC_MSG_BUF				; Allocate a message buffer.
	BLBC	R0, 12$				; Branch if connection failed.

73$:	INIT_MSCP_MSG ucb=(R3)			; Initialize message buffer.

	MOVZWL	#SS$_NORMAL, R0			; Indicate success.
	MOVAB	WALKERR, CDRP$L_UBARSRCE(R5)	; Setup in use error routine.
	JMP	@CDRP$L_WALK_SVPC(R5)		; Return to caller.
;
; Error during DUTU$BEGIN_CONN_WALK or DUTU$WALK_NEXT_CONN
;
; Input:
;
;	R5	CDRP address
;
BGXERR:
	.JSB_ENTRY INPUT=   <		    R5>,-
		   OUTPUT=  <>,-
		   SCRATCH= <>,-
		   PRESERVE=<>

	MOVL	CDRP$L_UCB(R5), R3		; Restore UCB.
	MOVL	CDRP$L_WALK_CDDB(R5), R0	; Get current CDDB address.
	MOVL	CDDB$L_PDT(R0), R4		; Restore current PDT address.
	CLRL	CDRP$L_LB_CDDB(R5)
	BRW	12$				; Go look for more connections.

	.DISABLE LSB
;
; Connection Walk General Error Routine
;
; Input:
;
;	R5	CDRP address
;
; Outputs:
;
;	R0	Status - ss$_illcdtst, no connections available
;
;--
WALKERR:
	.JSB_ENTRY INPUT=   <		    R5>,-
		   OUTPUT=  <R0>,-
		   SCRATCH= <>,-
		   PRESERVE=<>

	CLRL	CDRP$L_CDT(R5)
	MOVX	CDRP$Q_FR3(R5), R3		; Else, restore fork regs.
	MOVX	CDRP$Q_FR4(R5), R4
	MOVL	#SS$_ILLCDTST, R0		; Set status 
	JMP	@CDRP$L_FPC(R5)			; Go to error return address.



	.SBTTL	DUTU$END_CONN_WALK - End a Walk Through Connections
;++
;
;	DUTU$END_CONN_WALK - End a Walk Through Connections
;
; Functional Description:
;
;	This routine terminates a walk through the available connections
;	started by DUTU$BEGIN_CONN_WALK.  It undoes the special fork thread
;	setup done by DUTU$BEGIN_CONN_WALK.  Upon exit from this routine, no
;	connection resources are owned.
;
;	The method for using the connection walking routines is documented in
;	the functional description for DUTU$BEGIN_CONN_WALK.
;
; Inputs:
;
;	R0	Walk completion status
;	R3	UCB address
;	R5	CDRP address
;
; Implicit Inputs:
;
;	UCB$L_PDT(R3)		PDT address for output R4
;	UCB$L_CDT(R3)		CDT address for output CDRP$L_CDT
;	UCB$W_RWAITCNT(R3)	Wait counter address for output CDRP$L_RWCPTR
;	CDRP$L_MSG_BUF(R5)	message buffer address or zero
;	CDRP$L_RSPID(R5)	RSPID or zero
;	CDRP$T_LBUFHNDL(R5)	four longwords of connection walking context
;	CDRP$L_DUTUFLAGS(R5)	CDRP$V_CONNWALK set if walking connections,
;				else clear
; Outputs:
;
;	R0	Status, same as input
;	R2	CDDB address of located connection or zero
;	R4	PDT address from UCB$L_PDT(R3)
;
; Implicit Outputs:
;
;	CDRP$L_CDT(R5)		CDT address from UCB$L_CDT(R3)
;	CDRP$L_MSG_BUF(R5)	zero (deallocated)
;	CDRP$L_RSPID(R5)	zero (deallocated)
;	CDRP$L_DUTUFLAGS(R5)	CDRP$V_CONNWALK clear
;
; WARNINGS:
;
;	This routine releases the message buffer owned by the input fork
;	thread (if any) by executing a DEALLOC_MSG_BUF.	 This assumes that the
;	input fork thread owns the message buffer by virtue of receiving an
;	end message.  If the input fork thread owns a message buffer because
;	one was allocated and not used, that message buffer should be
;	deallocated using RESTORE_CREDIT before calling DUTU$END_CONN_WALK.
;
;--

DUTU$END_CONN_WALK::

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

	PUSHL	R0				; Save final locate status.
	MOVL	#1, R0				; Don't unmap port buffers.

	BSBW	DUTU$DEALLOC_ALL		; Deallocate RSPID & buffer.

	POPL	R0				; Restore final locate status.
	MOVL	UCB$L_CDDB(R3), R2		; Get first choice CDDB address.
	BBCC	#CDRP$V_CONNWALK, -		; Clear bit indicating that con-
		CDRP$L_DUTUFLAGS(R5), 100$	; nection walking in progress,
						; and clear the bit.
	MOVL	CDRP$L_WALK_CDDB(R5), R2	; Get last located CDDB.
	BLBS	R0, 40$				; Branch if a conn. was located.
	CLRL	R2				; Else, clear CDDB address.

40$:	MOVAB	UCB$W_RWAITCNT(R3), -		; Restore wait counter address.
		CDRP$L_RWCPTR(R5)
	MOVL	UCB$L_PDT(R3), R4		; Restore PDT address.
	MOVL	UCB$L_CDT(R3), CDRP$L_CDT(R5)	; Restore CDT address.
	CLRL	CDRP$T_LBUFHNDL(R5)		; Clear walking work area.
	CLRL	CDRP$T_LBUFHNDL+4(R5)
	CLRL	CDRP$T_LBUFHNDL+8(R5)
	CLRL	CDRP$L_UBARSRCE(R5)

100$:	RSB					; Return to caller.



	.SBTTL	----- MOUNT VERIFICATION ROUTINES -----
	.SBTTL	DUTU$REVALIDATE - Revalidate previously online disks and tapes
;++
;
;	DUTU$REVALIDATE - Revalidate previously online disks and tapes
;
; Functional Description:
;
;	This routine supervises the revalidation of all disks and tapes which
;	once were valid (online) but which have gone offline due to a
;	connection failure.  This routine is also called whenever the
;	connection manager wishes to stall all disk I/O activity by forcing
;	all disks into mount verification.  The first case is distinguished
;	from the second by the CDDB$V_QUORLOST bit in CDDB$L_STATUS being
;	clear.
;
;	For each non-shadow-set-member device not already in mount verification
;	whose UCB has the UCB$V_VALID bit set in UCB$L_STS, mount verification
;	is invoked.  Mount verification is started by feeding it one of the
;	permanent CDRP's associated with the CDDB and an appropriate error
;	status that is guaranteeded to excite mount verification.
;
;	If mount verification cannot be performed on the device (for example
;	the device is mounted foreign),	 then all pending I/O requests for the
;	device are located and terminated with SS$_DEVOFFLINE, device (or
;	controller) offline.
;
;	This routine is only interested in getting mount verification started
;	wherever possible.  If mount verification can be started it will
;	complete in one of the following ways:
;
;	    -	A suitable alternate path will be found.  This can happen even
;		before a new connection is established for this CDDB.
;
;	    -	The connection associated with this CDDB will be reestablished
;		and access to the device via that connection restored.
;
;	    -	Mount verification will timeout or otherwise abort.
;
;	All of these cases are handled in DU_END_MOUNTVER.
;
;	    -	Successful completion of mount verification (with RWAITCNT
;		equal to zero) causes the UCB to be unstalled and I/O activity
;		to be resumed.
;
;	    -	When mount verfication completes successfully but the RWAITCNT
;		is not zero, the UCB is not unstalled.	That duty befalls
;		whomever has bumped RWAITCNT.
;
;	    -	If mount verification aborted, all pending I/O requests must
;		be located and terminated with SS$_VOLINV.
;
; Synchronizing Device Revalidation After a Connection Failure
;
;	For each UCB placed into mount verification, this routine increments a
;	counter in the CDDB which represents the failed connection.  The UCB
;	is also made to point to this CDDB.  After reestablishing a connection
;	and polling for units, the reconnection routine determines whether or
;	not any UCBs are still waiting for mount verification via the new
;	connection.
;
;	If no UCBs are waiting, the single step processing CDRPs active at the
;	time the connection failed begins immediately.	Otherwise, the
;	reconnection thread exits, to wait for the pending mount verification
;	operations to complete.
;
;	As a UCB completes mount verification, the end mount verification
;	routine uses the CDDB pointer setup by this routine to locate the CDDB
;	which is waiting for mount verification completion.  The counter of
;	waiting UCBs in that CDDB is reduced by one (representing the UCB
;	which just finished mount verification).  When that count reaches zero,
;	the reconnection logic thread is resumed at DUTU$RESTART_NEXT_CDRP.
;
;	In this way, all the CDRPs active at the time of the connection
;	failure (which may have been due to a insane server) can be retried
;	one at a time after the connection has been restored and the disks
;	(or UCBs) validated by mount verification.  This is an important
;	element of error recovery for MSCP devices.
;
;	N.B. CDRPs for disks/tapes failed over to an alternate controller do
;	not participate in this single step retry mechanism, but by treating
;	all types of mount verification completetion equally, waiting
;	unnecessarily for a failed over disk/tape is avoided.
;
;	Since this routine is called from both reconnection processing on
;	controller initialization and since having DUTU$RESTART_NEXT_CDRP
;	entered due to controller initialization is very undesirable, this
;	routine does not set UCB$L_WAIT_CDDB when the CDDB$V_INITING flag
;	is set in the input CDDB.  This effectively eliminates the chain of
;	events which would cause DUTU$RESTART_NEXT_CDRP to be called.
;
;	Consider a few cases:
;
;	The connection fails for a device which has been chugging along and
;	has some requests outstanding.	The outstanding requests are placed on
;	the CDDB restart queue.	 Mount verification begins throwing requests
;	at the driver.	These requests complete quickly because no connection
;	is active and no alternate path is available.  The UCB points to the
;	CDDB which it turn is waiting for mount verification to complete for
;	the UCB.  When the connection is restored, the reconnect thread notices
;	the waiting UCB as exits before restarting the pending CDRPs.  This
;	leaves the wait count bumpped by 2, one for the mount verification and
;	one for the incomplete reconnection thread.  When mount verification
;	completes, the waint count is reduced by one to one, but this is not
;	sufficient to unstall any pending requests (those received while the
;	connection was broken).	 Eventually, the end mount verification
;	routine finds no more waiting UCBs on the CDDB and the single step
;	delivery of requests begins.  Once all outstanding requests (from the
;	time of the connection failure) have been singly passed to the server,
;	the wait count is reduced by one to zero, this allows the pending
;	requests to be starting (in parrallel mode).  If the device can
;	failover to another server or if mount verification aborts,
;	outstanding requests are removed from the CDDB restart queue and the
;	end of mount verification processing is performed as described above.
;
;	If mount verification were already in progress when the connection
;	failed, there is no need to begin it again for recovery after the
;	connection reforms.  Also, the CDDB need not wait for mount
;	verification to complete on the new connection before starting single
;	step delivery of outstanding requests.	The only possible outstanding
;	request is from mount verification.  All other requests are on the
;	pending I/O queue.  When the connection fails, the mount verification
;	I/O request is either active or inactive.  If it is active, it will be
;	queued for post processing with a status of SS$_DEVOFFLINE.  If it is
;	inactive, the next attempt to make a mount verification request will
;	be caught by the start I/O routine.
;
; Inputs:
;
;	R3	CDDB address
;
; Implicit inputs:
;
;	CDDB$L_UCBCHAIN(R3)	chain of UCBs on this connection
;	CDDB$L_STATUS(R3)	CDDB$V_INITING set if called from controller
;				initialization.
;				CDDB$V_QUORLOST set if called to force mount
;				verification after a quorum loss.
;
; Outputs:
;
;	R3	CDDB address (unchanged)
;
;	R0 - R2, and R4, R5 are destroyed.
;
; Implicit outputs:
;
;	CDDB$L_WTUCBCTR(R3)	incremented once for each UCB waiting for
;				mount verification to complete.
;	UCB$L_WAIT_CDDB		of each waiting UCB is made to point to the
;				input CDDB.
;--

DUTU$REVALIDATE::

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

	ADDL3	#<CDDB$L_UCBCHAIN -		; Get "previous" UCB address.
		 -UCB$L_CDDB_LINK>, R3, R5

NEXT_REVAL_UCB:

	.enabl lsb
	MOVL	UCB$L_CDDB_LINK(R5), R5		; Link to next UCB on conn.
	BEQL	REVAL_STARTED			; Branch if no more UCBs.

10$:	BBC	#UCB$V_VALID, UCB$L_STS(R5), -	; Does UCB need revalidation?
		NEXT_REVAL_UCB			; Branch if no revalidation.
	BBS	#DEV$V_SHD, -			; If this is a HBS shadow set
		UCB$L_DEVCHAR2(R5),-		;  member, branch to routine
		REVAL_HBS_MEMBER		;  to call volume processing.
	BBS	#UCB$V_MSCP_MNTVERIP, -		; Is mount verification already
		UCB$L_DEVSTS(R5), -		; in progress?
		NEXT_REVAL_UCB			; Branch if mv in progress.
	BBS	#UCB$V_MNTVERIP, -		; Is mount verification already
		UCB$L_STS(R5), -		; in progress?
		NEXT_REVAL_UCB			; Branch if mv in progress.
;
; For path move requests, set the CLUTRAN bit to force a minimal mount
; verification. This allows disks mounted /foreign to complete mv and
; speeds up the path move process. Note that this form of mv does not
; validate the file structure however if the connection does not reform
; on the first connect request, normal mv will take place for all
; devices allowing failover and normal validation.
;
	BBS	#CDDB$V_PATHMOVE,-		; If we are here for a path
		CDDB$L_STATUS(R3), 25$		;  move, use minimal mv.

	BBC	#CDDB$V_QUORLOST, -		; Branch if not quorum lost
		CDDB$L_STATUS(R3), 30$		; processing.
;
; For quorum lost processing, this routine is called for all disk
; CDDBs known to the system.  It must then decide whether or not the
; UCBs hanging off those CDDBs must have their I/O requests stalled.
; The rule is that any disk accessible from more than one host must
; have its I/O stalled.	 The following tests implement that rule.
;
	BBS	#MSCP$V_CF_MLTHS, -		; Branch if this is a multi-
		CDDB$W_CNTRLFLGS(R3), 25$	; host controller.
	BBC	#DEV$V_2P, -			; Branch if disk is not
		UCB$L_DEVCHAR2(R5), -		; dual-pathed.
		NEXT_REVAL_UCB

25$:	BISL	#UCB$M_CLUTRAN, -		; This disk needs MV after state
		UCB$L_STS(R5)			; transition,set UCB$V_CLUTRAN.
;
; Mount verification needs to be called for this device (or UCB).
; Understanding what follows requires several pieces of information:
;
;   1.	Mount verification returns to its caller only if it cannot
;	perform verification for the UCB submitted to it.  For
;	example, control is returned to the caller if the device is
;	not mounted.
;   2.	In all other cases, mount verification returns to its caller's
;	caller.
;   3.	If mount verification is possible, EXE$MOUNTVER will call the
;	driver's mount verification routine, DU$MOUNTVER, which is
;	supposed to process the IRP.  Since this IRP is a dummy, one
;	of the permanent IRP/CDRP pairs associated with the CDDB,
;	DU$MOUNTVER will do mount verification book keeping and then
;	RSB; since no special action is required.
;   4.	Mount verification could result in a failover, which would
;	alter UCB$L_CDDB_LINK.	Since the UCBs on the current
;	connection are the only ones of interest, the next UCB address
;	is preserved on the stack across the call to mount verification.
;   5.	Setting the UCB$M_SUPMVMSG bit causes those mount verficiation
;	messages associated with a successful mount verification
;	operation to be suppressed.  If something goes wrong or the
;	mount verfication operation nears the timeout stage, these
;	messages will be output (as usual).
;
30$:	BBSS	#UCB$V_MVFKBBSY, -
		UCB$L_DEVSTS(R5), -		; Skip this UCB if we're 
	       	NEXT_REVAL_UCB			;  waiting for memory.

9100$:	MOVZWL	#IRP$K_LENGTH, R1		; Get size of an IRP/CDRP.

	ALLOCATE_POOL
	BLBS	R0, 9110$			; Branch if allocation succeeded.

       	MOVL	R5,R4				; save UCB
      	MOVL	UCB$L_DUTUFKBLINK(R5), R5	; Get fork block pointer

    	FORK_WAIT				; Wait awhile

       	MOVL	R4, R5				; restore UCB
	BICL	#UCB$M_MVFKBBSY, -		; Clear busy bit & see if
		UCB$L_DEVSTS(R5)		; we still need to do MV
 	BRW	10$				; for this UCB.

9110$:	JSB	DUTU$INIT_IRP_CDRP		; Setup size and type fields.

	BICL	#UCB$M_MVFKBBSY, -
		UCB$L_DEVSTS(R5)  		; Clear waiting for memory flag. 
	PUSHL	UCB$L_CDDB_LINK(R5)		; Push next UCB address.
	BISL	#UCB$M_SUPMVMSG, UCB$L_STS(R5)	; Suppress mount ver. messages.
	PUSHR	#^M<R3,R5>			; Save CDDB and UCB addresses.
	MOVL	R2,R3				; Move IRP to R3
      	MOVL	R5, IRP$L_UCB(R3)		; Put device UCB in IRP
	MOVL	UCB$L_CDT(R5), IRP$L_CDT(R3)	; Put current CDT in IRP
	MOVW	#-1, IRP$L_FUNC(R3)	     	; Set up invalid function
	MOVL	#IRP$M_PHYSIO, -		; Set IRP status flags.
		 IRP$L_STS(R3)
	MOVL	#CDRP$M_PERM, -		     	; set perm bit so mount ver
		IRP$L_DUTUFLAGS(R3)	     	; won't queue this irp
	MOVZWL	#SS$_DEVOFFLINE, R0		; Setup a suitable status.
	CLRL	R1
;
; NOTE: Status is returned in R2 instead of R0,R1. R0 & R1 are preserved across
;       the call to mount verification.
;
	CALL_MOUNT_VER				; Start mount verification.
	.branch_likely
	BLBC	R2,50$				; LBC means MV started OK, 
						; LBS means we can't MV
;
; Cannot do mount verification
;
	MOVL	R3, R0				; Move input IRP to R0

       	JSB	G^EXE$DEANONPAGED 		; Deallocate the IRP

	POPR	#^M<R3,R5>			; Restore CDDB and UCB addrs.
;
; If we are serving this unit, call the server mount verification
; entry point since regular MV would have done this. If the device is served,
; this will cause the server data structures to be updated.
;
	MSCP_MV

	BICL	#UCB$M_VALID, UCB$L_STS(R5)	; Clear software volume valid.

	BSBW	DUTU$TERMINATE_PENDING		; Terminate all pending I/O.

	BRW	90$				; Loop through all UCBs.
;
; Mount verification is in progress
;
50$:	POPR	#^M<R3,R5>			; Restore CDDB and UCB addrs.
	BITL	#<CDDB$M_INITING -		; If called from controller
		 !CDDB$M_QUORLOST>, -		; initialization or quorum
		CDDB$L_STATUS(R3)		; lost processing, skip this
	BNEQ	90$				; setup.
	TSTL	UCB$L_WAIT_CDDB(R5)		; Check of double waiting UCB.
	BEQL	80$				; We have a wait-cddb.
	CMPL	UCB$L_WAIT_CDDB(R5),R3		; Is it the current CDDB?
	BEQL	90$				; If so, another thread got it
						;  already, so we can ignore it.
	BRW	DBL_WAIT_UCB			; This is a serious error an
						;  error recovery thread on
						;  another CDDB has been here.
80$:	INCL	CDDB$L_WTUCBCTR(R3)		; Count waiting UCB.
	MOVL	R3, UCB$L_WAIT_CDDB(R5)		; Store waiting CDDB address.

90$:	POPL	R5				; Get next UCB.
	BEQL	REVAL_STARTED
	BRW	10$				; If there is a UCB there, go
						; processs it.
	.dsabl lsb



;
; The following mount verification routine for shadow set members cannot
; be called until the calling convention is changed. Check out the interface
; to the ported mount verification for non-shadowed disks.
; The UCB found is a member of a host based shadow set. Since normal MV is not
; supported on these devices, we will call SHDRIVER's volume processing. If we
; are also serving this unit, call the server mount verification entry point.
;
REVAL_HBS_MEMBER:

	MSCP_MV					; Notify server of state change
;
; R0, R1, R3 and R5 are used in setting up the call, and shadowing
; may clobber other registers.
;
	PUSHR	#^M<R0,R1,R2,R3,R4,R5>		; Save our view of the universe.
	MOVL	UCB$L_SHAD(R5), R3		; Get the SHAD address.
	MOVL	SHAD$L_VU_UCB(R3), R5		; Replace physical with VU UCB.
	CLRL	R3				; No IRP!
	MOVZWL	#SS$_DEVOFFLINE, R0		; Use device offline error.
	MOVL	UCB$L_DDT(R5), R1		; Get DDT address.
;
; This will call SH$SHADOW_PROCESSING with input:
;
;	R5 -> VU UCB
;	R3 =  0	 Must be zero to denote no input IRP
;	R0 =  Error code
;
	JSB	@DDT$L_MNTV_SSSC(R1)		;  and make the call

10$:	POPR	#^M<R0,R1,R2,R3,R4,R5>		;  return, and restore regs.
	BRW	NEXT_REVAL_UCB			; Go look for more UCBs.
;
; All UCBs on the connection have been processed, one way or the
; other.  Control is now return this routine's caller.	As devices
; (or UCBs) complete mount verification DU$MOUNTVER will receive
; control and perform end of mount verification processing.
;
REVAL_STARTED:

	RSB

DBL_WAIT_UCB:

	BUG_CHECK MSCPCLASS, FATAL		; Doublely waiting UCB; fatal
						; error.


	.SBTTL	DUTU$MOUNTVER - Mount Verification Entry Point
;++
;
;  DUTU$MOUNTVER - Mount Verification Entry Point
;
;
; Functional Description:
;
;	The disk class driver and mount verification are in bed together.
;	They are so intimately related that one of them is most certainly
;	"with child" and nuptial announcements will soon be made.
;
; NOTE: If you haven't received your wedding invitation, don't hold your
;	breath.	 The above arrangement has now been complicated by the
;	tape class driver with the advent of mount verification for tapes.
;	Aside from requiring a larger bed, this menage a trois is bound
;	to raise a few questions and a few eyebrows.
;
;	In addition to the usual services performed by mount verification, the
;	class drivers call for its assistance in restoring access to disks and
;	tapes lost due to the various failure conditions which can occur with
;	a MSCP server.
;
;	The following is a list of reasons why this routine might be called:
;
;	   -	starting mount verification due to appropriate status is a
;		normal I/O request.  This probably is the result of the a
;		change of state in the drive.  Hopefully, mount verification
;		will restore the drive to the online state and work can
;		continue.
;	   -	completing mount verification for the above.
;
;	   -	starting mount verification to "remount" a disk or tape which
;		was automatically dismounted due to a connection breakage.
;		This is done by force feeding mount verification one of the
;		connection permanent CDRP's.
;	   -	completing mount verification for the above.
;
;	The first two cases represent pretty much normal mount verification.
;	At least, they are the kind of mount verification one would expect to
;	encounter for other disk and tape devices.  The second two cases are
;	a special usage of mount verification by the class drivers.
;
;	Since the connection lost processing done when mount verification
;	calls the driver is nearly identical to normal mount verification
;	processing, all that work is performed within this routine or its
;	servers.  To fully understand some aspects of this routine, it may be
;	necessary to review the DUTU$REVALIDATE routine.
;
; RWAITCNT Handling:
;
;	This routine increments RWAITCNT(ucb) when beginning mount
;	verification for the first time and decrements it when mount
;	verification is ended.	While RWAITCNT is non-zero, all I/O requests
;	are added to the I/O queue hanging off the UCB.	 Thus all I/O is
;	stalled until the mount verification completes.	 The mount
;	verification IRP is not stalled because the start I/O routine handles
;	it specially.
;
;	The code on this page acts as a simple dispatcher for the beginning
;	and ending mount verification cases.
;
; Inputs:
;
;	For beginning mount verification:
;
;	R3	IRP address
;	R5	UCB address
;
;	For ending mount verification:
;
;	R3	zero
;	R5	UCB address
;
; Outputs:
;
;	All registers are preserved.
;
;--

DUTU$MOUNTVER::

	$DRIVER_MNTVER_ENTRY FETCH=YES,PRESERVE=<R0,R1,R2,R3,R4,R5>

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

	TSTL	R3				; Is this ending mount ver.?
	BNEQ	10$				; Branch if ending.
	BRW	DUTU$END_MNTVER			; Branch if ending.

10$:;	BRW	DUTU$BEGIN_MNTVER		; Fall through if beginning.



	.SBTTL	 DUTU$BEGIN_MNTVER - Begin mount verification
;++
;
;	DUTU$BEGIN_MNTVER - Begin mount verification
;
; Functional Description:
;
;	This routine is called whenever mount verification needs to present a
;	new IRP to the driver to be "remembered" and restarted after mount
;	verification completes.	 If this is a "normal" mount verification
;	operation, the is inserted into the pending I/O request queue, ordered
;	by sequence number.  However, if the IRP is one of those permanantly
;	attached to a CDDB, it is simply discarded.  In any event, RWAITCNT is
;	bumped, unless its already bumped because of mount verification,
;	UCB$V_MSCP_MNTVERIP is set.  If a "normal" IRP with the IRP$V_SRVIO bit
;	set is input, the IRP is sent to the post queue instead of placed back
;	in the pending queue.
;
; Inputs:
;
;	R3	IRP address
;	R5	UCB address
;
; Implicit inputs:
;
;	IRP$L_DUTUFLAGS(R3)	CDRP$V_PERM bit set to indicate presence of
;				one of the permanent IRP/CDRP pairs belonging
;				to the connection.
;	IRP$L_SEQNUM(R3)	monotonically increasing I/O request sequence
;				number
;	UCB$L_IOQ[F/B]L(R5)	header for queue of pending I/O requests for
;				this unit
;	IRP$W_STS(R3)
;		IRP$V_SRVIO	If SRVIO, post and return rather than queue
;
;
; Outputs:
;
;	R0 and R1 are destroyed.
;	All other registers are preserved.
;
; Implicit outputs:
;
;	UCB$W_RWAITCNT(R5)	incremented if mount verification not already
;				in progress.
;	UCB$L_DEVSTS(R5)	UCB$V_MSCP_MNTVERIP bit set
;
; Side effects:
;
;	The IRP pointed to by R3 may be linked into the pending I/O request queue.
;
;--

DUTU$BEGIN_MNTVER:

	EXTZV	#UCB$V_MSCP_MNTVERIP, #1, -	; Get previous mount ver. bit
		UCB$L_DEVSTS(R5), R0		; for an upcomming sanity check.
	BBSS	#UCB$V_MSCP_MNTVERIP, -		; Set MSCP mount ver. in
		UCB$L_DEVSTS(R5), 15$		; progress and branch if already
	INCW	UCB$W_RWAITCNT(R5)		; set.	Else, bump wait count.

15$:	BBS	#CDRP$V_PERM, -			; Branch if permanent CDRP
		IRP$L_DUTUFLAGS(R3), 50$	; used to start special
;						; purpose mount verification?
; "Normal" mount verification
;
	BBS	#IRP$V_SRVIO, IRP$L_STS(R3), 45$; If SRVIO, post and return.
	MOVAB	<UCB$L_IOQFL-IRP$L_IOQFL>(R5), -; Get pending I/O queue
		R1				; listhead address.
	MOVL	R1, R0				; Copy listhead address.

20$:	MOVL	IRP$L_IOQFL(R0), R0		; Link to next pending IRP.
	CMPL	R0, R1				; Reached end of list yet?
	BEQL	40$				; Branch if end of list.
	CMPL	IRP$L_SEQNUM(R0), -		; Does new IRP belong here?
		IRP$L_SEQNUM(R3)
	BLSSU	20$				; Branch if IRP doesn't belong.

40$:	INSQUE	(R3), @IRP$L_IOQBL(R0)		; Insert new IRP between
						; previous and current IRPs in
						; pending I/O queue.
	RET					; Return.

45$:	CALL_POST_IRP				; Release IRP and return to MV.

	RET
;
; Special purpose mount verification, initiated from within the driver. Now
;  that mount verification has finished with our IRP we no longer need it.
;
50$:;	BLBS	R0, 999$			; Branch if nested mount ver.
	MOVL	R3, R0				; Move IRP to R0

	JSB	G^EXE$DEANONPAGED		; deallocate it 

	RET					; and return

999$:	BUG_CHECK MSCPCLASS, FATAL		; Initating special mount ver.
						; when mount ver. is already
						; in progress is a no no.



	.SBTTL	 DUTU$END_MNTVER - End mount verification
;++
;
;	DUTU$END_MNTVER - End mount verification
;
; Functional Description:
;
;	This routine completes mount verification processing.
;
;	The mount verification in progress flag is cleared and the wait count
;	is reduce one to offset its being raised one when mount verification
;	was initiated.	If mount verification failed to bring the disk back
;	online, all requests are aborted with SS$_VOLINV to indicate that the
;	volume is no longer valid.
;
;	Next, the status of the wait count and the wait count bumped bit must
;	be made appropriate for the status of the current connection to the
;	MSCP server.  Mount verification, failover, and reconnection processing
;	all occur independently of each other.	However, at this point they
;	must be coordinated.  If reconnection processing is in progress the
;	wait count must be bumped by at least one and the wait count bumped
;	bit must be set.  If reconnection processing is not in progress, the
;	wait count must not be bumped and the wait count bumped bit must be
;	clear.	During a reconnection attempt, the wait count must be bumped
;	from the beginning of the attempt to the end of the attempt, and this
;	routine, since it represents the last driver routine in a mount
;	verification attempt and since mount verification is the primary
;	failover tool, must coordinate handling of the wait count.
;	Reconnection modification of the wait count is handled in
;	DU$CONNECT_ERR, TU$CONNECT_ERR and DUTU$END_SINGLE_STREAM.
;
;	If mount verification succeded in bringing the disk or tape back
;	online, this routine performs the end of mount verification processing
;	necessary to stall attempting to single step CDRPs active when a
;	connection failed until all disks/tapes (or UCBs) on a given CDDB are
;	made valid by mount verification.  This scheme is detailed in the
;	description of DUTU$REVALIDATE.
;
;
; Inputs:
;
;	R5	UCB address
;
; Implicit inputs:
;
;	UCB$W_RWAITCNT(R5)	count of the reasons for stalling I/O requests
;				(zero implies requests can be restarted)
;	UCB$L_IOQ[F/B]L(R5)	header for queue of pending I/O requests for
;				this unit
;	UCB$L_STS(R5)		bit UCB$V_VALID clear if mount verification
;				failed
;	UCB$L_WAIT_CDDB(R5)	address of a CDDB waiting for mount
;				verification to complete, or zero if none
;
; Outputs:
;
;	R0 through R4 are destroyed.
;	All other registers are preserved.
;
; Implicit outputs:
;
;	UCB$W_RWAITCNT(R5)	decremented
;	UCB$L_DEVSTS(R5)	UCB$V_MSCP_MNTVERIP bit cleared.
;	UCB$L_STS(R5)		UCB$V_SUPMVMSG bit cleared.
;	UCB$L_WAIT_CDDB(R5)	zeroed
;
;--

DUTU$END_MNTVER:

	BICL	#UCB$M_SUPMVMSG, UCB$L_STS(R5)	; Make next mount ver. noisy.
	BBCC	#UCB$V_MSCP_MNTVERIP, -		; Clear mount verification in
		UCB$L_DEVSTS(R5), 21$		; progress bit & br. if clear.
	DECW	UCB$W_RWAITCNT(R5)		; Else, reduce wait count.

21$:	BBC	#UCB$V_VALID, UCB$L_STS(R5), 90$; Branch if mount ver. failed.
;
; Mount verification was successful.
;
	JSB	G^SCS$UNSTALLUCB		; If possible, start pending

70$:	MOVL	UCB$L_WAIT_CDDB(R5), R3		; Get wait CDDB address.
	BEQL	80$				; Branch if none.
	CLRL	UCB$L_WAIT_CDDB(R5)		; Else, clear waiting CDDB.
	DECL	CDDB$L_WTUCBCTR(R3)		; Decrement waiting UCB count.
	BNEQ	80$				; Branch if UCBs still waiting.
	BBCC	#CDDB$V_RSTRTWAIT, -		; Branch if connection is not
		CDDB$L_STATUS(R3), 80$		; waiting to restart CDRPs.
	PUSHL	R5				; Else, save UCB.

	BSBW	DUTU$RESTART_NEXT_CDRP		; Begin single steping CDRPs.
	POPL	R5				; Restore UCB.

80$:	CLRB	UCB$B_FAIL_MUTEX(R5)		; Clear to allow internal path
						; changes.
	RET					; Exit.
;
; Mount verification was not successful.
;
90$:	BSBW	DUTU$TERMINATE_PENDING		; Terminate all pending I/O.

	BRW	70$				; Rejoin end mount ver. code.



	.SBTTL	DUTU$RESTART_NEXT_CDRP - Initiate the first (i.e. next) CDRP on the restart queue.
;++
;
; Functional Description:
;
;	Here we attempt to initiate the first (i.e. next) CDRP on the restart
;	queue. In order to prevent getting caught in an infinite loop trying to
;	initiate an operation that the controller cannot complete for one
;	reason or another, we maintain a retry count and the address of the
;	CDRP that we are currently single streaming.
;
;	In the normal case this is an isolated re-CONNECTION and the first CDRP
;	on the restart queue is a random CDRP.	We notice this by seeing that
;	the address of our first CDRP is not equal to the current contents of
;	CDDB$L_RSTRTCDRP.
;
;	In the other case the connection failed while we were in single stream
;	mode and the CDRP which we happened to be processing is the same CDRP
;	that now heads our restart queue.  In this case, before initiating the
;	processing of this CDRP, we decrement 1 from the retry count and if it
;	remains non-zero, we restart the CDRP processing.  If the decrementing
;	results in a zero retry count, then we log the event and effectively
;	abort the CDRP by calling FUNCTION_EXIT with an appropriate error
;	status.	 FUNCTION_EXIT, due to the setting of the CDDB$M_SNGLSTRM bit
;	will then start the processing of the next CDRP on the restart queue.
;
;	We can arrive here either by falling through from the above code or via
;	a branch from FUNCTION_EXIT.  In either case we have:
;
; Input:
;
;	R3	CDDB address
;
;--

DUTU$RESTART_NEXT_CDRP::

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

	REMQUE	@CDDB$L_RSTRTQFL(R3),R5 ; R5 => 1st CDRP on restart queue.
	BVS	DUTU$END_SINGLE_STREAM	; VS implies restart was empty.
	BBCS	#CDDB$V_SNGLSTRM,-	; Set bit and if clear, this is 1st
		CDDB$L_STATUS(R3),20$	;  time here for this CDRP, so branch.
	CMPL	R5,CDDB$L_RSTRTCDRP(R3) ; See if same CDRP as last time.
	BNEQ	20$			; NEQ implies not the same.
	DECL	CDDB$L_RETRYCNT(R3)	; If same, decrement 1 from retries.
	BNEQ	30$			; NEQ implies retries remaining.

;
; *******************************Log this error.******************************
;

	MOVL	#SS$_CTRLERR,R0			; Indicate error status.
	CLRL	R1				; Set second part of I/O status.
	MOVL	CDRP$L_UCB(R5),R3		; Get UCB address,

	BSBW	FUNCTION_EXIT			;  and exit this function.

	RSB

20$:	MOVL	R5,CDDB$L_RSTRTCDRP(R3)		; Start new single stream CDRP.
	MOVL	#MAX_RETRY,-			; Establish fresh retry count.
		CDDB$L_RETRYCNT(R3)

30$:	MOVL	CDRP$L_UCB(R5),R3		; R3 => UCB.

	BSBW	SEND_IO				; Send the CDRP again.

	RSB



	.SBTTL - DUTU$END_SINGLE_STREAM
;++
;
; DUTU$END_SINGLE_STREAM
;
;
; Functional Description:
;
;	The purpose of this routine is to resume normal operation and get each
;	unit going. To do this SCS$UNSTALLUCB is called for each UCB in turn.
;	This has the effect of starting up as many (perhaps all) of the IRP's
;	(that's right IRP's) as possible that may have backed up on the UCB
;	input queue while the system was in single stream mode.
;
; Inputs:
;
;	R3 = CDDB
;
;
; Outputs:
;
;
;
;--

DUTU$END_SINGLE_STREAM::

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

	BICL	#CDDB$M_SNGLSTRM, -	; Clear single streaming CDRPs flag.
		CDDB$L_STATUS(R3)
	MOVL	CDDB$L_RSTRTCNT(R3), R0 ; Get current restart count.
	MOVAB	<CDDB$L_UCBCHAIN -	; Setup "previous" UCB address.
		-UCB$L_CDDB_LINK>(R3),R5

10$:	MOVL	UCB$L_CDDB_LINK(R5), R5 ; Point to next UCB.
	BEQL	30$			; Branch if no more UCBs to process.
	BBCC	#UCB$V_MSCP_WAITBMP, -	; Indicate RWAITCNT no longer bumped.
		UCB$L_DEVSTS(R5), 20$	;  and branch if it wasn't
	DECW	UCB$W_RWAITCNT(R5)	; Unbump wait count.

20$:	PUSHR	#^M<R0,R3>		; Save restart cnt. and CDDB address.

	JSB	G^SCS$UNSTALLUCB	; Start up IRPs on UCB.

	POPR	#^M<R0,R3>		; Restore restart cnt. and CDDB address.
	CMPL	R0, CDDB$L_RSTRTCNT(R3) ; Did the unstall cause a restart?
	BEQL	10$			; Branch if no restart was caused.
	RSB				; Else, discontinue this thread.

30$:	BICL	#CDDB$M_RECONNECT, -	; Clear reconnect in progress bit.
		CDDB$L_STATUS(R3)
	RSB				; Ta De, Ta De, that's all folks.



	.SBTTL	----- UNIT LOCATE / FAILOVER ROUTINES -----
	.SBTTL	DUTU$LOCATE_UNIT - Locate Working Access to a Specified Unit
;++
;
;	DUTU$LOCATE_UNIT - Locate Working Access to a Specified Unit
;
; Functional Description:
;
;	All remote servers in the same allocation class as the unit
;	represented by the input CDRP are checked for the presence of the
;	unit.  When the allocation class of the unit is zero, only one
;	controller is checked.	Upon completion, a return status indicates
;	whether or not the unit was located.  If the unit was located, the
;	CDDB representing the connection on which it was located is also
;	output.	 For devices which have seen at least one local (non-emulated)
;	controller path, VMS MSCPservers are ignored on the first pass through
;	the connections, and then scanned (with everything) if no local paths
;	were available.	 For devices which have two non-server paths or for
;	a request from an MSCPserver, the second pass is not done, since
;	other MSCPservers are to be ignored in these cases anyway.
;
; Inputs:
;
;	R2	Return address
;	R3	UCB address
;	R4	PDT address (for current primary connection)
;	R5	CDRP address
;
; Implicit Inputs:
;
;	UCB$L_CDT(R5)		CDT address current primary connection
;	UCB$W_MSCPUNIT(R3)	MSCP unit number for unit
;	UCB$W_LCL_MSCPUNIT(R3)	local controller MSCP unit representation
;	UCB$L_DDB(R3)		DDB address
;	UCB$L_CDDB(R3)		CDDB address
;	UCB$L_MEDIA_ID(R3)	MSCP Media Identification
;	CDDB$L_ALLOCLS(cddb)	unit's allocation class
;	CDRP$L_MSG_BUF(R5)	message buffer address or zero
;	CDRP$L_RSPID(R5)	RSPID or zero
;	CDRP$L_DUTUFLAGS(R5)	CDRP$V_CONNWALK clear
;	CDRP$L_EXTEND(R5)	one longword of scratch space
;	CDRP$T_LBUFHNDL(R5)	four longwords of scratch space
;
;	Any SCS resources held by the CDRP will be deallocated by the time
;	this subroutine completes.
;
; Outputs:
;
;	None
;
; Inputs to the completion routine:
;
;	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
;
;--

DUTU$LOCATE_UNIT::

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

	MOVL	R2, CDRP$L_EXTEND(R5)		; Save return address.
;
; Here we have to decide if we a load balancing pass as the first pass of
; connection walking. Currently only servers provide load information so we
; will clear the LOADBAL bit in DUTUFLAGS for all other cases. Similarly,
; if the device is not dual pathed, do not attempt load balancing, as
; connection walking will be disabled. Should the HSC choose to support load
;
        BICL    #<CDRP$M_LOC_ONLY -             ; Clean up CDRP before possible
                !CDRP$M_LOADBAL  -              ;   walk of non-server paths.
                !CDRP$M_WALK_2P>, -             ;
                CDRP$L_DUTUFLAGS(R5)            ;
 	BBS	#IRP$V_SRVIO, -			; Likewise, MSCPserver requests
		CDRP$L_STS(R5), 5$		;  will ignore other servers.
	BBC	#DEV$V_2P,-			; If the device is not dual path
		UCB$L_DEVCHAR2(R3),3$		;  cannot load balance.
	TSTB	UCB$B_FAIL_MUTEX(R3)		; Look for MSCP LB code.
	BGEQ	2$
;
; Do NOT set loadbal bit if the server has asked us to move.
;
	ASSUME	CDRP$V_WALK_2P GE 8		; We are here as a result of a
	BISB	#<CDRP$M_WALK_2P @ -8>,-	; LB request. The 2P will be the
		CDRP$L_DUTUFLAGS+1(R5)		; server that sent the message,
	BRB	5$				; use it as first choice.

2$:	ASSUME	CDRP$V_LOADBAL GE 8
	BISL	#CDRP$M_LOADBAL, -		; Assume we want a load balance
		CDRP$L_DUTUFLAGS(R5)		;  pass first (no local paths).

3$:	BBC	#DEV$V_LOC,-			; Have we seen a local path?
		UCB$L_DEVCHAR2(R3), 5$		; Else, try for a local path.
	BISL	#CDRP$M_LOC_ONLY, -		; Let first round of walking
		CDRP$L_DUTUFLAGS(R5)		;  only use non-server paths.

5$:	CLRB	UCB$B_FAIL_MUTEX(R3)		; Clear attention code.
	MOVAB	7$, R2				; Pass the return address

	BSBW	DUTU$BEGIN_CONN_WALK		;  and prepare for the conn walk

	RSB
;
; Inputs:
;
;	R0	Status
;		   SS$_NORMAL means a connection is setup for message delivery
;		   SS$_CONNECFAIL means no (more) connections to walk
;		   SS$_ABORT means the I/O request has been canceled
;	R2	message buffer address
;	R4	PDT address for selected connection
;

7$:	.JSB_ENTRY INPUT=   <R0,   R2,	 R4>,-
		   OUTPUT=  <>,-
		   SCRATCH= <>,-
		   PRESERVE=<>

	BLBC	R0, 50$				; Branch if no connections.

10$:	MOVL	CDRP$L_WALK_CDDB(R5), R0	; Find CDDB for this path.
	CMPB	#MSCP$K_CM_EMULA, -		; Is this path we're walking
		CDDB$B_CNTRLMDL(R0)		;  the VMS MSCPserver?
	BNEQ	20$				; Branch if not.
;
;  Walking server path.
;
	MOVW	#MSCP$K_SLUN_RSVP, -		; Setup magic SLUN RSVP
		MSCP$W_UNIT(R2)			;  unit number to request info.
	MOVW	UCB$W_UNIT(R3), -		; Setup unit number desired
		MSCP$W_SLUN_UNIT(R2)		;  in SLUN field.
	MOVL	UCB$L_DDB(R3), R0		; Get DDB address for device.
	MOVL	DDB$L_ALLOCLS(R0), -		; Setup allocation class
		MSCP$L_SLUN_ALLOCLS(R2)		;  desired.
	ASSUME	MSCP$V_SLUN_C EQ 0
	ASSUME	MSCP$S_SLUN_C LE 8
	ASSUME	<MSCP$V_MTYP_D1 + MSCP$S_MTYP_D1> EQ MSCP$V_MTYP_D0
	ASSUME	<MSCP$V_SLUN_D1 + MSCP$S_SLUN_D1> EQ MSCP$V_SLUN_D0
	SUBB3	#^a/A/-1, DDB$T_NAME+3(R0), -	; Put controller letter (1-26)
		MSCP$W_SLUN_DEVNAME(R2)		;  offset in low order devname.
	EXTZV	#MSCP$V_MTYP_D1, -		; Get D0/D1 from Media Id in
		#MSCP$S_MTYP_D1 -		;  UCB, corresponding to DDcu
		+MSCP$S_MTYP_D0, -		;  letters of device name.
		UCB$L_MEDIA_ID(R3), R0
	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.
	BRW	30$				; Rejoin common code.
;
;  Walking non-server path.
;
20$:	MOVW	UCB$W_LCL_MSCPUNIT(R3), -	; Reset unit number from local
		MSCP$W_UNIT(R2)			;  controller representation.
;
;  Finish setup and send message to locate unit
;
30$:	MOVW	#MSCP$K_OP_GTUNT, -		; The locate command is
		MSCP$B_OPCODE(R2)		; Get Unit Status.
	MOVL	CDRP$L_WALK_CDDB(R5),R1		; Get conn walking CDDB address.

	SEND_MSCP_MSG				; Send locate request.
;
; Control is returned here after receipt of the END PACKET corresponding to
; the MSCP Get Unit Status command.  Control is regained via a callback from
; DU$IDR.
;
; Inputs:
;
;	R0	Status
;	R2	MSCP message buffer address
;	R3	CDDB  address
;	R4	PDT  address
;	R5	CDRP address
;
	.JSB_ENTRY INPUT=   <R0,R1,R2,R3,R4,R5>,-
		   OUTPUT=  <                 >,-
		   SCRATCH= <>,-
		   PRESERVE=<                 >

	CMPW	#SS$_ILLCDTST, R0		; Check for CDT error
	BEQL	40$				; branch on error
	BICW3	#^cMSCP$M_ST_MASK, -		; Retrieve only the status
		MSCP$W_STATUS(R2), R0		;  from the MSCP status field.

	DISPATCH R0, type=W, prefix=MSCP$K_ST_, < -
		<SUCC,	60$>, -			; If the status is success,
		<AVLBL, 60$>, -			;  or available, goto 60$
		>				; otherwise fall through...

40$:	MOVAB	42$, R2				; Pass return address in R2

	BSBW	DUTU$WALK_NEXT_CONN		; Go to next testable conn.

	RSB

;
; Inputs:
;
;	R0	Status
;		   SS$_NORMAL means a connection is setup for message delivery
;		   SS$_CONNECFAIL means no (more) connections to walk
;		   SS$_ABORT means the I/O request has been canceled
;	R2	message buffer address
;	R3	UCB address
;	R4	PDT address for selected connection
;	R5	CDRP address
;

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

	BLBS	R0, 10$				; Branch if such a conn. exists.
	CMPW	#SS$_ABORT, R0			; Is error ABORT (canceled)?
	BEQL	50$				; If so, keep it.
	MOVZWL	#SS$_MEDOFL, R0			; Else, change it to MEDOFL.

50$:	MNEGW	#1, CDRP$W_ENDMSGSIZ(R5)	; Indicate no saved SLUN unit.
	BBCC	#CDRP$V_LOC_ONLY, -		; If this wasn't a LOC_ONLY
		CDRP$L_DUTUFLAGS(R5), 80$	;  first pass, end walking.
	ASSUME	CDRP$V_CAND EQ 0		; If request was canned,
	BLBS	CDRP$L_DUTUFLAGS(R5), 80$	;  get out of here.

	BSBW	DUTU$END_CONN_WALK		; Clean up before restart.
;
; Our local only pass has failed so we are going back to allow servers to be
;  considered and we should use load balancing information.
;
	BISL	#CDRP$M_LOADBAL, -		; 
		CDRP$L_DUTUFLAGS(R5)		; 
	BRW	5$				; 

60$:	MOVL	#SS$_NORMAL,R0			; Success, maybe
	CMPL	MSCP$L_MEDIA_ID(R2), -		; Make sure the media id
		UCB$L_MEDIA_ID(R3)		;  is up to date.
	BNEQ	40$
	MOVL	CDRP$L_WALK_CDDB(R5), R1	; Find CDDB for this path.
	CMPB	#MSCP$K_CM_EMULA, -		; Is this path we're walking
		CDDB$B_CNTRLMDL(R1)		;  the VMS MSCPserver?
	BEQL	90$				; Branch if emulator.
	BBS	#MSCP$V_SLUN, -			; SLUN bit should not be set
		MSCP$W_UNIT(R2), 100$		;  for local controllers.

70$:	MOVW	MSCP$W_UNIT(R2), -		; Setup saved MSCP unit number
		CDRP$W_ENDMSGSIZ(R5)		;  from end message.

80$:	BSBW	DUTU$END_CONN_WALK		; End connection walking.

	BICL	#CDRP$M_LOC_ONLY, -		; Clean up CDRP after possible
		CDRP$L_DUTUFLAGS(R5)		;  walk of non-server paths.
	JMP	@CDRP$L_EXTEND(R5)		; Return to caller.
;
;  Validate returned MSCP unit number from VMS MSCPserver
;
90$:	CMPW	#MSCP$K_SLUN_RSVP, -		; Make sure that we didn't get
		MSCP$W_UNIT(R2)			;  back magic unit number.
	BEQL	100$				; Something's broken if so.
	BBS	#MSCP$V_SLUN, -			; SLUN bit must be set in
		MSCP$W_UNIT(R2), 70$		;  server end message.

100$:	BUG_CHECK MSCPCLASS, FATAL		; Bogus unit number in end msg.



	.SBTTL	DUTU$MOVE_UNIT - Move a Unit to A New Connection
;++
;
;	DUTU$MOVE_UNIT - Move a Unit to A New Connection
;
; Functional Description:
;
;	The specified unit is moved to the connection whose CDDB address is
;	specified.  The only condition which prevents the move is the presence
;	of outstanding cancel operations for that unit.	 If the target
;	connection is already the primary connection for the specified unit,
;	calling this routine has no affect on the I/O database, although the
;	MSCP unit number fields are updated as necessary (DUTU$MOVE_UNIT only).
;
; Inputs:
;
;	R2	Target CDDB address
;	R3	UCB address
;	R4	PDT address (for current connection)
;	R5	CDRP address or zero
;
; Implicit Inputs:
;
;	CDRP$L_CDT(R5)		CDT address for current primary connection
;	CDRP$W_ENDMSGSIZ(R5)	New MSCP unit number to be used for SLUN devices
;	UCB$L_CDT(R3)		CDT address for current primary connection
;	UCB$L_CDDB(R3)		Address of current primary CDDB
;	UCB$L_DDB(R3)		Address of current primary DDB
;	UCB$L_LINK(R3)		Address of next UCB in primary DDB chain
;	UCB$L_2P_CDDB(R3)	Address of current secondary CDDB (or zero)
;	UCB$L_2P_DDB(R3)	Address of current secondary DDB (or zero)
;	UCB$L_2P_LINK(R3)	Address of next UCB in secondary DDB chain
;				(or zero)
;	UCB$L_CDDB_LINK(R3)	Address of next UCB in CDDB chain
;	UCB$W_MSCPUNIT(R3)	MSCP unit number for UCB
;	UCB$W_LCL_MSCPUNIT(R3)	Local controller MSCP unit number for reloading
;				MSCPUNIT
;	UCB$W_UNIT(R3)		Unit number for UCB
;	UCB$L_DEVSTS(R3)	UCB$V_MSCP_WAITBMP set if current (old)
;				connection is performing a reconnect
;	UCB$L_DEVCHAR2(R3)	DEV$V_2P set if device is dual-pathed
;
; Outputs:
;
;	R0	Status
;		   SS$_NORMAL means unit was moved
;		   SS$_CANCEL means unit could not be moved due to pending
;			      cancel operation
;	R3	UCB address
;	R4	PDT address (for new primary connection)
;	R5	CDRP address or zero (same as input)
;
;
; Implicit outputs:
;
;	CDRP$L_CDT(R5)		CDT address for new primary connection,
;				if CDRP supplied as input
;	UCB$W_MSCPUNIT(R3)	New MSCP unit for SLUN devices, or reloaded
;				from UCB$W_LCL_MSCPUNIT for non-SLUN devices
;	UCB$Q_UNIT_ID(R3)	If SLUN device, allocation class loaded from
;				DDB so DUTU$FIND_DDB uses the right value
;				(first longword only)
;	UCB$W_SRV_MSCPUNIT(R3)	Same as previous, for SLUN devices
;	UCB$L_DDB(R3)		Address of new primary DDB
;	UCB$L_CDDB(R3)		Address of new primary CDDB
;	UCB$L_PDT(R3)		PDT for new primary connection (same as R4)
;	UCB$L_CDT(R3)		CDT address for new primary connection
;	UCB$L_CRB(R3)		Address of CRB for new primary CDDB
;	UCB$L_2P_DDB(R3)	Address of previous primary DDB
;	UCB$L_2P_CDDB(R3)	Address of previous primary CDDB
;	UCB$L_LINK(R3)		Address of next UCB in new primary DDB chain
;	UCB$L_CDDB_LINK(R3)	Address of next UCB in new CDDB chain
;	UCB$L_2P_LINK(R3)	Address of next UCB in previous primary DDB
;				chain
;	UCB$W_RWAITCNT(R3)	bumped if new CDDB has CDDB$V_RECONNECT set:
;				decremented if new CDDB has CDDB$V_RECONNECT
;				clear and UCB$V_MSCP_WAITBMP is set
;	UCB$L_DEVSTS(R3)	UCB$V_WAITBMP is made to match CDDB$V_RECONNECT
;				in the new CDDB
;	UCB$L_DEVCHAR2(R3)	DEV$V_2P set for all devices except shadow set
;				virtual units (which cannot be dual-pathed)
;
;	Then the original primary CDDB is restart queue scanned for CDRPs with
;	CDRP$L_UCB matching the input UCB.  Each CDRP located is placed --
;	in the order found -- at the head of the UCB's I/O queue.
;
; Side Effects:
;
;	The I/O database DDB chains reflect a successfully moved unit.	This
;	requires waiting for write access to the I/O database mutex, among
;	other things.  However, this routine cannot stall its caller to wait
;	for the mutex.	This routine's caller may be executing on behalf of
;	mount verification, which is trying to restore access to the system
;	disk, said access being required to resolve a page fault, which must
;	be resolved before the mutex can be released.
;
;	Therefore, moving DDBs for the unit occurs in a separate fork thread,
;	and may not happen until long after this routine completes.
;	Incidently, the separate fork thread is found in routine
;	DUTU$MOVE_IODB.
;
; NOTE: The updated MSCPUNIT number field is assumed to be stored in the
;	input CDRP in the CDRP$W_ENDMSGSIZ field, for DUTU$MOVE_UNIT.
;	This field is automatically setup by DUTU$LOCATE_UNIT, which is
;	most commonly called directly before DUTU$MOVE_UNIT. If ths new
;	controller is not an emulator, the MSCPUNIT field is updated
;	directly from the UCB$W_LCL_MSCPUNIT field instead (harmless if
;	no VMS MSCP Servers are involved, otherwise reset stale emulator
;	unit number).
;
;--

DUTU$MOVE_UNIT::

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

	MNEGL	#1, R1			; Set high order word in case unit = 0.
	TSTL	R5			; CDRP input?
	BEQL	5$			; Branch if not, leave SLUN as RSVP.
	MOVW	CDRP$W_ENDMSGSIZ(R5),R1 ; Save unit number for SLUN devices.

5$:	CMPL	R2, UCB$L_CDDB(R3)	; Is the target CDDB already primary?
	BNEQ	10$			; Branch if not primary.

	JSB	15$			; Update SLUN fields if necessary.

	MOVZWL	#SS$_NORMAL, R0		; Indicate success, no move necessary.
	BRW	EXIT_MOVE_UNIT		; Exit routine.

10$:	PUSHL	R5			; Save CDRP address.
	MOVL	R3, R5			; Copy UCB address.
	CLRL	R4			; Signal test UCB only.
	PUSHL	R1			; Save flag for unit update/new unit.

	BSBW	DUTU$CHECK_NOCANCEL	; Check for active cancels.

	POPL	R1			; Restore flag for unit update/new unit.
	POPL	R5			; Restore CDRP address.
	MOVL	UCB$L_PDT(R3), R4	; Restore PDT address.
	BLBS	R0, 20$			; Branch if no active cancels.
	MOVZWL	#SS$_CANCEL, R0		; Else, indicate failure.
	BRW	EXIT_MOVE_UNIT		; Exit routine.

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

	TSTL	R1			; Are fields to be updated?
	BEQL	18$			; Branch if not.
	CMPB	#MSCP$K_CM_EMULA, -	; Is the new controller the VMS
		CDDB$B_CNTRLMDL(R2)	;  MSCPserver?
	BNEQ	16$			; Branch if not.
	BICL	#MSCP$M_SHADOW,R1	; Clear shadow bit if set
	MOVW	R1, UCB$W_MSCPUNIT(R3)	; Update MSCP unit field for emulator.
	MOVW	R1, -			; And also update the server unit
		UCB$W_SRV_MSCPUNIT(R3)	;  representation.
	PUSHL	R4			; Save R4 for a second (X-66)
	MOVL	UCB$L_DDB(R3),R4	; Get DDB address
	MOVL	DDB$L_ALLOCLS(R4), -	; Copy allocation class to UCB for
		UCB$Q_UNIT_ID(R3)	;  eventual use by DUTU$FIND_DDB
	POPL	R4			; Restore R4
	RSB				; Return.

16$:	MOVW	UCB$W_LCL_MSCPUNIT(R3),-; For non-server, reload the MSCP unit
		UCB$W_MSCPUNIT(R3)	;  from the local unit representation.

18$:	RSB				; And return.
;
; At this point, it is known that a unit move operation is required.
;
20$:	JSB	15$			; Update SLUN fields if necessary.

	PUSHL	R5			; Save CDRP address.
	MOVL	R3, R5			; Copy UCB address to R5.
	MOVL	R2, R3			; Copy target CDDB address to R3.
	ADDL3	#<CDDB$L_UCBCHAIN -	; Initialize previous UCB
		 -UCB$L_CDDB_LINK>, -	; address.
		UCB$L_CDDB(R5), R0

30$:	MOVL	R0, R1			; Save previous UCB address.
	MOVL	UCB$L_CDDB_LINK(R1), R0 ; Link to next UCB.
	CMPL	R0, R5			; Is this the severed UCB?
	BNEQ	30$			; Branch if not found right UCB.
	MOVL	UCB$L_CDDB_LINK(R5), -	; Make previous UCB point to
		UCB$L_CDDB_LINK(R1)	; UCB after severed UCB.
	MOVL	UCB$L_CDDB(R5), -	; Make the previous primary CDDB the
		UCB$L_2P_CDDB(R5)	; new secondary CDDB.

	BSBW	DUTU$INIT_CONN_UCB	; Adjust connection dep. UCB fields.
;
; Adjust UCB$V_MSCP_WAITBMP and UCB$W_RWAITCNT to reflect any change
;
; The following table describes the needed adjustment:
;
; WAITBMP  RWAITCNT	RSTRTWAIT	WAITBMP	 RWAITCNT  RWAITCNT
;   old	     old	new CDDB	  new	   new	  adjustment
;
;    0	       0	    0		   0	    0	       0
;    0	       0	    1		   1	   +1	      +1
;    1	      +1	    0		   0	    0	      -1
;    1	      +1	    1		   1	   +1	       0
;
; Start by assuming new CDDB$V_RSTRTWAIT is set and backout the
; adjustments if it is not.
;
	BBSS	#UCB$V_MSCP_WAITBMP, -	; Assume that a reconnect is in
		UCB$L_DEVSTS(R5), 125$	; progress and alter wait count
	INCW	UCB$W_RWAITCNT(R5)	; & bumped bit accordingly.

125$:	BBS	#CDDB$V_RECONNECT, -	; If reconnect in progress,
		CDDB$L_STATUS(R3), 130$ ; skip wait count re adjustment.
	DECW	UCB$W_RWAITCNT(R5)	; Else, decrement bumped count
	BICW	#UCB$M_MSCP_WAITBMP, -	; and clear wait count bumped bit.
		UCB$L_DEVSTS(R5)

130$:	MOVL	CDDB$L_CRB(R3), -	; Setup new CRB address in UCB.
		UCB$L_CRB(R5)

	BSBW	DUTU$LINK_UCB2CDDB	; Link UCB into new CDDB chain.

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

	BISW	#UCB$M_MSCP_FLOVR, -	; Set UCB failed-over flag.
		UCB$L_DEVSTS(R5)
	ADDL3	#CDDB$L_RSTRTQFL, -	; Get address of failed CDDB
		UCB$L_2P_CDDB(R5), R1	; restart queue header.
	ASSUME	CDRP$L_FQFL EQ 0	; Now form "previous" entry
	MOVL	R1, R0			; address for queue scan.
	MOVAL	UCB$L_IOQFL(R5), R2	; Init prev. entry for INSQUE.

150$:	MOVL	CDRP$L_FQFL(R0), R0	; Link to next restart CDRP.
	CMPL	R0, R1			; Is this the end?
	BEQL	170$			; Branch if this is the end.
	CMPL	CDRP$L_UCB(R0), R5	; Is this CDRP for this UCB?
	BNEQ	150$			; Branch if not our CDRP.
	REMQUE	(R0), R0		; Else, de-queue the CDRP.
	INSQUE	CDRP$L_IOQFL(R0), (R2)	; Add IRP to UCB I/O queue.
	MOVAL	CDRP$L_IOQFL(R0), R2	; Make this IRP predecessor
					; to the next.
	BRW	150$			; Go look for another CDRP.

170$:	MOVL	UCB$L_PDT(R5), R4	; Get "new" PDT address.
	POPL	R2			; Restore CDRP address.
	BEQL	180$			; Branch if no CDRP input.
	MOVL	UCB$L_CDT(R5), -	; Get "new" CDT address in
		CDRP$L_CDT(R2)		; the CDRP.

180$:	MOVL	UCB$L_CDDB(R5), R3	; Get the CDDB address
	BBS	#CDDB$V_2PBSY, -	; Branch if I/O database update
		CDDB$L_STATUS(R3), 190$ ; fork thread is already active.
	PUSHR	#^M<R0,R1,R2,R3,R4,R5>	; Save all registers
	MOVAL	CDDB$A_FKB2P(R3), R5	; Set up fork block address

	BSBW	DUTU$MOVE_IODB		; Move the UCB

	POPR	#^M<R0,R1,R2,R3,R4,R5>	; Restore saved registers

190$:	MOVL	R5, R3			; Put the UCB aaddress,
	MOVL	R2, R5			;  and the passed CDRP address, back
	MOVL	#SS$_NORMAL, R0		; Indicate success

EXIT_MOVE_UNIT:

	RSB				; Return to caller



	.SBTTL	DUTU$MOVE_IODB - Update DDB I/O database after a unit moves
;++
;
;	DUTU$MOVE_IODB - Update DDB I/O database after a unit moves
;
; Functional Description:
;
;	Since changes in the DDB - UCB chains in the I/O database must wait
;	until the I/O database mutex is unowned and since such changes are not
;	required for performing I/O requests by the class drivers, the I/O
;	database update is performed (and maybe postponed) in this fork
;	routine.  This routine uses a one-per-CDDB fork block.	Therefore, it
;	processes all UCBs connected to a CDDB.	 Since new unit movements can
;	occur while this routine is forked, all UCBs must be scaned during
;	every pass of the the main loop.  This routine is not finished until a
;	complete pass through all UCBs fails to locate a UCB in need of work.
;
;	For each UCB needing DDB changes, the correct primary DDB is located
;	and verified, the UCB is unlinked from the old primary chain, and
;	linked into the new primary chain.  This process is repeated for the
;	secondary DDB chain.  N.B. verification of a located DDB is required
;	because the process of locating a DDB forks and while the locate-DDB
;	thread relinquishes the CPU the unit may be moved again.
;
; Inputs:
;
;	R3	CDDB address
;	R5	Failover (2P) fork block address
;
; Implicit Inputs:
;
;	For each UCB chained to this CDDB
;
;	UCB$L_DEVSTS	UCB$V_MSCP_FLOVR set if UCB has been moved
;	UCB$L_CDDB	address of primary CDDB
;	UCB$L_2P_CDDB	address of seconcary CDDB
;
;--

DUTU$MOVE_IODB::

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

	BISL	#CDDB$M_2PBSY, -	; Set fork block busy flag.
		CDDB$L_STATUS(R3)

10$:	MOVAB	<CDDB$L_UCBCHAIN -	; Initialize "previous" UCB address.
		-UCB$L_CDDB_LINK>(R3), R2

20$:	MOVL	UCB$L_CDDB_LINK(R2), R2 ; Link to next UCB.
	BEQL	EXIT_MOVE_IODB		; Got one!
	BBC	#UCB$V_MSCP_FLOVR, -	; If UCB need not be moved,
		UCB$L_DEVSTS(R2), 20$	;  move on to the next UCB.
	MOVL	R2, FKB2P$L_SAVD_UCB(R5); Restore UCB address.
;
; Move primary path DDB links for located UCB, as necessary.
;
30$:	MOVL	UCB$L_CDDB(R2), R3	; Get primary path CDDB address.

31$:	MOVL	R3, R4			; Move CDDB to R4 for FIND_DDB
	ADDL3	#DDB$T_NAME, -		; Setup device name address string.
		UCB$L_DDB(R2), R3	;
	MOVAB	33$, R2			; Pass completion routine addr
	CLRL	R0			; IODB mutex is not currently held

	BSBW	DUTU$FIND_DDB		; Find or create the DDB.

	RSB

;
; Inputs:
;
;	R4	DDB  address
;	R5	Failover (2P) fork block address
;

33$:	.JSB_ENTRY INPUT=   <		 R4,R5>,-
		   OUTPUT=  <                 >,-
		   SCRATCH= <>,-
		   PRESERVE=<                 >

	MOVL	FKB2P$L_SAVD_UCB(R5),R2 ; Restore UCB address.
;
; Since DUTU$FIND_DDB may have forked and the primary path changed
; during that fork, the DDB just located must be validated as a DDB
; associated with the CURRENT primary path.
;
	MOVL	UCB$L_CDDB(R2), R3	; Get current primary CDDB address

	BSBW	DUTU$CHECK_DDB_FOR_CDDB ; Determine if DDB is still correct.
	BLBS	R0, 35$			; If so, continue.

	UNLOCK_IODB			; Else, release I/O database mutex

	BRW	31$			;  and try again.

35$:	CMPL	R4, UCB$L_DDB(R2)	; Is correct DDB already primary?
	BEQL	40$			; Branch if correct DDB already setup.
;
; The primary path DDB links for this UCB must be altered.  However,
; the DUTU$FIND_DDB call above has guaranteed write access to the
; I/O database; so no further waiting is necessary.
;
	PUSHL	R5			; Save the 2P fork block address.
	MOVL	R2, R5			; Copy the UCB address for unlinking.

	CALL_SEVER_UCB			; Unlink UCB from primary DDB chain.

	MOVL	R4, UCB$L_DDB(R5)	; Establish new primary DDB.

	CALL_LINK_UCB INTERFACE_WARNING=NO; Relink to primary DDB chain.
	BLBC	R0, 997$		; Branch if relinking error.

	POPL	R5			; Restore the 2P fork block address.
;
; Move secondary path DDB links for located UCB, as necessary.
;
40$:	MOVL	UCB$L_2P_CDDB(R2), R3	; Get secondary path CDDB address.
	BEQL	70$			; Branch if no secondary path.
					; Retain I/O database mutex.
45$:	MOVL	R3, R4			; Move CDDB to R4 for FIND_DDB
	ADDL3	#DDB$T_NAME, -		; Setup device name address string for
		UCB$L_DDB(R2), R3	; DUTU$FIND_DDB.
	MOVAB	46$, R2			; Set up return address
	MOVL	#DUTU$K_MUTEX_HELD, R0	; IODB mutex is already held

	BSBW	DUTU$FIND_DDB		; Find right DDB on this CDDB for UCB.

	RSB

;
; Inputs:
;
;	R4	DDB  address
;	R5	Failover (2P) fork block address
;

46$:	.JSB_ENTRY INPUT=   <		 R4,R5>,-
		   OUTPUT=  <                 >,-
		   SCRATCH= <>,-
		   PRESERVE=<                 >

	MOVL	FKB2P$L_SAVD_UCB(R5), R2; Restore UCB address.
;
; Since DUTU$FIND_DDB may have forked and the secondary path changed
; during that fork, the DDB just located must be validated as a DDB
; associated with the CURRENT secondary path.
;
	MOVL	UCB$L_2P_CDDB(R2), R3	; Get current secondary CDDB address.

	BSBW	DUTU$CHECK_DDB_FOR_CDDB ; Determine if DDB is still correct.
	BLBC	R0, 45$			; If not, try again.

	CMPL	R4, UCB$L_2P_DDB(R2)	; Is correct DDB already secondary?
	BEQL	50$			; Branch if correct DDB already setup.
;
; The secondary path DDB links for this UCB must be altered.  However,
; the DUTU$FIND_DDB call above has guaranteed write access to the
; I/O database; so no further waiting is necessary.
;
	PUSHL	R5			; Save the 2P fork block address.
	MOVL	R2, R5			; Copy the UCB address.
	BBC	#DEV$V_2P, -		; Branch if UCB not previously
		UCB$L_DEVCHAR2(R5), 48$ ; dual pathed.

	BSBW	DUTU$SEVER_SEC_UCB	; Else, undo previous linking.

48$:	MOVL	R4, UCB$L_2P_DDB(R5)	; Establish new primary DDB.

	BSBW	DUTU$LINK_SEC_UCB	; Relink to secondary DDB chain.
	BLBC	R0, 997$		; Branch if relinking error.

	BISL	#DEV$M_2P, -		; Ensure that dual pathed device
		UCB$L_DEVCHAR2(R5)	; flag is set.
	POPL	R5			; Restore the 2P fork block address.
;
; Final validation
;
;	DUTU$FIND_DDB was called to get a secondary path DDB. Since that
;	routine may have forked, the primary path DDB and I/O database
;	links may now be incorrect.  Therefore, they must be validated one
;	more time.  If this validation succeeds, this UCB has been completely
;	and correctly moved and UCB$V_MSCP_FLOVR can be cleared.  If the
;	validation fails, the UCB move operations must be restarted from
;	the top.
;
50$:	MOVL	UCB$L_DDB(R2), R4	; Get primary DDB address.
	MOVL	UCB$L_CDDB(R2), R3	; Get current primary CDDB address.

	BSBW	DUTU$CHECK_DDB_FOR_CDDB ; Determine if DDB is still correct.
	BLBS	R0, 70$			; If still fine, move to next UCB.

	UNLOCK_IODB			; Release I/O database mutex.

	BRW	30$			;  and try again
;
; Mark this UCB done, and restart UCB search
;
;	The UCB search is restarted with the first UCB owned by the CDDB
;	associated with the 2P fork block because any of the UCBs previously
;	skipped may now need attention.	 This routine could have forked
;	multiple times since the UCB just processed was selected.
;
70$:	BICL	#UCB$M_MSCP_FLOVR, -		; Clear UCB moved flag.
		UCB$L_DEVSTS(R2)		;
	MOVL	FKB2P$L_SAVD_CDDB(R5), R3	; Get pointer to CDDB

	UNLOCK_IODB				; Release I/O database mutex.

	BRW	10$				; Begin UCB search again

997$:	BUG_CHECK INCONSTATE, FATAL

EXIT_MOVE_IODB:

	BICL	#CDDB$M_2PBSY, -		; Clear fork block busy flag.
		CDDB$L_STATUS(R3)
	RSB					; End Fork thread



	.SBTTL	----- NEW UNIT ROUTINES -----
	.SBTTL	DUTU$NEW_UNIT - Process a possible new unit
;++
;
;	DUTU$NEW_UNIT - Process a possible new unit
;
; Functional Description:
;
;	This routine processes unit available attention messages, get unit
;	status end messages, or unit lookup requests from the IO$_CRESHAD FDT
;	routine.  If the I/O database does not reflect the presence of the
;	unit described in the "MSCP message", the new unit is added to the I/O
;	database in whatever manner is appropriate.  Several conditions can
;	prohibit adding the new unit:
;
;	 1) the unit number is 4095.  This is bogus unit number sometimes
;	    generated by the HSC.
;
;	 2) the unit is a shadow set virtual unit and the driver is linked in
;	    a way that does not support volume shadowing.  This test is intended
;	    to allow mixed clusters where some nodes perform volume shadowing
;	    and others do not.
;
;	 3) there is insufficient non-paged pool to accommodate the UCB.
;
;	 4) if various discrepancies appear within the message fields,
;	    especially when the controller is an emulator (VMS MSCPserver).
;	    Notably, if the controller is an emulator and the SLUN bit is not
;	    set, or if the controller is not an emulator and the SLUN bit is
;	    set, then a major error would occur if the message would be used
;	    verbatim.  Another case is invalid D0/D1 fields in the media id.
;
;	 5) the message does not contain enough fields to build a UCB.
;
;	Since 99.44% of the time the message validation should succeed, the
;	code to validate the message is optimized for speed (within reason).
;
;	Once the message is validated, a check is made to determine whether
;	the unit is already known to the I/O database.	The unit can be either
;	on a primary or secondary path.	 If an already existing UCB can be
;	located, its address is returned.  In addition, if the UCB was on
;	the primary path, then the MSCP unit, media id, and VMS device type
;	fields are updated if necessary; while if the UCB was on a secondary
;	path, the CDDB controller letter mask for the controller which issued
;	the message is updated in case no other units have previously been
;	found on that controller.
;
;	If the unit belongs on a secondary path and is not a shadow set
;	virtual unit, the appropriate secondary path links are setup.
;	If no primary or secondary path location of the unit is found,
;	a new UCB is allocated.
;
;	Upon successful allocation and set up by IOC$COPY_UCB, this routine
;	fills in a few more fields in the new UCB.  The most important fields
;	are those which are copies of the information in the MSCP message.
;	This is the last opportunity in the UCB creation process to reference
;	the MSCP message.  Once all the useful information has been copied
;	from the MSCP message to the new UCB, a fork thread is started to
;	complete initialization of the UCB and link it into the I/O database.
;
;	Whether or not it has been completely set up, the new UCB address is
;	returned in R2.
;
; Inputs:
;
;	R1	size of the MSCP message
;	R2	base address of the MSCP message
;	R3	CDDB address
;	R5	CDRP address
;	IPL	IPL$_SCS
;
; Implicit Inputs:
;
;	Template UCB		in .PSECT $$$200_TEMPLATE_UCB_01
;	Template ORB		DEVICE.DISK in [SYS]DEVICE_OBJECT
;	DUTU$L_CDDB_LISTHEAD	location containing the system virtual address
;				of the CDDB listhead for this device class
;
; Outputs:
;
;	R0	Status
;		    SS$_NORMAL means a UCB was located or created
;		    SS$_IVDEVNAM means no action was taken
;		    SS$_INSFMEM means there was no memory for a new UCB
;	R2	UCB address for unit represented in the MSCP message
;		    (for either a new or already existing UCB),
;		    or zero if R0 indicates failure.
;
;	R3,R4,R5 Preserved.
;
;
;
; WARNINGS:
;
;	The UCB address returned by this routine may or may not be correctly
;	linked into the I/O database.  The linking operation is performed in a
;	separate fork thread (which uses the UCB as a fork block).  This fork
;	thread can become stalled due to lack of memory needed for creation of
;	a DDB, due to lack of write access to the I/O database, or possibly
;	some other reason.  It is even possible that future events will cause
;	some other UCB (now floating around in limbo somewhere) to be used as
;	the UCB for the unit described in the inputs.
;
;	Therefore, the following rules must be observed:
;
;	   1.	If upon return from DUTU$NEW_UNIT, UCB$V_MSCP_INITING is clear
;		in the UCB pointed to be R2, then all is well and that UCB is
;		a permanent fixture in the I/O system.
;
;	   2.	Otherwise, do not "remember" the UCB address returned in R2
;		beyond a fork or other wait condition.	If a correct UCB
;		address is needed after a wait, call DUTU$NEW_UNIT again.
;
; FORKING CONSIDERATIONS:
;
;	The UCB is currently used as a fork block by the class drivers in
;	three different instances:
;
;	   1.	Creation of the CDDB when the controller initialization
;		routine is called.
;	   2.	For the fork thread which links a new UCB into the I/O
;		database.
;	   3.	For the fork thread which links the secondary path threads to
;		a UCB which has been determined to represent a dual pathed
;		device.
;
;	Athough it is not obvious, these three uses cannot be competing to use
;	the same UCB as a fork block concurrently.  Usage 1 occurs only when
;	no connection to the MSCP server exists.  At that time, no MSCP
;	messages can be received.  Therefore, usage 1 precludes usages 2 and 3.
;	While the fork block is in type 2 usage, the UCB is unknown to the
;	I/O database.  Therefore, it cannot be detected and put into use by a
;	type 3 fork thread.  Thus usages 2 and 3 are mutually exclusive.
;	The UCB$L_FQFL field acts as a busy bit for multiple type 3 usages:
;	  If it is zero, the fork block is available.
;	  If it is -1, the fork thread is currently active.
;	  Otherwise, it should be a valid queue link to a wait queue
;	    (typically fork'n'wait) for a stalled fork thread.
;
;	There is a race condition (which the LINK_NEW_UCB fork thread must
;	detect).  This race results from a stalled new UCB thread preventing
;	detection of a dual path condition.  Before actually linking a new UCB
;	into the I/O database but after all possible stall conditions have
;	been dealt with, the new UCB fork thread must make one last attempt to
;	dual path link the new UCB.
;
;--

;
;  Locate the template UCB for this class driver.
;
	.SAVE

	DRIVER_DATA	$$$200_TEMPLATE_UCB_00

DUTU$TEMPLATE_UCB::

	.RESTORE

	.ENABLE LSB
;
;  Validation for non-emulated (local) controller message.
;
10$:	CMPW	#4095, R0		; HSCs may spuriously generate unit 4095
	BNEQ	50$			;  on some occasions.  Ignore them.
;
;  Invalid device name, unit number, message size, or some other bogosity.
;
20$:	MOVZWL	#SS$_IVDEVNAM, R0	; Indicate failure status.
	CLRL	R2			; Show no UCB address found.
	RSB				; And return.

30$:	BUG_CHECK MSCPCLASS, FATAL	; Bogus message from remote server.
;
;  Validate incoming message fields before doing new unit processing.
;
DUTU$NEW_UNIT::

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

	BBS	#CDDB$V_DISABLED, -	; Do we like this controller?
		CDDB$L_STATUS(R3), 20$	; Ignore if in disabled state.
	CMPW	#MSCP$L_MEDIA_ID+4, R1	; Is the message large enough to hold
	BGTRU	20$			;  all needed data?  Branch if not.
	MOVZWL	MSCP$W_UNIT(R2), R0	; Get MSCP unit number into a register.
	BBS	#MSCP$V_SHADOW, R0, 20$ ; Branch if shadow set.
	CMPB	CDDB$B_CNTRLMDL(R3), -	; Further validation depends on the
		#MSCP$K_CM_EMULA	;  controller model type.
	BNEQ	10$			; Branch if not the VMS MSCPserver.
;
;  Validate VMS MSCPserver (emulator) message.
;
	CMPW	#MSCP$K_SLUN_RSVP, R0	; Is this the magic slun rsvp unit?
	BEQL	30$			; It isn't supposed to be.
	BICW	#^C<MSCP$M_SHADOW -	; Throw away everything except the
		   !MSCP$M_SLUN>, R0	;  shadow and slun bits.
	CMPW	#MSCP$M_SLUN, R0	; Only slun bit should be set.
	BNEQ	30$			; Get upset if these bits are wrong.
	ASSUME	MSCP$V_SLUN_C EQ 0
	ASSUME	MSCP$S_SLUN_C LE 8
	BICB3	#^c<MSCP$M_SLUN_C>, -	   ; Extract the controller letter from
		MSCP$W_SLUN_DEVNAME(R2), - ;  the SLUN field.  Must be between
		R0			   ;  1-26 (A-Z) inclusive.
	BEQL	30$			; Letter must be present.
	CMPB	#26, R0			; Is the letter bigger than Z?
	BLSSU	30$			; Don't believe it if so.
	CMPB	#^a/S/-^a/A/+1, R0	; Is the controller letter S?
	BNEQ	40$			; Branch if not.
	EXTZV	#MSCP$V_MTYP_D1, -	; Get the D1 device type letter from
		#MSCP$S_MTYP_D1, -	;  media_id field of the end message
		MSCP$L_MEDIA_ID(R2), R0 ;  and check device type.
	CMPL	R0, #^a/U/-^a/@/	; Does it match the U in DU as in DUS
	BEQL	20$			; It is DUS device..don't configure.
	CMPL	R0, #^a/J/-^a/@/	; Does it match the J in DJ as in DJS
	BEQL	20$			; It is DJS device..don't configure.

40$:	MOVZWL	MSCP$W_SLUN_UNIT(R2),R0 ; Set up actual drive unit number.
;
;  Finish common validation independent of controller model.
;
50$:	CMPW	#9999, R0		; Is the drive unit (plug number) ok?
	BLSSU	20$			; Branch if unit number too big.
	ASSUME	MSCP$V_MTYP_D1 GE 16
	BICW3	#^c<MSCP$M_MTYP_D1 @ -16>, -	; Extract D1 media id field,
		MSCP$L_MEDIA_ID+2(R2), R0	;  must be between A-Z.
	BEQL	20$				; If zero, ignore it. (D1 < A)
	CMPW	#<26@<MSCP$V_MTYP_D1 - 16>>, R0 ; Is D1 > Z?
	BLSSU	20$				; Branch if > Z.
	ASSUME	MSCP$V_MTYP_D0 GE 24
	BICB3	#^c<MSCP$M_MTYP_D0 @ -24>, -	; Extract D0 media id field,
		MSCP$L_MEDIA_ID+3(R2), R0	;  must be between A-Z.
	BEQL	20$				; If zero, ignore it. (D0 < A)
	CMPB	#<26@<MSCP$V_MTYP_D0 - 24>>, R0 ; Is D0 > Z?
	BLSSU	20$				; Branch if > Z.
;
;  "Candidate passes!"
;		- from "The Court Jester", starring Danny Kaye.
;
;  Message has passed validity tests.  Now check if this unit already exists.
;
60$:	MOVL	CDDB$L_UCBCHAIN(R3), R0 ; Get the first UCB to check
	BSBW	DUTU$LOOKUP_UCB		; Do we already know of this unit?
	TSTL	R0			; Unit address or zero returned.
	BNEQ	100$			; Branch if unit already known.
        PUSHR   #^M<R1,R2>              ; Save registers for no dual path case

	BSBW	DUTU$SETUP_DUAL_PATH	; Is or should this unit be dual pathed?

        POPR    #^M<R1,R2>              ; Restore registers
	TSTL	R0			; Was (is) unit dual pathed?
	BEQL	135$			; Branch if unit not dual pathed.
;
;  Dual pathed unit found on another controller.
;  Update controller letter mask according to found UCB.
;
	MOVL	UCB$L_DDB(R0), R2	; Get DDB address for found UCB.
	SUBB3	#^a/A/-1, -		; Get controller letter bit offset
		DDB$T_NAME+3(R2), R1	;  for device.
	ROTL	R1, #1, R1		; Transform bit number into mask.
	BISL	R1, -			; Set bit for controller letter in
		CDDB$L_CTRLTR_MASK(R3)	;  CDDB mask.
	BRW	75$			; Branch to exit
;
; We have received an AVATN for a known unit on its primary path. The unit
; may previously have failed over to another path but in the absence of any
; I/O we didn't notice this. The SSM check covers the
; case where a SSM is physically cabled bewteen a HSC and a local controller.
; N.B., this code assumes TRIGGER MV will check for mntverip,
; invoke server MV, and return the UCB in R0. If TRIGGER changes, so must
; this code.
;
70$:	BBC	#MSCP$V_CF_MLTHS, -	; Ignore shadow set members
		CDDB$W_CNTRLFLGS(R3), 701$; and served paths.
	BBS	#UCB$V_MSCP_IGNSRV, -
		UCB$L_DEVSTS(R0), 75$

701$:	BBS	#DEV$V_SSM, -		;
		UCB$L_DEVCHAR2(R0), 75$	;
	BBS	#DEV$V_MNT, -		; If the device is mounted,
		UCB$L_DEVCHAR(R0), 71$	;  continue checks.
	TSTB	UCB$B_ONLCNT(R0)	; Exit if not online.
	BEQL	75$			;
					; Avoid collisions with other
71$:	TSTB	UCB$B_FAIL_MUTEX(R0)	;  failover threads (TRIGGER will
	BEQL	74$			;  check for mntverip).
	CMPB	#MSCP$K_CM_EMULA,-	; Failover is in progress. Load this
		CDDB$B_CNTRLMDL(R3)	;  new attention code if the message
	BEQL	75$			;  is not from an emulator.

74$:	MOVB	MSCP$B_OPCODE(R2),-	; Save the AVATN code and lock
		UCB$B_FAIL_MUTEX(R0)	;  FEX
	PUSHR	#^M<R0,R3,R5>		; Save UCB, CDDB and CDRP pointers
	MOVL	R0,R5			; R5 => UCB for TRIGGER MV

	BSBW	DUTU$TRIGGER_MV		; preserves R4 

	POPR	#^M<R0,R3,R5>		; Restore registers

75$:	MOVL	R0, R2			; Copy address of previous UCB.

80$:	MOVZWL	#SS$_NORMAL, R0		; Set success status.
	RSB				; Return with UCB address in R2.

;90$:	BUG_CHECK MSCPCLASS, FATAL
;
;  Unit was previously found on this controller (primary path).
;  Update mscpunit, media id, and device type if necessary.
;
100$:	CMPW	#MSCP$K_SLUN_RSVP, -	; Does this unit need to have MSCPUNIT
		UCB$W_MSCPUNIT(R0)	;  recalculated?
	BNEQ	110$			; Branch if MSCPUNIT is ok.
;X-26U6
;	CMPB	#MSCP$K_CM_EMULA, -	; Only VMS MSCPserver should wind up
;		CDDB$B_CNTRLMDL(R3)	;  needing this update for MSCPUNIT.
;	BNEQ	90$			; So stop if this isn't an emulator.
	MOVW	MSCP$W_UNIT(R2), -	; Update MSCPUNIT for found device
		UCB$W_MSCPUNIT(R0)	;  on this emulator path.

110$:	CMPL	MSCP$L_MEDIA_ID(R2), -	; Do the MSCP media identifier fields
		UCB$L_MEDIA_ID(R0)	;  already match?
	BNEQ	112$			; Branch if update necessary.
	CMPB	#DC$_DISK,-		; Otherwise, if device type is generic
		UCB$B_DEVCLASS(R0)	;  must redo GET_DEVTYPE anyway
	BEQL	111$			; Possible a generic disk
	CMPB	#DT$_GENERIC_MK,-	; Is this a generic SCSI tape?
		UCB$B_DEVTYPE(R0)
	BEQL	112$
	CMPB	#DT$_GENERIC_TU,-	; Or a generic TMSCP tape?
		UCB$B_DEVTYPE(R0)
	BNEQ	70$			; No need to update DEVTYPE
	BRB	112$

111$:	CMPB	#DT$_GENERIC_DK,-	; Is this a generic SCSI disk?
		UCB$B_DEVTYPE(R0)
	BEQL	112$
	CMPB	#DT$_GENERIC_DU,-	; Or a generic MSCP disk?
		UCB$B_DEVTYPE(R0)
	BNEQ	70$			; No need to update DEVTYPE

112$:	PUSHR	#^M<R0,R3,R4,R5>	; Save registers.
	MOVL	R0, R3			; Get UCB address in R3.
	MOVL	MSCP$L_MEDIA_ID(R2), -	; Update media id field (D0/D1 were
		UCB$L_MEDIA_ID(R0)	;  a match from DUTU$LOOKUP_UCB).

	BSBW	DUTU$GET_DEVTYPE	; Update devtype too.

        POPR    #^M<R2,R3,R4,R5>        ; Restore registers, move UCB to R2.
	BRW	80$			; Exit with success code.
;
;  Unit not found on primary or secondary path.
;  Attempt to create new UCB.
;
135$:	BSBW	DUTU$FILL_MSCP_MSG	; Zero extend the MSCP message.

        PUSHR   #^M<R3,R4,R5>           ; Save a couple of registers.
	MOVL	R2, R4			; Copy MSCP message address.
	MOVAB	DUTU$TEMPLATE_UCB, R5	; Get template UCB address.
;
; Allocate permanent fork blocks for use when attempting to allocate memory
; for a mount verification IRP and for get unit name.
;
        MOVZWL  #<2*FKB$K_LENGTH>, R1	; Get size of a fork block

        ALLOCATE_POOL			; Allocate a list of fork blocks
	BLBC	R0, 150$		; Branch if create failed

        MOVW    R1, FKB$W_SIZE(R2)      ; Set up size,
        MOVB    #DYN$C_FRK, FKB$B_TYPE(R2);  type and
        MOVB    #SPL$C_SCS, FKB$B_FLCK(R2);  fork IPL of 1st fork block
        MOVL    R2, R3                  ; Save ptr to top of fork block list.
	MOVAB	FKB$K_LENGTH(R3),R2	; Point to 2nd fork block
        MOVW    #FKB$K_LENGTH, FKB$W_SIZE(R2); Set up size,
        MOVB    #DYN$C_FRK, FKB$B_TYPE(R2);  type and
        MOVB    #SPL$C_SCS, FKB$B_FLCK(R2);  fork IPL

	CALL_COPY_UCB			; Make new UCB from template.
	.branch_likely
	BLBS	R0, 138$		; Branch if allocated OK.

	MOVL	R3, R0			; Allocation failed, so 
					; deallocate list of 
	JSB	G^EXE$DEANONPAGED	; fork blocks

	BRW	150$			;  then exit.

138$:	MOVL    R3, UCB$L_DUTUFKBLINK(R2); Save ptr to fork block list in new UCB
        MOVL    (SP), R3                ; Restore CDDB from stack
	MOVW	UCB$W_RWAITCNT(R5), -	; Compensate for unwanted IOC$COPY_UCB
		UCB$W_RWAITCNT(R2)	;  initialization cleared from the
	MOVL	UCB$L_REFC(R5), -	;  prototype UCB:
		UCB$L_REFC(R2)		;   - Restore RWAITCNT.
	MOVL	UCB$L_DEVSTS(R5), -	;   - Restore reference count.
		UCB$L_DEVSTS(R2)	;   - Restore device dependent status.
	MOVL	UCB$L_STS(R5), -	;   - Restore Status field
		UCB$L_STS(R2)		;
	ASSUME	UCB$L_MAXBLOCK EQ -	; Since no I/O will go to this "disk"
		UCB$L_RECORD		; before the first PACKACK, zero the
	CLRL	UCB$L_MAXBLOCK(R2)	; maximum block number (for tapes this
					; harmlessly zeros the frame number).
	MOVL	MSCP$L_MEDIA_ID(R4), -	; Initialize fields required by the
		UCB$L_MEDIA_ID(R2)	;  final UCB setup fork thread to
	MOVQ	MSCP$Q_UNIT_ID(R4), -	;  link new UCB into I/O database:
		UCB$Q_UNIT_ID(R2)	;   - MSCP media identification.
	MOVW	MSCP$W_UNIT(R4), -	;   - MSCP unique unit identifier.
		UCB$W_MSCPUNIT(R2)	;   - MSCP unit number.
;
;  Check UF_REPLC and set the NOFE state in UCB accordingly.
;
	CMPB	CDDB$B_CNTRLMDL(R3),-	; Further validation depends on the
		#MSCP$K_CM_EMULA	;  controller model type.
	BNEQ	124$			;
	CMPB	#DC$_TAPE, -		; Is this a tape?
		UCB$B_DEVCLASS(R2)	;
	BEQL	123$			; Branch if a tape.
	BICL	#DEV$M_NOFE,-		; Assume success and
		UCB$L_DEVCHAR2(R2)	;  set bits for bad block
	BISW	#MSCP$M_UF_REPLC,-	;  replacement.
		UCB$W_UNIT_FLAGS(R2)	;
	BBS	#MSCP$V_UF_REPLC,-	; Does it support Forced Error.
		MSCP$W_UNT_FLGS(R4),124$;
123$:	BISL	#DEV$M_NOFE,-		; If not, set the NO Forced Error bit.
		UCB$L_DEVCHAR2(R2)
;
; Here for tape devices, we try to copy the FORMAT MENU from the MSCP message
; into the UCB.	 Note, if the MSCP message happens to be an End Message from
; a Get Unit Status, then we copy the correct information.  If the MSCP message
; happens to be an available attention message, then we copy zeros.
;
124$:	CMPB	#DC$_TAPE, -			; Is this a tape?
		UCB$B_DEVCLASS(R2)		;
	BNEQ	125$				; Branch if not a tape.
	MOVW	MSCP$W_FORMENU(R4),-		; Copy supported format / densities.
		UCB$W_TU_FORMENU(R2)		;  to UCB.
	EXTZV	#MSCP$V_MTYP_D0, -		; Get the D0 device type letter from the
		#MSCP$S_MTYP_D0, -		;  media_id field of the end message.
		MSCP$L_MEDIA_ID(R4), R0		;  Does it match the M in MK for
	CMPL	R0, #^a/M/-^a/@/		;  SCSI tape device type name?
	BNEQ	125$				; Branch if not SCSI device
	EXTZV	#MSCP$V_MTYP_D1, -		; Get the D1 device type letter from the
		#MSCP$S_MTYP_D1, -		;  media_id field of the end message.
		MSCP$L_MEDIA_ID(R4), R0		;  Does it match the K in MK for
	CMPL	R0, #^a/K/-^a/@/		;  SCSI tape device type name?
	BNEQ	125$				; NEQ implies no, so branch around.
	BICL	#<MT2$M_COMP_SUP! -		; Assume compaction is not
		MT2$M_COMP_ENA>, -		;  supported or enabled.
		UCB$L_DEVDEPND2(R2)		;
	CMPW	UCB$W_TU_FORMENU(R2), -		; Check if compaction is
		#<MSCP$K_TC_DAT!-		;  supported.
		MSCP$M_TF_NOR!MSCP$M_TF_DCP>	;
	BNEQ	125$				; Branch around if not.
	BISL	#<MT2$M_COMP_SUP>, -		; Set COMP SUP bit
		UCB$L_DEVDEPND2(R2)		;  in DEVDPEND2.
;
; Start a fork thread to complete setup and linking of the new UCB into the I/O
; database.
;          
125$:	PREPARE_TO_FORK -
		DUTU$LINK_NEW_UCB, -	;
		FRKBLK = (R2), -	; Fork on UCB
		MASK = <^M<R2,R3>>	; Only save R2, R3

        POPR    #^M<R3,R4,R5>           ; Restore saved registers.
	MOVZWL	#SS$_NORMAL, R0		; Set success status.
	RSB				; Return with UCB address in R2.
;
;  Insufficient memory error while creating new UCB.
;
150$:   CLRL    R2                      ; Indicate new UCB cannot be created.
        POPR    #^M<R3,R4,R5>           ; Restore saved registers.
        MOVZWL  #SS$_INSFMEM, R0        ; Set return status
        RSB                             ; Exit without creating new UCB.

	.DISABLE LSB


	.SBTTL	DUTU$LINK_NEW_UCB - Completes Initialization of a New UCB
;++
;
;	DUTU$LINK_NEW_UCB - Complete Initialization of a New UCB.
;
; Functional Description:
;
;	Acting in the context of an independent fork thread and using the UCB
;	as a fork block this routine completes initialization of a new UCB and
;	establishes the primary path linkage for that UCB.
;
;	The various backpointer fields in the UCB are filled in.  Previously
;	uninitialized queue headers are initialized.  Where appropriate, state
;	bits are altered.  If possible, a device type is determined.  The name
;	of the device (DDCn form) is established.  The controller letter (ddCu)
;	is represented in a bitmask in the CDDB for use in failover controller
;	selection.
;
;	A suitable DDB is located or created.  Then write access to the I/O
;	database is obtained.  Either one or both of these operations may
;	cause the thread to fork.
;
;	After completing those operations which can fork, a final check for
;	a dual path device is made.  This eliminates a possible race between
;	two new UCB creations for what is actually a dual pathed device.  If a
;	dual path condition is found, the already existing I/O database is
;	altered to reflect the dual path device and the UCB used as a fork
;	block for this thread (and the thread itself) are discarded.
;
;	If the dual path lookup fails, the new UCB is linked into the I/O and
;	class driver databases and some connection dependent fields are
;	initialized to reflect the current connection to the actual device.
;
; Inputs:
;
;	R3	CDDB address
;	R5	UCB address (also used as a fork block)
;	fork context at IPL$_SCS
;
; Implicit Inputs:
;
;	UCB$W_MSCPUNIT(R5)	MSCP unit number for device
;	UCB$L_MEDIA_ID(R5)	MSCP media identification for device
;	UCB$Q_UNIT_ID(R5)	MSCP unique identifier (VMS MSCP emulator
;					passes device name information here)
;
; Outputs: None.
;
; Implicit Outputs:
;
;    Backpointers:
;	UCB$L_CRB(R5)		CRB address
;	UCB$L_DDT(R5)		DDT address
;
;    I/O Database and Class Driver Linkages:
;	UCB$W_UNIT(R5)		I/O database unit number
;	UCB$L_DDB(R5)		DDB address
;	UCB$L_LINK(R5)		Link to next UCB in DDB chain
;	UCB$L_CDDB(R5)		CDDB address
;	UCB$L_CDDB_LINK(R5)	Link to next UCB in CDDB chain
;
;    Connection Dependent:
;	UCB$L_CDT(R5)		CDT address
;	UCB$L_PDT(R5)		PDT address
;
;    Other:
;	UCB$L_DEVSTS(R5)	UCB$M_MSCP_INITING cleared
;	UCB$L_MAXBCNT(R5)	copied from PDT$L_MAXBCNT(pdt)
;	CDDB$L_CTRLTR_MASK(R3)	bit corresponding to controller letter is set
;
;	if CDDB is not initializing or reconnecting:
;	  UCB$L_DEVSTS(R5)	UCB$M_MSCP_WAITBMP cleared
;	  UCB$W_RWAITCNT(R5)	decremented
;
; WARNINGS:
;
;	The CDDB$L_DDB in this CDDB must point to at least one valid DDB for
;	the devices which are (could be) accessed via that CDDB.  The major
;	implication here is that we can never discard the DU or TU DDB handed
;	us by SYSGEN, et. al. when calling the driver at its controller
;	initialization entry point.
;
;--

DUTU$LINK_NEW_UCB::

	.JSB_ENTRY INPUT=   <	      R3,   R5>,-
		   OUTPUT=  <	      R3,   R5>,-
		   SCRATCH= <R0,R1,R2,	 R4   >,-
		   PRESERVE=<>
;
; Guarantee sufficient space for device name formation.
;
	ASSUME	DDB$S_NAME EQ 16
	ASSUME	UCB$L_CDT	EQ <UCB$L_CDDB_LINK+4>
	ASSUME	UCB$L_WAIT_CDDB EQ <UCB$L_CDDB_LINK+8>
	ASSUME	UCB$L_PREF_CDDB EQ <UCB$L_CDDB_LINK+12>
;
; Check the 2PBSY bit to see if an I/O database update is in progress on this
; CDDB. MOVE_IODB may fork and until it is complete, DDB->UCB linkages may
; not be correct. The IOC$ routines we call later in this thread rely upon
; these linkages.
;
5$:	BBC	#CDDB$V_2PBSY, -		; Skip if I/O database update
		CDDB$L_STATUS(R3), 10$		; fork thread is not active.

	FORK_WAIT

	BRB	5$

10$:	BSBW	DUTU$INIT_CONN_UCB		; Initialize connection
						; dependent fields.
	MOVL	CDDB$L_DDB(R3), R4		; Get beginning DDB for scan.
	PUSHR	#^M<R3>				; save CDDB
	MOVAB	UCB$L_CDDB_LINK(R5), R3		; Begin device name setup.

	BSBW	DUTU$GET_DEVNAM			; Determine device name & unit.

	MOVAB	LN_AFTER_FIND_DDB, R2		; Pass completion routine addr
	CLRL	R0				; Indicate IODB Lock not held.
	POPR	#^M<R4>				; restore CDDB to R4 for FIND_DDB

	BSBW	DUTU$FIND_DDB			; Find or create the DDB.

	RSB
;
; Inputs:
;
;	R4	DDB  address
;	R5	UCB  address (preserved)
;
LN_AFTER_FIND_DDB:

	.JSB_ENTRY INPUT=   <		 R4,R5>,-
		   OUTPUT=  <                 >,-
		   SCRATCH= <>,-
		   PRESERVE=<                 >
	.enabl lsb
	MOVL	R4, UCB$L_DDB(R5)		; Save DDB address.
	CLRQ	UCB$L_CDDB_LINK(R5)		; Cleanup after name builder.
	CLRQ	UCB$L_CDDB_LINK+8(R5)		; Finish cleanup.
;
; Set the SCSI bit in DEVCHAR2 if DDB has DK in it.
;
	CMPW	DDB$T_NAME_STR(R4),#^A/DK/	; Is it a SCSI device?
	BNEQ	5$				; Skip if not.
	BISL	#DEV$M_SCSI,-			; Otherwise set the SCSI bit.
		UCB$L_DEVCHAR2(R5)
	BISL	#DEV$M_NLT, -			; also set not last track to
		UCB$L_DEVCHAR2(R5)		; prevent INIT from looking for
                                                ;  bad block data.
;
; Set the SCSI bit in DEVCHAR2 if DDB has MK in it.
;
5$:	CMPW	DDB$T_NAME_STR(R4),#^A/MK/	; Is it a SCSI tape?
	BNEQ	6$				; Skip if not.
	BISL	#DEV$M_SCSI,-			; Otherwise set the SCSI bit.
		UCB$L_DEVCHAR2(R5)
;
; Set the NLT bit in DEVCHAR2 if DDB has DR in it. This check ensures
;  the device is an ASTRO/PLUTO RAID disk and not a MASSBUS device (ie. RM),
;  which will also be configured as a DR device.
;
6$:	CMPW	DDB$T_NAME_STR(R4),#^A/DR/	; Is it a DR device?
	BNEQ	7$				; Skip if not.
	MOVL	DDB$L_SB(R4),R3			; Get the SB address
	CMPL	SB$T_HWTYPE(R3),#^A/ALPH/	; Check for ALPHA server
	BNEQ	7$				; Skip if not.
	BISL	#DEV$M_NLT, -			; Set Not Last Track to
		UCB$L_DEVCHAR2(R5)		;  prevent INIT from looking for
						;  bad block data.
;
; The DUTU$FIND_DDB call above has acquired write access to the I/O database.
; Set the bit corresponding to the controller letter in the CDDB's mask.
;
7$:	MOVL	UCB$L_CDDB(R5), R3		; Get CDDB address.
	SUBB3	#^a/A/-1, DDB$T_NAME+3(R4), R1	; Get controller letter bit no.
	ROTL	R1, #1, R1			; Transform bit number to mask.
	BISL	R1, CDDB$L_CTRLTR_MASK(R3)	; Set bit in CDDB mask.
;
; The following hack allows DUTU$SETUP_DUAL_PATH to be used for a last chance
; dual path lookup.  A phony unit available "message" is built on the stack.
;
	ASSUME	MSCP$W_UNIT+2	  LE  MSCP$L_MEDIA_ID
	ASSUME	MSCP$Q_UNIT_ID+8  LE  MSCP$L_MEDIA_ID
	MOVL	#MSCP$L_MEDIA_ID+4, R1		; Setup MSCP "msg" size.
	SUBL2	R1, SP				; Allocate MSCP "msg" on stack.
	MOVL	SP, R2				; Setup MSCP "msg" address.
	MOVW	UCB$W_MSCPUNIT(R5), -		; Fill in unit number.
		MSCP$W_UNIT(R2)
	MOVQ	UCB$Q_UNIT_ID(R5), -		; Fill in unique identifier.
		MSCP$Q_UNIT_ID(R2)
	MOVL	UCB$L_MEDIA_ID(R5), -		; Fill in media type id.
		MSCP$L_MEDIA_ID(R2)
;X-26U6
	MOVB	#^X40,MSCP$B_OPCODE(R2)		; Set Opcode type to AVAILABLE

	BSBW	DUTU$SETUP_DUAL_PATH		; Check for dual pathed device.

	ADDL2	#MSCP$L_MEDIA_ID+4, SP		; Deallocate "msg" from stack.
	TSTL	R0				; Was there a dual path?
	BNEQ	700$				;  Branch if we already have a dual path

20$:	PUSHR	#^M<R5>				; Save UCB
	MOVL	R5, R3				; Copy UCB address.

	BSBW	DUTU$GET_DEVTYPE		; Determine device type.

	POPR	#^M<R5>				; Restore UCB address.
	MOVL	UCB$L_CDDB(R5), R3		; Restore CDDB address.
        MOVL    UCB$L_DDB(R5),R4                ; Restore DDB pointer
 	MOVL	DDB$L_DDT(R4), UCB$L_DDT(R5)	; Setup DDT address.
	MOVL	CDDB$L_CRB(R3), UCB$L_CRB(R5)	; Setup CRB address.

	BSBW	DUTU$INIT_CONN_UCB		; Initialize connection
                                                ; dependent fields.
.IF DF IRP$Q_QIO_P1				; If backporting prior to V7.0
	BLBC	SGN$GL_SYSTEM_CHECK,80$		; Branch if monitoring not enabled
	MOVL	#IOCNT$C_LENGTH,R1		; Get Size of IOCNT structure

	ALLOCATE_POOL
	BLBC	R0,80$				; Branch if failed to allocate

	MOVL	R2,UCB$PS_IO_COUNTERS(R5)	; Save pointer to IOCNT structure
.ENDC
80$:	MOVL	UCB$L_PDT(R5), R0		; Get PDT address.
	MOVL	PDT$L_MAXBCNT(R0), R0		; Get port max bytes per xfer.
	CMPL	R0, CDDB$L_MAXBCNT(R3)		; Compare to controller max.
	BLEQU	83$				; Branch if port max is smaller.
	MOVL	CDDB$L_MAXBCNT(R3), R0		; Else, use controller max.

83$:	MOVL	R0, UCB$L_MAXBCNT(R5)		; Store unit max byte count.
;
; N.B. all actions which alter the condition of this UCB with respect
; to the remainder of the I/O database MUST occur following the call
; to IOC$LINK_UCB below.  This results from the possibility that
; IOC$LINK_UCB will determine that this UCB is a duplicate, afterwhich
; this UCB will be deallocated.
;
	MOVL	R5, R2				; Copy UCB address.

	CALL_LINK_UCB INTERFACE_WARNING=NO	; Link UCB to primary DDB.
	BLBC	R0, 700$			; If UCB is duplicate, quit.

	BSBW	DUTU$LINK_UCB2CDDB		; Link UCB into CDDB chain.

	MOVL	UCB$L_CDDB(R5), R3		; Restore CDDB address.
	CLRL	UCB$L_FQFL(R5)			; Make UCB fork block available.

	BSBW	DUTU$SETUP_CDP_UCB		; If needed, setup local UCB to
						; class driver UCB dual path.
	BITL	#<CDDB$M_INITING -		; Is init or reconnect "still"
		 !CDDB$M_RECONNECT>, -		; in progress?
		CDDB$L_STATUS(R3)		; If so somebody else will fix
	BNEQ	90$				; RWAITCNT and we can skip it.
	BBCC	#UCB$V_MSCP_WAITBMP, -		; Else, indicate RWAITCNT no
		UCB$L_DEVSTS(R5), 85$		; longer bumped.
	DECW	UCB$W_RWAITCNT(R5)		; Decrement wait count.

85$:	TSTW	UCB$W_RWAITCNT(R5)		; RWAITCNT ok now?
	.branch_likely
	BEQL	90$				; It had better be zero.

	BUG_CHECK MSCPCLASS, FATAL		; RWAITCNT not zero at end of
						; new UCB setup.
90$:	BICL	#UCB$M_MSCP_INITING, -		; Clear the "initing" flag.
		UCB$L_DEVSTS(R5)
	BICL	#UCB$M_NO_ASSIGN, -		; Clear the "no assign" flag.
		UCB$L_STS(R5)

	UNLOCK_IODB				; Release I/O database mutex.

	CMPB	#DC$_DISK,UCB$B_DEVCLASS(R5)	; Is this UCB for a disk?
	BNEQ	100$				;  branch if not
	CMPB	#MSCP$K_CM_EMULA,-		; Is this a served path
		CDDB$B_CNTRLMDL(R3)		;  to a remote device?
	BEQL	100$				;  branch is server.
;
;   Re-enable DAP processing for this controller if disabled.
;
	TSTL	CDDB$L_DAP_LIMIT(R3)		    ; If RSVD4 zero, reload
	BNEQ	100$				    ;  DAP_LIMIT to allow DAPs
	MOVL	#DAP_LIMIT, -			    ;  to proceed so we can find
		CDDB$L_DAP_LIMIT(R3)		    ;  a secondary path.
;
; This is a local path to a disk device. See if the server is interested.
;
	TSTL	G^SCS$GL_MSCP			; Test if MSCP is loaded.
	BEQL	100$				;  if not loaded, skip the rest

	JSB	G^SCS$DISK_MSCP_NEWDEV		; If its there go to it!

100$:	RSB					; dade, dade, dade; that's all
						; folks.
700$:	BBC	#UCB$V_GTUNMBSY,-		; If get unit name is not active, 
		UCB$L_DEVSTS(R5), 710$		; Go ahead and free the UCB now
	BISL	#UCB$M_DELETEUCB, -		; else set bit to tell get unit 
		UCB$L_STS(R5)			; name to free the UCB when it's 	
	BRB	730$				; done.

710$:	MOVL	UCB$L_DUTUFKBLINK(R5), R0	; Get pointer to top of fork block list

	JSB	G^EXE$DEANONPAGED		; Deallocate the fork blocks

.IF DF IRP$Q_QIO_P1				; If backporting prior to V7.0
	MOVL	UCB$PS_IO_COUNTERS(R5), R0	; Get pointer to IOCNT structure
	BEQL	720$				; Skip deallocate if nothing there
	MOVL	#IOCNT$C_LENGTH, -		; Get Size of IOCNT structure
		FKB$W_SIZE(R0)			;  and set it for deallocate

	JSB	G^EXE$DEANONPAGED		; Deallocate it
720$:
.ENDC
	JSB	G^IOC$FREE_UCB			; Found dual path or duplicate UCB

730$:	UNLOCK_IODB				; Release I/O database mutex.

	RSB

	.dsabl lsb

	.SBTTL	DUTU$LOOKUP_UCB - Locate a given MSCP unit on given CDDB chain
;++
;
;	DUTU$LOOKUP_UCB - Locate a given MSCP unit on given CDDB chain
;
; Functional Description:
;
;	The chain of UCBs linked to the CDDB whose address is input to this
;	routine is scanned for a UCB containing a MSCP unit number and D0/D1
;	media id information matching that in the input MSCP message.  If such
;	a match is found, the address of the UCB is returned in R0.  Otherwise,
;	R0 is returned as zero.
;
; Inputs:
;
;	R0	Address of the first UCB to be examined
;		 (could be zero if this is the end of the list)
;	R1	Length of MSCP message
;	R2	base address of the MSCP message
;	R3	CDDB address
;
; Outputs:
;
;	R0	address of the UCB with matching MSCP unit number, or zero
;
;	All registers except R0 are preserved
;
;--

DUTU$LOOKUP_UCB::

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

	MOVQ	R4, -(SP)			; Save a couple registers.
	MOVZWL	MSCP$W_UNIT(R2), R4		; Assume local controller.
	BBC	#MSCP$V_SLUN, -			; Branch if local controller.
		MSCP$W_UNIT(R2), 5$		; (SLUN bit set in server only).
	MOVZWL	MSCP$W_SLUN_UNIT(R2), R4	; Get SLUN unit.
	EXTZV	#MSCP$V_SLUN_C, -		; Test for served shadow set by
		#MSCP$S_SLUN_C, -		;  extracting controller letter
		MSCP$W_SLUN_DEVNAME(R2), R5	;  and comparing against "S".
	CMPB	#^a/S/-^a/A/+1, R5		; Is this a served shadow set?
	BNEQ	5$				; If not, SLUN unit is ok.
	BISW	#MSCP$M_SHADOW, R4		; Else set shadow bit too.

5$:	TSTL	R0				; Was a valid address passed?
	BRW	11$				; Branch in to test.

10$:	MOVL	UCB$L_CDDB_LINK(R0), R0		; Link to next UCB.

11$:	BEQL	20$				; Start the check over
	CMPW	UCB$W_LCL_MSCPUNIT(R0), R4	; Is the MSCP unit number right?
	BNEQ	10$				; If wrong, try next UCB.
	CMPW	R1, #MSCP$L_MEDIA_ID+4		; Is message big enough to hold
	BLSSU	20$				;  needed data; branch if not.
	EXTZV	#MSCP$V_MTYP_D1, -		; The MSCP unit numbers matched.
		#<MSCP$S_MTYP_D1 -		; Get UCB's D0/D1 media id
		 +MSCP$S_MTYP_D0>,-		; fields.
		UCB$L_MEDIA_ID(R0), R5
	CMPZV	#MSCP$V_MTYP_D1, -		; Do the D0/D1 media id fields
		#<MSCP$S_MTYP_D1 -		; match too?
		 +MSCP$S_MTYP_D0>,-
		MSCP$L_MEDIA_ID(R2), R5
	BNEQ	10$				; Branch if D0/D1 fields differ.
	BBC	#MSCP$V_SLUN, -			; Branch if local controller,
		MSCP$W_UNIT(R2), 20$		;  this must be the unit.
	PUSHL	#0				; Else setup stack.
	MOVL	UCB$L_DDB(R0), R5		; Get DDB address for this UCB.
	SUBB3	#^a/A/-1, -			; Get controller letter index
		DDB$T_NAME+3(R5), (SP)		;  and put on stack.
	EXTZV	#MSCP$V_SLUN_C, -		; Pickup controller letter
		#MSCP$S_SLUN_C, -		;  from the SLUN information
		MSCP$W_SLUN_DEVNAME(R2), R5	;  for final validation.
	CMPL	(SP)+, R5			; Is controller letter same?
	BNEQ	10$				; Search again if wrong letter.
	MOVL	UCB$L_DDB(R0), R5		; Get DDB address again
	CMPL	MSCP$L_SLUN_ALLOCLS(R2),-	; See if same alloclass, might
		DDB$L_ALLOCLS(R5)		;  be a port alloclass
	BNEQ	10$				; Search again if wrong A/C

20$:	MOVQ	(SP)+, R4			; Restore saved registers.
	RSB					;  and return.



	.SBTTL	DUTU$SETUP_DUAL_PATH - Reflect dual path access in I/O database
;++
;
;	DUTU$SETUP_DUAL_PATH - Reflect dual path access in I/O database
;
; Functional Description:
;
;	All UCBs linked to all CDDBs chained to the appropriate CDDB listhead
;	(except the CDDB whose address is given in R3) are scanned for a UCB
;	whose allocation class matches that of the input CDDB (pointed to by R3)
;	and whose MSCP unit number matches the one in the input MSCP message.
;	If such UCB is found, and it has not already been dual path chained,
;	and it does not represent a shadow set virtual unit, then the UCB will
;	be dual path chained to the input CDDB and its related I/O database.
;
;	If the UCB is already chained to the input CDDB, no action is taken.
;	If the UCB is already chained to some other CDDB, a test is made to
;	determine which of the old or new secondary path information is
;	preferred.  If the new secondary path is preferred, the previous
;	secondary path information is discarded and a new secondary path is
;	set up based upon the input information.  If the old secondary path is
;	preferred, the input information is ignored.
;
;	If two non-emulated paths have been found to a device, the UCB is
;	marked with the UCB$M_MSCP_IGNSRV bit in the UCB$L_DEVSTS field.
;	This will prohibit VMS MSCP servers from being used as possible failover
;	targets during connection walking (since there are at least two "local"
;	controller paths present).
;
;	If a UCB is found, its address is returned in R0.  Otherwise, R0 is
;	returned as zero.
;
; Inputs:
;
;	R1	size of the MSCP message
;	R2	base address of the MSCP message
;	R3	CDDB address
;
; Implicit Inputs:
;
;	DUTU$L_CDDB_LISTHEAD	location containing the system virtual address
;				of the CDDB listhead for this device class
; Outputs:
;
;	R0	address of the dual path UCB, or zero
;
;	DEV$M_LOC set in UCB$L_DEVCHAR2(R0) if at least one local (non-emulated)
;	    controller path exists to this device.
;	UCB$M_MSCP_IGNSRV set in UCB$L_DEVSTS(R0) if two or more non-emulated
;	    controller paths exist to this device.
;
;	Registers R3, R4 & R5 are preserved
;
; WARNINGS:
;
;	This routine does not scan the local node I/O database.	 Therefore,
;	only dual path devices which have both paths served by the disk or
;	tape class driver are found by this routine.  This feature is not
;	needed until the MSCP server becomes capable of issuing access path
;	attention messages.  Since access path and duplicate unit messages
;	are the only ones which don't have MEDIA_ID, and VMS emulators don't
;	send these, some useful assumptions can be made about controllers
;	and messages in this routine.
;
;	The dual path nature of the device may not be reflected in the I/O
;	database immediately upon exit from this routine.  That operation is
;	performed in a fork thread which can become stalled due to lack of
;	non-paged pool or inability to obtain write access to the I/O
;	database.  When this routine exits, however, the fork thread has been
;	established and it will complete at some future time.
;
;--


DUTU$SETUP_DUAL_PATH::

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

	PUSHR	#^M<R3,R4,R5>			; Save a few registers.
	MOVL	R3, R4				; Copy input CDDB address.
	SUBL3	#CDDB$L_CDDBLINK, -		; Initialize "previous" CDDB
		<DUTU$DATA -			;  address for search through
		  +DUTU$L_CDDB_LISTHEAD>, R3	;  CDDB list.
	CMPW	R1, #MSCP$L_MEDIA_ID+4		; Is message big enough to hold
	BLSSU	10$				;  needed data?	 Branch if not.
	EXTZV	#MSCP$V_SLUN_C, -		; Get controller letter index
		#MSCP$S_SLUN_C, -		;  (assuming served device) for
		MSCP$W_SLUN_DEVNAME(R2), R5	;  CDDB$L_CTRLTR_MASK compare.
	BBS	#MSCP$V_SLUN, -			; Branch if emulator.
		MSCP$W_UNIT(R2), 20$		; (SLUN bit set in server only).

10$:	FFS	#1, #26, -			; Find bit between 1-26 for
		CDDB$L_CTRLTR_MASK(R4), R5	;  controller letter.
	BEQL	50$				; We better find one.
	BBC	#MSCP$V_SHADOW, -		; Is this a shadow set virtual
		MSCP$W_UNIT(R2), 20$		;  unit?  Branch if not.
	MOVZBL	#^a/S/-^a/A/+1, R5		; Set controller letter to "S".
;
; Loop through CDDB list until matching CDDB with matching UCB is found.
; CDDB must have correct allocation class and controller letter mask bit set.
;
20$:	MOVL	CDDB$L_CDDBLINK(R3), R3		; Link to next CDDB.
	BNEQ	25$				; Continue if more CDDBs
	CLRL	R0				; Signal no UCB found.
	BRW	170$				; Then exit.

25$:	CMPL	R3, R4				; Is it the input CDDB?
	BEQL	20$				; Branch if input CDDB.
	MOVL	CDDB$L_ALLOCLS(R3), R0		; Can this CDDB dual path?
	BEQL	20$				; Branch if it can't dual path.
	CMPL	R0, CDDB$L_ALLOCLS(R4)		; Is it the right alloc. class?
	BNEQ	20$				; Branch if wrong alloc. class.
	BBC	R5, CDDB$L_CTRLTR_MASK(R3), 20$ ; Branch if wrong cntrlr letter.
	MOVL	CDDB$L_UCBCHAIN(R3), R0		; Get the first UCB to check

28$:	BSBW	DUTU$LOOKUP_UCB			; Is this unit already known?

30$:	TSTL	R0				; UCB address or zero.
	BEQL	20$				; Branch if none found.
;
; Rule out (port) allocation class 0 as not shareable, i.e., not dual-pathable.
; Test to make sure the found UCB has the same letter as the input controller.
;
	PUSHL	R5				; Save the input letter index.
	MOVL	UCB$L_DDB(R0), R5		; Get the DDB from found UCB.
	TSTL	DDB$L_ALLOCLS(R5)		; Zero allocation class?
	BEQL	33$				; Yes, no dual porting
	SUBB3	#^a/A/-1, DDB$T_NAME+3(R5), R5	; Convert ASCII to index.
	MOVZBL	R5, R5				; Zero out high order bytes.
	CMPL	R5, (SP)			; Compare controller letters
	POPR	#^M<R5>				;  and restore stack.
	BEQL	35$				; We found a candidate!
	BRB	34$				; We didn't find a candidate.

33$:	POPL	R5				; Restore input letter index.

34$:	MOVL	UCB$L_CDDB_LINK(R0), R0		; Link to next UCB in the chain.
	BNEQ	28$				;  and continue the search
	BRW	20$				; If this was the last UCB,
						;  move on to the next CDDB.
;
; We have a UCB that matches for unit number, allocation class and controller
; letter. If the incoming message contains a media ID, we have already verified
; that it is indeed the correct device. However, hardware generated ACPTH
; messages are not large enough to contain this information. If we have one of
; these, indentified by the message size, we need to make a sanity check to
; ensure that an ACPTH makes sense for the unit in hand. Since these can only
; apply to DU and DJ devices, we check against the second letter of the device
; name string in the DDB. The unit number and controller letter have been
; verified earlier.
;
35$:	CMPW	R1, #MSCP$L_MEDIA_ID+4		; Is message big enough to hold
	BGEQ	39$				;  a media ID. If it is, we
						;  will have used that to
						;  locate the correct UCB.
	PUSHL	R5				; Save the input letter index.
	MOVL	UCB$L_DDB(R0), R5		; Get the DDB from found UCB.
	CMPB	DDB$T_NAME+2(R5),#^a/U/		; Is this a dUc device?
	BEQL	37$				; Yes, proceed with setup.
	CMPB	DDB$T_NAME+2(R5),#^a/J/		; Is this a dJc device?
	BEQL	37$				; Yes, proceed with setup.
	BRB	33$				; Go restore controller letter
						;  index and try again.
;
; Else, here we have the desired UCB.
;
37$:	POPR	#^M<R5>				; Restore controller letter
						;  index. We may not be done
						;  with it yet.
39$:	BBS	#DEV$V_CDP, -			; Stop if this is a class
		UCB$L_DEVCHAR2(R0), 70$		; driver path to a local device.
;
; Test the value in the FAIL_MUTEX field and exit if non-zero since it implies
; that path failover is active in another thread. If it is not zero, load
; the MSCP attention code into it for use by LINK_2P_UCB and as a mutex to
; prevent race conditions when multiple available attention messages arrive
; for this unit
;
	TSTB	UCB$B_FAIL_MUTEX(R0)		; Failover processing active?
	BNEQ	60$				; Exit and discard attn msg
	MOVB	MSCP$B_OPCODE(R2),-		; Save the attn code for use by
		UCB$B_FAIL_MUTEX(R0)		; LINK_2P_UCB
	CLRL	R5				; Clear non-server path counter.
	MOVL	UCB$L_2P_CDDB(R0), R3		; Get secondary CDDB from UCB.
	BEQL	110$				; Branch if UCB not already 2P.
	CMPL	R3, R4				; Is previous dual path CDDB
	BNEQ	40$				;  input CDDB?	Branch to
						;  call failover routines
						;  if yes. It may be an AVATN
						;  on the secondary path.
	MOVL	R0,R5				; move UCB pointer

	BSBW	DUTU$FAILOVER			; jump into failover code in
						; LINK_2P_UCB
	POPR	#^M<R3,R4,R5>			; Restore saved registers.
	RSB					; Return to caller.
;
; Test for which secondary path is preferred.  If the new path is not an MSCP
; emulator, use it regardless of the old path.	If the new path IS the MSCP
; emulator, use it only if the old path was an emulator also.  This allows the
; secondary path to stay a "direct" connection in the face of many MSCP servers
; which are all serving the same device.
;
40$:	CMPB	#MSCP$K_CM_EMULA, -		; Is the new path an emulator?
		CDDB$B_CNTRLMDL(R4)
	BNEQ	100$				; Branch if not, using new path.
	CMPB	#MSCP$K_OP_ACPTH,-		; New path is emulator. If this
		MSCP$B_OPCODE(R2)		;  is a load bal msg (ACPTH)
	BEQL	100$				;  start failover.
	BRB	160$				; Otherwise, ignore it.

50$:	BUG_CHECK MSCPCLASS, FATAL		; CDDB$L_CTRLTR_MASK empty.
;
; We have received an attention message or a GUS response from a controller.
; However, another thread is executing path failover and we have no way of
; knowing if it is for a local path or for an emulator (which it could be if we
; are polling for units on all connections). Therefore, if this message is not
; from the emulator, we will set the local bit before exiting. This will ensure
; that any subsequent packack will go looking for a non-emulator path to this
; device, even if the primary and secondary paths are still emulators.
;
60$:	CMPB	#MSCP$K_CM_EMULA, -		; Is this path an emulator?
		CDDB$B_CNTRLMDL(R4)		;
	BEQL	170$				; Exit if it is.
	BISL	#DEV$M_LOC, UCB$L_DEVCHAR2(R0)	; Indicate local cntrlr exists.
	CMPB	#MSCP$K_OP_AVATN,-		; If we got here because of
		MSCP$B_OPCODE(R2)		;  an Available from a real
	BNEQ	170$				;  controller, set the Mount
	BISW	#UCB$M_MSCP_MVRESTART,-		;  Restart flag to pick up the
		UCB$L_DEVSTS(R0)		;  new path.

70$:	BRW	170$				; Branch assist
;
; Count the number of non-emulator paths to this device.  If two (or more)
; local "direct" controllers have been found, we don't want to ever use the
; MSCP emulator paths for this device from this host.
;
100$:	CMPB	#MSCP$K_CM_EMULA, -	; Is the old secondary path
		CDDB$B_CNTRLMDL(R3)	;  an emulator?
	BEQL	110$			; Branch if yes.
	INCL	R5			; Count non-emulator path.

110$:	CMPB	#MSCP$K_CM_EMULA, -	; Is the new secondary path
		CDDB$B_CNTRLMDL(R4)	;  an emulator?
	BEQL	120$			; Branch if yes.
;
; Now re-enable DAP processing for all controllers in the input
; controller's allocation class.
;
	INCL	R5				; Count non-emulator.
	CMPB	#MSCP$K_OP_ACPTH,-		; Don't re-enable on ACPTHs.
		MSCP$B_OPCODE(R2)		;  even if we do failover.
	BEQL	120$
	SUBL3	#CDDB$L_CDDBLINK, -		; Get CDDB listhead.
	W^<DUTU$DATA -
	  +DUTU$L_CDDB_LISTHEAD>, R1

115$:	 MOVL	 CDDB$L_CDDBLINK(R1), R1	; Get next CDDB.
	BEQL	120$				; Exit if no more.
	CMPB	#MSCP$K_CM_EMULA, -		; Ignore emulators.
		CDDB$B_CNTRLMDL(R1)
	BEQL	115$				; Check next CDDB.
	CMPL	CDDB$L_ALLOCLS(R4), -		; Ignore if not same allocls.
		CDDB$L_ALLOCLS(R1)
	BNEQ	115$
	MOVL	#DAP_LIMIT, -			; Load DAP_LIMIT.
		CDDB$L_DAP_LIMIT(R1)
	BRB	115$				 ; Look for another CDDB.

120$:	PUSHL	R5			; Save counter.
	MOVL	UCB$L_CDDB(R0), R5	; Get primary path CDDB address.
	CMPB	#MSCP$K_CM_EMULA, -	; Is the current primary path
		CDDB$B_CNTRLMDL(R5)	;  an emulator?
	BEQL	130$			; Branch if yes.
	INCL	(SP)			; Count non-emulator path.

130$:	CMPL	#1, (SP)+		; Is there at least one non-
	BGTRU	150$			;  emulated path? Branch if no.
	BEQL	140$			; Branch if only one local path.
;
; If we have found 2 or more local paths we will set the MSCP_IGNSRV bit
; in UCB$L_DEVSTS. This had previously been used in connection walking to
; ignore served paths as possible failover candidates, hence the name. It
; is no longer used for this purpose and is merely an indicator that two
; direct paths have existed.
;
	BISL	#UCB$M_MSCP_IGNSRV, -	; Set bit to ignore served paths
		UCB$L_DEVSTS(R0)	;  during failover processing.

140$:	ASSUME	DEV$V_LOC GE 8		; 1 or more local paths...
	BISL	#DEV$M_LOC, -		; Set bit indicating local path
		UCB$L_DEVCHAR2(R0)	;  controller exists.
;
; Set up the fork routine which will actually do the unlinking of the old
; secondary path (if present) and link in the new secondary path.
;
150$:	MOVL	R4, UCB$L_2P_CDDB(R0)	; Input CDDB is dual path CDDB.
	TSTL	UCB$L_FQFL(R0)		; Fork thread already active?
	BNEQ	160$			; Branch if already active.
					; Since failover mutex was clear, we
					; should clear it now
	MNEGL	#1, UCB$L_FQFL(R0)	; Make it busy now.

	PREPARE_TO_FORK -
		LINK_2P_UCB, -		; Prepare for control to return here
		FRKBLK = (R0), -	;  when fork occurs.
		MASK = <^M<R0,R1,R2>>	; Save these registers.

	POPR	#^M<R3,R4,R5>		; Restore saved registers.
	RSB				; Return to caller.

160$:	CLRB	UCB$B_FAIL_MUTEX(R0)	; No failover processing active
					; Leaving this set will prevent
					; further failover
170$:	POPR	#^M<R3,R4,R5>		; Restore saved registers.
	RSB				; Return to caller.



	.SBTTL	DUTU$LINK_2P_UCB - Fork thread to dual path link a UCB.
;++
;
;	DUTU$LINK_2P_UCB - Fork thread to dual path link a UCB
;
; Functional Description:
;
;	Acting in the context of an independent fork thread and using the UCB
;	as a fork block, this routine establishes the dual path links for a
;	UCB.  A suitable DDB is located or created.  Write access is obtained
;	for the I/O database.  Finally, the UCB is dual path linked to the
;	suitable DDB.
;
;	A further possible optimization is then checked:  if the primary
;	path is a VMS MSCPserver, and the (new) secondary path is not,
;	then it would be beneficial to switch the paths at this opportunity.
;
;	 - If the disk is currently mounted, mount verification is initiated
;	   to force a failover (via PACKACK at a later time).  This removes
;	   a level of indirection from served devices which can now also
;	   be directly accessed.  Note that this technique is currently
;	   only valid for devices which shouldn't be using a server at all -
;	   ie, there are two non-server paths to the disk (MSCP_IGNSRV is set
;	   in UCB$L_DEVSTS).  This prevents ping-pong effects in cases such
;	   as a served disk dual-pathed between two systems with UDAs.
;	   Each system would attempt to change the path to its own local UDA,
;	   causing semi-infinite grief.	 Further enhancements here require
;	   true preferred path support.	 If mount verification cannot be done
;	   (foreign volume, mount verification disabled, etc.) the device is
;	   left as is and life goes on.
;
;	 - If the disk is not currently being accessed in any way, shape,
;	   or form, this routine calls DUTU$MOVE_UNIT directly to switch
;	   the paths (seeing as mount verification takes a dim view of
;	   unmounted disks...).	 The most direct benefit here is that
;	   SHOW DEVICE will display something close to reality about the
;	   paths that will be used when/if the device is mounted.  Also, further
;	   secondary path information (especially another non-served path)
;	   will eventually remove extraneous served paths from the I/O database.
;	   (Inverse Manana Syndrome.)
;
;	This routine potentially modifies UCB$L_MAXBCNT, the field giving the
;	maximum number of bytes allowed in a single transfer.  The current
;	value in that field -- which was established when the primary path was
;	discovered -- is minimized with the PDT$L_MAXBCNT field in the PDT for
;	the secondary path.  The effect is to generally use the minimum value
;	accepted among the two paths on which the device is known.  A
;	potential problem exists if the secondary path MAXBCNT value is
;	smaller than the primary path value.  Some of the in progress
;	transfers may exceed the secondary path MAXBCNT.  Should control be
;	failed over to the secondary path before these transfers complete,
;	those transfers could not be performed.	 However, this risk is deemed
;	minimal and will be addressed by a nonfatal bugcheck in the
;	appropriate port driver (at the most).
;
; Inputs:
;
;	R5 = UCB
;
;
; Implicit Imputs:
;
;	UCB$L_2P_CDDB(R5)  address of new CDDB for the secondary path
;	UCB$L_DEVCHAR2(R5) DEV$V_2P set if UCB is already dual pathed
;	Fork context at IPL$_SCS  (R5 = UCB and fork block)
;
; Outputs: None.
;
; Implicit Outputs:
;
;	UCB$L_2P_DDB(R5)  altered to point to the secondary path DDB
;	UCB$L_2P_LINK(R5) altered to link the UCB into the secondary chain of
;			  UCBs for the DDB
;	DEV$M_2P set in UCB$L_DEVCHAR2(R5)
;	UCB$L_MAXBCNT(R5)	minimized with PDT$L_MAXBCNT(pdt)
;
;	The primary and secondary paths will be swapped (possibly in the
;	future) if the proper preference criteria are met.
;
; WARNINGS:
;
;	The CDDB$L_DDB in this CDDB must point to at least one valid DDB for
;	the devices which are (could be) accessed via that CDDB.  The major
;	implication here is that we can never discard the DU or MU DDB handed
;	us by SYSGEN, et. al. when calling the driver at its controller
;	initialization entry point.
;
;--

DUTU$LINK_2P_UCB::
LINK_2P_UCB:

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

	MNEGL	#1, UCB$L_FQFL(R5)		; Indicate fork block busy.
;
;  Minimize the maximum byte count amongst all active controllers.
;
	MOVL	UCB$L_2P_CDDB(R5), R0		; Get secondary CDDB address.
	MOVL	CDDB$L_MAXBCNT(R0), R1		; Get controller max byte count.
	MOVL	CDDB$L_PDT(R0), R0		; Get secondary PDT address.
	CMPL	PDT$L_MAXBCNT(R0), R1		; Ctrlr. max smaller than port?
	BGEQU	20$				; Branch if ctrlr max smaller.
	MOVL	PDT$L_MAXBCNT(R0), R1		; Else, get port max byte count.

20$:	CMPL	R1, UCB$L_MAXBCNT(R5)		; Primary path max < alt. path?
	BGEQU	30$				; Branch if prim. path smaller.
	MOVL	R1, UCB$L_MAXBCNT(R5)		; Else, store unit max byte cnt.
;
;  Find or create a suitable DDB.
;
30$:	MOVAB	L2_AFTER_FIND_DDB, R2		; Pass completion routine addr
	ADDL3	#DDB$T_NAME, UCB$L_DDB(R5), R3	; Get address of DDC name.
	MOVL	UCB$L_2P_CDDB(R5), R4		; Get sec. CDDB address.
	CLRL	R0				; Indicate IODB Lock not held.

	BSBW	DUTU$FIND_DDB			; Find or create the DDB.

	RSB
;
; Inputs:
;
;	R4	DDB  address
;	R5	UCB  address (preserved)
;
L2_AFTER_FIND_DDB:

	.JSB_ENTRY INPUT=   <		R4,R5>,-
		   OUTPUT=  <                 >,-
		   SCRATCH= <>,-
		   PRESERVE=<                 >
;
;  Since DUTU$FIND_DDB may have forked and the secondary path changed during
;  that fork, the DDB just located must be validated as a DDB associated with
;  the CURRENT secondary path.	The I/O database is now owned for write access.
;
	MOVL	UCB$L_2P_CDDB(R5), R3		; Get current 2nd CDDB address.

	BSBW	DUTU$CHECK_DDB_FOR_CDDB		; Is DDB still correct?
	BLBS	R0, 35$				; If so, continue.

	UNLOCK_IODB				; Release I/O database mutex.

	MOVB	#SPL$C_SCS, UCB$B_FLCK(R5)	; Ensure UCB spinlock index.

	FORK_WAIT				; Wait a second, then

	BSBW	LINK_2P_UCB			;  try again.

	RSB

35$:	CMPL	R4, UCB$L_2P_DDB(R5)		; Is 2nd DDB already correct?
	BEQL	50$				; If so, skip path linking.
;
;  The DUTU$FIND_DDB call above has acquired write access to the I/O database,
;  so setup the dual path links for this device.
;
	BBC	#DEV$V_2P, UCB$L_DEVCHAR2(R5),40$; Branch if UCB not previously
						; dual pathed.
	BSBW	DUTU$SEVER_SEC_UCB		; Else, undo previous linking.

40$:	MOVL	R4, UCB$L_2P_DDB(R5)		; Set new secondary DDB address.
	MOVL	R5, R2				; Copy UCB address for linking.

	BSBW	DUTU$LINK_SEC_UCB		; Make secondary DDB links.
	BLBS	R0, 50$				; Branch if successful.

	BUG_CHECK INCONSTATE, FATAL		; Signal inconsistent I/O db.

50$:	BISL	#DEV$M_2P, UCB$L_DEVCHAR2(R5)	; Flag device as dual pathed.

	UNLOCK_IODB				; Release I/O database mutex.

	CLRL	UCB$L_FQFL(R5)			; We no longer need to use this
						;  as fork block. Clear the flag.
;
;  New secondary path now correctly linked, check if it is a better choice
;  than the current primary path and switch them if possible.
;
	BSBW	DUTU$FAILOVER			; BR into failover code to check

	RSB



	.SBTTL	DUTU$FAILOVER - Determine whether to swap paths
;++
;	DUTU$FAILOVER - determine whether to swap primary and secondary paths
;
; Functional Description:
;
;	This routine is entered by DUTU$SETUP_DUAL_PATH as a result of an
;	attention message being received or as a result of polling for units.
;	If the newly discovered path is previous secondary path,
;	DUTU$SETUP_DUAL_PATH will JSB to this routine. If the new path is
;	better than the previous secondary path, LINK_2P_UCB will be called to
;	link the new secondary path. It will fall into this routine.
;
;	The attention message code (or end code for GTUNT) has been passed to
;	us via UCB$B_FAIL_MUTEX and we use it to determine what action to take.
;
;	If failover is needed for disks that are not mounted, DUTU$MOVE_UNIT is
;	called directly.
;
;	If failover is needed for mounted disks, mount verification is called
;	with a status code SS$_DEVOFFLINE. Mount verification will eventually
;	enter DUTU$LOCATE_UNIT which will begin connection walking. The
;	secondary path is always attempted first and will succeed under most
;	circumstances.
;
;	Before exiting this routine, the field that contained the attention code
;	is cleared. This field is checked in DUTU$SETUP_DUAL_PATH prior to
;	entering this routine and is used as a mutex to prevent race conditions
;	that can result when multiple attention messages arrive for a given
;	device.
;
; Inputs:
;
;	R5	UCB address
;
;  Implicit inputs:
;
;	UCB$B_FAIL_MUTEX(R5)	attention or end code
;	UCB$L_CDDB(R5)		current primary path
;	UCB$L_2P_CDDB(R5)	possibly new secondary path
;
;  Outputs:
;
;	R0	UCB address (for compatibility with DUTU$SETUP_DUAL_PATH)
;	R5	UCB address
;
;  Implicit outputs:
;
;	The secondary and primary paths may be swapped.
;	A mounted device may be placed in mount verification.
;
;--

;
; Alternate entry point to start MV after a forcepath request
;
DUTU$TRIGGER_MV::

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

	BRW	TRIGGER_MV

DUTU$FAILOVER::

	.ENABLE LSB

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

	MOVL	UCB$L_CDDB(R5), R2	  ; Find primary controller.
	MOVL	UCB$L_2P_CDDB(R5),R3	  ; And new secondary controller.
	MOVZBL	UCB$B_FAIL_MUTEX(R5), R4  ; Get the atn code
;
;  This routine attempts to determine how we got here. If it is the result of
;  an available attention message from a non-emulator controller, trigger
;  failover immediately. If it is from a determine access path command on
;  another controller, branch to a check for a special case. Finally any other
;  attention message from a non-emulator controller will trigger failover (a
;  catchall condition that will catch returns from GTUNT in the polling
;  routines).
;
	CMPB	#MSCP$K_CM_EMULA,-	  ; Is the new 2P emulated?
		CDDB$B_CNTRLMDL(R3)
	BEQL	51$			  ; If it is, look for LB msg.
	CMPB	#MSCP$K_OP_AVATN, R4	  ; Is this an avail attn?
	BEQL	57$			  ; It is, trigger failover
	CMPB	#MSCP$K_OP_ACPTH, R4	  ; Is this access path attn?
	BEQL	53$			  ; It is, check for special case
	CMPB	#MSCP$K_CM_EMULA, -	  ; Is the primary emulated
		CDDB$B_CNTRLMDL(R2)
	BEQL	57$			  ; Trigger failover if it is
	BRW	58$			  ; If not, then exit
;
; An attention message from a server. If this is an access path attention,
; then a server is indicating that it has been asked to become the primary
; path for this unit.
;
51$:	CMPB	#MSCP$K_OP_ACPTH, R4	  ; Is this access path attn?
	BEQL	510$
	BRW	58$			  ; No, exit immediately

510$:	CMPB	#MSCP$K_CM_EMULA,-	  ; Is the old 1P emulated?
		CDDB$B_CNTRLMDL(R2)
	BEQL	512$
	BRW	90$			  ; No!! Illegal LB request!

512$:	MNEGB	#1,UCB$B_FAIL_MUTEX(R5)	  ; Indicate special code for LB.
	BRW	57$
;
;  We have received an ACPTH message. If the message is from a multihost
;  controller and our current primary is an emulator we may have a good
;  candidate for failover. This will catch drives that get caught in the panic
;  when all MSCP connections are reforming. Note that in the event of a disk
;  being actively dual pathed between a local controller with the path selected
;  and an HSC (not currently supported) this will cause unnecessary calls to
;  mount verification.
;
;  If the primary path is a local (single host) controller and the new
;  secondary is a multihost controller (and is not the emulator, rejected
;  above) trigger failover to make this device available via the multihost
;  controller.
;
53$:	BBC	#MSCP$V_CF_MLTHS,-	; Ignore the ACPTH if not
		CDDB$W_CNTRLFLGS(R3),58$; from a multihost controller
	BBC	#MSCP$V_CF_MLTHS,-	; Is the primary controller
		CDDB$W_CNTRLFLGS(R2),57$; multihost? F/over if not.
	CMPB	#MSCP$K_CM_EMULA, -    	; Is the primary emulated?
		CDDB$B_CNTRLMDL(R2)    	; Trigger failover if it is
	BNEQ	58$		       	; Otherwise, ignore it.
;
;  Paths indicate a change is desireable. Determine technique to use,
;  brute-force mount verification versus brute-force structure tweaking.
;
;  Check to see if the device is marked as a shadow set member. If it is,
;  dismiss the attention message. The shadowing mount verification routines
;  will detect a change in shadow set state and react accordingly. Attempting
;  to failover a member here without the full shadowing context can result in
;  an inconsistent I/O database.
;
;  Only the shadowing routines know the evil that lurks in the hearts of
;  shadow sets	(with apologies to Lamont Cranston)
;
;  If the device is a tape device then don't alter the path just because of
;  the "available attention" messages (which begin again once the MSCP device
;  is dismounted). This causes "bouncing" back and forth between controllers
;  which can interfere with manual tape load balancing techniques. Normal
;  failover under "error" conditions works as usual.
;
57$:	BBS	#DEV$V_SSM, -		; Is this a shadow set member?
		UCB$L_DEVCHAR2(R5), 58$	; Exit if it is.
	BBS	#DEV$V_MNT, -		; If the device is mounted,
		UCB$L_DEVCHAR(R5), 60$	;  or if the online count is
	TSTB	UCB$B_ONLCNT(R5)	;  non-zero, or if there is a
	BNEQ	TRIGGER_MV		;  VCB, then mount verification
	TSTL	UCB$L_VCB(R5)		;  may be appropriate to cause
	BNEQ	60$			;  the path switch.
;X-26U6
	BBS	#UCB$V_MSCP_PKACK, -	; Do not alter path information
		UCB$L_DEVSTS(R5), 58$	;  if PACKACK in progress.
	CMPB	#DC$_TAPE, -
		UCB$B_DEVCLASS(R5)	; Check for tape device...
	BNEQ	575$			; If not a tape continue...
	CMPB	#MSCP$K_CM_EMULA, -	; Is the primary path emulated?
		CDDB$B_CNTRLMDL(R2)	; If it's not emulated then exit
	BNEQ	58$			; without failing over to 2nd path.
	CMPB	#MSCP$K_CM_EMULA, -	; Is the secondary path emulated?
		CDDB$B_CNTRLMDL(R3)	; If it is emulated then exit
	BEQL	58$			; without failing over to 2nd path.
;
;  The device is not in use, so the paths may be switched directly.
;
575$:	MOVL	R3, R2			; Setup 2P CDDB pointer
					; where MOVE_UNIT expects it.
	MOVL	R5, R3			; Put UCB address in R3.
	CLRL	R5			; Indicate no CDRP.
	MOVL	UCB$L_PDT(R3), R4	; Setup PDT 

	BSBW	DUTU$MOVE_UNIT		; Move unit to place just located.

	MOVL	R3, R5			; Restore UCB address and exit.

58$:	CLRB	UCB$B_FAIL_MUTEX(R5)	; Clear fail mutex to allow 
	BRW	70$			; other threads to run & exit


590$:	BISW	#UCB$M_MSCP_MVRESTART,-	; Set flag to make mount ver
		UCB$L_DEVSTS(R5)	;  restart.
	BRW	70$			; And then exit.



	.SBTTL - DUTU$TRIGGER_MV - Trigger mount verification
;++
;
; DUTU$TRIGGER_MV - Trigger mount verification
;
; Functional Description:
;
; The device is busy in some way, mount verification may be the ticket.
; However, recursive mount verification is not allowed (or necessary),
; nor is bouncing between two systems which share a device...
;
; If we are serving this unit, call the server mount verification entry
; point (mscp_mv) since regular MV would have done this, but the device
; is not marked as mounted locally.
;
; Use mount verification unless already in progress. If in progress, exit
; without clearing FEX. If here from SETUP, FEX contains the appropriate
; value to direct MV to do what SETUP wants anyway. If here from
; AVAILABLE_SUCC we have no knowledge of FEX and shouldn't change it.
;
; Inputs:
;
;	R5	UCB address
;
;
; Implicit Inputs:
;
;
;
; Outputs:
;
;	R0	UCB address
;
;
; Implicit Outputs:
;
;	R4, R5 - Preserved.
;
;	R1,R2,R3 - Scratched.
;
;
;--

TRIGGER_MV:

	MSCP_MV				; If the device is served, this
					;  cause the server datastructures to
					;  be updated.
60$:	BBS	#UCB$V_MNTVERIP, -	; If mount verification active
		UCB$L_STS(R5), 590$	;  don't restart.
	BBS	#UCB$V_MSCP_MNTVERIP, -	; Same applies for internal
		UCB$L_DEVSTS(R5), 590$	;  mount verification.
	BBS	#DEV$V_SHD, -		; If this is a HBS shadow set
		UCB$L_DEVCHAR2(R5),87$	;  branch to routine to call
					;  volume processing.
;
;  Initiate mount verification for the device to attempt path failover.
;
	BBSS	#UCB$V_MVFKBBSY, -	; Skip this UCB if we're
		UCB$L_DEVSTS(R5), 590$	;  waiting for memory.
	BISL	#UCB$M_SUPMVMSG, -	; Suppress mount verification
		UCB$L_STS(R5)		;  messages for this failover.

9200$:	MOVZWL	#IRP$K_LENGTH, R1   	; Get size of an IRP/CDRP.

     	ALLOCATE_POOL               
	BLBS	R0, 9210$	    	; Branch if allocation succeeded.

	MOVL	R5,R3		    	; save UCB
	MOVL	UCB$L_DUTUFKBLINK(R5), R5; Get fork block pointer

	FORK_WAIT		    	; Wait awhile

	MOVL	R3, R5		    	; restore UCB
	BICL	#UCB$M_MVFKBBSY, -	; Clear busy bit & see if
		UCB$L_DEVSTS(R5)	; we still need to do MV
 	BRW	60$			; for this UCB.

9210$:	JSB	DUTU$INIT_IRP_CDRP  	; Setup size and type fields.

	BICL	#UCB$M_MVFKBBSY, -
		UCB$L_DEVSTS(R5)	; Clear waiting for memory flag. 
	MOVL	R2,R3		    	; Move IRP to R3
	MOVL	R5, IRP$L_UCB(R3)   	; Put device UCB in IRP
	MOVL	UCB$L_CDT(R5), -
		IRP$L_CDT(R3)		; Put current CDT in IRP
	MOVW	#-1, IRP$L_FUNC(R3)	; Set up invalid function
	MOVL	#IRP$M_PHYSIO, -	; Set IRP status flags.
		 IRP$L_STS(R3)
	MOVL	#CDRP$M_PERM, -		; set perm bit so mount ver
		IRP$L_DUTUFLAGS(R3)	; won't queue this irp
	MOVZWL	#SS$_DEVOFFLINE, R0	; Setup a suitable status and
	CLRL	R1			;  clear second "IOSB" value.
	PUSHR	#^M<R4>			; Save register
;
; NOTE: Status is returned in R2 instead of R0,R1. R0 & R1 are preserved across
;       the call to mount verification.
;
	CALL_MOUNT_VER			; Start mount verification.
	.branch_likely
	BLBC	R2,65$			; LBS means we can't MV

;
; Cannot do mount verification
;
	MOVL	R3, R0			; Move input IRP to R0

	JSB	G^EXE$DEANONPAGED 	; Deallocate the IRP
					; ignore continue status.
	CLRB	UCB$B_FAIL_MUTEX(R5)	; Clear fail mutex to allow 
					; other threads to run & exit
65$:	POPR	#^M<R4>			; Restore register
;
; Common exit point for all possibilities.
; If mount verification started, LOCATE_UNIT will clear UCB$B_FAIL_MUTEX which we
; use to avoid possible race conditions. We have finished any possible failover
; processing for this UCB.
;
70$:	MOVL	R5,R0			; Restore UCB for return via
					; SETUP_DUAL_PATH
	RSB				; End fork thread.
;
; The following mount verification routine for shadow set members cannot
; be called until the calling convention is changed. Check out the interface
; to the ported mount verification for non-shadowed disks.
;
; The UCB found is a member of a host based shadow set. Since normal
; MV is not supported on these devices, SHDRIVER's volume processing
; routine is called. R0, R1, R3 and R5 are used in setting up the call,
; and shadowing may clobber other registers.
;
; If we are serving this unit, call the server mount verification
; entry point since regular MV would have done this.
;
87$:	MSCP_MV				; If the device is served, this
					;  cause the server datastructures to
					;  be updated.
	PUSHR	#^M<R4,R5>		; Save registers.
	MOVL	UCB$L_SHAD(R5), R3	; Get the SHAD address.
	MOVL	SHAD$L_VU_UCB(R3), R5	; Replace physical with VU UCB.
	CLRL	R3			; No IRP!
	MOVZWL	#SS$_DEVOFFLINE, R0	; Use device offline error.
	MOVL	UCB$L_DDT(R5), R1	; Get DDT address.
;
; This will call SHVP$SHADOW_MNTV_SSSC with input:
;
; R5 = VU UCB
; R3 =	0  Must be zero to denote no input IRP
; R1 = DDT
; R0 =	Error code
;
	JSB	@DDT$L_MNTV_SSSC(R1)	;  and make the call

	POPR	#^M<R4,R5>		; Restore registers
	CLRB	UCB$B_FAIL_MUTEX(R5)	; Clear fail mutex to allow 
	BRW	70$			; other threads to run & exit

90$:	BUG_CHECK	MSCPCLASS,FATAL

	.DISABLE LSB



	.SBTTL	DUTU$FIND_DDB - Find or create a DDB with the right DEVNAM
;++
;
;	DUTU$FIND_DDB - Find or create a DDB with the right DEVNAM
;

; Functional Description:
;
;	Starting with a given CDDB, this routine searches the chain of DDBs
;	attempting to find a requested DEVNAM (DDC - device DD, controller C).
;	If the requested DEVNAM is found, the corresponding DDB address is
;	returned.  If the requested DEVNAM is not found, a DDB is created with
;	attributes appropriate to the chain of DDBs being searched.  The
;	created DDB is given the requested DEVNAM and both its primary and
;	secondary UCB chains are initialized to be empty.  The newly created
;	DDB is linked to the end of the DDB and CDDB chains which begin in the
;	input DDB.  Finally, the address of the created DDB is returned.
;
;	The I/O database is locked for write access when this routine exits.
;	The caller is responsible for releasing the mutex after any further
;	manipulations have been completed, using the UNLOCK_IODB macro.
;
;	This routine is optimized (within reason) for the case where a DDB
;	is found to match the input DEVNAM, since it is more likely for
;	multiple devices with similar names to exist than many unique devices.
;	Finding a match is also more probable during failover processing.
;
; Inputs:
;
;	R0	Lock held indicator.
;		LBC => IODB Lock needs to be taken out
;		LBS => IODB Lock already held
;	R2	Return address
;	R3	address of the requested DEVNAM (a counted ASCII string)
;	R4	address of the CDDB 
;	R5	address of a fork block
;		(Must be either a UCB, a CDRP, or the 2P fork block.)
;
; Outputs:
;
;	None
;
;
; WARNINGS:
;
;	Under some circumstances (IODB mutex unavailable), this routine forks.
;
;--
	.ENABLE LSB

DUTU$FIND_DDB::

	.JSB_ENTRY INPUT=   <R0,   R2,R3,R4,R5>,-
		   OUTPUT=  <		      >,-
		   SCRATCH= <     	      >,-
		   PRESERVE=<                 >
;
;  Validate input fork block type.
;
	CMPB	#DYN$C_FRK, -			; Is this the 2P FRK block?
		FKB$B_TYPE(R5)
	BNEQ	10$				; Branch if not a FRK block.
	MOVL	R2, FKB2P$L_SAVD_RTN(R5)	; Else, save return address.
	BRW	27$				; And go lock I/O database.

10$:	CMPB	#DYN$C_UCB, -			; Is fork block a UCB?
		UCB$B_TYPE(R5)
	BNEQ	20$				; Branch if not a UCB.
	MOVL	R2, UCB$L_DPC(R5)		; Else, save return address.
	BRW	27$				; And go lock I/O database.

20$:	CMPB	#DYN$C_CDRP, -			; Is fork block a CDRP?
		CDRP$B_CD_TYPE(R5)
	.branch_likely
	BEQL	25$				; Branch if it's a CDRP.

	BUG_CHECK MSCPCLASS, FATAL		; DUTU$FIND_DDB called without
						;  proper fork block.
25$:	MOVL	R2, CDRP$L_SAVD_RTN(R5)		; Else, save return address.
;
;  Fork block valid.  Attempt DDB lookup after locking I/O database.
;
27$:	ASSUME	DUTU$K_MUTEX_HELD EQ 1		; Assume for BLBS
	BLBS	R0, DUTU$FIND_DDB_AFTER_LOCK	; Lock already held, skip over.

30$:	MOVB	#SPL$C_SCS, CDRP$B_FLCK(R5)	; Guarantee fork lock index.
	MOVAB	G^IOC$GQ_MUTEX, R0		; Get I/O database mutex.

	JSB	G^SCH$LOCKWEXEC_QUAD		; Lock for WRITE access.
	BLBS	R0, DUTU$FIND_DDB_AFTER_LOCK

	FORK_WAIT -
		ROUTINE=37$,-			; Wait a second (R3-R5 saved)
		CONTINUE=35$
35$:	RSB

37$:	.JSB_ENTRY INPUT=   <	      R3,R4,R5>,-
		   OUTPUT=  <	      R3,R4,R5>,-
		   SCRATCH= <>,-
		   PRESERVE=<                 >

	BRW	30$
;
; Inputs:
;
;	R3	address of the requested DEVNAM (a counted ASCII string)
;	R4	address of the CDDB 
;	R5	address of a fork block
;		(Must be either a UCB, a CDRP, or the 2P fork block.)
;
; Outputs:
;
;	R3	address of the requested DEVNAM (a counted ASCII string)
;	R4	DDB address
;	R5	Address of a fork block (unchanged)
;
;
DUTU$FIND_DDB_AFTER_LOCK:
;
;  NOTE: Code just past 56$ expects the CDDB from R4 to be the second thing on
;        the stack.
;
	PUSHR	#^M<R3,R4,R6,R7>		; Save some registers.
	MOVL	R3, R6				; Copy DEVNAM address.
	MOVL	R4, R3				; Move CDDB to R3
	MOVL	CDDB$L_DDB(R3), R4		; Setup starting DDB 
	MOVZBL	(R6)+, R7			; Get size of DEVNAM.
	BRB	50$				; Jump into search loop

40$:	MOVL	DDB$L_CONLINK(R4), R4		; Link to next DDB.
	BEQL	100$				; Branch if no more DDBs.

50$:	CMPB	R7, DDB$T_NAME(R4)		; Is DDB DEVNAM the right size?
	BNEQ	40$				; No, go try next DDB.
	CMPC3	R7,(R6),DDB$T_NAME+1(R4)	; Does the name match?
	BNEQ	40$				; No, go try another DDB.
;
; Names match. Now check DDB allocation class if served device.
;
	CMPB	#DYN$C_FRK, -			; 2P FRK block?
		FKB$B_TYPE(R5)
	BNEQ	52$				; No, check CDRP
	MOVL	FKB2P$L_SAVD_UCB(R5),R0		; Get UCB address from FKB
	BRB	56$				;  and go verify UCB

52$:	CMPB	#DYN$C_CDRP, -			; CDRP?
		CDRP$B_CD_TYPE(R5)
	BNEQ	55$				; No, UCB then
	MOVL	CDRP$L_UCB(R5),R0		; Get UCB address from CDRP
	BRB	56$				;  and go verify UCB

55$:	MOVL	R5,R0				; Not FRK or CDRP, must be UCB

56$:	BGEQ	58$				; Ignore if not pointer
	CMPB	#DYN$C_UCB, -			; Is UCB really a UCB?
		UCB$B_TYPE(R0)
	BNEQ	58$				; Br if not
	MOVL	4(SP), R3			; Restore CDDB from saved R4
	CMPB	#MSCP$K_CM_EMULA, -		; Is the controller for this
		CDDB$B_CNTRLMDL(R3)		;  device a vms mscpserver?
	BNEQ	58$				; Branch if not an emulator.
	CMPL	DDB$L_ALLOCLS(R4),-		; MSCP unit, check allocls
		UCB$Q_UNIT_ID(R0)
	BNEQ	40$				; Different A/C, try again

58$:	POPR	#^M<R0,R1,R6,R7>		; DEVNAM match found, restore
						; stack, discard saved R3 & R4.
;
;  Exit via appropriate saved return address.
;
60$:	CMPB	#DYN$C_FRK, -			; Is this the 2P FRK block?
		FKB$B_TYPE(R5)
	BNEQ	70$				; Branch if not a FRK block.
	JMP	@FKB2P$L_SAVD_RTN(R5)		; Else, return.

70$:	CMPB	#DYN$C_UCB, -			; Is fork block a UCB?
		UCB$B_TYPE(R5)
	BNEQ	80$				; Branch if not a UCB.
	JMP	@UCB$L_DPC(R5)			; Else, return.

80$:	CMPB	#DYN$C_CDRP, -			; Is fork block a CDRP?
		CDRP$B_CD_TYPE(R5)
	BNEQ	90$				; Branch if not a CDRP (uh-oh).
	JMP	@CDRP$L_SAVD_RTN(R5)		; Else, return.

90$:	BUG_CHECK MSCPCLASS, FATAL		; DUTU$FIND_DDB called without
						;  proper fork block.
;
;  No DEVNAM match found.  Create a new DDB instead.
;
;  Note: When we restore the registers below, the CDDB will be back in R4
;	 rather than the DDB, since the CDDB is now passed to this routine. We
;	 need to remember to restore the template DDB after we get pool for the
;	 new DDB we're creating.
;
100$:	POPR	#^M<R3,R4,R6,R7>		; Restore saved registers.
	MOVZBL	#DDB$K_LENGTH, R1		; Get size of a DDB.

	ALLOCATE_POOL				; Try to allocate space for one.
	BLBC	R0, 150$			; Branch on allocation error.

	PUSHL	R4				; Save the CDDB for later.
	MOVL	CDDB$L_DDB(R4), R4		; Get a template DDB 
	PUSHR	#^M<R2,R5>			; Save new DDB & fork block ptrs
	MOVQ	R3, -(SP)			; Save DEVNAM and template DDB ptrs.
	MOVC3	#DDB$K_LENGTH,(R4),(R2)		; Copy template DDB into new DDB.
	POPL	R3				; Restore requested DEVNAM addr.
	MOVZBL	(R3)+, R4			; Get size of requested DEVNAM.
	ADDL3	#DDB$T_NAME+1,4(SP),R2		; Locate DEVNAM in new DDB.
	MOVC3	R4, (R3), (R2)			; Insert requested DEVNAM.
	POPR	#^M<R3,R4,R5>			; Restore template DDB, new DDB & frk blk addrs.
;
; Since we just copied the whole DDB, we need to clear out a few fields here...
;
	CLRL	DDB$L_CONLINK(R4)		; New DDB ends CDDB chain and
	ASSUME DDB$L_UCB EQ DDB$L_LINK+4
	CLRQ	DDB$L_LINK(R4)			;  DDB chain.  Null primary UCB link.
	CLRL	DDB$L_2P_UCB(R4)		;  Null secondary UCB link.
	MOVL	R3, R0				; Init for DDB chain scan.

110$:	TSTL	DDB$L_LINK(R0)			; End of DDB chain?
	BEQL	120$				; Branch if end of chain.
	MOVL	DDB$L_LINK(R0), R0		; Else, link to next DDB
	BRW	110$				;  and loop.

120$:	MOVL	R4, DDB$L_LINK(R0)		; Put new DDB on end of chain.

130$:	TSTL	DDB$L_CONLINK(R3)		; End of CDDB chain?
	BEQL	140$				; Branch if end of chain.
	MOVL	DDB$L_CONLINK(R3), R3		; Else, link to next DDB on
	BRW	130$				;  CDDB chain and loop.

140$:	MOVL	R4, DDB$L_CONLINK(R3)		; Put new DDB on end of chain.
	MOVL	R5,R0				; R0/ UCB, if R5 is a UCB
	CMPB	#DYN$C_UCB, -			; Is fork block a UCB?
		UCB$B_TYPE(R5)			;
	BEQL	142$				; Yes, UCB in R0
	MOVL	FKB2P$L_SAVD_UCB(R5),R0		; R0/ UCB, if R5 is a FKB
	CMPB	#DYN$C_FRK,-			; Is fork block the 2P fork block?
		FKB$B_TYPE(R5)			;
	BEQL	142$				; Yes, UCB in R0
	MOVL	CDRP$L_UCB(R5),R0		; Must be #DYN$C_CDRP, get UCB in R0

142$:	POPL	R3				; Restore the CDDB
	CMPB	#MSCP$K_CM_EMULA, -		; Is the controller for this
		CDDB$B_CNTRLMDL(R3)		;  device a vms mscpserver?
	BNEQ	148$				; Branch if not an emulator
	MOVL	UCB$Q_UNIT_ID(R0),-		; Else copy allo class into DDB
		DDB$L_ALLOCLS(R4)

144$:	BRW	60$				; Branch to common exit dispatch.

148$:	MOVL	CDDB$L_ALLOCLS(R3),-		;  cloned a DDB that -might- have been
		DDB$L_ALLOCLS(R4)		;  a DDB with a port allocation class.
	BRB	144$				; Done.
;
;  Memory allocation failure attempting to create DDB.	Try again later.
;
150$:	UNLOCK_IODB				; Release IODB mutex,

	FORK_WAIT				;  wait a little while,

	CLRL	R0				;  then try looking again.
	BRW	30$

	.DISABLE LSB



	.SBTTL	DUTU$CHECK_DDB_FOR_CDDB - Verify DDB indicates correct CDDB
;++
;
;	DUTU$CHECK_DDB_FOR_CDDB - Verify DDB indicates correct CDDB
;
; Functional Description:
;
;	This routine is used after a call to DUTU$FIND_DDB to assure that
;	the DDB which was found is still correct for the current (input) CDDB.
;	Since DUTU$FIND_DDB may have forked, the class driver linkages may
;	have changed out from under the I/O database again.
;
;	First the systemid fields from the CDDB and DDB's SB are compared.
;	This will succeed for remote systems which match.  Next, the DDB's SB
;	is compared against the system local SB.  If this fails, then the
;	DDB is for a remote system, and the CDDB is unrelated.	At this point
;	the CDDB's SB is looked up.  If it is for a remote system (DDB pointer
;	is nonzero) then again no match.  Otherwise, both SBs are local.
;	Since two local SB's cannot contain the same DDB, this must be the
;	correct DDB, and therefore a match.  It would be desirable to check
;	that the PDT field from the CDDB is contained in one of the path
;	blocks in this final case, but since this thread may independently
;	continue running in the face of connection failures, this is not
;	possible.
;
; Inputs:
;
;	R3	CDDB address
;	R4	DDB  address
;
; Implicit Inputs:
;
;	DDB$L_SB(R4)		System block for DDB, either a remote system
;				  or SCS$AR_LOCALSB for local controllers
;	CDDB$B_SYSTEMID(R3)	Systemid for input CDDB
;
; Outputs:
;
;	R0	Status
;		   SS$_NORMAL means the DDB is current for the input CDDB
;		   SS$_ABORT means the DDB and CDDB are no longer associated
;
; Implicit Outputs:
;
;	R0 - R1 are destroyed.
;	All other registers are preserved.
;
;--

	ASSUME	SB$S_SYSTEMID	EQ 6
	ASSUME	CDDB$S_SYSTEMID GE 6

DUTU$CHECK_DDB_FOR_CDDB::

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

	MOVL	DDB$L_SB(R4), R1	; Get current SB from DDB.
	CMPL	SB$B_SYSTEMID(R1), -	; Compare low order portion
		CDDB$B_SYSTEMID(R3)	;  of system ids.
	BNEQ	10$			; If no match, check for local sysid.
	CMPW	SB$B_SYSTEMID+4(R1), -	; Compare high order portion
		CDDB$B_SYSTEMID+4(R3)	;  of system ids.
	BEQL	15$			; If system ids match, all done.

10$:	CMPL	G^SCS$AR_LOCALSB, R1	; Make sure DDB was for system local SB.
	BNEQ	30$			; If not, can't be a match.

	PUSHL	R2			; Save used register.

	CONFIG_SYS  CDDB$B_SYSTEMID(R3) ; Go find SB for this systemid.

	BLBC	R0, 40$			; Be annoyed if it doesn't exist.
	POPL	R2			; Restore register.  Now R1 = CDDB's SB.
	TSTL	SB$L_DDB(R1)		; There shouldn't be a DDB here.
	BNEQ	30$			; If DDB exists, CDDB's SB isn't local.

					;; In case we came over to this CDDB
					;; for the first time, make it right.
15$:	SUBB3	#^a/A/-1, -		;; Get controller letter bit offset
		DDB$T_NAME+3(R4), R1	;; for device.
	ROTL	R1, #1, R1		;; Transform bit number into mask.
	BISL	R1, -			;; Set bit for controller letter in
		CDDB$L_CTRLTR_MASK(R3)	;;  CDDB mask.

20$:	MOVZWL	#SS$_NORMAL, R0		; Success, DDB is for this CDDB.
	RSB

30$:	MOVZWL	#SS$_ABORT, R0		; Failure, DDB and CDDB are unrelated.
	RSB

40$:	BUG_CHECK INCONSTATE, FATAL	; CDDB contains non-existent system id.



	.SBTTL	DUTU$LINK_SEC_UCB - Link UCB to secondary DDB chain
;++
;
;	DUTU$LINK_SEC_UCB - Link UCB to secondary DDB chain
;
; Functional Description:
;
;	Search secondary UCB list pointed to by DDB referenced in input UCB and
;	link input UCB into list in ascending unit number order.
;
; Inputs:
;
;	R2	Address of UCB to be linked
;
; Implicit Inputs:
;
;	UCB$L_2P_DDB(R2)  Address of DDB on which UCB will be hung as a
;			    secondary UCB
;	UCB$W_UNIT(R2)	  Unit number for UCB
;
;	I/O database available for write access
;
; Outputs:
;
;	R0	SS$_NORMAL    ==> Link operation successful
;		SS$_OPINCOMPL ==> Link operation failed due to presence of UCB
;				    with same unit number
;	R1	Address of UCB following this one in the list
;	R2	Address of this UCB (same as input)
;	R3	Address of UCB preceding this one in the list
;
; Implicit Outputs:
;
;	UCB linked into secondary UCBs chain.
;
;	All non-output registers preserved.
;
;--

DUTU$LINK_SEC_UCB::

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

	MOVZWL	#SS$_OPINCOMPL, R0	; Assume failed link status.
	SUBL3	#<UCB$L_2P_LINK-DDB$L_2P_UCB>, -
		UCB$L_2P_DDB(R2), R1	; Get address of first UCB link.

20$:	MOVL	R1, R3			; Save address of previous UCB.
	MOVL	UCB$L_2P_LINK(R3), R1	; Get address of next UCB.
	BEQL	50$			; 0 ==> end-of-list reached; go insert.
	CMPW	UCB$W_UNIT(R2), UCB$W_UNIT(R1) ; Compare unit numbers.
	BGTRU	20$			; If new GT list, continue search.
	BEQL	90$			; If new EQ list, declare error.

50$:	MOVL	R1, UCB$L_2P_LINK(R2)	; Else, link UCB.  Forward link new UCB.
	MOVL	R2, UCB$L_2P_LINK(R3)	; Forward link previous UCB.
	MOVZWL	#SS$_NORMAL, R0		; Set successful link status

90$:	RSB				; and return



	.SBTTL	DUTU$LINK_UCB2CDDB - Link a UCB into a CDDB chain
;++
;
;	DUTU$LINK_UCB2CDDB - Link a UCB into a CDDB chain
;
; Functional Description:
;
;	The UCB in R5 is linked into the CDDB list of primary UCBs,
;	the CDDB is found through UCB$L_CDDB(R5).  The UCB list is
;	followed through UCB$L_CDDB_LINK, the listhead is CDDB$L_UCBCHAIN.
;
;	Due to fluctuating MSCPUNIT numbers from MSCP emulators, the chain
;	is not necessarily in ascending MSCP unit number order, although an
;	attempt is made to at least enter them that way when possible.
;	Duplicates are added after any other UCBs with the same MSCP unit
;	number.	 The only known duplicates at this time are multiple UCBs
;	with the reserved unit number of 7FFF, which indicates the need to
;	reacquire the true (possibly changed) unit number.  This should only
;	occur on VMS MSCP emulators.
;
; Inputs:
;
;	R5	UCB address
;	IPL	IPL$_SCS
;
; Implicit Inputs:
;
;	UCB$L_CDDB(R5)		address of the CDDB anchoring the UCB chain
;
; Outputs:
;
;	IPL	IPL$_SCS
;	R0 - R1 destroyed.
;	All other registers preserved.
;
; Implicit Outputs:
;
;	UCB linked into CDDB units chain.
;
;--

DUTU$LINK_UCB2CDDB::

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

	ADDL3	#<CDDB$L_UCBCHAIN -		; Initialize previous UCB
		 -UCB$L_CDDB_LINK>, -		; address.
		UCB$L_CDDB(R5), R0

10$:	MOVL	R0, R1				; Save previous UCB address.
	MOVL	UCB$L_CDDB_LINK(R1), R0		; Link to next UCB.
	BEQL	80$				; Branch if no more UCBs.
	CMPW	UCB$W_MSCPUNIT(R5), -		; Is this MSCP unit no. bigger
		UCB$W_MSCPUNIT(R0)		; than no. in this chain UCB?
	BGEQU	10$				; Branch if bigger or same.

80$:	MOVL	R0, UCB$L_CDDB_LINK(R5)		; Point new UCB to next UCB.
	MOVL	R5, UCB$L_CDDB_LINK(R1)		; Point previous UCB at new UCB.
	RSB



	.SBTTL	DUTU$SEVER_SEC_UCB - Unlink a secondary UCB
;++
;
;	DUTU$SEVER_SEC_UCB - Unlink a secondary UCB
;
; Functional Description:
;
;	Remove UCB in R5 from UCB secondary list (UCB$L_2P_LINK) of
;	the secondary DDB (UCB$L_2P_DDB).  The secondary DDB UCB chain
;	listhead is DDB$L_2P_UCB.
;
; Inputs:
;
;      R5	Address of UCB to be unlinked
;
; Implicit Inputs:
;
;	UCB$L_2P_DDB(R5) Address of DDB on which UCB is hung as a secondary UCB
;
;	I/O database available for write access
;
; Outputs:
;
;	R0 - R1 are destroyed.
;
; Implicit Outputs:
;
;	UCB unlinked from secondary UCBs chain.
;
; WARNINGS:
;
;	If the input UCB is not on the (secondary) chain of the secondary DDB,
;	this routine will bugcheck due to an accvio (thus, there is no special
;	case check made for that condition).
;
;--

DUTU$SEVER_SEC_UCB::

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

	SUBL3	#<UCB$L_2P_LINK-DDB$L_2P_UCB>, -
		UCB$L_2P_DDB(R5), R0	; Get address of first UCB link.

10$:	MOVL	R0, R1			; Save address of last UCB.
	MOVL	UCB$L_2P_LINK(R1), R0	; Get address of next UCB.
	CMPL	R0,R5			; Do the UCB addresses match?
	BNEQ	10$			; Branch and loop if no match.

	MOVL	UCB$L_2P_LINK(R5), -	; Else, remove UCB from UCB list.
		UCB$L_2P_LINK(R1)
	RSB



	.SBTTL	DUTU$GET_DEVNAM - Form new unit device name, ddcu
;++
;
;	DUTU$GET_DEVNAM - Form new unit device name, ddcu
;
; Functional Description:
;
;	Using the MSCP media identification (UCB$L_MEDIA_ID) and MSCP unit
;	number (UCB$W_MSCPUNIT) fields this routine builds a VMS device name
;	of the ddcu form.  The "ddc" is stored as a counted ASCII string based
;	at the address input in R3.  The "u" is stored as an unsigned integer
;	at UCB$W_UNIT in the input UCB.	 If the MSCP controller for the device
;	is a VMS MSCPserver, further device name information is present in the
;	MSCP unit identification area (UCB$Q_UNIT_ID).	The server specific and
;	non-emulator MSCP unit number representations are stored as appropriate.
;
; Inputs:
;
;	R3	base address to receive DDC counted ASCII string
;	R4	a prototype DDB address
;		  (used to determine the default controller letter "c")
;	R5	UCB address
;
; Implicit Inputs:
;
;	UCB$L_CDDB(R5)		CDDB address
;	UCB$L_MEDIA_ID(R5)	MSCP media identification
;	UCB$W_MSCPUNIT(R5)	MSCP unit number
;	UCB$Q_UNIT_ID(R5)	SLUN information (from VMS MSCP emulator)
;
;
;	- The MSCP media identification field is as defined in MSCP.
;
;
;	- The MSCP unit number has the following form:
;
;	      15   14  13			     0
;	    +----+----+-------------------------------+
;	    !SHAD!SLUN!		     UNIT	      !	 UCB$W_MSCPUNIT(R5)
;	    +----+----+-------------------------------+
;
;	    SHAD is MSCP$V_SHADOW and is set if this is a shadow virtual unit.
;		SHAD will not be set if the controller is the VMS MSCPserver.
;
;	    SLUN is MSCP$V_SLUN (for Server Local Unit Number), which indicates
;		this is a controller specific MSCP unit number for the
;		VMS MSCPserver.	 If the controller for the device is the
;		emulator, the SLUN bit is set.	Otherwise, it is clear.
;
;	    UNIT is the actual drive unit number if the controller is not the
;		VMS MSCP emulator, otherwise it is (together with the SLUN
;		bit) the Server Local Unit Number for this emulator.
;
;
;	- The unit identification area (supplied from the emulator) has more
;	    SLUN information in the following form:
;
;	     31					     0
;	    +-----------------------------------------+
;	    !		     ALLOCLASS		      !	 UCB$Q_UNIT_ID(R5)
;	    +--+-----+-----+-----+--------------------+
;	    !  !  D0 !	D1 !  C	 !	 UNIT	      !
;	    +--+-----+-----+-----+--------------------+
;	     31 30-26 25-21 20-16 15		     0
;
;	    ALLOCLASS is the allocation class for the remote served device.
;		This used to be the same as the allocation class for the
;		host node on which the VMS emulator is running. Now if the
;		device being served has a port allocation class, it could
;		be different.
;
;	    D0/D1/C are all constants from 1 to 26 inclusive representing
;		alphabetic characters A to Z respectively:
;		    D0 is the first letter in the device name ("Ddcu")
;		    D1 is the second letter in the name ("dDcu")
;		    C is the controller letter for the device name ("ddCu")
;
;	    UNIT is the actual drive unit number from the served controller,
;		used to form the VMS unit number ("ddcU")
;
;	    Bit 31 of the second longword is currently undefined and reserved.
;
; Outputs:
;
;	R0 is destroyed.
;	R2, R5 are returned unchanged.
;
; Implicit outputs:
;
;	(R3)			DDC counted ASCII string
;	UCB$W_UNIT(R5)		VMS unit number
;	UCB$W_LCL_MSCPUNIT(R5)	MSCP unit number for non-emulated controller
;	UCB$W_SRV_MSCPUNIT(R5)	Server Local Unit Number (SLUN) for emulator
;	DEV$M_LOC set in UCB$L_DEVCHAR2(R5) if this is a local controller
;
;	The "dd" portion of "ddc" is taken from D0 and D1 in the MSCP media
;	identifier (see the MSCP specification if this does not make sense).
;	(The SLUN information from the MSCP emulator partially duplicates
;	this information.)  If the MEDIA_ID fields are zero, a default name
;	is supplied based on the device class:	DU for disks, MU for tapes.
;
;	The "c" portion of "ddc" is formed as follows:
;	    - if not an emulated device:
;		- if MSCP$V_SHADOW is set, use "S"
;		- if MSCP$V_SHADOW is clear:
;		     if letter in prototype DDB is not "S", use letter in DDB;
;		     if letter in prototype DDB is "S", use "A";
;		     if "dd" portion is "DI", use "A".
;	    - if an emulated device, use letter defined in the SLUN unit
;		identification area "c".  (If none present, use the prototype
;		DDB letter as above.)
;
;	The unit number is derived as follows:
;	    - for non-emulated devices, UCB$W_MSCPUNIT without the SHADOW
;		or SLUN bits.
;	    - for emulated devices, the unit information in the SLUN unit id.
;
;	The MSCPUNIT field is copied into the SRV_MSCPUNIT or LCL_MSCPUNIT
;	    field, as appropriate for this controller (emulated or not,
;	    respectively).  For non-emulators, the SRV_MSCPUNIT field is
;	    cleared.  For emulators, the LCL_MSCPUNIT field is constructed
;	    for use in possible future failover processing.
;
;--

DUTU$GET_DEVNAM::

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

	MOVB	#3, (R3)			; Setup ddc string byte count.
	MOVW	#^a/DU/, 1(R3)			; Set default disk dd name.
	CMPB	#DC$_DISK, UCB$B_DEVCLASS(R5)	; Is this a disk?
	BEQL	10$				; Branch if a disk.
	MOVB	#^a/M/, 1(R3)			; Else change to default tape.

10$:	MOVZBL	DDB$T_NAME(R4), R0		; Get controller letter offset.
	MOVB	DDB$T_NAME(R4)[R0], 3(R3)	; Set default c controller name.
	CMPB	#^a/S/, 3(R3)			; Is default c an "S"?
	BNEQ	20$				; Branch if c is not an "S".
	MOVB	#^a/A/, 3(R3)			; Else change it to "A".

20$:	EXTZV	#MSCP$V_MTYP_D0, -		; Pickup first character of
		#MSCP$S_MTYP_D0, -		;  preferred device mnemonic
		UCB$L_MEDIA_ID(R5), R0		;  from MEDIA_ID.
	BEQL	30$				; Branch if no first char.
	ADDB3	#^a/A/-1, R0, 1(R3)		; Store first character.
	EXTZV	#MSCP$V_MTYP_D1, -		; Pickup second character of
		#MSCP$S_MTYP_D1, -		;  preferred device mnemonic
		UCB$L_MEDIA_ID(R5), R0		;  from MEDIA_ID.
	BEQL	30$				; Branch if no second char.
	ADDB3	#^a/A/-1, R0, 2(R3)		; Store second character.

30$:	MOVL	UCB$L_CDDB(R5), R0		; Get CDDB address.
	CMPB	#MSCP$K_CM_EMULA, -		; Rest of setup differs if
		CDDB$B_CNTRLMDL(R0)		;  this is the VMS emulator.
	BEQL	50$				; Branch if emulator.
;
;  Local (non-emulated) MSCP Controller
;
35$:	BISL	#DEV$M_LOC, UCB$L_DEVCHAR2(R5)	; Indicate local cntrlr exists.
	ASSUME	UCB$W_SRV_MSCPUNIT  EQ	UCB$W_LCL_MSCPUNIT+2
	MOVZWL	UCB$W_MSCPUNIT(R5), -		; Setup local MSCP unit number
		UCB$W_LCL_MSCPUNIT(R5)		;  and clear server unit number.
	BICW3	#<MSCP$M_SHADOW!MSCP$M_SLUN>, - ; Create VMS unit number
		UCB$W_MSCPUNIT(R5), -		;  from drive plug minus the
		UCB$W_UNIT(R5)			;  shadow bit (and slun bit).
	RSB					; Done for local device.
;
;  Served (VMS MSCPserver emulated) MSCP Controller
;
50$:	ASSUME	MSCP$W_SLUN_DEVNAME  EQ	 MSCP$Q_UNIT_ID+6
	EXTZV	#MSCP$V_SLUN_C, -		; Get controller letter from
		#MSCP$S_SLUN_C, -		;  slun devname field of
		UCB$Q_UNIT_ID+6(R5), R0		;  the unit id.
	BEQL	60$				; Branch if none (just in case).
	ADDB3	#^a/A/-1, R0, 3(R3)		; Set c in device name.

60$:	MOVW	UCB$W_MSCPUNIT(R5), -		; Copy the server local unit
		UCB$W_SRV_MSCPUNIT(R5)		;  number.
	ASSUME	MSCP$W_SLUN_UNIT  EQ  MSCP$Q_UNIT_ID+4
	MOVW	UCB$Q_UNIT_ID+4(R5), -		; Create the VMS unit number
		UCB$W_UNIT(R5)			;  from slun unit field.
	MOVW	UCB$Q_UNIT_ID+4(R5), -		; Setup what would have been
		UCB$W_LCL_MSCPUNIT(R5)		;  the local controller unit
	RSB					; Done for served device.



	.SBTTL	DUTU$GET_DEVTYPE - Translate MEDIA_ID to a device type
;++
;
;	DUTU$GET_DEVTYPE - Translate MEDIA_ID to a device type
;
; Functional Description:
;
;	This routine converts the MSCP media identifier from the input UCB
;	into a VMS device type which is stored in the input UCB.
;
; Inputs:
;
;	R3	UCB address
;
; Implicit Inputs:
;
;	UCB$L_MEDIA_ID(R3)	MSCP media identifier value to be converted
;	UCB$B_DEVCLASS(R3)	Device class, used if default type needed
;
;	MSCP media identifier to VMS device type conversion table
;	in PSECT $$$220_DEVTYPE_TABLE_01 (created by the MEDIA macro)
;
; Outputs:
;
;	UCB$B_DEVTYPE(R3)	VMS device type
;
;	All registers destroyed.
;
;--

DUTU$GET_DEVTYPE::
	.enabl lsb
	.JSB_ENTRY INPUT=   <	      R3      >,-
		   OUTPUT=  <		      >,-
		   SCRATCH= <		      >,-
		   PRESERVE=<	              >

	BBC	#DEV$V_DTN,-		; Is UCB currently DDRed?
		UCB$L_DEVCHAR2(R3),5$
	PUSHL	R3			; If so, remove its marking
	CALLS	#1,G^IOC$REMOVE_DEVICE_TYPE

5$:	MOVAB	DUTU$DEVTYPE_TABLE, R0	; Get conversion table address.
	BRW	20$			; Jump into table lookup loop.

10$:	TSTL	(R0)+			; Skip unused DEVTYPE value.
	TSTL	(R0)			; Check for end of table.
	BEQL	30$			; Branch if end of table.

20$:	CMPL	UCB$L_MEDIA_ID(R3), (R0)+; Is this MEDIA_ID in the table?
	BNEQ	10$			; Branch if not right id.
	MOVB	(R0), UCB$B_DEVTYPE(R3)	; Store that DEVTYPE.
;
; The following code differentiates between TA90 and TA90E devices. If the media
; identifier for the current device indicates that it is a TA90, a test is made to
; determine if its FORMAT MENU indicates that it supports data compaction. If it
; does, the device type is set to TA90E.
;
	CMPB	(R0),#DT$_TA90		; Is this a TA90 type device?
	BNEQ	25$			; NEQ implies no, so branch around.
	CMPB	UCB$W_TU_FORMENU(R3),-	; Low byte of format menu for TA90
		#<MSCP$M_TF_NOR!MSCP$M_TF_NDC>;  contains MSCP$M_TF_NOR bit set.
					;  TA90E also has MSCP$M_TF_NDC set.
	BNEQ	21$			; If NEQ, then we do not change DEVTYPE.
	MOVB	#DT$_TA90E, -
		UCB$B_DEVTYPE(R3)	; If TA90E, change DEVTYPE.
21$:	RSB
;
; Now that we have identified the device based on its media ID, determine if
; perhaps it is a generic SCSI device.	If so, the it is served by a VMS 
; system, send its server a GET UNIT NAME command.  If a name comes back, use 
; that as a dynamic name.
;
25$:	CMPB	#DT$_GENERIC_DK,(R0)	; Generic SCSI disk?
	BEQL	26$			; Do GET UNIT NAME
	CMPB	#DT$_GENERIC_MK,(R0)	; Generic SCSI tape?
	BNEQ	21$			; Ditto

26$:	BBSS	#UCB$V_GTUNMBSY, -	; Skip the call to get unit name
		UCB$L_DEVSTS(R3), 21$	;  if there's one already active.
	MOVL	UCB$L_DUTUFKBLINK(R3), R5; Get pointer to 1st perm UCB fork block
	MOVAB	FKB$K_LENGTH(R5), R5	; Fork on 2nd perm UCB fork block
;
;DUTU$ISSUE_GTUNM::
;
	MOVL	R3,R4			; Save UCB address
	MOVL	UCB$L_CDDB(R3),R3	; Get CDDB
	CMPB	#MSCP$K_CM_EMULA, -	; Is the controller for the system
		CDDB$B_CNTRLMDL(R3)	;  device a VMS MSCPserver?
	BEQL	100$			; Only VMS MSCPserver can do SCSI DDR
	MOVL	R4,R3			; Restore UCB
	BRW	29$			;  and exit

100$: 	MOVZWL	#IRP$K_LENGTH, R1	; Get size of an IRP/CDRP.

	ALLOCATE_POOL            
	BLBS	R0, 110$	 	; Branch if allocation succeeded.

	FORK_WAIT		  	; Wait awhile

	BRB	100$		  	; Try again

110$:	JSB	DUTU$INIT_IRP_CDRP	; Setup size and type fields.

	MOVAB	IRP$L_FQFL(R2),R5 	; Set up CDRP pointer
	MOVL	#CDRP$M_PERM, -		; Set perm bit so function exit
		CDRP$L_DUTUFLAGS(R5)	; won't post this irp.
;
; There are cases (SCSI comes to mind) where this code is executed but the CDT
;  hasn't been transferred to the UCB yet, but is available in the CDDB. We
;  need to get the current CDT for the UCB and our local CDRP.
;
        MOVL    CDDB$L_CDT(R3), -
                UCB$L_CDT(R4)           ; Put current CDT in UCB
	BEQL	281$			; Branch if no CDT
        MOVL    CDDB$L_CDT(R3), -
                CDRP$L_CDT(R5)          ; Put current CDT in our CDRP
	MOVL	R4,CDRP$L_UCB(R5)	; Squirrel away UCB address in CDRP
	MOVL	CDDB$L_PDT(R3),R4 	; Set PDT address
;
; R5 = CDRP (local)
; R4 = PDT
; R3 = CDDB
;
	ALLOC_RSPID			; Allocate a response_id.

	ALLOC_MSG_BUF			; Allocate a message buffer.
	BLBC	R0, 28$			; Branch if connection broken.
;
; R2 = MSCP message buffer
;
	INIT_MSCP_MSG			; Initialize message buffer for MSCP

	MOVL	R3,R1			; Move CDDB address for SEND MSCP MSG
	MOVL	CDRP$L_UCB(R5), R3	; Get UCB address for SEND MSCP MSG
	MOVW	UCB$W_MSCPUNIT(R3),-	; Set MSCP unit number in command
		MSCP$W_UNIT(R2)
	MOVB	#MSCP$K_OP_GTUNM,-	; GET UNIT NAME is the command
		MSCP$B_OPCODE(R2)
;
; R5 = CDRP (local)
; R4 = PDT
; R3 = UCB
; R2 = MSCP message buffer
; R1 = CDDB
;
	SEND_MSCP_MSG			; Send the message & wait for response
;
; Control is returned here after receipt of the END PACKET corresponding to
; the MSCP Get Unit Name command.  Control is regained via a callback from
; DU$IDR.
;
;
; 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>,-
		   OUTPUT=  <                 >,-
		   SCRATCH= <>,-
		   PRESERVE=<                 >

	CMPW	#SS$_ILLCDTST, R0	; Check for CDT error
	BEQL	28$			; In none, go to table
	ASSUME	MSCP$V_ST_MASK EQ 0
	BICW3	#^C<MSCP$M_ST_MASK>,-	; Extract major MSCP status
		MSCP$W_STATUS(R2),R0

	DISPATCH R0,TYPE=W,PREFIX=MSCP$K_ST_, < -
		<SUCC, 27$>, -
		<ICMD, 28$>, -
		>
	BRB	28$			; Ignore other statuses as well

27$:	TSTL	MSCP$L_DDR_NAMELEN(R2)	; See if a name was actually returned
	BEQL	28$			; No, just leave the device as generic
	PUSHAL	-(SP)			; Allocate return space for DTN pointer
	PUSHL	CDRP$L_UCB(R5)		; ioc$add_device_type(
	PUSHL	MSCP$L_DDR_NAMELEN(R2)	;   &name, namelen, &ucb, &(&dtn))
	PUSHAB	MSCP$T_DDR_NAME(R2)

	CALLS	#4,G^IOC$ADD_DEVICE_TYPE

	ADDL	#4,SP			; Clean the stack

28$:	DEALLOC_MSG_BUF			; Deallocate message buffer

	DEALLOC_RSPID			; Deallocate response id

281$:	MOVAL	CDRP$L_IOQFL(R5), R0	; Get pointer to IRP

	JSB	G^EXE$DEANONPAGED   	;  and deallocate it

29$:	MOVL	R3, R5			; Move UCB to R5 for FREE UCB
	BBC	#UCB$V_DELETEUCB, -	; If the UCB isn't marked for 
		UCB$L_STS(R5), 730$	;  removal, don't do it.
	MOVL	UCB$L_DUTUFKBLINK(R5), R0; Get pointer to top of list of

	JSB	G^EXE$DEANONPAGED	;  fork blocks & delete them.

.IF DF IRP$Q_QIO_P1			; If backporting prior to V7.0
	MOVL	UCB$PS_IO_COUNTERS(R5), R0; Get pointer to IOCNT structure
	BEQL	720$			; Skip deallocate if nothing there
	MOVL	#IOCNT$C_LENGTH, -	; Get Size of IOCNT structure
		FKB$W_SIZE(R0)		;  and set it for deallocate

	JSB	G^EXE$DEANONPAGED	; Deallocate it
720$:
.ENDC
	JSB	G^IOC$FREE_UCB		; Discard this UCB and

	BICL	#UCB$M_DELETEUCB, -	; clear the delete ucb bit.
		UCB$L_STS(R5)

730$:	BICL	#UCB$M_GTUNMBSY, -	; Clear the active bit.
		UCB$L_DEVSTS(R5)
	RSB				; and exit
;
; Didn't find media match:  use default based on device class.
;
30$:	MOVZBL	#DT$_GENERIC_DU, R0	; Assume generic disk type.
	CMPB	#DC$_DISK, -
		UCB$B_DEVCLASS(R3)	; Is this a disk?
	BEQL	40$			; Branch if a disk.
	MOVZBL	#DT$_GENERIC_TU, R0	; Else assume generic tape type.
	CMPB	#DC$_TAPE, -
		UCB$B_DEVCLASS(R3)	; Is this a tape?
	BEQL	40$			; Branch if a tape.
	CLRL	R0			; Else set unknown device.
	RSB				; Return.

40$:	MOVB	R0, UCB$B_DEVTYPE(R3)	; Store default device type.
;
; Decode the media ID into a printable string and use that for the device type 
;  name.
;
	PUSHL	R2			; Save a register
	SUBL	#12,SP			; Allocate workspace on the stack
	MOVL	SP,R2			; Set up working name string Bpointer
	MOVL	UCB$L_MEDIA_ID(R3),R0	; Get the media ID
	EXTZV	#MSCP$V_MTYP_A0,-	; Extract the first type letter
		#MSCP$S_MTYP_A0,R0,R1
	ADDB3	#^A/A/-1,R1,(R2)+	; Convert to alphabetic
	EXTZV	#MSCP$V_MTYP_A1,-	; Extract the second type letter
		#MSCP$S_MTYP_A1,R0,R1
	ADDB3	#^A/A/-1,R1,(R2)+	; Convert to alphabetic
	EXTZV	#MSCP$V_MTYP_A2,-	; Extract the third type letter
		#MSCP$S_MTYP_A2,R0,R1
	BEQL	45$			; If 0, optional character not present
	ADDB3	#^A/A/-1,R1,(R2)+	; Convert to alphabetic

45$:	BICL	#^C<MSCP$M_MTYP_N>,R0	; Isolate numeric portion of media ID
	DIVL3	#10,R0,R1		; Determine 10's digit
	ADDB3	#^A/0/,R1,(R2)+		; And convert to ASCII
	MULL	#10,R1			; Remove 10's from numeric portion
	SUBL	R1,R0			;   of media ID
	ADDB3	#^A/0/,R0,(R2)+		; Convert to ASCII
	SUBL	SP,R2			; Calculate name length
	PUSHAL	8(SP)			; Dummy DTN return field
	PUSHL	R3			; ioc$add_device_type(
	PUSHL	R2			;   &name, namelen, &ucb, &(&dtn))
	PUSHAB	12(SP)			; Name is now under 3 longwords

	CALLS	#4,G^IOC$ADD_DEVICE_TYPE

	ADDL	#12,SP			; Clean off stack
	POPL	R2			; Restore register
	RSB

	.dsabl lsb


	.SBTTL	DUTU$INIT_CONN_UCB - Initialize connection dependent UCB fields
;++
;
;	DUTU$INIT_CONN_UCB - Initialize connection dependent UCB fields
;
; Functional description:
;
;	This routine initializes those UCB fields which vary with connection.
;	It is called whenever the connection currently in use for a given UCB
;	changes.
;
; Inputs:
;
;	R3	CDDB address for "current" connection
;	R5	UCB address
;
; Implicit Inputs:
;
;	CDDB$L_CDT(R3)	CDT address for "current connection
;	CDDB$L_PDT(R3)	PDT address for "current connection
;
; Outputs:
;
;	All registers preserved.
;
; Implicit Outputs:
;
;	UCB$L_CDT(R5)	CDT address for "current connection
;	UCB$L_PDT(R5)	PDT address for "current connection
;
;--

DUTU$INIT_CONN_UCB::

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

	MOVL	R3, UCB$L_CDDB(R5)	; Establish new primary CDDB.
	MOVL	CDDB$L_CDT(R3), UCB$L_CDT(R5)	; Plant CDT address in UCB.
	MOVL	CDDB$L_PDT(R3), UCB$L_PDT(R5)	; Plant PDT address in UCB.
;
; Propagate CPU affinity from port to Class device UCB.
;
	PUSHL	R0				; Preserve R0.
	MOVL	CDDB$L_PDT(R3),R0		; R0 => PDT.
	MOVL	PDT$L_UCB0(R0),R0		; R0 => Port UCB.
	MOVL	UCB$L_AFFINITY(R0),-		; Copy port's CPU affinity
		UCB$L_AFFINITY(R5)		;  to Class Device.
.IF DF IRP$Q_QIO_P1				; If backporting prior to V7.0
;
; Propagate Fast Path CPU affinity from port to class device UCB. Fast Path
; is not enabled for tape devices for now.
;
	CMPB	#DC$_TAPE, -			; Is this a tape?
		UCB$B_DEVCLASS(R5)		;
	BEQL	50$				; Branch if tape.
	BICL	#UCB$M_FAST_PATH,UCB$L_STS(R5)	; Assume no Fast Path for device UCB
	BBC	#UCB$V_FAST_PATH,UCB$L_STS(R0),50$ ; Branch if Fast Path not enabled on port
	BISL	#UCB$M_FAST_PATH,UCB$L_STS(R5)	; Set Fast Path bit in device UCB
	MOVL	UCB$L_PORT_CPUDB(R0),-		; Clone Fast Path port CPU database
		UCB$L_PORT_CPUDB(R5)
.ENDC
50$:	POPL	R0				; Restore R0.

	RSB



	.SBTTL	DUTU$SETUP_CDP_UCB - Setup remote/local dual pathed device
;++
;
;	DUTU$SETUP_CDP_UCB - Setup remote/local dual pathed device
;
; Functional Description:
;
;	This routine scans the local I/O database for a possible local path to
;	an MSCP accessed device.  If such a local path (non-MSCP class driver)
;	is found, the MSCP path UCB is altered to point to the local UCB and
;	marked offline.	 The local UCB is also changed to point back to the
;	MSCP path UCB.
;
;	Non-MSCP devices cannot failover on the local node since there is
;	no communication between this driver and the local device driver.
;
; Inputs:
;
;	R3	CDDB address
;	R5	address of an MSCP UCB
;
; Implicit Inputs:
;
;	UCB$L_DDB(R5)	DDB address for the MSCP device
;	I/O database locked for scan access
;
; Outputs:
;
;	R0 - R2 destroyed.
;	All other registers preserved.
;
; Implicit Outputs:
;
;	If a suitable local UCB is found:
;
;	  In the MSCP UCB (address is R5):
;		UCB$L_2P_ALTUCB(R5)		is set to point to the local UCB
;		UCB$L_2P_DDB(R5)		is set to point to the local DDB
;		UCB$M_2P and UCB$M_CDP in UCB$L_DEVCHAR2(R5) are set
;		UCB$M_ONLINE in UCB$L_STS(R5)	is cleared
;		DEV$M_AVL in UCB$L_DEVCHAR	is cleared
;
;	  In the local UCB found:
;		UCB$L_2P_ALTUCB(local_UCB)	is set to point to the MSCP UCB
;		UCB$L_2P_DDB(local_UCB)		is set to point to the MSCP DDB
;		UCB$M_2P in UCB$L_DEVCHAR2(local_UCB) is set
;
; Notes:
;
;	This routine has been coded with a separate local UCB lookup subroutine
;	in the hope it can eventually be replaced with a call to an executive
;	routine.
;
;--

DUTU$SETUP_CDP_UCB::

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

	CMPB	#MSCP$K_CM_EMULA, -	; Only disks served by the MSCP server
		CDDB$B_CNTRLMDL(R3)	; can possibly have a local UCB.
	BNEQ	99$
;
;  The local UCB does not count if it represents local MSCP access to a
;  device, since such devices are already serviced by the class driver.
;
	PUSHR	#^M<R3,R4,R6,R7,R8>	; Save some registers.
	MOVL	UCB$L_DDB(R5), R0	; Get DDB address.
	MOVL	DDB$L_ALLOCLS(R0), R8	; Get allocation class.
	BEQL	90$			; Branch if allocation class not unique.
	MOVAB	DDB$T_NAME(R0), R7	; Get "DDC" address.
	MOVZWL	UCB$W_UNIT(R5), R6	; Get unit number.

	JSB	LOOKUP_LOCAL_UCB	; Attempt to find a local UCB.

	TSTL	R0			; Was a local UCB found?
	BEQL	90$			; Branch if no local UCB found.
	BBS	#DEV$V_MSCP, -		; Branch if the local UCB found
		UCB$L_DEVCHAR2(R0), 90$ ; is a class driver UCB.
	MOVL	R5, UCB$L_2P_ALTUCB(R0) ; Else point UCBs at each other.
	MOVL	R0, UCB$L_2P_ALTUCB(R5)
	.if	ndf,doswstuff
	BISL	#<DEV$M_CDP -		; Mark the MSCP UCB to show that there
		 !DEV$M_2P>, -		; is a local path to the device and
		UCB$L_DEVCHAR2(R5)	; it represents a dual pathed device.
	.iff
; If switching, we must NOT have CDP set or DUdriver will try to send I/O
; to the DK path.
	BISL	#<DEV$M_2P>, -		; Mark that there is a local path to
		UCB$L_DEVCHAR2(R5)	; a dual pathed device.
	.endc
	BISL	#DEV$M_2P, -		; Mark the local UCB to show that it
		UCB$L_DEVCHAR2(R0)	; represents a dual pathed device.
	MOVL	UCB$L_DDB(R0), -	; Setup alternate path DDB pointer
		UCB$L_2P_DDB(R5)	; in MSCP UCB.
	MOVL	UCB$L_DDB(R5), -	; Setup alternate path DDB pointer
		UCB$L_2P_DDB(R0)	; in local UCB.
	.if	ndf,doswstuff
	BICL	#UCB$M_ONLINE, -	; Mark the MSCP served UCB as being
		UCB$L_STS(R5)		; unusable.
	BICL	#DEV$M_AVL, -		; Make allocation and channel assignment
		UCB$L_DEVCHAR(R5)	; impossible on the MSCP server UCB.
	.endc

	.if	df,doswstuff
; If doing swdriver setup, send the pair of UCBs to a common routine
; to do the linkage. Note we are at fork level here.
	pushl	r5			; Pass second ucb
	pushl	r0			; Pass first ucb (preferred one)
	calls	#2,g^ioc_std$setup_switch ; go set the switch up
	.endc

90$:	POPR	#^M<R3,R4,R6,R7,R8>	; Restore registers.

99$:	RSB



	.SBTTL	DUTU$LOOKUP_LOCAL_UCB - Search for a local UCB.
;++
;
;	DUTU$LOOKUP_LOCAL_UCB - Search for a local UCB
;
; Functional Description:
;
;	This routine searches the local I/O database for a UCB whose
;	allocation class, and unit name (DDCn form), match those input.
;
; Inputs:
;
;	R6	desired unit number
;	R7	address of desired DDC counted ASCII string
;	R8	desired allocation class (presumably not zero)
;
; Outputs:
;
;	R0	address of a UCB meeting the criteria, or zero if none found
;
;	R0 through R4 are destroyed.
;	All other registers are preserved.
;
; Notes:
;
;	Hopefully, the fullness of time will allow this routine to be
;	replaced by a similar routine in the executive.
;
;--

DUTU$LOOKUP_LOCAL_UCB::
LOOKUP_LOCAL_UCB:

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

	ASSUME	DDB$L_LINK EQ 0
	MOVAL	G^IOC$GL_DEVLIST, R4	; Get initial DDB address.

10$:	MOVL	DDB$L_LINK(R4), R4	; Get next DDB.
	BEQL	90$			; Branch if no more DDBs.
	CMPL	R8, DDB$L_ALLOCLS(R4)	; Right allocation class?
	BNEQ	10$			; Branch if wrong allocation class.
	CMPB	(R7), DDB$T_NAME(R4)	; Right "DDC" string size?
	BNEQ	10$			; Branch if wrong "DDC" string size.
	MOVZBL	(R7), R1		; Get "DDC" string size.
	CMPC3	R1, 1(R7), -		; Right "DDC" name?
		DDB$T_NAME+1(R4)
	BNEQ	10$			; Branch if wrong "DDC" name.
	MOVAB	<DDB$L_UCB -		; Initialize UCB address.
		-UCB$L_LINK>(R4), R0

20$:	MOVL	UCB$L_LINK(R0), R0	; Get next UCB address.
	BEQL	90$			; Branch if no more UCBs.
	CMPW	R6, UCB$W_UNIT(R0)	; Is this the right unit?
	BGTRU	20$			; Branch if more units to check.
	BNEQ	90$			; Branch if not right unit.
	RSB				; Return if unit found.

90$:	CLRL	R0			; Indicate no UCB found.
	RSB				; Exit



	.SBTTL	----- CANCEL ROUTINES -----
	.SBTTL	Cancel-CDRP Definitions
;++
;
; Functional Description:
;
;	The special IRP/CDRP pair (the cancel-CDRP) allocated to represent a
;	cancel request is used only by the class driver.  It should never be
;	seen by any of the standard VMS I/O processing routines.  Therefore,
;	several IRP fields in the cancel-CDRP are resued to represent elements
;	of the cancel request.
;
;	Canceled IRP/CDRP pairs requiring an ABORT command also have a field
;	reused.	 It is the I/O queue forward link.  These IRP/CDRPs will be
;	returned to the control of cancel processing before that field is need
;	for another purpose.
;
;	These field redefinitions are accomplished below.  These definitions
;	apply only to the cancel routines which follow.
;
;--

;
; Define CDDB cancel queue links
;
ASSUME CDRP$L_IOQBL EQ <CDRP$L_IOQFL + 4>
ASSUME IRP$L_IOQBL EQ <IRP$L_IOQFL + 4>
CDRP$L_CANIOQFL = CDRP$L_IOQFL
CDRP$L_CANIOQBL = CDRP$L_IOQBL
IRP$L_CANIOQFL = IRP$L_IOQFL
IRP$L_CANIOQBL = IRP$L_IOQBL
;
; Define send an ABORT command queue header.
;
ASSUME CDRP$L_OBCNT EQ <CDRP$L_ABCNT + 4>
ASSUME IRP$L_OBCNT EQ <IRP$L_ABCNT + 4>
CDRP$L_SNDABTQFL = CDRP$L_ABCNT
CDRP$L_SNDABTQBL = CDRP$L_OBCNT
IRP$L_SNDABTQFL = IRP$L_ABCNT
IRP$L_SNDABTQBL = IRP$L_OBCNT
;
; Define ABORT sent queue header.
;
ASSUME CDRP$L_IOST2 EQ <CDRP$L_IOST1 + 4>
ASSUME IRP$L_IOST2 EQ <IRP$L_IOST1 + 4>
CDRP$L_ABTDQFL = CDRP$L_IOST1
CDRP$L_ABTDQBL = CDRP$L_IOST2
IRP$L_ABTDQFL = IRP$L_IOST1
IRP$L_ABTDQBL = IRP$L_IOST2
;
; Define place to save RSPID of canceled MSCP command.
;
CDRP$L_CAN_RSPID = CDRP$L_SEQNUM
IRP$L_CAN_RSPID = IRP$L_SEQNUM
;
; Define CANIO CDRP backpointer (for canceled IRP/CDRPs only).
;
CDRP$L_CANIOCDRP = CDRP$L_IOQFL
IRP$L_CANIOCDRP = IRP$L_IOQFL



	.SBTTL	DUTU$CANCEL - Cancel I/O Routine for class drivers
;++
;
;	DUTU$CANCEL - Cancel I/O Routine for class drivers
;
; Functional Description:
;
;	This is the driver cancel I/O routine for the disk and tape class
;	drivers.  It is called directly from the $CANCEL system service (due
;	to the vector information in the class driver DDT.
;
;	Cancel processing interacts with other significant class driver
;	operations including; device failover, reconnection processing, and
;	host initiated bad block replacement (for disks only).	Generally
;	speaking, these interactions are deliniated in this routine and the
;	routines which follow it.  However, the bad block replacement code is
;	in DUHIRT (due to the limited nature of its relivance).
;
;	The multi-threaded nature of the class drivers significantly
;	complicates processing for the cancel function.	 The requests to be
;	canceled must be located in any one of several different places.
;	Several different requests can be currently active.  Several requests
;	can be stalled waiting for resources.
;
;	As requests to be canceled are located they will either have an
;	associated MSCP request pending in the remote server, or for any one
;	of several reasons, they will not have associated MSCP server
;	requests.  When an associated MSCP server request exists, an MSCP
;	ABORT command must be sent to the remote server.  Otherwise, the
;	request can be completed immediately.
;
;	Before actually processing the cancel request, a check is maded to
;	determine whether or not a similar cancel request is still being
;	processed.  If a similar request is found, the routine exits without
;	performing any processing; the cancel request is redundant and can be
;	ignored.  Otherwise, further cancel processing is performed.
;
;	Next a IRP/CDRP pair is allocated to represent the cancel request.
;	Several fields in the IRP/CDRP are initialized, including the
;	CDRP$V_CANIO flag in CDRP$L_DUTUFLAGS which marks this IRP/CDRP as a
;	special IRP/CDRP representing a pending cancel request.	 Henceforth,
;	this special IRP/CDRP will be refered to as the cancel-CDRP.  The
;	cancel-CDRP is linked to the CDDB cancel queue (CDDB$L_CANCLQFL) so
;	that it can be located easily.	Failure to allocate a cancel-CDRP
;	results in immediate return to the $CANCEL system service without
;	having accomplished anything.
;
;	Now, all the little nooks and crannies in which class driver I/O
;	requests can be stashed are searched for IRP/CDRPs which need to be
;	canceled.  All IRP/CDRP pairs located are tested against the cancel
;	criteria.  An attempt is made to locate IRP/CDRPs holding no SCS
;	resources first, IRP/CDRPs waiting for some SCS resources next, and
;	IRP/CDRPs with assoicated MSCP requests last.
;
;	Those IRP/CDRPs which do not have an associated MSCP request are
;	immediately sent to I/O post processing.  Those IRP/CDRPs which do
;	have associated MSCP requests are marked as canceled (the CDRP$V_CAND
;	bit is set in CDDB$L_DUTUFLAGS) and queued to the send an ABORT
;	message queue hanging off of the cancel-CDRP (CDRP$L_SNDABTQFL).
;
;	N.B. no MSCP ABORT commands are sent until ALL IRP/CDRPs meeting the
;	cancel criteria are located and processed.
;
;	At this point, it is possible to adjust the wait counter based upon
;	the required adjustment factor accumulated as the IRP/CDRPs lookup and
;	processing performed above.  After adjusting the wait count, activity
;	on the device -- which was stalled due to an elevated wait count -- is
;	resumed.
;
;	Since all the IRP/CDRP pairs meeting the cancel criteria have been
;	located, it now is possible to issue the needed MSCP ABORT commands
;	in an asynchronous fork thread and return to the $CANCEL system
;	service.
;
;	Interactions with other class driver operations:
;
;	The major complication with other operations has to do with reforming
;	broken connections to the MSCP server.	Four distinct situations might
;	arise:
;
;	    o	the $CANCEL system service might be invoked during the middle
;		of a reconnection attempt.  In this case, cancelable requests
;		will be found on the restart queue (CDDB$L_RSTRTQFL) and
;		terminated immediately, as they do not have an associated MSCP
;		request.  A single cancelable request may be found to have an
;		associated MSCP request, as a result of single step mode.  It
;		must have an ABORT command sent to the remote MSCP server.
;
;	    o	the connection might fail during the lookup of IRP/CDRPs
;		requiring cancelation (i.e. before the ABORT commands can be
;		sent).	This condition is recognized by a failure to allocate
;		a message buffer to send the ABORT.  All the IRP/CDRPs to be
;		canceled have already been located and removed from normal
;		class driver work and SCS resource wait queues.	 Therefore,
;		they will not be automatically restarted; they only need to be
;		queued for I/O post processing.	 Then, the cancel operation
;		can simply be terminated.
;
;	    o	the connection might fail while and ABORT command is
;		outstanding.  This condition is detected by DUTU$INSERT_RESTRTQ
;		when it tries to insert a cancel-CDRP on the restart queue.
;		Instead of queueing the cancel-CDRP, all remaining IRP/CDRPs
;		to be canceled are queued for I/O post processing and the
;		cancel operation is terminated.
;
;	    o	the connection might fail after all the ABORT have completed
;		but before all the canceled I/O requests have terminated.
;		This is handled by reconnection processing.  At an appropriate
;		time, all canceled IRP/CDRPs are queued for I/O post
;		processing and the cancel operation is terminated.
;
;	There also is an interaction with device failover.  Because the
;	presence of incomplete cancel operations signals incomplete but soon
;	to be completed activity on a device and because it is not convenient
;	to move that activity to a new path, device failover is not permitted
;	when incomplete cancel operations are detected for the device.
;
;	The final, and perhaps most important, interaction with other class
;	driver operations occurs in MSCP end-packet processing.	 The common
;	MSCP end-packet processing routine tests the CDRP$V_CAND bit
;	associated with each incomming MSCP end-packet.	 If this bit is set,
;	DUTU$END_CAND_REQ is called, to test for possible completion of the
;	pending cancel operation.
;
; Inputs:
;
;	R2	Channel index number on which cancel is to be performed
;	R4	Process PCB address
;	R5	Device UCB address
;
;	IPL	UCB$B_FLCK's IPL, with fork spinlock held.
;
; Outputs:
;
;	R0 through R3 are destroyed.
;	All other registers are preserved.
;
;--

DUTU$CANCEL::

	$DRIVER_CANCEL_ENTRY FETCH=YES, PRESERVE=<R4,R5>

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

	TSTL	UCB$L_CDDB(R5)		; If we have no CDDB,
	BNEQ	5$			; or we are still
	BBC	#UCB$V_MSCP_INITING,-	; initing, just exit.
		UCB$L_DEVSTS(R5), 5$	;
	RET

5$:	BSBW	DUTU$CHECK_NOCANCEL	; Check for similar cancel request.
	BLBS	R0, 10$			; Branch if no similar cancel request
					; is in progress.
9$:	RET				; Else, exit doing nothing.

10$:	BSBW	DUTU$BUILD_CANIO_CDRP	; Build cancel-CDRP.
	BLBC	R0, 9$			; Branch if CDRP build failed.

	MOVL	R5, R3			; Move UCB address.
	MOVAB	IRP$L_FQFL(R1), R5	; Get cancel-CDRP address.
;
; Search for IRP/CDRP pairs meeting the cancel criteria.
; Search through the restart queue for IRP/CDRPs to cancel.
;
	ADDL3	#CDDB$L_RSTRTQFL, -	; Get restart queue header address.
		UCB$L_CDDB(R3), R1
	MOVL	R1, R2			; Copy header address.

110$:	MOVL	CDRP$L_FQFL(R2), R2	; Link to next restart CDRP.
	CMPL	R2,R1			; End of queue?
	BEQL	150$			; Branch if end of restart queue.

	IFNOCANCEL cdrp=(R2), then=110$ ; Branch if don't cancel this CDRP.

	REMQUE	(R2), R0		; Dequeue CDRP & get address in R0.

	POST_CDRP status=SS$_CANCEL	; Insert packet in IOPOST queue.

	BRW	110$
;
; Search the RDT wait queue and the RDT itself.
;
150$:	MOVL	CDRP$L_CDT(R5), R3	; Get CDT address for SCS searches.
	PUSHL	R4			; Save PCB address
	MOVL	CDT$L_PDT(R3),R4	; Get PDT address from CDT
	MOVL	R5, R1			; Copy CANIO CDRP addr. for SCS search.

	SCAN_RSPID_WAIT -		; Scan RDT wait queue.
		action = DUTU$CANCEL_RDTWAIT

	SCAN_RDT -			; Scan RDT.
		action = DUTU$CANCEL_RDT

	MOVL	R1, R5			; Restore CANIO CDRP address to R5.
	POPL	R4			; Restore PCB address
;
; Update wait count, and if necessary, unstall UCB.
;
	MOVW	CDRP$W_DUTUCNTR(R5), R0 ; Get number of decrements to do.
	BEQL	200$			; Branch if no decrements required.
	MOVQ	R4, -(SP)		; Save registers of interest.
	MOVL	CDRP$L_UCB(R5), R5	; Get UCB to unstall.
	SUBW	R0, UCB$W_RWAITCNT(R5)	; Decrement wait count.

	JSB	G^SCS$UNSTALLUCB	; Start up any stalled requests.

	MOVQ	(SP)+, R4		; Restore registers.
;
; If MSCP ABORT commands are required, start a fork thread to send them.
; Otherwise, simply end the cancel request now.
; In any case, return to the $CANCEL system service.
;
200$:	MOVAB	CDRP$L_SNDABTQFL(R5), R0; Get send-abort queue header.
	CMPL	R0, (R0)		; Test for an empty queue.
	BNEQ	250$			; Branch if queue not empty.
	PUSHL	CDRP$L_UCB(R5)		; Else, save UCB address.

	BSBW	DUTU$END_CANCEL		; End cancel operation now.

	POPL	R5			; Restore UCB address.
	RET				; Return to cancel system service.

250$:	PREPARE_TO_FORK -
		SEND_ABORTS, -		; Prepare for control to return here
		FRKBLK = (R5), -	;  when fork occurs.
		MASK = <^M<R4>>		; Save this register.

	MOVL	CDRP$L_UCB(R5), R5	; Restore UCB address in R5.
	RET				; Return to $CANCEL system service.



	.SBTTL	DUTU$SEND_ABORTS - Send ABORT commands for active MSCP requests
;++
;
;	DUTU$SEND_ABORTS - Send ABORT commands for active MSCP requests
;
; Functional Description:
;
;	This independent fork thread sends MSCP ABORT commands for every CDRP
;	on a cancel-CDRP SNDABT queue.	The cancel-CDRP is used as a fork
;	block for these operations.  Each CDRP queued to CDRP$L_SNDABTQFL is
;	removed from that queue and queued to CDRP$L_ABTDQBL, the ABORT sent
;	queue.	Then an MSCP ABORT command is sent to the remote server.
;	This proceeds until CDRP$L_SNDABTQFL is empty.
;
; Inputs:
;
;	R5	cancel-CDRP address
;
; Outputs:
;
;	R3
;	R4
;	R5
;
;	R0 through R5 destroyed.
;
;--

DUTU$SEND_ABORTS::
SEND_ABORTS:

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

	REMQUE	@CDRP$L_SNDABTQFL(R5), R1	; Get CDRP to abort.
	BVC	10$				; Branch if CDRP was found.
	CLRQ	CDRP$L_FQFL(R5)			; Signal cancel-CDRP not queued.
	MOVQ	R4, -(SP)			; Setup for next routine.
	BRW	ABORTS_DONE_TEST_CANCEL		; Go check for cancel operation
						; done, and kill fork thread.
10$:	INSQUE	(R1), @CDRP$L_ABTDQBL(R5)	; Queue "aborted" CDRP.
                                               
	MOVL	CDRP$L_RSPID(R1), -		; Save RSPID to abort.
		CDRP$L_CAN_RSPID(R5)
	MOVL	CDRP$L_UCB(R5), R3		; Get UCB address.
	MOVL	UCB$L_PDT(R3), R4		; Get PDT address.
	BBC	#CDRP$V_CONNWALK, -		; Branch if the request being
		CDRP$L_DUTUFLAGS(R1), 20$	; aborted is not a conn. walker.
	MOVL	CDRP$L_WALK_CDDB(R1), R0	; Else, get walking CDDB addr.
	MOVL	CDDB$L_PDT(R0), R4		; Use that to get the right PDT.

20$:	ALLOC_RSPID				; Allocate RSPID for ABORT cmd.

	ALLOC_MSG_BUF				; Allocate a message buffer too.
	BLBS	R0, 30$				; Branch if connection still OK.

	BSBW	DUTU$END_CANCEL			; Complete cancel operation

	RSB					; and terminate fork thread.

30$:	INIT_MSCP_MSG ucb=(R3)			; Initialize ABORT message.

	MOVB	#MSCP$K_OP_ABORT, -		; Set ABORT MSCP opcode.
		MSCP$B_OPCODE(R2)
	MOVL	CDRP$L_CAN_RSPID(R5), -		; Set RSPID to abort.
		MSCP$L_OUT_REF(R2)
	MOVL	UCB$L_CDDB(R3), R1		; Get CDDB of controller.

	SEND_MSCP_MSG				; Send off the abort.
;
; 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>,-
		   OUTPUT=  <                 >,-
		   SCRATCH= <>,-
		   PRESERVE=<                 >

	CMPW	#SS$_ILLCDTST, R0		; Check for CDT error
	BNEQ	40$				; Branch if connection still OK.

	BSBW	DUTU$END_CANCEL			; Complete cancel operation

	RSB					; and terminate fork thread.

40$:	MOVL	#1, R0				; Don't unmap port buffers.

	BSBW	DUTU$DEALLOC_ALL		; Deallocate RSPID & msg. buf.

	BSBW	SEND_ABORTS			; Repeat until no more CDRPs

	RSB					; to be canceled.



	.SBTTL	DUTU$CHECK_NOCANCEL - Check for no similar cancel requests
;++
;
;	DUTU$CHECK_NOCANCEL - Check for no similar cancel requests
;
; Functional Description:
;
;	This routine scans the CDDB in-progress "cancel operations" queue
;	looking for cancel operations that match those described by the other
;	input parameters. If no similar cancel operation is found, success
;	status is returned.  If one or more similar cancel operations are
;	found, failure status is returned. The input criteria may describe an
;	entire cancel operation or just the relevant UCB.
;
; Inputs:
;
;	R2	Channel index for cancel operation (not relevent unless R4 is
;						    non-zero)
;	R4	PCB address or zero (if PID and channel index tests are not to
;				     be done)
;	R5	UCB address
;
; Implicit inputs:
;
;	UCB$L_CDDB(R5)	CDDB address
;
; Outputs:
;
;	R0	Status
;		SS$_NORMAL  -  no similar cancel operation found
;		0	    -  similar cancel operation found
;	R2, R3	Preserved
;
;--

DUTU$CHECK_NOCANCEL::

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

	ADDL3	#CDDB$L_CANCLQFL, -		; Get CDDB cancel queue header
		UCB$L_CDDB(R5), R0		; address.
	MOVL	R0, R1				; Copy that address.

10$:	MOVL	IRP$L_CANIOQFL(R0), R0		; Link to next CANIO CDRP.
	CMPL	R0, R1				; Is this end of queue.
	BEQL	100$				; Branch if end of queue.
	CMPL	R5, IRP$L_UCB(R0)		; Is this the right UCB?
	BNEQ	10$				; Branch if wrong UCB.
	TSTL	R4				; Are full tests needed?
	BEQL	90$				; Branch if full tests unneeded.
	CMPL	PCB$L_PID(R4), IRP$L_PID(R0)	; Is this the right PID?
	BNEQ	10$				; Branch if wrong PID.
	CMPL	R2, IRP$L_CHAN(R0)		; Is this the right channel?
	BNEQ	10$				; Branch if wrong channel index.
;
; Oops.	 Found a similar cancel request.
;
90$:	CLRL	R0				; Signal similar request found.
	RSB					; Return error.
;
; Goodie.  No similar request found.
;
100$:	MOVL	#SS$_NORMAL, R0			; Signal no request found.
	RSB					; Return success.



	.SBTTL	DUTU$BUILD_CANIO_CDRP - Allocate & Initialize a cancel-CDRP
;++
;
;	DUTU$BUILD_CANIO_CDRP - Allocate & Initialize a cancel-CDRP
;
; Functional Description:
;
;	This routine allocates space for and initializes a cancel-CDRP, the
;	IRP/CDRP which represents a cancel operation in progress.  In addition,
;	this routine links the new cancel-CDRP to the CDDB queue of active
;	cancel-CDRPs.
;
;	If the cancel-CDRP cannot be allocated, an error status is returned in
;	R0.  Otherwise, a success status is returned.
;
; Inputs:
;
;	R2	Channel index number on which cancel is to be performed
;	R4	Process PCB address
;	R5	Device UCB address
;
; Outputs:
;
;	R0	SS$_NORMAL cancel-CDRP built without incident
;		SS$_INSFMEM insufficient non-paged pool to build cancel-CDRP
;	R1	IRP base address of cancel-CDRP (i.e. R1 + IRP$L_FQFL =
;		cancel-CDRP base address)
;
;	All other registers are preserved.
;
;--

DUTU$BUILD_CANIO_CDRP::

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

	PUSHR	#^M<R2>			; Save register
	MOVL	#IRP$C_LENGTH, R1	; Get size of an IRP/CDRP.

	ALLOCATE_POOL   
	BLBC	R0, 900$		; Branch if allocation failed.

	JSB	DUTU$INIT_IRP_CDRP 	; Setup size and type fields.

	MOVL	R2,R1		   	; Move IRP to R1
	POPR	#^M<R2>		   	; Restore register
	MOVL	PCB$L_PID(R4), -	; Save canceling process' PID.
		IRP$L_PID(R1)
	MOVL	R5, IRP$L_UCB(R1)	; Save UCB of cancel target device.
	MOVL	UCB$L_CDT(R5), -
		IRP$L_CDT(R1)	   	; Put current CDT in IRP
	MOVZWL	R2, IRP$L_CHAN(R1)	; Save cancel channel index.
	MOVAB	IRP$L_SNDABTQFL(R1), -	; Initialize the send an ABORT queue.
		IRP$L_SNDABTQFL(R1)
	MOVAB	IRP$L_SNDABTQFL(R1), -
		IRP$L_SNDABTQBL(R1)
	MOVAB	IRP$L_ABTDQFL(R1), -	; Initialize the ABORT sent queue.
		IRP$L_ABTDQFL(R1)
	MOVAB	IRP$L_ABTDQFL(R1), -
		IRP$L_ABTDQBL(R1)
	MOVAB	UCB$W_RWAITCNT(R5), -	; Point CDRP to wait counter.
		IRP$L_RWCPTR(R1)
	MOVL	#CDRP$M_CANIO, -	; Mark this as a cancel-CDRP.
		IRP$L_DUTUFLAGS(R1)
	MOVAB	IRP$L_FQFL(R1),R0	; Get start of CDRP

	SCS_INIT_CDRP CDRP=R0		; Perform SCS init of CDRP

	MOVL	UCB$L_CDDB(R5), R0	; Locate the cancel CDDB.
	INSQUE	IRP$L_CANIOQFL(R1), -	; Insert cancel-CDRP onto CDDB
		@CDDB$L_CANCLQBL(R0)	; queue of cancel-CDRPs.
	MOVL	#SS$_NORMAL, R0		; Setup success status.
	RSB				; Return.

900$:	POPR	#^M<R2>		   	; Error exit -- restore register
	RSB				; Return.



	.SBTTL	DUTU$CANCEL_RDTWAIT - Cancel a thread on the RDT wait queue
;++
;
;	DUTU$CANCEL_RDTWAIT - Cancel a thread on the RDT wait queue
;
; Functional Description:
;
;	This is the action routine for SCAN_RSPID_WAIT, the SCS service which
;	scans the RDT wait queue for entries belonging to a given connection.
;	After determining that a request presented for processing by this
;	routine meets the cancel criteria, this routine performs those
;	operations necessary to cancel the request.
;
;	Since a RSPID is a required SCS resource for all MSCP activities,
;	requests stuck on the RDT wait queue (i.e. requests in a RSPID wait
;	state), cannot have active MSCP requests outstanding to the server.
;	Therefore, this routine need only count the need to adjust the wait
;	count, remove the CDRP from the RDT wait queue, and queue the CDRP
;	for I/O post processing.
;
; Inputs:
;
;	R1	cancel-CDRP address
;	R3	CDT address
;	R4	PDT address
;	R5	address of a CDRP belonging to the connection on which the
;		cancel request was made
;
; Outputs:
;
;	R0, R2, and R5 are destroyed.
;	All other registers are preserved.
;
;	If the cancel criteria is satisfied by the CDRP that was taken off the
;	RDT wait queue, it is posted with SS$_CANCEL status.
;
;--

DUTU$CANCEL_RDTWAIT::

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

	MOVL	R5, R2			; Copy waiting CDRP address.
	MOVL	R1, R5			; Copy cancel-CDRP address.

	IFNOCANCEL cdrp=(R2), then=10$	; Branch if CDRP shouldn't be canceled.

	INCW	CDRP$W_DUTUCNTR(R5)	; Account for wait counter bump due
					; to RDT wait state.
	MOVL	R2, R5			; MOVE RDT CDRP INTO R5

	CANCEL_WAIT -			; Cancel CDRP Resource Wait State but
		RWCPTR_UPD=NO		; don't update Resource Wait counter now

	MOVL	R2, R0			; Move RDT CDRP to R0 for posting

	POST_CDRP status=SS$_CANCEL	; queue it for I/O post processing.

10$:	RSB				; Return to RDT wait queue scan.



	.SBTTL	DUTU$CANCEL_RDT - Cancel a thread holding a RSPID
;++
;
;	DUTU$CANCEL_RDT - Cancel a thread holding a RSPID
;
; Functional Description:
;
;	This is the action routine for SCAN_RDT, the SCS service which scans the
;	Response-id Descriptor Table (RDT) for entries belonging to a given
;	connection.  After determining that a request presented for processing
;	by this routine meets the cancel criteria, this routine performs those
;	operations necessary to cancel the request.
;
;	Class driver function threads found in the RDT may or may not
;	currently have MSCP transactions in progress.  Before canceling the
;	thread found in the RDT, the presence or absence of an active MSCP
;	transaction must be determined.	 This is done by searching the CDDB
;	queue of threads with active transactions for the CDRP presented for
;	processing.
;
;	If the CDRP is not found in the CDDB queue, it does not have an active
;	MSCP transaction.  Therefore, it can be canceled by counting the need
;	to adjust the wait count, removing the CDRP from the whatever wait
;	queue it is on, and queuing the CDRP for I/O post processing.
;
;	If the CDRP is found in the CDDB queue, it does have an active MSCP
;	transaction and that transaction must eventually have an MSCP ABORT
;	command issued against it.  This is done by removing the CDRP from the
;	CDDB queue and queueing it to the send-abort queue linked to the
;	cancel-CDRP.  Entries are placed in the send-abort queue in request
;	sequence number order.	This causes the MSCP ABORT requests to be
;	issued in the same order as the MSCP commands which they abort were
;	issued.	 This is necessary for proper tape class driver operation.
;
; Inputs:
;
;	R1	cancel-CDRP address
;	R3	CDT address
;	R4	PDT address
;	R5	address of a CDRP belonging to the connection on which the
;		cancel request was made
;
; Outputs:
;
;	R0, R2, and R5 are destroyed.
;	All other registers are preserved.
;
;--

DUTU$CANCEL_RDT::

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

	MOVL	R5, R2			; Copy RDT CDRP address.
	MOVL	R1, R5			; Copy cancel-CDRP address.

	IFNOCANCEL cdrp=(R2), then=90$	; Branch if CDRP shouldn't be canceled.

	MOVL	CDRP$L_UCB(R5), R1	; Get UCB address.
	MOVL	UCB$L_CDDB(R1), R1	; Get CDDB address.
	ASSUME	CDDB$L_CDRPQFL EQ 0
	ASSUME	CDRP$L_FQFL    EQ 0
	MOVL	R1, R0			; Initialize "previous" CDRP address.

10$:	MOVL	CDRP$L_FQFL(R0), R0	; Link to next CDRP.
	CMPL	R0, R1			; Reached end of CDDB queue yet?
	BEQL	50$			; Branch if reached end of queue.
	CMPL	R0, R2			; Found CDRP to be canceled?
	BNEQ	10$			; Branch if CDRP not found.
;
; The CDRP to be canceled has an active MSCP transaction.
;
.IF DF IRP$Q_QIO_P1			; If backporting prior to V7.0
	PUSHL	R5			; Save R5
	MOVL	R2,R5			; Pass CDRP to repossess in R5

	REPO_CDRP			; Repossess the CDRP

	POPL	R5			; Restore R5
.ENDC
	REMQUE	(R2), R2		; Remove CDRP from CDDB queue.
	BISL	#CDRP$M_CAND, -		; Flag CDRP as "canceled."
		CDRP$L_DUTUFLAGS(R2)
	MOVL	R5, CDRP$L_CANIOCDRP(R2); Set back pointer to cancel-CDRP.
	MOVAB	CDRP$L_SNDABTQFL(R5), R1; Get header for send-abort queue.
	MOVL	R1, R0			; Initialize "previous" CDRP address.

30$:	MOVL	CDRP$L_FQFL(R0), R0	; Link to next CDRP.
	CMPL	R0, R1			; Reached end of send-abort queue yet?
	BEQL	35$			; Branch if reached end of queue.
	CMPL	CDRP$L_SEQNUM(R2), -	; Is canceled-CDRP seqnum less than
		CDRP$L_SEQNUM(R0)	; current send-abort seqnum?
	BGEQU	30$			; If not, loop until it is.

35$:	INSQUE	(R2), @CDRP$L_FQBL(R0)	; Insert canceled-CDRP before current
					; send-abort queue entry.
	BRW	80$			; Join common exit code.
;
; The CDRP to be canceled does not have an active MSCP transaction.
;
50$:	INCW	CDRP$W_DUTUCNTR(R5)	; Account for wait counter bump due
	MOVL	R2, R5			; to RDT wait state.

	CANCEL_WAIT -			; Cancel Resrce Wait State for RDT CDRP
		RWCPTR_UPD=NO		; don't update Resource Wait counter now

	MOVL	R2, R0			; Move RDT CDRP address for posting

	POST_CDRP status=SS$_CANCEL	; queue it for I/O post processing.

80$:	MOVL	R5, R1			; Restore cancel-CDRP address.

90$:	RSB				; Return to RDT scan.



	.SBTTL	DUTU$TEST_CANCEL_DONE - Test for completed cancel operation
;++
;
;	DUTU$TEST_CANCEL_DONE - Test for completed cancel operation
;
; Functional Description:
;
;	This routine is called by MSCP end message processing whenever an MSCP
;	end message is associated with a CDRP which has the CDRP$M_CAND bit
;	set in CDRP$L_DUTUFLAGS.  The presence of that flag indicates that a
;	cancel-CDRP is waiting for canceled requests to complete.
;
;	It is assumed that the input CDRP has been removed from whatever queue
;	it was on when the MSCP message was received.
;
;	This routine determines whether or not all canceled requests for the
;	cancel-CDRP associated with the input CDRP (which represents one of
;	those canceled requests) have completed.  If they all have completed,
;	the cancel operation is ended.	Otherwise, no action is taken.
;
; Inputs:
;
;	R5	CDRP address (for a canceled request)
;
; Outputs:
;
;	All registers are preserved.
;
;--

DUTU$TEST_CANCEL_DONE::

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

	MOVQ	R4, -(SP)			; Save some registers.
	MOVL	CDRP$L_CANIOCDRP(R5), R5	; Get cancel-CDRP address.
	BISL3	CDRP$L_FQFL(R5), -		; Is CANIO CDRP queued to
		CDRP$L_FQBL(R5), R4		; anything?
	BNEQ	80$				; Branch if CANIO CDRP queued.
	MOVAB	CDRP$L_SNDABTQFL(R5), R4	; Get send-abort queue header.
	CMPL	R4, (R4)			; Is send-abort queue empty?
	BNEQ	80$				; Branch if queue not empty.
	MOVAB	CDRP$L_ABTDQFL(R5), R4		; Get abort-sent queue header.
	CMPL	R4, (R4)			; Is abort-sent queue empty.
	BNEQ	80$				; Branch if queue not empty.

	JSB	DUTU$END_CANCEL			; Cancel operation is finished.

80$:	MOVQ	(SP)+, R4			; Restore saved registers.
	RSB					; Return.



	.SBTTL	ABORTS_DONE_TEST_CANCEL - Check cancel done after aborts done
;++
;
; ABORTS_DONE_TEST_CANCEL- Check cancel done after aborts done
;
; Functional Description:
;
;	This special entry point receives control from the SEND_ABORTS fork
;	thread after all needed MSCP ABORT commands have been sent to the
;	controller.  Sending the last ABORT command may be all that is
;	requried to complete the cancel operation.  This entry point tests
;	that possibility and terminates the cancel operation, if appropriate.
;
; Inputs:
;
;	R5	CANIO CDRP address
;	 (SP)	Saved R4
;	4(SP)	Saved R5
;	8(SP)	Return address
;
; Outputs:
;
;	R4 & R5 are restored from the stack. ***???***
;	    N.B. R5 probably points to the deallocated CANIO CDRP.  It cannot
;	    be used to access said CDRP.
;	All registers are preserved.
;
;--

ABORTS_DONE_TEST_CANCEL:

	MOVAB	CDRP$L_SNDABTQFL(R5), R4	; Get send-abort queue header.
	CMPL	R4, (R4)			; Is send-abort queue empty?
	BNEQ	80$				; Branch if queue not empty.
	MOVAB	CDRP$L_ABTDQFL(R5), R4		; Get abort-sent queue header.
	CMPL	R4, (R4)			; Is abort-sent queue empty.
	BNEQ	80$				; Branch if queue not empty.

	JSB	DUTU$END_CANCEL			; Cancel operation is finished.

80$:	MOVQ	(SP)+, R4			; Restore saved registers.
	RSB					; Return.



	.SBTTL	DUTU$DISCONNECT_CANCEL - Do disconnect cleanup for cancels
;++
;
;	DUTU$DISCONNECT_CANCEL - Do disconnect cleanup for cancel requests
;
; Functional Description:
;
;	This routine is called to perform those cancel functions required
;	whenever a connection breaks.  For each cancel-CDRP on the CDDB cancel
;	opeations queue, this routine performs one of two cleanup tasks:
;
;	   o	If the cancel-CDRP has its fork block queued somewhere, it is
;		simply removed from the CDDB cancel operations queue.  Some
;		other portion of the broken connection cleanup will discover
;		the cancel-CDRP in whatever queue its fork block is hung.  The
;		cancel-CDRP will then be delivered to DUTU$INSERT_RESTRTQ
;		which will complete the needed cancel cleanup.	This cleanup
;		is done in this way because this routine cannot know the
;		proper connection-dependent cleanup required by the
;		cancel-CDRP as a result of its in progress SCS activities.
;		N.B. proper operation of this test depends upon SEND_ABORTS
;		clearing the cancel-CDRP fork block queue links when it is
;		done sending the needed MSCP ABORT commands.
;
;	    o	If the cancel-CDRP fork block is not queue anywhere, the
;		cancel-CDRP is dequeued from the CDDB cancel operations queue
;		and the cancel operation it represents is ended now.  All
;		CDRPs linked to the cancel-CDRP (awaiting completion of cancel
;		processing) are immediately queued for I/O post processing.
;
; Inputs:
;
;	R3	CDDB address
;
; Outputs:
;
;	R0 and R5 are destroyed.
;	All other registers are preserved.
;
;--

DUTU$DISCONNECT_CANCEL::

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

10$:	REMQUE	@CDDB$L_CANCLQFL(R3), R5	; Get a cancel-CDRP.
	BVS	90$				; Branch if no cancel-CDRPs.

	ASSUME	IRP$L_CANIOQFL EQ IRP$L_IOQFL
	ASSUME	IRP$L_IOQFL EQ 0
	BISL3	IRP$L_FQFL(R5), -		; Is CDRP fork block queue?
		IRP$L_FQBL(R5), R0		; If so, skip further
	BNEQ	10$				; processing.
	MOVAB	IRP$L_FQFL(R5), R5		; Else, cleanup cancel

	JSB	DUTU$CLEANUP_CANCEL		; operation.

	BRW	10$				; Loop till no more CDRPs.

90$:	RSB					; Return.



	.SBTTL	DUTU$END_CANCEL - Finish a cancel operation
	.SBTTL	DUTU$CLEANUP_CANCEL - Cleanup a cancel operation
;++
;
;	DUTU$END_CANCEL - Finish a cancel operation
;	DUTU$CLEANUP_CANCEL - Cleanup a cancel operation
;
; Functional Description:
;
;	These routines are called to end a cancel operation that is currently
;	in progress.  Both routines guarantee that all canceled requests not
;	previously posted for I/O post processing (or otherwise disposed of)
;	are queued for I/O post processing.  Both routines deallocate the
;	cancel-CDRP.
;
;	DUTU$END_CANCEL removes the cancel-CDRP from the CDDB cancel
;	operations queue.  DUTU$CLEANUP_CANCEL does not.
;
; Inputs:
;
;	R5	cancel-CDRP address
;
; Implicit Inputs:
;
;	CDRP$L_SNDABTQFL(R5)	send-abort queue header
;	CDRP$L_ABTDQFL(R5)	abort-sent queue header
;	CDRP$L_CANIOQFL(R5)	CDDB cancel operations queue links
;
; Outputs:
;
;	All registers preserved.
;	CDRP pointed to by R5 is deallocated.
;
;--

DUTU$END_CANCEL::

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

	REMQUE	CDRP$L_CANIOQFL(R5), -	; Remove cancel-CDRP from CDDB cancel
		-(SP)			; operations queue.
	TSTL	(SP)+			; Cleanup stack.

	BSBW	DUTU$CLEANUP_CANCEL

	RSB

DUTU$CLEANUP_CANCEL::

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

	PUSHR	#^M<R0,R1,R2,R3,R4,R5>	; Save registers.
;
; First drain any CDRP's that didn't even get there ABORT's sent.
;
20$:	REMQUE	@CDRP$L_SNDABTQFL(R5), R0 ; Get next canceled IRP/CDRP.
	BVS	40$			; Branch if no more IRPs/CDRPs.

	POST_CDRP status=SS$_CANCEL	; Insert packet onto IOPOST queue.

	BRW	20$			; Loop back until empty.
;
; Then drain CDRP's which did not terminate after an ABORT.
;
40$:	REMQUE	@CDRP$L_ABTDQFL(R5), R0 ; Get next canceled IRP/CDRP.
	BVS	60$			; Branch if no more IRPs/CDRPs.

	POST_CDRP status=SS$_CANCEL	; Insert packet onto IOPOST queue.

	BRW	40$			; Loop back until empty.
;
; Deallocate cancel-CDRP.
;
60$:	MOVAB	CDRP$L_IOQFL(R5), R0	; Point to IRP portion.

	CALL_DRVDEALMEM ; Deallocate IRP.

	POPR	#^M<R0,R1,R2,R3,R4,R5>	; Restore registers.
	RSB



	.SBTTL	DUTU$TEST_CANCEL_CDRP - Should a CDRP be canceled
;++
;
;	DUTU$TEST_CANCEL_CDRP - Should a CDRP be canceled
;
; Functional Description:
;
;	This routine tests one input CDRP against an input cancel CDRP to
;	determine whether or not the former should be canceled.	 The CDRP
;	should be canceled if and only if the following fields match in both
;	CDRPs:
;
;		CDRP$L_PID		requesting process PID
;		CDRP$L_CHAN		requesting process channel
;		CDRP$L_UCB		device UCB address
;
;	NOTE: virtual requests are canceled in the same manner as other
;	requests.  This differs from the approach used by other disk drivers.
;
; Inputs:
;
;	R2	test CDRP address
;	R5	cancel CDRP address
;
; Outputs:
;
;	R0	success implies test CDRP should be canceled
;		failure implies test CDRP should not be canceled.
;
;--

DUTU$TEST_CANCEL_CDRP::

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

	CLRL	R0				; Assume no cancel.
	CMPL	CDRP$L_UCB(R2), CDRP$L_UCB(R5)	; Do devices match?
	BNEQ	90$				; Branch if no match.
	CMPL	CDRP$L_PID(R2), CDRP$L_PID(R5)	; Do PIDs match?
	BNEQ	90$				; Branch if no match.
	CMPL	CDRP$L_CHAN(R2), CDRP$L_CHAN(R5); Do channels match?
	BNEQ	90$				; Branch if no match.
	INCL	R0				; All match, so cancel it.

90$:	RSB



	.SBTTL	----- GENERAL SUPPORT ROUTINES -----
	.SBTTL	DUTU$LOCK_IODB - Acquire Access to the I/O Database
;++
;
;	DUTU$LOCK_IODB - Acquire Access to the I/O Database
;
; Functional Description:
;
;	This routine gets the I/O database mutex in the desired mode, or it
;	waits until it can get it. When it gets the mutex, control is returned
;	to the address passed in R3.
;
; Inputs:
;
;	R3	CDDB address
;	R4	Address for continuation after getting mutex.
;	R5	Fork Block address
;
;
; Outputs:
;
;	R3	CDDB address
;	R4	Address for continuation after getting mutex.
;	R5	Fork Block address
;
;--

DUTU$LOCK_IODB::

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

	MOVB	#SPL$C_SCS, UCB$B_FLCK(R5)	; Guarantee fork lock index.
	MOVAB	G^IOC$GQ_MUTEX, R0		; Get I/O database mutex.

	JSB	G^SCH$LOCKWEXEC_QUAD		; Lock for WRITE access.
	BLBS	R0, 30$

	FORK_WAIT -
		ROUTINE=DUTU$LOCK_IODB,-	; Wait a second (R3-R5 saved)
		CONTINUE=40$

30$:	JSB	(R4)				; Now that the mutex is held,

40$:	RSB					;  continue processing.



	.SBTTL	DUTU$RESET_MSCP_MSG - Reset MSCP command packet
;++
;
;	DUTU$RESET_MSCP_MSG - Reset MSCP command packet
;
; Functional Description:
;
;	This routine causes a previously used MSCP command packet (or end
;	message) to be completely recycled and readied for use in the sending
;	of another MSCP command.  It is intended for use by those functions
;	which require more than one MSCP command to properly complete.	Any
;	failure of the message buffer operation is taken as an indication of a
;	broken connection and control is transfered to DUTU$KILL_THIS_THREAD.
;
;	This routine is the power behind the RESET_MSCP_MSG macro.
;
; Inputs:
;
;	R2	Caller's address
;	R3	UCB address
;	R5	CDRP address
;
; Outputs:
;
;	None (control is returned to caller via JMP)
;
;--

DUTU$RESET_MSCP_MSG::

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

	MOVL	R2,CDRP$L_IOQBL(R5)		; Save caller's return address.

	RECYCL_RSPID				; Recycle the RSPID.

	RECYCL_MSG_BUF				; Recycle the MSCP packet.
	BLBC	R0, 99$				; Branch if connection broken.

	MOVZWL	UCB$W_MSCPUNIT(R3), R1		; Get unit number.

	BSBW	DUTU$INIT_MSCP_MSG		; Initialize MSCP packet.

	JMP	@CDRP$L_IOQBL(R5)		; Restore return address.
;
; RECYCL_MSG_BUF detected a broken connection.
; Therefore, kill this execution thread.
;
99$:	JSB	DUTU$KILL_THIS_THREAD		; Then kill this thread.
	RSB



	.SBTTL	DUTU$INIT_MSCP_MSG - Initialize a MSCP message
;++
;
;	DUTU$INIT_MSCP_MSG - Initialize a MSCP message
;
; Functional Description:
;
;	These routines initialize an SCS message buffer for use in transmitting
;	a MSCP command packet.	The primary purpose of this initialization is
;	to insure that all reserved fields are zero.  In addition, the command
;	reference number is loaded with the RSPID.
;
;	These routines are the power behind the INIT_MSCP_MSG macro.
;
; Inputs:
;
;	R1	MSCP Unit Number (or zero if none)
;	R5	a CDRP address
;
; Implicit Inputs:
;
;	CDRP$L_MSG_BUF(R5)	SCS message buffer address
;	CDRP$L_RSPID(R5)	SCS RSPID
;
; Outputs:
;
;	R0 & R1 destroyed
;	R2	message buffer address
;	All other registers are preserved.
;
; Implicit Outputs:
;
;	MSCP$K_MXCMDLEN bytes of message buffer are zeroed.
;
;	MSCP$L_CMD_RED(R2)	<== CDRP$L_RSPID(R5)
;	(If R0 is nonzero)
;	MSCP$W_UNIT(R2)		<== UCB$W_MSCPUNIT(R3)
;
; Special notes:
;
;	These routines are somewhat inefficient and therefore are not suitable
;	for code paths requiring speedy execution.
;
;--

DUTU$INIT_MSCP_MSG::

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

	MOVL	CDRP$L_MSG_BUF(R5), R2		; Get message buffer address.
	MOVL	R2, R0				; Copy message buffer address.

	.REPEAT MSCP$K_MXCMDLEN / 8
	CLRQ	(R0)+				; Zero entire message buffer.
	.ENDR

	.IIF	NE MSCP$K_MXCMDLEN & 4, CLRL (R0)+
	.IIF	NE MSCP$K_MXCMDLEN & 2, CLRW (R0)+
	.IIF	NE MSCP$K_MXCMDLEN & 1, CLRB (R0)+

	MOVL	CDRP$L_RSPID(R5), -		; Setup command reference
		MSCP$L_CMD_REF(R2)		; number from RSPID.
	MOVW	R1, MSCP$W_UNIT(R2)		; Setup unit number or zero.
	RSB					; Return to caller.



	.SBTTL	DUTU$SEND_MSCP_MSG - Send command message to MSCP server
;++
;
;	DUTU$SEND_MSCP_MSG - Send command message to MSCP server
;
; Functional Description:
;
;	This routine causes the MSCP message packet pointed to by the CDRP
;	whose address is stored in R5 to be transmitted to the MSCP server
;	at the other end of the connection whose PDT address is in R4.	For
;	accounting purposes, the CDRP is queued to the active transfers queue
;	of the CDDB whose address is in R1.
;
;	Control is returned to the instruction following the call to this
;	routine when the corresponding MSCP end message has been received.
;
;  Inputs:
;
;	R1 CDDB address
;	R3 UCB	address
;	R4 PDT	address
;	R5 CDRP address
;	IPL is IPL$_SCS
;
; Outputs:
;
;	R3 - R5 preserved, all other registers destroyed
;	IPL is IPL$_SCS
;
;--

DUTU$SEND_MSCP_MSG::

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

	BITL	#IRP$M_DIAGBUF, -		;MAINL; Test for diagnostic buffer.
		CDRP$L_STS(R5)			;MAINL; If diagnostic buffer is present
	.BRANCH_LIKELY
	BEQL	100$				;MAINL;	 record the MSCP command.

	BSBW	DUTU$DUMP_COMMAND

100$:	.IF	DEFINED DEBUG$LOG
	ASSUME OWN$V_LOG_ENABLD EQ 0
	.BRANCH_LIKELY
	BLBC	<DUTU$OWN_STORAGE+OWN$W_STATUS>,- ;MAINL; Branch if logging isn't
		200$				;MAINL;	 enabled.
	MOVL	#PKT$C_MSCP_CMD,R0

	BSBW	DUTU$LOG_PKT			;MAINL; Log this packet.
200$:
	.ENDC

;	BSBW	FT$SEND_MESSAGE_FILTER		;MAINL; Filter MSCP Commands for potential error insertion

	MOVL	CDRP$L_CDT(R5), R0		;MAINL; Get CDT address
	CMPW	CDT$W_STATE(R0), #CDT$C_OPEN	;MAINL; Verify this conn is open
	BNEQ	300$				;MAINL;
	CMPZV	#IO$V_FCODE, #IO$S_FCODE, -	;MAINL; Branch if this was
		CDRP$L_FUNC(R5), #IO$_PACKACK	;MAINL; PACKACK function.
	BEQL	250$				;MAINL;
        BBS     #CDRP$V_PERM,-                  ;MAINL; If this is the perm CDRP,
                CDRP$L_DUTUFLAGS(R5),250$       ;MAINL;  let it through unchecked.
	TSTL	CDRP$L_UCB(R5)			;MAINL; Safety net for internal CDRPs
	BEQL	250$				;MAINL;	 with no UCB.
	CMPL	UCB$L_CDT(R3),CDRP$L_CDT(R5)	;MAINL; Make sure all structures
	BNEQ	400$				;MAINL;	 involved in sending this
	CMPL	CDDB$L_CDT(R1),CDRP$L_CDT(R5)	;MAINL;	 request agree on the CDT addr
	BNEQ	400$				;MAINL;

250$:	INSERT_EL -				;MAINL; Link CDRP onto CDDB active Q
		EL	= (R5),-
		QUE	= @CDDB$L_CDRPQBL(R1),-
		SCRATCH = R0

	MOVL	#MSCP$K_MXCMDLEN, R1		;MAINL; Pass longest command length

	SENDCNTMSGBUF				;MAINL;

	RSB					;MAINL;

300$:	BUG_CHECK	DISKCLASS,FATAL ; Connection does not point to a server, die...
;
; Connection does not match UCB and/or CDDB
;
400$:	INCL	CDDB$L_COUNTER(R1)	; Leave a clue at the scene of the crime.

	RESTORE_CREDIT			; Restore the send credit to the connection
					;  used by the CDRP.
	MOVZWL	#SS$_ILLCDTST,R0	; The generic error code.
	CLRL	R1
	BSBW	FUNCTION_EXIT		; Terminate with extreme prejudice.

	RSB

	.DISABLE LSB



	.SBTTL	DUTU$INSERT_RESTARTQ - Insert outstanding CDRP in restart queue
;++
;
;	DUTU$INSERT_RESTARTQ - Insert outstanding CDRP in restart queue
;
; Functional Description:
;
;	This routine is presented with outstanding I/O requests (represented
;	by CDRPs) whenever a connection-failure cleanup is required.  For each
;	request, this routine determines the appropriate cleanup action and
;	performs that action.
;
;	In all cases the RSPID and message buffer are deallocated.  N.B.
;	mapping resources are not deallocated.	This action is postponed until
;	after the connection is formally broken, to prevent an "insane" server
;	from incorrectly overwriting memory due to reallocation of mapping
;	resources.
;
;	The various cleanup processing cases are as follows:
;
;	    1.	Ordinary IRP/CDRP     - insert the CDRP in the restart queue
;					in sequence number order.
;
;	    2.	CDDB Permanent	      - no action other than deallocating SCS
;		IRP/CDRP		resources
;
;	    3.	Cancel Operation      - complete the cancel operation, posting
;		IRP/CDRP		all effected IRP/CDRPs because the
;					connection failure has done what the
;					ABORT command had (so far) failed to do.
;
;	    5.	Mount Verification    - Same as 1.  However, after the mapping
;		IRP/CDRP		resources are deallocated, these
;					IRP/CDRPs are queued for post
;					processing with a status of
;					SS$_DEVOFFLINE.
;
;	    6.	Connection walking    - Handle connection breakage via a call-
;		CDRP thread		back whose address is stored in
;					CDRP$L_UBARSRCE.
;					See DUTU$DO_CONN_WALKER.
;
;
; Inputs:
;
;	R3	CDDB address
;	R4	PDT address
;	R5	CDRP address
;
; Outputs:
;
;	R0 through R2 and R5 are destroyed.
;	All other registers are preserved.
;
;--

DUTU$INSERT_RESTARTQ::

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

	CMPB	#DYN$C_CDRP, -		; Guarantee that a CDRP is being
		CDRP$B_CD_TYPE(R5)	; processed.
	BNEQ	999$			; If not, bug check.
	MOVL	#1, R0			; Don't unmap port buffers.

	BSBW	DUTU$DEALLOC_ALL	; Always deallocate RSPID & msg. buf.

	BBS	#CDRP$V_PERM, -		; Is this a permanent CDRP?
		CDRP$L_DUTUFLAGS(R5), - ; Branch if permanent CDRP.
		200$
	BBS	#CDRP$V_CANIO, -	; Is this a CancelIO CDRP?
		CDRP$L_DUTUFLAGS(R5), - ; Branch if CancelIO CDRP.
		400$
	BBS	#CDRP$V_CONNWALK, -	; Is this a connection walking CDRP?
		CDRP$L_DUTUFLAGS(R5), - ; Branch if connection walking CDRP.
		DUTU$DO_CONN_WALKER

5$:	MOVAB	CDDB$L_RSTRTQFL(R3), R0 ; Get restart queue listhead.
	MOVL	R0, R1			; Copy that address.

10$:	MOVL	(R1), R1		; Get next restart CDRP.
	CMPL	R1, R0			; Returned to the listhead yet?
	BEQL	40$			; Branch if returned to listhead.
	CMPL	CDRP$L_SEQNUM(R5), -	; Is seqnum of CDRP of interest less
		CDRP$L_SEQNUM(R1)	; than seqnum of current list entry?
	BGEQU	10$			; If not, go back to try next list
					; entry.  Else fall thru and insert
					; CDRP of interest before the
					; current entry.
40$:	INSQUE	(R5),@CDRP$L_FQBL(R1)	; Insert before current entry.

200$:	RSB				; Return to caller.

400$:	BSBW	DUTU$CLEANUP_CANCEL	; For CancelIO, cleanup cancel and
	RSB				; return to caller.

999$:	BUG_CHECK MSCPCLASS, FATAL	; Attempt to insert non-CDRP in the
					; CDDB restart queue.



	.SBTTL	DUTU$DO_CONN_WALKER - Cleanup a Connection Walking CDRP
;++
;
;	DUTU$DO_CONN_WALKER - Cleanup a Connection Walking CDRP
;
; Functional Description:
;
;	This routine initiates cleanup for a connection walking CDRP --
;	CDRP$V_CONNWALK set in CDRP$L_DUTUFLAGS.  By convention, this involves
;	initiating a fork thread using the PC stored in CDRP$L_UBARSRCE.
;
; Inputs:
;
;	R5	Connection walking CDRP address
;
; Implicit Inputs:
;
;	CDRP$L_UBARSRCE(R5)	connection walking error routine address
;
; Outputs:
;
;	None.
;	R5 is destroyed.
;	All other registers are preserved.
;
;--

DUTU$DO_CONN_WALKER:

	PUSHR	#^M<R0,R1,R2,R3,R4>		; Save registers.

	JSB	@CDRP$L_UBARSRCE(R5)		; Initiate error fork thread.

	POPR	#^M<R0,R1,R2,R3,R4>		; Restore registers.
	RSB					; Return to caller.



	.SBTTL	DUTU$RECONN_LOOKUP - Reconnection SCS Lookup Action Routine
;++
;
;	DUTU$RECONN_LOOKUP - Reconnection SCS Lookup Action Routine
;
; Functional Description:
;
;	This is the action routine for both SCAN_RSPID_WAIT and SCAN_RDT which
;	are used to retrieve CDRPs from lower layers.  SCAN_RSPID_WAIT is the
;	SCS service which scans the RDT wait queue for entries belonging to a
;	given connection.  SCAN_RDT is the SCS service which scans the
;	Response-id Descriptor Table (RDT) for entries belonging to a given
;	connection.  For connection cleanup, the operations performed in both
;	cases are very similar.	 For those cases where some cleanup operation
;	is not appropriate R1 is used to determine which type of scan is in
;	progress (as setup by the caller of the SCS scan service).
;
;	Previously canceled CDRPs are ignored by this routine.	They are
;	processed when the request which canceled them is cleaned up.  In all
;	other cases, the CDRP is removed from the wait queue. All CDRPs
;	presented to this routine are assumed to be waiting for resources.
;
;	CDRPs are tested for a wait reasons counter pointer.  If one is present
;	the wait reasons counter is decremented.  CDRPs on the RSPID-wait queue
;	are always waiting for resources.  Otherwise, the fact that all
;	active requests have been cleaned up before this routine is called
;	guarantees that CDRPs presented to this routine are waiting for
;	resources.  Note: this routine helps perpetuate that guarantee by
;	exiting via DUTU$DRAIN_CDDB_CDRPQ, which cleans up all active requests.
;
;	The CDRP is then inserted into the restart queue and finally,
;	DUTU$DRAIN_CDDB_CDRPQ is called to start requests that were waiting
;	behind the CDRPs that have been removed from the wait queues.
;
;
; Inputs:
;
;	R1	LBC ==> SCAN_RSPID_WAIT
;		LBS ==> SCAN_RDT
;	R3	CDT address
;	R4	PDT address
;	R5	CDRP address
;
; Implicit Inputs:
;
;	CDT$L_AUXSTRUC(R3)	CDDB address
;
; Outputs:
;
;	R0, R2, and R5 are destroyed.
;	All other registers are preserved.
;
; Side Effects:
;
;	The CDRP presented to this routine is readied for retry (or reuse)
;	after the a new connection to the remote MSCP server is formed.
;
;--

DUTU$RECONN_LOOKUP::

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

	CDRP$L_STS2 = CDRP$L_IOQFL + IRP$L_STS2 ; Define offset for STS2

	CLRL	CDRP$L_CDT(R5)			; Clear out CDT pointer
	ASSUME	CDRP$V_CAND EQ 0		; Branch if this is a
	BLBS	CDRP$L_DUTUFLAGS(R5), 20$	; canceled CDRP.

	CANCEL_WAIT -				; Cancel CDRP Res Wait State but
		RWCPTR_UPD=NO			;  don't update Res Wait Cnt now
						;  as don't want UNSTALLUCB done
	MOVL	CDRP$L_RWCPTR(R5), R0		; Get wait counter pointer.
	BEQL	10$				; Branch if no pointer present.
	DECL	(R0)				; Else, decrement wait counter.

10$:	PUSHR	#^M<R1,R3>			; Save registers.
	MOVL	CDT$L_AUXSTRUC(R3), R3		; Get CDDB address.

	BSBW	DUTU$INSERT_RESTARTQ		; Setup CDRP restart.

	POPR	#^M<R1,R3>			; Restore registers.

20$:	BLBC	R1, 30$				; If SCAN_RSPID_WAIT, exit now.

	BSBW	DUTU$DRAIN_CDDB_CDRPQ		; Else, empty active CDRPs queue

30$:	RSB					; and return to caller.



	.SBTTL	DUTU$DRAIN_CDDB_CDRPQ - Drain Active CDRPs Queue
;++
;
;	DUTU$DRAIN_CDDB_CDRPQ - Drain Active CDRPs Queue
;
; Functional Description:
;
;	All CDRPs found on the active CDRPs queue, CDDB$L_CDRPQFL, are
;	processed and placed on the restart queue, CDDB$L_RSTRTQFL, as
;	appropriate.
;
; Inputs:
;
;	R3	CDT address
;	R4	PDT address
;
; Implicit Inputs:
;
;	CDT$L_AUXSTRUC(R3)	CDDB address
;
; Outputs:
;
;	All registers preserved.
;
; Side Effects:
;
;	Any CDRP found on the active queue is processed by DUTU$INSERT_RESTARTQ.
;
;--

DUTU$DRAIN_CDDB_CDRPQ::

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

	PUSHR	#^M<R0,R1,R2,R3,R4,R5>		; Save registers.
	MOVL	CDT$L_AUXSTRUC(R3), R3		; Get CDDB address.

10$:	REMQUE	@CDDB$L_CDRPQFL(R3), R5		; Get an active CDRP.
	BVS	20$				; Branch if no active CDRPs.

.IF DF IRP$Q_QIO_P1				; If backporting prior to V7.0
	REPO_CDRP				; Repossess the CDRP
.ENDC
	CLRL	CDRP$L_CDT(R5)			; Clear out CDT pointer

	BSBW	DUTU$INSERT_RESTARTQ		; Insert active CDRP in the

	BRW	10$				; restart queue and loop.

20$:	POPR	#^M<R0,R1,R2,R3,R4,R5>		; Restore registers.
	RSB					; Return.



	.SBTTL	DUTU$TERMINATE_PENDING - Fail pending requests with SS$_VOLINV
;++
;
;	DUTU$TERMINATE_PENDING - Fail pending requests with SS$_VOLINV
;
; Functional Description:
;
;	This routine locates queues for I/O post processing pending requests
;	on the input UCB.  All requests are completed with a SS$_VOLINV,
;	"volume is not software enabled" status.
;
;	The active requests queue is not scanned by this routine.  Either
;	there should not be requests in this queue (i.e. there is no good
;	connection for the requests to be processed on), or the active
;	requests will eventually be finished, successfully or otherwise by the
;	MSCP server.
;
; Inputs:
;
;	R5	UCB address
;
; Implicit inputs:
;
;	UCB$L_CDDB(R5)		CDDB address
;	UCB$L_IOQFL(R5)		header for pending I/O request queue
;	CDDB$L_RSTRTQFL(cddb)	header for queue of I/O requests awaiting
;				SCS connection reestablishment
;
; Outputs:
;
;	R0 through R2 are destroyed.
;	All other registers are preserved.
;
;--

DUTU$TERMINATE_PENDING::

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

	ADDL3	#CDDB$L_RSTRTQFL, -		; Get address of restart Qhead.
		UCB$L_CDDB(R5), R1
	ASSUME	CDRP$L_FQFL EQ 0
	MOVL	R1, R0				; Init "previous" CDRP pointer.

10$:	MOVL	CDRP$L_FQFL(R0), R0		; Link to next CDRP.

11$:	CMPL	R0, R1				; All restart CDRPs tested?
	BEQL	50$				; Branch if all CDRPs done.
	CMPL	CDRP$L_UCB(R0), R5		; Is this CDRP for right UCB?
	BNEQ	10$				; Branch if not right UCB.
	PUSHL	CDRP$L_FQFL(R0)			; Right UCB, save next CDRP
	REMQUE	(R0), R0			; address and dequeue this CDRP.

	POST_CDRP status=SS$_VOLINV		; Then, post this CDRP.

	POPL	R0				; Restore next CDRP address.
	BRW	11$				; Loop till no more CDRPs.

50$:	REMQUE	@UCB$L_IOQFL(R5), R0		; Get next pending IRP.
	BVS	80$				; Branch if no more IRPs.

	POST_IRP status=SS$_VOLINV		; Post IRP.
	BRW	50$				; Loop till no more IRPs.

80$:	RSB					; Return.



	.SBTTL	DUTU$DEALLOC_ALL - Deallocate SCS resources
;++
;
;	DUTU$DEALLOC_ALL - Deallocate SCS resources
;
; Functional Description:
;
;	This routine deallocates various combinations of SCS resources.
;	This routine is the power behind the DEALLOCATE macro.
;
; Inputs:
;
;	R0	Skip indicator
;			0 - Deallocate all resources held by this CDRP
;			1 - Do not deallocate map registers
;	R4	PDT address
;	R5	CDRP address
;
; Outputs:
;
;	R0 - R2 are destroyed.
;	All other registers are preserved.
;
;--

DUTU$DEALLOC_ALL::

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

	BLBS	R0, 10$				;MAINL; Skip unmap if flag is set.
	TSTL	CDRP$L_LBUFH_AD(R5)		;MAINL; Are map resources allocated?
	BEQL	10$				;MAINL; Branch if not allocated.

	UNMAP					;MAINL; Else, deallocate them.

	CLRL	CDRP$L_LBUFH_AD(R5)		;MAINL; Signal no map stuff allocated.

10$:	TSTL	CDRP$L_MSG_BUF(R5)		;MAINL; Is msg. buffer allocated?
	BEQL	20$				;MAINL; Branch if not allocated.

	DEALLOC_MSG_BUF				;MAINL; Else, deallocate it.

20$:	TSTL	CDRP$L_RSPID(R5)		;MAINL; Is RSPID allocated?
	BEQL	30$				;MAINL; Branch if not allocated.

	DEALLOC_RSPID				;MAINL; Else, deallocate it.

30$:	RSB					;MAINL; Return.



	.SBTTL	DUTU$POST_CDRP - Queue CDRP for post processing
;++
;
;	DUTU$POST_CDRP - Queue CDRP for post processing
;
; Functional Description:
;
;	The input CDRP is queued to I/O post processing with the input final
;	I/O status.  All SCS resources held by the CDRP are appropriately
;	released.
;
; Inputs:
;
;	R0	CDRP address
;	R1	final I/O status
;
; Outputs:
;
;	R0 & R1 are destroyed.
;	all other registers are preserved.
;
;--

DUTU$POST_CDRP::

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

	CMPB	#DYN$C_CDRP, -			; Guarantee that a CDRP is being
		CDRP$B_CD_TYPE(R0)		; processed.
	BEQL	2$				; If not, bug check.

	BUG_CHECK MSCPCLASS, FATAL		; Attempt to insert non-CDRP in
						; the I/O post processing queue.
2$:	PUSHR	#^M<R2,R3,R4,R5>		; Save registers.
	MOVL	R0, R5				; Move CDRP address.
	MOVL	CDRP$L_UCB(R5), R3		; Get UCB address.
	MOVL	UCB$L_PDT(R3), R4		; Get PDT address.
	MOVL	R1, CDRP$L_IOST1(R5)		; Set final I/O status.
	CLRL	CDRP$L_IOST2(R5)		; Clear second status longword.

       .IF DEFINED DEBUG$LOG
	.BRANCH_LIKELY
	BLBC	<DUTU$OWN_STORAGE+OWN$W_STATUS>,5$; Branch if logging isn't
						;  enabled.
	BSBW	DUTU$LOG_IRP_EXIT_PKT		; Log an IRP EXIT packet
5$:
	.ENDC

	TSTL	CDRP$L_MSG_BUF(R5)		; Is message buf. allocated?
	BEQL	10$				; Branch if not allocated.

	RESTORE_CREDIT

10$:	CLRL	R0				; Clear skip indicator to
						;  release map regs
	JSB	DUTU$DEALLOC_ALL		; Insure that all SCS
						; resources are deallocated.
	CMPZV	#IO$V_FCODE, #IO$S_FCODE, -	; Branch if this was not a
		CDRP$L_FUNC(R5), #IO$_PACKACK	; PACKACK function.
	BNEQ	12$
	BBCC	#UCB$V_MSCP_PKACK, -		; Clear packack flag and
		UCB$L_DEVSTS(R3), 12$		; branch if it was clear.
	DECW	UCB$W_RWAITCNT(R3)		; Decrement wait count.

12$:	CMPB	#DC$_DISK, UCB$B_DEVCLASS(R3)	; Is this a disk?
	BEQL	17$				; Branch if a disk.
						; Assume it is a tape...
14$:	CMPZV	#IO$V_FCODE, #IO$S_FCODE, -	; Branch if this was a
		CDRP$L_FUNC(R5), #IO$_SETCHAR	; SETCHAR function.
	BEQL	15$
	CMPZV	#IO$V_FCODE, #IO$S_FCODE, -	; Branch if this was not a
		CDRP$L_FUNC(R5), #IO$_SETMODE	; SETMODE function.
	BNEQ	17$

15$:	BBCC	#UCB$V_TU_SEQNOP, -		; Clear seq. NOP flag and
		UCB$L_DEVSTS(R3), 17$		; branch if it was clear.
	DECW	UCB$W_RWAITCNT(R3)		; Decrement wait count.

17$:	PUSHL	R5				; Save CDRP address.
	MOVL	R3, R5				; Copy UCB address.

	JSB	G^SCS$UNSTALLUCB		; Possibly unstall the UCB.

	POPL	R5				; Restore CDRP address.
	BBC	#IRP$V_BUFIO, -			; Is this a direct I/O request?
		CDRP$L_STS(R5), 20$		; Branch if direct I/O.
	BICL	#IRP$M_FUNC, CDRP$L_STS(R5)	; Else, clear buffered read bit.

20$:	BBC	#IRP$V_WLE,-			; Br if Not
		CDRP$L_STS2(R5),30$		;   write logging.
	MOVL	CDRP$L_IOST1(R5),R0		; Get status.
	CLRL	R1				; Cleanup R1.

	ALT_REQCOM  MODE=CONTINUE		; Complete request insuring

	BRB	40$				;  a return here.

30$:	MOVAB	CDRP$L_IOQFL(R5),R3		; Point R3 at IRP part of CDRP

	CALL_POST_NOCNT				; Post the I/O for completion

40$:	POPR	#^M<R2,R3,R4,R5>		; Restore registers.
	RSB					; Return.



	.SBTTL	DUTU$KILL_THIS_THREAD - Fix thread caught by async. conn. failure
;++
;
;	DUTU$KILL_THIS_THREAD - Fix thread caught by asynchronous connection failure
;
; Functional Description:
;
;	This is an internal routine that is either jumped (BRW) to or called
;	(BSBW).
;
;	It is jumped to by a thread that has suffered an allocation failure
;	indicating that its CONNECTION has failed.  This should only happen
;	when the CONNECTION fails asynchronously with respect to the class
;	driver and the cause of the CONNECTION failure (e.g. power failure)
;	interrupts this very driver thread in mid execution.
;
;	This code is called when the class driver determines that the
;	intelligent controller is acting in an "insane" manner and that the
;	class driver therefore should bring down the CONNECTION and
;	re-synchronize (i.e. re-CONNECT) with the controller.
;
;	In either case, the CDRP is placed at the tail of the connection
;	outstanding requests queue, CDDB$L_CDRPQBL.  This queue holds all
;	those CDRP's with outstanding requests in the controller plus those
;	CDRP's of "killed" driver threads.  What all these CDRP's have in
;	common is that they are NOT on any resource wait queues.  After
;	queueing the CDRP, this routine does an RSB which returns to either to
;	caller's caller if entry was made via a BRW or to caller if entry was
;	made via a BSBW.
;
; Inputs:
;
;	R3	UCB address
;	R5	CDRP address
;
; Implicit Inputs:
;
;	UCB$L_CDDB(R3)		CDDB address
;	CDDB$L_CDRPQBL(cddb)	outstanding request queue backlink
;
; Outputs:
;
;	R1 is destroyed.
;	All other registers are preserved.
;
;--

DUTU$KILL_THIS_THREAD::

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

	MOVL	UCB$L_CDDB(R3), R1		; Get CDDB address
	INSQUE	(R5), @CDDB$L_CDRPQBL(R1)	; Insert CDRP onto tail of queue
						; of CDRP's sent to the port.
	RSB					; Return to caller or
						; caller's caller.


	.SBTTL	DUTU$CHECK_RWAITCNT - Validate UCB$W_RWAITCNT
;++
;
;	DUTU$CHECK_RWAITCNT - Validate UCB$W_RWAITCNT
;
; Functional Description:
;
;	This routine compares UCB$W_RWAITCNT against a computed value based
;	upon information available as to the reasons that RWAITCNT might be
;	bumped.	 If the two values compare, control is returned to the caller.
;	If they do not compare, an INCONSTATE bug check is generated.
;
;	This routine does not test for SCS wait states as possible
;	incrementers of UCB$W_RWAITCNT.	 Therefore, it MUST NOT be called when
;	a possibility exists for a thread to be in an SCS wait state.
;
; Inputs:
;
;	R5	UCB address
;
; Outputs:
;
;	All registers preserved.
;
;--

DUTU$CHECK_RWAITCNT::

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

	MOVQ	R0, -(SP)			; Save corrupted registers.
	CLRL	R0				; Init accumulator.
	BBC	#UCB$V_MSCP_WAITBMP, -		; Check for wait count
		UCB$L_DEVSTS(R5), 10$		; explicitly bumped.
	INCL	R0				; If so, bump accumulator.

10$:	BBC	#UCB$V_MSCP_MNTVERIP, -		; Check for wait count bumped
		UCB$L_DEVSTS(R5), 20$		; by mount verification.
	INCL	R0				; If so, bump accumulator.

20$:	BBC	#UCB$V_MSCP_PKACK, -		; Check for wait count bumped
		UCB$L_DEVSTS(R5), 30$		; by packack in progress.
	INCL	R0				; If so, bump accumulator.

30$:	CMPB	#DC$_TAPE, UCB$B_DEVCLASS(R5)	; Is this a tape?
	BNEQ	40$				; Branch if not a tape.
	BBC	#UCB$V_TU_SEQNOP, -		; Is a sequential NOP in
		UCB$L_DEVSTS(R5), 40$		; progress:  branch if no.
	INCL	R0				; Else, bump accumulator.

40$:	CMPW	R0, UCB$W_RWAITCNT(R5)		; Check for correct RWAITCNT.
	BNEQ	99$				; Branch if no match.
	MOVQ	(SP)+, R0			; Restore registers.
	RSB					; Return.

99$:	BUG_CHECK MSCPCLASS, FATAL		; RWAITCNT test failed.



	.SBTTL	DUTU$DODAP - Do Determine Access Paths Processing
;++
;
;	DUTU$DODAP - Do Determine Access Paths Processing
;
; Functional Description:
;
;	This routine supervises the sending of Determine Access Path (DAP)
;	commands for the purpose of discovering the current topology of the
;	MSCP units (either disks or tapes) accesible to this processor.	 These
;	commands make us aware of alternate (not currently accessible) paths
;	to mounted units.
;
;	DAP_LIMIT is used to limit the number of DAPs issued. DAP_LIMIT is
;	loaded in MAKE_CONNECTION, LINK_NEW_UCB, and SETUP_DUAL_PATH.
;
; Inputs:
;
;	R0	Skip indicator
;		0 - Signals that this was an external call
;		1 - Signals call for subsequent UCB on same controller
;	R1	CDDB address
;
; Outputs:
;
;	Registers R0 through R5 are modified.
;
;--

;
; Number of passes through DODAP before each DAP processing thread is started.
;
DAP_COUNT = 11					;initiate DAP process on
						;every 12th call
DUTU$DODAP::

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

	BLBS	R0, 10$				; If R0 = 1 skip ahead
	BBSS	#CDDB$V_DAPBSY, -		; Branch if DAP CDRP is in
		CDDB$L_STATUS(R1), 40$		; use and set in use flag.
	CMPB	#MSCP$K_CM_EMULA, -		; Is this a VMS MSCPserver
		CDDB$B_CNTRLMDL(R1)		;  (emulator)?
	BEQL	30$				; Exit if emulator.
	BBS	#CDDB$V_DISABLED, -		; If this CDDB is disabled,
		CDDB$L_STATUS(R1), 30$		;  skip DAP processing.
	DECL	CDDB$L_DAPCOUNT(R1)		; Count number of passes through
	BGEQ	30$				; here before going again.
	MOVL	#DAP_COUNT, -			; Refresh passes counter.
		CDDB$L_DAPCOUNT(R1)
	TSTL	CDDB$L_DAP_LIMIT(R1)		; See if DAPs are enabled
	BEQL	30$				; EQL means no.
	DECL	CDDB$L_DAP_LIMIT(R1)		; Else, show we've been here.
	MOVL	CDDB$L_DAPCDRP(R1), R5		; Get DAP CDRP address.
	MOVL	CDDB$L_PDT(R1), R4		; Get PDT address.
	MOVAB	<CDDB$L_UCBCHAIN -		; Setup "previous" UCB address
		-UCB$L_CDDB_LINK>(R1), R3	; in R3.

	ALLOC_RSPID				; Allocate a RSPID.

10$:	MOVL	UCB$L_CDDB_LINK(R3), R3		; Link to next UCB.
	BEQL	20$				; Branch if no more UCBs.
	BBC	#UCB$V_VALID, -			; If this UCB is not VALID (or
		UCB$L_STS(R3), 10$		; MSCP online), then skip it.
	ASSUME	MSCP$V_SHADOW EQ 15
	TSTW	UCB$W_MSCPUNIT(R3)		; Is this a shadow set virtual
	BLSS	10$				; unit.	 If so, skip DAP.
	MOVL	R3, CDRP$L_UCB(R5)		; Put UCB in DAP CDRP.

	ALLOC_MSG_BUF				; Allocate a message buffer.
	.branch_likely
	BLBS	R0, 50$				; Continue if connection OK.

20$:	DEALLOC_RSPID				; Deallocate DAP RSPID.

	MOVL	CDRP$L_UBARSRCE(R5), R1		; Get the CDDB address.

30$:	BICL	#CDDB$M_DAPBSY, -		; Clear DAP CDRP in use flag.
		CDDB$L_STATUS(R1)
40$:	RSB					; Kill this fork thread.

50$:	INIT_MSCP_MSG ucb=(R3)			; Initialize MSCP packet.

	MOVB	#MSCP$K_OP_DTACP, -		; Set DAP opcode.
		MSCP$B_OPCODE(R2)		;
	MOVL	UCB$L_CDDB(R3), R1		; Get CDDB of controller.

	SEND_MSCP_MSG				; Send off the message
;
; 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>,-
		   OUTPUT=  <	              >,-
		   SCRATCH= <	              >,-
		   PRESERVE=<                 >

	CMPW	#SS$_ILLCDTST, R0		; Check for CDT error
	.branch_likely
	BNEQ	60$				; Branch if connection still OK.

	DEALLOC_MSG_BUF				; Deallocate message.
    
	DEALLOC_RSPID				; Deallocate DAP RSPID.

	MOVL	CDRP$L_UBARSRCE(R5), R1		; Get the CDDB address.

	BICL	#CDDB$M_DAPBSY, -		; Clear DAP CDRP in use flag.
		CDDB$L_STATUS(R1)
	RSB					; Kill this fork thread.

60$:	DEALLOC_MSG_BUF				; Deallocate response message.
    
	RECYCL_RSPID				; Recycle RSPID for reuse.

	MOVL	#1,R0				; Indicate internal call

	BSBW	DUTU$DODAP			; Loop through all UCBs.

	RSB



	.SBTTL	DUTU$SEND_DUPLICATE_UNIT - Send duplicate unit message to operator
;++
;
;	DUTU$SEND_DUPLICATE_UNIT - Send duplicate unit message to operator
;
; Functional Description:
;
;	A message indicating the detection of a duplicate MSCP unit number
;	is sent to OPCOM.
;
; Inputs:
;
;	R3	address of the UCB for which a MSCP server detected a
;		duplicate unit number
;
; Outputs:
;
;	All registers preserved.
;
;--

DUTU$SEND_DUPLICATE_UNIT::

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

	PUSHR	#^M<R0,R1,R2,R3,R4,R5>		; Save registers.
	MOVL	R3, R5				; Jockey UCB addr. to right reg.
	MOVZBL	#MSG$_DUPUNITNO, R4		; Get message number.
	MOVL	G^SYS$AR_OPRMBX, R3		; Get OPCOM's mailbox address.

	CALL_SNDEVMSG			; Send the message.

	POPR	#^M<R0,R1,R2,R3,R4,R5>		; Restore registers.
	RSB



	.SBTTL	"Register" Dump Routines
	.SBTTL	o DUTU$DUMP_COMMAND - Copy MSCP command to diagnostic buffer
;++
;
;	DUTU$DUMP_COMMAND - Copy MSCP command to diagnostic buffer
;
; Functional Description:
;
;	Copy the contents of the MSCP Command to the diagnostic buffer whose
;	address is pointed to by CDRP$L_DIAGBUF(R5).  This is the MSCP message
;	which will be sent to the remote server.
;
; Inputs:
;
;	R5	CDRP address
;
;--

DUTU$DUMP_COMMAND::

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

	PUSHR	#^M<R0,R1,R2,R3,R4,R5>	; Save registers.
	MOVL	@CDRP$L_DIAGBUF(R5), R0 ; Get diagnostic buffer address.
	MOVC3	#MSCP$K_MXCMDLEN, -	; Copy the entire command to the
		@CDRP$L_MSG_BUF(R5), -	;  diagnostic buffer.
		20(R0)
	POPR	#^M<R0,R1,R2,R3,R4,R5>	; Restore registers.
	RSB				; Return.



	.SBTTL	o DUTU$DUMP_ENDMESSAGE - Copy MSCP end message to diagnostic buffer

;++
;
;	DUTU$DUMP_ENDMESSAGE - Copy MSCP end message to diagnostic buffer
;
; Functional Description:
;
;	Copy the contents of the MSCP end message to the diagnostic buffer
;	whose address is pointed to by CDRP$L_DIAGBUF(R5).  This is the MSCP
;	end message returned to the class driver by the remote server.
;
; Inputs:
;
;	R1	END Message length
;	R2	END Message address
;	R5	CDRP address
;
;--

DUTU$DUMP_ENDMESSAGE::

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

	PUSHR	#^M<R0,R1,R2,R3,R4,R5>	; Save registers.
	MOVL	@CDRP$L_DIAGBUF(R5), R0 ; Get diagnostic buffer address.

	READ_SYSTIME	8(R0)		; Save ending time.

	CLRL	16(R0)			; Clear retry counts.
	MOVC5	R1, (R2), #0, -		; Copy end message to diagnostic
		#MSCP$K_LEN, -		;  buffer zero filling for short
		MSCP$K_MXCMDLEN+20(R0)	;  end messages.
	POPR	#^M<R0,R1,R2,R3,R4,R5>	; Restore registers.
	RSB				; Return.



	.SBTTL	DUTU$FILL_MSCP_MSG - Zero fill a partial MSCP message
;++
;
;	DUTU$FILL_MSCP_MSG - Zero fill a partial MSCP message
;
; Functional Description:
;
;	This routine zero fills an incomplete MSCP message.
;
; Inputs:
;
;	R1	size of MSCP message
;	R2	base address of MSCP message
;
; Outputs:
;
;	All registers preserved.
;
;--

DUTU$FILL_MSCP_MSG::

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

	PUSHR	#^M<R0,R1,R2,R3,R4,R5>		; Save registers.
	SUBL3	R1, #MSCP$K_LEN, R0		; Compute fill size.
	BLEQ	90$				; Branch if nothing to fill.
	MOVC5	#0, (SP), #0, R0, (R2)[R1]	; Zero fill the message.

90$:	POPR	#^M<R0,R1,R2,R3,R4,R5>		; Restore registers.
	RSB					; Return



	.SBTTL - DUTU$ALLOCATE_MEMORY - Allocate some pool space
;++
;
; DUTU$ALLOCATE_MEMORY- Allocate some pool space
;
; Functional Description:
;
;	This routine is invoked by the macro call
;	ALLOCATE_POOL defined in DUTUMAC.MAR
;	This routine will either bugcheck or continue
;	if it bugchecks, the callers PC can be found
;	on the top of the stack.
;
; Inputs:
;
;	R1	-- Size of pool to allocate
;	R2	-- Macro parameter either continue or bugcheck
;
; Outputs:
;
;	R0	-- Status code from EXE$ALONONPAGED call
;	R1	-- Size of pool that was allocated
;	R2	-- Address of the block that was allocated by EXE$ALONONPAGED
;
;--

DUTU$ALLOCATE_MEMORY::

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

	PUSHL	R2				; Save parameter flag

	JSB	G^EXE$ALONONPAGED		; Call system service for pool
	BLBS	R0,10$				; If normal go on

	TSTL	(SP)+				; Check flag
	BEQL	20$				; We failed so don't zero buff
	BUG_CHECK	INSF_NONPAGED,FATAL

10$:	ADDL	#4,SP				; Get rid of flag don't care.
	PUSHR	#^M<R0,R1,R2,R3,R4,R5>		; Save registers.
	MOVC5	#0,(R2),#0,R1,(R2)		; Zero buffer.
	POPR	#^M<R0,R1,R2,R3,R4,R5>		; Restore registers.

20$:	RSB



	.SBTTL - DUTU$INIT_IRP_CDRP - Setup length and type fields
;++
;
; DUTU$INIT_IRP_CDRP - Setup length and type fields
;
; Functional Description:
;
;	This routine is invoked by the macro call
;	INIT_IRP_CDRP defined in DUTUMAC.MAR
;	It will initialize the type fields and length
;	for a new IRP/CDRP
;
; Inputs:
;
;	R1	-- Size of pool that was allocated
;	R2	-- Pointer to new IRP
;
; Outputs:
;
;
;--

DUTU$INIT_IRP_CDRP::

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

	MOVB	#DYN$C_IRP, -		; Set packet type.
		IRP$B_TYPE(R2)
	MOVW	R1, IRP$W_SIZE(R2)	; Set packet size.
;	PUSHL	R3			; Save register
	MOVAB	IRP$L_FQFL(R2), R3	; Get CDRP pointer
	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(R3)
;	POPL	R3			; Restore register
	RSB				; Return to caller



	.IIF	NOT_DEFINED DEBUG$LOG, .NOSHOW CND
	.IF DEFINED DEBUG$LOG

	.SBTTL	----- COMMAND/END MESSAGE LOGGING ROUTINES -----
	.SBTTL	DUTU$CREATE_LOG - Create and initialize log buffer
;++
;
;	DUTU$CREATE_LOG - Create and initialize log buffer
;
; Functional Description:
;
;	This routine allocates a ring buffer from non-paged pool
;	for logging MSCP packets and initializes OWN STORAGE to
;	point to the buffer.
;
; Inputs:
;
;	R2	Address of OWN storage structure
;
; Outputs:
;
;	R0 through R2 scratch
;
; Implicit Outputs:
;
;	OWN$L_BUF_START		Address of start of buffer
;	OWN$L_BUF_END		Address of end of buffer
;	OWN$L_NEXT_READ		Address if next packet to read
;	OWN$L_NEXT_WRITE	Address if next packet to write
;	OWN$W_INC_LOLIM		Included units range low limit = 0
;	OWN$W_INC_HILIM		Included units range high limit = -1
;	OWN$W_EXC_LOLIM		Excluded units range low limit = -1
;	OWN$W_EXC_HILIM		Excluded units range high limit = 0
;	OWN$L_STATUS		Indicates logging code is present
;				 and initialized
;
;--

DUTU$CREATE_LOG::

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

	ADDL3	#12, #LOG_BUF_SIZE, R1 ; Get ring buffer size.

	ALLOCATE_POOL			; Attempt to allocate space.
	BLBC	R0, 99$			; Branch if allocation failed.
					; R2 = address / R1 = size.
	CLRQ	(R2)+			; Clear FLINK and BLINK.
	MOVW	R1, (R2)+		; Save size.
	MOVW	#DYN$C_CLASSDRV, (R2)+	; Set type and sub-type.
	MOVL	R2,-			; Save start address of logging buffer.
		<DUTU$OWN_STORAGE + OWN$L_LOG_BUF_START>
	ADDL3	#LOG_BUF_SIZE, R2,-	; Save end address of logging buffer.
		<DUTU$OWN_STORAGE + OWN$L_LOG_BUF_END>
	MOVL	R2,-			; Init read pointer.
		<DUTU$OWN_STORAGE + OWN$L_NEXT_READ>
	MOVL	R2,-			; Init write pointer.
		<DUTU$OWN_STORAGE + OWN$L_NEXT_WRITE>
;
; Set up include and exclude ranges as follows to facilitate easy patching.
;
	ASSUME	OWN$W_INC_HILIM EQ OWN$W_INC_LOLIM+2
	ASSUME	OWN$W_EXC_LOLIM EQ OWN$W_INC_HILIM+2
	ASSUME	OWN$W_EXC_HILIM EQ OWN$W_EXC_LOLIM+2
	MOVL	#-1@16,-		; Include all units
		<DUTU$OWN_STORAGE + OWN$W_INC_LOLIM>
	MOVZWL	#-1,-			; Exclude no units
		<DUTU$OWN_STORAGE + OWN$W_EXC_LOLIM>
	MOVW	#OWN$M_LOG_PRESENT,-	; Buffer present and accounted for, sir.
		<DUTU$OWN_STORAGE + OWN$W_STATUS>

99$:	RSB



	.SBTTL	DUTU$LOG_PKT - Log a MSCP packet
;++
;
;	DUTU$LOG_PKT - Log a MSCP packet
;
; Functional Description:
;
;	The following routines log an MSCP packet into the driver's
;	ring buffer.  logging a command packet via a SEND_MSCP macro
;	or an end packet or attention message via the class driver's
;	input dispatching routine.
;
;	Separate ranges of unit numbers can be selectively included
;	or excluded from being logged.	The included units range is
;	checked first, then the excluded units range.  If the unit
;	number is within the include range and NOT within the exclude
;	range, the packet is logged.  The default conditions are:
;		INCLUDE=ALL  (low =  0, high = -1)
;		EXCLUDE=NONE (low = -1, high =	0)
;
;
; Inputs:
;
;	R0	Packet Type (type assumed to be supported no error checking)
;	R1	Length of pkt to be logged  (End pkt case)
;	R2	Address of pkt to be logged
;	R3	CDT address		    (End pkt case)
;	R5	CDRP address		    (Command pkt case)
;
; Implicit inputs:
;
;	OWN$L_NEXT_READ		Address of next packet to be read
;	OWN$L_NEXT_WRITE	Address to write next packet
;	OWN$L_LOG_BUF_START	Address of start of buffer
;	OWN$L_LOG_BUF_END	Address of end of buffer
;	OWN$W_INC_LOLIM		Included units low limit
;	OWN$W_INC_HILIM		Included units high limit
;	OWN$W_EXC_LOLIM		Excluded units low limit
;	OWN$W_EXC_HILIM		Excluded units high limit
;
;	Status Bits in OWN$W_STATUS:
;
;	OWN$V_PKT_LOGGED	Packet has been logged since last read
;	OWN$V_PKT_LOST		One or more packets overwritten since
;				last read
;--
	ASSUME	OWN$W_INC_HILIM EQ OWN$W_INC_LOLIM+2
	ASSUME	OWN$W_EXC_LOLIM EQ OWN$W_INC_HILIM+2
	ASSUME	OWN$W_EXC_HILIM EQ OWN$W_EXC_LOLIM+2

	.ENABLE LSB

DUTU$LOG_PKT::

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

	MOVL	R0,R6				; save command type
	MNEGL	#1,R7				; Assume noirp
	CMPL	#PKT$C_MSCP_CMD,R6		; command packet?
	BNEQU	10$				; no then skip...
	MOVL	#MSCP$K_MXCMDLEN,R1		; Get length of packet.
	MOVL	CDRP$L_CDT(R5),R3		; Get CDT address.
	CMPW	#CDRP$L_IOQFL,CDRP$W_CDRPSIZE(R5);Is this CDRP embedded in an I
	BNEQ	10$				; EQL implies NOT no. (i.e. yes)
	MOVAL	CDRP$L_IOQFL(R5),R7		; Save Addr of "IRP" in R7

10$:	MOVAL	DUTU$OWN_STORAGE, R5		; Get OWN STORAGE address.
	MOVW	MSCP$W_UNIT(R2), R4		; Get unit number.
	MOVAL	OWN$W_INC_LOLIM(R5), R0		; Get include range low limit.
	CMPW	R4, (R0)+			; Within include range low end?
	BLSSU	35$				; LSS means not included.
	CMPW	R4, (R0)+			; Within include range high end?
	BGTRU	35$				; GTR means not included.
	CMPW	R4, (R0)+			; Outside of exclude range?
	BLSSU	15$				; LSS means NOT excluded.
	CMPW	R4, (R0)+			; Within exclude range?
	BLEQU	35$				; LEQ means excluded.

15$:	MOVL	OWN$L_NEXT_WRITE(R5), R0	; Get location to write pkt.
	BBCS	#OWN$V_PKT_LOGGED, -		; If bit was clear, buffer
		OWN$W_STATUS(R5), 20$		;  is logically empty.
	CMPL	R0, OWN$L_NEXT_READ(R5)		; Check for full buffer.
	BNEQ	20$				; Branch if buffer not full or
						;  already clobbered.
	BISW	#OWN$M_PKT_LOST, -		; Else, we have wrapped around
		OWN$W_STATUS(R5)		;  the buffer.
;
; Log header info to buffer
;
20$:	READ_SYSTIME	(R0)+			; Fill in system time.

	TSTL	R3				; CDT address valid?
	BGEQ	21$				; If not, use "***".
	MOVL	CDT$L_PB(R3), R3		; Get PB address.
	BGEQ	21$				; If no PB, use "***".
	MOVL	PB$L_SBLINK(R3), R3		; Get SB address.
	BLSS	22$				; If no SB, use "***".

21$:	ASSUME PKT$W_LENGTH   EQ    PKT$B_SYSTEMID+6
	CLRQ	(R0)+				; Show no SYSTEMID
	MOVW	R1, -2(R0)			; Packet length
	ASSUME	PKT$T_NODENAME	 EQ   PKT$W_LENGTH+2
	MOVL	#^X2A2A2A03, (R0)+		; Make nodename "***"
	CLRQ	(R0)+
	CLRL	(R0)+
	BRB	25$

22$:	MOVQ	SB$B_SYSTEMID(R3), (R0)+	; Fill in system id.
	MOVW	R1, -2(R0)			; Fill in logged pkt length.
	MOVQ	SB$T_NODENAME(R3), (R0)+	; Grab nodename, part 1.
	MOVQ	SB$T_NODENAME+8(R3), (R0)+	; Grab nodename, part 2.

25$:	MOVL	R7,(R0)+			; IRP address
	MOVL	R6,(R0)+			; packet type
	MOVW	R4,-2(R0)			; Mscp Unit
;
; Actually log the packet now
;
	MOVC5	R1,(R2),#0,#PKT$C_DATA_LEN,(R0) ; Zero fill packet into buffer.
;
; now R3 = new address for next write
;
	MOVAL	DUTU$OWN_STORAGE, R5		; Restore OWN address.
	CMPL	R3, OWN$L_LOG_BUF_END(R5)	; At end of ring buffer?
	BLSSU	30$				; Branch if not.
	MOVL	OWN$L_LOG_BUF_START(R5), R3	; Else reset to beginning.

30$:	MOVL	R3, OWN$L_NEXT_WRITE(R5)	; Set next write address.

35$:	RSB

	.DISABLE LSB



	.SBTTL	----- IRP/EXIT LOGGING ROUTINES -----
	.SBTTL	DUTU$LOG_IRP_PKT	- Log an IRP packets
;++
;
; DUTU$LOG_IRP_PKT	- Log an IRP packet
;
; Functional Description:
;
;	The following routines copy an IRP packet into the driver's IRP
;	ring buffer.  Log an IRP command packet at START_IO and an IRP exit
;	packet at FUNCTION_EXIT.
;
;	Separate ranges of unit numbers can be selectively included
;	or excluded from being logged.	The included units range is
;	checked first, then the excluded units range.  If the unit
;	number is within the include range and NOT within the exclude
;	range, the packet is logged.  The default conditions are:
;		INCLUDE=ALL  (low =  0, high = -1)
;		EXCLUDE=NONE (low = -1, high =	0)
;
;
; Inputs: (DUTU$LOG_IRP...PKT)
;
;
;	R0,R1	IOSB status
;	R3	UCB address
;	R5	CDRP address
;
; Implicit inputs:
;
;	OWN$L_NEXT_READ			Address of next packet to be read
;	OWN$L_NEXT_WRITE		Address to write next packet
;	OWN$L_LOG_BUF_START		Address of start of buffer
;	OWN$L_LOG_BUF_END		Address of end of buffer
;	OWN$W_INC_LOLIM			Included units low limit
;	OWN$W_INC_HILIM			Included units high limit
;	OWN$W_EXC_LOLIM			Excluded units low limit
;	OWN$W_EXC_HILIM			Excluded units high limit
;
;	Status Bits in OWN$W_STATUS:
;
;	OWN$V_PKT_LOGGED		Packet has been logged since last read
;	OWN$V_PKT_LOST			One or more packets overwritten since
;					 last read
;--
	ASSUME	OWN$W_INC_HILIM EQ OWN$W_INC_LOLIM+2
	ASSUME	OWN$W_EXC_LOLIM EQ OWN$W_INC_HILIM+2
	ASSUME	OWN$W_EXC_HILIM EQ OWN$W_EXC_LOLIM+2

	.ENABLE LSB

DUTU$LOG_IRP_PKT::

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

	MOVL	#PKT$C_IRP,R6			; It's an IRP CMD packet
	BRW	DUTU$LOG_IRP_COMMON

DUTU$LOG_IRP_EXIT_PKT::

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

	MOVL	#PKT$C_EXIT,R6			; It's an IRP exit packet

DUTU$LOG_IRP_COMMON:

	PUSHR	#^M<R0,R1>
	MOVAL	DUTU$OWN_STORAGE, R2		; Get OWN STORAGE address.
	MOVW	UCB$W_UNIT(R3), R4		; Get unit number.
	MOVAL	OWN$W_INC_LOLIM(R2), R0		; Get include range low limit.
	CMPW	R4, (R0)+			; Within include range low end?
	BLSSU	35$				; LSS means not included.
	CMPW	R4, (R0)+			; Within include range high end?
	BGTRU	35$				; GTR means not included.
	CMPW	R4, (R0)+			; Outside of exclude range?
	BLSSU	15$				; LSS means NOT excluded.
	CMPW	R4, (R0)+			; Within exclude range?
	BLEQU	35$				; LEQ means excluded.

15$:	MOVL	OWN$L_NEXT_WRITE(R2), R0	; Get location to write pkt.
	BBCS	#OWN$V_PKT_LOGGED,	-	; If bit was clear, buffer
		OWN$W_STATUS(R2), 20$		;  is logically empty.
	CMPL	R0, OWN$L_NEXT_READ(R2)		; Check for full buffer.
	BNEQ	20$				; Branch if buffer not full or
						;  already clobbered.
	BISW	#OWN$M_PKT_LOST, -		; Else, we have wrapped around
		OWN$W_STATUS(R2)		;  the buffer.
;
; Log header info to buffer
;
20$:	ASSUME	PKT$Q_SYSTIME		EQ	0

	READ_SYSTIME	(R0)+			; Fill in system time.

	ASSUME	PKT$B_SYSTEMID		EQ	PKT$Q_SYSTIME+8
;; NOTE:
;; Use the UCB's CDT address, as the CDRP's CDT may not be initialized.
;;	MOVL	CDRP$L_CDT(R5), R2		; R2 => CDT.
	MOVL	UCB$L_CDT(R3), R2		; R2 => CDT.
	BGEQ	21$				; If no CDT, use "***".
	MOVL	CDT$L_PB(R2), R2		; Get R2 => PB address.
	BLSS	22$				; If no PB, use "***".

21$:	ASSUME PKT$W_LENGTH   EQ    PKT$B_SYSTEMID+6
	CLRQ	(R0)+				; Show no SYSTEMID
	MOVW	#IRP$C_CDRP, -2(R0)		; Packet length
	ASSUME	PKT$T_NODENAME	 EQ   PKT$W_LENGTH+2
	MOVL	#^X2A2A2A03, (R0)+		; Make nodename ***
	CLRQ	(R0)+
	CLRL	(R0)+
	BRB	23$

22$:	MOVL	PB$L_SBLINK(R2), R2		; Get R2 => SB address.
	BGEQ	21$				; If no SB, use "***".
	MOVQ	SB$B_SYSTEMID(R2), (R0)+	; Fill in system id.
	ASSUME	PKT$W_LENGTH		EQ	PKT$B_SYSTEMID+6
	MOVW	#IRP$C_CDRP, -2(R0)		; Fill in logged pkt length.
	ASSUME	PKT$T_NODENAME		EQ	PKT$W_LENGTH+2
	MOVQ	SB$T_NODENAME(R2), (R0)+	; Grab nodename, part 1.
	MOVQ	SB$T_NODENAME+8(R2), (R0)+	; Grab nodename, part 2.

23$:	ASSUME	PKT$L_IRP_ADDR		EQ	PKT$T_NODENAME+16
	MOVAB	CDRP$L_IOQFL(R5), (R0)+		; Save IRP address
	ASSUME	PKT$B_TYPE		EQ	PKT$L_IRP_ADDR+4
	MOVW	R6, (R0)+			; Fill in type (IRP or Exit)
	ASSUME	PKT$W_UNIT		EQ	PKT$B_TYPE+2
	MOVW	R4, (R0)+			; Fill in MSCP unit.
	ASSUME	PKT$C_HDR_SIZE		EQ	PKT$W_UNIT+2
;
; Actually log the packet now
;
	PUSHR	#^M<R0,R5>
	MOVC5	#IRP$C_CDRP,-			; Copy packet into buffer.
		CDRP$L_IOQFL(R5),-
		#0,-
		#PKT$C_DATA_LEN,-
		(R0)
	POPR	#^M<R0,R5>
;
; now R3 = new address for next write.	Also it is just beyond the copied
; IRP.	As such, we could use CDRP offsets to get at IRP fields in the copy.
;
	MOVAL	DUTU$OWN_STORAGE, R2		; Restore OWN address.
	CMPL	R3, OWN$L_LOG_BUF_END(R2)	; At end of ring buffer?
	BLSSU	30$				; Branch if not.
	MOVL	OWN$L_LOG_BUF_START(R2), R3	; Else reset to beginning.

30$:	MOVL	R3, OWN$L_NEXT_WRITE(R2)	; Set next write address.
	CMPL	#PKT$C_EXIT,R6			; Is it an exit packet
	BNEQ	35$				; BR around if not
	MOVL	R0,R5				; Need to use R0 so move it
	POPR	#^M<R0,R1>			; Restore IOSB
	MOVL	R0,IRP$L_IOST1(R5)		; Copy IOST1 to logged exit IRP
	MOVL	R1,IRP$L_IOST2(R5)		; Copy IOST2 to logged exit IRP
	RSB

35$:	POPR	#^M<R0,R1>
	RSB

	.DISABLE LSB

	.ENDC


	.SBTTL	 - DUTU$COPY_UNIT_FLAGS
;++
;
; Functional Description:
;
;	This routine tests a subset of the flags at MSCP$W_UNT_FLGS
;	and sets appropriate flags in UCB$L_DEVCHAR2 and UCB$L_DEVDEPND2,
;	most noteably cacheing capability and enabled and for the presence
;	of a loader.
;
;	This routine is called through RECORD_ONLINE, RECORD_SETUNIT_CHAR,
;	RECORD_GETUNIT_CHAR or DUTU$POLL_FOR_UNITS and is used by TUdriver.
;
; Inputs:
;
;	R0	Message buffer address.
;	R2	UCB address
;
; Outputs:
;
;	All registers preserved
;
;--

DUTU$COPY_UNIT_FLAGS::

	.JSB_ENTRY INPUT=   <R0,   R2	      >,-
		   OUTPUT=  <>,-
		   SCRATCH= <>,-
		   PRESERVE=<R0,R1,R2,R3,R4,R5>
;
; Start by assuming no cache support
;
	BICL	#DEV$M_WBC,-		; Assume device does not support
		UCB$L_DEVCHAR2(R2)	;  write-back caching.
	BICL	#<MT2$M_WBC_ENABLE!-	; Assume device does not support
		  MT2$M_RDC_DISABLE>,-	;  write-back or read cacheing
		UCB$L_DEVDEPND2(R2)	;
	BITW	#MSCP$M_UF_CACH,-	; Does the device support write-back
		MSCP$W_UNT_FLGS(R0)	;  caching?
	BEQL	20$			; Branch if not.
;
; Device does support caching of some sort
;
	BISL	#DEV$M_WBC,-		; Otherwise, set write-back cache
		UCB$L_DEVCHAR2(R2)	;  characteristic in UCB.
	BITB	#<MSCP$M_UF_WBKNV>,-	; Is write-back caching enabled?
		MSCP$W_UNT_FLGS(R0)	;
	BEQL	10$			; Branch if not.
	BISL	#MT2$M_WBC_ENABLE,-	; Otherwise, indicate write-back
		UCB$L_DEVDEPND2(R2)	;  caching enabled in UCB.

10$:	BITW	#MSCP$M_UF_SCCHH,-	; Is high-speed read caching supressed?
		MSCP$W_UNT_FLGS(R0)	;
	BEQL	20$			; Branch if not.
	BISL	#MT2$M_RDC_DISABLE,-	; Otherwise, indicate read caching
		UCB$L_DEVDEPND2(R2)	;  disabled in UCB.
;
; check for the presence of a loader, regardless of cacheing (non)ablility
;
20$:	BBC	#MSCP$V_UF_LOADR,-	; Does device support a loader?
		MSCP$W_UNT_FLGS(R0),30$ ;
	BISL	#DEV$M_LDR,-		; Yes, set loader present
		UCB$L_DEVCHAR2(R2)	;  flag in the UCB
30$:	RSB



.IF DF IRP$Q_QIO_P1			; If backporting prior to V7.0
	.SBTTL	DUTU$CHANGE_AFFINITY - Change the affinity of units tied to a SCS port
;++
;
; Functional Description:
;
;	This routine is called when SCS wishes to notify a SYSAP of a change in
;	a SCS port's CPU affinity. This routine clones the port's new CPU affinity to
;	the device UCBs tied to this connection so that the I/O exec sends the start I/Os
;	to the right CPU. The new affinity is located in the port's UCB.
;
; Calling Sequence:
;
;	JSB	@CDT$L_CHANGE_AFFINITY(CDT)
;
; Inputs:
;
;	R3	CDT address
;	R4	PDT address
;	PDT$L_UCB0 - port's UCB address
;
; Outputs:
;	All registers preserved
;
;--

DUTU$CHANGE_AFFINITY::

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

	MOVL	CDT$L_AUXSTRUC(R3),R3	; Get CDDB into R3
	MOVL	PDT$L_UCB0(R4),R5	; Get port's UCB into R5
;
; Loop through all UCBs tied to CDDB and reset Fast Path port CPU affinity
;
	MOVAB	<CDDB$L_UCBCHAIN -	; Get head of CDDB UCB chain - offset UCB$L_CDDB_LINK
		-UCB$L_CDDB_LINK>(R3), -
		R2

10$:	MOVL	UCB$L_CDDB_LINK(R2), R2 ; Chain to next UCB.
	BEQL	100$			; Branch if no more UCBs as all done
	MOVL	UCB$L_PORT_CPUDB(R5),-	; Clone port CPU database to device UCB
		UCB$L_PORT_CPUDB(R2)
	BRW	10$			; Go get next UCB

100$:	RSB
.ENDC
	.END

