TMSCP$ADD::
	.IF DEFINED DEBUG$PC_HISTORY
	BSBW	TMSCP$PCHIST		; Save this PC if we are keeping track
	.ENDC	   ;DEBUG$PC_HISTORY

;
; If this routine is stalled and has to wait for resources while trying
; to allocate a message buffer, we want to return to the caller to resume
; working on something else. 
; 
	PUSHR	#^M<R1,R2,R4,R5>	; Save all the registers before starting
	BSBB	10$			; Treat this whole routine as a big
	POPR	#^M<R1,R2,R4,R5>	;  subroutine so that if this thread
	RSB				;  stalls the registers can be restored

;
; Send out an attention message to every host we know, anouncing
; the discovery of a new tape.
;

10$:
	MOVL	G^SCS$GL_TMSCP,R5	; Pick up the TSRV address
	MOVZWL	# SS$_NORMAL, R0	; No hosts found status.
	MOVL	TSRV$L_HQB_FL(R5),R1	; Get the address of the first HQB
	MOVL	R1,R2			; and save it for checking.

	ASSUME	HQB$L_FLINK EQ 0

13$:
	CMPL	HQB$L_FLINK(R1),R2	; Check for an empty queue
	BNEQ	19$			; Queue not empty, continue

15$:
	RSB				; No good hosts, return

19$:
	TSTL	HQB$L_CDT(R1)		; Check the HQB for a good CDT address
	BNEQ	20$			;  Good CDT.
	MOVL	HQB$L_FLINK(R1),R1	;  No CDT... move on to the next host
	BRB	13$			;  and try again

20$:
	BSBW	TMSCP$ALLOCATE_HRB	; Allocate and set up an HRB
					; IRP included automatically
	BLBC	R0,15$			; Return to the caller with the error
	MOVL	UQB(AP),R4		; Get the UQB address
	MOVL	R4,HRB$L_UQB(R3)	; Save this address
	INCW	UQB$W_CURRENT(R4)	; Another request active on this unit

;
; The attention message will actually be sent to all hosts that are linked
; into the HQB queue in the TSRV. 
;

30$:
	MOVL	R1,HRB$L_HQB(R3)	; Store the address of the HRB.
	INSQUE	HRB$L_FLINK(R3), -	; Insert the HRB into tail of queue.
		@HQB$L_HRB_BL(R1)
	INCW	HQB$W_NUM_QUE(R1)	; Bump host queue reference count
	CMPW	HQB$W_NUM_QUE(R1),-	; Is this number <= max?
		HQB$W_MAX_QUE(R1)	; 
	BLEQU	40$			; Yes, skip
	MOVW	HQB$W_NUM_QUE(R1),-	; No, set new max
		HQB$W_MAX_QUE(R1)	;

40$:
	MOVL	HRB$L_IRP_CDRP(R3),R5	; Put the IRP address in R5
	MOVAL	IRP$L_FQFL(R5),R5	; Move down to the CDRP portion
	MOVL	HQB$L_CDT(R1),R4	; Get the address of the CDT
;
; Initialize the CDRP
;
	MOVL	R4,CDRP$L_CDT(R5)	; Get the address of the CDT 
	MOVL	CDT$L_PDT(R4),R4	; Prepare the PDT address for SCS
	MOVL	R4,HRB$L_PDT(R3)	;  and also store it away for future use

;
; Allocate and send out the attention message. Set the state, in case
; the connection to the host is lost, and we need to clean up. This module
; is called like a subroutine to allow us an opportunity to clean up the
; registers that were pushed on the stack in the event of failure.
;
	MOVW	#HRB$K_ST_MSG_WAIT,-	; Set the state of the request to
		HRB$W_STATE(R3)		;  reflect possible stall 
	ALLOC_MSG_BUF			; Allocate a message buffer
;		JSB	@PDT$L_ALLOCMSG(R4)
	PUSHR	#^M<R0,R1,R2,R3,R4,R5>	; Save registers destroyed by MOVC5

	ASSUME	MSCP$K_MXCMDLEN LE MSCP$K_LEN

	MOVC5	#0,(SP),#0,-		; Initialize entire structure in
		#MSCP$K_LEN,(R2)	;  in one fell swoop. 
	POPR	#^M<R0,R1,R2,R3,R4,R5>	; Save registers destroyed by MOVC5
	BISW	#HRB$M_STATE_INVALID,-	; The state of this request is "current"
		HRB$W_STATE(R3)		;  leave the old state for diagnosis
	BLBC	R0, 60$			; If LBC connection is broken, skip host

