doswstuff=0
.IF DF CA$_IO_DEBUG             ; if debug version
	.TITLE	IOSUBNPAG_MON - NONPAGED I/O RELATED SUBROUTINES
.IFF
	.TITLE	IOSUBNPAG - NONPAGED I/O RELATED SUBROUTINES
.ENDC
	.IDENT	'X-57'
;****************************************************************************
;*									    *
;*  COPYRIGHT © 1978, 1980, 1982, 1984, 1988, 1991, 1992, 1993, 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.		    *
;* 									    *
;*									    *
;****************************************************************************
;
; D. N. CUTLER 13-JUN-76
;
;
; NONPAGED I/O RELATED SUBROUTINES
;
; MODIFIED BY:
;
;
;
;	X-57	GCE	Glenn C. Everhart		10-Jun-1997
;		The "finipl8" processing as previously defined requires
;		that intercept code determine whether to clear ucb busy
;		and arrange for the next I/O to start.
;		This is too hard. Change the fast_finish macro to continue
;		I/O when called from contexts which otherwise would continue
;		it, and to just return when not. That way a routine called
;		via an irp$l_pid intercept can just execute, and the I/O
;		restart processing (normal in places like REQCOM) will be
;		done if the call came from something like REQCOM, but not
;		if it came from something like COM$POST.
;		Also arrange to check kernel stack in ioc$initiate
;		to see if it is above an "alert zone" (2 pagelets
;		has been determined experimentally to be enough). If the
;		kernel stack is in the alert zone, fork to clear it rather
;		than allowing recursive calls to overflow it.
;
;
; MACRO LIBRARY CALLS
;
 
	$ADPDEF				;DEFINE ADP OFFSETS
	$CADEF				;DEFINE CONDITIONAL ASSEMBLY PARAMETERS
	$CANDEF				;DEFINE CANCEL I/O REASON CODES
	$CCBDEF				;define CCB offsets
	$CDDBDEF			;DEFINE CDDB OFFSETS
	$CDRPDEF			;DEFINE CLASS DRIVER I/O REQUEST PACKET
	$CPBDEF				;DEFINE CPU CAPABILITIES
	$CPUDEF				;DEFINE PER-CPU DATA BLOCK OFFSETS
	$CRBDEF				;DEFINE CRB OFFSETS
	$DCDEF				;DEFINE DEVICE CLASSES
	$DDBDEF				;DEFINE DDB OFFSETS
	$DDTDEF				;DEFINE DDT OFFSETS
 	$DEVDEF				;DEFINE DEVICE CHARACTERISTICS FLAGS
	$DPTDEF				;DEFINE THE DEVICE PROLOGUE TABLE
	$DTNDEF				;define DTN offsets
	$DYNDEF				;DEFINE DYNAMIC POOL BLOCK TYPES
	$EMBDEF				;DEFINE EMB OFFSETS
	$FKBDEF				;DEFINE FORK BLOCK
	$IDBDEF				;DEFINE IDB OFFSETS
	$IOCDEF				;DEFINE IOC$SEARCHxxx FLAGS
	$IPLDEF				;DEFINE INTERRUPT PRIORITY LEVELS
	$IRPDEF				;DEFINE IRP OFFSETS
	.IF	NDF,IRP$M_FINIPL8
