diff -r 000000000000 -r dfb7c4ff071f serialserver/packetloopbackcsy/src/loopback.cpp --- /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 +#include + +#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" + +