/***************************************************************************
        Name      :     DDI.C

        Copyright (c) Microsoft Corp. 1991 1992 1993

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

#pragma warning(disable:4100)   // unreferenced formal param

#include "prep.h"

#include "mmsystem.h"
#include "comdevi.h"
#include "class1.h"
// #include "modem.h"
#include "debug.h"
#include "decoder.h"

///RSL
#include "glbproto.h"


#define faxTlog(m)              DEBUGMSG(ZONE_DDI, m)
#define faxT2log(m)             DEBUGMSG(ZONE_FRAMES, m)
#define FILEID                  FILEID_DDI

/* Converts a the T30 code for a speed to the Class1 code
 * Generates V.17 with Long Training.
 * Add 1 to V.17 codes to get teh Short-train version
 */
BYTE T30toC1[16] =
{
/* V27_2400             0 */    24,
/* V29_9600             1 */    96,
/* V27_4800             2 */    48,
/* V29_7200             3 */    72,
/* V33_14400    4 */    145,    // 144, // V33==V17_long_train FTM=144 is illegal
                                                0,
/* V33_12000    6 */    121,    // 120, // V33==V17_long_train FTM=120 is illegal
/* V21 squeezed in */   3,
/* V17_14400    8 */    145,
/* V17_9600             9 */    97,
/* V17_12000    10 */   121,
/* V17_7200             11 */   73,
                                                0,
                                                0,
                                                0,
                                                0
};



int T30toSpeed[16] =
{
/* V27_2400             0 */    2400,
/* V29_9600             1 */    9600,
/* V27_4800             2 */    4800,
/* V29_7200             3 */    7200,
/* V33_14400    4 */    14400,    // 144, // V33==V17_long_train FTM=144 is illegal
                                                0,
/* V33_12000    6 */    12000,    // 120, // V33==V17_long_train FTM=120 is illegal
/* V21 squeezed in */   300,
/* V17_14400    8 */    14400,
/* V17_9600             9 */    9600,
/* V17_12000    10 */   12000,
/* V17_7200             11 */   7200,
                                                0,
                                                0,
                                                0,
                                                0
};

// used only for checking

static BYTE SpeedtoCap[16] =
{
/* V27_2400             0 */    0,
/* V29_9600             1 */    V29,    // 1
/* V27_4800             2 */    V27,    // 2
/* V29_7200             3 */    V29,    // 1
/* V33_14400    4 */    V33,    // 4
                                                0,
/* V33_12000    6 */    V33,    // 4
/* V21 squeezed in */   0,
/* V17_14400    8 */    V17,    // 8
/* V17_9600             9 */    V17,    // 8
/* V17_12000    10 */   V17,    // 8
/* V17_7200             11 */   V17,    // 8
                                                0,
                                                0,
                                                0,
                                                0
};


CBSZ cbszFTH3   = "AT+FTH=3\r";
CBSZ cbszFRH3   = "AT+FRH=3\r";

CBSZ cbszFRS    = "AT+FRS=%d\r";
CBSZ cbszFTS    = "AT+FTS=%d\r";
CBSZ cbszFTM    = "AT+FTM=%d\r";
CBSZ cbszFRM    = "AT+FRM=%d\r";

#ifdef MDRV
        CBSZ cbszOK, cbszCONNECT, cbszNOCARRIER, cbszERROR, cbszFCERROR;
#else //MDRV

// echo off, verbose response, no auto answer, hangup on DTR drop
// 30 seconds timer on connect, speaker always off, speaker volume=0
// busy&dialtone detect enabled
extern  CBSZ cbszOK       ;
extern  CBSZ cbszCONNECT   ;
extern  CBSZ cbszNOCARRIER  ;
extern  CBSZ cbszERROR       ;
extern  CBSZ cbszFCERROR      ;

#endif //MDRV



#define         ST_MASK         (0x8 | ST_FLAG)         // 8 selects V17 only. 16 selects ST flag

/******************** Global Vars *********/
BYTE                            bDLEETX[3] = { DLE, ETX, 0 };
BYTE                            bDLEETXOK[9] = { DLE, ETX, '\r', '\n', 'O', 'K', '\r', '\n', 0 };
/******************** Global Vars *********/




/****************** begin prototypes from ddi.c *****************/
void iModemParamsReset(PThrdGlbl pTG);
void iModemInitGlobals(PThrdGlbl pTG);

// If defined, iModemRecvFrame retries FTH until timeout,
// if it receiving a null frame followed by ERROR or NO_CARRIER.
//#define USR_HACK

#ifdef USR_HACK
USHORT iModemFRHorM(PThrdGlbl pTG, ULONG ulTimeout);
#endif // USR_HACK

/****************** begin prototypes from ddi.c *****************/


#ifndef MDRV

#ifdef DEBUG
        DBGPARAM dpCurSettings = {
                "Modem Driver", 0x0000, {
                "DDI", "Frames", "", "",
                "Class0", "", "", "",
                "", "", "SW Framing", "SW Framing Hi"
                "Debug", "", "", "" },
                // 0x00000FFF
                // 0x00000001
                // 0x0000000F0
                // 0x00000400   // too much for 14400 ECM
                // 0x00000F01
                0x00000000
                // 0xFFFFFFFF
        };
#endif


#define szMODULENAME    "awcl1_32"


#endif //MDRV










void iModemInitGlobals(PThrdGlbl pTG)
{

        _fmemset(&pTG->Class1Modem, 0, sizeof(CLASS1_MODEM));
        _fmemset(&pTG->Class1Status, 0, sizeof(CLASS1_STATUS));

        pTG->Class1Modem.eRecvFCS = RECV_FCS_NO;

#if 0 /// RSL #ifndef MDDI
        {
                LPCMDTAB lpCmdTab = iModemGetCmdTabPtr(pTG);
                pTG->Class1Modem.eRecvFCS = RECV_FCS_DUNNO;
                if (lpCmdTab)
                {
                        if (lpCmdTab->dwFlags&fMDMSP_C1_FCS_NO)
                                pTG->Class1Modem.eRecvFCS = RECV_FCS_NO;
                        else if (lpCmdTab->dwFlags&fMDMSP_C1_FCS_YES_BAD)
                                pTG->Class1Modem.eRecvFCS = RECV_FCS_NOCHK;
                }
        }
#endif //!MDDI

}













void iModemParamsReset(PThrdGlbl pTG)
{
        _fmemset(&pTG->ModemParams, 0 , sizeof(pTG->ModemParams));

        pTG->ModemParams.uSize = sizeof(pTG->ModemParams);
        pTG->ModemParams.Class = FAXCLASS1;

        // Don't use this. Need differnet number of flags according to speed
        // pTG->ModemParams.PreambleFlags = 400;     // 200ms @ 14400bps
        pTG->ModemParams.InterframeFlags = 3;        // random. one or two may do!
        // pTG->ModemParams.InterframeFlags = 10;    // too much!

        // must be **less* than 50, so need a
        // different length for each speed
        // pTG->ModemParams.ClosingFlags = 50;
        // pTG->ModemParams.fCEDOff = 0;
        // pTG->ModemParams.fCNGOff = 0;
        // pTG->ModemParams.InactivityTimer = 0;

        // pTG->ModemParams.cbLineMin = 0;
        // pTG->ModemParams.hJob = 0;
}














// added for CLASS0
BOOL   ModemSetParams(PThrdGlbl pTG, USHORT uModem, LPMODEMPARAMS lpParams)
{
        BG_CHK((uModem==1||uModem==5) && lpParams->uSize >= sizeof(MODEMPARAMS));

        if(lpParams->Class && lpParams->Class != (-1))
        {
#ifdef CL0
                BG_CHK(lpParams->Class == FAXCLASS1 || lpParams->Class == FAXCLASS0);
#else //CL0
                BG_CHK(lpParams->Class == FAXCLASS1);
#endif //CL0

                pTG->ModemParams.Class = lpParams->Class;
        }
        return TRUE;
}

















