// 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 "FONT8X16.DAT"
#include "ZEROS.DAT"
#include "FAXMDRV.H"
#include "FAXMODEM.H"
#include "FAXLOG.H"
#include <et_phone.h>
const TInt KLineReadTimeout=6; // < The time-out (in secs) for a line read
const TInt KECMLineReadTimeout=132; // < The time-out (in secs) for a line read when using Error Correction Mode
const TInt KSubsequentLineReadTimeout=10; // < The time-out (in secs) for a subsequent line read
const TInt KSubsequentECMLineReadTimeout=140; // < The time-out (in secs) for a subsequent line read when using Error Correction Mode
/********************************************************************/
// we need to keep a local pointer to the current TDialstorModemPreferences in order to
// do whatever is required
void CFaxModemDriver::ConstructL (TFaxServerSessionSettings * iFaxServerSessionSettings, RFax::TProgress & aProgress)
{
iModem = CFaxModem::NewL (iFaxServerSessionSettings, aProgress);
}
/********************************************************************/
CFaxModemDriver::CFaxModemDriver()
:CBase(), iSharedHandles(NULL)
{
}
CFaxModemDriver::~CFaxModemDriver ()
{
delete iModem;
}
/********************************************************************/
// function to check that we don't send commands out when there's less than half a second
// before the next RING comes in
/**
* function to check that we don't send commands out when there's less than half a second
* before the next RING comes in.
* This function has been modified by Dmitry Lyokhin for the sake of PIA-586KGE defect fix
*
* @param aCommand - contains the command to be sent to the modem.
*
* @see ExportL function
*
* @return number of symbols sent to the modem.
*
*
*/
TInt CFaxModemDriver::CheckCadenceExportL(const TDesC8 & aCommand)
{
const TInt RingTimeout_Sec = 5; //-- 5 sec. waiting for 'RING' timeout
const TInt RingCadence_uSec = 3000000; //-- 3 sec. time span after 'RING' receiving during that commands
//-- can be sent to the modem
const TInt CmdSendDelay_uSec= 100000; //-- 100 ms delay between adjacent commands to the modem
TTime CurrentTime;
__FLOG_FAXSRV( _L8("-CFaxModemDriver::CheckCadenceExportL entering"));
//-- @note iCadence now is used like a flag. If its value is 0 (thai is set initially) we will
//-- try to wait for 'RING' indication i.e synchronize with incoming rings. Otherwise - no.
while( iCadence.Int64() == 0 )
{
if(iTimeOfLastRing.Int64() == 0)
{//-- we need to wait for 'RING' from the modem
__FLOG_FAXSRV( _L8("-CFaxModemDriver::CheckCadenceExportL waiting for RING"));
//-- wait for 'RING' from modem
if( iModem->GetMatchL (_L8 ("RING"), RingTimeout_Sec) == 0)
{ //User::Leave(KFaxErrReceiveTimeout); //-- 'RING' waiting timeout, leaving
//-- There is no 'RING'indication, no we will not leave, instead of that
//-- disable sync. with incoming rings and pass on to sending commands to the modem straightforward.
iCadence = 1;
break;
}
iTimeOfLastRing.UniversalTime(); //-- note the time
}
//-- get current time and check if we are in time to send a command to the modem
CurrentTime.UniversalTime ();
if( CurrentTime < iTimeOfLastRing + TTimeIntervalMicroSeconds32(RingCadence_uSec) )
break; //-- send the command
else
{ //-- wait for the next 'RING'
__FLOG_FAXSRV( _L8("-CFaxModemDriver::CheckCadenceExportL Resetting"));
iTimeOfLastRing = 0;
}
}
//-- I had to introduce this delay between sending adjacent commands to the modem because
//-- some modems (e.g. Nokia9210) lose data.
iModem->Silence(CmdSendDelay_uSec);
//-- send the command
return iModem->ExportL (aCommand);
}
/********************************************************************/
// if we have called FaxInL with a dial request, it calls this routine
// to dial up a fax on demand service
//
// if not polling, we end with a colon to return to command mode and then
// we delay for whatever time has been requested by the user
// before returning, at which point FaxInL continues with ATA
void CFaxModemDriver::DialFaxOnDemandL ()
{
iModem->ExportL (_L8 ("ATD"));
iModem->ExportL (iFaxServerSessionSettings->iPhoneNumber);
if (iFaxServerSessionSettings->iMode & KFaxPoll)
iModem->TxcharL (Kreturn);
else
{
iModem->ExportL (_L8 (";"));
iModem->TxcharL (Kreturn);
if ((iModem->GetMatchL (_L8 ("OK"), KDialTimeout)) == 0)
User::Leave (KFaxErrNoDial);
iModem->Silence (CLK_TCK * iFaxServerSessionSettings->iFaxOnDemandDelay);
}
}
/********************************************************************/
// now the routines to add a header line to the top of each fax page
/********************************************************************/
// we send four blank scan lines
// we create a line of text containing time and date,
// the Fax ID and Username,
// the page number and total number of pages,
// we digitize that, and send it out
// our font height is 16 so that means 20 scan lines
// are added to the top of each sent fax
void CFaxModemDriver::SendFaxHeaderL ()
{
TFaxHeaderInfo faxHeader;
TRawScanLine headline;
TRawScanLine fontline;
TBuf8 < KFaxT4MaxDesLength > encodedHeadLine;
TTime timeOfTransmission;
TBuf < 12 > timeText;
timeOfTransmission.HomeTime();
timeOfTransmission.FormatL (timeText, (_L ("%F%D%M%Y%H%T")));
iModem->iOurMessage.Format (_L8 ("Sending page header"));
iModem->ProgressUpdateL ();
for (TInt r = iModem->iProgress.iResolution ; r >= 0; r--)
{
for (TInt x = 0, y = 4 ; x < 4; x++)
{
iModem->iTransmitBuffer.Append (0x00);
if (iModem->iProgress.iCompression)
{
iModem->iTransmitBuffer.Append (Invert (0x00));
iModem->iTransmitBuffer.Append (Invert (0x34));
y = 5;
}
else
iModem->iTransmitBuffer.Append (Invert (0x14));
iModem->iTransmitBuffer.Append (Invert (0xD9));
iModem->iTransmitBuffer.Append (Invert (0xA8));
padLineL (y);
}
}
CFaxT4 * faxT4 =CFaxT4::NewLC();
faxT4->PageInitialize (iModem->iProgress.iResolution, iModem->iProgress.iCompression);
if(!iSharedHandles)
{
User::Leave(KErrBadHandle);
}
CFaxHeaderLines * faxheader = CFaxHeaderLines::NewLC (&iSharedHandles->File());
faxheader->ReadFaxHeaderInfoL (faxHeader);
for (TInt n = 0; n < 12; n++)
timeText[n] -= '0';
// Forces 2 digit day - 2 digit month - 4 digit year - 2 digit hour - 2 digit minute
for (TInt scanline = 0; scanline < faxHeader.iHeaderFontHeightInLines; scanline++)
{
faxheader->ReadRawHeaderLineL (scanline, headline);
faxheader->ReadRawFontLineL (scanline, fontline);
for (TInt fontByte = 0; fontByte < faxHeader.iHeaderFontWidthInBytes; fontByte++)
{
headline[((faxHeader.iOffsetToDay) * faxHeader.iHeaderFontWidthInBytes) + fontByte] = fontline[(timeText[0] * faxHeader.iHeaderFontWidthInBytes) + fontByte];
headline[((faxHeader.iOffsetToDay + 1) * faxHeader.iHeaderFontWidthInBytes) + fontByte] = fontline[(timeText[1] * faxHeader.iHeaderFontWidthInBytes) + fontByte];
headline[((faxHeader.iOffsetToMonth) * faxHeader.iHeaderFontWidthInBytes) + fontByte] = fontline[(timeText[2] * faxHeader.iHeaderFontWidthInBytes) + fontByte];
headline[((faxHeader.iOffsetToMonth + 1) * faxHeader.iHeaderFontWidthInBytes) + fontByte] = fontline[(timeText[3] * faxHeader.iHeaderFontWidthInBytes) + fontByte];
headline[((faxHeader.iOffsetToYear) * faxHeader.iHeaderFontWidthInBytes) + fontByte] = fontline[(timeText[4] * faxHeader.iHeaderFontWidthInBytes) + fontByte];
headline[((faxHeader.iOffsetToYear + 1) * faxHeader.iHeaderFontWidthInBytes) + fontByte] = fontline[(timeText[5] * faxHeader.iHeaderFontWidthInBytes) + fontByte];
headline[((faxHeader.iOffsetToYear + 2) * faxHeader.iHeaderFontWidthInBytes) + fontByte] = fontline[(timeText[6] * faxHeader.iHeaderFontWidthInBytes) + fontByte];
headline[((faxHeader.iOffsetToYear + 3) * faxHeader.iHeaderFontWidthInBytes) + fontByte] = fontline[(timeText[7] * faxHeader.iHeaderFontWidthInBytes) + fontByte];
headline[((faxHeader.iOffsetToHour) * faxHeader.iHeaderFontWidthInBytes) + fontByte] = fontline[(timeText[8] * faxHeader.iHeaderFontWidthInBytes) + fontByte];
headline[((faxHeader.iOffsetToHour + 1) * faxHeader.iHeaderFontWidthInBytes) + fontByte] = fontline[(timeText[9] * faxHeader.iHeaderFontWidthInBytes) + fontByte];
headline[((faxHeader.iOffsetToMinute) * faxHeader.iHeaderFontWidthInBytes) + fontByte] = fontline[(timeText[10] * faxHeader.iHeaderFontWidthInBytes) + fontByte];
headline[((faxHeader.iOffsetToMinute + 1) * faxHeader.iHeaderFontWidthInBytes) + fontByte] = fontline[(timeText[11] * faxHeader.iHeaderFontWidthInBytes) + fontByte];
// put the page info in
if (iModem->iProgress.iPage > 9)
headline[((faxHeader.iOffsetToCurrentPage) * faxHeader.iHeaderFontWidthInBytes) + fontByte] = fontline[((iModem->iProgress.iPage / 10) * faxHeader.iHeaderFontWidthInBytes) + fontByte];
headline[((faxHeader.iOffsetToCurrentPage + 1) * faxHeader.iHeaderFontWidthInBytes) + fontByte] = fontline[((iModem->iProgress.iPage % 10) * faxHeader.iHeaderFontWidthInBytes) + fontByte];
if (iFaxServerSessionSettings->iTxPages > 9)
headline[((faxHeader.iOffsetToTotalPages) * faxHeader.iHeaderFontWidthInBytes) + fontByte] = fontline[((iFaxServerSessionSettings->iTxPages / 10) * faxHeader.iHeaderFontWidthInBytes) + fontByte];
headline[((faxHeader.iOffsetToTotalPages + 1) * faxHeader.iHeaderFontWidthInBytes) + fontByte] = fontline[((iFaxServerSessionSettings->iTxPages % 10) * faxHeader.iHeaderFontWidthInBytes) + fontByte];
}
// we send the line once in normal resolution but twice in fine resolution
for (TInt r = iModem->iProgress.iResolution ; r >= 0; r--)
{
faxT4->EncodeScanLine (headline, encodedHeadLine);
// always add the extra null at the start for the initial eol
iModem->iTransmitBuffer.Append (Knul);
// and then invert and finally send the encoded line
const TUint8 *px = CONST_CAST (TUint8 *, encodedHeadLine.Ptr ());
const TUint8 *ex = px + encodedHeadLine.Length ();;
TUint8 thisChar;
TInt bytesSent = 0;
while (px < ex)
{
thisChar = Invert (*px++);
iModem->iTransmitBuffer.Append (thisChar);
if (thisChar == Kdle)
iModem->iTransmitBuffer.Append (Kdle);
iModem->SendTransmitBufferL ();
bytesSent++;
}
padLineL (bytesSent);
}
}
CleanupStack::PopAndDestroy(faxheader);
CleanupStack::PopAndDestroy(faxT4);
}
/********************************************************************/
// this function handles line padding out for minimum scan line times
// this feature should really only be needed on class 1 modems ...
// it takes a single integer parameter, which is the number of bytes sent
void CFaxModemDriver::padLineL (TInt aByteCount)
{
if (iFaxServerSessionSettings->iFaxClass == EClass1)
{
while (iMinlinelength > aByteCount)
{
aByteCount++;
iModem->iTransmitBuffer.Append (Knul);
iModem->SendTransmitBufferL ();
}
}
}
/********************************************************************/
// rewritten to avoid using any AT commands August 1997 Andrew Margolis
// pointer arithmetic courtesy of Andrew Thoelke
// Though this doesn't use any AT command but it does need to know
// about the various modem classes ... GetFaxDataL is in the same boat
// the client has buffered up lines to minimize interaction
// aData starts with a TInt containing the number of lines
// Each line follows, preceded with a TInt containing its length
// which must be copied as it might not be aligned on a 4-byte
// boundary
TInt CFaxModemDriver::SendFaxDataL (const TDesC8 * aData)
{
TUint8 thisChar;
TInt ticks;
TInt numberOfLines;
TInt lengthOfLine;
TUint8 *thisLine = CONST_CAST (TUint8 *, (*aData).Ptr ());
Mem::Copy (&numberOfLines, thisLine, sizeof (TInt));
thisLine += sizeof (TInt);
// buffering debug message
iModem->iOurMessage.Format (_L8 ("%u lines in buffer taking up %u bytes"), numberOfLines, (*aData).Length ());
iModem->ProgressUpdateL ();
while (numberOfLines--)
{
Mem::Copy (&lengthOfLine, thisLine, sizeof (TInt));
thisLine += sizeof (TInt);
iModem->iProgress.iLines++;
TInt bytesSent = 0;
const TUint8 *px = thisLine;
const TUint8 *ex = px + lengthOfLine;
while (px < ex)
{
thisChar = Invert (*px++);
iModem->iTransmitBuffer.Append (thisChar);
if (thisChar == Kdle)
iModem->iTransmitBuffer.Append (Kdle);
iModem->SendTransmitBufferL ();
bytesSent++;
}
padLineL (bytesSent);
// we must check for cancel commands from the modem in class 2.0 transmission
if (iFaxServerSessionSettings->iFaxClass == EClass2point0)
{
while (iModem->Rxstat () != 0)
{
ticks = CLK_TCK;
iModem->RxcharWaitL (ticks);
if (iModem->iReadone[0] == Kcan)
{
iModem->TxcharL (Kdle);
iModem->TxcharL (Ketx);
iModem->CommitTransmitBufferL ();
iModem->Xonoff ();
iModem->GetMatchL (_L8 ("OK"), 5);
return (KFaxErrModemDisconnect);
}
}
}
thisLine += lengthOfLine;
}
return (KErrNone);
}
/********************************************************************/
// This function takes a pointer to a binary data buffer.
// We are guaranteed that the buffer is big enough to take
// two entire scan lines up to KMaxT4Des in size.
// We call GetLineL to fill the buffer up with scan lines, and
// we keep a count of the number of lines received as an integer
// at the start of the buffer. We return when either we haven't
// enough room to guarantee another line, or when we have received
// a line of zero length, which means an end of page. The descriptor
// is set to the correct length on return
// So on return, aData starts with a TInt containing the number of lines
// Each line follows, preceded with a TInt containing its length
// which must be copied as it might not be aligned on a 4-byte
// boundary - a line of zero length indicates we have reached
// the end of the page
void CFaxModemDriver::GetFaxDataL (TDes8 * aData)
{
TUint8 *startData;
TUint8 *lineData;
const TUint8 *maxData;
TInt lineLength;
TInt numberOfLines = 0;
lineData = startData = CONST_CAST (TUint8 *, (*aData).Ptr ());
maxData = startData + (*aData).MaxLength () - KMaxT4Des - sizeof (TInt);
(*aData).SetMax ();
lineData += sizeof (TInt);
for (;;)
{
numberOfLines++;
lineLength = GetLineL (lineData);
Mem::Copy (lineData, &lineLength, sizeof (TInt));
lineData += sizeof (TInt);
lineData += lineLength;
if (lineData > maxData)
break;
if (lineLength == 0)
break;
}
Mem::Copy (startData, &numberOfLines, sizeof (TInt));
(*aData).SetLength (lineData - startData);
// buffering debug message
iModem->iOurMessage.Format (_L8 ("%u lines in buffer taking up %u bytes"), numberOfLines, (*aData).Length ());
iModem->ProgressUpdateL ();
}
/********************************************************************/
// This function takes a pointer to a binary data buffer.
// We are guaranteed that the buffer is big enough to take
// an entire scan line up to KMaxT4Des in size.
// We receive the scan line, with a leave if we timeout.
// We return with the length of the scan line and the buffer has
// a space for this to be placed as an integer by the caller,
// followed by the scan line data. If the length of the scan line
// is zero, we have reached the end of the page
TInt CFaxModemDriver::GetLineL (TUint8 * aFaxData)
{
TUint8 *lineStart;
TUint8 *lineEnd;
TUint8 *currentByte;
lineStart = currentByte = (aFaxData + sizeof (TInt));
lineEnd = lineStart + KMaxT4Des;
TUint8 thisChar = 0;
TUint8 leading0s = 0;
TUint8 trailing0s = 0;
TInt nullcount = 0;
TInt ticks = CLK_TCK * KLineReadTimeout;
if (iModem->iProgress.iECM != 0)
ticks = CLK_TCK * KECMLineReadTimeout;
iModem->iProgress.iLines++;
TInt bol = 1;
TUint8 lastChar = 0xff;
// lastChar set to 0xff flags the entry to the function
// during iterations lastChar must be either 0 or 1
// we always come here just after detecting an EOL, and the character
// which contains the EOL bit is guaranteed to be re-readable
// lastChar set to 0xff indicates that's is not been read,
// so we re-read it, set its trailing0s, and put it in the buffer
// without bothering to do any dle checking (we already know it's ok)
for (;;)
{
if (lastChar == 0xff)
{
lastChar = 0;
thisChar = iModem->iReadone[0];
}
else
{
if ((iModem->RxcharWaitL (ticks)) == 0)
User::Leave (KFaxErrReceiveTimeout);
thisChar = iModem->iReadone[0];
// check if we have the character after a leading dle
// if we have unmark the last character as being a dle -
// dle etx is end of data
// dle dle is a shielded dle
// dle sub is two times dle for class 2.0 only
// dle and anything else we ignore
if (lastChar == Kdle)
{
lastChar = 0;
if (thisChar == Ketx)
{
iModem->iOurMessage.Format (_L8 ("<dle><etx> detected after %u lines"), iModem->iProgress.iLines);
iModem->ProgressUpdateL ();
return (0);
}
if (iFaxServerSessionSettings->iFaxClass == EClass2point0)
{
if (thisChar == 0x1a)
{
thisChar = Kdle;
*currentByte++ = Invert (thisChar); // invert class 2.0
if (currentByte == lineEnd)
{
return (KMaxT4Des);
}
trailing0s = 4;
}
}
if (thisChar != Kdle)
continue;
} // drop through only with a data dle
// if not a trailing dle
// check if this character is itself a leading dle
// drop through only if it isn't
else if (thisChar == Kdle)
{
lastChar = Kdle;
continue;
}
}
// if we've received six EOL codes already, ignore everything
// till dle etx arrives
if (bol == 6)
continue;
// have we a null ? if yes we ignore nulls if they come in
// anything more than pairs - if no, we zero nullcount and
// invert the byte back the right way for non-class 2 modems
// THIS LAST IS IMPORTANT
if (thisChar == Knul)
{
if (nullcount == 2)
continue;
else
nullcount++;
}
else
{
nullcount = 0;
if (iFaxServerSessionSettings->iFaxClass != EClass2)
thisChar = Invert (thisChar);
}
// count the leading zeros in this byte
leading0s = zerotable[thisChar][0];
// if the leading zeros in this byte and the trailing zeros in the
// previous byte total 11 or more we have ourselves an EOL
// so we write the data we have so far as an entire line
// we are guaranteed than an eol will span at least two bytes
// so the data we have must include the end of the last line
// if this is a nul we don't write anything yet as we haven't
// detected a proper eol code
// we don't write anything for consecutibe eols
if (((trailing0s + leading0s) > 10) && (thisChar != Knul))
{
bol++;
if ((bol == 1) && (currentByte != lineStart))
{
return (currentByte - lineStart);
}
if (iModem->iProgress.iECM == 0)
ticks = CLK_TCK * KSubsequentLineReadTimeout;
else
ticks = CLK_TCK * KSubsequentECMLineReadTimeout; // 11/1/01 AMC: ECM requires longer time-outs due to retries
}
// else if we had received an eol and this character is not nul
// we have ourselves a new line start
else
{
if (bol)
if (thisChar != Knul)
bol = 0;
}
// if we have a nul, add 8 to our trailing zero bits
// else count them by hand
if (thisChar == Knul)
trailing0s += 8;
else
trailing0s = zerotable[thisChar][1];
// ignore multiple eols
if (bol > 1)
continue;
// save everything else - we've already inverted the data if needed
*currentByte++ = thisChar;
if (currentByte == lineEnd)
{
return (KMaxT4Des);
}
}
}
/********************************************************************/
TInt CFaxModemDriver::TxStartPageL ()
{
iModem->iProgress.iPhase = EDataTransfer;
iModem->iProgress.iLines = 0;
iModem->iOurMessage.Format (_L8 ("About to send page %u"), ++iModem->iProgress.iPage);
iModem->ProgressUpdateL ();
iModem->Xonon ();
// for class 1 modems we start each page with a short burst of binary 1s
if (iFaxServerSessionSettings->iFaxClass == EClass1)
{
for (TInt x = (iActualFaxSpeed * 20 / 8); x; x--)
{
iModem->iTransmitBuffer.Append (0xff);
iModem->SendTransmitBufferL ();
}
iModem->CommitTransmitBufferL ();
}
// we're now in phase C so we start the page by sending the fax header
SendFaxHeaderL ();
return (KErrNone);
}
/********************************************************************/
TInt CFaxModemDriver::RxStartPageL ()
{
TUint8 thisChar, leading0s, trailing0s = 0;
TInt ticks = CLK_TCK * KLineReadTimeout;
if (iModem->iProgress.iECM != 0)
ticks = CLK_TCK * KECMLineReadTimeout; // ECM mode requires longer time-outs due to retries
iModem->iProgress.iPhase = EDataTransfer;
iModem->iProgress.iLines = 0;
iModem->iOurMessage.Format (_L8 ("Awaiting page %u"), ++iModem->iProgress.iPage);
iModem->ProgressUpdateL ();
trailing0s = 0;
// this function looks for the start of the received fax
// this is the first EOL code - we invert bytes for non-class 2 modems
for (;;)
{
if ((iModem->RxcharWaitL (ticks)) == 0)
return (KFaxErrReceiveTimeout);
thisChar = iModem->iReadone[0];
if (iFaxServerSessionSettings->iFaxClass != EClass2)
thisChar = Invert (thisChar);
leading0s = zerotable[thisChar][0];
if (((trailing0s + leading0s) > 10) && (thisChar != 0))
break;
if (thisChar == Knul)
trailing0s += 8;
else
trailing0s = zerotable[thisChar][1];
}
// we've found the first EOL - it's left in iModem->iReadone[0]
iModem->iOurMessage.Format (_L8 ("Receiving data .... "));
iModem->ProgressUpdateL ();
return (KErrNone);
}
/********************************************************************/
TInt CFaxModemDriver::RxConnectL ()
{
return (KFaxErrWrongModemType);
}
TInt CFaxModemDriver::RxPrePageL ()
{
return (KFaxErrWrongModemType);
}
TInt CFaxModemDriver::RxPostPageL ()
{
return (KFaxErrWrongModemType);
}
TInt CFaxModemDriver::TxConnectL ()
{
return (KFaxErrWrongModemType);
}
TInt CFaxModemDriver::TxPrePageL ()
{
return (KFaxErrWrongModemType);
}
TInt CFaxModemDriver::TxPostPageL ()
{
return (KFaxErrWrongModemType);
}
/********************************************************************/