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.

2387 lines
86 KiB

  1. /*++
  2. Copyright (c) 1997 Microsoft Corporation
  3. Module Name:
  4. class2.c
  5. Abstract:
  6. This is the main source for Class2 specific functions for fax-modem T.30 driver
  7. Author:
  8. Source base was originated by Win95 At Work Fax package.
  9. RafaelL - July 1997 - port to NT
  10. Revision History:
  11. --*/
  12. #define USE_DEBUG_CONTEXT DEBUG_CONTEXT_T30_CLASS2
  13. #include "prep.h"
  14. #include "efaxcb.h"
  15. #include "tiff.h"
  16. #include "glbproto.h"
  17. #include "t30gl.h"
  18. #include "cl2spec.h"
  19. #include "psslog.h"
  20. #define FILE_ID FILE_ID_CLASS2
  21. #include "pssframe.h"
  22. #define STRSAFE_NO_DEPRECATE
  23. #include <strsafe.h>
  24. extern WORD CodeToBPS[16];
  25. extern UWORD rguwClass2Speeds[];
  26. extern DWORD PageWidthInPixelsFromDCS[];
  27. // Here is the table we are using so far for manufacturer specific stuff
  28. MFRSPEC Class2ModemTable[] = {
  29. { "Practical Peripherals", "PM14400FXPPM", 1, 2, "", FALSE, FALSE, FALSE, FALSE },
  30. { "Practical Peripherals", "PM9600FXMT", 1, 2, "", FALSE, FALSE, FALSE ,FALSE },
  31. { "Everex Systems", "Everfax 24/96E", 0, 2, "", FALSE, FALSE, FALSE, FALSE },
  32. { "ROCKWELL", "V.32AC", 1, 2, "", FALSE, FALSE, FALSE, FALSE },
  33. { "ROCKWELL", "RC9624AC", 1, 2, "", FALSE, FALSE, FALSE, FALSE },
  34. { "Multi-Tech", "MT1432BA", 0, 0, "", FALSE, FALSE, FALSE, FALSE },
  35. { "SIERRA", "SX196", 1, 0, "", TRUE, FALSE, FALSE, FALSE },
  36. { "EXAR", "ROCKWELL 144DP", 1, 0, "", FALSE, TRUE, FALSE, FALSE },
  37. { "ELSA", "MicroLink 2460TL", 1, 0, "", FALSE, TRUE, FALSE, FALSE },
  38. { "GVC", "ROCKWELL 144DP", 1, 0, "", FALSE, TRUE, TRUE , FALSE }, // Intel144Ex
  39. { "ADC", "SL144V32", 1, 0, "", FALSE, TRUE, FALSE, FALSE },
  40. { "UMC", "", 1, 0, "", FALSE, TRUE, FALSE ,FALSE },
  41. { "NetComm", "", 1, 0, "", FALSE, TRUE, FALSE, FALSE },
  42. { "HALCYON", "Bit Blitzer", 0, 0, "", FALSE, FALSE, FALSE, FALSE },
  43. { "", "", 1, 0, "", FALSE, FALSE, FALSE, FALSE }
  44. };
  45. void
  46. Class2Init(
  47. PThrdGlbl pTG
  48. )
  49. {
  50. pTG->lpCmdTab = 0;
  51. pTG->Class2bDLEETX[0] = DLE;
  52. pTG->Class2bDLEETX[1] = ETX;
  53. pTG->Class2bDLEETX[2] = 0;
  54. sprintf( pTG->cbszFDT, "AT+FDT\r" );
  55. sprintf( pTG->cbszFDR, "AT+FDR\r" );
  56. sprintf( pTG->cbszFPTS, "AT+FPTS=%%d\r" );
  57. sprintf( pTG->cbszFCR, "AT+FCR=1\r" );
  58. sprintf( pTG->cbszFCQ, "AT+FCQ=0\r" );
  59. sprintf( pTG->cbszFBUG, "AT+FBUG=0\r" );
  60. sprintf( pTG->cbszSET_FBOR, "AT+FBOR=%%d\r" );
  61. // DCC - set High Res, Huffman, no ECM/BFT, default all others.
  62. sprintf( pTG->cbszFDCC_ALL, "AT+FDCC=%%d,%%d,,,0,0,0,\r" );
  63. sprintf( pTG->cbszFDCC_RECV_ALL, "AT+FDCC=1,%%d,0,2,0,0,0,\r" );
  64. sprintf( pTG->cbszFDIS_RECV_ALL, "AT+FDIS=1,%%d,0,2,0,0,0,\r" );
  65. sprintf( pTG->cbszFDCC_RES, "AT+FDCC=%%d\r" );
  66. sprintf( pTG->cbszFDCC_RECV_RES, "AT+FDCC=1\r" );
  67. sprintf( pTG->cbszFDCC_BAUD, "AT+FDCC=%%d,%%d\r" );
  68. sprintf( pTG->cbszFDIS_BAUD, "AT+FDIS=1,%%d\r" );
  69. sprintf( pTG->cbszFDIS_IS, "AT+FDIS?\r" );
  70. sprintf( pTG->cbszFDIS_NOQ_IS, "AT+FDIS\r" );
  71. sprintf( pTG->cbszFDCC_IS, "AT+FDCC?\r" );
  72. sprintf( pTG->cbszFDIS_STRING, "+FDIS" );
  73. sprintf( pTG->cbszFDIS, "AT+FDIS=%%1d,%%1d,%%1d,%%1d,%%1d,0,0,0\r" );
  74. sprintf( pTG->cbszONE, "1" );
  75. sprintf( pTG->cbszCLASS2_FMFR, "AT+FMFR?\r" );
  76. sprintf( pTG->cbszCLASS2_FMDL, "AT+FMDL?\r" );
  77. sprintf( pTG->cbszFDT_CONNECT, "CONNECT" );
  78. sprintf( pTG->cbszFCON, "+FCON" );
  79. sprintf( pTG->cbszFLID, "AT+FLID=\"%%s\"\r" );
  80. sprintf( pTG->cbszENDPAGE, "AT+FET=0\r" );
  81. sprintf( pTG->cbszENDMESSAGE, "AT+FET=2\r" );
  82. sprintf( pTG->cbszCLASS2_ATTEN, "AT\r" );
  83. sprintf( pTG->cbszATA, "ATA\r" );
  84. sprintf( pTG->cbszCLASS2_HANGUP, "ATH0\r" );
  85. sprintf( pTG->cbszCLASS2_CALLDONE, "ATS0=0\r" );
  86. sprintf( pTG->cbszCLASS2_ABORT, "AT+FK\r" );
  87. sprintf( pTG->cbszCLASS2_DIAL, "ATD%%c %%s\r" );
  88. sprintf( pTG->cbszCLASS2_NODIALTONE, "NO DIAL" );
  89. sprintf( pTG->cbszCLASS2_BUSY, "BUSY" );
  90. sprintf( pTG->cbszCLASS2_NOANSWER, "NO ANSWER" );
  91. sprintf( pTG->cbszCLASS2_OK, "OK" );
  92. sprintf( pTG->cbszCLASS2_FHNG, "+FHNG" );
  93. sprintf( pTG->cbszCLASS2_ERROR, "ERROR" );
  94. sprintf( pTG->cbszCLASS2_NOCARRIER, "NO CARRIER" );
  95. Class2SetProtParams(pTG, &pTG->Inst.ProtParams);
  96. }
  97. /*++
  98. Routine Description:
  99. Issue "AT+FDIS=?" command, parse response into pTG->DISPcb
  100. Return Value:
  101. TRUE - success, FALSE - failure
  102. --*/
  103. BOOL Class2GetDefaultFDIS(PThrdGlbl pTG)
  104. {
  105. UWORD uwRet=0;
  106. BYTE bTempBuf[200+RESPONSE_BUF_SIZE];
  107. LPBYTE lpbyte;
  108. HRESULT hr;
  109. DEBUG_FUNCTION_NAME("Class2GetDefaultFDIS");
  110. // Find out what the default DIS is
  111. if (!pTG->CurrentMFRSpec.bIsExar)
  112. {
  113. if (!(uwRet=Class2iModemDialog( pTG,
  114. pTG->cbszFDIS_IS,
  115. (UWORD)(strlen(pTG->cbszFDIS_IS)),
  116. LOCALCOMMAND_TIMEOUT,
  117. 0,
  118. TRUE,
  119. pTG->cbszCLASS2_OK,
  120. pTG->cbszCLASS2_ERROR,
  121. (C2PSTR) NULL)))
  122. {
  123. DebugPrintEx(DEBUG_WRN,"FDIS failed");
  124. // ignore
  125. }
  126. }
  127. // See if the reply was ERROR or timeout, if so try a different command
  128. // Exar modems, for example, don't take AT+FDIS?
  129. if ((uwRet==2)||(uwRet==0)||pTG->CurrentMFRSpec.bIsExar)
  130. {
  131. // FDIS did not work!!! Try FDCC?
  132. if (!(uwRet=Class2iModemDialog( pTG,
  133. pTG->cbszFDCC_IS,
  134. (UWORD)(strlen(pTG->cbszFDCC_IS)),
  135. LOCALCOMMAND_TIMEOUT,
  136. 0,
  137. TRUE,
  138. pTG->cbszCLASS2_OK,
  139. pTG->cbszCLASS2_ERROR,
  140. (C2PSTR) NULL)))
  141. {
  142. DebugPrintEx(DEBUG_WRN,"FDCC_IS failed");
  143. // Ignore
  144. }
  145. if ((uwRet==2)||(uwRet==0))
  146. {
  147. // The FDCC failed - maybe it is an Exar that likes FDIS?
  148. // try that
  149. if (!(uwRet=Class2iModemDialog( pTG,
  150. pTG->cbszFDIS_IS,
  151. (UWORD)(strlen(pTG->cbszFDIS_IS)),
  152. LOCALCOMMAND_TIMEOUT,
  153. 0,
  154. TRUE,
  155. pTG->cbszCLASS2_OK,
  156. pTG->cbszCLASS2_ERROR,
  157. (C2PSTR) NULL)))
  158. {
  159. DebugPrintEx(DEBUG_WRN,"FDIS_IS failed");
  160. //ignore
  161. }
  162. // Maybe it is the Class 2 modem referred to in
  163. // Elliot bug #1238 that wants FDIS without a
  164. // question mark
  165. if ((uwRet==2)||(uwRet==0))
  166. {
  167. if (!(uwRet=Class2iModemDialog( pTG,
  168. pTG->cbszFDIS_NOQ_IS,
  169. (UWORD)(strlen(pTG->cbszFDIS_NOQ_IS)),
  170. LOCALCOMMAND_TIMEOUT,
  171. 0,
  172. TRUE,
  173. pTG->cbszCLASS2_OK,
  174. (C2PSTR) NULL)))
  175. {
  176. // No FDIS, FDCC worked - quit!
  177. DebugPrintEx(DEBUG_ERR,"No FDIS? or FDCC? worked");
  178. return FALSE;
  179. }
  180. }
  181. }
  182. // If the first character in the reply before a number
  183. // is a ',', insert a '1' for normal & fine res (Exar hack)
  184. for (lpbyte = pTG->lpbResponseBuf2; *lpbyte != '\0'; lpbyte++)
  185. {
  186. if (*lpbyte == ',')
  187. {
  188. // found a leading comma
  189. hr = StringCchPrintf(bTempBuf, ARR_SIZE(bTempBuf), "%s%s", pTG->cbszONE, lpbyte);
  190. if (FAILED(hr))
  191. {
  192. DebugPrintEx(DEBUG_WRN,"StringCchPrintf failed (ec=0x%08X)",hr);
  193. }
  194. else
  195. {
  196. hr = StringCchCopy(lpbyte, ARR_SIZE(pTG->lpbResponseBuf2) - (lpbyte-pTG->lpbResponseBuf2), bTempBuf);
  197. if (FAILED(hr))
  198. {
  199. DebugPrintEx(DEBUG_WRN,"StringCchCopy failed (ec=0x%08X)",hr);
  200. }
  201. }
  202. DebugPrintEx(DEBUG_MSG, "Leading comma in DCC string =%s", (LPSTR)&pTG->lpbResponseBuf2);
  203. }
  204. if ((*lpbyte>='0')&&(*lpbyte<='9'))
  205. {
  206. break;
  207. }
  208. }
  209. }
  210. // If the repsonse was just a number string without "+FDIS" in front
  211. // of it, add the +FDIS. Some modem reply with it, some do not. The
  212. // general parsing algorithm used below in Class2ResponseAction needs
  213. // to know the command that the numbers refer to.
  214. if ( pTG->lpbResponseBuf2[0] != '\0' &&
  215. (strstr((LPSTR)pTG->lpbResponseBuf2, (LPSTR)pTG->cbszFDIS_STRING)==NULL))
  216. {
  217. // did not get the FDIS in the response!
  218. hr = StringCchPrintf(bTempBuf, ARR_SIZE(bTempBuf), "%s: %s", pTG->cbszFDIS_STRING, pTG->lpbResponseBuf2);
  219. if (FAILED(hr))
  220. {
  221. DebugPrintEx(DEBUG_WRN,"StringCchPrintf failed (ec=0x%08X)",hr);
  222. }
  223. else
  224. {
  225. hr = StringCchCopy(pTG->lpbResponseBuf2, ARR_SIZE(pTG->lpbResponseBuf2), bTempBuf);
  226. if (FAILED(hr))
  227. {
  228. DebugPrintEx(DEBUG_WRN,"StringCchCopy failed (ec=0x%08X)",hr);
  229. }
  230. }
  231. }
  232. DebugPrintEx(DEBUG_MSG, "Received %s from FDIS", (LPSTR)(&(pTG->lpbResponseBuf2)));
  233. // Process default DIS to see if we have to send a DCC to change
  234. // it. Some modems react badly to just sending a DCC with ",,,"
  235. // so we can't rely on the modem keeping DIS parameters unchanged
  236. // after a DCC like that. We'll use the FDISResponse routine to load
  237. // the default DIS values into a PCB structure
  238. if (Class2ResponseAction(pTG, (LPPCB) &pTG->DISPcb) == FALSE)
  239. {
  240. DebugPrintEx(DEBUG_WRN,"Failed to process FDIS Response");
  241. return FALSE;
  242. }
  243. return TRUE;
  244. }
  245. BOOL T30Cl2Tx(PThrdGlbl pTG,LPSTR szPhone)
  246. {
  247. USHORT uRet1, uRet2;
  248. BYTE bBuf[200];
  249. UWORD Encoding, Res, PageWidth, PageLength, uwLen;
  250. BYTE bIDBuf[200+max(MAXTOTALIDLEN,20)+4];
  251. CHAR szTSI[max(MAXTOTALIDLEN,20)+4] = {0};
  252. BOOL fBaudChanged;
  253. BOOL RetCode;
  254. DEBUG_FUNCTION_NAME("T30Cl2Tx");
  255. uRet2 = 0;
  256. if (!(pTG->lpCmdTab = iModemGetCmdTabPtr(pTG)))
  257. {
  258. DebugPrintEx(DEBUG_ERR,"iModemGetCmdTabPtr failed");
  259. uRet1 = T30_CALLFAIL;
  260. pTG->fFatalErrorWasSignaled = 1;
  261. SignalStatusChange(pTG, FS_FATAL_ERROR);
  262. RetCode = FALSE;
  263. goto done;
  264. }
  265. // first get SEND_CAPS if possible.
  266. if (!Class2GetBC(pTG, SEND_CAPS)) // get send caps
  267. {
  268. DebugPrintEx(DEBUG_ERR,"Class2GetBC failed");
  269. uRet1 = T30_CALLFAIL;
  270. pTG->fFatalErrorWasSignaled = 1;
  271. SignalStatusChange(pTG, FS_FATAL_ERROR);
  272. RetCode = FALSE;
  273. goto done;
  274. }
  275. // Go to Class2
  276. if (!iModemGoClass(pTG, 2))
  277. {
  278. DebugPrintEx(DEBUG_ERR,"Failed to Go to Class 2");
  279. uRet1 = T30_CALLFAIL;
  280. pTG->fFatalErrorWasSignaled = 1;
  281. SignalStatusChange(pTG, FS_FATAL_ERROR);
  282. RetCode = FALSE;
  283. goto done;
  284. }
  285. // Begin by checking for manufacturer and ATI code.
  286. // Look this up against the modem specific table we
  287. // have and set up the send strings needed for
  288. // this modem.
  289. if (!Class2GetModemMaker(pTG))
  290. {
  291. DebugPrintEx(DEBUG_WRN,"Call to GetModemMaker failed");
  292. // Ignore failure!!!
  293. }
  294. // set manufacturer specific strings
  295. Class2SetMFRSpecific(pTG);
  296. // Get the capabilities of the software. I am only using this
  297. // right now for the TSI field (below where I send +FLID).
  298. // Really, this should also be used instead of the hardcoded DIS
  299. // values below.
  300. // ALL COMMANDS LOOK FOR MULTILINE RESPONSES WHILE MODEM IS ONHOOK.
  301. // A "RING" COULD APPEAR AT ANY TIME!
  302. Class2SetDIS_DCSParams( pTG,
  303. SEND_CAPS,
  304. (LPUWORD)&Encoding,
  305. (LPUWORD)&Res,
  306. (LPUWORD)&PageWidth,
  307. (LPUWORD)&PageLength,
  308. (LPSTR) szTSI,
  309. sizeof(szTSI)/sizeof(szTSI[0]));
  310. bIDBuf[0] = '\0';
  311. uwLen = (UWORD)wsprintf(bIDBuf, pTG->cbszFLID, (LPSTR)szTSI);
  312. if (!Class2iModemDialog(pTG,
  313. bIDBuf,
  314. uwLen,
  315. LOCALCOMMAND_TIMEOUT,
  316. 0,
  317. TRUE,
  318. pTG->cbszCLASS2_OK,
  319. pTG->cbszCLASS2_ERROR,
  320. (C2PSTR) NULL))
  321. {
  322. DebugPrintEx(DEBUG_WRN,"Local ID failed");
  323. // ignore failure
  324. }
  325. // Turn off Bug mode
  326. if (!Class2iModemDialog(pTG,
  327. pTG->cbszFBUG,
  328. (UWORD)(strlen(pTG->cbszFBUG)),
  329. LOCALCOMMAND_TIMEOUT,
  330. 0,
  331. TRUE,
  332. pTG->cbszCLASS2_OK,
  333. pTG->cbszCLASS2_ERROR,
  334. (C2PSTR) NULL))
  335. {
  336. DebugPrintEx(DEBUG_WRN,"FBUG failed");
  337. // Ignore FBUG failure!!!
  338. }
  339. // Find out what the default DIS is
  340. if (!Class2GetDefaultFDIS(pTG))
  341. {
  342. DebugPrintEx(DEBUG_ERR, "Class2GetDefaultFDIS failed");
  343. pTG->fFatalErrorWasSignaled = 1;
  344. SignalStatusChange(pTG, FS_FATAL_ERROR);
  345. RetCode = FALSE;
  346. goto done;
  347. }
  348. fBaudChanged = FALSE;
  349. // See if we have to change the baud rate to a lower value.
  350. // This only happens if the user set an ini string constraining
  351. // the high end speed or if the user turned off V.17 for sending
  352. // Check the V.17 inhibit and lower baud if necessary
  353. if ((pTG->DISPcb.Baud>3)&&(!pTG->ProtParams2.fEnableV17Send))
  354. {
  355. DebugPrintEx(DEBUG_MSG, "Lowering baud from %d for V.17 inihibit", CodeToBPS[pTG->DISPcb.Baud]);
  356. pTG->DISPcb.Baud = 3; //9600 won't use V.17
  357. fBaudChanged = TRUE;
  358. }
  359. // Now see if the high end baud rate has been constrained
  360. if ( (pTG->ProtParams2.HighestSendSpeed != 0) &&
  361. (CodeToBPS[pTG->DISPcb.Baud] > (WORD)pTG->ProtParams2.HighestSendSpeed))
  362. {
  363. DebugPrintEx( DEBUG_MSG,
  364. "Have to lower baud from %d to %d",
  365. CodeToBPS[pTG->DISPcb.Baud],
  366. pTG->ProtParams2.HighestSendSpeed);
  367. fBaudChanged = TRUE;
  368. switch (pTG->ProtParams2.HighestSendSpeed)
  369. {
  370. case 2400: pTG->DISPcb.Baud = 0;
  371. break;
  372. case 4800: pTG->DISPcb.Baud = 1;
  373. break;
  374. case 7200: pTG->DISPcb.Baud = 2;
  375. break;
  376. case 9600: pTG->DISPcb.Baud = 3;
  377. break;
  378. case 12000: pTG->DISPcb.Baud = 4;
  379. break;
  380. default: DebugPrintEx(DEBUG_MSG,"Bad HighestSpeed");
  381. uRet1 = T30_CALLFAIL;
  382. pTG->fFatalErrorWasSignaled = 1;
  383. SignalStatusChange(pTG, FS_FATAL_ERROR);
  384. RetCode = FALSE;
  385. goto done;
  386. break;
  387. }
  388. }
  389. // Now, look and see if any of the values in the DIS are "bad"
  390. // That is, make sure we can send high res and we are not
  391. // claiming that we are sending MR or MMR. Also, see if we changed
  392. // the baud rate.
  393. if ((((pTG->DISPcb.Resolution == AWRES_mm080_038 ) && (Res==0)) ||
  394. ((pTG->DISPcb.Resolution == (AWRES_mm080_077|AWRES_mm080_038)) && (Res==1)) ) &&
  395. (pTG->DISPcb.Encoding == MH_DATA) &&
  396. (!fBaudChanged) )
  397. {
  398. //Do nothing - leave DIS alone!
  399. DebugPrintEx(DEBUG_MSG,"no need to change DIS");
  400. }
  401. else
  402. {
  403. // Send DCC command to the modem to set it up
  404. // Do the minimum necessary - only set resoultion if possible
  405. // (Again, this is because some modems don't like FDCC).
  406. if ((pTG->DISPcb.Encoding==MH_DATA)&&(!fBaudChanged))
  407. {
  408. uwLen=(UWORD)wsprintf((LPSTR)bBuf, pTG->cbszFDCC_RES, Res);
  409. if (!Class2iModemDialog(pTG,
  410. bBuf,
  411. (UWORD)(strlen(bBuf)),
  412. LOCALCOMMAND_TIMEOUT,
  413. 0,
  414. TRUE,
  415. pTG->cbszCLASS2_OK,
  416. (C2PSTR) NULL))
  417. {
  418. DebugPrintEx(DEBUG_ERR,"FDCC_RES failed");
  419. uRet1 = T30_CALLFAIL;
  420. pTG->fFatalErrorWasSignaled = 1;
  421. SignalStatusChange(pTG, FS_FATAL_ERROR);
  422. RetCode = FALSE;
  423. goto done;
  424. }
  425. }
  426. else if ( (pTG->DISPcb.Encoding == MH_DATA) && (fBaudChanged) )
  427. {
  428. // Changed the baud rate, but Encoding is OK.
  429. uwLen=(UWORD)wsprintf((LPSTR)bBuf, pTG->cbszFDCC_BAUD, Res, pTG->DISPcb.Baud);
  430. if (!Class2iModemDialog(pTG,
  431. bBuf,
  432. uwLen,
  433. LOCALCOMMAND_TIMEOUT,
  434. 0,
  435. TRUE,
  436. pTG->cbszCLASS2_OK,
  437. (C2PSTR) NULL))
  438. {
  439. DebugPrintEx(DEBUG_ERR,"FDCC_BAUD failed");
  440. uRet1 = T30_CALLFAIL;
  441. pTG->fFatalErrorWasSignaled = 1;
  442. SignalStatusChange(pTG, FS_FATAL_ERROR);
  443. RetCode = FALSE;
  444. goto done;
  445. }
  446. }
  447. else // the encoding format has changed
  448. {
  449. uwLen=(UWORD)wsprintf((LPSTR)bBuf, pTG->cbszFDCC_ALL, Res, pTG->DISPcb.Baud);
  450. if (!Class2iModemDialog( pTG,
  451. bBuf,
  452. uwLen,
  453. LOCALCOMMAND_TIMEOUT,
  454. 0,
  455. TRUE,
  456. pTG->cbszCLASS2_OK,
  457. (C2PSTR) NULL))
  458. {
  459. DebugPrintEx(DEBUG_ERR,"FDCC_ALL failed");
  460. uRet1 = T30_CALLFAIL;
  461. pTG->fFatalErrorWasSignaled = 1;
  462. SignalStatusChange(pTG, FS_FATAL_ERROR);
  463. RetCode = FALSE;
  464. goto done;
  465. }
  466. }
  467. }
  468. // Do BOR based on the value from the modem table set in
  469. // Class2SetMFRSpecific
  470. uwLen = (UWORD)wsprintf(bBuf, pTG->cbszSET_FBOR, pTG->CurrentMFRSpec.iSendBOR);
  471. if (!Class2iModemDialog(pTG,
  472. bBuf,
  473. uwLen,
  474. LOCALCOMMAND_TIMEOUT,
  475. 0,
  476. TRUE,
  477. pTG->cbszCLASS2_OK,
  478. pTG->cbszCLASS2_ERROR,
  479. (C2PSTR) NULL))
  480. {
  481. DebugPrintEx(DEBUG_ERR,"FBOR failed");
  482. // Ignore BOR failure!!!
  483. }
  484. // Dial the number
  485. // have to call hangup on every path out of here
  486. // after Dial is called. If Dial fails, it calls Hangup
  487. // if it succeeds we have to call Hangup when we're done
  488. PSSLogEntry(PSS_MSG, 0, "Phase A - Call establishment");
  489. SignalStatusChange(pTG, FS_DIALING);
  490. PSSLogEntry(PSS_MSG, 1, "Dialing...");
  491. if ((uRet2=Class2Dial(pTG, szPhone))!=CONNECT_OK)
  492. {
  493. DebugPrintEx(DEBUG_ERR,"Class2Dial failed");
  494. uRet1 = T30_DIALFAIL;
  495. if (!pTG->fFatalErrorWasSignaled)
  496. {
  497. pTG->fFatalErrorWasSignaled = 1;
  498. SignalStatusChange(pTG, FS_FATAL_ERROR);
  499. }
  500. RetCode = FALSE;
  501. goto done;
  502. }
  503. pTG->Inst.state = BEFORE_RECVCAPS;
  504. // we should be using the sender msg here but that says Training
  505. // at speed=xxxx etc which we don't know, so we just use the
  506. // Recvr message which just says "negotiating"
  507. // Send the data
  508. uRet1 = (USHORT)Class2Send(pTG);
  509. if (uRet1 == T30_CALLDONE)
  510. {
  511. DebugPrintEx(DEBUG_MSG,"DONE WITH CALL, ALL OK");
  512. // have to call hangup on every path out of here
  513. // we have to call Hangup here
  514. Class2ModemHangup(pTG);
  515. SignalStatusChange(pTG, FS_COMPLETED);
  516. RetCode = TRUE;
  517. }
  518. else
  519. {
  520. DebugPrintEx(DEBUG_ERR,"DONE WITH CALL, FAILED");
  521. // Make sure Modem is in OK state
  522. FComOutFilterClose(pTG );
  523. FComXon(pTG, FALSE);
  524. // have to call hangup on every path out of here
  525. // Class2ModemABort calls Hangup
  526. Class2ModemAbort(pTG );
  527. Class2SignalFatalError(pTG);
  528. RetCode = FALSE;
  529. }
  530. uRet2 = 0;
  531. done:
  532. return RetCode;
  533. }
  534. BOOL Class2Send(PThrdGlbl pTG)
  535. {
  536. LPBUFFER lpbf;
  537. SWORD swRet;
  538. ULONG lTotalLen=0;
  539. PCB Pcb;
  540. USHORT uTimeout=30000;
  541. BOOL err_status, fAllPagesOK = TRUE;
  542. BC bc;
  543. UWORD Encoding, Res=0, PageWidth, PageLength, uwLen;
  544. BYTE bFDISBuf[200];
  545. CHAR szTSI[max(MAXTOTALIDLEN,20)+4];
  546. BYTE bNull = 0;
  547. DWORD TiffConvertThreadId;
  548. DEBUG_FUNCTION_NAME("Class2Send");
  549. /*
  550. * We have just dialed... Now we have to look for the FDIS response from
  551. * the modem. It will be followed by an OK - hunt for the OK.
  552. */
  553. PSSLogEntry(PSS_MSG, 0, "Phase B - Negotiation");
  554. if (!Class2iModemDialog(pTG,
  555. NULL,
  556. 0,
  557. STARTSENDMODE_TIMEOUT,
  558. 0,
  559. TRUE,
  560. pTG->cbszCLASS2_OK,
  561. (C2PSTR) NULL))
  562. {
  563. DebugPrintEx(DEBUG_ERR,"Failed to get OK for FDIS");
  564. PSSLogEntry(PSS_ERR, 1, "Failed to receive DIS - aborting");
  565. err_status = T30_CALLFAIL;
  566. return err_status;
  567. }
  568. if (pTG->fFoundFHNG)
  569. {
  570. PSSLogEntry(PSS_ERR, 1, "Call was disconnected");
  571. err_status = T30_CALLFAIL;
  572. return err_status;
  573. }
  574. // The response will be in pTG->lpbResponseBuf2 - this is loaded in
  575. // Class2iModemDialog.
  576. // Parse through the received strings, looking for the DIS, CSI,
  577. // NSF
  578. if (Class2ResponseAction(pTG, (LPPCB) &Pcb) == FALSE)
  579. {
  580. DebugPrintEx(DEBUG_MSG, "Failed to process ATD Response");
  581. PSSLogEntry(PSS_ERR, 1, "Failed to parse received DIS - aborting");
  582. err_status = T30_CALLFAIL;
  583. return err_status;
  584. }
  585. PSSLogEntry(PSS_MSG, 1, "CSI is %s", Pcb.szID);
  586. PSSLogEntry(PSS_MSG, 1, "DIS specified the following capabilities:");
  587. LogClass2DISDetails(pTG, &Pcb);
  588. //Now that pcb is set up, call ICommReceiveCaps to tell icomfile
  589. Class2InitBC(pTG, (LPBC)&bc, sizeof(bc), RECV_CAPS);
  590. Class2PCBtoBC(pTG, (LPBC)&bc, sizeof(bc), &Pcb);
  591. // Class2 modems do their own negotiation & we need to stay in sync
  592. // Otherwise, we might send MR data while the modem sends a DCS
  593. // saying it is MH. This happens a lot with Exar modems because
  594. // they dont accept an FDIS= command during the call.
  595. // FIX: On all Class2 sends force remote caps to always be MH
  596. // Then in efaxrun we will always negotiate MH & encode MH
  597. // We are relying on the fact that (a) it seems that all/most
  598. // Class2 modems negotiate MH (b) Hopefully ALL Exar ones
  599. // negotiate MH and (c) We will override all non-Exar modem's
  600. // intrinsic negotiation by sending an AT+FDIS= just before the FDT
  601. // Also (d) This change makes our behaviour match Snowball exactly
  602. // so we will work no better or worse than it :-)
  603. bc.Fax.Encoding = MH_DATA;
  604. if( ICommRecvCaps(pTG, (LPBC)&bc) == FALSE )
  605. {
  606. DebugPrintEx(DEBUG_ERR,"Failed return from ICommRecvCaps");
  607. err_status = T30_CALLFAIL;
  608. return err_status;
  609. }
  610. // now get the SEND_PARAMS
  611. if (!Class2GetBC(pTG, SEND_PARAMS)) // sleep until we get it
  612. {
  613. DebugPrintEx(DEBUG_ERR,"Class2GetBC Failed");
  614. err_status = T30_CALLFAIL;
  615. return err_status;
  616. }
  617. // Turn off flow control.
  618. FComXon(pTG, FALSE);
  619. // The Send params were set during the call to Class2GetBC
  620. // We'll use these to set the ID (for the TSI) and the DCS params
  621. // Send the FDT and get back the DCS. The FDT must be followed by
  622. // CONNECT and a ^Q (XON)
  623. // The FDT string must have the correct resolution and encoding
  624. // for this session. FDT=Encoding, Res, width, length
  625. // Encoding 0=MH, 1=MR,2=uncompressed,3=MMR
  626. // Res 0=200x100 (normal), 1=200x200 (fine)
  627. // PageWidth 0=1728pixels/215mm,1=2048/255,2=2432/303,
  628. // 3=1216/151,4=864/107
  629. // PageLength 0=A4,1=B4,2=unlimited
  630. Class2SetDIS_DCSParams( pTG,
  631. SEND_PARAMS,
  632. (LPUWORD)&Encoding,
  633. (LPUWORD)&Res,
  634. (LPUWORD)&PageWidth,
  635. (LPUWORD)&PageLength,
  636. (LPSTR) szTSI,
  637. sizeof(szTSI)/sizeof(szTSI[0]));
  638. //
  639. // Current Win95 version of Class2 TX is limited to MH only.
  640. // While not changing this, we will at least allow MR selection in future.
  641. //
  642. if (!pTG->fTiffThreadCreated)
  643. {
  644. if (Encoding)
  645. {
  646. pTG->TiffConvertThreadParams.tiffCompression = TIFF_COMPRESSION_MR;
  647. }
  648. else
  649. {
  650. pTG->TiffConvertThreadParams.tiffCompression = TIFF_COMPRESSION_MH;
  651. }
  652. if (Res)
  653. {
  654. pTG->TiffConvertThreadParams.HiRes = 1;
  655. }
  656. else
  657. {
  658. pTG->TiffConvertThreadParams.HiRes = 0;
  659. // use LoRes TIFF file prepared by FaxSvc
  660. // pTG->lpwFileName[ wcslen(pTG->lpwFileName) - 1] = (unsigned short) ('$');
  661. }
  662. _fmemcpy (pTG->TiffConvertThreadParams.lpszLineID, pTG->lpszPermanentLineID, 8);
  663. pTG->TiffConvertThreadParams.lpszLineID[8] = 0;
  664. DebugPrintEx(DEBUG_MSG,"Creating TIFF helper thread");
  665. pTG->hThread = CreateThread(NULL,
  666. 0,
  667. (LPTHREAD_START_ROUTINE) TiffConvertThread,
  668. (LPVOID) pTG,
  669. 0,
  670. &TiffConvertThreadId);
  671. if (!pTG->hThread)
  672. {
  673. DebugPrintEx(DEBUG_ERR,"TiffConvertThread create FAILED");
  674. err_status = T30_CALLFAIL;
  675. return err_status;
  676. }
  677. pTG->fTiffThreadCreated = 1;
  678. pTG->AckTerminate = 0;
  679. pTG->fOkToResetAbortReqEvent = 0;
  680. if ((pTG->RecoveryIndex >=0)&&(pTG->RecoveryIndex < MAX_T30_CONNECT))
  681. {
  682. T30Recovery[pTG->RecoveryIndex].TiffThreadId = TiffConvertThreadId;
  683. T30Recovery[pTG->RecoveryIndex].CkSum = ComputeCheckSum((LPDWORD) &T30Recovery[pTG->RecoveryIndex].fAvail,
  684. sizeof(T30_RECOVERY_GLOB)/sizeof(DWORD)-1);
  685. }
  686. }
  687. // Even modems that take FDT=x,x,x,x don't seem to really do it
  688. // right. So, for now, just send FDIS followed by FDT except for
  689. // the EXAR modems!!
  690. if (!pTG->CurrentMFRSpec.bIsExar)
  691. {
  692. uwLen = (WORD)wsprintf( bFDISBuf,
  693. pTG->cbszFDIS,
  694. Res,
  695. min(Pcb.Baud, pTG->DISPcb.Baud),
  696. PageWidth,
  697. PageLength,
  698. Encoding);
  699. if (!Class2iModemDialog(pTG,
  700. bFDISBuf,
  701. uwLen,
  702. LOCALCOMMAND_TIMEOUT,
  703. 0,
  704. TRUE,
  705. pTG->cbszCLASS2_OK,
  706. pTG->cbszCLASS2_ERROR,
  707. (C2PSTR) NULL))
  708. {
  709. DebugPrintEx(DEBUG_ERR,"Failed get response from FDIS!!");
  710. // Ignore it -we are going to send what we have!
  711. }
  712. }
  713. if (Class2iModemDialog(pTG,
  714. pTG->cbszFDT,
  715. (UWORD)(strlen(pTG->cbszFDT)),
  716. STARTSENDMODE_TIMEOUT,
  717. 0,
  718. TRUE,
  719. pTG->cbszFDT_CONNECT,
  720. (C2PSTR) NULL) != 1)
  721. {
  722. DebugPrintEx(DEBUG_MSG,"FDT Received %s",(LPSTR)(&(pTG->lpbResponseBuf2)));
  723. if (pTG->fFoundFHNG)
  724. {
  725. PSSLogEntry(PSS_ERR, 1, "Call was disconnected");
  726. }
  727. else
  728. {
  729. PSSLogEntry(PSS_ERR, 1, "Failed to start first page");
  730. }
  731. err_status = T30_CALLFAIL;
  732. return err_status;
  733. }
  734. DebugPrintEx(DEBUG_MSG,"FDT Received %s", (LPSTR)(&(pTG->lpbResponseBuf2)));
  735. if (pTG->CurrentMFRSpec.fSkipCtrlQ)
  736. {
  737. DebugPrintEx(DEBUG_WRN,"Skipping <XON> - sending immedaitely after CONNECT");
  738. }
  739. // Get the  from the COMM driver
  740. else if (!FComGetOneChar(pTG, 0x11))
  741. {
  742. PSSLogEntry(PSS_WRN, 1, "Didn't receive <XON> - continuing anyway");
  743. }
  744. else
  745. {
  746. PSSLogEntry(PSS_MSG, 2, "recv: <XON>");
  747. }
  748. // Turn on flow control.
  749. FComXon(pTG, TRUE);
  750. // Search through Response for the DCS frame - need it so set
  751. // the correct zero stuffing
  752. if (Class2ResponseAction(pTG, (LPPCB) &Pcb) == FALSE)
  753. {
  754. DebugPrintEx(DEBUG_ERR,"Failed to process FDT Response");
  755. PSSLogEntry(PSS_ERR, 1, "Failed to parse sent DCS - aborting");
  756. err_status = T30_CALLFAIL;
  757. return err_status;
  758. }
  759. PSSLogEntry(PSS_MSG, 1, "TSI is \"%s\"", szTSI);
  760. PSSLogEntry(PSS_MSG, 1, "DCS was sent as follows:");
  761. LogClass2DCSDetails(pTG, &Pcb);
  762. // Got a response - see if baud rate is OK
  763. DebugPrintEx( DEBUG_MSG,
  764. "Negotiated Baud Rate = %d, lower limit is %d",
  765. Pcb.Baud,
  766. pTG->ProtParams2.LowestSendSpeed);
  767. if (CodeToBPS[Pcb.Baud]<(WORD)pTG->ProtParams2.LowestSendSpeed)
  768. {
  769. DebugPrintEx(DEBUG_ERR,"Aborting due to too low baud rate!");
  770. err_status = T30_CALLFAIL;
  771. return err_status;
  772. }
  773. // Use values obtained from the DCS frame to set zero stuffing.
  774. // (These were obtained by call to Class2ResponseAction above).
  775. // Zero stuffing is a function of minimum scan time (determined
  776. // by resolution and the returned scan minimum) and baud.
  777. // Fixed the Hack--added a Baud field
  778. // Init must be BEFORE SetStuffZero!
  779. FComOutFilterInit(pTG);
  780. FComSetStuffZERO( pTG,
  781. Class2MinScanToBytesPerLine(pTG,
  782. Pcb.MinScan,
  783. (BYTE)Pcb.Baud,
  784. Pcb.Resolution));
  785. err_status = T30_CALLDONE;
  786. while ((swRet=ICommGetSendBuf(pTG, &lpbf, SEND_STARTPAGE)) == 0)
  787. {
  788. PSSLogEntry(PSS_MSG, 0, "Phase C - Page Transmission");
  789. PSSLogEntry(PSS_MSG, 1, "Sending page %d data...", pTG->PageCount);
  790. lTotalLen = 0;
  791. FComOverlappedIO(pTG, TRUE); // TRUE
  792. while ((swRet=ICommGetSendBuf(pTG, &lpbf, SEND_SEQ)) == 0)
  793. {
  794. lTotalLen += lpbf->wLengthData;
  795. DebugPrintEx(DEBUG_MSG,"Total length: %ld",lTotalLen);
  796. if (! (Class2ModemSendMem( pTG,
  797. lpbf->lpbBegData,
  798. lpbf->wLengthData) &
  799. (MyFreeBuf(pTG, lpbf))))
  800. {
  801. DebugPrintEx(DEBUG_ERR,"Class2ModemSendBuf Failed");
  802. PSSLogEntry(PSS_ERR, 1, "Failed to send page data - aborting");
  803. err_status = T30_CALLFAIL;
  804. FComOverlappedIO(pTG, FALSE);
  805. return err_status;
  806. }
  807. } // end of SEND_SEQ while
  808. // don't check swRet for error yet - even in case of page preparation
  809. // failure or abort, we still want to send <dle><etx> and then abort.
  810. FComOverlappedIO(pTG, FALSE);
  811. PSSLogEntry(PSS_MSG, 2, "send: page %d data, %d bytes", pTG->PageCount, lTotalLen);
  812. PSSLogEntry(PSS_MSG, 2, "send: <dle><etx>");
  813. // Terminate the Page with DLE-ETX
  814. if (!FComDirectAsyncWrite(pTG, pTG->Class2bDLEETX, 2))
  815. {
  816. PSSLogEntry(PSS_ERR, 1, "Failed to send <dle><etx> - aborting");
  817. err_status = T30_CALLFAIL;
  818. return err_status;
  819. }
  820. if (swRet != SEND_EOF)
  821. {
  822. DebugPrintEx(DEBUG_ERR,"ICommGetSendBuf failed, swRet=%d", swRet);
  823. PSSLogEntry(PSS_ERR, 1, "Failed to send page data - aborting");
  824. err_status = T30_CALLFAIL;
  825. return err_status;
  826. }
  827. PSSLogEntry(PSS_MSG, 1, "Successfully sent page data");
  828. if (1 != Class2ModemDrain(pTG))
  829. {
  830. DebugPrintEx(DEBUG_ERR,"Failed to drain");
  831. err_status = T30_CALLFAIL;
  832. return err_status;
  833. }
  834. if (pTG->fFoundFHNG)
  835. {
  836. PSSLogEntry(PSS_ERR, 1, "Call was disconnected");
  837. err_status = T30_CALLFAIL;
  838. return err_status;
  839. }
  840. PSSLogEntry(PSS_MSG, 0, "Phase D - Post Message Exchange");
  841. //See if more pages to send...
  842. if (ICommNextSend(pTG) == NEXTSEND_MPS)
  843. {
  844. // We are about to send a second or more page. Terminate the
  845. // last page with FET=0, signalling a new one to come
  846. PSSLogEntry(PSS_MSG, 1, "Sending MPS");
  847. if (!Class2iModemDialog(pTG,
  848. pTG->cbszENDPAGE,
  849. (UWORD)(strlen(pTG->cbszENDPAGE)),
  850. STARTSENDMODE_TIMEOUT,
  851. 0,
  852. TRUE,
  853. pTG->cbszCLASS2_OK,
  854. (C2PSTR)NULL))
  855. {
  856. PSSLogEntry(PSS_ERR, 1, "Failed to send MPS - aborting");
  857. err_status = T30_CALLFAIL;
  858. return err_status;
  859. }
  860. if (pTG->fFoundFHNG)
  861. {
  862. PSSLogEntry(PSS_ERR, 1, "Call was disconnected");
  863. err_status = T30_CALLFAIL;
  864. return err_status;
  865. }
  866. }
  867. else
  868. {
  869. // Purge input COM queue to purge all OKs
  870. FComFlushInput(pTG);
  871. // Send end of message sequence
  872. PSSLogEntry(PSS_MSG, 1, "Sending EOP");
  873. if (!Class2iModemDialog(pTG,
  874. pTG->cbszENDMESSAGE,
  875. (UWORD)(strlen(pTG->cbszENDMESSAGE)),
  876. STARTSENDMODE_TIMEOUT,
  877. 0,
  878. TRUE,
  879. pTG->cbszCLASS2_OK,
  880. (C2PSTR)NULL))
  881. {
  882. PSSLogEntry(PSS_ERR, 1, "Failed to send EOP - aborting");
  883. err_status = T30_CALLFAIL;
  884. return err_status;
  885. }
  886. // Don't test for pTG->fFoundFHNG - it might be OK
  887. }
  888. // Acknowledge that we sent the page
  889. // Parse the FPTS response and see if the page is good or bad.
  890. // Keep track of any bad pages in fAllPagesOK
  891. if (!ParseFPTS_SendAck(pTG ))
  892. {
  893. // fAllPagesOK = FALSE; // It's still ok - we'll retransmit the page
  894. // We want ICommGetSendBuf(SEND_STARTPAGE) to give us the same page again
  895. pTG->T30.ifrResp = ifrRTN;
  896. }
  897. else
  898. {
  899. // We want ICommGetSendBuf(SEND_STARTPAGE) to give us the next page
  900. // Note: This includes RTP too!
  901. pTG->T30.ifrResp = ifrMCF;
  902. }
  903. if ((ICommNextSend(pTG) == NEXTSEND_MPS) || (pTG->T30.ifrResp == ifrRTN))
  904. {
  905. if (pTG->fFoundFHNG)
  906. {
  907. // This could've happened after we sent the EOP, and only now we
  908. // know it's not OK
  909. PSSLogEntry(PSS_ERR, 1, "Call was disconnected");
  910. err_status = T30_CALLFAIL;
  911. return err_status;
  912. }
  913. // Now, Send the FDT to start the next page (this was done for
  914. // the first page before entering the multipage loop).
  915. if (Class2iModemDialog(pTG,
  916. pTG->cbszFDT,
  917. (UWORD)(strlen(pTG->cbszFDT)),
  918. STARTSENDMODE_TIMEOUT,
  919. 0,
  920. TRUE,
  921. pTG->cbszFDT_CONNECT,
  922. (C2PSTR)NULL) != 1)
  923. {
  924. DebugPrintEx(DEBUG_ERR,"FDT to start next PAGE Failed!");
  925. if (pTG->fFoundFHNG)
  926. {
  927. PSSLogEntry(PSS_ERR, 1, "Call was disconnected");
  928. }
  929. err_status = T30_CALLFAIL;
  930. return err_status;
  931. }
  932. // Get the  from the COMM driver
  933. if (!FComGetOneChar(pTG, 0x11))
  934. {
  935. PSSLogEntry(PSS_WRN, 1, "Didn't receive <XON> - proceeding to next page anyway");
  936. }
  937. else
  938. {
  939. PSSLogEntry(PSS_MSG, 2, "recv: <XON>");
  940. }
  941. // Turn on flow control.
  942. FComXon(pTG, TRUE);
  943. } //if we do not have another page, do the else...
  944. else
  945. {
  946. break; // All done sending pages...
  947. }
  948. if (err_status==T30_CALLFAIL)
  949. {
  950. break;
  951. }
  952. } //End of multipage while
  953. DebugPrintEx(DEBUG_MSG,"out of while multipage loop.");
  954. FComOutFilterClose(pTG);
  955. FComXon(pTG, FALSE);
  956. // If *any* page failed to send correctly, the call failed!
  957. if (!fAllPagesOK)
  958. {
  959. err_status = T30_CALLFAIL;
  960. }
  961. return err_status;
  962. }
  963. /**************************************************************
  964. Receive specific routines start here
  965. ***************************************************************/
  966. BOOL T30Cl2Rx(PThrdGlbl pTG)
  967. {
  968. USHORT uRet1, uRet2;
  969. BYTE bBuf[200];
  970. UWORD uwLen, uwRet;
  971. UWORD Encoding, Res, PageWidth, PageLength;
  972. BYTE bIDBuf[200+max(MAXTOTALIDLEN,20)+4];
  973. CHAR szCSI[max(MAXTOTALIDLEN,20)+4];
  974. BOOL fBaudChanged;
  975. BOOL RetCode;
  976. DEBUG_FUNCTION_NAME("T30Cl2Rx");
  977. uRet2 = 0;
  978. if (!(pTG->lpCmdTab=iModemGetCmdTabPtr(pTG)))
  979. {
  980. DebugPrintEx(DEBUG_ERR,"iModemGetCmdTabPtr failed");
  981. uRet1 = T30_CALLFAIL;
  982. pTG->fFatalErrorWasSignaled = 1;
  983. SignalStatusChange(pTG, FS_FATAL_ERROR);
  984. RetCode = FALSE;
  985. goto done;
  986. }
  987. // first get SEND_CAPS
  988. if (!Class2GetBC(pTG, SEND_CAPS)) // sleep until we get it
  989. {
  990. DebugPrintEx(DEBUG_ERR,"Class2GetBC failed");
  991. uRet1 = T30_CALLFAIL;
  992. pTG->fFatalErrorWasSignaled = 1;
  993. SignalStatusChange(pTG, FS_FATAL_ERROR);
  994. RetCode = FALSE;
  995. goto done;
  996. }
  997. // Go to Class2
  998. // Elliot Bug#3421 -- incoming RING sometimes clobbers AT+FCLASS=1/2 cmd.
  999. if (pTG->lpCmdTab->dwFlags & fMDMSP_ANS_GOCLASS_TWICE)
  1000. {
  1001. iModemGoClass(pTG, 2);
  1002. }
  1003. if(!iModemGoClass(pTG, 2))
  1004. {
  1005. DebugPrintEx(DEBUG_ERR,"Failed to Go to Class 2");
  1006. uRet1 = T30_CALLFAIL;
  1007. pTG->fFatalErrorWasSignaled = 1;
  1008. SignalStatusChange(pTG, FS_FATAL_ERROR);
  1009. RetCode = FALSE;
  1010. goto done;
  1011. }
  1012. // Begin by checking for manufacturer and ATI code.
  1013. // Look this up against the modem specific table we
  1014. // have and set up the receive strings needed for
  1015. // this modem.
  1016. if (!Class2GetModemMaker(pTG))
  1017. {
  1018. DebugPrintEx(DEBUG_WRN,"Call to GetModemMaker failed");
  1019. // Ignore failure!!!
  1020. }
  1021. // set manufacturer specific strings
  1022. Class2SetMFRSpecific(pTG);
  1023. // Get the capabilities of the software. I am only using this
  1024. // right now for the CSI field (below where I send +FLID).
  1025. // Really, this should also be used instead of the hardcoded DIS
  1026. // values below.
  1027. // ALL COMMANDS LOOK FOR MULTILINE RESPONSES WHILE MODEM IS ONHOOK.
  1028. // A "RING" COULD APPEAR AT ANY TIME!
  1029. _fmemset((LPB)szCSI, 0, sizeof(szCSI));
  1030. Class2SetDIS_DCSParams( pTG,
  1031. SEND_CAPS,
  1032. (LPUWORD)&Encoding,
  1033. (LPUWORD)&Res,
  1034. (LPUWORD)&PageWidth,
  1035. (LPUWORD)&PageLength,
  1036. (LPSTR) szCSI,
  1037. sizeof(szCSI)/sizeof(szCSI[0]));
  1038. // Find out what the default DIS is
  1039. if (!Class2GetDefaultFDIS(pTG))
  1040. {
  1041. DebugPrintEx(DEBUG_ERR, "Class2GetDefaultFDIS failed");
  1042. pTG->fFatalErrorWasSignaled = 1;
  1043. SignalStatusChange(pTG, FS_FATAL_ERROR);
  1044. RetCode = FALSE;
  1045. goto done;
  1046. }
  1047. fBaudChanged = FALSE;
  1048. // See if we have to change the baud rate to a lower value.
  1049. // This only happens if the user set an ini string inhibiting
  1050. // V.17 receive
  1051. if ((pTG->DISPcb.Baud>3) && (!pTG->ProtParams2.fEnableV17Recv))
  1052. {
  1053. DebugPrintEx( DEBUG_MSG,
  1054. "Lowering baud from %d for V.17 receive inihibit",
  1055. CodeToBPS[pTG->DISPcb.Baud]);
  1056. pTG->DISPcb.Baud = 3; //9600 won't use V.17
  1057. fBaudChanged = TRUE;
  1058. }
  1059. // Now, look and see if any of the values in the DIS are "bad"
  1060. // That is, make sure we can receive high res and we are not
  1061. // claiming that we are capable of MR or MMR. Also, see if we changed
  1062. // the baud rate. Also make sure we can receive wide pages.
  1063. if ( (pTG->DISPcb.Resolution & AWRES_mm080_077) &&
  1064. ( pTG->DISPcb.Encoding == MH_DATA) &&
  1065. (!fBaudChanged) &&
  1066. (pTG->DISPcb.PageLength == 2) &&
  1067. (pTG->DISPcb.PageWidth == 0) )
  1068. {
  1069. //Do nothing - leave DIS alone!
  1070. DebugPrintEx(DEBUG_MSG,"no need to change DIS");
  1071. }
  1072. else
  1073. {
  1074. // Send DCC command to the modem to set it up
  1075. // Do the minimum necessary - only set resoultion if possible
  1076. // (Again, this is because some modems don't like FDCC).
  1077. if ( (pTG->DISPcb.Encoding == MH_DATA) &&
  1078. (!fBaudChanged) &&
  1079. (pTG->DISPcb.PageLength == 2) &&
  1080. (pTG->DISPcb.PageWidth == 0) )
  1081. {
  1082. if (!Class2iModemDialog(pTG,
  1083. pTG->cbszFDCC_RECV_RES,
  1084. (UWORD)(strlen(pTG->cbszFDCC_RECV_RES)),
  1085. LOCALCOMMAND_TIMEOUT,
  1086. 0,
  1087. TRUE,
  1088. pTG->cbszCLASS2_OK,
  1089. (C2PSTR) NULL))
  1090. {
  1091. DebugPrintEx(DEBUG_ERR,"FDCC_RES Failed");
  1092. //Ignore it
  1093. }
  1094. }
  1095. else if ( (pTG->DISPcb.Encoding == MH_DATA) &&
  1096. (fBaudChanged) &&
  1097. (pTG->DISPcb.PageLength == 2) &&
  1098. (pTG->DISPcb.PageWidth == 0) )
  1099. {
  1100. // Changed the baud rate, but Encoding is OK.
  1101. uwLen=(USHORT)wsprintf((LPSTR)bBuf, pTG->cbszFDCC_BAUD, 1, pTG->DISPcb.Baud);
  1102. if(!Class2iModemDialog( pTG,
  1103. bBuf,
  1104. uwLen,
  1105. LOCALCOMMAND_TIMEOUT,
  1106. 0,
  1107. TRUE,
  1108. pTG->cbszCLASS2_OK,
  1109. (C2PSTR) NULL))
  1110. {
  1111. DebugPrintEx(DEBUG_ERR,"FDCC_BAUD Failed");
  1112. //Ignore it
  1113. }
  1114. }
  1115. else // the encoding format has changed or page size is bad
  1116. {
  1117. uwLen=(USHORT)wsprintf((LPSTR)bBuf, pTG->cbszFDCC_RECV_ALL, pTG->DISPcb.Baud);
  1118. if (!(uwRet=Class2iModemDialog( pTG,
  1119. bBuf,
  1120. uwLen,
  1121. LOCALCOMMAND_TIMEOUT,
  1122. 0,
  1123. TRUE,
  1124. pTG->cbszCLASS2_OK,
  1125. pTG->cbszCLASS2_ERROR,
  1126. (C2PSTR) NULL)))
  1127. {
  1128. DebugPrintEx(DEBUG_ERR,"FDCC_RECV_ALL Failed");
  1129. // ignore it.
  1130. }
  1131. // If the FDCC failed, try FDIS.
  1132. if ((uwRet == 0)||(uwRet == 2))
  1133. {
  1134. uwLen=(USHORT)wsprintf((LPSTR)bBuf, pTG->cbszFDIS_RECV_ALL, pTG->DISPcb.Baud);
  1135. if (!Class2iModemDialog(pTG,
  1136. bBuf,
  1137. uwLen,
  1138. LOCALCOMMAND_TIMEOUT,
  1139. 0,
  1140. TRUE,
  1141. pTG->cbszCLASS2_OK,
  1142. pTG->cbszCLASS2_ERROR,
  1143. (C2PSTR) NULL))
  1144. {
  1145. DebugPrintEx(DEBUG_ERR,"FDIS_RECV_ALL Failed");
  1146. // ignore it.
  1147. }
  1148. // if the above failed, try just setting the baud
  1149. // rate and resolution with FDCC.
  1150. if ((uwRet == 0)||(uwRet == 2))
  1151. {
  1152. uwLen=(USHORT)wsprintf((LPSTR)bBuf, pTG->cbszFDCC_BAUD, 1, pTG->DISPcb.Baud);
  1153. if (!(uwRet=Class2iModemDialog( pTG,
  1154. bBuf,
  1155. uwLen,
  1156. LOCALCOMMAND_TIMEOUT,
  1157. 0,
  1158. TRUE,
  1159. pTG->cbszCLASS2_OK,
  1160. pTG->cbszCLASS2_ERROR,
  1161. (C2PSTR) NULL)))
  1162. {
  1163. DebugPrintEx(DEBUG_ERR,"FDCC_BAUD Failed");
  1164. // Ignore it
  1165. }
  1166. }
  1167. // if the above failed, try just setting the baud
  1168. // rate and resolution with FDIS.
  1169. if ((uwRet == 0)||(uwRet == 2))
  1170. {
  1171. uwLen=(USHORT)wsprintf((LPSTR)bBuf, pTG->cbszFDIS_BAUD, pTG->DISPcb.Baud);
  1172. if (!(uwRet=Class2iModemDialog( pTG,
  1173. bBuf,
  1174. uwLen,
  1175. LOCALCOMMAND_TIMEOUT,
  1176. 0,
  1177. TRUE,
  1178. pTG->cbszCLASS2_OK,
  1179. pTG->cbszCLASS2_ERROR,
  1180. (C2PSTR) NULL)))
  1181. {
  1182. DebugPrintEx(DEBUG_ERR,"FDIS_BAUD Failed");
  1183. // Ignore it
  1184. }
  1185. }
  1186. }
  1187. }
  1188. }
  1189. // Enable Reception
  1190. if (!Class2iModemDialog(pTG,
  1191. pTG->cbszFCR,
  1192. (UWORD)(strlen(pTG->cbszFCR)),
  1193. ANS_LOCALCOMMAND_TIMEOUT,
  1194. 0,
  1195. TRUE,
  1196. pTG->cbszCLASS2_OK,
  1197. pTG->cbszCLASS2_ERROR,
  1198. (C2PSTR) NULL))
  1199. {
  1200. DebugPrintEx(DEBUG_WRN,"FCR failed");
  1201. // ignore failure
  1202. }
  1203. // Turn off Copy Quality Checking - also skip for Sierra type modems
  1204. if (!pTG->CurrentMFRSpec.bIsSierra)
  1205. {
  1206. if (!Class2iModemDialog(pTG,
  1207. pTG->cbszFCQ,
  1208. (UWORD)(strlen(pTG->cbszFCQ)),
  1209. ANS_LOCALCOMMAND_TIMEOUT,
  1210. 0,
  1211. TRUE,
  1212. pTG->cbszCLASS2_OK,
  1213. pTG->cbszCLASS2_ERROR,
  1214. (C2PSTR) NULL))
  1215. {
  1216. DebugPrintEx(DEBUG_ERR,"FCQ failed");
  1217. // Ignore CQ failure!!!
  1218. }
  1219. }
  1220. // Turn off Bug mode
  1221. if (!Class2iModemDialog(pTG,
  1222. pTG->cbszFBUG,
  1223. (UWORD)(strlen(pTG->cbszFBUG)),
  1224. ANS_LOCALCOMMAND_TIMEOUT,
  1225. 0,
  1226. TRUE,
  1227. pTG->cbszCLASS2_OK,
  1228. pTG->cbszCLASS2_ERROR,
  1229. (C2PSTR) NULL))
  1230. {
  1231. DebugPrintEx(DEBUG_ERR,"FBUG failed");
  1232. // Ignore FBUG failure!!!
  1233. }
  1234. // Do BOR based on the value from the modem table set in
  1235. // Class2SetMFRSpecific
  1236. bBuf[0] = '\0';
  1237. {
  1238. UINT uBOR = pTG->CurrentMFRSpec.iReceiveBOR;
  1239. if (pTG->CurrentMFRSpec.fSWFBOR && uBOR==1)
  1240. {
  1241. DebugPrintEx(DEBUG_WRN,"SWFBOR Enabled. Using AT+FBOR=0 instead of AT+FBOR=1");
  1242. uBOR = 0;
  1243. }
  1244. uwLen = (USHORT)wsprintf(bBuf, pTG->cbszSET_FBOR, uBOR);
  1245. }
  1246. if (!Class2iModemDialog(pTG,
  1247. bBuf,
  1248. uwLen,
  1249. ANS_LOCALCOMMAND_TIMEOUT,
  1250. 0,
  1251. TRUE,
  1252. pTG->cbszCLASS2_OK,
  1253. pTG->cbszCLASS2_ERROR,
  1254. (C2PSTR) NULL))
  1255. {
  1256. DebugPrintEx(DEBUG_ERR,"FBOR failed");
  1257. // Ignore BOR failure!!!
  1258. }
  1259. // Set the local ID - need ID from above to do this.
  1260. bIDBuf[0] = '\0';
  1261. uwLen = (USHORT)wsprintf(bIDBuf, pTG->cbszFLID, (LPSTR)szCSI);
  1262. if (!Class2iModemDialog(pTG,
  1263. bIDBuf,
  1264. uwLen,
  1265. ANS_LOCALCOMMAND_TIMEOUT,
  1266. 0,
  1267. TRUE,
  1268. pTG->cbszCLASS2_OK,
  1269. pTG->cbszCLASS2_ERROR,
  1270. (C2PSTR) NULL))
  1271. {
  1272. DebugPrintEx(DEBUG_ERR,"Local ID failed");
  1273. // ignore failure
  1274. }
  1275. // Answer the phone
  1276. // have to call hangup on every path out of here
  1277. // after Answer is called. If Answer fails, it calls Hangup.
  1278. // if it succeeds we have to call Hangup when we're done
  1279. SignalStatusChange(pTG, FS_ANSWERED);
  1280. PSSLogEntry(PSS_MSG, 0, "Phase A - Call establishment");
  1281. PSSLogEntry(PSS_MSG, 1, "Answering...");
  1282. if((uRet2 = Class2Answer(pTG)) != CONNECT_OK)
  1283. {
  1284. DebugPrintEx(DEBUG_ERR, "Failed to answer - aborting");
  1285. // SignalStatusChange is called inside Class2Answer
  1286. uRet1 = T30_CALLFAIL;
  1287. RetCode = FALSE;
  1288. goto done;
  1289. }
  1290. DebugPrintEx(DEBUG_MSG,"Done with Class2 Answer - succeeded");
  1291. // Receive the data
  1292. PSSLogEntry(PSS_MSG, 0, "Phase B - Negotiation");
  1293. PSSLogEntry(PSS_MSG, 1, "CSI is %s", szCSI);
  1294. PSSLogEntry(PSS_MSG, 1, "DIS was composed with the following capabilities:");
  1295. LogClass2DISDetails(pTG, &pTG->DISPcb);
  1296. uRet1 = (USHORT)Class2Receive(pTG );
  1297. // t-jonb: If we've already called PutRecvBuf(RECV_STARTPAGE), but not
  1298. // PutRecvBuf(RECV_ENDPAGE / DOC), then InFileHandleNeedsBeClosed==1, meaning
  1299. // there's a .RX file that hasn't been copied to the .TIF file. Since the
  1300. // call was disconnected, there will be no chance to send RTN. Therefore, we call
  1301. // PutRecvBuf(RECV_ENDDOC_FORCESAVE) to keep the partial page and tell
  1302. // rx_thrd to terminate.
  1303. if (pTG->InFileHandleNeedsBeClosed)
  1304. {
  1305. ICommPutRecvBuf(pTG, NULL, RECV_ENDDOC_FORCESAVE);
  1306. }
  1307. if (uRet1 == T30_CALLDONE)
  1308. {
  1309. DebugPrintEx(DEBUG_MSG,"DONE WITH CALL, ALL OK");
  1310. // have to call hangup on every path out of here
  1311. // we have to call Hangup here
  1312. Class2ModemHangup(pTG );
  1313. SignalStatusChange(pTG, FS_COMPLETED);
  1314. RetCode = TRUE;
  1315. }
  1316. else
  1317. {
  1318. DebugPrintEx(DEBUG_ERR,"DONE WITH CALL, FAILED");
  1319. // Make sure modem is in an OK state!
  1320. FComXon(pTG, FALSE);
  1321. // have to call hangup on every path out of here
  1322. // Abort calls Hangup
  1323. Class2ModemAbort(pTG );
  1324. Class2SignalFatalError(pTG);
  1325. RetCode = FALSE;
  1326. }
  1327. uRet2 = 0;
  1328. done:
  1329. return RetCode;
  1330. }
  1331. BOOL Class2Receive(PThrdGlbl pTG)
  1332. {
  1333. LPBUFFER lpbf;
  1334. SWORD swRet;
  1335. UWORD uwLen;
  1336. ULONG lTotalLen=0;
  1337. PCB Pcb;
  1338. USHORT uTimeout=30000, uRet, uEndPageAction=NO_MORE_PAGES;
  1339. BOOL err_status;
  1340. BC bc;
  1341. BYTE bBuf[200];
  1342. LPSTR lpsTemp;
  1343. DWORD HiRes;
  1344. DEBUG_FUNCTION_NAME("Class2Receive");
  1345. /*
  1346. * We have just answered!
  1347. */
  1348. // The repsonse to the ATA command is in the global variable
  1349. // pTG->lpbResponseBuf2.
  1350. if (Class2ResponseAction(pTG, (LPPCB) &Pcb) == FALSE)
  1351. {
  1352. PSSLogEntry(PSS_ERR, 1, "Failed to parse response from ATA - aborting");
  1353. err_status = T30_CALLFAIL;
  1354. return err_status;
  1355. }
  1356. PSSLogEntry(PSS_MSG, 1, "TSI is %s", Pcb.szID);
  1357. PSSLogEntry(PSS_MSG, 1, "Received DCS is as follows");
  1358. LogClass2DCSDetails(pTG, &Pcb);
  1359. if (!Class2IsValidDCS(&Pcb))
  1360. {
  1361. PSSLogEntry(PSS_ERR, 1, "Received bad DCS parameters - aborting");
  1362. err_status = T30_CALLFAIL;
  1363. return err_status;
  1364. }
  1365. if (!Class2UpdateTiffInfo(pTG, &Pcb))
  1366. {
  1367. DebugPrintEx(DEBUG_WRN, "Class2UpdateTiffInfo failed");
  1368. }
  1369. //Now that pcb is set up, call ICommReceiveParams to tell icomfile
  1370. Class2InitBC(pTG, (LPBC)&bc, sizeof(bc), RECV_PARAMS);
  1371. Class2PCBtoBC(pTG, (LPBC)&bc, sizeof(bc), &Pcb);
  1372. if (ICommRecvParams(pTG, (LPBC)&bc) == FALSE)
  1373. {
  1374. DebugPrintEx(DEBUG_ERR,"Failed return from ICommRecvParams");
  1375. err_status = T30_CALLFAIL;
  1376. return err_status;
  1377. }
  1378. //
  1379. // once per RX - create TIFF file as soon as we know the compression / resolution.
  1380. //
  1381. pTG->Encoding = Pcb.Encoding;
  1382. pTG->Resolution = Pcb.Resolution;
  1383. if (Pcb.Resolution & (AWRES_mm080_077 | AWRES_200_200) )
  1384. {
  1385. HiRes = 1;
  1386. }
  1387. else
  1388. {
  1389. HiRes = 0;
  1390. }
  1391. if ( !pTG->fTiffOpenOrCreated)
  1392. {
  1393. //
  1394. // top 32bits of 64bit handle are guaranteed to be zero
  1395. //
  1396. pTG->Inst.hfile = TiffCreateW ( pTG->lpwFileName,
  1397. pTG->TiffInfo.CompressionType,
  1398. pTG->TiffInfo.ImageWidth,
  1399. FILLORDER_LSB2MSB,
  1400. HiRes
  1401. );
  1402. if (! (pTG->Inst.hfile))
  1403. {
  1404. lpsTemp = UnicodeStringToAnsiString(pTG->lpwFileName);
  1405. DebugPrintEx( DEBUG_ERR,
  1406. "ERROR:Can't create tiff file %s compr=%d",
  1407. lpsTemp,
  1408. pTG->TiffInfo.CompressionType);
  1409. MemFree(lpsTemp);
  1410. err_status = T30_CALLFAIL;
  1411. return err_status;
  1412. }
  1413. pTG->fTiffOpenOrCreated = 1;
  1414. lpsTemp = UnicodeStringToAnsiString(pTG->lpwFileName);
  1415. DebugPrintEx( DEBUG_MSG,
  1416. "Created tiff file %s compr=%d HiRes=%d",
  1417. lpsTemp,
  1418. pTG->TiffInfo.CompressionType,
  1419. HiRes);
  1420. MemFree(lpsTemp);
  1421. }
  1422. // **** Apparently, we don't want flow control on, so we'll turn
  1423. // it off. Is this true???? If I turn it on, fcom.c fails a
  1424. // debug check in filterreadbuf.
  1425. FComXon(pTG, FALSE);
  1426. // Send the FDR. The FDR must be responded to by a CONNECT.
  1427. if (Class2iModemDialog(pTG,
  1428. pTG->cbszFDR,
  1429. (UWORD)(strlen(pTG->cbszFDR)),
  1430. STARTSENDMODE_TIMEOUT,
  1431. 0,
  1432. TRUE,
  1433. pTG->cbszFDT_CONNECT,
  1434. (C2PSTR) NULL) != 1)
  1435. {
  1436. DebugPrintEx(DEBUG_ERR,"Failed get response from initial FDR");
  1437. if (pTG->fFoundFHNG)
  1438. {
  1439. PSSLogEntry(PSS_ERR, 1, "Call was disconnected");
  1440. }
  1441. err_status = T30_CALLFAIL;
  1442. return err_status;
  1443. }
  1444. DebugPrintEx(DEBUG_MSG,"FDR Received %s", (LPSTR)(&(pTG->lpbResponseBuf2)));
  1445. // Might have to search through FDR response, but I doubt it.
  1446. // Now we need to send a DC2 (0x12) to tell the modem it is OK
  1447. // to give us data.
  1448. // Some modems use ^Q instead of ^R - The correct value was written
  1449. // into the DC@ string in Class2Callee where we checked for
  1450. // manufacturer
  1451. PSSLogEntry(PSS_MSG, 0, "Phase C - Receive page");
  1452. PSSLogEntry(PSS_MSG, 2, "send: <DC2> (=ASCII %d)", *(pTG->CurrentMFRSpec.szDC2));
  1453. FComDirectSyncWriteFast(pTG, pTG->CurrentMFRSpec.szDC2, 1);
  1454. // Now we can receive the data and give it to the icomfile routine
  1455. err_status = T30_CALLDONE;
  1456. while ((swRet=(SWORD)ICommPutRecvBuf(pTG, NULL, RECV_STARTPAGE)) == TRUE)
  1457. {
  1458. PSSLogEntry(PSS_MSG, 1, "Receiving page %d data...", pTG->PageCount+1);
  1459. // The READ_TIMEOUT is used to timeout calls to ReadBuf() either in the
  1460. #define READ_TIMEOUT 15000
  1461. lTotalLen = 0;
  1462. do
  1463. {
  1464. DebugPrintEx(DEBUG_MSG,"In receiving a page loop");
  1465. uRet = Class2ModemRecvBuf(pTG, &lpbf, READ_TIMEOUT);
  1466. DebugPrintEx(DEBUG_MSG,"Class2ModemRecvBuf returned uRet=%x",uRet);
  1467. if(lpbf)
  1468. {
  1469. lTotalLen += lpbf->wLengthData;
  1470. DebugPrintEx( DEBUG_MSG,
  1471. "In lpbf if. length = %ld, Total Length %ld",
  1472. lpbf->wLengthData,
  1473. lTotalLen);
  1474. if (!ICommPutRecvBuf(pTG, lpbf, RECV_SEQ))
  1475. {
  1476. DebugPrintEx(DEBUG_ERR,"Bad return - PutRecvBuf in page");
  1477. err_status=T30_CALLFAIL;
  1478. return err_status;
  1479. }
  1480. lpbf = 0;
  1481. }
  1482. }
  1483. while(uRet == RECV_OK);
  1484. PSSLogEntry(PSS_MSG, 2, "recv: page %d data, %d bytes", pTG->PageCount+1, lTotalLen);
  1485. if(uRet == RECV_EOF)
  1486. {
  1487. DebugPrintEx(DEBUG_MSG,"Got EOF from RecvBuf");
  1488. // RSL needed interface to TIFF thread
  1489. pTG->fLastReadBlock = 1;
  1490. ICommPutRecvBuf(pTG, NULL, RECV_FLUSH);
  1491. }
  1492. else
  1493. {
  1494. // Timeout from ModemRecvBuf
  1495. BYTE bCancel = 0x18;
  1496. DebugPrintEx(DEBUG_ERR,"ModemRecvBuf Timeout or Error=%d",uRet);
  1497. PSSLogEntry(PSS_ERR, 1, "Failed to receive page data - aborting");
  1498. PSSLogEntry(PSS_MSG, 2, "send: <can> (=ASCII 24)");
  1499. FComDirectSyncWriteFast(pTG, &bCancel, 1);
  1500. ICommPutRecvBuf(pTG, NULL, RECV_FLUSH);
  1501. err_status = T30_CALLFAIL;
  1502. return err_status;
  1503. }
  1504. PSSLogEntry(PSS_MSG, 1, "Successfully received page data");
  1505. PSSLogEntry(PSS_MSG, 0, "Phase D - Post Message Exchange");
  1506. // See if more pages to receive by parsing the FDR response...
  1507. // After the DLEETX was received by Class2ModemRecvBuf, the
  1508. // FPTS and FET response should be coming from the modem, terminated
  1509. // by an OK. Let's go read that!
  1510. if (!Class2iModemDialog(pTG,
  1511. NULL,
  1512. 0,
  1513. STARTSENDMODE_TIMEOUT,
  1514. 0,
  1515. TRUE,
  1516. pTG->cbszCLASS2_OK,
  1517. (C2PSTR)NULL))
  1518. {
  1519. PSSLogEntry(PSS_ERR, 1, "Failed to receive EOP or MPS or EOM - aborting");
  1520. err_status = T30_CALLFAIL;
  1521. return err_status;
  1522. }
  1523. if (pTG->fFoundFHNG)
  1524. {
  1525. PSSLogEntry(PSS_ERR, 1, "Call was disconnected");
  1526. err_status = T30_CALLFAIL;
  1527. return err_status;
  1528. }
  1529. DebugPrintEx(DEBUG_MSG,"EOP Received %s", (LPSTR)(&(pTG->lpbResponseBuf2)));
  1530. // Process the response and see if more pages are coming
  1531. uEndPageAction = Class2EndPageResponseAction(pTG);
  1532. if (uEndPageAction == MORE_PAGES)
  1533. {
  1534. ICommPutRecvBuf(pTG, NULL, RECV_ENDPAGE);
  1535. }
  1536. else
  1537. {
  1538. ICommPutRecvBuf(pTG, NULL, RECV_ENDDOC);
  1539. }
  1540. // Send the FPTS - don't do this for Exar modems!
  1541. if (!pTG->CurrentMFRSpec.bIsExar)
  1542. {
  1543. if (pTG->fPageIsBad)
  1544. {
  1545. PSSLogEntry(PSS_MSG, 1, "Sending RTN");
  1546. }
  1547. else
  1548. {
  1549. PSSLogEntry(PSS_MSG, 1, "Sending MCF");
  1550. }
  1551. uwLen = (UWORD)wsprintf(bBuf, pTG->cbszFPTS, pTG->fPageIsBad ? 2 : 1);
  1552. if (!Class2iModemDialog(pTG,
  1553. bBuf,
  1554. uwLen,
  1555. LOCALCOMMAND_TIMEOUT,
  1556. 0,
  1557. TRUE,
  1558. pTG->cbszCLASS2_OK,
  1559. pTG->cbszCLASS2_ERROR,
  1560. (C2PSTR) NULL))
  1561. {
  1562. PSSLogEntry(PSS_WRN, 1, "Failed to send MCF/RTN - continuing anyway");
  1563. // Ignore FPTS failure!!!
  1564. }
  1565. }
  1566. if ((uEndPageAction==MORE_PAGES) || (pTG->fPageIsBad))
  1567. {
  1568. // Now, Send the FDR to start the next page (this was done for
  1569. // the first page before entering the multipage loop).
  1570. if (Class2iModemDialog(pTG,
  1571. pTG->cbszFDR,
  1572. (UWORD)(strlen(pTG->cbszFDR)),
  1573. STARTSENDMODE_TIMEOUT,
  1574. 0,
  1575. TRUE,
  1576. pTG->cbszFDT_CONNECT,
  1577. (C2PSTR) NULL) != 1)
  1578. {
  1579. DebugPrintEx(DEBUG_ERR,"FDR to start next PAGE Failed");
  1580. if (pTG->fFoundFHNG)
  1581. {
  1582. PSSLogEntry(PSS_ERR, 1, "Call was disconnected");
  1583. }
  1584. err_status = T30_CALLFAIL;
  1585. return err_status;
  1586. }
  1587. // Need to check whether modem performed re-negotiation, and
  1588. // update the TIFF accordingly
  1589. if (Class2ResponseAction(pTG, (LPPCB) &Pcb))
  1590. {
  1591. PSSLogEntry(PSS_MSG, 1, "Received DCS is as follows");
  1592. LogClass2DCSDetails(pTG, &Pcb);
  1593. if (!Class2UpdateTiffInfo(pTG, &Pcb))
  1594. {
  1595. DebugPrintEx(DEBUG_WRN, "Class2UpdateTiffInfo failed");
  1596. }
  1597. }
  1598. PSSLogEntry(PSS_MSG, 0, "Phase C - Receive page");
  1599. PSSLogEntry(PSS_MSG, 2, "send: <DC2> (=ASCII %d)", *(pTG->CurrentMFRSpec.szDC2));
  1600. // Now send the correct DC2 string set in Class2Callee
  1601. // (DC2 is standard, some use ^q instead)
  1602. FComDirectSyncWriteFast(pTG, pTG->CurrentMFRSpec.szDC2, 1);
  1603. } //if we do not have another page, do the else...
  1604. else
  1605. {
  1606. // Send last FDR
  1607. if (!Class2iModemDialog(pTG,
  1608. pTG->cbszFDR,
  1609. (UWORD)(strlen(pTG->cbszFDR)),
  1610. STARTSENDMODE_TIMEOUT,
  1611. 0,
  1612. TRUE,
  1613. pTG->cbszCLASS2_OK,
  1614. (C2PSTR)NULL))
  1615. {
  1616. DebugPrintEx(DEBUG_ERR,"FDR failed");
  1617. err_status = T30_CALLFAIL;
  1618. return err_status;
  1619. }
  1620. // pTG->fFoundFHNG should be TRUE here - that's normal
  1621. break; // All done receiving pages...
  1622. }
  1623. } //End of multipage while
  1624. FComXon(pTG, FALSE);
  1625. return err_status;
  1626. }
  1627. BOOL Class2GetModemMaker(PThrdGlbl pTG)
  1628. {
  1629. HRESULT hr;
  1630. DEBUG_FUNCTION_NAME("Class2GetModemMaker");
  1631. // Initialize the current modem variable's (global) strings.
  1632. pTG->CurrentMFRSpec.szMFR[0] = '\0';
  1633. pTG->CurrentMFRSpec.szMDL[0] = '\0';
  1634. // For all responses, "ERROR" may come back - that is OK - we will
  1635. // never match ERROR to an acceptable modem manufacturer name, model,
  1636. // revision, etc.
  1637. // Get the FMFR - repsonse is in pTG->lpbResponseBuf2
  1638. if (!Class2iModemDialog(pTG,
  1639. pTG->cbszCLASS2_FMFR,
  1640. (UWORD)(strlen(pTG->cbszCLASS2_FMFR)),
  1641. ANS_LOCALCOMMAND_TIMEOUT,
  1642. 0,
  1643. TRUE,
  1644. pTG->cbszCLASS2_OK,
  1645. pTG->cbszCLASS2_ERROR,
  1646. (C2PSTR) NULL))
  1647. {
  1648. DebugPrintEx(DEBUG_ERR,"FMFR failed");
  1649. // Ignore FMFR failure!!!
  1650. }
  1651. else
  1652. {
  1653. // copy FMFR answer into FMFR variable
  1654. hr = StringCchCopy(pTG->CurrentMFRSpec.szMFR, ARR_SIZE(pTG->CurrentMFRSpec.szMFR), pTG->lpbResponseBuf2);
  1655. if (FAILED(hr))
  1656. {
  1657. DebugPrintEx(DEBUG_WRN,"StringCchCopy failed (ec=0x%08X)",hr);
  1658. }
  1659. DebugPrintEx(DEBUG_MSG,"Received FMFR %s", (LPSTR)(&(pTG->lpbResponseBuf2)));
  1660. }
  1661. // Get the FMDL - repsonse is in pTG->lpbResponseBuf2
  1662. if (!Class2iModemDialog(pTG,
  1663. pTG->cbszCLASS2_FMDL,
  1664. (UWORD)(strlen(pTG->cbszCLASS2_FMDL)),
  1665. ANS_LOCALCOMMAND_TIMEOUT,
  1666. 0,
  1667. TRUE,
  1668. pTG->cbszCLASS2_OK,
  1669. pTG->cbszCLASS2_ERROR,
  1670. (C2PSTR) NULL))
  1671. {
  1672. DebugPrintEx(DEBUG_ERR,"FMDL failed");
  1673. // Ignore FMDL failure!!!
  1674. }
  1675. else
  1676. {
  1677. // copy FMDL answer into FMDL variable
  1678. hr = StringCchCopy(pTG->CurrentMFRSpec.szMDL, ARR_SIZE(pTG->CurrentMFRSpec.szMDL), pTG->lpbResponseBuf2);
  1679. if (FAILED(hr))
  1680. {
  1681. DebugPrintEx(DEBUG_WRN,"StringCchCopy failed (ec=0x%08X)",hr);
  1682. }
  1683. DebugPrintEx(DEBUG_MSG,"Received FMDL %s", (LPSTR)(&(pTG->lpbResponseBuf2)));
  1684. }
  1685. return TRUE;
  1686. }
  1687. void Class2SetMFRSpecific(PThrdGlbl pTG)
  1688. {
  1689. USHORT iIndex, iFoundMFR,iFoundMDL;
  1690. LPMFRSPEC lpmfrMatched;
  1691. DEBUG_FUNCTION_NAME("Class2SetMFRSpecific");
  1692. // Find the index into the table that corresponds most closely
  1693. // to the modem. If we can't find the mfr and model, find a mfr
  1694. // that matches (use the last one). If neither, use the default
  1695. // last entry.
  1696. // Look for Manufacturer name
  1697. iIndex = 0;
  1698. iFoundMFR = 0;
  1699. iFoundMDL = 0;
  1700. DebugPrintEx(DEBUG_MSG,"Entering search table loop");
  1701. while (Class2ModemTable[iIndex].szMFR[0] != '\0')
  1702. {
  1703. lpmfrMatched = &(Class2ModemTable[iIndex]);
  1704. // Look and see if the current name matches
  1705. // the name in the list.
  1706. if (strstr( (LPSTR)pTG->CurrentMFRSpec.szMFR,
  1707. (LPSTR)lpmfrMatched->szMFR) != NULL)
  1708. {
  1709. // Found a match!
  1710. DebugPrintEx( DEBUG_MSG,
  1711. "Matched manufacturer name: %s %s",
  1712. (LPSTR)(&pTG->CurrentMFRSpec.szMFR),
  1713. (LPSTR)(&(lpmfrMatched->szMFR)));
  1714. iFoundMFR = iIndex;
  1715. //Now see if this matches the model number, too.
  1716. if(strstr( (LPSTR) pTG->CurrentMFRSpec.szMDL,
  1717. (LPSTR) lpmfrMatched->szMDL) != NULL)
  1718. {
  1719. //Got a MDL match, too! Stop looking.
  1720. iFoundMDL = iIndex;
  1721. DebugPrintEx( DEBUG_MSG,
  1722. "Matched model: %s %s",
  1723. (LPSTR)(&pTG->CurrentMFRSpec.szMDL),
  1724. (LPSTR)(&(lpmfrMatched->szMDL)));
  1725. break;
  1726. }
  1727. }
  1728. iIndex++;
  1729. }
  1730. // We now either have the modem match or are using the defaults!
  1731. if (iFoundMFR != 0)
  1732. {
  1733. lpmfrMatched = &Class2ModemTable[iFoundMFR];
  1734. }
  1735. else
  1736. {
  1737. lpmfrMatched = &Class2ModemTable[iIndex];
  1738. }
  1739. // All these settings were read from the registry during T30ModemInit.
  1740. // Since we want registry values to take precedence over internal table
  1741. // values, change only the settings that were not found in the registry
  1742. // (they'll have a value of CL2_DEFAULT_SETTING).
  1743. // Set proper BOR for receive and send
  1744. if (pTG->CurrentMFRSpec.iSendBOR == CL2_DEFAULT_SETTING)
  1745. {
  1746. pTG->CurrentMFRSpec.iSendBOR = lpmfrMatched->iSendBOR;
  1747. }
  1748. if (pTG->CurrentMFRSpec.iReceiveBOR == CL2_DEFAULT_SETTING)
  1749. {
  1750. pTG->CurrentMFRSpec.iReceiveBOR = lpmfrMatched->iReceiveBOR;
  1751. }
  1752. if (pTG->CurrentMFRSpec.fSWFBOR == CL2_DEFAULT_SETTING)
  1753. {
  1754. pTG->CurrentMFRSpec.fSWFBOR = lpmfrMatched->fSWFBOR;
  1755. }
  1756. // Set the DC2 string - this is used in receive mode
  1757. // after sending the FDR to tell the modem we are ready
  1758. // to receive data. The standard says it should be a Dc2
  1759. // (^R). But, some modems use ^Q
  1760. if (pTG->CurrentMFRSpec.szDC2[0] == (CHAR)CL2_DEFAULT_SETTING)
  1761. {
  1762. pTG->CurrentMFRSpec.szDC2[0] = lpmfrMatched->szDC2[0];
  1763. }
  1764. // Set the Sierra and Exar flags flag
  1765. if (pTG->CurrentMFRSpec.bIsSierra == CL2_DEFAULT_SETTING)
  1766. {
  1767. pTG->CurrentMFRSpec.bIsSierra = lpmfrMatched->bIsSierra;
  1768. }
  1769. if (pTG->CurrentMFRSpec.bIsExar == CL2_DEFAULT_SETTING)
  1770. {
  1771. pTG->CurrentMFRSpec.bIsExar = lpmfrMatched->bIsExar;
  1772. }
  1773. if (pTG->CurrentMFRSpec.fSkipCtrlQ == CL2_DEFAULT_SETTING)
  1774. {
  1775. pTG->CurrentMFRSpec.fSkipCtrlQ = lpmfrMatched->fSkipCtrlQ;
  1776. }
  1777. DebugPrintEx(DEBUG_MSG, "CurrentMFRSpec = %02x, %02x, %02x, %02x, %02x, %02x, %02x",
  1778. pTG->CurrentMFRSpec.iReceiveBOR,
  1779. pTG->CurrentMFRSpec.iSendBOR,
  1780. pTG->CurrentMFRSpec.szDC2[0],
  1781. pTG->CurrentMFRSpec.bIsSierra,
  1782. pTG->CurrentMFRSpec.bIsExar,
  1783. pTG->CurrentMFRSpec.fSkipCtrlQ = lpmfrMatched->fSkipCtrlQ,
  1784. pTG->CurrentMFRSpec.fSWFBOR);
  1785. }
  1786. BOOL Class2Parse(PThrdGlbl pTG, CL2_COMM_ARRAY *cl2_comm, BYTE lpbBuf[])
  1787. {
  1788. int i,
  1789. j,
  1790. comm_numb = 0,
  1791. parameters;
  1792. BYTE switch_char,
  1793. char_1,
  1794. char_2;
  1795. char c;
  1796. BOOL found_command = FALSE;
  1797. DEBUG_FUNCTION_NAME("Class2Parse");
  1798. #define STRING_PARAMETER 1
  1799. #define NUMBER_PARAMETERS 2
  1800. for (i = 0; lpbBuf[i] != '\0'; ++i)
  1801. {
  1802. if (comm_numb >= MAX_CLASS2_COMMANDS)
  1803. {
  1804. DebugPrintEx(DEBUG_WRN, "Reached maximum number of commands");
  1805. break;
  1806. }
  1807. switch (lpbBuf[i])
  1808. {
  1809. case 'C':
  1810. if (lpbBuf[++i] == 'O' && lpbBuf[++i] == 'N')
  1811. {
  1812. cl2_comm->command[comm_numb++] = CL2DCE_CONNECT;
  1813. for(; lpbBuf[i] != '\r'; ++i )
  1814. ;
  1815. }
  1816. else
  1817. {
  1818. DebugPrintEx(DEBUG_ERR,"Parse: Bad First C values");
  1819. return FALSE;
  1820. }
  1821. break;
  1822. case 'O':
  1823. if (lpbBuf[++i] == 'K' )
  1824. {
  1825. cl2_comm->command[comm_numb++] = CL2DCE_OK;
  1826. for(; lpbBuf[i] != '\r'; ++i )
  1827. ;
  1828. }
  1829. else
  1830. {
  1831. DebugPrintEx(DEBUG_ERR,"Parse: Bad O values");
  1832. return FALSE;
  1833. }
  1834. break;
  1835. case 0x11:
  1836. cl2_comm->command[comm_numb++] = CL2DCE_XON;
  1837. break;
  1838. case '+':
  1839. if( lpbBuf[++i] != 'F' )
  1840. {
  1841. DebugPrintEx(DEBUG_ERR,"Parse: Bad + values");
  1842. return FALSE;
  1843. }
  1844. switch_char = lpbBuf[++i];
  1845. char_1 = lpbBuf[++i];
  1846. char_2 = lpbBuf[++i];
  1847. switch ( switch_char )
  1848. {
  1849. case 'C':
  1850. // Connect Message +FCON.
  1851. if ( char_1 == 'O' && char_2 == 'N' )
  1852. {
  1853. cl2_comm->command[comm_numb] = CL2DCE_FCON;
  1854. parameters = FALSE;
  1855. }
  1856. // Report of Remote ID. +FCIG.
  1857. else if (char_1 == 'I' && char_2 == 'G' )
  1858. {
  1859. cl2_comm->command[comm_numb] = CL2DCE_FCIG;
  1860. parameters = STRING_PARAMETER;
  1861. }
  1862. // Prepare to receive prompt. +FCFR.
  1863. else if ( char_1 == 'F' && char_2 == 'R' )
  1864. {
  1865. cl2_comm->command[comm_numb] = CL2DCE_FCFR;
  1866. parameters = FALSE;
  1867. }
  1868. // Report the Remote ID CSI +FCSI.
  1869. else if ( char_1 == 'S' && char_2 == 'I' )
  1870. {
  1871. cl2_comm->command[comm_numb] = CL2DCE_FCSI;
  1872. parameters = STRING_PARAMETER;
  1873. }
  1874. else
  1875. {
  1876. DebugPrintEx(DEBUG_ERR,"Parse: Bad C values");
  1877. return FALSE;
  1878. }
  1879. break;
  1880. case 'D':
  1881. // Report DCS frame information +FDCS.
  1882. if ( char_1 == 'C' && char_2 == 'S' )
  1883. {
  1884. cl2_comm->command[comm_numb] = CL2DCE_FDCS;
  1885. parameters = NUMBER_PARAMETERS;
  1886. }
  1887. // Report DIS frame information +FDIS.
  1888. else if ( char_1 == 'I' && char_2 == 'S' )
  1889. {
  1890. cl2_comm->command[comm_numb] = CL2DCE_FDIS;
  1891. parameters = NUMBER_PARAMETERS;
  1892. }
  1893. // Report DTC frame information +FDTC.
  1894. else if ( char_1 == 'T' && char_2 == 'C' )
  1895. {
  1896. cl2_comm->command[comm_numb] = CL2DCE_FDTC;
  1897. parameters = NUMBER_PARAMETERS;
  1898. }
  1899. else
  1900. {
  1901. DebugPrintEx(DEBUG_ERR,"Parse: Bad D values");
  1902. return FALSE;
  1903. }
  1904. break;
  1905. case 'E':
  1906. // Post page message report. +FET.
  1907. if ( char_1 == 'T' )
  1908. {
  1909. --i;
  1910. cl2_comm->command[comm_numb] = CL2DCE_FET;
  1911. parameters = NUMBER_PARAMETERS;
  1912. }
  1913. else
  1914. {
  1915. DebugPrintEx(DEBUG_ERR,"Parse: Bad E values");
  1916. return FALSE;
  1917. }
  1918. break;
  1919. case 'H':
  1920. // Debug report transmitted HDLC frames +FHT
  1921. if ( char_1 == 'T' )
  1922. {
  1923. --i;
  1924. cl2_comm->command[comm_numb] = CL2DCE_FHT;
  1925. parameters = STRING_PARAMETER;
  1926. }
  1927. // Debug report received HDLC frames +FHR
  1928. else if ( char_1 == 'R' )
  1929. {
  1930. --i;
  1931. cl2_comm->command[comm_numb] = CL2DCE_FHR;
  1932. parameters = STRING_PARAMETER;
  1933. }
  1934. // Report hang up. +FHNG.
  1935. else if ( char_1 == 'N' && char_2 == 'G' )
  1936. {
  1937. cl2_comm->command[comm_numb] = CL2DCE_FHNG;
  1938. parameters = NUMBER_PARAMETERS;
  1939. DebugPrintEx(DEBUG_MSG, "Found FHNG");
  1940. pTG->fFoundFHNG = TRUE;
  1941. }
  1942. else
  1943. {
  1944. DebugPrintEx(DEBUG_ERR,"Parse: Bad H values");
  1945. return FALSE;
  1946. }
  1947. break;
  1948. case 'N':
  1949. // Report NSF frame reciept.
  1950. if ( char_1 == 'S' && char_2 == 'F' )
  1951. {
  1952. cl2_comm->command[comm_numb] = CL2DCE_FNSF;
  1953. parameters = NUMBER_PARAMETERS;
  1954. }
  1955. // Report NSS frame reciept.
  1956. else if ( char_1 == 'S' && char_2 == 'S' )
  1957. {
  1958. cl2_comm->command[comm_numb] = CL2DCE_FNSS;
  1959. parameters = NUMBER_PARAMETERS;
  1960. }
  1961. // Report NSC frame reciept.
  1962. else if ( char_1 == 'S' && char_2 == 'C' )
  1963. {
  1964. cl2_comm->command[comm_numb] = CL2DCE_FNSC;
  1965. parameters = NUMBER_PARAMETERS;
  1966. }
  1967. else
  1968. {
  1969. DebugPrintEx(DEBUG_ERR,"Parse: Bad N values");
  1970. return FALSE;
  1971. }
  1972. break;
  1973. case 'P':
  1974. // Report poll request. +FPOLL
  1975. if ( char_1 == 'O' && char_2 == 'L' )
  1976. {
  1977. cl2_comm->command[comm_numb] = CL2DCE_FPOLL;
  1978. parameters = FALSE;
  1979. }
  1980. // Page Transfer Status Report +FPTS.
  1981. else if ( char_1 == 'T' && char_2 == 'S' )
  1982. {
  1983. cl2_comm->command[comm_numb] = CL2DCE_FPTS;
  1984. parameters = NUMBER_PARAMETERS;
  1985. }
  1986. else
  1987. {
  1988. DebugPrintEx(DEBUG_ERR,"Parse: Bad P values");
  1989. return FALSE;
  1990. }
  1991. break;
  1992. case 'T':
  1993. // Report remote ID +FTSI.
  1994. if ( char_1 == 'S' && char_2 == 'I' )
  1995. {
  1996. cl2_comm->command[comm_numb] = CL2DCE_FTSI;
  1997. parameters = STRING_PARAMETER;
  1998. }
  1999. else
  2000. {
  2001. DebugPrintEx(DEBUG_ERR,"Parse: Bad T values");
  2002. return FALSE;
  2003. }
  2004. break;
  2005. case 'V':
  2006. // Report voice request +FVOICE.
  2007. if ( char_1 == 'O' && char_2 == 'I' )
  2008. {
  2009. cl2_comm->command[comm_numb] = CL2DCE_FVOICE;
  2010. parameters = FALSE;
  2011. }
  2012. else
  2013. {
  2014. DebugPrintEx(DEBUG_ERR,"Parse: Bad V values");
  2015. return FALSE;
  2016. }
  2017. }
  2018. // Transfer the associated paramters to the parameter array.
  2019. if (parameters == NUMBER_PARAMETERS)
  2020. {
  2021. for (i+=1,j=0; lpbBuf[i] != '\r' && lpbBuf[i] != '\0'; ++i)
  2022. {
  2023. // Skip past the non numeric characters.
  2024. if ( lpbBuf[i] < '0' || lpbBuf[i] > '9' )
  2025. {
  2026. continue;
  2027. }
  2028. /* Convert the character representation of the numeric
  2029. parameter into a true number, and store in the
  2030. parameter list. */
  2031. cl2_comm->parameters[comm_numb][j] = 0;
  2032. for (; lpbBuf[i] >= '0' && lpbBuf[i] <= '9'; ++i)
  2033. {
  2034. cl2_comm->parameters[comm_numb][j] *= 10;
  2035. cl2_comm->parameters[comm_numb][j] += lpbBuf[i] - '0';
  2036. }
  2037. i--; // the last for loop advanced 'i' past the numeric.
  2038. j++; // get set up for next parameter
  2039. }
  2040. }
  2041. else if (parameters == STRING_PARAMETER )
  2042. {
  2043. // Skip the : that follows the +f command (eg +FTSI:)
  2044. if (lpbBuf[i+1] == ':')
  2045. {
  2046. i++;
  2047. }
  2048. // Also skip leading blanks
  2049. while (lpbBuf[i+1] == ' ')
  2050. {
  2051. i++;
  2052. }
  2053. for (i+=1, j=0; (j < MAX_PARAM_LENGTH-1) &&
  2054. (c = lpbBuf[i]) != '\r' && c != '\n' && c != '\0'; ++i, ++j)
  2055. {
  2056. cl2_comm->parameters[comm_numb][j] = c;
  2057. if ( lpbBuf[i] == '\"' )
  2058. {
  2059. --j;
  2060. }
  2061. }
  2062. cl2_comm->parameters[comm_numb][j] = '\0';
  2063. }
  2064. // No parameters, so just skip to end of line.
  2065. else
  2066. {
  2067. for(; (c=lpbBuf[i]) != '\r' && c != '\n' && c != '\0'; ++i)
  2068. ;
  2069. }
  2070. if (cl2_comm->command[comm_numb] == CL2DCE_FHNG)
  2071. {
  2072. pTG->dwFHNGReason = cl2_comm->parameters[comm_numb][0];
  2073. DebugPrintEx(DEBUG_MSG, "Found FHNG, reason = %d", pTG->dwFHNGReason);
  2074. }
  2075. // Increment command count.
  2076. ++comm_numb;
  2077. break;
  2078. default:
  2079. break;
  2080. }
  2081. }
  2082. cl2_comm->comm_count = (USHORT)comm_numb;
  2083. return TRUE;
  2084. }