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