#pragma module pppd$asnstrstp "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 start and I/O completion rotuines. They are: asn$read_done - Complete a read request asn$read_startio - Start a read request asn$return_read_pkt - Decide what to do with read packet asn$validate_read_pkt - Validate a read packet asn$write_done - Complete a write request asn$write_startio - Start a write request AUTHOR: Forrest A. Kenney 14-February-1996 REVISION HISTORY: X-7 FAK006 Forrest A. Kenney 07-March-1997 Remove the acquisition of the device lock before seeing if we have an outstanding write. Just check the write state and if we have a write queued. If not then start the write if so queue the request. X-6 BWK001 Barry W. Kierstein 71-December-1996 Replaced the standard Digital copyright with one compatible with the CMU copyright. X-5 FAK005 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. In asn$write_done deallocate the write buffer, and no longer clear the writing flag it is cleared when we queue the fork to complete the write. X-4 FAK003 Forrest A. Kenney 03-October-1996 Clear the writing state once the write packet has been returned. X-3 FAK002 Forrest A. Kenney 24-September-1996 Fill in VCRP$L_BOFF to be end of VCRP$T_DATA . Seems that even though some versions of the VCI specification say this is not used unless VCRP$L_SVAPTE is filled in that is not the case. X-2 FAK001 Forrest A. Kenney 02-July-1996 In asn$return_read_pkt get the device lock. Add check on $QIO path to make sure that packet fits buffer if not. Return length of 0, and status of SS$_BUFFEROVF. */ /* Define system data structure types and constants */ #include /* AST control block definitions */ #include /* Channel Control Block definitons */ #include /* Controller Request Block definitons */ #include /* Device data block definitions */ #include /* Driver Dispatch table definitions */ #include /* DEscriptor definitions */ #include /* Data structure type definitions */ #include /* Fork block definitions */ #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 /* 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_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" */ /* **++ ** asn$read_done - Complete a read request ** ** Functional description: ** ** This routine will execute as a fork thread it will complete the read ** request. The decision loop is fairly straight forward. If we have a VCIB ** then complete the read using the VCI path. If not the see if there us a ** read IRP active. If so it will complete the request using the IRP. ** ** Calling convention: ** ** void asn$read_done (uint64 fr3, uint64 fr4, ASNRD *asnrd) ** ** Input parameters: ** ** FR3 Unused ** FR4 Unused ** ASNRD Pointer to ASN read buffer ** ** ** Output parameters: ** ** None ** ** Return value: ** ** None ** ** Environment: ** ** Kernel mode IPL 8 with the fork lock held. ** **-- */ void asn$read_done(uint64 fr3, uint64 fr4, ASNRD *asnrd) { int length; int request_size; int32 return_size; int saved_dipl; int status; VCRPSTACK *stack; ASNUCB *asnucb; ASNVCIB *vcib; IRP *irp; SYSBUF_HDR *buf_hdr; TTY_UCB *phyucb; VCRP *vcrp; asnucb = (ASNUCB *) asnrd->asnrd$a_ucb; vcib = (ASNVCIB *) asnucb->ucb$l_asn_vcib; phyucb = (TTY_UCB *) asnucb->ASNLOG.ucb$l_tl_phyucb; __ADD_ATOMIC_LONG(&asnucb->ucb$l_asn_fork_cnt, -1); if (!asnucb->ucb$v_asn_delpend) { /* ** ** The VCI interface is the one most likely to be used. So handle it ** first. ** */ if ((void *) vcib != (void *) 0) { length = (char *) asnrd->asnrd$l_putptr - (char *) &asnrd->asnrd$t_frame; request_size = sizeof(VCRP) + length; status = exe_std$alononpaged(request_size, &return_size, (void *) &vcrp); if (status & SS$_NORMAL) { vcrp->vcrp$w_size = return_size; vcrp->vcrp$b_type = DYN$C_VCRP; vcrp->vcrp$b_rmod = SPL$C_IOLOCK8; vcrp->vcrp$w_common_flags = 0; vcrp->vcrp$a_dealloc_rtn = 0; vcrp->vcrp$a_dcb_link = 0; vcrp->vcrp$l_svapte = 0; vcrp->vcrp$l_buffer_address = (void *) ((char *) vcrp + sizeof(VCRP)); vcrp->vcrp$l_boff = (unsigned int) sizeof(VCRP); vcrp->vcrp$l_function = VCRP$K_FC_RECEIVE; vcrp->vcrp$q_request_status = SS$_NORMAL; vcrp->vcrp$l_connection_id = (unsigned int) asnucb->ucb$l_asn_vcib; stack = (struct vcrpstackdef *) vcrp->vcrp$t_internal_stack; vcrp->vcrp$a_stack = (void *) stack; stack->stack$l_btm = (void *) ((char *) stack->stack$t_stack + STACK$K_STACK_SIZE); stack->stack$l_lastused = stack->stack$l_btm; stack->stack$l_top = (void *) ((char *) stack->stack$t_stack); status = asn$validate_read_pkt(asnucb, (unsigned char *) &asnrd->asnrd$t_frame, length, (unsigned char *) vcrp->vcrp$l_buffer_address, &length); if (status & SS$_NORMAL) { vcrp->vcrp$l_bcnt = length; vcrp->vcrp$l_total_pdu_size = length; VCI$XXX_Receive_Complete(vcrp, vcib); } else { /* ** ** Bad FCS count it return the read packet and delete the VCRP ** */ asnucb->ucb$q_asn_bad_fcs += 1; exe_std$deanonpaged((void *) vcrp); } asn$return_read_pkt(asnucb, asnrd); } else { /* ** ** Note that if another read packet is completed while this fork ** is outstanding and it can get a VCRP then reads will come in out of ** order. This should never happen. ** */ __ADD_ATOMIC_LONG(&asnucb->ucb$l_asn_fork_cnt, 1); fork_wait(asn$read_done, 0, 0, (FKB *) asnrd); 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); } } else if ((asnucb->ASNBASE.ucb$v_bsy) && ((void *)asnucb->ASNBASE.ucb$l_irp != (void *) 0)) { /* ** ** If here then the VCI interface is not being used. So see if ** there is an outstanding read. If so we will give the packet to the ** read path. If some one is nutty enough to try and use both the read ** and VCI paths at the same time they will have loads of fun. ** */ length = (char *) asnrd->asnrd$l_putptr - (char *) &asnrd->asnrd$t_frame; irp = asnucb->ASNBASE.ucb$l_irp; buf_hdr = (SYSBUF_HDR *) irp->irp$l_svapte; status = asn$validate_read_pkt(asnucb, (unsigned char *) &asnrd->asnrd$t_frame, length, (unsigned char *) buf_hdr->pkt_datap, &length); if (length <= irp->irp$l_bcnt) { memcpy(buf_hdr->pkt_datap, &asnrd->asnrd$t_frame, length); } else { length = 0; status = SS$_BUFFEROVF; } asn$return_read_pkt(asnucb, asnrd); irp->irp$l_bcnt = length; ioc_std$reqcom(length << 16 | status, 0, (UCB *) asnucb); if (!asnucb->ASNBASE.ucb$v_bsy) { asnucb->ASNBASE.ucb$l_irp = 0; } } else { /* ** ** Assumption here is that we are using the $QIO path so place ** packet on queue of pending reads. When the next read gets started it ** will pick up this read. ** */ __PAL_INSQUEL((void *) asnucb->ucb$l_asn_packetq_bl, (void *) asnrd); } } else { /* ** ** Delete is pending for this guy there is no reason to do anything with ** this read buffer. Place it on the queue of read packets there is no ** reason to try and complete it. ** */ __PAL_INSQUEL((void *) asnucb->ucb$l_asn_readq_bl, (void *) asnrd); } return; } /* **++ ** asn$read_startio - Start a read request ** ** Functional description: ** ** This routine is called by ioc_std$initiate to start the next read ** request. For the ASN driver the behavior is a bit different than the normal ** model. Once input has been enabled on the device it will always read data. ** What this routine will do is see if a packet has already been received and ** if so it will complete the request. If not it will simply return as the ** read done fork thread in the driver will locate the current read and ** complete it with the packet it has in hand. ** ** Calling convention: ** ** void asn$read_startio(IRP *irp, ASNUCB *asnucb) ** ** Input parameters: ** ** irp pointer to current I/O request packet ** asnucb pointer to the ASN UCB ** ** Output parameters: ** ** None ** ** Return value: ** ** None ** ** Environment: ** ** Kernel mode IPL8 with the fork lock held ** **-- */ void asn$read_startio(IRP *irp, ASNUCB *asnucb) { int length; int status; ASNRD *asnrd; SYSBUF_HDR *buf_hdr; TTY_UCB *phyucb; if ((void *) asnucb->ucb$l_asn_packetq_fl != (void *) &asnucb->ucb$l_asn_packetq_bl) { status = __PAL_REMQUEL((void *) asnucb->ucb$l_asn_packetq_fl, (void *) &asnrd); length = (char *) asnrd->asnrd$l_putptr - (char *) &asnrd->asnrd$t_frame; buf_hdr = (SYSBUF_HDR *) irp->irp$l_svapte; status = asn$validate_read_pkt(asnucb, (unsigned char *) &asnrd->asnrd$t_frame, length, (unsigned char *) &asnrd->asnrd$t_frame, &length); if (length <= irp->irp$l_bcnt) { memcpy(buf_hdr->pkt_datap, &asnrd->asnrd$t_frame, length); } else { length = 0; status = SS$_BUFFEROVF; } asn$return_read_pkt(asnucb, asnrd); irp->irp$l_bcnt = length; ioc_std$reqcom(length << 16 | status, 0, (UCB *) asnucb); if (!asnucb->ASNBASE.ucb$v_bsy) { asnucb->ASNBASE.ucb$l_irp = 0; } } return; } /* **++ ** asn$return_read_pkt - Decide what to do with read packet ** ** Functional description: ** ** This routine is called once the data has been copied from a read ** buffer to either a VCRP, or a read IRP. It decides what to do with the ** packet. It will either place it on the queue of free read packets, or turn ** it into the current read buffer. ** ** Calling convention: ** ** void asn$return_read_pkt (ASNUCB *asnucb) ** ** Input parameters: ** ** ASNUCB Pointer to the ASN UCB ** ASNRD Pointer to a read packet ** ** Output parameters: ** ** None ** ** Return value: ** ** None ** ** Environment: ** ** Kernel mode, system context, fork IPL with fork lock held. ** **-- */ void asn$return_read_pkt(ASNUCB *asnucb, ASNRD *read_pkt) { int saved_fipl; TTY_UCB *phyucb; phyucb = (TTY_UCB *) asnucb->ASNLOG.ucb$l_tl_phyucb; read_pkt->asnrd$l_putptr = (void *) &read_pkt->asnrd$t_frame; read_pkt->asnrd$l_status = SS$_NORMAL; device_lock(asnucb->ASNBASE.ucb$l_dlck, RAISE_IPL, &saved_fipl); if (((void *)phyucb != (void *) 0) && (!asnucb->ucb$v_asn_input_disabled)) { if ((phyucb->tty$v_st_read) && ((void *)phyucb->ucb$l_tt_typahd == (void *) 0)) { phyucb->ucb$l_tt_typahd = (void *) read_pkt; asnucb->ucb$v_asn_drop_chars = 0; if (asnucb->ucb$l_asn_rcv_state == RCV_OFF) { asn$input_resume(asnucb); } } else { __PAL_INSQUEL((void *) asnucb->ucb$l_asn_readq_bl, (void *) read_pkt); } } else { __PAL_INSQUEL((void *) asnucb->ucb$l_asn_readq_bl, (void *) read_pkt); } device_unlock(asnucb->ASNBASE.ucb$l_dlck, saved_fipl, SMP_RESTORE); return; } /* **++ ** asn$validate_read_pkt - Validate a read packet ** ** Functional description: ** ** This routine takes a raw read packet and removes the quoting and ** validates the checksum. After unquoting the packet it makes sure that the ** packet is large enough if not it returns an error ** ** Calling convention: ** ** int asn$validate_read_pkt (ASNUCB *asnucb, unsigned char *in_buffer, ** int in_length, unsigned char *out_buffer, ** int *ret_length); ** ** Input parameters: ** ** asnucb Poitner to the ASN UCB ** in_buffer Raw packet to be validated ** in_length Length of raw input packet in bytes ** out_buffer Receive packet with quoting removed ** ret_length Lenght of packet minus checksum ** ** Output parameters: ** ** out_buffer Receive packet with quoting removed ** ret_length Lenght of packet minus checksum ** ** Return value: ** ** SS$_BADCHKSUM FCS was not valid ** SS$_IVBUFLEN Packet was to small ** SS$_NORMAL Packet was valid ** ** Environment: ** ** Kernel mode, system context, fork IPL with fork lock held. ** **-- */ int asn$validate_read_pkt(ASNUCB *asnucb, unsigned char *in_buffer, int in_length, unsigned char *out_buffer, int *ret_length) { int status; *ret_length = asn$unquote(in_buffer, in_length, out_buffer); if (*ret_length > MINIMUM_MRU) { status = asn$fcs_check(asnucb, out_buffer, *ret_length); if (!(status & SS$_NORMAL)) { asnucb->ucb$q_asn_bad_fcs += 1; } *ret_length = *ret_length - asnucb->ucb$l_asn_fcs_rcv/8; } else { asnucb->ucb$q_asn_runt_pkts += 1; status = SS$_IVBUFLEN; } return status; } /* **++ ** asn$write_done - Complete a write request ** ** Functional description: ** ** This routine executes at FORK IPL and will either be called from the ** cancel I/O code path, or as a fork thread with the ASNWRT buffer acting as ** the fork block. The request is completed to the appropriate place. Then ** the write buffer will be reset for next use. Then if there are any more ** packets to transmit they will be started. If one of the packets cannot be ** started it will be aborted and we will look to see if there is another ** packet to start. ** ** Calling convention: ** ** void asn$write_done (uint64 fr3, uint64 fr4, ASNWRT *asnwrt) ** ** Input parameters: ** ** fr3 Unused ** fr4 Unused ** asnwrt pointer to ASNWRT buffer. ** ** Output parameters: ** ** None ** ** Return value: ** ** None ** ** Environment: ** ** Kernel mode IPL 8 fork lock held. ** **-- */ void asn$write_done(uint64 fr3, uint64 fr4, ASNWRT *asnwrt) { int status; ASNUCB *asnucb; ASNVCIB *vcib; IRP *irp; TTY_UCB *phyucb; VCRP *vcrp; vcrp = (VCRP *) asnwrt->asnwrt$a_vcrp; asnucb = (ASNUCB *) asnwrt->asnwrt$a_ucb; phyucb = (TTY_UCB *) asnucb->ASNLOG.ucb$l_tl_phyucb; vcib = (ASNVCIB *) asnucb->ucb$l_asn_vcib; __ADD_ATOMIC_LONG(&asnucb->ucb$l_asn_fork_cnt, -1); if (vcrp->vcrp$b_type == DYN$C_VCRP) { vcrp->vcrp$q_request_status = asnwrt->asnwrt$l_status; VCI$XXX_Transmit_Complete(vcrp, vcib); } else { irp = (IRP *) vcrp; irp->irp$l_iost1 = irp->irp$l_bcnt << 16 | asnwrt->asnwrt$l_status; irp->irp$l_iost2 = 0; com_std$post_nocnt(irp); } exe_std$deanonpaged(asnwrt); if ((void *) phyucb != (void *) 0) { if (!asnucb->ucb$v_asn_delpend) { do { status = __PAL_REMQUEL((void *) asnucb->ucb$l_asn_writeq_fl, (void *) &irp); if (status >= 0) { status = asn$write_start(asnucb, phyucb, irp); if (!(status & SS$_NORMAL)) { if (vcrp->vcrp$b_type == DYN$C_VCRP) { vcrp = (VCRP *) irp; vcrp->vcrp$q_request_status = status; VCI$XXX_Transmit_Complete(vcrp, vcib); } else { irp->irp$l_iost1 = irp->irp$l_bcnt << 16 | status; irp->irp$l_iost2 = 0; com_std$post_nocnt(irp); } } else { break; } } else { break; } } while (((void *) &asnucb->ucb$l_asn_writeq_fl != (void *) asnucb->ucb$l_asn_writeq_bl)); } } return; } /* **++ ** asn$write_startio - Start a write request ** ** Functional description: ** ** This routine is called by ioc_std$altqpkt to start the next write ** request. For the ASN driver the behavior is a emulation of sorts of what ** TTDRIVER does. If the device is busy it will simply place the IRP on the ** queue of pending write requests. If the device is not busy it will call ** the write startio routine. The write startio code will quote the users ** buffer, and copy it to the output buffer. It will also start the request. ** ** Calling convention: ** ** void asn$write_startio (IRP *irp, ASNUCB *asnucb) ** ** Input parameters: ** ** irp pointer to current I/O request packet ** asnucb pointer to the ASN UCB ** ** Output parameters: ** ** None ** ** Return value: ** ** None ** ** Environment: ** ** Kernel mode IPL 8 with fork lock held ** **-- */ void asn$write_startio(IRP *irp, ASNUCB *asnucb) { unsigned int fcs; int saved_fipl; int status; SYSBUF_HDR *buf_hdr; TTY_UCB *phyucb; phyucb = (TTY_UCB *) asnucb->ASNLOG.ucb$l_tl_phyucb; if (((void *)phyucb != (void *) 0) && (!asnucb->ucb$v_asn_delpend)) { buf_hdr = (SYSBUF_HDR *) irp->irp$l_svapte; irp->irp$l_bcnt = asn$fcs_compute(asnucb, (unsigned char *) buf_hdr->pkt_datap, irp->irp$l_bcnt); if ((!phyucb->tty$v_st_write) && ((void *) asnucb->ucb$l_asn_writeq_bl == (void *) asnucb->ucb$l_asn_writeq_fl)) { status = asn$write_start(asnucb, phyucb, irp); if (!(status & SS$_NORMAL)) { irp->irp$l_iost1 = status; irp->irp$l_iost2 = 0; com_std$post_nocnt(irp); } } else { __PAL_INSQUEL((void *) asnucb->ucb$l_asn_writeq_bl, (void *) irp); } } else { irp->irp$l_iost1 = SS$_DEVINACT; irp->irp$l_iost2 = 0; com_std$post_nocnt(irp); } return; }