USHORT   NCUDial(PThrdGlbl pTG, HLINE hLine, LPSTR szPhoneNum)
{
        USHORT uRet;

        D_PrintDDI("NCUDial", (USHORT)hLine, 0);
        IF_BG_CHK(hLine==HLINE_2);
        IF_BG_CHK(pTG->DDI.uComPort && pTG->DDI.fModemOpen && pTG->DDI.fLineInUse && pTG->DDI.fNCUModemLinked);

        iModemInitGlobals(pTG);

#ifndef CL0
        pTG->ModemParams.Class = FAXCLASS1;
#endif

        if((uRet = iModemDial(pTG, szPhoneNum, pTG->ModemParams.Class)) == CONNECT_OK)
                pTG->Class1Modem.ModemMode = FRH;

        return uRet;
}
















USHORT   NCULink(PThrdGlbl pTG, HLINE hLine, HMODEM hModem, USHORT uHandset, USHORT uFlags)
{
        USHORT uRet;

        D_PrintDDI("NCULink", (USHORT)hLine, (USHORT)hModem);
        D_PrintDDI("NCULink", uHandset, uFlags);
        IF_BG_CHK(hLine==HLINE_2);
        IF_BG_CHK(pTG->DDI.uComPort && pTG->DDI.fModemOpen && pTG->DDI.fLineInUse);

        switch(uFlags & NCULINK_MODEMASK)
        {
        case NCULINK_HANGUP:
                                        if(iModemHangup(pTG))
                                        {
                                                MDDISTMT(pTG->pTG->DDI.fNCUModemLinked = FALSE);
                                                uRet = CONNECT_OK;
                                        }
                                        else
                                                uRet = CONNECT_ERROR;
                                        break;

        case NCULINK_TX:
                                        MDDISTMT(pTG->DDI.fNCUModemLinked = TRUE);
                                        uRet = CONNECT_OK;
                                        break;
        case NCULINK_RX:
                                        iModemInitGlobals(pTG);

#                                       ifdef MDDI
                                                pTG->ModemParams.Class = FAXCLASS1;
#                                       endif //MDDI

                                        pTG->ModemParams.Class = FAXCLASS1;  //RSL
                                        if((uRet = iModemAnswer(pTG, (uFlags & NCULINK_IMMEDIATE), pTG->ModemParams.Class)) == CONNECT_OK)
                                        {
                                                MDDISTMT(pTG->DDI.fNCUModemLinked = TRUE);
                                                pTG->Class1Modem.ModemMode = FTH;
                                        }
                                        break;

        case NCULINK_OFFHOOK:
                                        // fall through. Can't handle yet
        default:                BG_CHK(FALSE);
                                        uRet = CONNECT_ERROR;
                                        break;
        }
//done:
        MDDISTMT((MyDebugPrint(pTG,  LOG_ALL,  "NCULink: uRet=%d Linked=%d\r\n", uRet, pTG->DDI.fNCUModemLinked)));
        return uRet;
}







// dangerous. May get 2 OKs, may get one. Generally avoid
// CBSZ cbszATAT                        = "AT\rAT\r";
CBSZ cbszAT1                    = "AT\r";

BOOL iModemSyncEx(PThrdGlbl pTG, HMODEM hModem, ULONG ulTimeout, DWORD dwFlags);






BOOL   ModemSync(PThrdGlbl pTG, HMODEM hModem, ULONG ulTimeout)
{
        return iModemSyncEx(pTG, hModem, ulTimeout, 0);
}





#ifndef MDDI
BOOL   ModemSyncEx(PThrdGlbl pTG, HMODEM hModem, ULONG ulTimeout, DWORD dwFlags)
{
        return iModemSyncEx(pTG, hModem, ulTimeout, dwFlags);
}
#endif // !MDDI












BOOL iModemSyncEx(PThrdGlbl pTG, HMODEM hModem, ULONG ulTimeout, DWORD dwFlags)
{
        IF_BG_CHK(hModem==HMODEM_3 && pTG->DDI.uComPort && pTG->DDI.fModemOpen && pTG->DDI.fLineInUse && pTG->DDI.fNCUModemLinked);
        faxIFlog(("In ModemSync fModemOpen=%d fLineInUse=%d\r\n", pTG->DDI.fModemOpen, pTG->DDI.fLineInUse));

        ///// Do cleanup of global state //////
        FComOutFilterClose(pTG);
        FComOverlappedIO(pTG, FALSE);
        SWFramingSendSetup(pTG, FALSE);
        SWFramingRecvSetup(pTG, FALSE);
        FComXon(pTG, FALSE);
        EndMode(pTG);
        ///// Do cleanup of global state //////

#ifdef CL0
        if(pTG->ModemParams.Class == FAXCLASS0)
                return TRUE;
#endif //CL0

        {
                LPCMDTAB lpCmdTab = iModemGetCmdTabPtr(pTG);
                if (
#ifndef MDDI
                        (dwFlags & fMDMSYNC_DCN) &&
#endif  //MDDI
                                pTG->Class1Modem.ModemMode == COMMAND
                        &&  lpCmdTab
                    &&  (lpCmdTab->dwFlags&fMDMSP_C1_NO_SYNC_IF_CMD) )
                {
                        ERRMSG((SZMOD "<<WARNING>> ModemSync: NOT Syching modem (MSPEC)\r\n"));
#ifdef WIN32
                        Sleep(100); // +++ 4/12 JosephJ -- try to elim this -- it's juse
#endif // WIN32
                                                // that we used to always issue an AT here, which
                                                // we now don't, so I issue a 100ms delay here instead.
                                                // MOST probably unnessary. The AT was issued by
                                                // accident on 4/94 -- as a side effect of
                                                // a change in T.30 code -- when ModemSync was
                                                // called just before a normal dosconnect. Unfortunately
                                                // we discovered in 4/95, 2 weeks before code freeze,
                                                // that the AT&T DataPort express (TT14), didn't
                                                // like this AT.
                        return TRUE;
                }
                else
                {
                        return (iModemPauseDialog(pTG, (LPSTR)cbszAT1, sizeof(cbszAT1)-1, ulTimeout, cbszOK)==1);
                }

        }
}












// Does nothing in this driver
BOOL   ModemFlush(PThrdGlbl pTG, HMODEM hModem)
{
        D_PrintDDI("ModemFlush", (USHORT)hModem, 0);
        IF_BG_CHK(hModem==HMODEM_3 && pTG->DDI.uComPort && pTG->DDI.fModemOpen && pTG->DDI.fLineInUse && pTG->DDI.fNCUModemLinked);
        return TRUE;
}

// #if (PAGE_PREAMBLE_DIV != 0)

// length of TCF = 1.5 * bpscode * 100 / 8 == 75 * bpscode / 4
USHORT TCFLen[16] =
{
/* V27_2400             0 */    450,
/* V29_9600             1 */    1800,
/* V27_4800             2 */    900,
/* V29_7200             3 */    1350,
/* V33_14400    4 */    2700,
                                                0,
/* V33_12000    6 */    2250,
                                                0,
/* V17_14400    8 */    2700,
/* V17_9600             9 */    1800,
/* V17_12000    10 */   2250,
/* V17_7200             11 */   1350,
                                                0,
                                                0,
                                                0,
                                                0
};


#define min(x,y)        (((x) < (y)) ? (x) : (y))













