.title TWDRIVER Pseudo terminal driver .ident "V05.02 © '88" ;++ ; ; Facility: ; ; VAX/VMS Pseudo terminal driver terminal interface ; ; Author of this version: ; Nick de Smith ; Applied Telematics Group ; 7 Vale Avenue ; Tunbridge Wells ; Kent TN1 1DJ ; England ; +44 892 511000, PSI%234213300154::NICK ; ; Abstract: ; ; The pseudo terminal consists of two devices: ; ; o A terminal port driver (TWDRIVER, this driver) ; o A control driver (PYDRIVER) ; ; Revision History: ; ; Edit Edit date By Why ; 5.01 01-Dec-88 NMdS VMS V5.0 ; 01 24-May-88 NMdS New. ; ; History: ; ; See PYDRIVER.MAR for a discussion of the history of this driver. ;-- .subtitle Declarations .library "SYS$LIBRARY:LIB.MLB" .link "SYS$SYSTEM:SYS.STB" /Selective_Search ; ; External definitions ; $dcdef ; Define device classes/types $ddbdef ; Define DDB offsets $ddtdef ; Define DDT offsets $devdef ; Device characteristics $dyndef ; Dynamic structure definitions $msgdef ; Message types $orbdef ; Define Object's Rights Block offsets $splcoddef ; Define spin lock indices $tt2def ; Define extended characteristics $ttdef ; Define terminal types $ttydefs ; Define terminal driver symbols $ttymacs ; Define terminal driver macros $vecdef ; Define vector for CRB ; ; Local definitions ; DT$_TW = ^xFF ; Foreign device type ; ; TW device UCB extensions. ; As the TW device is a terminal port driver, all extensions must go after ; the "standard" terminal UCB fields. ; $DEFINI UCB .=UCB$K_TT_LENGTH ; Start at end of standard (terminal) UCB $DEF UCB$L_PY_UCB .BLKL 1 ; => UCB of corresponding PY device $DEF UCB$K_TW_LEN ; New size of TW UCB $DEFEND UCB ; ; PY device UCB extensions. ; The PY (control) device is not a terminal, and therefore only needs a basic ; UCB. All extensions go after the basic UCB fields. ; $DEFINI UCB .=UCB$K_LENGTH ; Start at end of standard UCB $DEF UCB$L_TW_UCB .BLKL 1 ; => UCB of corresponding TW device $DEF UCB$K_PY_LEN ; New size of PY UCB $DEFEND UCB .subtitle Standard Tables ; ; Driver Prologue Table ; DPTAB - ; Driver Prologue Table SMP = YES ,- ; VMS V5 SMP driver END = TW$END ,- ; End of driver UCBSIZE = UCB$K_TW_LEN ,- ; Size of UCB FLAGS = DPT$M_NOUNLOAD,- ; Do not allow unload ADAPTER = NULL ,- ; No adapter NAME = TWDRIVER ,- ; Name of driver VECTOR = TW$PORT_VECTOR ; Address of port driver vector table DPT_STORE INIT DPT_STORE UCB, UCB$W_UNIT_SEED , W, 0 ; Set unit # seed to zero DPT_STORE UCB, UCB$B_FLCK , B, SPL$C_IOLOCK8 ; Fork spin lock DPT_STORE UCB, UCB$B_DIPL , B, 8 ; Device interrupt IPL (no device) DPT_STORE UCB, UCB$L_DEVCHAR , L, <- ; Device characteristics DEV$M_REC!- ; record oriented DEV$M_IDV!- ; input device DEV$M_ODV!- ; output device DEV$M_TRM!- ; terminal device DEV$M_CCL> ; carriage control device DPT_STORE UCB, UCB$L_DEVCHAR2 , L, ; Device characteristics 2 DPT_STORE UCB, UCB$L_DEVDEPEND , L, 0 ; Device dependant flags ; (only TT$M_REMOTE is looked at by TTY$SETUP_UCB) DPT_STORE UCB, UCB$B_DEVCLASS , B, DC$_TERM ; Device class: TERMINAL DPT_STORE UCB, UCB$W_DEVBUFSIZ ,@W, TTY$GW_DEFBUF ; Device buffer size DPT_STORE UCB, UCB$B_TT_DETYPE , B, TT$_UNKNOWN ; Terminal type: UNKNOWN DPT_STORE UCB, UCB$W_TT_DESIZE ,@W, TTY$GW_DEFBUF ; Default buffer size DPT_STORE UCB, UCB$L_TT_DECHAR ,@L, TTY$GL_DEFCHAR ; Default device characteristics DPT_STORE UCB, UCB$L_TT_DECHA1 ,@L, TTY$GL_DEFCHAR2 ; '' DPT_STORE UCB, UCB$L_TT_DECHA2 , L, 0 ; '' DPT_STORE UCB, UCB$L_TT_DECHA3 , L, 0 ; '' DPT_STORE UCB, UCB$W_TT_DESPEE ,@B, TTY$GB_DEFSPEED ; Default transmit speed DPT_STORE UCB, UCB$W_TT_DESPEE+1,@B, TTY$GB_RSPEED ; Default receive speed DPT_STORE UCB, UCB$B_TT_DEPARI ,@B, TTY$GB_PARITY ; Default parity/character size DPT_STORE UCB, UCB$L_TT_WFLINK , L, 0 ; Write queue forward link DPT_STORE UCB, UCB$L_TT_WBLINK , L, 0 ; Write queue backwards link DPT_STORE ORB, ORB$B_FLAGS , B, ; SOGW protection word DPT_STORE ORB, ORB$W_PROT ,@W, TTY$GW_PROT ; Default allocation protection DPT_STORE ORB, ORB$L_OWNER ,@L, TTY$GL_OWNUIC ; Default owner UIC ; DPT_STORE DDB, DDB$L_DDT , D, TW$DDT ; => Driver Dispatch Table (dummy) ; DPT_STORE REINIT DPT_STORE CRB, CRB$L_INTD+VEC$L_INITIAL , D, TW$INITIAL ; Controller initialisation DPT_STORE CRB, CRB$L_INTD+VEC$L_UNITINIT, D, TW$UNITINIT ; Unit initialisation DPT_STORE END .subtitle Driver Dispatch Table and Function Decision Table ; ; Driver Dispatch Table ; ; As this is a port driver, the DDT is a stub only, and there is no FDT. ; DDTAB DEVNAM = TW ,- ; Device name is TW START = 0 ,- ; No STARTIO routine FUNCTB = 0 ; No function decision tables .subtitle Port Vector Table ; ; The terminal class driver uses this table to command the port driver. ; The address of the table is contained in the terminal UCB extension area ; and in the DPT. ; TW$PORT_VECTOR:: $VECINI TW, TW$NULL $VEC STARTIO , TW$STARTIO ; Start new output $VEC DISCONNECT , TW$DISCONNECT ; Disconnect line $VEC XON , TW$XON ; Send XON $VEC XOFF , TW$XOFF ; Send XOFF $VEC ABORT , TW$ABORT ; Abort current output $VECEND TW$NULL:: ; Null port routine rsb .subtitle TW$INITIAL Controller initialisation ;++ ; TW$INITIAL Initialise controller interface ; ; Functional description: ; ; This routine is called by: ; o SYSGEN CONNECT ; o SYSGEN RELOAD ; o Power recovery ; ; As we are a terminal port driver, we just connect ourselves to the ; terminal class driver. ; ; Inputs: ; ; R4 => Unit CSR (none) ; R5 => Unit IDB ; R6 => driver DDB ; R8 => driver CRB ; All interrupts are locked out, IPL = IPL$_POWER ; ; Outputs: ; ; R0-R3 Destroyed ; R4-R11 Preserved ;-- TW$INITIAL:: CLASS_CTRL_INIT DPT$TAB, TW$PORT_VECTOR ; Identify ourselves to TTDRVR movb #DT$_TW, CRB$B_TT_TYPE(r8) ; Set device type in CRB rsb .subtitle TW$UNITINIT Initialise single unit ;++ ; TW$UNITINIT Initialise single unit ; ; Functional description: ; ; This routine performs a simple unit initialisation. ; It is called from PY$CLONEDUCB in PYDRIVER to initialise the ; generated TW device. ; ; Inputs: ; ; R4 => caller's PCB ; R5 => TW UCB ; ; Outputs: ; ; R0-R3 Destroyed ; R4-R11 Preserved ;-- TW$UNITINIT:: ; Reset single line tstw UCB$W_UNIT(r5) ; Skip initialisation of TEMPLATE bneq 10$ ; Unit #0 = Template: do nothing rsb 10$: movab TW$VEC, r0 ; Set TW port vector table CLASS_UNIT_INIT ; Go online to the class driver bbc #UCB$V_POWER, UCB$L_STS(r5), 20$ ; Powerfail handler movl UCB$L_TT_CLASS(r5), r1 ; R1 => class vector table jsb @CLASS_POWERFAIL(r1) ; Call class driver powerfail routine brb 40$ ; Exit 20$: movl UCB$L_TT_LOGUCB(r5), r1 ; R1 => UCB of logical device beql 30$ ; Skip reference check if no logical UCB tstw UCB$W_REFC(r1) ; See if it has any references bneq 40$ ; If so don't reinit UCB 30$: bicl #TT2$M_DMA!TT2$M_AUTOBAUD, UCB$L_TT_DECHA1(r5) ; No DMA and no AUTOBAUD bisl #TT2$M_HANGUP, UCB$L_TT_DECHA1(r5) ; We want hangup messages movl UCB$L_TT_CLASS(r5), r1 ; R1 => class vector table jsb @CLASS_SETUP_UCB(r1) ; Init UCB fields bisw #- ; Do not count as a user , UCB$W_TT_PRTCTL(r5) ; Update port control flags 40$: rsb .subtitle TW$DISCONNECT Shut off unit ;++ ; TW$DISCONNECT Shut off unit ; ; Functional description: ; ; The terminal class driver calls this routine when there are no longer ; any channels assigned to the TW device. ; ; If the PY device has an associated mailbox, signal an MSG$_TRMHANGUP in it. ; ; Although we are disconnecting a virtual device, we don't do anything ; more than send a hangup message because we want to allow the device to ; be reusable. It's really only if the control device (PY) goes away ; that we mark the TW offline and delete it's UCB. That code's all in ; PYDRIVER. ; ; Inputs: ; ; R0 = <0> clear for hangup (delete UCB) ; set for nohangup (do not delete UCB) ; R5 => TW UCB ; ; Outputs: ; ; R0-R11 Preserved ;-- TW$DISCONNECT:: blbs r0, 20$ ; If no hangup, skip all this. R_SAVE_MASK = ^m pushr #R_SAVE_MASK ; Save registers movl UCB$L_PY_UCB(r5), r5 ; R5 => PY UCB beql 10$ ; If disconnected, ignore movl UCB$L_AMB(r5), r3 ; R3 => PY associated mailbox UCB beql 10$ ; If EQL then no mailbox movzwl #MSG$_TRMHANGUP, r4 ; Load Message Type jsb g^EXE$SNDEVMSG ; Send the message 10$: popr #R_SAVE_MASK ; Restore everything 20$: rsb .subtitle TW$STARTIO Start I/O routine ;++ ; TW$STARTIO Start I/O operation on TW ; ; Functional description: ; ; The terminal class driver calls TW$STARTIO to start output on ; a line that is currently inactive. ; The TW$STARTIO routine is always called with either a character ; or a burst of data, and is never called unless the line is idle ; (UCB$V_INT is clear in UCB$L_STS). ; This routine can also be called by PY$FDTWRITE to echo characters ; from the terminal class driver. In this case, it is possible that ; there will be no characters to echo, so we just return. ; ; Inputs: ; ; R0 = First I/O status longword (IRP$L_IOST1) ; R3 = Character to be output (if UCB$B_TT_OUTYPE is 1) ; R5 => TW UCB ; UCB$B_TT_OUTYPE ; 0 if there is no character to be output ; 1 '' '' '' 1 '' '' '' '' ; -ve '' '' '' a burst '' '' '' ; UCB$L_TT_OUTADDR ; Address of burst to be output (if UCB$B_TT_OUTYPE is -ve) ; UCB$W_TT_OUTLEN ; Length of burst (if UCB$B_TT_OUTYPE is -ve) ; ; UCB$V_INT has been set in UCB$L_STS by the class driver. ; ; Outputs: ; ; R0-R11 Preserved ;-- TW$STARTIO:: ; Start I/O on unit tstb UCB$B_TT_OUTYPE(r5) ; Check type of I/O (burst or character) beql 60$ ; Skip if no data (safety check). bgtr 10$ ; Single character bisw #TTY$M_TANK_BURST, UCB$W_TT_HOLD(r5) ; Signal burst active brb 20$ 10$: movb r3, UCB$W_TT_HOLD(r5) ; Save output character bisw #TTY$M_TANK_HOLD, UCB$W_TT_HOLD(r5) ; Signal character in tank ; ; Notify the associated PY device that there is data to pick up ; R_SAVE_MASK = ^m 20$: pushr #R_SAVE_MASK ; Save registers movl UCB$L_PY_UCB(r5), r5 ; R5 => PY UCB beql 40$ ; PY not available ; ; As PY devices have the same FLCK as TW devices, we should not need to ; take out a FORKLOCK here, as we already have the required spinlock. ; bbc #UCB$V_BSY, UCB$L_STS(r5), 30$ ; If the device isn't busy, then don't do I/O movl UCB$L_IRP(r5), r3 ; R3 => IRP jsb g^IOC$INITIATE ; Start the I/O going 30$: popr #R_SAVE_MASK ; R5 => TW UCB rsb ; ; We get here if there is no PY device associated with the current TW. ; We eat all data from the TW device and junk them. ; 40$: popr #R_SAVE_MASK ; R5 => TW UCB 50$: bicl #UCB$M_INT, UCB$L_STS(r5) ; Say port driver not busy jsb @UCB$L_TT_GETNXT(r5) ; Get next data tstb UCB$B_TT_OUTYPE(r5) ; Was there any data? bneq 50$ ; Yes, try for some more 60$: rsb ; No more data to eat, so return .subtitle TW$XON Send XON .subtitle TW$XOFF Send XOFF ;++ ; TW$XON Send XON ; TW$XOFF Send XOFF ; ; Functional Description: ; ; The terminal class driver calls these routines when it is ; approaching or has reached its input limit, or when it has ; cleared its input path and is ready to accept more data. ; The specified flow control character (in R3) is inserted ; into the output stream as soon as is possible. ; The character is saved as a "pre-empt" character in the tank. ; This means that it will be output before any other pending ; characters. ; ; Inputs: ; ; R3 = Flow control character to be inserted in the output ; data stream. ; R5 => TW UCB ; UCB$L_STS ; UCB$V_INT may or may not be set ; ; In theory, these routines should set UCB$V_INT and initiate ; I/O. ; ; Outputs: ; ; R0-R11 Preserved ;-- TW$XON:: TW$XOFF:: movb r3, UCB$B_TT_PREMPT(r5) ; Save character bisw #TTY$M_TANK_PREMPT, UCB$W_TT_HOLD(r5) ; Schedule XON/XOFF rsb .subtitle TW$ABORT Abort output ;++ ; TW$ABORT Abort output ; ; Functional Description: ; ; The terminal class driver calls the port abort routine to abort any ; currently active output. ; The best we can do is to stop the next scheduled burst to be output ; (if there is one). ; ; Inputs: ; ; R5 => TW UCB ; ; Outputs: ; ; R0-R11 Preserved ;-- TW$ABORT:: bbcc #TTY$V_TANK_BURST, UCB$W_TT_HOLD(r5), 10$ ; Reset burst active 10$: rsb TW$END:: ; End of driver .end