IRP$M_FINIPL8 = ^X8000000 ;temporary def of new bit
IRP$V_FINIPL8 = 27
	.ENDC
	$JIBDEF				;DEFINE JIB OFFSETS
	$LCKDEF				;DEFINE LOCK MANAGER SYMBOLS
	$MSCPDEF			;DEFINE MSCP STRUCTURES
	$NSADEF				;DEFINE PRIVILEGE AUDITING ITEM CODES
	$ORBDEF				;DEFINE OBJECT RIGHTS BLOCK OFFSETS
	$PCBDEF				;DEFINE PCB OFFSETS
	$PDTDEF				;Define PDT offsets
	$PHDDEF				;define PHD offsets
	$PRDEF				;DEFINE PROCESSOR REGISTERS
	$PRVDEF				;DEFINE PRIVILEGE BITS
	$SBDEF				; Define system block offsets
	$SPDTDEF			; SCSI PDT definitions
	$SPLCODDEF			;DEFINE SPINLOCK INDICES
	$SSDEF				;DEFINE SYSTEM STATUS CODES
	$TTYDEF				;DEFINE TERMINAL WRITE PACKET OFFSETS
	$UBMDDEF			;Define UNIBUS Map Descriptor structure
	$UCBDEF				;DEFINE UCB OFFSETS
	$VECDEF				;DEFINE CRB VECTOR OFFSETS

	driver_code
	.if	df,doswstuff
	.SBTTL	Hook in switching driver control for multiple disk paths
;++
; IOC_STD$SETUP_SWITCH	-	Set up switching driver
;
; This routine will add a secondary UCB to the list of switching
; paths for a primary UCB, setting up the primary UCB and its path
; as the primary switch path first if necessary. 
;
; CALLING SEQUENCE:
;       IOC_STD$SETUP_SWITCH(PrimaryUCB,SecondaryUCB)
;       NOTE: If calling sequence changes, the MACRO in SYSMAR must be changed.
; ENVIRONMENT:
;	This routine must be called holding forklock and the I/O database
;	write mutex. Code underlying just assumes these and will not
;	disturb them.
; INPUTS:
;       ARG$_PUCB(AP)	- Primary path UCB
;	ARG$_SUCB(AP)	- Secondary path UCB
; OUTPUTS:
;	None
; RETURN VALUE:
;       None (actually R0 is preserved)
$OFFDEF ARG,< -
        PUCB, -
	SUCB -
        >

UNIVERSAL_ENTRY IOC_STD$SETUP_SWITCH, -
		MASK=<M^<R2,R3,R4,R5,R6,R7,R8,R9,R11>>
;IOC_STD$SETUP_SWITCH::
	SUBL2	#<560>,SP		; GET SOME SPACE
	MOVL	SP,R11			; POINT AT THE BASE
; 528 BYTES BUFFER for swdriver
;   4 sw ucb
;   4 length
;   4 descriptor hdr word
;   4 buffer address for descriptor
lc.desc=0
lc.dsca=4
lc.len=8
lc.ucb=12
lc.buf=16
	MOVL	#^A/SWA0/,LC.BUF(R11)	; SET UP DATA FOR DEVICE NAME
	MOVB	#^A/:/,<LC.BUF+4>(R11)	; SWA1: TO BEGIN WITH
	MOVAB	LC.BUF(R11),LC.DSCA(R11) ; SET UP DESCRIPTOR
	MOVW	#5,LC.DESC(R11)		; LENGTH=5 CHARS
	MOVB	#DSC$K_DTYPE_T,<LC.DESC+2>(R11)
	MOVB	#1,<LC.DESC+3>(R11)	; SET UP STATIC TEXT BUFFER
	MOVAB	LC.DESC(R11),R1		; POINT AT DESCRIPTOR
; Cannot use ioc$searchdev at high IPL so call IOC$SEARCHINT
; instead.

	pushl	r11
	clrl	r2			; unit zero sought
	clrl	r3			; no alloc class
	movl	#5,r8			; "SWA0:" is 5 chars long
	movab	LC.BUF(R11),r9		; Address of name string
	movl	#<IOC$M_ANY!IOC$M_LOCAL>,r10	; flags
	movab	<lc.buf+20>(r11),r11	; space for lock value block
	jsb	g^IOC$SEARCHINT		; Go find a device if possible
	popl	r11			; we need our buffer pointer back
	blbc	r0,999$			; bail out if we fail
; r5 should be the found UCB
	movl	r5,r1			; fix up for test
	bgeq	999$			; so we are REAL sure the UCB is neg.