;
; The address of the allocated message buffer is returned in R2.
; Initialize the proper fields to turn the message buffer into an
; attention message.
;
	MOVL	R2,HRB$L_MSGBUF(R3)	; Save the message buffer address
	MOVB	#MSCP$K_OP_AVATN,-	; Set the attn msg op-code
		MSCP$B_OPCODE(R2)	;  
	MOVL	HRB$L_UQB(R3),R4	; Get the address of the UQB
	MOVW	UQB$W_SLUN(R4),-	; Get the unit number
		MSCP$W_UNIT(R2)
	MOVQ	UQB$Q_UNIT_ID(R4),-	; Copy unit identifier
		MSCP$Q_UNIT_ID(R2)	; 
	MOVW	UQB$W_UNIT_FLAGS(R4),-	; Get the unit_flags
		MSCP$W_UNT_FLGS(R2)	; 
	MOVL	UQB$L_UCB(R4),R1	; Get the UCB address
	MOVL	UCB$L_MEDIA_ID(R1),-	;  then copy the media identifier
		MSCP$L_MEDIA_ID(R2)	;  from the UCB into the msg packet

;
; Get the CDRP fields and the registers properly set up to send out the 
; attention message to all interested hosts.
;
	MOVL	HRB$L_IRP_CDRP(R3),R5	; Get the address of IRP for this 
	MOVAL	IRP$L_FQFL(R5),R5	;  request and position at the CDRP
	CLRL	CDRP$L_RWCPTR(R5)	; 
	CLRL	CDRP$L_RSPID(R5)	; Clear since no response is expected
	MOVL	HRB$L_PDT(R3),R4	; Pick up the PDT address for SCS
	MOVL	#MSCP$K_LEN,R1		; Size of the message to be sent
	CLRL	HRB$L_MSGBUF(R3)	; The message buffer is no longer ours

	.IF DEFINED DEBUG$LOG

	ASSUME TSRV$V_LOG_ENABLD  EQ  0

	MOVL	G^SCS$GL_TMSCP,R0	; Get the TSRV address.
	BLBC	TSRV$W_STATE(R0),50$	; Branch if logging is disabled.
	BSBW	TMSCP$LOG_END_PKT	; Otherwise, log the attention message.

50$:
	.ENDC	; DEBUG$LOG

	MOVW	#HRB$K_ST_SNDMS_WAIT,-	; Set the state for this request
		HRB$W_STATE(R3)		;  before calling the SCS service
	SEND_CNT_MSG_BUF		; Send it out
;		JSB	@PDT$L_SNDCNTMSG(R4)
	BISW	#HRB$M_STATE_INVALID,-	; The state of this request is "current"
		HRB$W_STATE(R3)		;  leave the old state for diagnosis

;
; Remove the HRB from the queue and loop to send the same attention 
; message to the next host. Continue sending out these messages until 
; all the hosts have been notified.
;

	ASSUME	HRB$L_FLINK EQ 0

60$:
	REMQUE	HRB$L_FLINK(R3), R3	; Take the HRB out of the queue
	MOVL	HRB$L_HQB(R3),R5	; Restore the HQB address
	DECW	HQB$W_NUM_QUE(R5)	; Take care of the reference count
	CLRL	HRB$L_FLINK(R3)		; Show this HRB as unhooked

70$:
	MOVL	HQB$L_FLINK(R5),R1	; Get the addr of the next HQB
	MOVL	HQB$L_TSRV(R5),R5	; Get the address of the TSRV structure
	MOVAL	TSRV$L_HQB_FL(R5),R0	; If the next HQB address does not 
	CMPL	R0,R1			;  not match the queue header
	BEQL	90$			;  then we have another host to notify
	TSTL	HQB$L_CDT(R1)		; Before sending out the message, check
	BNEQ	85$			;  for a valid CDT. If none, this host.
	MOVL	R1,R5			;  Get the next one off the queue.
	BRB	70$			;

