	.title tt_ucb
;;; idllb - determine idle (and other) information for terminals (privileged)
;
; revision history
; 8-Mar-1985	Get RTT stuff to work after Craig Leres (LBL).
;30-Jan-1985	Rename to tt_ucb since it now gets a lot more than idle time.
;29-Jan-1985	Add arguments for Physical terminal name and number
;24-JAN-1985	VMS V4.0 changes.   Craig Watkins (Penn State) and
;			Mark London (MIT):  Virtual terminal stuff;
;			and IPID --> EPID 
;
; 03a 27Oct83 cal .Added a new argument to hold the ucb$w_rtt_link value
;                  for remote terminals. Modified so that the idle time
;                  and ucb link value buffers are optional arguments.
; 02b 07Oct83 cal .Corrected addressing modes in the probew instructions.
; 02a 08Nov82 cal .Written; this routine has been coded again from
;		   scratch. Revision history after this point has
;		   no reason for existing and can be safely ingnored.
; 01b 16jul82 vp  .Added .title
; 01a 12Mar82 cal .Taken from a Decus tape
;
; synopsis
;
;   status = tt_ucb ( [buf_tim], buf_pid, [buf_dev], [buf_unit], 
;			[buf_ucb], buf_siz )
;
;	buf_tim - buffer of longwords to receive idle times (optional)
;	buf_pid - buffer of longwords to receive pids
;	buf_dev - buffer for physical device controller generic names (4 words) (opt.)
;	buf_unit - buffer for physical device unit numbers (opt.)
;	buf_ucb - buffer of longwords to receive ucb decnet link addresses (optional)
;	buf_siz - number of units to get information about
;
; The buffers are longwords (integer*4) arrays of size buf_siz. The idle
; times, process ids, and remote link values are stored in the two buffers
; in consecutive elements with a zero longword in the pid buffer to mark
; end of the list. The idle time and link value buffer arguments are optional.
; The information is ordered by terminal name.
;
; Note: Use of this routine requires the privilege CMKRNL.
;
	.library  /sys$library:lib.mlb/
	$ucbdef
	$ddbdef
	$ipldef
	$ttyucbdef
;
;	Useful constants
;
acp_remote = 3					; acp class number for remote

ap_tim = 4					; offset to due time buffer
ap_pid = 8					; offset to pid buffer
ap_dev = 12
ap_unit = 16
ap_ucb = 20					; offset to ucb buffer
ap_siz = 24					; offset to buffer size
num_args = 6					; number of arguments

fudge = 2					; add this to exe$gl_abstim
sizeof_long = 4					; size of a longword
max_units = 128					; max units to report about
sizeof_buf = max_units * sizeof_long		; size of return buffers

	.entry	tt_ucb,^M<r2,r3,r4,r5,r6>

	cmpl	#num_args,(ap)			; need this many arguments
	beql	10$				; branch if correct
	movl	#ss$_badparam,r0		; setup error status
	ret					; go give him the bad news

10$:	cmpl	@ap_siz(ap),#max_units		; want at least this many units
	bgequ	20$				; we'll allow his to be bigger
	movl	#ss$_ivbuflen,r0		; setup error status
	ret					; return to caller

20$:	tstl	ap_tim(ap)			; test for time buffer argument
	beql	30$				; branch if none
	probew  #0,#sizeof_buf,@ap_tim(ap)	; see if we can write there...
	beql	60$				; bail out if not

30$:	probew  #0,#sizeof_buf,@ap_pid(ap)	; see if we can write there...
	beql	40$				; bail out if not

40$:	tstl	ap_dev(ap)			; test for device buffer argument
	beql	50$				; branch if none
	probew  #0,#sizeof_buf,@ap_dev(ap)	; see if we can write there...
	beql	60$				; bail out if not

50$:	tstl	ap_unit(ap)			; test for unit buffer argument
	beql	55$				; branch if none
	probew  #0,#sizeof_buf,@ap_unit(ap)	; see if we can write there...
	beql	60$				; bail out if not

55$:	tstl	ap_ucb(ap)			; test for ucb buffer argument
	beql	70$				; branch if none
	probew  #0,#sizeof_buf,@ap_ucb(ap)	; see if we can write here too
	bneq	70$				; branch if we can
60$:	movl	#ss$_accvio,r0			; setup error status
65$:	ret					; return to caller

70$:	$lkwset_s inadr=lock			; lock routine into working set
	cmpl	#ss$_wasclr,r0			; did we lock the pages?
	beql	80$				; branch if so
	cmpl	#ss$_wasset,r0			; were they already locked?
	bneq	65$				; if not, then error

80$:	$cmkrnl_s routin=ttucbkrn		; do the privileged stuff
	movl	r0,r6				; save status for later

	$ulwset_s inadr=lock			; unlock kernel routine
	cmpl	#ss$_wasclr,r0			; did we unlock the pages?
	beql	90$				; branch if so
	cmpl	#ss$_wasset,r0			; were they already unlocked?
	bneq	err				; if not, then error

90$:	tstl	ap_tim(ap)			; test for time buffer argument
	beql	100$				; branch if none
	movc3	al_cnt,bl_tim,@ap_tim(ap)	; store idle time info

100$:	movc3	al_cnt,bl_pid,@ap_pid(ap)	; store pid info

	tstl	ap_dev(ap)			; test for devicebuffer argument
	beql	110$				; branch if none
	movl	al_cnt,r0			; get size of buffer
	ashl	#2,r0,r0			; mutiply by 4 for this one
	movc3	r0,bl_dev,@ap_dev(ap)		; store device name info

