Source code of Windows XP (NT5)
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.
 
 
 
 
 
 

2509 lines
83 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.
***************************************************************************/
#include "prep.h"
#include "mmsystem.h"
#include "modemint.h"
#include "fcomint.h"
#include "fdebug.h"
#ifndef MDDI
# include "efaxcb.h"
#endif //MDDI
#define DEFINE_MDMCMDS
#include "mdmcmds.h"
///RSL
#include "glbproto.h"
// 12/18/94 JosephJ: CHECKGOCLASS code issues a +FCLASS? after
// each +FCLASS=x to verify that the modem has gone to CLASS x.
// We have disabled it because we've not been able to repro
// a potential bug of the modem not going to CLASS1 when we expect it -
// we currently issue AT+FCLASS=x, wait 500ms, and then issue
// an AT to resync, but don't check that the class is currect.
// The CHECGOCLASS code has been TESTED on a couple of modems, but
// I haven't been able to actually kick in -- i.e., catch the modem
// in the wrong mode and retry the +FCLASS=x command, so we've
// commented out all the code until we can repro the problem.
// #define CHECKGOCLASS
// no need for this--LPZ is now compulsory, PCMODEMS is optional in this
// module, and we will use inifiles here whether INIFILE is defd or not
// (INIFILE is not defined for IFAX so that the other (non-test) modules
// don't use INI files). In IFAX this module is just for test-purposes.
// #if !defined(INIFILE) || !defined(PCMODEMS) || !defined(LPZ)
// #error "INIFILE and LPZ and PCMODEMS _must_ be defined"
// #endif
#define faxTlog(m) DEBUGMSG(ZONE_MD, m)
#define faxT2log(m) DEBUGMSG(ZONE_DIA, m)
#define FILEID FILEID_MODEM
#ifdef DEBUG
# define ST_DIA(x) if(ZONE_DIA) { x; }
# define ST_MOD(x) if(ZONE_MD) { x; }
#else
# define ST_DIA(x) { }
# define ST_MOD(x) { }
#endif
#ifdef MON3
// Enhanced comm monitor logging...
#define wEVFLAGS_DIAL fEVENT_TRACELEVEL_1
#define wEVFLAGS_ANSWER fEVENT_TRACELEVEL_1
#define wEVFLAGS_INIT fEVENT_TRACELEVEL_1
#define wEVFLAGS_DEINIT fEVENT_TRACELEVEL_1
#define wEVFLAGS_HANGUP fEVENT_TRACELEVEL_1
void InitMonitorLogging(PThrdGlbl pTG);
#else // !MON3
#define InitMonitorLogging(PThrdGlbl pTG) 0
#endif // !MON3
# 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[] = {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, BOOL fTryCurrent, 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;
rguwSpeeds[0] = pTG->CurrentSerialSpeed;
if ( rguwSpeeds[0] == rguwSpeeds[1]) {
ilWhich++;
}
BG_CHK(cbszCommand && uwLen);
BG_CHK(fTryCurrent);
// has to be TRUE, or we won't work with autobauding modems
if (!fTryCurrent)
if(!FComSetBaudRate(pTG, rguwSpeeds[++ilWhich]))
return -1;
for(;;)
{
(MyDebugPrint(pTG, LOG_ALL, "Sync Trying: ilWhich=%d speed=%d\r\n", ilWhich,
rguwSpeeds[ilWhich]));
if(iSyncModemDialog(pTG, (LPSTR)cbszCommand, uwLen, cbszOK))
{
(MyDebugPrint(pTG, LOG_ALL, "Succeeded in Syncing at Speed = %d (il=%d)\r\n",
rguwSpeeds[ilWhich], ilWhich));
return (ilWhich>=0 ? rguwSpeeds[ilWhich] : 0);
}
/* failed. try next speed. */
if (rguwSpeeds[++ilWhich]==0)
{
// Tried all speeds. No response
(MyDebugPrint(pTG, LOG_ERR,"<<ERROR>> Cannot Sync with Modem on Command %s\r\n", (LPSTR)cbszCommand));
iModemSetError(pTG, MODEMERR_HARDWARE, ERR_MODEM_NORESPONSE, MODEMERRFLAGS_FATAL);
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, TRUE, cbszAT, sizeof(cbszAT)-1);
}
SWORD iModemReset(PThrdGlbl pTG, CBPSTR szCmd)
{
SWORD swRet;
if (szCmd == NULL) {
return -1;
}
if((swRet = HayesSyncSpeed(pTG, TRUE, 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 "brilliant" idea messed up 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 HayesSyncSpeed(TRUE, cbszATE0, sizeof(cbszATE0)-1);
//
return 0;
}
}
//////////////////////////////////////////////////////////////////////////////////////////////////////////////
USHORT
T30ModemInit(
PThrdGlbl pTG,
HANDLE hComm,
DWORD dwLineID,
DWORD dwLineIDType,
DWORD dwProfileID,
LPSTR lpszKey,
BOOL fInstall
)
{
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
***/
// Save the profile ID and key string.
pTG->FComModem.dwProfileID = dwProfileID;
uLen = min(_fstrlen(lpszKey), sizeof(pTG->FComModem.rgchKey)-1);
_fmemcpy(pTG->FComModem.rgchKey, lpszKey, uLen);
pTG->FComModem.rgchKey[uLen] = 0;
if (!uLen)
{
(MyDebugPrint(pTG, LOG_ERR,"<<ERROR>> Bad param: ProfileID=0x%lx; Key=%s\r\n",
(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, dwLineID, dwLineIDType,
&pTG->FComModem.CurrCmdTab, &pTG->FComModem.CurrMdmCaps,
&pTG->FComModem.CurrMdmExtCaps, fInstall))
{
goto error;
}
if (dwLineIDType==LINEID_COMM_HANDLE)
{
// Let's try to sync up with a simple AT command here...
// Sometimes the modem could be busy sending RINGS at the wrong
// speed and it seems to return OK to the reset string but
// not interpret it?????
// ALSO: (this is a general problem, in faxt) the modem could be
// in NUMERIC mode, in which case it will return "0", not "OK"!
// So we check for 0 as well...
#define ATV1 "ATV1"
#define AT "AT"
#define cr "\r"
#define cbszZero "0"
if (!iSyncModemDialog2(pTG, AT cr,sizeof(AT cr)-1,cbszOK,cbszZero))
{
(MyDebugPrint(pTG, LOG_ERR,"<<ERROR>> couldn't sync up to modem on takeover"));
}
}
// 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)
{
(MyDebugPrint(pTG, LOG_ERR,"<<ERROR>> Error in SETUP string: %s\r\n", (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
iModemSetNCUParams(pTG, (fInstall==fMDMINIT_ANSWER)?(-1): pTG->NCUParams.DialPauseTime,
pTG->NCUParams.SpeakerControl,
pTG->NCUParams.SpeakerVolume, pTG->NCUParams.DialBlind,
pTG->NCUParams.SpeakerRing);
pTG->fNCUParamsChanged=FALSE;
InitCommErrCount(pTG);
// Why is this here??
FComFlush(pTG);
pTG->FComStatus.fModemInit = TRUE;
// pTG->fNCUAbort = 0;
uRet = INIT_OK;
goto end;
error:
FComClose(pTG);
pTG->FComStatus.fModemInit = FALSE;
// fall through...
end:
PUTEVENT(pTG, wEVFLAGS_INIT, EVENT_ID_MODEM_STATE, EVENT_SubID_MODEM_INIT_END,
uRet, 0, NULL);
return uRet;
}
LPCMDTAB iModemGetCmdTabPtr(PThrdGlbl pTG)
{
BG_CHK(pTG->FComStatus.fModemInit);
BG_CHK(pTG->FComModem.CurrCmdTab.szReset);
return (pTG->FComStatus.fModemInit) ? &pTG->FComModem.CurrCmdTab: NULL;
}
BOOL iModemSetNCUParams(PThrdGlbl pTG, int comma, int speaker,
int volume, int fBlind, int fRingAloud)
{
#define PARAMSBUFSIZE 60
char bBuf[PARAMSBUFSIZE];
USHORT uLen;
_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) )
{
#define SPLIT_BLIND
#ifdef SPLIT_BLIND
# define fDETECT_DIALTONE 1
# define fDETECT_BUSYTONE 2
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);
#else // !SPLIT_BLIND
uLen += (USHORT)wsprintf(bBuf+uLen, cbszXn, (fBlind ? 0 : 4));
#endif
}
if(comma >= 0)
{
if(comma > 255)
{
BG_CHK(FALSE);
comma = 255;
}
uLen += (USHORT)wsprintf(bBuf+uLen, cbszS8, comma);
}
if(speaker >= 0)
{
if(speaker > 2)
{
BG_CHK(FALSE);
speaker = 2;
}
uLen += (USHORT)wsprintf(bBuf+uLen, cbszMn, speaker);
}
if(volume >= 0)
{
if(volume > 3)
{
BG_CHK(FALSE);
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)
{
BG_CHK(FALSE);
(MyDebugPrint(pTG, LOG_ERR,"<<ERROR>> Can't Set NCU params\r\n"));
return FALSE;
}
return TRUE;
}
UWORD GetCap(PThrdGlbl pTG, CBPSTR cbpstrSend, UWORD uwLen)
{
UWORD uRet1=0, uRet2=0, uRet3=0;
// 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)
{
(MyDebugPrint(pTG, LOG_ERR,"<<WARNING>> 2nd getcaps return differs 1=%u,2=%u\r\n",
(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
{
(MyDebugPrint(pTG, LOG_ERR,"<<ERROR>> all 2 getcaps differ! 1=%u,2=%u, 3=%u\r\n",
(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;
retry = 0;
restart:
retry++;
if(retry > 2)
return 0;
(MyDebugPrint(pTG, LOG_ALL, "Want Caps for (%s)\r\n", (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
(MyDebugPrint(pTG, LOG_ALL, "LastLine = (%s)\r\n ", (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:
MyDebugPrint(pTG, LOG_ALL, "WARNING: Ignoring unknown Modulation code = %d\r\n", code);
// +++ goto restart;
code=0;
break;
// BG_CHK(FALSE);
// return 0;
}
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
(MyDebugPrint(pTG, LOG_ALL, "Can't get Caps for (%s\r\n) = 0x%04x Highest=%d\r\n", (LPSTR)cbpstrSend, speed, high));
return 0;
}
if(speed == 0x0F) speed = V27_V29_V33_V17;
/// RICOH's broken 1st prototype ////////////
/// speed = V27_SLOW;
/// high = 24;
/// RICOH's broken 1st prototype ////////////
(MyDebugPrint(pTG, LOG_ALL, "Got Caps for (%s\r\n) = 0x%04x Highest=%d\r\n", (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;
if (!*lpdwGot) {_fmemset(lpMdmCaps, 0, sizeof(MODEMCAPS));}
(MyDebugPrint(pTG, LOG_ALL, "Entering ModemGetCaps\r\n"));
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
(MyDebugPrint(pTG, LOG_ALL, "LastLine = (%s)\r\n", (LPSTR)(&(pTG->FComModem.bLastReply))));
lpMdmCaps->uClasses = 0;
#ifdef CL0
lpMdmCaps->uClasses |= FAXCLASS0; // Class0 always available
#endif //CL0
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:
BG_CHK(*lpdwGot & fGOTCAP_CLASSES);
BG_CHK(lpMdmCaps->uClasses & (FAXCLASS1|FAXCLASS2|FAXCLASS2_0));
if(lpMdmCaps->uClasses & FAXCLASS2_0)
{
// Test Class2.0 ability
#ifndef CL2_0
(MyDebugPrint(pTG, LOG_ERR,"Class2.0 modem -- IS NOT supported\r\n"));
lpMdmCaps->uClasses &= (~FAXCLASS2_0);
#else //CL2_0
/****************************************************
(MyDebugPrint(pTG, LOG_ERR,"Class2.0 modem -- IS NOW supported\r\n"));
if(!iiModemGoClass(GOCLASS2_0) || !iiModemGoClass(0))
{
(MyDebugPrint(pTG, LOG_ERR,"Failed to change Class\r\n"));
lpMdmCaps->uClasses &= (~FAXCLASS2_0);
}
****************************************************/
#endif //CL2_0
}
if(lpMdmCaps->uClasses & FAXCLASS2)
{
// Test Class2 ability
#ifndef CL2
(MyDebugPrint(pTG, LOG_ERR,"Class2 modem -- IS NOT supported\r\n"));
lpMdmCaps->uClasses &= (~FAXCLASS2);
#else //CL2
// +++ why is this and class2.0 below commented out?
/****************************************************
(MyDebugPrint(pTG, LOG_ERR,"Class2 modem -- IS NOW supported\r\n"));
if(!iiModemGoClass(2) || !iiModemGoClass(0))
{
(MyDebugPrint(pTG, LOG_ERR,"Failed to change Class\r\n"));
lpMdmCaps->uClasses &= (~FAXCLASS2);
}
****************************************************/
#endif //CL2
}
if(!lpMdmCaps->uClasses)
{
(MyDebugPrint(pTG, LOG_ERR,"<<ERROR>> Not a fax modem or unsupported fax class\r\n"));
iModemSetError(pTG, MODEMERR_HARDWARE, ERR_NOTFAX, MODEMERRFLAGS_FATAL);
*lpdwGot &= ~(fGOTCAP_CLASSES|fGOTCAP_SENDSPEEDS|fGOTCAP_RECVSPEEDS);
return FALSE;
}
if(!(lpMdmCaps->uClasses & FAXCLASS1)) return TRUE;
///////////////// rest is for Class1 only //////////////////////////
//////////
// MERGED from SNOWBALL.RC, but can't really do this in new scheme!!
// The US Robotics Sportster Swedish resets itself after AT+FCLASS=?
// So we need to issue a ATE0 here before trying to get caps
// This is timing independent.
// if(OfflineDialog2((LPSTR)cbszATE0, sizeof(cbszATE0)-1, cbszOK, cbszERROR) != 1)
// {
// (MyDebugPrint(pTG, LOG_ERR,"<<WARNING>> Error on ATE0 in GetCaps %s\r\n", (LPSTR)cbszATE0));
// }
//////////
// +++ why is this done only for Class1?
// What we do instead is send the Reset command
if(lpszReset && *lpszReset && iModemReset(pTG, lpszReset) < 0) return FALSE;
//////////
if(!iiModemGoClass(pTG, 1, dwSpeed)) goto NotClass1;
err = FALSE;
/**
uwRet = GetCap(cbszQUERY_FTH, sizeof(cbszQUERY_FTH)-1);
// err = (err || uwRet==0); // not an err for FTH
speed = LOBYTE(uwRet);
BG_CHK((speed & ~0x0F)==0);
lpMdmCaps->uHDLCSendSpeeds = speed;
**/
if (!(*lpdwGot & fGOTCAP_SENDSPEEDS))
{
uwRet = GetCap(pTG, cbszQUERY_FTM, sizeof(cbszQUERY_FTM)-1);
err = (err || uwRet==0);
speed = LOBYTE(uwRet);
BG_CHK((speed & ~0x0F)==0);
lpMdmCaps->uSendSpeeds = speed;
*lpdwGot |= fGOTCAP_SENDSPEEDS;
}
/**
uwRet = GetCap(cbszQUERY_FRH, sizeof(cbszQUERY_FRH)-1);
// err = (err || uwRet==0); // not an err for FTH
speed = LOBYTE(uwRet);
BG_CHK((speed & ~0x0F)==0);
lpMdmCaps->uHDLCRecvSpeeds = speed;
**/
if (!(*lpdwGot & fGOTCAP_RECVSPEEDS))
{
uwRet = GetCap(pTG, cbszQUERY_FRM, sizeof(cbszQUERY_FRM)-1);
err = (err || uwRet==0);
speed = LOBYTE(uwRet);
BG_CHK((speed & ~0x0F)==0);
lpMdmCaps->uRecvSpeeds = speed;
*lpdwGot |= fGOTCAP_RECVSPEEDS;
}
if(!iiModemGoClass(pTG, 0, dwSpeed))
err = TRUE;
if(err)
{
(MyDebugPrint(pTG, LOG_ERR,"<<ERROR>> Cannot get capabilities\r\n"));
iModemSetError(pTG, MODEMERR_HARDWARE, ERR_NOCAPS, MODEMERRFLAGS_FATAL);
goto NotClass1;
}
(MyDebugPrint(pTG, LOG_ALL, "Got Caps\r\n"));
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;
}
}
void TwiddleThumbs1( ULONG ulTime)
{
BEFORESLEEP;
// doesnt make sense to sleep for less
// when talking to bad modems
// BG_CHK(ulTime >= 40);
MY_TWIDDLETHUMBS(ulTime);
AFTERSLEEP(ulTime);
}
BOOL iModemGoClass(PThrdGlbl pTG, USHORT uClass)
{
return iiModemGoClass(pTG, uClass, pTG->FComModem.CurrCmdTab.dwSerialSpeed);
}
// #ifdef CHECKGOCLASS
// #define GOCLASS_PAUSE 500
// #endif // CHECKGOCLASS
BOOL iiModemGoClass(PThrdGlbl pTG, USHORT uClass, DWORD dwSpeed)
{
int i;
USHORT uBaud;
// #ifdef CHECKGOCLASS
// USHORT uPause=GOCLASS_PAUSE;
// #endif // CHECKGOCLASS
BG_CHK(!(dwSpeed & 0xFFFF0000L));
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
TwiddleThumbs1(100);
FComFlush(pTG);
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
// #ifdef CHECKGOCLASS
// TwiddleThumbs1(uPause);
//#else // !CHECKGOCLASS
TwiddleThumbs1(500);
//#endif // !CHECKGOCLASS
if(dwSpeed)
{
USHORT usSpeed = (USHORT) dwSpeed;
BG_CHK((usSpeed >= 4800) &&
((usSpeed % 2400) == 0) &&
((1152 % (usSpeed/100)) == 0));
uBaud = usSpeed;
}
else if (pTG->SerialSpeedInit) {
uBaud = pTG->SerialSpeedInit;
}
else {
uBaud = 19200;
}
// RSL don't do hard-coded 2400 for class0.
FComSetBaudRate(pTG, uBaud);
FComFlush(pTG);
if(iModemSync(pTG) >= 0)
{
//#ifdef CHECKGOCLASS
// // Let's verify the class..
// if (!iModemVerifyClass(uClass)) {uPause*=2; continue;}
//#endif CHECKGOCLASS
InitCommErrCount(pTG);
return TRUE;
}
}
error:
// no point -- and we'll smash our settings
// iModemReset();
// error is already set to ERR_NO_RESPONSE inside HayesSync()
(MyDebugPrint(pTG, LOG_ERR,"<<ERROR>> Cant go to Class %d\r\n", uClass));
return FALSE;
}
BOOL iModemClose(PThrdGlbl pTG)
{
USHORT uLen;
BOOL fRet=FALSE;
if(!pTG->FComStatus.fModemInit)
return TRUE;
PUTEVENT(pTG, wEVFLAGS_DEINIT, EVENT_ID_MODEM_STATE,
EVENT_SubID_MODEM_DEINIT_START, 0, 0, NULL);
/** Hangs up the phone if it is off hook, closes the COM port
and returns. If hangup fails then port is also left open. **/
////////////////////////////////////////////////////////////
/// we should close the port anyway ////////////////////////
/// Or put up a Dialog box or something......///////////////
////////////////////////////////////////////////////////////
if(!iModemHangup(pTG))
goto end;
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)
{
(MyDebugPrint(pTG, LOG_ERR,"<<ERROR>> Error in EXIT string: %s\r\n", (LPSTR)pTG->FComModem.CurrCmdTab.szExit));
}
}
lNext:
if(FComClose(pTG))
{
pTG->FComStatus.fModemInit = FALSE;
fRet=TRUE;
}
end:
PUTEVENT(pTG, wEVFLAGS_DEINIT, EVENT_ID_MODEM_STATE,
EVENT_SubID_MODEM_DEINIT_END, fRet, 0, NULL);
#ifdef MON3
if (pTG->gMonInfo.fInited)
{
MonDump(pTG);
MonDeInit(pTG);
}
#endif // MON3
return fRet;
}
BOOL iModemHangup(PThrdGlbl pTG)
{
BOOL fRet=FALSE;
if(!pTG->FComStatus.fOffHook) 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...
// RSL if (pTG->Comm.fEnableHandoff && pTG->Comm.fExternalHandle && pTG->Comm.fDataCall)
if (pTG->Comm.fEnableHandoff && pTG->Comm.fDataCall)
{
(MyDebugPrint(pTG, LOG_ALL, "<<WARNING>> iModemHangup: IGNORING Hangup of datamodem call\r\n"));
return TRUE;
}
PUTEVENT(pTG, wEVFLAGS_HANGUP, EVENT_ID_MODEM_STATE,
EVENT_SubID_MODEM_HANGUP_START, 0, 0, NULL);
// ST_MOD(D_FComPrint(pTG->FComStatus.uComPort-1));
// 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, TRUE, cbszHANGUP, sizeof(cbszHANGUP)-1);
if(HayesSyncSpeed(pTG, TRUE, cbszHANGUP, sizeof(cbszHANGUP)-1) < 0)
{
FComDTR(pTG, FALSE); // Lower DTR on stubborn hangups in ModemHangup
TwiddleThumbs1(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, TRUE, 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
BG_CHK(!fRet);
// fall through...
end:
PUTEVENT(pTG, wEVFLAGS_HANGUP, EVENT_ID_MODEM_STATE,
EVENT_SubID_MODEM_HANGUP_END, fRet, 0, NULL);
return fRet;
}
USHORT iModemDial(PThrdGlbl pTG, LPSTR lpszDial, USHORT uClass)
{
ULONG ulTimeout;
USHORT uRet, uLen, uDialStringLen;
BYTE bBuf[DIALBUFSIZE];
CBPSTR cbpstr;
char chMod = pTG->NCUParams.chDialModifier;
DWORD dw=0;
char KeyName[200];
HKEY hKey;
char BlindDialString[200];
char RegBlindDialString[200];
long lRet;
DWORD dwSize;
DWORD dwType;
BG_CHK(lpszDial);
(MyDebugPrint(pTG, LOG_ALL, "Entering ModemDial\r\n"));
pTG->FComStatus.fOffHook = TRUE; // Has to be here. Can get an error return
// below even after connecting
// and we want to hangup after that!!
BG_CHK(pTG->Comm.fDataCall==FALSE);
pTG->Comm.fDataCall=FALSE;
BG_CHK(uClass==FAXCLASS0 || uClass==FAXCLASS1);
//
// 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) {
MyDebugPrint(pTG, LOG_ERR, "Can't read Unimodem Settings key %s\n", KeyName);
}
else {
dwSize = sizeof(RegBlindDialString);
lRet = RegQueryValueEx(
hKey,
"Blind_On",
0,
&dwType,
RegBlindDialString,
&dwSize);
RegCloseKey(hKey);
if (lRet != ERROR_SUCCESS) {
MyDebugPrint(pTG, LOG_ERR, "Can't read Unimodem key\\Settings\\Blind_On value \n");
}
else if (RegBlindDialString) {
sprintf(BlindDialString, "AT%s\r", RegBlindDialString);
}
}
}
// Let's update the modem settings here, if required.
if (pTG->fNCUParamsChanged)
{
if (!iModemSetNCUParams(pTG, pTG->NCUParams.DialPauseTime,
pTG->NCUParams.SpeakerControl,
pTG->NCUParams.SpeakerVolume, pTG->NCUParams.DialBlind,
pTG->NCUParams.SpeakerRing))
{
(MyDebugPrint(pTG, LOG_ERR,"<<WARNING>> iModemSetNCUParams FAILED\r\n"));
}
pTG->fNCUParamsChanged=FALSE;
}
if(uClass==FAXCLASS1)
{
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)
{
(MyDebugPrint(pTG, LOG_ERR,"<<ERROR>> Error in BLIND DIAL string: %s\r\n", 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)
{
(MyDebugPrint(pTG, LOG_ERR,"<<ERROR>> Error in PREDIAL string: %s\r\n", (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++;
}
}
BG_CHK(chMod=='P' || chMod=='T' || chMod=='p' || chMod=='t');
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);
#define DIAL_TIMEOUT 60000L
ulTimeout=0;
if(uDialStringLen > 7)
ulTimeout += ((chMod=='p' || chMod=='P')?8000:3000)
* (uDialStringLen - 7);
if(pTG->NCUParams.AnswerTimeout != -1 &&
(((ULONG)pTG->NCUParams.AnswerTimeout * 1000L) > ulTimeout))
{
ulTimeout = 1000L * (ULONG)pTG->NCUParams.AnswerTimeout;
if (ulTimeout<20000L) ulTimeout=20000L;
} else
ulTimeout += DIAL_TIMEOUT;
if(pTG->fNCUAbort)
{
(MyDebugPrint(pTG, LOG_ERR,"NCUDial--aborting\r\n"));
pTG->fNCUAbort = 0;
uRet = CONNECT_ERROR;
goto error;
}
#ifndef MDDI
ICommStatus(pTG, T30STATS_DIALING, 0, 0, 0);
#endif //MDDI
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);
TwiddleThumbs1(200); // 100 is not too long for this IMPORTANT one!
FComFlushInput(pTG);
FComDirectAsyncWrite(pTG, bBuf, uLen);
// now try to get a response
dw = GetTickCount();
uRet = iiModemDialog(pTG, 0, 0, ulTimeout, TRUE, 1, TRUE,
cbszCONNECT, cbszBUSY, cbszNOANSWER,
cbszNODIALTONE, cbszERROR, cbszNOCARRIER, (CBPSTR)NULL);
pTG->FComStatus.fInDial = FALSE;
(MyDebugPrint(pTG, LOG_ALL, "ModemDial -- got %d response from Dialog\r\n", uRet));
#if !((CONNECT_TIMEOUT==0) && (CONNECT_OK==1) && (CONNECT_BUSY==2) && (CONNECT_NOANSWER == 3) && (CONNECT_NODIALTONE==4) && (CONNECT_ERROR==5))
#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);
(MyDebugPrint(pTG, LOG_ERR,"<<ERROR>> Dial: Got timeout\r\n"));
break;
case CONNECT_OK:
MyDebugPrint(pTG, LOG_ALL, "Dial: Got CONNECT\r\n");
break;
case CONNECT_BUSY:
pTG->fFatalErrorWasSignaled = 1;
SignalStatusChange(pTG, FS_BUSY);
(MyDebugPrint(pTG, LOG_ERR,"<<WARNING>> Dial: Got BUSY\r\n"));
break;
case CONNECT_NOANSWER:
pTG->fFatalErrorWasSignaled = 1;
SignalStatusChange(pTG, FS_NO_ANSWER);
(MyDebugPrint(pTG, LOG_ERR,"<<WARNING>> Dial: Got NOANSWER\r\n"));
break;
case CONNECT_NODIALTONE:
pTG->fFatalErrorWasSignaled = 1;
SignalStatusChange(pTG, FS_NO_DIAL_TONE);
(MyDebugPrint(pTG, LOG_ERR,"<<WARNING>> Dial: Got NODIALTONE\r\n"));
break;
case CONNECT_ERROR:
pTG->fFatalErrorWasSignaled = 1;
SignalStatusChange(pTG, FS_NO_ANSWER);
(MyDebugPrint(pTG, LOG_ERR,"<<ERROR>> Dial: Got ERROR\r\n"));
break;
case 6: {
#define TIME_DELTA(prev, now)\
(((prev)<=(now)) ?((now)-(prev)) : (now) + (0xffffffffL-(prev)))
DWORD dwNow=GetTickCount();
DWORD dwDelta = TIME_DELTA(dw, dwNow);
(MyDebugPrint(pTG, LOG_ERR,"<<ERROR>> Dial: Got NO CARRIER after %lums\r\n",
(unsigned long) dwDelta));
if (dwDelta < 5000L)
{
(MyDebugPrint(pTG, LOG_ERR,"<<WARNING>> Dial: Pretending it's BUSY\r\n"));
pTG->fFatalErrorWasSignaled = 1;
SignalStatusChange(pTG, FS_BUSY);
uRet = CONNECT_BUSY;
}
else
{
(MyDebugPrint(pTG, LOG_ERR,"<<WARNING>> Dial: Pretending it's TIMEOUT\r\n"));
pTG->fFatalErrorWasSignaled = 1;
SignalStatusChange(pTG, FS_NO_ANSWER);
uRet = CONNECT_TIMEOUT;
}
}
break;
default:
BG_CHK(FALSE);
}
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;
}
// no fallthru here
BG_CHK(FALSE);
error:
if(!iModemHangup(pTG))
{
// BG_CHK(FALSE);
// at this point in teh production version we
// need to call some OS reboot function!!
(MyDebugPrint(pTG, LOG_ERR,"<<ERROR>> Can't Hangup after DIALFAIL\r\n"));
uRet = CONNECT_ERROR;
}
// fall through
#ifndef MDDI
ICommFailureCode(pTG, T30FAILS_NCUDIAL_ERROR + uRet);
#endif //MDDI
done:
PUTEVENT(pTG, wEVFLAGS_DIAL, EVENT_ID_MODEM_STATE, EVENT_SubID_MODEM_DIAL_END,
uRet, 0, NULL);
return uRet;
}
// fImmediate==FALSE -- wait for NCUParams.NumRings else answer right away
USHORT iModemAnswer(PThrdGlbl pTG, BOOL fImmediate, USHORT uClass)
{
CBPSTR cbpstr;
USHORT uLen, uRet;
char Command[400];
int i;
(MyDebugPrint(pTG, LOG_ALL, "Entering ModemAnswer\r\n"));
pTG->FComStatus.fOffHook=TRUE; // Has to be here. Can mess up after answering
// but before CONNECT and we want to hangup
// after that!!
BG_CHK(pTG->Comm.fDataCall==FALSE);
pTG->Comm.fDataCall=FALSE;
/**** Not looking for RING should speed things up *********************
(MyDebugPrint(pTG, "Looking for RING\r\n"));
// However with an external modem this can cause problems
// because we may go into a 14second wait for answer, with phone
// off hook. Better to read S1 here. If 0 return error. If non-zero
// loop until reaches X or timeout (in case other guy hangs up before
// X rings).
// Don't resync here. We'll flush the RING from the buffer!!
// if EfaxCheckForRing() fails, the callers will resync
// through FaxSync()
#define RING_TIMEOUT 15000 // Random Timeout
// Need to wait reasonably long, so that we don't give up too easily
if(!iModemResp1(RING_TIMEOUT, cbszRING))
{uRet=CONNECT_NORING_ERROR; goto done;}
**********************************************************************/
BG_CHK(uClass==FAXCLASS0 || uClass==FAXCLASS1);
if (pTG->fNCUParamsChanged)
{
if (!iModemSetNCUParams(pTG, pTG->NCUParams.DialPauseTime,
pTG->NCUParams.SpeakerControl,
pTG->NCUParams.SpeakerVolume, pTG->NCUParams.DialBlind,
pTG->NCUParams.SpeakerRing))
{
(MyDebugPrint(pTG, LOG_ERR,"<<WARNING>> iModemSetNCUParams FAILED\r\n"));
}
pTG->fNCUParamsChanged=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++) {
sprintf (Command, "%s", pTG->AnswerCommand[i] );
if (i == (int) pTG->AnswerCommandNum - 1) {
// last command-answer
FComFlushOutput(pTG);
TwiddleThumbs1(200); // 100 is not too long for this IMPORTANT one!
FComFlushInput(pTG);
FComDirectAsyncWrite(pTG, (LPSTR) Command, (USHORT) strlen(Command) );
pTG->FComStatus.fInAnswer = TRUE;
break;
}
if( (uRet = OfflineDialog2(pTG, (LPSTR) Command, (USHORT) strlen(Command), cbszOK, cbszERROR) ) != 1) {
MyDebugPrint(pTG, LOG_ERR, "Answer %d=%s FAILED\n", i, Command);
}
else {
MyDebugPrint(pTG, LOG_ALL, "Answer %d=%s rets OK\n", 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 if(uClass==FAXCLASS1)
{
// 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)
{
(MyDebugPrint(pTG, LOG_ERR,"<<WARNING>> Error on PREANSWER string: %s\r\n", (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;
if(pTG->fNCUAbort)
{
(MyDebugPrint(pTG, LOG_ERR,"NCUAnswer--aborting\r\n"));
pTG->fNCUAbort = 0;
uRet = CONNECT_ERROR;
goto error;
}
#ifndef MDDI
ICommStatus(pTG, T30STATR_ANSWERING, 0, 0, 0);
#endif //MDDI
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);
TwiddleThumbs1(200); // 100 is not too long for this IMPORTANT one!
FComFlushInput(pTG);
FComDirectAsyncWrite(pTG, cbpstr, uLen);
// now try to get a response
if(!iiModemDialog(pTG, 0, 0, ANSWER_TIMEOUT, TRUE, 1, TRUE, cbszCONNECT, (CBPSTR)NULL))
{
pTG->FComStatus.fInAnswer = FALSE;
(MyDebugPrint(pTG, LOG_ERR,"<<ERROR>> AnswerPhone: Can't get CONNECT after RING\r\n"));
// 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;
uRet = CONNECT_OK;
goto done;
}
// no fallthru here
BG_CHK(FALSE);
error:
if (pTG->Comm.fEnableHandoff && uRet==CONNECT_WRONGMODE_DATAMODEM)
{
// We won't hangup.
ICommFailureCode(pTG, T30FAILR_NCUANSWER_ERROR); // ++ Change
// We deliberately leave pTG->FComStatus.fOffHook to TRUE, because
// it is off hook.
goto done;
}
if(!iModemHangup(pTG))
{
// BG_CHK(FALSE);
// at this point in teh production version we need to
// call some OS reboot function!!
// In WFW this can occur if an external modem has been
// powered down. so just drop thru & return ERROR
(MyDebugPrint(pTG, LOG_ERR,"<<ERROR>> Can't Hangup after ANSWERFAIL\r\n"));
uRet = CONNECT_ERROR;
}
#ifndef MDDI
ICommFailureCode(pTG, T30FAILR_NCUANSWER_ERROR);
#endif //MDDI
// fall through
done:
PUTEVENT(pTG, wEVFLAGS_DIAL, EVENT_ID_MODEM_STATE, EVENT_SubID_MODEM_ANSWER_END,
uRet, 0, NULL);
return uRet;
}
LPSTR my_fstrstr( LPSTR sz1, LPSTR sz2)
{
int i, len1, len2;
if ( (sz1 == NULL) || (sz2 == NULL) ) {
return NULL;
}
len1 = _fstrlen(sz1);
len2 = _fstrlen(sz2);
for(i=0; i<=(len1-len2); i++)
{
if(_fmemcmp(sz1+i, sz2, len2) == 0)
return sz1+i;
}
return NULL;
}
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;
}
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;
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
BG_CHK(uwRepeatCount>0);
// 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;
BG_CHK(uwWantCount>0);
if(szSend)
{
(MyDebugPrint(pTG, LOG_ALL, "Dialog: Send (%s\r\n) len=%d WantCount=%d time=%ld rep=%d\r\n", (LPSTR)szSend,
uwLen, uwWantCount, ulTimeout, uwRepeatCount));
}
else
{
(MyDebugPrint(pTG, LOG_ALL, "Response: WantCount=%d time=%ld rep=%d\r\n",
uwWantCount, ulTimeout, uwRepeatCount));
}
for(j=1; j<=uwWantCount; j++)
(MyDebugPrint(pTG, LOG_ALL, "Want %s\r\n", (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++)
{
#define DIALOGRETRYMIN 600
startTimeOut(pTG, lpto, ulTimeout);
fFirstSend = TRUE;
do
{
if(szSend)
{
if(!fFirstSend)
{
ulLeft = leftTimeOut(pTG, lpto);
if(ulLeft <= DIALOGRETRYMIN)
{
(MyDebugPrint(pTG, LOG_ALL, "ulLeft=%ul too low\r\n", ulLeft));
break;
}
else
{
(MyDebugPrint(pTG, LOG_ALL, "ulLeft=%ul OK\r\n", ulLeft));
}
}
fFirstSend = 0;
// 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)
TwiddleThumbs1(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
//////////////////////////////////////////////////////
FComCritical(pTG, TRUE); // make sure to exit on all paths out of here
BG_CHK(szSend[uwLen-1] == '\r');
if(!FComDirectSyncWriteFast(pTG, szSend, (UWORD)(uwLen-1)))
{
FComCritical(pTG, FALSE);
// Need to check that we are sending only ASCII or pre-stuffed data here
(MyDebugPrint(pTG, LOG_ERR,"<<ERROR>> Modem Dialog Sync Write timed Out\r\n"));
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))
{
FComCritical(pTG, FALSE);
(MyDebugPrint(pTG, LOG_ERR,"<<ERROR>> Modem Dialog Write timed Out on CR\r\n"));
uwRet = 0;
goto error;
}
FComCritical(pTG, FALSE);
}
// Try to get a response until timeout or bogus response
pTG->FComModem.bLastReply[0] = 0;
fGotFirstLine=FALSE;
#define SECONDLINE_TIMEOUT 500
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:
bReply[0] = 0;
swNumRead = FComFilterReadLine(pTG, bReply, REPLYBUFSIZE-1, lptoRead);
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))
{
(MyDebugPrint(pTG, LOG_ALL, "<<WARNING>> bEntireReply: out of space\r\n"));
BG_CHK(FALSE);
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;
}
for(bReply[REPLYBUFSIZE-1]=0, j=1; j<=uwWantCount; j++)
{
if(my_fstrstr(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
(MyDebugPrint(pTG, LOG_ALL, "ModemDialog: Saved line (%s)\r\n", (LPSTR)(&(pTG->FComModem.bLastReply))));
}
// we come here only on unknown reply.
BG_CHK(swNumRead > 0 || fGotFirstLine);
}
while(checkTimeOut(pTG, lpto));
if(fGotFirstLine)
continue;
(MyDebugPrint(pTG, LOG_ERR,"<<WARNING>> Weird!! got timeout in iiModemDialog loop\r\n"));
timeout:
// Need to send anychar to abort the previous command.
// use random 120ms timeout -- too short. upped to 250
# define ABORT_TIMEOUT 250
BG_CHK(swNumRead == 0);
// send \rAT\r
// no need for pause--we just timed out!!
// TwiddleThumbs1(40);
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
{
// don't abort inside ReadLine for this (abort dialog)
if(pTG->fNCUAbort >= 2) pTG->fNCUAbort = 1;
swNumRead = FComFilterReadLine(pTG, bReply, REPLYBUFSIZE-1, lpto0);
}
while(swNumRead==2 && bReply[0]=='\r'&& bReply[1]=='\n');
// While we get a blank line. Get another.
//
// BugBug (andrewr): if FComFilterReadLine fails, then bReply
// is not initialed and we can have bogus data in our debug
// statement below. Safest way to initialize this is to
// initialize the data if this call fails since we don't
// necessarily know all the side effects of changing this in
// the calling function or calling it above.
//
if (swNumRead == 0) {
bReply[0] = 0;
}
if(bReply[0] && my_fstrstr(bReply, cbszOK)==NULL)
(MyDebugPrint(pTG, LOG_ERR,"<<ERROR>> Anykey abort reply not OK. Got <<%s>>\r\n", (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);
if(pTG->fNCUAbort)
{
(MyDebugPrint(pTG, LOG_ERR,"ModemDialog--aborting\r\n"));
pTG->fNCUAbort = 0;
break; // drop out of loop to error
}
}
error:
BG_CHK(uwRet == 0);
(MyDebugPrint(pTG, LOG_ERR,"<<WARNING>> ModemDialog: (%s\r\n) --> (%d)(%s, etc) Failed\r\n", (LPSTR)(szSend?szSend:"null"), uwWantCount, (LPSTR)rgcbszWant[1]));
iModemSetError(pTG, MODEMERR_HARDWARE, ERR_COMMAND_FAILED, MODEMERRFLAGS_TRANSIENT);
pTG->FComStatus.fInDialog = 0;
return 0;
end:
BG_CHK(uwRet != 0);
(MyDebugPrint(pTG, LOG_ALL, "ModemDialog: GOT IT %d (%s)\r\n", uwRet, (LPSTR)(rgcbszWant[uwRet])));
pTG->FComStatus.fInDialog = 0;
return uwRet;
}
#ifdef MON3
void InitMonitorLogging(PThrdGlbl pTG)
{
DWORD_PTR dwKey;
pTG->Comm.fEnableHandoff=1;
if (pTG->Comm.fEnableHandoff)
{
(MyDebugPrint(pTG, LOG_ALL, "<<WARNING>> ADAPTIVE ANSWER ENABLED\r\n"));
}
if ( 0 ) // RSL !fBeenHere
{
dwKey = ProfileOpen(DEF_BASEKEY, szGENERAL, fREG_READ);
if (dwKey)
{
MONOPTIONS mo;
#ifdef DEBUG
# define DEFMONVAL 1
#else //!DEBUG
# define DEFMONVAL 0
#endif //!DEBUG
BOOL fDoMonitor =(BOOL)(ProfileGetInt(dwKey, szMONITORCOMM, DEFMONVAL, NULL));
_fmemset(&mo, 0, sizeof(mo));
if (fDoMonitor)
{
UINT uPrefDataBufSizeKB=0;
UINT uMaxExistingSizeKB=0;
uPrefDataBufSizeKB=ProfileGetInt(dwKey, szMONITORBUFSIZEKB, (0x1<<6), NULL);
#define szMONITOREXISTINGFILESIZE "MonitorMaxOldSizeKB"
#define szMONITORDIR "MonitorDir"
uMaxExistingSizeKB=
ProfileGetInt(dwKey, szMONITOREXISTINGFILESIZE, 64, NULL);
mo.dwMRBufSize = ((DWORD)uPrefDataBufSizeKB)<<(10-2);
mo.dwDataBufSize = ((DWORD)uPrefDataBufSizeKB)<<10;
mo.dwMaxExistingSize = ((DWORD)uMaxExistingSizeKB)<<10;
ProfileGetString(dwKey, szMONITORDIR, "c:\\",
mo.rgchDir, sizeof(mo.rgchDir)-1);
}
ProfileClose(dwKey); dwKey=0;
if (fDoMonitor)
{
INT i = _fstrlen(mo.rgchDir);
BG_CHK((i+1)<sizeof(mo.rgchDir));
if (i && mo.rgchDir[i-1]!='\\')
{mo.rgchDir[i]='\\';mo.rgchDir[i+1]=0;}
MonInit(pTG, &mo);
(MyDebugPrint(pTG, LOG_ALL, "MONITOR OPTIONS: dwMR=%lu; dwDB=%lu; dwMES=%lu; szDir=\"%s\":%s\r\n",
(unsigned long) mo.dwMRBufSize,
(unsigned long) mo.dwDataBufSize,
(unsigned long) mo.dwMaxExistingSize,
(LPSTR) mo.rgchDir,
(LPSTR) ((pTG->gMonInfo.fInited)? "ON" : "OFF")));
}
}
}
}
#endif // MON3
USHORT iModemGetAdaptiveResp(PThrdGlbl pTG)
{
USHORT uRet=CONNECT_ERROR;
BOOL fGotOK=FALSE;
BOOL fGotData=FALSE;
BOOL fGotFax=FALSE;
LONG lRet;
char Command[400];
pTG->Comm.fDataCall = FALSE;
// RSL was 60 000
#define AA_ANSWER_TIMEOUT 40000
//
// 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;
(MyDebugPrint(pTG, LOG_ALL,"AdaptiveAnswer: got FAX response\n"));
break;
case 2:
fGotData = 1;
(MyDebugPrint(pTG, LOG_ALL,"AdaptiveAnswer: got DATA response\n"));
break;
case 3:
(MyDebugPrint(pTG, LOG_ERR,"<<ERROR>> AnswerPhone: Can't get CONNECT before FAX/DATA\r\n"));
pTG->Comm.fDataCall = FALSE;
uRet = CONNECT_ERROR;
goto end;
case 4:
(MyDebugPrint(pTG, LOG_ERR,"<<ERROR>> AnswerPhone: Can't get OK before FAX/DATA\r\n"));
pTG->Comm.fDataCall = FALSE;
uRet = CONNECT_ERROR;
goto end;
default:
case 0:
(MyDebugPrint(pTG, LOG_ERR,"<<ERROR>> AnswerPhone: Can't get default before FAX/DATA\r\n"));
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) {
sprintf (Command, "%s", pTG->HostCommandFaxDetect );
FComFlushOutput(pTG);
FComDirectAsyncWrite(pTG, (LPSTR) Command, (USHORT) strlen(Command) );
}
}
else if (fGotData) {
if (pTG->SerialSpeedDataDetect) {
FComSetBaudRate(pTG, pTG->SerialSpeedDataDetect);
}
if (pTG->HostCommandDataDetect) {
sprintf (Command, "%s", pTG->HostCommandDataDetect );
FComFlushOutput(pTG);
FComDirectAsyncWrite(pTG, (LPSTR) Command, (USHORT) strlen(Command) );
}
}
else {
(MyDebugPrint(pTG, LOG_ERR,"<<ERROR>> AnswerPhone: LOGICAL PGM ERROR\r\n"));
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:
(MyDebugPrint(pTG, LOG_ERR,"<<ERROR>> AnswerPhone: Can't get OK after FAX/DATA\r\n"));
pTG->Comm.fDataCall = FALSE;
uRet = CONNECT_ERROR;
goto end;
default:
case 0:
(MyDebugPrint(pTG, LOG_ERR,"<<ERROR>> AnswerPhone: Can't get default after FAX/DATA\r\n"));
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
//
MyDebugPrint(pTG, LOG_ALL, "AdaptiveAnswer: lineSetCallParams called at %ld \n", GetTickCount() );
if (!itapi_async_setup(pTG)) {
MyDebugPrint(pTG, LOG_ERR, "ERROR: AdaptiveAnswer: itapi_async_setup failed \n");
pTG->Comm.fDataCall = FALSE;
uRet = CONNECT_ERROR;
goto end;
}
lRet = lineSetCallParams(pTG->CallHandle,
LINEBEARERMODE_VOICE,
0,
0xffffffff,
NULL);
if (lRet < 0) {
MyDebugPrint(pTG, LOG_ERR, "ERROR: AdaptiveAnswer: lineSetCallParams failed \n");
pTG->fFatalErrorWasSignaled = 1;
SignalStatusChange(pTG, FS_FATAL_ERROR);
pTG->Comm.fDataCall = FALSE;
uRet = CONNECT_ERROR;
goto end;
}
else {
MyDebugPrint(pTG, LOG_ALL, "AdaptiveAnswer: lineSetCallParams returns ID %ld\n", (long) lRet);
}
if(!itapi_async_wait(pTG, (DWORD)lRet, (LPDWORD)&lRet, NULL, ASYNC_TIMEOUT)) {
MyDebugPrint(pTG, LOG_ERR, "ERROR: AdaptiveAnswer: itapi_async_wait failed \n");
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;
}