85$:
	BRW	30$			; This one is good, send the message

;
;		Final status is set by CLEANUP_HRB which places SS$_NORMAL in R0.
;

90$:
	BRW	TMSCP$CLEANUP_HRB	; We are finished with this request.


	.SBTTL	-	NEW_DEVICE - Serve a new DSA device unit

;+
; Functional Description:
;
; This is an entry point provided for the tape class driver to call 
; directly with the address of a newly discovered UCB. This avoids the
; time delay that used to occur when the tape had to be discovered by
; the configure process. This change was originally conceived to solve
; a problem with mounting shadow set virtual units cluster wide, but
; has found universal benefit, and so now is used for all newly found
; DSA (tape class driver) devices.
;
;
; Inputs:
;
;	R5  =  UCB address of device to be served
;
; Outputs:
;
;	R0  =  status code to be returned to the caller
;		SS$_NORMAL 	- Unit Available attention messages have been sent
;		SS$_DEVACTIVE	- This device is already being served
;		SS$_DEVICEFULL	- There are already MAX_UNITS being served
;		SS$_DEVNOTDISM	- This device is not mounted for cluster access
;		SS$_INSFMEM	- Not enough memory to allocate HQB
;
;	R5  =  UCB address of device to be served
;-

; 
; This is a list of the premature end codes. 
;

TMSCP$NOSERV::	
TMSCP$NORMAL::
	MOVL	#SS$_NORMAL,R0		; They already have what they want

TMSCP$RESTOR::
	POPR	#^M<R1,R2,R3,R4,AP>	; Restore all the registers used 
	RSB				;  and return to the caller

TMSCP$NEW_DEVICE::
	.IF	DEFINED DEBUG$PC_HISTORY
	BSBW	TMSCP$PCHIST		; Log the PC for debugging.
	.ENDC  ;DEBUG$PC_HISTORY
	PUSHR	#^M<R1,R2,R3,R4,AP>	; Save all the registers before starting

;
; If auto-serving is not turned on, we know this device should not be served. 
; Control can be returned to caller with no further action. Otherwise, the 
; device  information needs to be checked to make sure it complies with the 
; serving guidelines.
;

	CMPB	#DC$_TAPE, -		; Make sure this device is a tape
		UCB$B_DEVCLASS(R5)	;  before we go any farther
	BNEQ	TMSCP$NORMAL		; Not a tape, get out now!
	MOVL	UCB$L_DDB(R5),R4	; Get the DDB address
	CMPL	G^CLU$GL_TAPE_ALLOCLS,-	; Compare allocation class of host
		DDB$L_ALLOCLS(R4)	;  with allocation class of device
	BNEQ	TMSCP$NOSERV		; Must be the same (for now at least)
	BBS	#DEV$V_SRV,-		; If the device is already
		UCB$L_DEVCHAR2(R5),-	;  served,
		TMSCP$NOSERV		;  don't serve it again

;
; Now we are dealing with a valid device for serving. Check for a local 
; connection that the allocation class is properly set.
; 
	BBC	#DEV$V_MSCP,-		; If not a MSCP tape, this device is
		UCB$L_DEVCHAR2(R5),10$	;  ok to serve due to non-zero serve_all
	BBS	#DEV$V_CDP,-		; If the class driver created this UCB
		UCB$L_DEVCHAR2(R5),-	;  for temporary use, this is not a real
		TMSCP$NOSERV		;  device and should not be served.
	MOVL	UCB$L_CDDB(R5),R0	; Get a pointer to the CDDB
	BEQL	TMSCP$NOSERV		; If this is a "temp" UCB don't serve
	TSTL	G^CLU$GL_TAPE_ALLOCLS	; If the allocation class of this
	BNEQ	7$			;  node is zero,

5$:
	BBC	#MSCP$V_CF_MLTHS,-	; or the controller for this device
		CDDB$W_CNTRLFLGS(R0),7$	;  is an HSC, don't serve it.

6$:
	BRW	TMSCP$NOSERV		; Provide a place to reach exit

