diff -r 000000000000 -r dfb7c4ff071f serialserver/c32serialserver/LOOPBACK/loopback.CPP --- /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 +#include "LOGGER.H" +#include +#include +#include +#include + +#ifdef __LOGGER__XTRA +#define LOGI(AAA) CETelLogger::WriteFormat(TRefByValue(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 +class RDebugConsole : public RConsole + { +public: + RDebugConsole(); +public: + void Printf(TRefByValue 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 iServerConfig; //< A placeholder; no real function in loopback. + TPckgBuf 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 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