#pragma module pppd$terminal "X-5" /* ***************************************************************************** * * 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 class driver emulation routines. They are: class$getnext - Class get next routine class$putnxt - Class put next routine class$setupucb - Class setup UCB routine class$data_set_trans - Class data set transition routine class$read_error - Class read error routine class$disconnect - Class disconnect routine class$fork - Class fork routine class$powerfail - Class power fail routine AUTHOR: Forrest A. Kenney 01-March-1996 REVISION HISTORY: X-5 FAK004 Forrest A. Kenney 04-March-1997 There are three places in the class driver emulation layer that make an attempt to see if the UCB we read is the ASN UCB. They are doing the check incorrectly they are checking against 0. What they should check against is if the logical UCB pointer in the physical UCB points to itself. As part of changing the test I am adding a bugcheck TTDRVR1 if the logical UCB pointer points to the physical. The idea here is to crash as soon as we detect a problem instead of hiding it. In the future we may want to just silenty ignore these error. X-4 FAK003 Forrest A. Kenney 04-November-1996 We are now going to get a write buffer when we start a write and free it when a write is completed. This allows us to clear the write flag when we abort a write or when it is done. We loose a little performance in the worse case allocating and deallocating pool but we simplify the logic for writes. X-3 FAK002 Forrest A. Kenney 03-October-1996 Do not clear the writing state when the write done fork is queued. It will be cleared when the write completion fork runs. X-2 FAK001 Forrest A. Kenney 18-July-1996 Remove MRU check in class$putnext. */ /* 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 attribute 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 /* Terminal attributes TT3$xx */ #include /* More terminal defintions */ #include /* Modem states */ #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*/ #include /* C definitions for common VMS MACROS */ /* Define the DEC C functions used by this driver */ #include /* OpenVMS AXP specific C builtin functions */ #include /* String routines provided by "kernel CRTL" */ /* **++ ** class$getnext - Class get next routine ** ** Functional description: ** ** This routine returns with the next characters to be output on the unit. ** It is called whenever the terminal port driver has completed the current ** character or burst. If data is returned by CLASS_GETNXT, a timer is set up ** (unless explicitly disabled) and the interrupt expected bit is set. ** ** Calling convention: ** ** unsigned char class$getnext (TTY_UCB *phyucb) ** ** Inputs: ** ** R5 - UCB address ** ** Outputs: ** ** R5 - UCB address ** UCB$B_TT_OUTYPE - Zero - no data to be output ** One - single character in R3 ** Negative - output a burst ** Burst mode only: ** UCB$L_TT_OUTADR - Address of the first character of the burst ** UCB$W_TT_OUTLEN - Length of the burst ** ** R0 - preserved All others destroyed. ** ** Returns: ** ** None ** ** Environment: ** ** Called at device IPL holding the device spinlock. Exit at calling ** IPL with locks still held ** **-- */ unsigned char class$getnext(TTY_UCB *phyucb) { extern EXE$GL_ABSTIM; int length; unsigned char out_char; ASNUCB *asnucb; ASNWRT *asnwrt; /* ** ** Start out assuming nothing to output ** */ out_char = 0; phyucb->ucb$b_tt_outype = 0; if ((!phyucb->PHYBASE.ucb$v_int) && (!phyucb->tty$v_st_ctrls) && (!phyucb->tty$v_st_lostcts)) { do { if (phyucb->tty$v_st_multi) { /* ** ** We have a special message to send out. This string can ** interrupt a write. ** */ if (phyucb->ucb$w_tt_multilen > 0) { /* ** ** Send out a single character and fall out to code to compute ** character timeout. */ phyucb->ucb$w_tt_outlen = 1; out_char = *((char *) phyucb->ucb$l_tt_multi); phyucb->ucb$w_tt_multilen -= 1; phyucb->ucb$l_tt_multi = (char *)phyucb->ucb$l_tt_multi + 1; phyucb->ucb$b_tt_outype = 1; break; } else { phyucb->tty$v_st_multi = 0; } } else if (phyucb->tty$v_st_write) { /* ** ** If here we are processing a write we can get here a couple of ** ways: ** ** 1) A new write packet to get going ** 2) A packet has been sent and we need to complete it ** ** If we are completing a write we just simply fork on the write ** packet and it will be completed and if another write is ready it ** get back in here to start again. If it is a new packet we will ** simple make the whole packet a big burst and send it in one shot. ** */ asnucb = (ASNUCB *) phyucb->ucb$l_tt_logucb; asnwrt = asnucb->ucb$l_asn_write_buffer; if (asnwrt->asnwrt$ps_data == &asnwrt->asnwrt$t_data) { phyucb->ucb$l_tt_outadr = asnwrt->asnwrt$ps_data; phyucb->ucb$w_tt_outlen = asnwrt->asnwrt$l_bcnt; phyucb->ucb$b_tt_outype = -1; asnwrt->asnwrt$ps_data = (char *) asnwrt->asnwrt$ps_data + asnwrt->asnwrt$l_bcnt; break; } else { asnucb->ucb$q_asn_pkts_sent += 1; phyucb->tty$v_st_write = 0; __ADD_ATOMIC_LONG(&asnucb->ucb$l_asn_fork_cnt, 1); exe_std$queue_fork((FKB *) asnwrt); break; } } else { /* ** ** If get here there is nothing left to do ** */ break; } } while (1); if (phyucb->ucb$b_tt_outype != 0) { /* ** ** If we have any characters to output see if we need to compute ** a timeout interval for the character. Also detrmine what we return ** if a burst or no character return NULL, otherwise return character to ** be output. ** */ if (!phyucb->tty$v_pc_notime) { if (phyucb->ucb$b_tt_outype == 1) { length = 2; } else { length = 2 + (phyucb->ucb$w_tt_outlen / 4); } phyucb->PHYBASE.ucb$l_duetim = length + EXE$GL_ABSTIM; phyucb->PHYBASE.ucb$v_tim = 1; } phyucb->PHYBASE.ucb$v_int = 1; } } return out_char; } /* **++ ** class$putnxt - Class put next routine ** ** Functional description: ** ** This routine is called by port drivers to pass input characters. ** Characters received are filtered for immediate control sequences. ** ** ** Calling convention: ** ** unsigned char class$putnext (unsigned char read_character, ** TTY_UCB *phyucb) ** ** Input parameters: ** ** R3 - Input character ** R5 - UCB address ** ** Output parameters: ** ** R5 - UCB address ** UCB$B_TT_OUTYPE - Zero - no data to be output ** One - single character in R3 ** Negative - output a burst ** Burst mode only: ** UCB$L_TT_OUTADR - Address of the first character of the burst ** UCB$W_TT_OUTLEN - Length of the burst ** ** R0 - preserved All others destroyed. ** ** Return value: ** ** None ** ** Environment: ** ** Called at device IPL holding the device spinlock. Exit at calling IPL ** with locks still held ** **-- */ unsigned char class$putnext(unsigned char read_character, TTY_UCB *phyucb) { unsigned char out_char; int length; int status; ASNUCB *asnucb; ASNRD *asnrd; asnucb = (ASNUCB *) phyucb->ucb$l_tt_logucb; asnrd = (ASNRD *) phyucb->ucb$l_tt_typahd; asnucb->ucb$q_asn_total_chars += 1; /* ** ** Lets see if this is a special character these are not counted as ** dropped even though they are not placed in the data buffer. ** */ if ((read_character <= 32) && (asnucb->ucb$l_asn_rcv_accm & (1 << read_character))) { /* ** ** Now see if it is an XON or an XOFF character if so and we are doing ** software flow control then either stop or start the output stream. ** */ if (asnucb->ucb$l_asn_flow == ASN$M_XON_XOFF) { if (read_character == 0x11) { /* ** ** XON so clear XOFF for output state, tell port to resume. Then ** call and see if we need to call the port driver with more data. ** */ phyucb->tty$v_st_ctrls = 0; port$resume(phyucb); out_char = class$getnext(phyucb); if (phyucb->ucb$b_tt_outype != 0) { port$startio(out_char, phyucb); } } else if (read_character == 0x13) { /* ** ** XOFF set state to XOFF and tell port driver to stop sending ** output. ** */ phyucb->tty$v_st_ctrls = 1; port$stop(phyucb); } } } else if ((void *) phyucb->ucb$l_tt_typahd != (void *) 0) { length = (char *)asnrd->asnrd$l_putptr - (char *) &asnrd->asnrd$t_frame; if ((read_character == 0x7e) && (length == 0)) { /* ** ** If character is a frame character and there is no data read ** then we either have a start of frame, or back to back frame ** characters. In both of these cases we can just set the frame seen ** character and clear the drop flag. ** */ asnucb->ucb$v_asn_frame_seen = 1; asnucb->ucb$v_asn_drop_chars = 0; } else if ((read_character == 0x7e) && (length > 0)) { /* ** We have a frame mark and data in hand so we can just complete ** this read and try to start another one. If the packet is bogus the ** the code to remove quoting, or the FCS check will catch it. ** ** We will try to get another read packet if we fail then we will ** try to tell the other end to stop sending data. If we fail the data ** will end up in the bit bucket and a whole frame can get dropped. ** */ asnucb->ucb$q_asn_pkts_rcv += 1; __ADD_ATOMIC_LONG(&asnucb->ucb$l_asn_fork_cnt, 1); asnrd->asnrd$l_status = SS$_NORMAL; asnrd->asnrd$l_fpc = asn$read_done; exe_std$queue_fork((FKB *) asnrd); status = __PAL_REMQUEL((void *) asnucb->ucb$l_asn_readq_fl, (void *) &asnrd); if (status == -1) { phyucb->ucb$l_tt_typahd = (void *) 0; asn$input_stop(asnucb); } else { phyucb->ucb$l_tt_typahd = (void *) asnrd; } } else if (!asnucb->ucb$v_asn_drop_chars) { /* ** ** If the buffer is full complete the request set the drop flag, ** and try to get another read buffer. The drop flag is set so we drop ** characters we got in a frame that is to large. This just prevents us ** from producing frames with apparent bad checksums that are really ** packtes that are to large. ** ** We have a character to insert into the buffer save it. We do ** no check for overflow as we could get and END of FRAME character and ** that would be OK. We only want to count frames as to long if the ** really exceed maximum size. ** */ if ((void *) asnrd->asnrd$l_putptr > (void *) asnrd->asnrd$l_end_frame) { asnucb->ucb$q_asn_dropped_chars += 1; asnucb->ucb$q_asn_long_pkts += 1; __ADD_ATOMIC_LONG(&asnucb->ucb$l_asn_fork_cnt, 1); asnrd->asnrd$l_status = SS$_TOOMUCHDATA; exe_std$queue_fork((FKB *) asnrd); status = __PAL_REMQUEL((void *) asnucb->ucb$l_asn_readq_fl, (void *) &asnrd); if (status == -1) { phyucb->ucb$l_tt_typahd = (void *) 0; asn$input_stop(asnucb); } else { phyucb->ucb$l_tt_typahd = (void *) asnrd; } } else { /* ** ** Save the character and bump the put pointer. ** */ *((unsigned char *) asnrd->asnrd$l_putptr) = read_character; asnrd->asnrd$l_putptr = (void *) ((char *) asnrd->asnrd$l_putptr + 1); } } else { /* ** ** Just drop the character and add it to the count of dropped ** characters. ** */ asnucb->ucb$q_asn_dropped_chars += 1; } } else { /* ** ** There is no read buffer so drop the character and count it as a lost ** character. ** */ asnucb->ucb$q_asn_dropped_chars += 1; } phyucb->ucb$b_tt_outype = 0; return 0; } /* **++ ** class$setupucb - Class setup UCB routine ** ** Functional description: ** ** This routine is called at unit init during both system startup and ** power failure. ** ** ** Calling convention: ** ** void class$setupucb (TTY_UCB *phyucb) ** ** Input parameters: ** ** R5 - UCB address ** ** Output parameters: ** ** None ** ** Return value: ** ** None ** ** Environment: ** ** Called at device IPL holding the device spinlock. Exit at calling IPL ** with locks still held ** **-- */ void class$setupucb(TTY_UCB *phyucb) { if (phyucb->PHYBASE.ucb$l_refc == 0) { phyucb->PHYLOG.ucb$l_tl_phyucb = (UCB *) phyucb; phyucb->PHYBASE.ucb$l_devchar = phyucb->PHYBASE.ucb$l_devchar | DEV$M_AVL & ~DEV$M_RED; phyucb->ucb$w_tt_speed = phyucb->ucb$w_tt_despee; phyucb->ucb$b_tt_parity = phyucb->ucb$b_tt_depari; phyucb->PHYBASE.ucb$b_devtype = phyucb->ucb$b_tt_detype; phyucb->PHYBASE.ucb$w_devbufsiz = phyucb->ucb$w_tt_desize; phyucb->PHYBASE.ucb$l_devdepnd3 = phyucb->PHYBASE.ucb$l_devdepnd3 & ~TT3$M_POSIX; phyucb->ucb$l_tt_decha2 = phyucb->ucb$l_tt_decha2 & ~TT3$M_POSIX; phyucb->PHYBASE.ucb$l_devdepnd4 = 0; if (phyucb->PHYBASE.ucb$l_devdepend & TT$M_REMOTE) { phyucb->PHYBASE.ucb$l_devdepend = phyucb->ucb$l_tt_dechar | TT$M_REMOTE; } else { phyucb->PHYBASE.ucb$l_devdepend = phyucb->ucb$l_tt_dechar; } phyucb->PHYBASE.ucb$l_devdepnd2 = phyucb->ucb$l_tt_decha1; phyucb->ucb$w_tt_prtctl = phyucb->ucb$w_tt_prtctl & ~TTY$M_PC_BREAK; if ((void *)phyucb->ucb$l_tt_wflink == (void *) 0) { phyucb->ucb$l_tt_wflink = (TWP *)&phyucb->ucb$l_tt_wflink; phyucb->ucb$l_tt_wblink = (TWP *)&phyucb->ucb$l_tt_wflink; } } return; } /* **++ ** class$data_set_trans - Class data set transition routine ** ** Functional description: ** ** This routine handles data set state transitions. The inputs to ** CLASS_DS_TRANS include a type code indicating what type of transition this ** is. If it is a transition of modem signals, the changed signals are also ** provided. ** ** It is important to note that this routine should be called with the ** MODEM$C_INIT transition type from the unit init routine of the port driver ** if the unit is capable of having data set transitions. ** ** Calling convention: ** ** void class$data_set_trans (int modem_event, int modem_signals, ** TTY_UCB *phyucb) ** ** Input parameters: ** ** R1 - Transition type (one of the following) ** MODEM$C_INIT Initialize modem control ** MODEM$C_INIT_NORESET Start modem protocol but don't init ** signals ** MODEM$C_SHUTDWN Shut down the line and disconnect the ** process ** MODEM$C_SHUTDWN_NOHANGUP Stop modem protocol but don't stop the ** signals ** MODEM$C_DATASET Data set signal changes ** R2 - Type specific argument ** DATASET - new receive modem mask ** R5 - UCB address ** ** Output parameters: ** ** R0 - R4 Destroyed ** ** Return value: ** ** None ** ** Environment: ** ** Kernel mode, system context, device IPL with device lock held. ** **-- */ void class$data_set_trans(int modem_event, int modem_signals, TTY_UCB *phyucb) { unsigned char out_char; if ((phyucb->PHYBASE.ucb$l_devdepend & TT$M_MODEM) || (phyucb->PHYBASE.ucb$l_devdepnd2 & TT2$M_COMMSYNC)) { if (modem_event == MODEM$C_SHUTDWN) { /* ** ** Tear down the connection. ** */ class$disconnect(phyucb); } else if (modem_event == MODEM$C_DATASET) { if ((!(modem_signals & TT$M_DS_CARRIER)) || (!(modem_signals & TT$M_DS_DSR))) { /* ** ** Lost CD or DSR hang up the line. Should really wait 2 seconds ** for lost CD but for time being treat lost CD as instantly fatal. ** */ class$disconnect(phyucb); } else if (!(modem_signals & TT$M_DS_CTS)) { /* ** ** Lost CTS see if need to stop output. ** */ if (!phyucb->tty$v_st_lostcts) { phyucb->tty$v_st_lostcts = 1; port$stop(phyucb); } } else if (modem_signals & TT$M_DS_CTS) { /* ** ** Have CTS if state is lost CTS then clear it and restart output. ** */ if (phyucb->tty$v_st_lostcts) { phyucb->tty$v_st_lostcts = 0; port$resume(phyucb); out_char = class$getnext(phyucb); if (phyucb->ucb$b_tt_outype != 0) { port$startio(out_char, phyucb); } } } } } phyucb->ucb$b_tt_ds_rcv = (unsigned char)modem_signals & 0xff; return; } /* **++ ** class$read_error - Class read error routine ** ** Functional description: ** ** CLASS_READERROR is called when the terminal port driver detects a ** parity, data overrun or framing error on the terminal line. CLASS_READERROR ** increments the appropriate error counter and just returns. ** ** Note: ** Partity errors are counted as framing errors. The line must be ** no parity. Thus a parity error really means a framing error occured. ** ** Calling convention: ** ** unsigned char class$read_error (unsigned int read_character, ** TTY_UCB *phyucb) ** ** Input parameters: ** ** R3 - Character and flags: ** bit 12 - parity error on the given character ** bit 13 - Framing error on the given character ** bit 14 - data overrun. ** ** R5 - UCB address ** ** Output parameters: ** ** R0 - R3 are destroyed ** UCB$B_TT_OUTYPE - 0 if no character to output ** 1 if valid character to output ** ** Return value: ** ** None ** ** Environment: ** ** Called at device IPL holding the device spinlock. Exit at calling IPL ** with locks still held ** **-- */ unsigned char class$read_error(unsigned int read_character, TTY_UCB *phyucb) { int original_ipl; ASNUCB *asnucb; asnucb = (ASNUCB *) phyucb->ucb$l_tt_logucb; if ((void *) asnucb != (void *) phyucb) { if (!(asnucb->ucb$v_asn_delpend)) { asnucb->ucb$q_asn_total_chars += 1; if ((read_character >> TERMINAL_PE_BIT) & 1) { asnucb->ucb$q_asn_fe += 1; } if ((read_character >> TERMINAL_FE_BIT) & 1) { asnucb->ucb$q_asn_fe += 1; } if ((read_character >> TERMINAL_OR_BIT) & 1) { asnucb->ucb$q_asn_data_lost += 1; } } } else { bug_check(TTDRVR1, FATAL, WARM); } phyucb->ucb$b_tt_outype = 0; return 0; } /* **++ ** class$disconnect - Class disconnect routine ** ** Functional description: ** ** The port driver calls CLASS_DISCONNECT to indicate to the terminal ** class driver that the terminal is no longer connected to the system. This ** is the preferred way of disconnecting a process from a terminal on a ** non-modem line. ** ** Calling convention: ** ** void class$disconnect (TTY_UCB *phyucb) ** ** Input parameters: ** ** R5 - UCB address ** ** Output parameters: ** ** R4 Destroyed ** ** Return value: ** ** None ** ** Environment: ** ** Called at device IPL holding the device spinlock. Exit at calling IPL ** with locks still held ** **-- */ void class$disconnect(TTY_UCB *phyucb) { int original_ipl; uint64 fr3; uint64 fr4; ASNUCB *asnucb; asnucb = (ASNUCB *) phyucb->ucb$l_tt_logucb; if ((void *) asnucb != (void *) phyucb) { if (!(asnucb->ucb$v_asn_delpend)) { fr4 = asnucb->ASNBASE.ucb$q_fr4; if (!(fr4 & TTY$M_FD_BUSY)) { fr3 = asnucb->ASNBASE.ucb$q_fr3; fr4 = fr4 | TTY$M_FD_DISCONNECT | TTY$M_FD_BUSY; fork(asn$fork_dispatch, fr3, fr4, (FKB *) asnucb); } else { asnucb->ASNBASE.ucb$q_fr4 = fr4 | TTY$M_FD_DISCONNECT; } } } else { bug_check(TTDRVR1, FATAL, WARM); } return; } /* **++ ** class$fork - Class fork routine ** ** Functional description: ** ** CLASS_FORK is the routine a port driver calls if it needs to start a ** driver fork process that would normally use the UCB. The port driver must ** never initiate a fork directly -- it must always call this routine. ** CLASS_FORK, using the UCB, will set up the fork block and follow other ** necessary protocol on the port driver's behalf. When the fork has taken ** place, the class driver will call the port driver back at the port service ** routine PORT_FORKRET. ** ** Calling convention: ** ** void class$fork (TTY_UCB *phyucb) ** ** Input parameters: ** ** R5 - UCB address ** ** Output parameters: ** ** R4 Destroyed ** ** Return value: ** ** None ** ** Environment: ** ** Called at device IPL holding the device spinlock. Exit at calling IPL ** with locks still held ** **-- */ void class$fork(TTY_UCB *phyucb) { int original_ipl; uint64 fr3; uint64 fr4; ASNUCB *asnucb; asnucb = (ASNUCB *) phyucb->ucb$l_tt_logucb; if ((void *) asnucb != (void *) phyucb) { if (!(asnucb->ucb$v_asn_delpend)) { fr4 = asnucb->ASNBASE.ucb$q_fr4; if (!(fr4 & TTY$M_FD_BUSY)) { fr3 = asnucb->ASNBASE.ucb$q_fr3; fr4 = fr4 | TTY$M_FD_PORTFORK | TTY$M_FD_BUSY; fork(asn$fork_dispatch, fr3, fr4, (FKB *) asnucb); } else { asnucb->ASNBASE.ucb$q_fr4 = fr4 | TTY$M_FD_PORTFORK; } } } else { bug_check(TTDRVR1, FATAL, WARM); } return; } /* **++ ** class$powerfail - Class power fail routine ** ** Functional description: ** ** This routine is called the port driver's unit init routine when a ** powerfail is detected. ** ** Calling convention: ** ** void class$powerfail (TTY_UCB *phyucb) ** ** Input parameters: ** ** R5 - UCB address ** ** Output parameters: ** ** All registers are preserved ** UCB$W_STS UCB$V_INT is cleared ** UCB$V_TIM is set ** UCB$L_DUETIM is cleared ** ** Return value: ** ** None ** ** Environment: ** ** Kernel mode, system context, device IPL with device lock held. ** **-- */ void class$powerfail(TTY_UCB *phyucb) { return; }