7$:
	CMPB	#MSCP$K_CM_EMULA,-	; Otherwise,
		CDDB$B_CNTRLMDL(R0)	; If this is not a served path
	BNEQ	10$			; Serve the device       

	BBC	#DEV$V_2P,-		; Check to make sure this is a 
		UCB$L_DEVCHAR2(R5),6$	;  dual path device
	MOVL	UCB$L_2P_CDDB(R5),R0	; Get a pointer to the CDDB
	BEQL	6$			; Give up if there is no alternate
	CMPB	#MSCP$K_CM_EMULA,-	; Check to see if the alternate path
		CDDB$B_CNTRLMDL(R0)	;  is to a server also
	BEQL	6$			; This is a server path too give up 

10$:
	MOVL	G^SCS$GL_TMSCP,R4	;  and the address of the TSRV 
	CLRL	R3			; Init this reg for table index later
	MOVZWL	TSRV$W_NUM_UNIT(R4),R0	; Get the number of units
	CMPL	R0,#MAX_UNITS		;  and see if we are over the max
	BGTR	TMSCP$FULL		;  if we are, return an error

	BBC	#DEV$V_MNT,-		; If the tape is not mounted yet,
		UCB$L_DEVCHAR(R5),20$	;  we can begin serving it
	BRB	TMSCP$NOTSHR		; Can't serve an allocated device.

;
; Search through the UQB queue to make sure that a UQB for this unit does 
; not already exist. If none is found, memory is allocated, the UQB is 
; filled in, and linked into the UQB list in proper order by ascending 
; unit number.
;

20$:
	MOVL	TSRV$L_UNITS(R4)[R3],R1	; Get UQB address from the table
	BEQL	TMSCP$CREATE_UQB	; If this is the end, create one
	INCL	R3			; Add one to the index
	CMPL	UQB$L_UCB(R1),R5	; See if the UCB for this unit matches
	BNEQ	20$			; No match, keep looking
	BRW	TMSCP$NORMAL		; They already have what they want

TMSCP$FULL::
	MOVL	#SS$_DEVICEFULL,R0	; There are already MSX_UNITS
	BRW	TMSCP$RESTOR		;  being served

TMSCP$NOTSHR::
	MOVL	#SS$_DEVNOTDISM,R0	; This device is not mounted for 
	BRW	TMSCP$RESTOR		;  cluster wide access

TMSCP$NOMEM::
	MOVZWL	#SS$_INSFMEM,R0		; Not enough memory to allocate
	BRW	TMSCP$RESTOR		;  a decent UQB

;
; There is no UQB for a device with this unit number.
;

TMSCP$CREATE_UQB::
	.IF	DEFINED DEBUG$PC_HISTORY
	BSBW	TMSCP$PCHIST		; Log the PC for debugging.
	.ENDC  ;DEBUG$PC_HISTORY
	MOVZWL	#UQB$K_LENGTH,R1	; Set size of the UQB structure
	JSB	G^EXE$ALONONPAGED	; Grab some pool
	BLBC	R0,TMSCP$NOMEM		; If there was insf memory for alloc

	PUSHR	#^M<R0,R1,R2,R3,R4,R5>	; Save registers destroyed by MOVC5
	MOVC5	#0,(SP),#0,-		; Initialize entire structure in
		#UQB$K_LENGTH,(R2)	;  in one fell swoop. 
	POPR	#^M<R0,R1,R2,R3,R4,R5>	; Save registers destroyed by MOVC5