void SendZeros1(PThrdGlbl pTG, USHORT uCount)
{
#define         ZERO_BUFSIZE    256
        BYTE    bZero[ZERO_BUFSIZE];
        short   i;              // must be signed

        _fmemset(bZero, 0, ZERO_BUFSIZE);
        for(i=uCount; i>0; i -= ZERO_BUFSIZE)
        {
                // no need to stuff. They're all zeros!
                FComDirectAsyncWrite(pTG, bZero, (UWORD)(min((UWORD)i, (UWORD)ZERO_BUFSIZE)));
        }
        TRACE((SZMOD "Sent %d zeros\r\n", uCount));
}
// #endif //PAGE_PREAMBLE_DIV != 0















BOOL   ModemSendMode(PThrdGlbl pTG, HMODEM hModem, USHORT uMod, BOOL fHDLC, USHORT ifrHint)
{
#ifdef CL0
        if(pTG->ModemParams.Class == FAXCLASS0)
                return Class0ModemSendMode(pTG, hModem, fHDLC);
#endif //CL0

        BG_CHK(ifrHint && ifrHint < ifrEND);
        IF_BG_CHK(hModem==HMODEM_3 && pTG->DDI.uComPort && pTG->DDI.fModemOpen && pTG->DDI.fLineInUse && pTG->DDI.fNCUModemLinked);

        pTG->Class1Modem.CurMod = T30toC1[uMod & 0xF];

        pTG->CurrentCommSpeed = T30toSpeed[uMod & 0xF];

        BG_CHK(pTG->Class1Modem.CurMod);

        if((uMod & ST_MASK) == ST_MASK)         // mask selects V.17 and ST bits
                pTG->Class1Modem.CurMod++;
        // BG_CHK((pTG->Class1Modem.CurMod>24 && ModemCaps.uSendSpeeds!=V27_V29_V33_V17) ?
        //                      (SpeedtoCap[uMod & 0xF] & ModemCaps.uSendSpeeds) : 1);

        (MyDebugPrint(pTG,  LOG_ALL,  "In ModemSendMode uMod=%d CurMod=%d fHDLC=%d\r\n", uMod, pTG->Class1Modem.CurMod, fHDLC));

        if(uMod == V21_300)
        {
                BG_CHK(pTG->Class1Modem.CurMod == 3);
                _fstrcpy(pTG->Class1Modem.bCmdBuf, (LPSTR)cbszFTH3);
                pTG->Class1Modem.uCmdLen = sizeof(cbszFTH3)-1;
                pTG->Class1Modem.fHDLC = TRUE;
                FComXon(pTG, FALSE);                 // for safety. _May_ be critical
        }
        else
        {
                pTG->Class1Modem.uCmdLen = (USHORT)wsprintf(pTG->Class1Modem.bCmdBuf, cbszFTM, pTG->Class1Modem.CurMod);
                pTG->Class1Modem.fHDLC = FALSE;
                FComXon(pTG, TRUE);          // critical!! Start of PhaseC
                // no harm doing it here(i.e before issuing +FTM)

                if(fHDLC)
                {
                        if(!SWFramingSendSetup(pTG, TRUE))
                        {
                                BG_CHK(FALSE);
                                goto error2;
                        }
                }
        }
        FComOutFilterInit(pTG);    // _not_ used for 300bps HDLC
                                                        // but here just in case

        // want to do all the work _before_ issuing command

        pTG->Class1Modem.DriverMode = SEND;
        pTG->Class1Status.ifrHint = ifrHint; // need this before ModemDialog

        if(pTG->Class1Modem.ModemMode == FTH)
        {
                // already in send mode. This happens on Answer only
                BG_CHK(fHDLC && uMod==V21_300);
                BG_CHK(pTG->Class1Modem.CurMod==3 && pTG->Class1Modem.fHDLC == TRUE);
                return TRUE;
        }

#define STARTSENDMODE_TIMEOUT 5000                              // Random Timeout

        //// Try to cut down delay between getting CONNECT and writing the
        // first 00s (else modems can quit because of underrun).
        // Can do this by not sleeping in this. Only in fatal
        // cases will it lock up for too long (max 5secs). In those cases
        // the call is trashed too.

        FComCritical(pTG, TRUE);     // start Crit in ModemSendMode

        if(!iModemNoPauseDialog(pTG, (LPB)pTG->Class1Modem.bCmdBuf, pTG->Class1Modem.uCmdLen, STARTSENDMODE_TIMEOUT, cbszCONNECT))
        {
                FComCritical(pTG, FALSE);    // end Crit in ModemSendMode
                goto error;
        }

        // can't set this earlier. We'll trash previous value
        pTG->Class1Modem.ModemMode = ((uMod==V21_300) ? FTH : FTM);

        // Turn OFF overlapped I/O if in V.21 else ON
        FComOverlappedIO(pTG, uMod != V21_300);

        if(pTG->Class1Modem.fSendSWFraming)  // set in SWFramingSendSetup
        {
                SWFramingSendPreamble(pTG, pTG->Class1Modem.CurMod);
        }
#if (PAGE_PREAMBLE_DIV != 0)
        else if(pTG->Class1Modem.ModemMode == FTM)
        {
                // don't send 00s if ECM
                BG_CHK(ifrHint==ifrPIX_MH || ifrHint==ifrPIX_MR || ifrHint==ifrTCF);
                BG_CHK(PAGE_PREAMBLE_DIV);
                SendZeros1(pTG, (USHORT)(TCFLen[uMod & 0x0F] / PAGE_PREAMBLE_DIV));
        }
#else
        else if(ifrHint==ifrPIX_MH || ifrHint==ifrPIX_MR)
        {
                // even if MDDI is on need to send some 00s otherwise
                // some modems underrun and hangup
                SendZeros1(pTG, TCFLen[uMod & 0x0F] / 2);
        }
#endif

        // FComDrain(-,FALSE) causes fcom to write out any internally-
        // maintained buffers, but not to drain the comm-driver buffers.
        FComDrain(pTG, TRUE,FALSE);


        FComCritical(pTG, FALSE);    // end Crit in ModemSendMode

        TRACE((SZMOD "Starting Send at %d\r\n", pTG->Class1Modem.CurMod));
        return TRUE;

error:
        FComOutFilterClose(pTG);
        FComOverlappedIO(pTG, FALSE);
        SWFramingSendSetup(pTG, FALSE);
error2:
        FComXon(pTG, FALSE);         // important. Cleanup on error
        EndMode(pTG);
        return FALSE;
}





















BOOL iModemDrain(PThrdGlbl pTG)
{
        (MyDebugPrint(pTG,  LOG_ALL,  "Entering iModemDrain\r\n"));

        if(!FComDrain(pTG, TRUE, TRUE))
                return FALSE;

                // Must turn XON/XOFF off immediately *after* drain, but before we
                // send the next AT command, since recieved frames have 0x13 or
                // even 0x11 in them!! MUST GO AFTER the getOK ---- See BELOW!!!!

// increase this---see bug number 495. Must be big enough for
// COM_OUTBUFSIZE to safely drain at 2400bps(300bytes/sec = 0.3bytes/ms)
// let's say (COM_OUTBUFSIZE * 10 / 3) == (COM_OUTBUFSIZE * 4)
// can be quite long, because on failure we just barf anyway

#ifdef CL0
        if(pTG->ModemParams.Class == FAXCLASS0)
                return TRUE;
#endif //CL0

#define POSTPAGEOK_TIMEOUT (10000L + (((ULONG)COM_OUTBUFSIZE) << 2))

        // Here we were looking for OK only, but some modems (UK Cray Quantun eg)
        // give me an ERROR after sending TCF or a page (at speeds < 9600) even
        // though the page was sent OK. So we were timing out here. Instead look
        // for ERROR (and NO CARRIER too--just in case!), and accept those as OK
        // No point returning ERROR from here, since we just abort. We can't/don't
        // recover from send errors
        // if(!iModemResp1(POSTPAGEOK_TIMEOUT, cbszOK))

        if(iModemResp3(pTG, POSTPAGEOK_TIMEOUT, cbszOK, cbszERROR, cbszNOCARRIER) == 0)
                return FALSE;

                // Must change FlowControl State *after* getting OK because in Windows
                // this call takes 500 ms & resets chips, blows away data etc.
                // So do this *only* when you *know* both RX & TX are empty.
                // check this in all usages of this function

        return TRUE;
}


