#pragma module pppd$asnmisc "X-10" /* ***************************************************************************** * * 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 number of misceallanous routines used by the driver. They are: asn$cancel_active_io - Cancels active read and write if any are active. asn$cancel_all_io - Kills off all pending and current I/O requests asn$delete_ucb - Delete ASN UCB asn$do_sense_item - Get value for specified parameter asn$do_set_item - Set specified parameter asn$enable_receive - Allow input to start asn$fcs_check - Check received packets FCS is valid asn$fcs_compute - Compute the FCS and save it in buffer asn$fcs16_compute - Compute 16 bit FCS asn$fcs32_compute - Compute 32 bit FCS asn$fork_dispatch - Dispatch various fork requests asn$input_resume - Start reception of chracters asn$input_stop - Stop receipt of chracters asn$move_sense_data - Copy data from system data area to user bufer asn$prepare_for_delete - Get device ready to be deleted asn$quote - Quote a buffer asn$unquote - Remove quoting from a buffer asn$write_start - Start a write request AUTHOR: Forrest A. Kenney 13-January-1996 REVISION HISTORY: X-10 FAK009 Forrest A. Kenney 17-march-1997 Remove extra fork lock and unlock from X-9 FAK008 Forrest A. Kenney 26-February-1997 In asn$write_start change exe_std$allocbuf to exe_std$alononpaged, exe_std$allocbuf sets IPL 2 not a good idea when the routine is called at IPL 8. X-8 FAK007 Forrest A. Kenney 30-December-1996 In the asn$write_start add comment to explain the addition of 2 to the size of the packet to be sent. Also add code to delete the write buffer if the quote operation should fail. Note at the present time this should be impossible as the buffer is always allocated to be large enough to have every character quoted. BWK001 Barry W. Kierstein 17-December-1996 Replaced the standard Digital copyright with one compatible with the CMU copyright. X-7 FAK006 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. 1) asn$write_start add code to allocate the write buffer. 2) asn$delete_ucb do not delete write buffer 3) asn$cancel_active_io clear the writing state flag X-6 FAK005 Forrest A. Kenney 03-October-1996 Do not clear the writing state when aborting the write. It will be cleared when the write completion fork runs. X-5 FAK004 Forrest A. Kenney 17-July-1996 Remove infinite loop from the asn$fork_dispatch routine. X-4 FAK003 Forrest A. Kenney 13-June-1996 Return correct status when setting transmit ACCM. X-3 FAK002 Forrest A. Kenney 24-April-1996 Fix up some problems found during a code inspection. a) Comment in cancel_all_io about IPL was incorrect b) Treated IRP as VCRP and VCRP as IRP in cancel_all_io c) In asn$do_set_item for the disconnect call set return status to normal always. d) In asn$do_set_item and asn$do_sense_item fix broken attempt to make sure we only got the bits we expected. e) In asn$do_sense_item was passing was passing counter instead of address of counter. X-2 FAK001 Forrest A. Kenney 12-April-1996 Fix a number of bugs found in testing. a) Reset counters on a set was using wrong item length. b) In the reset counters set return status to success it will be changed if there is an error. */ /* Define system data structure types and constants */ #include /* AST control block definitions */ #include /* Channel Control Block definitons */ #include /* Controller Request Block definitons */ #include /* DEscriptor definitions */ #include /* Device data block definitions */ #include /* Driver Dispatch table definitions */ #include /* Data structure type definitions */ #include /* Fork block definitions */ #include /* I/O database constants */ #include /* Interrupt Dispatch Block Definitions */ #include /* interger definitions */ #include /* IPL definitions */ #include /* I/O Request Packet definitions */ #include /* Object rights Block definitions */ #include /* Process Control Block definitions */ #include /* Spinlock definitions */ #include /* Status return valuse */ #include /* Terminal definitions TT$xxx */ #include /* TTY symbols private to class driver */ #include /* TTY UCB offsets */ #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_fcstab.h" /* FSC lookup tables */ #include "pppd$asn_hide_ptrs.h" /* Hide long paths to items */ #include "pppd$asn_linkages.h" /* JSR register linkages */ #include "pppd$asn_msgtbl.h" /* Abort packet message */ #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" */ /* **++ ** asn$cancel_active_io - Cancels active read and write if any are active. ** ** Functional description: ** ** This routine will cancel the active read and write requests. ** ** Calling convention: ** ** void asn$cancel_active_io (ASNUCB *asnucb) ** ** Input parameters: ** ** asnucb Address of the ASN UCB ** ** Output parameters: ** ** None ** ** Return value: ** ** None ** ** Environment: ** ** Kernel mode IPL 8 fork thread with the fork lock held. The device lock ** is acquired and released. ** **-- */ void asn$cancel_active_io(ASNUCB *asnucb) { unsigned char out_char; int saved_dipl; ASNRD *asnrd; ASNWRT *asnwrt; TTY_UCB *phyucb; asnrd = 0; asnwrt = 0; device_lock(asnucb->ASNBASE.ucb$l_dlck, RAISE_IPL, &saved_dipl); phyucb = (TTY_UCB *) asnucb->ASNLOG.ucb$l_tl_phyucb; if ((void *) phyucb != (void *) 0) { if (phyucb->tty$v_st_write) { asnwrt = (ASNWRT *) asnucb->ucb$l_asn_write_buffer; asnwrt->asnwrt$l_status = SS$_ABORT; phyucb->tty$v_st_write = 0; __ADD_ATOMIC_LONG(&asnucb->ucb$l_asn_fork_cnt, 1); exe_std$queue_fork((FKB *) asnwrt); port$abort(phyucb); port$resume(phyucb); phyucb->ucb$w_tt_multilen = strlen(asn$abort_string); phyucb->ucb$l_tt_multi = asn$abort_string; phyucb->tty$v_st_multi = 1; out_char = class$getnext(phyucb); if (phyucb->ucb$b_tt_outype != 0) { port$startio(out_char, phyucb); } } asn$input_stop(asnucb); if (phyucb->tty$v_st_read) { asnucb->ucb$v_asn_drop_chars = 1; asnrd = (ASNRD *) phyucb->ucb$l_tt_typahd; if ((void *) asnrd != (void *) 0) { phyucb->ucb$l_tt_typahd = 0; asnrd->asnrd$l_status = SS$_ABORT; asnrd->asnrd$l_fpc = asn$read_done; __ADD_ATOMIC_LONG(&asnucb->ucb$l_asn_fork_cnt, 1); exe_std$queue_fork((FKB *) asnrd); } } } device_unlock(asnucb->ASNBASE.ucb$l_dlck, saved_dipl, SMP_RESTORE); return; } /* **++ ** asn$cancel_all_io - Kills off all pending and current I/O requests ** ** Functional description: ** ** This routine will unconditionally cancel all I/Os that are queued or on ** going. It does the following: ** ** 1) Walks the queue of pending writes and aborts them. ** 2) Walks the queue of pending reads and aborts them. ** 3) Call asn$cancel_active_io to abort current read and write ** ** Calling convention: ** ** void asn$cancel_all_io (ASNUCB *asnucb) ** ** Input parameters: ** ** UCB Pointer to the ASN UCB ** ** Output parameters: ** ** None ** ** Return value: ** ** None ** ** Environment: ** ** Kernel mode, system context, fork IPL with fork lock held. ** **-- */ void asn$cancel_all_io(ASNUCB *asnucb) { int saved_fipl; int status; IRP *irp; TTY_UCB *phyucb; ASNVCIB *asnvcib; VCRP *vcrp; asnvcib = (ASNVCIB *) asnucb->ucb$l_asn_vcib; /* ** ** Clear the queue of pending writes ** */ while ((void *) &asnucb->ucb$l_asn_writeq_fl != (void *) asnucb->ucb$l_asn_writeq_bl) { status = __PAL_REMQUEL((void *) asnucb->ucb$l_asn_writeq_fl, (void *) &irp); if (status >= 0) { if (irp->irp$b_type == DYN$C_IRP) { irp->irp$l_iost1 = SS$_CANCEL; irp->irp$l_iost2 = 0; com_std$post_nocnt(irp); } else { vcrp = (VCRP *) irp; vcrp->vcrp$q_request_status = SS$_CANCEL; VCI$XXX_Transmit_Complete(vcrp, asnvcib); } } } /* ** ** Flush the queue of pending read I/O request. In normal operation this ** should never really be needed. ** */ while ((void *) &asnucb->ASNBASE.ucb$l_ioqfl != (void *) asnucb->ASNBASE.ucb$l_ioqbl) { status = __PAL_REMQUEL((void *) asnucb->ASNBASE.ucb$l_ioqfl, (void *) &irp); if (status >= 0) { irp->irp$l_iost1 = SS$_CANCEL; irp->irp$l_iost2 = 0; com_std$post_nocnt(irp); } } asn$cancel_active_io(asnucb); } /* **++ ** asn$delete_ucb - Delete ASN UCB ** ** Functional description: ** ** The delete device routine is called from the fork dispatcher and is ** responsible for the actual deletion of the UCB. This routine is called ** holding the fork and must acquire the I/O database mutex for write access ** before doing any significant processing. If the mutex cannot be accessed, ** the fork must be rescheduled to try again in one second. Once the routine ** acquires the mutex it performs a series of tests to make sure the device ** is truly idle and can be deleted. If any of the tests fails the process ** will be started again in one second. ** ** Calling convention: ** ** void asn$delete_ucb (uint64 fr3, ASNUCB *asnucb, FKB *asnfkb) ** ** Input parameters: ** ** R3 zero ** R4 pointer to ASN UCB ** R5 pointer to fork block ** ** Output parameters: ** ** None ** ** Return value: ** ** None ** ** Environment: ** ** This routine is called holding the fork lock; it acquires the I/O ** database mutex. ** **-- */ void asn$delete_ucb(uint64 fr3, ASNUCB *asnucb, FKB *asnfkb) { int32 buffer_size; int saved_pid; int saved_quota; int status; void *packet; ACB *acb; PCB *pcb; extern IOC$GQ_MUTEX; if (asnucb->ASNBASE.ucb$w_unit != 0) { status = sch_std$lockwexec_quad((MUTEX *) &IOC$GQ_MUTEX); if (status & SS$_NORMAL) { if ((asnucb->ASNBASE.ucb$l_refc == 0) && (asnucb->ASNBASE.ucb$q_fr4 == 0) && ((void *) asnucb->ucb$l_asn_vcib == (void *) 0) && (asnucb->ucb$l_asn_fork_cnt == 0)) { if ((void *) asnucb->ASNLOG.ucb$l_tl_phyucb != (void *) 0) { dyn$revert(asnucb); } asnucb->ASNBASE.ucb$v_deleteucb = 1; /* ** ** Clean queue of free read packets and delete them ** */ while ((void *) &asnucb->ucb$l_asn_readq_fl != (void *) asnucb->ucb$l_asn_readq_fl) { status = __PAL_REMQUEL((void *) asnucb->ucb$l_asn_readq_fl, (void *) &packet); if (status >= 0) { exe_std$deanonpaged(packet); } } /* ** ** Clean queue of filled read packets and delete them ** */ while ((void *) &asnucb->ucb$l_asn_packetq_fl != (void *) asnucb->ucb$l_asn_packetq_fl) { status = __PAL_REMQUEL((void *) asnucb->ucb$l_asn_packetq_fl, (void *) &packet); if (status >= 0) { exe_std$deanonpaged(packet); } } /* ** ** Now save away the quota and PID of process that created the UCB. ** Then allocate a ACB and fill it in. If cannot get an ACB then ** quota will not be returned. Then queue the AST to the procees who ** created the UCB. If the process is gone the the queue of the AST ** will delete the ACB. Finally delete the UCB. ** */ saved_pid = asnucb->ASNBASE.ucb$l_cpid; saved_quota = asnucb->ASNBASE.ucb$w_charge; status = exe_std$alononpaged(ACB$K_LENGTH, &buffer_size, (void *) &acb); if (status & SS$_NORMAL) { acb->acb$w_size = buffer_size; acb->acb$b_type = DYN$C_ACB; acb->acb$b_rmod = ACB$M_KAST; acb->acb$l_pid = saved_pid; acb->acb$l_astprm = saved_quota; acb->acb$l_kast = asn$return_quota; status = sch_std$qast(0, acb, (PCB *) &pcb); } ioc_std$delete_ucb((UCB *) asnucb); sch_std$unlockexec_quad((MUTEX *) &IOC$GQ_MUTEX); } else { sch_std$unlockexec_quad((MUTEX *) &IOC$GQ_MUTEX); fork_wait(asn$delete_ucb, 0, (uint64) asnucb, (FKB *) &asnucb->ucb$l_asn_fqfl); } } else { fork_wait(asn$delete_ucb, 0, (uint64) asnucb, (FKB *) &asnucb->ucb$l_asn_fqfl); } } } /* **++ ** ** Local kernel AST routine to return quota back to process that created ** the pseudo terminal. ** ** Inputs: ** ** ACB pointer to AST control block ** ** Outputs: ** ** None ** ** Returns: ** ** None ** ** Environment: ** ** Kernel mode IPL 2 ** **-- */ void asn$return_quota(PCB *pcb, ACB *acb) { exe_std$credit_bytcnt_bytlm(acb->acb$l_astprm, pcb); exe_std$deanonpaged(acb); } /* **++ ** asn$do_sense_item - Get value for specified parameter ** ** Functional description: ** ** This routine handles the determining what item to get data for and ** getting that data It take a pointer to the itemlist item that is to be ** handled. It also takes a flag that indicates if it is being called from a ** trusted mode or not. This routine is common code for both the FDT and VCI ** set code. The VCI call is assumed to be trusted. ** ** Calling convention: ** ** int asn$do_sense_item (ASNUCB *asnucb, LSTITM *item, int trusted_flag) ** ** Input parameters: ** ** ASNUCB Pointer to ASN UCB ** itemlist_item Pointer to the itemlist item to be processed ** trusted Flag that indicates that the call is trusted or not ** ** Output parameters: ** ** Various fields in the UCB may be changed. ** ** Return value: ** ** SS$_NORMAL Call completed successfuly ** SS$_ACCVIO Could not access the buffer ** SS$_BADPARAM Parameter was invalid ** SS$_DEVINACT Device is not in a state where the request can be ** honored ** Others ** ** Environment: ** ** Kernel mode, user process context, IPL 2, or as an IPL 8 fork thread ** During execution IPL may be as high as DEVICE IPL and both the device ** and the fork lock may be held. ** **-- */ int asn$do_sense_item(ASNUCB *asnucb, LSTITM *item, int trusted_flag) { char *out_addr; int byte_count; int item_counter; int mask; int status; __int64 *src_item; switch (item->itm$w_type) { /* ** ** Receive ACCM ** */ case ASN$_ACCM_RCV : if (item->itm$w_length == 4) { status = asn$move_sense_data((char*) &asnucb->ucb$l_asn_rcv_accm, (char *) item->itm$l_retaddr, item->itm$w_length, trusted_flag); } else { status = SS$_BADPARAM; } break; /* ** ** Transmit ACCM ** */ case ASN$_ACCM_XMIT : if (item->itm$w_length == 32) { status = asn$move_sense_data((char *) &asnucb->ucb$l_asn_xmit_accm, (char *) item->itm$l_retaddr, item->itm$w_length, trusted_flag); } else { status = SS$_BADPARAM; } break; /* ** ** Connection type ** */ case ASN$_CONNECT_TYPE : if (item->itm$w_length == 4) { status = asn$move_sense_data((char*) &asnucb->ucb$l_asn_con_type, (char *) item->itm$l_retaddr, item->itm$w_length, trusted_flag); } else { status = SS$_BADPARAM; } break; /* ** ** Receive FCS ** */ case ASN$_FCS_RCV : if (item->itm$w_length == 4) { status = asn$move_sense_data((char*) &asnucb->ucb$l_asn_fcs_rcv, (char *) item->itm$l_retaddr, item->itm$w_length, trusted_flag); } else { status = SS$_BADPARAM; } break; /* ** ** Transmit FCS ** */ case ASN$_FCS_XMIT : if (item->itm$w_length == 4) { status = asn$move_sense_data((char*) &asnucb->ucb$l_asn_fcs_xmit, (char *) item->itm$l_retaddr, item->itm$w_length, trusted_flag); } else { status = SS$_BADPARAM; } break; /* ** ** Type of Flow control ** */ case ASN$_FLOW : if (item->itm$w_length == 4) { status = asn$move_sense_data((char*) &asnucb->ucb$l_asn_flow, (char *) item->itm$l_retaddr, item->itm$w_length, trusted_flag); } else { status = SS$_BADPARAM; } break; /* ** ** Maximum receive packet size ** */ case ASN$_MRU : if (item->itm$w_length == 4) { status = asn$move_sense_data((char*) &asnucb->ucb$l_asn_mru, (char *) item->itm$l_retaddr, item->itm$w_length, trusted_flag); } else { status = SS$_BADPARAM; } break; /* ** ** Maximum tansmit packet size ** */ case ASN$_MTU : if (item->itm$w_length == 4) { status = asn$move_sense_data((char*) &asnucb->ucb$l_asn_mtu, (char *) item->itm$l_retaddr, item->itm$w_length, trusted_flag); } else { status = SS$_BADPARAM; } break; /* ** ** Read counters ** */ case ASN$_READ_COUNTERS : if ((item->itm$w_length >= 8) && ((int) item->itm$l_buffaddr <= ASN$M_ALL)) { byte_count = 0; item_counter = 0; mask = (int) item->itm$l_buffaddr & (ASN$M_ALL | (ASN$M_ALL -1)); out_addr = (char *) item->itm$l_retaddr; src_item = &asnucb->ucb$q_asn_bad_fcs; if (mask & ASN$M_ALL) { mask = ASN$M_ALL - 1; } /* ** ** Now loop through mask of items and pick them up ** */ while (mask) { if (mask & 1) { byte_count += 8; if (byte_count <= item->itm$w_length) { status = asn$move_sense_data((char *) &src_item[item_counter], out_addr, 8, trusted_flag); if (!(status & SS$_NORMAL)) { break; } out_addr += 8; } else { status = SS$_BADPARAM; break; } } mask = mask >> 1; item_counter += 1; } } else { status = SS$_BADPARAM; } break; /* ** ** Default case nothing matched ** */ default : status = SS$_BADPARAM; break; } return status; } /* **++ ** asn$do_set_item - Set specified paramter ** ** Functional description: ** ** This routine handles the actual work of setting the specified item. It ** takes a pointer to the itemlist item that is to be handled. This routine ** is common code for both the FDT and VCI set code. ** ** Calling convention: ** ** int asn$do_set_item (ASNUCB *asnucb, LSTITM *item) ** ** Input parameters: ** ** ASNUCB Pointer to ASN UCB ** itemlist_item Pointer to the itemlist item to be processed ** ** Output parameters: ** ** Various fields in the UCB may be changed. ** ** Return value: ** ** SS$_NORMAL Call completed successfuly ** SS$_ACCVIO Could not access the buffer ** SS$_BADPARAM Parameter was invalid ** SS$_BADCONTEXT Request was made and an IPL that was not valid ** SS$_DEVINACT Device is not in a state where the request can be ** honored ** Others ** ** Environment: ** ** Kernel mode, user process context, IPL 2, or as an IPL 8 fork thread ** During execution IPL may be as high as DEVICE IPL and both the device ** and the fork lock may be held. ** **-- */ int asn$do_set_item(ASNUCB *asnucb, LSTITM *item) { char *devname_ptr; int item_counter; int mask; int saved_dipl; int saved_fipl; int status; __int64 *src_item; struct dsc$descriptor *devname; ASNRD *asnrd; TTY_UCB *phyucb; switch (item->itm$w_type) { /* ** ** Receive ACCM ** */ case ASN$_ACCM_RCV : if (item->itm$w_length == 4) { asnucb->ucb$l_asn_rcv_accm = (unsigned int) item->itm$l_buffaddr; status = SS$_NORMAL; } else { status = SS$_BADPARAM; } break; /* ** ** Transmit ACCM ** */ case ASN$_ACCM_XMIT : if (item->itm$w_length == 32) { memcpy((void *) &asnucb->ucb$l_asn_xmit_accm, (void *) item->itm$l_buffaddr, item->itm$w_length); status = SS$_NORMAL; } else { status = SS$_BADPARAM; } break; /* ** ** Bind to a physical device ** */ case ASN$_BIND : if (item->itm$w_length == 8) { if ((void *)asnucb->ASNLOG.ucb$l_tl_phyucb == (void *) 0) { if (__PAL_MFPR_IPL() == IPL$_ASTDEL) { status = exe_std$prober_dsc((void *)item->itm$l_buffaddr); if (status & SS$_NORMAL) { devname = item->itm$l_buffaddr; status = dyn$switch(asnucb, devname); } } else { status = SS$_BADCONTEXT; } } else { /* ** ** Terminal already bound so return an error saying we did not ** bind it ** */ status = SS$_OPINCOMPL; } } else { status = SS$_BADPARAM; } break; /* ** ** Set connection type ** */ case ASN$_CONNECT_TYPE : if ((item->itm$w_length == 4) && (((unsigned int) item->itm$l_buffaddr & ASN$M_PERMANENT) || ((unsigned int) item->itm$l_buffaddr & ASN$M_TRANSIENT))) { asnucb->ucb$l_asn_con_type = (unsigned int) item->itm$l_buffaddr; status = SS$_NORMAL; } else { status = SS$_BADPARAM; } break; /* ** ** Close down a line but keep it bound ** */ case ASN$_DEVICE_CLOSED : status = SS$_BADPARAM; break; /* ** ** Disable reception of characters ** */ case ASN$_DISABLE_RCV: fork_lock(asnucb->ASNBASE.ucb$b_flck, &saved_fipl); phyucb = (TTY_UCB *) asnucb->ASNLOG.ucb$l_tl_phyucb; if ((void *) phyucb != (void *) 0) { device_lock(asnucb->ASNBASE.ucb$l_dlck, RAISE_IPL, &saved_dipl); asn$input_stop(asnucb); device_unlock(asnucb->ASNBASE.ucb$l_dlck, saved_dipl, SMP_RESTORE); status = SS$_NORMAL; } else { status = SS$_DEVINACT; } fork_unlock(asnucb->ASNBASE.ucb$b_flck, saved_fipl, SMP_RESTORE); break; /* ** ** Break the connection between the physical terminal and the logical ** */ case ASN$_DISCONNECT : fork_lock(asnucb->ASNBASE.ucb$b_flck, &saved_fipl); asnucb->ucb$v_asn_delpend = 1; asn$cancel_all_io(asnucb); if ((void *) asnucb->ASNLOG.ucb$l_tl_phyucb != (void *) 0) { dyn$revert(asnucb); } fork_unlock(asnucb->ASNBASE.ucb$b_flck, saved_fipl, SMP_RESTORE); status = SS$_NORMAL; break; /* ** ** Enable receipt of data ** */ case ASN$_ENABLE_RCV : phyucb = (TTY_UCB *) asnucb->ASNLOG.ucb$l_tl_phyucb; if ((void *) phyucb != (void *) 0) { status = asn$enable_receive(asnucb, phyucb); } else { status = SS$_DEVINACT; } break; /* ** ** Receive FCS size ** */ case ASN$_FCS_RCV : if ((item->itm$w_length == 4) && (((int) item->itm$l_buffaddr == 0) || ((int) item->itm$l_buffaddr == 16) || ((int) item->itm$l_buffaddr == 32))) { asnucb->ucb$l_asn_fcs_rcv = (int) item->itm$l_buffaddr; status = SS$_NORMAL; } else { status = SS$_BADPARAM; } break; /* ** ** Transmit FCS size ** */ case ASN$_FCS_XMIT : if ((item->itm$w_length == 4) && (((int) item->itm$l_buffaddr == 0) || ((int) item->itm$l_buffaddr == 16) || ((int) item->itm$l_buffaddr == 32))) { asnucb->ucb$l_asn_fcs_xmit = (int) item->itm$l_buffaddr; status = SS$_NORMAL; } else { status = SS$_BADPARAM; } break; /* ** ** Flow control type ** */ case ASN$_FLOW : if ((item->itm$w_length == 4) && (((int) item->itm$l_buffaddr & ASN$M_XON_XOFF) || ((int) item->itm$l_buffaddr & ASN$M_HW))) { asnucb->ucb$l_asn_flow = (int) item->itm$l_buffaddr; status = SS$_NORMAL; } else { status = SS$_BADPARAM; } break; /* ** ** Maximum recive size ** */ case ASN$_MRU : if ((item->itm$w_length == 4) && (((int) item->itm$l_buffaddr <= MAXIMUM_MRU) && ((int) item->itm$l_buffaddr >= MINIMUM_MRU))) { asnucb->ucb$l_asn_mru = (int) item->itm$l_buffaddr; status = SS$_NORMAL; } else { status = SS$_BADPARAM; } break; /* ** ** Maximum transmit size ** */ case ASN$_MTU : if ((item->itm$w_length == 4) && (((int) item->itm$l_buffaddr <= MAXIMUM_MTU) && ((int) item->itm$l_buffaddr >= MINIMUM_MTU))) { asnucb->ucb$l_asn_mtu = (int) item->itm$l_buffaddr; status = SS$_NORMAL; } else { status = SS$_BADPARAM; } break; /* ** ** Reset the line counter(s) ** */ case ASN$_RESET_COUNTERS: if ((item->itm$w_length == 4) && ((int) item->itm$l_buffaddr <= ASN$M_ALL)) { status = SS$_NORMAL; item_counter = 0; mask = (int) item->itm$l_buffaddr & (ASN$M_ALL | (ASN$M_ALL -1)); src_item = &asnucb->ucb$q_asn_bad_fcs; if (mask & ASN$M_ALL) { mask = ASN$M_ALL - 1; } /* ** ** Now loop through mask of items and set them to 0 ** */ while (mask) { if (mask & 1) { src_item[item_counter] = 0; } mask = mask >> 1; item_counter += 1; } } else { status = SS$_BADPARAM; } break; /* ** ** Default case nothing matched ** */ default : status = SS$_BADPARAM; break; } return status; } /* **++ ** asn$enable_receive - Allow input to start ** ** Functional description: ** ** This routine will allows input to start. If there is no current ** read buffer it will attempt to get a read buffer and make it the current ** read buffer. ** ** Calling convention: ** ** int asn$enable_receive (ASNUCB *asnucb, TTY_UCB *phyucb) ** ** Input parameters: ** ** None ** ** Output parameters: ** ** None ** ** Return value: ** ** SS$_NORMAL ** ** Environment: ** ** Kernel mode, system context, fork IPL with fork lock held. The device ** lock is acquired and released. ** **-- */ int asn$enable_receive(ASNUCB *asnucb, TTY_UCB *phyucb) { int saved_dipl; int status; ASNRD *asnrd; device_lock(asnucb->ASNBASE.ucb$l_dlck, RAISE_IPL, &saved_dipl); if (!phyucb->tty$v_st_read) { phyucb->tty$v_st_read = 1; status = __PAL_REMQUEL((void *) asnucb->ucb$l_asn_readq_fl, (void *) &asnrd); if (status >= 0) { /* ** ** Should never not get a packet ** */ asnrd->asnrd$l_putptr = (void *) &asnrd->asnrd$t_frame; asnrd->asnrd$l_status = SS$_NORMAL; phyucb->ucb$l_tt_typahd = (void *) asnrd; asn$input_resume(asnucb); } } else { if ((void *) phyucb->ucb$l_tt_typahd == (void *) 0) { status = __PAL_REMQUEL((void *) asnucb->ucb$l_asn_readq_fl, (void *) &asnrd); if (status >= 0) { asnrd->asnrd$l_putptr = (void *) &asnrd->asnrd$t_frame; asnrd->asnrd$l_status = SS$_NORMAL; phyucb->ucb$l_tt_typahd = (void *) asnrd; asn$input_resume(asnucb); } } } asnucb->ucb$v_asn_input_disabled = 0; asnucb->ucb$v_asn_drop_chars = 0; device_unlock(asnucb->ASNBASE.ucb$l_dlck, saved_dipl, SMP_RESTORE); return SS$_NORMAL; } /* **++ ** asn$fcs_check - See if recieved packet has valid FCS ** ** Functional description: ** ** Check that the packet we received is valid. The way this is done is ** to compute an FCS for the whole packet including the FCS. The resulting ** FCS is checked against a constant. ** ** Calling convention: ** ** int asn$fcs_check (ASNUCB *asnucb, char *buffer, int length) ** ** Input parameters: ** ** ucb pointer to the physical UCB ** buffer pointer to start of packet ** length length of packet in bytes includes received FCS ** ** Output parameters: ** ** None ** ** Return value: ** ** SS$_NORMAL ** SS$_BADCHKSUM ** ** Environment: ** ** Kernel mode IPL 8 fork thread with the fork lock held ** **-- */ int asn$fcs_check(ASNUCB *asnucb, unsigned char *buffer, int length) { unsigned short int fcs16; unsigned int fcs32; if (asnucb->ucb$l_asn_fcs_rcv == 16) { fcs16 = asn$fcs16_compute(buffer, length); if (fcs16 == 0xf0b8) { return SS$_NORMAL; } else { return SS$_BADCHKSUM; } } else { fcs32 = asn$fcs32_compute(buffer, length); if (fcs32 == 0xdebb20e3) { return SS$_NORMAL; } else { return SS$_BADCHKSUM; } } } /* **++ ** asn$fcs_compute - Compute the FCS and save it in buffer ** ** Functional description: ** ** The supplied buffer has the FCS computed and added to it. The buffer ** is assumed to have sufficeint space to store the FCS. ** ** Calling convention: ** ** int asn$fcs_compute (ASNUCB *asnucb, unsigned char *in_buff, ** int length) ** ** ** Input parameters: ** ** asnucb Pointer to ASN devices UCB ** in_buff Pointer to buffer ** length Length of input buffer in bytes ** ** Output parameters: ** ** buffer FCS added to the end of the buffer ** ** Return value: ** ** length of string with FCS added in ** ** Environment: ** ** Kernel mode, IPL 2 or IPL 8 depending upon caller. ** **-- */ int asn$fcs_compute(ASNUCB *asnucb, unsigned char *in_buff, int length) { unsigned int fcs; if (asnucb->ucb$l_asn_fcs_xmit == 16) { fcs = asn$fcs16_compute(in_buff, length); fcs = fcs ^ 0xffff; *(in_buff + length++) = fcs & 0xff; *(in_buff + length++) = (fcs >> 8) & 0xff; } else if (asnucb->ucb$l_asn_fcs_xmit == 32) { fcs = asn$fcs32_compute(in_buff, length); fcs = fcs ^ 0xffffffff; *(in_buff + length++) = fcs & 0xff; *(in_buff + length++) = (fcs >> 8) & 0xff; *(in_buff + length++) = (fcs >> 16) & 0xff; *(in_buff + length++) = (fcs >> 24) & 0xff; } return length; } /* **++ ** asn$fcs16_compute - Compute 16 bit FCS ** ** Functional description: ** ** Compute a 16 bit FCS for the string. The FCS is computed by looking up ** the FCS value in a table and and the result with 0xff and XORing it with the ** low 8 bits of the old FCS. ** ** Calling convention: ** ** unsigned short int asn$fcs16_compute (char *buffer, int length) ** ** Input parameters: ** ** buffer Pointer to packet to compute FCS for ** length Length in byte of buffer ** ** Output parameters: ** ** None ** ** Return value: ** ** 16 bit FCS ** ** Environment: ** ** Kernel mode either IPL 2 or IPL 8 depending upon the caller. ** **-- */ unsigned short int asn$fcs16_compute(unsigned char *buffer, int length) { unsigned short int fcs; fcs = 0xffff; while (length--) { fcs = (fcs >> 8) ^ fcstab_16[(fcs ^ *buffer++) & 0xff]; } return fcs; } /* **++ ** asn$fcs32_compute - Compute 32 bit FCS ** ** Functional description: ** ** Compute a 32 bit FCS for the string. The FCS is computed by looking up ** the FCS value in a table and and the result with 0xffff and XORing it with ** the low 8 bits of the old FCS. ** ** Calling convention: ** ** unsigned int asn$fcs32_comput (char *buffer, int length) ** ** Input parameters: ** ** buffer Pointer to packet to compute FCS for ** length Length in byte of buffer ** ** Output parameters: ** ** None ** ** Return value: ** ** 32 bit FCS ** ** Environment: ** ** Kernel mode either IPL 2 or IPL 8 depending upon the caller. ** **-- */ unsigned int asn$fcs32_compute(unsigned char *buffer, int length) { unsigned int fcs; fcs = 0xffffffff; while (length--) { fcs = (((fcs) >> 8) ^ fcstab_32[((fcs) ^ (*buffer++)) & 0xff]); } return fcs; } /* **++ ** asn$fork_dispatch - Handle fork on ASN UCB ** ** Functional description: ** ** This routine take care of performing all the forking for the ASN driver ** and for the terminal port driver. It roughly emulates what the TTDRIVER ** does for forking. The code will use the same fork flags as ttdriver does ** but only has code for a small subset of the possible forks used by TTDRIVER. ** ** Calling convention: ** ** void asn$fork_dispatch (uint64 fr3, uint64 fr4, ASNUCB *ucb) ** ** Input parameters: ** ** FR3 saved R3 (unused) ** FR4 saved R4 (flag bits for the fork) ** UCB pointer to the ASN UCB ** ** Output parameters: ** ** None ** ** Return value: ** ** None ** ** Environment: ** ** Kernel mode IPL8 with the fork lock held. The device lock will be ** acquired and released. ** **-- */ void asn$fork_dispatch(uint64 fr3, uint64 fr4, ASNUCB *asnucb) { int bit_count; uint64 flag; int original_ipl; ASNVCIB *asnvcib; TTY_UCB *phyucb; while (asnucb->ASNBASE.ucb$q_fr4 & TTY$M_FD_BUSY) { device_lock(asnucb->ASNBASE.ucb$l_dlck, RAISE_IPL, &original_ipl); bit_count = 0; flag = asnucb->ASNBASE.ucb$q_fr4 & 0xffffffff; while (!(flag & 1)) /* Find Next task to perform */ { bit_count += 1; flag = flag >> 1; } flag = 1 << bit_count; asnucb->ASNBASE.ucb$q_fr4 = asnucb->ASNBASE.ucb$q_fr4 & (~flag); device_unlock(asnucb->ASNBASE.ucb$l_dlck, original_ipl, SMP_RESTORE); if (flag == TTY$M_FD_DISCONNECT) /* This is really a hangup */ { asn$cancel_all_io(asnucb); asnvcib = (ASNVCIB *) asnucb->ucb$l_asn_vcib; if ((void *) asnvcib != (void *) 0) { VCI$XXX_Report_Event(asnvcib); } if ((void *) asnucb->ASNLOG.ucb$l_tl_ctrly != (void *) 0) { com_std$delattnast((ACB **) &asnucb->ASNLOG.ucb$l_tl_ctrly, (UCB *) asnucb); } } else if (flag == TTY$M_FD_PORTFORK) { phyucb = (TTY_UCB *) asnucb->ASNLOG.ucb$l_tl_phyucb; if ((void *) phyucb != (void *) 0) { port$forkret(phyucb); } } } } /* **++ ** asn$input_resume - Start reception of chracters ** ** Functional description: ** ** This routine is used to resume input from the remote node.. It has two ** paths of execution for hardware flow control it will raise the RTS signal. ** For XON/XOFF flow control it will send an XON character. Note that if a ** users has changed flow control methods while input is stopped they will ** remain stopped. ** ** Calling convention: ** ** void asn$input_resume (ASNUCB *asnucb) ** ** Input parameters: ** ** ucb pointer the the ASN UCB ** ** Output parameters: ** ** None ** ** Return value: ** ** None ** ** Environment: ** ** Kernel mode with the device lock for the physical device held. ** **-- */ void asn$input_resume(ASNUCB *asnucb) { unsigned int bits; TTY_UCB *phyucb; asnucb->ucb$l_asn_rcv_state = RCV_ON; phyucb = (TTY_UCB *) asnucb->ASNLOG.ucb$l_tl_phyucb; if ((void *) phyucb != (void *) 0) { if (asnucb->ucb$l_asn_flow == ASN$M_HW) { bits = TT$M_DS_RTS | TT$M_DS_DTR; /* leAve DTR on and set RTS */ port$ds_set(bits, phyucb); } else { port$xon(0x11, phyucb); } } } /* **++ ** asn$input_stop - Stop the receipt of data ** ** Functional description: ** ** This routine is used to disable input from the remote node. It cannot ** insure that input is stopped only that an attempt is made to stop input. ** It has two paths of execution for hardware flow control it will lower the ** RTS signal. For XON/XOFF flow control it will send an XOFF character. ** Note that if a users chooses to switch methods while input is stopped they ** will remain stopped. ** ** Calling convention: ** ** void asn$input_stop (TTY_UCB *asnucb) ** ** Input parameters: ** ** ucb pointer to the ASN UCB ** ** Output parameters: ** ** None ** ** Return value: ** ** None ** ** Environment: ** ** Kernel mode with the device lock for the physical device held. ** **-- */ void asn$input_stop(ASNUCB *asnucb) { unsigned int bits; TTY_UCB *phyucb; asnucb->ucb$l_asn_rcv_state = RCV_OFF; phyucb = (TTY_UCB *) asnucb->ASNLOG.ucb$l_tl_phyucb; if ((void *) phyucb != (void *) 0) { if (asnucb->ucb$l_asn_flow == ASN$M_HW) { bits = TT$M_DS_RTS << 8 | TT$M_DS_DTR; /* Leave DTR on */ port$ds_set(bits, phyucb); } else { port$xoff(0x13, phyucb); } } } /* **++ ** asn$move_sense_data - Copy data from system data area to user bufer ** ** Functional description: ** ** This routine is used to move data from the sysem bufer to the users ** buffer. It can be called at IPL 2 for the sense mode QIO case, and from ** IPL 8 when doing a VCI port management request. In the VCI case it does ** not bother to probe the users buffer it trusts that the buffer can be ** written to. ** ** Calling convention: ** int asn$move_sense_data (char *src_buf, char *target_buf, int buf_len, ** int probe_flag) ** Input parameters: ** ** src_buf pointer to buffer to copy data from ** target_buf pointer to the buffer to receive the data ** buf_len length in bytes of the buffer ** probe_flag flag to indicate if targer buffer should be probed. ** ** Output parameters: ** ** target_buf will have the data ** ** Return value: ** ** SS$_NORMAL data copied fine ** SS$_ACCVIO target buffer no writeable ** ** Environment: ** ** Kernel mode IPL 2.users process context, or kernel mode IPL 8. ** **-- */ int asn$move_sense_data(char *src_buf, char *target_buf, int buf_len, int probe_flag) { int acmode = 0; int status; status = SS$_NORMAL; if (probe_flag == PROBE) { status = exe_std$probew((void *) target_buf, buf_len, acmode); } if (status & SS$_NORMAL) { memcpy((void *) target_buf, (void *) src_buf, buf_len); } return status; } /* **++ ** asn$prepare_for_delete - Get device ready to be deleted ** ** Functional description: ** ** Prepare the device for later deletion. This routine will cancel all ** outstanding I/O on the device, try to block any new input, and indicate that ** we want to drop any characters that arrive from this point forward. It will ** break the connection between the physical terminal and the ASN device. This ** will insure that the device can no longer be used. This code will start the ** once a second deletion fork running. The actual deletion will take place ** later when it is safe to do so. ** ** Calling convention: ** ** void asn$prepare_for_delete (ASNUCB *asnucb) ** ** Input parameters: ** ** ** ** Output parameters: ** ** None ** ** Return value: ** ** None ** ** Environment: ** ** Kernel mode, user process context, IPL 2, or as an IPL 8 fork thread. ** **-- */ void asn$prepare_for_delete(ASNUCB *asnucb) { int saved_fipl; int status; fork_lock(asnucb->ASNBASE.ucb$b_flck, &saved_fipl); if (!asnucb->ucb$v_asn_delpend) { asnucb->ucb$v_asn_delpend = 1; } asn$cancel_all_io(asnucb); if ((void *) asnucb->ASNLOG.ucb$l_tl_phyucb != (void *) 0) { dyn$revert(asnucb); } if (!asnucb->ucb$v_asn_delactive) { asnucb->ucb$v_asn_delactive = 1; asnucb->ucb$b_asn_type = DYN$C_FRK; asnucb->ucb$b_asn_flck = SPL$C_IOLOCK8; fork_wait(asn$delete_ucb, 0, (uint64) asnucb, (FKB *) &asnucb->ucb$l_asn_fqfl); } fork_unlock(asnucb->ASNBASE.ucb$b_flck, saved_fipl, SMP_RESTORE); } /* **++ ** asn$quote - Take users output buffer and quote control charcters ** ** Functional description: ** ** This routine will take the supplied buffer and examine each character ** one at a time and decide if it needs to be quoted. If the character needs ** to be quoted it will add the quote character to the output buffer and then ** xor the character with 0x20 and add it to the output buffer. It is assumed ** that the FCS has already been computed and added to the end of the packet. ** ** Calling convention: ** ** int asn$quote (ASNUCB *ucb, char *in_char, int in_length, ** char *out_char, int out_length, int *ret_len); ** ** Input parameters: ** ** ucb pointer the ASN UCB ** in_char pointer to start input bufer ** in_length length of input buffer ** out_char pointer to start of output buffer ** out_length length of output buffer ** ret_length pointer to variable to receive actual length of buffer ** ** Output parameters: ** ** out_char quoted packet to transmit ** ret_length will contain actual number of characters to send ** ** Return value: ** ** SS$_NORMAL quote completed ** SS$_BUFFEROVF to much data for output buffer ** ** Environment: ** ** Kernel mode IPL 8 fork thread with the fork lock held. ** **-- */ int asn$quote(ASNUCB *asnucb, unsigned char *in_char, int in_length, unsigned char *out_char, int out_length, int *ret_len) { int bit_pos; int slot; int status; status = SS$_NORMAL; *ret_len = 0; if (out_length >= in_length) { do { slot = (int)*in_char/32; bit_pos = (int)*in_char % 32; if ((*in_char == 0x5e) || (!(asnucb->ucb$l_asn_xmit_accm[slot] & (1 << bit_pos))) || ((*in_char >= 0x20) && (*in_char <= 0x3f))) { *out_char++ = *in_char++; *ret_len += 1; } else /* If here must quote character */ { *out_char++ = 0x7d; out_length -= 1; *ret_len += 1; if (out_length) { *out_char++ = *in_char++ ^ 0x20; *ret_len += 1; } else { status = SS$_BUFFEROVF; break; } } } while ((--in_length) && (--out_length)); } else { status = SS$_BUFFEROVF; } return status; } /* **++ ** ans$unquote - Remove quoting from received packet ** ** Functional description: ** ** This routine will remove the quoting from the buffer. This is done by ** examining each character if it is 0x7d it is removed and the next character ** is XORed with 0x20. This resulting character is saved in the output buffer. ** ** Calling convention: ** ** int asn$unquote (char *in_char, int in_length, char *out_char) ** ** Input parameters: ** ** in_char pointer to packet received ** in_length length of packet ** out_char pointer of buffer to receive packet ** ** Output parameters: ** ** out_buf packet with quoting removed ** ** Return value: ** ** Number of chracters in output buffer ** ** Environment: ** ** Kernel mode IPL 8 fork thread with the fork lock held. ** **-- */ int asn$unquote(unsigned char *in_char, int in_length, unsigned char *out_char) { int i = 0; int out_len; out_len = 0; do { i += 1; if (*in_char != 0x7d) { *out_char++ = *in_char++; out_len += 1; } else { in_char++; *out_char++ = *in_char++ ^ 0x20; out_len += 1; i += 1; } } while (i < in_length); return out_len; } /* **++ ** asn$write_start - Start a write request ** ** Functional description: ** ** This routine will take a transmit IRP or VCRP and do the necessary ** steps to get it set to the physical device. It will add the framing ** characters and quote the data, and finally call the port driver to send the ** packet out over the wire. ** ** Calling convention: ** ** int asn$write_start (ASNUCB *asnucb, TTY_UCB *phyucb, IRP *irp) ** ** Input parameters: ** ** asnucb pointer to ASN UCB ** phyucb pointer to physical UCB ** vcrp/irp pointer to current VCRP or IRP ** ** Output parameters: ** ** packet if aborted or writen to physical device ** ** Return value: ** ** SS$_NORMAL worked ** SS$_BUFFEROVF Insufficient space to hold output packet ** SS$_xxxx Failed to allocate pool errors ** ** Environment: ** ** Kernel mode IPL 8 with fork lock held ** **-- */ int asn$write_start(ASNUCB *asnucb, TTY_UCB *phyucb, IRP *irp) { unsigned char *in_buffer; unsigned char *out_buffer; unsigned char out_char; int buffer_len; int frame_len; int frame_size; int request_size; int32 return_size; int saved_dipl; int status; ASNWRT *asnwrt; VCRP *vcrp; /* ** ** Allocate a write packet and save its address ** ** Request size = 2 * mtu + (16 bytes for address, control, and protocol ** fields) + SIZE of write buffer header ** */ request_size = 2 * asnucb->ucb$l_asn_mtu + PACKET_OVERHEAD + sizeof(ASNWRT); status = exe_std$alononpaged(request_size, &return_size, (void *) &asnwrt); if (status & SS$_NORMAL) { asnwrt->asnwrt$w_size = return_size; asnwrt->asnwrt$b_type = DYN$C_FRK; asnwrt->asnwrt$b_flck = SPL$C_IOLOCK8; asnwrt->asnwrt$l_fpc = asn$write_done; asnwrt->asnwrt$q_fr3 = 0; asnwrt->asnwrt$q_fr4 = 0; asnwrt->asnwrt$a_ucb = (void *) asnucb; asnwrt->asnwrt$ps_data = (void *) &asnwrt->asnwrt$t_data; asnucb->ucb$l_asn_write_buffer = (void *) asnwrt; if (irp->irp$b_type == DYN$C_VCRP) { vcrp = (VCRP *) irp; in_buffer = (unsigned char *) vcrp->vcrp$l_buffer_address; buffer_len = vcrp->vcrp$l_bcnt; asnwrt->asnwrt$a_vcrp = (void *) vcrp; } else { in_buffer = (unsigned char *) irp->irp$l_svapte; buffer_len = irp->irp$l_bcnt; asnwrt->asnwrt$a_vcrp = (void *) irp; } frame_size = asnwrt->asnwrt$w_size - sizeof(ASNWRT) - 1; out_buffer = (unsigned char *) &asnwrt->asnwrt$t_data; *out_buffer++ = (unsigned char) 0x7e; status = asn$quote(asnucb, in_buffer, buffer_len, out_buffer, frame_size, &buffer_len); if (status & SS$_NORMAL) { device_lock(asnucb->ASNBASE.ucb$l_dlck, RAISE_IPL, &saved_dipl); /* ** ** Account for start & stop of frame characters finish write buffer ** and send it ** */ asnwrt->asnwrt$l_bcnt = buffer_len + 2; asnwrt->asnwrt$ps_data = &asnwrt->asnwrt$t_data; asnwrt->asnwrt$l_status = SS$_NORMAL; *(out_buffer+buffer_len) = (unsigned char) 0x7e; phyucb->tty$v_st_write = 1; out_char = class$getnext(phyucb); if (phyucb->ucb$b_tt_outype != 0) { port$startio(out_char, phyucb); } device_unlock(asnucb->ASNBASE.ucb$l_dlck, saved_dipl, SMP_RESTORE); } else { /* ** ** The quote operation failed, free up the write buffer and return the ** error. ** */ exe_std$deanonpaged(asnwrt); } } return status; }