;
; Make the allocated memory look like a UQB. R2 points to the allocated region
;
	BISL	#<DEV$M_CLU!-		; Set the 'Avail Cluster wide' bit
		DEV$M_SRV>,-		;  and 'MSCP SERVER' bits
		UCB$L_DEVCHAR2(R5)
	MOVW	R1,UQB$W_SIZE(R2)	; Set the size
	MOVL	R5,UQB$L_UCB(R2)	; Save away the UCB address
	MOVB	#DYN$C_TSRV,-		; This is the tape based server
		UQB$B_TYPE(R2)		;  data structure
	MOVB	#DYN$C_TSRV_UQB,-	; More specifically, it is a 
		UQB$B_SUBTYPE(R2)	;  Unit Queue Block structure
	MOVW	#UQB$K_ST_AVAILABLE,-	; Set the initial state of this
		UQB$W_STATE(R2)		;  unit to available
	MOVW	UCB$W_UNIT(R5),-	; Get the "real" unit number
		UQB$W_UNIT(R2)		;  (the one off the plug)

	MOVL	UCB$L_DDB(R5),R0	; Get the device data block
	MOVL	DDB$L_ALLOCLS(R0),-	; Move the allocation class into
		UQB$L_ALLOCLS(R2)	;  the unit ID field
	MOVW	TSRV$W_NUM_UNIT(R4),-	; Then assign the local unit
		UQB$W_SLUN(R2)		;  number (unit table index)
	BISW	#MSCP$M_SLUN,-		; Set the bit in the unit number
		UQB$W_SLUN(R2)		;  indicating a server local unit
	INCW	TSRV$W_NUM_UNIT(R4)	; Ratchet the number of known units
	MOVZBL	DDB$T_NAME+1(R0),R1	; Put the first letter in a reg
	SUBB2	#^X40,R1		;  and normalize it 
	INSV	R1,-			; Add to those the first letter of 
		#UQB$V_D0,-		;  the device name
		#UQB$S_D0,-		; 
		UQB$W_DEVNAME(R2)	; 
	MOVZBL	DDB$T_NAME+2(R0),R1	; Put the second character in a reg
	SUBB2	#^X40,R1		;  and normalize it
	INSV	R1,-			; Get the second letter
		#UQB$V_D1,-		;  of the device name
		#UQB$S_D1,-		;  and save that also
		UQB$W_DEVNAME(R2)	; 
	MOVZBL	DDB$T_NAME+3(R0),R1	; Put the controller letter in a reg
 	SUBB2	#^X40,R1		;  and normalize that before saving
	INSV	R1,-			; Retrieve the controller letter
		#UQB$V_C,-		;  and save that away as the
       		#UQB$S_C,-		;  last of the device name
		UQB$W_DEVNAME(R2)	;  to be saved

;
; For the unique multi unit code, use the server local unit number as the 
; spindle specifier in the high byte, and set the low byte (access path) 
; to zero.
;
	MOVZWL	UQB$W_SLUN(R2),R0	; Get the "local" unit number
	BICL	#<MSCP$M_SHADOW!-	; Clear out the shadow bit
		MSCP$M_SLUN>,R0		;  and the SLUN bit before range checking
	MOVL	R2,TSRV$L_UNITS(R4)[R0]	; Save the UQB address in the table
	ASHL	#8,R0,R0		; Shift it into the high byte
	MOVW	R0,UQB$W_MULT_UNIT(R2)	; Save it away in the UQB
	MOVW	UCB$W_UNIT_FLAGS(R5),-	; Use the unit flags from the UCB 
		UQB$W_UNIT_FLAGS(R2)	;  just as they are in the UQB
	BBC	#DEV$V_SWL,-		; Check the device characteristics
		UCB$L_DEVCHAR(R5),60$	;  for software writelock
	BISW	#MSCP$M_UF_WRTPH,-	; If it is, set the hw flag 
		UQB$W_UNIT_FLAGS(R2)	;  so we don't issue writes to it

;
; Set the queue header counters and list heads to their initial values.
;

60$:
	MOVAL	UQB$L_BLOCKED_FL(R2),-	; Initialize the forward and 
		UQB$L_BLOCKED_FL(R2)	; 
	MOVAL	UQB$L_BLOCKED_FL(R2),-	;  backward pointers to point to
		UQB$L_BLOCKED_BL(R2)	;  the forward link of the blocked que
	INSQUE	UQB$L_FLINK(R2), -	; Insert the UQB into the queue
		@TSRV$L_UQB_BL(R4)

;
; Go off to MSCP to ADD a tape unit. When the ADDUNIT routine is eliminated,
; change this call format to use a simple JSB and leave the stack alone. 
;
	PUSHL	R2			; Copy UQB pointer
	PUSHL	#TSRV$K_AR_ADD		; Opcode for MSCP action routines
	PUSHL	#2			; Only 2 parameters
	MOVL	SP,AP			; Set address of parameter area
	JSB	TMSCP$ADD		; Go to the routine
	ADDL	#12,SP			; Clean up the stack before returning
	BRW	TMSCP$NORMAL		; Return a successful status


	.SBTTL	-	MV_SET_OFFLINE - Mount Verification set a device to the "offline" state.

