You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
2010 lines
63 KiB
2010 lines
63 KiB
/***************************************************************************
|
|
Name : MODEM.C
|
|
Comment : Various modem dialog & support functions, specific
|
|
to COM connected modems. For a modem on the bus
|
|
everything below & including this file is replaced
|
|
by the modem driver.
|
|
|
|
Copyright (c) Microsoft Corp. 1991, 1992, 1993
|
|
|
|
Revision Log
|
|
Num Date Name Description
|
|
--- -------- ---------- -----------------------------------------------
|
|
101 06/04/92 arulm Modif to SUPPORT to provide a replaceable interface
|
|
and to use new FCom functions.
|
|
***************************************************************************/
|
|
#define USE_DEBUG_CONTEXT DEBUG_CONTEXT_T30_CLASS1
|
|
|
|
#include "prep.h"
|
|
|
|
#include "mmsystem.h"
|
|
#include "modemint.h"
|
|
#include "fcomint.h"
|
|
#include "fdebug.h"
|
|
|
|
#include "efaxcb.h"
|
|
|
|
#define DEFINE_MDMCMDS
|
|
#include "mdmcmds.h"
|
|
|
|
///RSL
|
|
#include "glbproto.h"
|
|
|
|
#include "psslog.h"
|
|
#define FILE_ID FILE_ID_MODEM
|
|
|
|
void InitMonitorLogging(PThrdGlbl pTG);
|
|
|
|
|
|
# pragma message("Compiling with ADAPTIVE_ANSWER")
|
|
USHORT iModemGetAdaptiveResp(PThrdGlbl pTG);
|
|
#define uMULTILINE_SAVEENTIRE 0x1234 // +++ HACK passed in as fMultiLine
|
|
// in iiModemDialog to get it so save
|
|
// entire buffer in FComModem.bEntireReply.
|
|
|
|
// Need to have these in descending order so that we'll
|
|
// Sync at teh highest common speed with auto-bauding modems!
|
|
static UWORD rguwSpeeds[] = {57600,19200, 19200, 9600, 2400, 1200, 300, 0};
|
|
// static UWORD rguwSpeeds[] = {19200, 2400, 9600, 1200, 300, 0};
|
|
// static UWORD rguwSpeeds[] = {2400, 19200, 9600, 1200, 300, 0};
|
|
|
|
SWORD HayesSyncSpeed(PThrdGlbl pTG, CBPSTR cbszCommand, UWORD uwLen)
|
|
{
|
|
/* Internal routine to synchronize with the modem's speed. Tries to
|
|
get a response from the modem by trying the speeds in rglSpeeds
|
|
in order (terminated by a 0). If fTryCurrent is nonzero, checks for
|
|
a response before trying to reset the speeds.
|
|
|
|
Returns the speed it found, 0 if they're in sync upon entry (only
|
|
checked if fTryCurrent!=0), or -1 if it couldn't sync.
|
|
*/
|
|
// short i;
|
|
short ilWhich = -1;
|
|
|
|
DEBUG_FUNCTION_NAME(("HayesSyncSpeed"));
|
|
rguwSpeeds[0] = pTG->CurrentSerialSpeed;
|
|
|
|
if ( rguwSpeeds[0] == rguwSpeeds[1])
|
|
{
|
|
ilWhich++;
|
|
}
|
|
|
|
for(;;)
|
|
{
|
|
DebugPrintEx( DEBUG_MSG,
|
|
"Trying: ilWhich=%d speed=%d",
|
|
ilWhich,
|
|
rguwSpeeds[ilWhich]);
|
|
|
|
if(iSyncModemDialog(pTG, (LPSTR)cbszCommand, uwLen, cbszOK))
|
|
{
|
|
DebugPrintEx( DEBUG_MSG,
|
|
"Succeeded in Syncing at Speed = %d (il=%d)",
|
|
rguwSpeeds[ilWhich],
|
|
ilWhich);
|
|
|
|
return (ilWhich>=0 ? rguwSpeeds[ilWhich] : 0);
|
|
}
|
|
|
|
/* failed. try next speed. */
|
|
if (rguwSpeeds[++ilWhich]==0)
|
|
{
|
|
// Tried all speeds. No response
|
|
DebugPrintEx( DEBUG_ERR,
|
|
"Cannot Sync with Modem on Command %s",
|
|
(LPSTR)cbszCommand);
|
|
return -1;
|
|
}
|
|
if(!FComSetBaudRate(pTG, rguwSpeeds[ilWhich]))
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
SWORD iModemSync(PThrdGlbl pTG)
|
|
{
|
|
// The command used here must be guaranteed to be harmless,
|
|
// side-effect free & non-dstructive. i.e. we can issue it
|
|
// at any point in command mode without chnageing the state
|
|
// of teh modem or disrupting anything.
|
|
// ATZ does not qualify. AT does, I think.....
|
|
|
|
return HayesSyncSpeed(pTG, cbszAT, sizeof(cbszAT)-1);
|
|
}
|
|
|
|
|
|
SWORD iModemReset(PThrdGlbl pTG, CBPSTR szCmd)
|
|
{
|
|
SWORD swRet;
|
|
|
|
if (szCmd == NULL)
|
|
{
|
|
return -1;
|
|
}
|
|
|
|
if((swRet = HayesSyncSpeed(pTG, szCmd, (UWORD) _fstrlen(szCmd))) < 0)
|
|
{
|
|
return swRet;
|
|
}
|
|
else
|
|
{
|
|
// ATZ may result in a change in the state/baud rate of the modem
|
|
// (eg. Thought board drops to 2400), therefore we must Sync up
|
|
// again because this function is really a Reset&Sync function.
|
|
|
|
// instead of syncing up on AT and then doing ATE0, just
|
|
// sync up on ATE0 directly
|
|
|
|
if(iModemSync(pTG) < 0)
|
|
return -1;
|
|
|
|
/////////////////////
|
|
// the above idea does not work with Sharad's PP9600FXMT
|
|
// somehow I end up sending it ATATE0 and it answers the phone
|
|
// In other cases, the ATE0 simply has no effect (because the AT&F
|
|
// thing above got confused and teh ATE0 ended up just aborting
|
|
// some previous command) and on ATA I get the ATA echoed,
|
|
// get confused (because multi-line is FALSE) & send ATA again
|
|
// which aborts the whole thing....
|
|
//
|
|
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
#define ATV1 "ATV1"
|
|
#define AT "AT"
|
|
#define cr "\r"
|
|
#define cbszZero "0"
|
|
|
|
USHORT
|
|
T30ModemInit(PThrdGlbl pTG)
|
|
{
|
|
USHORT uLen, uRet;
|
|
|
|
/*** Inits (or re-inits) the COM port, Syncs up with Modem (at whatever,
|
|
speed), gets modem capabilities, puts it into CLASS0, syncs again,
|
|
flushes buffers and returns TRUE on success FALSE on failure
|
|
***/
|
|
|
|
DEBUG_FUNCTION_NAME(("T30ModemInit"));
|
|
|
|
PSSLogEntry(PSS_MSG, 0, "Modem initialization");
|
|
|
|
// Save the profile ID and key string.
|
|
pTG->FComModem.dwProfileID = DEF_BASEKEY;
|
|
|
|
uLen = min(_fstrlen(pTG->lpszPermanentLineID), sizeof(pTG->FComModem.rgchKey)-1);
|
|
_fmemcpy(pTG->FComModem.rgchKey, pTG->lpszPermanentLineID, uLen);
|
|
pTG->FComModem.rgchKey[uLen] = 0;
|
|
|
|
if (!uLen)
|
|
{
|
|
DebugPrintEx( DEBUG_ERR,
|
|
"Bad param: ProfileID=0x%lx; Key=%s",
|
|
(unsigned long) pTG->FComModem.dwProfileID,
|
|
(LPSTR) pTG->FComModem.rgchKey);
|
|
return INIT_INTERNAL_ERROR;
|
|
}
|
|
|
|
InitMonitorLogging(pTG);
|
|
|
|
//
|
|
// Get the modem info before talking to h/w.
|
|
//
|
|
|
|
if(uRet = iModemGetCmdTab( pTG,
|
|
&pTG->FComModem.CurrCmdTab,
|
|
&pTG->FComModem.CurrMdmCaps))
|
|
{
|
|
goto error;
|
|
}
|
|
|
|
// use MultiLine because we may get asynchronous RING responses
|
|
// at arbitrary times when on-hook
|
|
|
|
if(pTG->FComModem.CurrCmdTab.szSetup && (uLen=(USHORT)_fstrlen(pTG->FComModem.CurrCmdTab.szSetup)))
|
|
{
|
|
if(OfflineDialog2(pTG, (LPSTR)pTG->FComModem.CurrCmdTab.szSetup, uLen, cbszOK, cbszERROR) != 1)
|
|
{
|
|
DebugPrintEx( DEBUG_ERR,
|
|
"Error in SETUP string: %s",
|
|
(LPSTR)pTG->FComModem.CurrCmdTab.szSetup);
|
|
// SETUP is usually the defaults?? So do nothing if this fails
|
|
// uRet = INIT_MODEMERROR;
|
|
// goto error;
|
|
}
|
|
}
|
|
|
|
switch (pTG->dwSpeakerMode)
|
|
{
|
|
case MDMSPKR_OFF:
|
|
pTG->NCUParams.SpeakerControl = 0;
|
|
break;
|
|
|
|
case MDMSPKR_DIAL:
|
|
pTG->NCUParams.SpeakerControl = 1;
|
|
break;
|
|
|
|
case MDMSPKR_ON:
|
|
pTG->NCUParams.SpeakerControl = 2;
|
|
break;
|
|
|
|
default:
|
|
pTG->NCUParams.SpeakerControl = 0;
|
|
break;
|
|
}
|
|
|
|
switch (pTG->dwSpeakerVolume)
|
|
{
|
|
case MDMVOL_LOW:
|
|
pTG->NCUParams.SpeakerVolume = 0;
|
|
break;
|
|
|
|
case MDMVOL_MEDIUM:
|
|
pTG->NCUParams.SpeakerVolume = 2;
|
|
break;
|
|
|
|
case MDMVOL_HIGH:
|
|
pTG->NCUParams.SpeakerVolume = 3;
|
|
break;
|
|
|
|
default:
|
|
pTG->NCUParams.SpeakerVolume = 0;
|
|
break;
|
|
}
|
|
|
|
|
|
pTG->NCUParams.DialBlind = 4; //X4
|
|
|
|
// need to do this every time after a Reset/AT&F
|
|
if(! iModemSetNCUParams( pTG,
|
|
-1,
|
|
pTG->NCUParams.SpeakerControl,
|
|
pTG->NCUParams.SpeakerVolume,
|
|
pTG->NCUParams.DialBlind,
|
|
pTG->NCUParams.SpeakerRing))
|
|
{
|
|
DebugPrintEx(DEBUG_WRN,"Can't Set NCU params - Ignoring that");
|
|
}
|
|
|
|
// Why is this here??
|
|
FComFlush(pTG);
|
|
|
|
pTG->FComStatus.fModemInit = TRUE;
|
|
uRet = INIT_OK;
|
|
goto end;
|
|
|
|
error:
|
|
FComClose(pTG);
|
|
pTG->FComStatus.fModemInit = FALSE;
|
|
// fall through...
|
|
end:
|
|
return uRet;
|
|
}
|
|
|
|
|
|
LPCMDTAB iModemGetCmdTabPtr(PThrdGlbl pTG)
|
|
{
|
|
return (pTG->FComStatus.fModemInit) ? &pTG->FComModem.CurrCmdTab: NULL;
|
|
}
|
|
|
|
|
|
#define PARAMSBUFSIZE 60
|
|
#define fDETECT_DIALTONE 1
|
|
#define fDETECT_BUSYTONE 2
|
|
|
|
BOOL iModemSetNCUParams
|
|
(
|
|
PThrdGlbl pTG,
|
|
int comma,
|
|
int speaker,
|
|
int volume,
|
|
int fBlind,
|
|
int fRingAloud
|
|
)
|
|
{
|
|
|
|
char bBuf[PARAMSBUFSIZE];
|
|
USHORT uLen;
|
|
|
|
DEBUG_FUNCTION_NAME(("iModemSetNCUParams"));
|
|
|
|
_fstrcpy(bBuf, cbszJustAT);
|
|
uLen = sizeof(cbszJustAT)-1;
|
|
|
|
// +++ If we want to split this into dial-tone & busy-tone we
|
|
// Do it here...
|
|
if ( (fBlind >= 0) && (pTG->ModemKeyCreationId == MODEMKEY_FROM_NOTHING) )
|
|
{
|
|
UINT u=0;
|
|
switch(fBlind)
|
|
{
|
|
case 0:
|
|
break;
|
|
case fDETECT_DIALTONE:
|
|
u=2;
|
|
break;
|
|
case fDETECT_BUSYTONE:
|
|
u=3;
|
|
break;
|
|
default:
|
|
u=4;
|
|
break;
|
|
}
|
|
uLen += (USHORT)wsprintf(bBuf+uLen, cbszXn, u);
|
|
}
|
|
|
|
if(comma >= 0)
|
|
{
|
|
if(comma > 255)
|
|
{
|
|
comma = 255;
|
|
}
|
|
uLen += (USHORT)wsprintf(bBuf+uLen, cbszS8, comma);
|
|
}
|
|
if(speaker >= 0)
|
|
{
|
|
if(speaker > 2)
|
|
{
|
|
speaker = 2;
|
|
}
|
|
uLen += (USHORT)wsprintf(bBuf+uLen, cbszMn, speaker);
|
|
}
|
|
if(volume >= 0)
|
|
{
|
|
if(volume > 3)
|
|
{
|
|
volume = 3;
|
|
}
|
|
uLen += (USHORT)wsprintf(bBuf+uLen, cbszLn, volume);
|
|
}
|
|
|
|
// do something with RingAloud
|
|
|
|
bBuf[uLen++] = '\r';
|
|
bBuf[uLen] = 0;
|
|
|
|
// use MultiLine because we may get asynchronous RING responses
|
|
// at arbitrary times when on-hook
|
|
if(OfflineDialog2(pTG, (LPSTR)bBuf, uLen, cbszOK, cbszERROR) != 1)
|
|
{
|
|
DebugPrintEx(DEBUG_ERR,"Can't Set NCU params");
|
|
return FALSE;
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
UWORD GetCap(PThrdGlbl pTG, CBPSTR cbpstrSend, UWORD uwLen)
|
|
{
|
|
UWORD uRet1=0, uRet2=0, uRet3=0;
|
|
|
|
DEBUG_FUNCTION_NAME(("GetCap"));
|
|
// We call GetCapAux twice and if they don't match we
|
|
// call it a 3rd time and arbitrate. Provided it doesn't
|
|
// fail the first time.
|
|
if (!(uRet1=GetCapAux(pTG, cbpstrSend, uwLen)))
|
|
goto end;
|
|
|
|
uRet2=GetCapAux(pTG, cbpstrSend, uwLen);
|
|
if (uRet1!=uRet2)
|
|
{
|
|
DebugPrintEx( DEBUG_WRN,
|
|
"2nd getcaps return differs 1=%u,2=%u",
|
|
(unsigned)uRet1,
|
|
(unsigned)uRet2);
|
|
|
|
uRet3=GetCapAux(pTG, cbpstrSend, uwLen);
|
|
if (uRet1==uRet2 || uRet1==uRet3)
|
|
{
|
|
goto end;
|
|
}
|
|
else if (uRet2==uRet3)
|
|
{
|
|
uRet1=uRet2;
|
|
goto end;
|
|
}
|
|
else
|
|
{
|
|
DebugPrintEx( DEBUG_ERR,
|
|
"all 2 getcaps differ! 1=%u,2=%u, 3=%u",
|
|
(unsigned) uRet1, (unsigned) uRet2,
|
|
(unsigned) uRet3);
|
|
}
|
|
}
|
|
|
|
end:
|
|
return uRet1;
|
|
|
|
}
|
|
|
|
UWORD GetCapAux(PThrdGlbl pTG, CBPSTR cbpstrSend, UWORD uwLen)
|
|
{
|
|
NPSTR sz;
|
|
BYTE speed, high;
|
|
UWORD i, code;
|
|
USHORT retry;
|
|
USHORT uRet;
|
|
|
|
DEBUG_FUNCTION_NAME(("GetCapAux"));
|
|
retry = 0;
|
|
restart:
|
|
retry++;
|
|
if(retry > 2)
|
|
return 0;
|
|
|
|
DebugPrintEx(DEBUG_MSG,"Want Caps for (%s)", (LPSTR)cbpstrSend);
|
|
|
|
pTG->fMegaHertzHack = TRUE;
|
|
uRet = OfflineDialog2(pTG, (LPSTR)cbpstrSend, uwLen, cbszOK, cbszERROR);
|
|
pTG->fMegaHertzHack=FALSE;
|
|
|
|
// sometimes we don't get the OK so try to parse what we got anyway
|
|
DebugPrintEx(DEBUG_MSG,"LastLine = (%s)",(LPSTR)(&(pTG->FComModem.bLastReply)));
|
|
|
|
if(uRet == 2)
|
|
goto restart;
|
|
|
|
if(_fstrlen((LPSTR)pTG->FComModem.bLastReply) == 0)
|
|
goto restart;
|
|
|
|
speed = 0;
|
|
high = 0;
|
|
for(i=0, sz=pTG->FComModem.bLastReply, code=0; i<REPLYBUFSIZE && sz[i]; i++)
|
|
{
|
|
if(sz[i] >= '0' && sz[i] <= '9')
|
|
{
|
|
code = code*10 + (sz[i] - '0');
|
|
continue;
|
|
}
|
|
// reached a non-numeric char
|
|
// if its teh first after a code, need to process the code.
|
|
|
|
switch(code)
|
|
{
|
|
case 0: continue; // not the first char after a code
|
|
case 3: break;
|
|
case 24: break;
|
|
case 48: speed |= V27; break;
|
|
case 72:
|
|
case 96: speed |= V29; break;
|
|
case 73:
|
|
case 97:
|
|
case 121:
|
|
case 145: speed |= V33; break; // long-train codes
|
|
case 74:
|
|
case 98:
|
|
case 122:
|
|
case 146: speed |= V17; break; // short-train codes
|
|
|
|
//case 92:
|
|
//case 93: break;
|
|
// case 120: // not legal
|
|
// case 144: // not legal
|
|
default:
|
|
DebugPrintEx( DEBUG_WRN,
|
|
"Ignoring unknown Modulation code = %d",
|
|
code);
|
|
code=0;
|
|
break;
|
|
}
|
|
if(code > high)
|
|
high=(BYTE)code;
|
|
|
|
// reset code counter after processing the baud rate code
|
|
code = 0;
|
|
}
|
|
|
|
if(speed == 0)
|
|
{
|
|
// got garbage in response to query
|
|
DebugPrintEx( DEBUG_MSG,
|
|
"Can't get Caps for (%s) = 0x%04x Highest=%d",
|
|
(LPSTR)cbpstrSend,
|
|
speed,
|
|
high);
|
|
return 0;
|
|
}
|
|
|
|
if(speed == 0x0F)
|
|
speed = V27_V29_V33_V17;
|
|
|
|
DebugPrintEx( DEBUG_MSG,
|
|
"Got Caps for (%s) = 0x%04x Highest=%d",
|
|
(LPSTR)cbpstrSend,
|
|
speed,
|
|
high);
|
|
|
|
return MAKEWORD(speed, high); // speed==low byte
|
|
}
|
|
|
|
BOOL iModemGetCaps
|
|
(
|
|
PThrdGlbl pTG,
|
|
LPMODEMCAPS lpMdmCaps,
|
|
DWORD dwSpeed,
|
|
LPSTR lpszReset,
|
|
LPDWORD lpdwGot
|
|
)
|
|
{
|
|
/** Modem must be synced up and in normal (non-fax) mode.
|
|
Queries available classes,
|
|
HDLC & Data receive and transmit speeds. Returns
|
|
TRUE if Modem is Class1 or Class2, FALSE if not fax modem
|
|
or other error. Sets the fields in the ET30INST struct **/
|
|
// lpszReset, if nonempty, will be used to reset the modem after
|
|
// the FCLASS=? command see comment about US Robotics Sportster below...
|
|
|
|
UWORD i, uwRet;
|
|
BYTE speed;
|
|
BOOL err;
|
|
NPSTR sz;
|
|
USHORT retry, uResp;
|
|
|
|
DEBUG_FUNCTION_NAME(("iModemGetCaps"));
|
|
if (!*lpdwGot)
|
|
{
|
|
_fmemset(lpMdmCaps, 0, sizeof(MODEMCAPS));
|
|
}
|
|
|
|
if (*lpdwGot & fGOTCAP_CLASSES)
|
|
goto GotClasses;
|
|
|
|
for(retry=0; retry<2; retry++)
|
|
{
|
|
pTG->fMegaHertzHack = TRUE;
|
|
uResp = OfflineDialog2(pTG, (LPSTR)cbszQUERY_CLASS, sizeof(cbszQUERY_CLASS)-1, cbszOK, cbszERROR);
|
|
pTG->fMegaHertzHack=FALSE;
|
|
if(uResp != 2)
|
|
break;
|
|
}
|
|
|
|
// sometimes we don't get the OK so try to parse what we got anyway
|
|
DebugPrintEx( DEBUG_MSG,
|
|
"LastLine = (%s)",
|
|
(LPSTR)(&(pTG->FComModem.bLastReply)));
|
|
|
|
|
|
lpMdmCaps->uClasses = 0;
|
|
for(i=0, sz=pTG->FComModem.bLastReply; i<REPLYBUFSIZE && sz[i]; i++)
|
|
{
|
|
UINT uDig=0, uDec=(UINT)-1;
|
|
|
|
// This code will accept 1.x as class1, 2 as class2 and 2.x as class2.0
|
|
// Also, it will not detect class 1 in 2.1 or class2 in 1.2 etc.
|
|
// (JDecuir newest class2.0 is labeled class2.1, and he talks
|
|
// of class 1.0...)
|
|
if(sz[i] >= '0' && sz[i] <= '9')
|
|
{
|
|
uDig = sz[i]- '0';
|
|
if (sz[i+1]=='.')
|
|
{
|
|
i++;
|
|
if(sz[i+1] >= '0' && sz[i+1] <= '9')
|
|
{
|
|
uDec = sz[i] - '0';
|
|
i++;
|
|
}
|
|
}
|
|
}
|
|
if(uDig==1)
|
|
{
|
|
lpMdmCaps->uClasses |= FAXCLASS1;
|
|
}
|
|
if(uDig==2)
|
|
{
|
|
if (uDec==((UINT)-1))
|
|
{
|
|
lpMdmCaps->uClasses |= FAXCLASS2;
|
|
}
|
|
else
|
|
{
|
|
lpMdmCaps->uClasses |= FAXCLASS2_0;
|
|
}
|
|
}
|
|
}
|
|
*lpdwGot |= fGOTCAP_CLASSES;
|
|
|
|
GotClasses:
|
|
|
|
if(!lpMdmCaps->uClasses)
|
|
{
|
|
DebugPrintEx(DEBUG_ERR,"Not a fax modem or unsupported fax class");
|
|
*lpdwGot &= ~(fGOTCAP_CLASSES|fGOTCAP_SENDSPEEDS|fGOTCAP_RECVSPEEDS);
|
|
return FALSE;
|
|
}
|
|
|
|
if(!(lpMdmCaps->uClasses & FAXCLASS1))
|
|
return TRUE;
|
|
|
|
///////////////// rest is for Class1 only //////////////////////////
|
|
|
|
if(lpszReset && *lpszReset && iModemReset(pTG, lpszReset) < 0)
|
|
return FALSE;
|
|
//////////
|
|
|
|
if(!iiModemGoClass(pTG, 1, dwSpeed))
|
|
goto NotClass1;
|
|
|
|
err = FALSE;
|
|
if (!(*lpdwGot & fGOTCAP_SENDSPEEDS))
|
|
{
|
|
uwRet = GetCap( pTG, cbszQUERY_FTM, sizeof(cbszQUERY_FTM)-1);
|
|
err = (err || uwRet==0);
|
|
speed = LOBYTE(uwRet);
|
|
lpMdmCaps->uSendSpeeds = speed;
|
|
*lpdwGot |= fGOTCAP_SENDSPEEDS;
|
|
}
|
|
if (!(*lpdwGot & fGOTCAP_RECVSPEEDS))
|
|
{
|
|
uwRet = GetCap(pTG, cbszQUERY_FRM, sizeof(cbszQUERY_FRM)-1);
|
|
err = (err || uwRet==0);
|
|
speed = LOBYTE(uwRet);
|
|
lpMdmCaps->uRecvSpeeds = speed;
|
|
*lpdwGot |= fGOTCAP_RECVSPEEDS;
|
|
}
|
|
|
|
if(!iiModemGoClass(pTG, 0, dwSpeed))
|
|
err = TRUE;
|
|
|
|
if(err)
|
|
{
|
|
DebugPrintEx(DEBUG_ERR,"Cannot get capabilities");
|
|
goto NotClass1;
|
|
}
|
|
|
|
DebugPrintEx(DEBUG_MSG,"Got Caps");
|
|
return TRUE;
|
|
|
|
NotClass1:
|
|
// Reported Class1 but failed AT+FCLASS=1 or one of the Cap queries
|
|
// GVC9624Vbis does this. See bug#1016
|
|
// FIX: Just zap out the Class1 bit. If any other class supported
|
|
// then return TRUE, else FALSE
|
|
|
|
lpMdmCaps->uClasses &= (~FAXCLASS1); // make the Class1 bit==0
|
|
if(lpMdmCaps->uClasses)
|
|
{
|
|
return TRUE;
|
|
}
|
|
else
|
|
{
|
|
*lpdwGot &= ~(fGOTCAP_CLASSES|fGOTCAP_SENDSPEEDS|fGOTCAP_RECVSPEEDS);
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
BOOL iModemGoClass(PThrdGlbl pTG, USHORT uClass)
|
|
{
|
|
return iiModemGoClass(pTG, uClass, pTG->FComModem.CurrCmdTab.dwSerialSpeed);
|
|
}
|
|
|
|
|
|
BOOL iiModemGoClass(PThrdGlbl pTG, USHORT uClass, DWORD dwSpeed)
|
|
{
|
|
int i;
|
|
USHORT uBaud;
|
|
|
|
DEBUG_FUNCTION_NAME(("iiModemGoClass"));
|
|
|
|
for(i=0; i<3; i++)
|
|
{
|
|
// UDS V.3257 modem needs this time, because if we send it a
|
|
// command too quickly after the previous response, it ignores
|
|
// it or gets garbage
|
|
Sleep(100);
|
|
FComFlush(pTG);
|
|
PSSLogEntry(PSS_MSG, 2, "send: \"%s\"", rgcbpstrGO_CLASS[uClass]);
|
|
if(!FComDirectSyncWriteFast(pTG, (LPB)rgcbpstrGO_CLASS[uClass], uLenGO_CLASS[uClass]))
|
|
goto error;
|
|
// wait 500ms. Give modem enough time to get into Class1 mode
|
|
// otherwise the AT we send may abort the transition
|
|
Sleep(500);
|
|
|
|
if(dwSpeed)
|
|
{
|
|
USHORT usSpeed = (USHORT) dwSpeed;
|
|
uBaud = usSpeed;
|
|
}
|
|
else if (pTG->SerialSpeedInit)
|
|
{
|
|
uBaud = pTG->SerialSpeedInit;
|
|
}
|
|
else
|
|
{
|
|
uBaud = 57600;
|
|
}
|
|
|
|
// RSL don't do hard-coded 2400 for class0.
|
|
|
|
FComSetBaudRate(pTG, uBaud);
|
|
|
|
FComFlush(pTG);
|
|
if(iModemSync(pTG) >= 0)
|
|
{
|
|
return TRUE;
|
|
}
|
|
}
|
|
error:
|
|
// no point -- and we'll smash our settings
|
|
// iModemReset();
|
|
// error is already set to ERR_NO_RESPONSE inside HayesSync()
|
|
DebugPrintEx(DEBUG_ERR,"Cant go to Class %d", uClass);
|
|
return FALSE;
|
|
}
|
|
|
|
BOOL iModemClose(PThrdGlbl pTG)
|
|
{
|
|
USHORT uLen;
|
|
BOOL fRet=FALSE;
|
|
|
|
DEBUG_FUNCTION_NAME(("iModemClose"));
|
|
|
|
if(!pTG->FComStatus.fModemInit)
|
|
return TRUE;
|
|
|
|
|
|
/** Hangs up the phone if it is off hook, closes the COM port
|
|
and returns. If hangup fails then port is also left open. **/
|
|
|
|
|
|
if(!iModemHangup(pTG))
|
|
goto lNext;
|
|
|
|
|
|
if (pTG->Comm.fEnableHandoff && pTG->Comm.fDataCall)
|
|
{
|
|
goto lNext;
|
|
}
|
|
|
|
if(pTG->FComModem.CurrCmdTab.szExit && (uLen=(USHORT)_fstrlen(pTG->FComModem.CurrCmdTab.szExit)))
|
|
{
|
|
if(OfflineDialog2(pTG, (LPSTR)pTG->FComModem.CurrCmdTab.szExit, uLen, cbszOK, cbszERROR) != 1)
|
|
{
|
|
DebugPrintEx( DEBUG_ERR,
|
|
"Error in EXIT string: %s",
|
|
(LPSTR)pTG->FComModem.CurrCmdTab.szExit);
|
|
}
|
|
}
|
|
|
|
lNext:
|
|
if(FComClose(pTG))
|
|
{
|
|
pTG->FComStatus.fModemInit = FALSE;
|
|
fRet=TRUE;
|
|
}
|
|
|
|
return fRet;
|
|
}
|
|
|
|
BOOL iModemHangup(PThrdGlbl pTG)
|
|
{
|
|
BOOL fRet=FALSE;
|
|
|
|
DEBUG_FUNCTION_NAME(("iModemHangup"));
|
|
|
|
if(!pTG->FComStatus.fOffHook)
|
|
{
|
|
DebugPrintEx( DEBUG_WRN,
|
|
"The modem is already on-hook!!!! return without doing nothing");
|
|
return TRUE;
|
|
}
|
|
|
|
// Note: iModemHangup is called by NCULink in ddi.c.
|
|
// Rather than do adaptive-answer-specific code in ddi.c as well,
|
|
// we simply ignore the hangup command in the following case...
|
|
|
|
if (pTG->Comm.fEnableHandoff && pTG->Comm.fDataCall)
|
|
{
|
|
DebugPrintEx(DEBUG_WRN,"IGNORING Hangup of datamodem call");
|
|
return TRUE;
|
|
}
|
|
|
|
PSSLogEntry(PSS_MSG, 1, "Hanging up");
|
|
|
|
// FComDTR(FALSE); // Lower DTR to hangup in ModemHangup
|
|
// Need to have &D2 in init string for this.
|
|
|
|
// Do this twice. There is a bizarre case where you drop DTR,
|
|
// then go into Dialog, flush, send ATH0, then the modem gives
|
|
// you an OK for the DTR, and you take it as one for the ATH0
|
|
// maybe that's ok....if this gets too slow, skip this.
|
|
HayesSyncSpeed(pTG, cbszHANGUP, sizeof(cbszHANGUP)-1);
|
|
|
|
if(HayesSyncSpeed(pTG, cbszHANGUP, sizeof(cbszHANGUP)-1) < 0)
|
|
{
|
|
FComDTR(pTG, FALSE); // Lower DTR on stubborn hangups in ModemHangup
|
|
Sleep(1000); // pause 1 second
|
|
FComDTR(pTG, TRUE); // raise it again. Some modems return to cmd state
|
|
// only when this is raised again
|
|
|
|
if(iModemReset(pTG, pTG->FComModem.CurrCmdTab.szReset) < 0)
|
|
goto error;
|
|
if(HayesSyncSpeed(pTG, cbszHANGUP, sizeof(cbszHANGUP)-1) < 0)
|
|
goto error;
|
|
}
|
|
pTG->FComStatus.fOffHook = FALSE;
|
|
|
|
if(!iiModemGoClass(pTG, 0, pTG->FComModem.CurrCmdTab.dwSerialSpeed))
|
|
goto end;
|
|
// Can also ignore this return value. Just for tidier cleanup
|
|
|
|
// Avoid! we'll smash our settings
|
|
// iModemReset();
|
|
fRet=TRUE;
|
|
goto end;
|
|
|
|
error:
|
|
FComDTR(pTG, TRUE); // raise it again
|
|
// fall through...
|
|
|
|
end:
|
|
return fRet;
|
|
}
|
|
|
|
/*++
|
|
Routine Description:
|
|
Print the dial command to PSS log, hiding the actual number, like this: "ATDT ####"
|
|
Arguments:
|
|
pTG
|
|
lpszFormat - format for sprintf, usually #defined as "ATD%c %s"
|
|
chMod - Dial mode ('T' or 'P')
|
|
iLen - Length of number
|
|
Return Value:
|
|
None
|
|
--*/
|
|
|
|
void LogDialCommand(PThrdGlbl pTG, LPSTR lpszFormat, char chMod, int iLen)
|
|
{
|
|
BYTE bBufHideDest[DIALBUFSIZE] = {'\0'};
|
|
int i;
|
|
|
|
sprintf(bBufHideDest, lpszFormat, chMod, TEXT(""));
|
|
if (_tcslen(bBufHideDest)+iLen > DIALBUFSIZE-1)
|
|
{ // Not enough room - don't log!
|
|
return;
|
|
}
|
|
for (i=0; i<iLen; i++)
|
|
{
|
|
strcat(bBufHideDest, "#");
|
|
}
|
|
PSSLogEntry(PSS_MSG, 2, "send: \"%s\"", bBufHideDest);
|
|
}
|
|
|
|
|
|
USHORT iModemDial(PThrdGlbl pTG, LPSTR lpszDial)
|
|
{
|
|
ULONG ulTimeout;
|
|
USHORT uRet, uLen, uDialStringLen;
|
|
BYTE bBuf[DIALBUFSIZE];
|
|
CBPSTR cbpstr;
|
|
char chMod = pTG->NCUParams.chDialModifier;
|
|
DWORD dwDialTime = 0;
|
|
char KeyName[200];
|
|
HKEY hKey;
|
|
char BlindDialString[200];
|
|
char RegBlindDialString[200];
|
|
long lRet;
|
|
DWORD dwSize;
|
|
DWORD dwType;
|
|
|
|
DEBUG_FUNCTION_NAME(("iModemDial"));
|
|
|
|
pTG->FComStatus.fOffHook = TRUE; // Has to be here. Can get an error return
|
|
// below even after connecting
|
|
// and we want to hangup after that!!
|
|
pTG->Comm.fDataCall=FALSE;
|
|
|
|
//
|
|
// check "Modems->Properties->Connection->Wait for dial tone" setting before dialing
|
|
// to correctly set ATX to possibly blind dial
|
|
//
|
|
if (pTG->fBlindDial)
|
|
{
|
|
// create default string
|
|
sprintf(BlindDialString, "ATX3\r");
|
|
|
|
// need to check Unimodem Settings\Blind_On key.
|
|
sprintf(KeyName, "%s\\Settings", pTG->lpszUnimodemKey);
|
|
|
|
lRet = RegOpenKeyEx(
|
|
HKEY_LOCAL_MACHINE,
|
|
KeyName,
|
|
0,
|
|
KEY_READ,
|
|
&hKey);
|
|
|
|
if (lRet != ERROR_SUCCESS)
|
|
{
|
|
DebugPrintEx(DEBUG_ERR, "Can't read Unimodem Settings key %s", KeyName);
|
|
}
|
|
else
|
|
{
|
|
dwSize = sizeof(RegBlindDialString);
|
|
|
|
lRet = RegQueryValueEx(
|
|
hKey,
|
|
"Blind_On",
|
|
0,
|
|
&dwType,
|
|
RegBlindDialString,
|
|
&dwSize);
|
|
|
|
RegCloseKey(hKey);
|
|
|
|
if (lRet != ERROR_SUCCESS)
|
|
{
|
|
DebugPrintEx( DEBUG_ERR,
|
|
"Can't read Unimodem key\\Settings\\Blind_On value");
|
|
}
|
|
else if (RegBlindDialString)
|
|
{
|
|
sprintf(BlindDialString, "AT%s\r", RegBlindDialString);
|
|
}
|
|
}
|
|
}
|
|
|
|
if(!iiModemGoClass(pTG, 1, pTG->FComModem.CurrCmdTab.dwSerialSpeed))
|
|
{
|
|
uRet = CONNECT_ERROR;
|
|
goto error;
|
|
}
|
|
|
|
//
|
|
// blind dial set here if requested by user
|
|
//
|
|
if (pTG->fBlindDial && BlindDialString)
|
|
{
|
|
uLen = (USHORT)strlen(BlindDialString);
|
|
if(OfflineDialog2(pTG, BlindDialString, uLen, cbszOK, cbszERROR) != 1)
|
|
{
|
|
DebugPrintEx( DEBUG_ERR,
|
|
"Error in BLIND DIAL string: %s",
|
|
BlindDialString);
|
|
}
|
|
}
|
|
|
|
if(pTG->FComModem.CurrCmdTab.szPreDial && (uLen=(USHORT)_fstrlen(pTG->FComModem.CurrCmdTab.szPreDial)))
|
|
{
|
|
if(OfflineDialog2(pTG, (LPSTR)pTG->FComModem.CurrCmdTab.szPreDial, uLen, cbszOK, cbszERROR) != 1)
|
|
{
|
|
DebugPrintEx( DEBUG_ERR,
|
|
"Error in PREDIAL string: %s",
|
|
(LPSTR)pTG->FComModem.CurrCmdTab.szPreDial);
|
|
}
|
|
}
|
|
|
|
cbpstr = cbszDIAL;
|
|
|
|
// If the dial string already has a T or P prefix, we use that
|
|
// instead.
|
|
{
|
|
char c=0;
|
|
while((c=*lpszDial) && c==' ')
|
|
{
|
|
lpszDial++;
|
|
}
|
|
|
|
if (c=='t'|| c=='T' || c=='p'|| c=='P')
|
|
{
|
|
chMod = c;
|
|
lpszDial++;
|
|
while((c=*lpszDial) && c==' ')
|
|
{
|
|
lpszDial++;
|
|
}
|
|
}
|
|
}
|
|
|
|
// in mdmcmds.h you can find this line: cbszDIAL = "ATD%c %s\r"
|
|
uLen = (USHORT)wsprintf(bBuf, cbpstr, chMod, (LPSTR)lpszDial);
|
|
|
|
// Need to set an approriate timeout here. A minimum of 15secs is too short
|
|
// (experiment calling machines within a PABX), plus one has to give extra
|
|
// time for machines that pick up after 2 or 4 rings and also for long distance
|
|
// calls. I take a minumum of 30secs and add 3secs for each digits over 7
|
|
// (unless it's pulse dial in which case I add 8secs/digit).
|
|
// (I'm assuming that a long-distance call will take a minimum of 8 digits
|
|
// anywhere in ths world!). Fax machines I've tested wait about 30secs
|
|
// independent of everything.
|
|
|
|
uDialStringLen = (USHORT)_fstrlen(lpszDial);
|
|
|
|
ulTimeout = DIAL_TIMEOUT;
|
|
if(uDialStringLen > 7)
|
|
{
|
|
ulTimeout += ((chMod=='p' || chMod=='P')?8000:3000)
|
|
* (uDialStringLen - 7);
|
|
}
|
|
|
|
|
|
pTG->FComStatus.fInDial = TRUE;
|
|
// look for MultiLine, just in case we get echo or garbage.
|
|
// Nothing lost, since on failure of this we can't do anything
|
|
|
|
// uRet = iiModemDialog((LPB)bBuf, uLen, ulTimeout, TRUE, 1, TRUE,
|
|
// cbszCONNECT, cbszBUSY, cbszNOANSWER,
|
|
// cbszNODIALTONE, cbszERROR, (CBPSTR)NULL);
|
|
// Send seperately & use iiModemDialog only for the response
|
|
|
|
// all this just to send the ATDT
|
|
FComFlushOutput(pTG);
|
|
Sleep(200); // 100 is not too long for this IMPORTANT one!
|
|
FComFlushInput(pTG);
|
|
|
|
LogDialCommand(pTG, cbszDIAL, chMod, uDialStringLen);
|
|
|
|
FComDirectAsyncWrite(pTG, bBuf, uLen);
|
|
// now try to get a response
|
|
dwDialTime = GetTickCount();
|
|
uRet = iiModemDialog( pTG,
|
|
0,
|
|
0,
|
|
ulTimeout,
|
|
TRUE,
|
|
1,
|
|
TRUE,
|
|
cbszCONNECT,
|
|
cbszBUSY,
|
|
cbszNOANSWER,
|
|
cbszNODIALTONE,
|
|
cbszERROR,
|
|
cbszBLACKLISTED,
|
|
cbszDELAYED,
|
|
cbszNOCARRIER,
|
|
(CBPSTR)NULL);
|
|
|
|
pTG->FComStatus.fInDial = FALSE;
|
|
DebugPrintEx(DEBUG_MSG,"ModemDial -- got %d response from Dialog", uRet);
|
|
|
|
|
|
#if !((CONNECT_TIMEOUT==0) && (CONNECT_OK==1) && (CONNECT_BUSY==2) && (CONNECT_NOANSWER == 3) && (CONNECT_NODIALTONE==4) && (CONNECT_ERROR==5) && (CONNECT_BLACKLISTED==6) && (CONNECT_DELAYED==7))
|
|
#error CONNECT defines not correct ERROR, OK, BUSY, NOANSWER, NODIALTONE == CONNECT_ERROR, CONNECT_OK, CONNECT_BUSY, CONNECT_NOANSWER, CONNECT_NODIALTONE
|
|
#else
|
|
#pragma message("verified CONNECT defines")
|
|
#endif
|
|
|
|
|
|
switch(uRet)
|
|
{
|
|
case CONNECT_TIMEOUT:
|
|
pTG->fFatalErrorWasSignaled = 1;
|
|
SignalStatusChange(pTG, FS_NO_ANSWER);
|
|
PSSLogEntry(PSS_ERR, 1, "Response - timeout");
|
|
break;
|
|
|
|
case CONNECT_OK:
|
|
PSSLogEntry(PSS_MSG, 1, "Response - CONNECT");
|
|
pTG->fReceivedHDLCflags = TRUE;
|
|
break;
|
|
|
|
case CONNECT_BUSY:
|
|
pTG->fFatalErrorWasSignaled = 1;
|
|
SignalStatusChange(pTG, FS_BUSY);
|
|
PSSLogEntry(PSS_ERR, 1, "Response - BUSY");
|
|
break;
|
|
|
|
case CONNECT_NOANSWER:
|
|
pTG->fFatalErrorWasSignaled = 1;
|
|
SignalStatusChange(pTG, FS_NO_ANSWER);
|
|
PSSLogEntry(PSS_ERR, 1, "Response - NO ANSWER");
|
|
break;
|
|
|
|
case CONNECT_NODIALTONE:
|
|
pTG->fFatalErrorWasSignaled = 1;
|
|
SignalStatusChange(pTG, FS_NO_DIAL_TONE);
|
|
PSSLogEntry(PSS_ERR, 1, "Response - NO DIALTONE");
|
|
break;
|
|
|
|
case CONNECT_ERROR:
|
|
pTG->fFatalErrorWasSignaled = 1;
|
|
SignalStatusChange(pTG, FS_NO_ANSWER);
|
|
PSSLogEntry(PSS_ERR, 1, "Response - ERROR");
|
|
break;
|
|
|
|
case CONNECT_BLACKLISTED:
|
|
pTG->fFatalErrorWasSignaled = 1;
|
|
SignalStatusChange(pTG, FS_CALL_BLACKLISTED);
|
|
PSSLogEntry(PSS_ERR, 1, "Response - BLACKLISTED");
|
|
break;
|
|
|
|
case CONNECT_DELAYED:
|
|
pTG->fFatalErrorWasSignaled = 1;
|
|
SignalStatusChange(pTG, FS_CALL_DELAYED);
|
|
PSSLogEntry(PSS_ERR, 1, "Response - DELAYED");
|
|
break;
|
|
|
|
case 8: {
|
|
DWORD dwDelta = GetTickCount() - dwDialTime;
|
|
PSSLogEntry(PSS_ERR, 1, "Response - NO CARRIER");
|
|
if (dwDelta < 5000L)
|
|
{
|
|
DebugPrintEx(DEBUG_WRN,"Dial: Pretending it's BUSY");
|
|
pTG->fFatalErrorWasSignaled = 1;
|
|
SignalStatusChange(pTG, FS_BUSY);
|
|
uRet = CONNECT_BUSY;
|
|
}
|
|
else
|
|
{
|
|
DebugPrintEx(DEBUG_WRN,"Dial: Pretending it's TIMEOUT");
|
|
pTG->fFatalErrorWasSignaled = 1;
|
|
SignalStatusChange(pTG, FS_NO_ANSWER);
|
|
uRet = CONNECT_TIMEOUT;
|
|
}
|
|
}
|
|
break;
|
|
|
|
}
|
|
|
|
if(uRet == CONNECT_OK)
|
|
{
|
|
goto done;
|
|
}
|
|
else
|
|
{
|
|
if(uRet == CONNECT_TIMEOUT)
|
|
{
|
|
pTG->fFatalErrorWasSignaled = 1;
|
|
SignalStatusChange(pTG, FS_NO_ANSWER);
|
|
|
|
uRet = CONNECT_NOANSWER;
|
|
// call it a no answer
|
|
}
|
|
|
|
goto error;
|
|
}
|
|
|
|
error:
|
|
if(!iModemHangup(pTG))
|
|
{
|
|
// at this point in teh production version we
|
|
// need to call some OS reboot function!!
|
|
DebugPrintEx(DEBUG_ERR,"Can't Hangup after DIALFAIL");
|
|
uRet = CONNECT_ERROR;
|
|
}
|
|
// fall through
|
|
done:
|
|
return uRet;
|
|
}
|
|
|
|
|
|
USHORT iModemAnswer(PThrdGlbl pTG)
|
|
{
|
|
CBPSTR cbpstr;
|
|
USHORT uLen, uRet;
|
|
char Command[400];
|
|
int i;
|
|
|
|
DEBUG_FUNCTION_NAME(("iModemAnswer"));
|
|
|
|
pTG->FComStatus.fOffHook=TRUE; // Has to be here. Can screwup after answering
|
|
// but before CONNECT and we want to hangup
|
|
// after that!!
|
|
pTG->Comm.fDataCall=FALSE;
|
|
|
|
//
|
|
// below is Adaptive Answer handling.
|
|
// It is separate because all the commands are defined via INF
|
|
//
|
|
|
|
if (pTG->AdaptiveAnswerEnable)
|
|
{
|
|
for (i=0; i< (int) pTG->AnswerCommandNum; i++)
|
|
{
|
|
strcpy (Command, pTG->AnswerCommand[i] );
|
|
|
|
if (i == (int) pTG->AnswerCommandNum - 1)
|
|
{
|
|
// last command-answer
|
|
FComFlushOutput(pTG);
|
|
Sleep(200); // 100 is not too long for this IMPORTANT one!
|
|
FComFlushInput(pTG);
|
|
|
|
PSSLogEntry(PSS_MSG, 2, "send: \"%s\"", Command);
|
|
FComDirectAsyncWrite(pTG, (LPSTR) Command, (USHORT) strlen(Command) );
|
|
|
|
pTG->FComStatus.fInAnswer = TRUE;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
if( (uRet = OfflineDialog2(pTG, (LPSTR) Command, (USHORT) strlen(Command), cbszOK, cbszERROR) ) != 1)
|
|
{
|
|
DebugPrintEx(DEBUG_ERR, "Answer %d=%s FAILED", i, Command);
|
|
}
|
|
else
|
|
{
|
|
DebugPrintEx(DEBUG_MSG, "Answer %d=%s rets OK", i, Command);
|
|
}
|
|
}
|
|
|
|
uRet=iModemGetAdaptiveResp(pTG);
|
|
pTG->FComStatus.fInAnswer=FALSE;
|
|
if (uRet==CONNECT_OK)
|
|
goto done;
|
|
else
|
|
goto error;
|
|
}
|
|
|
|
//
|
|
// assuming FAX call since can't determine that anyway...
|
|
//
|
|
else
|
|
{
|
|
// 5/95 JosephJ:Elliot Bug#3421 -- we issue the AT+FCLASS=1 command
|
|
// twice so that if one gets zapped by a RING the other will
|
|
// be OK.
|
|
if (pTG->FComModem.CurrCmdTab.dwFlags&fMDMSP_ANS_GOCLASS_TWICE)
|
|
iiModemGoClass(pTG, 1, pTG->FComModem.CurrCmdTab.dwSerialSpeed);
|
|
if(!iiModemGoClass(pTG, 1, pTG->FComModem.CurrCmdTab.dwSerialSpeed))
|
|
{
|
|
uRet = CONNECT_ERROR;
|
|
goto error;
|
|
}
|
|
}
|
|
|
|
if(pTG->FComModem.CurrCmdTab.szPreAnswer && (uLen=(USHORT)_fstrlen(pTG->FComModem.CurrCmdTab.szPreAnswer)))
|
|
{
|
|
if(OfflineDialog2(pTG, (LPSTR)pTG->FComModem.CurrCmdTab.szPreAnswer, uLen, cbszOK, cbszERROR) != 1)
|
|
{
|
|
DebugPrintEx( DEBUG_WRN,
|
|
"Error on PREANSWER string: %s",
|
|
(LPSTR)pTG->FComModem.CurrCmdTab.szPreAnswer);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
#define ANSWER_TIMEOUT 40000 // Random Timeout
|
|
// Need to wait reasonably long, so that we don't give up too easily
|
|
|
|
cbpstr = cbszANSWER;
|
|
uLen = sizeof(cbszANSWER)-1;
|
|
|
|
pTG->FComStatus.fInAnswer = TRUE;
|
|
|
|
// if(!iModemDialog((LPSTR)cbpstr, uLen, ANSWER_TIMEOUT, cbszCONNECT))
|
|
// look for MultiLine, just in case we get echo or garbage.
|
|
// Nothing lost, since on failure of this we can't do anything
|
|
|
|
// if(!iiModemDialog((LPB)cbpstr, uLen, ANSWER_TIMEOUT, TRUE, 1, TRUE,
|
|
// cbszCONNECT, (CBPSTR)NULL))
|
|
// Send seperately & use iiModemDialog only for the response
|
|
|
|
// all this just to send the ATA
|
|
|
|
|
|
FComFlushOutput(pTG);
|
|
Sleep(200); // 100 is not too long for this IMPORTANT one!
|
|
FComFlushInput(pTG);
|
|
PSSLogEntry(PSS_MSG, 2, "send: \"%s\"", cbpstr);
|
|
FComDirectAsyncWrite(pTG, cbpstr, uLen);
|
|
|
|
// this is used to complete a whole IO operation (presumably a short one)
|
|
// when this flag is set, the IO won't be disturbed by the abort event
|
|
// this flag should NOT be set for long periods of time since abort
|
|
// is disabled while it is set.
|
|
pTG->fStallAbortRequest = TRUE;
|
|
// now try to get a response
|
|
|
|
if(!iiModemDialog(pTG, 0, 0, ANSWER_TIMEOUT, TRUE, 1, TRUE, cbszCONNECT, (CBPSTR)NULL))
|
|
{
|
|
pTG->FComStatus.fInAnswer = FALSE;
|
|
PSSLogEntry(PSS_ERR, 1, "Response - ERROR");
|
|
|
|
// try to hangup and sync with modem. This should work
|
|
// even if phone is not really off hook
|
|
uRet = CONNECT_ERROR;
|
|
goto error;
|
|
}
|
|
else
|
|
{
|
|
pTG->FComStatus.fInAnswer = FALSE;
|
|
PSSLogEntry(PSS_MSG, 1, "Response - CONNECT");
|
|
|
|
uRet = CONNECT_OK;
|
|
goto done;
|
|
}
|
|
|
|
error:
|
|
|
|
if (pTG->Comm.fEnableHandoff && uRet==CONNECT_WRONGMODE_DATAMODEM)
|
|
{
|
|
// We won't hangup.
|
|
// We deliberately leave pTG->FComStatus.fOffHook to TRUE, because
|
|
// it is off hook.
|
|
goto done;
|
|
}
|
|
|
|
if(!iModemHangup(pTG))
|
|
{
|
|
// at this point in teh production version we need to
|
|
// call some OS reboot function!!
|
|
DebugPrintEx(DEBUG_ERR,"Can't Hangup after ANSWERFAIL");
|
|
uRet = CONNECT_ERROR;
|
|
}
|
|
// fall through
|
|
|
|
done:
|
|
return uRet;
|
|
|
|
}
|
|
|
|
|
|
int my_strcmp(LPSTR sz1, LPSTR sz2)
|
|
{
|
|
|
|
if ( (sz1 == NULL) || (sz2 == NULL) )
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
if ( strcmp(sz1, sz2) == 0 )
|
|
{
|
|
return TRUE;
|
|
}
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
|
|
BOOL fHasNumerals(PThrdGlbl pTG, LPSTR sz)
|
|
{
|
|
int i;
|
|
|
|
if (sz == NULL)
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
for(i=0; sz[i]; i++)
|
|
{
|
|
if(sz[i] >= '0' && sz[i] <= '9')
|
|
return TRUE;
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
#define DIALOGRETRYMIN 600
|
|
#define SECONDLINE_TIMEOUT 500
|
|
#define ABORT_TIMEOUT 250
|
|
#ifdef DEBUG
|
|
# define DEFMONVAL 1
|
|
#else //!DEBUG
|
|
# define DEFMONVAL 0
|
|
#endif //!DEBUG
|
|
#define szMONITOREXISTINGFILESIZE "MonitorMaxOldSizeKB"
|
|
#define szMONITORDIR "MonitorDir"
|
|
|
|
|
|
UWORD far iiModemDialog
|
|
( PThrdGlbl pTG,
|
|
LPSTR szSend,
|
|
UWORD uwLen,
|
|
ULONG ulTimeout,
|
|
BOOL fMultiLine,
|
|
UWORD uwRepeatCount,
|
|
BOOL fPause,
|
|
CBPSTR cbpstrWant1,
|
|
CBPSTR cbpstrWant2,
|
|
...
|
|
)
|
|
{
|
|
/** Takes a command string, and it's lengt writes it out to the modem
|
|
and tries to get one of the allowed responses. It writes the command
|
|
out, waits ulTimeOut millisecs for a response. If it gets one of the
|
|
expected responses it returns immediately.
|
|
|
|
If it gets an unexpected/illegal response it tries (without any
|
|
waiting) for subsequent lines to the same response. When all the
|
|
lines (if > 1) of the response lines are exhausted, if none is among the
|
|
expected responses, it writes the command again and tries again,
|
|
until ulTimeout has expired. Note that if no response is received,
|
|
the command will be written just once.
|
|
|
|
The whole above thing will be repeated upto uwRepeatCount times
|
|
if uwRepeatCount is non-zero
|
|
|
|
<<<<<NOTE:::uwRepeatCount != 0 should not be used except for local sync>>>>>
|
|
|
|
It returns when (a) one of the specified responses is received or
|
|
(b) uwRepeatCount tries have failed (each having returned an
|
|
illegal response or having returned no response in ulTimeout
|
|
millsecs) or (c) the command write failed, in which
|
|
case it returns immediately.
|
|
|
|
It flushes the modem inque before each Command Write.
|
|
|
|
Returns 0 on failure and the 1 based index of the successful
|
|
response on success.
|
|
|
|
This can be used in the following way:-
|
|
|
|
for Local Dialogs (AT, AT+FTH=? etc), set ulTimeout to a lowish
|
|
value, of the order of the transmission time of the longest
|
|
possible (erroneous or correct) line of response plus the size
|
|
of the command. eg. at 1200baud we have about 120cps = about
|
|
10ms/char. Therefore a timeout of about 500ms is more than
|
|
adequate, except for really long command lines.
|
|
|
|
for Local Sync dialogs, used to sync up with the modem which may
|
|
be in an unsure state, use the same timeout, but also a repeat
|
|
count of 2 or 3.
|
|
|
|
for remote-driven dialogs, eg. AT+FRH=xx which returns a CONNECT
|
|
after the flags have been received, and which may incur a delay
|
|
before a response (ATDT is teh same. CONNECT is issued after a
|
|
long delay & anything the DTE sends will abort the process).
|
|
For these cases the caller should supply a long timeout and
|
|
probably a repeatcount of 1, so that the
|
|
routine will timeout after one try but go on issuing teh command
|
|
as long as an error repsonse is received.
|
|
|
|
For +FRH etc, the long timeout should be T1 or T2 in teh case of
|
|
CommandRecv and ResponseRecv respectively.
|
|
|
|
**/
|
|
|
|
|
|
BYTE bReply[REPLYBUFSIZE];
|
|
UWORD i, j, uwRet, uwWantCount;
|
|
SWORD swNumRead;
|
|
CBPSTR rgcbszWant[10];
|
|
va_list ap;
|
|
LPTO lpto, lptoRead, lpto0;
|
|
BOOL fGotFirstLine, fFirstSend;
|
|
ULONG ulLeft;
|
|
UINT uPos=0;
|
|
|
|
DEBUG_FUNCTION_NAME(("iiModemDialog"));
|
|
pTG->FComModem.bEntireReply[0]=0;
|
|
|
|
// ensure that we'll abort in FComm only on fresh calls to NCUAbort
|
|
// protecting ourselves against this var being randomly left set.
|
|
// Note we check this variable _just_ before calling ModemDialog
|
|
// in NCUDial and NCUAnswer & assuming atomicity between then and here
|
|
// we'll never miss an abort in a Dial/Answer
|
|
|
|
// extract the (variable length) list of acceptable responses.
|
|
// each is a CBSZ, code based 2 byte ptr
|
|
|
|
// first response always present
|
|
rgcbszWant[1] = cbpstrWant1;
|
|
|
|
if((rgcbszWant[2] = cbpstrWant2) != NULL)
|
|
{
|
|
// if more than one response
|
|
va_start(ap, cbpstrWant2);
|
|
for(j=3; j<10; j++)
|
|
{
|
|
if((rgcbszWant[j] = va_arg(ap, CBPSTR)) == NULL)
|
|
break;
|
|
}
|
|
uwWantCount = j-1;
|
|
va_end(ap);
|
|
}
|
|
else
|
|
{
|
|
uwWantCount = 1;
|
|
}
|
|
|
|
if(szSend)
|
|
{
|
|
DebugPrintEx( DEBUG_MSG,
|
|
"Dialog: Send (%s) len=%d WantCount=%d time=%ld rep=%d",
|
|
(LPSTR)szSend,
|
|
uwLen,
|
|
uwWantCount,
|
|
ulTimeout,
|
|
uwRepeatCount);
|
|
}
|
|
else
|
|
{
|
|
DebugPrintEx( DEBUG_MSG,
|
|
"Response: WantCount=%d time=%ld rep=%d",
|
|
uwWantCount,
|
|
ulTimeout,
|
|
uwRepeatCount);
|
|
}
|
|
for(j=1; j<=uwWantCount; j++)
|
|
{
|
|
DebugPrintEx(DEBUG_MSG,"Want %s",(LPSTR)(rgcbszWant[j]));
|
|
}
|
|
|
|
lpto = &(pTG->FComModem.toDialog);
|
|
lpto0 = &(pTG->FComModem.toZero);
|
|
pTG->FComStatus.fInDialog = TRUE;
|
|
|
|
// Try the dialog upto uwRepeatCount times
|
|
for(uwRet=0, i=0; i<uwRepeatCount; i++)
|
|
{
|
|
startTimeOut(pTG, lpto, ulTimeout);
|
|
fFirstSend = TRUE;
|
|
do
|
|
{
|
|
if(szSend)
|
|
{
|
|
if(!fFirstSend)
|
|
{
|
|
ulLeft = leftTimeOut(pTG, lpto);
|
|
if(ulLeft <= DIALOGRETRYMIN)
|
|
{
|
|
DebugPrintEx(DEBUG_MSG,"ulLeft=%ul too low",ulLeft);
|
|
break;
|
|
}
|
|
else
|
|
{
|
|
DebugPrintEx(DEBUG_MSG,"ulLeft=%ul OK",ulLeft);
|
|
}
|
|
}
|
|
fFirstSend = FALSE;
|
|
|
|
// If a command is supplied, write it out, flushing input
|
|
// first to get rid of spurious input.
|
|
|
|
/*** SyncWrite calls Drain here which we should not need **
|
|
*** as we are immediately waiting for a response *********
|
|
**********************************************************
|
|
if(!FComDirectSyncWrite(szSend, uwLen))
|
|
**********************************************************/
|
|
|
|
if(fPause)
|
|
Sleep(40); // 100 is too long
|
|
|
|
// FComFlushInput();
|
|
FComFlush(pTG); // Need to flush output too? Maybe...
|
|
// there's nowhere else to flush/loosen up teh output
|
|
|
|
// The flush has to be as late in the game as possible,
|
|
// because if teh previous command got confused & accepted
|
|
// a response to an earlier command or something, then
|
|
// it's response may still be in transit (this happened
|
|
// on Sharad's PP9600FXMT), so the later we do this the
|
|
// better. So we send the entire command w/o teh \r,
|
|
// wait for it to drain, then Flush again (input only
|
|
// this time) then send the CR
|
|
|
|
///////// Potential Major source of failures ////////
|
|
// DirectSyncWrite calls Drain which calls DllSleep if everything
|
|
// is not drained, so we could end up waiting for 1 time slice
|
|
// which is at least 50ms and looks like it can be much higher on
|
|
// some machines. This was screwing up our AT+FTM=96 is some cases
|
|
// FIX: Enter Crit section here exit after this is done
|
|
//////////////////////////////////////////////////////
|
|
|
|
PSSLogEntry(PSS_MSG, 2, "send: \"%s\"", szSend);
|
|
|
|
if(!FComDirectSyncWriteFast(pTG, szSend, (UWORD)(uwLen-1)))
|
|
{
|
|
// Need to check that we are sending only ASCII or pre-stuffed data here
|
|
DebugPrintEx(DEBUG_ERR,"Modem Dialog Sync Write timed Out");
|
|
uwRet = 0;
|
|
goto error;
|
|
// If Write fails, fail & return immediately.
|
|
// SetMyError() will already have been called.
|
|
}
|
|
// output has drained. Now flush input
|
|
FComFlushInput(pTG);
|
|
// and then send the CR
|
|
if(!FComDirectAsyncWrite(pTG, "\r", 1))
|
|
{
|
|
DebugPrintEx(DEBUG_ERR,"Modem Dialog Write timed Out on CR");
|
|
uwRet = 0;
|
|
goto error;
|
|
}
|
|
}
|
|
|
|
// Try to get a response until timeout or bad response
|
|
pTG->FComModem.bLastReply[0] = 0;
|
|
fGotFirstLine=FALSE;
|
|
|
|
for(lptoRead=lpto;;startTimeOut(pTG, lpto0, SECONDLINE_TIMEOUT), lptoRead=lpto0)
|
|
{
|
|
// get a CR-LF terminated line
|
|
// for the first line use macro timeout, for multi-line
|
|
// responses use 0 timeout.
|
|
retry:
|
|
swNumRead = FComFilterReadLine(pTG, bReply, REPLYBUFSIZE-1, lptoRead);
|
|
DebugPrintEx(DEBUG_MSG,"FComFilterReadLine returns %d",swNumRead);
|
|
if(swNumRead == 2 && bReply[0] == '\r' && bReply[1] == '\n')
|
|
goto retry; // blank line -- throw away & get another
|
|
|
|
// Fix Bug#1226. Elsa Microlink returns this garbage line in
|
|
// response to AT+FCLASS=?, followed by the real reply. Since
|
|
// we only look at the first line, we see only this garbage line
|
|
// and we never see the real reply (0, 1, 2, 2.0)
|
|
if(swNumRead==3 && bReply[0]==0x13 && bReply[1]=='\r' && bReply[2]=='\n')
|
|
goto retry;
|
|
|
|
// Fix Elliot bug#3619 -- German modem TE3801 sends us
|
|
// \r\r\nOK\r\n -- so we treat \r\r\n as blank line.
|
|
if(swNumRead==3 && bReply[0]=='\r' && bReply[1]=='\r' && bReply[2]=='\n')
|
|
goto retry;
|
|
|
|
if(swNumRead == 0) // timeout
|
|
{
|
|
if(fGotFirstLine)
|
|
{
|
|
// for MegaHertz, which returns no OK after
|
|
// capabilities queries
|
|
if(pTG->fMegaHertzHack)
|
|
{
|
|
if(fHasNumerals(pTG, pTG->FComModem.bLastReply))
|
|
{
|
|
uwRet = 1;
|
|
goto end;
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
else
|
|
{
|
|
goto timeout;
|
|
}
|
|
}
|
|
if(swNumRead < 0) // error-but lets see what we got anyway
|
|
swNumRead = (-swNumRead);
|
|
|
|
fGotFirstLine=TRUE;
|
|
|
|
|
|
//
|
|
// +++ HACK:
|
|
// We add everything upto the first NULL of each
|
|
// line of reply to bEntireReply, for the specific
|
|
// case of fMultiLine==uMULTILINE_SAVEENTIRE
|
|
// This is so we save things like:
|
|
// \r\nDATA\r\n\r\nCONNECT 12000\r\n
|
|
//
|
|
if(pTG->Comm.fEnableHandoff && fMultiLine==uMULTILINE_SAVEENTIRE
|
|
&& uPos<sizeof(pTG->FComModem.bEntireReply))
|
|
{
|
|
UINT cb;
|
|
bReply[REPLYBUFSIZE-1]=0;
|
|
cb = _fstrlen(bReply);
|
|
if ((cb+1)> (sizeof(pTG->FComModem.bEntireReply)-uPos))
|
|
{
|
|
DebugPrintEx(DEBUG_WRN, "bEntireReply: out of space");
|
|
cb=sizeof(pTG->FComModem.bEntireReply)-uPos;
|
|
if (cb) cb--;
|
|
}
|
|
_fmemcpy((LPB)pTG->FComModem.bEntireReply+uPos, (LPB)bReply, cb);
|
|
uPos+=cb;
|
|
pTG->FComModem.bEntireReply[uPos]=0;
|
|
}
|
|
|
|
PSSLogEntry(PSS_MSG, 2, "recv: \"%s\"", bReply);
|
|
|
|
for(bReply[REPLYBUFSIZE-1]=0, j=1; j<=uwWantCount; j++)
|
|
{
|
|
if(rgcbszWant[j] && (strstr(bReply, rgcbszWant[j]) != NULL))
|
|
{
|
|
uwRet = j;
|
|
goto end;
|
|
}
|
|
}
|
|
|
|
|
|
if(!fMultiLine)
|
|
break;
|
|
// Got something unknown
|
|
// Retry command and response until timeout
|
|
|
|
// We reach here it IFF we got a non blank reply, but it wasn't what
|
|
// we wanted. Squirrel teh first line away somewhere so that we can
|
|
// retrieve is later. We use this hack to get multi-line informational
|
|
// responses to things like +FTH=? Very important to ensure that
|
|
// blank-line replies don't get recorded here. (They may override
|
|
// the preceding line that we need!).
|
|
|
|
if( (pTG->FComModem.bLastReply[0] == 0) ||
|
|
( ! _fstrcmp(pTG->FComModem.bLastReply, cbszRING) ) )
|
|
{
|
|
// copy only if _first_ response line
|
|
_fmemcpy((LPB)pTG->FComModem.bLastReply, (LPB)bReply, REPLYBUFSIZE);
|
|
}
|
|
// copies whole of bReply which includes zero-termination put
|
|
// there by FComFilterReadLine
|
|
DebugPrintEx( DEBUG_MSG,
|
|
"Saved line (%s)",
|
|
(LPSTR)(&(pTG->FComModem.bLastReply)));
|
|
}
|
|
}
|
|
while(checkTimeOut(pTG, lpto));
|
|
|
|
if(fGotFirstLine)
|
|
continue;
|
|
|
|
DebugPrintEx(DEBUG_WRN,"Weird!! got timeout in iiModemDialog loop");
|
|
timeout:
|
|
PSSLogEntryStrings(PSS_WRN, 2, &rgcbszWant[1], uwWantCount,
|
|
"failed to receive expected response: ");
|
|
// Need to send anychar to abort the previous command.
|
|
// use random 120ms timeout -- too short. upped to 250
|
|
// send \rAT\r
|
|
// no need for pause--we just timed out!!
|
|
|
|
PSSLogEntry(PSS_MSG, 2, "send: \"AT\"");
|
|
FComFlush(pTG); // flush first--don't wnat some old garbage result
|
|
FComDirectSyncWriteFast(pTG, "\rAT", 3);
|
|
FComFlushInput(pTG); // flush input again
|
|
FComDirectAsyncWrite(pTG, "\r", 1);
|
|
startTimeOut(pTG, lpto0, ABORT_TIMEOUT);
|
|
do
|
|
{
|
|
swNumRead = FComFilterReadLine(pTG, bReply, REPLYBUFSIZE-1, lpto0);
|
|
}
|
|
while(swNumRead==2 && bReply[0]=='\r'&& bReply[1]=='\n');
|
|
// While we get a blank line. Get another.
|
|
bReply[REPLYBUFSIZE-1] = 0;
|
|
if (bReply[0])
|
|
{
|
|
PSSLogEntry(PSS_MSG, 2, "recv: \"%s\"", bReply);
|
|
}
|
|
if(bReply[0] && strstr(bReply, cbszOK)==NULL)
|
|
DebugPrintEx( DEBUG_ERR,
|
|
"Anykey abort reply not OK. Got <<%s>>",
|
|
(LPSTR)bReply);
|
|
|
|
// Need Flush here, because \rAT\r will often get us
|
|
// a cr-lf-OK-cr-lf-cr-lfOK-cr-lf response. If we send
|
|
// just a \r, sometimes we may get nothing
|
|
|
|
// FComFlushInput();
|
|
FComFlush(pTG);
|
|
}
|
|
|
|
error:
|
|
DebugPrintEx( DEBUG_WRN,
|
|
"(%s) --> (%d)(%s, etc) Failed",
|
|
(LPSTR)(szSend?szSend:"null"),
|
|
uwWantCount,
|
|
(LPSTR)rgcbszWant[1]);
|
|
|
|
pTG->FComStatus.fInDialog = 0;
|
|
return 0;
|
|
|
|
end:
|
|
|
|
DebugPrintEx(DEBUG_MSG,"GOT IT %d (%s)", uwRet, (LPSTR)(rgcbszWant[uwRet]));
|
|
pTG->FComStatus.fInDialog = 0;
|
|
return uwRet;
|
|
}
|
|
|
|
|
|
void InitMonitorLogging(PThrdGlbl pTG)
|
|
{
|
|
DEBUG_FUNCTION_NAME(("InitMonitorLogging"));
|
|
pTG->Comm.fEnableHandoff=1;
|
|
if (pTG->Comm.fEnableHandoff)
|
|
{
|
|
DebugPrintEx(DEBUG_WRN,"ADAPTIVE ANSWER ENABLED");
|
|
}
|
|
}
|
|
|
|
// RSL was 60 000
|
|
#define AA_ANSWER_TIMEOUT 40000
|
|
|
|
USHORT iModemGetAdaptiveResp(PThrdGlbl pTG)
|
|
{
|
|
USHORT uRet=CONNECT_ERROR;
|
|
BOOL fGotOK=FALSE;
|
|
BOOL fGotData=FALSE;
|
|
BOOL fGotFax=FALSE;
|
|
LONG lRet;
|
|
char Command[400];
|
|
|
|
|
|
DEBUG_FUNCTION_NAME(("iModemGetAdaptiveResp"));
|
|
|
|
pTG->Comm.fDataCall = FALSE;
|
|
//
|
|
// handle Adaptive Answer
|
|
// should get FAX/DATA response
|
|
//
|
|
switch( iiModemDialog( pTG,
|
|
0,
|
|
0,
|
|
AA_ANSWER_TIMEOUT,
|
|
uMULTILINE_SAVEENTIRE,
|
|
1,
|
|
TRUE,
|
|
pTG->ModemResponseFaxDetect,
|
|
pTG->ModemResponseDataDetect,
|
|
cbszCONNECT,
|
|
cbszOK,
|
|
(CBPSTR)NULL))
|
|
{
|
|
|
|
case 1:
|
|
fGotFax = 1;
|
|
DebugPrintEx(DEBUG_MSG,"AdaptiveAnswer: got FAX response");
|
|
break;
|
|
|
|
case 2:
|
|
fGotData = 1;
|
|
DebugPrintEx(DEBUG_MSG,"AdaptiveAnswer: got DATA response");
|
|
break;
|
|
|
|
case 3:
|
|
DebugPrintEx(DEBUG_ERR,"AnswerPhone: Can't get CONNECT before FAX/DATA");
|
|
pTG->Comm.fDataCall = FALSE;
|
|
uRet = CONNECT_ERROR;
|
|
goto end;
|
|
|
|
case 4:
|
|
DebugPrintEx(DEBUG_ERR,"AnswerPhone: Can't get OK before FAX/DATA");
|
|
pTG->Comm.fDataCall = FALSE;
|
|
uRet = CONNECT_ERROR;
|
|
goto end;
|
|
|
|
default:
|
|
case 0:
|
|
DebugPrintEx(DEBUG_ERR,"AnswerPhone: Can't get default before FAX/DATA");
|
|
pTG->Comm.fDataCall = FALSE;
|
|
uRet = CONNECT_ERROR;
|
|
goto end;
|
|
}
|
|
|
|
// here we may have to change the serial speed and send some cmds (such as ATO-go online)
|
|
|
|
if (fGotFax)
|
|
{
|
|
if (pTG->SerialSpeedFaxDetect)
|
|
{
|
|
FComSetBaudRate(pTG, pTG->SerialSpeedFaxDetect);
|
|
}
|
|
|
|
if (pTG->HostCommandFaxDetect)
|
|
{
|
|
strcpy (Command, pTG->HostCommandFaxDetect );
|
|
|
|
FComFlushOutput(pTG);
|
|
FComDirectAsyncWrite(pTG, (LPSTR) Command, (USHORT) strlen(Command) );
|
|
}
|
|
|
|
}
|
|
else if (fGotData)
|
|
{
|
|
if (pTG->SerialSpeedDataDetect)
|
|
{
|
|
FComSetBaudRate(pTG, pTG->SerialSpeedDataDetect);
|
|
}
|
|
|
|
if (pTG->HostCommandDataDetect)
|
|
{
|
|
strcpy (Command, pTG->HostCommandDataDetect );
|
|
|
|
FComFlushOutput(pTG);
|
|
FComDirectAsyncWrite(pTG, (LPSTR) Command, (USHORT) strlen(Command) );
|
|
}
|
|
}
|
|
else
|
|
{
|
|
DebugPrintEx(DEBUG_ERR,"AnswerPhone: LOGICAL PGM ERROR");
|
|
pTG->Comm.fDataCall = FALSE;
|
|
uRet = CONNECT_ERROR;
|
|
goto end;
|
|
}
|
|
|
|
|
|
// wait for connect now.
|
|
|
|
switch( iiModemDialog( pTG,
|
|
0,
|
|
0,
|
|
AA_ANSWER_TIMEOUT,
|
|
uMULTILINE_SAVEENTIRE,
|
|
1,
|
|
TRUE,
|
|
(fGotFax) ? pTG->ModemResponseFaxConnect : pTG->ModemResponseDataConnect,
|
|
cbszCONNECT,
|
|
cbszOK,
|
|
(CBPSTR)NULL))
|
|
{
|
|
|
|
case 1:
|
|
if (fGotFax)
|
|
{
|
|
uRet=CONNECT_OK;
|
|
goto end;
|
|
}
|
|
else
|
|
{
|
|
goto lDetectDataCall;
|
|
}
|
|
|
|
case 2:
|
|
if (fGotFax)
|
|
{
|
|
uRet=CONNECT_OK;
|
|
goto end;
|
|
}
|
|
else
|
|
{
|
|
goto lDetectDataCall;
|
|
}
|
|
|
|
case 3:
|
|
DebugPrintEx(DEBUG_ERR,"AnswerPhone: Can't get OK after FAX/DATA");
|
|
pTG->Comm.fDataCall = FALSE;
|
|
uRet = CONNECT_ERROR;
|
|
goto end;
|
|
|
|
default:
|
|
case 0:
|
|
DebugPrintEx(DEBUG_ERR,"AnswerPhone: Can't get default after FAX/DATA");
|
|
pTG->Comm.fDataCall = FALSE;
|
|
uRet = CONNECT_ERROR;
|
|
goto end;
|
|
}
|
|
|
|
|
|
|
|
lDetectDataCall:
|
|
// Now we've got to fake out modem and fcom into thinking that
|
|
// the phone is off hook when in fact it isn't.
|
|
pTG->Comm.fDataCall = TRUE;
|
|
uRet = CONNECT_WRONGMODE_DATAMODEM;
|
|
//
|
|
// New TAPI: Have to switch out of passtrough before handing off the call
|
|
//
|
|
|
|
DebugPrintEx(DEBUG_MSG,"AdaptiveAnswer: lineSetCallParams called");
|
|
|
|
if (!itapi_async_setup(pTG))
|
|
{
|
|
DebugPrintEx(DEBUG_ERR,"AdaptiveAnswer: itapi_async_setup failed");
|
|
|
|
pTG->Comm.fDataCall = FALSE;
|
|
uRet = CONNECT_ERROR;
|
|
goto end;
|
|
}
|
|
|
|
lRet = lineSetCallParams(pTG->CallHandle,
|
|
LINEBEARERMODE_VOICE,
|
|
0,
|
|
0xffffffff,
|
|
NULL);
|
|
|
|
if (lRet < 0)
|
|
{
|
|
DebugPrintEx(DEBUG_ERR, "AdaptiveAnswer: lineSetCallParams failed");
|
|
|
|
pTG->fFatalErrorWasSignaled = 1;
|
|
SignalStatusChange(pTG, FS_FATAL_ERROR);
|
|
|
|
pTG->Comm.fDataCall = FALSE;
|
|
uRet = CONNECT_ERROR;
|
|
goto end;
|
|
}
|
|
else
|
|
{
|
|
DebugPrintEx( DEBUG_MSG,
|
|
"AdaptiveAnswer: lineSetCallParams returns ID %ld",
|
|
(long) lRet);
|
|
}
|
|
|
|
if(!itapi_async_wait(pTG, (DWORD)lRet, (LPDWORD)&lRet, NULL, ASYNC_TIMEOUT))
|
|
{
|
|
DebugPrintEx(DEBUG_ERR, "AdaptiveAnswer: itapi_async_wait failed");
|
|
pTG->fFatalErrorWasSignaled = 1;
|
|
SignalStatusChange(pTG, FS_FATAL_ERROR);
|
|
|
|
pTG->Comm.fDataCall = FALSE;
|
|
uRet = CONNECT_ERROR;
|
|
goto end;
|
|
}
|
|
|
|
pTG->fFatalErrorWasSignaled = 1;
|
|
SignalStatusChange(pTG, FS_NOT_FAX_CALL);
|
|
|
|
end:
|
|
return uRet;
|
|
|
|
}
|