diff -r 000000000000 -r 3553901f7fa8 fax/faxclientandserver/FAXSVR/CFAX1.CPP --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/fax/faxclientandserver/FAXSVR/CFAX1.CPP Tue Feb 02 01:41:59 2010 +0200 @@ -0,0 +1,1855 @@ +// Copyright (c) 1997-2009 Nokia Corporation and/or its subsidiary(-ies). +// All rights reserved. +// This component and the accompanying materials are made available +// under the terms of "Eclipse Public License v1.0" +// which accompanies this distribution, and is available +// at the URL "http://www.eclipse.org/legal/epl-v10.html". +// +// Initial Contributors: +// Nokia Corporation - initial contribution. +// +// Contributors: +// +// Description: +// + +#include "FAXSERV.H" +#include "fax_reversebytes.h" +#include "FAXMDRV.H" +#include "FAXMODEM.H" + + +// this module has three parts +// first receive routines rx +// second transmit routines tx +// third utilities + +/********************************************************************/ + +CFaxModemDriver *CFaxClass1::NewLC (TFaxServerSessionSettings * aFaxServerSessionSettings, RFax::TProgress & aProgress) +{ + CFaxModemDriver *self = new (ELeave) CFaxClass1; + CleanupStack::PushL (self); + self->ConstructL (aFaxServerSessionSettings, aProgress); + return self; +} + +CFaxModemDriver *CFaxClass1::NewL (TFaxServerSessionSettings * aFaxServerSessionSettings, RFax::TProgress & aProgress) +{ + CFaxModemDriver *self = NewLC (aFaxServerSessionSettings, aProgress); + CleanupStack::Pop (); + return self; +} +/********************************************************************/ + +// here we set up a fax receive - Phase A +// this does require HDLC frames to be sent + +TInt CFaxClass1::RxConnectL () +{ + TInt faxIdFcf; // CSI or CIG + TInt capabilityFcf; // DIS or DTC + TBuf8 < 3 > faxIdTxt; // CSI or CIG + TBuf8 < 3 > capabilityTxt; // DIS or DTC + + TInt i, x; + iDisBytes = 3; + iOldFrame.Zero (); + + // we query the modem to find out what its speed capabilities are + + CheckCadenceExportL (_L8 ("AT+FRM=?\r")); + // coverity[check_return] + iModem->ImportL (iResults, 2); + iModem->iOurMessage.Format (_L8 ("%S"), &iResults); + iModem->ProgressUpdateL (); + iModem->GetMatchL (_L8 ("OK"), 1); + + // the available speeds are stored in iResults + // we set our proposed speed to the highest compatible with faxini settings + + if ((iResults.FindF (_L8 ("24"))) >= 0) + iActualFaxSpeed = 24; + else + return (KFaxErrModemNotWorking); + if ((iFaxServerSessionSettings->iMaxSpeed) > 2400) + if ((iResults.FindF (_L8 ("48"))) >= 0) + iActualFaxSpeed = 48; + if ((iFaxServerSessionSettings->iMaxSpeed) > 4800) + if ((iResults.FindF (_L8 ("96"))) >= 0) + iActualFaxSpeed = 96; + if ((iFaxServerSessionSettings->iMaxSpeed) > 9600) + if ((iResults.FindF (_L8 ("145"))) >= 0) + iActualFaxSpeed = 145; + + // we now prepare our DIS/DTC answer capabilities frame + // the resolution and compression are taken from our settings + + for (x = 0; x < 5; x++) + iDisFrame.byte[x] = 0; + iDisFrame.bit.b09 = 0; + iDisFrame.bit.b10 = 1; + iDisFrame.bit.b20 = 1; + iDisFrame.bit.b21 = 1; + iDisFrame.bit.b22 = 1; + iDisFrame.bit.b23 = 1; + if (iFaxServerSessionSettings->iRxResolution == EFaxFine) + iDisFrame.bit.b15 = 1; + if (iFaxServerSessionSettings->iRxCompression == EModifiedRead) + iDisFrame.bit.b16 = 1; + +// if (iFaxServerSessionSettings->iMode & KFaxWaitForRing) +// { + // while ((iModem->GetMatchL (_L8 ("RING"), 3)) == 0); + // iTimeOfLastRing.UniversalTime(); +// } +// else +// { + if (((iFaxServerSessionSettings->iMode & KFaxOffHook) == 0) && (!(iFaxServerSessionSettings->iMode & KFaxWaitForRing))) +// if ((iFaxServerSessionSettings->iMode & KFaxOffHook) == 0) + DialFaxOnDemandL (); +// } + + // if we a trying to poll, we've dialled, so we wait for a DIS from the answerer + // otherwise we do an answer ourselves + + if (iFaxServerSessionSettings->iMode & KFaxPoll) + { + faxIdFcf = KT30_CIG; + faxIdTxt.Copy (_L8 ("CIG")); + capabilityFcf = KT30_DTC; + capabilityTxt.Copy (_L8 ("DTC")); + iModem->iOurMessage.Format (_L8 ("about to poll fax")); + + } + else + { + faxIdFcf = KT30_CSI; + faxIdTxt.Copy (_L8 ("CSI")); + capabilityFcf = KT30_DIS; + capabilityTxt.Copy (_L8 ("DIS")); + CheckCadenceExportL (_L8 ("ATA\r")); + iModem->iOurMessage.Format (_L8 ("about to receive fax")); + } + + iModem->iProgress.iPhase = ECallEstablishment; + iModem->ProgressUpdateL (); + + for (;;) + { + if (!(iModem->ImportL (iResults, KT30_T1))) + return (KFaxErrCannotConnect); + iModem->iOurMessage.Format (_L8 ("%S"), &iResults); + iModem->ProgressUpdateL (); + if ((iResults.FindF (_L8 ("NO DIALTONE"))) >= 0 || + iResults.FindF (_L8 ("NO DIAL TONE")) >= 0) + return (KFaxErrNoDialTone); + if ((iResults.FindF (_L8 ("BUSY"))) >= 0) + return (KFaxErrBusy); + if ((iResults.FindF (_L8 ("NO ANSWER"))) >= 0) + return (KFaxErrNoAnswer); + if ((iResults.FindF (_L8 ("NO CARRIER"))) >= 0) + return (KFaxErrNoCarrier); + if ((iResults.FindF (_L8 ("CONNECT"))) >= 0) + break; + } + + if (iFaxServerSessionSettings->iMode & KFaxPoll) + User::LeaveIfError (RxPrePollL ()); + else + { + iModem->iOurMessage.Format (_L8 ("Fax call detected")); + iModem->ProgressUpdateL (); + } + + iModem->iOurMessage.Format (_L8 ("sending %S"), &faxIdTxt); + iModem->ProgressUpdateL (); + iFrame.Zero (); + iFrame.Append (KT30_CTLNXT); + iFrame.Append (faxIdFcf); + for (i = 20; i > iFaxServerSessionSettings->iFaxId.Length (); i--) + iFrame.Append (0x20); + for (i = iFaxServerSessionSettings->iFaxId.Length (); i;) + iFrame.Append (iFaxServerSessionSettings->iFaxId[--i]); + if (SendframeL (iFrame) != 1) + return (KFaxErrCSIorCIG); + + // we follow that with our DIS frame + + iModem->iOurMessage.Format (_L8 ("sending %S"), &capabilityTxt); + iModem->iProgress.iPhase = ESessionNegotiation; + iModem->ProgressUpdateL (); + iDisFrame.byte[1] &= 0xc3; + switch (iActualFaxSpeed) + { + case 48: + iDisFrame.byte[1] |= 0x08; + break; /* V.27 4800+2400 */ + case 96: + iDisFrame.byte[1] |= 0x0c; + break; /* & V.29 9600+7200 */ + case 145: + iDisFrame.byte[1] |= 0x2c; + break; /* & V.17 14400+1200+9600+7200 */ + default: + iDisFrame.byte[1] |= 0x00; /* V.27 fallback 2400 only */ + } + iFrame.Zero (); + iFrame.Append (KT30_CTLLST); + iFrame.Append (capabilityFcf); + for (i = 0; i < iDisBytes; i++) + iFrame.Append (iDisFrame.byte[i]); + if (SendframeL (iFrame) != 1) + return (KFaxErrDISorDTC); + + // and now we await the negotiation from the caller + // note that we'll resend the last frame (DIS or DTC) if we get no reply + // until we get a TSI or a DCS (which show the DIS or DTC was received) + + return (RxPrePageL ()); +} + +/********************************************************************/ + +// here we prepare for receiving via a poll + // we have received a DIS in iResults, so check the polling bit + // iResults[0] is the address + // iResults[1] is the control + // iResults[2] is the FCF + // iResults[3] has bits 1-8 of the FIF + // iResults[4] has bits 9-16 of the FIF + // the polling bit is bit 9 + +TInt CFaxClass1::RxPrePollL () +{ + TInt pollDocsAvailable = 0; + TInt i; + iResults.Copy (_L8 ("CALL JUST ANSWERED")); + for (;;) + { + if (GetframeL (iResults) == 0) + return (KFaxErrFrameFail); + iModem->ProgressUpdateL (); + + // the third byte in the frame is the FCF (fax control field) + + switch ((TUint8) iResults[2]) + { + case 0x20: // this marks a non-standard frame, which we ignore + iModem->iOurMessage.Format (_L8 ("NSF nonstandard facilities Frame")); + iModem->ProgressUpdateL (); + break; + + case 0x40: // this marks the receiver ID + iModem->iOurMessage.Format (_L8 ("CSI identity Frame")); + iModem->ProgressUpdateL (); + iModem->iProgress.iAnswerback.Zero (); + for (i = 22; i > 2; i--) + iModem->iProgress.iAnswerback.Append (iResults[i]); + iModem->iOurMessage.Format (_L8 ("Remote fax ID is %S"), &iModem->iProgress.iAnswerback); + iModem->ProgressUpdateL (); + break; // the capability frame should follows + + case 0x80: // this marks the receiver capability frame + iModem->iOurMessage.Format (_L8 ("DIS capability Frame")); + iModem->ProgressUpdateL (); + iFcfXbit = 1; // we've received a DIS, so set the X bit + pollDocsAvailable = iResults[4] & 0x01; // and record the polling bit too + break; + + case 0xfa: // this means we were asked to disconnect + RxDCNL (); + return (KFaxErrRemoteDCN); + + default:; + } + + // if a final frame we return + // else we just issue AT+FRH=3 and continue + + if (iResults[1] & 0x10) + break; + iModem->ExportL (_L8 ("AT+FRH=3\r")); + } + + if (pollDocsAvailable) + { + iModem->iOurMessage.Format (_L8 ("Polling bit set")); + iModem->ProgressUpdateL (); + return (KErrNone); + } + return (KFaxNothingToPoll); // if the other machine isn't pollable we exit +} +/********************************************************************/ + +// here we negotiate a fax reception - Phase B + // this function is always called after we have connected. However, + // it can be called at other times if we have requested a renegotiation + // or if the sender want to change the fax parameters + +TInt CFaxClass1::RxPrePageL () +{ + TInt x, z, i, nullCounter; + TUint8 thisChar, lastChar; + TInt ticks; + + for (;;) + { + iModem->ExportL (_L8 ("AT+FRH=3\r")); + if (GetframeL (iResults) == 0) + return (KFaxErrFrameFail); + iModem->iOurMessage.Format (_L8 ("Response received")); + iModem->ProgressUpdateL (); + + // analyse the possible responses + + switch ((TUint8) iResults[2]) + { + case 0x42: // this is the sender ID - their capability should follow + + iModem->iOurMessage.Format (_L8 ("TSI identity Frame")); + iModem->ProgressUpdateL (); + iModem->iProgress.iAnswerback.Zero (); + for (i = 22; i > 2; i--) + iModem->iProgress.iAnswerback.Append (iResults[i]); + iModem->iOurMessage.Format (_L8 ("Remote fax ID is %S"), &iModem->iProgress.iAnswerback); + iModem->ProgressUpdateL (); + break; + + case 0x82: // here's the sender capability frame - the most complex case + + iModem->iOurMessage.Format (_L8 ("DCS capability Frame")); + iModem->ProgressUpdateL (); + for (i = 0; i < 5; i++) + iDcsFrame.byte[i] = (TUint8) iResults[i + 3]; + + // we have the DCS saved - we analyse it for speed and resolution and compression + + if (iDcsFrame.bit.b24 == 0) + iDcsFrame.byte[3] = 0; + + iModem->iProgress.iResolution = TFaxResolution (iDcsFrame.bit.b15); + iModem->iProgress.iCompression = TFaxCompression (iDcsFrame.bit.b16); + + switch (iDcsFrame.byte[1] & 0x3c) + { + case 0x08: + iActualFaxSpeed = 48; + break; /* 4800 V.27 */ + case 0x04: + iActualFaxSpeed = 96; + break; /* 9600 V.29 */ + case 0x0c: + iActualFaxSpeed = 72; + break; /* 7200 V.29 */ + case 0x24: + iActualFaxSpeed = 97; + break; /* 9600 V.17 */ + case 0x2c: + iActualFaxSpeed = 73; + break; /* 7200 V.17 */ + case 0x20: + iActualFaxSpeed = 145; + break; /* 14400 V.17 */ + case 0x28: + iActualFaxSpeed = 121; + break; /* 12000 V.17 */ + default: + iActualFaxSpeed = 24; /* 2400 V.27 */ + } + + i = (iActualFaxSpeed & (~1)); + + // now we prepare to recieve the training frame that follows the DCS + // we try to get the carrier at this speed three times before giving up + + for (x = 0; x < 3; x++) + { + iModem->iOurMessage.Format (_L8 ("setting %d00"), i); + iModem->iProgress.iSpeed = (i * 100); + iModem->ProgressUpdateL (); + + iResults.Copy (_L8 ("AT+FRM=")); + iResults.AppendNum (iActualFaxSpeed); + iResults.Append (_L8 ("\r")); + iModem->ExportL (iResults); + z = FramestatL (); + if (z == 1) + break; + if (z != 0) + { + iModem->TxcharL (Kcan); + if (FramestatL () < 0) + iModem->TxcharL (Kreturn); + ReceiveSilenceL (); + iModem->iOurMessage.Format (_L8 ("sending FTT")); + iModem->ProgressUpdateL (); + iFrame.Append (KT30_FTT); + if (SendframeL (iFrame) == 0) + return (KFaxErrTrainFail); + break; + } + } + if (x == 3) + return (KFaxErrAtNegotiatedSpeed); + + // once we have a carrier, we start receiving the training frame + // we look for a clear 750 milliseconds of zeros ending in + // this is determined by calculating the number of number of null bytes + // taken at any given speed + + iModem->iOurMessage.Format (_L8 ("training .... ")); + iModem->ProgressUpdateL (); + + ticks = (CLK_TCK * 165) / 100; // bug fix - was originally "CLK_TICK * (165/100)" + // This failed because 165/100 is rounded to 1 because + // ticks is an integer and that made the fax server + // training for 1 second instead of 1.5 + for (lastChar = 0, nullCounter = 0;;) + { + if (iModem->RxcharWaitL (ticks) == 0) + { + break; + } + thisChar = iModem->iReadone[0]; + if (nullCounter != (i * 75 / 8)) + { + if (thisChar != 0) + nullCounter = 0; + else + ++nullCounter; + } + if ((thisChar == Ketx) && (lastChar == Kdle)) + break; + lastChar = thisChar; + } + if (FramestatL () < 0) + { + iModem->TxcharL (Kcan); + if (FramestatL () < 0) + iModem->TxcharL (Kreturn); + } + + // now we check the count of null bytes and either send FTT + // (in which case the sender will send a new DCS and try again) + // or else send CFR confirmation and wait for the first page + + iFrame.Zero (); + iFrame.Append (KT30_CTLLST); + if (nullCounter == (i * 75 / 8)) + { + iModem->iOurMessage.Format (_L8 ("training OK")); + iModem->ProgressUpdateL (); + } + else + { + ReceiveSilenceL (); + iModem->iOurMessage.Format (_L8 ("sending FTT")); + iModem->ProgressUpdateL (); + iFrame.Append (KT30_FTT); + if (SendframeL (iFrame) == 0) + return (KFaxErrTrainFail); + break; + } + + iModem->iOurMessage.Format (_L8 ("sending CFR")); + iModem->ProgressUpdateL (); + + iFrame.Append (KT30_CFR); + if (SendframeL (iFrame) == 0) + return (KFaxErrCFR); + + // after we send a CFR, we interpret a failure to + // establish a high-speed carrier as an indication + // that the sender didn't get our CFR, and will + // act as if they received an FTT + + if (RxSetHighSpeedL () != KErrNone) + break; + return (KErrNone); + + // lastly, we cater for the sender disconnecting us, + // either because we couldn't train or because our + // capabilities were wrong, or because they were only trying + // to hack our fax machine + + case 0xfa: + RxDCNL (); + return (KFaxErrRemoteDCN); + + default:; + } + } +} +/********************************************************************/ + +// this is a small function to set a class 1 fax modem to phase C + // reception speed (found in iActualFaxSpeed) in preparation for + // receiving data. This is called before the first page and also + // between pages. If the modem can't find a high speed carrier, we + // leave the caller to decide what action to take - if we'd just sent a + // page confirmation we should try resending our last negotiating frame + // in case it was lost, but if we have just sent a CFR, we wait for the + // sender to retrain. + +// If the protocol is out of sync and we get a low speed carrier + // then we'll get a +FCERROR response (same as ERROR) + +TInt CFaxClass1::RxSetHighSpeedL () +{ + TInt x, portSpeed; + switch (iActualFaxSpeed) + { + case 145: + x = 144; + portSpeed = 146; + break; + case 121: + x = 120; + portSpeed = 122; + break; + case 97: + x = 96; + portSpeed = 98; + break; + case 73: + x = 72; + portSpeed = 74; + break; + default: + x = portSpeed = iActualFaxSpeed; + } + + iModem->iOurMessage.Format (_L8 ("setting %d00"), x); + iModem->iProgress.iSpeed = (x * 100); + iModem->ProgressUpdateL (); + + iResults.Copy (_L8 ("AT+FRM=")); + iResults.AppendNum (portSpeed); + iResults.Append (_L8 ("\r")); + iModem->ExportL (iResults); + + x = FramestatL (KT30_T2); // always wait 6 seconds before a timeout + if (x == 1) + { + return (RxStartPageL ()); + } + if (x != 0) + { + iModem->TxcharL (Kcan); + if (FramestatL () < 0) + iModem->TxcharL (Kreturn); + } + return (KFaxErrAtNegotiatedSpeed); +} +/********************************************************************/ + +// after page data has been received, we go back to 300 bps negotiation +// for the post-page message which the transmitter sends 75ms after the +// end of the data + +TInt CFaxClass1::RxPostPageL () +{ + TInt i, x = 0; + iOldFrame.Zero (); + iModem->iProgress.iPhase = EPostPageStatus; + iModem->ProgressUpdateL (); + + // wait for the modem to react to the end of fax data before proceeding + + if ((iModem->GetMatchL (_L8 ("NO CARRIER"), 5)) == 0) + return (KFaxErrCannotEndData); + + for (;;) + { + + // we start by requesting a frame + + iModem->ExportL (_L8 ("AT+FRH=3\r")); + if (GetframeL (iResults) == 0) + { + if (x++ == 3) + return (KFaxErrFrameFail); + continue; + } + + iModem->iOurMessage.Format (_L8 ("Response received")); + iModem->ProgressUpdateL (); + + // now we work out what it is + + switch ((TUint8) iResults[2]) + { + + // the first case is where the last page was the end of the fax + + case 0x3e: // we recognize PRI-Q frames but treat them like non-PRI-Q variants + iModem->iOurMessage.Format (_L8 ("PRI-Q bit set")); + iModem->ProgressUpdateL (); + // fallthrough + case 0x2e: + iModem->iOurMessage.Format (_L8 ("EOP end of page %u and transmission"), iModem->iProgress.iPage); + iModem->ProgressUpdateL (); + iModem->iOurMessage.Format (_L8 ("sending MCF")); + iModem->ProgressUpdateL (); + iFrame.Zero (); + iFrame.Append (KT30_CTLLST); + iFrame.Append (KT30_MCF); + if (SendframeL (iFrame) == 0) + return (KFaxErrMCF); + + // now it isn't an error if get a DCN and hang up + // so, loop and wait for it + + iModem->iProgress.iPhase = EDisconnection; + continue; + + // the second case is where the sender demands a renegotiation + + case 0x9e: // we recognize PRI-Q frames but treat them like non-PRI-Q variants + iModem->iOurMessage.Format (_L8 ("PRI-Q bit set")); + iModem->ProgressUpdateL (); + // fallthrough + case 0x8e: + iModem->iOurMessage.Format (_L8 ("EOM end of page %u and document"), iModem->iProgress.iPage); + iModem->ProgressUpdateL (); + iModem->iOurMessage.Format (_L8 ("sending MCF")); + iModem->ProgressUpdateL (); + iFrame.Zero (); + iFrame.Append (KT30_CTLLST); + iFrame.Append (KT30_MCF); + if (SendframeL (iFrame) == 0) + return (KFaxErrMCF); + iModem->iProgress.iPhase = ESessionNegotiation; + iOldFrame.Zero (); + iOldFrame.Append (KT30_CTLLST); + iOldFrame.Append (KT30_DIS); + for (i = 0; i < iDisBytes; i++) + iOldFrame.Append (iDisFrame.byte[i]); + return (RxPrePageL ()); + + // the third case is where another page is going to follow + + case 0x5e: // we recognize PRI-Q frames but treat them like non-PRI-Q variants + + iModem->iOurMessage.Format (_L8 ("PRI-Q bit set")); + iModem->ProgressUpdateL (); + // fallthrough + case 0x4e: + iModem->iOurMessage.Format (_L8 ("MPS end of page %u"), iModem->iProgress.iPage); + iModem->ProgressUpdateL (); + + iModem->iOurMessage.Format (_L8 ("sending MCF")); + iModem->ProgressUpdateL (); + + iFrame.Zero (); + iFrame.Append (KT30_CTLLST); + iFrame.Append (KT30_MCF); + if (SendframeL (iFrame) == 0) + return (KFaxErrMCF); + + for (x = 0; x < 3; x++) + { + if (RxSetHighSpeedL () == KErrNone) + return (KErrNone); + iModem->iOurMessage.Format (_L8 ("Resending last response .... ")); + iModem->ProgressUpdateL (); + if (SendframeL (iOldFrame) == 0) + return (KFaxErrMCF); + } + return (KFaxErrMCF); + + // the fourth case is where we are told to disconnect + // it's an error if we hadn't been expecting it + + case 0xfa: + if (iModem->iProgress.iPhase == EDisconnection) + { + RxDCNL (); + return (KErrNone); + } + + RxDCNL (); + return (KFaxErrRemoteDCN); + + // the fifth case is where we see a negotiation frame + // the sixth case is where we see a negotiation frame + // our supposed page may have been a mistake + // just go back to phase B and try to recover that way + + case 0x42: // TSI frame + case 0x82: // DCS frame + if ((TUint8) iResults[2] == 0x42) + iModem->iOurMessage.Format (_L8 ("TSI identity")); + else + iModem->iOurMessage.Format (_L8 ("DCS capability")); + iModem->iOurMessage.Append (_L8 (" Frame - renegotiating session parameters")); + iModem->ProgressUpdateL (); + iModem->iProgress.iPhase = ESessionNegotiation; + return (RxPrePageL ()); + + // the last case is where we see an unsupported frame + // if it is a final frame we ask for a repeat via CRP + + default: + if (SendCRPL () != KErrNone) + return (KFaxErrCRP); + } + } +} +/********************************************************************/ + +// here we have detected a disconnection frame so we hang up the modem + +void CFaxClass1::RxDCNL () +{ + iModem->iOurMessage.Format (_L8 ("DCN disconnect Frame")); + iModem->iProgress.iPhase = EDisconnection; + iModem->ProgressUpdateL (); +} +/********************************************************************/ + +// here we send a DCN disconnect frame and then hang up the modem + +TInt CFaxClass1::TxDCNL () +{ + iModem->iOurMessage.Format (_L8 ("sending DCN")); + iModem->iProgress.iPhase = EDisconnection; + iModem->ProgressUpdateL (); + ReceiveSilenceL (); + iFrame.Zero (); + iFrame.Append (KT30_CTLLST); + iFrame.Append (KT30_DCN); + if (SendframeL (iFrame) == 0) + return (KFaxErrDCN); + return (KErrNone); +} +/********************************************************************/ + +// here we set up a fax transmit - Phase A + // there's no HDLC stuff here + +TInt CFaxClass1::TxConnectL () +{ + + // we query the modem to find out what its speed capabilities are + iModem->ExportL (_L8 ("AT+FTM=?\r")); + // coverity[check_return] + iModem->ImportL (iResults, 2); + iModem->iOurMessage.Format (_L8 ("%S"), &iResults); + iModem->ProgressUpdateL (); + iModem->GetMatchL (_L8 ("OK"), 1); + + // the available speeds are stored in iResults + // we set our proposed speed to the highest compatible with faxini settings + + if ((iResults.FindF (_L8 ("24"))) >= 0) + iActualFaxSpeed = 24; + else + return (KFaxErrModemNotWorking); + if ((iFaxServerSessionSettings->iMaxSpeed) > 2400) + if ((iResults.FindF (_L8 ("48"))) >= 0) + iActualFaxSpeed = 48; + if ((iFaxServerSessionSettings->iMaxSpeed) > 4800) + if ((iResults.FindF (_L8 ("96"))) >= 0) + iActualFaxSpeed = 96; + if ((iFaxServerSessionSettings->iMaxSpeed) > 9600) + if ((iResults.FindF (_L8 ("145"))) >= 0) + iActualFaxSpeed = 145; + + // we now issue our ATD command, and if we aren't in immediate + // transmit mode (already off hook) then we dial a number + + iModem->ExportL (_L8 ("ATD")); + if ((iFaxServerSessionSettings->iMode & KFaxOffHook) == 0) + iModem->ExportL (iFaxServerSessionSettings->iPhoneNumber); + iModem->TxcharL (Kreturn); + iModem->iOurMessage.Format (_L8 ("Call has been dialled")); + iModem->iProgress.iPhase = ECallEstablishment; + iModem->ProgressUpdateL (); + + // now we wait up to KDialTimeout seconds for the modem to connect + + for (;;) + { + if (!(iModem->ImportL (iResults, KDialTimeout))) + return (KFaxErrNoDial); + iModem->iOurMessage.Format (_L8 ("%S"), &iResults); + iModem->ProgressUpdateL (); + if ((iResults.FindF (_L8 ("NO DIALTONE"))) >= 0 || + iResults.FindF (_L8 ("NO DIAL TONE")) >= 0) + return (KFaxErrNoDialTone); + if ((iResults.FindF (_L8 ("BUSY"))) >= 0) + return (KFaxErrBusy); + if ((iResults.FindF (_L8 ("NO ANSWER"))) >= 0) + return (KFaxErrNoAnswer); + if ((iResults.FindF (_L8 ("NO CARRIER"))) >= 0) + return (KFaxErrNoCarrier); + if ((iResults.FindF (_L8 ("CONNECT"))) >= 0) + break; + } + + // we can now go on to phase B + + iResults.Copy (_L8 ("CALL JUST ANSWERED")); + return (TxPrePageL ()); +} +/********************************************************************/ + +// here we negotiate a fax transmission, or a polled reception - Phase B +// this function is always called after we have connected. However, +// it can be called at other times if the receiver has requested a +// renegotiation or if we want to change the fax parameters, in which +// case we would enter with iModem->iProgress.iPhase == RFax::EPostPageStatus + +TInt CFaxClass1::TxPrePageL () +{ + TInt i; + //TInt x; + TInt successiveErrors = 0; + TInt trainingAttempts = 0; + iDcsBytes = 3; + iOldFrame.Zero (); + TInt ticks; + TInt trainbytes; + + // this routine is one big frame waiting loop - note that on first entry + // here we have set the length of the last frame stored in iOldFrame to + // zero, so we don't resend any last frame on first entry here. + // Subsequent iterations of our receive loop will resend the iOldFrame + // if nothing is received, in an attempt at error recovery, unless of + // course we have deliberately reset iOldFrame to zero again + + //x = 0; + for (;;) + { + if (iModem->iProgress.iPhase != EPostPageStatus) + { + if (GetframeL (iResults) == 0) + { + if (successiveErrors++ > 3) + return (KFaxErrFrameFail); + + if (iModem->iProgress.iPhase == ECallEstablishment) + // no point in carrying on without any capability frame + { + iModem->ExportL (_L8 ("AT+FRH=3\r")); + continue; + } + // if (iModem->iProgress.iPhase == ECallEstablishment) + // { + // if (x++ == 3) + // return (KFaxErrFrameFail); + // + // iModem->ExportL (_L8 ("AT+FRH=3\r")); + // continue; + // } + + // no point in carrying on without any capability frame + // else + + iResults[1] = 0x10; + iResults[2] = 0xff; + + // this is a nonexistent response + // the effect of this is to force a retransmission of the TSI and DCS + // with (hopefully) a subsequent retrain in an attempt to resync + } + else + successiveErrors = 0; + + + + if (iResults[2] != 0xff) + { + + iModem->iOurMessage.Format (_L8 ("Response received")); + iModem->ProgressUpdateL (); + + // the third byte in the frame is the FCF (fax control field) + + switch ((TUint8) iResults[2]) + { + case 0xff: // this is our dummy octet to force a restart + break; + + case 0x40: // this marks the receiver ID + iModem->iOurMessage.Format (_L8 ("CSI identity Frame")); + iModem->ProgressUpdateL (); + iModem->iProgress.iAnswerback.Zero (); + for (i = 22; i > 2; i--) + iModem->iProgress.iAnswerback.Append (iResults[i]); + iModem->iOurMessage.Format (_L8 ("Remote fax ID is %S"), &iModem->iProgress.iAnswerback); + iModem->ProgressUpdateL (); + break; // the capability frame should follows + + case 0x80: // this marks the receiver capability frame + iFcfXbit = 1; // we've received a DIS, so set the X bit + iModem->iOurMessage.Format (_L8 ("DIS capability Frame")); + iModem->ProgressUpdateL (); + AnalyseDISL (); // analyse the DIS and compose a DCS + if (iDisFrame.bit.b10 != 1) + return (KFaxErrRemoteCannotReceive); // if the other machine can't receive we exit + break; + + case 0x84: // this marks a good train and is the normal exit from this loop + iModem->iOurMessage.Format (_L8 ("CFR confirmation Frame")); + iModem->ProgressUpdateL (); + return (TxSetHighSpeedL ()); + + case 0x44: // this marks a failed train so we drop the speed + iModem->iOurMessage.Format (_L8 ("FTT failure to train Frame")); + iModem->ProgressUpdateL (); + if (++trainingAttempts & 1) // train down on failures 2 4 6 8 + break; + if (iActualFaxSpeed == 73) + iActualFaxSpeed = 96; + else + iActualFaxSpeed -= 24; + if (iActualFaxSpeed < (iFaxServerSessionSettings->iMinSpeed / 100)) + { + TxDCNL (); + return (KFaxBelowMinSpeed); + } + break; + + case 0xfa: // this means we were asked to disconnect + RxDCNL (); + return (KFaxErrRemoteDCN); + + case 0x20: // this marks a non-standard frame, which we ignore + iModem->iOurMessage.Format (_L8 ("NSF nonstandard facilities Frame")); + iModem->ProgressUpdateL (); + break; + + // the last case is where we see an unsupported frame + // if it is a final frame we ask for a repeat via CRP + + default: + if (SendCRPL () != KErrNone) + return (KFaxErrCRP); + } + + // if not a final frame we just issue AT+FRH=3 and continue + + if (!(iResults[1] & 0x10)) + { + iModem->ExportL (_L8 ("AT+FRH=3\r")); + continue; + } + // otherwise we send our proposals, starting with our own ID + + iModem->iOurMessage.Format (_L8 ("Final frame received")); + iModem->ProgressUpdateL (); + } + }// if (iResults[2] !=0) statement + if (iModem->iProgress.iPhase == ECallEstablishment) + { + iModem->iOurMessage.Format (_L8 ("sending TSI")); + iModem->ProgressUpdateL (); + iFrame.Zero (); + iFrame.Append (KT30_CTLNXT); + iFrame.Append (KT30_TSI); + for (i = 20; i > iFaxServerSessionSettings->iFaxId.Length (); i--) + iFrame.Append (0x20); + for (i = iFaxServerSessionSettings->iFaxId.Length (); i;) + iFrame.Append (iFaxServerSessionSettings->iFaxId[--i]); + if (SendframeL (iFrame) != 1) + return (KFaxErrHDLC); + } + else + { + iModem->ExportL (_L8 ("AT+FTH=3\r")); + if (FramestatL () != 1) + return (KFaxErrHDLC); + } + + iModem->iProgress.iPhase = ESessionNegotiation; + + // before sending our DCS frame we ensure the speeds bits match what we want + + iDcsFrame.byte[1] &= 0xc3; + switch (iActualFaxSpeed) + { + case 48: + iDcsFrame.byte[1] |= 0x08; + break; /* 4800 */ + case 96: + iDcsFrame.byte[1] |= 0x04; + break; /* 9600 V.29 */ + case 97: + iDcsFrame.byte[1] |= 0x24; + break; /* 9600 V.17 */ + case 72: + iDcsFrame.byte[1] |= 0x0c; + break; /* 7200 V.29 */ + case 73: + iDcsFrame.byte[1] |= 0x2c; + break; /* 7200 V.17 */ + case 145: + iDcsFrame.byte[1] |= 0x20; + break; /* 14400 */ + case 121: + iDcsFrame.byte[1] |= 0x28; + break; /* 12000 */ + default: + iDcsFrame.byte[1] |= 0x00; /* 2400 */ + } + iModem->iOurMessage.Format (_L8 ("sending DCS ")); + iModem->ProgressUpdateL (); + iFrame.Zero (); + iFrame.Append (KT30_CTLLST); + iFrame.Append (KT30_DCS); + //x = 3; + for (i = 0; i < iDcsBytes; i++) + iFrame.Append (iDcsFrame.byte[i]); + if (SendframeL (iFrame) != 1) + return (KFaxErrHDLC); + + // after sending our DCS frame we wait fot 75 ms before training + // + // Note on the 75 millisecond delays + // ================================= + // At this point we need to introduce a 75 ms delay (+-20%). + // this is usually done with the AT+FTS=8 command and the code + // would normally run as follows : + // + // iModem->ExportL (_L8 ("AT+FTS=8\r")); + // if ((iModem->GetMatchL (_L8 ("OK"), 3)) == 0) + // return (KFaxErrStopAndWait); + // + // or, alternatively, we could use our own routines to delay + // for this amount of time using + // + // iModem->Silence (75000); + // + // However, the innards of the comms driver in EPOC32 introduces + // 2-tick delays on timed reads and writes - in other words, there + // was a 2-tick delay before the OK from our last frame was received + // and there will also be a 2-tick delay before the next command + // reaches the modem. Note that a 2-tick delay could be from 15 + // to 30 ms - we want a delay from 60ms to 90 ms (75 ms +- 20%) - + // which must be between 4 and 6 ticks. All the delays we use + // here are empirically arrived at via Faxlab testing rather + // than being worked out in advance. + + // NOTE : these delays are really applicable ONLY to Protea/ARM7100 + // the machine dependency is unavoidable under the + // circumstances. Protea ticks @ 64 Hz, which gives + // us 15.625 ms per tick. WINS ticks @ 10 Hz, + // which gives us 100 microseconds per tick - clearly a + // significant difference - the delta timers in the comms + // kernel are therefore also very different + + iModem->iOurMessage.Format (_L8 ("delaying for 75 ms")); + iModem->ProgressUpdateL (); + + // iModem->Silence ((iModem->iGranularity - 125) * 4); // 4 ticks pre-TCF - below iModem->iGranularity x 1 + + TInt delay=iModem->iCalls*37; //iCalls for 2ms *37 to get an approximatelly 75ms delay + TInt k=0; + + for (k=0;kclock(); + + i = (iActualFaxSpeed & 0xfe); + iModem->iOurMessage.Format (_L8 ("setting %d00"), i); + iModem->iProgress.iSpeed = (i * 100); + iModem->ProgressUpdateL (); + iResults.Copy (_L8 ("AT+FTM=")); + iResults.AppendNum (iActualFaxSpeed); + iResults.Append (_L8 ("\r")); + iModem->ExportL (iResults); + + // say how many bytes in the 1.5 second TCF + // prepare a null filled transmit buffer length and get length to maxnulls + // calculate minimum scan line times + // wait for the modem CONNECT + + trainbytes = (i * 150 / 8); + iModem->iOurMessage.Format (_L8 ("training sequence for %d bytes"), trainbytes); + + iModem->iTransmitBuffer.SetMax (); + iModem->iTransmitBuffer.FillZ (); + TInt maxnulls = iModem->iTransmitBuffer.Length (); + iModem->ProgressUpdateL (); + if (iMinscan == 0) + iMinlinelength = 0; + else + iMinlinelength = ((iModem->iProgress.iSpeed / (1000 / iMinscan)) / 8) + 1; + if ((iModem->GetMatchL (_L8 ("CONNECT"), 5)) == 0) + return (KFaxErrAtNegotiatedSpeed); + + // now we send our TCF with flow control + + iModem->Xonon (); + + iModem->iOurMessage.Format (_L8 ("Entering training loop")); + iModem->ProgressUpdateL (); + + while (trainbytes != 0) + { + if (maxnulls > trainbytes) + { + iModem->iTransmitBuffer.SetLength (trainbytes); + trainbytes = 0; + } + else + { + iModem->iTransmitBuffer.SetMax (); + trainbytes -= maxnulls; + } + iModem->CommitTransmitBufferL (); + } + + iModem->iTransmitBuffer.Append (Kdle); + iModem->iTransmitBuffer.Append (Ketx); + iModem->CommitTransmitBufferL (); + + + iModem->iOurMessage.Format (_L8 ("Train complete")); + iModem->ProgressUpdateL (); + + // now wait for the modem to return to command mode + + while (iModem->Rxstat () != 0) + { + ticks = CLK_TCK; + iModem->RxcharWaitL (ticks); + } + if ((iModem->GetMatchL (_L8 ("OK"), 5)) == 0) + return (KFaxErrTrainStop); + + iModem->Xonoff (); + // there's no frame to resend, so we prepare a CRP in case of + // any errors before looping for the response + + iOldFrame.Zero (); + iModem->ExportL (_L8 ("AT+FRH=3\r")); + } +} +/********************************************************************/ + +// this is a small function to set a class 1 fax modem to phase C + // transmission speed (found in iActualFaxSpeed) in preparation for + // sending data. This function is called before the first page + // and also between pages. Class 1 modems require that we delay for + // 75 ms before going to phase C transmission - see the note + // earlier on method of achieving a 75 ms delay here + +TInt CFaxClass1::TxSetHighSpeedL () +{ + TInt x, portSpeed; + iFrame.SetMax (); + iModem->iOurMessage.Format (_L8 ("delaying for 75 ms")); + iModem->ProgressUpdateL (); +// iModem->Silence ((iModem->iGranularity - 125) * 4); // 4 ticks pre-T4 - below iModem->iGranularity x 3 + + TInt delay=iModem->iCalls*37; //iCalls for 2ms *37 to get an approximatelly 75ms delay + TInt k=0; + + for (k=0;kclock(); + + switch (iActualFaxSpeed) + { + case 145: + x = 144; + portSpeed = 146; + break; + case 121: + x = 120; + portSpeed = 122; + break; + case 97: + x = 96; + portSpeed = 98; + break; + case 73: + x = 72; + portSpeed = 74; + break; + default: + x = portSpeed = iActualFaxSpeed; + } + iModem->iOurMessage.Format (_L8 ("setting %d00"), x); + iModem->iProgress.iSpeed = (x * 100); + iModem->ProgressUpdateL (); + iResults.Copy (_L8 ("AT+FTM=")); + iResults.AppendNum (portSpeed); + iResults.Append (_L8 ("\r")); + iModem->ExportL (iResults); + if (FramestatL () != 1) + return (KFaxErrAtNegotiatedSpeed); + return (TxStartPageL ()); +} +/********************************************************************/ + +// here's where we wait after sending a page and the postpage +// message to see what the receiver thought - there are five responses +// MCF RTP PIP = good page RTN PIN = bad page +// +// TxPostPage should return either with +// +// a) an error code and iPhase set to RFax::EPostPageStatus, in which case the send returns with the error +// b) KErrNone and iPhase set to RFax::EDataTransfer, in which case we send the next page +// c) KErrNone and iPhase set to RFax::EDisconnection, in which case the send returns with KErrNone +// + +TInt CFaxClass1::TxPostPageL () +{ + if (iModem->iProgress.iCompression == EModifiedRead) + { + iModem->iTransmitBuffer.Append (0x00); + iModem->iTransmitBuffer.Append (0x60); + iModem->iTransmitBuffer.Append (0x00); + iModem->iTransmitBuffer.Append (0x0C); + iModem->iTransmitBuffer.Append (0x80); + iModem->iTransmitBuffer.Append (0x01); + iModem->iTransmitBuffer.Append (0x30); + iModem->iTransmitBuffer.Append (0x00); + iModem->iTransmitBuffer.Append (0x06); + iModem->iTransmitBuffer.Append (0xC0); + } + else + { + for (TInt x = 3; x; x--) + { + iModem->iTransmitBuffer.Append (0x0); + iModem->iTransmitBuffer.Append (0x08); + iModem->iTransmitBuffer.Append (0x80); + } + } + iModem->iTransmitBuffer.Append (Kdle); + iModem->iTransmitBuffer.Append (Ketx); + iModem->CommitTransmitBufferL (); + + iModem->iOurMessage.Format (_L8 (" transmitted after %d lines"), iModem->iProgress.iLines); + iModem->iProgress.iPhase = EPostPageStatus; + iModem->ProgressUpdateL (); + while (iModem->Txstat () != 0) + ; + + // we've just ended phase C data, so we need to wait for the modem to respond with OK + + if (iModem->GetMatchL (_L8 ("OK"), (32 * 1024) / (iModem->iProgress.iSpeed / 10)) == 0) + return (KFaxErrCannotEndData); + + iModem->Xonoff (); + + iModem->iOurMessage.Format (_L8 ("delaying for 75 ms")); + iModem->ProgressUpdateL (); + + +/************************************* NOTE ******************************************************* +// see the note earlier on reason for the lack of an explicit 75 ms delay here + + +// iModem->Silence ((iModem->iGranularity - 125) * 4); 4 ticks post-T4 - below iModem->iGranularity x 1 +// The above line of code was removed because the Silence function calls User::After which is fairly inaccurate +// Faxlab revealed that we were actually waiting for 432 ms !!! instead of the recomended 75ms +- 20% +// The delay is now generated using the timing callibration loop +// This is more CPU intensive than the User::After call because essentially is a aoftware delay loop but +// will enhance the reliability of fax class 1 especially over GSM +*****************************************************************************************************************/ + TInt delay=iModem->iCalls*30; //iCalls for 2ms *37 to get an approximatelly 75ms delay + TInt k=0; + + for (k=0;kclock(); + + iOldFrame.Zero (); + iFrame.Zero (); + iFrame.Append (KT30_CTLLST); + + + if ((iFaxServerSessionSettings->iTxPages) == iModem->iProgress.iPage) + { + iModem->iOurMessage.Format (_L8 ("sending EOP")); + iModem->ProgressUpdateL (); + + iFrame.Append ((KT30_EOP)); + if (SendframeL (iFrame) == 0) + return (KFaxErrEOP); + + iModem->iOurMessage.Format (_L8 ("End of document transmitted")); + iModem->ProgressUpdateL (); + } + else + { + iModem->iOurMessage.Format (_L8 ("sending MPS")); + iModem->ProgressUpdateL (); + + iFrame.Append ((KT30_MPS)); + if (SendframeL (iFrame) == 0) + return (KFaxErrMPS); + + iModem->iOurMessage.Format (_L8 ("End of page %u transmitted"), iModem->iProgress.iPage); + iModem->ProgressUpdateL (); + } + + // now we await the post-page response from the receiver + // we loop here because we need a final frame + + for (;;) + { + iModem->ExportL (_L8 ("AT+FRH=3\r")); + if (GetframeL (iResults) == 0) + return (KFaxErrFrameFail); + iModem->iOurMessage.Format (_L8 ("Response received")); + iModem->ProgressUpdateL (); + + // the third byte in the frame is the FCF (fax control field) + // for those we recognize straight off, we say so + // for any others, we either loop immediately or after a CRP request + + switch ((TUint8) iResults[2]) + { + case 0x8c: + iModem->iOurMessage.Format (_L8 ("MCF")); + break; + case 0xcc: + iModem->iOurMessage.Format (_L8 ("RTP")); + break; + case 0xac: + iModem->iOurMessage.Format (_L8 ("PIP")); + break; + case 0x4c: + iModem->iOurMessage.Format (_L8 ("RTN")); + break; + case 0x2c: + iModem->iOurMessage.Format (_L8 ("PIN")); + break; + + // the last case is where we see an unsupported frame + // if it is a final frame we ask for a repeat via CRP + + default: + if (SendCRPL () != KErrNone) + return (KFaxErrCRP); + continue; + } + + // now back to look at the FCF some more + switch ((TUint8) iResults[2]) + { + case 0x8c: + case 0xcc: // for good pages we say confirmed + case 0xac: + iModem->iOurMessage.Append (_L8 (" message confirmation")); + iModem->ProgressUpdateL (); + break; + + case 0x4c: // for bad pages we say no good + case 0x2c: // if we haven't done so, resent last page + iModem->iOurMessage.Append (_L8 (" : page not confirmed")); + iModem->ProgressUpdateL (); + if (iRepeatPage == 0) + { + iRepeatPage++; + iModem->iProgress.iPage--; + return (TxPrePageL ()); + } + + default:; + } + + // we aren't going to resend the last page now + + iRepeatPage = 0; + + // if we've reached the end, we just quit + + if ((iFaxServerSessionSettings->iTxPages) == iModem->iProgress.iPage) + { + return (TxDCNL ()); + } + + // if we've received an MCF we carry on with phase C + + if (iResults[2] == KT30_MCF) // carry on with phase C only if MCF + + { + return (TxSetHighSpeedL ()); + } + + // we renegotiate if PIP or RTP, or PIN or RTN with no resend + + iModem->iOurMessage.Format (_L8 ("Renegotiating session parameters")); + iModem->ProgressUpdateL (); + return (TxPrePageL ()); + } +} +/********************************************************************/ + +// the analysis of the DIS frame and composition of the DCS frame + // has been moved here for readability + +inline void CFaxClass1::AnalyseDISL () +{ + TInt i; + + // we copy iResults to our iDisFrame and compose our reply in iDcsFrame + + for (i = 0; i < 5; i++) + iDisFrame.byte[i] = (TUint8) iResults[i + 3]; + + for (i = 0; i < 5; i++) + iDcsFrame.byte[i] = 0; + + // we always set T.4 + + iDcsFrame.bit.b10 = 1; + + // we check the speed capability next and reset our iActualFaxSpeed + + switch (iDisFrame.byte[1] & 0x3c) + { + case 0x08: + i = 48; + break; /* V.27 ter 4800 2400 */ + case 0x0c: + i = 96; + break; /* V.29 9600 7200 + V.27 */ + case 0x2c: + i = 145; + break; /* V.17 14400 + V.29 + V.27 */ + default: + i = 24; /* V.27 fallback 2400 only */ + } + if (i < (iActualFaxSpeed)) + iActualFaxSpeed = i; + + // we set our resolution to that of the fax we want to send + // but if the receiver can only understand normal resolution + // then we send all our faxes as normal and resign ourselves + // to stretching them to double length + + iDcsFrame.bit.b15 = iFaxServerSessionSettings->iTxResolution; + if (iDisFrame.bit.b15 == 0) + iDcsFrame.bit.b15 = 0; + iModem->iProgress.iResolution = TFaxResolution (iDcsFrame.bit.b15); + + // we set our compression to that of the fax we want to send + // unless the receiver can only understand 1D compression - in + // which case the sender should be able to compensate from the + // progress settings + + iDcsFrame.bit.b16 = iFaxServerSessionSettings->iTxCompression; + if (iDisFrame.bit.b16 == 0) + { + iDcsFrame.bit.b16 = 0; + } + + if ((iDisFrame.bit.b16==1) && ((iFaxServerSessionSettings->iTxCompression==EModifiedRead))) + { + iDcsFrame.bit.b16 = 1; + } + + iModem->iProgress.iCompression = TFaxCompression (iDcsFrame.bit.b16); + + if (iModem->iProgress.iCompression==0) + { + iModem->iOurMessage.Format (_L8("DCS frame set to 1D")); + iModem->ProgressUpdateL(); + } + else + { + iModem->iOurMessage.Format (_L8("DCS frame set to 2D")); + iModem->ProgressUpdateL(); + } + + + // we set the minumum scan line time to that of the receiver + + iDcsFrame.byte[2] &= 0x8f; + switch (iDisFrame.byte[2] & 0x70) + { + case 0x70: + iMinscan = 0; + iDcsFrame.byte[2] |= 0x70; + break; /* b21=1 b22=1 b23=1 */ + case 0x50: + if (iDcsFrame.bit.b15 == 0) + { + iMinscan = 40; + iDcsFrame.byte[2] |= 0x40; + } + else + { + iMinscan = 20; + } + break; /* b21=1 b22=0 b23=1 - for fine res, /by 2 */ + case 0x30: + if (iDcsFrame.bit.b15 == 0) + { + iMinscan = 20; + } + else + { + iMinscan = 10; + iDcsFrame.byte[2] |= 0x20; + } + break; /* b21=1 b22=1 b23=0 - for fine res, /by 2 */ + case 0x60: + if (iDcsFrame.bit.b15 == 0) + { + iMinscan = 10; + iDcsFrame.byte[2] |= 0x20; + } + else + { + iMinscan = 5; + iDcsFrame.byte[2] |= 0x10; + } + break; /* b21=0 b22=1 b23=1 - for fine res, /by 2 */ + case 0x10: + iMinscan = 5; + iDcsFrame.byte[2] |= 0x10; + break; /* b21=1 b22=0 b23=0 */ + case 0x20: + iMinscan = 10; + iDcsFrame.byte[2] |= 0x20; + break; /* b21=0 b22=1 b23=0 */ + case 0x40: + iMinscan = 40; + iDcsFrame.byte[2] |= 0x40; + break; /* b21=0 b22=0 b23=1 */ + default: + iMinscan = 20; /* b21=0 b22=0 b23=0 */ + } + + // lastly, we always match our page length to the receiver page length + + iDcsFrame.bit.b19 = iDisFrame.bit.b19; + iDcsFrame.bit.b20 = iDisFrame.bit.b20; +} +/********************************************************************/ + +// this is where we wait for modem responses + + // this function can be called either with a timeout in seconds + // or with nothing, in which case we use a default as follows : + + // when we wait for the frame data we use a 6 second timeout + // as specified in T.30 as timer T2 if we are waiting for + // a command or a 3 second timer as specified in timer T4 + // if we are waiting for a response, with the presence of + // a resendable frame in iOldFrame being the test + + // notice that importL() takes a timeout specified in seconds + + // normally OK and CONNECT are good with ERROR or NO CARRIER being bad + // we save the actual result for inspection as ERROR codes after + // frame reception with AT+FRH needs special handling via GETFRAMESTAT + +TInt CFaxClass1::FramestatL () +{ + TInt ticks; + if (iOldFrame.Length () == 0) + ticks = KT30_T2; + else + ticks = KT30_T4; + return (FramestatL (ticks)); +} +/********************************************************************/ + +TInt CFaxClass1::FramestatL (TInt aTicks) +{ + for (;;) + { + iModemString.SetMax (); + if (iModem->ImportL (iModemString, aTicks) == 0) + return (-1);; + + iModem->iOurMessage.Format (_L8 ("%S"), &iModemString); + iModem->ProgressUpdateL (); + + if ((iModemString.FindF (_L8 ("OK"))) >= 0) + return (1); + if ((iModemString.FindF (_L8 ("CONNECT"))) >= 0) + return (1); + if ((iModemString.FindF (_L8 ("ERROR"))) >= 0) + return (0); + if ((iModemString.FindF (_L8 ("NO CARRIER"))) >= 0) + return (0); + } +} +/********************************************************************/ + +// if we have an ERROR result on receiving a frame it means that + // the CRC was incorrect - the action is to wait until 200 ms of + // silence have elapsed before resending the last frame - we use + // the ReceiveSilenceL function for this + +TInt CFaxClass1::GetFramestatL () +{ + TInt code = FramestatL (); + if ((iModemString.FindF (_L8 ("ERROR"))) >= 0) + ReceiveSilenceL (); + return (code); +} +/********************************************************************/ + +// this is the HDLC frame reception handler after AT+FRH commands + +TInt CFaxClass1::GetframeL (TDes8 & aResult) +{ + TInt i, retries = 0, frameStatus = 1; + TInt ticks; + TUint8 thisChar, lastChar; + TBuf8 < 4 > iHexchar; + + iModem->iOurMessage.Format (_L8 ("Waiting for Frame")); + iModem->ProgressUpdateL (); + + // framestat is set to 1 for no error before entry to the loop + + for (;;) + { + + // we only try three times before giving up + + retries++; + if (retries > 3) + return (0); + + // if we timed out, cancel the frame before proceeding + + if (frameStatus < 0) + { + iModem->TxcharL (Kcan); + iModem->iOurMessage.Format (_L8 ("Frame timed out")); + iModem->ProgressUpdateL (); + GetFramestatL (); + } + + // on any error, we resend the last frame if possible before + // waiting for another go + + if (frameStatus != 1) + { + if (SendframeL (iOldFrame) == 0) + { + iModem->iOurMessage.Format (_L8 ("Cannot resend Frame")); + iModem->ProgressUpdateL (); + return (0); + } + iModem->iOurMessage.Format (_L8 ("Frame has been resent")); + iModem->ProgressUpdateL (); + iModem->ExportL (_L8 ("AT+FRH=3\r")); + } + + // if we've just answered the phone, we don't wait for a result + // otherwise we must have an OK or CONNECT before proceeding + + if ((aResult.Compare (_L8 ("CALL JUST ANSWERED"))) == 0) + { + frameStatus = 1; + } + else + { + frameStatus = GetFramestatL (); + } + + aResult.FillZ (); + aResult.SetMax (); + + // if we got NO CARRIER or ERROR then we have to try again + + if (frameStatus != 1) + continue; + + // when we wait for the frame data we use a 6 second timeout + // as specified in T.30 as timer T2 if we are waiting for + // a command or a 3 second timer as specified in timer T4 + // if we are waiting for a response, with the presence of + // a resendable frame in iOldFrame being the test + + // note that RxcharWaitL () takes a timeout in microseconds + + if (iOldFrame.Length () == 0) + ticks = CLK_TCK * KT30_T2; + else + ticks = CLK_TCK * KT30_T4; + + // we expect data with dle shielding, ending with dle etx, + // and with a hex dump for our session log + + // there is a 3 second maximum length to a frame but + // the modem will detect bad HDLC frames for us and + // flag with ERROR + + for (i = 0, lastChar = 0; i < 64;) + { + if ((iModem->RxcharWaitL (ticks)) == 0) + { + frameStatus = (-1); + break; + } + thisChar = iModem->iReadone[0]; + if (lastChar == Kdle) + { + if (thisChar == Ketx) + break; + lastChar = 0; + if (thisChar != Kdle) + continue; + } + else if (thisChar == Kdle) + { + lastChar = Kdle; + continue; + } + aResult[i++] = thisChar; + + iHexchar.Format (_L8 ("%x "), thisChar); + if ((iModem->iOurMessage.Length () + iHexchar.Length ()) + 18 < iModem->iOurMessage.MaxLength ()) + iModem->iOurMessage.Append (iHexchar); + } + + iModem->ProgressUpdateL (); + + // if we timed out during the wait, then go round again and handle it + + if (frameStatus != 1) + continue; + + // otherwise we wait for the result code following the dle etx + // and handle any errors from that + + frameStatus = GetFramestatL (); + if (frameStatus != 1) + continue; + + // the third byte in the frame is the FCF (fax control field) + // we don't care whether we originated or answered the call + // so we always knock off the T.30 X bit (LSB) + + aResult[2] &= 0xfe; + + // if we have been asked for a frame repeat we do that in here + + if (aResult[2] == 0x1a) + { + iModem->iOurMessage.Format (_L8 ("CRP command repeat Frame")); + iModem->ProgressUpdateL (); + ReceiveSilenceL (); + frameStatus = 0; // treat as a timeout and resend + continue; + } + + // otherwise we can now return with success + + return (1); + } +} +/********************************************************************/ + +// this is the send HDLC frame handler following AT+FTH commands + +TInt CFaxClass1::SendframeL (TDes8 & newframe) +{ + TUint8 i; + TInt frameSize; + TBuf8 < 4 > iHexchar; + + // we take a copy of the frame we've been passed in case we want + // to resend for error recovery during GetFrame - it's the copy + // we work with + + if (&newframe != &iOldFrame) + iOldFrame.Copy (newframe); + frameSize = iOldFrame.Length (); + if (frameSize < 2) + return (0); + + // all frames must be at least three characters + // note that calling SendframeL with an empty frame disables + // resends - iOldFrame.Zero() is rather quicker though + + // we don't need to tell the modem that we're going to send a frame + // if we're sending DCS or CSI or DIS/DTC for the first time after a CONNECT response + // (not a resend) - in all other cases we need to send AT+FTH + + iOldFrame[1] |= iFcfXbit; // combine the FCF with the X bit + i = iOldFrame[1]; + + if (!((&newframe != &iOldFrame) && (i == (KT30_DCS|iFcfXbit)) || (i == KT30_CSI) || (i == (KT30_DIS|iFcfXbit)))) + { + iModem->ExportL (_L8 ("AT+FTH=3\r")); + if (FramestatL () != 1) + return (0); + } + + // we have a short delay before sending data here after any modem response + + iModem->ExportL (_L8 ("")); + + // we now send the frame, starting with the fixed address followed + // by the data we have been passed. We use dle shielding and end + // with dle etx and a hex dump before returning with the modem + // response code + + iModem->TxcharL (KT30_ADDR); + iModem->iOurMessage.Format (_L8 ("%x "), KT30_ADDR); + for (i = 0; i < frameSize; i++) + { + iModem->TxcharL (iOldFrame[i]); + iHexchar.Format (_L8 ("%x "), iOldFrame[i]); + if ((iModem->iOurMessage.Length () + iHexchar.Length ()) + 18 < iModem->iOurMessage.MaxLength ()) + iModem->iOurMessage.Append (iHexchar); + if (iOldFrame[i] == Kdle) + iModem->TxcharL (Kdle); + } + iModem->TxcharL (Kdle); + iModem->TxcharL (Ketx); + iModem->ProgressUpdateL (); + return (FramestatL ()); +} +/********************************************************************/ + +// this is short routine to request a resend of a frame + +TInt CFaxClass1::SendCRPL () +{ + iModem->iOurMessage.Format (_L8 ("Inappropriate frame %x"), (TUint8) iResults[2]); + iModem->ProgressUpdateL (); + if (iResults[1] & 0x10) + { + iModem->iOurMessage.Format (_L8 ("sending CRP")); + iModem->ProgressUpdateL (); + ReceiveSilenceL (); + iFrame.Zero (); + iFrame.Append (KT30_CTLLST); + iFrame.Append (KT30_CRP); + if (SendframeL (iFrame) == 0) + return (KFaxErrCRP); + iResults[1] = 0x0; + } + return (KErrNone); +} +/********************************************************************/ + +// this utility waits for 200 ms of silence before proceeding. We allow +// a three second timeout here in case we are waiting for a train to +// finish. we used the modem AT+FRS command - if it fails we'll have +// waited for three seconds in any case, so why bother with an error ? - +// this code is used mostly for error recovery purposes but note that +// not all modems support the +FRS command properly for example the +// Megahertz PCMCIA sportster + +void CFaxClass1::ReceiveSilenceL () +{ + iModem->ExportL (_L8 ("AT+FRS=20\r")); + if ((iModem->GetMatchL (_L8 ("OK"), 3)) == 0) + { + iModem->iOurMessage.Format (_L8 ("Timeout waiting for silence")); + iModem->ProgressUpdateL (); + iModem->TxcharL (Kcan); + if (iModem->GetMatchL (_L8 ("OK"), 1) == 0) + iModem->TxcharL (Kreturn); + } +} +/********************************************************************/ +