;+
; Functional Description:
;
; This routine is JSB'd to from a mount verification thread. 
; The UCB of a particular tape is passed to this routine in
; R5. The corresponding UQB for this device is found, the
; field UQB$L_ONLINE_HQB is cleared, and the state is changed to
; "AVAILABLE".
;
;
; Inputs:
;
;	R5  =  UCB address
;
; Outputs:
;
;	None - all registers preserved
;-

TMSCP$MV_SET_OFFLINE::
	PUSHR	#^M<R0,R1,R2,R4>	; Save all the registers before starting
	LOCK	LOCKNAME=SCS,-		; Lock SCS access
		PRESERVE=NO,-		;  no need to preserve R0
		SAVIPL=-(SP)		;  save the current IPL
;	 MFPR	S^#PR$_IPL,-(SP)
;	BLBC	G^SMP$GL_FLAGS,30026$
;	 MOVZBL	S^#SPL$C_SCS,R0
;	 JSB	G^SMP$ACQUIRE
;	BRB	30027$
;30026$:
;		MTPR	S^#IPL$_SCS,S^#PR$_IPL
;30027$:
	.IF	DEFINED DEBUG$PC_HISTORY
	BSBW	TMSCP$PCHIST		; Log the PC for debugging.
	.ENDC  ;DEBUG$PC_HISTORY
	MOVL	G^SCS$GL_TMSCP,R1	; Get the address of the server
	MOVAL	TSRV$L_UQB_FL(R1),R4	; Get the queue list head address
	MOVL	R4,R2			;  and save it to verify the whole

	ASSUME	UQB$L_FLINK EQ 0

10$:
	MOVL	UQB$L_FLINK(R4),R4	;  queue has been searched
	CMPL	R4,R2			; End of UQB list?
	BEQL	60$			; If eql yes
	CMPL	UQB$L_UCB(R4),R5	; Is this the UQB for this unit?
	BNEQ	10$			; If neq no

;
; A UQB was found that corresponds to the UCB address passed.
; The HQB field that represented the host that held 
; the tape online is cleared. The UCB online count is sanity tested.
;

	MOVW	#UQB$K_ST_AVAILABLE,-	; Now set the state of this device to
		UQB$W_STATE(R4)		;   offline.
	CLRL	UQB$L_ONLINE_HQB(R4)	; Not online to any host now.
	DECB	UCB$B_ONLCNT(R5)	;  and note the change in online count
	TSTB	UCB$B_ONLCNT(R5)	; Online count should be zero
	BEQL	60$			;

	BUG_CHECK MSCPSERV, FATAL	; Online count was not "1" when we entered
;		.WORD	^XFEFF
;		.IIF IDN <FATAL>,<FATAL> , .WORD	BUG$_MSCPSERV!4
					;  this routine.

60$:
	UNLOCK	LOCKNAME=SCS,-		; Release SCS access
		PRESERVE=NO,-		;  don't preserve R0
		NEWIPL=(SP)+,-		;  return to former IPL
		CONDITION=RESTORE	;  restore spinlock to previous state
;	BLBC	G^SMP$GL_FLAGS,30033$
;	 MOVZBL	S^#SPL$C_SCS,R0
;	 JSB	G^SMP$RESTORE
;30033$:
;	 MTPR	(SP)+,S^#PR$_IPL
	POPR	#^M<R0,R1,R2,R4>	; Restore all the registers 
	RSB				; And resume mount verification


	.SBTTL	-	LISTEN - Listen for a Connect Request

;+
; Functional Description:
;
; When another cluster member wants to connect with the server, control
; is passed here as a fork process initiated by the port driver at IPL$_SCS.
; Upon a successful acceptance, this routine allocates a host queue block 
; and is then prepared to receive MSCP request packets from that host.
;
; The connection application itself is not treated as a true request. No 
; HRB is created for it, and no context is saved. It is either accepted or 
; rejected in this routine.
;
; Inputs:                                 
;	R0  =  Scratch
;	R1  =  Scratch
;	R2  =  Connect message packet address
;	R3  =  CDT
;	R4  =  PDT
;	R5  =  Scratch
;
; Outputs:
;	None (the connection is either accepted or rejected)
;
; All registers are preserved.
;-