;	JSB	G^IOC$SEARCHDEV		; HUNT FOR THE DEVICE
; Since we will call an internal routine it doesn't matter that the
; device is a template one. The routine will work in any case.
;	BLBC	R0,999$			; If no SW device exists, bail out.
;
; We now have a pointer to the SW UCB. Store it.
; Be sure the devices are online.
	BITL	#UCB$M_ONLINE,UCB$L_STS(R5)	; If device is not online exit
;	BITL	#UCB$M_ONLINE,UCB$L_STS(R1)	; If device is not online exit
	BEQL	999$
	MOVL	R5,LC.UCB(R11)		; SW UCB SWA0:
	MOVL	R5,R9			; Keep a SWA0: pointer
;	MOVL	R1,LC.UCB(R11)		; SW UCB SWA0:
;	MOVL	R1,R9			; Keep a SWA0: pointer
	MOVL	UCB$L_DDT(R1),R10	; SW DDT
;
; CALL THE SWDRIVER ROUTINE TO SEE IF THE PRIMARY PATH IS ALREADY
; INTERCEPTED.
;
	MOVL	#5,LC.BUF(R11)		; REPORT IF DEVICE HAS SW INTC
	MOVL	ARG$_PUCB(AP),R7
; Be sure the device we're adding is online too.
	BITL	#UCB$M_ONLINE,UCB$L_STS(R7)	; If device is not online exit
	BEQL	999$
	MOVL	R7,<LC.BUF+4>(R11) 	; ASK OF PRIMARY UCB
	MOVL	ARG$_SUCB(AP),R8	; Load the secondary UCB address here
	PUSHL	R1			; SW UCB ADDRESS
	PUSHL	#528			; BUFFER SIZE
	PUSHAB	LC.BUF(R11)		; BUFFER ADDRESS
	CALLS	#3,@DDT$PS_MNTVER_2(R10) ; CALL THE ENTRY
; SW UCB USED OR 0 RETURNS IN BUF+8
	TSTL	<LC.BUF+8>(R11)		; IS A SW UCB THERE NOW FOR PRIMARY?
	BLSS	20$			; IF LSS, APPARENTLY YES
;
;
; If the primary does NOT have a UCB associated, guard against a whole passel
; of possible race conditions by checking also that what was passed as our
; secondary UCB is also not intercepted at this point. If it is intercepted
; we want to switch roles for the arguments and use the existing
; SW UCB and hook in the primary instead...
;
	MOVL	R8,<LC.BUF+4>(R11)	; Is the secondary intercepted already?
	MOVL	#5,LC.BUF(R11)		; REPORT IF DEVICE HAS SW INTC
	PUSHL	R9			; SW UCB address for a SW unit (swa0:)
	PUSHL	#528			; buffer size
	PUSHAB	LC.BUF(R11)		; buffer address
	CALLS	#3,@DDT$PS_MNTVER_2(R10) ; CALL THE ENTRY
; SW UCB USED OR 0 RETURNS IN BUF+8
	TSTL	<LC.BUF+8>(R11)		; IS A SW UCB THERE NOW FOR SECONDARY?
	BGEQ	15$			; If not negative then no
;OOPS. We have an intercepted secondary UCB (thus already hooked in).
; Reverse roles and skip cloning a new UCB.
	MOVL	R7,R0
	MOVL	R8,R7
	MOVL	R0,R8			; Switch primary and secondary
	BRW	20$			; And go hook the "secondary" in now
15$:
; NO SW UCB SEEN FOR THE PRIMARY UCB. THEREFORE GO CLONE ONE.
	CLRL	-(SP)			; Make a cell
	MOVL	SP,R6			; Use (R6) as buffer
	PUSHL	R6			; Store UCB Address here
	PUSHL	LC.UCB(R11)		; The template is SWA0:
	CALLS	#2,G^IOC_STD$CLONE_UCB	; Clone the UCB
	MOVL	(R6),LC.UCB(R11)	; Copy the new UCB address here
	MOVL	(R6),R6			; Keep a copy in reg too
	ADDL2	#4,SP			; Fix stack up
	BLBC	R0,999$
