Leaked source code of windows server 2003
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

  1. /***************************************************************************
  2. Name : MODEM.C
  3. Comment : Various modem dialog & support functions, specific
  4. to COM connected modems. For a modem on the bus
  5. everything below & including this file is replaced
  6. by the modem driver.
  7. Copyright (c) Microsoft Corp. 1991, 1992, 1993
  8. Revision Log
  9. Num Date Name Description
  10. --- -------- ---------- -----------------------------------------------
  11. 101 06/04/92 arulm Modif to SUPPORT to provide a replaceable interface
  12. and to use new FCom functions.
  13. ***************************************************************************/
  14. #define USE_DEBUG_CONTEXT DEBUG_CONTEXT_T30_CLASS1
  15. #include "prep.h"
  16. #include "mmsystem.h"
  17. #include "modemint.h"
  18. #include "fcomint.h"
  19. #include "fdebug.h"
  20. #include "efaxcb.h"
  21. #define DEFINE_MDMCMDS
  22. #include "mdmcmds.h"
  23. ///RSL
  24. #include "glbproto.h"
  25. #include "psslog.h"
  26. #define FILE_ID FILE_ID_MODEM
  27. void InitMonitorLogging(PThrdGlbl pTG);
  28. # pragma message("Compiling with ADAPTIVE_ANSWER")
  29. USHORT iModemGetAdaptiveResp(PThrdGlbl pTG);
  30. #define uMULTILINE_SAVEENTIRE 0x1234 // +++ HACK passed in as fMultiLine
  31. // in iiModemDialog to get it so save
  32. // entire buffer in FComModem.bEntireReply.
  33. // Need to have these in descending order so that we'll
  34. // Sync at teh highest common speed with auto-bauding modems!
  35. static UWORD rguwSpeeds[] = {57600,19200, 19200, 9600, 2400, 1200, 300, 0};
  36. // static UWORD rguwSpeeds[] = {19200, 2400, 9600, 1200, 300, 0};
  37. // static UWORD rguwSpeeds[] = {2400, 19200, 9600, 1200, 300, 0};
  38. SWORD HayesSyncSpeed(PThrdGlbl pTG, CBPSTR cbszCommand, UWORD uwLen)
  39. {
  40. /* Internal routine to synchronize with the modem's speed. Tries to
  41. get a response from the modem by trying the speeds in rglSpeeds
  42. in order (terminated by a 0). If fTryCurrent is nonzero, checks for
  43. a response before trying to reset the speeds.
  44. Returns the speed it found, 0 if they're in sync upon entry (only
  45. checked if fTryCurrent!=0), or -1 if it couldn't sync.
  46. */
  47. // short i;
  48. short ilWhich = -1;
  49. DEBUG_FUNCTION_NAME(("HayesSyncSpeed"));
  50. rguwSpeeds[0] = pTG->CurrentSerialSpeed;
  51. if ( rguwSpeeds[0] == rguwSpeeds[1])
  52. {
  53. ilWhich++;
  54. }
  55. for(;;)
  56. {
  57. DebugPrintEx( DEBUG_MSG,
  58. "Trying: ilWhich=%d speed=%d",
  59. ilWhich,
  60. rguwSpeeds[ilWhich]);
  61. if(iSyncModemDialog(pTG, (LPSTR)cbszCommand, uwLen, cbszOK))
  62. {
  63. DebugPrintEx( DEBUG_MSG,
  64. "Succeeded in Syncing at Speed = %d (il=%d)",
  65. rguwSpeeds[ilWhich],
  66. ilWhich);
  67. return (ilWhich>=0 ? rguwSpeeds[ilWhich] : 0);
  68. }
  69. /* failed. try next speed. */
  70. if (rguwSpeeds[++ilWhich]==0)
  71. {
  72. // Tried all speeds. No response
  73. DebugPrintEx( DEBUG_ERR,
  74. "Cannot Sync with Modem on Command %s",
  75. (LPSTR)cbszCommand);
  76. return -1;
  77. }
  78. if(!FComSetBaudRate(pTG, rguwSpeeds[ilWhich]))
  79. return -1;
  80. }
  81. }
  82. SWORD iModemSync(PThrdGlbl pTG)
  83. {
  84. // The command used here must be guaranteed to be harmless,
  85. // side-effect free & non-dstructive. i.e. we can issue it
  86. // at any point in command mode without chnageing the state
  87. // of teh modem or disrupting anything.
  88. // ATZ does not qualify. AT does, I think.....
  89. return HayesSyncSpeed(pTG, cbszAT, sizeof(cbszAT)-1);
  90. }
  91. SWORD iModemReset(PThrdGlbl pTG, CBPSTR szCmd)
  92. {
  93. SWORD swRet;
  94. if (szCmd == NULL)
  95. {
  96. return -1;
  97. }
  98. if((swRet = HayesSyncSpeed(pTG, szCmd, (UWORD) _fstrlen(szCmd))) < 0)
  99. {
  100. return swRet;
  101. }
  102. else
  103. {
  104. // ATZ may result in a change in the state/baud rate of the modem
  105. // (eg. Thought board drops to 2400), therefore we must Sync up
  106. // again because this function is really a Reset&Sync function.
  107. // instead of syncing up on AT and then doing ATE0, just
  108. // sync up on ATE0 directly
  109. if(iModemSync(pTG) < 0)
  110. return -1;
  111. /////////////////////
  112. // the above idea does not work with Sharad's PP9600FXMT
  113. // somehow I end up sending it ATATE0 and it answers the phone
  114. // In other cases, the ATE0 simply has no effect (because the AT&F
  115. // thing above got confused and teh ATE0 ended up just aborting
  116. // some previous command) and on ATA I get the ATA echoed,
  117. // get confused (because multi-line is FALSE) & send ATA again
  118. // which aborts the whole thing....
  119. //
  120. return 0;
  121. }
  122. }
  123. //////////////////////////////////////////////////////////////////////////////////////////////////////////////
  124. #define ATV1 "ATV1"
  125. #define AT "AT"
  126. #define cr "\r"
  127. #define cbszZero "0"
  128. USHORT
  129. T30ModemInit(PThrdGlbl pTG)
  130. {
  131. USHORT uLen, uRet;
  132. /*** Inits (or re-inits) the COM port, Syncs up with Modem (at whatever,
  133. speed), gets modem capabilities, puts it into CLASS0, syncs again,
  134. flushes buffers and returns TRUE on success FALSE on failure
  135. ***/
  136. DEBUG_FUNCTION_NAME(("T30ModemInit"));
  137. PSSLogEntry(PSS_MSG, 0, "Modem initialization");
  138. // Save the profile ID and key string.
  139. pTG->FComModem.dwProfileID = DEF_BASEKEY;
  140. uLen = min(_fstrlen(pTG->lpszPermanentLineID), sizeof(pTG->FComModem.rgchKey)-1);
  141. _fmemcpy(pTG->FComModem.rgchKey, pTG->lpszPermanentLineID, uLen);
  142. pTG->FComModem.rgchKey[uLen] = 0;
  143. if (!uLen)
  144. {
  145. DebugPrintEx( DEBUG_ERR,
  146. "Bad param: ProfileID=0x%lx; Key=%s",
  147. (unsigned long) pTG->FComModem.dwProfileID,
  148. (LPSTR) pTG->FComModem.rgchKey);
  149. return INIT_INTERNAL_ERROR;
  150. }
  151. InitMonitorLogging(pTG);
  152. //
  153. // Get the modem info before talking to h/w.
  154. //
  155. if(uRet = iModemGetCmdTab( pTG,
  156. &pTG->FComModem.CurrCmdTab,
  157. &pTG->FComModem.CurrMdmCaps))
  158. {
  159. goto error;
  160. }
  161. // use MultiLine because we may get asynchronous RING responses
  162. // at arbitrary times when on-hook
  163. if(pTG->FComModem.CurrCmdTab.szSetup && (uLen=(USHORT)_fstrlen(pTG->FComModem.CurrCmdTab.szSetup)))
  164. {
  165. if(OfflineDialog2(pTG, (LPSTR)pTG->FComModem.CurrCmdTab.szSetup, uLen, cbszOK, cbszERROR) != 1)
  166. {
  167. DebugPrintEx( DEBUG_ERR,
  168. "Error in SETUP string: %s",
  169. (LPSTR)pTG->FComModem.CurrCmdTab.szSetup);
  170. // SETUP is usually the defaults?? So do nothing if this fails
  171. // uRet = INIT_MODEMERROR;
  172. // goto error;
  173. }
  174. }
  175. switch (pTG->dwSpeakerMode)
  176. {
  177. case MDMSPKR_OFF:
  178. pTG->NCUParams.SpeakerControl = 0;
  179. break;
  180. case MDMSPKR_DIAL:
  181. pTG->NCUParams.SpeakerControl = 1;
  182. break;
  183. case MDMSPKR_ON:
  184. pTG->NCUParams.SpeakerControl = 2;
  185. break;
  186. default:
  187. pTG->NCUParams.SpeakerControl = 0;
  188. break;
  189. }
  190. switch (pTG->dwSpeakerVolume)
  191. {
  192. case MDMVOL_LOW:
  193. pTG->NCUParams.SpeakerVolume = 0;
  194. break;
  195. case MDMVOL_MEDIUM:
  196. pTG->NCUParams.SpeakerVolume = 2;
  197. break;
  198. case MDMVOL_HIGH:
  199. pTG->NCUParams.SpeakerVolume = 3;
  200. break;
  201. default:
  202. pTG->NCUParams.SpeakerVolume = 0;
  203. break;
  204. }
  205. pTG->NCUParams.DialBlind = 4; //X4
  206. // need to do this every time after a Reset/AT&F
  207. if(! iModemSetNCUParams( pTG,
  208. -1,
  209. pTG->NCUParams.SpeakerControl,
  210. pTG->NCUParams.SpeakerVolume,
  211. pTG->NCUParams.DialBlind,
  212. pTG->NCUParams.SpeakerRing))
  213. {
  214. DebugPrintEx(DEBUG_WRN,"Can't Set NCU params - Ignoring that");
  215. }
  216. // Why is this here??
  217. FComFlush(pTG);
  218. pTG->FComStatus.fModemInit = TRUE;
  219. uRet = INIT_OK;
  220. goto end;
  221. error:
  222. FComClose(pTG);
  223. pTG->FComStatus.fModemInit = FALSE;
  224. // fall through...
  225. end:
  226. return uRet;
  227. }
  228. LPCMDTAB iModemGetCmdTabPtr(PThrdGlbl pTG)
  229. {
  230. return (pTG->FComStatus.fModemInit) ? &pTG->FComModem.CurrCmdTab: NULL;
  231. }
  232. #define PARAMSBUFSIZE 60
  233. #define fDETECT_DIALTONE 1
  234. #define fDETECT_BUSYTONE 2
  235. BOOL iModemSetNCUParams
  236. (
  237. PThrdGlbl pTG,
  238. int comma,
  239. int speaker,
  240. int volume,
  241. int fBlind,
  242. int fRingAloud
  243. )
  244. {
  245. char bBuf[PARAMSBUFSIZE];
  246. USHORT uLen;
  247. DEBUG_FUNCTION_NAME(("iModemSetNCUParams"));
  248. _fstrcpy(bBuf, cbszJustAT);
  249. uLen = sizeof(cbszJustAT)-1;
  250. // +++ If we want to split this into dial-tone & busy-tone we
  251. // Do it here...
  252. if ( (fBlind >= 0) && (pTG->ModemKeyCreationId == MODEMKEY_FROM_NOTHING) )
  253. {
  254. UINT u=0;
  255. switch(fBlind)
  256. {
  257. case 0:
  258. break;
  259. case fDETECT_DIALTONE:
  260. u=2;
  261. break;
  262. case fDETECT_BUSYTONE:
  263. u=3;
  264. break;
  265. default:
  266. u=4;
  267. break;
  268. }
  269. uLen += (USHORT)wsprintf(bBuf+uLen, cbszXn, u);
  270. }
  271. if(comma >= 0)
  272. {
  273. if(comma > 255)
  274. {
  275. comma = 255;
  276. }
  277. uLen += (USHORT)wsprintf(bBuf+uLen, cbszS8, comma);
  278. }
  279. if(speaker >= 0)
  280. {
  281. if(speaker > 2)
  282. {
  283. speaker = 2;
  284. }
  285. uLen += (USHORT)wsprintf(bBuf+uLen, cbszMn, speaker);
  286. }
  287. if(volume >= 0)
  288. {
  289. if(volume > 3)
  290. {
  291. volume = 3;
  292. }
  293. uLen += (USHORT)wsprintf(bBuf+uLen, cbszLn, volume);
  294. }
  295. // do something with RingAloud
  296. bBuf[uLen++] = '\r';
  297. bBuf[uLen] = 0;
  298. // use MultiLine because we may get asynchronous RING responses
  299. // at arbitrary times when on-hook
  300. if(OfflineDialog2(pTG, (LPSTR)bBuf, uLen, cbszOK, cbszERROR) != 1)
  301. {
  302. DebugPrintEx(DEBUG_ERR,"Can't Set NCU params");
  303. return FALSE;
  304. }
  305. return TRUE;
  306. }
  307. UWORD GetCap(PThrdGlbl pTG, CBPSTR cbpstrSend, UWORD uwLen)
  308. {
  309. UWORD uRet1=0, uRet2=0, uRet3=0;
  310. DEBUG_FUNCTION_NAME(("GetCap"));
  311. // We call GetCapAux twice and if they don't match we
  312. // call it a 3rd time and arbitrate. Provided it doesn't
  313. // fail the first time.
  314. if (!(uRet1=GetCapAux(pTG, cbpstrSend, uwLen)))
  315. goto end;
  316. uRet2=GetCapAux(pTG, cbpstrSend, uwLen);
  317. if (uRet1!=uRet2)
  318. {
  319. DebugPrintEx( DEBUG_WRN,
  320. "2nd getcaps return differs 1=%u,2=%u",
  321. (unsigned)uRet1,
  322. (unsigned)uRet2);
  323. uRet3=GetCapAux(pTG, cbpstrSend, uwLen);
  324. if (uRet1==uRet2 || uRet1==uRet3)
  325. {
  326. goto end;
  327. }
  328. else if (uRet2==uRet3)
  329. {
  330. uRet1=uRet2;
  331. goto end;
  332. }
  333. else
  334. {
  335. DebugPrintEx( DEBUG_ERR,
  336. "all 2 getcaps differ! 1=%u,2=%u, 3=%u",
  337. (unsigned) uRet1, (unsigned) uRet2,
  338. (unsigned) uRet3);
  339. }
  340. }
  341. end:
  342. return uRet1;
  343. }
  344. UWORD GetCapAux(PThrdGlbl pTG, CBPSTR cbpstrSend, UWORD uwLen)
  345. {
  346. NPSTR sz;
  347. BYTE speed, high;
  348. UWORD i, code;
  349. USHORT retry;
  350. USHORT uRet;
  351. DEBUG_FUNCTION_NAME(("GetCapAux"));
  352. retry = 0;
  353. restart:
  354. retry++;
  355. if(retry > 2)
  356. return 0;
  357. DebugPrintEx(DEBUG_MSG,"Want Caps for (%s)", (LPSTR)cbpstrSend);
  358. pTG->fMegaHertzHack = TRUE;
  359. uRet = OfflineDialog2(pTG, (LPSTR)cbpstrSend, uwLen, cbszOK, cbszERROR);
  360. pTG->fMegaHertzHack=FALSE;
  361. // sometimes we don't get the OK so try to parse what we got anyway
  362. DebugPrintEx(DEBUG_MSG,"LastLine = (%s)",(LPSTR)(&(pTG->FComModem.bLastReply)));
  363. if(uRet == 2)
  364. goto restart;
  365. if(_fstrlen((LPSTR)pTG->FComModem.bLastReply) == 0)
  366. goto restart;
  367. speed = 0;
  368. high = 0;
  369. for(i=0, sz=pTG->FComModem.bLastReply, code=0; i<REPLYBUFSIZE && sz[i]; i++)
  370. {
  371. if(sz[i] >= '0' && sz[i] <= '9')
  372. {
  373. code = code*10 + (sz[i] - '0');
  374. continue;
  375. }
  376. // reached a non-numeric char
  377. // if its teh first after a code, need to process the code.
  378. switch(code)
  379. {
  380. case 0: continue; // not the first char after a code
  381. case 3: break;
  382. case 24: break;
  383. case 48: speed |= V27; break;
  384. case 72:
  385. case 96: speed |= V29; break;
  386. case 73:
  387. case 97:
  388. case 121:
  389. case 145: speed |= V33; break; // long-train codes
  390. case 74:
  391. case 98:
  392. case 122:
  393. case 146: speed |= V17; break; // short-train codes
  394. //case 92:
  395. //case 93: break;
  396. // case 120: // not legal
  397. // case 144: // not legal
  398. default:
  399. DebugPrintEx( DEBUG_WRN,
  400. "Ignoring unknown Modulation code = %d",
  401. code);
  402. code=0;
  403. break;
  404. }
  405. if(code > high)
  406. high=(BYTE)code;
  407. // reset code counter after processing the baud rate code
  408. code = 0;
  409. }
  410. if(speed == 0)
  411. {
  412. // got garbage in response to query
  413. DebugPrintEx( DEBUG_MSG,
  414. "Can't get Caps for (%s) = 0x%04x Highest=%d",
  415. (LPSTR)cbpstrSend,
  416. speed,
  417. high);
  418. return 0;
  419. }
  420. if(speed == 0x0F)
  421. speed = V27_V29_V33_V17;
  422. DebugPrintEx( DEBUG_MSG,
  423. "Got Caps for (%s) = 0x%04x Highest=%d",
  424. (LPSTR)cbpstrSend,
  425. speed,
  426. high);
  427. return MAKEWORD(speed, high); // speed==low byte
  428. }
  429. BOOL iModemGetCaps
  430. (
  431. PThrdGlbl pTG,
  432. LPMODEMCAPS lpMdmCaps,
  433. DWORD dwSpeed,
  434. LPSTR lpszReset,
  435. LPDWORD lpdwGot
  436. )
  437. {
  438. /** Modem must be synced up and in normal (non-fax) mode.
  439. Queries available classes,
  440. HDLC & Data receive and transmit speeds. Returns
  441. TRUE if Modem is Class1 or Class2, FALSE if not fax modem
  442. or other error. Sets the fields in the ET30INST struct **/
  443. // lpszReset, if nonempty, will be used to reset the modem after
  444. // the FCLASS=? command see comment about US Robotics Sportster below...
  445. UWORD i, uwRet;
  446. BYTE speed;
  447. BOOL err;
  448. NPSTR sz;
  449. USHORT retry, uResp;
  450. DEBUG_FUNCTION_NAME(("iModemGetCaps"));
  451. if (!*lpdwGot)
  452. {
  453. _fmemset(lpMdmCaps, 0, sizeof(MODEMCAPS));
  454. }
  455. if (*lpdwGot & fGOTCAP_CLASSES)
  456. goto GotClasses;
  457. for(retry=0; retry<2; retry++)
  458. {
  459. pTG->fMegaHertzHack = TRUE;
  460. uResp = OfflineDialog2(pTG, (LPSTR)cbszQUERY_CLASS, sizeof(cbszQUERY_CLASS)-1, cbszOK, cbszERROR);
  461. pTG->fMegaHertzHack=FALSE;
  462. if(uResp != 2)
  463. break;
  464. }
  465. // sometimes we don't get the OK so try to parse what we got anyway
  466. DebugPrintEx( DEBUG_MSG,
  467. "LastLine = (%s)",
  468. (LPSTR)(&(pTG->FComModem.bLastReply)));
  469. lpMdmCaps->uClasses = 0;
  470. for(i=0, sz=pTG->FComModem.bLastReply; i<REPLYBUFSIZE && sz[i]; i++)
  471. {
  472. UINT uDig=0, uDec=(UINT)-1;
  473. // This code will accept 1.x as class1, 2 as class2 and 2.x as class2.0
  474. // Also, it will not detect class 1 in 2.1 or class2 in 1.2 etc.
  475. // (JDecuir newest class2.0 is labeled class2.1, and he talks
  476. // of class 1.0...)
  477. if(sz[i] >= '0' && sz[i] <= '9')
  478. {
  479. uDig = sz[i]- '0';
  480. if (sz[i+1]=='.')
  481. {
  482. i++;
  483. if(sz[i+1] >= '0' && sz[i+1] <= '9')
  484. {
  485. uDec = sz[i] - '0';
  486. i++;
  487. }
  488. }
  489. }
  490. if(uDig==1)
  491. {
  492. lpMdmCaps->uClasses |= FAXCLASS1;
  493. }
  494. if(uDig==2)
  495. {
  496. if (uDec==((UINT)-1))
  497. {
  498. lpMdmCaps->uClasses |= FAXCLASS2;
  499. }
  500. else
  501. {
  502. lpMdmCaps->uClasses |= FAXCLASS2_0;
  503. }
  504. }
  505. }
  506. *lpdwGot |= fGOTCAP_CLASSES;
  507. GotClasses:
  508. if(!lpMdmCaps->uClasses)
  509. {
  510. DebugPrintEx(DEBUG_ERR,"Not a fax modem or unsupported fax class");
  511. *lpdwGot &= ~(fGOTCAP_CLASSES|fGOTCAP_SENDSPEEDS|fGOTCAP_RECVSPEEDS);
  512. return FALSE;
  513. }
  514. if(!(lpMdmCaps->uClasses & FAXCLASS1))
  515. return TRUE;
  516. ///////////////// rest is for Class1 only //////////////////////////
  517. if(lpszReset && *lpszReset && iModemReset(pTG, lpszReset) < 0)
  518. return FALSE;
  519. //////////
  520. if(!iiModemGoClass(pTG, 1, dwSpeed))
  521. goto NotClass1;
  522. err = FALSE;
  523. if (!(*lpdwGot & fGOTCAP_SENDSPEEDS))
  524. {
  525. uwRet = GetCap( pTG, cbszQUERY_FTM, sizeof(cbszQUERY_FTM)-1);
  526. err = (err || uwRet==0);
  527. speed = LOBYTE(uwRet);
  528. lpMdmCaps->uSendSpeeds = speed;
  529. *lpdwGot |= fGOTCAP_SENDSPEEDS;
  530. }
  531. if (!(*lpdwGot & fGOTCAP_RECVSPEEDS))
  532. {
  533. uwRet = GetCap(pTG, cbszQUERY_FRM, sizeof(cbszQUERY_FRM)-1);
  534. err = (err || uwRet==0);
  535. speed = LOBYTE(uwRet);
  536. lpMdmCaps->uRecvSpeeds = speed;
  537. *lpdwGot |= fGOTCAP_RECVSPEEDS;
  538. }
  539. if(!iiModemGoClass(pTG, 0, dwSpeed))
  540. err = TRUE;
  541. if(err)
  542. {
  543. DebugPrintEx(DEBUG_ERR,"Cannot get capabilities");
  544. goto NotClass1;
  545. }
  546. DebugPrintEx(DEBUG_MSG,"Got Caps");
  547. return TRUE;
  548. NotClass1:
  549. // Reported Class1 but failed AT+FCLASS=1 or one of the Cap queries
  550. // GVC9624Vbis does this. See bug#1016
  551. // FIX: Just zap out the Class1 bit. If any other class supported
  552. // then return TRUE, else FALSE
  553. lpMdmCaps->uClasses &= (~FAXCLASS1); // make the Class1 bit==0
  554. if(lpMdmCaps->uClasses)
  555. {
  556. return TRUE;
  557. }
  558. else
  559. {
  560. *lpdwGot &= ~(fGOTCAP_CLASSES|fGOTCAP_SENDSPEEDS|fGOTCAP_RECVSPEEDS);
  561. return FALSE;
  562. }
  563. }
  564. BOOL iModemGoClass(PThrdGlbl pTG, USHORT uClass)
  565. {
  566. return iiModemGoClass(pTG, uClass, pTG->FComModem.CurrCmdTab.dwSerialSpeed);
  567. }
  568. BOOL iiModemGoClass(PThrdGlbl pTG, USHORT uClass, DWORD dwSpeed)
  569. {
  570. int i;
  571. USHORT uBaud;
  572. DEBUG_FUNCTION_NAME(("iiModemGoClass"));
  573. for(i=0; i<3; i++)
  574. {
  575. // UDS V.3257 modem needs this time, because if we send it a
  576. // command too quickly after the previous response, it ignores
  577. // it or gets garbage
  578. Sleep(100);
  579. FComFlush(pTG);
  580. PSSLogEntry(PSS_MSG, 2, "send: \"%s\"", rgcbpstrGO_CLASS[uClass]);
  581. if(!FComDirectSyncWriteFast(pTG, (LPB)rgcbpstrGO_CLASS[uClass], uLenGO_CLASS[uClass]))
  582. goto error;
  583. // wait 500ms. Give modem enough time to get into Class1 mode
  584. // otherwise the AT we send may abort the transition
  585. Sleep(500);
  586. if(dwSpeed)
  587. {
  588. USHORT usSpeed = (USHORT) dwSpeed;
  589. uBaud = usSpeed;
  590. }
  591. else if (pTG->SerialSpeedInit)
  592. {
  593. uBaud = pTG->SerialSpeedInit;
  594. }
  595. else
  596. {
  597. uBaud = 57600;
  598. }
  599. // RSL don't do hard-coded 2400 for class0.
  600. FComSetBaudRate(pTG, uBaud);
  601. FComFlush(pTG);
  602. if(iModemSync(pTG) >= 0)
  603. {
  604. return TRUE;
  605. }
  606. }
  607. error:
  608. // no point -- and we'll smash our settings
  609. // iModemReset();
  610. // error is already set to ERR_NO_RESPONSE inside HayesSync()
  611. DebugPrintEx(DEBUG_ERR,"Cant go to Class %d", uClass);
  612. return FALSE;
  613. }
  614. BOOL iModemClose(PThrdGlbl pTG)
  615. {
  616. USHORT uLen;
  617. BOOL fRet=FALSE;
  618. DEBUG_FUNCTION_NAME(("iModemClose"));
  619. if(!pTG->FComStatus.fModemInit)
  620. return TRUE;
  621. /** Hangs up the phone if it is off hook, closes the COM port
  622. and returns. If hangup fails then port is also left open. **/
  623. if(!iModemHangup(pTG))
  624. goto lNext;
  625. if (pTG->Comm.fEnableHandoff && pTG->Comm.fDataCall)
  626. {
  627. goto lNext;
  628. }
  629. if(pTG->FComModem.CurrCmdTab.szExit && (uLen=(USHORT)_fstrlen(pTG->FComModem.CurrCmdTab.szExit)))
  630. {
  631. if(OfflineDialog2(pTG, (LPSTR)pTG->FComModem.CurrCmdTab.szExit, uLen, cbszOK, cbszERROR) != 1)
  632. {
  633. DebugPrintEx( DEBUG_ERR,
  634. "Error in EXIT string: %s",
  635. (LPSTR)pTG->FComModem.CurrCmdTab.szExit);
  636. }
  637. }
  638. lNext:
  639. if(FComClose(pTG))
  640. {
  641. pTG->FComStatus.fModemInit = FALSE;
  642. fRet=TRUE;
  643. }
  644. return fRet;
  645. }
  646. BOOL iModemHangup(PThrdGlbl pTG)
  647. {
  648. BOOL fRet=FALSE;
  649. DEBUG_FUNCTION_NAME(("iModemHangup"));
  650. if(!pTG->FComStatus.fOffHook)
  651. {
  652. DebugPrintEx( DEBUG_WRN,
  653. "The modem is already on-hook!!!! return without doing nothing");
  654. return TRUE;
  655. }
  656. // Note: iModemHangup is called by NCULink in ddi.c.
  657. // Rather than do adaptive-answer-specific code in ddi.c as well,
  658. // we simply ignore the hangup command in the following case...
  659. if (pTG->Comm.fEnableHandoff && pTG->Comm.fDataCall)
  660. {
  661. DebugPrintEx(DEBUG_WRN,"IGNORING Hangup of datamodem call");
  662. return TRUE;
  663. }
  664. PSSLogEntry(PSS_MSG, 1, "Hanging up");
  665. // FComDTR(FALSE); // Lower DTR to hangup in ModemHangup
  666. // Need to have &D2 in init string for this.
  667. // Do this twice. There is a bizarre case where you drop DTR,
  668. // then go into Dialog, flush, send ATH0, then the modem gives
  669. // you an OK for the DTR, and you take it as one for the ATH0
  670. // maybe that's ok....if this gets too slow, skip this.
  671. HayesSyncSpeed(pTG, cbszHANGUP, sizeof(cbszHANGUP)-1);
  672. if(HayesSyncSpeed(pTG, cbszHANGUP, sizeof(cbszHANGUP)-1) < 0)
  673. {
  674. FComDTR(pTG, FALSE); // Lower DTR on stubborn hangups in ModemHangup
  675. Sleep(1000); // pause 1 second
  676. FComDTR(pTG, TRUE); // raise it again. Some modems return to cmd state
  677. // only when this is raised again
  678. if(iModemReset(pTG, pTG->FComModem.CurrCmdTab.szReset) < 0)
  679. goto error;
  680. if(HayesSyncSpeed(pTG, cbszHANGUP, sizeof(cbszHANGUP)-1) < 0)
  681. goto error;
  682. }
  683. pTG->FComStatus.fOffHook = FALSE;
  684. if(!iiModemGoClass(pTG, 0, pTG->FComModem.CurrCmdTab.dwSerialSpeed))
  685. goto end;
  686. // Can also ignore this return value. Just for tidier cleanup
  687. // Avoid! we'll smash our settings
  688. // iModemReset();
  689. fRet=TRUE;
  690. goto end;
  691. error:
  692. FComDTR(pTG, TRUE); // raise it again
  693. // fall through...
  694. end:
  695. return fRet;
  696. }
  697. /*++
  698. Routine Description:
  699. Print the dial command to PSS log, hiding the actual number, like this: "ATDT ####"
  700. Arguments:
  701. pTG
  702. lpszFormat - format for sprintf, usually #defined as "ATD%c %s"
  703. chMod - Dial mode ('T' or 'P')
  704. iLen - Length of number
  705. Return Value:
  706. None
  707. --*/
  708. void LogDialCommand(PThrdGlbl pTG, LPSTR lpszFormat, char chMod, int iLen)
  709. {
  710. BYTE bBufHideDest[DIALBUFSIZE] = {'\0'};
  711. int i;
  712. sprintf(bBufHideDest, lpszFormat, chMod, TEXT(""));
  713. if (_tcslen(bBufHideDest)+iLen > DIALBUFSIZE-1)
  714. { // Not enough room - don't log!
  715. return;
  716. }
  717. for (i=0; i<iLen; i++)
  718. {
  719. strcat(bBufHideDest, "#");
  720. }
  721. PSSLogEntry(PSS_MSG, 2, "send: \"%s\"", bBufHideDest);
  722. }
  723. USHORT iModemDial(PThrdGlbl pTG, LPSTR lpszDial)
  724. {
  725. ULONG ulTimeout;
  726. USHORT uRet, uLen, uDialStringLen;
  727. BYTE bBuf[DIALBUFSIZE];
  728. CBPSTR cbpstr;
  729. char chMod = pTG->NCUParams.chDialModifier;
  730. DWORD dwDialTime = 0;
  731. char KeyName[200];
  732. HKEY hKey;
  733. char BlindDialString[200];
  734. char RegBlindDialString[200];
  735. long lRet;
  736. DWORD dwSize;
  737. DWORD dwType;
  738. DEBUG_FUNCTION_NAME(("iModemDial"));
  739. pTG->FComStatus.fOffHook = TRUE; // Has to be here. Can get an error return
  740. // below even after connecting
  741. // and we want to hangup after that!!
  742. pTG->Comm.fDataCall=FALSE;
  743. //
  744. // check "Modems->Properties->Connection->Wait for dial tone" setting before dialing
  745. // to correctly set ATX to possibly blind dial
  746. //
  747. if (pTG->fBlindDial)
  748. {
  749. // create default string
  750. sprintf(BlindDialString, "ATX3\r");
  751. // need to check Unimodem Settings\Blind_On key.
  752. sprintf(KeyName, "%s\\Settings", pTG->lpszUnimodemKey);
  753. lRet = RegOpenKeyEx(
  754. HKEY_LOCAL_MACHINE,
  755. KeyName,
  756. 0,
  757. KEY_READ,
  758. &hKey);
  759. if (lRet != ERROR_SUCCESS)
  760. {
  761. DebugPrintEx(DEBUG_ERR, "Can't read Unimodem Settings key %s", KeyName);
  762. }
  763. else
  764. {
  765. dwSize = sizeof(RegBlindDialString);
  766. lRet = RegQueryValueEx(
  767. hKey,
  768. "Blind_On",
  769. 0,
  770. &dwType,
  771. RegBlindDialString,
  772. &dwSize);
  773. RegCloseKey(hKey);
  774. if (lRet != ERROR_SUCCESS)
  775. {
  776. DebugPrintEx( DEBUG_ERR,
  777. "Can't read Unimodem key\\Settings\\Blind_On value");
  778. }
  779. else if (RegBlindDialString)
  780. {
  781. sprintf(BlindDialString, "AT%s\r", RegBlindDialString);
  782. }
  783. }
  784. }
  785. if(!iiModemGoClass(pTG, 1, pTG->FComModem.CurrCmdTab.dwSerialSpeed))
  786. {
  787. uRet = CONNECT_ERROR;
  788. goto error;
  789. }
  790. //
  791. // blind dial set here if requested by user
  792. //
  793. if (pTG->fBlindDial && BlindDialString)
  794. {
  795. uLen = (USHORT)strlen(BlindDialString);
  796. if(OfflineDialog2(pTG, BlindDialString, uLen, cbszOK, cbszERROR) != 1)
  797. {
  798. DebugPrintEx( DEBUG_ERR,
  799. "Error in BLIND DIAL string: %s",
  800. BlindDialString);
  801. }
  802. }
  803. if(pTG->FComModem.CurrCmdTab.szPreDial && (uLen=(USHORT)_fstrlen(pTG->FComModem.CurrCmdTab.szPreDial)))
  804. {
  805. if(OfflineDialog2(pTG, (LPSTR)pTG->FComModem.CurrCmdTab.szPreDial, uLen, cbszOK, cbszERROR) != 1)
  806. {
  807. DebugPrintEx( DEBUG_ERR,
  808. "Error in PREDIAL string: %s",
  809. (LPSTR)pTG->FComModem.CurrCmdTab.szPreDial);
  810. }
  811. }
  812. cbpstr = cbszDIAL;
  813. // If the dial string already has a T or P prefix, we use that
  814. // instead.
  815. {
  816. char c=0;
  817. while((c=*lpszDial) && c==' ')
  818. {
  819. lpszDial++;
  820. }
  821. if (c=='t'|| c=='T' || c=='p'|| c=='P')
  822. {
  823. chMod = c;
  824. lpszDial++;
  825. while((c=*lpszDial) && c==' ')
  826. {
  827. lpszDial++;
  828. }
  829. }
  830. }
  831. // in mdmcmds.h you can find this line: cbszDIAL = "ATD%c %s\r"
  832. uLen = (USHORT)wsprintf(bBuf, cbpstr, chMod, (LPSTR)lpszDial);
  833. // Need to set an approriate timeout here. A minimum of 15secs is too short
  834. // (experiment calling machines within a PABX), plus one has to give extra
  835. // time for machines that pick up after 2 or 4 rings and also for long distance
  836. // calls. I take a minumum of 30secs and add 3secs for each digits over 7
  837. // (unless it's pulse dial in which case I add 8secs/digit).
  838. // (I'm assuming that a long-distance call will take a minimum of 8 digits
  839. // anywhere in ths world!). Fax machines I've tested wait about 30secs
  840. // independent of everything.
  841. uDialStringLen = (USHORT)_fstrlen(lpszDial);
  842. ulTimeout = DIAL_TIMEOUT;
  843. if(uDialStringLen > 7)
  844. {
  845. ulTimeout += ((chMod=='p' || chMod=='P')?8000:3000)
  846. * (uDialStringLen - 7);
  847. }
  848. pTG->FComStatus.fInDial = TRUE;
  849. // look for MultiLine, just in case we get echo or garbage.
  850. // Nothing lost, since on failure of this we can't do anything
  851. // uRet = iiModemDialog((LPB)bBuf, uLen, ulTimeout, TRUE, 1, TRUE,
  852. // cbszCONNECT, cbszBUSY, cbszNOANSWER,
  853. // cbszNODIALTONE, cbszERROR, (CBPSTR)NULL);
  854. // Send seperately & use iiModemDialog only for the response
  855. // all this just to send the ATDT
  856. FComFlushOutput(pTG);
  857. Sleep(200); // 100 is not too long for this IMPORTANT one!
  858. FComFlushInput(pTG);
  859. LogDialCommand(pTG, cbszDIAL, chMod, uDialStringLen);
  860. FComDirectAsyncWrite(pTG, bBuf, uLen);
  861. // now try to get a response
  862. dwDialTime = GetTickCount();
  863. uRet = iiModemDialog( pTG,
  864. 0,
  865. 0,
  866. ulTimeout,
  867. TRUE,
  868. 1,
  869. TRUE,
  870. cbszCONNECT,
  871. cbszBUSY,
  872. cbszNOANSWER,
  873. cbszNODIALTONE,
  874. cbszERROR,
  875. cbszBLACKLISTED,
  876. cbszDELAYED,
  877. cbszNOCARRIER,
  878. (CBPSTR)NULL);
  879. pTG->FComStatus.fInDial = FALSE;
  880. DebugPrintEx(DEBUG_MSG,"ModemDial -- got %d response from Dialog", uRet);
  881. #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))
  882. #error CONNECT defines not correct ERROR, OK, BUSY, NOANSWER, NODIALTONE == CONNECT_ERROR, CONNECT_OK, CONNECT_BUSY, CONNECT_NOANSWER, CONNECT_NODIALTONE
  883. #else
  884. #pragma message("verified CONNECT defines")
  885. #endif
  886. switch(uRet)
  887. {
  888. case CONNECT_TIMEOUT:
  889. pTG->fFatalErrorWasSignaled = 1;
  890. SignalStatusChange(pTG, FS_NO_ANSWER);
  891. PSSLogEntry(PSS_ERR, 1, "Response - timeout");
  892. break;
  893. case CONNECT_OK:
  894. PSSLogEntry(PSS_MSG, 1, "Response - CONNECT");
  895. pTG->fReceivedHDLCflags = TRUE;
  896. break;
  897. case CONNECT_BUSY:
  898. pTG->fFatalErrorWasSignaled = 1;
  899. SignalStatusChange(pTG, FS_BUSY);
  900. PSSLogEntry(PSS_ERR, 1, "Response - BUSY");
  901. break;
  902. case CONNECT_NOANSWER:
  903. pTG->fFatalErrorWasSignaled = 1;
  904. SignalStatusChange(pTG, FS_NO_ANSWER);
  905. PSSLogEntry(PSS_ERR, 1, "Response - NO ANSWER");
  906. break;
  907. case CONNECT_NODIALTONE:
  908. pTG->fFatalErrorWasSignaled = 1;
  909. SignalStatusChange(pTG, FS_NO_DIAL_TONE);
  910. PSSLogEntry(PSS_ERR, 1, "Response - NO DIALTONE");
  911. break;
  912. case CONNECT_ERROR:
  913. pTG->fFatalErrorWasSignaled = 1;
  914. SignalStatusChange(pTG, FS_NO_ANSWER);
  915. PSSLogEntry(PSS_ERR, 1, "Response - ERROR");
  916. break;
  917. case CONNECT_BLACKLISTED:
  918. pTG->fFatalErrorWasSignaled = 1;
  919. SignalStatusChange(pTG, FS_CALL_BLACKLISTED);
  920. PSSLogEntry(PSS_ERR, 1, "Response - BLACKLISTED");
  921. break;
  922. case CONNECT_DELAYED:
  923. pTG->fFatalErrorWasSignaled = 1;
  924. SignalStatusChange(pTG, FS_CALL_DELAYED);
  925. PSSLogEntry(PSS_ERR, 1, "Response - DELAYED");
  926. break;
  927. case 8: {
  928. DWORD dwDelta = GetTickCount() - dwDialTime;
  929. PSSLogEntry(PSS_ERR, 1, "Response - NO CARRIER");
  930. if (dwDelta < 5000L)
  931. {
  932. DebugPrintEx(DEBUG_WRN,"Dial: Pretending it's BUSY");
  933. pTG->fFatalErrorWasSignaled = 1;
  934. SignalStatusChange(pTG, FS_BUSY);
  935. uRet = CONNECT_BUSY;
  936. }
  937. else
  938. {
  939. DebugPrintEx(DEBUG_WRN,"Dial: Pretending it's TIMEOUT");
  940. pTG->fFatalErrorWasSignaled = 1;
  941. SignalStatusChange(pTG, FS_NO_ANSWER);
  942. uRet = CONNECT_TIMEOUT;
  943. }
  944. }
  945. break;
  946. }
  947. if(uRet == CONNECT_OK)
  948. {
  949. goto done;
  950. }
  951. else
  952. {
  953. if(uRet == CONNECT_TIMEOUT)
  954. {
  955. pTG->fFatalErrorWasSignaled = 1;
  956. SignalStatusChange(pTG, FS_NO_ANSWER);
  957. uRet = CONNECT_NOANSWER;
  958. // call it a no answer
  959. }
  960. goto error;
  961. }
  962. error:
  963. if(!iModemHangup(pTG))
  964. {
  965. // at this point in teh production version we
  966. // need to call some OS reboot function!!
  967. DebugPrintEx(DEBUG_ERR,"Can't Hangup after DIALFAIL");
  968. uRet = CONNECT_ERROR;
  969. }
  970. // fall through
  971. done:
  972. return uRet;
  973. }
  974. USHORT iModemAnswer(PThrdGlbl pTG)
  975. {
  976. CBPSTR cbpstr;
  977. USHORT uLen, uRet;
  978. char Command[400];
  979. int i;
  980. DEBUG_FUNCTION_NAME(("iModemAnswer"));
  981. pTG->FComStatus.fOffHook=TRUE; // Has to be here. Can screwup after answering
  982. // but before CONNECT and we want to hangup
  983. // after that!!
  984. pTG->Comm.fDataCall=FALSE;
  985. //
  986. // below is Adaptive Answer handling.
  987. // It is separate because all the commands are defined via INF
  988. //
  989. if (pTG->AdaptiveAnswerEnable)
  990. {
  991. for (i=0; i< (int) pTG->AnswerCommandNum; i++)
  992. {
  993. strcpy (Command, pTG->AnswerCommand[i] );
  994. if (i == (int) pTG->AnswerCommandNum - 1)
  995. {
  996. // last command-answer
  997. FComFlushOutput(pTG);
  998. Sleep(200); // 100 is not too long for this IMPORTANT one!
  999. FComFlushInput(pTG);
  1000. PSSLogEntry(PSS_MSG, 2, "send: \"%s\"", Command);
  1001. FComDirectAsyncWrite(pTG, (LPSTR) Command, (USHORT) strlen(Command) );
  1002. pTG->FComStatus.fInAnswer = TRUE;
  1003. break;
  1004. }
  1005. if( (uRet = OfflineDialog2(pTG, (LPSTR) Command, (USHORT) strlen(Command), cbszOK, cbszERROR) ) != 1)
  1006. {
  1007. DebugPrintEx(DEBUG_ERR, "Answer %d=%s FAILED", i, Command);
  1008. }
  1009. else
  1010. {
  1011. DebugPrintEx(DEBUG_MSG, "Answer %d=%s rets OK", i, Command);
  1012. }
  1013. }
  1014. uRet=iModemGetAdaptiveResp(pTG);
  1015. pTG->FComStatus.fInAnswer=FALSE;
  1016. if (uRet==CONNECT_OK)
  1017. goto done;
  1018. else
  1019. goto error;
  1020. }
  1021. //
  1022. // assuming FAX call since can't determine that anyway...
  1023. //
  1024. else
  1025. {
  1026. // 5/95 JosephJ:Elliot Bug#3421 -- we issue the AT+FCLASS=1 command
  1027. // twice so that if one gets zapped by a RING the other will
  1028. // be OK.
  1029. if (pTG->FComModem.CurrCmdTab.dwFlags&fMDMSP_ANS_GOCLASS_TWICE)
  1030. iiModemGoClass(pTG, 1, pTG->FComModem.CurrCmdTab.dwSerialSpeed);
  1031. if(!iiModemGoClass(pTG, 1, pTG->FComModem.CurrCmdTab.dwSerialSpeed))
  1032. {
  1033. uRet = CONNECT_ERROR;
  1034. goto error;
  1035. }
  1036. }
  1037. if(pTG->FComModem.CurrCmdTab.szPreAnswer && (uLen=(USHORT)_fstrlen(pTG->FComModem.CurrCmdTab.szPreAnswer)))
  1038. {
  1039. if(OfflineDialog2(pTG, (LPSTR)pTG->FComModem.CurrCmdTab.szPreAnswer, uLen, cbszOK, cbszERROR) != 1)
  1040. {
  1041. DebugPrintEx( DEBUG_WRN,
  1042. "Error on PREANSWER string: %s",
  1043. (LPSTR)pTG->FComModem.CurrCmdTab.szPreAnswer);
  1044. }
  1045. }
  1046. #define ANSWER_TIMEOUT 40000 // Random Timeout
  1047. // Need to wait reasonably long, so that we don't give up too easily
  1048. cbpstr = cbszANSWER;
  1049. uLen = sizeof(cbszANSWER)-1;
  1050. pTG->FComStatus.fInAnswer = TRUE;
  1051. // if(!iModemDialog((LPSTR)cbpstr, uLen, ANSWER_TIMEOUT, cbszCONNECT))
  1052. // look for MultiLine, just in case we get echo or garbage.
  1053. // Nothing lost, since on failure of this we can't do anything
  1054. // if(!iiModemDialog((LPB)cbpstr, uLen, ANSWER_TIMEOUT, TRUE, 1, TRUE,
  1055. // cbszCONNECT, (CBPSTR)NULL))
  1056. // Send seperately & use iiModemDialog only for the response
  1057. // all this just to send the ATA
  1058. FComFlushOutput(pTG);
  1059. Sleep(200); // 100 is not too long for this IMPORTANT one!
  1060. FComFlushInput(pTG);
  1061. PSSLogEntry(PSS_MSG, 2, "send: \"%s\"", cbpstr);
  1062. FComDirectAsyncWrite(pTG, cbpstr, uLen);
  1063. // this is used to complete a whole IO operation (presumably a short one)
  1064. // when this flag is set, the IO won't be disturbed by the abort event
  1065. // this flag should NOT be set for long periods of time since abort
  1066. // is disabled while it is set.
  1067. pTG->fStallAbortRequest = TRUE;
  1068. // now try to get a response
  1069. if(!iiModemDialog(pTG, 0, 0, ANSWER_TIMEOUT, TRUE, 1, TRUE, cbszCONNECT, (CBPSTR)NULL))
  1070. {
  1071. pTG->FComStatus.fInAnswer = FALSE;
  1072. PSSLogEntry(PSS_ERR, 1, "Response - ERROR");
  1073. // try to hangup and sync with modem. This should work
  1074. // even if phone is not really off hook
  1075. uRet = CONNECT_ERROR;
  1076. goto error;
  1077. }
  1078. else
  1079. {
  1080. pTG->FComStatus.fInAnswer = FALSE;
  1081. PSSLogEntry(PSS_MSG, 1, "Response - CONNECT");
  1082. uRet = CONNECT_OK;
  1083. goto done;
  1084. }
  1085. error:
  1086. if (pTG->Comm.fEnableHandoff && uRet==CONNECT_WRONGMODE_DATAMODEM)
  1087. {
  1088. // We won't hangup.
  1089. // We deliberately leave pTG->FComStatus.fOffHook to TRUE, because
  1090. // it is off hook.
  1091. goto done;
  1092. }
  1093. if(!iModemHangup(pTG))
  1094. {
  1095. // at this point in teh production version we need to
  1096. // call some OS reboot function!!
  1097. DebugPrintEx(DEBUG_ERR,"Can't Hangup after ANSWERFAIL");
  1098. uRet = CONNECT_ERROR;
  1099. }
  1100. // fall through
  1101. done:
  1102. return uRet;
  1103. }
  1104. int my_strcmp(LPSTR sz1, LPSTR sz2)
  1105. {
  1106. if ( (sz1 == NULL) || (sz2 == NULL) )
  1107. {
  1108. return FALSE;
  1109. }
  1110. if ( strcmp(sz1, sz2) == 0 )
  1111. {
  1112. return TRUE;
  1113. }
  1114. return FALSE;
  1115. }
  1116. BOOL fHasNumerals(PThrdGlbl pTG, LPSTR sz)
  1117. {
  1118. int i;
  1119. if (sz == NULL)
  1120. {
  1121. return FALSE;
  1122. }
  1123. for(i=0; sz[i]; i++)
  1124. {
  1125. if(sz[i] >= '0' && sz[i] <= '9')
  1126. return TRUE;
  1127. }
  1128. return FALSE;
  1129. }
  1130. #define DIALOGRETRYMIN 600
  1131. #define SECONDLINE_TIMEOUT 500
  1132. #define ABORT_TIMEOUT 250
  1133. #ifdef DEBUG
  1134. # define DEFMONVAL 1
  1135. #else //!DEBUG
  1136. # define DEFMONVAL 0
  1137. #endif //!DEBUG
  1138. #define szMONITOREXISTINGFILESIZE "MonitorMaxOldSizeKB"
  1139. #define szMONITORDIR "MonitorDir"
  1140. UWORD far iiModemDialog
  1141. ( PThrdGlbl pTG,
  1142. LPSTR szSend,
  1143. UWORD uwLen,
  1144. ULONG ulTimeout,
  1145. BOOL fMultiLine,
  1146. UWORD uwRepeatCount,
  1147. BOOL fPause,
  1148. CBPSTR cbpstrWant1,
  1149. CBPSTR cbpstrWant2,
  1150. ...
  1151. )
  1152. {
  1153. /** Takes a command string, and it's lengt writes it out to the modem
  1154. and tries to get one of the allowed responses. It writes the command
  1155. out, waits ulTimeOut millisecs for a response. If it gets one of the
  1156. expected responses it returns immediately.
  1157. If it gets an unexpected/illegal response it tries (without any
  1158. waiting) for subsequent lines to the same response. When all the
  1159. lines (if > 1) of the response lines are exhausted, if none is among the
  1160. expected responses, it writes the command again and tries again,
  1161. until ulTimeout has expired. Note that if no response is received,
  1162. the command will be written just once.
  1163. The whole above thing will be repeated upto uwRepeatCount times
  1164. if uwRepeatCount is non-zero
  1165. <<<<<NOTE:::uwRepeatCount != 0 should not be used except for local sync>>>>>
  1166. It returns when (a) one of the specified responses is received or
  1167. (b) uwRepeatCount tries have failed (each having returned an
  1168. illegal response or having returned no response in ulTimeout
  1169. millsecs) or (c) the command write failed, in which
  1170. case it returns immediately.
  1171. It flushes the modem inque before each Command Write.
  1172. Returns 0 on failure and the 1 based index of the successful
  1173. response on success.
  1174. This can be used in the following way:-
  1175. for Local Dialogs (AT, AT+FTH=? etc), set ulTimeout to a lowish
  1176. value, of the order of the transmission time of the longest
  1177. possible (erroneous or correct) line of response plus the size
  1178. of the command. eg. at 1200baud we have about 120cps = about
  1179. 10ms/char. Therefore a timeout of about 500ms is more than
  1180. adequate, except for really long command lines.
  1181. for Local Sync dialogs, used to sync up with the modem which may
  1182. be in an unsure state, use the same timeout, but also a repeat
  1183. count of 2 or 3.
  1184. for remote-driven dialogs, eg. AT+FRH=xx which returns a CONNECT
  1185. after the flags have been received, and which may incur a delay
  1186. before a response (ATDT is teh same. CONNECT is issued after a
  1187. long delay & anything the DTE sends will abort the process).
  1188. For these cases the caller should supply a long timeout and
  1189. probably a repeatcount of 1, so that the
  1190. routine will timeout after one try but go on issuing teh command
  1191. as long as an error repsonse is received.
  1192. For +FRH etc, the long timeout should be T1 or T2 in teh case of
  1193. CommandRecv and ResponseRecv respectively.
  1194. **/
  1195. BYTE bReply[REPLYBUFSIZE];
  1196. UWORD i, j, uwRet, uwWantCount;
  1197. SWORD swNumRead;
  1198. CBPSTR rgcbszWant[10];
  1199. va_list ap;
  1200. LPTO lpto, lptoRead, lpto0;
  1201. BOOL fGotFirstLine, fFirstSend;
  1202. ULONG ulLeft;
  1203. UINT uPos=0;
  1204. DEBUG_FUNCTION_NAME(("iiModemDialog"));
  1205. pTG->FComModem.bEntireReply[0]=0;
  1206. // ensure that we'll abort in FComm only on fresh calls to NCUAbort
  1207. // protecting ourselves against this var being randomly left set.
  1208. // Note we check this variable _just_ before calling ModemDialog
  1209. // in NCUDial and NCUAnswer & assuming atomicity between then and here
  1210. // we'll never miss an abort in a Dial/Answer
  1211. // extract the (variable length) list of acceptable responses.
  1212. // each is a CBSZ, code based 2 byte ptr
  1213. // first response always present
  1214. rgcbszWant[1] = cbpstrWant1;
  1215. if((rgcbszWant[2] = cbpstrWant2) != NULL)
  1216. {
  1217. // if more than one response
  1218. va_start(ap, cbpstrWant2);
  1219. for(j=3; j<10; j++)
  1220. {
  1221. if((rgcbszWant[j] = va_arg(ap, CBPSTR)) == NULL)
  1222. break;
  1223. }
  1224. uwWantCount = j-1;
  1225. va_end(ap);
  1226. }
  1227. else
  1228. {
  1229. uwWantCount = 1;
  1230. }
  1231. if(szSend)
  1232. {
  1233. DebugPrintEx( DEBUG_MSG,
  1234. "Dialog: Send (%s) len=%d WantCount=%d time=%ld rep=%d",
  1235. (LPSTR)szSend,
  1236. uwLen,
  1237. uwWantCount,
  1238. ulTimeout,
  1239. uwRepeatCount);
  1240. }
  1241. else
  1242. {
  1243. DebugPrintEx( DEBUG_MSG,
  1244. "Response: WantCount=%d time=%ld rep=%d",
  1245. uwWantCount,
  1246. ulTimeout,
  1247. uwRepeatCount);
  1248. }
  1249. for(j=1; j<=uwWantCount; j++)
  1250. {
  1251. DebugPrintEx(DEBUG_MSG,"Want %s",(LPSTR)(rgcbszWant[j]));
  1252. }
  1253. lpto = &(pTG->FComModem.toDialog);
  1254. lpto0 = &(pTG->FComModem.toZero);
  1255. pTG->FComStatus.fInDialog = TRUE;
  1256. // Try the dialog upto uwRepeatCount times
  1257. for(uwRet=0, i=0; i<uwRepeatCount; i++)
  1258. {
  1259. startTimeOut(pTG, lpto, ulTimeout);
  1260. fFirstSend = TRUE;
  1261. do
  1262. {
  1263. if(szSend)
  1264. {
  1265. if(!fFirstSend)
  1266. {
  1267. ulLeft = leftTimeOut(pTG, lpto);
  1268. if(ulLeft <= DIALOGRETRYMIN)
  1269. {
  1270. DebugPrintEx(DEBUG_MSG,"ulLeft=%ul too low",ulLeft);
  1271. break;
  1272. }
  1273. else
  1274. {
  1275. DebugPrintEx(DEBUG_MSG,"ulLeft=%ul OK",ulLeft);
  1276. }
  1277. }
  1278. fFirstSend = FALSE;
  1279. // If a command is supplied, write it out, flushing input
  1280. // first to get rid of spurious input.
  1281. /*** SyncWrite calls Drain here which we should not need **
  1282. *** as we are immediately waiting for a response *********
  1283. **********************************************************
  1284. if(!FComDirectSyncWrite(szSend, uwLen))
  1285. **********************************************************/
  1286. if(fPause)
  1287. Sleep(40); // 100 is too long
  1288. // FComFlushInput();
  1289. FComFlush(pTG); // Need to flush output too? Maybe...
  1290. // there's nowhere else to flush/loosen up teh output
  1291. // The flush has to be as late in the game as possible,
  1292. // because if teh previous command got confused & accepted
  1293. // a response to an earlier command or something, then
  1294. // it's response may still be in transit (this happened
  1295. // on Sharad's PP9600FXMT), so the later we do this the
  1296. // better. So we send the entire command w/o teh \r,
  1297. // wait for it to drain, then Flush again (input only
  1298. // this time) then send the CR
  1299. ///////// Potential Major source of failures ////////
  1300. // DirectSyncWrite calls Drain which calls DllSleep if everything
  1301. // is not drained, so we could end up waiting for 1 time slice
  1302. // which is at least 50ms and looks like it can be much higher on
  1303. // some machines. This was screwing up our AT+FTM=96 is some cases
  1304. // FIX: Enter Crit section here exit after this is done
  1305. //////////////////////////////////////////////////////
  1306. PSSLogEntry(PSS_MSG, 2, "send: \"%s\"", szSend);
  1307. if(!FComDirectSyncWriteFast(pTG, szSend, (UWORD)(uwLen-1)))
  1308. {
  1309. // Need to check that we are sending only ASCII or pre-stuffed data here
  1310. DebugPrintEx(DEBUG_ERR,"Modem Dialog Sync Write timed Out");
  1311. uwRet = 0;
  1312. goto error;
  1313. // If Write fails, fail & return immediately.
  1314. // SetMyError() will already have been called.
  1315. }
  1316. // output has drained. Now flush input
  1317. FComFlushInput(pTG);
  1318. // and then send the CR
  1319. if(!FComDirectAsyncWrite(pTG, "\r", 1))
  1320. {
  1321. DebugPrintEx(DEBUG_ERR,"Modem Dialog Write timed Out on CR");
  1322. uwRet = 0;
  1323. goto error;
  1324. }
  1325. }
  1326. // Try to get a response until timeout or bad response
  1327. pTG->FComModem.bLastReply[0] = 0;
  1328. fGotFirstLine=FALSE;
  1329. for(lptoRead=lpto;;startTimeOut(pTG, lpto0, SECONDLINE_TIMEOUT), lptoRead=lpto0)
  1330. {
  1331. // get a CR-LF terminated line
  1332. // for the first line use macro timeout, for multi-line
  1333. // responses use 0 timeout.
  1334. retry:
  1335. swNumRead = FComFilterReadLine(pTG, bReply, REPLYBUFSIZE-1, lptoRead);
  1336. DebugPrintEx(DEBUG_MSG,"FComFilterReadLine returns %d",swNumRead);
  1337. if(swNumRead == 2 && bReply[0] == '\r' && bReply[1] == '\n')
  1338. goto retry; // blank line -- throw away & get another
  1339. // Fix Bug#1226. Elsa Microlink returns this garbage line in
  1340. // response to AT+FCLASS=?, followed by the real reply. Since
  1341. // we only look at the first line, we see only this garbage line
  1342. // and we never see the real reply (0, 1, 2, 2.0)
  1343. if(swNumRead==3 && bReply[0]==0x13 && bReply[1]=='\r' && bReply[2]=='\n')
  1344. goto retry;
  1345. // Fix Elliot bug#3619 -- German modem TE3801 sends us
  1346. // \r\r\nOK\r\n -- so we treat \r\r\n as blank line.
  1347. if(swNumRead==3 && bReply[0]=='\r' && bReply[1]=='\r' && bReply[2]=='\n')
  1348. goto retry;
  1349. if(swNumRead == 0) // timeout
  1350. {
  1351. if(fGotFirstLine)
  1352. {
  1353. // for MegaHertz, which returns no OK after
  1354. // capabilities queries
  1355. if(pTG->fMegaHertzHack)
  1356. {
  1357. if(fHasNumerals(pTG, pTG->FComModem.bLastReply))
  1358. {
  1359. uwRet = 1;
  1360. goto end;
  1361. }
  1362. }
  1363. break;
  1364. }
  1365. else
  1366. {
  1367. goto timeout;
  1368. }
  1369. }
  1370. if(swNumRead < 0) // error-but lets see what we got anyway
  1371. swNumRead = (-swNumRead);
  1372. fGotFirstLine=TRUE;
  1373. //
  1374. // +++ HACK:
  1375. // We add everything upto the first NULL of each
  1376. // line of reply to bEntireReply, for the specific
  1377. // case of fMultiLine==uMULTILINE_SAVEENTIRE
  1378. // This is so we save things like:
  1379. // \r\nDATA\r\n\r\nCONNECT 12000\r\n
  1380. //
  1381. if(pTG->Comm.fEnableHandoff && fMultiLine==uMULTILINE_SAVEENTIRE
  1382. && uPos<sizeof(pTG->FComModem.bEntireReply))
  1383. {
  1384. UINT cb;
  1385. bReply[REPLYBUFSIZE-1]=0;
  1386. cb = _fstrlen(bReply);
  1387. if ((cb+1)> (sizeof(pTG->FComModem.bEntireReply)-uPos))
  1388. {
  1389. DebugPrintEx(DEBUG_WRN, "bEntireReply: out of space");
  1390. cb=sizeof(pTG->FComModem.bEntireReply)-uPos;
  1391. if (cb) cb--;
  1392. }
  1393. _fmemcpy((LPB)pTG->FComModem.bEntireReply+uPos, (LPB)bReply, cb);
  1394. uPos+=cb;
  1395. pTG->FComModem.bEntireReply[uPos]=0;
  1396. }
  1397. PSSLogEntry(PSS_MSG, 2, "recv: \"%s\"", bReply);
  1398. for(bReply[REPLYBUFSIZE-1]=0, j=1; j<=uwWantCount; j++)
  1399. {
  1400. if(rgcbszWant[j] && (strstr(bReply, rgcbszWant[j]) != NULL))
  1401. {
  1402. uwRet = j;
  1403. goto end;
  1404. }
  1405. }
  1406. if(!fMultiLine)
  1407. break;
  1408. // Got something unknown
  1409. // Retry command and response until timeout
  1410. // We reach here it IFF we got a non blank reply, but it wasn't what
  1411. // we wanted. Squirrel teh first line away somewhere so that we can
  1412. // retrieve is later. We use this hack to get multi-line informational
  1413. // responses to things like +FTH=? Very important to ensure that
  1414. // blank-line replies don't get recorded here. (They may override
  1415. // the preceding line that we need!).
  1416. if( (pTG->FComModem.bLastReply[0] == 0) ||
  1417. ( ! _fstrcmp(pTG->FComModem.bLastReply, cbszRING) ) )
  1418. {
  1419. // copy only if _first_ response line
  1420. _fmemcpy((LPB)pTG->FComModem.bLastReply, (LPB)bReply, REPLYBUFSIZE);
  1421. }
  1422. // copies whole of bReply which includes zero-termination put
  1423. // there by FComFilterReadLine
  1424. DebugPrintEx( DEBUG_MSG,
  1425. "Saved line (%s)",
  1426. (LPSTR)(&(pTG->FComModem.bLastReply)));
  1427. }
  1428. }
  1429. while(checkTimeOut(pTG, lpto));
  1430. if(fGotFirstLine)
  1431. continue;
  1432. DebugPrintEx(DEBUG_WRN,"Weird!! got timeout in iiModemDialog loop");
  1433. timeout:
  1434. PSSLogEntryStrings(PSS_WRN, 2, &rgcbszWant[1], uwWantCount,
  1435. "failed to receive expected response: ");
  1436. // Need to send anychar to abort the previous command.
  1437. // use random 120ms timeout -- too short. upped to 250
  1438. // send \rAT\r
  1439. // no need for pause--we just timed out!!
  1440. PSSLogEntry(PSS_MSG, 2, "send: \"AT\"");
  1441. FComFlush(pTG); // flush first--don't wnat some old garbage result
  1442. FComDirectSyncWriteFast(pTG, "\rAT", 3);
  1443. FComFlushInput(pTG); // flush input again
  1444. FComDirectAsyncWrite(pTG, "\r", 1);
  1445. startTimeOut(pTG, lpto0, ABORT_TIMEOUT);
  1446. do
  1447. {
  1448. swNumRead = FComFilterReadLine(pTG, bReply, REPLYBUFSIZE-1, lpto0);
  1449. }
  1450. while(swNumRead==2 && bReply[0]=='\r'&& bReply[1]=='\n');
  1451. // While we get a blank line. Get another.
  1452. bReply[REPLYBUFSIZE-1] = 0;
  1453. if (bReply[0])
  1454. {
  1455. PSSLogEntry(PSS_MSG, 2, "recv: \"%s\"", bReply);
  1456. }
  1457. if(bReply[0] && strstr(bReply, cbszOK)==NULL)
  1458. DebugPrintEx( DEBUG_ERR,
  1459. "Anykey abort reply not OK. Got <<%s>>",
  1460. (LPSTR)bReply);
  1461. // Need Flush here, because \rAT\r will often get us
  1462. // a cr-lf-OK-cr-lf-cr-lfOK-cr-lf response. If we send
  1463. // just a \r, sometimes we may get nothing
  1464. // FComFlushInput();
  1465. FComFlush(pTG);
  1466. }
  1467. error:
  1468. DebugPrintEx( DEBUG_WRN,
  1469. "(%s) --> (%d)(%s, etc) Failed",
  1470. (LPSTR)(szSend?szSend:"null"),
  1471. uwWantCount,
  1472. (LPSTR)rgcbszWant[1]);
  1473. pTG->FComStatus.fInDialog = 0;
  1474. return 0;
  1475. end:
  1476. DebugPrintEx(DEBUG_MSG,"GOT IT %d (%s)", uwRet, (LPSTR)(rgcbszWant[uwRet]));
  1477. pTG->FComStatus.fInDialog = 0;
  1478. return uwRet;
  1479. }
  1480. void InitMonitorLogging(PThrdGlbl pTG)
  1481. {
  1482. DEBUG_FUNCTION_NAME(("InitMonitorLogging"));
  1483. pTG->Comm.fEnableHandoff=1;
  1484. if (pTG->Comm.fEnableHandoff)
  1485. {
  1486. DebugPrintEx(DEBUG_WRN,"ADAPTIVE ANSWER ENABLED");
  1487. }
  1488. }
  1489. // RSL was 60 000
  1490. #define AA_ANSWER_TIMEOUT 40000
  1491. USHORT iModemGetAdaptiveResp(PThrdGlbl pTG)
  1492. {
  1493. USHORT uRet=CONNECT_ERROR;
  1494. BOOL fGotOK=FALSE;
  1495. BOOL fGotData=FALSE;
  1496. BOOL fGotFax=FALSE;
  1497. LONG lRet;
  1498. char Command[400];
  1499. DEBUG_FUNCTION_NAME(("iModemGetAdaptiveResp"));
  1500. pTG->Comm.fDataCall = FALSE;
  1501. //
  1502. // handle Adaptive Answer
  1503. // should get FAX/DATA response
  1504. //
  1505. switch( iiModemDialog( pTG,
  1506. 0,
  1507. 0,
  1508. AA_ANSWER_TIMEOUT,
  1509. uMULTILINE_SAVEENTIRE,
  1510. 1,
  1511. TRUE,
  1512. pTG->ModemResponseFaxDetect,
  1513. pTG->ModemResponseDataDetect,
  1514. cbszCONNECT,
  1515. cbszOK,
  1516. (CBPSTR)NULL))
  1517. {
  1518. case 1:
  1519. fGotFax = 1;
  1520. DebugPrintEx(DEBUG_MSG,"AdaptiveAnswer: got FAX response");
  1521. break;
  1522. case 2:
  1523. fGotData = 1;
  1524. DebugPrintEx(DEBUG_MSG,"AdaptiveAnswer: got DATA response");
  1525. break;
  1526. case 3:
  1527. DebugPrintEx(DEBUG_ERR,"AnswerPhone: Can't get CONNECT before FAX/DATA");
  1528. pTG->Comm.fDataCall = FALSE;
  1529. uRet = CONNECT_ERROR;
  1530. goto end;
  1531. case 4:
  1532. DebugPrintEx(DEBUG_ERR,"AnswerPhone: Can't get OK before FAX/DATA");
  1533. pTG->Comm.fDataCall = FALSE;
  1534. uRet = CONNECT_ERROR;
  1535. goto end;
  1536. default:
  1537. case 0:
  1538. DebugPrintEx(DEBUG_ERR,"AnswerPhone: Can't get default before FAX/DATA");
  1539. pTG->Comm.fDataCall = FALSE;
  1540. uRet = CONNECT_ERROR;
  1541. goto end;
  1542. }
  1543. // here we may have to change the serial speed and send some cmds (such as ATO-go online)
  1544. if (fGotFax)
  1545. {
  1546. if (pTG->SerialSpeedFaxDetect)
  1547. {
  1548. FComSetBaudRate(pTG, pTG->SerialSpeedFaxDetect);
  1549. }
  1550. if (pTG->HostCommandFaxDetect)
  1551. {
  1552. strcpy (Command, pTG->HostCommandFaxDetect );
  1553. FComFlushOutput(pTG);
  1554. FComDirectAsyncWrite(pTG, (LPSTR) Command, (USHORT) strlen(Command) );
  1555. }
  1556. }
  1557. else if (fGotData)
  1558. {
  1559. if (pTG->SerialSpeedDataDetect)
  1560. {
  1561. FComSetBaudRate(pTG, pTG->SerialSpeedDataDetect);
  1562. }
  1563. if (pTG->HostCommandDataDetect)
  1564. {
  1565. strcpy (Command, pTG->HostCommandDataDetect );
  1566. FComFlushOutput(pTG);
  1567. FComDirectAsyncWrite(pTG, (LPSTR) Command, (USHORT) strlen(Command) );
  1568. }
  1569. }
  1570. else
  1571. {
  1572. DebugPrintEx(DEBUG_ERR,"AnswerPhone: LOGICAL PGM ERROR");
  1573. pTG->Comm.fDataCall = FALSE;
  1574. uRet = CONNECT_ERROR;
  1575. goto end;
  1576. }
  1577. // wait for connect now.
  1578. switch( iiModemDialog( pTG,
  1579. 0,
  1580. 0,
  1581. AA_ANSWER_TIMEOUT,
  1582. uMULTILINE_SAVEENTIRE,
  1583. 1,
  1584. TRUE,
  1585. (fGotFax) ? pTG->ModemResponseFaxConnect : pTG->ModemResponseDataConnect,
  1586. cbszCONNECT,
  1587. cbszOK,
  1588. (CBPSTR)NULL))
  1589. {
  1590. case 1:
  1591. if (fGotFax)
  1592. {
  1593. uRet=CONNECT_OK;
  1594. goto end;
  1595. }
  1596. else
  1597. {
  1598. goto lDetectDataCall;
  1599. }
  1600. case 2:
  1601. if (fGotFax)
  1602. {
  1603. uRet=CONNECT_OK;
  1604. goto end;
  1605. }
  1606. else
  1607. {
  1608. goto lDetectDataCall;
  1609. }
  1610. case 3:
  1611. DebugPrintEx(DEBUG_ERR,"AnswerPhone: Can't get OK after FAX/DATA");
  1612. pTG->Comm.fDataCall = FALSE;
  1613. uRet = CONNECT_ERROR;
  1614. goto end;
  1615. default:
  1616. case 0:
  1617. DebugPrintEx(DEBUG_ERR,"AnswerPhone: Can't get default after FAX/DATA");
  1618. pTG->Comm.fDataCall = FALSE;
  1619. uRet = CONNECT_ERROR;
  1620. goto end;
  1621. }
  1622. lDetectDataCall:
  1623. // Now we've got to fake out modem and fcom into thinking that
  1624. // the phone is off hook when in fact it isn't.
  1625. pTG->Comm.fDataCall = TRUE;
  1626. uRet = CONNECT_WRONGMODE_DATAMODEM;
  1627. //
  1628. // New TAPI: Have to switch out of passtrough before handing off the call
  1629. //
  1630. DebugPrintEx(DEBUG_MSG,"AdaptiveAnswer: lineSetCallParams called");
  1631. if (!itapi_async_setup(pTG))
  1632. {
  1633. DebugPrintEx(DEBUG_ERR,"AdaptiveAnswer: itapi_async_setup failed");
  1634. pTG->Comm.fDataCall = FALSE;
  1635. uRet = CONNECT_ERROR;
  1636. goto end;
  1637. }
  1638. lRet = lineSetCallParams(pTG->CallHandle,
  1639. LINEBEARERMODE_VOICE,
  1640. 0,
  1641. 0xffffffff,
  1642. NULL);
  1643. if (lRet < 0)
  1644. {
  1645. DebugPrintEx(DEBUG_ERR, "AdaptiveAnswer: lineSetCallParams failed");
  1646. pTG->fFatalErrorWasSignaled = 1;
  1647. SignalStatusChange(pTG, FS_FATAL_ERROR);
  1648. pTG->Comm.fDataCall = FALSE;
  1649. uRet = CONNECT_ERROR;
  1650. goto end;
  1651. }
  1652. else
  1653. {
  1654. DebugPrintEx( DEBUG_MSG,
  1655. "AdaptiveAnswer: lineSetCallParams returns ID %ld",
  1656. (long) lRet);
  1657. }
  1658. if(!itapi_async_wait(pTG, (DWORD)lRet, (LPDWORD)&lRet, NULL, ASYNC_TIMEOUT))
  1659. {
  1660. DebugPrintEx(DEBUG_ERR, "AdaptiveAnswer: itapi_async_wait failed");
  1661. pTG->fFatalErrorWasSignaled = 1;
  1662. SignalStatusChange(pTG, FS_FATAL_ERROR);
  1663. pTG->Comm.fDataCall = FALSE;
  1664. uRet = CONNECT_ERROR;
  1665. goto end;
  1666. }
  1667. pTG->fFatalErrorWasSignaled = 1;
  1668. SignalStatusChange(pTG, FS_NOT_FAX_CALL);
  1669. end:
  1670. return uRet;
  1671. }