diff -r 000000000000 -r 3553901f7fa8 telephonyprotocols/rawipnif/rawipnif2/src/RawIP2Flow.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/telephonyprotocols/rawipnif/rawipnif2/src/RawIP2Flow.cpp Tue Feb 02 01:41:59 2010 +0200 @@ -0,0 +1,676 @@ +// Copyright (c) 2006-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: +// Implements the CRawIPFlow class, which provides a central point for the NIF +// to communicate with NifMan and the BCA controllers. This class creates an interface +// to the IPv4 protocol when it is required. +// +// + +/** + @file +*/ + +#include +#include +#include +#include +#include +#include "IPv4Binder.h" +#include "IPv6Binder.h" +#include "RawIP2Flow.h" +#include "BcaController.h" +#include +#include +#include // for SAccessPointConfig +#include +#include +#include + +using namespace Messages; +using namespace MeshMachine; + +#ifdef __FLOG_ACTIVE +//These variables are used only if flog is active. +_LIT8(KTcpDumpFirstTag,"TcpDump"); +static const TUint16 KTcpDumpLinkType = 12; +#endif + +CRawIP2Flow::CRawIP2Flow(ESock::CSubConnectionFlowFactoryBase& aFactory, const Messages::TNodeId& aSubConnId, ESock::CProtocolIntfBase* aProtocolIntf, CBttLogger* aTheLogger) +/** + * Constructor. + * + * @param aFactory Reference to the factory which created this object. + * @param aSubConnId Reference to the node id for sub connection. + * @param aProtocolIntf pointer to the CProtocolIntfBase + * @param aTheLogger The logging object, ownership is passed to this object + */ + : CSubConnectionFlowBase(aFactory, aSubConnId, aProtocolIntf), + iTheLogger(aTheLogger), + iInitError(KErrNone), + iStarted(EFalse), + iStopping(EFalse) + { + LOG_NODE_CREATE(KNifSubDir, CRawIP2Flow); + } + +void CRawIP2Flow::ConstructL() +/** + * Second-phase constructor. Creates the Etel + * control engine + * + * @param aName The name of the NIF (unused) + */ + { + _LOG_L1C2(_L8("CRawIP2Flow %08x:\tConstructL()"), this); + + iBinderControl = new (ELeave) TBinderControlProxy(*this); + iBcaController = CBcaController::NewL(*this, iTheLogger); + } + +CRawIP2Flow::~CRawIP2Flow() +/** + * Destructor. Cancels and deletes all owned active objects. + */ + { + // Note that we don't delete iBinder because it's not owned by us. + delete iBcaController; + // This class also deletes the logging object + delete iTheLogger; + + ASSERT(iBinder == NULL); + + delete iBinderControl; + LOG_NODE_DESTROY(KNifSubDir, CRawIP2Flow); + // Cleanup when stop was not called - harmless as it null's the pointer + __PACKETLOG_DELETE; + } + +void CRawIP2Flow::StartFlowL() +/** + * Starts the Flow up. This process involves connecting to the BCA. + * + * Called in response to corresponding SCPR message. + * + * @return Allways KErrNone + */ + { + ASSERT(iStarting==EFalse); + iStarting = ETrue; + _LOG_L1C2(_L8("CRawIP2Flow %08x:\tStartFlowL()"), this); + + // If there were any errors during earlier processing of the ProvisionConfig message + // then send an Error message in response to the StartFlow (because ProvisionConfig + // does not have a response code). + if (iProvisionError != KErrNone) + { + User::Leave(iProvisionError); + } + + // Retrieve Agent provisioned information only available at StartFlow time + DynamicProvisionConfigL(); + InitialiseL(); + + // associate the binder object with the lowersender + iBinder->SetSender(&(iBcaController->Bca()->GetSender())); + //the name calculation should be done only if logging is enabled +#ifdef __FLOG_ACTIVE + const TUint KModemNameLen = KCommsDbSvrMaxColumnNameLength + 10; // need enough for ppp-XXX.txt - e.g. ppp-comm-1.txt, ppp-btcomm-10.txt etc + TBuf8 modemName; + + _LIT8(KDoubleColon, "::"); + + modemName.Copy(iBCAProvisionConfig->GetPortName()); + const TInt pos = modemName.FindC((const TDesC8&)KDoubleColon); + if(pos != KErrNotFound) + { + modemName.Delete(pos, 2); + } + + //append time stamp + const TUint KTimeStampLen = 8; + TBuf8 logFileName; + + _LIT8(KTimeFormat, "%08X"); + TUint32 counter = User::FastCounter(); + logFileName.Format(KTimeFormat, counter); + + TRAPD(err,__PACKETLOG_NEWL(KTcpDumpFirstTag, logFileName, CPacketLogger::ETcpDump, KTcpDumpLinkType)); + if (err) + { + _LOG_L1C1(_L8("Trapped leave from __PACKETLOG_NEWL")); + } + + const TUint KLogTextLen = KModemNameLen+KTimeStampLen+30; + TBuf8 logText; + _LIT8(KLogTimeText, "TcpDump log file time stamp:"); + _LIT8(KLogModemText, " for modem:"); + logText.Append(KLogTimeText); + logText.Append(logFileName); + logText.Append(KLogModemText); + logText.Append(modemName); + _LOG_L1C1(logText); +#endif + } + + +void CRawIP2Flow::LinkLayerUp() +/** + * This function is called when initialisation is complete and the Flow is + * ready to send/receive data. It notifies NifMan and the IP interface that it's + * ready to go. + */ + { + _LOG_L1C2(_L8("CRawIP2Flow %08x:\tLinkLayerUp()"), this); + + iLastRequestOriginator.ReplyTo(Id(), ESock::TCFDataClient::TStarted().CRef()); + + iStarted = ETrue; + iStarting = EFalse; + } + +void CRawIP2Flow::LinkLayerDown(TInt aError) +/** + * This function is called when the context has been deactivated and the NIF + * is ready to be deleted, or if there is an error on startup. Its job is to + * notify NifMan that the link has gone down, so it can delete the NIF. + * + * @param aError An error code to propagate to NifMan + */ + { + _LOG_L1C3(_L8("CRawIP2Flow %08x:\tLinkLayerDown(aError %d)"), this, aError); + __PACKETLOG_DELETE; + + if (iStopping) + { + iLastRequestOriginator.ReplyTo(Id(), ESock::TCFDataClient::TStopped(aError).CRef()); + } + else if (iStarting) + { + iSubConnectionProvider.PostMessage(Id(), TEBase::TError(ESock::TCFServiceProvider::TStart::Id(), aError).CRef()); + } + else + { + iSubConnectionProvider.PostMessage(Id(), ESock::TCFControlProvider::TDataClientGoneDown(aError, MNifIfNotify::EDisconnect).CRef()); + } + + iStarted = EFalse; + iStarting = EFalse; + iStopping = EFalse; + } + +void CRawIP2Flow::StopFlow(TInt aError) +/** + * This function is called by NifMan to bring down the link. It starts + * the asynchronous context deactivation process; LinkLayerDown() will be + * called when the context has been deactivated. + * + * @param aError A code specifying why the link is to be brought down + * @param aAction The action to take: disconnect or reconnect + */ + { + _LOG_L1C3(_L8("CRawIP2Flow %08x:\tStopFlow(aError %d)"), this, aError); + __PACKETLOG_DELETE; + iStopping = ETrue; + ShutDown(aError); + } + + +void CRawIP2Flow::InitialiseL() +/** + * This method controls the initialisation sequence of the different modules + * in the Flow. When a module completes all the initialisation tasks it will + * call again this method with the new state. If an error occurs then the + * ShutDown() method will be called. + * + * @param aState The current initialisation step + * @param aError A possible error + */ + { + _LOG_L1C2(_L8("CRawIPFlow %08x:\tInitialiseL"),this); + + // Update local state from TSY information originally from Agent. + GetBinder()->UpdateContextConfigL(*iAgentProvision->iGprsConfig); + GetBinder()->UpdateConnectionSpeed(iAgentProvision->iConnectionSpeed); + + UpdateContextState(RPacketContext::EStatusActive, KErrNone); + + iBcaController->StartLoadL(iBCAProvisionConfig,iBinder,iBinder); + } + +void CRawIP2Flow::ShutDown(TInt aError) +/** + * This method controls the shutdown sequence of the different modules + * in the Flow. When a module completes all the shutdown tasks it will + * call again this method with the new state. + * + * @param aState The current state + * @param aError A possible error (only during initialisation) + */ + { + _LOG_L1C3(_L8("CRawIPFlow %08x:\tShutDown(aError %d)"),this, aError); + + if (aError != KErrNone) + { + // Flow is shutting down due to an error. + // The error code must be stored in order to call linklayer down + // passing that value + iInitError = aError; + } + + iBcaController->Stop(aError); + LinkLayerDown(iInitError); + } + +CBinderBase* CRawIP2Flow::GetBinder() +/** + * Returns a pointer to the Binder class + * + * @return a pointer to a CBinderBase derived class + */ + { + return iBinder; + } + +CBcaController* CRawIP2Flow::GetBcaController() +/** + * Returns a pointer to the CBcaIoController class + * + * @return a pointer to CBcaIoController + */ + { + return iBcaController; + } + +void CRawIP2Flow::UpdateContextState( + RPacketContext::TContextStatus aState, TInt /*aError*/) +/** + * Updates the state of the iContextState variable + * + * @param aState The new state + * @param aError A possible error + */ + { + _LOG_L1C3(_L8("CRawIP2Flow %08x:\tUpdateContextState(aState %d)"), this, aState); + + // Note that we do not need to close the Flow down if there's been an + // error, as the context state machine will do this for us. + + iContextStatus = aState; + } + +// ===================================================================================== +// +// CSubConnectionFlowBase +// + +ESock::MFlowBinderControl* CRawIP2Flow::DoGetBinderControlL() + { + return iBinderControl; + } + +// ===================================================================================== +// +// MFlowBinderControl methods +// + +ESock::MLowerControl* CRawIP2Flow::GetControlL(const TDesC8& aProtocol) + { +#ifdef __BTT_LOGGING__ + //TBuf8<256> debugBuffer; + //debugBuffer.Copy(aProtocol); + _LOG_L1C3(_L8("CRawIP2Flow %08x:\tGetControlL(aProtocol %S)"), this, &aProtocol); +#endif + + // IPv4 and ICMP Protocols + if (aProtocol.CompareF(KDescIp()) == 0 || aProtocol.CompareF(KDescIcmp()) == 0) + { + if (!iBinder) + { + iBinder = new (ELeave) CIPv4Binder(*this, iTheLogger); +#ifdef RAWIP_HEADER_APPENDED_TO_PACKETS + iBinder->SetType(KIp4FrameType); +#endif // RAWIP_HEADER_APPENDED_TO_PACKETS + } + else + { + _LOG_L1C2(_L8("CRawIP2Flow %08x:\tGetControlL(): IPv4 binder already exists"), this); + } + } + // IPv6 Protocol + else if (aProtocol.CompareF(KDescIp6()) == 0) + { + if (!iBinder) + { + iBinder = new (ELeave) CIPv6Binder(*this, iTheLogger); +#ifdef RAWIP_HEADER_APPENDED_TO_PACKETS + iBinder->SetType(KIp6FrameType); +#endif // RAWIP_HEADER_APPENDED_TO_PACKETS + } + else + { + _LOG_L1C2(_L8("CRawIP2Flow %08x:\tGetControlL(): IPv6 binder already exists"), this); + } + } + else + { + User::Leave(KErrNotSupported); + } + + return iBinder; + } + +ESock::MLowerDataSender* CRawIP2Flow::BindL(const TDesC8& aProtocol, ESock::MUpperDataReceiver* aReceiver, ESock::MUpperControl* aControl) +/** + * Binds upper CFProtocol to this CFProtocol + * + * @param aUpperReceiver A pointer to Upper layer Receive class + * @param aUpperControl A pointer to Upper layer control class + */ + { +#ifndef _DEBUG + (void) aProtocol; +#endif + + ASSERT (aProtocol.CompareF(KDescIp()) == 0 || aProtocol.CompareF(KDescIcmp()) == 0 + || aProtocol.CompareF(KDescIp6()) == 0); + ASSERT(iBinder); + + iSubConnectionProvider.PostMessage(Id(), ESock::TCFControlProvider::TActive().CRef()); + return iBinder->Bind(aReceiver, aControl); + } + +void CRawIP2Flow::Unbind(ESock::MUpperDataReceiver* aUpperReceiver, ESock::MUpperControl* aUpperControl) + { + _LOG_L1C2(_L8("CRawIP2Flow %08x:\tUnbind()"), this); + + ASSERT(iBinder); + iBinder->Unbind(aUpperReceiver, aUpperControl); + + delete iBinder; + iBinder = NULL; + + SendDataClientIdleIfNoClients(); + } + + +void CRawIP2Flow::SendDataClientIdleIfNoClients() + { + if (iBinder == NULL) + { + iSubConnectionProvider.PostMessage(Id(), ESock::TCFControlProvider::TIdle().CRef()); + } + } + +ESock::CSubConnectionFlowBase* CRawIP2Flow::Flow() +/** +Return the Flow corresponding to the MFlowBinderControl +*/ + { + return this; + } + +// ===================================================================================== +// +// MCFNode +// + +void CRawIP2Flow::ReceivedL(const TRuntimeCtxId& aSender, const TNodeId& aRecipient, TSignatureBase& aMessage) + { + CSubConnectionFlowBase::ReceivedL(aSender, aRecipient, aMessage); + + if (aMessage.IsMessage()) + { + SubConnectionError(static_cast(aMessage).iValue); + } + else if (TEChild::ERealmId == aMessage.MessageId().Realm()) + { + switch (aMessage.MessageId().MessageId()) + { + case TEChild::TDestroy::EId : + Destroy(); + break; + default: +//TODO - logging + ASSERT(EFalse); + } + } + else if (ESock::TCFDataClient::ERealmId == aMessage.MessageId().Realm()) + { + switch (aMessage.MessageId().MessageId()) + { + case ESock::TCFDataClient::TStart::EId : + StartFlowL(); + break; + case ESock::TCFDataClient::TStop::EId : + StopFlow(static_cast(aMessage).iValue); + break; + case ESock::TCFDataClient::TProvisionConfig::EId: + ProvisionConfig(static_cast(aMessage).iConfig); + break; + case ESock::TCFDataClient::TBindTo::EId: + { + ESock::TCFDataClient::TBindTo& bindToReq = message_cast(aMessage); + if (!bindToReq.iNodeId.IsNull()) + { + User::Leave(KErrNotSupported); + } + RClientInterface::OpenPostMessageClose(Id(), aSender, ESock::TCFDataClient::TBindToComplete().CRef()); + } + break; + default: +//TODO - logging + ASSERT(EFalse); + } + } + else if (ESock::TCFFlow::ERealmId == aMessage.MessageId().Realm()) + { + switch (aMessage.MessageId().MessageId()) + { + case ESock::TCFFlow::TBlock::EId : + iBlocked = ETrue; + break; + case ESock::TCFFlow::TUnBlock::EId : + iBlocked = EFalse; + if (iBinder) + { + iBinder->StartSending(); + } + break; + case ESock::TCFFlow::TRejoin::EId: + { + ESock::TCFFlow::TRejoin& rejoinMsg = message_cast(aMessage); + ESock::TDefaultFlowFactoryQuery query(rejoinMsg.iNodeId); + CRawIP2Flow* otherFlow = static_cast(Factory().Find(query)); + ASSERT(otherFlow); + + //This rather ugly looking snipped of code implements + //the scenario whereat the network tears down the default + //scpr and the default scpr selects a new default amongst + //the non-default and subsequently performs a transparant + //swap - i.e.: the upper layer perceives this as a + //non-default going down. Since the datapath is pre-bound + //to the flow representing the default, the datapath needs + //to perform a transparent swap too, so that the upper layer + //doesn't notice anything. It does that by swapping the + //flows below the binders. + CBinderBase* localBinder = iBinder; + CBttLogger* logger = localBinder->iTheLogger; + + TBinderControlProxy* localBinderControl = iBinderControl; + iBinder = otherFlow->iBinder; + otherFlow->iBinder->ChangeFlow(*this); + localBinder->ChangeFlow(*otherFlow); + otherFlow->iBinder = localBinder; + iBinderControl = otherFlow->iBinderControl; + otherFlow->iBinderControl = localBinderControl; + otherFlow->iBinderControl->iFlow = otherFlow; + iBinderControl->iFlow = this; + + iBinder->iTheLogger = logger; + + iSubConnectionProvider.Close(); + iSubConnectionProvider.Open(address_cast(rejoinMsg.iNodeId)); + } + break; + default: +//TODO - logging + ASSERT(EFalse); + } + } + else // not a recognised realm or message + { + ASSERT(EFalse); + } + } + + +// ===================================================================================== +// +// Methods for handling incoming SCPR messages +// + +void CRawIP2Flow::SubConnectionGoingDown() + { + } + +void CRawIP2Flow::SubConnectionError(TInt /*aError*/) + { + } + +/* +Provisioning description for RawIp CFProtocol Flow: + +- on receipt of the ProvisionConfig message, the pointer contained within is stored + in iAccessPointConfig and the provisioning information contained within it is validated: + - CBCAProvision must be present. It is added by the RawIpMCPr and populated from CommsDat. A pointer to it + is stored in iProvision. + if any are missing, an error is stored in iProvisionError and sent in response to the StartFlow + message later on (ProvisionConfig has no response). + +- on receipt of the StartFlow message, further provisioning information is retrieved: + - CIPConfig must be present. It is added by the RawIpMCPr and populated from CommsDat. The binder retrieves + individual fields from it. + - CRawIpAgentConfig must be present. It is added by the RawIpAgentHandler and populated via calls + to the Agent. A pointer to it is stored in iAgentProvision. Note that the iGprsConfig field + is a pointer to a (large) structure in the Agent (see CRawIpAgentHandler). + if any of the above are missing, an Error message is signalled back to the SCPr in response + to the StartFlow. +*/ + +void CRawIP2Flow::ProvisionConfig(const ESock::RMetaExtensionContainerC& aConfigData) +/** +Handle ProvisionConfig message from SCPR. + +Just save the pointer for now - validate it later on StartFlow. + +@param aData provisioning pointer from message +*/ + { + _LOG_L1C2(_L8("CRawIP2Flow %08x:\tProvisionConfig()"), this); + iProvisionError = KErrNone; + + AccessPointConfig().Close(); + AccessPointConfig().Open(aConfigData); + + ASSERT(iBCAProvisionConfig == NULL); + iBCAProvisionConfig = static_cast(AccessPointConfig().FindExtension(STypeId::CreateSTypeId(CBCAProvision::EUid, CBCAProvision::ETypeId))); + + if (iBCAProvisionConfig == NULL) + { + _LOG_L1C1(_L8("CRawIP2Flow:\tProvisionConfigL() - CBCAProvision config incomplete")); + iProvisionError = KErrCorrupt; + return; + } + } + +void CRawIP2Flow::DynamicProvisionConfigL() +/** +Retrieve provisioning information available only at StartFlow time. +*/ + { + ASSERT(iAgentProvision == NULL); + iAgentProvision = static_cast(AccessPointConfig().FindExtension(STypeId::CreateSTypeId(CRawIpAgentConfig::EUid, CRawIpAgentConfig::ETypeId))); + + if (iAgentProvision == NULL) + { + User::Leave(KErrCorrupt); + } + + const CIPConfig* wcdmaIpProvision = static_cast(AccessPointConfig().FindExtension(STypeId::CreateSTypeId(CIPConfig::EUid, CIPConfig::ETypeId))); + if (wcdmaIpProvision == NULL) + { + _LOG_L1C2(_L8("CRawIP2Flow %08x:\tDynamicProvisionConfigL() - WCDMA config incomplete"), this); + iProvisionError = KErrCorrupt; + return; + } + // Pass pointer to binder specific provisioning information to binder + if (iBinder) + { + iBinder->SetProvision(*wcdmaIpProvision); + } + } + + +void CRawIP2Flow::Destroy() +/** +Handle Destroy message from SCPR. +*/ + { + DeleteThisFlow(); + } + +ESock::MLowerControl* CRawIP2Flow::TBinderControlProxy::GetControlL(const TDesC8& aProtocol) + { + return RawIP2Flow().GetControlL(aProtocol); + } + +ESock::MLowerDataSender* CRawIP2Flow::TBinderControlProxy::BindL(const TDesC8& aProtocol, ESock::MUpperDataReceiver* aReceiver, ESock::MUpperControl* aControl) + { + return RawIP2Flow().BindL(aProtocol, aReceiver, aControl); + } + +void CRawIP2Flow::TBinderControlProxy::Unbind(ESock::MUpperDataReceiver* aReceiver, ESock::MUpperControl* aControl) + { + return RawIP2Flow().Unbind(aReceiver, aControl); + } + +ESock::CSubConnectionFlowBase* CRawIP2Flow::TBinderControlProxy::Flow() + { + return iFlow; + } + +// ===================================================================================== +// +// Utility functions +// + +void CRawIP2Flow::PostProgressMessage(TInt aStage, TInt aError) + { + iSubConnectionProvider.PostMessage(Id(), ESock::TCFMessage::TStateChange(Elements::TStateChange( aStage, aError)).CRef()); + } + +// +// NOTE: +// Attribute table should be in the control side module that instantiates CRawIpProvision. +// RawIP CFProtocol does not instantiate the CRawIpProvision object, so does not need to +// define the attribute tables. The attribute table can therefore be placed directly into +// the control side module. +// +// The reason for this is to save splitting RawIP CFProtocol into two DLL's - ECOM and Main - +// just for control side module to link against Main DLL to retrieve the attribute table. +// In fact, it can be placed into control side module as "START_ATTRIBUTE_TABLE" rather +// than "EXPORT_START_ATTRIBUTE_TABLE". In fact, do we just need "TYPEID_TABLE" ? +// +// EXPORT_START_ATTRIBUTE_TABLE(CRawIpProvision, KRawIpConfigUid, KRawIpConfigType) +// END_ATTRIBUTE_TABLE()