kerneltest/e32test/device/t_serial.cpp
changeset 0 a41df078684a
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/kerneltest/e32test/device/t_serial.cpp	Mon Oct 19 15:55:17 2009 +0100
@@ -0,0 +1,2944 @@
+// 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);
+	}