/*++ Copyright (c) 1997 Microsoft Corporation Module Name: class2.c Abstract: This is the main source for Class2 specific functions for fax-modem T.30 driver Author: Source base was originated by Win95 At Work Fax package. RafaelL - July 1997 - port to NT Revision History: --*/ #include "prep.h" #include "oemint.h" #include "efaxcb.h" #include "tiff.h" #include "glbproto.h" #include "t30gl.h" #include "cl2spec.h" extern WORD CodeToBPS[16]; extern UWORD rguwClass2Speeds[]; // Here is the table we are using so far for manufacturer specific stuff MFRSPEC Class2ModemTable[] = { { "", "Practical Peripherals", "PM14400FXPPM", "", 1, 2, "", FALSE, FALSE, FALSE, FALSE }, { "", "Practical Peripherals", "PM9600FXMT", "", 1, 2, "", FALSE, FALSE, FALSE ,FALSE}, { "", "Everex Systems", "Everfax 24/96E", "", 0, 2, "", FALSE, FALSE, FALSE, FALSE }, { "", "ROCKWELL", "V.32AC", "", 1, 2, "", FALSE, FALSE, FALSE, FALSE }, { "", "ROCKWELL", "RC9624AC", "", 1, 2, "", FALSE, FALSE, FALSE, FALSE }, { "", "Multi-Tech", "MT1432BA", "", 0, 0, "", FALSE, FALSE, FALSE, FALSE }, { "", "SIERRA", "SX196", "", 1, 0, "", TRUE, FALSE, FALSE, FALSE }, { "", "EXAR", "ROCKWELL 144DP", "", 1, 0, "", FALSE, TRUE, FALSE, FALSE }, { "", "ELSA", "MicroLink 2460TL", "", 1, 0, "", FALSE, TRUE, FALSE, FALSE }, { "", "GVC", "ROCKWELL 144DP", "", 1, 0, "", FALSE, TRUE, TRUE , FALSE }, // Intel144Ex { "", "ADC", "SL144V32", "", 1, 0, "", FALSE, TRUE, FALSE, FALSE }, { "", "UMC", "", "", 1, 0, "", FALSE, TRUE, FALSE ,FALSE}, { "", "NetComm", "", "", 1, 0, "", FALSE, TRUE, FALSE, FALSE }, { "", "HALCYON", "Bit Blitzer", "", 0, 0, "", FALSE, FALSE, FALSE, FALSE }, { "", "", "", "", 1, 0, "", FALSE, FALSE, FALSE, FALSE } }; void Class2Init( PThrdGlbl pTG ) { pTG->lpCmdTab = 0; pTG->Class2bDLEETX[0] = DLE; pTG->Class2bDLEETX[1] = ETX; pTG->Class2bDLEETX[2] = 0; sprintf( pTG->cbszFDT, "AT+FDT\r" ); sprintf( pTG->cbszINITIAL_FDT, "AT+FDT=%%1d,%%1d,%%1d,%%1d\r" ); sprintf( pTG->cbszFDR, "AT+FDR\r" ); sprintf( pTG->cbszFPTS, "AT+FPTS=%%d\r" ); sprintf( pTG->cbszFCR, "AT+FCR=1\r" ); sprintf( pTG->cbszFCQ, "AT+FCQ=0\r" ); sprintf( pTG->cbszFBUG, "AT+FBUG=0\r" ); sprintf( pTG->cbszSET_FBOR, "AT+FBOR=%%d\r" ); // DCC - set High Res, Huffman, no ECM/BFT, default all others. sprintf( pTG->cbszFDCC_ALL, "AT+FDCC=1,%%d,,,0,0,0,\r" ); sprintf( pTG->cbszFDCC_RECV_ALL, "AT+FDCC=1,%%d,2,2,0,0,0,\r" ); sprintf( pTG->cbszFDIS_RECV_ALL, "AT+FDIS=1,%%d,2,2,0,0,0,\r" ); sprintf( pTG->cbszFDCC_RES, "AT+FDCC=1\r" ); sprintf( pTG->cbszFDCC_BAUD, "AT+FDCC=1,%%d\r" ); sprintf( pTG->cbszFDIS_BAUD, "AT+FDIS=1,%%d\r" ); sprintf( pTG->cbszFDIS_IS, "AT+FDIS?\r" ); sprintf( pTG->cbszFDIS_NOQ_IS, "AT+FDIS\r" ); sprintf( pTG->cbszFDCC_IS, "AT+FDCC?\r" ); sprintf( pTG->cbszFDIS_STRING, "+FDIS" ); sprintf( pTG->cbszFDIS, "AT+FDIS=%%1d,%%1d,%%1d,%%1d,%%1d,0,0,0\r" ); sprintf( pTG->cbszZERO, "0" ); sprintf( pTG->cbszONE, "1" ); sprintf( pTG->cbszQUERY_S1, "ATS1?\r" ); sprintf( pTG->cbszRING, "RING" ); sprintf( pTG->cbszCLASS2_ATI, "ATI\r" ); sprintf( pTG->cbszCLASS2_FMFR, "AT+FMFR?\r" ); sprintf( pTG->cbszCLASS2_FMDL, "AT+FMDL?\r" ); sprintf( pTG->cbszFDT_CONNECT, "CONNECT" ); sprintf( pTG->cbszFDT_CNTL_Q, "" ); sprintf( pTG->cbszFCON, "+FCON" ); sprintf( pTG->cbszGO_CLASS2, "AT+FCLASS=2\r" ); sprintf( pTG->cbszFLID, "AT+FLID=\"%%s\"\r" ); sprintf( pTG->cbszENDPAGE, "AT+FET=0\r" ); sprintf( pTG->cbszENDMESSAGE, "AT+FET=2\r" ); sprintf( pTG->cbszCLASS2_QUERY_CLASS,"AT+FCLASS=?\r" ); sprintf( pTG->cbszCLASS2_GO_CLASS0, "AT+FCLASS=0\r" ); sprintf( pTG->cbszCLASS2_ATTEN, "AT\r" ); sprintf( pTG->cbszATA, "ATA\r" ); sprintf( pTG->cbszCLASS2_NODIALTONE, "NO DIALTONE"); sprintf( pTG->cbszCLASS2_HANGUP, "ATH0\r" ); sprintf( pTG->cbszCLASS2_CALLDONE, "ATS0=0\r" ); sprintf( pTG->cbszCLASS2_ABORT, "AT+FK\r" ); sprintf( pTG->cbszCLASS2_DIAL, "ATD%%c %%s\r" ); sprintf( pTG->cbszCLASS2_NODIALTONE, "NO DIALTONE" ); sprintf( pTG->cbszCLASS2_BUSY, "BUSY" ); sprintf( pTG->cbszCLASS2_NOANSWER, "NO ANSWER" ); sprintf( pTG->cbszCLASS2_OK, "OK" ); sprintf( pTG->cbszCLASS2_FHNG, "+FHNG" ); sprintf( pTG->cbszCLASS2_ERROR, "ERROR" ); Class2SetProtParams(pTG, &pTG->Inst.ProtParams); } BOOL T30Cl2Tx( PThrdGlbl pTG, LPSTR szPhone ) // If lpszSection is NON-NULL, we will override our internal CurrentMSPEC // structure based on the settings in the specified section. // { LPSTR lpszSection = pTG->FComModem.rgchKey; USHORT uRet1, uRet2; BYTE bBuf[200], bTempBuf[200+RESPONSE_BUF_SIZE]; LPBYTE lpbyte; UWORD Encoding, Res, PageWidth, PageLength, uwLen, uwRet; BYTE bIDBuf[200+max(MAXTOTALIDLEN,20)+4]; CHAR szTSI[max(MAXTOTALIDLEN,20)+4]; BOOL fBaudChanged; BOOL RetCode; uRet2 = 0; if(!(pTG->lpCmdTab = iModemGetCmdTabPtr(pTG))) { (MyDebugPrint (pTG, LOG_ALL, "<> Class2Caller: iModemGetCmdTabPtr failed.\n\r")); uRet1 = T30_CALLFAIL; pTG->fFatalErrorWasSignaled = 1; SignalStatusChange(pTG, FS_FATAL_ERROR); RetCode = FALSE; goto done; } // first get SEND_CAPS if possible. If using PSI (IFAX/Winpad) then we // can't make callback this on the Sender. Only on Receiver! Otherwise // we deadlock & hang in PSI #ifdef PSI if(!Class2GetBC(pTG, BC_NONE)) // Set it to some defaults! #else if(!Class2GetBC(pTG, SEND_CAPS)) // get send caps #endif { uRet1 = T30_CALLFAIL; pTG->fFatalErrorWasSignaled = 1; SignalStatusChange(pTG, FS_FATAL_ERROR); RetCode = FALSE; goto done; } // Go to Class2 if(!iModemGoClass(pTG, 2)) { (MyDebugPrint (pTG, LOG_ALL, "Class2Caller: Failed to Go to Class 2 \n\r")); uRet1 = T30_CALLFAIL; pTG->fFatalErrorWasSignaled = 1; SignalStatusChange(pTG, FS_FATAL_ERROR); RetCode = FALSE; goto done; } // Begin by checking for manufacturer and ATI code. // Look this up against the modem specific table we // have and set up the send strings needed for // this modem. if(!Class2GetModemMaker(pTG )) { (MyDebugPrint (pTG, LOG_ALL, "Call to GetModemMaker failed\n\r")); // Ignore failure!!! } // set manufacturer specific strings Class2SetMFRSpecific(pTG, lpszSection); // Get the capabilities of the software. I am only using this // right now for the TSI field (below where I send +FLID). // Really, this should also be used instead of the hardcoded DIS // values below. // ALL COMMANDS LOOK FOR MULTILINE RESPONSES WHILE MODEM IS ONHOOK. // A "RING" COULD APPEAR AT ANY TIME! _fmemset((LPB)szTSI, 0, strlen(szTSI)); Class2SetDIS_DCSParams(pTG, SEND_CAPS, (LPUWORD)&Encoding, (LPUWORD)&Res, (LPUWORD)&PageWidth, (LPUWORD)&PageLength, (LPSTR) szTSI); bIDBuf[0] = '\0'; uwLen = (UWORD)wsprintf(bIDBuf, pTG->cbszFLID, (LPSTR)szTSI); if(!Class2iModemDialog(pTG, bIDBuf, uwLen, LOCALCOMMAND_TIMEOUT, TRUE, 0, pTG->cbszCLASS2_OK, pTG->cbszCLASS2_ERROR, (C2PSTR) NULL)) { (MyDebugPrint (pTG, LOG_ALL, "Local ID failed\n\r")); // ignore failure } // // Turn off ECM - don't do for sierra type modems! // if (!pTG->CurrentMFRSpec.bIsSierra) // if(!Class2iModemDialog(pTG->cbszFECM, sizeof(pTG->cbszFECM)-1, // LOCALCOMMAND_TIMEOUT, TRUE, 0, pTG->cbszCLASS2_OK, // pTG->cbszCLASS2_ERROR, (C2PSTR) NULL)) // { // (MyDebugPrint (pTG, LOG_ALL, "FECM failed\n\r")); // // Ignore ECM failure!!! // } // Turn off Bug mode if(!Class2iModemDialog(pTG, pTG->cbszFBUG, (UWORD) (strlen(pTG->cbszFBUG) ), LOCALCOMMAND_TIMEOUT, TRUE, 0, pTG->cbszCLASS2_OK, pTG->cbszCLASS2_ERROR, (C2PSTR) NULL)) { (MyDebugPrint (pTG, LOG_ALL, "FBUG failed\n\r")); // Ignore FBUG failure!!! } // Find out what the default DIS is if (!pTG->CurrentMFRSpec.bIsExar) { if(!(uwRet=Class2iModemDialog(pTG, pTG->cbszFDIS_IS, (UWORD) (strlen(pTG->cbszFDIS_IS) ), LOCALCOMMAND_TIMEOUT, TRUE, 0, pTG->cbszCLASS2_OK, pTG->cbszCLASS2_ERROR, (C2PSTR) NULL))) { (MyDebugPrint (pTG, LOG_ALL, "FDIS failed\n\r")); // ignore } } // See if the reply was ERROR or timeout, if so try a different command // Exar modems, for example, don't take AT+FDIS? if ( ( uwRet == 2) || (uwRet == 0) || pTG->CurrentMFRSpec.bIsExar) { // FDIS did not work!!! Try FDCC? if(!(uwRet=Class2iModemDialog(pTG, pTG->cbszFDCC_IS, (UWORD) (strlen(pTG->cbszFDCC_IS) ), LOCALCOMMAND_TIMEOUT, TRUE, 0, pTG->cbszCLASS2_OK, pTG->cbszCLASS2_ERROR, (C2PSTR) NULL))) { // Ignore } if ( (uwRet == 2) || (uwRet == 0 ) ) { // The FDCC failed - maybe it is an Exar that likes FDIS? // try that if(!(uwRet=Class2iModemDialog(pTG, pTG->cbszFDIS_IS, (UWORD) (strlen(pTG->cbszFDIS_IS) ), LOCALCOMMAND_TIMEOUT, TRUE, 0, pTG->cbszCLASS2_OK, pTG->cbszCLASS2_ERROR, (C2PSTR) NULL))) { //ignore } // Maybe it is the Class 2 modem referred to in // Elliot bug #1238 that wants FDIS without a // question mark if ( (uwRet == 2) || (uwRet == 0 ) ) { if(!(uwRet=Class2iModemDialog(pTG, pTG->cbszFDIS_NOQ_IS, (UWORD) (strlen(pTG->cbszFDIS_NOQ_IS) ), LOCALCOMMAND_TIMEOUT, TRUE, 0, pTG->cbszCLASS2_OK, (C2PSTR) NULL))) { // No FDIS, FDCC worked - quit! (MyDebugPrint (pTG, LOG_ALL, "<> No FDIS? or FDCC? worked\n\r")); uRet1 = T30_CALLFAIL; pTG->fFatalErrorWasSignaled = 1; SignalStatusChange(pTG, FS_FATAL_ERROR); RetCode = FALSE; goto done; } } } // If the first character in the reply before a number // is a ',', insert a '1' for normal & fine res (Exar hack) for (lpbyte = pTG->lpbResponseBuf2; *lpbyte != '\0'; lpbyte++) { if (*lpbyte == ',') { // found a leading comma bTempBuf[0] = '\0'; _fstrcpy((LPSTR)bBuf, (LPSTR)pTG->cbszONE); wsprintf((LPSTR)bTempBuf, "%s%s",(LPSTR)bBuf, lpbyte); _fstrcpy(lpbyte, bTempBuf); (MyDebugPrint (pTG, LOG_ALL, "Leading comma in DCC string =\n\r", (LPSTR)&pTG->lpbResponseBuf2)); } if ( (*lpbyte >= '0') && (*lpbyte <= '9') ) break; } } // If the repsonse was just a number string without "+FDIS" in front // of it, add the +FDIS. Some modem reply with it, some do not. The // general parsing algorithm used below in Class2ResponseAction needs // to know the command that the numbers refer to. if ( pTG->lpbResponseBuf2[0] != '\0' && (Class2_fstrstr((LPSTR)pTG->lpbResponseBuf2, (LPSTR)pTG->cbszFDIS_STRING)==NULL)) { // did not get the FDIS in the response! bTempBuf[0] = '\0'; _fstrcpy((LPSTR)bBuf, (LPSTR)pTG->cbszFDIS_STRING); wsprintf((LPSTR)bTempBuf, "%s: %s",(LPSTR)bBuf, (LPSTR)pTG->lpbResponseBuf2); _fstrcpy(pTG->lpbResponseBuf2, bTempBuf); } (MyDebugPrint (pTG, LOG_ALL, "\n\rReceived %s from FDIS\r\n", (LPSTR)(&(pTG->lpbResponseBuf2)))); // Process default DIS to see if we have to send a DCC to change // it. Some modems react badly to just sending a DCC with ",,," // so we can't rely on the modem keeping DIS parameters unchanged // after a DCC like that. We'll use the FDISResponse routine to load // the default DIS values into a PCB structure if ( Class2ResponseAction(pTG, (LPPCB) &pTG->DISPcb) == FALSE ) { (MyDebugPrint (pTG, LOG_ALL, "Failed to process FDIS Response\n\r")); uRet1 = T30_CALLFAIL; pTG->fFatalErrorWasSignaled = 1; SignalStatusChange(pTG, FS_FATAL_ERROR); RetCode = FALSE; goto done; } (MyDebugPrint (pTG, LOG_ALL, "pTG->DISPcb baud value is %d\n\r", pTG->DISPcb.Baud)); fBaudChanged = FALSE; // See if we have to change the baud rate to a lower value. // This only happens if the user set an ini string constraining // the high end speed or if the user turned off V.17 for sending // Check the V.17 inhibit and lower baud if necessary if ( (pTG->DISPcb.Baud > 3) && (!pTG->ProtParams2.fEnableV17Send) ) { (MyDebugPrint (pTG, LOG_ALL, "Lowering baud from %d for V.17 inihibit\n\r", CodeToBPS[pTG->DISPcb.Baud])); pTG->DISPcb.Baud = 3; //9600 won't use V.17 fBaudChanged = TRUE; } // - commented out 3/6/95 by JosephJ (this code was never checked in -- it // fixed one modem and didn't fix another. // else if (pTG->DISPcb.Baud == 5) // { // // Several 14.4K modems require us to explicitly set // // +FDCC=1,5 or ,5 to work, else they send at 2400! // // So force the specification of +FDCC // (MyDebugPrint (pTG, LOG_ALL, "Faking fBaudChanged for 14.4K modems\n\r")); // fBaudChanged=TRUE; // } // Now see if the high end baud rate has been constrained if ( (pTG->ProtParams2.HighestSendSpeed != 0) && (CodeToBPS[pTG->DISPcb.Baud] > (WORD)pTG->ProtParams2.HighestSendSpeed)) { (MyDebugPrint (pTG, LOG_ALL, "Have to lower baud from %d to %d\n\r", CodeToBPS[pTG->DISPcb.Baud], pTG->ProtParams2.HighestSendSpeed)); fBaudChanged = TRUE; switch (pTG->ProtParams2.HighestSendSpeed) { case 2400: pTG->DISPcb.Baud = 0; break; case 4800: pTG->DISPcb.Baud = 1; break; case 7200: pTG->DISPcb.Baud = 2; break; case 9600: pTG->DISPcb.Baud = 3; break; case 12000: pTG->DISPcb.Baud = 4; break; default: (MyDebugPrint (pTG, LOG_ALL, "Bad HighestSpeed\n\r")); uRet1 = T30_CALLFAIL; pTG->fFatalErrorWasSignaled = 1; SignalStatusChange(pTG, FS_FATAL_ERROR); RetCode = FALSE; goto done; break; } } // Now, look and see if any of the values in the DIS are "bad" // That is, make sure we can send high res and we are not // claiming that we are sending MR or MMR. Also, see if we changed // the baud rate. if ((pTG->DISPcb.Resolution & AWRES_mm080_077) && ( pTG->DISPcb.Encoding == MH_DATA) && (!fBaudChanged) ) { //Do nothing - leave DIS alone! (MyDebugPrint (pTG, LOG_ALL, "no need to change DIS\n\r")); } else { // Send DCC command to the modem to set it up // Do the minimum necessary - only set resoultion if possible // (Again, this is because some modems don't like FDCC). if ( (pTG->DISPcb.Encoding == MH_DATA) && (!fBaudChanged) ) { if(!Class2iModemDialog(pTG, pTG->cbszFDCC_RES, (UWORD) (strlen(pTG->cbszFDCC_RES) ), LOCALCOMMAND_TIMEOUT, TRUE, 0, pTG->cbszCLASS2_OK, (C2PSTR) NULL)) { uRet1 = T30_CALLFAIL; pTG->fFatalErrorWasSignaled = 1; SignalStatusChange(pTG, FS_FATAL_ERROR); RetCode = FALSE; goto done; } } else if ( (pTG->DISPcb.Encoding == MH_DATA) && (fBaudChanged) ) { // Changed the baud rate, but Encoding is OK. uwLen=(UWORD)wsprintf((LPSTR)bBuf, pTG->cbszFDCC_BAUD, pTG->DISPcb.Baud); if(!Class2iModemDialog(pTG, bBuf, uwLen, LOCALCOMMAND_TIMEOUT, TRUE, 0, pTG->cbszCLASS2_OK, (C2PSTR) NULL)) { uRet1 = T30_CALLFAIL; pTG->fFatalErrorWasSignaled = 1; SignalStatusChange(pTG, FS_FATAL_ERROR); RetCode = FALSE; goto done; } } else // the encoding format has changed { uwLen=(UWORD)wsprintf((LPSTR)bBuf, pTG->cbszFDCC_ALL, pTG->DISPcb.Baud); if(!Class2iModemDialog(pTG, bBuf, uwLen, LOCALCOMMAND_TIMEOUT, TRUE, 0, pTG->cbszCLASS2_OK, (C2PSTR) NULL)) { uRet1 = T30_CALLFAIL; pTG->fFatalErrorWasSignaled = 1; SignalStatusChange(pTG, FS_FATAL_ERROR); RetCode = FALSE; goto done; } } } // Do BOR based on the value from the modem table set in // Class2SetMFRSpecific uwLen = (UWORD)wsprintf(bBuf, pTG->cbszSET_FBOR, pTG->CurrentMFRSpec.iSendBOR); if(!Class2iModemDialog(pTG, bBuf, uwLen, LOCALCOMMAND_TIMEOUT, TRUE, 0, pTG->cbszCLASS2_OK, pTG->cbszCLASS2_ERROR, (C2PSTR) NULL)) { (MyDebugPrint (pTG, LOG_ALL, "FBOR failed\n\r")); // Ignore BOR failure!!! } // Dial the number // have to call hangup on every path out of here // after Dial is called. If Dial fails, it calls Hangup // if it succeeds we have to call Hangup when we're done SignalStatusChange(pTG, FS_DIALING); if((uRet2 = Class2Dial(pTG, szPhone)) != CONNECT_OK) { uRet1 = T30_DIALFAIL; if (! pTG->fFatalErrorWasSignaled) { pTG->fFatalErrorWasSignaled = 1; SignalStatusChange(pTG, FS_FATAL_ERROR); } RetCode = FALSE; goto done; } ICommGotAnswer(pTG ); // we should be using the sender msg here but that says Training // at speed=xxxx etc which we don't know, so we just use the // Recvr message which just says "negotiating" ICommStatus(pTG, T30STATR_TRAIN, 0, 0, 0); // Send the data uRet1 = (USHORT)Class2Send(pTG ); if ( uRet1 == T30_CALLDONE) { (MyDebugPrint (pTG, LOG_ALL, "******* DONE WITH CALL, ALL OK\r\n")); ICommStatus(pTG, T30STATS_SUCCESS, 0, 0, 0); // have to call hangup on every path out of here // we have to call Hangup here Class2ModemHangup(pTG ); SignalStatusChange(pTG, FS_COMPLETED); RetCode = TRUE; } else { (MyDebugPrint (pTG, LOG_ALL, "******* DONE WITH CALL, **** FAILED *****\r\n")); ICommStatus(pTG, T30STATS_FAIL, 0, 0, 0); // Make sure Modem is in OK state FComOutFilterClose(pTG ); FComXon(pTG, FALSE); // have to call hangup on every path out of here // Class2ModemABort calls Hangup Class2ModemAbort(pTG ); if (! pTG->fFatalErrorWasSignaled) { pTG->fFatalErrorWasSignaled = 1; SignalStatusChange(pTG, FS_FATAL_ERROR); } RetCode = FALSE; } BG_CHK(uRet1==T30_CALLDONE || uRet1==T30_CALLFAIL); uRet2 = 0; done: BG_CHK((uRet1 & 0xFF) == uRet1); BG_CHK((uRet2 & 0xFF) == uRet2); return RetCode; } BOOL Class2Send(PThrdGlbl pTG) { LPBUFFER lpbf; SWORD swRet; ULONG lTotalLen=0; PCB Pcb; USHORT uTimeout=30000, PagesSent; BOOL err_status, fAllPagesOK = TRUE; BCwithTEXT bc; UWORD Encoding, Res, PageWidth, PageLength, uwLen; BYTE bFDISBuf[200]; CHAR szTSI[max(MAXTOTALIDLEN,20)+4]; BYTE bNull = 0; DWORD TiffConvertThreadId; /* * We have just dialed... Now we have to look for the FDIS response from * the modem. It will be followed by an OK - hunt for the OK. */ if(!Class2iModemDialog(pTG, NULL, 0, STARTSENDMODE_TIMEOUT, TRUE, 0, pTG->cbszCLASS2_OK, (C2PSTR) NULL)) { err_status = T30_CALLFAIL; return err_status; } // The response will be in pTG->lpbResponseBuf2 - this is loaded in // Class2iModemDialog. // (MyDebugPrint (pTG, LOG_ALL, "Received %s\r", (LPSTR)(&(pTG->lpbResponseBuf2)))); // Parse through the received strings, looking for the DIS, CSI, // NSF if ( Class2ResponseAction(pTG, (LPPCB) &Pcb) == FALSE ) { (MyDebugPrint (pTG, LOG_ALL, "Failed to process ATD Response\n\r")); err_status = T30_CALLFAIL; return err_status; } //Now that pcb is set up, call ICommReceiveCaps to tell icomfile Class2InitBC(pTG, (LPBC)&bc, sizeof(bc), RECV_CAPS); Class2PCBtoBC(pTG, (LPBC)&bc, sizeof(bc), &Pcb); // Class2 modems do their own negotiation & we need to stay in sync // Otherwise, we might send MR data while the modem sends a DCS // saying it is MH. This happens a lot with Exar modems because // they dont accept an FDIS= command during the call. // FIX: On all Class2 sends force remote caps to always be MH // Then in efaxrun we will always negotiate MH & encode MH // We are relying on the fact that (a) it seems that all/most // Class2 modems negotiate MH (b) Hopefully ALL Exar ones // negotiate MH and (c) We will override all non-Exar modem's // intrinsic negotiation by sending an AT+FDIS= just before the FDT // Also (d) This change makes our behaviour match Snowball exactly // so we will work no better or worse than it :-) bc.Fax.Encoding = MH_DATA; if( ICommRecvCaps(pTG, (LPBC)&bc) == FALSE ) { (MyDebugPrint (pTG, LOG_ALL, "Failed return from ICommRecvCaps.\r\n")); err_status = T30_CALLFAIL; return err_status; } // now get the SEND_PARAMS if(!Class2GetBC(pTG, SEND_PARAMS)) // sleep until we get it { err_status = T30_CALLFAIL; return err_status; } #ifdef FILET30 // Send the raw capabilities string - most values // will be null, since CAS does not tell us things like // DIS, NSF, etc. But, we can put in the CSI. ICommRawCaps(pTG, (LPBYTE) &bNull, (LPBYTE) &bNull, 0, NULL, 0); #endif ICommSetSendMode(pTG, FALSE, MY_BIGBUF_SIZE, MY_BIGBUF_ACTUALSIZE-4, FALSE); // Turn off flow control. FComXon(pTG, FALSE); // The Send params were set during the call to Class2GetBC // We'll use these to set the ID (for the TSI) and the DCS params // Send the FDT and get back the DCS. The FDT must be followed by // CONNECT and a ^Q (XON) // The FDT string must have the correct resolution and encoding // for this session. FDT=Encoding, Res, width, length // Encoding 0=MH, 1=MR,2=uncompressed,3=MMR // Res 0=200x100 (normal), 1=200x200 (fine) // PageWidth 0=1728pixels/215mm,1=2048/255,2=2432/303, // 3=1216/151,4=864/107 // PageLength 0=A4,1=B4,2=unlimited Class2SetDIS_DCSParams(pTG, SEND_PARAMS, (LPUWORD)&Encoding, (LPUWORD)&Res, (LPUWORD)&PageWidth, (LPUWORD)&PageLength, (LPSTR) szTSI); // // Current Win95 version of Class2 TX is limited to MH only. // While not changing this, we will at least allow MR selection in future. // if (!pTG->fTiffThreadCreated) { if (Encoding) { pTG->TiffConvertThreadParams.tiffCompression = TIFF_COMPRESSION_MR; } else { pTG->TiffConvertThreadParams.tiffCompression = TIFF_COMPRESSION_MH; } if (Res) { pTG->TiffConvertThreadParams.HiRes = 1; } else { pTG->TiffConvertThreadParams.HiRes = 0; // use LoRes TIFF file prepared by FaxSvc // pTG->lpwFileName[ wcslen(pTG->lpwFileName) - 1] = (unsigned short) ('$'); } _fmemcpy (pTG->TiffConvertThreadParams.lpszLineID, pTG->lpszPermanentLineID, 8); pTG->TiffConvertThreadParams.lpszLineID[8] = 0; (MyDebugPrint(pTG, LOG_ALL, "Creating TIFF helper thread \r\n")); pTG->hThread = CreateThread( NULL, 0, (LPTHREAD_START_ROUTINE) TiffConvertThreadSafe, (LPVOID) pTG, 0, &TiffConvertThreadId ); if (!pTG->hThread) { (MyDebugPrint(pTG, LOG_ERR, "<> TiffConvertThread create FAILED\r\n")); err_status = T30_CALLFAIL; return err_status; } pTG->fTiffThreadCreated = 1; pTG->AckTerminate = 0; pTG->fOkToResetAbortReqEvent = 0; if ( (pTG->RecoveryIndex >=0 ) && (pTG->RecoveryIndex < MAX_T30_CONNECT) ) { T30Recovery[pTG->RecoveryIndex].TiffThreadId = TiffConvertThreadId; T30Recovery[pTG->RecoveryIndex].CkSum = ComputeCheckSum( (LPDWORD) &T30Recovery[pTG->RecoveryIndex].fAvail, sizeof ( T30_RECOVERY_GLOB ) / sizeof (DWORD) - 1 ); } } // Even modems that take FDT=x,x,x,x don't seem to really do it // right. So, for now, just send FDIS followed by FDT except for // the EXAR modems!! if (pTG->CurrentMFRSpec.bIsExar) { if(!Class2iModemDialog(pTG, pTG->cbszFDT, (UWORD) (strlen(pTG->cbszFDT) ), STARTSENDMODE_TIMEOUT, TRUE, 0, pTG->cbszFDT_CONNECT,(C2PSTR) NULL)) { (MyDebugPrint (pTG, LOG_ALL, "Failed get response from initial FDT!!\r\n")); err_status = T30_CALLFAIL; return err_status; } // (MyDebugPrint (pTG, LOG_ALL, "FDT Received %s\r\n", (LPSTR)(&(pTG->lpbResponseBuf2)))); } else { uwLen = (WORD)wsprintf(bFDISBuf, pTG->cbszFDIS, Res, min(Pcb.Baud, pTG->DISPcb.Baud), PageWidth, PageLength, Encoding); if(!Class2iModemDialog(pTG, bFDISBuf, uwLen, LOCALCOMMAND_TIMEOUT, TRUE, 0, pTG->cbszCLASS2_OK, pTG->cbszCLASS2_ERROR, (C2PSTR) NULL)) { (MyDebugPrint (pTG, LOG_ALL, "Failed get response from FDIS!!\r\n")); // Ignore it -we are going to send what we have! } if(!Class2iModemDialog(pTG, pTG->cbszFDT, (UWORD) (strlen(pTG->cbszFDT) ), STARTSENDMODE_TIMEOUT, TRUE, 0, pTG->cbszFDT_CONNECT,(C2PSTR) NULL)) { (MyDebugPrint (pTG, LOG_ALL, "FDT Received %s\r\n",(LPSTR)(&(pTG->lpbResponseBuf2)))); (MyDebugPrint (pTG, LOG_ALL, "FDT to start first PAGE Failed!\n\r")); err_status = T30_CALLFAIL; return err_status; } (MyDebugPrint (pTG, LOG_ALL, "FDT Received %s\r\n", (LPSTR)(&(pTG->lpbResponseBuf2)))); } if (pTG->CurrentMFRSpec.fSkipCtrlQ) { (MyDebugPrint (pTG, LOG_ALL, "WARNING!! Skipping cntl-q - sending immedaitely after CONNECT\n\r")); } // Get the  from the COMM driver else if(!FComGetOneChar(pTG, 0x11)) { (MyDebugPrint (pTG, LOG_ALL, "Couldn't Get cntl q - we'll go ahead anyway\n\r")); } // Turn on flow control. FComXon(pTG, TRUE); // Search through Response for the DCS frame - need it so set // the correct zero stuffing if ( Class2ResponseAction(pTG, (LPPCB) &Pcb) == FALSE ) { (MyDebugPrint (pTG, LOG_ALL, "Failed to process FDT Response\n\r")); err_status = T30_CALLFAIL; return err_status; } // Got a response - see if baud rate is OK (MyDebugPrint (pTG, LOG_ALL, "Negotiated Baud Rate = %d, lower limit is %d\n\r", Pcb.Baud, pTG->ProtParams2.LowestSendSpeed)); if (CodeToBPS[Pcb.Baud] < (WORD)pTG->ProtParams2.LowestSendSpeed) { (MyDebugPrint (pTG, LOG_ALL, "Aborting due to too low baud rate!\n\r")); err_status = T30_CALLFAIL; return err_status; } // Use values obtained from the DCS frame to set zero stuffing. // (These were obtained by call to Class2ResponseAction above). // Zero stuffing is a function of minimum scan time (determined // by resolution and the returned scan minimum) and baud. // Fixed the Hack--added a Baud field // Init must be BEFORE SetStuffZero! FComOutFilterInit(pTG ); FComSetStuffZERO(pTG, Class2MinScanToBytesPerLine(pTG, Pcb.MinScan, (BYTE) Pcb.Baud, Pcb.Resolution)); PagesSent = 0; err_status = T30_CALLDONE; while ((swRet=ICommGetSendBuf(pTG, &lpbf, SEND_STARTPAGE)) == 0) { (MyDebugPrint (pTG, LOG_ALL, "IN MULTIPAGE WHILE LOOP. Pages Sent = %d\n\r", PagesSent)); //// faxTlog(("SENDING Reams of Page Data.....\r\n")); lTotalLen = 0; FComOverlappedIO(pTG, TRUE); // TRUE while ((swRet=ICommGetSendBuf(pTG, &lpbf, SEND_SEQ)) == 0) { BG_CHK(lpbf && lpbf->wLengthData > 0); lTotalLen += lpbf->wLengthData; (MyDebugPrint (pTG, LOG_ALL, "TOTAL LENGTH: %ld\r\n", lTotalLen)); if(!(Class2ModemSendMem(pTG, lpbf->lpbBegData, lpbf->wLengthData) & (MyFreeBuf(pTG, lpbf)))) { (MyDebugPrint (pTG, LOG_ALL, "Class2ModemSendBuf Failed\r\n")); err_status = T30_CALLFAIL; FComOverlappedIO(pTG, FALSE); return err_status; } if (pTG->fAbort) { (MyDebugPrint (pTG, LOG_ALL, "Abort during Send loop\r\n")); pTG->fAbort = FALSE; err_status = T30_CALLFAIL; FComOverlappedIO(pTG, FALSE); return err_status; } } // end of SEND_SEQ while FComOverlappedIO(pTG, FALSE); (MyDebugPrint (pTG, LOG_ALL, "OUT OF WHILE SEND_SEQ LOOP. \r\n")); (MyDebugPrint (pTG, LOG_ALL, "TOTAL LENGTH: %ld\r\n", lTotalLen)); // Terminate the Page with DLE-ETX if(!FComDirectAsyncWrite(pTG, pTG->Class2bDLEETX, 2)) { (MyDebugPrint (pTG, LOG_ALL, "Failed to terminate page with DLE-ETX\n\r")); err_status = T30_CALLFAIL; return err_status; } if(!Class2ModemDrain(pTG )) { (MyDebugPrint (pTG, LOG_ALL, "Failed to drain\n\r")); err_status = T30_CALLFAIL; return err_status; } //See if more pages to send... if ( ICommNextSend(pTG) == NEXTSEND_MPS ) { // We are about to send a second or more page. Terminate the // last page with FET=0, signalling a new one to come if(!Class2iModemDialog(pTG, pTG->cbszENDPAGE, (UWORD) (strlen(pTG->cbszENDPAGE) ), STARTSENDMODE_TIMEOUT, TRUE, 0, pTG->cbszCLASS2_OK, (C2PSTR)NULL)) { (MyDebugPrint (pTG, LOG_ALL, "END PAGE Processing Failed!\n\r")); err_status = T30_CALLFAIL; return err_status; } // Acknowledge that we sent the page // Parse the FPTS response and see if the page is good or bad. // Keep track of any bad pages in fAllPagesOK if (!ParseFPTS_SendAck(pTG )) fAllPagesOK = FALSE; PagesSent++; // Now, Send the FDT to start the next page (this was done for // the first page before entering the multipage loop). if(!Class2iModemDialog(pTG, pTG->cbszFDT, (UWORD) (strlen(pTG->cbszFDT) ), STARTSENDMODE_TIMEOUT, TRUE, 0, pTG->cbszFDT_CONNECT,(C2PSTR) NULL)) { (MyDebugPrint (pTG, LOG_ALL, "FDT to start next PAGE Failed!\n\r")); err_status = T30_CALLFAIL; return err_status; } // Get the  from the COMM driver if(!FComGetOneChar(pTG, 0x11)) { (MyDebugPrint (pTG, LOG_ALL, "Couldn't Get cntl q - we'll go ahead anyway\n\r")); } // Turn on flow control. FComXon(pTG, TRUE); } //if we do not have another page, do the else... else break; // All done sending pages... if ( err_status == T30_CALLFAIL) break; } //End of multipage while (MyDebugPrint (pTG, LOG_ALL, "OUT OF WHILE MULTIPAGE LOOP. ABOUT TO SEND FINAL.\r\n")); // // Purge input COM queue to purge all OKs // FComFlushInput(pTG); // Send end of message sequence if(!Class2iModemDialog(pTG, pTG->cbszENDMESSAGE, (UWORD) (strlen(pTG->cbszENDMESSAGE) ), STARTSENDMODE_TIMEOUT, TRUE, 0, pTG->cbszCLASS2_OK, (C2PSTR)NULL)) { (MyDebugPrint (pTG, LOG_ALL, "End message failed\n\r")); err_status = T30_CALLFAIL; return err_status; } // Acknowledge that we sent the page // Parse the FPTS response and see if the page is good or bad. if (!ParseFPTS_SendAck(pTG )) fAllPagesOK = FALSE; PagesSent++; FComOutFilterClose(pTG ); FComXon(pTG, FALSE); // If *any* page failed to send correctly, the call failed! if (!fAllPagesOK) err_status = T30_CALLFAIL; return err_status; } /************************************************************** Receive specific routines start here ***************************************************************/ BOOL T30Cl2Rx (PThrdGlbl pTG) // If lpszSection is NON-NULL, we will override our internal CurrentMSPEC // structure based on the settings in the specified section. { LPSTR lpszSection = pTG->FComModem.rgchKey; USHORT uRet1, uRet2; BYTE bBuf[200], bTempBuf[200+RESPONSE_BUF_SIZE]; UWORD uwLen, uwRet; UWORD Encoding, Res, PageWidth, PageLength; BYTE bIDBuf[200+max(MAXTOTALIDLEN,20)+4]; CHAR szCSI[max(MAXTOTALIDLEN,20)+4]; LPBYTE lpbyte; BOOL fBaudChanged; BOOL RetCode; (MyDebugPrint (pTG, LOG_ALL, "Entering Class2 Callee\n\r")); uRet2 = 0; if(!(pTG->lpCmdTab = iModemGetCmdTabPtr(pTG ))) { (MyDebugPrint (pTG, LOG_ALL, "<> Class2Callee: iModemGetCmdTabPtr failed.\n\r")); uRet1 = T30_CALLFAIL; pTG->fFatalErrorWasSignaled = 1; SignalStatusChange(pTG, FS_FATAL_ERROR); RetCode = FALSE; goto done; } // first get SEND_CAPS if(!Class2GetBC(pTG, SEND_CAPS)) // sleep until we get it { uRet1 = T30_CALLFAIL; pTG->fFatalErrorWasSignaled = 1; SignalStatusChange(pTG, FS_FATAL_ERROR); RetCode = FALSE; goto done; } // Go to Class2 // Elliot Bug#3421 -- incoming RING sometimes clobbers AT+FCLASS=1/2 cmd. if(pTG->lpCmdTab->dwFlags & fMDMSP_ANS_GOCLASS_TWICE) iModemGoClass(pTG, 2); if(!iModemGoClass(pTG, 2)) { (MyDebugPrint (pTG, LOG_ALL, "Class2Callee: Failed to Go to Class 2 \n\r")); uRet1 = T30_CALLFAIL; pTG->fFatalErrorWasSignaled = 1; SignalStatusChange(pTG, FS_FATAL_ERROR); RetCode = FALSE; goto done; } // Begin by checking for manufacturer and ATI code. // Look this up against the modem specific table we // have and set up the receive strings needed for // this modem. if(!Class2GetModemMaker(pTG )) { (MyDebugPrint (pTG, LOG_ALL, "Call to GetModemMaker failed\n\r")); // Ignore failure!!! } // set manufacturer specific strings Class2SetMFRSpecific(pTG, lpszSection); // Get the capabilities of the software. I am only using this // right now for the CSI field (below where I send +FLID). // Really, this should also be used instead of the hardcoded DIS // values below. // ALL COMMANDS LOOK FOR MULTILINE RESPONSES WHILE MODEM IS ONHOOK. // A "RING" COULD APPEAR AT ANY TIME! _fmemset((LPB)szCSI, 0, sizeof(szCSI)); Class2SetDIS_DCSParams(pTG, SEND_CAPS, (LPUWORD)&Encoding, (LPUWORD)&Res, (LPUWORD)&PageWidth, (LPUWORD)&PageLength, (LPSTR) szCSI); // Find out what the default DIS is if (!pTG->CurrentMFRSpec.bIsExar) { if(!(uwRet=Class2iModemDialog(pTG, pTG->cbszFDIS_IS, (UWORD) (strlen(pTG->cbszFDIS_IS) ), LOCALCOMMAND_TIMEOUT, TRUE, 0, pTG->cbszCLASS2_OK, pTG->cbszCLASS2_ERROR, (C2PSTR) NULL))) { (MyDebugPrint (pTG, LOG_ALL, "FDIS? failed\n\r")); // ignore } } // See if the reply was ERROR or timeout, if so try a different command // Exar modems, for example, don't take AT+FDIS? if ( ( uwRet == 2) || (uwRet == 0) || pTG->CurrentMFRSpec.bIsExar) { // FDIS did not work!!! Try FDCC? if(!(uwRet=Class2iModemDialog(pTG, pTG->cbszFDCC_IS, (UWORD) (strlen(pTG->cbszFDCC_IS) ), LOCALCOMMAND_TIMEOUT, TRUE, 0, pTG->cbszCLASS2_OK, pTG->cbszCLASS2_ERROR, (C2PSTR) NULL))) { // Ignore } if ( (uwRet == 2) || (uwRet == 0 ) ) { // The FDCC failed - maybe it is an Exar that likes FDIS? // try that if(!(uwRet=Class2iModemDialog(pTG, pTG->cbszFDIS_IS, (UWORD) (strlen(pTG->cbszFDIS_IS) ), LOCALCOMMAND_TIMEOUT, TRUE, 0, pTG->cbszCLASS2_OK, pTG->cbszCLASS2_ERROR, (C2PSTR) NULL))) { //ignore } // Maybe it is the Class 2 modem referred to in // Elliot bug #1238 that wants FDIS without a // question mark if ( (uwRet == 2) || (uwRet == 0 ) ) { if(!(uwRet=Class2iModemDialog(pTG, pTG->cbszFDIS_NOQ_IS, (UWORD) (strlen(pTG->cbszFDIS_NOQ_IS) ), LOCALCOMMAND_TIMEOUT, TRUE, 0, pTG->cbszCLASS2_OK, (C2PSTR) NULL))) { // No FDIS, FDCC worked - quit! (MyDebugPrint (pTG, LOG_ALL, "<> No FDIS? or FDCC? worked\n\r")); uRet1 = T30_CALLFAIL; pTG->fFatalErrorWasSignaled = 1; SignalStatusChange(pTG, FS_FATAL_ERROR); RetCode = FALSE; goto done; } } } // If the first character in the reply before a number // is a ',', insert a '1' for normal & fine res (Exar hack) for (lpbyte = pTG->lpbResponseBuf2; *lpbyte != '\0'; lpbyte++) { if (*lpbyte == ',') { // found a leading comma bTempBuf[0] = '\0'; _fstrcpy((LPSTR)bBuf, (LPSTR)pTG->cbszONE); wsprintf((LPSTR)bTempBuf, "%s%s",(LPSTR)bBuf, lpbyte); _fstrcpy(lpbyte, bTempBuf); (MyDebugPrint (pTG, LOG_ALL, "Leading comma in DCC string =\n\r", (LPSTR)&pTG->lpbResponseBuf2)); } if ( (*lpbyte >= '0') && (*lpbyte <= '9') ) break; } } // If the repsonse was just a number string without "+FDIS" in front // of it, add the +FDIS. Some modem reply with it, some do not. The // general parsing algorithm used below in Class2ResponseAction needs // to know the command that the numbers refer to. if ( pTG->lpbResponseBuf2[0] != '\0' && (Class2_fstrstr( (LPSTR)pTG->lpbResponseBuf2, (LPSTR)pTG->cbszFDIS_STRING)==NULL)) { // did not get the FDIS in the response! bTempBuf[0] = '\0'; _fstrcpy((LPSTR)bBuf, (LPSTR)pTG->cbszFDIS_STRING); wsprintf((LPSTR)bTempBuf, "%s: %s",(LPSTR)bBuf, (LPSTR)pTG->lpbResponseBuf2); _fstrcpy(pTG->lpbResponseBuf2, bTempBuf); } (MyDebugPrint (pTG, LOG_ALL, "Received %s from FDIS?\r", (LPSTR)(&(pTG->lpbResponseBuf2)))); // Process default DIS to see if we have to send a DCC to change // it. Some modems react badly to just sending a DCC with ",,," // so we can't rely on the modem keeping DIS parameters unchanged // after a DCC like that. We'll use the FDISResponse routine to load // the default DIS values into a PCB structure if ( Class2ResponseAction(pTG, (LPPCB) &pTG->DISPcb) == FALSE ) { (MyDebugPrint (pTG, LOG_ALL, "Failed to process FDIS Response\n\r")); uRet1 = T30_CALLFAIL; pTG->fFatalErrorWasSignaled = 1; SignalStatusChange(pTG, FS_FATAL_ERROR); RetCode = FALSE; goto done; } fBaudChanged = FALSE; // See if we have to change the baud rate to a lower value. // This only happens if the user set an ini string inhibiting // V.17 receive if ( (pTG->DISPcb.Baud > 3) && (!pTG->ProtParams2.fEnableV17Recv) ) { (MyDebugPrint (pTG, LOG_ALL, "Lowering baud from %d for V.17 receive inihibit\n\r", CodeToBPS[pTG->DISPcb.Baud])); pTG->DISPcb.Baud = 3; //9600 won't use V.17 fBaudChanged = TRUE; } // - commented out 3/6/95 by JosephJ (this code was never checked in -- it // fixed one modem and didn't fix another. // else if (pTG->DISPcb.Baud == 5) // { // // Several 14.4K modems require us to explicitly set // // +FDCC=1,5 or ,5 to work, else they send at 2400! // // So force the specification of +FDCC // (MyDebugPrint (pTG, LOG_ALL, "Faking fBaudChanged for 14.4K modems\n\r")); // fBaudChanged=TRUE; // } // Now, look and see if any of the values in the DIS are "bad" // That is, make sure we can receive high res and we are not // claiming that we are capable of MR or MMR. Also, see if we changed // the baud rate. Also make sure we can receive wide pages. if ((pTG->DISPcb.Resolution & AWRES_mm080_077) && ( pTG->DISPcb.Encoding == MH_DATA) && (!fBaudChanged) && (pTG->DISPcb.PageLength == 2) && (pTG->DISPcb.PageWidth == 2) ) { //Do nothing - leave DIS alone! (MyDebugPrint (pTG, LOG_ALL, "no need to change DIS\n\r")); } else { // Send DCC command to the modem to set it up // Do the minimum necessary - only set resoultion if possible // (Again, this is because some modems don't like FDCC). if ( (pTG->DISPcb.Encoding == MH_DATA) && (!fBaudChanged) && (pTG->DISPcb.PageLength == 2) && (pTG->DISPcb.PageWidth == 2) ) { if(!Class2iModemDialog(pTG, pTG->cbszFDCC_RES, (UWORD) (strlen(pTG->cbszFDCC_RES) ), LOCALCOMMAND_TIMEOUT, TRUE, 0, pTG->cbszCLASS2_OK, (C2PSTR) NULL)) { //Ignore it } } else if ( (pTG->DISPcb.Encoding == MH_DATA) && (fBaudChanged) && (pTG->DISPcb.PageLength == 2) && (pTG->DISPcb.PageWidth == 2) ) { // Changed the baud rate, but Encoding is OK. uwLen=(USHORT)wsprintf((LPSTR)bBuf, pTG->cbszFDCC_BAUD, pTG->DISPcb.Baud); if(!Class2iModemDialog(pTG, bBuf, uwLen, LOCALCOMMAND_TIMEOUT, TRUE, 0, pTG->cbszCLASS2_OK, (C2PSTR) NULL)) { //Ignore it } } else // the encoding format has changed or page size is bad { uwLen=(USHORT)wsprintf((LPSTR)bBuf, pTG->cbszFDCC_RECV_ALL, pTG->DISPcb.Baud); if(!(uwRet=Class2iModemDialog(pTG, bBuf, uwLen, LOCALCOMMAND_TIMEOUT, TRUE, 0, pTG->cbszCLASS2_OK, pTG->cbszCLASS2_ERROR, (C2PSTR) NULL))) { // ignore it. } // If the FDCC failed, try FDIS. if ( (uwRet == 0) || (uwRet == 2) ) { uwLen=(USHORT)wsprintf((LPSTR)bBuf, pTG->cbszFDIS_RECV_ALL, pTG->DISPcb.Baud); if(!Class2iModemDialog(pTG, bBuf, uwLen, LOCALCOMMAND_TIMEOUT, TRUE, 0, pTG->cbszCLASS2_OK, pTG->cbszCLASS2_ERROR, (C2PSTR) NULL)) { // ignore it. } // if the above failed, try just setting the baud // rate and resolution with FDCC. if ( (uwRet == 0) || (uwRet == 2) ) { uwLen=(USHORT)wsprintf((LPSTR)bBuf, pTG->cbszFDCC_BAUD, pTG->DISPcb.Baud); if(!(uwRet=Class2iModemDialog(pTG, bBuf, uwLen, LOCALCOMMAND_TIMEOUT, TRUE, 0, pTG->cbszCLASS2_OK, pTG->cbszCLASS2_ERROR, (C2PSTR) NULL))) { // Ignore it } } // if the above failed, try just setting the baud // rate and resolution with FDIS. if ( (uwRet == 0) || (uwRet == 2) ) { uwLen=(USHORT)wsprintf((LPSTR)bBuf, pTG->cbszFDIS_BAUD, pTG->DISPcb.Baud); if(!(uwRet=Class2iModemDialog(pTG, bBuf, uwLen, LOCALCOMMAND_TIMEOUT, TRUE, 0, pTG->cbszCLASS2_OK, pTG->cbszCLASS2_ERROR, (C2PSTR) NULL))) { // Ignore it } } } } } // Enable Reception if(!Class2iModemDialog(pTG, pTG->cbszFCR, (UWORD) (strlen(pTG->cbszFCR) ), ANS_LOCALCOMMAND_TIMEOUT, TRUE, 0, pTG->cbszCLASS2_OK, pTG->cbszCLASS2_ERROR, (C2PSTR) NULL)) { (MyDebugPrint (pTG, LOG_ALL, "FCR failed\n\r")); // ignore failure } // // Turn off ECM - don't do for sierra type modems! // if (!pTG->CurrentMFRSpec.bIsSierra) // if(!Class2iModemDialog(pTG->cbszFECM, sizeof(pTG->cbszFECM)-1, // ANS_LOCALCOMMAND_TIMEOUT, TRUE, 0, pTG->cbszCLASS2_OK, // pTG->cbszCLASS2_ERROR, (C2PSTR) NULL)) // { // (MyDebugPrint (pTG, LOG_ALL, "FECM failed\n\r")); // // Ignore ECM failure!!! // } // Turn off Copy Quality Checking - also skip for Sierra type modems if (!pTG->CurrentMFRSpec.bIsSierra) if(!Class2iModemDialog(pTG, pTG->cbszFCQ, (UWORD) (strlen(pTG->cbszFCQ) ), ANS_LOCALCOMMAND_TIMEOUT, TRUE, 0, pTG->cbszCLASS2_OK, pTG->cbszCLASS2_ERROR, (C2PSTR) NULL)) { (MyDebugPrint (pTG, LOG_ALL, "FCQ failed\n\r")); // Ignore CQ failure!!! } // Turn off Bug mode if(!Class2iModemDialog(pTG, pTG->cbszFBUG, (UWORD) (strlen(pTG->cbszFBUG) ), ANS_LOCALCOMMAND_TIMEOUT, TRUE, 0, pTG->cbszCLASS2_OK, pTG->cbszCLASS2_ERROR, (C2PSTR) NULL)) { (MyDebugPrint (pTG, LOG_ALL, "FBUG failed\n\r")); // Ignore FBUG failure!!! } // Do BOR based on the value from the modem table set in // Class2SetMFRSpecific bBuf[0] = '\0'; { UINT uBOR = pTG->CurrentMFRSpec.iReceiveBOR; if (pTG->CurrentMFRSpec.fSWFBOR && uBOR==1) { (MyDebugPrint (pTG, LOG_ALL, "<> SWFBOR Enabled. Using AT+FBOR=0 instead of AT+FBOR=1\r\n")); uBOR=0; } uwLen = (USHORT)wsprintf(bBuf, pTG->cbszSET_FBOR, uBOR); } if(!Class2iModemDialog(pTG, bBuf, uwLen, ANS_LOCALCOMMAND_TIMEOUT, TRUE, 0, pTG->cbszCLASS2_OK, pTG->cbszCLASS2_ERROR, (C2PSTR) NULL)) { (MyDebugPrint (pTG, LOG_ALL, "FBOR failed\n\r")); // Ignore BOR failure!!! } // Set the local ID - need ID from above to do this. bIDBuf[0] = '\0'; uwLen = (USHORT)wsprintf(bIDBuf, pTG->cbszFLID, (LPSTR)szCSI); if(!Class2iModemDialog(pTG, bIDBuf, uwLen, ANS_LOCALCOMMAND_TIMEOUT, TRUE, 0, pTG->cbszCLASS2_OK, pTG->cbszCLASS2_ERROR, (C2PSTR) NULL)) { (MyDebugPrint (pTG, LOG_ALL, "Local ID failed\n\r")); // ignore failure } // Answer the phone // have to call hangup on every path out of here // after Answer is called. If Answer fails, it calls Hangup. // if it succeeds we have to call Hangup when we're done SignalStatusChange(pTG, FS_ANSWERED); if((uRet2 = Class2Answer(pTG, FALSE)) != CONNECT_OK) { (MyDebugPrint (pTG, LOG_ALL, "Answer failed\n\r")); uRet1 = T30_CALLFAIL; pTG->fFatalErrorWasSignaled = 1; SignalStatusChange(pTG, FS_FATAL_ERROR); RetCode = FALSE; goto done; } (MyDebugPrint (pTG, LOG_ALL, "Done with Class2 Answer - succeeded!!\n\r")); ICommStatus(pTG, T30STATR_TRAIN, 0, 0, 0); // Receive the data uRet1 = (USHORT)Class2Receive(pTG ); if ( uRet1 == T30_CALLDONE) { (MyDebugPrint (pTG, LOG_ALL, "******* DONE WITH CALL, ALL OK\r\n")); ICommStatus(pTG, T30STATR_SUCCESS, 0, 0, 0); // have to call hangup on every path out of here // we have to call Hangup here Class2ModemHangup(pTG ); SignalStatusChange(pTG, FS_COMPLETED); RetCode = TRUE; } else { (MyDebugPrint (pTG, LOG_ALL, "******* DONE WITH CALL, **** FAILED *****\r\n")); ICommStatus(pTG, T30STATR_FAIL, 0, 0, 0); // Make sure modem is in an OK state! FComXon(pTG, FALSE); // have to call hangup on every path out of here // Abort calls Hangup Class2ModemAbort(pTG ); pTG->fFatalErrorWasSignaled = 1; SignalStatusChange(pTG, FS_FATAL_ERROR); RetCode = FALSE; } BG_CHK(uRet1==T30_CALLDONE || uRet1==T30_CALLFAIL); uRet2 = 0; done: BG_CHK((uRet1 & 0xFF) == uRet1); BG_CHK((uRet2 & 0xFF) == uRet2); return RetCode; } BOOL Class2Receive(PThrdGlbl pTG) { LPBUFFER lpbf; SWORD swRet; UWORD uwLen; ULONG lTotalLen=0; PCB Pcb; USHORT uTimeout=30000, uRet, PagesReceived, uFPTSarg; BOOL err_status; BCwithTEXT bc; BYTE bBuf[200]; DWORD tiffCompression; LPSTR lpsTemp; DWORD HiRes; // FComCriticalNeg(TRUE); //// faxTlog(("\r\n\r\n\r\n\r\n\r\n======================= Entering Class 2 Receive*** ==============================\r\n\r\n\r\n\r\n")); /* * We have just answered! */ // The repsonse to the ATA command is in the global variable // pTG->lpbResponseBuf2. if ( Class2ResponseAction(pTG, (LPPCB) &Pcb) == FALSE ) { (MyDebugPrint (pTG, LOG_ALL, "Failed to process ATA Response\n\r")); err_status = T30_CALLFAIL; return err_status; } //Now that pcb is set up, call ICommReceiveParams to tell icomfile Class2InitBC(pTG, (LPBC)&bc, sizeof(bc), RECV_PARAMS); Class2PCBtoBC(pTG, (LPBC)&bc, sizeof(bc), &Pcb); if( ICommRecvParams(pTG, (LPBC)&bc) == FALSE ) { (MyDebugPrint (pTG, LOG_ALL, "Failed return from ICommRecvParams.\r\n")); err_status = T30_CALLFAIL; return err_status; } // // once per RX - create TIFF file as soon as we know the compression / resolution. // pTG->Encoding = Pcb.Encoding; pTG->Resolution = Pcb.Resolution; if (Pcb.Encoding == MR_DATA) { tiffCompression = TIFF_COMPRESSION_MR; } else { tiffCompression = TIFF_COMPRESSION_MH; } if (Pcb.Resolution & (AWRES_mm080_077 | AWRES_200_200) ) { HiRes = 1; } else { HiRes = 0; } if ( !pTG->fTiffOpenOrCreated) { // // top 32bits of 64bit handle are guaranteed to be zero // pTG->Inst.hfile = PtrToUlong( TiffCreateW ( pTG->lpwFileName, tiffCompression, 1728, FILLORDER_LSB2MSB, HiRes ) ); if (! (pTG->Inst.hfile)) { lpsTemp = UnicodeStringToAnsiString(pTG->lpwFileName); MyDebugPrint(pTG, LOG_ERR, "ERROR:Can't create tiff file %s compr=%d \n", lpsTemp, tiffCompression); MemFree(lpsTemp); err_status = T30_CALLFAIL; return err_status; } pTG->fTiffOpenOrCreated = 1; lpsTemp = UnicodeStringToAnsiString(pTG->lpwFileName); MyDebugPrint(pTG, LOG_ALL, "Created tiff file %s compr=%d HiRes=%d \n", lpsTemp, tiffCompression, HiRes); MemFree(lpsTemp); } // we set buffer sizes and recv mode (ECM/non-ECM) here ICommSetRecvMode(pTG, FALSE); // always non-ECM? // **** Apparently, we don't want flow control on, so we'll turn // it off. Is this true???? If I turn it on, fcom.c fails a // debug check in filterreadbuf. FComXon(pTG, FALSE); // Send the FDR. The FDR must be responded to by a CONNECT. if(!Class2iModemDialog(pTG, pTG->cbszFDR, (UWORD) (strlen(pTG->cbszFDR) ), STARTSENDMODE_TIMEOUT, TRUE, 0, pTG->cbszFDT_CONNECT,(C2PSTR) NULL)) { (MyDebugPrint (pTG, LOG_ALL, "Failed get response from initial FDR!!\r\n")); err_status = T30_CALLFAIL; return err_status; } (MyDebugPrint (pTG, LOG_ALL, "FDR Received %s\r", (LPSTR)(&(pTG->lpbResponseBuf2)))); // Might have to search through FDR response, but I doubt it. // Now we need to send a DC2 (0x12) to tell the modem it is OK // to give us data. // Some modems use ^Q instead of ^R - The correct value was written // into the DC@ string in Class2Callee where we checked for // manufacturer FComDirectSyncWriteFast(pTG, pTG->CurrentMFRSpec.szDC2, 1); // Now we can receive the data and give it to the icomfile routine PagesReceived = 0; err_status = T30_CALLDONE; while ((swRet=(SWORD)ICommPutRecvBuf(pTG, NULL, RECV_STARTPAGE)) == TRUE) { (MyDebugPrint (pTG, LOG_ALL, "IN MULTIPAGE WHILE LOOP. Pages Received = %d\n\r", PagesReceived)); //// faxTlog(("Receiving Reams of Page Data.....\r\n")); // The READ_TIMEOUT is used to timeout calls to ReadBuf() either in the #define READ_TIMEOUT 15000 lTotalLen = 0; do { (MyDebugPrint (pTG, LOG_ALL, "In receiving a page loop\n\r")); uRet=Class2ModemRecvBuf(pTG, &lpbf, READ_TIMEOUT); (MyDebugPrint(pTG, LOG_ALL, "Class2ModemRecvBuf: uRet=%x\n\r", uRet )); if(lpbf) { lTotalLen += lpbf->wLengthData; (MyDebugPrint (pTG, LOG_ALL, "In lpbf if. length = %ld, Total Length %ld\n\r", lpbf->wLengthData, lTotalLen)); if(!ICommPutRecvBuf(pTG, lpbf, RECV_SEQ)) { (MyDebugPrint (pTG, LOG_ALL, "Bad return - PutRecvBuf in page\r\n")); err_status=T30_CALLFAIL; return err_status; } lpbf = 0; } } while(uRet == RECV_OK); (MyDebugPrint (pTG, LOG_ALL, "Out of RECV_SEQ do loop\n\r")); if(uRet == RECV_EOF) { (MyDebugPrint (pTG, LOG_ALL, "Got EOF from RecvBuf\n\r")); // FComCriticalNeg(TRUE); // RSL needed interface to TIFF thread pTG->fLastReadBlock = 1; ICommPutRecvBuf(pTG, NULL, RECV_FLUSH); } else { // Timeout from ModemRecvBuf (MyDebugPrint (pTG, LOG_ALL, "ModemRecvBuf Timeout or Error=%d\r\n", uRet)); err_status = T30_CALLFAIL; return err_status; } //// faxTlog(pTG, ("Page Recv Done.....len=(%ld, 0x%08x)\r\n", lTotalLen, lTotalLen)); PagesReceived++; Class2GetRecvPageAck(pTG ); // Set the FPTS parameter based on the global pTG->fRecvPageOK variable. // This variable was set during the call to Class2GetRecvPageAck above. // The quality of the page was checked during that call. // This command will cause an the next FDR command to send either // "MCF" or "RTN". if (pTG->fRecvPageOK) uFPTSarg = 1; // 1 indicates a good page else uFPTSarg = 2; // 2 indicates a bad page, retrain requested. // See if more pages to receive by parsing the FDR response... // After the DLEETX was received by Class2ModemRecvBuf, the // FPTS and FET response should be coming from the modem, terminated // by an OK. Let's go read that! if(!Class2iModemDialog(pTG, NULL, 0, STARTSENDMODE_TIMEOUT, TRUE, 0, pTG->cbszCLASS2_OK, (C2PSTR)NULL)) { (MyDebugPrint (pTG, LOG_ALL, "END PAGE Processing Failed!\n\r")); err_status = T30_CALLFAIL; return err_status; } (MyDebugPrint (pTG, LOG_ALL, "EOP Received %s\r", (LPSTR)(&(pTG->lpbResponseBuf2)))); // Process the response and see if more pages are coming if (Class2EndPageResponseAction(pTG ) == MORE_PAGES) { ICommPutRecvBuf(pTG, NULL, RECV_ENDPAGE); // Send the FPTS - don't do this for Exar modems! if (!pTG->CurrentMFRSpec.bIsExar) { uwLen = (UWORD)wsprintf(bBuf, pTG->cbszFPTS, uFPTSarg); if(!Class2iModemDialog(pTG, bBuf, uwLen, LOCALCOMMAND_TIMEOUT, TRUE, 0, pTG->cbszCLASS2_OK, pTG->cbszCLASS2_ERROR, (C2PSTR) NULL)) { (MyDebugPrint (pTG, LOG_ALL, "FPTS= failed\n\r")); // Ignore FPTS failure!!! } } // Now, Send the FDR to start the next page (this was done for // the first page before entering the multipage loop). if(!Class2iModemDialog(pTG, pTG->cbszFDR, (UWORD) (strlen(pTG->cbszFDR) ), STARTSENDMODE_TIMEOUT, TRUE, 0, pTG->cbszFDT_CONNECT,(C2PSTR) NULL)) { (MyDebugPrint (pTG, LOG_ALL, "FDR to start next PAGE Failed!\n\r")); err_status = T30_CALLFAIL; return err_status; } // Now send the correct DC2 string set in Class2Callee // (DC2 is standard, some use ^q instead) FComDirectSyncWriteFast(pTG, pTG->CurrentMFRSpec.szDC2, 1); } //if we do not have another page, do the else... else break; // All done receiving pages... } //End of multipage while (MyDebugPrint (pTG, LOG_ALL, "OUT OF WHILE MULTIPAGE LOOP. ABOUT TO SEND FINAL FDR.\r\n")); //RSL ICommPutRecvBuf(pTG, NULL, RECV_ENDDOC); // Send end of message sequence // Send the last FPTS - do we really need to do this??? if (!pTG->CurrentMFRSpec.bIsExar) { uwLen = (UWORD)wsprintf(bBuf, pTG->cbszFPTS, uFPTSarg); if(!Class2iModemDialog(pTG, bBuf, uwLen, LOCALCOMMAND_TIMEOUT, TRUE, 0, pTG->cbszCLASS2_OK, pTG->cbszCLASS2_ERROR, (C2PSTR) NULL)) { (MyDebugPrint (pTG, LOG_ALL, "FPTS= failed\n\r")); // Ignore FPTS failure!!! } } // Send last FDR if(!Class2iModemDialog(pTG, pTG->cbszFDR, (UWORD) (strlen(pTG->cbszFDR) ), STARTSENDMODE_TIMEOUT, TRUE, 0, pTG->cbszCLASS2_OK, (C2PSTR)NULL)) { err_status = T30_CALLFAIL; return err_status; } FComXon(pTG, FALSE); ICommGotDisconnect(pTG ); return err_status; } BOOL Class2GetModemMaker(PThrdGlbl pTG) { USHORT i; // Initialize the current modem variable's (global) strings. pTG->CurrentMFRSpec.szATI[0] = '\0'; pTG->CurrentMFRSpec.szMFR[0] = '\0'; pTG->CurrentMFRSpec.szMDL[0] = '\0'; // pTG->CurrentMFRSpec.szREV[0] = '\0'; // // Get the ATI - repsonse is in pTG->lpbResponseBuf2 // // For all responses, "ERROR" may come back - that is OK - we will // // never match ERROR to an acceptable modem manufacturer name, model, // // revision, etc. // if(!Class2iModemDialog(cbszCLASS2_ATI, sizeof(cbszCLASS2_ATI)-1, // ANS_LOCALCOMMAND_TIMEOUT, TRUE, 0, cbszCLASS2_OK, // cbszCLASS2_ERROR, (C2PSTR) NULL)) // { // (MyDebugPrint (pTG, LOG_ALL, "ATI failed\n\r")); // // Ignore ATI failure!!! // } // else // { // // copy ATI answer into ATI variable // for (i=0; iCurrentMFRSpec.szATI[i] = pTG->lpbResponseBuf2[i]; // } // // (MyDebugPrint (pTG, LOG_ALL, "Received ATI %s\r", (LPSTR)(&(pTG->lpbResponseBuf2)))); // Get the FMFR - repsonse is in pTG->lpbResponseBuf2 if(!Class2iModemDialog(pTG, pTG->cbszCLASS2_FMFR, (UWORD) (strlen(pTG->cbszCLASS2_FMFR) ), ANS_LOCALCOMMAND_TIMEOUT, TRUE, 0, pTG->cbszCLASS2_OK, pTG->cbszCLASS2_ERROR, (C2PSTR) NULL)) { (MyDebugPrint (pTG, LOG_ALL, "FMFR failed\n\r")); // Ignore FMFR failure!!! } else { // copy FMFR answer into FMFR variable for (i=0; iCurrentMFRSpec.szMFR[i] = pTG->lpbResponseBuf2[i]; } (MyDebugPrint (pTG, LOG_ALL, "Received FMFR %s\r", (LPSTR)(&(pTG->lpbResponseBuf2)))); // Get the FMDL - repsonse is in pTG->lpbResponseBuf2 if(!Class2iModemDialog(pTG, pTG->cbszCLASS2_FMDL, (UWORD) (strlen(pTG->cbszCLASS2_FMDL) ), ANS_LOCALCOMMAND_TIMEOUT, TRUE, 0, pTG->cbszCLASS2_OK, pTG->cbszCLASS2_ERROR, (C2PSTR) NULL)) { (MyDebugPrint (pTG, LOG_ALL, "FMDL failed\n\r")); // Ignore FMDL failure!!! } else { // copy FMDL answer into FMDL variable for (i=0; iCurrentMFRSpec.szMDL[i] = pTG->lpbResponseBuf2[i]; } (MyDebugPrint (pTG, LOG_ALL, "Received FMDL %s\r", (LPSTR)(&(pTG->lpbResponseBuf2)))); // // Get the FREV - repsonse is in pTG->lpbResponseBuf2 // if(!Class2iModemDialog(cbszCLASS2_FREV, sizeof(cbszCLASS2_FREV)-1, // ANS_LOCALCOMMAND_TIMEOUT, TRUE, 0, cbszCLASS2_OK, // cbszCLASS2_ERROR, (C2PSTR) NULL)) // { // (MyDebugPrint (pTG, LOG_ALL, "FREV failed\n\r")); // // Ignore FREV failure!!! // } // else // { // // copy FREV answer into REV variable // for (i=0; iCurrentMFRSpec.szREV[i] = pTG->lpbResponseBuf2[i]; // } // (MyDebugPrint (pTG, LOG_ALL, "Received REV %s\r", (LPSTR)(&(pTG->lpbResponseBuf2)))); return TRUE; } void Class2SetMFRSpecific(PThrdGlbl pTG, LPSTR lpszSection) { USHORT iIndex, iFoundMFR,iFoundMDL; LPMFRSPEC lpmfrMatched; (MyDebugPrint (pTG, LOG_ALL, "Entering Class2SetMFRSpecific\n\r")); // Find the index into the table that corresponds most closely // to the modem. If we can't find the mfr and model, find a mfr // that matches (use the last one). If neither, use the default // last entry. // Look for Manufacturer name iIndex = 0; iFoundMFR = 0; iFoundMDL = 0; (MyDebugPrint (pTG, LOG_ALL, "Entering search table loop\n\r")); while (Class2ModemTable[iIndex].szMFR[0] != '\0') { lpmfrMatched = &(Class2ModemTable[iIndex]); // Look and see if the current name matches // the name in the list. if(Class2_fstrstr( (LPSTR) pTG->CurrentMFRSpec.szMFR, (LPSTR) lpmfrMatched->szMFR) != NULL) { // Found a match! (MyDebugPrint (pTG, LOG_ALL, "MATCHED MANUFACTURER NAME: %s %s\n\r", (LPSTR)(&pTG->CurrentMFRSpec.szMFR), (LPSTR)(&(lpmfrMatched->szMFR)) )); iFoundMFR=iIndex; //Now see if this matches the model number, too. if(Class2_fstrstr( (LPSTR) pTG->CurrentMFRSpec.szMDL, (LPSTR) lpmfrMatched->szMDL) != NULL) { //Got a MDL match, too! Stop looking. iFoundMDL = iIndex; (MyDebugPrint (pTG, LOG_ALL, "MATCHED MODEL: %s %s\n\r", (LPSTR)(&pTG->CurrentMFRSpec.szMDL), (LPSTR)(&(lpmfrMatched->szMDL)) )); break; } } iIndex++; } // We now either have the modem match or are using the defaults! if (iFoundMFR != 0) lpmfrMatched = &Class2ModemTable[iFoundMFR]; else lpmfrMatched = &Class2ModemTable[iIndex]; // Set proper BOR for receive and send pTG->CurrentMFRSpec.iSendBOR = lpmfrMatched->iSendBOR; pTG->CurrentMFRSpec.iReceiveBOR = lpmfrMatched->iReceiveBOR; pTG->CurrentMFRSpec.fSWFBOR = lpmfrMatched->fSWFBOR; // Set the DC2 string - this is used in receive mode // after sending the FDR to tell the modem we are ready // to receive data. The standard says it should be a Dc2 // (^R). But, some modems use ^Q pTG->CurrentMFRSpec.szDC2[0] = lpmfrMatched->szDC2[0]; // Set the Sierra and Exar flags flag pTG->CurrentMFRSpec.bIsSierra = lpmfrMatched->bIsSierra; pTG->CurrentMFRSpec.bIsExar = lpmfrMatched->bIsExar; pTG->CurrentMFRSpec.fSkipCtrlQ = lpmfrMatched->fSkipCtrlQ; (MyDebugPrint (pTG, LOG_ALL, "Leaving Class2SetMFRSpecific\n\r")); } BOOL Class2Parse(PThrdGlbl pTG, CL2_COMM_ARRAY *cl2_comm, BYTE lpbBuf[]) { int i, j, comm_numb = 0, parameters; BYTE switch_char, char_1, char_2; char c; BOOL found_command = FALSE; #define STRING_PARAMETER 1 #define NUMBER_PARAMETERS 2 for(i = 0; lpbBuf[i] != '\0'; ++i) { switch ( lpbBuf[i] ) { case 'C': if (lpbBuf[++i] == 'O' && lpbBuf[++i] == 'N') { cl2_comm->command[comm_numb++] = CL2DCE_CONNECT; for(; lpbBuf[i] != '\r'; ++i ) ; } else { (MyDebugPrint (pTG, LOG_ALL, "Parse: Bad First C values\n\r")); return FALSE; } break; case 'O': if (lpbBuf[++i] == 'K' ) { cl2_comm->command[comm_numb++] = CL2DCE_OK; for(; lpbBuf[i] != '\r'; ++i ) ; } else { (MyDebugPrint (pTG, LOG_ALL, "Parse: Bad O values\n\r")); return FALSE; } break; case 0x11: cl2_comm->command[comm_numb++] = CL2DCE_XON; break; case '+': if( lpbBuf[++i] != 'F' ) { (MyDebugPrint (pTG, LOG_ALL, "Parse: Bad + values\n\r")); return FALSE; } switch_char = lpbBuf[++i]; char_1 = lpbBuf[++i]; char_2 = lpbBuf[++i]; // (MyDebugPrint (pTG, LOG_ALL, "Parse: in + command - %c%c%c \n\r", // switch_char, char_1, char_2)); switch ( switch_char ) { case 'C': // Connect Message +FCON. if ( char_1 == 'O' && char_2 == 'N' ) { cl2_comm->command[comm_numb] = CL2DCE_FCON; parameters = FALSE; } // Report of Remote ID. +FCIG. else if (char_1 == 'I' && char_2 == 'G' ) { cl2_comm->command[comm_numb] = CL2DCE_FCIG; parameters = STRING_PARAMETER; } // Prepare to receive prompt. +FCFR. else if ( char_1 == 'F' && char_2 == 'R' ) { cl2_comm->command[comm_numb] = CL2DCE_FCFR; parameters = FALSE; } // Report the Remote ID CSI +FCSI. else if ( char_1 == 'S' && char_2 == 'I' ) { cl2_comm->command[comm_numb] = CL2DCE_FCSI; parameters = STRING_PARAMETER; } else { (MyDebugPrint (pTG, LOG_ALL, "Parse: Bad C values\n\r")); return FALSE; } break; case 'D': // Report DCS frame information +FDCS. if ( char_1 == 'C' && char_2 == 'S' ) { cl2_comm->command[comm_numb] = CL2DCE_FDCS; parameters = NUMBER_PARAMETERS; } // Report DIS frame information +FDIS. else if ( char_1 == 'I' && char_2 == 'S' ) { cl2_comm->command[comm_numb] = CL2DCE_FDIS; parameters = NUMBER_PARAMETERS; } // Report DTC frame information +FDTC. else if ( char_1 == 'T' && char_2 == 'C' ) { cl2_comm->command[comm_numb] = CL2DCE_FDTC; parameters = NUMBER_PARAMETERS; } else { (MyDebugPrint (pTG, LOG_ALL, "Parse: Bad D values\n\r")); return FALSE; } break; case 'E': // Post page message report. +FET. if ( char_1 == 'T' ) { --i; cl2_comm->command[comm_numb] = CL2DCE_FET; parameters = NUMBER_PARAMETERS; } else { (MyDebugPrint (pTG, LOG_ALL, "Parse: Bad E values\n\r")); return FALSE; } break; case 'H': // Debug report transmitted HDLC frames +FHT if ( char_1 == 'T' ) { --i; cl2_comm->command[comm_numb] = CL2DCE_FHT; parameters = STRING_PARAMETER; } // Debug report received HDLC frames +FHR else if ( char_1 == 'R' ) { --i; cl2_comm->command[comm_numb] = CL2DCE_FHR; parameters = STRING_PARAMETER; } // Report hang up. +FHNG. else if ( char_1 == 'N' && char_2 == 'G' ) { cl2_comm->command[comm_numb] = CL2DCE_FHNG; parameters = NUMBER_PARAMETERS; } else { (MyDebugPrint (pTG, LOG_ALL, "Parse: Bad H values\n\r")); return FALSE; } break; case 'N': // Report NSF frame reciept. if ( char_1 == 'S' && char_2 == 'F' ) { cl2_comm->command[comm_numb] = CL2DCE_FNSF; parameters = NUMBER_PARAMETERS; } // Report NSS frame reciept. else if ( char_1 == 'S' && char_2 == 'S' ) { cl2_comm->command[comm_numb] = CL2DCE_FNSS; parameters = NUMBER_PARAMETERS; } // Report NSC frame reciept. else if ( char_1 == 'S' && char_2 == 'C' ) { cl2_comm->command[comm_numb] = CL2DCE_FNSC; parameters = NUMBER_PARAMETERS; } else { (MyDebugPrint (pTG, LOG_ALL, "Parse: Bad N values\n\r")); return FALSE; } break; case 'P': // Report poll request. +FPOLL if ( char_1 == 'O' && char_2 == 'L' ) { cl2_comm->command[comm_numb] = CL2DCE_FPOLL; parameters = FALSE; } // Page Transfer Status Report +FPTS. else if ( char_1 == 'T' && char_2 == 'S' ) { cl2_comm->command[comm_numb] = CL2DCE_FPTS; parameters = NUMBER_PARAMETERS; } else { (MyDebugPrint (pTG, LOG_ALL, "Parse: Bad P values\n\r")); return FALSE; } break; case 'T': // Report remote ID +FTSI. if ( char_1 == 'S' && char_2 == 'I' ) { cl2_comm->command[comm_numb] = CL2DCE_FTSI; parameters = STRING_PARAMETER; } else { (MyDebugPrint (pTG, LOG_ALL, "Parse: Bad T values\n\r")); return FALSE; } break; case 'V': // Report voice request +FVOICE. if ( char_1 == 'O' && char_2 == 'I' ) { cl2_comm->command[comm_numb] = CL2DCE_FVOICE; parameters = FALSE; } else { (MyDebugPrint (pTG, LOG_ALL, "Parse: Bad V values\n\r")); return FALSE; } } // Transfer the associated paramters to the parameter array. if ( parameters == NUMBER_PARAMETERS) { for(i+=1,j=0; lpbBuf[i] != '\r' && lpbBuf[i] != '\0'; ++i) { // Skip past the non numeric characters. if ( lpbBuf[i] < '0' || lpbBuf[i] > '9' ) continue; /* Convert the character representation of the numeric parameter into a true number, and store in the parameter list. */ cl2_comm->parameters[comm_numb][j] = 0; for(; lpbBuf[i] >= '0' && lpbBuf[i] <= '9'; ++i) { cl2_comm->parameters[comm_numb][j] *= 10; cl2_comm->parameters[comm_numb][j] += lpbBuf[i] - '0'; } i--; // the last for loop advanced 'i' past the numeric. j++; // get set up for next parameter } } else if (parameters == STRING_PARAMETER ) { // Skip the : that follows the +f command (eg +FTSI:) if (lpbBuf[i+1] == ':') i++; // Also skip the " that follows the : if (lpbBuf[i+1] == '\"') i++; // Also skip leading blanks while (lpbBuf[i+1] == ' ') i++; for(i+=1, j=0; (c = lpbBuf[i]) != '\r' && c != '\n' && c != '\0'; ++i, ++j) { cl2_comm->parameters[comm_numb][j] = c; } // Skip the trailing " if ((j > 0) && (cl2_comm->parameters[comm_numb][j - 1] == '\"')) j--; // Also skip the trailing blanks for ( ; (j > 0) && (cl2_comm->parameters[comm_numb][j - 1] == ' '); j--); cl2_comm->parameters[comm_numb][j] = '\0'; } // No parameters, so just skip to end of line. else { for(; (c=lpbBuf[i]) != '\r' && c != '\n' && c != '\0'; ++i) ; } // Increment command count. ++comm_numb; break; default: break; } } cl2_comm->comm_count = (USHORT)comm_numb; return TRUE; }