kerneltest/e32test/device/t_serial.cpp
author hgs
Mon, 11 Oct 2010 17:54:41 +0100
changeset 286 48e57fb1237e
parent 0 a41df078684a
permissions -rw-r--r--
201039_11

// Copyright (c) 1995-2009 Nokia Corporation and/or its subsidiary(-ies).
// All rights reserved.
// This component and the accompanying materials are made available
// under the terms of the License "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:
// e32test\device\t_serial.cpp
// 
//

//#define _DEBUG_DEVCOMM

#define __E32TEST_EXTENSION__
#include <e32base.h>
#include <e32base_private.h>
#include <e32test.h>
#include <e32cons.h>
#include <e32svr.h>
#include <e32hal.h>
#include <d32comm.h>
#include <e32uid.h>
#include <hal.h>
#include "d_lddturnaroundtimertest.h"
#include <u32hal.h>

//#define DRIVER_TRACE_ON // disables or adjusts timeout for tests affected by LDD trace

// Enable aggressive paging policy if required
#if 0
#define WDP_ENABLED // affects some tests
#define FLUSH_WDP_CACHE UserSvr::HalFunction(EHalGroupVM,EVMHalFlushCache,0,0)
#else
#define FLUSH_WDP_CACHE
#endif

#if defined (__WINS__)
#define PDD_NAME _L("ECDRV.PDD")
#define LDD_NAME _L("ECOMM.LDD")
#else
#define PDD_NAME _L("EUART")
#define LDD_NAME _L("ECOMM")
#endif

const char KSpinner[]={'|','/','-','\\',};

#include "../power/async.h"

#define CHECK(r,v)	{if ((r)!=(v)) {test.Printf(_L("Line %d Expected %d Got %d\n"),__LINE__,(v),(r)); test(0);}}

// constant expressions for elements in an array, and 1st address past the end
#define ELEMENTS(A) (sizeof(A)/sizeof(A[0]))
#define LIMIT(A) (A + ELEMENTS(A))

// Our own comms object with synchronous writes
class RComm : public RBusDevComm
	{
public:
	TInt WriteS(const TDesC8& aDes);
	TInt WriteS(const TDesC8& aDes,TInt aLength);
	// Override the read functions to flush the paging cache
	inline void Read(TRequestStatus &aStatus,TDes8 &aDes)
		{
		FLUSH_WDP_CACHE;
		RBusDevComm::Read(aStatus, aDes);
		}
	inline void Read(TRequestStatus &aStatus,TDes8 &aDes,TInt aLength)
		{
		FLUSH_WDP_CACHE;
		RBusDevComm::Read(aStatus, aDes, aLength);
		}
	inline void ReadOneOrMore(TRequestStatus &aStatus,TDes8 &aDes)
		{
		FLUSH_WDP_CACHE;
		RBusDevComm::ReadOneOrMore(aStatus, aDes);
		}
	};

LOCAL_D RTest test(_L("T_SERIAL"));
RComm* theSerialPorts[2];
TCommCaps2 theCaps1Buf;
TCommCapsV02& theCaps1=theCaps1Buf();
TCommCaps2 theCaps2Buf;
TCommCapsV02& theCaps2=theCaps2Buf();
TInt PortA;
TInt PortB;

const TInt KWriteSize=250;
const TInt KXonNumReads=0x10;
const TInt KXonReadSize=0x400;

class TSpeedAndName
	{
public:
	TUint iMask;
	TBps iSpeed;
	const TText* iName;
	};

class TSpeedAndNameV2
	{
public:
	TUint iMask;
	TBps iSpeed;
	const TText* iName;
	TUint iBps;
	};

const TSpeedAndName KSpeeds[]=
	{
//	{KCapsBps50,EBps50,_S("50")},
//	{KCapsBps75,EBps75,_S("75")},
//	{KCapsBps110,EBps110,_S("110")},
//	{KCapsBps134,EBps134,_S("134")},
//	{KCapsBps150,EBps150,_S("150")},
//	{KCapsBps300,EBps300,_S("300")},
//	{KCapsBps600,EBps600,_S("600")},
//	{KCapsBps1200,EBps1200,_S("1200")},
//	{KCapsBps1800,EBps1800,_S("1800")},
//	{KCapsBps2000,EBps2000,_S("2000")},
//	{KCapsBps2400,EBps2400,_S("2400")},
//	{KCapsBps3600,EBps3600,_S("3600")},
//	{KCapsBps4800,EBps4800,_S("4800")},
//	{KCapsBps7200,EBps7200,_S("7200")},
	{KCapsBps9600,EBps9600,_S("9600")},
	{KCapsBps19200,EBps19200,_S("19200")},
//	{KCapsBps38400,EBps38400,_S("38400")},
	{KCapsBps57600,EBps57600,_S("57600")},
	{KCapsBps115200,EBps115200,_S("115200")},
	};

// These speeds are used to test break handling
const TSpeedAndNameV2 KBreakSpeeds[]=
	{
//	{KCapsBps50,EBps50,_S("50"),50},
//	{KCapsBps75,EBps75,_S("75"),75},
//	{KCapsBps110,EBps110,_S("110"),110},
//	{KCapsBps134,EBps134,_S("134"),134},
//	{KCapsBps150,EBps150,_S("150"),150},
	{KCapsBps300,EBps300,_S("300"),300},
//	{KCapsBps600,EBps600,_S("600"),600},
	{KCapsBps1200,EBps1200,_S("1200"),1200},
//	{KCapsBps1800,EBps1800,_S("1800"),1800},
//	{KCapsBps2000,EBps2000,_S("2000"),2000},
//	{KCapsBps2400,EBps2400,_S("2400"),2400},
//	{KCapsBps3600,EBps3600,_S("3600"),3600},
	{KCapsBps4800,EBps4800,_S("4800"),4800},
//	{KCapsBps7200,EBps7200,_S("7200"),7200},
//	{KCapsBps9600,EBps9600,_S("9600"),9600},
//	{KCapsBps19200,EBps19200,_S("19200"),19200},
//	{KCapsBps38400,EBps38400,_S("38400"),38400},
	{KCapsBps57600,EBps57600,_S("57600"),57600},
	{KCapsBps115200,EBps115200,_S("115200"),115200},
	};

// Multiplying factors to give Min turnaround times in microseconds between Rx and Tx
#if defined (__WINS__)
const TUint KTurnaroundTimes[] =
{
	150,
	120,
	90,
	60
};
#else
const TUint KTurnaroundTimes[] =
{
#ifdef DRIVER_TRACE_ON
	150,
	120,
	90,
	60
#else
	15,
	12,
	9,
	6
#endif
};
#endif

class TFrameAndName
	{
public:
	TDataBits iData;
	TStopBits iStop;
	TParity iParity;
	const TText* iName;
	};

const TFrameAndName KFrameTypes[]=
	{
	{EData8,EStop1,EParityNone,_S("8,N,1")},
	{EData8,EStop1,EParityEven,_S("8,E,1")},
	{EData8,EStop1,EParityOdd,_S("8,O,1")},

	{EData8,EStop2,EParityNone,_S("8,N,2")},
	{EData8,EStop2,EParityEven,_S("8,E,2")},
	{EData8,EStop2,EParityOdd,_S("8,O,2")},

	{EData7,EStop2,EParityNone,_S("7,N,2")},
	{EData7,EStop2,EParityEven,_S("7,E,2")},
	{EData7,EStop2,EParityOdd,_S("7,O,2")},

	{EData7,EStop1,EParityNone,_S("7,N,1")},
	{EData7,EStop1,EParityEven,_S("7,E,1")},
	{EData7,EStop1,EParityOdd,_S("7,O,1")},

//	{EData6,EStop2,EParityNone,_S("6,N,2")},
//	{EData6,EStop2,EParityEven,_S("6,E,2")},
//	{EData6,EStop2,EParityOdd,_S("6,O,2")},

//	{EData6,EStop1,EParityNone,_S("6,N,1")},
//	{EData6,EStop1,EParityEven,_S("6,E,1")},
//	{EData6,EStop1,EParityOdd,_S("6,O,1")},

//	{EData5,EStop1,EParityNone,_S("5,N,1")},
//	{EData5,EStop1,EParityEven,_S("5,E,1")},
//	{EData5,EStop1,EParityOdd,_S("5,O,1")},
	};

class THandShakeAndName
	{
public:
	TUint iHandshake;
	const TText* iName;
	};

THandShakeAndName KHandshakes[]=
	{
//	{KConfigObeyDSR,_S("DSR/DTR")},		// most cables don't actually support this
	{KConfigObeyCTS,_S("CTS/RTS")},
// 	{KConfigObeyDCD,_S("DCD")},
	};

enum TSerialTestFault
	{
	EBadArg,
	};
_LIT(KLddFileName, "D_LDDTURNAROUNDTIMERTEST.LDD");
RLddTest1 ldd;

#ifdef _DEBUG_DEVCOMM
void CommDebug(RBusDevComm& aComm)
	{
	TCommDebugInfoPckg infopckg;
	TCommDebugInfo& info = infopckg();
	aComm.DebugInfo(infopckg);

	test.Printf(_L("  LDD State        :    TX         RX    \r\n"));
	test.Printf(_L("  Busy             : %10d %10d\r\n"), info.iTxBusy, info.iRxBusy);
	test.Printf(_L("  Held             : %10d %10d\r\n"), info.iTxHeld, info.iRxHeld);
	test.Printf(_L("  Length           : %10d %10d\r\n"), info.iTxLength, info.iRxLength);
	test.Printf(_L("  Offset           : %10d %10d\r\n"), info.iTxOffset, info.iRxOffset);
	test.Printf(_L("  Int Count        : %10d %10d\r\n"), info.iTxIntCount, info.iRxIntCount);
	test.Printf(_L("  Err Count        : %10d %10d\r\n"), info.iTxErrCount, info.iRxErrCount);
	test.Printf(_L("  Buf Count        : %10d %10d\r\n"), info.iTxBufCount, info.iRxBufCount);
	test.Printf(_L("  Fill/Drain       : %10d %10d\r\n"), info.iFillingTxBuf, info.iFillingTxBuf);
	test.Printf(_L("  XON              : %10d %10d\r\n"), info.iTxXon, info.iRxXon);
	test.Printf(_L("  XOFF             : %10d %10d\r\n"), info.iTxXoff, info.iRxXoff);
	test.Printf(_L("  Chars            : %10d %10d\r\n"), info.iTxChars, info.iRxChars);
//	test.Printf(_L("  DFC Pending      : %10d %10d\r\n"), info.iTxDfcPend, info.iTxDfcPend);
//	test.Printf(_L("  DFC Run/Count    : %10d %10d\r\n"), info.iRunningDfc, info.iDfcCount);
//	test.Printf(_L("  DFC Req/Do/Drain : %10d %10d %10d\r\n"), info.iDfcReqSeq, info.iDfcHandlerSeq, info.iDoDrainSeq);
	}
#else
void CommDebug(RBusDevComm& /*aComm*/)
	{
	test.Printf(_L("Debug Dump not available\r\n"));
	}
#endif

TInt RComm::WriteS(const TDesC8& aDes)
//
// Syncronous write
//
	{

	return(WriteS(aDes,aDes.Length()));
	}


TInt RComm::WriteS(const TDesC8& aDes,TInt aLength)
//
// Syncronous write
//
	{

	TRequestStatus s;

	// Force there to be paging events
	FLUSH_WDP_CACHE;

	//
	Write(s,aDes,aLength);
	User::WaitForRequest(s);
	return(s.Int());
	}

void Panic(TSerialTestFault const& aFault)
//
// Panic the test code
//
	{
	User::Panic(_L("Comm Test"),aFault);
	}

void StripeMem(TDes8& aBuf,TUint aStartChar,TUint anEndChar)
//
// Mark a buffer with repeating byte pattern
//
	{

	__ASSERT_ALWAYS(aStartChar<=anEndChar,Panic(EBadArg));
	if (aStartChar==anEndChar)
		{
		aBuf.Fill(aStartChar);
		return;
		}

	TUint character=aStartChar;
	for (TInt i=0;i<aBuf.Length();i++)
		{
		aBuf[i]=(TText8)character;
		if(++character>anEndChar)
			character=aStartChar;
		}
	}

#define COLUMN_HEADER _L("                 InBuf         |         outbuf\n\r")

bool CompareDescriptors(TDes8 &aLeft,TDes8 &aRight)
//
// Compare a couple of descriptors and dump them if they don't match
//
	{
	TInt lLen=aLeft.Length();
	TInt rLen=aRight.Length();
	TInt minLen=Min(lLen,rLen);

	aRight.SetLength(minLen);
	aLeft.SetLength(minLen);
	bool r = (aLeft.Compare(aRight)==0);
	if (!r)
		{
		RDebug::Print(_L("Compare failed - dumping descriptors\n\r"));
		TInt len=aLeft.Length();
		RDebug::Print(COLUMN_HEADER);
		TBuf8<0x100> buf;
		for (TInt i=0;i<=len/8;i++)
		{
			buf.Zero();
			buf.SetLength(0);
			buf.AppendFormat(_L8("%4d: "),i*8);

			for (TInt j=0;j<8;j++)
				{
				if ((i*8)+j<len)
					{
					TInt v=aLeft[(i*8)+j];
					buf.AppendFormat(_L8("%02x "),v);
					}
				else
					buf.AppendFormat(_L8("   "));
				}
			buf.AppendFormat(_L8(" | "));
			for (TInt k=0;k<8;k++)
				{
				if ((i*8)+k>=len)
					break;
				TInt v=aRight[(i*8)+k];
				buf.AppendFormat(_L8("%02x "),v);
				}
			buf.AppendFormat(_L8("\r\n"));
			RDebug::RawPrint(buf);	
			}
		}

	if (!r) {
		theSerialPorts[0]->Close();
		theSerialPorts[1]->Close();
		aRight.SetLength(rLen);
		aLeft.SetLength(lLen);
	}
	return r;
}