110$:	tstl	ap_unit(ap)			; test for unit no.buffer argument
	beql	120$				; branch if none
	movc3	al_cnt,bl_unit,@ap_unit(ap)	; store unit no. info

120$:	tstl	ap_ucb(ap)			; test for ucb buffer argument
	beql	130$				; branch if none
	movc3	al_cnt,bl_ucb,@ap_ucb(ap)	; store ucb info

130$:	movl	r6,r0				; restore status

err:	ret					; that's all folks!

;
;	Descriptor used to lock kernel routine into our working set
;
lock:	.address	ttucbkrn
	.address	ttucbkrnend

;
; Entry point for the privileged search and slurp. The following
; registers are used:
;
;	r0 - return status
;	r1 - scratch
;	r3 - system absolute time
;	r4 - reserved for PCB pointer by SCH$... routines - set by CHKRNL
;	r5 - address of due time storage area
;	r6 - address of pid storage area 
;	r7 - address of ucb decnet link storage area 
;	r8 - address of device name storage area
;	r9 - address of unit no. storage area.
;	r10 - address of current unit control block
;	r11 - address of current device data block
;
	.entry	ttucbkrn,^M<r3,r4,r5,r6,r7,r8,r9,r10,r11>

	movl	#bl_tim,r5			; address of due time buffer
	movl	#bl_pid,r6			; address of pid buffer
	movl	#bl_ucb,r7			; address of ucb link buffer
	movl	#bl_dev,r8			; address of device name buffer
	movl	#bl_unit,r9			; address of unit no. buffer

	jsb	sch$iolockr			; lock io data base for read
	dsbint	#ipl$_synch			; raise ipl while mucking...

	addl3	#fudge,exe$gl_abstim,r3		;; add fudge to abstim

	movl	ioc$gl_devlist,r11		;; ddb list in r11
	brb	jump1				;; jump into middle loop

loop1:	movl	ddb$l_link(r11),r11		;; look at next ddb
jump1:	bneq	1$				;; already did last controller?
	jmp	finish				;; no more - all done

1$:	movl	ddb$l_ucb(r11),r10		;; fetch first ucb of this ddb
	brb	jump2				;; jump into innermost loop
loop2:	movl	ucb$l_link(r10),r10		;; look at next ucb
jump2:	beql	loop1				;; no more units left

	cmpb	#dc$_term,ucb$b_devclass(r10)	;; is this unit a terminal?
	bneq	loop1				;; next ddb if not

	bbs	#dev$V_rtt,ucb$l_devchar2(r10),3$ ;; if remote skip down

	tstl	ucb$l_tl_phyucb(r10)		;; is this a physical terminal?
	beql	loop1				;; next ddb if not

	bbs	#dev$v_red,ucb$l_devchar2(r10),5$
						;; is this redirected terminal?
						;; if so, skip process test
3$:	tstl	ucb$l_pid(r10)			;; check for a process
	beql	loop2				;; skip if no process

5$:	movl	ucb$l_tt_logucb(r10),r1		;; get logical UCB address
	bbc	#dev$V_rtt,ucb$l_devchar2(r10),6$ ;; if not remote skip
	movl	r10,r1				;; fake logical ucb for rtt
6$:	subl3	ucb$l_duetim(r10),r3,r2		;; idle time in r2
	bbc	#ucb$v_tim,ucb$w_sts(r10),10$	;; see if unit is timed out
	brb	20$				;; timed out so go zero time
10$:	bbs	#ucb$v_bsy,ucb$w_sts(r1),30$	;; see if unit is busy
20$:	clrl	r2				;; if true, zero idle time

30$:	movl	r2,(r5)+			;; stow idle time
	movl	ucb$l_pid(r1),r0		;; get owner pid
	jsb	g^exe$ipid_to_epid		;; convert to extended PID
	movl	r0,(r6)+			;; stow owner pid

	clrl	r2				;; assume not remote
	bbc	#dev$V_rtt,ucb$l_devchar2(r10),40$ ;; if not remote skip down

	movzwl	ucb$w_rtt_link(r10),r2		;; link value in r2
40$:	movl	r2,(r7)+			;; stow link address

	movl	ddb$t_name(r11),(r8)+		;; stow device name
	movl	ddb$t_name+4(r11),(r8)+		;;  ''    ''    ''
	movl	ddb$t_name+8(r11),(r8)+		;;  ''    ''    ''
	movl	ddb$t_name+12(r11),(r8)+	;;  ''    ''    ''
	movzwl	ucb$w_unit(r10),(r9)+		;; stow physical unit number

	cmpl	#bl_pid+sizeof_buf,r6		;; have we run out of buffer?
	blssu	50$
	jmp	loop2				;; if not negative we're ok
50$:	subl	#sizeof_long,r6			;; backup just a tad
	movl	#ss$_bufferovf,r0		;; setup return status
	brb	krnerr				;; and exit

finish:	movl	#ss$_normal,r0			;; setup return status

krnerr:	movl	r0,r3				;; store status
	jsb	sch$iounlock			;; unlock io data base
	enbint					;; restore ipl

	clrl	(r6)+				; flag end of list
	subl3	#bl_pid,r6,al_cnt		; calculate size of transfer
	movl	r3,r0				; restore status
	ret					; back to user mode

bl_tim:	.blkl	sizeof_buf			; storage for due time
bl_pid:	.blkl	sizeof_buf			; storage for pid
bl_dev:	.blkl	sizeof_buf*4			; storage for device names
bl_unit: .blkl	sizeof_buf			; storage for unit numbers
bl_ucb:	.blkl	sizeof_buf			; storage for magic ucb address

al_cnt:	.long	0				; size of transfer

ttucbkrnend:					; end of locked memory

	.end