TMSCP$LISTEN::
	.IF DEFINED DEBUG$PC_HISTORY
	BSBW	TMSCP$PCHIST_R3		; Save this PC if we are keeping track
	.ENDC	   ;DEBUG$PC_HISTORY

;
; Allocate an HQB for this host.
;

10$:
	PUSHL	R2			; Save the connect message address
	MOVZWL	#HQB$K_LENGTH,R1	; Get the size of the host queue block
	JSB	G^EXE$ALONONPAGED	;  and allocate that much memory
	MOVL	R2,R5			; Get the HQB address in R5
	POPL	R2			; Pop the saved connect message address
	BLBS	R0,15$			; Continue if successful
	.IF DEFINED DEBUG$REJECT_TRACKING
	MOVL	G^ SCS$GL_TMSCP, R1	; Get TSRV address.
	MOVL	R0,TSRV$L_REPLC_CNT+8(R1); Save away the error code
	INCL	TSRV$L_REPLC_CNT+12(R1)	; Save away a record of the reject
	.ENDC	   ;DEBUG$REJECT_TRACKING
	BRW	TMSCP$REJECT		; Reject this connection.

;
; Fill in as many fields as possible to initialize the HQB for use.
;

15$:
	PUSHR	#^M<R0,R1,R2,R3,R4,R5>	; Save registers destroyed by MOVC5
	MOVC5	#0,(SP),#0,-		; Initialize entire structure in
		#HQB$K_LENGTH,(R5)	;  in one fell swoop. 
	POPR	#^M<R0,R1,R2,R3,R4,R5>	; Save registers destroyed by MOVC5
	MOVW	R1,HQB$W_SIZE(R5)	; Set length of the data structure
	MOVB	#DYN$C_TSRV,-		; The structure type
		HQB$B_TYPE(R5)		;  is tape server
	MOVB	#DYN$C_TSRV_HQB,-	; The structure subtype
		HQB$B_SUBTYPE(R5)	;  is Host Queue Block

	MOVL	<SCS$B_CON_DAT-SCS$T_DST_PROC>(R2),R0	; Get the address of
					;  the data field
	BEQL	25$			; Can't talk to V4.x class drivers - branch.
	CMPL	R0,#^A/V5.2/		; Does remote class driver support tape
	BEQL	30$			; servers?  Yes - branch.

25$:					; If not, we can't communicate.
	BRW	TMSCP$REJECT_REQUEST	; Kill this request.