BOOL iModemSendData(PThrdGlbl pTG, LPB lpb, USHORT uCount, USHORT uFlags)
{
        BG_CHK((uFlags & SEND_ENDFRAME) == 0);
        BG_CHK(lpb);
        BG_CHK(!pTG->Class1Modem.fSendSWFraming);

        // if(uFlags & SEND_STUFF)
        {
                // always DLE-stuff here. Sometimes zero-stuff

                (MyDebugPrint(pTG,  LOG_ALL,  "In ModemSendData calling FComFilterAsyncWrite at %ld \n", GetTickCount() ) );

                if(!FComFilterAsyncWrite(pTG, lpb, uCount, FILTER_DLEZERO))
                        goto error;
        }
/*******
        else
        {
                if(!FComDirectAsyncWrite(pTG, lpb, uCount))
                        goto error;
        }
*******/

        if(uFlags & SEND_FINAL)
        {
                (MyDebugPrint(pTG,  LOG_ALL,  "In ModemSendData calling FComDIRECTAsyncWrite at %ld \n", GetTickCount() ) );

                // if(!FComDirectAsyncWrite(bDLEETXCR, 3))
                if(!FComDirectAsyncWrite(pTG, bDLEETX, 2))
                        goto error;

                if(!iModemDrain(pTG))
                        goto error;

                FComOutFilterClose(pTG);
            FComOverlappedIO(pTG, FALSE);
                FComXon(pTG, FALSE);         // critical. End of PhaseC
                                                        // must come after Drain
                EndMode(pTG);
        }
        return TRUE;

error:
        FComXon(pTG, FALSE);                 // critical. End of PhaseC (error)
        FComFlush(pTG);                    // clean out the bad stuff if we got an error
        FComOutFilterClose(pTG);
        FComOverlappedIO(pTG, FALSE);
        EndMode(pTG);
        return FALSE;
}























BOOL iModemSendFrame(PThrdGlbl pTG, LPB lpb, USHORT uCount, USHORT uFlags)
{
        UWORD   uwResp=0;

        (MyDebugPrint(pTG,  LOG_ALL,  "Entering iModemSendFrame\r\n"));

        BG_CHK(uFlags & SEND_ENDFRAME);
        BG_CHK(lpb && uCount);
        BG_CHK(!pTG->Class1Modem.fSendSWFraming);

        // always DLE-stuff here. Never zero-stuff
        // This is only called for 300bps HDLC
        BG_CHK(pTG->Class1Modem.fHDLC && pTG->Class1Modem.CurMod == 3);

        if(pTG->Class1Modem.ModemMode != FTH)        // Special case on just answering!!
        {
#define FTH_TIMEOUT 5000                                // Random Timeout
                if(!iModemNoPauseDialog(pTG, (LPB)pTG->Class1Modem.bCmdBuf, pTG->Class1Modem.uCmdLen, FTH_TIMEOUT, cbszCONNECT))
                        goto error;
        }

        // if(uFlags & SEND_STUFF)
        {
                // always DLE-stuff here. Never zero-stuff
                if(!FComFilterAsyncWrite(pTG, lpb, uCount, FILTER_DLEONLY))
                        goto error;
        }
/***
        else
        {
                if(!FComDirectAsyncWrite(pTG, lpb, uCount))
                        goto error;
        }
***/


        // if(uFlags & SEND_STUFF)
        {
                // SyncWrite call Drain here which we should not need
                // as we are immediately waiting for a response
                // if(!FComDirectSyncWrite(bDLEETX, 2))
                //              goto error;

                if(!FComDirectAsyncWrite(pTG, bDLEETX, 2))
                        goto error;
        }

// 2000 is too short because PPRs can be 32+7 bytes long and
// preamble is 1 sec, so set this to 3000
// 3000 is too short because NSFs and CSIs can be arbitrarily long
// MAXFRAMESIZE is defined in et30type.h. 30ms/byte at 300bps
// async (I think V.21 is syn though), so use N*30+1000+slack

#define WRITEFRAMERESP_TIMEOUT  (1000+30*MAXFRAMESIZE+500)
        if(!(uwResp = iModemResp2(pTG, WRITEFRAMERESP_TIMEOUT, cbszOK, cbszCONNECT)))
                goto error;
        pTG->Class1Modem.ModemMode = ((uwResp == 2) ? FTH : COMMAND);


        if(uFlags & SEND_FINAL)
        {
                FComOutFilterClose(pTG);
        FComOverlappedIO(pTG, FALSE);
                // FComXon(FALSE);      // at 300bps. no Xon-Xoff in use

                // in some weird cases (Practical Peripherals PM14400FXMT) we get
                // CONNECT<cr><lf>OK, but we get the CONNECT here. Should we
                // just set pTG->Class1Modem.ModemMode=COMMAND?? (EndMode does that)
                // Happens on PP 144FXSA also. Ignore it & just set mode to COMMAND
                // BG_CHK(pTG->Class1Modem.ModemMode == COMMAND);
                EndMode(pTG);
        }
        // ST_FRAMES(TRACE((SZMOD "FRAME SENT-->\r\n")); D_PrintFrame(lpb, uCount));
        return TRUE;

error:
        FComOutFilterClose(pTG);
        FComOverlappedIO(pTG, FALSE);
        FComXon(pTG, FALSE);         // just for safety. cleanup on error
        EndMode(pTG);
        return FALSE;
}






















BOOL   ModemSendMem(PThrdGlbl pTG, HMODEM hModem, LPBYTE lpb, USHORT uCount, USHORT uFlags)
{
#ifdef CL0
        if(pTG->ModemParams.Class == FAXCLASS0)
                return Class0ModemSendMem(pTG, hModem, lpb, uCount, uFlags);
#endif //CL0

        IF_BG_CHK(hModem==HMODEM_3 && pTG->DDI.uComPort && pTG->DDI.fModemOpen && pTG->DDI.fLineInUse && pTG->DDI.fNCUModemLinked);
        BG_CHK(pTG->Class1Modem.CurMod);
        BG_CHK(lpb);
        (MyDebugPrint(pTG,  LOG_ALL,  "In ModemSendMem lpb=%08lx uCount=%d wFlags=%04x\r\n", lpb, uCount, uFlags));

        if(pTG->Class1Modem.DriverMode != SEND)
        {
                BG_CHK(FALSE);
                return FALSE;
        }

        if(pTG->Class1Modem.fSendSWFraming)
                return SWFramingSendFrame(pTG, lpb, uCount, uFlags);
        else if(pTG->Class1Modem.fHDLC)
                return iModemSendFrame(pTG, lpb, uCount, uFlags);
        else
                return iModemSendData(pTG, lpb, uCount, uFlags);
}
















void TwiddleThumbs(ULONG ulTime);

#ifndef MDRV
void TwiddleThumbs(ULONG ulTime)
{
        MY_TWIDDLETHUMBS(ulTime);
}
#endif //MDRV