TInt CheckedWrite(TInt aBufSize)
//
// Write a buffer from one serial port to the other and vice versa.
//
	{
	TUint8* inBuf=new TUint8[aBufSize];
	test(inBuf!=NULL);
	TUint8* outBuf=new TUint8[aBufSize];
	test(outBuf!=NULL);
	TPtr8 outDes(outBuf,aBufSize,aBufSize);
	TPtr8 inDes(inBuf,aBufSize,aBufSize);

	RTimer tim;
	tim.CreateLocal();
	TRequestStatus readStatus;
	TRequestStatus timeStatus;

	StripeMem(outDes,'A','Z');
	inDes.FillZ();

	const TInt KReadFirstPort=0;
	const TInt KWriteFirstPort=1;

	// Check the driver rejects an attempt to read more data than the buffer allows
	theSerialPorts[KReadFirstPort]->Read(readStatus,inDes,aBufSize+1);
	test(readStatus==KErrGeneral);

	// Start reading for real
	theSerialPorts[KReadFirstPort]->Read(readStatus,inDes);
	test_Equal(KRequestPending, readStatus.Int());

	// Synchronous write
	TInt ret=theSerialPorts[KWriteFirstPort]->WriteS(outDes,aBufSize);
	test(ret==KErrNone);

	// Set a 6 second timer going
	const TUint KTimeOut=12000000;
	tim.After(timeStatus,KTimeOut);
	test(timeStatus==KRequestPending);

	// Wait for EITHER the read to complete, OR for the timer to timeout
	User::WaitForRequest(readStatus,timeStatus);
	if (timeStatus==KErrNone) // timer completed normally... oh dear, what happened to the read??
		{
		test.Printf(_L("RComm::Read() timed out!\n\r"));
		theSerialPorts[KReadFirstPort]->ReadCancel();
		test(EFalse);	// fail
		}
	else
		{
		tim.Cancel();
		if (readStatus!=KErrNone)
			test.Printf(_L("Read Failed! (%d)\n\r"),readStatus.Int());
		test(readStatus==KErrNone);
		test(ret==KErrNone);
		test.Printf(_L("\rRead %d of %d\n\r"),inDes.Length(),outDes.Length());
		test(CompareDescriptors(outDes,inDes));
		}

	test.Printf(_L("\t\t\tReverse\n"));
	theSerialPorts[KWriteFirstPort]->Read(readStatus,inDes,aBufSize);
	theSerialPorts[KReadFirstPort]->WriteS(outDes,aBufSize);
	User::WaitForRequest(readStatus);
	tim.After(timeStatus,KTimeOut);
	test(timeStatus==KRequestPending);
	User::WaitForRequest(readStatus,timeStatus);
	if (timeStatus==KErrNone)
		{
		test.Printf(_L("Timed Out!\n\r"));
		theSerialPorts[KWriteFirstPort]->ReadCancel();
		test(EFalse);	// fail
		}
	else
		{
		tim.Cancel();
		if (readStatus!=KErrNone)
				test.Printf(_L("Read Failed! (%d)\n\r"),readStatus.Int());
		test(readStatus==KErrNone);
		test(ret==KErrNone);
		test.Printf(_L("\rRead %d of %d\n\r"),inDes.Length(),outDes.Length());
		outDes.SetLength(inDes.Length());
		test(CompareDescriptors(outDes,inDes));
		}

	tim.Close();
	delete [] inBuf;
	delete [] outBuf;

	return inDes.Length();
	}

TUint CheckZeroTurnaround(TInt aBufSize, TUint aDirection)
//
// Checks that when a Turnaround of 0ms was selected the write takes place immediately
// aBufSize is selected such as it takes only slightly less than User timer granularity
// at the Baud rate selected to transmit that buffer.
// Checks that it takes less than a User side timer tick to complete a Write request
// at the selected Baud rate. Therefore proving the write is not being delayed in the driver
//
	{
	TUint8* inBuf=new TUint8[aBufSize];
	test(inBuf!=NULL);
	TUint8* outBuf=new TUint8[aBufSize];
	test(outBuf!=NULL);
	TPtr8 outDes(outBuf,aBufSize,aBufSize);
	TPtr8 inDes(inBuf,aBufSize,aBufSize);
	TInt numberRead = 0;

	TTimeIntervalMicroSeconds32 aTimeOut=0;
	UserHal::TickPeriod(aTimeOut);

	RTimer timeoutTimer;
	timeoutTimer.CreateLocal();
	TRequestStatus readStatus;
	TRequestStatus writeStatus;
	TRequestStatus timeStatus;

	StripeMem(outDes,'A','Z');
	inDes.FillZ();

	const TUint port_A = aDirection?1:0;
	const TUint port_B = 1 - port_A;

	// queue a read on port_A
	test.Printf(_L("\r\nRead %d \r\n"), port_A);
	theSerialPorts[port_A]->Read(readStatus,inDes);
	test(readStatus==KRequestPending);

	// write on port_B to complete read
	theSerialPorts[port_B]->SetMinTurnaroundTime(0);
	theSerialPorts[port_B]->Write(writeStatus,outDes,aBufSize);
	test(writeStatus==KRequestPending || writeStatus==KErrNone );

	// start the local turnaround timer
	timeoutTimer.After(timeStatus, (20*aTimeOut.Int()));		// give it a 20% margin
	test(timeStatus==KRequestPending);

	User::WaitForRequest(readStatus, timeStatus);

	if(timeStatus == KErrNone)
		{
		// if timeout first -> BAD
		test.Printf(_L("Timed out!\r\n"));
		theSerialPorts[port_A]->ReadCancel();
		test(EFalse);	// fail
		}
	else
		{
		// else read was first -> GOOD
		timeoutTimer.Cancel();

		if (readStatus!=KErrNone)
				test.Printf(_L("Read Failed! (%d)\n\r"),readStatus.Int());
		test(readStatus==KErrNone);
		test(writeStatus==KErrNone);
		test.Printf(_L("Good, read OK and took less than timeout\r\n"));
		test(CompareDescriptors(outDes,inDes));
		numberRead = inDes.Length();
		}
	timeoutTimer.Close();
	delete inBuf;
	delete outBuf;

	return numberRead;
	}


TUint TimedCheckedWrite(TInt aBufSize, TUint aTurnaround, TUint aDirection)
//
// Checks that Write requests are delayed if a Turnaround != 0 is selected.
// aTurnarund is chosen to be significantly greater than the time it takes to transmit
// a buffer of aBufSize at the Baud rate.
// Checks that for a given Turnaround time it always takes > that time to transmit
// a buffer of aBufSize.
// aDirection specifies the direction of transmission.
//
	{
	TUint8* inBuf=new TUint8[aBufSize];
	test(inBuf!=NULL);
	TUint8* outBuf=new TUint8[aBufSize];
	test(outBuf!=NULL);
	TPtr8 outDes(outBuf,aBufSize,aBufSize);
	TPtr8 inDes(inBuf,aBufSize,aBufSize);
	TInt numberRead = 0;

	TTimeIntervalMicroSeconds32 p=0;
	UserHal::TickPeriod(p);
	TInt tPeriod = p.Int();

	const TUint KTimeOut = 1500000;		// 1500 milliseconds
	RTimer timeoutTimer;
	timeoutTimer.CreateLocal();
	TRequestStatus readStatus;
	TRequestStatus writeStatus;
	TRequestStatus timeStatus;

	RTimer turnaroundTimer;
	turnaroundTimer.CreateLocal();
	TRequestStatus turnaroundTimerStatus;

	StripeMem(outDes,'A','Z');
	inDes.FillZ();

	const TUint port_A = aDirection?1:0;
	const TUint port_B = 1 - port_A;

	// set turnaround on port_A
	TInt r = theSerialPorts[port_A]->SetMinTurnaroundTime(aTurnaround+tPeriod);
	test(r== KErrNone);
	r = theSerialPorts[port_B]->SetMinTurnaroundTime(0);
	test(r== KErrNone);

	// queue a short read on port_A
	test.Printf(_L("\r\nRead %d to set turnaround %d\r\n"), port_A, aTurnaround+tPeriod);
	theSerialPorts[port_A]->Read(readStatus,inDes);
	test(readStatus==KRequestPending);

	// start the local turnaround timer
	turnaroundTimer.After(turnaroundTimerStatus, aTurnaround);
	test(turnaroundTimerStatus==KRequestPending);

	// write on port_B to complete read and start the driver's turnaround timer on A
	theSerialPorts[port_B]->Write(writeStatus,outDes,aBufSize);
	test((writeStatus==KRequestPending)||(writeStatus==KErrNone));		// may complete before coming back here as buffer size's small

	User::WaitForRequest(readStatus);
	test(readStatus==KErrNone);
	test(writeStatus==KErrNone);
	test(CompareDescriptors(outDes,inDes));
	inDes.FillZ();

	// queue a short read on port_B
	theSerialPorts[port_B]->Read(readStatus, inDes);
	test(readStatus==KRequestPending);

	// write on port_A
	theSerialPorts[port_A]->Write(writeStatus,outDes,aBufSize);
	test(writeStatus==KRequestPending);

	// wait on both the read on port_B and the local turnaround timer
	User::WaitForRequest(readStatus, turnaroundTimerStatus);

	if(turnaroundTimerStatus == KErrNone)
		{
		// if local turnaround timeout first -> GOOD
		// start big timeout and wait on either timeout or read on port_B
		timeoutTimer.After(timeStatus, KTimeOut);
		test(timeStatus==KRequestPending);

		User::WaitForRequest(readStatus, timeStatus);
		if(timeStatus == KErrNone)
			{
			// if timeout first -> BAD
			test.Printf(_L("Timed out!\r\n"));

			theSerialPorts[port_B]->ReadCancel();
			test(EFalse);	// fail
			}
		else
			{
			// else read was first -> GOOD
			timeoutTimer.Cancel();

			if (readStatus!=KErrNone)
					test.Printf(_L("Read Failed! (%d)\n\r"),readStatus.Int());
			test(readStatus==KErrNone);
			test(writeStatus==KErrNone);
			test.Printf(_L("Good, read OK, write took longer than turnaround\r\n"));
			test(CompareDescriptors(outDes,inDes));
			numberRead = inDes.Length();
			}
		}
	else if(readStatus == KErrNone)
		{
		TInt timerStatus = turnaroundTimerStatus.Int();
		// else read was first -> BAD
		turnaroundTimer.Cancel();
		test.Printf(_L("read completed before turnaround\r\n"));
		test.Printf(_L("turnaroundTImer status  %d ms!\r\n"),timerStatus);
		test(EFalse);	// fail
		}

	timeoutTimer.Close();
	turnaroundTimer.Close();
	delete inBuf;
	delete outBuf;

	return numberRead;
	}

