From:	CRDGW2::CRDGW2::MRGATE::"SMTP::CRVAX.SRI.COM::RELAY-INFO-VAX"  9-MAR-1991 19:52:59.30
To:	ARISIA::EVERHART
CC:	
Subj:	STOPIO -- stop I/O on someone else's terminal (part 1 of 1)

From:	RELAY-INFO-VAX@CRVAX.SRI.COM@SMTP@CRDGW2
To:	Everhart@Arisia@MRGATE

Received:  by crdgw1.ge.com (5.57/GE 1.89)
	 id AA07611; Sat, 9 Mar 91 19:37:45 EST
Received: From UCBVAX.BERKELEY.EDU by CRVAX.SRI.COM with TCP; Fri,  8 MAR 91 15:54:14 PST
Received: by ucbvax.Berkeley.EDU (5.63/1.42)
	id AA27788; Fri, 8 Mar 91 15:35:51 -0800
Received: from USENET by ucbvax.Berkeley.EDU with netnews
	for info-vax@kl.sri.com (info-vax@kl.sri.com)
	(contact usenet@ucbvax.Berkeley.EDU if you have questions)
Date: 8 Mar 91 23:26:19 GMT
From: peirce@gumby.wisc.edu  (Leonard Peirce)
Organization: Western Michigan University Academic Computing Services
Subject: STOPIO -- stop I/O on someone else's terminal (part 1 of 1)
Message-Id: <1991Mar8.232619.3496@gumby.cc.wmich.edu>
Sender: info-vax-request@kl.sri.com
To: info-vax@kl.sri.com


NOTE:  I am posting this for a friend.  I didn't write it and I haven't tried
       it yet (although it looks like fun) so use it at your own risk.  I've
       included a small BUILD.COM to help build it but you're on your own if
       you use it.

       To build it, run the BUILD.COM to compile STOPIO.FOR and create the
       system service.  Copy TOGGLE_DISP.EXE to SYS$SHARE, INSTALL it with
       /OPEN/HEADER/SHARE/PROTECT, and link STOPIO.OBJ using the following
       command:

	$ LINK STOPIO.OBJ,BUILD.OPT/OPT

       There is no documentation included other than the source (which is enough
       for some people :~).


--
Leonard Peirce                  Internet:  peirce@gumby.cc.wmich.edu
Western Michigan University                peirce@gw.wmich.edu
Academic Computing Services     UUCP:      ...!uunet!sharkey!wmichgw!peirce
Kalamazoo, MI  49008            Phone:     (616) 387-5469


==============================================================================

Following is a demo program and a user-written system service that will allow
you to turn off a terminal's output under program control.  If you have SHARE
privilege the demo program will allow you to turn output on and off on another
persons terminal.  The disabling of output is not absolute a number of things
can cause it to be enabled without using the routine to do it.  Some of them are:

    1) Code talking to the terminal that had output disabled deassigned a
       channel.

    2) The user enters CTRL/C or Y or O from the keyboard.

    3) A write is done to the terminal with the IO$M_CANCTRLO modifier. 
       Error output using $PUTMSG will do this.

    4) Some types of broadcast messages will also cause it to be turned back
       on.

The code only works for terminal devices that use TTDRIVER, it does not
work on DECnet terminals.   I have performed a minimal set of testing
on the code so use it at your own risk.  One thing to watch out for is
that I just grabbed two system service numbers at random.  It is
possible that they will conflict with other user written services.

    Forrest