BOOL   ModemSendSilence(PThrdGlbl pTG, HMODEM hModem, USHORT uMillisecs, ULONG ulTimeout)
{
        //USHORT uTemp;

        IF_BG_CHK(hModem==HMODEM_3 && pTG->DDI.uComPort && pTG->DDI.fModemOpen && pTG->DDI.fLineInUse && pTG->DDI.fNCUModemLinked);
        (MyDebugPrint(pTG,  LOG_ALL,  "Before ModemSendSilence uMillsecs=%d ulTimeout=%ld at %ld\r\n",
                       uMillisecs, ulTimeout, GetTickCount() ));

#ifdef CL0
        if(pTG->ModemParams.Class == FAXCLASS0)
                return TRUE;
#endif //CL0

        // we're so slow it seems we don't need to and should not do this
        // I measured teh dealy due to this (FTS=10) to be about 500ms,
        // all of it on our side (dunno why?) except exactly the 100ms
        // by the modem. If we really want to insert teh delay we should
        // use TwiddleThumbs
        // Can't just return here, because we do _need_ the delay between
        // send DCS and send TCF so that receiver is not overwhelmed
        // return TRUE;

        // use TwiddleThumbs
        TwiddleThumbs(uMillisecs);

        (MyDebugPrint(pTG,  LOG_ALL,  "After ModemSendSilence  at %ld\r\n", GetTickCount() ));


        return TRUE;

        // uTemp = wsprintf(pTG->Class1Modem.bCmdBuf, cbszFTS, uMillisecs/10);
        // return (iModemNoPauseDialog((LPB)pTG->Class1Modem.bCmdBuf, uTemp, ulTimeout, cbszOK) == 1);
}






















BOOL   ModemRecvSilence(PThrdGlbl pTG, HMODEM hModem, USHORT uMillisecs, ULONG ulTimeout)
{
        // USHORT  uTemp;
        // CBPSTR  cbpstr;

        D_PrintDDI("RecvSilence", (USHORT)hModem, uMillisecs);
        IF_BG_CHK(hModem==HMODEM_3 && pTG->DDI.uComPort && pTG->DDI.fModemOpen && pTG->DDI.fLineInUse && pTG->DDI.fNCUModemLinked);
        (MyDebugPrint(pTG,  LOG_ALL,  "Before ModemRecvSilence uMillsecs=%d ulTimeout=%ld  at %ld \r\n",
                   uMillisecs, ulTimeout, GetTickCount() ));

#ifdef CL0
        if(pTG->ModemParams.Class == FAXCLASS0)
                return TRUE;
#endif //CL0

        // can't use AT+FRS -- see above for why. Basically, we take so long
        // sending the command and getting teh reply etc, that we can't use
        // it accurately. So use TwiddleThumbs.

        TwiddleThumbs(uMillisecs);

        (MyDebugPrint(pTG,  LOG_ALL,  "After ModemRecvSilence at %ld \r\n", GetTickCount() ));

        return TRUE;

        // uTemp = wsprintf(pTG->Class1Modem.bCmdBuf, cbpstr, uMillisecs/10);
        // return (iModemNoPauseDialog((LPB)pTG->Class1Modem.bCmdBuf, uTemp, ulTimeout, cbszOK)==1);
}





#define MINRECVMODETIMEOUT      500
#define RECVMODEPAUSE           200




















USHORT   ModemRecvMode(PThrdGlbl pTG, HMODEM hModem, USHORT uMod, BOOL fHDLC, ULONG ulTimeout, USHORT ifrHint)
{
        USHORT  uRet;
        ULONG ulBefore, ulAfter, ulDelta;

#ifdef CL0
        if(pTG->ModemParams.Class == FAXCLASS0)
                return Class0ModemRecvMode(pTG, hModem, fHDLC, ulTimeout);
#endif //CL0

        // Here we should watch for a different modulation scheme from what we expect.
        // Modems are supposed to return a +FCERROR code to indicate this condition,
        // but I have not seen it from any modem yet, so we just scan for ERROR
        // (this will catch +FCERROR too since iiModemDialog does not expect whole
        // words or anything like that!), and treat both the same.

        pTG->Class1Modem.CurMod = T30toC1[uMod & 0xF];

        pTG->CurrentCommSpeed = T30toSpeed[uMod & 0xF];

        BG_CHK(pTG->Class1Modem.CurMod);
        if((uMod & ST_MASK) == ST_MASK)         // mask selects V.17 and ST bits
                pTG->Class1Modem.CurMod++;

        if(uMod == V21_300)
        {
                BG_CHK(fHDLC && pTG->Class1Modem.CurMod==3);
                _fstrcpy(pTG->Class1Modem.bCmdBuf, (LPSTR)cbszFRH3);
                pTG->Class1Modem.uCmdLen = sizeof(cbszFRH3)-1;
        }
        else
        {
                pTG->Class1Modem.uCmdLen = (USHORT)wsprintf(pTG->Class1Modem.bCmdBuf, cbszFRM, pTG->Class1Modem.CurMod);
        }

        pTG->Class1Status.ifrHint = ifrHint; // need this before ModemDialog

        if(pTG->Class1Modem.ModemMode == FRH)
        {
                // already in receive mode. This happens upon Dial only
                BG_CHK(fHDLC && uMod==V21_300);
                BG_CHK(pTG->Class1Modem.CurMod == 3);

                pTG->Class1Modem.fHDLC = TRUE;
                pTG->Class1Modem.DriverMode = RECV;
                pTG->Class1Modem.fRecvNotStarted = TRUE;     // fNoFlags = FALSE;
                // pTG->Class1Modem.sRecvBufSize = sBufSize;
                FComInFilterInit(pTG);
                return RECV_OK;
        }


#ifdef WIN32
        // On Win32, we have a problem going into 2400baud recv.
        // +++ remember to put this into iModemFRHorM when that code is enabled.
        if (pTG->Class1Modem.CurMod==24) TwiddleThumbs(80);
#endif // WIN32

#ifdef USR_HACK
        uRet = iModemFRHorM(pTG, ulTimeout);
#else // !USR_HACK (iModemFRHorM contains the following code..)
retry:

        ulBefore=GetTickCount();
        // Don't look for NO CARRIER. Want it to retry until FRM timeout on NO CARRIER
        // ----This is changed. See below----
        uRet = iModemNoPauseDialog3(pTG, pTG->Class1Modem.bCmdBuf, pTG->Class1Modem.uCmdLen, ulTimeout, cbszCONNECT, cbszFCERROR, cbszNOCARRIER);
        // uRet = iModemNoPauseDialog2(pTG->Class1Modem.bCmdBuf, pTG->Class1Modem.uCmdLen, ulTimeout, cbszCONNECT, cbszFCERROR);
        ulAfter=GetTickCount();

        if(uRet==2 || uRet==3)  // uRet==FCERROR or uRet==NOCARRIER
        {
                ulDelta = (ulAfter >= ulBefore) ? (ulAfter - ulBefore) : (0xFFFFFFFFL - ulBefore) + ulAfter;

                if(ulTimeout < (ulDelta + MINRECVMODETIMEOUT))
                {
                        ERRMSG((SZMOD "<<WARNING>> Giving up on RecvMode. uRet=%d ulTimeout=%ld\r\n", uRet, ulTimeout));
                }
                else
                {
                        ulTimeout -= (ulAfter-ulBefore);

                        // need this pause for NO CARRIER for USR modems. See bug#1516
                        // for the RC229DP, dunno if it's reqd because I dunno why theyre
                        // giving the FCERROR. Don't want to miss the carrier so currently
                        // don't pause. (Maybe we can achieve same effect by simply taking
                        // FCERROR out of the response list above--but that won't work for
                        // NOCARRIER because we _need_ teh pause. iiModemDialog is too fast)
                        if(uRet == 3)
                                TwiddleThumbs(RECVMODEPAUSE);
                        BG_CHK(ulTimeout >= MINRECVMODETIMEOUT);

                        goto retry;
                }
        }
#endif // !USR_HACK

        (MyDebugPrint(pTG,  LOG_ALL,  "Ex ModemRecvMode uMod=%d CurMod=%d fHDLC=%d ulTimeout=%ld: Got=%d\r\n", uMod, pTG->Class1Modem.CurMod, fHDLC, ulTimeout, uRet));
        if(uRet != 1)
        {
                EndMode(pTG);
                if(uRet == 2)
                {
                        ERRMSG((SZMOD "<<WARNING>> RecvMode:: Got FCERROR after %ldms\r\n", ulAfter-ulBefore));
                        return RECV_WRONGMODE;  // need to return quickly
                }
                else
                {
                        BG_CHK(uRet == 0);
                        ERRMSG((SZMOD "<<WARNING>> RecvMode:: Got Timeout after %ldms\r\n", ulAfter-ulBefore));
                        return RECV_TIMEOUT;
                }
        }

        BG_CHK(ifrHint && ifrHint < ifrEND);
        IF_BG_CHK(hModem==HMODEM_3 && pTG->DDI.uComPort && pTG->DDI.fModemOpen && pTG->DDI.fLineInUse && pTG->DDI.fNCUModemLinked);
        BG_CHK(pTG->Class1Modem.CurMod);
        // BG_CHK((pTG->Class1Modem.CurMod>24 && ModemCaps.uRecvSpeeds!=V27_V29_V33_V17) ?
        //                      (SpeedtoCap[uMod & 0xF] & ModemCaps.uRecvSpeeds) : 1);

        if(uMod==V21_300)
        {
                pTG->Class1Modem.ModemMode = FRH;
                pTG->Class1Modem.fHDLC = TRUE;
        }
        else
        {
                pTG->Class1Modem.ModemMode = FRM;
                pTG->Class1Modem.fHDLC = FALSE;
                if(fHDLC)
                {
                        if(!SWFramingRecvSetup(pTG, TRUE))
                        {
                                BG_CHK(FALSE);
                                EndMode(pTG);
                                return RECV_ERROR;
                        }
                }
        }
        // pTG->Class1Modem.sRecvBufSize = sBufSize;
        pTG->Class1Modem.DriverMode = RECV;
        pTG->Class1Modem.fRecvNotStarted = TRUE;     // fNoFlags = FALSE;
        FComInFilterInit(pTG);
        TRACE((SZMOD "Starting Recv at %d\r\n", pTG->Class1Modem.CurMod));
        return RECV_OK;
}