// Checks that setting the turnaround first time before any read or write, will start the
// turnaround timer. It is make sure that first write will be delayed atleast min turnaround
// time.
void TestFirstDelayedWrite(TInt aBufSize, TUint aTurnaround, TUint aDirection)
	{
	test.Printf(_L("Loading logical device for getting kernel timer tick & count\n"));
	TInt r=User::LoadLogicalDevice(KLddFileName);
	test(r == KErrNone || r == KErrAlreadyExists);

	test.Printf(_L("Opening of logical device\n"));
	r = ldd.Open();
	test(r == KErrNone || r == KErrAlreadyExists);


	// Create input and an output buffers
	TUint8* inBuf=new TUint8[aBufSize];
	test(inBuf!=NULL);
	TUint8* outBuf=new TUint8[aBufSize];
	test(outBuf!=NULL);

	// Fill the output buffer with stuff and empty the input buffer
	TPtr8 outDes(outBuf,aBufSize,aBufSize);
	TPtr8 inDes(inBuf,aBufSize,aBufSize);
	StripeMem(outDes,'A','Z');
	inDes.FillZ();
	

	// Configure both ports to 9600bps.
    TCommConfig cBuf1;
	TCommConfigV01& c1=cBuf1();
	theSerialPorts[0]->Config(cBuf1);
	TCommConfig cBuf2;
	TCommConfigV01& c2=cBuf2();
	theSerialPorts[0]->Config(cBuf2);
	c1.iHandshake=0;
	c2.iHandshake=0;
	c2.iFifo=EFifoDisable;
	c2.iDataBits=c1.iDataBits=EData8;
	c2.iStopBits=c1.iStopBits=EStop1;
	c2.iParity=c1.iParity=EParityNone;
	c2.iRate=c1.iRate=EBps9600;
    r = theSerialPorts[0]->SetConfig(cBuf1);
    test_Equal(KErrNone, r);
	r = theSerialPorts[1]->SetConfig(cBuf2);
	test(r == KErrNone);

	// Create a timer
	RTimer timeoutTimer;
	timeoutTimer.CreateLocal();
	TRequestStatus readStatus = 0xbaadf00d;
	TRequestStatus writeStatus = 0xbaadf00d;
	//TRequestStatus timeStatus = 0xbaadf00d;

	const TUint port_A = aDirection?1:0;
	const TUint port_B = 1 - port_A;

	TUint time1 = 0, time2 = 0, time3 = 0;
	// set turnaround on port_A
	r = theSerialPorts[port_A]->SetMinTurnaroundTime(aTurnaround);
	test(r== KErrNone);

	//Capture the turnaround time
	ldd.Test_getTimerCount(time1);

	// set turnaround on port_B
	r = theSerialPorts[port_B]->SetMinTurnaroundTime(0);
	test(r== KErrNone);

	// queue a short read on port_B
	theSerialPorts[port_B]->Read(readStatus, inDes);
	/* BOGUS TEST: Zero-length reads complete immediately.
	test_Equal(KRequestPending, readStatus.Int());
	*/

	// write on port_A
	theSerialPorts[port_A]->Write(writeStatus,outDes,aBufSize);
	/* BOGUS TEST
	test(writeStatus==KRequestPending);
	*/

	/* BOGUS TEST
	The turnaround timer exists to introduce small delays between SUCCESSIVE reads & writes, 
	so as not to flummox IrDA transceivers which are slow in changing between write & read 
	modes. Setting a timer value does not have an immediate effect, it will
	apply *after* the next read/write.

	// start a local timeout with aTurnaround/3 and wait on it
	timeoutTimer.After(timeStatus, aTurnaround/3);
	test(timeStatus==KRequestPending);
	User::WaitForRequest(timeStatus);
	test(timeStatus==KErrNone);

	// check that read on port_B has not completed yet (write on port_A has been delayed in the driver)
	test_Equal(KRequestPending, readStatus.Int());
	*/

	// wait for write to complete
	User::WaitForRequest(writeStatus);
	if(writeStatus == KErrNone)
		{
		//record the time of write complete
		ldd.Test_getTimerCount(time2);
		}

	//Wait for read to complete
	User::WaitForRequest(readStatus);
	test(readStatus==KErrNone);

	//Covert turnaround time to timer ticks
	time3 = aTurnaround / 1000;
	ldd.Test_getTimerTicks(time3);

	test.Printf(_L("Time1 %d\n"), time1);
	test.Printf(_L("Time2 %d\n"), time2);
	test.Printf(_L("Time3 %d\n"), time3);
	//Write takes apporximately 250 ticks to write.
	time2 = (time2 - time1); //Includes turnaround time + write time
	time1 = time3 > time2 ? time3 - time2 : time2 - time3;
	test.Printf(_L("Time differece %d\n"), time1);
	//test(time1 == 0 || time1 == 1); <-- Apparently unreasonable on SMP hardware

	timeoutTimer.Close();
	test.Printf(_L("Closing the channel\n"));
	ldd.Close();

	test.Printf(_L("Freeing logical device\n"));
	r = User::FreeLogicalDevice(KLddFileName);;
	test(r==KErrNone);

	delete inBuf;
	delete outBuf;

	}




TUint ChangeTurnaroundTimeInDelayedWrite(TInt aBufSize, TUint aTurnaround, TUint aNewTurnaround, TUint aDirection)
//
// Checks that a delayed write will go based on the new timeout value if the Turnaround time is changed
// when a write is being delayed in the driver
// aBufSize is such that transmission of a buffer of that size at the baud rate selected << aTurnaround
// Check that a Write is being delayed by a previous Read and that changing the turnaround will adjust
// the turnaround timer based on the new value and write will happend after minturnaround time has elapsed
//
	{
	test.Printf(_L("Loading logical device for getting kernel timer tick & count\n"));
	TInt r=User::LoadLogicalDevice(KLddFileName);
	test(r == KErrNone || r == KErrAlreadyExists);

	test.Printf(_L("Opening of logical device\n"));
	r = ldd.Open();
	test(r == KErrNone || r == KErrAlreadyExists);

	TUint8* inBuf=new TUint8[aBufSize];
	test(inBuf!=NULL);
	TUint8* outBuf=new TUint8[aBufSize];
	test(outBuf!=NULL);
	TPtr8 outDes(outBuf,aBufSize,aBufSize);
	TPtr8 inDes(inBuf,aBufSize,aBufSize);
	TInt numberRead = 0;

	StripeMem(outDes,'A','Z');
	inDes.FillZ();

	RTimer timeoutTimer;
	timeoutTimer.CreateLocal();
	TRequestStatus readStatus;
	TRequestStatus writeStatus;
	TRequestStatus timeStatus;

	const TUint port_A = aDirection?1:0;
	const TUint port_B = 1 - port_A;

	// set turnaround on port_A
	r = theSerialPorts[port_A]->SetMinTurnaroundTime(aTurnaround);
	test(r== KErrNone);
	// set turnaround on port_B
	r = theSerialPorts[port_B]->SetMinTurnaroundTime(0);
	test(r== KErrNone);

	// Issue a zero length read on port_A
	theSerialPorts[port_A]->Read(readStatus,inDes,0);
	User::WaitForRequest(readStatus);
	test(readStatus==KErrNone);
	//Record the start of turnaround time on port_A
	TUint time1 = 0, time2 = 0, time3 = 0;
	ldd.Test_getTimerCount(time1);

	// queue a short read on port_B
	theSerialPorts[port_B]->Read(readStatus, inDes);
	test(readStatus==KRequestPending);

	// write on port_A
	theSerialPorts[port_A]->Write(writeStatus,outDes,aBufSize);
	test(writeStatus==KRequestPending);

	// start a local timeout with aTurnaround/3 and wait on it
	timeoutTimer.After(timeStatus, aTurnaround/3);
	test(timeStatus==KRequestPending);
	User::WaitForRequest(timeStatus);
	test(timeStatus==KErrNone);

	// check that read on port_B has not completed yet (write on port_A has been delayed in the driver)
#ifndef WDP_ENABLED // lots of paging screws up timing assumptions
	test(readStatus==KRequestPending);
	test(writeStatus==KRequestPending);
#endif

	// change turnaround on port_A (should adjust turnaround time accordingly)
	r = theSerialPorts[port_A]->SetMinTurnaroundTime(aNewTurnaround);
	test(r==KErrNone);

	//Check read on port_B & write on port_A is still delayed.
#if !defined(DRIVER_TRACE_ON) && !defined(WDP_ENABLED)
	test(readStatus==KRequestPending);
	test(writeStatus==KRequestPending);
#endif
	// wait for write to complete
	User::WaitForRequest(writeStatus);
	if(writeStatus == KErrNone)
	{
		//record the time of write complete
		ldd.Test_getTimerCount(time2);
	}

	//Wait for read to complete
	User::WaitForRequest(readStatus);
	test(readStatus==KErrNone);

	//Calculate the turnaround time, write should be delayed.
	time3 = aNewTurnaround/1000;
	//Convert to timer ticks
	ldd.Test_getTimerTicks(time3);
	test.Printf(_L("aTurnaround = %d, aNewTurnaround = %d\n"), aTurnaround, aNewTurnaround);
	test.Printf(_L("Time1 = %d\n"), time1);
	test.Printf(_L("Time2 = %d\n"), time2);
	test.Printf(_L("Time3 = %d\n"), time3);
	time1 = time2 - time1;
	time1 = time3 > time1 ? (time3 - time1) : (time1 - time3);
	test.Printf(_L("Time difference %d\n"), time1);
#if !defined(DRIVER_TRACE_ON) && !defined(WDP_ENABLED)
//	test((time1 == 1) || (time1 == 0));
#endif
	test.Printf(_L("Write delayed for requested time\r\n"));
	test(CompareDescriptors(outDes,inDes));
	numberRead = inDes.Length();

	timeoutTimer.Close();
	test.Printf(_L("Closing the channel\n"));
	ldd.Close();

	test.Printf(_L("Freeing logical device\n"));
	r = User::FreeLogicalDevice(KLddFileName);;
	test(r==KErrNone);

	delete inBuf;
	delete outBuf;

	return numberRead;
	}


TUint StopInDelayedWrite(TInt aBufSize, TUint aTurnaround, TUint aDirection)
//
// Checks that when a write is being delayed and then is cancelled the driver's turnaround timer continues
// ticking and if another write is queued it will be delayed by the remaining time
// aBufSize is such that transmission of a buffer of that size at the baud rate selected << aTurnaround
// Check that a Write is being delayed by a previous Read and that changing the Turnaround will make it
// go ahead immediately
//
	{
	TUint8* inBuf=new TUint8[aBufSize];
	test(inBuf!=NULL);
	TUint8* outBuf=new TUint8[aBufSize];
	test(outBuf!=NULL);
	TPtr8 outDes(outBuf,aBufSize,aBufSize);
	TPtr8 inDes(inBuf,aBufSize,aBufSize);
	TInt numberRead = 0;

	TTimeIntervalMicroSeconds32 p=0;
	UserHal::TickPeriod(p);
	TInt tPeriod = p.Int();

	const TUint KTimeOut = 1500000;		// 150 milliseconds
	RTimer timeoutTimer;
	timeoutTimer.CreateLocal();
	TRequestStatus readStatus;
	TRequestStatus writeStatus;
	TRequestStatus timeStatus;

	RTimer turnaroundTimer;
	turnaroundTimer.CreateLocal();
	TRequestStatus turnaroundTimerStatus;

	StripeMem(outDes,'A','Z');
	inDes.FillZ();

	const TUint port_A = aDirection?1:0;
	const TUint port_B = 1 - port_A;

	// set turnaround on port_A
	TInt r = theSerialPorts[port_A]->SetMinTurnaroundTime(aTurnaround+tPeriod);
	test(r== KErrNone);
	r = theSerialPorts[port_B]->SetMinTurnaroundTime(0);
	test(r== KErrNone);


	// queue a zero length read to start the turnaround on port_A
	test.Printf(_L("\r\nRead Zero Length on %d to start turnaround %d\r\n"), port_A, aTurnaround);
	test.Printf(_L("\r\nUsing %d character buffers\r\n"),aBufSize);

	theSerialPorts[port_A]->Read(readStatus,inDes,0);
	User::WaitForRequest(readStatus);
	test(readStatus==KErrNone);

	// start the local turnaround timer
	turnaroundTimer.After(turnaroundTimerStatus, aTurnaround);
	test(turnaroundTimerStatus==KRequestPending);

	// queue a short read on port_B
	theSerialPorts[port_B]->Read(readStatus, inDes);
	test(readStatus==KRequestPending);

	// write on port_A
	theSerialPorts[port_A]->Write(writeStatus,outDes,aBufSize);
	test(writeStatus==KRequestPending);

	// start a local timeout with aTurnaround/3 and wait on it
	timeoutTimer.After(timeStatus, aTurnaround/3);
	test(timeStatus==KRequestPending);
	User::WaitForRequest(timeStatus);
	test(timeStatus==KErrNone);

	// check that read on port_B has not completed yet (write on port_A has been delayed in the driver)
	test(readStatus==KRequestPending);

	// cancel write on port_A
	theSerialPorts[port_A]->WriteCancel();
	test(writeStatus==KErrCancel);

	// ...and restart it again
	theSerialPorts[port_A]->Write(writeStatus,outDes,aBufSize);
#ifndef DRIVER_TRACE_ON
	test(writeStatus==KRequestPending);
#endif

	// wait on both the read on port_B and the local turnaround timer
	User::WaitForRequest(readStatus, turnaroundTimerStatus);

	// We are expecting this to have gone off by now...
	if(turnaroundTimerStatus == KErrNone) // this local timer is LESS than the driver turnaround
		{
		// if local turnaround timeout first -> GOOD
		// start big timeout and wait on either timeout or read on port_B
		timeoutTimer.After(timeStatus, KTimeOut);
		test(timeStatus==KRequestPending);

		User::WaitForRequest(readStatus, timeStatus);
		if(timeStatus == KErrNone)
			{
			// if timeout first -> BAD
			test.Printf(_L("Timed out!\r\n"));

			theSerialPorts[port_B]->ReadCancel();
			test(EFalse);	// fail
			}
		else
			{
			// else read was first -> GOOD
			timeoutTimer.Cancel();

			if (readStatus!=KErrNone)
					test.Printf(_L("Read Failed! (%d) - should have completed (on delayed write data)\n\r"),readStatus.Int());
			test(readStatus==KErrNone);
			test(writeStatus==KErrNone);
			test.Printf(_L("OK, write later than turnaround\r\n"));
			test(CompareDescriptors(outDes,inDes));
			numberRead = inDes.Length();
			}
		}
	// failed here => second write has gone off faster than expected...
	else if(readStatus == KErrNone)
		{
		// else read was first -> BAD
		TInt timerStatus = turnaroundTimerStatus.Int();
		turnaroundTimer.Cancel();
		test.Printf(_L("read completed before turnaround\r\n"));
		test.Printf(_L("Turnaround timer status = %d\r\n"),timerStatus);
		test(EFalse);	// fail
		}

	timeoutTimer.Close();
	turnaroundTimer.Close();
	delete inBuf;
	delete outBuf;

	return numberRead;
	}

