Latest bug-fixes with added tests.
// Copyright (c) 2004-2009 Nokia Corporation and/or its subsidiary(-ies).
// All rights reserved.
// This component and the accompanying materials are made available
// under the terms of "Eclipse Public License v1.0"
// which accompanies this distribution, and is available
// at the URL "http://www.eclipse.org/legal/epl-v10.html".
//
// Initial Contributors:
// Nokia Corporation - initial contribution.
//
// Contributors:
//
// Description:
// C32 Baseband Channel Adaptor(BCA) APIs implementation.
// This file contains all the API implementation of the C32BCA interface for Symbian OS.
//
//
/**
@file
@internalComponent
*/
#include "C32Bca.h"
#include <cdbcols.h>
#include <commsdat_partner.h>
using namespace BasebandChannelAdaptation;
using namespace BasebandChannelAdaptation::C32Bca;
const static TUint KVersion = ((static_cast<TUint>(KC32BcaMajorVersionNumber)) << 16) | KC32BcaMinorVersionNumber;
/**
C++ constructor for an object representing a c32 async user
@param aUser the target of the notifications originating in C32
@param aPort the C32 port to be used with this object
@param aAoPriority the priority to assign to this active object
*/
CCommBase::CCommBase(MC32User& aUser, RComm& aPort, TInt aAoPriority):
CActive(aAoPriority),
iUser(aUser),
iPort(aPort)
{
CActiveScheduler::Add(this);
__FLOG_OPEN(KC32BcaLogFolder, KC32BcaLogFile); // Connect to the BCA logger
}
/**
C++ destructor */
CCommBase::~CCommBase()
{
__FLOG_CLOSE;
}
/**
C++ constructor
The comm-reader has a higher priority than the comm-writer, to
ensure the reader gets the chance to receive the incomming data
@param aUser the target of the notifications originating in C32
@param aPort the C32 port to be used with this object
*/
CCommReader::CCommReader(MC32User& aUser, RComm& aPort):
CCommBase(aUser, aPort, EPriorityHigh)
{
}
/**
C++ destructor */
CCommReader::~CCommReader()
{
Cancel();
}
/**
Cancels the outstanding read */
void CCommReader::DoCancel()
{
iPort.ReadCancel();
}
/**
Issues a read on the serial port
@param aBuf the buffer to read to */
void CCommReader::Read(TDes8& aBuf)
{
iPort.ReadOneOrMore(iStatus, aBuf);
SetActive();
}
/**
Called on read completion */
void CCommReader::RunL()
{
iUser.CommReadComplete(iStatus.Int());
}
/**
C++ constructor
The comm-writer has a lower priority than the comm-reader, to
ensure the reader gets the chance to receive the incomming data
@param aUser the target of the notifications originating in C32
@param aPort the C32 port to be used with this object */
CCommWriter::CCommWriter(MC32User& aUser, RComm& aPort):
CCommBase(aUser, aPort, EPriorityUserInput)
{
}
/**
C++ destructor */
CCommWriter::~CCommWriter()
{
Cancel();
}
/**
Issues a write on the serial port */
void CCommWriter::Write(const TDesC8& aBuf)
{
iPort.Write(iStatus, aBuf);
SetActive();
}
/**
Called when the write completes
*/
void CCommWriter::RunL()
{
iUser.CommWriteComplete(iStatus.Int());
}
/**
Cancels the outstanding write
*/
void CCommWriter::DoCancel()
{
iPort.WriteCancel();
}
/**
C++ constructor
@param aUser the target of the notifications originating in C32
@param aPort the C32 port to be used with this object
Note on Monitor AO priority:
Monitor AO is the same priority as the Reader / Writer by design.
We do NOT want link failure notification to complete before all possible outstanding reads / writes
have completed, to make sure that we do not lose any data that was already received from the
baseband and is buffered by the driver or the CSY.
Consider a scenario where a UDP datagram is received by the CSY, and then the serial link is
severed (e.g. cable is unplugged by the user). If the Monitor has higher priority than reader/writer,
we may get failure notification before we are notified on the read of this UDP datagram, potentially
resulting in its loss (E.g. PPP shuts down when on Link Down notification.)
*/
CCommLinkMonitor::CCommLinkMonitor(MC32User& aUser, RComm& aPort):
CCommBase(aUser, aPort, EPriorityStandard)
{
}
/**
Sets the object to monitor the link for the configured Role, DTE or DCE
@param aMask DTE or DCE: determines when the link is considered to be down.
*/
void CCommLinkMonitor::Setup(TUint32 aMask)
{
iNotifyChangeSignalMask = aMask;
__FLOG_1(_L8("CommLinkMonitor setup: Notify on signal mask [0x%X]"),iNotifyChangeSignalMask);
}
/**
Requests to be notified when the link is down.
Can be called an more than once
*/
void CCommLinkMonitor::NotifyLinkDown()
{
ASSERT(!IsActive());
// get the current state of the line so when we get notification we can tell if it really has changed
iSavedSignalState = iPort.Signals(iNotifyChangeSignalMask) & iNotifyChangeSignalMask;
iPort.NotifySignalChange(iStatus, iSignals, iNotifyChangeSignalMask);
__FLOG_1(_L8("CommLinkMonitor::NotifyLinkDown: initial signals: [0x%X]"), iSavedSignalState);
SetActive();
}
/**
Called when EIA-232 signals change,i.e. on potential link failure
*/
void CCommLinkMonitor::RunL()
{
__FLOG_2(_L8("CommLinkMonitor::RunL: Signals changed [0x%X]; new signals [0x%X]"), iSignals & (~0x1F), iSignals & 0x1F);
// We report link failure if and only if a monitored line went from high to low.
// Method: mask the saved signals using inverted monitored bits in the signal bitmask,
// to filter out all transitions except for high -> low.
// 1 & ~0 = 1; 1 & ~1 = 0; 0 & ~1 = 0; 0 & ~0 = 0
// This is necessary, because we may be monitoring more than one signal, and the change can be arbitrary.
if ((~(iNotifyChangeSignalMask & iSignals)) & iSavedSignalState)
{
iUser.CommLinkDown(KErrCommsLineFail);
}
iSavedSignalState = iSignals & iNotifyChangeSignalMask; // update saved state with relevant bits
iPort.NotifySignalChange(iStatus, iSignals, iNotifyChangeSignalMask);
SetActive();
// Note: If the link went down, the user will likely shutdown the BCA on outstanding signal notification.
}
/**
Cancels notification request of link failure */
void CCommLinkMonitor::DoCancel()
{
__FLOG(_L8("CommLinkMonitor::DoCancel: Cancelling signal change notification."));
iPort.NotifySignalChangeCancel();
}
/**
* default constructor to create a C32Bca instance.
*/
CC32Bca::CC32Bca()
{
__FLOG_OPEN(KC32BcaLogFolder,KC32BcaLogFile);
__FLOG_2(_L8("====== CC32Bca::CC32Bca: Shim BCA for C32 [Vesion: major= %d minor= %d] Constructed. ======"), KC32BcaMajorVersionNumber, KC32BcaMinorVersionNumber);
}
/**
2nd phase of construction: allocates member objects
@leave when members cannot be constructed. */
void CC32Bca::ConstructL()
{
__ASSERT_DEBUG(!iReader, Panic(EReaderAlreadyExists));
__ASSERT_DEBUG(!iWriter, Panic(EWriterAlreadyExists));
__ASSERT_DEBUG(!iLinkMonitor, Panic(EMonitorAlreadyExists));
// Comm is not initialized yet. We have to be Opened before it can be used. C32 panics otherwise.
iReader = new (ELeave)CCommReader(*this, iComm);
iWriter = new (ELeave)CCommWriter(*this, iComm);
// Monitor is created upon user request only, we may never need it at all.
}
/**
* Destructor
*/
CC32Bca::~CC32Bca()
{
__FLOG(_L8("CC32Bca::~CC32Bca: releasing resources..."));
CloseCommDbConnection();
CloseCommPort(); // Cancels reader / writer / monitor
// N.B.:
// It is critical that these objects are cancelled before deletion. Else DoCancel operates on closed C32 port,
// causing C32 to panic.
delete iReader;
delete iWriter;
delete iLinkMonitor; // Note: may have never been created.
__FLOG(_L8("CC32Bca::~CC32Bca: CC32Bca destroyed."));
__FLOG_CLOSE;
}
/** This method deletes the BCA itself.*/
void CC32Bca::Release()
{
__FLOG(_L8("CC32Bca::Release"));
delete this;
}
/**
* Informs that the BCA is required by the client(for instance, Raw IP NIF).Comms Server is
* connected, and serial port is opened.
* @param aStatus complete status, KErrNone if successful, C32 error code otherwise.
* @param aChannelId comm port name.
*/
void CC32Bca::Open(TRequestStatus& aStatus, const TDesC& aChannelId)
{
// We don't have the ConstructL, so we have to allocate memory here.
aStatus = KRequestPending;
// avoid compiler warning.
TRequestStatus* ptrStatus = &aStatus;
if(iCommPortOpen)
{
__FLOG(_L8("Warning: C32Bca is already Opened."));
User::RequestComplete(ptrStatus,KErrNone);
return;
}
if(aChannelId == KChannelIdNotOverridden) // Channel ID is not supplied - Comm Port Name is not overriden.
{ // Read it from CommDB.
// Fetch port name from CommDB.
TRAPD(commPortErr, ReadCommPortFromCommDbL(iCommPort));
// If the column is missing from CommDB, we can get an empty string with KErrNone.
// Opening RComm with empty string as Port name fails with ECUART, LOOOPBACK CSYs.
// with different error codes on 8.1 and 9.0.
// It would be better to intercept such invalid value and fail here.
// This is not done, because theoretically it is possible to write a CSY that somehow takes an
// emty descriptor as a port name. As of December 2004, C32 does not allow that, however, this
// may change in the future.
// So, we just log a warning.
if(KErrNone == commPortErr && iCommPort.Size() == 0)
{
__FLOG(_L8("Warning: Null string read from CommDB. Will try to open Comm Port anyway."));
}
if(KErrNone != commPortErr) // Fatal: we do not have a port name. Can't open RComm.
{
__FLOG(_L8("Error: Failed to get C32 PortName from CommDB "));
CloseCommDbConnection();
User::RequestComplete(ptrStatus, commPortErr);
return;
}
}
else // Channel ID supplied - use it as Port Name.
{
_LIT(KDoubleColon, "::");
TInt len1 = aChannelId.Find(KDoubleColon);
if (len1 < KErrNone)
{
__FLOG(_L8("** ERROR: No :: (Double Colon) in aChannelId **"));
User::RequestComplete(ptrStatus, KErrBadName);
return;
}
else
{
TUint numPos = len1 + KDoubleColon.iTypeLength;
TInt len2 = (aChannelId.Mid(numPos)).Find(KDoubleColon);
if (len2 < KErrNone)
{
iCommPort = aChannelId;
}
else
{
iCommPort = aChannelId.Mid(numPos);
iCsyName = aChannelId.Mid(0, len1);
iCsyNameOverride = ETrue;
}
}
}
// We have the Port Name at this point: either supplied as a parameter, or read from CommDB.
if(!iCsyNameOverride) // CSY name is not provisioned by Ioctl: read it from CommDB.
{
TRAPD(getCsyErr, ReadCsyNameFromCommDbL(iCsyName));
if(KErrNone != getCsyErr) // Fatal: we do not have a CSY name. Can't load CSY module.
{
// We do not use a hardcoded value (e.g. ECUART), because the client code or the environment are
// confused - it is better to fail them, rather than mask their problems.
__FLOG(_L8("Error: Failed to get CSY name from CommDB "));
CloseCommDbConnection();
User::RequestComplete(ptrStatus,getCsyErr);
return;
}
}
// We have the CSY name at this point, either provisioned via Ioctl, or read from CommDB.
// Decide the Comm Role (DCE / DTE) of the port.
TBool isCommRoleDefault = EFalse;
// We use a concept of "Default Comm Role", because C32 has it. We could use DTE as a default
// setting, but we prefer to call the "default" method on C32 and let it decide what the role should be.
if(!iCommRoleOverride) // Port role not overriden via Ioctl
{
TRAPD(getCommRoleErr, ReadCommRoleFromCommDbL(iCommRole));
if(KErrNone != getCommRoleErr) // Port role not specified via CommDb
{
isCommRoleDefault = ETrue; // Port role not specified via Ioctl or CommDb
}
}
// We have the Comm Role: provisioned via Ioctl, read from CommDB, or default(DTE)
CloseCommDbConnection(); // We do not read any settings once the connection is opened.
// We have all the necessary C32 settings at this point.
// Open the actual C32 session:
TInt ret = iCommServ.Connect();
if(ret)
{
__FLOG_1(_L8("Open: C32 Server connection error %d"), ret);
User::RequestComplete(ptrStatus,ret);
return;
}
ret = iCommServ.LoadCommModule(iCsyName);
__FLOG_2(_L16("Open: CSY module [%S] loaded with error %d"), &iCsyName, ret);
if(ret)
{
User::RequestComplete(ptrStatus,ret);
return;
}
if(isCommRoleDefault)
{
ret = iComm.Open(iCommServ, iCommPort, ECommShared);
}
else
{
ret = iComm.Open(iCommServ, iCommPort, ECommShared, iCommRole);
}
__FLOG_2(_L16("Open: C32 port [%S] opened with error %d"), &iCommPort, ret);
if(ret)
{
User::RequestComplete(ptrStatus,ret);
return;
}
//
//Open successfull, we update our state.
//
// We may have been closed, and are being reopened now. Make sure no errors persist from the previous session.
iErrorOnNextRead = KErrNone;
iErrorOnNextWrite = KErrNone;
iCommPortOpen = ETrue;
User::RequestComplete(ptrStatus,KErrNone);
// Don't start control line monitoring. This is done on user request via Ioctl only.
}
/**
Shuts the BCA channel down in a graceful manner. BCA releases its resources.
Cancels all outstanding operations: Writes, Reads and Ioctls.
Due to the specifics of C32Bca implementation, this call completes immediately and is identical to ::Close.
@param aStatus completion status: Always KErrNone.
*/
void CC32Bca::Shutdown(TRequestStatus& aStatus)
{
aStatus = KRequestPending;
CloseCommPort();
// Don't notify reader / writer / monitor. Read / Write should not be called after this anyway. Let C32 panic.
TRequestStatus* request = &aStatus;
User::RequestComplete(request, KErrNone);
__FLOG_1(_L8("C32Bca::Shutdown: BCA shut down with error %d"), KErrNone);
}
/**
* Closes the BCA immediately. BCA releases its resources.
* cancels all Writes, Reads and Controls.
*/
void CC32Bca::Close()
{
CloseCommPort();
__FLOG_1(_L8("C32Bca::Close:Close: BCA closed with error %d"), KErrNone);
}
/**
* Queues a Read on C32 serial port.
* @param aStatus complete status, KErrNone if successful, C32 error code otherwise.
* @param aBuf buffer for data to be read.
* @note The buffer is owned by the client. Client must not access / modify the buffer until the Read completes.
*/
void CC32Bca::Read(TRequestStatus& aStatus,TDes8& aBuf)
{
iReadRequest = &aStatus;
*iReadRequest = KRequestPending;
if(iErrorOnNextRead)
{
User::RequestComplete(iReadRequest, iErrorOnNextRead);
// Error persists until the BCA is closed and opened
}
else
{
iReader->Read(aBuf);
}
}
/**
* Queues a Write on C32 serial port.
* @param aStatus the complete status, KErrNone if successful, C32 error code otherwise.
* @param aBuf the buffer to sent.
* @note The buffer is owned by the client. Client must not access / modify the buffer until the Write completes.
*/
void CC32Bca::Write(TRequestStatus& aStatus,const TDesC8& aBuf)
{
iWriteRequest = &aStatus;
*iWriteRequest = KRequestPending;
if(iErrorOnNextWrite)
{
User::RequestComplete(iWriteRequest, iErrorOnNextWrite);
// Error persists until BCA is closed and opened.
}
else
{
iWriter->Write(aBuf);
}
}
/**
Cancels the outstanding Read operation.(best effort operation: the read may have been completed already.)
*/
void CC32Bca:: CancelRead()
{
__FLOG(_L8("CancelRead: Read is cancelled by client."));
iReader->Cancel();
if(iReadRequest != NULL)
{
User::RequestComplete(iReadRequest, KErrCancel);
}
}
/**
Cancels all Write operations.(best effort attempt: the write may have been completed already.)
*/
void CC32Bca::CancelWrite()
{
__FLOG(_L8("CancelWrite: Write is cancelled by client."));
iWriter->Cancel();
if(iWriteRequest != NULL)
{
User::RequestComplete(iWriteRequest, KErrCancel);
}
}
// Debug dumps:
#ifdef __FLOG_ACTIVE
_LIT8(KLitOptLevelGeneric, "KBcaOptLevelGeneric");
_LIT8(KLitOptLevelExtSerial, "KBcaOptLevelExtSerial");
_LIT8(KLitOptLevelUnsupported, "**Unsupported**");
/**
Returns a string for an option level
@param aOptLevel BCA Option Level
@return readable string for aOptLevel
*/
static const TDesC8& IoctlOptLevelStr(TUint32 aOptLevel)
{
switch(aOptLevel)
{
case KBcaOptLevelGeneric:
return KLitOptLevelGeneric;
case KBcaOptLevelExtSerial:
return KLitOptLevelExtSerial;
default:
return KLitOptLevelUnsupported;
}
}
_LIT8(KLitOptNameBcaVersionNumber, "KBcaOptNameVersionNumber");
_LIT8(KLitOptNameBcaCaps, "KBcaOptNameBcaCaps");
_LIT8(KLitOptNameBcaSetIapId, "KBcaOptNameBcaSetIapId");
_LIT8(KLitOptNameBcaSetBcaStack, "KBcaOptNameBcaSetBcaStack");
_LIT8(KLitOptNameBcaResetBuffers, "KBcaOptNameBcaResetBuffers");
_LIT8(KLitOptNameSerialPortName, "KBcaOptNameSerialPortName");
_LIT8(KLitOptNameSerialConfig, "KBcaOptNameSerialSerialConfig");
_LIT8(KLitOptNameSerialSetConfig, "KBcaOptNameSerialSerialSetConfig");
_LIT8(KLitOptNameSerialSetCsyName, "KBcaOptNameBcaSetCsyName");
_LIT8(KLitOptNameSerialSetCommRole, "KBcaOptNameSetCommRole");
_LIT8(KLitSerialSetTxRxBufferSize, "KSerialSetTxRxBufferSize");
_LIT8(KLitSerialTxRxBufferSize, "KSerialTxRxBufferSize");
_LIT8(KLitSerialMonitorControlLines, "KSerialMonitorControlLines");
_LIT8(KLitSerialSetControlLines, "KSerialSetControlLines");
_LIT8(KLitOptNameUnsupported, "**Unsupported**");
/**
Returns a string for the given option name
@param aOptLevel BCA Option Name
@return readable string for aOptName
*/
static const TDesC8& IoctlOptNameStr(TUint32 aOptName)
{
switch(aOptName)
{
case KBCACaps:
return KLitOptNameBcaCaps;
case KVersionNumber:
return KLitOptNameBcaVersionNumber;
case KBCASetIapId:
return KLitOptNameBcaSetIapId;
case KBCASetBcaStack:
return KLitOptNameBcaSetBcaStack;
case KBCAResetBuffers:
return KLitOptNameBcaResetBuffers;
case KSerialPortName:
return KLitOptNameSerialPortName;
case KSerialConfig:
return KLitOptNameSerialConfig;
case KSerialSetConfig:
return KLitOptNameSerialSetConfig;
case KSerialSetCsyName:
return KLitOptNameSerialSetCsyName;
case KSerialSetCommRole:
return KLitOptNameSerialSetCommRole;
case KSerialSetTxRxBufferSize:
return KLitSerialSetTxRxBufferSize;
case KSerialTxRxBufferSize:
return KLitSerialTxRxBufferSize;
case KSerialMonitorControlLines:
return KLitSerialMonitorControlLines;
case KSerialSetControlLines:
return KLitSerialSetControlLines;
default:
return KLitOptNameUnsupported;
}
}
#endif // __FLOG_ACTIVE
#ifdef __FLOG_ACTIVE
/**
Print debug output of the TCommComfig parameters.
@param The configuration whose parameters should be logged.
*/
void CC32Bca::LogCommConfig(TCommConfig& c)
{
__FLOG_5(_L8("Rate[%d] DataBits[%d] StopBits[%d] Parity[%d] Handshake[0x%x]"), c().iRate, c().iDataBits, c().iStopBits, c().iParity, c().iHandshake);
__FLOG_4(_L8("ParityError[%d] Fifo[%d] SpecialRate[%d] terminatorCount[%d]"), c().iParityError, c().iFifo, c().iSpecialRate, c().iTerminatorCount);
__FLOG_1(_L8("Terminator[0x%x]"), c().iTerminator);
}
#endif // __FLOG_ACTIVE
/**
* Ioctl: asynchronously controls the C32Bca.
* @param aStatus complete status, KErrNone if successful, system-wide error code otherwise.
* @param aOptLevel option level to be used.
* @param aOptName option name to be used.
* @param aOpt an optional parameter,holds the option value on return or the option value to be set.
*/
void CC32Bca::Ioctl(TRequestStatus& aStatus, TUint aOptLevel, TUint aOptName, TDes8& aOpt)
{
__FLOG_4(_L8("Ioctl: Level[%S](0x%X) Name[%S](0x%X)."), &IoctlOptLevelStr(aOptLevel), aOptLevel, &IoctlOptNameStr(aOptName), aOptName);
aStatus = KRequestPending;
TInt ret(KErrNone); // If operation fails, this will be set to the failure code explicitly.
// Don't check the port status. Let C32 panic. It's the caller's responsibility to make sure that
// the port is in a valid state (for most operations, open & not having any Tx Rx ops outstanding)
if(aOptLevel == KBcaOptLevelGeneric)
{
switch(aOptName)
{
case KBCACaps:
{
const TPckg<TUint> capPckg(KBcaCapSerial);
aOpt = capPckg;
break;
}
case KVersionNumber:
{
const TPckg<TUint> versionPckg(KVersion); //lower 16 contains KC32BcaMinorversionNumber, upper 16 bit contains KC32BcaMajorVersionNumber.
aOpt = versionPckg;
break;
}
case KBCASetIapId:
{
TUint32 tempIapId = *(reinterpret_cast<const TUint32*>(aOpt.Ptr()));
__FLOG_1(_L8("Ioctl: IAP ID [%d] specified."),tempIapId);
if(tempIapId < 1 )
{
ret = KErrArgument;
}
else
{
iIapId = tempIapId;
}
break;
}
case KBCAResetBuffers:
{
TUint resetBufMaskArg = *(reinterpret_cast<const TUint32*>(aOpt.Ptr()));
TUint resetBufMask(0x0);
if(resetBufMaskArg & KResetRxBuf)
{
resetBufMask |= KCommResetRx;
}
if(resetBufMaskArg & KResetTxBuf)
{
resetBufMask |= KCommResetTx;
}
ret = iComm.ResetBuffers(resetBufMask);
break;
}
default:
{
ret = KErrNotSupported;
}
}
}
else if(aOptLevel == KBcaOptLevelExtSerial)
{
switch(aOptName)
{
case KSerialCaps:
{
iComm.Caps(aOpt);
break;
}
case KSerialConfig:
{
__FLOG(_L8("Ioctl: KSerialConfig"));
TCommConfig cfg;
iComm.Config(cfg);
#ifdef __FLOG_ACTIVE
LogCommConfig(cfg);
#endif // __FLOG_ACTIVE
TPckgBuf<TCommConfig> cfgBuf(cfg);
aOpt.Copy(cfgBuf);
break;
}
case KSerialSetConfig:
{
__FLOG(_L8("Ioctl: KSerialSetConfig"));
TCommConfig cfg(*(reinterpret_cast<const TCommConfig*>(aOpt.Ptr())));
#ifdef __FLOG_ACTIVE
LogCommConfig(cfg);
#endif // __FLOG_ACTIVE
ret = iComm.SetConfig(cfg);
break;
}
case KSerialPortName:
{
aOpt.Copy(iCommPort);
break;
}
case KSerialSetCsyName:
{
if(!iCommPortOpen)
{
iCsyName.Copy(*(
reinterpret_cast<const TName*>(aOpt.Ptr())
));
iCsyNameOverride = ETrue;
__FLOG_1(_L8("Ioctl: CSY Name set to [%S]"), &iCsyName);
}
else
{
__FLOG(_L8("Ioctl: Warning: Cannot set the CSY name because the Comm Port is already open."));
ret = KErrAlreadyExists;
}
break;
}
case KSerialSetCommRole:
{
if(!iCommPortOpen)
{
iCommRole = *(reinterpret_cast<const TCommRole*>(aOpt.Ptr()));
iCommRoleOverride = ETrue;
__FLOG_1(_L8("Ioctl: Comm Role set to [%d]"), iCommRole);
}
else
{
__FLOG(_L8("Ioctl: Warning: Cannot set Comm Role because the Comm Port is already open."));
ret = KErrAlreadyExists;
}
break;
}
case KSerialSetTxRxBufferSize:
{
TInt bufSize = *(reinterpret_cast<const TInt*>(aOpt.Ptr()));
__FLOG_1(_L8("Ioctl: Setting Rx Tx buffer size to [%d]"), bufSize);
iComm.SetReceiveBufferLength(bufSize);
break;
}
case KSerialMonitorControlLines:
{
TRAP(ret, MonitorControlLinesL(*(reinterpret_cast<const TUint*>(aOpt.Ptr()))));
break;
}
case KSerialSetControlLines:
{
if (aOpt.Length() != sizeof(TSerialSetControlLines))
{
ret = KErrArgument;
}
else
{
const TSerialSetControlLines& lines = *(reinterpret_cast<const TSerialSetControlLines*>(aOpt.Ptr()));
__FLOG_2(_L8("Ioctl: Setting/clearing control lines %x/%x"), lines.iSetMask, lines.iClearMask);
iComm.SetSignals(lines.iSetMask, lines.iClearMask);
ret = KErrNone;
}
break;
}
default:
ret = KErrNotSupported;
}
}
else
{
ret = KErrNotSupported;
}
__FLOG_1(_L8("Ioctl completed with error %d"), ret);
TRequestStatus* ptrStatus = &aStatus;
User::RequestComplete(ptrStatus, ret);
}
/** Enables / Disables monitoring & link down reporting on the specified control lines
@param aMask bitmaks specifying the lines to monitor, OR
KMonitorOff to turn monitoring off, OR
KConfigFailBcaSpecificOnly to set up monitoring according to Comm Role only
@leave if the monitoring object cannot be constructed, typically due to OOM.
*/
void CC32Bca::MonitorControlLinesL(TUint32 aArgMask)
{
__FLOG_1(_L8("Ioctl: argument bitmask = [0x%X] "), aArgMask);
if(iLinkMonitor) // We may have never started...
{
// We are called to either stop or change the monitoring options.
iLinkMonitor->Cancel(); // If we are not monitoring, this has no effect.
}
if(KMonitorOff == aArgMask) // Stop monitoring:
{
__FLOG(_L8("MonitorControlLinesL: Stopping Control Lines monitoring."));
// We either never started, or we just cancelled above.
}
else // Start Monitoring, or change the monitored lines.
{
__FLOG(_L8("MonitorControlLinesL: Starting to monitor Control Lines."));
if(!iLinkMonitor) // We are starting to monitor for the first time
{
iLinkMonitor = new (ELeave)CCommLinkMonitor(*this, iComm);
}
// At this point we have a monitoring object that isn't active.
// A CSY does not necessarily handle the KConfigFail* correctly. For example, if
// a DTR is initially low, because the terminal was not plugged in yet, we get a failure straight away,
// So, we translate the failure arguments and monitor the relevant lines ourselves. We fail only when a line
// is disasserted. This is absolutely necessary to support server-mode PPP, because we may need to wait
// an arbitrary period of time for the user to plug the terminal in.
TUint32 lineMask(0x0);
if(KFailBcaSpecificOnly == aArgMask) // We decide what lines to monitor
{
// N.B. When a DTE, fail on low DSR. This is necessary to detect a disconnection on serial links that
// do not utilise DCD.
lineMask = (ECommRoleDCE == iCommRole) ? KSignalDTR : KSignalDCD | KSignalDSR;
}
else // User explicitly specified the control lines.
{
// Don't bother with sanity checks on the lines. Let C32 handle it.
if(KConfigFailCTS & aArgMask)
{
lineMask |= KSignalCTS;
}
if(KConfigFailDSR & aArgMask)
{
lineMask |= KSignalDSR;
}
if(KConfigFailDCD & aArgMask)
{
lineMask |= KSignalDCD;
}
if(KConfigFailDTR & aArgMask)
{
lineMask |= KSignalDTR;
}
if(KConfigFailRTS & aArgMask)
{
lineMask |= KSignalRTS;
}
}
iLinkMonitor->Setup(lineMask);
iLinkMonitor->NotifyLinkDown();
__FLOG(_L8("MonitorControlLinesL: Control Lines monitoring started."));
}
}
/**
Closes the connection to CommDB, if it is open */
void CC32Bca::CloseCommDbConnection()
{
delete iCommsDat;
iCommsDat = NULL;
}
/**
Read Comm Port from CommDB bearer record
@param aPortName
@leave if the value could not be read */
void CC32Bca::ReadCommPortFromCommDbL(TDes& aPortName)
{
__FLOG(_L8("CC32Bca::ReadCommPortFromCommDbL()"));
ConnectToCommDbBearerRecordL();
TInt ret(0);
CMDBField<TDesC>* portField = new(ELeave) CMDBField<TDesC>(KCDTIdPortName);
CleanupStack::PushL(portField);
portField->SetRecordId(iModemId);
portField->SetMaxLengthL(KMaxTextLength);
TRAP(ret,portField->LoadL(*iCommsDat));
if(ret!=KErrNone)
{
__FLOG_1(_L8("portField->LoadL(*iCommsDat) left with[%d] "), ret);
User::Leave(ret);
}
aPortName = *portField;
CleanupStack::PopAndDestroy(portField);
}
/**
Read the CSY name from CommDB bearer record.
@param aCsyName the CSY name
@leave if the value could not be read */
void CC32Bca::ReadCsyNameFromCommDbL(TDes& aCsyName)
{
__FLOG(_L8("CC32Bca::ReadCsyNameFromCommDbL()"));
ConnectToCommDbBearerRecordL();
TInt ret(0);
CMDBField<TDesC>* csyField = new(ELeave) CMDBField<TDesC>(KCDTIdCsyName);
CleanupStack::PushL(csyField);
csyField->SetRecordId(iModemId);
csyField->SetMaxLengthL(KMaxTextLength);
TRAP(ret,csyField->LoadL(*iCommsDat));
if(ret!=KErrNone)
{
__FLOG_1(_L8("csyField->LoadL(*iCommsDat) left with[%d] "), ret);
User::Leave(ret);
}
aCsyName = *csyField;
CleanupStack::PopAndDestroy(csyField);
}
/**
Read the specified Comm role (DTE / DCE) from CommDB bearer record
@param aCommRole the specified role.
@leave if the value could not be read */
void CC32Bca::ReadCommRoleFromCommDbL(TCommRole& aCommRole)
{
__FLOG(_L8("CC32Bca::ReadCommRoleFromCommDbL()"));
ConnectToCommDbBearerRecordL();
TUint32 role(0);
TInt ret(0);
CMDBField<TUint32>* roleField = new(ELeave) CMDBField<TUint32>(KCDTIdCommRole);
CleanupStack::PushL(roleField);
roleField->SetRecordId(iModemId);
TRAP(ret,roleField->LoadL(*iCommsDat));
if(ret!=KErrNone)
{
__FLOG_1(_L8("roleField->LoadL(*iCommsDat) left with[%d] "), ret);
User::Leave(ret);
}
role = *roleField;
CleanupStack::PopAndDestroy(roleField);
aCommRole = (0 == (role & KModemCommRoleDCE)) ? ECommRoleDTE : ECommRoleDCE;
}
/**
Opens a connection to the ModemBearer record specified by the provisioned IAP ID.
@leave KErrNotReady, if the provisioned IAP id is invalid. System-wide error code
if the connection cannot be opened for some other reason.
*/
void CC32Bca::ConnectToCommDbBearerRecordL()
{
__FLOG(_L8("CC32Bca::ConnectToCommDbBearerRecordL()"));
if(iCommsDat != NULL) // CommDB is already open, we don't need to do anything
{
return;
}
if(iIapId < 1) // Can't access CommDB if IAP ID is unknown
{
__FLOG_1(_L8("iIapId[%d] is unknown"), iIapId);
User::Leave(KErrNotReady);
}
//
// Open connecton to CommDB Bearer record specified by the IAP.
//
#ifdef SYMBIAN_NON_SEAMLESS_NETWORK_BEARER_MOBILITY
iCommsDat = CMDBSession::NewL(KCDVersion1_2);
#else
iCommsDat = CMDBSession::NewL(KCDVersion1_1);
#endif
CCDIAPRecord *iapRecord = static_cast<CCDIAPRecord*>(CCDRecordBase::RecordFactoryL(KCDTIdIAPRecord));
CleanupStack::PushL(iapRecord);
iapRecord->SetRecordId(iIapId);
TBool hiddenAttributeMaskValue = iCommsDat->IsSetAttributeMask(ECDHidden);
TBool privateAttributeMaskValue = iCommsDat->IsSetAttributeMask(ECDPrivate);
// Check to see if viewing hidden records is enabled or not
if(!hiddenAttributeMaskValue || !privateAttributeMaskValue)
{
// Reveal hidden or private IAP records if a licensee has chosen to protect a record
// using one of these flags - the API to do this is public so internal components
// have to support the use of such records.
iCommsDat->SetAttributeMask(ECDHidden | ECDPrivate);
}
TRAPD(ret,iapRecord->LoadL(*iCommsDat));
if (ret != KErrNone)
{
__FLOG_1(_L8("iapRecord->LoadL(*iCommsDat) left with[%d] "), ret);
User::Leave(ret);
}
// If we enabled viewing hidden records now disable viewing them.
if(!hiddenAttributeMaskValue)
{
iCommsDat->ClearAttributeMask(ECDHidden);
}
if(!privateAttributeMaskValue)
{
iCommsDat->ClearAttributeMask(ECDPrivate);
}
iModemId = iapRecord->iBearer;
CleanupStack::PopAndDestroy(iapRecord);
if(iModemId == 0) // ID not found.
{
__FLOG_1(_L8("iModemId[%d] is not found"), iModemId);
User::Leave(KErrNotFound);
}
}
/** Cancels an outstanding Ioctl, if any. */
void CC32Bca::CancelIoctl()
{
__FLOG(_L8("CancelIoctl(): Ioctl cancel request. No Ioctl to cancel."));
}
/** Closes the Comm port and Comm Server.*/
void CC32Bca::CloseCommPort()
{
__ASSERT_ALWAYS(iReader, Panic(ENullReaderOnClose));
__ASSERT_ALWAYS(iWriter, Panic(ENullWriterOnClose));
// When we close the port, all async requests outstanding are cancelled.
// We must make sure nobody is waiting on them. If the underlying request is cancelled,
// but the owner AO is not, Cancel() on such AO hangs waiting for the request to complete.
iReader->Cancel();
iWriter->Cancel();
if(iLinkMonitor) // Link monitor is constructed on client request only.
{
iLinkMonitor->Cancel();
}
if (iCommPortOpen)
{
iComm.Close();
iCommServ.Close();
iCommPortOpen = EFalse;
}
__FLOG(_L8("CloseCommPort(): Session with C32 & RComm closed."));
}
/**
C32Bca Panic function
@internalComponent
*/
void C32Bca::Panic(TC32BcaPanic aPanic)
{
__FLOG_STATIC2(KC32BcaLogFolder,KC32BcaLogFile,_L8("%S Panic %d"), &KC32BcaPanic(), aPanic);
User::Panic(KC32BcaPanic, aPanic);
}
// MC32User implementation:
// Read has completed.
void CC32Bca::CommReadComplete(TInt aErr)
{
__ASSERT_ALWAYS(iReadRequest, Panic(ENullReadRequestStatus));
__ASSERT_DEBUG(KRequestPending == iReadRequest->Int(), Panic(EReadRequestNotPending));
User::RequestComplete(iReadRequest, aErr);
}
// Write has completed.
void CC32Bca::CommWriteComplete(TInt aErr)
{
__ASSERT_ALWAYS(iWriteRequest, Panic(ENullWriteRequestStatus));
__ASSERT_DEBUG(KRequestPending == iWriteRequest->Int(), Panic(EWriteRequestNotPending));
User::RequestComplete(iWriteRequest, aErr);
}
// Upcall from the link monitor: Link has gone down.
void CC32Bca::CommLinkDown(TInt aErr)
{
__FLOG_1(_L8("CommLinkDown: Warning: serial link has gone down with error[%d]. Erroring the outstanding Read & Write."), aErr);
__ASSERT_DEBUG(KErrNone != aErr, Panic(EGeneralLogicError)); // If KErrNone, use has no way to know that the read has failed.
if(iReader->IsActive())
{
CommReadComplete(aErr);
iReader->Cancel();
}
else
{
iErrorOnNextRead = aErr;
}
if(iWriter->IsActive())
{
CommWriteComplete(aErr);
iWriter->Cancel();
}
else
{
iErrorOnNextWrite = aErr;
}
}