#ifdef CL0
void   ModemEndRecv(PThrdGlbl pTG, HMODEM hModem)
{
#ifdef CL0
        if(pTG->ModemParams.Class == FAXCLASS0)
                Class0ModemEndRecv(pTG, hModem);
#endif //CL0
}
#endif //CL0
























USHORT iModemRecvData(PThrdGlbl pTG, LPB lpb, USHORT cbMax, ULONG ulTimeout, USHORT far* lpcbRecv)
{
        SWORD   swEOF;
        USHORT  uRet;

        BG_CHK(pTG->Class1Modem.ModemMode == FRM);
        BG_CHK(lpb && cbMax && lpcbRecv);
        BG_CHK(!pTG->Class1Modem.fRecvSWFraming);

        startTimeOut(pTG, &(pTG->Class1Modem.toRecv), ulTimeout);
        // 4th arg must be FALSE for Class1
        *lpcbRecv = FComFilterReadBuf(pTG, lpb, cbMax, &(pTG->Class1Modem.toRecv), FALSE, &swEOF);
        if(swEOF == -1)
        {
                // we got a DLE-ETX _not_ followed by OK or NO CARRIER. So now
                // we have to decide whether to (a) declare end of page (swEOF=1)
                // or (b) ignore it & assume page continues on (swEOF=0).
                //
                // The problem is that some modems produce spurious EOL during a page
                // I believe this happens due a momentary loss of carrier that they
                // recover from. For example IFAX sending to the ATI 19200. In those
                // cases we want to do (b). The opposite problem is that we'll run
                // into a modem whose normal response is other than OK or NO CARRIER.
                // Then we want to do (a) because otherwise we'll _never_ work with
                // that modem.
                //
                // So we have to either do (a) always, or have an INI setting that
                // can force (a), which could be set thru the AWMODEM.INF file. But
                // we also want to do (b) if possible because otehrwise we'll not be
                // able to recieve from weak or flaky modems or machines or whatever
                //
                // Snowball does (b). I believe best soln is an INI setting, with (b)
                // as default

                // option (a)
                // ERRMSG((SZMOD "<<WARNING>> Got arbitrary DLE-ETX. Assuming END OF PAGE!!!\r\n"));
                // swEOF = 1;

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

        switch(swEOF)
        {
        case 1:         uRet = RECV_EOF; break;
        case 0:         return RECV_OK;
        default:        BG_CHK(FALSE);  // fall through
        case -2:        uRet = RECV_ERROR; break;
        case -3:        uRet = RECV_TIMEOUT; break;
        }

        EndMode(pTG);
        return uRet;
}


const static BYTE LFCRETXDLE[4] = { LF, CR, ETX, DLE };























USHORT iModemRecvFrame(PThrdGlbl pTG, LPB lpb, USHORT cbMax, ULONG ulTimeout, USHORT far* lpcbRecv)
{
        SWORD swRead, swRet;
        USHORT i;
        BOOL fRestarted=0;
        USHORT uRet;
        BOOL fGotGoodCRC = 0;   // see comment-block below

        /** Sometimes modems give use ERROR even when thr frame is good.
                Happens a lot from Thought to PP144MT on CFR. So we check
                the CRC. If the CRc was good and everything else looks good
                _except_ the "ERROR" response from teh modem then return
                RECV_OK, not RECV_BADFRAME.
                This should fix BUG#1218
        **/

        BG_CHK(lpb && cbMax && lpcbRecv);
restart:
        *lpcbRecv=0;
        if(pTG->Class1Modem.ModemMode!= FRH)
        {
#ifdef USR_HACK
                swRet = iModemFRHorM(pTG, ulTimeout);
#else // !USR_HACK
                swRet=iModemNoPauseDialog2(pTG, (LPB)pTG->Class1Modem.bCmdBuf, pTG->Class1Modem.uCmdLen, ulTimeout, cbszCONNECT, cbszNOCARRIER);
#endif // !USR_HACK
                if(swRet==2||swRet==3)
                {
                        (MyDebugPrint(pTG,  LOG_ALL,  "Got NO CARRIER from FRH=3\r\n"));
                        EndMode(pTG);
                        return RECV_EOF;
                }
                else if(swRet != 1)
                {
                        ERRMSG((SZMOD "<<WARNING>> Can't get CONNECT from FRH=3\r\n"));
                        EndMode(pTG);
                        return RECV_TIMEOUT;    // may not need this, since we never got flags??
                        // actually we dont know what the heck we got!!
                }
        }


        pTG->Class1Modem.fRecvNotStarted = FALSE;    // fNoFlags = FALSE;

        /*** Got CONNECT (i.e. flags). Now try to get a frame ***/

        /****************************************************************
         * Using 3 secs here is a misinterpretation of the T30 CommandReceived?
         * flowchart. WE want to wait here until we get something or until T2
         * or T4 timeout. It would have been best if we started T2 ot T4 on
         * entry into the search routine (t30.c), but starting it here is good
         * enough.
         * Using this 3secs timeout fails when Genoa simulates a bad frame
         * because Zoom PKT modem gives us a whole raft of bad frames for one
         * bad PPS-EOP and then gives a CONNECT that we timeout below exactly
         * as the sender's T4 timeout expires and he re-sends the PPS-EOP
         * so we miss all of them.
         * Alternately, we could timeout here on 2sec & retry. But that's risky
         * If less than 2sec then we'll timeout on modems that give connect
         * first flag, then 2sec elapse before CR-LF (1sec preamble & 1sec for
         * long frames, e.g. PPR!)
         ****************************************************************/

        // #define TIMEOUT_3SECS        3000L
        // startTimeOut(&(pTG->Class1Modem.toRecv), TIMEOUT_3SECS);

        startTimeOut(pTG, &(pTG->Class1Modem.toRecv), ulTimeout);
        swRead = FComFilterReadLine(pTG, lpb, cbMax, &(pTG->Class1Modem.toRecv));

        pTG->Class1Modem.ModemMode = COMMAND;
        // may change this to FRH if we get CONNECT later.
        // but set it here just in case we short circuit out due to errors


        if(swRead<=0)
        {
                // Timeout
                ERRMSG((SZMOD "<<WARNING>> Can't get frame after connect. Got-->\r\n"));
                D_HexPrint(lpb, (WORD)-swRead);
                EndMode(pTG);
                *lpcbRecv = -swRead;
                return RECV_ERROR;              // goto error;
        }

        faxT2log(("FRAME>>> \r\n"));
        ST_FRAMES(D_HexPrint(lpb, swRead));


        for(i=0, swRead--; i<4 && swRead>=0; i++, swRead--)
        {
                if(lpb[swRead] != LFCRETXDLE[i])
                        break;
        }
        // exits when swRead is pointing to last non-noise char
        // or swRead == -1
        // incr by 1 to give actual non-noise data size.
        // (size = ptr to last byte + 1!)
        swRead++;


        // Hack for AT&T AK144 modem that doesn't send us the CRC
        // only lop off last 2 bytes IFF the frame is >= 5 bytes long
        // that will leave us at least the FF 03/13 FCF
        // if(i==4 && swRead>=2)        // i.e. found all of DLE-ETX_CR-LF

   // 09/25/95 This code was changed to never lop of the CRC.
   // All of the routines except NSxtoBC can figure out the correct length,
   // and that way if the modem doesn't pass on the CRC, we no longer
   // lop off the data.
   // NSxtoBC has been changed to expect the CRC.

        // we really want this CRC-checking in the MDDI case too
// #ifdef MDDI
//      if(i==4 && swRead>=5)   // i.e. found all of DLE-ETX_CR-LF
//      {
//              swRead -= 2;            // get rid of CRC
//              uRet = RECV_OK;
//      }
// #else //MDDI
        if(i==4)                                // i.e. found all of DLE-ETX_CR-LF
        {
                // Determine if the frame has the CRC or not..
                // (AT&T and NEC modems don't have them)
                if (pTG->Class1Modem.eRecvFCS != RECV_FCS_NO)
                {
                        if (swRead > 2)
                        {

#ifdef PORTABLE_CODE
                                WORD wCRCgot = *(UNALIGNED WORD FAR*)(lpb+swRead-2); // +++alignment
#else
                                WORD wCRCgot = *(LPWORD)(lpb+swRead-2); // +++alignment
#endif
                            WORD wCRCcalc;

// Elliot bug 1811: TI PCMCIA modem TI1411 sends control byte twice
// instead of address (0xff) followed by control.
// So we correct for that here...
                                if  (lpb[0]==lpb[1] && (*lpb==0x3 || *lpb==0x13))
                                {
                                        ERRMSG((SZMOD
                                        "<<WARNING>> V.21 w/ wrong address:%04x.\r\n"
                                        "\t\tZapping 1st byte with 0xff.\r\n",
                                                (unsigned)*lpb));
                                        *lpb = 0xff;
                                }

                            wCRCcalc = CalcCRC(pTG, lpb, (USHORT) (swRead-2));
                            if  (wCRCgot==wCRCcalc)
                            {
//                                      swRead -=2;
                                        fGotGoodCRC = TRUE;
                            }
                            else
                            {
#define MUNGECRC(crc) MAKEWORD(LOBYTE(crc),\
                                                        (HIBYTE(crc)<<2)|(LOBYTE(crc)>>6))
                                        ERRMSG((SZMOD
                                                "<<WARNING>> V.21 CRC mismatch. Got 0x%04x."
                                                                                                                        " Want 0x%04x\r\n",
                                                (unsigned) wCRCgot, (unsigned) wCRCcalc ));
                                        // MC1411, MH9611 hack...
                                        if (wCRCgot == MUNGECRC(wCRCcalc))
                                        {
                                        ERRMSG((SZMOD
                                                        "<<WARNING>> mutant V.21 CRC:%04x\r\n",
                                                                        MUNGECRC(wCRCcalc)));
//                                              swRead -=2;
                                                fGotGoodCRC = TRUE;
                                        }
// Elliot bug 2659: PP PM288MT II V.34 adds a flag (0x7e) to the END
// Of every V.21 flag it receives! So we check for this special case.
                                        else if (swRead>3 && *(lpb+swRead-1)==0x7e)
                                        {
                                        ERRMSG((SZMOD
                                                        "<<WARNING>> Last byte == 0x7e\r\n"));

#ifdef PORTABLE_CODE
                                                wCRCgot = *(UNALIGNED WORD FAR*)(lpb+swRead-3);
#else
                                                wCRCgot = *(LPWORD)(lpb+swRead-3);
#endif
                                                wCRCcalc = CalcCRC(pTG, lpb, (USHORT) (swRead-3));
                                        ERRMSG((SZMOD
                                                        "<<WARNING>> Final flag? New Calc:%04x;Got=%04x\r\n",
                                                                        (unsigned) wCRCcalc,
                                                                        (unsigned) wCRCgot));
                                                if (wCRCgot==wCRCcalc)
                                                {
//                                                      swRead -=3;
                     swRead--;
                                                        fGotGoodCRC = TRUE;
                                                }
                                        }
                                        else if (pTG->Class1Modem.eRecvFCS == RECV_FCS_NOCHK)
                                        {
                                        ERRMSG((SZMOD
                                                        "<<WARNING>> ASSUMING BAD V.21 CRC\r\n"));
//                                              swRead -=2;
                                        }
                                        else
                                        {
                                        ERRMSG((SZMOD
                                                        "<<WARNING>> no/bad V.21 CRC\r\n"));
                                        }
                            }
                        }
                }

                uRet = RECV_OK;
        }
// #endif //MDDI
        else
        {
                ERRMSG((SZMOD "<<WARNING>> Frame doesn't end in dle-etx-cr-lf\r\n"));
                // leave tast two bytes in. We don't *know* it's a CRC, since
                // frame ending was non-standard
                uRet = RECV_BADFRAME;
        }
        *lpcbRecv = swRead;

        // check if it is the NULL frame (i.e. DLE-ETX-CR-LF) first.
        // (check is: swRead==0 and uRet==RECV_OK (see above))
        // if so AND if we get OK or CONNECT or ERROR below then ignore
        // it completely. The Thought modem and the PP144MT generate
        // this poor situation! Keep a flag to avoid a possible
        // endless loop

        // broaden this so that we Restart on either a dle-etx-cr-lf
        // NULL frame or a simple cr-lf NULL frame. But then we need
        // to return an ERROR (not BADFRAME) after restarting once,
        // otheriwse there is an infinite loop with T30 calling us
        // again and again (see bug#834)

        // chnage yet again. This takes too long, and were trying to tackle
        // a specific bug (the PP144MT) bug here, so let's retsrat only
        // on dle-etx-cr-lf (not just cr-lf), and in teh latter case
        // return a response according to what we get


        BG_CHK(uRet==RECV_OK || uRet==RECV_BADFRAME);

        /*** Got Frame. Now try to get OK or ERROR. Timeout=0! ***/

        switch(swRet = iModemResp4(pTG, 0, cbszOK, cbszCONNECT, cbszNOCARRIER, cbszERROR))
        {
        case 2: pTG->Class1Modem.ModemMode = FRH;
                        // fall through and do exactly like OK!!
        case 1: // ModemMode already == COMMAND
                        if(swRead<=0 && uRet==RECV_OK && !fRestarted)
                        {
                                ERRMSG((SZMOD "<<WARNING>> Got %d after frame. RESTARTING\r\n", swRet));
                                fRestarted = 1;
                                goto restart;
                        }
                        //uRet already set
                        break;

        case 3: // NO CARRIER. If got null-frame or no frame return
                        // RECV_EOF. Otherwise if got OK frame then return RECV_OK
                        // and return frame as usual. Next time around it'll get a
                        // NO CARRIER again (hopefully) or timeout. On a bad frame
                        // we can return RECV_EOF, but this will get into trouble if
                        // the recv is not actually done. Or return BADFRAME, and hope
                        // for a NO CARRIER again next time. But next time we may get a
                        // timeout. ModemMode is always set to COMMAND (already)
                        ERRMSG((SZMOD "<<WARNING>> Got NO CARRIER after frame. swRead=%d uRet=%d\r\n", swRead, uRet));
                        if(swRead <= 0)
                                uRet = RECV_EOF;
                        // else uRet is already BADFRAME or OK
                        break;

                        // this is bad!!
                        // alternately:
                        // if(swRead<=0 || uRet==RECV_BADFRAME)
                        // {
                        //              uRet = RECV_EOF;
                        //              *lpcbRecv = 0;          // must return 0 bytes with RECV_EOF
                        // }

        case 4: // ERROR
                        if(swRead<=0)
                        {
                                // got no frame
                                if(uRet==RECV_OK && !fRestarted)
                                {
                                        // if we got dle-etx-cr-lf for first time
                                        ERRMSG((SZMOD "<<WARNING>> Got ERROR after frame. RESTARTING\r\n"));
                                        fRestarted = 1;
#ifdef USR_HACK
                                        TwiddleThumbs(RECVMODEPAUSE);
#endif //USR_HACK
                                        goto restart;
                                }
                                else
                                        uRet = RECV_ERROR;
                        }
                        else
                        {
                                // if everything was OK until we got the "ERROR" response from
                                // the modem and we got a good CRC then treat it as "OK"
                                // This should fix BUG#1218
                                if(uRet==RECV_OK && fGotGoodCRC)
                                        uRet = RECV_OK;
                                else
                                        uRet = RECV_BADFRAME;
                        }

                        ERRMSG((SZMOD "<<WARNING>> Got ERROR after frame. swRead=%d uRet=%d\r\n", swRead, uRet));
                        break;

        case 0: // timeout
                        ERRMSG((SZMOD "<<WARNING>> Got TIMEOUT after frame. swRead=%d uRet=%d\r\n", swRead, uRet));
                        // if everything was OK until we got the timeout from
                        // the modem and we got a good CRC then treat it as "OK"
                        // This should fix BUG#1218
                        if(uRet==RECV_OK && fGotGoodCRC)
                                uRet = RECV_OK;
                        else
                                uRet = RECV_BADFRAME;
                        break;
        }
        return uRet;
}





