void turnaroundTestReadWrite()
//
// Read and write at various speeds, with various turnarounds
// Check that the data received data matches sent data
	{
	// Open both serial ports
	TInt r=theSerialPorts[0]->Open(PortA);
	test(r==KErrNone);
	r=theSerialPorts[0]->QueryReceiveBuffer();
	test(r==0);
	r=theSerialPorts[1]->Open(PortB);
	test(r==KErrNone);
	r=theSerialPorts[1]->QueryReceiveBuffer();
	test(r==0);

	// Perform the read/write test at each baudrate for 8N1, no handshaking
	TCommConfig cBuf1;
	TCommConfigV01& c1=cBuf1();
	theSerialPorts[0]->Config(cBuf1);
	TCommConfig cBuf2;
	TCommConfigV01& c2=cBuf2();
	theSerialPorts[0]->Config(cBuf2);
	c1.iHandshake=0;
	c2.iHandshake=0;
	c2.iFifo=EFifoDisable;

	c2.iDataBits=c1.iDataBits=EData8;
	c2.iStopBits=c1.iStopBits=EStop1;
	c2.iParity=c1.iParity=EParityNone;
	c2.iRate=c1.iRate=EBps9600;

	TBuf<0x40> msg;

	test.Start(_L("Read/write test with default turnaround and at 9600 Bps"));

	TTimeIntervalMicroSeconds32 p=0;
	UserHal::TickPeriod(p);
	TInt tPeriod = p.Int();
	test.Printf(_L("Tick period %d\r\n"), tPeriod);

	TUint aBufLength = 96*p.Int()/10000;
	test.Printf(_L("Need to transmit %d chars at 9600 Bps\r\n"), aBufLength); // let's try with 10*tick period (approx)

	theSerialPorts[0]->SetConfig(cBuf1);
	theSerialPorts[1]->SetConfig(cBuf2);

	// These work fine
	test(CheckZeroTurnaround(aBufLength, 0)==aBufLength);
	test(CheckZeroTurnaround(aBufLength, 1)==aBufLength);

	test.Next(_L("Read/write test at various speeds and min turnarounds"));
#if defined (__WINS__)
	const TUint KShortBufferSize=100;
#else
	const TUint KShortBufferSize=10;
#endif
	TUint direction=0;
	for(TUint i = 0; i < ELEMENTS(KSpeeds); ++i)
		{
		TInt turnaround;
		turnaround = KTurnaroundTimes[i]*p.Int();

		if (theCaps1.iRate&KSpeeds[i].iMask && theCaps2.iRate&KSpeeds[i].iMask)
			{
			msg.Format(_L("\r\nRead/write @ %s Bps with %d millisec turnaround\r\n"), KSpeeds[i].iName, turnaround/1000);
			test.Next(msg);

			c1.iRate=KSpeeds[i].iSpeed;
			TInt r=theSerialPorts[0]->SetConfig(cBuf1);
			test(r==KErrNone);
			c2.iRate=KSpeeds[i].iSpeed;
			r=theSerialPorts[1]->SetConfig(cBuf2);
			test(r==KErrNone);

			test.Printf(_L("Do TimedCheckedWrite\r\n"));
			test(TimedCheckedWrite(KShortBufferSize, turnaround, direction)==KShortBufferSize);
			}
		else
			{
			msg.Format(_L("%s Bps not supported\r\n"),KSpeeds[i].iName);
			test.Next(msg);
			}

		direction=1-direction;

		msg.Format(_L("\r\nRead turnaround time back\r\n"));
			test.Next(msg);

		TInt n = theSerialPorts[0]->MinTurnaroundTime();
		test(n==turnaround+tPeriod);

		msg.Format(_L("Value returned was %d\r\n"), n/1000);
		test.Next(msg);

		test.Printf(_L("Decrease turnaroundtime during delayed write\n"));
		test(ChangeTurnaroundTimeInDelayedWrite(KShortBufferSize, turnaround, turnaround - 10000, direction)==KShortBufferSize);

		test.Printf(_L("Increase turnaroundtime during delayed write\n"));
		test(ChangeTurnaroundTimeInDelayedWrite(KShortBufferSize, turnaround, turnaround + 30000 ,direction)==KShortBufferSize);

		direction=1-direction;

		test.Printf(_L("\r\nDo StopInDelayedWrite @ %s Bps\r\n"), KSpeeds[i].iName);
		test(StopInDelayedWrite(KShortBufferSize, turnaround, direction)==KShortBufferSize);
		}

	// return defaults for following tests

	msg.Format(_L("\r\nSet default turnaround (0) on both ports \r\n"));
	test.Next(msg);

	test(theSerialPorts[0]->SetMinTurnaroundTime(0)==KErrNone);
	test(theSerialPorts[1]->SetMinTurnaroundTime(0)==KErrNone);

	theSerialPorts[0]->Close();
	theSerialPorts[1]->Close();

	msg.Format(_L("\r\n... End of turnaround tests ...\r\n"));
	test.Next(msg);

	test.End();
	}

void testReadWrite()
//
// Read and write at various speeds
	{
	test.Start(_L("Testing read and write"));

	TInt r=theSerialPorts[0]->Open(PortA);
	test(r==KErrNone);
	r=theSerialPorts[0]->QueryReceiveBuffer();
	test(r==0);
	r=theSerialPorts[1]->Open(PortB);
	test(r==KErrNone);
	r=theSerialPorts[1]->QueryReceiveBuffer();
	test(r==0);

	TCommConfig cBuf1;
	TCommConfigV01& c1=cBuf1();
	theSerialPorts[0]->Config(cBuf1);
	TCommConfig cBuf2;
	TCommConfigV01& c2=cBuf2();
	theSerialPorts[0]->Config(cBuf2);
	c1.iHandshake=0;
	c2.iHandshake=0;
	c2.iFifo=EFifoDisable;

	c2.iDataBits=c1.iDataBits=EData8;
	c2.iStopBits=c1.iStopBits=EStop1;
	c2.iParity=c1.iParity=EParityNone;
	c2.iRate=c1.iRate=EBps9600;

	TBuf<0x40> mess;
	test.Printf(_L("Delayed first write\n"));
    TestFirstDelayedWrite(0, 2343750, 1);

	TInt numTests=sizeof(KSpeeds)/sizeof(TSpeedAndName);
	for (TInt i=0;i<numTests;i++)
		{
		if (theCaps1.iRate&KSpeeds[i].iMask && theCaps2.iRate&KSpeeds[i].iMask)
			{
			mess.Format(_L("read/write @ %s Bps"),KSpeeds[i].iName);
			test.Next(mess);
			c1.iRate=KSpeeds[i].iSpeed;
			TInt r=theSerialPorts[0]->SetConfig(cBuf1);
			test(r==KErrNone);
			c2.iRate=KSpeeds[i].iSpeed;
			r=theSerialPorts[1]->SetConfig(cBuf2);
			test(r==KErrNone);
			test.Printf(_L("DoCheckedWrite\r\n"));
			test(CheckedWrite(KWriteSize)==KWriteSize);
			}
		else
			{
			mess.Format(_L("%s Bps not supported"),KSpeeds[i].iName);
			test.Next(mess);
			}
		}

	theSerialPorts[0]->Close();
	theSerialPorts[1]->Close();

	test.End();
	}

void testTiming()
//
// Read and write at various speeds
	{

	test.Start(_L("Testing read and write speed"));
	const TInt KSamples=10;
	const TInt KNumWrites=100;
	const TInt KBufSize=2000;
	test.Printf(_L("%d sets of %d by %d characters @ 19200\n\r"),KSamples,KNumWrites,KBufSize);

	TCommConfig cBuf1;
	TCommConfigV01& c1=cBuf1();
	theSerialPorts[0]->Config(cBuf1);
	TCommConfig cBuf2;
	TCommConfigV01& c2=cBuf2();
	theSerialPorts[0]->Config(cBuf2);
	c1.iHandshake=0;
	c2.iHandshake=0;
	c2.iFifo=EFifoDisable;

	c2.iDataBits=c1.iDataBits=EData8;
	c2.iStopBits=c1.iStopBits=EStop1;
	c2.iParity=c1.iParity=EParityNone;
	c2.iRate=c1.iRate=EBps19200;

	TInt r=theSerialPorts[0]->SetConfig(cBuf1);
	test(r==KErrNone);
	r=theSerialPorts[1]->SetConfig(cBuf2);
	test(r==KErrNone);

	TUint samples[KSamples];

	for (TInt i=0;i<KSamples;i++)
		{
		test.Printf(_L("."));

		TUint8* inBuf=new TUint8[KBufSize];
		TUint8* outBuf=new TUint8[KBufSize];
		TPtr8 outDes(outBuf,KBufSize,KBufSize);
		TPtr8 inDes(inBuf,KBufSize,KBufSize);


		RTimer tim;
		tim.CreateLocal();
		TRequestStatus readStatus;
		TRequestStatus timeStatus;

		StripeMem(outDes,'A','Z');
		inDes.FillZ();

		TTime startTime;
		startTime.HomeTime();
		for (TInt l=0;l<KNumWrites;l++)
			{
			inDes.SetLength(KBufSize/3);
			theSerialPorts[0]->Read(readStatus,inDes);

			TInt ret=theSerialPorts[1]->WriteS(outDes,KBufSize);
			const TUint KTimeOut=6000000;
			tim.After(timeStatus,KTimeOut);

			User::WaitForRequest(readStatus,timeStatus);


			if (timeStatus==KErrNone)
				{
				test.Printf(_L("Timed Out!\n\r"));
				theSerialPorts[0]->ReadCancel();
				}
			else
				{
				tim.Cancel();
				if (readStatus!=KErrNone)
						test.Printf(_L("Read Failed! (%d)\n\r"),readStatus.Int());
				test(readStatus==KErrNone);
				test(ret==KErrNone);
				test(inDes.Length()==inDes.MaxLength());
				test(inDes.Length()==KBufSize);
				test(CompareDescriptors(outDes,inDes));
				}

			}
		TTime endTime;
		endTime.HomeTime();

		TInt64 delta=endTime.MicroSecondsFrom(startTime).Int64();
		delta=delta/1000;
		TInt delta32=I64INT(delta);
		samples[i]=delta32;
		test.Printf(_L("Read/Write %d time = %d ms\n\r"),KNumWrites*KBufSize,delta32);
		}

	TInt avg=0;
	for (TInt j=0;j<KSamples;j++)
		{
		avg=avg+samples[j];
		}
	avg/=KSamples;
	test.Printf(_L("      Average time = %d ms\n\r"),avg);
	test.Printf(_L("Press a key\n\r"));
	test.Getch();

	test.End();
	}

