.TITLE PTYDRIVER - Port Driver for PTY's .IDENT /5.00/ ;++ ; Facility: ; VAX/VMS Pseudo-terminal driver ; ; Abstract: ; This is a port driver to provide a pseudo-terminal capability for VAX ; VMS. The pseudo-terminal is a pair of pseudo devices with consecutive ; unit numbers. These devices behave like mailboxes in that ; they are created dynamically when a channel is assigned. Assigning ; a channel on the device PTY0: will automatically create two new ; devices, PTYn and PTY(n+1). A PTY differs from a mailbox in that it ; is a terminal class device, and responds correctly to all terminal ; driver QIO functions. ; ; The PTY(n+1) device is called the terminal device. It is ; usually assigned as the primary input/output device (the "terminal") ; for a process created to perform some task. Alternately, if no process ; is assigned to it, unsolicited data sent from the control device will ; cause job control to log a process into the device (like any standard ; terminal). ; ; The PTY(n) device is called the control device. It is usually assigned ; to a program which transmits and receives messages from the terminal ; device process. ; ; The two devices communicate at STARTIO level through ; the code of this driver. Both devices are terminal class devices and ; are serviced by the terminal class driver. The characteristics of ; the control device are fixed and cannot be changed. Any changes made ; to the control device characteristics will be propagated to the ; terminal device. ; ; The process which creates a PTY device must have sufficient BYTLM ; quota for both device's UCBs. ; ; Author: ; Originally: Gary L. Grebus, 27-Nov-1983 00:28:15 ; ; Rewrite: Kevin Carosso at Hughes Aircraft, Space and Comm., CTC. ; May 24, 1985. ; ; Rewritten extensively by Kevin Carosso for VMS V4 and then enhanced ; further to support all the hangup functions correctly and also to ; allow job controller to create interactive processes if desired. ; ; Revision History: ; ; 5.00 -- Kevin Carosso, 6-Jan-1985 ; Converted to VMS V4. Did the following: ; - Changed references to owner and prot to use new ORB structure. ; - Changed PORT/CLASS interface table to use $VEC macros. ; - Changed SET_UNIT port interface to SET_LINE for the table. ; - Changed PORT/CLASS interface to use unit/controller init macros. ; - Moved PTY specific UCB fields to end of the TTY UCB, since it ; used to use (unused) TT_MAP which is no longer in the UCB. ; - Completely rewrote the mechanism whereby master and slave UCBs ; are cloned. I now use the CLONEDUCB entry point in the DDT to ; initialize the master UCB. The CLONE initialization code then ; calls the proper IOC$ routines to clone yet another UCB for the ; slave. This gets rid of the odd restrictions on UCB size ; brought about by the old method of cloning a huge UCB and ; cutting it in half for the two PTY's. ; - I have a better way of handling DISCONNECT so that the PTY ; works more like a terminal with the HANGUP characteristic. ; When the terminal device needs to be hung up, it also forces ; a hangup on the control device. Thus, a program managing the ; control device can simply wait for a "DATA-SET HANGUP" error ; from it's QIO or it can set up an associated mailbox on the ; control device and wait for a hangup message. ; ; This causes trouble with PHOTO because it tries to SPAWN a ; process to use the device and SPAWN deassigns the device at ; least once before creating the process. The device thus gets ; hung up prematurely. In order to use SPAWN, PHOTO needs to ; first set NOHANGUP on the control device (which propagates ; to the terminal device) and then do it's SPAWN. ; ; - I fixed this up so it works even more like a terminal. Once ; the devices are created, data (like a return) sent at the ; control device will start a job logging in on the terminal ; device. If you don't want LOGINOUT to create a job for you, ; the controlling program can create the process and assign ; input and output to the terminal device BEFORE sending any data ; at the device. ; ;-- .PAGE .SBTTL Declarations .LIBRARY /SYS$LIBRARY:LIB/ ; ; External Constants ; $CRBDEF ; Define CRB $DCDEF ; Define adapter types $DDBDEF ; Define DDB $DDTDEF ; DDT $DEVDEF ; Define device types $DYNDEF ; Define DYN symbols $IDBDEF ; Define IDB $TTYDEF ; Define terminal driver symbols $TTDEF ; Define terminal types $TT2DEF ; Define extended characteristics $UCBDEF ; Define UCB $VECDEF ; Define CRB vector fields $TTYMACS ; Define terminal driver macros $TTYDEFS ; Define terminal driver symbols $ORBDEF ; Object rights block ; ; Add some fields to the end of the UCB. ; $DEFINI LOCAL,GLOBAL .=UCB$C_TT_LENGTH ; Add to end of normal TT UCB $DEF UCB$L_PTY_CPID .BLKL 1 ; Place to save creator PID $DEF UCB$L_PARTNER_UCB .BLKL 1 ; Address of partner's UCB. $DEF UCB$L_PTY_CLASS_DDT .BLKL 1 ; Class driver's DDT $DEF UCB$C_PTY_LENGTH .BLKW 1 ; New length of UCB $DEFEND UCB ; ; Control PTY characteristics ; PTY_DEFCHAR = TT$M_HOSTSYNC ! TT$M_LOWER ! TT$M_MECHFORM ! TT$M_MECHTAB - ! TT$M_NOECHO ! TT$M_SCOPE ! TT$M_TTSYNC ! TT$M_NOBRDCST - ! TT$M_EIGHTBIT ! TT$M_REMOTE PTY_DEFCHAR2 = TT2$M_HANGUP ! TT2$M_ALTYPEAHD ! TT2$M_PASTHRU PTY_DEFWIDTH = 80 PTY_DEFPAGE = 0 PTY_DEFTYPE = TT$_UNKNOWN ; ; Local macros ; .MACRO IF_TERMINAL LABEL BLBC UCB$W_UNIT(R5),LABEL ;; Branch if even (terminal) unit .ENDM IF_TERMINAL .MACRO IF_CONTROL LABEL BLBS UCB$W_UNIT(R5),LABEL ;; Branch if odd (control) unit .ENDM IF_CONTROL ; ; Local storage ; .PSECT $$$105_PROLOGUE ; ; Driver prologue table ; PTY$DPT:: ; Driver start DPTAB - END=PTY$END,- ; End of driver UCBSIZE=UCB$C_PTY_LENGTH,- ; Size of UCB (double size) FLAGS=DPT$M_NOUNLOAD,- ; No unload allowed ADAPTER=NULL,- ; No adapter NAME=PTYDRIVER,- ; Name of driver VECTOR=PORT_VECTOR ; Port driver vector table DPT_STORE INIT ; DPT initialization DPT_STORE UCB,UCB$B_FIPL,B,8 ; Fork IPL DPT_STORE UCB,UCB$L_DEVCHAR,L,<- DEV$M_REC!- ; Leave AVL clear, will be DEV$M_IDV!- ; set in the UNIT_INIT if DEV$M_ODV!- ; no errors creating new DDT. DEV$M_TRM!- DEV$M_CCL> DPT_STORE UCB, UCB$L_DEVCHAR2,L,<- DEV$M_NNM> ; Prefix with node name ; ; The site-defined default characteristics are specified for PTY devices so ; that the terminal device will resemble a default terminal. The values which ; could affect PTYDRIVER operation are reset in the unit init routine. ; DPT_STORE UCB,UCB$B_DEVCLASS,B,DC$_TERM ; Class DPT_STORE UCB,UCB$B_TT_DETYPE,B,TT$_UNKNOWN ; Type DPT_STORE UCB,UCB$W_TT_DESIZE,@W,TTY$GW_DEFBUF ; Buffer size DPT_STORE UCB,UCB$L_TT_DECHAR,@L,TTY$GL_DEFCHAR ; Default character DPT_STORE UCB,UCB$L_TT_DECHA1,@L,TTY$GL_DEFCHAR2 ; Ditto DPT_STORE UCB,UCB$W_TT_DESPEE,@B,TTY$GB_DEFSPEED ; Default speed DPT_STORE UCB,UCB$W_TT_DESPEE+1,@B,TTY$GB_RSPEED ; Default rspeed DPT_STORE UCB,UCB$B_TT_DEPARI,@B,TTY$GB_PARITY ; Default parity DPT_STORE UCB,UCB$B_TT_PARITY,@B,TTY$GB_PARITY ; Ditto DPT_STORE UCB,UCB$B_DEVTYPE,B,TT$_UNKNOWN ; Type DPT_STORE UCB,UCB$W_DEVBUFSIZ,@W,TTY$GW_DEFBUF ; Buffer size DPT_STORE UCB,UCB$L_DEVDEPEND,@L,TTY$GL_DEFCHAR ; Default character DPT_STORE UCB,UCB$L_TT_DEVDP1,@L,TTY$GL_DEFCHAR2 ; Ditto DPT_STORE UCB,UCB$W_TT_SPEED,@B,TTY$GB_DEFSPEED ; Default speed DPT_STORE UCB,UCB$W_TT_SPEED+1,@B,TTY$GB_RSPEED ; Default rspeed DPT_STORE UCB,UCB$B_DIPL,B,8 ; Device IPL (no device involved) DPT_STORE UCB,UCB$L_TT_WFLINK,L,0 ; Zero write queue DPT_STORE UCB,UCB$L_TT_WBLINK,L,0 ; Ditto DPT_STORE UCB,UCB$L_TT_RTIMOU,L,0 ; Zero read timed out disp. DPT_STORE ORB,ORB$B_FLAGS,B, ; Allow world access DPT_STORE ORB,ORB$W_PROT,@W,TTY$GW_PROT ; Default TTY prot DPT_STORE ORB,ORB$L_OWNER,@L,TTY$GL_OWNUIC ; Default TTY owner DPT_STORE DDB,DDB$L_DDT,D,PTY$DDT ; Dispatch table DPT_STORE REINIT ; Reinitialization DPT_STORE CRB,CRB$L_INTD+VEC$L_INITIAL,D,PTY$INITIAL ; Ctrller init DPT_STORE END DDTAB DEVNAM = PTY,- ; Dummy dispatch table START = 0,- FUNCTB = 0, - UNITINIT = PTY$INIT_UNIT, - CLONEDUCB = PTY$CLONE_UCB .PSECT $$$115_DRIVER,LONG ; The associated class driver uses this table to command the port driver. ; The address of this table is contained in the terminal UCB extension area. ; The offset definitions are defined by $TTYDEFS. PORT_VECTOR: ; PTY port specific dispatch table $VECINI PTY, PTY$NULL $VEC STARTIO, PTY$STARTIO ; Start new output $VEC DISCONNECT, PTY$DISCONNECT ; Disconnect port $VEC ABORT, PTY$ABORT ; Abort output in progress $VEC XON, PTY$XON ; Send XON sequence $VEC XOFF, PTY$XOFF ; Send XOFF sequence $VEC STOP, PTY$STOP ; Stop output $VEC RESUME, PTY$RESUME ; Resume stopped output $VEC SET_LINE, PTY$SET_LINE ; Set unit characteristics $VECEND ; Dummy entry points for unsupported functions PTY$null:: RSB ; Null routine .PAGE .SBTTL Controller Initialization ;++ ; Functional Description: ; ; This routine is entered at controller configuration and power ; recovery. ; ; Inputs: ; R4 - address of the CSR (controller status register) ; R5 - address of the IDB (interrupt data block) ; R6 - address of the DDB (device data block) ; R8 - address of the CRB (channel request block) ; ; Outputs: ; None ; ; Implicit inputs: ; IPL = IPL$_POWER ; ;-- PTY$INITIAL:: class_ctrl_init PTY$DPT, port_vector ; Relocate port driver table CLRB CRB$B_TT_TYPE(R8) ; Set unknown controller type RSB .PAGE .SBTTL Unit Initialization ;++ ; PTY$INIT_UNIT - Unit Initialization ; ; Functional Description: ; ; This routine performs unit initialization for the PTY0 device. ; This can only be called for unit 0, since all the other units get ; pointed at the standard TTDRIVER DDT, which has no UNIT_INIT. ; ; Main thing to do here is ignore any powerfails, hook up to the ; class driver (we also did some of this in the controller init ; routine), then some special initialization for unit 0. ; ; Inputs: ; R5 - Address of our UCB ; ; Outputs: ; R3, R4, R5 must be preserved ; Device available (AVL) bit set in UCB$L_DEVCHAR if no errors. ; ; Implicit inputs: ; IPL = IPL$_POWER ;-- PTY$INIT_UNIT:: bbc #UCB$V_POWER, - ; Do nothing if powerfail UCB$L_STS(r5), 10$ brw 99$ 10$: ; ; Init class/port interface ; moval PTY$VEC, r0 ; Set PTY port vector table ; ; Note well! The following code comes directly from the CLASS_UNIT_INIT ; macro in $TTYMACS. We cannot use the macro directly, however, ; because it changes our DDT to that of the class driver. We want ; our template device (unit 0) to have our own DDT. We will ; explicitly alter UCB's we clone later to point to the class ; driver's DDT. ; MOVL G^TTY$GL_DPT,R1 MOVZWL DPT$W_VECTOR(R1),R2 ADDL R2,R1 MOVL R1,UCB$L_TT_CLASS(R5) movl CLASS_DDT(r1), - ; Save class driver's DDT UCB$L_PTY_CLASS_DDT(r5) MOVL R0,UCB$L_TT_PORT(R5) MOVL CLASS_GETNXT(R1),UCB$L_TT_GETNXT(R5) MOVL CLASS_PUTNXT(R1),UCB$L_TT_PUTNXT(R5) MOVB G^TTY$GB_PARITY,UCB$B_TT_PARITY(R5) MOVB G^TTY$GB_PARITY,UCB$B_TT_DEPARI(R5) ; ; Perform special processing for unit zero. Most fields have already been ; set by DPT_STORE. Set various characteristics and things that we want ; all clones (and clones of clones!) to inherit. Also make this a template ; device. ; movl #UCB$M_TEMPLATE,- UCB$L_STS(r5) clrl UCB$L_PARTNER_UCB(r5) ; No partner to start off. clrl UCB$L_TT_LOGUCB(r5) ; Make sure initialization clrl UCB$L_TT_WFLINK(r5) ; by the class driver is done. clrw UCB$W_TT_OUTLEN(r5) ; No data waiting bisl2 #TT$M_REMOTE, - ; Remote terminal UCB$L_TT_DECHAR(r5) bisl2 #TT2$M_HANGUP, - ; Default to HANGUP UCB$L_TT_DECHA1(r5) bicl2 #TT2$M_AUTOBAUD, - ; No autobaud UCB$L_TT_DECHA1(r5) bisw2 #TTY$M_PC_NOTIME, - ; No timer services UCB$W_TT_PRTCTL(r5) ; ; Make it available for use. ; bisl2 #DEV$M_AVL, UCB$L_DEVCHAR(r5) ; Make it available 99$: rsb .PAGE .SBTTL Clone a new UCB and create a new pair of PTY's ;++ ; PTY$Clone_UCB - Clone UCB from PTY0 ; ; Functional Description: ; ; We are called with the cloned UCB after a $ASSIGN to the template device. ; This clone will become the controlling device. We then clone a newer ; device by calling the appropriate VMS UCB cloning routines. Note that ; the method for this madness is taken from the code for EXE$ASSIGN. ; ; Inputs: ; R0 - SS$_NORMAL ; R2 - UCB address of cloned UCB ; R3 - DDT address ; R4 - PCB address ; R5 - Address of template UCB ; ; Outputs: ; R0 - SS$_NORMAL ; R1 - trashed ; R2 - UCB address of control unit ; R5 - trashed (actually it is the UCB address of terminal unit) ; All other registers and IPL are preserved ; ; Implicit inputs: ; IPL = IPL$_ASTDEL ;-- PTY$CLONE_UCB:: bicl2 #UCB$M_DELETEUCB, - ; Don't automatically delete this UCB UCB$L_STS(r2) ; since our hangup routine will do it. ; ; Clone yet another device. After the clone, put the control device ; in r2 and the new terminal device in r5. ; jsb G^IOC$CHKUCBQUOTA ; See if process has enough for another blbc r0, 90$ ; branch on not enough BYTLM pushl r2 ; Save the controller UCB jsb G^IOC$CLONE_UCB ; make terminal unit in r2. movl r2, r5 ; Put new terminal in into r5 popl r2 ; and get back controller unit blbc r0, 90$ clrw UCB$W_REFC(r5) ; Clear reference count in new UCB jsb G^IOC$DEBIT_UCB ; Debit the process's BYTLM ; ; The two new units now need to be set up as terminals. First make sure ; they both use the terminal driver's DDT. ; movl UCB$L_DDB(r2), r0 ; Get our DDB movl UCB$L_PTY_CLASS_DDT(r2), - ; Make DDB use class DDT DDB$L_DDT(r0) movl UCB$L_PTY_CLASS_DDT(r2), - ; Set new DDT in UCBs too UCB$L_DDT(r2) movl UCB$L_PTY_CLASS_DDT(r5), - UCB$L_DDT(r5) movl UCB$L_CPID(r2),- ; Save creator PID so we can UCB$L_PTY_CPID(r2) ; return quota later movl UCB$L_CPID(r2),- ; Same creator for both UCBs UCB$L_PTY_CPID(r5) ; ; Setup on the terminal device. ; movl UCB$L_TT_CLASS(r5), r1 ; Do class driver setup jsb @CLASS_SETUP_UCB(r1) bsbb PTY$SET_LINE ; Set our characteristics bsbb copy_perm ; make them permanent ; ; Setup on the control device ; movl r5,r0 ; Save control UCB address movl r2,r5 ; Point to terminal UCB movl UCB$L_ORB(r5), r1 ; Get ORB address movw #^xFF00,- ; Only allow owner or system ORB$W_PROT(r1) ; to access the control device ; ; Now class driver setup on the terminal device. ; movl UCB$L_TT_CLASS(r5), r1 jsb @CLASS_SETUP_UCB(r1) bsbb PTY$SET_LINE ; Set our characteristics bsbb copy_perm ; make them permanent movl r0, r5 ; Get back control UCB movl r5,UCB$L_PARTNER_UCB(r2) ; Link terminal to control movl r2,UCB$L_PARTNER_UCB(r5) ; and vice versa movl #SS$_NORMAL, r0 90$: rsb ; ; Copy the temporary characteristics to permanent characteristics ; COPY_PERM: MOVB UCB$B_DEVTYPE(R5),- UCB$B_TT_DETYPE(R5) ; Terminal type MOVW UCB$W_DEVBUFSIZ(R5),- UCB$W_TT_DESIZE(R5) ; Terminal width MOVL UCB$L_DEVDEPEND(R5),- UCB$L_TT_DECHAR(R5) ; Characteristic bits (part 1) MOVL UCB$L_TT_DEVDP1(R5),- UCB$L_TT_DECHA1(R5) ; Characteristic bits (part 2) rsb .PAGE .SBTTL Clone new UCB's ;++ ; Functional Description: ; This routine is called by the class driver whenever device ; characteristics have been changed or by the CLONE_UCB routine ; just after the device is created. On a real device, it would ; set the device registers that control characteristics such as ; speed and parity. We use this routine to control the ; terminal specific characteristics on the PTYs. ; ; Inputs: ; R5 - UCB address ; Outputs: ; Affects fields in the UCB ; Affects NO registers ;-- PTY$SET_LINE:: PUSHR #^M ; Save registers IF_CONTROL 10$ ; Branch if this is a control PTY ; Fixup characteristics for the terminal device MOVL R5,R0 ; UCB address parameter in R0 BSBb TERMINAL_UNIT ; Fix characteristics BRB 5$ ; For control device, propagate any changes to the terminal device and restore ; the special set of control characteristics 10$: MOVL UCB$L_PARTNER_UCB(R5),R0 ; Get our partner (terminal) UCB BEQL 20$ ; Branch if terminal is gone MOVL UCB$L_DEVDEPEND(R5),- UCB$L_DEVDEPEND(R0) ; Copy terminal characteristics MOVL UCB$L_TT_DEVDP1(R5),- UCB$L_TT_DEVDP1(R0) MOVB UCB$B_DEVTYPE(R5),- UCB$B_DEVTYPE(R0) ; Copy terminal type MOVW UCB$W_DEVBUFSIZ(R5),- UCB$W_DEVBUFSIZ(R0) ; and width MOVB UCB$B_VERTSZ(R5),- UCB$B_VERTSZ(R0) ; and page length BSBB TERMINAL_UNIT ; Enforce terminal device requirements ; ; Restore fixed characteristics for control device ; 20$: MOVL #PTY_DEFCHAR,- UCB$L_DEVDEPEND(R5) ; Reset characteristics MOVL #PTY_DEFCHAR2,- UCB$L_TT_DEVDP1(R5) MOVB #PTY_DEFTYPE,- UCB$B_DEVTYPE(R5) ; Restore terminal type MOVW #PTY_DEFWIDTH,- UCB$W_DEVBUFSIZ(R5) ; and width MOVB #PTY_DEFPAGE,- UCB$B_VERTSZ(R5) ; and page length 5$: POPR #^M ; Restore registers RSB ; ; For a terminal device, just insure that the user has not changed any bits ; which will break the driver. ; TERMINAL_UNIT: BICL2 #,- UCB$L_DEVDEPEND(R0) ; Temporary char - longword 1 BISL2 #,- UCB$L_DEVDEPEND(R0) ; Temporary char - longword 1 BICL2 #,- UCB$L_TT_DEVDP1(R0) ; Temporary char - longword 2 BISL2 #TT2$M_ALTYPEAHD,- UCB$L_TT_DEVDP1(R0) ; Temporary char - longword 2 RSB .PAGE .SBTTL Start I/O Routine ;++ ; PTY$STARTIO - Start I/O operation ; ; Functional Description: ; This routine is called from the class driver to start output ; on the device. It accomplishes its transfer by calling the ; equivalent of the interrupt service routine for its partner ; device. ; ; Inputs: ; R3 = Character AND CC=Plus ; Address AND CC=Negative ; R5 = UCB address ; ; Outputs: ; R5 = UCB address ; -- PTY$STARTIO:: BGEQ 20$ ; Branch if single character I/O ; Here for multiple character case TSTW UCB$W_TT_OUTLEN(R5) ; Really characters to output? BLEQ 30$ ; Branch if not 10$: BBS #TTY$V_ST_CTRLS,- UCB$Q_TT_STATE(R5),40$ ; If we get XOFF'ed, abort the output MOVZBL @UCB$L_TT_OUTADR(R5),- R3 ; Get next character to output BSBB PTY$INPUT_CHAR ; Ship the character to our partner INCL UCB$L_TT_OUTADR(R5) ; Adjust pointer DECW UCB$W_TT_OUTLEN(R5) ; Decrement character count BGTR 10$ ; Loop while characters remain BRB 30$ ; Here for single character case 20$: beql 30$ ; If no character then skip. BSBB PTY$INPUT_CHAR ; Ship character to our partner 30$: PUSHR #^M ; Save registers JSB @UCB$L_TT_GETNXT(R5) ; Get new characters to output POPR #^M ; Restore registers bneq PTY$STARTIO ; do another if there is one 40$: rsb ; Else, all done .PAGE .SBTTL Receiver service routine ;++ ; PTY$INPUT_CHAR - Receive a character ; ; Functional Description: ; This routine is entered when a character is to be passed to ; the current device's partner. It is analogous to an interrupt ; service routine for the receive side. It is important that ; the interrupt expected (INT) bit be kept clear since parts ; of the class driver will not initiate actions when they think ; the next device interrupt (which we lack) will trigger them. ; ; Inputs: ; R3 - Character to be received ; R5 - Address of sender's UCB ; ; Outputs: ; R5 - Address of sender's UCB ;-- PTY$INPUT_CHAR:: BICL2 #,- UCB$L_STS(R5) ; Make things happen synchronously. ; Don't let write time out. PUSHL R5 ; Save UCB context MOVL UCB$L_PARTNER_UCB(R5),- R5 ; Switch to receiver's context BEQL 10$ ; Branch if partner has vanished BICL2 #UCB$M_INT,- UCB$L_STS(R5) ; Make driver think things must be ; handled synchronously PUSHR #^M ; Save registers JSB @UCB$L_TT_PUTNXT(R5) ; Store the character in receiver's ; buffer POPR #^M ; Restore registers ; ; At this point, the PUTNXT routine may have returned characters to be ; output on the receiver's output side. This may be something like a ; multi-character echo sequence or an XOFF. Heaven help us if we ; ever get here with conventional echo turned on for the control device!! ; (Can you say "infinite recursion at driver IPL"? Shuure you can...) ; BEQL 10$ ; Branch if no character BSBB PTY$STARTIO ; Output the character 10$: POPL R5 ; Restore UCB context RSB .PAGE .SBTTL Port control routines ;++ ; PTY$XON and PTY$XOFF - Propagate flow control to partner ; ; Functional description: ; Do flow control by telling partner to start or stop output ; ; Inputs: ; R5 = UCB address ; ; Outputs: ; Preserve all registers. ;-- PTY$XOFF:: PUSHL R5 ; Save UCB context MOVL UCB$L_PARTNER_UCB(R5),R5 ; Get partner's UCB BEQL 10$ ; Branch if he is gone BSBB PTY$STOP ; Call his port stop routine 10$: POPL R5 ; Restore our context RSB PTY$XON:: PUSHL R5 ; Save UCB context MOVL UCB$L_PARTNER_UCB(R5),R5 ; Get partner's UCB BEQL 10$ ; Branch if he is gone BSBB PTY$RESUME ; Call his port resume routine 10$: POPL R5 ; Restore our context RSB PTY$STOP:: BICW #UCB$M_INT!UCB$M_TIM,- UCB$W_STS(R5) ; Clear timer and device active status BBCS #TTY$V_ST_CTRLS,- UCB$Q_TT_STATE(R5),5$ ; Interrupt current output and block new ; ; The following is commented out because I don't really know what it does. ; This was in some other port drivers. It may be necessary, I'm not sure. ; Flow control SEEMS to work without it... ; ; BISW2 #TTY$M_TANK_STOP,- ; UCB$W_TT_HOLD(R5) ; CTRLS already set - require two XON's 5$: RSB PTY$RESUME:: ; BBSC #TTY$V_TANK_STOP,- ; UCB$W_TT_HOLD(R5),10$ ; We were XOFF'ed twice - require two ; XON's PUSHL R3 ; Save register BBCC #TTY$V_ST_CTRLS,- UCB$Q_TT_STATE(R5),5$ ; Clear output blocked flag 5$: MOVL UCB$L_TT_OUTADR(R5),- R3 ; Get address of output burst (also sets ; CC=negative BSBW PTY$STARTIO ; Go do the output POPL R3 ; Restore register 10$: RSB ;++ ; PTY$ABORT - Abort current port activity ; ; Functional description: ; Abort current output on port ; ; Inputs: ; R5 = UCB address ; ; Outputs: ; Preserve all registers. ;-- PTY$ABORT:: ; BISW2 #TTY$M_TANK_STOP,- ; UCB$W_TT_HOLD(R5) ; Unblock port output BBCC #TTY$V_ST_CTRLS,- UCB$Q_TT_STATE(R5),10$ 10$: clrw UCB$W_TT_OUTLEN(r5) ; Dump any pending data RSB ;++ ; PTY$DISCONNECT - "Hangup" a deassigned unit ; ; Functional description: ; This entry point is called by TTDRIVER's cancel I/O processing when ; the last channel has been removed from the device. ; ; This is a little tricky. What happens is this... If we are supposed ; to hangup the device, then we first force a hangup on our partner and ; then set our UCB offline and deletable. If no hangup then just skip ; all this stuff. ; ; Since the control device is ALWAYS set /HANGUP, whenever it's reference ; count goes to zero we will get called with a HANGUP flag in R0. We ; then go ahead and force a disconnect on our partner (the terminal ; device). We leave R0 = 0 to force the terminal device to hangup no ; matter what it's /HANGUP setting when the control device is going ; away. ; ; The terminal device may, if changed to /NOHANGUP from it's default of ; /HANGUP, get here with R0 = 1. If so, don't do anything. Since the ; device does not have the DELETEUCB bit, it'll stay around after the ; deassignment allowing another login to take place. ; ; Inputs: ; R0 = (0 for hangup, 1 for nohangup) ; R5 = UCB address ; ; Outputs: ; Preserve all registers. ;-- PTY$DISCONNECT:: blbs r0, 99$ ; If no hangup, skip all this pushr #^M ; Save r0 movl UCB$L_PARTNER_UCB(r5), r1 ; If no partner, skip code beql 10$ ; forces disconnect on him. clrl UCB$L_PARTNER_UCB(r1) ; Partner now has no partner ; to avoid recursive disconnect pushr #^M movl r1, r5 ; Force a disconnect on partner bicl2 #UCB$M_INT, UCB$L_STS(r5) ; No ints, allows data to flush movl UCB$L_TT_CLASS(r5), r1 ; R0 already says to hangup jsb @CLASS_DISCONNECT(r1) popr #^M clrl UCB$L_PARTNER_UCB(r5) ; We have no more partner now 10$: bisl2 #UCB$M_DELETEUCB, UCB$L_STS(r5) ; Now the device can go away bicl2 #UCB$M_ONLINE, UCB$L_STS(r5) ; Mark the thing OFFLINE movl UCB$L_PTY_CPID(r5), - ; Process to restore quota to UCB$L_CPID(r5) popr #^M 99$: rsb .PAGE .SBTTL End of PTYDRIVER PTY$END:: .END