; Now we have one. Now call the SWA0: cloned UCB routine
	PUSHL	R9			; Template UCB
	PUSHL	#1			; flag so swdriver will NOT need fork
	PUSHL	R10			; DDT
	PUSHL	LC.UCB(R11)		; Cloned UCB we just got
	CALLS	#4,@DDT$PS_CLONEDUCB_2(R10) ; Call cloned UCB routine
;
; By passing the address 1 for a "PCB" we trigger a mod in the SWDRIVER
; cloned UCB routine so it will not fork but rather just run all its
; startup before returning. Therefore on return, the full UCB of the
; SW unit is set up for use and we now have a pointer to it.
; Now we must do the necessary setup of the primary path so that the
; SW unit just cloned will contain it.
;
	MOVL	#9,LC.BUF(R11)		; Set up to mung
	MOVL	R7,<LC.BUF+8>(R11)	; Primary UCB is what to mung
	CLRL	<LC.BUF+4>(R11)
	PUSHL	R6			; Use the cloned SW UCB, not SWA0:
	PUSHL	#528			; magic buffer size
	PUSHAB	LC.BUF(R11)		; Push the buffer address
	CALLS	#3,@DDT$PS_MNTVER_2(R10)	; Call the setup routine
	BLBC	R0,999$			; if we fail, exit
; At this point the master path is set up.
20$:
	MOVL	<LC.BUF+8>(R11),LC.UCB(R11)	;Save the SW UCB actually hooked
; SW UCB FOR PRIMARY IS SET UP NOW. ADD THE SECONDARY TO IT.
	MOVL	#9,LC.BUF(R11)		; Set up to mung
	MOVL	R8,<LC.BUF+8>(R11)	; Mung secondary UCB
	MOVL	#65535,<LC.BUF+4>(R11)
	PUSHL	R6			; R6 is the cloned SW UCB address
	PUSHL	#528			; magic buffer size
	PUSHAB	LC.BUF(R11)		; Push the buffer address
	CALLS	#3,@DDT$PS_MNTVER_2(R10)	; Call the setup routine
; Now the secondary path should be set up, if all went as we
; may hope
;
999$:
	ADDL2	#<560>,SP		; GET SOME SPACE
	RET
;
; 
	.SBTTL	Test for duplicate device and hook switch
;++
; IOC_STD$TEST_SWITCH
;
; This routine will look for a device with the same name as the
; device pointed at by the supplied UCB. If one is found, it will
; call IOC_STD$SETUP_SWITCH to connect it with the switching
; system. This routine is needed for places like iogen's scsi configuration
; code. The idea is that the first such UCB to be found will be the
; primary path as a general matter (with exception in DKdriver where its
; search knows of two at least and has a preference). Subsequent code can
; request a preferred path be made current if it is inactive.
;
; CALLING SEQUENCE:
;       IOC_STD$TEST_SWITCH(PrimaryUCB)
;       NOTE: If calling sequence changes, the MACRO in SYSMAR must be changed.
; ENVIRONMENT:
;	This routine must be called holding forklock and the I/O database
;	write mutex. Code underlying just assumes these and will not
;	disturb them.
; INPUTS:
;       ARG$_PUCB(AP)	- UCB to be tested for duplication
; OUTPUTS:
;	None
; RETURN VALUE:
;       Success if we find a dupliate and hook it in.
$OFFDEF ARG,< -
        PUCB -
        >

UNIVERSAL_ENTRY IOC_STD$TEST_SWITCH, -
		MASK=<M^<R2,R3,R4,R5,R6,R7,R8,R9,R11>>
