fax/faxclientandserver/FAXSVR/CFAX1.CPP
author Pat Downey <patd@symbian.org>
Wed, 01 Sep 2010 12:40:21 +0100
branchRCL_3
changeset 20 07a122eea281
parent 0 3553901f7fa8
permissions -rw-r--r--
Revert incorrect RCL_3 drop: Revision: 201035 Kit: 201035

// 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 <dle><etx>
				// 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;k<delay;k++)		// this loop will  generate the 75ms delay
		iModem->clock();
		
		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;k<delay;k++)		// this loop will  generate the 75ms delay
		iModem->clock();

	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 ("<dle><etx> 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;k<delay;k++)		// this loop will  generate the 75ms delay
		iModem->clock();

	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);
		}
}
/********************************************************************/