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

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