void testBreak()
///
/// Tests serial breaks
///
 	{
	TBuf<256> msg;
	test.Next(_L("Testing breaks"));

	TCommConfig cBuf0;
	TCommConfigV01& c0=cBuf0();
	TCommConfig cBuf1;
	TCommConfigV01& c1=cBuf1();

	TRequestStatus breakStatus;
	TRequestStatus readStatus;
	TRequestStatus writeStatus;
	TRequestStatus timerStatus;

	TInt r=theSerialPorts[0]->Open(PortA);
	test(r==KErrNone);
	r=theSerialPorts[0]->QueryReceiveBuffer();
	test(r==0);
	r=theSerialPorts[1]->Open(PortB);
	test(r==KErrNone);
	r=theSerialPorts[1]->QueryReceiveBuffer();
	test(r==0);

	theSerialPorts[0]->Config(cBuf0);
	theSerialPorts[1]->Config(cBuf1);
	c0.iRate=c1.iRate=EBps110;
	c0.iParityError=c1.iParityError=0;
	c0.iHandshake=c1.iHandshake=0;

	c0.iDataBits=c1.iDataBits=EData8;
	c0.iStopBits=c1.iStopBits=EStop1;
	c0.iParity=c1.iParity=EParityNone;


	r=theSerialPorts[0]->SetConfig(cBuf0);
	test(r==KErrNone);
	r=theSerialPorts[1]->SetConfig(cBuf1);
	test(r==KErrNone);

for(TUint i = 0; i < ELEMENTS(KBreakSpeeds) ; ++i)
		{
		if (theCaps1.iRate&KBreakSpeeds[i].iMask && theCaps2.iRate&KBreakSpeeds[i].iMask)
			{
			msg.Format(_L("Break tests @ %s Bps"), KBreakSpeeds[i].iName);
			test.Start(msg);

			c0.iRate=KBreakSpeeds[i].iSpeed;
			TInt r=theSerialPorts[0]->SetConfig(cBuf0);
			test(r==KErrNone);
			c1.iRate=KBreakSpeeds[i].iSpeed;
			r=theSerialPorts[1]->SetConfig(cBuf1);
			test(r==KErrNone);


			// should take more than 1.5s

			HBufC8* bigReadBuffer=HBufC8::NewL(KWriteSize);
			HBufC8* bigWriteBuffer=HBufC8::NewMaxL(KWriteSize);
			TPtr8 bigReadBufferPtr(bigReadBuffer->Des());
			TPtr8 bigWriteBufferPtr(bigWriteBuffer->Des());

			StripeMem(bigWriteBufferPtr, 'A', 'Z');
			bigReadBufferPtr.FillZ();


			const TUint KWriteSize=1024 + KBreakSpeeds[i].iBps/4;
			const TInt KTimerTime=1500000;
			const TInt KBreakTime=3000000;
			const TInt KMinTurnaroundTime=150000;

			RTimer timer;
			test(timer.CreateLocal()==KErrNone);


// Test 1
			test.Start(_L("Break after write"));
		//- start a user timer which will expire just after the TX would complete with no break
			timer.After(timerStatus, KTimerTime);

		//- request TX (and RX) and request a break
			theSerialPorts[0]->Write(writeStatus, *bigWriteBuffer, KWriteSize);
			theSerialPorts[0]->Break(breakStatus, KBreakTime);
			theSerialPorts[1]->Read(readStatus, bigReadBufferPtr, KWriteSize);

		// Make sure the timer completes first
			User::WaitForRequest(writeStatus, readStatus);
			User::WaitForRequest(breakStatus);

			test(readStatus!=KErrNone && readStatus!=KRequestPending);
			test(breakStatus==KErrNone);
			test(writeStatus==KErrNone || writeStatus==KRequestPending);	// Can be still pending, since if the read is completed with an error then the write won't complete since the buffers may fill up
			test(timerStatus==KErrNone);

			if (writeStatus==KRequestPending)
				theSerialPorts[0]->WriteCancel();

// Test 2
			test.Next(_L("Write after break"));
		//- start a user timer which will expire just after the TX would complete with no break
			timer.After(timerStatus, KTimerTime);

		//- request TX (and RX) and request a break
			theSerialPorts[1]->Read(readStatus, bigReadBufferPtr, KWriteSize);
			theSerialPorts[0]->Break(breakStatus, KBreakTime);
			theSerialPorts[0]->Write(writeStatus, *bigWriteBuffer, KWriteSize);

		// Make sure the timer completes first
			User::WaitForRequest(breakStatus);
			User::WaitForRequest(writeStatus, readStatus);

			test(readStatus!=KErrNone && readStatus!=KRequestPending);
			test(breakStatus==KErrNone);
			test(writeStatus==KErrNone || writeStatus==KRequestPending);	// write may not be able to cmoplete due to no remaining pending read
			test(timerStatus==KErrNone);

			if (writeStatus==KRequestPending)
				theSerialPorts[0]->WriteCancel();

// Test 3
			test.Next(_L("Cancellation of break"));
		//- Check cancellation of breaks

		//- request TX (and RX) and request a break
			theSerialPorts[0]->Break(breakStatus, KBreakTime);
			theSerialPorts[1]->Read(readStatus, bigReadBufferPtr, KWriteSize);
			theSerialPorts[0]->Write(writeStatus, *bigWriteBuffer, KWriteSize);

		//- cancel break
			theSerialPorts[0]->BreakCancel();

			User::WaitForRequest(breakStatus);
			test(breakStatus.Int()==KErrCancel);

			User::WaitForRequest(readStatus);

			if (writeStatus==KRequestPending)
				theSerialPorts[0]->WriteCancel();

// Test 4

			test.Next(_L("Break during turnaround"));
		//- Check break still works during turnaround
			test (KErrNone==theSerialPorts[0]->SetMinTurnaroundTime(KMinTurnaroundTime));

			theSerialPorts[0]->Read(readStatus, bigReadBufferPtr, 1);
			theSerialPorts[1]->Write(writeStatus, *bigWriteBuffer, 1);
			User::WaitForRequest(readStatus);
			User::WaitForRequest(writeStatus);

		//- start a user timer which will expire just after the TX would complete with no break
			timer.After(timerStatus, KTimerTime);

		//- request TX (and RX) and request a break
			theSerialPorts[0]->Break(breakStatus, KBreakTime);
			theSerialPorts[1]->Read(readStatus, bigReadBufferPtr, KWriteSize);
			theSerialPorts[0]->Write(writeStatus, *bigWriteBuffer, KWriteSize);

		// Make sure the timer completes first
			User::WaitForRequest(writeStatus, readStatus);
			User::WaitForRequest(breakStatus);

			test(readStatus!=KErrNone && readStatus!=KRequestPending);
			test(breakStatus==KErrNone);
			test(writeStatus==KErrNone || writeStatus==KRequestPending);
			test(timerStatus==KErrNone);

			if (writeStatus==KRequestPending)
				theSerialPorts[0]->WriteCancel();

			test (KErrNone==theSerialPorts[0]->SetMinTurnaroundTime(0));

// End tests
			timer.Close();
			test.End();
			test.End();
			}
		else
			{
			msg.Format(_L("%s Bps not supported"),KBreakSpeeds[i].iName);
			test.Next(msg);
			}
		}	// end rate loop

	theSerialPorts[0]->Close();
	theSerialPorts[1]->Close();
	}



void testFraming()
//
// Test framing
//
	{
	test.Start(_L("Testing framing"));

	TInt r=theSerialPorts[0]->Open(PortA);
	test(r==KErrNone);
	r=theSerialPorts[0]->QueryReceiveBuffer();
	test(r==0);
	r=theSerialPorts[1]->Open(PortB);
	test(r==KErrNone);
	r=theSerialPorts[1]->QueryReceiveBuffer();
	test(r==0);

	TCommConfig cBuf0;
	TCommConfigV01& c0=cBuf0();
	TCommConfig cBuf1;
	TCommConfigV01& c1=cBuf1();
	TBuf<0x40> mess;

	theSerialPorts[0]->Config(cBuf0);
	c0.iRate=EBps9600;
	c0.iHandshake=0;
	theSerialPorts[1]->Config(cBuf1);
	c1.iRate=EBps9600;
	c1.iHandshake=0;

	TInt numTests=sizeof(KFrameTypes)/sizeof(TFrameAndName);
	for (TInt i=0;i<numTests;i++)
		{
		c0.iDataBits=KFrameTypes[i].iData;
		c0.iStopBits=KFrameTypes[i].iStop;
		c0.iParity=KFrameTypes[i].iParity;
		TInt r=theSerialPorts[0]->SetConfig(cBuf0);
		if (r==KErrNone)
			{

			c1.iDataBits=KFrameTypes[i].iData;
			c1.iStopBits=KFrameTypes[i].iStop;
			c1.iParity=KFrameTypes[i].iParity;
			r=theSerialPorts[1]->SetConfig(cBuf1);
			if(r==KErrNone)
				{
				mess.Format(_L("read/write using %s "),KFrameTypes[i].iName);
				test.Next(mess);
				test(CheckedWrite(KWriteSize)==KWriteSize);
				}
			}

		if (r!=KErrNone)
			test.Printf(_L("%s not supported\n\r"),KFrameTypes[i].iName);
		}

	theSerialPorts[0]->Close();
	theSerialPorts[1]->Close();
	test.End();
	}
//
void testTerminators()
//
// Test termination masks - assumes that Checked write stripes memory starting with 'A'
//
	{

	test.Next(_L("Testing termination masks"));

	TCommConfig cBuf;
	TCommConfigV01& c=cBuf();

	theSerialPorts[0]->Close();
	theSerialPorts[1]->Close();

	TInt r=theSerialPorts[0]->Open(PortA);
	test(r==KErrNone);
	r=theSerialPorts[0]->QueryReceiveBuffer();
	test(r==0);
	r=theSerialPorts[1]->Open(PortB);
	test(r==KErrNone);
	r=theSerialPorts[1]->QueryReceiveBuffer();
	test(r==0);
	theSerialPorts[0]->Config(cBuf);
	c.iTerminator[0]='C';
	c.iTerminatorCount=1;
	c.iHandshake=0;

	c.iDataBits=EData8;
	c.iStopBits=EStop1;
	c.iParity=EParityNone;

	r=theSerialPorts[0]->SetConfig(cBuf);
	test(r==KErrNone);

	TCommConfig cBuf1;
	TCommConfigV01& c1=cBuf1();
	theSerialPorts[1]->Config(cBuf1);

	c1.iDataBits=EData8;
	c1.iStopBits=EStop1;
	c1.iParity=EParityNone;

	c1.iTerminator[0]='C';
	c1.iTerminatorCount=1;
	c1.iHandshake=0;

	r=theSerialPorts[1]->SetConfig(cBuf1);
	test(r==KErrNone);

	User::After(100000);
	theSerialPorts[0]->ResetBuffers();
	theSerialPorts[1]->ResetBuffers();

	test(CheckedWrite(KWriteSize)==3);

	// Clear the ldd buffers
	theSerialPorts[0]->ResetBuffers();
	theSerialPorts[1]->ResetBuffers();

	c.iTerminator[0]='Z';
	c.iTerminator[1]='X';
	c.iTerminator[2]='Y';
	c.iTerminator[3]='D';

	c1.iTerminator[0]='Z';
	c1.iTerminator[1]='X';
	c1.iTerminator[2]='Y';
	c1.iTerminator[3]='D';

/* 	Not yet - we have too much buffering in the driver & device.
	Unfortunately the ResetBuffers() above doesn't (and really can't) go
	deep enough. Under WINS NT buffers up some data and the following read
	(inside checked write) completes before the WriteS (and infact, after
	reading a semi random number of characters)

    c.iTerminatorCount=4;
    c1.iTerminatorCount=4;
	r=theSerialPorts[0]->SetConfig(cBuf);
	test(r==KErrNone);
	r=theSerialPorts[1]->SetConfig(cBuf1);
	test(r==KErrNone);

	test(CheckedWrite(KWriteSize)==4);
	theSerialPorts[0]->Config(cBuf);
*/
	// Reset termination mask.
	c.iTerminatorCount=0;
	c1.iTerminatorCount=0;
	r=theSerialPorts[0]->SetConfig(cBuf);
	test(r==KErrNone);
	r=theSerialPorts[1]->SetConfig(cBuf1);
	test(r==KErrNone);

	theSerialPorts[0]->Close();
	theSerialPorts[1]->Close();
	}

void testXonXoff()
//
// tests XonXoff
//
	{
	test.Next(_L("Testing xon xoff"));
	test.Start(_L("Setup"));

	TInt r=theSerialPorts[0]->Open(PortA);
	test(r==KErrNone);
	r=theSerialPorts[0]->QueryReceiveBuffer();
	test(r==0);
	r=theSerialPorts[1]->Open(PortB);
	test(r==KErrNone);
	r=theSerialPorts[1]->QueryReceiveBuffer();
	test(r==0);

	TCommConfig cBuf;
	TCommConfigV01& c=cBuf();

	theSerialPorts[0]->Config(cBuf);
	c.iHandshake=KConfigObeyXoff|KConfigSendXoff;
	c.iStopBits=EStop1;
	c.iParity=EParityNone;
	c.iDataBits=EData8;
	c.iRate=EBps19200;
	c.iXonChar=0x11;
	c.iXoffChar=0x13;
	c.iParityError=KConfigXonXoffDebug;
	c.iTerminatorCount=0;
	test(theSerialPorts[0]->SetConfig(cBuf)==KErrNone);

	theSerialPorts[1]->Config(cBuf);
	c.iHandshake=KConfigObeyXoff|KConfigSendXoff;
	c.iStopBits=EStop1;
	c.iParity=EParityNone;
	c.iDataBits=EData8;
	c.iRate=EBps19200;
	c.iXonChar=0x11;
	c.iXoffChar=0x13;
	c.iParityError=KConfigXonXoffDebug;
	c.iTerminatorCount=0;
	test(theSerialPorts[1]->SetConfig(cBuf)==KErrNone);

	theSerialPorts[0]->SetReceiveBufferLength(0x400);
	theSerialPorts[1]->SetReceiveBufferLength(0x400);

	const TInt KXonWriteSize=KXonNumReads*KXonReadSize;

	TUint8* inBuf=new TUint8[KXonReadSize];
	TUint8* outBuf=new TUint8[KXonWriteSize];
	TPtr8 outDes(outBuf,KXonWriteSize,KXonWriteSize);
	TPtr8 inDes(inBuf,KXonReadSize,KXonReadSize);

	TRequestStatus readStatus;
	TRequestStatus writeStatus;
	TRequestStatus timeStatus;
	RTimer timer;
	timer.CreateLocal();
	TInt writePos=0;

	StripeMem(outDes,'A','Z');
	inDes.FillZ();

	test.Next(_L("Write bytes to com1"));
	test.Printf(_L("Reading after delay (1 of %d) avail = %d\n\r"),KXonNumReads, theSerialPorts[0]->QueryReceiveBuffer());
	theSerialPorts[0]->Read(readStatus,inDes,KXonReadSize);
	theSerialPorts[1]->Write(writeStatus,outDes,KXonWriteSize);
	timer.After(timeStatus,1000000);
	User::WaitForRequest(readStatus,timeStatus);
	test(readStatus==KErrNone);
	test(timeStatus==KRequestPending);
	TPtrC8 aOutDes = outDes.Mid(writePos,KXonReadSize);
	test(CompareDescriptors(inDes,(TDes8&)aOutDes));

	writePos+=KXonReadSize;

	if (timeStatus==KRequestPending)
		User::WaitForRequest(timeStatus);

	TInt i;
	for (i=0;i<KXonNumReads-1;++i)
		{
		inDes.FillZ();
		timer.After(timeStatus,450000);
		User::WaitForRequest(timeStatus);
		test(timeStatus==KErrNone);

		test.Printf(_L("Reading after delay (%d of %d) avail = %d\n\r"),i+2,KXonNumReads, theSerialPorts[0]->QueryReceiveBuffer());
		theSerialPorts[0]->Read(readStatus,inDes,KXonReadSize);
		timer.After(timeStatus,1000000);
		User::WaitForRequest(readStatus,timeStatus);
		if (readStatus!=KErrNone)
			test.Printf(_L("Read error %d\n\r"),readStatus.Int());
		test(readStatus==KErrNone);
		test(timeStatus==KRequestPending);
		TPtrC8 aOutDes = outDes.Mid(writePos,KXonReadSize);
		test(CompareDescriptors(inDes,(TDes8&)aOutDes));
		timer.Cancel();
		writePos+=KXonReadSize;
		}

	test.Next(_L("2nd Large Write complete"));
	test(writeStatus==KErrNone);

	delete [] inBuf;
	delete [] outBuf;

	theSerialPorts[0]->Close();
	theSerialPorts[1]->Close();

	test.End();
	}