30$:
	BISW	#HQB$M_V5CL,-		; Tape servers supported, use SLUNS (V5).
		HQB$W_FLAGS(R5)		;  flag (let's save a flag in $HQBDEF.)
	MOVAL	HQB$L_HRB_FL(R5),-	; Initialize the list head for the
		HQB$L_HRB_FL(R5)	;  Host Request Blocks that are
	MOVAL	HQB$L_HRB_FL(R5),-	;  currently being processed on 
		HQB$L_HRB_BL(R5)	;  behalf of this host
	MOVL	R3,HQB$L_CDT(R5)	; Save away the CDT address
	MOVL	G^SCS$GL_TMSCP,R1	; Get the TSRV address
	MOVL	R1,HQB$L_TSRV(R5)	;  and store it away in the HQB

	CMPW	TSRV$W_NUM_HOST(R1),-	; Max number of hosts being served?
		#MAX_HOSTS		;
	BGEQ	TMSCP$NO_ROOM		;  beyond the end of the table
	MOVW	#60,HQB$W_HTIMO(R5)	; Default host access timeout 

;
; ACCEPT the connection request, and link in the HQB.
;		R3  =  CDT address
;		R4  =  PDT address
;		R5  =  HQB address
;

	ASSUME	HQB$L_FLINK EQ 0

	ACCEPT	MSGADR=TMSCP$MSG_IN,-	; Message input address
		ERRADR=TMSCP$VC_ERR,-	; Virtual circuit error address
		INITCR=#30,-		; Initial credit extended
		MINSCR=#1,-		; Minimum send credit
		CONDAT=TSRV$W_VERSION(R1),- ; CONNECT/ACCEPT data
		AUXSTR=HQB$L_FLINK(R5)	; HQB address
;		PUSHAB	B^30037$
;	PUSHL	 #4
;		PUSHL	#0
;		PUSHL	#0
;		PUSHAB	HQB$L_FLINK(R5)
;		PUSHAB	TSRV$W_VERSION(R1)
;		MOVZBW	#0,-(SP)
;		MOVW	#0,-(SP)
;		MOVW	#1,-(SP)
;		MOVW	#30,-(SP)
;		PUSHAB	TMSCP$VC_ERR
;		PUSHL	#0
;		PUSHAB	TMSCP$MSG_IN
;		MOVL	#12,-(SP)
;		JMP	G^SCS$ACCEPT
;30037$:
	BLBS	R0,40$			; If acceptance was unsuccessful,
	MOVL	HQB$L_CDT(R5),R3	;  restore the CDT address and 
	.IF DEFINED DEBUG$REJECT_TRACKING
	MOVL	G^ SCS$GL_TMSCP, R1	; Get TSRV address.
	MOVL	R0,TSRV$L_REPLC_CNT+16(R1); Save away the error code
	INCL	TSRV$L_REPLC_CNT+20(R1)	;  and keep a count	
	.ENDC	   ;DEBUG$REJECT_TRACKING
	BRB	TMSCP$REJECT_CONNECTION	; Reject this request.

40$:
	MOVL	HQB$L_TSRV(R5),R1	; Restore the TSRV address 
	INSQUE	HQB$L_FLINK(R5),-	; Link this HQB into the list of
		@TSRV$L_HQB_BL(R1)	;  all hosts being served
	INCW	TSRV$W_NUM_HOST(R1)	; Add one to the host count
	MOVL	R3,HQB$L_CDT(R5)	; Keep the CDT address
	RSB

TMSCP$NO_ROOM::
TMSCP$REJECT_REQUEST::
	.IF DEFINED DEBUG$REJECT_TRACKING
	MOVL	G^ SCS$GL_TMSCP, R1	; Get TSRV address.
	MOVL	R0,TSRV$L_REPLC_CNT+24(R1); Save away the bit number
	INCL	TSRV$L_REPLC_CNT+28(R1)	;  and keep a count	
	.ENDC	   ;DEBUG$REJECT_TRACKING

TMSCP$REJECT_CONNECTION::
	MOVL	R5,R0			; Address of the structure to deallocate
	JSB	G^EXE$DEANONPAGED	; Free the memory allocated to the HQB

TMSCP$REJECT::
	REJECT	#1			; Reject the connect application
;		MOVL	#1, R0
;		JSB	@PDT$L_REJECT(R4)
	RSB				;  and return to the caller


	.SBTTL	-	Virtual Circuit Error Routine

;+
; Functional Description:
;
; This routine handles a virtual circuit failure, cleaning up all the 
; requests that were outstanding for the host whose connection failed,
; and then removing the HQB for the failed host from the T/MSCP server
; data structures. 
;
; This routine is set up as the virtual circuit error handling routine
; when the connect is issued. Control is passed here in fork context when
; a link failure is detected, or a disconnect is issued.
;
; Inputs:
;
;	R0  =  Scratch
;	R1  =  Scratch
;	R2  =  Scratch
;	R3  =  CDT address
;	R4  =  PDT address
;	R5  =  Scratch
;
; Outputs:
;	
;	None
;
;-

TMSCP$VC_ERR_ABORT::
	RSB				; Out of line return to caller this
					;  connection is already being terminated

TMSCP$VC_ERR::
	.IF DEFINED DEBUG$PC_HISTORY
	BSBW	TMSCP$PCHIST_R3		; Save this PC if we are keeping track
	.ENDC	   ;DEBUG$PC_HISTORY
	MOVL	CDT$L_AUXSTRUC(R3),R5	; Pick up the HQB address
	BBSS	#HQB$V_DISCON_INIT,-	; If this system is VERY low on NPP
		HQB$B_STATE(R5),-	;  this routine could be called from
		TMSCP$VC_ERR_ABORT	;  within the server (see TMSCP$MSG_IN)