$! ------------------ CUT HERE -----------------------
$ v='f$verify(f$trnlnm("SHARE_VERIFY"))'
$!
$! This archive created by VMS_SHARE Version 7.2-007  22-FEB-1990
$!   On  8-MAR-1991 17:56:30.80   By user PEIRCE 
$!
$! This VMS_SHARE Written by:
$!    Andy Harper, Kings College London UK
$!
$! Acknowledgements to:
$!    James Gray       - Original VMS_SHARE
$!    Michael Bednarek - Original Concept and implementation
$!
$! TO UNPACK THIS SHARE FILE, CONCATENATE ALL PARTS IN ORDER
$! AND EXECUTE AS A COMMAND PROCEDURE  (  @name  )
$!
$! THE FOLLOWING FILE(S) WILL BE CREATED AFTER UNPACKING:
$!       1. BUILD.COM;3
$!       2. BUILD.OPT;1
$!       3. STOPIO.FOR;1
$!       4. TOGGLE_DISP.MAR;2
$!
$set="set"
$set symbol/scope=(nolocal,noglobal)
$f=f$parse("SHARE_TEMP","SYS$SCRATCH:.TMP_"+f$getjpi("","PID"))
$e="write sys$error  ""%UNPACK"", "
$w="write sys$output ""%UNPACK"", "
$ if f$trnlnm("SHARE_LOG") then $ w = "!"
$ ve=f$getsyi("version")
$ if ve-f$extract(0,1,ve) .ges. "4.4" then $ goto START
$ e "-E-OLDVER, Must run at least VMS 4.4"
$ v=f$verify(v)
$ exit 44
$UNPACK: SUBROUTINE ! P1=filename, P2=checksum
$ if f$search(P1) .eqs. "" then $ goto file_absent
$ e "-W-EXISTS, File ''P1' exists. Skipped."
$ delete 'f'*
$ exit
$file_absent:
$ if f$parse(P1) .nes. "" then $ goto dirok
$ dn=f$parse(P1,,,"DIRECTORY")
$ w "-I-CREDIR, Creating directory ''dn'."
$ create/dir 'dn'
$ if $status then $ goto dirok
$ e "-E-CREDIRFAIL, Unable to create ''dn'. File skipped."
$ delete 'f'*
$ exit
$dirok:
$ w "-I-PROCESS, Processing file ''P1'."
$ if .not. f$verify() then $ define/user sys$output nl:
$ EDIT/TPU/NOSEC/NODIS/COM=SYS$INPUT 'f'/OUT='P1'
PROCEDURE Unpacker ON_ERROR ENDON_ERROR;SET(FACILITY_NAME,"UNPACK");SET(
SUCCESS,OFF);SET(INFORMATIONAL,OFF);f:=GET_INFO(COMMAND_LINE,"file_name");b:=
CREATE_BUFFER(f,f);p:=SPAN(" ")@r&LINE_END;POSITION(BEGINNING_OF(b));
LOOP EXITIF SEARCH(p,FORWARD)=0;POSITION(r);ERASE(r);ENDLOOP;POSITION(
BEGINNING_OF(b));g:=0;LOOP EXITIF MARK(NONE)=END_OF(b);x:=ERASE_CHARACTER(1);
IF g=0 THEN IF x="X" THEN MOVE_VERTICAL(1);ENDIF;IF x="V" THEN APPEND_LINE;
MOVE_HORIZONTAL(-CURRENT_OFFSET);MOVE_VERTICAL(1);ENDIF;IF x="+" THEN g:=1;
ERASE_LINE;ENDIF;ELSE IF x="-" THEN IF INDEX(CURRENT_LINE,"+-+-+-+-+-+-+-+")=
1 THEN g:=0;ENDIF;ENDIF;ERASE_LINE;ENDIF;ENDLOOP;t:="0123456789ABCDEF";
POSITION(BEGINNING_OF(b));LOOP r:=SEARCH("`",FORWARD);EXITIF r=0;POSITION(r);
ERASE(r);x1:=INDEX(t,ERASE_CHARACTER(1))-1;x2:=INDEX(t,ERASE_CHARACTER(1))-1;
COPY_TEXT(ASCII(16*x1+x2));ENDLOOP;WRITE_FILE(b,GET_INFO(COMMAND_LINE,
"output_file"));ENDPROCEDURE;Unpacker;QUIT;
$ delete/nolog 'f'*
$ CHECKSUM 'P1'
$ IF CHECKSUM$CHECKSUM .eqs. P2 THEN $ EXIT
$ e "-E-CHKSMFAIL, Checksum of ''P1' failed."
$ ENDSUBROUTINE
$START:
$ create 'f'
X$ FORTRAN STOPIO.FOR
X$ MACRO TOGGLE_DISP.MAR
X$ !
X$ ! Command file to link TOGGLE_DISP
X$ !
X$ LINK/PROTECT/NOSYSSHR /SHARE=TOGGLE_DISP`09-
X`09`09`09/MAP=TOGGLE_DISP`09-
X`09`09`09/FULL `09`09`09-
X`09`09`09SYS$INPUT:/OPTIONS
X!
X!`09Options file for the link of the PTD dispatcher
X!
X`09SYS$SYSTEM:SYS.STB/SELECTIVE
X!
X!
X!
XCLUSTER=TOGGLE$TRANSFER_VECTOR,,,SYS$DISK:`5B`5DTOGGLE_DISP.OBJ
X!
XGSMATCH=LEQUAL,1,1
X!
X$ exit
$ CALL UNPACK BUILD.COM;3 574321440
$ create 'f'
Xsys$share:toggle_disp/share
$ CALL UNPACK BUILD.OPT;1 1192233061
$ create 'f'
XC`09STOPIO -- Simple fortran program to turn off terminal output.
X   `20
X`09character`09dev_name *20
X`09character`09ans`09*30
X
X`09integer`09*4`09name_len
X`09integer`09*4`09ret_stat
X
X`09external`09toggle_off, toggle_on
X`09integer`09*4`09toggle_off, toggle_on
X`09external`09sys$assign
X`09integer`09*4`09sys$assign
X
X
X
X`09write (6, fmt='(''$enter device name: '')')
X`09read (5, fmt='(q,a)',end=100,err=200) name_len, dev_name(:name_len)
X`09ret_stat = sys$assign(dev_name(1:name_len), chan, ,)
X`09if (.not. ret_stat) then
X
X`09`09write(6, fmt='(''/ Assign failed status is: '', z8, /)') ret_stat
X`09`09stop`20
X`09endif
X
X`09do while (.true.)
X
X`09`09write (6, fmt='(''$Enter on to enable output or off to disable: '')')
X`09`09read (5, fmt='(q,a)', end=100, err=200) name_len, ans(:name_len)
X`09`09call STR$UPCASE(ANS, ANS)
X`09`09if (ans(:name_len) .eq. 'OFF') then
X`09`09   ret_stat = toggle_off(%val(chan))
X`09`09   write (6, fmt='(''/ status is:'', z8, /)') ret_stat
X`09`09else if (ans(:name_len) .eq. 'ON') then
X`09`09   ret_stat = toggle_on(%val(chan))
X`09`09   write (6, fmt='(''/ status is:'', z8, /)') ret_stat
X`09`09endif
X`09`09
X`09enddo
X
X100`09stop`09'That''s all folks'
X200`09stop`09'Read error'
X
X`09end
$ CALL UNPACK STOPIO.FOR;1 1094087752
$ create 'f'
X`09.TITLE`09TOGGLE_DISP - Toggle state of CTRLO`20
X`09.IDENT`09/X-1/
X`09.LIBRARY /SYS$LIBRARY:LIB.MLB/
X
X;++
X; Abstract:
X;`09This module provides the KERNEL MODE dispatcher and routines required
X;`09to toggle input on and off for a terminal. The following entry points ar
Ve
X;`09provided:
X;
X;`09TOGGLE_OFF`09-`09Turn off terminal output
X;`09TOGGLE_ON`09-`09Turn on terminal output
X;
X; AUTHOR:
X;
X;`09F. Kenney`09`0914-january-1991
X;
X; Revision History:
X;
X;`09X-n`09XXX000n `09Xxxxxxx X. Xxxxxx`09`09dd-mmm-yyyy
X;`09`09What was done.
X;
X;--
X;
X; Link Command File Example:
X;
X;`09$!
X;`09$!`09Command file to link User System Service example.
X;`09$!
X;`09$ LINK/PROTECT/NOSYSSHR/SHARE=PTD$SERVICES_SHR/MAP=PTD$SERVICES_SHR/FULL
V SYS$INPUT/OPTIONS
X;`09!
X;`09!`09Options file for the link of User System Service example.
X;`09!
X;`09`09SYS$SYSTEM:SYS.STB/SELECTIVE
X;`09!
X;`09!`09Create a separate cluster for the transfer vector.
X;`09!
X;`09CLUSTER=PTD$TRANSFER_VECTOR,,,SYS$DISK:`5B`5DUSSDISP.OBJ
X;`09!
X;`09GSMATCH=LEQUAL,1,1
X;
X;--
X`0C`09.SBTTL`09Definitions
X
X`09$CCBDEF`09`09`09`09`09; CCB definitions
X`09$DEVDEF`09`09`09`09`09; Device characterisitcs definitions
X`09$IODEF`09`09`09`09`09; I/O function codes
X`09$IPLDEF`09`09`09`09`09; IPL definitions
X`09$PCBDEF`09`09`09`09`09; Process control block definitions
X`09$PLVDEF`09`09`09`09`09; Define PLV offsets and valuse
X`09$PRVDEF`09`09`09`09`09; Privilege bit definitions
X`09$PSLDEF`09`09`09`09`09; Define PSL bits
X`09$SSDEF`09`09`09`09`09; System status codes
X`09$TTYUCBDEF`09`09`09`09; Terminal UCB extensions
X`09$TTYVECDEF`09`09`09`09; Terminal port and class vector offsets
X`09$UCBDEF`09`09`09`09`09; UCB definitions
X
X
X;+
X; Local Definitions
X;-
X
X`09TOGGLE_OFF_NUMBER`09= -1001
X`09TOGGLE_ON_NUMBER`09= -1000
X
X`0C
X`09.SBTTL`09TOGGLE_STORAGE - Own storage
X`09.PSECT`09TOGGLE$KERNEL_ARGS`09LONG,NOWRT,EXE,PIC
X
XTOGGLE$KERNEL_NARG:
X`09.BYTE`091`09`09`09`09; Create arguments
X`09.BYTE`091`09`09`09`09; Read arguments
X
XTOGGLE_BUFF:
X`09.BYTE`090`09`09`09`09; Single byte
X
X`0C
X`09.SBTTL`09TOGGLE Transfer vector
X`09.PSECT`09$$$TOGGLE$TRANSFER_VECTOR`09PAGE,NOWRT,EXE,PIC
X
X`09.ALIGN`09QUAD
X`09.TRANSFER`09TOGGLE_OFF`09`09; Define routine public name
X`09.MASK`09`09TOGGLE_OFF_K`09`09; Define entry mask
X`09CHMK`09#TOGGLE_OFF_NUMBER`09`09; Change to Kernel mode and execute
X`09RET
X
X`09.ALIGN`09QUAD
X`09.TRANSFER`09TOGGLE_ON`09`09; Define routine public name
X`09.MASK`09`09TOGGLE_ON_K`09`09; Define entry mask
X`09CHMK`09#TOGGLE_ON_NUMBER`09`09; Change to Kernel mode and execute
X`09RET`09`09`09`09`09; Common exit
X
X`0C
X`09.SBTTL`09Change Mode Dispatcher Vector Block
X;++
X; This vector is used by the image activator to connect the privileged share
Vable
X; image to the VMS change mode dispatcher.  The offsets in the vector are se
Vlf-
X; relative to enable the construction of position independent images.  The s
Vystem
X; version number will be used by the image activator to verify that this sha
Vreable
X; image was linked with the symbol table for the current system.
X;
X;`09`09`09Change Mode Vector Format`09`09
X;
X;`09   +------------------------------------------+
X;`09   !             Vector Type Code             !   PLV$L_TYPE
X;`09   !             (PLV$C_TYP_CMOD)             !
X;`09   +------------------------------------------+
X;`09   !                Reserved                  !
X;`09   !                                          !
X;`09   +------------------------------------------+
X;`09   !      Kernel Mode Dispatcher Offset       !   PLV$L_KERNEL
X;`09   !                                          !
X;`09   +------------------------------------------+
X;`09   !         Exec Mode Entry Offset           !   PLV$L_EXEC
X;`09   !                                          !
X;`09   +------------------------------------------+
X;`09   !`09   User Rundown Service Offset        !   PLV$L_USRUNDWN
X;`09   !                                          !
X;`09   +------------------------------------------+
X;`09   !                Reserved                  !
X;`09   !                                          !
X;`09   +------------------------------------------+
X;`09   !          RMS Dispatcher Offset           !   PLV$L_RMS `20
X;`09   !                                          !
X;`09   +------------------------------------------+
X;`09   !             Address Check                !   PLV$L_CHECK
X;`09   !                                          !
X;`09   +------------------------------------------+
X;
X;
X;
X`09.PSECT`09TOGGLE$SERVICES,PAGE,VEC,PIC,NOWRT,EXE
X
X`09.LONG`09PLV$C_TYP_CMOD`09`09`09; Set type of vector to change mode dispat
Vcher
X`09.LONG`090`09`09`09`09; Reserved
X`09.LONG`09TOGGLE$KERNEL_DISPATCH-.`09; Offset to kernel mode dispatcher
X`09.LONG`090`09`09`09`09; Offset to executive mode dispatcher
X`09.LONG`090`09`09`09`09; Offset to user rundown service
X`09.LONG`090`09`09`09`09; Reserved.
X`09.LONG`090`09`09`09`09; No RMS dispatcher
X`09.LONG`090`09`09`09`09; Address check - PIC image
X
X`0C
X`09.SBTTL`09Kernel Mode Dispatcher
X;++
X; Input Parameters:
X;
X;`09(SP) - Return address if bad change mode value
X;
X;`09 R0  - Change mode argument value.
X;                                                `20
X;`09 R4  - Current PCB Address. (Therefore R4 must be specified in all
X;`09`09 register save masks for kernel routines.)
X;
X;`09 AP  - Argument pointer existing when the change
X;`09       mode instruction was executed.
X;
X;`09 FP  - Address of minimal call frame to exit
X;`09       the change mode dispatcher and return to
X;`09       the original mode.
X;--
X`09.PSECT`09TOGGLE$KERNEL_DISP`09`09LONG,NOWRT,EXE,PIC
X
XKACCVIO:`09`09`09`09`09; Kernel access violation
X`09MOVZWL`09#SS$_ACCVIO,R0`09`09`09; Set access violation status code
X`09RET`09`09`09`09`09;  and return
XKINSFARG:`09`09`09`09`09; Kernel insufficient arguments.
X`09MOVZWL`09#SS$_INSFARG,R0`09`09`09; Set status code and
X`09RET`09`09`09`09`09;  return
XKNOTME:`09RSB`09`09`09`09`09; RSB to forward request
X
XTOGGLE$KERNEL_DISPATCH::`09`09`09; Entry to dispatcher
X`09MOVAB`09W`5E-TOGGLE_OFF_NUMBER(R0),R1`09; Normalize dispatch code value
X`09BLSS`09KNOTME`09`09`09`09; Branch if code value too low
X`09CMPW`09R1,#2`09`09`09`09; Check high limit
X`09BGEQU`09KNOTME`09`09`09`09; Branch if out of range
X;+
X; The dispatch code has now been verified as being handled by this dispatche
Vr,
X; now the argument list will be probed and the required number of arguments
X; verified.
X;-
X`09MOVZBL`09W`5ETOGGLE$KERNEL_NARG`5BR1`5D,R1`09; Get required argument coun
Vt
X`09MOVAL`09@#4`5BR1`5D,R1`09`09`09; Compute byte count including arg count
X`09IFNORD`09R1,(AP),KACCVIO`09`09`09; Branch if arglist not readable
X`09CMPB`09(AP),`09`09`09-`09; Check for required number
X`09`09W`5E<TOGGLE$KERNEL_NARG-TOGGLE_OFF_NUMBER>`5BR0`5D`20
X`09BLSSU`09KINSFARG`09`09`09;  of arguments
X`09MOVL`09FP,SP`09`09`09`09; Reset stack for service routine
X`09CASEW`09R0,-`09`09`09`09; Case on change mode
X`09`09-`09`09`09`09; argument value
X`09`09#TOGGLE_OFF_NUMBER,- `09`09; Base value
X`09`09#<1>`09`09`09`09; Limit value (number of entries)
XKCASE_BASE:
X`09.WORD`09<2+TOGGLE_OFF_K-KCASE_BASE>`09; Offset to toggle output off
X`09.WORD`09<2+TOGGLE_ON-KCASE_BASE>`09; Offset to toggle output on
X
X`09BUG_CHECK IVSSRVRQST,FATAL`09`09; Since the change mode code is validated
X`09`09`09`09`09`09; above, we should never get here
X
X`0C
X
X`09.SBTTL`09TOGGLE_OFF - Turn off terminal output
X;+
X;TOGGLE_OFF - Turn off terminal output
X;
X; Functional Description:
X;
X;`09To set the terminal assigned to the supplied channels output off.
X;
X; Synchronization:
X;
X;`09This routine must be called at IPL 0.  It acquires the devices fork and
X; device lock while processing.  If device still ok then it`20
X;
X; INPUTS:
X;
X;`09R0 `09- `09Change mode code
X;`09R4 `09- `09PCB address
X;`09AP `09- `09Argument list
X;`09 4(AP)`09-`09Channel
X;
X; OUTPUTS:
X;
X;`09R0`09-`09SS$_NORMAL- Successful completion
X;`09`09`09SS$_DEVOFFLINE - Device is offline and request cannot proceed
X;`09`09`09SS$_IVCHAN - Illegal channel
X;`09`09`09SS$_IVDEVNAME - Illegal device
X;`09`09`09SS$_NOPRIV - Insufficient privilege to perform request
X;
X; Notes:
X;`09None
X;
X;-
XTOGGLE_OFF::
X`09.ENTRY`09TOGGLE_OFF_K,`09`09-`09; Turn output off
X`09`09`5EM<R2,R3,R4,R5,R6,R7,R8,R9,R10,R11>
X`09MOVZWL`094(AP),R0`09`09`09; Get channel number
X`09BSBW`09TOGGLE$VALIDATE_CHAN`09`09; Validate channel number
X`09MOVL`09R5,R1`09`09`09`09; Copy UCB address
X
X;+
X; Use poor mans lockdown to fault this page in while at elevated IPL.
X; Then finish validation and toggle output off......
X;-
X`09SAVIPL`09`09`09`09`09; Save the IPL
X`09SETIPL`09SAVE_IPL,ENVIRON=UNIPROCESSOR`09; Lock down page
X`09FORKLOCK LOCK=UCB$B_FLCK(R1),-`09`09; Take fork lock to prevent TTDRIVER
X`09`09SAVIPL=-(SP),`09-`09`09; from unlinking device for us
X`09`09PRESERVE=YES`09`09`09;
X`09MOVL`09#SS$_IVDEVNAM,R0`09`09; Assume failure
X`09MOVL`09UCB$L_TL_PHYUCB(R1),R5`09`09; Get physical UCB
X`09BEQL`0940$`09`09`09`09; EQL device is disconnected just exit
X`09CMPW`09UCB$W_SIZE(R5),`09-`09`09; See if UCB is large enough to be a`20
X`09`09#UCB$K_TT_LENGTH`09`09; physical UCB
X`09BLSS`0940$`09`09`09`09; LSS not a PHYSICAL UCB exit
X30$:`09
X`09DEVICELOCK`09`09-`09`09; Take device lock TTDRIVER assumes it
X`09`09LOCKADDR=UCB$L_DLCK(R5), - `09; is held
X`09`09LOCKIPL=UCB$B_DIPL(R5), -  `09;
X`09`09SAVIPL=-(SP),`09-`09`09;
X`09`09PRESERVE=NO`09`09`09;
X`09BISL`09#TTY$M_ST_CTRLO, -`09`09; Set control o bit
X`09`09UCB$L_TT_STATE2(R5)`09`09;
X`09DEVICEUNLOCK`09`09-`09`09; Release the device lock
X`09`09LOCKADDR=UCB$L_DLCK(R5), - `09;
X`09`09NEWIPL=(SP)+,`09-`09`09;
X`09`09CONDITION=RESTORE,`09-`09;
X`09`09PRESERVE=NO`09`09`09;
X`09MOVZWL`09#SS$_NORMAL,R0`09`09`09; Store success as exit reason
X40$:
X`09FORKUNLOCK `09`09-`09`09; Release fork lock so TTDRIVER`20
X`09`09LOCK=UCB$B_FLCK(R1), -  `09; can run the disconnect fork
X`09`09NEWIPL=(SP)+,`09-`09`09;
X`09`09CONDITION=RESTORE, -`09`09;
X`09`09PRESERVE=YES`09`09`09;
X`09SETIPL`09(SP)+,ENVIRON=UNIPROCESSOR`09; Get back to calling IPL
X`09RET`09`09`09`09`09; Exit
X
XSAVE_IPL:`09
X`09.long`09IPL$_SYNCH `09`09`09; Rasie to SYNCH to block scheduling
X
X`0C
X
X`09.SBTTL`09TOGGLE_OFF - Turn off terminal output
X;+
X;TOGGLE_OFF - Turn off terminal output
X;
X; Functional Description:
X;
X;`09To set the terminal assigned to the supplied channels output off.
X;
X; Synchronization:
X;
X;`09This routine must be called at IPL 0.  It acquires the devices fork and
X; device lock while processing.  If device still ok then it`20
X;
X; INPUTS:
X;
X;`09R0 `09- `09Change mode code
X;`09R4 `09- `09PCB address
X;`09AP `09- `09Argument list
X;`09 4(AP)`09-`09Channel
X;
X; OUTPUTS:
X;
X;`09R0`09-`09SS$_NORMAL- Successful completion
X;`09`09`09SS$_DEVOFFLINE - Device is offline and request cannot proceed
X;`09`09`09SS$_IVCHAN - Illegal channel
X;`09`09`09SS$_IVDEVNAME - Illegal device
X;`09`09`09SS$_NOPRIV - Insufficient privilege to perform request
X;
X; Notes:
X;`09None
X;
X;-
XTOGGLE_ON::
X`09.ENTRY`09TOGGLE_ON_K,`09`09-`09; Turn output off
X`09`09`5EM<R2,R3,R4,R5,R6,R7,R8,R9,R10,R11>
X`09MOVZWL`094(AP),R0`09`09`09; Get channel number
X`09BSBW`09TOGGLE$VALIDATE_CHAN`09`09; Validate channel number
X`09MOVL`09R5,R6`09`09`09`09; Copy UCB address
X
X;+
X; Use poor mans lockdown to fault this page in while at elevated IPL.
X; Then finish validation and toggle output off......
X;-
X`09SAVIPL`09`09`09`09`09; Save the IPL
X`09SETIPL`09SAVE_IPL_1,ENVIRON=UNIPROCESSOR`09; Lock down page
X`09FORKLOCK LOCK=UCB$B_FLCK(R6),-`09`09; Take fork lock to prevent TTDRIVER
X`09`09SAVIPL=-(SP),`09-`09`09; from unlinking device for us
X`09`09PRESERVE=YES`09`09`09;
X`09MOVL`09#SS$_IVDEVNAM,R0`09`09; Assume failure
X`09MOVL`09UCB$L_TL_PHYUCB(R6),R5`09`09; Get physical UCB
X`09BEQL`0940$`09`09`09`09; EQL device is disconnected just exit
X`09CMPW`09UCB$W_SIZE(R5),`09-`09`09; See if UCB is large enough to be a`20
X`09`09#UCB$K_TT_LENGTH`09`09; physical UCB
X`09BLSS`0940$`09`09`09`09; LSS not a PHYSICAL UCB exit
X30$:`09
X`09DEVICELOCK`09`09-`09`09; Take device lock TTDRIVER assumes it
X`09`09LOCKADDR=UCB$L_DLCK(R5), - `09; is held
X`09`09LOCKIPL=UCB$B_DIPL(R5), -  `09;
X`09`09SAVIPL=-(SP),`09-`09`09;
X`09`09PRESERVE=NO`09`09`09;
X`09BICL`09#TTY$M_ST_CTRLO, -`09`09; Set control o bit
X`09`09UCB$L_TT_STATE2(R5)`09`09;
X`09BBS`09#UCB$V_INT,UCB$W_STS(R5),39$`09; If output expected do not ask for
V more
X`09JSB`09@UCB$L_TT_GETNXT(R5)`09`09; See if output`20
X`09MOVL`09UCB$L_TT_PORT(R5),R0`09`09; Get port vector address
X`09TSTB`09UCB$B_TT_OUTYPE(R5)`09`09; See if characters to output
X`09BEQL`0939$`09`09`09`09; EQL no output exit
X`09JSB`09@PORT_STARTIO(R0)`09`09; Call port to ttart up I/O`20
X39$:
X`09DEVICEUNLOCK`09`09-`09`09; Release the device lock
X`09`09LOCKADDR=UCB$L_DLCK(R5), - `09;
X`09`09NEWIPL=(SP)+,`09-`09`09;
X`09`09CONDITION=RESTORE,`09-`09;
X`09`09PRESERVE=NO`09`09`09;
X`09MOVZWL`09#SS$_NORMAL,R0`09`09`09; Store success as exit reason
X40$:
X`09FORKUNLOCK `09`09-`09`09; Release fork lock so TTDRIVER`20
X`09`09LOCK=UCB$B_FLCK(R6), -  `09; can run the disconnect fork
X`09`09NEWIPL=(SP)+,`09-`09`09;
X`09`09CONDITION=RESTORE, -`09`09;
X`09`09PRESERVE=YES`09`09`09;
X`09SETIPL`09(SP)+,ENVIRON=UNIPROCESSOR`09; Get back to calling IPL
X`09RET`09`09`09`09`09; Exit
X
XSAVE_IPL_1:`09
X`09.long`09IPL$_SYNCH `09`09`09; Rasie to SYNCH to block scheduling
X
X`0C
X`09.SBTTL`09TOGGLE$VALIDATE_CHAN - Validate channel is legal
X;+
X; TOGGLE$VALIDATE_CHAN - Validate channel is legal
X;
X; Functional Description:
X;
X;`09This routine is called by the system services to verify that the I/O
X; channel supplied for the request is valid.  It performs the same checks th
Vat
X; EXE$QIO does when validating a channel.  Additionally it adds checks to ma
Vke
X; sure it is talking to the correct device.
X;
X; Synchronization:
X;
X;`09This routine is entered at IPL 0, and requires no special
X; synchronization.
X;
X; INPUTS:
X;
X;`09R0`09-`09Channel number
X;
X; OUTPUTS:
X;
X;`09R0`09-`09SS$_NORMAL - Successful completion
X;`09`09`09SS$_DEVOFFLINE - Device is offline and request cannot proceed
X;`09`09`09SS$_IVCHAN - Illegal channel
X;`09`09`09SS$_NOPRIV - Insufficient privilege to perform request
X;`09R3`09-`09PSL
X;`09R5`09-`09UCB
X;`09R6`09-`09CCB
X;`09R11`09-`09Current mode
X;
X; NOTES:
X;
X;`09If an error occurs this routine returns directly to the calling
X; application.
X;
X;-
XTOGGLE$VALIDATE_CHAN:
X`09BICL`09#<`5EXFFFF0000!<CCB$C_LENGTH-1>>,- ; Fetch channel number and
X`09`09R0`09`09`09`09; extraneous bits
X`09BEQL`09INVCHAN`09`09`09`09; EQL bad channel
X`09CMPW`09R0,G`5ECTL$GW_CHINDX`09`09; Legal channel index
X`09BGTRU`09INVCHAN`09`09`09`09; GTRU bad channel
X`09MNEGL`09R0,R0`09`09`09`09; Convert to channel index
X`09MOVL`09G`5ECTL$GL_CCBBASE,R6`09`09; Get CCB base address
X`09MOVAB`09(R6)`5BR0`5D,R6`09`09`09; Get CCB address
X`09MOVPSL`09R3`09`09`09`09; Get current PSL
X`09MOVZBL`09CCB$B_AMOD(R6),R5`09`09; Get channel access mode
X`09BEQL`09INVCHAN`09`09`09`09; EQL Illegal channel
X`09EXTZV`09#PSL$V_PRVMOD,#PSL$S_PRVMOD,-`09; Get previous mode from PSL
X`09`09R3,R11`09`09`09`09;
X`09CMPB`09R11,R5`09`09`09`09; Caller have priv to use this channel
X`09BGEQ`09PRIVERR`09`09`09`09; GEQ no priv error exit
X`09MOVL`09CCB$L_UCB(R6),R5`09`09; Get UCB address
X`09BBC`09#DEV$V_TRM,`09-`09`09; See if device is a terminal
X`09`09UCB$L_DEVCHAR(R5),IVDEVNAM`09;
X`09BBS`09#DEV$V_RTT,`09-`09`09; Is this a remote terminal
X`09`09UCB$L_DEVCHAR2(R5),IVDEVNAM`09;
X`09CMPW`09UCB$W_SIZE(R5),`09`09-`09; See if UCB is large enough to have`20
X`09`09#UCB$K_TL_LENGTH`09`09; a physical UCB pointer if not exit
X`09BLSS`09IVDEVNAM`09`09`09; LSS cannot be a terminal using TTDRIVER
X
X`09BBC`09#UCB$V_ONLINE,UCB$W_STS(R5), -`09; Is device online if not error
X`09`09OFFLINE`09`09`09`09; exit
X`09RSB
X
XINVCHAN:
X`09MOVZWL`09#SS$_IVCHAN,R0`09`09`09; Illegal channel
X`09RET
XOFFLINE:
X`09MOVL`09#SS$_DEVOFFLINE,R0`09`09; Device offline
X`09RET
XPRIVERR:
X`09MOVL`09#SS$_NOPRIV,R0`09`09`09; No privilege
X`09RET
XIVDEVNAM:
X`09MOVZWL`09#SS$_IVDEVNAM,R0`09`09; Illegal channel
X`09RET
X
X`09.END
X
$ CALL UNPACK TOGGLE_DISP.MAR;2 477474165
$ v=f$verify(v)
$ EXIT