;IOC_STD$TEST_SWITCH::
; Search in the SB that is pointed to via the supplied UCB
	MOVL	ARG$_PUCB(AP),R5		; Get the supplied UCB
	MOVL	UCB$L_DDB(R5),R6	; Point at the DDB
	MOVL	DDB$L_SB(R6),R7		; Point at the SB also
	CLRL	R11			; Set to scan the IODB
10$:
	JSB	G^IOC$SCAN_IODB		; Look for a device
	BLBC	R0,999$			; If done with the search scram
; R10 is the found UCB now, and R11 is the found DDB.
; First, don't use our own UCB
	CMPL	R10,R5			; Same UCB is what we know thanks
	BEQL	10$			; So look for a different one
; UCBs are not the same, so see if their name IS the same.
	CMPW	UCB$W_UNIT(R5),UCB$W_UNIT(R10)	; diff unit number =>no
	BNEQ	10$
; Compare name in the DDB
	.DSABL	FLAGGING		;suppress align warnings
	CMPL	DDB$T_NAME_STR(R6),DDB$T_NAME_STR(R11) ; Names differ?
	BNEQ	10$			; Compare all positions
	CMPL	<DDB$T_NAME_STR+4>(R6),<DDB$T_NAME_STR+4>(R11) ; Names differ?
	BNEQ	10$			; Compare all positions
	CMPL	<DDB$T_NAME_STR+8>(R6),<DDB$T_NAME_STR+8>(R11) ; Names differ?
	BNEQ	10$			; Compare all positions
	CMPL	<DDB$T_NAME_STR+12>(R6),<DDB$T_NAME_STR+12>(R11) ; Names differ?
	BNEQ	10$			; Compare all positions
	.ENABL FLAGGING
; Names are the same and unit number the same. Same alloc class?
	CMPL	DDB$L_ALLOCLS(R6),DDB$L_ALLOCLS(R11)	;same?
	BNEQ	10$			; if not, not what we want
; If the alloc classes are the same and ZERO are the SBs the same?
	TSTL	DDB$L_ALLOCLS(R11)	; Allo class zero?
	BNEQ	15$			; if ne no, names ARE the same
; alloc class zero might still differ if the SB differs
	CMPL	DDB$L_SB(R6),DDB$L_SB(R11) ; If SB differs, diff. node
	BNEQ	10$
15$:
; Be sure this is a disk. If it is a tape however, recall we get here
; when a second device has been set up. Therefore so long as the UCB
; we were entered with (the newly connected one, pointed at by R5)
; and the original one (pointed at by R10) are tape class still, set the
; no_assign bit for the UCB pointed at by R5.
	CMPB	UCB$B_DEVCLASS(R5),UCB$B_DEVCLASS(R10)
	BNEQ	17$				; Be sure both classesa re alike
	CMPB	UCB$B_DEVCLASS(R5),#DC$_TAPE	; See if this is tape
	BNEQ	17$				; If not, nothing special at all
; OK, we have a second path to a tape. 
; Since we don't support (in V1 anyway) switching of tape paths, and don't
; have facilities in SWdriver to store UCBs, we just set the no_assign
; bit in the UCB we just got. Thus the FIRST UCB to be seen, which
; will show failure to find anything, will not get marked unusable.
	BISL	#UCB$M_NO_ASSIGN,UCB$L_STS(R5)	; Set the second path as not to be used
	BRW	998$
17$:
;;	CMPB	UCB$B_DEVCLASS(R5),#DC$_DISK	; If not a disk, no switch
;;	BNEQ	998$
18$:
; Now we have a second device UCB that differs from the first but has the
; same name.
; Bind them together. Since we have presumably just created the UCB passed
; here, treat the one that we found already in the I/O database as the
; primary one.
	PUSHL	R5			; The new UCB is secondary
	PUSHL	R10			; the existing one was found earlier
	CALLS	#2,G^IOC_STD$SETUP_SWITCH	; Set the switch up
998$:
	MOVL	#SS$_NORMAL,R0		; And claim success
999$:
	RET
	.endc	;doswstuff
	.END
