/***************************************************************************
 Name     :     FRAMING.C
 Comment  :
 Functions:     (see Prototypes just below)

        Copyright (c) Microsoft Corp. 1991 1992 1993

 Revision Log
 Date     Name  Description
 -------- ----- ---------------------------------------------------------
***************************************************************************/

#include "prep.h"

#include "encoder.h"
#include "decoder.h"

#include "class1.h"
#include "debug.h"


#include "glbproto.h"


#define         faxTlog(m)              DEBUGMSG(ZONE_SWFRAME, m)
#define         faxT2log(m)             DEBUGMSG(ZONE_SWFRAME2, m)
#define         FILEID                  FILEID_FRAMING


#ifdef DEBUG
#       define  ST_SWFRAME2(x)                  if(ZONE_SWFRAME2) { x; }
#else
#       define  ST_SWFRAME2(x)                  { }
#endif



BOOL FramingBufSetup(PThrdGlbl pTG, BOOL fOn);








BOOL SWFramingSendSetup(PThrdGlbl pTG, BOOL fOn)
{
        // If fOn=TRUE, Check that Recv framing is not ON.
        // Can't use Recv and Send framing simultaneously
        BG_CHK(!(fOn && pTG->Class1Modem.fRecvSWFraming));

        if(fOn)
        {
                BG_CHK(!pTG->Class1Modem.fSendSWFraming);
        }
        else if(!pTG->Class1Modem.fSendSWFraming)
        {
                TRACE((SZMOD "<<WARNING>> SWFramingSendSetup(FALSE) called when not inited. Ignoring\r\n"));
                return TRUE;
        }

        if(!FramingBufSetup(pTG, fOn))
                return FALSE;

        pTG->Class1Modem.fSendSWFraming = fOn;
        InitEncoder(pTG, pTG->Framing.EncodeState);
        return TRUE;
}













BOOL SWFramingRecvSetup(PThrdGlbl pTG, BOOL fOn)
{
        // if fOn=TRUE Check that Send framing is not ON.
        // Can't use Recv and Send framing simultaneously
        BG_CHK(!(fOn && pTG->Class1Modem.fSendSWFraming));

        if(fOn)
        {
                BG_CHK(!pTG->Class1Modem.fRecvSWFraming);
        }
        else if(!pTG->Class1Modem.fRecvSWFraming)
        {
                TRACE((SZMOD "<<WARNING>> SWFramingRecvSetup(FALSE) called when not inited. Ignoring\r\n"));
                return TRUE;
        }

        if(!FramingBufSetup(pTG, fOn))
                return FALSE;

        pTG->Class1Modem.fRecvSWFraming = fOn;
        InitDecoder(pTG, pTG->Framing.DecodeState);
        return TRUE;
}