//
void testHWHandshaking()
//
// test hardware hand shaking
//
	{

#if defined (__WINS__)
const TInt KHWReadSize=0x2000;
#else
const TInt KHWReadSize=0x400;
#endif

	test.Start(_L("Testing hardware handshaking"));

	TInt r=theSerialPorts[0]->Open(PortA);
	test(r==KErrNone);
	r=theSerialPorts[0]->QueryReceiveBuffer();
	test(r==0);
	r=theSerialPorts[1]->Open(PortB);
	test(r==KErrNone);
	r=theSerialPorts[1]->QueryReceiveBuffer();
	test(r==0);

	TCommConfig cBuf0;
	TCommConfigV01& c0=cBuf0();
	TBuf<0x40> mess;
	theSerialPorts[0]->Config(cBuf0);
	c0.iRate=EBps115200;
	c0.iParityError=0;
	c0.iHandshake=0;

	TCommConfig cBuf1;
	TCommConfigV01& c1=cBuf1();
	test(theSerialPorts[0]->SetConfig(cBuf0)==KErrNone);

	theSerialPorts[1]->Config(cBuf1);
	c1.iRate=EBps115200;
	c1.iParityError=0;
	c1.iHandshake=0;
	test(theSerialPorts[1]->SetConfig(cBuf1)==KErrNone);

	const TInt KXonWriteSize=KXonNumReads*KHWReadSize;

	TUint8* inBuf=new TUint8[KHWReadSize];
	TUint8* outBuf=new TUint8[KXonWriteSize];
	TPtr8 outDes(outBuf,KXonWriteSize,KXonWriteSize);
	TPtr8 inDes(inBuf,KHWReadSize,KHWReadSize);

//TUint8* inBuf2=new TUint8[KXonWriteSize];
//TPtr8 inDes2(inBuf2,KXonWriteSize,KXonWriteSize);

#if defined (__WINS__)
	theSerialPorts[0]->SetReceiveBufferLength(0x50);
	theSerialPorts[1]->SetReceiveBufferLength(0x50);
#else
	theSerialPorts[0]->SetReceiveBufferLength(0x400);
	theSerialPorts[1]->SetReceiveBufferLength(0x400);
#endif

	TInt numTests=sizeof(KHandshakes)/sizeof(THandShakeAndName);
	for(TInt j=0;j<numTests;j++)
		{
		mess.Format(_L("read/write using %s "),KHandshakes[j].iName);
		test.Next(mess);
		c0.iHandshake=c1.iHandshake=KHandshakes[j].iHandshake;

		if((theCaps1.iHandshake & KHandshakes[j].iHandshake)
			&& (theCaps2.iHandshake & KHandshakes[j].iHandshake))
			{
			test(theSerialPorts[0]->SetConfig(cBuf0)==KErrNone);
			test(theSerialPorts[1]->SetConfig(cBuf1)==KErrNone);
			TRequestStatus readStatus;
			TRequestStatus writeStatus;

			StripeMem(outDes,'A','Z');
			inDes.FillZ();

			theSerialPorts[1]->Write(writeStatus,outDes,KXonWriteSize);

//TRequestStatus writeStatus2;
//theSerialPorts[0]->Write(writeStatus2,outDes,KXonWriteSize);

			TInt i;
			for (i=0;i<KXonNumReads;i++)
				{
				inDes.FillZ();
#if defined (__WINS__)
				User::After(600000);
#else
				User::After(300000);
#endif
				test.Printf(_L("Reading %d after delay (%d of %d) avail = %d\r\n"),KHWReadSize, i+1,KXonNumReads, theSerialPorts[0]->QueryReceiveBuffer());
				theSerialPorts[0]->Read(readStatus,inDes,KHWReadSize);
				User::WaitForRequest(readStatus);
				test(readStatus==KErrNone);
				TPtrC8 aOutDes = outDes.Mid(KHWReadSize*i,KHWReadSize);
				test(CompareDescriptors(inDes,(TDes8&)aOutDes));
				test(inDes.Length()==KHWReadSize);
				}

			test.Next(_L("2nd Large Write complete"));
			User::WaitForRequest(writeStatus);
			test(writeStatus==KErrNone);

//theSerialPorts[1]->Read(readStatus,inDes2,KXonWriteSize);
//User::WaitForRequest(writeStatus2);
//test(writeStatus2==KErrNone);

//User::WaitForRequest(readStatus);
//test(readStatus==KErrNone);

			}
		else
			{
			test.Printf(_L("Config not supported\r\n"));
			}
		}
	delete [] inBuf;
	delete [] outBuf;

	theSerialPorts[0]->Close();
	theSerialPorts[1]->Close();

	test.End();
	}

void testWriteZero()
//
// Test a write of 0 bytes is still blocked by CTS flow control. 
// Test does a flow controlled Write(0) which is blocked by the remote 
// port state being closed (hence remote RTS disasserted, hence writer's 
// CTS likewise). Then it opens the remote port and asserts RTS - this 
// unblocks the original Write(0). 
// 
	{
	TInt r=theSerialPorts[0]->Open(PortA);
	test(r==KErrNone);
	r=theSerialPorts[0]->QueryReceiveBuffer();
	test(r==0);

	test.Next(_L("Testing Write 0"));

	TCommConfig cBuf1;
	TCommConfigV01& c1=cBuf1();
	theSerialPorts[0]->Config(cBuf1);
	c1.iRate=EBps19200;
	c1.iParityError=0;
	c1.iHandshake=KConfigObeyCTS;

	r=theSerialPorts[0]->SetConfig(cBuf1);
	test(r==KErrNone);

	test.Start(_L("Test Write(0) with remote RTS disasserted blocks"));
	TRequestStatus writeStat;
	theSerialPorts[0]->Write(writeStat,TPtr8(NULL,0),0);

	RTimer timer;
	timer.CreateLocal();
	TRequestStatus timeStatus;
	timer.After(timeStatus,1000000);
	User::WaitForRequest(timeStatus,writeStat);

	test(timeStatus==KErrNone);
	test(writeStat==KRequestPending);

	r=theSerialPorts[1]->Open(PortB);
	test(r==KErrNone);
	r=theSerialPorts[1]->QueryReceiveBuffer();
	test(r==0);

	TCommConfig cBuf2;
	TCommConfigV01& c2=cBuf2();
	theSerialPorts[1]->Config(cBuf2);
	c2.iRate=EBps19200;
	c2.iParityError=0;
	c2.iHandshake |= KConfigFreeRTS;
	r=theSerialPorts[1]->SetConfig(cBuf2);
	test(r==KErrNone);

	test.Next(_L("Test Write(0) with remote RTS asserted completes"));
	timer.After(timeStatus,10000000);
	theSerialPorts[1]->SetSignals(KSignalRTS,0);

	User::WaitForRequest(timeStatus,writeStat);
	if (writeStat==KRequestPending)
		test.Printf(_L("     Timed out!\n"));
	User::After(2000000);

	test(writeStat==KErrNone);
	test(timeStatus==KRequestPending);

	timer.Cancel();

	theSerialPorts[0]->Close();
	theSerialPorts[1]->Close();

	test.End();
	}


void testSingleCharacterReads()
//
// Test reading one character at a time.
//
	{
	const TInt KWriteSize=100;
	test.Start(_L("Test partial reads"));

	TInt r=theSerialPorts[0]->Open(PortA);
	test(r==KErrNone);
	r=theSerialPorts[0]->QueryReceiveBuffer();
	test(r==0);
	r=theSerialPorts[1]->Open(PortB);
	test(r==KErrNone);
	r=theSerialPorts[1]->QueryReceiveBuffer();
	test(r==0);

	TCommConfig cBuf0;
	TCommConfigV01& c0=cBuf0();
	theSerialPorts[0]->Config(cBuf0);

	TCommConfig cBuf1;
	TCommConfigV01& c1=cBuf1();
	theSerialPorts[1]->Config(cBuf1);

	c0.iRate=c1.iRate=EBps9600;
	c0.iParityError=c1.iParityError=0;
	c0.iHandshake=c1.iHandshake=KConfigObeyCTS;

	c0.iDataBits=c1.iDataBits=EData8;
	c0.iStopBits=c1.iStopBits=EStop1;
	c0.iParity=c1.iParity=EParityNone;

	r=theSerialPorts[0]->SetConfig(cBuf0);
	test(r==KErrNone);
	r=theSerialPorts[1]->SetConfig(cBuf1);
	test(r==KErrNone);
	test.Printf(_L("Setconfig OK\r\n"));

	TInt bufSiz=KWriteSize+3+(KWriteSize/2);

	r=theSerialPorts[0]->SetReceiveBufferLength(bufSiz);
	if (r!=KErrNone)
		test.Printf(_L("Setting buffers to %d bytes for com0 failed %d\n\r"),bufSiz,r);
	r=theSerialPorts[1]->SetReceiveBufferLength(bufSiz+1);
	if (r!=KErrNone)
		test.Printf(_L("Setting buffers to %d bytes for com1 failed %d\n\r"),bufSiz,r);

	TUint8* singleCharReadBuf=new TUint8[1];
	test(singleCharReadBuf!=NULL);
	TPtr8 singleCharReadDes(singleCharReadBuf,1,1);
	TUint8* multiCharWriteBuf=new TUint8[KWriteSize];
	test(multiCharWriteBuf!=NULL);
	TPtr8 multiCharWriteDes(multiCharWriteBuf,KWriteSize,KWriteSize);
	multiCharWriteDes.Fill('m');

	RTimer tim;
	tim.CreateLocal();

	for (TInt j=0;j<2;j++)
		{
		TInt readPort=0;
		TInt writePort=0;
		readPort=1-j;
		writePort=j;

		TBuf<256> message;
		message.Format(_L("Reading single chars from port %d, writing %d to port %d"),readPort,multiCharWriteDes.Length(),writePort);
		test.Next(message);

		TRequestStatus readZeroStat;
		theSerialPorts[readPort]->Read(readZeroStat,singleCharReadDes,0);//a zero length read completes immediately and
		User::WaitForRequest(readZeroStat);								 //will wake up the receiver
		test.Printf(_L("Have done a read zero: %d\n\r"),readZeroStat.Int());
		User::After(1000000);

		TRequestStatus multiWriteStat;
		theSerialPorts[writePort]->Write(multiWriteStat,multiCharWriteDes);
//		User::WaitForRequest(multiWriteStat);
//		test.Printf(_L("Have done a write: %d\n\r"),multiWriteStat.Int());

		TRequestStatus timStat;
		TInt spin=0;
		for (TInt i=0;i<KWriteSize;i++)
			{
			tim.After(timStat,10000000);
			TRequestStatus readStat;
			singleCharReadDes.SetLength(0);
			theSerialPorts[readPort]->Read(readStat,singleCharReadDes);
			User::WaitForRequest(readStat,timStat);

			test.Printf(_L("r"));
			if (i%32==0)
				test.Printf(_L("\r%c"),KSpinner[spin++%4]);

			if (readStat!=KErrNone)
				{
				TBuf<256> message;
				if (readStat==KRequestPending)
					{
					message.Format(_L("\n\rRead timed out after %d chars (of %d)\n\r"),i,KWriteSize);
					/*if (multiWriteStat==KErrNone)
						{
						User::WaitForRequest(multiWriteStat);
						theSerialPorts[readPort]->ReadCancel();
						theSerialPorts[writePort]->Write(multiWriteStat,multiCharWriteDes);
						}*/
					}
				else
					if (readStat!=KErrOverflow && readStat!=KErrCommsOverrun)
						message.Format(_L("\n\rRead Failed %d after %d chars (of %d)\n\r"),readStat.Int(),i,KWriteSize);

				test.Printf(message);
				User::After(2000000);
				test(EFalse);
				}

			tim.Cancel();
			if (singleCharReadDes[0]!='m')
				{
				test.Printf(_L("Received character: 0x%02x\n"),singleCharReadDes[0]);
				test(EFalse);
				}
			}

		test.Printf(_L("Done\n\r"));

		tim.After(timStat,1000000);
		User::WaitForRequest(timStat,multiWriteStat);
		if (timStat.Int()==KErrNone)
			{
			test.Printf(_L("Lost at least one char!\n\r"));
			theSerialPorts[writePort]->WriteCancel();
			test(EFalse);
			}
		else
			{
			tim.Cancel();
			}
		}

	TUint8* singleCharWriteBuf=new TUint8[1];
	test(singleCharWriteBuf!=NULL);
	TPtr8 singleCharWriteDes(singleCharWriteBuf,1,1);
	singleCharWriteDes.Fill('s');
	TUint8* multiCharReadBuf=new TUint8[KWriteSize];
	test(multiCharReadBuf!=NULL);
	TPtr8 multiCharReadDes(multiCharReadBuf,KWriteSize,KWriteSize);

	for (TInt k=0;k<2;k++)
		{
		TInt readPort=0;
		TInt writePort=0;

		readPort=k;
		writePort=1-k;

		TRequestStatus multiReadStat;
		theSerialPorts[readPort]->Read(multiReadStat,multiCharReadDes);

		TBuf<256> message;
		message.Format(_L("Writing single chars to port %d"),readPort);
		test.Next(message);

		TRequestStatus timStat;
		TInt spin=0;
		for (TInt i=0;i<KWriteSize;i++)
			{
			TRequestStatus writeStat;
			tim.After(timStat,5000000);
			theSerialPorts[writePort]->Write(writeStat,singleCharWriteDes);
			User::WaitForRequest(writeStat,timStat);

			if ((i%32)==0)
				test.Printf(_L("\r%c"),KSpinner[spin++%4]);

			if (writeStat!=KErrNone)
				{
				TBuf<256> message;
				if (writeStat==KRequestPending)
					message.Format(_L("\n\rWrite timed out after %d chars (of %d)\n\r"),i,KWriteSize);
				else
					message.Format(_L("\n\rWrite Failed %d after %d chars (of %d)\n\r"),writeStat.Int(),i,KWriteSize);

				test.Printf(message);
				}
			test(writeStat==KErrNone);
			tim.Cancel();
			}
		test.Printf(_L("Done\n\r"));

		tim.After(timStat,1000000);
		User::WaitForRequest(timStat,multiReadStat);
		if (timStat.Int()==KErrNone)
			{
			test.Printf(_L("Lost at least one char!\n\r"));
			theSerialPorts[readPort]->ReadCancel();
			test(EFalse);
			}
		else
			{
			tim.Cancel();
			test(multiReadStat==KErrNone);
			test(multiCharWriteDes.Length()==multiCharWriteDes.MaxLength());
			}
		}

	test.End();
	tim.Close();

	delete [] multiCharWriteBuf;
	delete [] singleCharReadBuf;
	delete [] singleCharWriteBuf;
	delete [] multiCharReadBuf;

	theSerialPorts[0]->Close();
	theSerialPorts[1]->Close();
	}