USHORT   ModemRecvMem(PThrdGlbl pTG, HMODEM hModem, LPBYTE lpb, USHORT cbMax, ULONG ulTimeout, USHORT far* lpcbRecv)
{
        USHORT uRet;

#ifdef CL0
        if(pTG->ModemParams.Class == FAXCLASS0)
                return Class0ModemRecvMem(pTG, hModem, lpb, cbMax, ulTimeout, lpcbRecv);
#endif //CL0

        IF_BG_CHK((WORD)hModem==3 && pTG->DDI.uComPort && pTG->DDI.fModemOpen && pTG->DDI.fLineInUse && pTG->DDI.fNCUModemLinked);
        BG_CHK(pTG->Class1Modem.CurMod);
        BG_CHK(lpb && cbMax && lpcbRecv);
        (MyDebugPrint(pTG,  LOG_ALL,  "In ModemRecvMem lpb=%08lx cbMax=%d ulTimeout=%ld\r\n", lpb, cbMax, ulTimeout));

        if(pTG->Class1Modem.DriverMode != RECV)
        {
                BG_CHK(FALSE);
                return RECV_ERROR;      // see bug#1492
        }
        *lpcbRecv=0;

        if(pTG->Class1Modem.fRecvSWFraming)
                uRet = SWFramingRecvFrame(pTG, lpb, cbMax, ulTimeout, lpcbRecv);
        else if(pTG->Class1Modem.fHDLC)
                uRet = iModemRecvFrame(pTG, lpb, cbMax, ulTimeout, lpcbRecv);
        else
                uRet = iModemRecvData(pTG, lpb, cbMax, ulTimeout, lpcbRecv);

        (MyDebugPrint(pTG,  LOG_ALL,  "Ex ModemRecvMem lpbf=%08lx uCount=%d uRet=%d\r\n", lpb, *lpcbRecv, uRet));
        return uRet;
}






