BOOL FramingBufSetup(PThrdGlbl pTG, BOOL fOn)
{
        // UWORD uwJunk;

        if(fOn)
        {
#ifdef SMM
                pTG->Framing.lpbBuf = pTG->bStaticFramingBuf;
                pTG->Framing.cbBufSize = FRAMEBUFINITIALSIZE;
#else
#error NYI ERROR: Code Not Complete--Have to pick GMEM_ flags (FIXED? SHARE?)
                ...also have to call IFProcSetResFlags(hinstMe).....
                BG_CHK(pTG->Framing.lpbBuf==0);
                if(!(pTG->Framing.lpbBuf = IFMemAlloc(0, FRAMEBUFINITIALSIZE, &uwJunk)))
                {
                        ERRMSG((SZMOD "<<ERROR>> Out of global memory!!));
                        // iModemSetError(MODEMERR_RESOURCES, ERR_OUT_OF_MEMORY, 0);
                        return FALSE;
                }
                pTG->Framing.cbBufSize = uwJunk;
#endif
        }
        else
        {
                BG_CHK(fOn == FALSE);
#ifndef SMM
                BG_CHK(pTG->Framing.lpbBuf);
                IFMemFree(pTG->Framing.lpbBuf);
#endif
                pTG->Framing.lpbBuf = 0;
                pTG->Framing.cbBufSize = 0;
        }
        pTG->Framing.cbBufCount = 0;
        pTG->Framing.lpbBufSrc = pTG->Framing.lpbBuf;
        pTG->Framing.swEOF = 0;
        return TRUE;
}










BOOL SWFramingSendFrame(PThrdGlbl pTG, LPBYTE lpb, USHORT uCount, USHORT uFlags)
{
        WORD    wCRC;
        USHORT  cbDst;

        (MyDebugPrint(pTG, LOG_ALL, "iSW: lpb=%08lx c=%d f=%02x ", lpb, uCount, uFlags, lpb[0]));

        // always DLE-stuff here. Never zero-stuff
        BG_CHK(uFlags & SEND_ENDFRAME);
        BG_CHK(lpb && uCount);

        BG_CHK(uCount <= 260);  // can add stuff to realloc the Dst buffer
                                                                // to a bigger one as needed here

        wCRC = CalcCRC(pTG, lpb, uCount);
        faxT2log((SZMOD "CRC=%04x ", wCRC));

        cbDst = HDLC_Encode(pTG, lpb, uCount, pTG->Framing.lpbBuf, &pTG->Framing.EncodeState);
        faxT2log((SZMOD "D1=%d ", cbDst));

        cbDst += HDLC_Encode(pTG, (LPB)(&wCRC), 2, pTG->Framing.lpbBuf+cbDst, &pTG->Framing.EncodeState);
        faxT2log((SZMOD "D2=%d\r\n", cbDst));

        BG_CHK(cbDst + pTG->ModemParams.InterframeFlags <= pTG->Framing.cbBufSize);
        cbDst += HDLC_AddFlags(pTG, pTG->Framing.lpbBuf+cbDst, pTG->ModemParams.InterframeFlags, &pTG->Framing.EncodeState);
        faxT2log((SZMOD "D3=%d f=%02x l=%02x\r\n", cbDst, pTG->Framing.lpbBuf[0], pTG->Framing.lpbBuf[cbDst-1]));

        // always DLE-stuff here. Never zero-stuff
        if(!FComFilterAsyncWrite(pTG, pTG->Framing.lpbBuf, cbDst, FILTER_DLEONLY))
        {
                ERRMSG((SZMOD "<<ERROR>> DataWrite Timeout\r\n"));
                goto error;
        }

        if(uFlags & SEND_FINAL)
        {
                if(!SWFramingSendPostamble(pTG, pTG->Class1Modem.CurMod))
                        goto error;

#ifdef CL0
                if(pTG->ModemParams.Class == FAXCLASS0)
                {
                        if(!FComDirectAsyncWrite(pTG, bDLEETXOK, 8))
                                goto error;
                }
                else
#endif //CL0
                {
                        // if(!FComDirectAsyncWrite(bDLEETXCR, 3))
                        if(!FComDirectAsyncWrite(pTG, bDLEETX, 2))
                                goto error;
                }

                if(!iModemDrain(pTG))
                        goto error;

                SWFramingSendSetup(pTG, FALSE);
                FComOutFilterClose(pTG);
            FComOverlappedIO(pTG, FALSE);
                FComXon(pTG, FALSE);         // critical. End of PhaseC
                EndMode(pTG);
        }
        return TRUE;

error:
        SWFramingSendSetup(pTG, FALSE);
        FComOutFilterClose(pTG);
        FComOverlappedIO(pTG, FALSE);
        FComXon(pTG, FALSE);                 // critical. End of PhaseC (err)
        EndMode(pTG);
        return FALSE;
}







BOOL SWFramingSendFlags(PThrdGlbl pTG, USHORT uHowMany)
{
        int cb, i;      // must be signed

        (MyDebugPrint(pTG, LOG_ALL,  "SENDING ECM Flags (%d).....\r\n", uHowMany));

        for(i=0, cb = uHowMany; cb>0; i++, cb -= pTG->Framing.cbBufSize)
        {
                if(i<=1)
                        HDLC_AddFlags(pTG, pTG->Framing.lpbBuf, pTG->Framing.cbBufSize, &pTG->Framing.EncodeState);

                // always DLE-stuff here. Never zero-stuff
                if(!FComFilterAsyncWrite(pTG, pTG->Framing.lpbBuf,(USHORT) min((USHORT)pTG->Framing.cbBufSize, (USHORT)cb), FILTER_DLEONLY))
                {
                        ERRMSG((SZMOD "<<ERROR>> PreFlagDataWrite Timeout\r\n"));
                        return FALSE;
                }
        }
        return TRUE;
}
















USHORT SWFramingRecvFrame(PThrdGlbl pTG, LPBYTE lpb, USHORT cbMax, ULONG ulTimeout, USHORT far* lpcbRecv)
{
        USHORT  cbDst, cbProduce, cbConsume, uRet;
        LPB             lpbDst;

        (MyDebugPrint(pTG, LOG_ALL,  "iSW: Buf=%08lx Src=%08lx cbSrc=%d cbMax=%d\r\n", pTG->Framing.lpbBuf, pTG->Framing.lpbBufSrc, pTG->Framing.cbBufCount, cbMax));
/**/
        BG_CHK(pTG->Class1Modem.ModemMode == FRM);
        BG_CHK(pTG->Class1Modem.fRecvSWFraming);
        startTimeOut(pTG, &(pTG->Class1Modem.toRecv), ulTimeout);
/**/
        BG_CHK(lpb && cbMax && lpcbRecv);

        *lpcbRecv = 0;

        for(lpbDst=lpb, cbDst=cbMax ;; )
        {
                if(pTG->Framing.cbBufCount == 0)
                {
                        if(pTG->Framing.swEOF == 0)
                        {
                                pTG->Framing.lpbBufSrc = pTG->Framing.lpbBuf;
                                // 4th arg must be FALSE for Class1
                                pTG->Framing.cbBufCount = FComFilterReadBuf(pTG, pTG->Framing.lpbBufSrc, pTG->Framing.cbBufSize, &(pTG->Class1Modem.toRecv), FALSE, &pTG->Framing.swEOF);
                                faxT2log((SZMOD "f=%02x l=%02x\r\n", pTG->Framing.lpbBufSrc[0], pTG->Framing.lpbBufSrc[pTG->Framing.cbBufCount-1]));
                                if(pTG->Framing.swEOF == -1)
                                {

#ifdef CL0
                                        if(pTG->ModemParams.Class == FAXCLASS0)
                                        {
                                                TRACE((SZMOD "Got Class0 DLE-ETX\r\n"));
                                                pTG->Framing.swEOF = 1;
                                        }
                                        else
#endif //CL0
                                        {

                                                // See LONG comment under DDI.C ModemRecvData()
                                                // option (a)
                                                // ERRMSG((SZMOD "<<WARNING>> Got arbitrary DLE-ETX. Assuming END OF PAGE!!!\r\n"));
                                                // pTG->Framing.swEOF = 1;

                                                // option (b)
                                                ERRMSG((SZMOD "<<WARNING>> Got arbitrary DLE-ETX. Ignoring\r\n"));
                                                pTG->Framing.swEOF = 0;
                                        }
                                }
                                BG_CHK(pTG->Framing.swEOF == 0 || pTG->Framing.swEOF == 1 ||
                                                 pTG->Framing.swEOF == -2 || pTG->Framing.swEOF == -3);

                                // check for progress
                                BG_CHK(pTG->Framing.cbBufCount!=0 || pTG->Framing.swEOF!=0);
                        }
                        else
                        {
                                // pTG->Framing.swEOF != 0

                                BG_CHK(pTG->Framing.swEOF == 1 || pTG->Framing.swEOF == -2 || pTG->Framing.swEOF == -3);

                                if(cbDst != cbMax)
                                {
                                        ERRMSG((SZMOD "<<WARNING>> SWFramingRecv: Got EOF with partial frame %d bytes\r\n", cbMax-cbDst));
                                        ST_SWFRAME2(D_HexPrint(lpb, (USHORT)(cbMax-cbDst)));
                                        uRet = RECV_BADFRAME;
                                        goto done;
                                }


                                if(pTG->Framing.swEOF == 1)  // class1 eof
                                {
                                        SWFramingRecvSetup(pTG, FALSE);
                                        EndMode(pTG);
                                        uRet = RECV_EOF;
                                        goto done;
                                }
                                else if(pTG->Framing.swEOF < 0)      // error or timeout
                                {
                                        SWFramingRecvSetup(pTG, FALSE);
                                        EndMode(pTG);
                                        uRet = ((pTG->Framing.swEOF == -2) ? RECV_ERROR : RECV_TIMEOUT);
                                        goto done;
                                }
                        }
                }

                // cbDst=space left in destination                      lpbDst=start of space
                // pTG->Framing.cbBufCount=bytes left in source      pTG->Framing.lpbBufSrc=start of bytes

                cbConsume = HDLC_Decode(pTG, pTG->Framing.lpbBufSrc,(USHORT) min(pTG->Framing.cbBufCount, cbDst), lpbDst, &cbProduce, &pTG->Framing.DecodeState);
                BG_CHK(cbConsume <= pTG->Framing.cbBufCount && pTG->Framing.lpbBufSrc+cbConsume<=pTG->Framing.lpbBuf+pTG->Framing.cbBufSize);
                BG_CHK(cbProduce <= cbDst && lpbDst+cbProduce <= lpb+cbMax);


                faxT2log((SZMOD "iSW: C=%d P=%d fa=%d\r\n", cbConsume, cbProduce, pTG->Framing.DecodeState.flagabort));
                pTG->Framing.cbBufCount      -= cbConsume;
                pTG->Framing.lpbBufSrc       += cbConsume;
                cbDst   -= cbProduce;
                lpbDst  += cbProduce;

                // check for progress.
                BG_CHK(pTG->Framing.DecodeState.flagabort==FLAG ||           // exits below
                                        cbProduce || cbConsume ||                       // exits eventually
                                        pTG->Framing.cbBufCount==0);         // exits or continues above

                if(pTG->Framing.DecodeState.flagabort == FLAG)
                {
                        if((cbMax-cbDst)>=2 && (CalcCRC(pTG, lpb, (USHORT)(cbMax-cbDst)) == ((WORD)(~0xF0B8))) )
                        {
                                cbDst += 2;
                                if(cbMax <= cbDst)
                                {
                                        ERRMSG(("GOOD Frame BAD Length!! cbMax=%d cbDst=%d, lpb[0]=%02x lpb[1]=%02x, CRC=%04x\r\n",
                                                cbMax, cbDst, lpb[0], lpb[1], CalcCRC(pTG, lpb, (USHORT)(cbMax+2-cbDst))));
                                        BG_CHK(FALSE);  // can't get good frame of 0 length!
                                        // must return RECV_BADFRAME here otherwise we break.
                                        // In HDLC.C the (RECV_OK swRead==0) pair gets converted
                                        // to RECV_ERROR and we quit receiving. On some modems
                                        // this can happen often. See BUG#1684
                                        uRet = RECV_BADFRAME;
                                }
                                else
                                        uRet = RECV_OK;
                        }
                        else
                        {
                                BG_CHK(cbMax >= cbDst);
#ifdef DEBUG
                                if (cbMax-cbDst)
                                {
                                ERRMSG((SZMOD "<<WARNING>> BADFR: SWFrRecvFr: CbMax-cbDst = %d bytes\r\n", cbMax-cbDst));
                                }
#endif

                                ST_SWFRAME2(D_HexPrint(lpb, (USHORT)(cbMax-cbDst)));
                                uRet = RECV_BADFRAME;
                        }
                        goto done;
                }
                else if(pTG->Framing.DecodeState.flagabort == ABORT)
                {
                        lpbDst=lpb;     cbDst=cbMax;
                }
                else if(cbDst == 0)
                {
                        // bad frames can be very long
                        ERRMSG((SZMOD "<<WARNING>> SWFramingRecv: Overflow. Got %d chars -- no flag\r\n", cbMax));
                        uRet = RECV_BADFRAME;
                        goto done;
                }
        }

done:
        *lpcbRecv = cbMax-cbDst;
        (MyDebugPrint(pTG, LOG_ALL,  "xSW: uR=%d *lpcbR=%d\r\n", uRet, *lpcbRecv));
        return uRet;
}











/*********************

        ECM Preamble and PostAmble: In T.4 appendix A it says
        that the Preamble must be 200ms +/1 100ms of flags,
        and the postamble must be 50ms max.

        However we also have recving modems that don't know they
        are getting ECM and need the 6eols to tell them when to
        stop recving. Also some of them lose large chunks of data
        at the end of PhaseC due to "carrier squelch", i.e. whne
        they lose carrier, they throw away the contents of their
        recv buffer.

        To get 240ms of flags, just multiply CurMod by 3
        // #define SWFramingSendPreamble(b)     \
        //      (FComDirectAsyncWrite(b00EOL, 2), SWFramingSendFlags(b + b + b))

        Postamble must have *max* 50ms of flags, so let's say 30ms, which is
        9 flags at 2400 and 54 at 14400. Just multiply CurMod by (3/8)
        to get correct number of flags
        // #define SWFramingSendPostamble(b)    SWFramingSendFlags((b+b+b) >> 3)
        but with carrier squelch and all that, so few may not be safe.
        Murata sends out 44 flags at 2400 baud on closing. So lets
        just send out 80ms (just CurMod flags)

        This is the 80ms postamble
        // #define SWFramingSendPostamble(b)    SWFramingSendFlags(b)

        // Fix for DSI bug -- drops last 500 bytes or so ////
        // #define SWFramingSendPostamble(b)    SWFramingSendFlags(600)

*********************/


BYTE                            b00EOL[3] = { 0x00, 0x80, 0 };
BYTE b6EOLs[10] = { 0x00, 0x08, 0x80, 0x00, 0x08, 0x80, 0x00, 0x08, 0x80, 0x00 };

BOOL SWFramingSendPreamble(PThrdGlbl pTG, USHORT uCurMod)
{
        BOOL fRet = TRUE;

#ifdef CL0
        if(pTG->ModemParams.Class != FAXCLASS0)
#endif //CL0
        {
                // fix bug#1762. DataRace modem misses leading EOL unless
                // preceded by some zeros, and it then chews up all data
                // until an EOL, thereby eating up part of the first frame
                SendZeros1(pTG, 100);
                fRet = fRet && FComDirectAsyncWrite(pTG, b00EOL, 2);
        }
        fRet = fRet && SWFramingSendFlags(pTG, (USHORT)(uCurMod + uCurMod + uCurMod));

        return fRet;
}











BOOL SWFramingSendPostamble(PThrdGlbl pTG, USHORT uCurMod)
{
        BYTE bZero[50];
        USHORT i;
        BOOL fRet = TRUE;

        // send 80ms of flags
        fRet &= SWFramingSendFlags(pTG, uCurMod);

#ifdef CL0
        if(pTG->ModemParams.Class != FAXCLASS0)
#endif //CL0
        {
                // then send 6EOLs
                fRet &= FComDirectAsyncWrite(pTG, b6EOLs, 9);

                // then send 250 or so 00s
                _fmemset(bZero, 0, 50);

                for (i=0; i<5; i++)
                        fRet &= FComDirectAsyncWrite(pTG, bZero, 50);

        }
        return fRet;
}