void testBiDirectionalSingleCharacterReads()
//
// Test reading and writing one character at a time.
//
	{

	test.Start(_L("Test concurrent partial reads and writes"));

	TInt r=theSerialPorts[0]->Open(PortA);
	test(r==KErrNone);
	r=theSerialPorts[0]->QueryReceiveBuffer();
	test(r==0);
	r=theSerialPorts[1]->Open(PortB);
	test(r==KErrNone);
	r=theSerialPorts[1]->QueryReceiveBuffer();
	test(r==0);

	TCommConfig cBuf0;
	TCommConfigV01& c0=cBuf0();
	theSerialPorts[0]->Config(cBuf0);

	TCommConfig cBuf1;
	TCommConfigV01& c1=cBuf1();
	theSerialPorts[1]->Config(cBuf1);

	c0.iRate=c1.iRate=EBps9600;
	c0.iParityError=c1.iParityError=0;
	c0.iHandshake=c1.iHandshake=KConfigObeyCTS;

	c0.iDataBits=c1.iDataBits=EData8;
	c0.iStopBits=c1.iStopBits=EStop1;
	c0.iParity=c1.iParity=EParityNone;

	r=theSerialPorts[0]->SetConfig(cBuf0);
	test(r==KErrNone);
	r=theSerialPorts[1]->SetConfig(cBuf1);
	test(r==KErrNone);

	const TInt KWriteSize=4000;
	TUint8* singleCharReadBuf=new TUint8[1];
	test(singleCharReadBuf!=NULL);
	TPtr8 singleCharReadDes(singleCharReadBuf,1,1);
	TUint8* multiCharWriteBuf=new TUint8[KWriteSize];
	test(multiCharWriteBuf!=NULL);
	TPtr8 multiCharWriteDes(multiCharWriteBuf,KWriteSize,KWriteSize);
	multiCharWriteDes.Fill('m');
	TUint8* singleCharWriteBuf=new TUint8[1];
	test(singleCharWriteBuf!=NULL);
	TPtr8 singleCharWriteDes(singleCharWriteBuf,1,1);
	singleCharWriteDes.Fill('s');
	TUint8* multiCharReadBuf=new TUint8[KWriteSize];
	test(multiCharReadBuf!=NULL);
	TPtr8 multiCharReadDes(multiCharReadBuf,KWriteSize,KWriteSize);

	TRequestStatus multiWriteStat;
	TRequestStatus multiReadStat;
	theSerialPorts[0]->Write(multiWriteStat,multiCharWriteDes);
	theSerialPorts[0]->Read(multiReadStat,multiCharReadDes);

	TInt spin=0;
	for (TInt i=0;i<KWriteSize;i++)
		{
		if (i%32==0)
			test.Printf(_L("\r%c"),KSpinner[spin++%4]);

		TRequestStatus readStat;
		TRequestStatus writeStat;
		theSerialPorts[1]->Read(readStat,singleCharReadDes);
		theSerialPorts[1]->Write(writeStat,singleCharWriteDes);
		User::WaitForRequest(readStat);
		User::WaitForRequest(writeStat);

		if (readStat!=KErrNone)
			{
			test.Printf(_L("Read Failed %d after %d chars\n\r"),readStat.Int(),i);
			test(EFalse);
			}
		if (writeStat!=KErrNone)
			{
			test.Printf(_L("Write Failed %d after %d chars\n\r"),writeStat.Int(),i);
			test(EFalse);
			}
		}

	test.Printf(_L("\n\r"));

	RTimer tim;
	tim.CreateLocal();
	TRequestStatus timStat;
	tim.After(timStat,3000000);
	User::WaitForRequest(multiWriteStat,timStat);
	test(timStat==KRequestPending);
	tim.Cancel();
	User::WaitForRequest(timStat);
	test(timStat==KErrCancel);
	test(multiWriteStat==KErrNone);
	tim.After(timStat,3000000);
	User::WaitForRequest(multiReadStat,timStat);
	test(timStat==KRequestPending);
	tim.Cancel();
	tim.Close();
	User::WaitForRequest(timStat);
	test(timStat==KErrCancel);
	test(multiReadStat==KErrNone);
	test(multiCharWriteDes.Length()==multiCharWriteDes.MaxLength());

	test.End();

	delete [] multiCharWriteBuf;
	delete [] singleCharReadBuf;
	delete [] singleCharWriteBuf;
	delete [] multiCharReadBuf;

	theSerialPorts[0]->Close();
	theSerialPorts[1]->Close();
	}

void testMultiTerminatorCompletion()
//
// Test multiple terminator completions
//
	{
	test.Next(_L("Test partial reads with terminators"));

	TInt r=theSerialPorts[0]->Open(PortA);
	test(r==KErrNone);
	r=theSerialPorts[0]->QueryReceiveBuffer();
	test(r==0);
	r=theSerialPorts[1]->Open(PortB);
	test(r==KErrNone);
	r=theSerialPorts[1]->QueryReceiveBuffer();
	test(r==0);

	TCommConfig cBuf0;
	TCommConfigV01& c0=cBuf0();
	theSerialPorts[0]->Config(cBuf0);
	TCommConfig cBuf1;
	TCommConfigV01& c1=cBuf1();
	theSerialPorts[1]->Config(cBuf1);

	c0.iRate=c1.iRate=EBps9600;
	c0.iParityError=c1.iParityError=0;

	c0.iHandshake=c1.iHandshake=KConfigObeyCTS;

	c0.iDataBits=c1.iDataBits=EData8;
	c0.iStopBits=c1.iStopBits=EStop1;
	c0.iParity=c1.iParity=EParityNone;

	r=theSerialPorts[0]->SetConfig(cBuf0);
	test(r==KErrNone);
	c1.iTerminator[0]='a';
	c1.iTerminatorCount=1;
	r=theSerialPorts[1]->SetConfig(cBuf1);
	test(r==KErrNone);
	const TInt KWriteSize=4000;
	TUint8* writeBuf=new TUint8[KWriteSize];
	test(writeBuf!=NULL);
	TPtr8 writeDes(writeBuf,KWriteSize,KWriteSize);
	writeDes.Fill('a');
	TUint8* readBuf=new TUint8[KWriteSize];
	test(readBuf!=NULL);
	TPtr8 readDes(readBuf,KWriteSize,KWriteSize);
	TRequestStatus writeStat;
	theSerialPorts[0]->Write(writeStat,writeDes);
	test(writeStat==KRequestPending);
	TInt spin=0;
	for (TInt i=0;i<KWriteSize;i++)
		{
		if (i%32==0)
			test.Printf(_L("\r%c"),KSpinner[spin++%4]);
		TRequestStatus readStat;
		readDes.SetLength(KWriteSize/2);
		theSerialPorts[1]->Read(readStat,readDes);
		User::WaitForRequest(readStat);
		test(readStat==KErrNone);
		test(readDes.Length()==1);
		}
	test.Printf(_L("\n\r"));
	User::WaitForRequest(writeStat);

	delete [] readBuf;
	delete [] writeBuf;

	theSerialPorts[0]->Close();
	theSerialPorts[1]->Close();
	}

void TestSimpleWriting()
	{
	test.Next(_L("Test we can still write 0->1"));
	const TPtrC8 string1=_L8("If you strike me down, I shall become more powerful than you can possibly imagine.");
	TBuf8<100> inBuf;
	TRequestStatus stat;
	theSerialPorts[1]->Read(stat,inBuf,string1.Length());
	test(stat==KRequestPending);
	TInt r=theSerialPorts[0]->WriteS(string1,string1.Length());
	test(r==KErrNone);
	User::WaitForRequest(stat);
	test(stat==KErrNone);
	test(inBuf==string1);

	test.Next(_L("Test we can still write 1->0"));
	const TPtrC8 string2=_L8("Who's the more foolish... the fool or the fool who follows him?");
	theSerialPorts[0]->Read(stat,inBuf,string2.Length());
	test(stat==KRequestPending);
	r=theSerialPorts[1]->WriteS(string2,string2.Length());
	test(r==KErrNone);
	User::WaitForRequest(stat);
	test(stat==KErrNone);
	test(inBuf==string2);
	}

