serialserver/c32serialserver/LOOPBACK/loopback.CPP
changeset 0 dfb7c4ff071f
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/serialserver/c32serialserver/LOOPBACK/loopback.CPP	Thu Dec 17 09:22:25 2009 +0200
@@ -0,0 +1,1991 @@
+// Copyright (c) 1997-2009 Nokia Corporation and/or its subsidiary(-ies).
+// All rights reserved.
+// This component and the accompanying materials are made available
+// under the terms of "Eclipse Public License v1.0"
+// which accompanies this distribution, and is available
+// at the URL "http://www.eclipse.org/legal/epl-v10.html".
+//
+// Initial Contributors:
+// Nokia Corporation - initial contribution.
+//
+// Contributors:
+//
+// Description:
+// This file implements a loopback driver for use with the ETel Regression test harness.  This
+// driver supports two ports with signalling capabilities for DCD, DSR, DTR, RTS, RI, and CTS.
+// In order for the signalling to function properly, one port should be opened as a DCE, though
+// the driver will allow both ports to operate as DTE's.
+// By default, the driver marks or asserts (sets to 1) the following signals when the method
+// SetRole is called.
+// For the DTE:
+// KSignalRTS	KSignalDTR
+// For the DCE:
+// KSignalCTS	KSignalDSR
+// Note that the DCE does NOT set DCD, thus it's initial state is space or de-asserted
+// (set to zero).  This was chosen because of the desire of test script authors to fully
+// control DCD.
+// The driver supports the following signalling configuration handshake capabilities:
+// For the DTE:
+// KCapsObeyCTSSupported  KCapsFailCTSSupported
+// KCapsObeyDSRSupported  KCapsFailDSRSupported
+// KCapsObeyDCDSupported  KCapsFailDCDSupported
+// For the DCE:
+// KCapsObeyRTSSupported  KCapsObeyDTRSupported
+// Break, Configuration Change Notification, Flow Control Notification, and Data Available
+// Notifications are not supported.
+// directory exists.  The log file is named loopback.
+// 
+//
+
+/**
+ @file
+ @note	This driver will create a log file in the Logs\ETel directory if the Logs\ETel
+*/
+#include <cs_port.h>
+#include "LOGGER.H"
+#include <d32comm.h>
+#include <c32comm.h>
+#include <e32hal.h>
+#include <c32comm_internal.h>
+
+#ifdef __LOGGER__XTRA
+#define LOGI(AAA)	CETelLogger::WriteFormat(TRefByValue<const TDesC8>(AAA),iPortName,iSignals,&iPtr);
+#else
+#define LOGI(AAA)
+#endif
+
+#if defined(__VC32__) && _MSC_VER==1100
+// Disable MSVC++ 5.0 aggressive warnings about non-expansion of inline functions.
+#pragma warning(disable : 4710)	// function '...' not expanded
+#endif
+
+const TUint KCommLowUnit=0;
+const TUint KBufferGrowthIncrement=0x1000;
+const TUint KMaxBufferSize = 0x8000;
+
+// This should be even
+const TUint KLoopbackCount=8;
+
+
+#define SERIAL_DESCRIPTION _L("Loopback CSY")
+#define SERIAL_NAME _S("Loopback")
+
+// #define _DEBUG_CONSOLE_
+
+#if defined(_DEBUG_CONSOLE_)
+// This class is not used in the loopback csy.  It is left here for future reference.
+#include <e32twin.h>
+class RDebugConsole : public RConsole
+	{
+public:
+	RDebugConsole();
+public:
+	void Printf(TRefByValue<const TDesC> aFmt,...);
+	};
+
+#define DEBUG_TRACE(m) m
+
+#else	// (_DEBUG)
+
+#define DEBUG_TRACE(m)
+//
+#endif
+
+//
+// "Entry point object" makes the objects which do the work
+class CHWPort;
+
+
+/**
+ * This class is the factory port object.  It drives the "entry point object" which
+ * makes the reset of the objects do their work.  It is based on the basic serial port
+ * class CSerial.
+ */
+class CHWPortFactory : public CSerial
+	{
+public:
+    CHWPortFactory();
+    ~CHWPortFactory();
+	virtual CPort * NewPortL(const TUint aUnit);
+	virtual void Info(TSerialInfo &aSerialInfo);
+	void Remove(CHWPort* aPort);
+public: //CSerial
+	TSecurityPolicy PortPlatSecCapability(TUint aPort) const;
+private:
+
+	CHWPort* iPort[KLoopbackCount];	// pairs of ports
+	CHWPort* PairedPort(TUint aPort) { return iPort[aPort^1]; } // returns the other half
+
+	};
+
+
+/**
+ *	This class is the object that interfaces with the commserver.  An instance of this class
+ *	represents one port in the loopback driver.
+ */
+class CHWPort : public CPort
+	{
+public:
+	static CHWPort * NewL(TUint aUnit);
+private:
+	 CHWPort();
+
+public:
+	virtual void StartRead(const TAny* aClientBuffer,TInt aLength);
+	virtual void ReadCancel();
+	virtual TInt QueryReceiveBuffer(TInt& aLength) const;
+	virtual void ResetBuffers(TUint aFlags);
+	virtual void StartWrite(const TAny* aClientBuffer,TInt aLength);
+	virtual void WriteCancel();
+	virtual void Break(TInt aTime);
+	virtual void BreakCancel();
+	virtual TInt GetConfig(TDes8& aDes) const;
+	virtual TInt SetConfig(const TDesC8& aDes);
+	virtual TInt SetServerConfig(const TDesC8& aDes);
+	virtual TInt GetServerConfig(TDes8& aDes);
+	virtual TInt GetCaps(TDes8& aDes);
+	virtual TInt GetSignals(TUint& aSignals);
+	virtual TInt SetSignalsToMark(TUint aSignals);
+	virtual TInt SetSignalsToSpace(TUint aSignals);
+	virtual TInt GetReceiveBufferLength(TInt& aLength) const;
+	virtual TInt SetReceiveBufferLength(TInt aSignals);
+	virtual void Destruct();
+	virtual void FreeMemory();
+	virtual void NotifySignalChange(TUint aSignalMask);
+	virtual void NotifySignalChangeCancel();
+	virtual void NotifyConfigChange();
+	virtual void NotifyConfigChangeCancel();
+	virtual void NotifyFlowControlChange();
+	virtual void NotifyFlowControlChangeCancel();
+	virtual void NotifyBreak();
+	virtual void NotifyBreakCancel();
+	virtual void NotifyDataAvailable();
+	virtual void NotifyDataAvailableCancel();
+	virtual void NotifyOutputEmpty();
+	virtual void NotifyOutputEmptyCancel();
+	virtual TInt GetFlowControlStatus(TFlowControl& aFlowControl);
+	virtual TInt GetRole(TCommRole& aRole);
+	virtual TInt SetRole(TCommRole aRole);
+
+	virtual ~CHWPort();
+#if defined (_DEBUG_DEVCOMM)
+	virtual void DoDumpDebugInfo(const RMessage2 &aMessage);
+#endif
+public:
+	void SetLoopbackPort(CHWPort* aHWPort);
+	TInt WriteBuf(const TAny* aClientBuffer,TInt aLength, TBool& aIssueComplete);
+	void TryToCompleteRead();
+	void UpdatePortResources();
+
+private:
+	void CheckSigsAndCompleteRead();
+public:
+	TPtr8 iPtr;				//< A pointer to the buffer created during class creation.
+
+private:
+	TCommRole iRole;		//< The role of this port (ECommRoleDTE or ECommRoleDCE)
+	TUint iSignals;			//< The current signals for this port
+	TUint iSignalMask;		//< The mask used when a NotifySignalChange request is posted.
+							//< If it is clear, no request is outstanding.  It is cleared when
+							//< a request is either cancelled or completed.
+
+	TBool iWritePending;			//< True if a write is left pending
+	TInt  iWritePendingLength;		//< The length of the buffer that was left pending.
+	const TAny* iClientWriteBuffer; //< The buffer pointer that was pended.  A pended write
+									//< implies that the WriteCompleted was NOT called when
+									//< the write was posted.
+
+	TPckgBuf<TCommServerConfigV01> iServerConfig;	//< A placeholder; no real function in loopback.
+	TPckgBuf<TCommConfigV01> iConfig;	//< The configuration of this port.  Important fields for
+										//< the loopback driver are: iHandShake
+
+	CHWPort* iLoopbackPort;	//< The pointer to the loopback port to which this port is connected.
+
+	/**
+	An HBufC created during port creation, pointed to by iPtr.
+	This buffer is only appended to by this object on a write request;
+	this object never reads from the buffer.  iLoopbackPort is the
+	object responsible for reading from this iBuf (via iPtr), and send
+	the data it reads to iLoopbackPort's client (as a completion of a
+	read request).
+	*/
+	HBufC8* iBuf;			//< An HBuf created during port creation, pointed to by iPtr.
+							//< Note that this buffer can grow during operation, but it will
+							//< not shrink.
+	TInt iBufSize;			//< The initial size of the buffer and it's growth increment.
+	TBool iReadPending;		//< Boolean indicating that a read has been left pending for later
+							//< completion.
+
+	TBool iReadOneOrMore;	//< Should the current read (even if it is pending) complete before
+							//< the buffer is full?
+
+	TBool iDataNotify;		//< Set when a data notify event is pending
+
+	TInt iBytesWritten;		//< The number of bytes written to the current read buffer.
+	TInt iPendingLength;	//< Count of how many more bytes can be written to the read buffer
+	/**
+	Pointer to the client buffer to be filled by a read.  The read
+	actually takes the data from iLoopbackPort->iPtr to send to the
+	client.
+	*/
+	const TAny* iClientReadBuffer;		//< Pointer to the client buffer to be filled by a read.
+
+	TUint iPortName;		//< Unit number of this port
+
+#if defined (_DEBUG_CONSOLE_)
+	// Not used in the current loopback driver.
+#if defined (_DEBUG_DEVCOMM)
+	CCommDebugDumper *iDumper;		//< Pointer to debug class.
+#endif
+public:
+	RDebugConsole iConsole;			//< Console for debugging.
+#endif
+	};
+
+//
+// CHWPort definitions
+//
+CHWPort::CHWPort() : iPtr(NULL,0,0)
+/**
+ * This method is the basic constructor for the CHWPort class.  In initializes
+ * the iPtr member specifically to NULL.
+ *
+ * @param	None
+ *
+ * @return	None
+ */
+	{
+	__DECLARE_NAME(_S("CHWPort"));
+	}
+
+void CloseObject(TAny* anObject)
+/**
+ * This method simply closes an object from the cleanup stack.  The object must contain
+ * a Close method.
+ *
+ * @param	anObject - a TAny pointer to the object to close.
+ * @return	None
+ */
+	{
+	((CObject*)anObject)->Close();
+	}
+
+CHWPort* CHWPort::NewL(TUint aUnit)
+/**
+ * This method is used by the factory object to create the new CHWPort instances.
+ * After newing the CHWPort object, the buffer is created, roles and signals are defaulted,
+ * and names are initialized.
+ *
+ * @param	aUnit - the unit to create.
+ *
+ * @return	A pointer to the created object
+ */
+	{
+
+	LOGTEXTREL(_S8("Loopback:NewL: Called"));
+	LOGTEXTREL2(_L8("Loopback:NewL:  Unit %d..."), aUnit);
+
+	CHWPort *p=new(ELeave) CHWPort;
+	TCleanupItem closePort(CloseObject,p);
+
+	CleanupStack::PushL(closePort);
+
+
+	p->iBuf=HBufC8::NewL(KBufferGrowthIncrement);
+	p->iBufSize=KBufferGrowthIncrement;
+	p->iPtr.Set((TUint8*)p->iBuf->Ptr(),0,KBufferGrowthIncrement);
+
+	p->iRole = ECommRoleDTE;
+	p->iSignals = 0;		// start with no signals asserted.
+	p->iSignalMask = 0;		// Prevents any spurrious notifications, etc.
+
+	TName name;
+	name.Format(_L("%d"),aUnit);
+	p->SetName(&name);
+	p->iPortName = aUnit;
+#if defined (_DEBUG_CONSOLE_)
+	name.Format(_L("Comm::%d"),aUnit);
+	p->iConsole.SetTitle(name);
+#if defined (_DEBUG_DEVCOMM)
+	p->iDumper=CCommDebugDumper::NewL(p->iConsole);
+#endif
+#endif
+	CleanupStack::Pop();
+
+	return p;
+	}
+
+void CHWPort::SetLoopbackPort(CHWPort* aHWPort)
+/**
+ * This method sets up the loopback port member of the CHWPort.  It is used after
+ * both ports have been created (NewL).  This allows each port to "know" to whom he is
+ * connected.
+ *
+ * @param	aHWPort - the port which THIS port should be connected to.
+ *
+ * @return	None
+ */
+	{
+	if (aHWPort != NULL)
+		{
+		LOGTEXT3(_L8("SetLoopbackPort:  Unit %d-%d"), iPortName, aHWPort->iPortName);
+		}
+	else
+		{
+		LOGTEXT2(_L8("Loopback:SetLoopbackPort:  Unit %d..."), iPortName);
+		}
+
+	// Now set up the loopback
+	iLoopbackPort=aHWPort;
+
+	}
+
+
+void CHWPort::CheckSigsAndCompleteRead()
+/**
+ * This method checks the configuration of the port and the current state of the signals
+ * and determines if the read should even attempt to be completed.  Based on configurations,
+ * reads can fail or be left pending (KConfigObeyXXX).  This method calls TryToCompleteRead
+ * if all configuration and signal state allows the read to continue.
+ *
+ * Note that this method is called to either complete a pended read (from WriteBuf) or
+ * to complete an incoming read request (from StartRead).  Also note that this routine does
+ * not check the fail flags.  These flags are checked when the read is first posted and
+ * when the signals are first asserted/deasserted.  It is unnecessary (and maybe even undesirable)
+ * to check the fail flags here.
+ *
+ * @param	None
+ *
+ * @return	None
+ */
+	{
+	LOGI(_L8("CheckSigsAndCompleteRead:%04d %x \"%S\""));
+
+	LOGTEXT2(_L8("Loopback:CheckSigsAndCompleteRead:  Unit %d..."), iPortName);
+
+
+	// At this point, we must check the config flags and signals.  If we have been
+	// configured to obey signals, then we must pend the read and not complete it.
+	// Note that the iReadPending flag was set in StartRead().
+	if (ECommRoleDTE == iRole)
+		{
+		if (((iConfig().iHandshake & KConfigObeyDCD) && (!(iSignals & KSignalDCD))) ||
+			((iConfig().iHandshake & KConfigObeyDSR) && (!(iSignals & KSignalDSR))))
+			{
+			LOGI(_L8("CheckSigsAndCompleteRead DTE:%04d %x \"%S\""));
+			return;
+			}
+		}
+	else if (ECommRoleDCE == iRole)
+		{
+		if ((iConfig().iHandshake & (KConfigObeyDTR)) && (!(iSignals & (KSignalDTR))))
+			{
+			LOGI(_L8("CheckSigsAndCompleteRead DCE:%04d %x \"%S\""));
+			return;
+			}
+		}
+
+
+	// Can the request be satisfied now?  Note that we call TryToCompleteRead on THIS instance.
+	// This means that CheckSigsAndCompleteRead must be called on the desired instance.
+	TryToCompleteRead();
+	LOGI(_L8("CheckSigsAndCompleteRead:%04d %x \"%S\""));
+	}
+
+
+void CHWPort::StartRead(const TAny* aClientBuffer,TInt aLength)
+/**
+ * This method queues a read operation to the driver.  If the read length is zero (which is a
+ * special case used during initialization) the read completes immediately without being
+ * concerned about any of the configuration flags.  Failure flags are checked in this routine.
+ * If the port has been configured to fail (KConfigFailXXX) and the signals are NOT asserted,
+ * then the read will fail immediately with KErrCommsLineFail.  Obey flags are not handled here,
+ * but are handled in a different method.  The code was structured in this manner so that the
+ * Obey flags are checked any time a read is attempted to be completed, not just when the read
+ * is initially sent to the driver.  This is needed because reads can pend and before the reads
+ * are completed the signal state could change.
+ *
+ * @param	aClientBuffer	- a TAny * to the buffer into which data is placed.
+ * @param	aLength			- the length of the buffer.  If the length is less than zero the
+ *                            read can be completed with less than length bytes available.  If
+ *                            a positive length value is specified, the read will not complete
+ *                            until length bytes have been read.
+ *
+ * @return	None
+ */
+	{
+
+	//	DEBUG_TRACE((iConsole.Write(_L("DoRead \n\r"))));
+	LOGI(_L8("LOGIStartRead:%04d %x \"%S\""));
+
+	LOGTEXT2(_L8("Loopback:StartRead:  Unit %d..."), iPortName);
+
+	// Because a length of zero is a special case, we will complete this without
+	// worries about the config/fail flags.
+	if(aLength==0)
+		{
+		ReadCompleted(KErrNone);
+
+		LOGI(_L8("StartRead 0:%04d %x \"%S\""));
+
+		return;
+		}
+
+
+	// At this point, we must check the config flags and signals.  If we have been
+	// configured to fail operations if certain signals are not set, then we must
+	// fail the read.
+	if (ECommRoleDTE == iRole)
+		{
+		if (((iConfig().iHandshake & KConfigFailDCD) && (!(iSignals & KSignalDCD))) ||
+			((iConfig().iHandshake & KConfigFailDSR) && (!(iSignals & KSignalDSR))))
+			{
+			ReadCompleted(KErrCommsLineFail);
+			LOGI(_L8("XtartRead DTE:%04d %x \"%S\""));
+			return;
+			}
+		}
+	else if (ECommRoleDCE == iRole)
+		{
+		if ((iConfig().iHandshake & (KConfigFailDTR)) && (!(iSignals & (KSignalDTR))))
+			{
+			ReadCompleted(KErrCommsLineFail);
+			LOGI(_L8("StartRead DCE:%04d %x \"%S\""));
+			return;
+			}
+		}
+
+
+	// At this point, we set up for the read.  If the obey flags later don't let us
+	// complete the read, having this work done is vital.
+	iReadOneOrMore=EFalse;
+	if(aLength<0)
+		{
+		aLength=-aLength;
+		iReadOneOrMore=ETrue;
+		}
+
+	iBytesWritten=0;
+	iPendingLength=aLength;
+	iClientReadBuffer=aClientBuffer;
+	iReadPending=ETrue;
+
+	// Later code will assert iLoopback, so check it now to avoid crashes during startup.
+	// The CheckSigsAndCompeteRead method will actually process the Obey flags.
+	if (iLoopbackPort != NULL)
+		{
+		CheckSigsAndCompleteRead();
+		}
+
+	LOGI(_L8("StartRead:%04d %x \"%S\""));
+	}
+
+void CHWPort::ReadCancel()
+/**
+ * Cancel a pending read and complete it with KErrCancel.  The handling of the CActive class
+ * will by default complete any outstanding read with KErrCancel, but it is cleaner to handle
+ * that processing here.
+ *
+ * @param	None
+ *
+ * @return	None
+ */
+	{
+	LOGI(_L8("ReadCancel:%04d %x \"%S\""));
+
+	LOGTEXT2(_L8("Loopback:ReadCancel:  Unit %d..."), iPortName);
+
+	iReadPending=EFalse;
+
+	ReadCompleted(KErrCancel);
+
+	}
+
+
+TInt CHWPort::WriteBuf(const TAny* aClientBuffer,TInt aLength, TBool& aIssueComplete)
+/**
+ * This method writes the client buffer to the loopback port.  It must take into consideration
+ * all of the KConfigFailXXX and KConfigObeyXXX flags that might have been specified in the
+ * configuration of the port.  Any fail configuration results with writes failing with the
+ * KErrCommsLineFail error.  This method is used both when the write is originally posted to
+ * the driver and when trying to finish any write that was pended by the driver based on the
+ * Obey flags.  This routine does not actually complete the read, but returns a value with
+ * which the caller should complete the read.  Because this routine can also leave the operation
+ * pended, it uses the aIssueComplete boolean to tell the caller whether or not to complete the
+ * read.
+ *
+ * @param	aClientBuffer	- a TAny * to the buffer which contains the data to write
+ * @param	aLength			- the length of the buffer.
+ * @param	aIssueComplete	- a reference to a boolean used to indicate to the caller if the
+ *                            write operation should be completed.
+ *							- ETrue  - Caller should issue the WriteCompleted with returned
+ *							           result code.
+ *							- EFalse - Caller shoud not issue the WriteCompleted.
+ *
+ * @return	KErrNone		- Everything is okay
+ * @return	KErrCommsLineFail  - Write failed based on signal state and configuration.
+ * @return	Varies			- Non-Zero return from Kernel calls
+ */
+
+	{
+	TInt res=KErrNone;
+
+	LOGI(_L8("WriteBuf:%04d %x \"%S\""));
+
+	// Plan on completeing, so set aIssueComplete to TRUE.
+	aIssueComplete = ETrue;
+
+
+	LOGTEXT3(_L8("Config 0x%x  Sigs 0x%x"), iConfig().iHandshake, iSignals);
+
+	// At this point, we must check the config flags and signals.  Note that Fail
+	// flags always take precedence over Obey flags.
+	if (ECommRoleDTE == iRole)
+		{
+		// if we are configured to fail if CD, DSR, or CTS are not present, then we
+		// must fail the write here.
+		if (((iConfig().iHandshake & KConfigFailDCD) && (!(iSignals & KSignalDCD))) ||
+			((iConfig().iHandshake & KConfigFailDSR) && (!(iSignals & KSignalDSR))) ||
+			((iConfig().iHandshake & KConfigFailCTS) && (!(iSignals & KSignalCTS))))
+			{
+			LOGTEXT2(_L8("WriteBuf:  DTE Failure  Unit %d..."), iPortName);
+			LOGTEXT3(_L8("...Config 0x%x  Sigs 0x%x..."), (iConfig().iHandshake), iSignals);
+			res=KErrCommsLineFail;
+			LOGI(_L8("WriteBuf:%04d %x \"%S\""));
+			return res;
+			}
+
+		// At this point, we must check the config flags and signals.  If we have been
+		// configured to obey signals, then we must pend the write and not complete the
+		// write.
+		if (((iConfig().iHandshake & KConfigObeyDCD) && (!(iSignals & KSignalDCD))) ||
+			((iConfig().iHandshake & KConfigObeyDSR) && (!(iSignals & KSignalDSR))) ||
+			((iConfig().iHandshake & KConfigObeyCTS) && (!(iSignals & KSignalCTS))))
+			{
+			LOGTEXT2(_L8("WriteBuf:  DTE Pended:  Unit %d..."), iPortName);
+			iWritePending = ETrue;
+			iClientWriteBuffer = aClientBuffer;
+			iWritePendingLength = aLength;
+			aIssueComplete = EFalse;
+			res=KErrNone;
+			LOGI(_L8("WriteBuf:%04d %x \"%S\""));
+			return res;
+			}
+		}
+	else if (ECommRoleDCE == iRole)
+		{
+		// if we are configured to fail when DTR or RTS are not present, then
+		// fail the write here.
+		if (((iConfig().iHandshake & KConfigFailRTS) && (!(iSignals & KSignalRTS))) ||
+			((iConfig().iHandshake & KConfigFailDTR) && (!(iSignals & KSignalDTR))))
+			{
+			LOGTEXT2(_L8("WriteBuf:  DCE Failure...  Unit %d..."), iPortName);
+			LOGTEXT3(_L8("...Config 0x%x  Sigs 0x%x..."), (iConfig().iHandshake), iSignals);
+			res=KErrCommsLineFail;
+			LOGI(_L8("WriteBuf:%04d %x \"%S\""));
+			return res;
+			}
+
+		// At this point, we must check the config flags and signals.  If we have been
+		// configured to obey signals, then we must pend the write and not complete the
+		// write.
+		if (((iConfig().iHandshake & KConfigObeyRTS) && (!(iSignals & KSignalRTS))) ||
+			((iConfig().iHandshake & KConfigObeyDTR) && (!(iSignals & KSignalDTR))))
+			{
+			LOGTEXT2(_L8("WriteBuf:  DCE Pended:  Unit %d..."), iPortName);
+			LOGTEXT3(_L8("...Config 0x%x  Sigs 0x%x..."), (iConfig().iHandshake), iSignals);
+			iWritePending = ETrue;
+			iClientWriteBuffer = aClientBuffer;
+			iWritePendingLength = aLength;
+			aIssueComplete = EFalse;
+			res=KErrNone;
+			LOGI(_L8("WriteBuf:%04d %x \"%S\""));
+			return res;
+			}
+		}
+
+	// Resize the receiving buffer if it is not big enough or it is
+	// more than twice as big as it needs to be for this write.
+	if ((iPtr.Length() + aLength) > iBufSize)
+		{
+		// New buffer size will be just big enough to fit the existing
+		// data + new data, rounded up to nearest multiple of
+		// KBufferGrowthIncrement.
+		TInt newBufSize;
+
+		if (((iPtr.Length() + aLength) % KBufferGrowthIncrement) == 0)
+			{
+			newBufSize = iPtr.Length() + aLength;
+
+			}
+		else
+			{
+			// New buffer size = length of data already in the buffer
+			//					 + length of data to be added to the buffer
+			//					 + round up to next KBufferGrowthIncrement
+			newBufSize = iPtr.Length() + aLength
+						 + KBufferGrowthIncrement - ((iPtr.Length() + aLength)%KBufferGrowthIncrement);
+			}
+
+
+		// Need to resize the buffer
+		// Note: iBuf should not be deleted, buffer may be emptied
+		//       later if this ReAllocL fails and tmpBuffer remains NULL.
+	    HBufC8* tmpBuffer = NULL;
+	    TRAP(res, tmpBuffer = iBuf->ReAllocL(newBufSize));
+
+		if (tmpBuffer == NULL || res != KErrNone)
+			{
+			// Could not reallocate the buffer (maybe not enough
+			// memory) so write request fails.
+			iWritePending = EFalse;
+
+			// return with an Issue complete and an error message
+			aIssueComplete = ETrue;
+			LOGI(_L8("WriteBuf:%04d %x \"%S\""));
+			return KErrNoMemory;
+			}
+
+		iBufSize = newBufSize;
+		iBuf = tmpBuffer;
+
+
+		// Note: the aIssueComplete flag has already been set to ETrue
+		// so, don't need to set it again here.
+		iWritePending = EFalse;
+		}
+
+	// Fill the receiving buffer
+	if (aLength!=0)
+		{
+		// point to where the new data will be located in our buffer
+		TPtr8 ptrWriteLocation((TUint8*)(iBuf->Ptr() + iPtr.Length()), aLength);
+
+		res = IPCRead(aClientBuffer,ptrWriteLocation);  // Must go through correct port
+
+		LOGTEXT2(_L8("Read \"%S\""), &ptrWriteLocation);
+		if(res!=KErrNone)
+			{
+			LOGI(_L8("WriteBuf:%04d %x \"%S\""));
+			return res;
+			}
+
+		// memory leak detection : we create a single memory leak, if our input buffer contains
+		// special token string:
+		_LIT8(KMemLeakToken, "--MemoryLeakToken--");
+		TInt nRetVal = ptrWriteLocation.Compare(KMemLeakToken);
+		if (nRetVal == 0)
+			{
+            // coverity [returned_pointer]
+			// coverity [memory_leak] - create a memory leak intentionally, look at the comments above 
+            TInt* pArr = new TInt[1024]; // deliberatly causing a memory leak here...
+			}
+
+		// set the iPtr to point to the new iBuffer and keep a record of data length
+		iPtr.Set((TUint8*)iBuf->Ptr(), iPtr.Length() + aLength, iBufSize);
+		}
+	// Since we are completing a write on THIS instance, see if we can complete a read on
+	// the loopback port.  A read could have been left pending.
+	if (iLoopbackPort != NULL)
+		{
+		iLoopbackPort->CheckSigsAndCompleteRead();
+		}
+
+	return res;
+	}
+
+void CHWPort::TryToCompleteRead()
+/**
+ * This method attempts to complete reads, either as they are initially issued or at a later
+ * time because they were pended when initially issued.  Data is moved from the buffer associated
+ * with this port to a buffer that was supplied by the client when the read is issued.  The
+ * read is completed when either the client buffer is full or when some data has been written
+ * to the client buffer and the iReadOneOrMore member set.  This member data is set when then
+ * initial read was passed a negative length value.  Using this method allows a read to complete
+ * without filling the entire buffer.
+ *
+ * @param	None
+ *
+ * @return	None
+ */
+
+	{
+	LOGI(_L8("TryToCompleteRead:%04d %x \"%S\""));
+
+	LOGTEXT2(_L8("Loopback:TryToCompleteRead: Unit %d ..."), iPortName);
+
+	ASSERT(iLoopbackPort);
+	TPtr8& refLoopBackiPtr = iLoopbackPort->iPtr; // Loopback listening port's buffer
+
+	// Is there a Data Available notification pending?
+	TInt s = refLoopBackiPtr.Length();
+	if(iDataNotify)
+		{
+		NotifyDataAvailableCompleted(KErrNone);
+		iDataNotify=EFalse;
+		}
+
+	// Is there a Read Pending?
+	if(!iReadPending)
+		return;
+
+	// If there's somthing in the buffer then try to copy that...
+	TInt stillToBeWritten=iPendingLength-iBytesWritten;
+	TInt len=Min(s, stillToBeWritten);
+
+	// only point to the data we are going to read
+	TPtrC8 ptr(refLoopBackiPtr.Ptr(), len);
+
+	LOGTEXT2(_L8("Write \"%S\""), &ptr);
+
+	// Search for terminator characters
+	TBool terminatorFound = EFalse;
+	const TText8* terminators = iConfig().iTerminator;
+	TInt termNum;
+	for (termNum=0; termNum < iConfig().iTerminatorCount; ++termNum)
+		{
+		TInt termIndex = ptr.Locate(terminators[termNum]);
+		if (termIndex >= 0)
+			{
+			// Found the terminator; reduce the length of ptr and search for the next one
+			len = termIndex + 1;
+			ptr.Set(refLoopBackiPtr.Ptr(), len);
+			terminatorFound = ETrue;
+			}
+		}
+
+	TInt res=IPCWrite(iClientReadBuffer,ptr,iBytesWritten);
+	if(res == KErrNone)
+		{
+		// Delete the read data from the buffer
+		ASSERT(refLoopBackiPtr.Length() >= len);
+		TPtrC8 source = refLoopBackiPtr.Right(refLoopBackiPtr.Length() - len);
+		// a memory move occurs on each read. This must be inefficient
+		refLoopBackiPtr.Copy(source);
+		iBytesWritten+=len;
+		}
+
+	if((iBytesWritten==iPendingLength)||(iReadOneOrMore&&(s>=1))||terminatorFound)
+		{
+		LOGTEXT2(_L8("Loopback:TryToCompleteRead: Completing Read  Unit %d ..."), iPortName);
+		iReadPending=EFalse;
+		// a complete read has succeeded, now update the Ports memory usage
+		iLoopbackPort->UpdatePortResources();
+		ReadCompleted(res);
+		}
+	}
+
+void CHWPort::UpdatePortResources()
+/**
+ * This method recalculate how much memory is actually required by a ports write buffer
+ *
+ * @param Not used
+ *
+ * @return None
+ */
+	{
+	LOGTEXT2(_L8("Loopback:UpdatePortResources: Unit %d ..."), iPortName);
+
+
+	TInt newBufSize = 0;
+
+	// it is possible to have a length of 0 therefore we leave the buffer size
+	// to KBufferGrowthIncrement
+	if(iPtr.Length() > 0)
+		{
+		newBufSize = iPtr.Length() + KBufferGrowthIncrement - ((iPtr.Length()) % KBufferGrowthIncrement);
+		}
+	else
+		{
+		// the minimum buffer size is KBufferGrowthIncrement
+		newBufSize = KBufferGrowthIncrement;
+		}
+
+	// if the buffer needs changing then resize it otherwise leave as is
+	if(newBufSize != iBufSize)
+		{
+		TInt res = KErrNone;
+		// Shrink the buffer to the new size calculated above. If this fails we return leaving memory as it was
+		HBufC8* tmpBuffer = NULL;
+	    TRAP(res, tmpBuffer = iBuf->ReAllocL(newBufSize));
+
+		if (tmpBuffer == NULL || res != KErrNone)
+			{
+	   		// could not resize the buffer - return without modifying anything
+	   		return;
+			}
+
+		iBuf = tmpBuffer;
+		iBufSize = newBufSize;
+
+		// update the public interface to our buffer.
+		iPtr.Set((TUint8*)iBuf->Ptr(), iPtr.Length(), iBufSize);
+		}
+	}
+
+TInt CHWPort::QueryReceiveBuffer(TInt& aLength) const
+/**
+ * This method returns the length of the buffer associated with this instance of the
+ * port.
+ *
+ * @param	aLength			- a reference to return the length of the buffer.
+ *
+ * @return	KErrNone
+ */
+	{
+	LOGTEXT2(_L8("Loopback:QueryReceiveBuffer:  Unit %d..."), iPortName);
+
+	aLength=iPtr.Length();
+	return KErrNone;
+	}
+
+void CHWPort::ResetBuffers(TUint)
+/**
+ * This method resets the buffer used by this loopback port
+ *
+ * @note Note that most ResetBuffers methods derived from CPort allow a parameter for flags.
+ * This ResetBuffers method does not.
+ *
+ * @param	Not Used
+ *
+ * @return	None
+ */
+	{
+
+	LOGI(_L8("ResetBuffers:%04d %x \"%S\""));
+
+	LOGTEXT2(_L8("Loopback:ResetBuffers:  Unit %d..."), iPortName);
+
+	// reset the length of data to zero and update port resources
+	iPtr.Set((TUint8*)iBuf->Ptr(), 0, iBufSize);
+
+	// this method will perform any memory re-sizing required on the port
+	UpdatePortResources();
+	}
+
+void CHWPort::StartWrite(const TAny* aClientBuffer,TInt aLength)
+/**
+ * This method queues a write operation to the driver.  This method is simply the outside
+ * interface to the class for writing data.  It calls a private method to actually process
+ * the write.  StartWrite passes a reference to a boolean value to the private routine that
+ * indicates whether or not StartWrite should issue a WriteCompleted response.  This is necessary
+ * because some combinations of signals and configuration can cause writes to be pended and
+ * thus NOT completed.
+ *
+ * @param	aClientBuffer	- a TAny * to the buffer into which data should be read.
+ * @param	aLength			- the length of the data to be written.
+ *
+ * @return	None
+ */
+	{
+
+	LOGI(_L8("StartWrite:%04d %x \"%S\""));
+
+	LOGTEXT2(_L8("Loopback:StartWrite:  Unit %d..."), iPortName);
+
+	// memory leak detection mechanism:
+	// if special write buffer token is received is received, we create memory leak
+	// this condition is triggered, when working with "te_C32_leakdetection.script"
+
+	DEBUG_TRACE((iConsole.Write(_L("DoWrite \n\r"))));
+	TBool issueComplete = ETrue;
+
+	TInt res = KErrNone;
+
+	if((iPtr.Length() + aLength) > KMaxBufferSize)
+		{
+		// we are exceeding our buffer growth size. Do not process the
+		// write message
+		iWritePending = EFalse;
+		res = KErrNoMemory;
+		}
+	else
+		{
+		res = WriteBuf(aClientBuffer,aLength, issueComplete);
+		}
+
+	// Only complete the write if allowed to by WriteBuf.
+	if (issueComplete)
+		{
+		LOGTEXT2(_L8("Loopback:StartWrite:  Completing Write Unit %d..."), iPortName);
+		LOGTEXT2(_L8("Loopback:StartWrite:  Completing Write Unit %d..."), res);
+		WriteCompleted(res);
+		}
+
+	LOGI(_L8("StartWrite:%04d %x \"%S\""));
+
+	}
+
+void CHWPort::WriteCancel()
+/**
+ * This method cancels a pending write and issues a WriteCompleted with the result
+ * KErrCancel.  If no writes are pending, then this method simply returns.
+ *
+ * @param	None
+ *
+ * @return	None
+ */
+
+	{
+
+	LOGI(_L8("WriteCancel:%04d %x \"%S\""));
+
+	LOGTEXT2(_L8("Loopback:WriteCancel:  Unit %d..."), iPortName);
+
+
+	// if there is a pending write (which could happen with the obey
+	// flags), then we have to cancel the write.
+	if (iWritePending)
+		{
+		iWritePending = EFalse;
+		WriteCompleted(KErrCancel);
+		}
+	}
+
+void CHWPort::Break(TInt /* aTime */)
+/**
+ * This method is currently not implemented in the loopback driver as breaks are
+ * not supported.
+ *
+ * @param	Not Used
+ *
+ * @return	None
+ */
+
+//
+// Queue a Break
+//
+	{}
+
+void CHWPort::BreakCancel()
+/**
+ * This method is currently not implemented in the loopback driver as breaks are
+ * not supported.
+ *
+ * @param	None
+ *
+ * @return	None
+ */
+//
+// Cancel a pending break
+//
+	{}
+
+TInt CHWPort::GetConfig(TDes8& aDes) const
+/**
+ * This gets the current configuration from the loopback driver.
+ *
+ * @param	aDes	- a TDes8 reference to copy the configuration into.
+ *
+ * @return	KErrNone
+ */
+	{
+
+	LOGI(_L8("GetConfig:%04d %x \"%S\""));
+
+	LOGTEXT2(_L8("Loopback:GetConfig:  Unit %d..."), iPortName);
+
+	aDes.Copy(iConfig);
+	return KErrNone;
+	}
+
+TInt CHWPort::SetConfig(const TDesC8& aDes)
+/**
+ * This sets the current configuration for the loopback driver.  Note that
+ * no error checking is done when setting the configuration.
+ *
+ * @param	aDes	- a TDes8 reference to copy the configuration from.
+ *
+ * @return	KErrNone
+ */
+	{
+
+	LOGI(_L8("SetConfig:%04d %x \"%S\""));
+
+	LOGTEXT2(_L8("Loopback:SetConfig:  Unit %d..."), iPortName);
+
+	iConfig.Copy(aDes);
+	return KErrNone;
+	}
+
+TInt CHWPort::GetCaps(TDes8& aDes)
+/**
+ * This gets the supported capabilities from the loopback driver.  The actual capabilities of
+ * the driver will vary based on the role the port is playing (DCE or DTE).  The loopback driver
+ * supports capabilities via TCommCapsV01 and TCommCapsV02.
+ *
+ * @param	aDes				- a TDes8 reference to copy the capabilities into.
+ *
+ * @return	KErrNone			- Everything is okay.
+ * @return	KErrNotSupported	- The length of the descriptor passed to this method indicates a
+ *                                capabilities structure which we don't support.
+ */
+	{
+
+	LOGI(_L8("GetCaps:%04d %x \"%S\""));
+
+	LOGTEXT2(_L8("Loopback:GetCaps:  Unit %d..."), iPortName);
+
+	if(aDes.Length()==sizeof(TCommCapsV01))
+		{
+		TCommCapsV01* commcaps=(TCommCapsV01*)(aDes.Ptr());
+
+		// We've got all of these
+		commcaps->iRate=0x3fffff;
+		commcaps->iDataBits=0xf;
+		commcaps->iStopBits=0x3;
+		commcaps->iParity=0x1f;
+		commcaps->iFifo=0x1;
+
+		if (ECommRoleDTE == iRole)
+			{
+			commcaps->iHandshake= KCapsObeyCTSSupported | KCapsFailCTSSupported |
+			                      KCapsObeyDSRSupported | KCapsFailDSRSupported |
+			                      KCapsObeyDCDSupported | KCapsFailDCDSupported;
+			}
+		else
+			{
+			commcaps->iHandshake= KCapsObeyRTSSupported | KCapsObeyDTRSupported;
+			}
+
+		commcaps->iSignals=0x3f;
+		commcaps->iSIR=0x0;
+		return KErrNone;
+		}
+	else if(aDes.Length()==sizeof(TCommCapsV02))
+		{
+		TCommCapsV02* commcaps=(TCommCapsV02*)(aDes.Ptr());
+	// We've got all of these
+		commcaps->iRate=0x3fffff;
+		commcaps->iDataBits=0xf;
+		commcaps->iStopBits=0x3;
+		commcaps->iParity=0x1f;
+		commcaps->iFifo=0x1;
+
+		if (ECommRoleDTE == iRole)
+			{
+			commcaps->iHandshake= KCapsObeyCTSSupported | KCapsFailCTSSupported |
+			                      KCapsObeyDSRSupported | KCapsFailDSRSupported |
+			                      KCapsObeyDCDSupported | KCapsFailDCDSupported |
+			                      KCapsFreeRTSSupported | KCapsFreeDTRSupported;
+			}
+		else
+			{
+			commcaps->iHandshake= KCapsObeyRTSSupported | KCapsObeyDTRSupported;
+			}
+
+		commcaps->iSignals=0x3f;
+		commcaps->iSIR=0x0;
+		commcaps->iNotificationCaps=KNotifySignalsChangeSupported;
+		commcaps->iRoleCaps=0x0;
+		return KErrNone;
+		}
+	else
+		return KErrNotSupported;
+	}
+
+TInt CHWPort::SetServerConfig(const TDesC8& aDes)
+/**
+ * This sets the current server configuration for the loopback driver.  The loopback driver
+ * stores this information but does nothing with it.
+ *
+ * @param	aDes	- a TDes8 reference to copy the configuration from.
+ *
+ * @return	KErrNone
+ */
+	{
+
+	LOGI(_L8("SetServerConfig:%04d %x \"%S\""));
+
+	LOGTEXT2(_L8("Loopback:SetServerConfig:  Unit %d..."), iPortName);
+
+	iServerConfig.Copy(aDes);
+	return KErrNone;
+	}
+
+TInt CHWPort::GetServerConfig(TDes8& aDes)
+/**
+ * This gets the current server configuration for the loopback driver.  The loopback driver
+ * stores this information but does nothing with it other than return it here.
+ *
+ * @param	aDes	- a TDes8 reference to copy the configuration to.
+ *
+ * @return	KErrNone
+ */
+	{
+
+	LOGI(_L8("GetServerConfig:%04d %x \"%S\""));
+
+	LOGTEXT2(_L8("Loopback:GetServerConfig:  Unit %d..."), iPortName);
+
+	aDes.Copy(iServerConfig);
+	return KErrNone;
+	}
+
+TInt CHWPort::GetSignals(TUint& aSignals)
+/**
+ * This method retrieves the current setting of the signals for THIS port.
+ *
+ * @param	aSignals	- A reference to a TUint to return the signal settings.
+ *
+ * @return	KErrNone
+ */
+	{
+
+	LOGI(_L8("GetSignals:%04d %x \"%S\""));
+
+	LOGTEXT3(_L8("Loopback:GetSignals:  Unit %d... Sigs 0x%x"), iPortName, iSignals);
+
+	aSignals=iSignals;
+	return KErrNone;
+	}
+
+TInt CHWPort::SetSignalsToMark(TUint aSignals)
+/**
+ * This method asserts the signals specified by the parameter aSignals.  In addition to
+ * simply asserting the signals, this routine will complete any signal notification requests
+ * that are outstanding.  After handling any signal notification requests, this routine will
+ * also update the signal state of it's partner loopback port.  The other port's signals are
+ * updated only if they are considered output signals for the role that this port is playing.
+ * For example, assume that a DTE port is setting the RTS signal.  It determines that RTS is
+ * an output signal for a DTE port, so it must propagate the signal to the DCE by calling
+ * this routine on the DCE instance of the port.  When the DCE instance runs, it sets RTS, then
+ * determines that RTS is NOT a DCE output signal, so it does NOT attempt to propagate the signal.
+ * It is NOT an error for a DCE port to call this routine with DTE output signals.  In fact,
+ * this behaviour is required in order to propagate signal settings from one port to the other.
+ *
+ * After propagating the signal settings, this method will attempt to complete reads and writes
+ * that have been outstanding, based on the changed signals and the configuration of the
+ * port.  For example, say that the DTE port had been configured to Obey CTS (i.e., to flow control
+ * when CTS is not asserted).  When the DCE asserts CTS, this signal would be propagated to the
+ * DTE port.  Then the DTE port would check the current configuration and note that CTS has
+ * been asserted.  At this point, there could be operations which were pended (because of the
+ * lack of CTS) that should be attempted.  This method will attempt these operations.
+ *
+ *
+ * @param	aSignals	- a bitmask specifying which signals to assert (See definition
+ *                        of KSignalDCD, KSignalCTS, etc. for bit values).
+ *
+ * @return	KErrNone
+ */
+	{
+
+	TBool attemptRead = EFalse;
+	TBool attemptWrite = EFalse;
+	TUint sigsChanged;
+	TUint tmpSigs;
+
+
+
+	LOGI(_L8("SetSignalsToMark:%04d %x \"%S\""));
+
+
+	LOGTEXT3(_L8("Loopback:SetSignalsToMark:  Unit %d...Sigs 0x%x"), iPortName, aSignals);
+
+	// If no signals are passed in to set, then get out of here.  This is possible when
+	// the upper layers have used the RComm::SetSignals interface.  This interface is used
+	// to both space and mark signals with a single call.  Frequently, this interface is used
+	// with one of the two masks set to zero.
+	if (!aSignals)
+		return KErrNone;
+
+	tmpSigs = iSignals;
+	iSignals |= aSignals;
+
+	// sigsChanged contains only the signals changed by this operation, no history is contained.
+	sigsChanged = (iSignals ^ tmpSigs);
+
+	// Only complete notifications if the changed sigs were specified in the mask.
+	if (sigsChanged & iSignalMask)
+		{
+		// Notify people that the signals have changed.
+		// Note that the KSignalChanged bits are stored in sigsChanged, but passed.
+		// The tmpSigs value has the state of all the signals that they were interested in
+		// as specified by the iSignalMask and the Changed flags.
+        tmpSigs = (((sigsChanged & iSignalMask) * KSignalChanged) | (iSignals & iSignalMask));
+		SignalChangeCompleted(tmpSigs, KErrNone);
+		// Reset signal mask, another NotifiySignalChange is necessary to get any
+		// more signal information out of the driver.
+		iSignalMask = 0;
+		}
+
+	// if I'm a DTE port and the signals changed are DTE outputs, then
+    // I've got to figure out what signals to change on the DCE side (as inputs).
+ 	// else if I'm a DCE port and the signals changed are DCE outputs, then
+    // I've got to figure out what DTE signals I've got to change.
+    if ((ECommRoleDTE == iRole) && (sigsChanged & KSignalDTEOutputs))
+		{
+		if (iLoopbackPort)
+			iLoopbackPort->SetSignalsToMark(aSignals);
+        }
+	else if ((ECommRoleDCE == iRole) && (sigsChanged  & KSignalDCEOutputs))
+		{
+		if (iLoopbackPort)
+			iLoopbackPort->SetSignalsToMark(aSignals);
+		}
+
+
+	// if we dropped DCD, CTS, or DSR, then we need to complete any outstanding writes and
+    // reads on the DTE port if they have been configured to fail.
+	// We only look at the DTE input signals to see if the roles, etc. change.
+	if (ECommRoleDTE == iRole)
+		{
+
+		// DCD and DSR affect both Reads and Writes, so we'll want to attempt both
+		// reads and writes later.
+		if (((iConfig().iHandshake & KConfigObeyDCD) && (sigsChanged & KSignalDCD)) ||
+			((iConfig().iHandshake & KConfigObeyDSR) && (sigsChanged & KSignalDSR)))
+			{
+			attemptRead = attemptWrite = ETrue;
+			}
+
+		// CTS has NO effect on the Reads, so don't attempt to complete any reads here.
+		// No need to do this if we executed the above if ...
+		else if (((iConfig().iHandshake & KConfigObeyCTS) && (sigsChanged & KSignalCTS)))
+			{
+			attemptWrite = ETrue;
+			}
+		}
+	// if the DTE dropped the signals (RTS and DTR) the complete actions for the DCE
+	// only look at the DCE input signals.
+	else if (ECommRoleDCE == iRole)
+		{
+		// if DTR has changed, then we need to try to complete both reads and writes.
+		if (((iConfig().iHandshake & KConfigObeyDTR) && (sigsChanged & KSignalDTR)))
+			{
+			attemptRead = attemptWrite = ETrue;
+			}
+		// if RTS has changed, only attempt the Writes, RTS does not effect Reads.
+		// No need to do this if we executed the above if ...
+		else if (((iConfig().iHandshake & KConfigObeyRTS) && (sigsChanged & KSignalRTS)))
+			{
+			attemptWrite = ETrue;
+			}
+
+		}
+
+    // Attempt to complete any writes if necessary.  Note that if we do a write for THIS
+	// port, it will attempt to complete a read for the Loopback port.  Because this method
+	// calls itself (on the other port), we can end up trying to complete reads and writes
+	// a couple of times.  By checking the pending flags for writes and reads, we should
+	// avoid this extra work (even though it probably would not hurt anything).
+	if ((attemptWrite) && (iWritePending))
+		{
+		TBool issueComplete = EFalse;
+		TInt res=WriteBuf(iClientWriteBuffer,iWritePendingLength, issueComplete);
+
+		if (issueComplete)
+			{
+			WriteCompleted(res);
+			}
+		}
+
+	// Attempt to complete any reads if necessary.  See comment above writes for information
+	// regarding the use of the pending flags.  Note that we call this on our own port.  This
+	// is by design.  If a write was attempted, then it called the the read completion on the
+	// other port.
+	if ((attemptRead) && (iReadPending))
+	{
+		CheckSigsAndCompleteRead();
+	}
+
+
+	return KErrNone;
+	}
+
+
+
+TInt CHWPort::SetSignalsToSpace(TUint aSignals)
+/**
+ * This method de-asserts the signals specified by the parameter aSignals.  In addition to
+ * simply de-asserting the signals, this routine will complete any signal notification requests
+ * that are outstanding.  After handling any signal notification requests, this routine will
+ * also update the signal state of it's partner loopback port.  The other ports signals are
+ * updated only if they are considered output signals for the role that this port is playing.
+ * For example, assume that a DTE port is deasserting the RTS signal.  It determines that RTS is
+ * an output signal for a DTE port, so it must propagate the signal to the DCE by calling
+ * this routine on the DCE instance of the port.  When the DCE instance runs, it clears RTS, then
+ * determines that RTS is NOT a DCE output signal, so it does NOT attempt to propagate the signal.
+ * It is NOT an error for a DCE port to call this routine with DTE output signals.
+ * In fact, this behaviour is required in order to  * propagate signal settings from one port
+ * to the other.
+ *
+ * If signals are de-asserted, then it may be necessary to FAIL pending operations.  For example,
+ * if the port is configured to Fail if DCD is de-asserted and there is a read pending, this
+ * routine will complete the outstanding read with KErrCommsLineFail.
+ *
+ *
+ * @param	aSignals	- a bitmask specifying which signals to assert (See definition
+ *                        of KSignalDCD, KSignalCTS, etc. for bit values).
+ *
+ * @return	KErrNone
+ */
+	{
+	TBool completeRead = EFalse;
+	TBool completeWrite = EFalse;
+	TUint sigsChanged;
+	TUint tmpSigs;
+
+	LOGI(_L8("SetSignalsToSpace:%04d %x \"%S\""));
+
+	LOGTEXT3(_L8("Loopback:SetSignalsToSpace:  Unit %d...Sigs 0x%x"), iPortName, aSignals);
+
+
+	// If no signals are passed in to set, then get out of here.  This is possible when
+	// the upper layers have used the RComm::SetSignals interface.  This interface is used
+	// to both space and mark signals with a single call.  Frequently, this interface is used
+	// with one of the two masks set to zero.
+	if (!aSignals)
+		return KErrNone;
+
+
+	// iSignals is used to store the current state of the signals only, it does not
+	// include the changed masks.  This is so that history will not be reflected.
+	tmpSigs = iSignals;
+	iSignals &= ~aSignals;
+
+	// sigsChanged contains only the signals changed by this operation, no history is contained.
+	sigsChanged = (iSignals ^ tmpSigs);
+
+	// Only complete notifications if the changed sigs were specified in the mask.
+	if (sigsChanged & iSignalMask)
+		{
+		// Notify people that the signals have changed.
+		// Note that the KSignalChanged bits are stored in sigsChanged, but passed.
+		// The tmpSigs value has the state of all the signals that they were interested in
+		// as specified by the iSignalMask and the Changed flags.
+		tmpSigs = (((sigsChanged & iSignalMask) * KSignalChanged) | (iSignals & iSignalMask));
+		SignalChangeCompleted(tmpSigs, KErrNone);
+		// Reset signal mask, another NotifiySignalChange is necessary to get any
+		// more signal information out of the driver.
+		iSignalMask = 0;
+		}
+
+	// if I'm a DTE port and the signals changed are DTE outputs, then
+    // I've got to figure out what signals to change on the DCE side (as inputs).
+ 	// else if I'm a DCE port and the signals changed are DCE outputs, then
+    // I've got to figure out what DTE signals I've got to change.
+
+	// The DTE Role could be ignored safely (Req7) but as long as the test
+    // harness does not ever request notification, it won't matter.  This is
+    // for potential future use.
+    if ((ECommRoleDTE == iRole) && (sigsChanged & KSignalDTEOutputs))
+		{
+		if (iLoopbackPort)
+			iLoopbackPort->SetSignalsToSpace(aSignals);
+        }
+	else if ((ECommRoleDCE == iRole) && (sigsChanged & KSignalDCEOutputs))
+		{
+		if (iLoopbackPort)
+			iLoopbackPort->SetSignalsToSpace(aSignals);
+		}
+
+	// if we dropped DCD, CTS, or DSR, then we need to complete any outstanding writes and
+    // reads on the DTE port if they have been configured to fail.
+	// We only look at the DTE input signals to see if the roles, etc. change.
+	if (ECommRoleDTE == iRole)
+		{
+		// DCD and DSR affect both Reads and Writes, so we'll want to complete both
+		// reads and writes later.
+		if (((iConfig().iHandshake & KConfigFailDCD) && (sigsChanged & KSignalDCD)) ||
+			((iConfig().iHandshake & KConfigFailDSR) && (sigsChanged & KSignalDSR)))
+			{
+			completeRead = completeWrite = ETrue;
+			}
+
+		// CTS has NO effect on the Reads, so don't attempt to complete any reads here.
+		// No need to do this if we executed the above if ...
+		else if (((iConfig().iHandshake & KConfigFailCTS) && (sigsChanged & KSignalCTS)))
+			{
+			completeWrite = ETrue;
+			}
+		}
+	// if the DTE dropped the signals (RTS and DTR) the complete actions for the DCE
+	// only look at the DCE input signals.
+	else if (ECommRoleDCE == iRole)
+		{
+		// if DTR has changed, then we need to try to complete both reads and writes.
+		if (((iConfig().iHandshake & KConfigFailDTR) && (sigsChanged & KSignalDTR)))
+			{
+			completeRead = completeWrite = ETrue;
+			}
+		// if RTS has changed, only attempt the Writes, RTS does not effect Reads.
+		// No need to do this if we executed the above if ...
+		else if (((iConfig().iHandshake & KConfigFailRTS) && (sigsChanged & KSignalRTS)))
+			{
+			completeWrite = ETrue;
+			}
+		}
+
+	// Note:  We don't have to work with the Obey flags when we set signals.  The obey flags
+	// when something is set force future operations to be pended.  Operations currently pended
+	// or already completed don't have any effect.  If any of the signals were treated as Active
+	// Low (or Asserted means error condition) then we would have to attempt to complete
+	// reads or writes.
+	//
+
+	// if we need to complete the read and there is one pending, fail it.
+	if ((completeRead) && (iReadPending))
+		{
+		ReadCompleted(KErrCommsLineFail);
+		iReadPending = EFalse;
+		}
+
+	// if we need to complete writes, do it here.
+	if ((completeWrite) && (iWritePending))
+		{
+		WriteCompleted(KErrCommsLineFail);
+		iWritePending = EFalse;
+		}
+
+	return KErrNone;
+	}
+
+TInt CHWPort::GetReceiveBufferLength(TInt& /* aLength */) const
+/**
+ * This method is currently not implemented in the loopback driver.  Calling this
+ * method will return an error
+ *
+ * @param	Not Used
+ *
+ * @return	KErrNotSupported
+ */
+	{
+
+	LOGI(_L8("GetReceiveBufferLength:%04d %x \"%S\""));
+	return KErrNotSupported;
+	}
+
+TInt CHWPort::SetReceiveBufferLength(TInt /* aLength */)
+/**
+ * This method is currently not implemented in the loopback driver.  Calling this
+ * method will return an error
+ *
+ * @param	Not Used
+ *
+ * @return	KErrNotSupported
+ */
+	{
+	LOGI(_L8("SetReceiveBufferLength:%04d %x \"%S\""));
+	return KErrNotSupported;
+	}
+
+#ifdef _DEBUG_DEVCOMM
+// This code will not compile given current class structure, etc.  It is left here for
+// future reference.
+void CHWPort::DoDumpDebugInfo(const RMessage2 &aMessage)
+	{
+	TCommDebugInfoPckg d;
+	if (iRole==ECommRoleDTE)
+		iPort.DebugInfo(d);
+	else
+		iPortDCE.DebugInfo(d);
+	TRAPD(leave,aMessage.WriteL(0,d));		// trap but ignore leaves
+	aMessage.Complete(KErrNone);
+	}
+#endif
+
+void CHWPort::Destruct()
+/**
+ * This method is simply deletes this instance of the port, comitting sucide.
+ *
+ * @param	None
+ *
+ * @return	None
+ */
+	{
+	delete this;
+	}
+
+
+
+void CHWPort::NotifySignalChange(TUint aSignalMask)
+/**
+ * This method sets up a request to be notified when a signal change occurs on the specified
+ * signals.  Later operations will send a message to the requestor indicating the signal
+ * change.
+ *
+ * @param	aSignalMask		-	the signals that the caller is interested in monitoring.
+ *
+ * @return	None
+ */
+	{
+
+	LOGTEXT3(_L8("Loopback:NotifySignalChange:  Unit %d...Mask 0x%x"), iPortName, aSignalMask);
+
+	iSignalMask|=aSignalMask;
+
+	}
+
+
+void CHWPort::NotifySignalChangeCancel()
+/**
+ * This method cancels an outstanding request to be notified when a signal change occurs.  Any
+ * outstanding signal change request will be completed with KErrCancel.
+ *
+ * @param	None
+ *
+ * @return	None
+ */
+	{
+
+	LOGTEXT2(_L8("Loopback:NotifySignalChangeCancel:  Unit %d..."), iPortName);
+
+	if (iSignalMask != 0)
+		{
+		// Complete any outstanding notifications with KErrCancel
+		SignalChangeCompleted(0, KErrCancel);
+		iSignalMask = 0;	// set mask to zero
+		}
+	}
+
+
+void CHWPort::NotifyConfigChange()
+/**
+ * This method is currently not implemented in the loopback driver.
+ *
+ * @param	None
+ *
+ * @return	None
+ */
+	{}
+
+void CHWPort::NotifyConfigChangeCancel()
+/**
+ * This method is currently not implemented in the loopback driver.
+ *
+ * @param	None
+ *
+ * @return	None
+ */
+	{}
+
+void CHWPort::NotifyFlowControlChange()
+/**
+ * This method is currently not implemented in the loopback driver.
+ *
+ * @param	None
+ *
+ * @return	None
+ */
+	{}
+
+void CHWPort::NotifyFlowControlChangeCancel()
+/**
+ * This method is currently not implemented in the loopback driver.
+ *
+ * @param	None
+ *
+ * @return	None
+ */
+	{}
+
+
+void CHWPort::NotifyBreak()
+/**
+ * This method is currently not implemented in the loopback driver.
+ *
+ * @param	None
+ *
+ * @return	None
+ */
+	{}
+
+void CHWPort::NotifyBreakCancel()
+/**
+ * This method is currently not implemented in the loopback driver.
+ *
+ * @param	None
+ *
+ * @return	None
+ */
+	{}
+
+void CHWPort::NotifyDataAvailable()
+/**
+ * Wake up when data is sent by the other side
+ *
+ * @param	None
+ *
+ * @return	None
+ */
+	{
+	iDataNotify=ETrue;
+	CheckSigsAndCompleteRead();
+	}
+
+void CHWPort::NotifyDataAvailableCancel()
+/**
+ * Cancel data available notification
+ *
+ * @param	None
+ *
+ * @return	None
+ */
+	{
+	iDataNotify=EFalse;
+	NotifyDataAvailableCompleted(KErrCancel);
+	}
+
+void CHWPort::NotifyOutputEmpty()
+/**
+ * This method is currently not implemented in the loopback driver.
+ *
+ * @param	None
+ *
+ * @return	None
+ */
+	{}
+
+void CHWPort::NotifyOutputEmptyCancel()
+/**
+ * This method is currently not implemented in the loopback driver.
+ *
+ * @param	None
+ *
+ * @return	None
+ */
+	{
+	}
+
+TInt CHWPort::GetFlowControlStatus(TFlowControl& /* aFlowControl */)
+/**
+ * This method is currently not implemented in the loopback driver.
+ *
+ * @param	Not Used
+ *
+ * @return	KErrNotSupported
+ */
+	{
+	return KErrNotSupported;
+	}
+
+TInt CHWPort::GetRole(TCommRole& aRole)
+/**
+ * This method returns the current Role that this port is playing (ECommRoleDCE or ECommRoleDTE)
+ *
+ * @param	aRole	-	a reference to a TCommRole to return the role value in.
+ *
+ * @return	KErrNone
+ */
+	{
+	LOGTEXT2(_L8("Loopback:GetRole:  Unit %d..."), iPortName);
+
+	aRole=iRole;
+	return KErrNone;
+	}
+
+TInt CHWPort::SetRole(TCommRole aRole)
+/**
+ * This method sets the role of the port.  Additionally, it sets the default state of the
+ * signals for this type of port.  This is the first place where signals are set as a port
+ * is opening.  The ports will assert their output signals with the exception of DCD (which
+ * is an output signal from the DCE).  DCD is left to be driven by the test harness.
+ *
+ * A test could be put into place to insure that each port is in a different role.  This was
+ * not done at this time for backwards compatibility reasons.
+ *
+ * @param	aRole	-	the TCommRole value that this port should be set to.
+ *
+ * @return	None
+ */
+	{
+	LOGTEXT2(_L8("Loopback:SetRole:  Unit %d..."), iPortName);
+
+	if (ECommRoleDTE == aRole)
+		{
+		SetSignalsToMark(KSignalDTR|KSignalRTS);
+		}
+	else // DCE
+		{
+		SetSignalsToMark(KSignalDSR|KSignalCTS);
+		}
+
+
+	// Informational test only.  This will produce output to the log file if both sides have the
+	// same role set.  With both sides having the same role, none of the signal handling will
+	// function properly.
+#if defined (_DEBUG)
+	if (iLoopbackPort)
+		{
+		TCommRole otherSide;
+		iLoopbackPort->GetRole(otherSide);
+		if (otherSide == aRole)
+			LOGTEXT3(_L8("Loopback:SetRole:  Unit %d...Both sides same role %d"), iPortName, aRole);
+		}
+#endif
+
+	iRole = aRole;
+	return KErrNone;
+	}
+
+CHWPort::~CHWPort()
+/**
+ * This method is the standard destructor for the port.  It deletes the buffer
+ * which was allocated to the port and resets the loopback port pointer to NULL.
+ *
+ * @param	None
+ *
+ * @return	None
+ */
+	{
+
+
+	delete iBuf;
+	iBuf=NULL;
+	iPtr.Set(NULL,0,0);
+	if(iLoopbackPort)
+		iLoopbackPort->SetLoopbackPort(NULL);
+	((CHWPortFactory*)Owner())->Remove(this);
+
+#if defined (_DEBUG_CONSOLE_)
+#if defined (_DEBUG_DEVCOMM)
+	delete iDumper;
+#endif
+	iConsole.Close();
+#endif
+	}
+
+void CHWPort::FreeMemory()
+/**
+ * This method is currently not implemented in the loopback driver.
+ *
+ * @param	None
+ *
+ * @return	None
+ */
+	{}
+
+ CPort* CHWPortFactory::NewPortL(const TUint aUnit)
+/**
+ * This method creates a new port object.  It identifies the new object with the unit number that
+ * is supplied.  If both ports that are supported by the CHWPortFactory object have been created,
+ * then the loopback ports are initialized.
+ *
+ * @param	aUnit		-	The unit number to create.
+ *
+ * @return	CPort *		-	A pointer to the newly created object.
+ */
+	{
+	LOGTEXT2(_L8("Loopback:NewPortL:  Unit %d"), aUnit);
+	if(aUnit >= (KLoopbackCount&~1))
+		User::Leave(KErrNotSupported);
+
+	CPort* newPort;
+	if (iPort[aUnit])
+		{
+		LOGTEXT3(_L8("Loopback:NewPortL:  Unit %d already exists! @%x"), aUnit, iPort[aUnit]);
+		}
+	newPort=iPort[aUnit]=CHWPort::NewL(aUnit);
+
+	if((iPort[aUnit])&&(PairedPort(aUnit)))
+		{
+		iPort[aUnit]->SetLoopbackPort(PairedPort(aUnit));
+		PairedPort(aUnit)->SetLoopbackPort(iPort[aUnit]);
+		}
+	return newPort;
+	}
+
+void CHWPortFactory::Info(TSerialInfo &aSerialInfo)
+/**
+ * This method fills information into the passed structure.  It is required for factory objects.
+ *
+ * @param	aSerialInfo		-	a reference to the structure to fill in.
+ *
+ * @return	None
+ */
+	{
+	aSerialInfo.iDescription=SERIAL_DESCRIPTION;
+	aSerialInfo.iName=SERIAL_NAME;
+	aSerialInfo.iLowUnit=KCommLowUnit;
+	aSerialInfo.iHighUnit=KLoopbackCount - 1;
+	}
+
+CHWPortFactory::CHWPortFactory()
+/**
+ * This method is the constructor for the factory object.
+ *
+ * @param	None
+ *
+ * @return	None
+ */
+	{
+	__DECLARE_NAME(_S("CHWPortFactory"));
+	TName name(SERIAL_NAME);
+	SetName(&name);
+	iVersion=TVersion(KEC32MajorVersionNumber,KEC32MinorVersionNumber,KEC32BuildVersionNumber);
+	}
+
+ void CHWPortFactory::Remove(CHWPort* aPort)
+/**
+ * This method removes an instance of the CHWPort from the factory package CHWPortFactory.
+ *
+ * @param	aPort	-	The pointer to the CHWPort pointer to be removed from the factory object.
+ *
+ * @return	None
+ *
+ * @note	If the passed in value does not match a current port, this method will panic.
+ */
+ 	{
+	LOGTEXT2(_L8("Loopback:Remove:  Port %x"), aPort);
+	for(TUint i=0; i<KLoopbackCount; i++)
+		{
+		if(iPort[i]==aPort)
+			{
+			iPort[i]=NULL;
+			return;
+			}
+		}
+	User::Panic(_L("CHWPortFactory Panic"),0);
+	}
+
+CHWPortFactory::~CHWPortFactory()
+/**
+ * This method is the destructor for the factory object.
+ *
+ * @param	None
+ *
+ * @return	None
+ *
+ */
+	{
+
+	LOGDESTROY();
+
+	}
+
+/**
+Returns capabilities for requested port
+*/
+TSecurityPolicy CHWPortFactory::PortPlatSecCapability(TUint /*aPort*/) const
+	{
+	return TSecurityPolicy(TSecurityPolicy::EAlwaysPass);
+	}
+
+extern "C"
+	{
+	IMPORT_C CSerial * LibEntry(void);	// Force export
+	}
+
+EXPORT_C CSerial * LibEntry(void)
+/**
+ * This method is the library's main entry point.  It simply new's the factory object.
+ *
+ * @param	None
+ *
+ * @return	None
+ *
+ */
+	{
+
+	return new CHWPortFactory;
+	}
+
+
+#if defined(_DEBUG_CONSOLE_)
+// This code will not compile given current class structure, etc.  It is left here for
+// future reference.
+RDebugConsole::RDebugConsole()
+	{
+	Create();
+	Set(_L(""),TSize(64,15));
+
+	}
+
+void RDebugConsole::Printf(TRefByValue<const TDesC> aFmt,...)
+//
+// Print to a console screen.
+//
+	{
+
+	VA_LIST list;
+	VA_START(list,aFmt);
+	TBuf<0x100> aBuf;
+	aBuf.AppendFormatList(aFmt,list);
+	Write(aBuf);
+	}
+#endif
+
+#if defined (_DEBUG_DEVCOMM) && defined (_DEBUG_CONSOLE_)
+CCommDebugDumper* CCommDebugDumper::NewL(RDebugConsole &aConsole)
+	{
+	CCommDebugDumper* p=new CCommDebugDumper(aConsole);
+	return p;
+	}
+
+CCommDebugDumper::CCommDebugDumper(RDebugConsole &aConsole)
+	:CActive(EPriorityStandard)
+	{
+	iRole=ECommRoleDTE;
+	iConsole=&aConsole;
+	CActiveScheduler::Add(this);
+	SetActive();
+	iConsole->Read(iKeystroke,iStatus);
+	};
+
+CCommDebugDumper::~CCommDebugDumper()
+	{
+	Cancel();
+	}
+
+void CCommDebugDumper::RunL()
+	{
+	TInt key=iKeystroke.Code();
+	switch(key)
+		{
+		case 'd':
+		case 'D':
+			{
+			TCommDebugInfoPckg d;
+			if (iRole==ECommRoleDTE)
+				iParent->DTEPort().DebugInfo(d);
+			else
+				iParent->DCEPort().DebugInfo(d);
+			TCommDebugInfo& debug=d();
+			iConsole->Printf(_L("rxbusy  : 0x%04x, rxHeld   : 0x%04x, \n\r"),debug.iRxBusy,debug.iRxHeld);
+			iConsole->Printf(_L("txbusy  : 0x%04x, txHeld   : 0x%04x, \n\r"),debug.iTxBusy,debug.iTxHeld);
+			iConsole->Printf(_L("drainRx : 0x%04x, fillTx   : 0x%04x\n\r"),debug.iDrainingRxBuf,debug.iFillingTxBuf);
+			iConsole->Printf(_L("Txonchar: 0x%04x, TxOffchar: 0x%04x\n\r"),debug.iTxXon,debug.iTxXoff);
+			iConsole->Printf(_L("RxonChar: 0x%04x, RxOffchar: 0x%04x\n\r"),debug.iRxXon,debug.iRxXoff);
+			iConsole->Printf(_L("NumTX   : 0x%04x, NumRx    : 0x%04x\n\r"),debug.iTxChars,debug.iRxChars);
+			iConsole->Printf(_L("TxLen   : 0x%04x, RxLen    : 0x%04x\n\r"),debug.iTxLength,debug.iRxLength);
+			iConsole->Printf(_L("TxOffset: 0x%04x, RxOffset : 0x%04x\n\r"),debug.iTxOffset,debug.iRxOffset);
+			iConsole->Printf(_L("TxInts  : 0x%04x, RxInts   : 0x%04x\n\r"),debug.iTxIntCount,debug.iRxIntCount);
+			}
+			break;
+		case 's':
+		case 'S':
+			{
+			TUint signals=0;
+			if (iRole==ECommRoleDTE)
+				signals=iParent->DTEPort().Signals();
+			else
+				signals=iParent->DCEPort().Signals();
+			iConsole->Printf(_L("Signals: "));
+			if (signals&KSignalCTS)
+				iConsole->Printf(_L("CTS "));
+			if (signals&KSignalDSR)
+				iConsole->Printf(_L("DSR "));
+			if (signals&KSignalDCD)
+				iConsole->Printf(_L("DCD "));
+			if (signals&KSignalRTS)
+				iConsole->Printf(_L("RTS "));
+			if (signals&KSignalDTR)
+				iConsole->Printf(_L("DTR "));
+			iConsole->Printf(_L("\n\r"));
+			}
+			break;
+		default:
+			break;
+		}
+
+	SetActive();
+	iConsole->Read(iKeystroke,iStatus);
+	};
+
+void CCommDebugDumper::DoCancel()
+	{
+	iConsole->ReadCancel();
+	}
+
+#endif