#pragma module pppd$dynswitch "X-7" /* ***************************************************************************** * * Copyright © 1996 Digital Equipment Corporation. * All rights reserved. * * Redistribution and use in source and binary forms are permitted * provided that the above copyright notice and this paragraph are * duplicated in all such forms and that any documentation, * advertising materials, and other materials related to such * distribution and use acknowledge that the software was developed * by Digital Equipment Corporation. The name of the * Corporation may not be used to endorse or promote products derived * from this software without specific prior written permission. * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. * ***************************************************************************** FACILITY: ASNDRIVER ABSTRACT: This module contains a the terminal driver switching rotuines. They are: dyn$revert - switch physical UCB back to TTDRIVER dyn$switch - switch physical terminal to ASNDRIVER from TTDRIVER AUTHOR: Forrest A. Kenney 29-February-1996 REVISION HISTORY: X-7 FAK005 Forrest A. Kenney 23-March-1997 If the type-ahead buffer still exists when we go to switch the line delete it. If not this hunk of pool is lost forever. This is only the case when we dial out. When we dial in the buffer gets cleaned up for us by TTDRIVER. X-6 FAK004 Forrest A. Kenney 03-March-1997 If the line being switched is modem don't switch it if the remote attribute is not set. Also for modem lines being switched clear the timer and remove it from the count of devices on the controller that the modem timer is ticking for. X-5 FAK003 Forrest A. Kenney 05-September-1996 Copy DIPL from physical UCB to ASN UCB. Unless the line is set permanently to no hangup hang the modem up and flip the current attribute back to hangup. X-4 FAK002 Forrest A. Kenney 25-July-1996 It is now time remove the development quality line switching logic and do the real thing. The real code needs to make sure that the following things are true: a) The class driver is TTDRIVER b) That we are not using OPAx port c) That we own the line to be switched d) That the line is idle If any of these are not true then the request will be aborted with an error. X-3 FAK001 Forrest A. Kenney 30-May-1996 Make sure that we clear out the PID field when we give up the device. If not it has a dangling owner of sorts. */ /* Define system data structure types and constants */ #include /* AST control block definitions */ #include /* Channel Control Block definitons */ #include /* Controller Request Block definitons */ #include /* VMS descriptor definitions */ #include /* Device characteristics bits */ #include /* Device data block definitions */ #include /* Driver Dispatch table definitions */ #include /* Data structure type definitions */ #include /* Fork block definitions */ #include /* I/O routines constants */ #include /* Interrupt Dispatch Block Definitions */ #include /* interger definitions */ #include /* I/O Request Packet definitions */ #include /* Object rights Block definitions */ #include /* Process Control Block definitions */ #include /* System data block definitions */ #include /* Spinlock definitions */ #include /* Status return valuse */ #include /* Terminal attrubutes TT$xxx */ #include /* Terminal attributes TT2$xx */ #include /* TTY symbols private to class driver */ #include /* TTY UCB offsets */ #include /* Terminal vector definitions */ #include /* UCB offsets */ #include /* VCRP defnitions */ #define VCIBDEF vcibdef /* This is needed to work around an incorrect definition for VCIB in LIB *. /* Define ASN specific data structures types and constants */ #include "asndef.h" /* ASN public definitions */ #include "asnmiscdef.h" /* ASN miscellanous items */ #include "asnvcibdef.h" /* ASN VCIB */ #include "pppd$asn_hide_ptrs.h" /* Hide long paths to items */ #include "pppd$asn_linkages.h" /* JSR register linkages */ #include "pppd$asn_prototypes.h" /* Prototypes for ASN routines */ /* Define function prototypes for system routines */ #include /* Prototypes for com$ and com_sdt$ routines */ #include /* Prototypes for exe$ and exe_std$ routines */ #include /* Prototypes for ioc$ and ioc_std$ routines */ #include /* Prototypes for sch$ and sch_std$ routines */ /* Define various device driver macros */ #include /* Device driver support macros, including */ /* table initialization macros and prototypes*/ /* Define the DEC C functions used by this driver */ #include /* OpenVMS AXP specific C builtin functions */ #include /* String routines provided by "kernel CRTL" */ /* **++ ** dyn$revert - switch physical UCB back to TTDRIVER ** ** Functional description: ** ** The dyn$revert routine is used to restore the terminal class driver as ** the class driver for the specified terminal port. The routine will place ** the class driver vector address in the class field of the UCB. Fill in the ** get and put pointers with the addresses of the get and put routines in the ** class driver. Finally it will drive the unit to a idle state. This will ** cause the class driver to reset the unit to the state it had when it was ** switched from the default terminal class driver. This routine makes no ** sanity checks it trusts that is it safe to do what it is about to do. ** ** Calling convention: ** ** int dyn$revert (ASNUCB *asnucb) ** ** Input parameters: ** ** ASNUCB Pointer to the ASN devices UCB ** ** Output parameters: ** ** None ** ** Return value: ** ** SS$_NORMAL Terminal switched back ** SS$_DEVINACT Could not swith terminal back already switched ** ** Environment: ** ** Called at IPL 8 with the fork lock held. Upon exit the IPL will be at ** callers IPL this is assumed to be IPL 8. ** **-- */ int dyn$revert(ASNUCB *asnucb) { int saved_ipl; int status; extern DPT *TTY$GL_DPT; TT_CLASS *CLASS_VECTOR; TTY_UCB *phyucb; phyucb = (TTY_UCB *) asnucb->ASNLOG.ucb$l_tl_phyucb; if ((void *) phyucb != (void *) 0) { device_lock(asnucb->ASNBASE.ucb$l_dlck, RAISE_IPL, &saved_ipl); phyucb->PHYBASE.ucb$l_ddt = (DDT *) asnucb->ASNLOG.ucb$l_tl_posix_data; CLASS_VECTOR = TTY$GL_DPT->dpt$ps_vector; phyucb->ucb$l_tt_logucb = (UCB *) phyucb; phyucb->ucb$l_tt_class = (int) CLASS_VECTOR; phyucb->ucb$l_tt_getnxt = CLASS_VECTOR->class_getnxt; phyucb->ucb$l_tt_putnxt = CLASS_VECTOR->class_putnxt; phyucb->ucb$l_tt_state1 = 0; phyucb->ucb$l_tt_state2 = 0; /* ** ** Retore devices original PORT CONTROL flags ** */ phyucb->ucb$w_tt_prtctl = asnucb->ASNLOG.ucb$l_tl_outband; if ((phyucb->PHYBASE.ucb$l_devdepend & TT$M_MODEM) && (phyucb->ucb$l_tt_decha1 & TT2$M_HANGUP)) { /* ** ** Drop DTR & RTS ** */ port$ds_set(((TT$M_DS_DTR | TT$M_DS_RTS) << 8) , phyucb); phyucb->PHYBASE.ucb$l_devdepnd2 | TT2$M_HANGUP; } port$abort(phyucb); port$resume(phyucb); /* ** ** Use real class driver code instead of ASN code to force a ** hangup and UCB reset ** */ tty$class_disconnect(phyucb); tty$class_setupucb(phyucb); port$set_line(phyucb); phyucb->PHYBASE.ucb$l_refc -= 1; if ( phyucb->PHYBASE.ucb$l_refc < 0) { phyucb->PHYBASE.ucb$l_refc = 0; } phyucb->PHYBASE.ucb$l_pid = 0; phyucb->PHYBASE.ucb$v_online = 1; phyucb->PHYBASE.ucb$l_devchar = phyucb->PHYBASE.ucb$l_devchar | DEV$M_AVL; phyucb->PHYBASE.ucb$l_devchar2 = phyucb->PHYBASE.ucb$l_devchar2 & ~DEV$M_RED; asnucb->ASNLOG.ucb$l_tl_phyucb = 0; status = SS$_NORMAL; device_unlock(asnucb->ASNBASE.ucb$l_dlck, saved_ipl, SMP_RESTORE); } else { status = SS$_DEVINACT; } return status; } /* **++ ** dyn$switch - switch physical terminal to ASNDRIVER from TTDRIVER ** ** Functional description: ** ** Describe routine ** ** Calling convention: ** ** int dyn$switch (ASNUCB *asnucb, TTY_UCB *phyucb) ** ** Input parameters: ** ** asnucb Pointer to ASN UCB ** devname String descriptor with name of device to connect to ** ** Output parameters: ** ** None ** ** Return value: ** ** SS$_NORMAL Terminal switched back ** SS$_DEVALLOC Device to be bound to is beind used by another user ** SS$_IVDEVNAM Illegal device specified ** SS$_BADPARAM Device name length to long ** ** Environment: ** ** Called at IPL 2, IPL is raised to IPL 8. Upon exit the IPL will be at ** callers IPL. ** **-- */ int dyn$switch(ASNUCB *asnucb, struct dsc$descriptor *devname) { extern DDT asndummy_ddt; extern TT_CLASS CLASS_VECTOR; extern PCB *CTL$GL_PCB; extern DPT *TTY$GL_DPT; char *devname_ptr; char devname_str[64]; int flags_in = IOC$M_ANY | IOC$M_PHY; int32 flags_out; int32 name_length; int saved_dipl; int saved_fipl; int32 scs_length; int status; int32 unit; void *dummy_lock; DDB *ddb; MUTEX *mutex; SB *sb; TTY_UCB *phyucb; TTY_UCB *tmpucb; if (devname->dsc$w_length > sizeof(devname_str)) { return SS$_BADPARAM; } /* ** ** We have a valid starting point for looking at the physical device to ** see if it can be switched. From here on out we need to insure that the ** I/O database will not change underneath us. So we will grab the MUTEX for ** read access. It will be released on the way out. ** */ memcpy(devname_str, devname->dsc$a_pointer, devname->dsc$w_length); mutex = sch_std$iolockr(CTL$GL_PCB); status = ioc_std$parsdevnam(devname->dsc$w_length,devname_str, flags_in, &unit, &scs_length, &name_length, &devname_ptr, &flags_out); if (status & SS$_NORMAL) { status = ioc_std$searchint(unit, scs_length, name_length, devname_ptr, flags_in, (UCB **) &tmpucb, &ddb, &sb, &dummy_lock); if (status & SS$_NORMAL) { if (tmpucb->PHYBASE.ucb$l_pid == CTL$GL_PCB->pcb$l_pid) { if ((tmpucb->PHYBASE.ucb$l_devchar & DEV$M_TRM) && (tmpucb->PHYBASE.ucb$w_size >= UCB$K_TL_LENGTH) && ((void *) tmpucb->PHYLOG.ucb$l_tl_phyucb != (void *) 0)) { /* ** ** We have a UCB that claim to be a terminal UCB and it is large ** enough. Now lets make sure we have the physical UCB, and it is ** not OPAX, and that the driver being used is the terminal class ** driver. ** */ fork_lock(asnucb->ASNBASE.ucb$b_flck, &saved_fipl); if ((void *) tmpucb != (void *) tmpucb->PHYLOG.ucb$l_tl_phyucb) { phyucb= (TTY_UCB *) tmpucb->PHYLOG.ucb$l_tl_phyucb; } else { phyucb = tmpucb; } ddb = phyucb->PHYBASE.ucb$l_ddb; if (((void *) TTY$GL_DPT != (void *) phyucb->ucb$l_tt_class) && (strcmp("OPA", (char *) &ddb->ddb$t_name) != 0)) { /* ** ** We have a viable candiate to try and switch. Set the ** phyucb and potential logical UCB to nohangup. Bump the ** reference count in phyucb and force a line disconnect. ** */ phyucb->PHYBASE.ucb$l_refc += 1; phyucb->PHYBASE.ucb$l_devdepnd2 = phyucb->PHYBASE.ucb$l_devdepnd2 & ~TT2$M_HANGUP; tmpucb->PHYBASE.ucb$l_devdepnd2 = tmpucb->PHYBASE.ucb$l_devdepnd2 & ~TT2$M_HANGUP; device_lock(phyucb->PHYBASE.ucb$l_dlck, RAISE_IPL, &saved_dipl); tty$class_disconnect(phyucb); device_unlock(phyucb->PHYBASE.ucb$l_dlck, saved_dipl, SMP_RESTORE); fork_unlock(asnucb->ASNBASE.ucb$b_flck, saved_fipl, SMP_RESTORE); /* ** ** Now check if it is still ok to switch the device. The ** the reference count must be 2 or lower, and the logical ** UCB must point at the physical one. The device also need ** to be quiet. If it set to modem the remote attribute must ** be set. ** */ if ((phyucb->PHYBASE.ucb$l_refc > 2) || ((void *) phyucb != (void *) phyucb->ucb$l_tt_logucb) || (phyucb->tty$v_st_read) || (phyucb->tty$v_st_write) || (phyucb->tty$v_st_multi)) { phyucb->PHYBASE.ucb$l_refc -= 1; status = SS$_DEVALLOC; } } else { status = SS$_IVDEVNAM; fork_unlock(asnucb->ASNBASE.ucb$b_flck, saved_fipl, SMP_RESTORE); } /* ** ** Do not need to release fork lock here as all paths to here have ** already done so. ** */ } else { status = SS$_IVDEVNAM; } } else { status = SS$_DEVALLOC; } } } /* ** ** We have found a device and got it all ready to do the line switch. So ** If everthing up to this point was successful then we can go ahead and swtich ** it. If not we will just fall through release the I/O database MUTEX and and ** return with the error. ** */ if (status & SS$_NORMAL) { fork_lock(asnucb->ASNBASE.ucb$b_flck, &saved_fipl); if ((void *) phyucb->ucb$l_tt_logucb == (void *) phyucb) { device_lock(phyucb->PHYBASE.ucb$l_dlck, RAISE_IPL, &saved_dipl); /* ** ** We need to block out device interrupts to do this check if the ** line is set to modem then the remote bit must be set. ** */ if (phyucb->PHYBASE.ucb$l_devdepend & TT$M_MODEM) { if ((phyucb->ucb$b_tt_ds_rcv & (TT$M_DS_DSR | TT$M_DS_CTS | TT$M_DS_CARRIER))) { phyucb->ucb$w_tt_ds_tim = 0; } else { status = SS$_DEVREQERR; } } if (status & SS$_NORMAL) { /* ** ** Save away PORT CONTROL flags. They are going to be ** altered and the original values will need to be restored ** when the line is given back to TTDRIVER. ** */ asnucb->ASNLOG.ucb$l_tl_outband = phyucb->ucb$w_tt_prtctl; asnucb->ASNLOG.ucb$l_tl_posix_data = (void *) phyucb->PHYBASE.ucb$l_ddt; phyucb->PHYBASE.ucb$l_ddt = &asndummy_ddt; /* ** ** Now switch from TT to ASN and point at ASN UCB. ** */ asnucb->ASNLOG.ucb$l_tl_phyucb = (UCB *) phyucb; asnucb->ASNBASE.ucb$l_dlck = phyucb->PHYBASE.ucb$l_dlck; asnucb->ASNBASE.ucb$b_dipl = phyucb->PHYBASE.ucb$b_dipl; asnucb->ucb$l_asn_flags = UCB$M_ASN_DROP_CHARS | UCB$M_ASN_INPUT_DISABLED; asnucb->ucb$l_asn_rcv_state = RCV_OFF; phyucb->ucb$l_tt_logucb = (UCB *) asnucb; phyucb->PHYBASE.ucb$v_online = 0; phyucb->PHYBASE.ucb$v_tim = 0; phyucb->PHYBASE.ucb$l_devchar = phyucb->PHYBASE.ucb$l_devchar &~DEV$M_AVL; phyucb->PHYBASE.ucb$l_devchar2 = phyucb->PHYBASE.ucb$l_devchar2 | DEV$M_RED; phyucb->ucb$l_tt_state1 = 0; phyucb->ucb$l_tt_state2 = 0; if ((void *) phyucb->ucb$l_tt_typahd != (void *) 0) { com_std$drvdealmem(phyucb->ucb$l_tt_typahd); } phyucb->ucb$l_tt_typahd = 0; phyucb->ucb$l_tt_class = (int) &CLASS_VECTOR; phyucb->ucb$l_tt_getnxt = CLASS_VECTOR.class_getnxt; phyucb->ucb$l_tt_putnxt = CLASS_VECTOR.class_putnxt; phyucb->ucb$w_tt_prtctl = phyucb->ucb$w_tt_prtctl | TTY$M_PC_NOTIME; status = SS$_NORMAL; } else { phyucb->PHYBASE.ucb$l_refc -= 1; if (phyucb->PHYBASE.ucb$l_refc < 0) { phyucb->PHYBASE.ucb$l_refc = 0; } } device_unlock(phyucb->PHYBASE.ucb$l_dlck, saved_dipl, SMP_RESTORE); } else { phyucb->PHYBASE.ucb$l_refc -= 1; if (phyucb->PHYBASE.ucb$l_refc < 0) { phyucb->PHYBASE.ucb$l_refc = 0; } status = SS$_DEVALLOC; } fork_unlock(asnucb->ASNBASE.ucb$b_flck, saved_fipl, SMP_RESTORE); } sch_std$iounlock(CTL$GL_PCB); return status; }