-- File: EFTPRecv.mesa, Last Edit: -- MAS April 17, 1980 8:51 PM -- HGM October 13, 1980 8:01 PM -- Copyright Xerox Corporation 1979, 1980 DIRECTORY InlineDefs: FROM "InlineDefs" USING [COPY], CommUtilDefs: FROM "CommUtilDefs" USING [SetTimeout, MsecToTicks], PupDefs: FROM "PupDefs" USING [ DataWordsPerPupBuffer, GetFreePupBuffer, ReturnFreePupBuffer, GetPupContentsBytes, SetPupContentsBytes, PupAddress, PupBuffer, PupRouterSendThis, PupSocket, PupSocketMake, PupSocketDestroy, SecondsToTocks, SwapPupSourceAndDest], PupTypes: FROM "PupTypes" USING [ maxDataBytesPerGatewayPup, fillInPupAddress], EFTPDefs: FROM "EFTPDefs" USING [ EFTPTimeOut, EFTPAbortCode, eftpExternalReceiverAbort, eftpOutOfSyncAbort, eftpReceiverBusyAbort]; EFTPRecv: MONITOR IMPORTS InlineDefs, CommUtilDefs, EFTPDefs, PupDefs EXPORTS EFTPDefs = BEGIN OPEN PupDefs, EFTPDefs; EFTPAlreadyReceiving: PUBLIC ERROR = CODE; EFTPNotReceiving: PUBLIC ERROR = CODE; EFTPEndReceiving: PUBLIC ERROR = CODE; EFTPTroubleReceiving: PUBLIC ERROR [e: EFTPAbortCode, s: STRING] = CODE; recv: CONDITION; recverStop: BOOLEAN; recverFork: PROCESS; receiveSocket: PupSocket ← NIL; recvStarted: BOOLEAN; recvHim: PupAddress; receiveSeqNumber: CARDINAL; receivePupBuffer: PupBuffer ← NIL; receiveGobbled: CARDINAL ← 0; -- in words ! EFTPSetRecvTimeout: PUBLIC PROCEDURE [ms: CARDINAL] = BEGIN CommUtilDefs.SetTimeout[@recv,CommUtilDefs.MsecToTicks[ms]]; END; EFTPOpenForReceiving: PUBLIC PROCEDURE [me: PupAddress] RETURNS [PupAddress] = BEGIN EFTPOpenCheck[me]; BEGIN ENABLE UNWIND => EFTPKillReceiver[]; EFTPOpenLocked[]; END; RETURN[recvHim]; END; EFTPOpenCheck: ENTRY PROCEDURE [me: PupAddress] = BEGIN ENABLE UNWIND => NULL; IF receiveSocket#NIL THEN ERROR EFTPAlreadyReceiving; recvStarted ← FALSE; recverStop ← FALSE; recverFork ← FORK EFTPRecvDataProcess[]; receiveSocket ← PupSocketMake[ me.socket, PupTypes.fillInPupAddress, SecondsToTocks[1]]; receiveSeqNumber ← 0; END; EFTPOpenLocked: ENTRY PROCEDURE = BEGIN ENABLE UNWIND => NULL; UNTIL receivePupBuffer#NIL DO THROUGH [0..10) UNTIL receivePupBuffer#NIL DO WAIT recv; ENDLOOP; IF receivePupBuffer#NIL THEN EXIT; SIGNAL EFTPTimeOut; -- 10*1 sec ENDLOOP; receiveSocket.setRemoteAddress[recvHim]; END; EFTPGetBlock: PUBLIC ENTRY PROCEDURE [p: POINTER, l: CARDINAL] RETURNS [CARDINAL] = BEGIN ENABLE UNWIND => NULL; n: CARDINAL; -- bytes to transfer pupLength: CARDINAL; IF receiveSocket=NIL THEN ERROR EFTPNotReceiving; IF (l MOD 2)=1 THEN l ← l-1; UNTIL receivePupBuffer#NIL DO WAIT recv; IF receivePupBuffer#NIL THEN EXIT; SIGNAL EFTPTimeOut; -- 1*1 sec ENDLOOP; IF receivePupBuffer.pupType=eEnd OR receivePupBuffer.pupType=eAbort THEN BEGIN b: PupBuffer ← receivePupBuffer; receivePupBuffer ← NIL; EFTPSendAck[b,receiveSeqNumber]; RETURN WITH ERROR EFTPEndReceiving; END; pupLength ← GetPupContentsBytes[receivePupBuffer]; n ← pupLength-(receiveGobbled*2); n ← MIN[l,n]; InlineDefs.COPY[ from: @receivePupBuffer.pupWords[receiveGobbled], nwords: (n+1)/2, to: p]; receiveGobbled ← receiveGobbled+(n/2); IF l>n OR receiveGobbled*2=pupLength THEN BEGIN -- done with this buffer, use it to send back the ack b: PupBuffer ← receivePupBuffer; receivePupBuffer ← NIL; EFTPSendAck[b,receiveSeqNumber]; receiveSeqNumber ← receiveSeqNumber+1; END; RETURN[n]; END; EFTPFinishReceiving: PUBLIC PROCEDURE = BEGIN IF receiveSocket=NIL THEN ERROR EFTPNotReceiving; EFTPKillReceiver[]; END; EFTPAbortReceiving: PUBLIC PROCEDURE [s: STRING] = BEGIN IF receiveSocket=NIL THEN RETURN; EFTPAbortReceivingLocked[s]; EFTPKillReceiver[]; END; EFTPAbortReceivingLocked: ENTRY PROCEDURE [s: STRING] = BEGIN i, end: CARDINAL; b: PupBuffer; b ← GetFreePupBuffer[]; b.pupType ← eAbort; b.pupID ← [0,receiveSeqNumber]; b.pupWords[0] ← eftpExternalReceiverAbort; end ← MIN[ s.length+2, 2*DataWordsPerPupBuffer[], PupTypes.maxDataBytesPerGatewayPup]; FOR i ← 2,i+1 UNTIL i=end DO b.pupChars[i] ← s[i-2]; ENDLOOP; SetPupContentsBytes[b,end]; receiveSocket.put[b]; END; -- internal procedures EFTPRecvDataProcess: PROCEDURE = BEGIN b: PupBuffer; UNTIL recverStop DO b ← receiveSocket.get[]; IF b#NIL THEN EFTPRecvDataLocked[b]; ENDLOOP; END; EFTPRecvDataLocked: ENTRY PROCEDURE [b: PupBuffer] = BEGIN -- better not SIGNAL here IF b.pupType ~IN[eData..eAbort] THEN -- not EFTP type packet BEGIN ReturnFreePupBuffer[b]; GOTO AllDone; END; IF b.source=recvHim AND b.pupType=eData AND b.pupID=[0,receiveSeqNumber-1] THEN BEGIN -- duplicate: use the buffer to send back the ack EFTPSendAck[b,receiveSeqNumber-1]; GOTO AllDone; END; IF b.source#recvHim OR b.pupID#[0,receiveSeqNumber] THEN BEGIN -- from wrong place IF ~recvStarted AND b.pupID=[0,0] THEN BEGIN -- we are listening, and this is the first packet IF receivePupBuffer=NIL THEN -- take this new user BEGIN receivePupBuffer ← b; receiveGobbled ← 0; recvStarted ← TRUE; recvHim ← receivePupBuffer.source; NOTIFY recv; GOTO AllDone; END; END; IF b.pupType=eData THEN BEGIN -- use the buffer to send back an abort b.pupType ← eAbort; b.pupWords[0] ← IF b.pupID#[0,0] THEN eftpOutOfSyncAbort ELSE eftpReceiverBusyAbort; SetPupContentsBytes[b,2]; -- should send some text SwapPupSourceAndDest[b]; PupRouterSendThis[b]; GOTO AllDone; END; ReturnFreePupBuffer[b]; GOTO AllDone; END; -- must be the one we want IF receivePupBuffer#NIL THEN BEGIN -- Oops, retransmission. We will ack when we finish reading this buffer. ReturnFreePupBuffer[b]; GOTO AllDone; END; receivePupBuffer ← b; receiveGobbled ← 0; NOTIFY recv; EXITS AllDone => NULL; END; EFTPSendAck: PROCEDURE [b: PupBuffer, id: CARDINAL] = BEGIN b.pupType ← eAck; b.pupID ← [0,id]; SetPupContentsBytes[b,0]; receiveSocket.put[b]; END; EFTPKillReceiver: PROCEDURE = BEGIN recverStop ← TRUE; JOIN recverFork; IF receiveSocket#NIL THEN PupSocketDestroy[receiveSocket]; receiveSocket ← NIL; IF receivePupBuffer#NIL THEN BEGIN b: PupBuffer ← receivePupBuffer; receivePupBuffer ← NIL; ReturnFreePupBuffer[b]; END; END; -- initialization EFTPSetRecvTimeout[1000]; END.