#ifdef USR_HACK
USHORT iModemFRHorM(PThrdGlbl pTG, ULONG ulTimeout)
{
        ULONG   ulBefore, ulAfter, ulDelta;
        USHORT uRet;

retry:

        ulBefore=GetTickCount();

        // Don't look for NO CARRIER. Want it to retry until FRM timeout on NO CARRIER
        // ----This is changed. See below----
        uRet = iModemNoPauseDialog3(pTG, pTG->Class1Modem.bCmdBuf, pTG->Class1Modem.uCmdLen, ulTimeout, cbszCONNECT, cbszFCERROR, cbszNOCARRIER);
        // uRet = iModemNoPauseDialog2(pTG, pTG->Class1Modem.bCmdBuf, pTG->Class1Modem.uCmdLen, ulTimeout, cbszCONNECT, cbszFCERROR);
        ulAfter=GetTickCount();

        if(uRet==2 || uRet==3)  // uRet==FCERROR or uRet==NOCARRIER
        {
                ulDelta = (ulAfter >= ulBefore) ? (ulAfter - ulBefore) : (0xFFFFFFFFL - ulBefore) + ulAfter;

                if(ulTimeout < (ulDelta + MINRECVMODETIMEOUT))
                {
                        ERRMSG((SZMOD "<<WARNING>> Giving up on RecvMode. uRet=%d ulTimeout=%ld\r\n", uRet, ulTimeout));
                }
                else
                {
                        ulTimeout -= (ulAfter-ulBefore);

                        // need this pause for NO CARRIER for USR modems. See bug#1516
                        // for the RC229DP, dunno if it's reqd because I dunno why theyre
                        // giving the FCERROR. Don't want to miss the carrier so currently
                        // don't pause. (Maybe we can achieve same effect by simply taking
                        // FCERROR out of the response list above--but that won't work for
                        // NOCARRIER because we _need_ teh pause. iiModemDialog is too fast)
                        if(uRet == 3)
                                TwiddleThumbs(RECVMODEPAUSE);
                        BG_CHK(ulTimeout >= MINRECVMODETIMEOUT);

                        goto retry;
                }
        }

        return uRet;
}
#endif // USR_HACK