/*************************************************************************** Name : ECM.C Comment : Contains the ECM T30 routines Revision Log Date Name Description -------- ----- --------------------------------------------------------- ***************************************************************************/ #include "prep.h" #include "efaxcb.h" #include "t30.h" #include "hdlc.h" #include "debug.h" ///RSL #include "glbproto.h" #define faxTlog(m) DEBUGMSG(ZONE_ECM, m) #define FILEID FILEID_ECM typedef enum { ECMRECVOK_OK, ECMRECVOK_BADFR, ECMRECVOK_ABORT } ECMRECVOK; /****************** begin prototypes from ecm.c *****************/ IFR RNR_RRLoop(PThrdGlbl pTG); IFR CTC_RespRecvd(PThrdGlbl pTG, USHORT uBaud); BOOL Recv_NotReadyLoop(PThrdGlbl pTG, IFR ifrFirst, IFR ifrLast); BOOL FillInFrames(PThrdGlbl pTG, USHORT N, LONG sBufSize, USHORT uDataSize); ECMRECVOK ECMRecvOK(PThrdGlbl pTG); /***************** end of prototypes from ecm.c *****************/ BYTE RCP[3] = { 0xFF, 0x03, 0x86 }; ET30ACTION ECMPhaseC(PThrdGlbl pTG, BOOL fReTx) { USHORT uFrameNum, uFramesSent, uLim; SWORD swRet; ULONG lTotalLen=0; LPBYTE lpPPRMask; LPBUFFER lpbf=0; USHORT uMod; /******** Transmitter ECM Phase C. Fig A-7/T.30 (sheet 1) ********/ /*** switch(action = Params.lpfnWhatNext(pTG, eventSTARTSEND)) { case actionCONTINUE: break; case actionDCN: case actionHANGUP: return action; case actionERROR: return action; // goto PhaseLoop & exit default: return BadAction(action); } ***/ pTG->ECM.uFrameSize = ProtGetECMFrameSize(pTG); BG_CHK(pTG->ECM.uFrameSize==6 || pTG->ECM.uFrameSize==8); // already done in WhatNext // uSize = (1 << pTG->ECM.uFrameSize); // ICommSetSendMode(TRUE, uSize+ECM_EXTRA, uSize, TRUE); if(fReTx) { lpPPRMask = ProtGetRetransmitMask(pTG); } else { pTG->ECM.uPPRCount = 0; if(pTG->ECM.fEndOfPage) { pTG->ECM.SendPageCount++; pTG->ECM.SendBlockCount = 1; pTG->ECM.dwPageSize=0; faxTlog((SZMOD "Waiting for Startpage in ECM at 0x%08lx\r\n", GetTickCount())); DEBUGSTMT(IFProcProfile((HTASK)(-1), TRUE)); // Callback to open file to send. Doesn't return any data if((swRet=GetSendBuf(pTG, 0, SEND_STARTPAGE)) != SEND_OK) { ERRMSG((SZMOD "<> Nonzero return %d from SendProc at Start Page\r\n", swRet)); // return actionDCN; return actionERROR; } DEBUGSTMT(IFProcProfile((HTASK)(-1), FALSE)); faxTlog((SZMOD "Got Startpage in ECM at 0x%08lx\r\n", GetTickCount())); } else { pTG->ECM.SendBlockCount++; faxTlog((SZMOD "Waiting for Startblock in ECM at 0x%08lx\r\n", GetTickCount())); DEBUGSTMT(IFProcProfile((HTASK)(-1), TRUE)); // Callback to open file to send. Doesn't return any data if((swRet=GetSendBuf(pTG, 0, SEND_STARTBLOCK)) != SEND_OK) { ERRMSG((SZMOD "<> Nonzero return %d from SendProc at Start Page\r\n", swRet)); // return actionDCN; return actionERROR; } DEBUGSTMT(IFProcProfile((HTASK)(-1), FALSE)); faxTlog((SZMOD "Got Startblock in ECM at 0x%08lx\r\n", GetTickCount())); } } faxTlog((SZMOD "Starting ECM Partial Page SEND.......P=%d B=%d ReTx=%d\r\n", pTG->ECM.SendPageCount, pTG->ECM.SendBlockCount, fReTx)); if(fReTx) ICommStatus(pTG, T30STATS_RESEND_ECM, pTG->ECM.SendPageCount, 0, pTG->ECM.SendBlockCount); uMod = ProtGetSendMod(pTG); if(uMod >= V17_START && !pTG->ECM.fSentCTC) uMod |= ST_FLAG; pTG->ECM.fSentCTC = FALSE; // here we should use a small timeout (100ms?) and if it fails, // should go back to sending the previous V21 frame (which could be DCS // or MPS or whatever, which is why it gets complicated & we havn't // done it!). Meanwhile use a long timeout, ignore return value // and send anyway. if(!ModemRecvSilence(pTG, pTG->Params.hModem, RECV_PHASEC_PAUSE, LONG_RECVSILENCE_TIMEOUT)) { ERRMSG((SZMOD "<> ECM Pix RecvSilence(%d, %d) FAILED!!!\r\n", RECV_PHASEC_PAUSE, LONG_RECVSILENCE_TIMEOUT)); } if(!ModemSendMode(pTG, pTG->Params.hModem, uMod, TRUE, ifrECMPIX)) { ERRMSG((SZMOD "<> ModemSendMode failed in Tx ECM PhaseC\r\n")); ICommFailureCode(pTG, T30FAILSE_SENDMODE_PHASEC); BG_CHK(FALSE); return actionERROR; } #ifdef IFAX BroadcastMessage(pTG, IF_PSIFAX_DATAMODE, (PSIFAX_SEND|PSIFAX_ECM|(fReTx ? PSIFAX_RESEND : 0)), (uMod & (~ST_FLAG))); #endif faxTlog((SZMOD "SENDING ECM Page Data.....\r\n")); FComCriticalNeg(pTG, FALSE); uLim = (fReTx ? pTG->ECM.SendFrameCount : 256); BG_CHK(uLim); BG_CHK(lpbf == 0); for(uFrameNum=0, uFramesSent=0, lTotalLen=0, swRet=0; uFrameNumECM.uFrameSize <=8); // shift below won't ovf 16 bits BG_CHK(lpbf == 0); swRet = GetSendBuf(pTG, &lpbf, (fReTx ? ((SLONG)(uFrameNum << pTG->ECM.uFrameSize)) : SEND_SEQ)); if(swRet == SEND_ERROR) { ERRMSG((SZMOD "<> Error return from SendProc in ECM retransmit\r\n")); BG_CHK(lpbf == 0); // return actionDCN; // goto NodeC; return actionERROR; } else if(swRet == SEND_EOF) { BG_CHK(lpbf == 0); if(!fReTx) break; else { BG_CHK(FALSE); ICommFailureCode(pTG, T30FAILSE_PHASEC_RETX_EOF); return actionDCN; } } BG_CHK(swRet == SEND_OK); BG_CHK(lpbf); BG_CHK(lpbf->lpbBegBuf+4 == lpbf->lpbBegData); lpbf->lpbBegBuf[0] = 0xFF; lpbf->lpbBegBuf[1] = 0x03; lpbf->lpbBegBuf[2] = 0x06; lpbf->lpbBegBuf[3] = (BYTE) uFrameNum; lpbf->lpbBegData -= 4; lpbf->wLengthData += 4; lTotalLen += lpbf->wLengthData; if(!ModemSendMem(pTG, pTG->Params.hModem, lpbf->lpbBegData, lpbf->wLengthData, SEND_ENDFRAME)) { ERRMSG((SZMOD "<> DataWrite Timeout in ECM Phase C\r\n")); ICommFailureCode(pTG, T30FAILSE_MODEMSEND_PHASEC); BG_CHK(FALSE); return actionERROR; // goto error; } // faxTlog((SZMOD "Freeing 0x%08lx in ECM\r\n", lpbf)); if(!MyFreeBuf(pTG, lpbf)) { ERRMSG((SZMOD "<> FReeBuf failed in ECM Phase C\r\n")); ICommFailureCode(pTG, T30FAILSE_FREEBUF_PHASEC); BG_CHK(FALSE); return actionERROR; // goto error; } lpbf = 0; uFramesSent++; } } if( !ModemSendMem(pTG, pTG->Params.hModem, RCP, 3, SEND_ENDFRAME) || !ModemSendMem(pTG, pTG->Params.hModem, RCP, 3, SEND_ENDFRAME) || !ModemSendMem(pTG, pTG->Params.hModem, RCP, 3, SEND_ENDFRAME|SEND_FINAL)) { ERRMSG((SZMOD "<> DataWrite Timeout on RCPs\r\n")); ICommFailureCode(pTG, T30FAILSE_MODEMSEND_ENDPHASEC); BG_CHK(FALSE); return actionERROR; // goto error; } /*** if(!ModemDrain()) return FALSE; ***/ FComCriticalNeg(pTG, TRUE); faxTlog((SZMOD "Page Send Done.....len=(%ld, 0x%08x)\r\n", lTotalLen, lTotalLen)); pTG->ECM.FramesSent = uFramesSent; if(!fReTx) { BG_CHK(lTotalLen>=(ULONG)uFramesSent*4); pTG->ECM.dwPageSize+= (lTotalLen-uFramesSent*4); // 4-bytes of framing data pTG->ECM.SendFrameCount = uFrameNum; switch(GetSendBuf(pTG, 0, SEND_QUERYENDPAGE)) { case SEND_OK: pTG->ECM.fEndOfPage = FALSE; break; case SEND_EOF: pTG->ECM.fEndOfPage = TRUE; break; default: ERRMSG((SZMOD "<> Got SEND_ERROR from GetSendBuf at end of page\r\n")); return actionERROR; } } if(!pTG->ECM.FramesSent) { ERRMSG((SZMOD "<> Sent 0 frames--Bad PPR recvd or bad send file\r\n")); ICommFailureCode(pTG, T30FAILSE_BADPPR); return actionERROR; } pTG->T30.fSendAfterSend = TRUE; // ECM PhaseC/PIX--PPS-X return actionGONODE_V; } ET30ACTION ECMPhaseD(PThrdGlbl pTG) { USHORT uTryCount, i; ET30ACTION action; BYTE bPPSfif[3]; LPBYTE lpPPR; /******** Transmitter ECM Phase D. Fig A-8 to A-17/T.30 ********/ if(!pTG->ECM.fEndOfPage) { pTG->T30.ifrSend = ifrPPS_NULL; } else { switch(action = pTG->Params.lpfnWhatNext(pTG, eventPOSTPAGE)) { case actionSENDMPS: pTG->T30.ifrSend = ifrPPS_MPS; break; case actionSENDEOM: pTG->T30.ifrSend = ifrPPS_EOM; break; case actionSENDEOP: pTG->T30.ifrSend = ifrPPS_EOP; break; #ifdef PRI case actionSENDPRIMPS: pTG->T30.ifrSend = ifrPPS_PRI_MPS; break; case actionSENDPRIEOM: pTG->T30.ifrSend = ifrPPS_PRI_EOM; break; case actionSENDPRIEOP: pTG->T30.ifrSend = ifrPPS_PRI_EOP; break; #endif case actionERROR: return action; // goto PhaseLoop & exit default: return BadAction(pTG, action); } } bPPSfif[0] = pTG->ECM.SendPageCount-1; bPPSfif[1] = pTG->ECM.SendBlockCount-1; BG_CHK(pTG->ECM.FramesSent && pTG->ECM.FramesSent<=256); // bPPSfif[2] = pTG->ECM.SendFrameCount-1; // don't know which one..!! bPPSfif[2] = pTG->ECM.FramesSent-1; // this one! For sure for(uTryCount=0 ;;) { SendSingleFrame(pTG, pTG->T30.ifrSend, bPPSfif, 3, 1); echoretry: pTG->T30.ifrResp = GetResponse(pTG, ifrPPSresponse); // if we hear our own frame, try to recv again. DONT retransmit! if(pTG->T30.ifrResp==pTG->T30.ifrSend) { ECHOMSG(pTG->T30.ifrResp); goto echoretry; } if(pTG->T30.ifrResp != ifrNULL && pTG->T30.ifrResp != ifrBAD) break; if(++uTryCount >= 3) { ERRMSG((SZMOD "<> ECM 3 PostPages, No reply\r\n")); ICommFailureCode(pTG, T30FAILSE_3POSTPAGE_NOREPLY); return actionDCN; } } switch(pTG->T30.ifrResp) { case ifrBAD: case ifrNULL: BG_CHK(FALSE); // should never get here ICommFailureCode(pTG, T30FAILSE_BUG2); return actionERROR; // in case they do :-) case ifrDCN: ERRMSG((SZMOD "<> Got ifrDCN from GetResponse after sending post-page command\r\n")); ICommFailureCode(pTG, T30FAILSE_POSTPAGE_DCN); return actionHANGUP; case ifrPPR: faxTlog((SZMOD "PPR (P=%d B=%d F=%d) Received: ", pTG->ECM.SendPageCount-1, pTG->ECM.SendBlockCount-1, pTG->ECM.FramesSent-1)); lpPPR = ProtGetRetransmitMask(pTG); #ifdef DEBUG for(i=0; i<32; i++) faxTlog((" %02x", lpPPR[i])); faxTlog(("]\r\n")); #endif //DEBUG if(++pTG->ECM.uPPRCount >= 4) goto FourthPPR; return actionGONODE_ECMRETRANSMIT; case ifrRNR: if((pTG->T30.ifrResp=RNR_RRLoop(pTG)) == ifrDCN) { ERRMSG((SZMOD "<> RR_RNR loop failed\r\n")); // ICommFailureCode already called in RR_RNRLoop() return actionDCN; } faxTlog((SZMOD "Got %d from RNR\r\n", pTG->T30.ifrResp)); break; } switch(pTG->T30.ifrResp) { case ifrPIP: case ifrPIN: # ifdef PRI return GONODE_E; # else ERRMSG((SZMOD "<> Procedure interrupts not supported\r\n")); // return actionERROR; // fallthru and treat like MCF pTG->T30.ifrResp = ifrMCF; # endif case ifrMCF: { WORD wSize = (WORD) (pTG->ECM.dwPageSize>>10); //Units are KB ICommStatus(pTG, T30STATS_CONFIRM_ECM, (USHORT) LOBYTE(wSize), (USHORT) HIBYTE(wSize), (USHORT)(pTG->ECM.SendPageCount&0xff)); ERRMSG((SZMOD "Sending T30STATS_CONFIRM_pTG->ECM. wSize=%u\r\n", (unsigned) wSize)); } action=pTG->Params.lpfnWhatNext(pTG, eventGOT_ECM_PPS_RESP, (UWORD)pTG->T30.ifrResp, (LPVOID)((DWORD)pTG->T30.ifrSend)); if(pTG->T30.ifrSend==ifrPPS_EOP && pTG->T30.ifrResp==ifrMCF && action==actionDCN) { ICommFailureCode(pTG, T30FAILSE_SUCCESS); return actionDCN_SUCCESS; } else return action; default: ERRMSG((SZMOD "<> Got UNKNOWN from GetResponse after sending post-page command\r\n")); ICommFailureCode(pTG, T30FAILSE_POSTPAGE_UNKNOWN); return actionDCN; } FourthPPR: action = pTG->Params.lpfnWhatNext(pTG, event4THPPR, (WORD)pTG->ECM.SendFrameCount, (DWORD)pTG->ECM.FramesSent); switch(action) { case actionGONODE_ECMRETRANSMIT: if(CTC_RespRecvd(pTG, ProtGetSendMod(pTG)) == ifrCTR) { pTG->ECM.uPPRCount = 0; pTG->ECM.fSentCTC = TRUE; return actionGONODE_ECMRETRANSMIT; } else { ERRMSG((SZMOD "<> CTC-CTR failed\r\n")); // ICommFailureCode already called in CTC_RespRecvd(pTG) return actionDCN; } case actionSENDEOR_EOP: case actionDCN: case actionERROR: return action; default: return BadAction(pTG, action); // none of the EOR stuff // return actionDCN; } } ET30ACTION ECMSendEOR_EOP(PThrdGlbl pTG) { // dont set new ICommFailure codes in this function. We have already // set the 'too many retries' code USHORT uTryCount; for(uTryCount=0 ;;) { RETAILMSG((SZMOD "<> Sending EOR-EOP\r\n")); pTG->T30.ifrSend = ifrEOR_EOP; SendEOR_EOP(pTG); echoretry: pTG->T30.ifrResp = GetResponse(pTG, ifrEORresponse); // if we hear our own frame, try to recv again. DONT retransmit! if(pTG->T30.ifrResp==pTG->T30.ifrSend) { ECHOMSG(pTG->T30.ifrResp); goto echoretry; } if(pTG->T30.ifrResp != ifrNULL && pTG->T30.ifrResp != ifrBAD) break; if(++uTryCount >= 3) { ERRMSG((SZMOD "<> ECM 3 EORs, No reply\r\n")); return actionDCN; } } switch(pTG->T30.ifrResp) { case ifrBAD: case ifrNULL: BG_CHK(FALSE); // should never get here return actionERROR; // in case they do :-) case ifrDCN: ERRMSG((SZMOD "<> Got ifrDCN from GetResponse after sending EOR\r\n")); return actionHANGUP; case ifrRNR: RETAILMSG((SZMOD "<> Sent EOR-EOP, got RNR\r\n")); if((pTG->T30.ifrResp=RNR_RRLoop(pTG)) == ifrDCN) { ERRMSG((SZMOD "<> RR_RNR loop failed\r\n")); // ICommFailureCode already called in RR_RNRLoop(pTG) return actionDCN; } faxTlog((SZMOD "Got %d from RNR\r\n", pTG->T30.ifrResp)); break; } switch(pTG->T30.ifrResp) { case ifrERR: RETAILMSG((SZMOD "<> Sent EOR-EOP. Got ERR. Sending DCN\r\n")); return actionDCN; default: ERRMSG((SZMOD "<> Got UNKNOWN from GetResponse after sending EOR\r\n")); return actionDCN; } } // reduce this so that when externally measured it always ends up less // then the specified max of 65s, so we pass protocol conformance tests #define T5_TIMEOUT 62000L // 60s + 5s IFR RNR_RRLoop(PThrdGlbl pTG) { /** Flowchart is:- Enter this on getting an RNR. Then start T5, send RR, get response (standard ResponseRecvd routine). If no response, send RR again. Repeat 3 times. If RNR response recvd, send RR again & repeat, until T5 expires, upon which send DCN & hangup. This routine returns ifrDCN, implying teh caller should go to NodeC, or ifrXXX, which is used as the response to be analysed further down the chart. Never returns ifrNULL **/ UWORD i; IFR ifr; TstartTimeOut(pTG, &(pTG->ECM.toT5), T5_TIMEOUT); do { for(i=0; i<3; i++) { if(!TcheckTimeOut(pTG, &(pTG->ECM.toT5))) { ERRMSG((SZMOD "<> T5 timeout on Sender\r\n")); ICommFailureCode(pTG, T30FAILSE_RR_T5); return ifrDCN; // T5 timed out } SendRR(pTG); echoretry: ifr = GetResponse(pTG, ifrRRresponse); // if we hear our own frame, try to recv again. DONT retransmit! if(ifr==ifrRR) { ECHOMSG(ifr); goto echoretry; } if(ifr!=ifrNULL && ifr!=ifrBAD) break; // on ifrNULL (T4 timeout) we resend RR & try again -- 3 times } } while(ifr == ifrRNR); // BG_CHK(ifr!=ifrRNR && ifr!=ifrNULL && ifr!=ifrBAD && ifr!=ifrTIMEOUT); // can get BAD or NULL here when i=3 BG_CHK(ifr!=ifrRNR && ifr!=ifrTIMEOUT); if(ifr == ifrDCN) { ERRMSG((SZMOD "<> Got DCN in response to RR\r\n")); ICommFailureCode(pTG, T30FAILSE_RR_DCN); } if(ifr==ifrBAD || ifr==ifrNULL) { BG_CHK(i==3); ERRMSG((SZMOD "<> No response to RR 3 times\r\n")); ICommFailureCode(pTG, T30FAILSE_RR_3xT4); ifr=ifrDCN; // same as T5 timeout } return ifr; // finally got a non-RNR response // return ifrDCN or ifrXXXX (not RNR) } IFR CTC_RespRecvd(PThrdGlbl pTG, USHORT uBaud) { UWORD i; IFR ifr = ifrDCN; BYTE bCTCfif[2]; BG_CHK((uBaud & (~0x0F)) == 0); bCTCfif[0] = 0; bCTCfif[1] = (uBaud << 2); for(i=0; i<3; i++) { SendCTC (pTG, bCTCfif); echoretry: ifr = GetResponse(pTG, ifrCTCresponse); // if we hear our own frame, try to recv again. DONT retransmit! if(ifr==ifrCTC) { ECHOMSG(ifr); goto echoretry; } if(ifr!=ifrNULL && ifr!=ifrBAD) break; // on ifrNULL (T4 timeout) we resend RR & try again -- 3 times } if(ifr==ifrNULL || ifr==ifrBAD) { BG_CHK(i == 3); ERRMSG((SZMOD "<> No response to CTC 3 times\r\n")); ICommFailureCode(pTG, T30FAILSE_CTC_3xT4); ifr = ifrDCN; } else if(ifr != ifrCTR) { ERRMSG((SZMOD "<> Bad response CTC\r\n")); ICommFailureCode(pTG, T30FAILSE_CTC_UNKNOWN); } return ifr; // return ifrDCN or ifrXXXX } ET30ACTION ECMRecvPhaseD ( PThrdGlbl pTG) { DWORD CurrPPS; ET30ACTION action; switch(pTG->T30.ifrCommand) { case ifrPRI_MPS: case ifrPRI_EOM: case ifrPRI_EOP: # ifdef PRI return actionGONODE_RECVPRIQ; # else pTG->T30.ifrCommand = pTG->T30.ifrCommand-ifrPRI_MPS+ifrMPS; break; # endif case ifrPPS_PRI_MPS: case ifrPPS_PRI_EOM: case ifrPPS_PRI_EOP: # ifdef PRI goto RecvPPSPRIQ; # else pTG->T30.ifrCommand = pTG->T30.ifrCommand-ifrPPS_PRI_MPS+ifrPPS_MPS; break; # endif case ifrEOR_PRI_MPS: case ifrEOR_PRI_EOM: case ifrEOR_PRI_EOP: # ifdef PRI goto RecvEORPRIQ; # else pTG->T30.ifrCommand = pTG->T30.ifrCommand-ifrEOR_PRI_MPS+ifrEOR_MPS; break; # endif } UsePrevCommand: switch(pTG->T30.ifrCommand) { case ifrCTC: EnterPageCrit(); //start CTR--PAGE critsection pTG->ECM.fRecvdCTC = TRUE; SendCTR(pTG); ECHOPROTECT(ifrCTR, modeECMRETX); return actionGONODE_RECVECMRETRANSMIT; case ifrPPS_NULL: case ifrPPS_MPS: case ifrPPS_EOM: case ifrPPS_EOP: // saved for PPS--RNR--RR--MCF(missed)--RR--MCF sequences pTG->ECM.ifrPrevCommand = pTG->T30.ifrCommand; //////BugFix 396////// CurrPPS = ProtGetPPS(pTG); if(pTG->ECM.ifrPrevResponse==ifrMCF && _fmemcmp(pTG->ECM.bPrevPPS, (LPBYTE)(&CurrPPS), 4)==0) goto GoSendMCF; _fmemcpy(pTG->ECM.bPrevPPS, (LPBYTE)(&CurrPPS), 4); pTG->ECM.ifrPrevResponse = 0; //////BugFix 396////// switch(ECMRecvOK(pTG)) { default: case ECMRECVOK_ABORT: return actionERROR; case ECMRECVOK_OK: break; case ECMRECVOK_BADFR: EnterPageCrit(); //start PPR--PAGE critsection SendPPR(pTG, pTG->ECM.bRecvBadFrameMask); ECHOPROTECT(ifrPPR, modeECMRETX); return actionGONODE_RECVECMRETRANSMIT; } // now we can mark eop here // #ifdef PRI is on, this won't work (e.g. if the guy sends // PRI_MPS at end of page, then this needs to be called but it won't. if(pTG->T30.ifrCommand != ifrPPS_NULL && !pTG->ECM.fRecvEndOfPage) // so we won't call this twice { // RECV_ENDDOC if PPS_EOP or PPS_EOM PutRecvBuf(pTG, NULL, ((pTG->T30.ifrCommand==ifrPPS_MPS) ? RECV_ENDPAGE : RECV_ENDDOC)); // ignore error/abort. We'll catch it soon enough pTG->ECM.fRecvEndOfPage = TRUE; } if(!Recv_NotReadyLoop(pTG, ifrPPS_FIRST, ifrPPS_LAST)) { ICommFailureCode(pTG, T30FAILRE_PPS_RNR_LOOP); return actionHANGUP; } if(pTG->T30.ifrCommand == ifrPPS_NULL) { action = actionSENDMCF; } else switch(action = pTG->Params.lpfnWhatNext(pTG, eventRECVPOSTPAGECMD,(WORD)pTG->T30.ifrCommand)) { case actionSENDMCF: break; case actionSENDRTN: case actionHANGUP: case actionERROR: default: return BadAction(pTG, action); } #ifdef PRI if(pTG->T30.ifrCommand != ifrPPS_NULL) { if((action = pTG->Params.lpfnWhatNext(pTG, eventQUERYLOCALINT))==actionTRUE) { ECHOPROTECT(ifrPIP, 0); SendPIP(pTG); break; } else if(action == actionERROR) return action; } #endif //PRI GoSendMCF: pTG->ECM.ifrPrevResponse = ifrMCF; if(pTG->T30.ifrCommand == ifrPPS_NULL || pTG->T30.ifrCommand == ifrPPS_MPS) { EnterPageCrit(); //start ECM MCF--PAGE critsection ECHOPROTECT(ifrMCF, modeECM); SendMCF(pTG); return actionGONODE_RECVPHASEC; } ECHOPROTECT(ifrMCF, 0); SendMCF(pTG); if(pTG->T30.ifrCommand==ifrPPS_EOP) return actionNODEF_SUCCESS; else break; case ifrEOR_NULL: case ifrEOR_MPS: case ifrEOR_EOM: case ifrEOR_EOP: // saved for EOR--RNR--RR--ERR(missed)--RR--ERR sequences pTG->ECM.ifrPrevCommand = pTG->T30.ifrCommand; if(pTG->T30.ifrCommand!=ifrEOR_NULL && !pTG->ECM.fRecvEndOfPage) // so we won't call this twice { // RECV_ENDDOC if EOR_EOP or EOR_EOM PutRecvBuf(pTG, NULL, ((pTG->T30.ifrCommand==ifrEOR_MPS) ? RECV_ENDPAGE : RECV_ENDDOC)); // ignore error/abort. We'll catch it soon enough pTG->ECM.fRecvEndOfPage = TRUE; } if(!Recv_NotReadyLoop(pTG, ifrEOR_FIRST, ifrEOR_LAST)) { ICommFailureCode(pTG, T30FAILRE_EOR_RNR_LOOP); return actionHANGUP; } #ifdef PRI if(pTG->T30.ifrCommand != ifrPPS_NULL) { if((action = pTG->Params.lpfnWhatNext(pTG, eventQUERYLOCALINT))==actionTRUE) { ECHOPROTECT(ifrPIN, 0); SendPIN(pTG); break; } else if(action == actionERROR) return action; } #endif //PRI if(pTG->T30.ifrCommand == ifrEOR_NULL || pTG->T30.ifrCommand == ifrEOR_MPS) { EnterPageCrit(); //start ERR--PAGE critsection ECHOPROTECT(ifrERR, modeECM); SendERR(pTG); return actionGONODE_RECVPHASEC; } ECHOPROTECT(ifrERR, 0); SendERR(pTG); break; case ifrRR: if(pTG->ECM.ifrPrevCommand) { pTG->T30.ifrCommand = pTG->ECM.ifrPrevCommand; goto UsePrevCommand; } else { ERRMSG((SZMOD "<> ignoring ERR at weird time\r\n")); break; } default: ERRMSG((SZMOD "<> Random Command frame received=%d\r\n", pTG->T30.ifrCommand)); break; // ignore it } return actionGONODE_F; #ifdef PRI RecvPRIPPS: switch(ECMRecvOK(pTG)) { default: ECMRECVOK_ABORT: return actionERROR; ECMRECVOK_OK: break; ECMRECVOK_BADFR: EnterPageCrit(); //start PRI PPR--PAGE critsection ECHOPROTECT(ifrPPR, modeECMRETX); SendPPR(pTG, pTG->ECM.bRecvBadFrameMask); return actionGONODE_RECVECMRETRANSMIT; } RecvPRIEOR: switch(action = pTG->Params.lpfnWhatNext(pTG, eventGOTPRIQ, (WORD)pTG->T30.ifrCommand)) { case actionERROR: break; // return to PhaseLoop case actionHANGUP: break; case actionGONODE_F: break; case actionSENDPIP: pTG->T30.ifrSend=ifrPIP; return actionGOVOICE; case actionSENDPIN: pTG->T30.ifrSend=ifrPIN; return actionGOVOICE; case actionGO_RECVPOSTPAGE: if(pTG->T30.ifrCommand >= ifrPPS_PRI_FIRST && pTG->T30.ifrCommand <= ifrPPS_PRI_LAST) { pTG->T30.ifrCommand = pTG->T30.ifrCommand-ifrPPS_PRI_MPS+ifrPPS_MPS; goto NodeVIIIa; } else if(pTG->T30.ifrCommand >= ifrEOR_PRI_FIRST && pTG->T30.ifrCommand <= ifrEOR_PRI_LAST) { pTG->T30.ifrCommand = pTG->T30.ifrCommand-ifrEOR_PRI_MPS+ifrEOR_MPS; goto NodeIXa; } else { BG_CHK(FALSE); } } return action; #endif //PRI } BOOL Recv_NotReadyLoop(PThrdGlbl pTG, IFR ifrFirst, IFR ifrLast) { IFR ifrCommand; // OBSOLETE:- (??) // this is wrong. We should exit only on DCN I think. // We should definitely loop again on BAD // dunno about TIMEOUT and NULL do { if(ICommRecvBufIsEmpty(pTG)) return TRUE; // sleep for a while, to give FMTRES etc some CPU cycles to catch // up with us. Can sleep just before sending response, since sender // waits upto 3 (6?) secs for it. #ifndef FILET30 IFProcSleep(1000); #endif SendRNR(pTG); echoretry: ifrCommand = GetCommand(pTG, ifrRNRcommand); // if we hear our own frame, try to recv again. DONT retransmit! if(ifrCommand==ifrRNR) { ECHOMSG(ifrCommand); goto echoretry; } } while( ifrCommand==ifrNULL || ifrCommand==ifrBAD || ifrCommand == ifrRR || (ifrCommand >= ifrFirst && ifrCommand <= ifrLast) ); // This means we exit on any valid frame _except_ RR and the PPS-X // or EOR-X we were expecting. We also exit on ifrTIMEOUT which is // when the GetCommand() times out without getting any CONNECT. // Otherwise we'd end up sending an MCF after timing out which // Ricoh's protocol tester doesnt like. (Ricoh's bug numbbers B3-0106) return FALSE; // just hangs up after this } ECMRECVOK ECMRecvOK(PThrdGlbl pTG) { USHORT N, i; N = ProtGetRecvECMFrameCount(pTG); // N==actual number of frames, i.e. PPR value+1 faxTlog((SZMOD "ECMRecvChk %d frames\r\n", N)); if(!N || N>256) { BG_CHK(FALSE); return ECMRECVOK_ABORT; } for(i=0; iECM.bRecvBadFrameMask[i/8] & (1 << (i%8))) { faxTlog((SZMOD "ECMRecvChk--bad fr=%d\r\n", i)); return ECMRECVOK_BADFR; } } return ECMRECVOK_OK; } ET30ACTION ECMRecvPhaseC(PThrdGlbl pTG, BOOL fRetrans) { /******** Receiver ECM Phase C. Fig A-7/T.30 (sheet 1) ********/ ULONG lTotalLen=0; USHORT uRet, uFrameNum, uRCPCount, uSize; USHORT uRecvFrame, uMod; LPBUFFER lpbf; LPBUFFER lpbfShort; // There is a race between sending the CFR and sending out an // +FRM=xx command, so we want to do it ASAP. pTG->ECM.uFrameSize = ProtGetRecvECMFrameSize(pTG); BG_CHK(pTG->ECM.uFrameSize==6 || pTG->ECM.uFrameSize==8); uSize = (1 << pTG->ECM.uFrameSize); uMod = ProtGetRecvMod(pTG); if(uMod >= V17_START && !pTG->ECM.fRecvdCTC) uMod |= ST_FLAG; pTG->T30.sRecvBufSize = MY_ECMBUF_SIZE; if((uRet = ModemECMRecvMode(pTG, pTG->Params.hModem, uMod, PHASEC_TIMEOUT)) != RECV_OK) { ExitPageCrit(); ERRMSG((SZMOD "<> ECMPhC: RecvMode ret=%d\r\n", uRet)); if(!fRetrans) { // in case we miss the page entirely // and jump into PhaseD directly // init bad frame bitmask to all-bad memset(pTG->ECM.bRecvBadFrameMask, 0xFF, 32); // all bad // init the status vars for the PPS/EOR--(RR-RNR)*--PPR/MCF loop pTG->ECM.ifrPrevCommand = 0; // reset the FrameInThisBlock count. Reset only on // non-retransmit. Set when first PPS is recvd ProtResetRecvECMFrameCount(pTG); if(pTG->ECM.fRecvEndOfPage) // as opposed to end-of-block uRet = (USHORT)PutRecvBuf(pTG, NULL, RECV_STARTPAGE); else uRet = (USHORT)PutRecvBuf(pTG, NULL, RECV_STARTBLOCK); pTG->ECM.fRecvEndOfPage = FALSE; if(!uRet) { ERRMSG((SZMOD "<> Got Error from PutRecvBuf at StartPage/Block\r\n")); return actionERROR; } pTG->EchoProtect.modePrevRecv = modeECM; } else pTG->EchoProtect.modePrevRecv = modeECMRETX; // set global flag if we got WRONGMODE pTG->EchoProtect.fGotWrongMode = (uRet==RECV_WRONGMODE); // elim flush--does no good & wastes 10ms // ModemFlush(pTG->Params.hModem); return actionGONODE_F; // try to get 300bps command instead } ExitPageCrit(); // as soon as we get good carrier ZERO the pTG->EchoProtect state _fmemset(&pTG->EchoProtect, 0, sizeof(pTG->EchoProtect)); // reset this flag AFTER we successfully recv LONG_TRAIN. We may get an // echo of our last command, go to NodeF, reject the echo and loop back // here. In that case we want to retry LONG train, otherwise we always // croak after a CTC pTG->ECM.fRecvdCTC = FALSE; #ifdef IFAX BroadcastMessage(pTG, IF_PSIFAX_DATAMODE, (PSIFAX_RECV|PSIFAX_ECM|(fRetrans ? PSIFAX_RERECV : 0)), (uMod & (~ST_FLAG))); #endif faxTlog((SZMOD "RECEIVING ECM Page.......\r\n")); if(fRetrans) ICommStatus(pTG, T30STATR_RERECV_ECM, 0, 0, 0); // Turn yielding on *after* entering receive mode safely! FComCriticalNeg(pTG, FALSE); /*** switch(action = pTG->Params.lpfnWhatNext(eventSTARTRECV)) { case actionCONTINUE: break; case actionDCN: ERRMSG((SZMOD "<> Got actionDCN from eventSTARTRECV\r\n")); return actionDCN; // goto NodeC; case actionHANGUP: ERRMSG((SZMOD "<> Got actionHANGUP from eventSTARTRECV\r\n")); return action; // goto NodeB; case actionERROR: return action; default: return BadAction(action); } ***/ if(!fRetrans) { memset(pTG->ECM.bRecvBadFrameMask, 0xFF, 32); ProtResetRecvECMFrameCount(pTG); if(pTG->ECM.fRecvEndOfPage) // as opposed to end-of-block uRet = (USHORT)PutRecvBuf(pTG, NULL, RECV_STARTPAGE); else uRet = (USHORT)PutRecvBuf(pTG, NULL, RECV_STARTBLOCK); if(!uRet) { ERRMSG((SZMOD "<> Got Error from PutRecvBuf at StartPage/Block\r\n")); return actionERROR; } } pTG->ECM.ifrPrevCommand = 0; pTG->ECM.fRecvEndOfPage = FALSE; pTG->ECM.ifrPrevResponse = 0; DEBUGSTMT(if(fRetrans) D_PSIFAXCheckMask(pTG, pTG->ECM.bRecvBadFrameMask)); // make it large, in case of large buffers & slow modems #define READ_TIMEOUT 25000 lpbfShort=0; for(uFrameNum=0, lpbf=0, lTotalLen=0, uRCPCount=0, uRet=RECV_OK; uRet!=RECV_EOF;) { if(lpbf) { faxTlog((SZMOD "<> ECM RecvPhC: Freeing leftover Buf 0x%08lx inside loop\r\n", lpbf)); MyFreeBuf(pTG, lpbf); // need to free after bad frames etc } lpbf = 0; uRet=ModemRecvBuf(pTG, pTG->Params.hModem, TRUE, &lpbf, READ_TIMEOUT); if(uRet == RECV_BADFRAME) { ERRMSG((SZMOD "<> ModemRecvBuf returns BADFRAME\r\n")); continue; } else if(uRet == RECV_EOF) { BG_CHK(lpbf == 0); if(lpbfShort) { lpbf = lpbfShort; lpbfShort = 0; if(fRetrans && ((USHORT)(lpbf->lpbBegData[3])+1 < ProtGetRecvECMFrameCount(pTG))) { RETAILMSG((SZMOD "<> DISCARDING Short but not-really-terminal FCD frame(N=%d). len=%d. PADDING to %d!!\r\n", lpbf->lpbBegData[3], lpbf->wLengthData, uSize+4)); IFBufFree(lpbf); break; } else { RETAILMSG((SZMOD "<> Short Terminal FCD frame(N=%d). len=%d. PADDING to %d!!\r\n", lpbf->lpbBegData[3], lpbf->wLengthData, uSize+4)); BG_CHK(lpbf->wLengthBuf >= uSize+4); _fmemset(lpbf->lpbBegData+lpbf->wLengthData, 0, (uSize+4-lpbf->wLengthData)); lpbf->wLengthData = uSize+4; goto skipchks; } } else break; } else if(uRet != RECV_OK) { ERRMSG((SZMOD "<> Got %d from RecvBuf in ECMREcvPhaseC\r\n", uRet)); break; } else if(lpbf==0) { // sometimes we get RECV_OK with no data. Treat same as bad frame continue; } BG_CHK(uRet==RECV_OK && lpbf && lpbf->lpbBegData); if(uRCPCount >= 3) { ERRMSG((SZMOD "<> Got a good frame after %d RCP\r\n", uRCPCount)); continue; } if( lpbf->lpbBegData[0] != 0xFF || lpbf->lpbBegData[1] != 0x03 || (lpbf->lpbBegData[2] & 0x7F) != 0x06) { ERRMSG((SZMOD "<> Bad frame (N=%d) not caught FCF=%02x!!\r\n", uFrameNum, lpbf->lpbBegData[2])); BG_CHK(FALSE); continue; } if(lpbf->lpbBegData[2] == 0x86) { // got RCP if(lpbf->wLengthData != 3) { ERRMSG((SZMOD "<> Bad RCP frame len=%d\r\n", lpbf->wLengthData)); BG_CHK(FALSE); continue; } uRCPCount++; faxTlog((SZMOD "Got %d RCP\r\n", uRCPCount)); #ifdef CL0 if(uRCPCount >= 3) { // tell modem that recv is done // but keep calling RecvMem until we get RECV_EOF ModemEndRecv(pTG, pTG->Params.hModem); } #endif //CL0 continue; } if(lpbfShort) { ERRMSG((SZMOD "<> Short FCD frame(N=%d). len=%d DISCARDING\r\n", lpbfShort->lpbBegData[3], lpbfShort->wLengthData)); MyFreeBuf(pTG, lpbfShort); lpbfShort = NULL; } if(lpbf->wLengthData > (uSize+4)) { ERRMSG((SZMOD "<> FCD frame too long(N=%d %d). len=%d. DISCARDING\r\n", uFrameNum, lpbf->lpbBegData[3], lpbf->wLengthData)); continue; } else if(lpbf->wLengthData < (uSize+4)) { RETAILMSG((SZMOD "<> Short FCD frame(N=%d %d). len=%d. Storing\r\n", uFrameNum, lpbf->lpbBegData[3], lpbf->wLengthData)); BG_CHK(lpbfShort==0); lpbfShort = lpbf; lpbf = NULL; continue; } skipchks: BG_CHK(lpbf->lpbBegData[2] == 0x06); BG_CHK(lpbf->wLengthData == (uSize+4)); uRecvFrame = lpbf->lpbBegData[3]; lpbf->lpbBegData += 4; lpbf->wLengthData -= 4; if(!fRetrans) { if(uFrameNum > uRecvFrame) { ERRMSG((SZMOD "<> Out of order frame. Got %d Looking for %d\r\n", uRecvFrame, uFrameNum)); BG_CHK(FALSE); // ignore this frame in non-debug mode continue; } else if(uFrameNum < uRecvFrame) { if(!FillInFrames(pTG, (USHORT)(uRecvFrame-uFrameNum), pTG->T30.sRecvBufSize, uSize)) { ERRMSG((SZMOD "<> Zero return from PutRecvBuf in FillInFrames\r\n")); return actionERROR; } } lTotalLen += lpbf->wLengthData; } uFrameNum = uRecvFrame; if(!fRetrans || (pTG->ECM.bRecvBadFrameMask[uFrameNum/8] & (1 << (uFrameNum%8)))) { BG_CHK(uFrameNum < 256 && pTG->ECM.uFrameSize <=8); // shift below wont ovf 16 bits if(!PutRecvBuf(pTG, lpbf, (fRetrans ? ((SLONG)(uFrameNum << pTG->ECM.uFrameSize)) : RECV_SEQ))) { ERRMSG((SZMOD "<> Zero return from PutRecvBuf in page\r\n")); return actionERROR; } pTG->ECM.bRecvBadFrameMask[uFrameNum/8] &= ~(1 << (uFrameNum%8)); uFrameNum++; lpbf = 0; } } if(lpbf) { ERRMSG((SZMOD "<> ECMRecvPhC: Freeing leftover Buf 0x%08lx outside loop\r\n", lpbf)); MyFreeBuf(pTG, lpbf); // need to free after bad frames etc } if(uRet == RECV_EOF) { FComCriticalNeg(pTG, TRUE); // cant mark end of page until we know if end of page or block etc. // PutRecvBuf(NULL, RECV_ENDPAGE); // to mark end of Page } else { ERRMSG((SZMOD "<> DataRead Timeout or Error=%d\r\n", uRet)); BG_CHK(FALSE); ICommFailureCode(pTG, T30FAILRE_MODEMRECV_PHASEC); return actionERROR; // goto error; } faxTlog((SZMOD "ECM Page Recv Done.....len=(%ld, 0x%08x)\r\n", lTotalLen, lTotalLen)); ECHOPROTECT(0, 0); return actionGONODE_F; // goto NodeF; // get post-message command } BOOL FillInFrames(PThrdGlbl pTG, USHORT N, LONG sBufSize, USHORT uDataSize) { USHORT i; LPBUFFER lpbf; for(i=0; iT30.toBuf, WAITFORBUF_TIMEOUT); while(!(lpbf = MyAllocBuf(pTG, sBufSize))) { if(!TcheckTimeOut(pTG, &pTG->T30.toBuf)) { ERRMSG((SZMOD "<> Giving up on BufAlloc in T30-ECM after %ld millisecs\r\n", ((ULONG)WAITFORBUF_TIMEOUT))); BG_CHK(FALSE); return FALSE; } RETAILMSG((SZMOD "<> BufAlloc failed in T30-pTG->ECM. Trying again\r\n")); IFProcSleep(100); } #else if(!(lpbf = MyAllocBuf( pTG, sBufSize))) return FALSE; #endif lpbf->wLengthData = uDataSize; if(!PutRecvBuf(pTG, lpbf, RECV_SEQBAD)) { ERRMSG((SZMOD "<> Zero return from PutRecvBuf in page\r\n")); return FALSE; } } return TRUE; } #ifdef SWECM USHORT ModemECMRecvMode(PThrdGlbl pTG, HMODEM h, USHORT uMod, ULONG ulTimeout) { if(!SWECMRecvSetup(pTG, TRUE)) { BG_CHK(FALSE); return RECV_ERROR; } return ModemRecvMode(pTG, h, uMod, FALSE, ulTimeout, ifrPIX_SWECM); } #endif //SWECM