serialserver/packetloopbackcsy/src/loopback.cpp
changeset 0 dfb7c4ff071f
child 4 928ed51ddc43
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/serialserver/packetloopbackcsy/src/loopback.cpp	Thu Dec 17 09:22:25 2009 +0200
@@ -0,0 +1,1042 @@
+// Copyright (c) 2004-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 3GNIF test harness.
+// 
+//
+
+/**
+ @file
+*/
+
+#include <c32comm.h>
+#include <c32comm_internal.h>
+
+#include "Loopback.h"
+#include "LoopbackConfig.h"
+#include "LoopbackTimer.h"
+#include "LoopbackQueue.h"
+
+CHWPort::CHWPort(TUint aUnit) : iRole(ECommRoleDTE), iPortName(aUnit)
+	{
+	TName name;
+	name.Format(_L("%d"),aUnit);
+	SetName(&name);
+	}
+
+static 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. 
+ */
+	{
+	((CObject*)anObject)->Close();
+	}
+
+CHWPort* CHWPort::NewPacketLoopbackL(TUint aUnit, TUint aDelay, TUint aPacketLength, TUint aQueueLength)
+/**
+ * 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 port number to create.
+ * @param	aDelay - the delay for sending on the port
+ * @param	aPacketLength - the size in bytes of a packet
+ * @param	aQueueLength - the maximum number of packets to queue
+ *
+ * @return	A pointer to the created object
+ */
+	{
+	LOGTEXT2(_L8("PKTLOOPBACK:NewL: Called for Unit %d"), aUnit);
+
+	CHWPort *p = new(ELeave) CHWPort(aUnit);
+	TCleanupItem closePort(CloseObject,p);
+	CleanupStack::PushL(closePort);
+	p->ConstructPacketLoopbackL(aDelay, aPacketLength, aQueueLength);
+	CleanupStack::Pop(p);
+
+	return p;
+	}
+
+CHWPort* CHWPort::NewSerialLoopbackL(TUint aUnit, TUint aDelay, TUint aBufferSize)
+/**
+ * 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 port number to create.
+ * @param	aDelay - the delay for sending on the port
+ * @param	aBuffersize - the size of the buffer to allocate
+ *
+ * @return	A pointer to the created object
+ */
+	{
+	LOGTEXT2(_L8("PKTLOOPBACK:NewL: Called for Unit %d"), aUnit);
+
+	CHWPort *p = new(ELeave) CHWPort(aUnit);
+	TCleanupItem closePort(CloseObject,p);
+	CleanupStack::PushL(closePort);
+	p->ConstructSerialLoopbackL(aDelay, aBufferSize);
+	CleanupStack::Pop(p);
+
+	return p;
+	}
+
+void CHWPort::ConstructSerialLoopbackL(TUint aDelay, TUint aBufferSize)
+	{
+	iWriteDelayTimer = CLoopbackTimer::NewL(aDelay, this);
+	iFlowControlChange = CFlowControlChange::NewL(iPortName, this);
+	
+	iPortType = ESerialLoopbackPortType;
+	TCommConfigV01 *config = &iConfig();
+	iReadWriteQueue = CSerialBufferQueue::NewL(aBufferSize, config);
+	}
+	
+void CHWPort::ConstructPacketLoopbackL(TUint aDelay, TUint aPacketLength, TUint aQueueLength)
+	{
+	iWriteDelayTimer = CLoopbackTimer::NewL(aDelay, this);
+	iFlowControlChange = CFlowControlChange::NewL(iPortName, this);
+	iReadWriteQueue = CPacketBufferQueue::NewL(aPacketLength, aQueueLength);
+	}
+	
+TUint CHWPort::PortName() const
+	{
+	return iPortName;
+	}
+
+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.
+ */
+	{
+	LOGTEXT2(_L8("PKTLOOPBACK:SetLoopbackPort:  Unit %d..."), iPortName);
+
+	// Now set up the loopback
+	iLoopbackPort = aHWPort;
+	}
+
+TBool CHWPort::GetFlowControl() const
+/**
+ * @return ETrue if flow control is on, EFalse if it is not
+ */
+	{
+	return iFlowControlChange->FlowControlIsOn();
+	}
+
+void CHWPort::StartFlowControl()
+/**
+ * Performs any necessary actions to set flow control on
+ */
+	{
+	iWriteDelayTimer->Cancel();
+	}
+	
+void CHWPort::StopFlowControl()
+/**
+ * Performs any necessary actions to stop flow control
+ */
+	{
+	if (!iLoopbackPort)
+		{
+		return;
+		}
+	
+	if (iReadWriteQueue->IsWriteBufferEmpty())
+		{
+		return;
+		}
+	
+	TInt nextWriteLen = iReadWriteQueue->PeekNextWriteBuffer().Length();
+	if (!iLoopbackPort->iReadWriteQueue->IsReadBufferFull(nextWriteLen)
+		&& !iWriteDelayTimer->IsActive())
+		{
+		iWriteDelayTimer->Start();
+		}
+	}
+	
+TInt CHWPort::GetReadRequestStatus()
+/**
+ * @return The status that Read() calls are to complete with
+ */
+	{
+	TInt readRequestStatus = KErrNone;
+	TInt ret = RProperty::Get(KUidPSCsyReadResultCategory, iPortName, readRequestStatus);
+	if (ret == KErrNone)
+		return readRequestStatus;
+	// if we can't find this property, we make everything work
+	return KErrNone;
+	}
+	
+TInt CHWPort::GetWriteRequestStatus()
+/**
+ * @return The status that Write() calls are to complete with
+ */
+	{
+	TInt writeRequestStatus = KErrNone;
+	TInt ret = RProperty::Get(KUidPSCsyWriteResultCategory, iPortName, writeRequestStatus);
+	if (ret == KErrNone)
+		return writeRequestStatus;
+	// if we can't find this property, we make everything work
+	return KErrNone;
+	}
+	
+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.
+ * If the read buffer is insufficient in size, the read completes with error KErrOverflow.
+ *
+ * @param	aClientBuffer	- a TAny * to the buffer into which data is placed.
+ * @param	aLength			- the length of the buffer supplied
+ */
+	{
+	LOGTEXT2(_L8("PKTLOOPBACK:StartRead:  Unit %d..."), iPortName);
+
+	// Because a length of zero is a special case (for initialization), we will complete this immediately
+	if(aLength == 0)
+		{
+		ReadCompleted(KErrNone);
+		return;
+		}
+	else if(aLength < 0)
+		{
+		// if the read is through the function ReadOneOrMore, the length will be negative
+		// in this CSY, the behavior is always ReadOneOrMore
+		aLength = -aLength;
+		iReadOneOrMore = ETrue;
+		}
+	else // aLength > 0
+		{
+		iReadOneOrMore = EFalse;
+		}
+	
+	// Save the client read buffer
+	iClientReadBuffer = aClientBuffer;
+	iClientReadBufferLength = aLength;
+	iReadPending = ETrue;
+	
+	// if there is already an entry in the read buffer, we can complete the read now
+	TryToCompleteRead();
+	}
+
+void CHWPort::TryToCompleteRead()
+/**
+ * This method attempts to complete reads - it is either called as they are initially issued (and is
+ * completed if there is data already in the read buffer), or is is called later, when data is supplied 
+ * to the read buffer (and is completed if there is a read waiting)
+ * Data is moved from the buffer associated with this port to a buffer that was supplied by the client 
+ * when the read was issued.
+ */
+
+	{
+	LOGTEXT2(_L8("PKTLOOPBACK:TryToCompleteRead: Unit %d ..."), iPortName);
+
+	// If there is not read pending or no loopback port, we can't complete a read
+	if (!iReadPending)
+		return;
+	
+	if (!iLoopbackPort)
+		return;
+	
+	// Determine if there an entry available to be read
+	if (iReadWriteQueue->IsReadBufferEmpty())
+		return;
+	
+	TInt res = KErrNone;
+	TBool completeIfBufferNotFull;
+	TPtrC8 nextReadBuffer = iReadWriteQueue->PeekNextReadBuffer(completeIfBufferNotFull);
+	if ( iClientReadBufferLength > nextReadBuffer.Length() )
+		{
+		if (iPortType == ESerialLoopbackPortType) 
+			{
+		 	if (iReadOneOrMore == EFalse) 
+				{
+		 		if (!completeIfBufferNotFull)
+		 			{
+					return;
+		 			}
+				}
+			}
+		}
+		
+	TInt readRequestStatus = GetReadRequestStatus();
+	if ( (iClientReadBufferLength < nextReadBuffer.Length()) && (iPortType == EPacketLoopbackPortType) )
+		{
+		// The client buffer is not large enough
+		res = KErrOverflow;
+		}
+	else if (readRequestStatus != KErrNone)
+		{
+		// We are configured to complete with an error
+		res = readRequestStatus;
+		iReadWriteQueue->DiscardNextReadBuffer();
+		}
+	else
+		{
+		// Write the data to the client's supplied buffer
+		res = IPCWrite(iClientReadBuffer, nextReadBuffer, 0);
+		if(res == KErrNone)
+			{
+			// It is normally the writing buffer's responsibility to start the timer, but in the case where the 
+			// reading port's buffer is full, now that the read buffer is no longer full, we have to signal to the 
+			// writing port to start sending again
+			if (!iLoopbackPort->iReadWriteQueue->IsWriteBufferEmpty())
+				{
+				TInt nextWriteLen = iLoopbackPort->iReadWriteQueue->PeekNextWriteBuffer().Length();
+				if (iReadWriteQueue->IsReadBufferFull(nextWriteLen)
+					&& !iLoopbackPort->iWriteDelayTimer->IsActive() )
+					{
+					iLoopbackPort->iWriteDelayTimer->Start();
+					}
+				}
+			// Read has been completed successfully, so dequeue
+			iReadWriteQueue->DiscardNextReadBuffer();
+			}
+		}
+	// complete the read
+	iReadPending = EFalse;
+	LOGTEXT3(_L8("PKTLOOPBACK:TryToCompleteRead: Unit %d completing read with error %d"), iPortName, res);
+	ReadCompleted(res);
+	}
+
+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.
+ */
+	{
+	LOGTEXT2(_L8("PKTLOOPBACK:ReadCancel:  Unit %d..."), iPortName);
+
+	iReadPending = EFalse;
+
+	ReadCompleted(KErrCancel);
+	}
+
+TInt CHWPort::WriteBuf(const TAny* aClientBuffer, TInt aLength)
+/**
+ * This method queues the client buffer to the loopback port.
+ * A delay in the port is simulated, so the read corresponding to this buffer will not complete
+ * until after this delay. The buffers are queued up and sent one per delay period.
+ *
+ * @param	aClientBuffer	- a TAny * to the buffer which contains the data to write
+ * @param	aLength			- the length of the buffer.
+ *
+ * @return	KErrNone if everything is okay, KErrOverflow if the write queue is full and no more entries can be written until some have been sent, 
+ * KErrNotReady	if the loopback port has not yet been opened or there is no loopback port configured for this port, or KErrArgument	if the supplied buffer is too large
+ */
+	{
+	TInt res = KErrNone;
+
+	LOGTEXT2(_L8("PKTLOOPBACK:WriteBuf:  Unit %d..."), iPortName);
+
+	TInt writeRequestStatus = GetWriteRequestStatus();
+	if (writeRequestStatus != KErrNone)
+		return writeRequestStatus;
+	
+	// Fill the receiving buffer
+	if (aLength != 0)
+		{
+		if (iReadWriteQueue->IsWriteBufferFull(aLength))
+			{
+			return KErrOverflow;
+			}
+		if (aLength > iReadWriteQueue->BufferSize())
+			{
+			return KErrArgument;
+			}
+		
+		TPtr8 currentPacket = iReadWriteQueue->AppendToWriteBuffer(aLength);
+		
+		res = IPCRead(aClientBuffer, currentPacket);
+		if(res != KErrNone)
+			return res;
+		}
+	// Queue up the request and start the timer to simulate the delay in sending the data across the port
+	if (!iWriteDelayTimer->IsActive())
+		{
+		iWriteDelayTimer->Start();
+		}
+
+	return KErrNone;
+	}
+
+void CHWPort::TimerCallBack()
+/**
+ * This method is called after the simulated delay for sending data across the port
+ */
+	{
+	if (!iLoopbackPort)
+		{
+		return;
+		}
+	
+	if (iReadWriteQueue->IsWriteBufferEmpty())
+		{
+		return;
+		}
+	
+	TPtrC8 writeBuffer = iReadWriteQueue->PeekNextWriteBuffer();
+	if (iLoopbackPort->iReadWriteQueue->IsReadBufferFull(writeBuffer.Length()))
+		{
+		return;
+		}
+	
+	// Move the buffer from the write queue to the read queue
+	iLoopbackPort->iReadWriteQueue->AppendToReadBuffer(writeBuffer);
+	iReadWriteQueue->DiscardNextWriteBuffer();
+
+	// If the loopback port has a read outstanding it can complete now
+	iLoopbackPort->TryToCompleteRead();
+	// If there is another write outstanding we have to simulate a delay for that entry
+	if (!iReadWriteQueue->IsWriteBufferEmpty() && !iWriteDelayTimer->IsActive())
+		{
+		iWriteDelayTimer->Start();
+		}
+	}
+
+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.
+ */
+	{
+	LOGTEXT2(_L8("PKTLOOPBACK:QueryReceiveBuffer:  Unit %d..."), iPortName);
+
+	TBool dummy;
+	const TPtrC8& iReadBuf = iReadWriteQueue->PeekNextReadBuffer(dummy);
+	aLength = iReadBuf.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
+ */
+	{
+	LOGTEXT2(_L8("PKTLOOPBACK:ResetBuffers:  Unit %d..."), iPortName);
+	
+	iReadWriteQueue->Clear();
+	}
+
+void CHWPort::StartWrite(const TAny* aClientBuffer,TInt aLength)
+/**
+ * This method queues a write operation to the driver.
+ *
+ * @param	aClientBuffer	- a TAny * to the buffer into which data should be read.
+ * @param	aLength			- the length of the data to be written.
+ */
+	{
+
+	LOGTEXT2(_L8("PKTLOOPBACK:StartWrite:  Unit %d..."), iPortName);
+
+	TInt res = WriteBuf(aClientBuffer, aLength);
+	
+	LOGTEXT3(_L8("PKTLOOPBACK:StartWrite:  Completing Write for Unit %d with error %d"), iPortName, res);
+	WriteCompleted(res);
+	}
+
+void CHWPort::WriteCancel()
+/**
+ * This method cancels a pending write and issues a WriteCompleted with the result
+ * KErrCancel.
+ */
+
+	{
+	LOGTEXT2(_L8("PKTLOOPBACK:WriteCancel:  Unit %d..."), iPortName);
+
+	WriteCompleted(KErrCancel);
+	}
+
+void CHWPort::Break(TInt /*aTime*/)
+/**
+ * This method is currently not implemented in the loopback driver as breaks are
+ * not supported.
+ */
+	{
+	LOGTEXT2(_L8("PKTLOOPBACK:Break is not supported:  Unit %d..."), iPortName);
+	}
+
+void CHWPort::BreakCancel()
+/**
+ * This method is currently not implemented in the loopback driver as breaks are
+ * not supported.
+ */
+	{
+	LOGTEXT2(_L8("PKTLOOPBACK:BreakCancel is not supported:  Unit %d..."), iPortName);
+	}
+
+TInt CHWPort::GetConfig(TDes8& /*aDes*/) const
+/**
+ * This gets the current configuration from the loopback driver.
+ * 
+ * @return KErrNotSupported
+ */
+	{
+	LOGTEXT2(_L8("PKTLOOPBACK:GetConfig is not supported:  Unit %d..."), iPortName);
+
+	return KErrNotSupported;
+	}
+
+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.
+ *
+ * @return KErrNotSupported
+ */
+	{
+	LOGTEXT2(_L8("PKTLOOPBACK:SetConfig is not supported:  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, KErrNotSupported it the length of the descriptor passed to this method indicates a
+ *								  capabilities structure which we don't support.
+ */
+	{
+	LOGTEXT2(_L8("PKTLOOPBACK: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;
+		commcaps->iHandshake=0;
+		commcaps->iSignals=0x3f;
+		commcaps->iSIR=0x0;
+		return KErrNone;
+		}
+	else if(aDes.Length()==sizeof(TCommCapsV02))
+		{
+		TCommCapsV02* commcaps=(TCommCapsV02*)(aDes.Ptr());
+		commcaps->iRate=0x3fffff;
+		commcaps->iDataBits=0xf;
+		commcaps->iStopBits=0x3;
+		commcaps->iParity=0x1f;
+		commcaps->iFifo=0x1;
+		commcaps->iHandshake=0;
+		commcaps->iSignals=0x3f;
+		commcaps->iSIR=0x0;
+		commcaps->iNotificationCaps=0;
+		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	KErrNotSupported
+ */
+	{
+	LOGTEXT2(_L8("PKTLOOPBACK:SetServerConfig is not supported:  Unit %d..."), iPortName);
+
+	return KErrNotSupported;
+	}
+
+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	KErrNotSupported
+ */
+	{
+	LOGTEXT2(_L8("PKTLOOPBACK:GetServerConfig is not supported:  Unit %d..."), iPortName);
+
+	return KErrNotSupported;
+	}
+
+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	KErrNotSupported
+ */
+	{
+	LOGTEXT2(_L8("PKTLOOPBACK:GetSignals is not supported:  Unit %d..."), iPortName);
+
+	aSignals = 0;
+	return KErrNotSupported;
+	}
+
+TInt CHWPort::SetSignalsToMark(TUint /*aSignals*/)
+/**
+ * This method asserts the signals specified by the parameter aSignals.
+ *
+ * @param	aSignals	- a bitmask specifying which signals to assert (See definition
+ *                        of KSignalDCD, KSignalCTS, etc. for bit values).
+ *
+ * @return	KErrNotSupported
+ */
+	{
+	LOGTEXT2(_L8("PKTLOOPBACK:SetSignalsToMark is not supported:  Unit %d..."), iPortName);
+
+	return KErrNotSupported;
+	}
+
+
+
+TInt CHWPort::SetSignalsToSpace(TUint /*aSignals*/)
+/**
+ * This method de-asserts the signals specified by the parameter aSignals.
+ *
+ * @return KErrNotSupported
+ */
+	{
+	LOGTEXT2(_L8("PKTLOOPBACK:SetSignalsToSpace is not supported:  Unit %d..."), iPortName);
+
+	return KErrNotSupported;
+	}
+
+TInt CHWPort::GetReceiveBufferLength(TInt& /*aLength*/) const
+/**
+ * This method is currently not implemented in the loopback driver.  Calling this
+ * method will return an error
+ *
+ * @return	KErrNotSupported
+ */
+	{
+	LOGTEXT2(_L8("PKTLOOPBACK:GetReceiveBufferLength is not supported:  Unit %d..."), iPortName);
+
+	return KErrNotSupported;
+	}
+
+TInt CHWPort::SetReceiveBufferLength(TInt /*aLength*/)
+/**
+ * This method is currently not implemented in the loopback driver.  Calling this
+ * method will return an error
+ *
+ * @return	KErrNotSupported
+ */
+	{
+	LOGTEXT2(_L8("PKTLOOPBACK:SetReceiveBufferLength is not supported:  Unit %d..."), iPortName);
+
+	return KErrNotSupported;
+	}
+
+void CHWPort::Destruct()
+/**
+ * This method is simply deletes this instance of the port, comitting sucide.
+ */
+	{
+	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.
+ */
+	{
+	LOGTEXT2(_L8("PKTLOOPBACK:NotifySignalChange is not supported:  Unit %d..."), iPortName);
+	}
+
+
+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.
+ */
+	{
+	LOGTEXT2(_L8("PKTLOOPBACK:NotifySignalChangeCancel is not supported:  Unit %d..."), iPortName);
+	}
+
+
+void CHWPort::NotifyConfigChange()
+/**
+ * This method is currently not implemented in the loopback driver.
+ */
+	{
+	LOGTEXT2(_L8("PKTLOOPBACK:NotifyConfigChange is not supported:  Unit %d..."), iPortName);
+	}
+
+void CHWPort::NotifyConfigChangeCancel()
+/**
+ * This method is currently not implemented in the loopback driver.
+ */
+	{
+	LOGTEXT2(_L8("PKTLOOPBACK:NotifyConfigChangeCancel is not supported:  Unit %d..."), iPortName);
+	}
+
+void CHWPort::NotifyFlowControlChange()
+/**
+ * This method is currently not implemented in the loopback driver.
+ */
+	{
+	LOGTEXT2(_L8("PKTLOOPBACK:NotifyFlowControlChange is not supported:  Unit %d..."), iPortName);
+	}
+
+void CHWPort::NotifyFlowControlChangeCancel()
+/**
+ * This method is currently not implemented in the loopback driver.
+ */
+	{
+	LOGTEXT2(_L8("PKTLOOPBACK:NotifyFlowControlChangeCancel is not supported:  Unit %d..."), iPortName);
+	}
+
+
+void CHWPort::NotifyBreak()
+/**
+ * This method is currently not implemented in the loopback driver.
+ */
+	{
+	LOGTEXT2(_L8("PKTLOOPBACK:NotifyBreak is not supported:  Unit %d..."), iPortName);
+	}
+
+void CHWPort::NotifyBreakCancel()
+/**
+ * This method is currently not implemented in the loopback driver.
+ */
+	{
+	LOGTEXT2(_L8("PKTLOOPBACK:NotifyBreakCancel is not supported:  Unit %d..."), iPortName);
+	}
+
+void CHWPort::NotifyDataAvailable()
+/**
+ * This method is currently not implemented in the loopback driver.
+ */
+	{
+	LOGTEXT2(_L8("PKTLOOPBACK:NotifyDataAvailable is not supported:  Unit %d..."), iPortName);
+	}
+
+void CHWPort::NotifyDataAvailableCancel()
+/**
+ * This method is currently not implemented in the loopback driver.
+ */
+	{
+	LOGTEXT2(_L8("PKTLOOPBACK:NotifyDataAvailableCancel is not supported:  Unit %d..."), iPortName);
+	}
+
+void CHWPort::NotifyOutputEmpty()
+/**
+ * This method is currently not implemented in the loopback driver.
+ */
+	{
+	LOGTEXT2(_L8("PKTLOOPBACK:NotifyOutputEmpty is not supported:  Unit %d..."), iPortName);
+	}
+
+void CHWPort::NotifyOutputEmptyCancel()
+/**
+ * This method is currently not implemented in the loopback driver.
+ */
+	{
+	LOGTEXT2(_L8("PKTLOOPBACK:NotifyOutputEmptyCancel is not supported:  Unit %d..."), iPortName);
+	}
+
+TInt CHWPort::GetFlowControlStatus(TFlowControl& /* aFlowControl */)
+/**
+ * This method is currently not implemented in the loopback driver.
+ *
+ * @return	KErrNotSupported
+ */
+	{
+	LOGTEXT2(_L8("PKTLOOPBACK:GetFlowControlStatus is not supported:  Unit %d..."), iPortName);
+
+	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
+ */
+	{
+	LOGTEXT3(_L8("PKTLOOPBACK:GetRole:  Unit %d, aRole %d..."), iPortName, aRole);
+
+	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	KErrNone
+ */
+	{
+	LOGTEXT3(_L8("PKTLOOPBACK:SetRole:  Unit %d, aRole %d..."), iPortName, aRole);
+
+	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("PKTLOOPBACK: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.
+ */
+	{
+	delete iFlowControlChange;
+	iFlowControlChange = NULL;
+	
+	delete iReadWriteQueue;
+	iReadWriteQueue = NULL;
+	
+	delete iWriteDelayTimer;
+	iWriteDelayTimer = NULL;
+
+	if(iLoopbackPort)
+		{
+		iLoopbackPort->SetLoopbackPort(NULL);
+		iLoopbackPort->iWriteDelayTimer->Cancel();
+		}
+	
+	((CHWPortFactory*)Owner())->Remove(this);
+	}
+
+void CHWPort::FreeMemory()
+/**
+ * This method is currently not implemented in the loopback driver.
+ */
+	{}
+
+CHWPort* CHWPortFactory::FindPort(TUint aUnit)
+/**
+ * Returns the port corresponding to the unit aUnit
+ *
+ * @param aUnit The port number for the port object to find
+ *
+ * @return The port object corresponding to unit aUnit
+ */
+	{
+	for (TInt i = 0; i < iPorts.Count(); i++)
+		{
+		if (iPorts[i]->PortName() == aUnit)
+			return iPorts[i];
+		}
+	return NULL;
+	}
+	
+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("PKTLOOPBACK:NewPortL:  Unit %d"), aUnit);
+	
+	// Get the settings for the given port. The iLoopbackConfig will look at the test's ini file for this information
+	TLoopbackConfigItem portSettings;
+	if (iLoopbackConfig)
+		{
+		if (iLoopbackConfig->GetPortSettings(aUnit, portSettings) != KErrNone)
+			User::Leave(KErrNotFound);
+		}
+	else
+		{
+		User::Leave(KErrNotFound);
+		}
+	
+	// If the port already exists, just return it
+	CHWPort *existingPort = FindPort(aUnit);
+	if (existingPort)
+		{
+		LOGTEXT2(_L8("Loopback:NewPortL:  Unit %d already exists! @%x"), aUnit);
+		return existingPort;
+		}
+	// Create the port and add it to the list of ports
+	CHWPort *newPort;
+	if (iLoopbackConfig->PortType() == ESerialLoopbackPortType)
+		{
+		newPort = CHWPort::NewSerialLoopbackL(aUnit, portSettings.iDelay, portSettings.iBufferSize);
+		}
+	else
+		{
+		newPort = CHWPort::NewPacketLoopbackL(aUnit, portSettings.iDelay, portSettings.iPacketLength, portSettings.iQueueLength);
+		}
+	CleanupStack::PushL(newPort);
+	iPorts.AppendL(newPort);
+	CleanupStack::Pop(newPort);
+	
+	// Get the name of the opposite loopback port
+	TUint oppositePortName;
+	if (portSettings.iPortA == aUnit)
+		oppositePortName = portSettings.iPortB;
+	else
+		oppositePortName = portSettings.iPortA;
+	
+	// If the opposite loopback port is open, set it as the current port's loopback
+	CHWPort *oppositePort = FindPort(oppositePortName);
+	if (oppositePort)
+		{
+		oppositePort->SetLoopbackPort(newPort);
+		newPort->SetLoopbackPort(oppositePort);
+		}
+	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.
+ */
+	{
+	aSerialInfo.iDescription = KSerialDescription;
+	aSerialInfo.iName = KSerialName;
+	aSerialInfo.iLowUnit = KCommLowUnit;
+	aSerialInfo.iHighUnit = KCommHighUnit;
+	}
+
+CHWPortFactory::CHWPortFactory()
+/**
+ * This method is the constructor for the factory object.
+ * We initialize iPorts to contain the number of ports from KMinLoopBackPort to KMaxLoopBackPort
+ */
+	{
+	TName name(KSerialName);
+	SetName(&name);
+	iVersion = TVersion(KEC32MajorVersionNumber,KEC32MinorVersionNumber,KEC32BuildVersionNumber);
+	
+	// Create the object to read the loopback port settings for this test
+	TRAP_IGNORE(iLoopbackConfig = CLoopbackConfig::NewL());
+	}
+
+void CHWPortFactory::Remove(CHWPort* aPort)
+/**
+ * This method removes an instance of the CHWPort from the factory package CHWPortFactory.
+ * The object is not deleted.
+ *
+ * @param	aPort	-	The pointer to the CHWPort pointer to be removed from the factory object.
+ *
+ * @note	If the passed in value does not match a current port, this method will panic.
+ */
+	{
+	LOGTEXT2(_L8("PKTLOOPBACK:Remove:  Port %d"), aPort->PortName());
+	for (TInt i = 0; i < iPorts.Count(); i++)
+		{
+		if(iPorts[i] == aPort)
+			{
+			iPorts.Remove(i);
+			return;
+			}
+		}
+	User::Panic(_L("CHWPortFactory: Port not found"),0);
+	}
+
+CHWPortFactory::~CHWPortFactory()
+/**
+ * This method is the destructor for the factory object.
+ */
+	{
+	delete iLoopbackConfig;
+	iLoopbackConfig = NULL;
+	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.
+	 */
+		{
+		return new CHWPortFactory;
+		}
+
+	} // extern "C"
+
+