void TestPower()
	{
	test.Next(_L("Power up and down"));

	TInt r=theSerialPorts[0]->Open(PortA);
	test(r==KErrNone);
	r=theSerialPorts[0]->QueryReceiveBuffer();
	test(r==0);
	r=theSerialPorts[1]->Open(PortB);
	test(r==KErrNone);
	r=theSerialPorts[1]->QueryReceiveBuffer();
	test(r==0);

	test.Start(_L("Power down while writing 0->1"));
	TCommConfig cBuf1;
	TCommConfigV01& c1=cBuf1();
	theSerialPorts[0]->Config(cBuf1);
	TCommConfig cBuf2;
	TCommConfigV01& c2=cBuf2();
	theSerialPorts[1]->Config(cBuf2);
	c1.iFifo=EFifoEnable;

	c2.iDataBits=c1.iDataBits=EData8;
	c2.iStopBits=c1.iStopBits=EStop1;
	c2.iParity=c1.iParity=EParityEven;
	c1.iRate=c2.iRate=EBps19200;
	c1.iHandshake=c2.iHandshake=0;

	r=theSerialPorts[0]->SetConfig(cBuf1);
	test(r==KErrNone);
	r=theSerialPorts[1]->SetConfig(cBuf2);
	test(r==KErrNone);

	RTimer timer;
	test(timer.CreateLocal()==KErrNone);
	TTime wakeup;
	wakeup.HomeTime();
	wakeup+=TTimeIntervalSeconds(10);
	TRequestStatus done;
	timer.At(done,wakeup);
	test(done==KRequestPending);
	RAsyncSwitchOff async;
	r=async.Start(2000000);
	test(r==KErrNone);

//	test(PowerCheckedWrite(KWriteSize*200)==KErrNone);

	const TUint bigWriteSize=KWriteSize*200;
	TUint8* inBuf=new TUint8[bigWriteSize];
	test(inBuf!=NULL);
	TUint8* outBuf=new TUint8[bigWriteSize];
	test(outBuf!=NULL);
	TPtr8 outDes(outBuf,bigWriteSize,bigWriteSize);
	TPtr8 inDes(inBuf,bigWriteSize,bigWriteSize);

	RTimer tim;
	tim.CreateLocal();
	TRequestStatus readStatus;
	TRequestStatus timeStatus;

	StripeMem(outDes,'A','Z');
	inDes.FillZ();

	theSerialPorts[0]->Read(readStatus,inDes,bigWriteSize);
	test(readStatus==KRequestPending);

	test.Printf(_L("Write........."));
	r=theSerialPorts[1]->WriteS(outDes,bigWriteSize);
	test(r==KErrAbort);
	test.Printf(_L("Aborted by power down\n"));
	r=async.Wait();
	test(r==KErrNone);
	r=async.Start(2000000);
	test(r==KErrNone);
	const TUint KTimeOut=6000000;
	tim.After(timeStatus,KTimeOut);
	User::WaitForRequest(readStatus,timeStatus);
	if (timeStatus==KErrNone)
		{
		test.Printf(_L("Timed Out!\n\r"));
		theSerialPorts[0]->ReadCancel();
		test(EFalse);
		}
	tim.Cancel();
	test(readStatus==KErrAbort);
	r=async.Wait();
	test(r==KErrNone);

	User::WaitForRequest(done);
	test(done==KErrNone);

	test.Next(_L("Reset config"));
	TestSimpleWriting();
	test.Next(_L("Close and reopen"));
	theSerialPorts[0]->Close();
	theSerialPorts[1]->Close();

	r=theSerialPorts[0]->Open(PortA);
	test(r==KErrNone);
	r=theSerialPorts[1]->Open(PortB);
	test(r==KErrNone);

	theSerialPorts[0]->Config(cBuf1);
	theSerialPorts[1]->Config(cBuf2);
	c1.iFifo=EFifoEnable;
	c2.iDataBits=c1.iDataBits=EData8;
	c2.iStopBits=c1.iStopBits=EStop1;
	c2.iParity=c1.iParity=EParityNone;
	c1.iRate=c2.iRate=EBps19200;
	c1.iHandshake=c2.iHandshake=0;

	r=theSerialPorts[0]->SetConfig(cBuf1);
	test(r==KErrNone);
	r=theSerialPorts[1]->SetConfig(cBuf2);
	test(r==KErrNone);

	TestSimpleWriting();

	test.Next(_L("Power down while writing 1->0"));
	theSerialPorts[0]->Config(cBuf1);
	theSerialPorts[1]->Config(cBuf2);
	c1.iFifo=EFifoEnable;

	c2.iDataBits=c1.iDataBits=EData8;
	c2.iStopBits=c1.iStopBits=EStop1;
	c2.iParity=c1.iParity=EParityEven;
	c1.iRate=c2.iRate=EBps9600;
	c1.iHandshake=c2.iHandshake=0;

	r=theSerialPorts[0]->SetConfig(cBuf1);
	test(r==KErrNone);
	r=theSerialPorts[1]->SetConfig(cBuf2);
	test(r==KErrNone);

	wakeup.HomeTime();
	wakeup+=TTimeIntervalSeconds(10);
	timer.At(done,wakeup);
	test(done==KRequestPending);
	r=async.Start(2000000);
	test(r==KErrNone);

//	test(PowerCheckedWrite(KWriteSize*200)==KErrNone);
	StripeMem(outDes,'A','Z');
	inDes.FillZ();

	theSerialPorts[1]->Read(readStatus,inDes,bigWriteSize);
	test(readStatus==KRequestPending);

	test.Printf(_L("Write........."));
	r=theSerialPorts[0]->WriteS(outDes,bigWriteSize);
	test(r==KErrAbort);
	test.Printf(_L("Aborted by power down\n"));
	tim.After(timeStatus,KTimeOut);
	User::WaitForRequest(readStatus,timeStatus);
	if (timeStatus==KErrNone)
		{
		test.Printf(_L("Timed Out!\n\r"));
		theSerialPorts[1]->ReadCancel();
		test(EFalse);
		}
	tim.Cancel();
	CHECK(readStatus.Int(),KErrAbort);
	r=async.Wait();
	test(r==KErrNone);

	User::WaitForRequest(done);
	test(done==KErrNone);

	test.Next(_L("Reset config"));
	TestSimpleWriting();
	test.Next(_L("Close and reopen"));
	theSerialPorts[0]->Close();
	theSerialPorts[1]->Close();

	r=theSerialPorts[0]->Open(PortA);
	test(r==KErrNone);
	r=theSerialPorts[0]->QueryReceiveBuffer();
	test(r==0);
	r=theSerialPorts[1]->Open(PortB);
	test(r==KErrNone);
	r=theSerialPorts[1]->QueryReceiveBuffer();
	test(r==0);

	theSerialPorts[0]->Config(cBuf1);
	theSerialPorts[1]->Config(cBuf2);
	c1.iFifo=EFifoEnable;
	c2.iDataBits=c1.iDataBits=EData8;
	c2.iStopBits=c1.iStopBits=EStop1;
	c2.iParity=c1.iParity=EParityNone;
	c1.iRate=c2.iRate=EBps19200;
	c1.iHandshake=c2.iHandshake=0;

	r=theSerialPorts[0]->SetConfig(cBuf1);
	test(r==KErrNone);
	r=theSerialPorts[1]->SetConfig(cBuf2);
	test(r==KErrNone);

	TestSimpleWriting();

	theSerialPorts[0]->Close();
	theSerialPorts[1]->Close();

	test.Next(_L("Test signals are preserved"));

	r=theSerialPorts[0]->Open(PortA);
	test(r==KErrNone);
	r=theSerialPorts[0]->QueryReceiveBuffer();
	test(r==0);
	r=theSerialPorts[1]->Open(PortB);
	test(r==KErrNone);
	r=theSerialPorts[1]->QueryReceiveBuffer();
	test(r==0);

	if((theCaps1.iHandshake & KCapsFreeRTSSupported) && (theCaps2.iHandshake & KCapsFreeRTSSupported))
		{//should also check for KConfigFreeDTR
		theSerialPorts[0]->Config(cBuf1);
		theSerialPorts[1]->Config(cBuf2);

		c1.iHandshake=KConfigFreeRTS|KConfigFreeDTR;
		c2.iHandshake=KConfigFreeRTS|KConfigFreeDTR;
		r=theSerialPorts[0]->SetConfig(cBuf1);
		CHECK(r,KErrNone);
		r=theSerialPorts[1]->SetConfig(cBuf2);
		CHECK(r,KErrNone);

		theSerialPorts[0]->SetSignals(KSignalRTS,KSignalDTR);
		theSerialPorts[1]->SetSignals(KSignalDTR,KSignalRTS);

		TUint signals=theSerialPorts[0]->Signals();
		//test(signals==(KSignalRTS|KSignalDSR));//something weird happens here under WINS - the CD line is set(?)
		CHECK((signals&(KSignalRTS|KSignalDSR)) , (KSignalRTS|KSignalDSR));
		signals=theSerialPorts[1]->Signals();
		CHECK(signals,(KSignalDTR|KSignalCTS));

		wakeup.HomeTime();
		wakeup+=TTimeIntervalSeconds(10);
		timer.At(done,wakeup);
		r=async.Start(5000000);
		CHECK(r,KErrNone);
		test(done==KRequestPending);
		User::WaitForRequest(done);
		test(done==KErrNone);
		r=async.Wait();
		CHECK(r,KErrNone);

		User::After(100000);	// wait for both ports to power back up
		signals=theSerialPorts[0]->Signals();
		//test(signals==(KSignalRTS|KSignalDSR));
		CHECK((signals&(KSignalRTS|KSignalDSR)) , (KSignalRTS|KSignalDSR));
		signals=theSerialPorts[1]->Signals();
		CHECK(signals,(KSignalDTR|KSignalCTS));
		}

	c1.iHandshake=0;
	c2.iHandshake=0;
	r=theSerialPorts[0]->SetConfig(cBuf1);
	test(r==KErrNone);
	r=theSerialPorts[1]->SetConfig(cBuf2);
	test(r==KErrNone);

	theSerialPorts[0]->Close();
	theSerialPorts[1]->Close();

	test.End();
	}

void testSwitchIrDA()
	{
	test.Next(_L("Switch to IrDA"));
//Open the serial port channel.

	TInt r=theSerialPorts[0]->Open(PortA);
	test(r==KErrNone);
	r=theSerialPorts[0]->QueryReceiveBuffer();
	test(r==0);

	theSerialPorts[0]->Caps(theCaps1Buf);
	if (!(theCaps1.iSIR&KCapsSIR115kbps))
		{
		theSerialPorts[0]->Close();
		test.Printf(_L("\t\tIrDA not supported\n"));
		return;
		}

	r=theSerialPorts[0]->QueryReceiveBuffer();
	test(r==0);


//Configure the channel for IrDA at 115.2k baud.
	TCommConfig cBuf1;
	TCommConfigV01& c1=cBuf1();
	theSerialPorts[0]->Config(cBuf1);
	c1.iSIREnable=ESIREnable;
	c1.iRate=EBps115200;
	c1.iDataBits=EData8;
	c1.iParity=EParityNone;
	c1.iStopBits=EStop1;
	c1.iHandshake=0;
	c1.iHandshake|=KConfigFreeDTR;
	c1.iHandshake|=KConfigFreeRTS;
	r=theSerialPorts[0]->SetConfig(cBuf1);
	test(r==KErrNone);
	r=theSerialPorts[0]->QueryReceiveBuffer();
	test(r==0);
	const TUint8 KData[1] ={0x00};
	const TPtrC8 KDataPtr(KData,1);
	TRequestStatus stat;
	theSerialPorts[0]->Write(stat,KDataPtr);
	User::WaitForRequest(stat);
	r=theSerialPorts[0]->QueryReceiveBuffer();
	test.Printf(_L("ReceiveBuf = %d\n"),r);
//	test(r==0);

	theSerialPorts[0]->Write(stat,KDataPtr);
	User::WaitForRequest(stat);
	User::After(1000000);
	r=theSerialPorts[0]->QueryReceiveBuffer();
	test.Printf(_L("ReceiveBuf = %d\n"),r);
	while (theSerialPorts[0]->QueryReceiveBuffer())
		{
		TBuf8<1> buf;
		theSerialPorts[0]->Read(stat,buf,1);
		test.Printf(_L("Data = "),&buf);
		User::WaitForRequest(stat);
		test.Printf(_L("%d\n"),buf[0]);
		}
	r=theSerialPorts[0]->QueryReceiveBuffer();
	test.Printf(_L("ReceiveBuf = %d\n"),r);
	theSerialPorts[0]->Write(stat,KDataPtr);
	User::WaitForRequest(stat);
//Check for any received data pending (the answer is 1! Which is incorrect as nothing has sent me any IrDA data)
	r=theSerialPorts[0]->QueryReceiveBuffer();
	test.Printf(_L("ReceiveBuf = %d\n"),r);
//	test(r==0);
	theSerialPorts[0]->Write(stat,KDataPtr);
	User::WaitForRequest(stat);
	User::After(1000000);
	r=theSerialPorts[0]->QueryReceiveBuffer();

	theSerialPorts[0]->Close();
	}

GLDEF_C TInt E32Main()
//
//
//
    {

#if defined (__WINS__)
	test.SetLogged(ETrue);	// log to $TEMP/EPOCWIND.OUT
#else
	test.SetLogged(EFalse);	//turn off serial port debugging!
#endif


	test.Title();
	test.Start(_L("Serial loopback test"));

	TInt muid=0;
	test(HAL::Get(HAL::EMachineUid, muid)==KErrNone);
//CF
	TBool isAssabet=(muid==HAL::EMachineUid_Assabet);

	PortA=0;
	PortB=3; // used to be 1 but it apparently doesn't exist
	TBuf <0x100> cmd;
	User::CommandLine(cmd);

	TBool stress = EFalse;
	if (cmd.Length()>0)
		{
		if (cmd.Length() == 1)
			{
			if ((cmd[0] == 'S') || (cmd[0] == 's'))
				stress = ETrue;
			}
		else
			{
			if (cmd[0]>='0' && cmd[0]<='9')
				PortA=(TInt)(cmd[0]-'0');
			if (cmd[2]>='0' && cmd[2]<='9')
				PortB=(TInt)(cmd[2]-'0');
			if ((cmd[cmd.Length()-1] == 'S') || (cmd[cmd.Length()-1] == 's'))
				stress = ETrue;
			}
		}


	test.Printf(_L("Primary Port:%d Secondary Port:%d\n\r"),PortA,PortB);


	TInt r;
    TBuf<10> pddName=PDD_NAME;
	test.Next(_L("Load PDDs"));
#ifdef __WINS__
	const TInt KMaxPdds=0;
#else
	const TInt KMaxPdds=10;
#endif
	TInt i;
	for (i=-1; i<KMaxPdds; ++i)
		{
		if (i==0)
			pddName.Append(TChar('0'));
		else if (i>0)
			pddName[pddName.Length()-1] = (TText)('0'+i);
		r=User::LoadPhysicalDevice(pddName);
		if (r==KErrNone || r==KErrAlreadyExists)
			test.Printf(_L("PDD %S loaded\n"),&pddName);
		}

	test.Next(_L("Load LDD"));
	r=User::LoadLogicalDevice(LDD_NAME);
	test.Printf(_L("Load LDD Return %d\n\r"),r);

	test.Next(_L("Create RComm objects"));
	theSerialPorts[0]=new RComm;
	theSerialPorts[1]=new RComm;
	test(theSerialPorts[0]!=NULL);
	test(theSerialPorts[1]!=NULL);
//

	do
		{		

		test.Next(_L("Open:"));
		r=theSerialPorts[0]->Open(PortA);
		test.Printf(_L("Open(Unit0)=%d\n\r"),r);
		test(r==KErrNone);
		r=theSerialPorts[1]->Open(PortB);
		test.Printf(_L("Open(Unit1)=%d\n\r"),r);
		test(r==KErrNone);

		test.Next(_L("Get caps"));
		theSerialPorts[0]->Caps(theCaps1Buf);
		test(r==KErrNone);
		theSerialPorts[1]->Caps(theCaps2Buf);
		test(r==KErrNone);

		theSerialPorts[0]->Close();
		theSerialPorts[1]->Close();

		testReadWrite();

		// testTiming();

		turnaroundTestReadWrite();

		testTerminators();
		testHWHandshaking();

		if((theCaps1.iHandshake & KCapsObeyXoffSupported) && (theCaps2.iHandshake & KCapsObeyXoffSupported))
			testXonXoff();

		if((theCaps1.iHandshake & KCapsObeyCTSSupported) && (theCaps2.iHandshake & KCapsObeyCTSSupported))
			{
			testSingleCharacterReads();
			testWriteZero();
	//CF - see description of problem with testTerminators()
			if (!isAssabet) testMultiTerminatorCompletion();
			testBiDirectionalSingleCharacterReads();
			}

		testFraming();
		testBreak();
		testSwitchIrDA();
		} while (stress);

	User::After(3000000);
	test.End();
	return(KErrNone);
	}