diff -r 000000000000 -r 3553901f7fa8 telephonyprotocols/gprsumtsqosprt/src/iface.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/telephonyprotocols/gprsumtsqosprt/src/iface.cpp Tue Feb 02 01:41:59 2010 +0200 @@ -0,0 +1,704 @@ +// Copyright (c) 2007-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: +// + +#include + +#include "context.h" +#include "async_request.h" +#include "iface.h" +#include "tc.h" +#include "guqos.h" +#include "guqos_log.h" + +CNif* CNif::NewL(CNifIfBase& aInterface, CModuleGuqos& aModule) + { + CNif* nif = new (ELeave) CNif(aInterface, aModule); + CleanupStack::PushL(nif); + nif->ConstructL(); + CleanupStack::Pop(); + return nif; + } + +// XNifTimeoutLinkage +// ****************** +// Glue to bind timeout callback from the timeout manager into Timeout() call +// on the CPdpContext +// +// *NOTE* +// This kludgery is all static and compile time, and only used in the constructor +// of CPdpContext. +// + +// This ungainly manoevure is forced on us because the offset is not evaluated early enough by GCC3.4 to be +// passed as a template parameter +#if defined(__X86GCC__) || defined(__GCCE__) +#define KNifTimeoutOffset 2144 +__ASSERT_COMPILE(KNifTimeoutOffset == _FOFF(CNif, iTimeout)); +#else +#define KNifTimeoutOffset _FOFF(CNif, iTimeout) +#endif + +class XNifTimeoutLinkage : public TimeoutLinkage + { +public: + static void Timeout(RTimeout &aLink, const TTime & /*aNow*/, TAny * /*aPtr*/) + { + Object(aLink)->RunPendingRequests(); + } + }; + +CNif::CNif(CNifIfBase& aNif, CModuleGuqos& aModule) : iNif(aNif), iModule(aModule), iTimeout(XNifTimeoutLinkage::Timeout) + { + LOG(Log::Printf(_L("new\tCNif[%u] size=%d"), (TInt)this, sizeof(*this))); + iContexts.SetOffset(_FOFF(CPdpContext, iNext)); + iPending.SetOffset(_FOFF(CRequestBase, iLink)); + } + +void CNif::ConstructL() + { + // The instance for the primary context exists always for CNif + iPrimary = CPdpContext::NewL(*this, EPrimaryContext, 0); + AddContext(*iPrimary); + + User::LeaveIfError(RegisterEventHandler()); + + // Get IapId from Nif + TSoIfConnectionInfo netinfo; + TPckg option(netinfo); + if (iNif.Control(KSOLInterface, KSoIfGetConnectionInfo, option) == KErrNone) + iIapId = netinfo.iIAPId; + } + +CNif::~CNif() + { + LOG(Log::Printf(_L("~\tCNif[%u] Start"), (TInt)this)); + // No upcalls from NIF, we are in destructor! + CloseInterface(); + + // Unconditionallly terminate all request activities + + // Note: CompleteAndDestruct ends up calling "CloseRequest", + // which may attempt to activate the next request in queue. To avoid + // this, set iCurrentRequest to NULL. + iCurrentRequest = NULL; + while (!iPending.IsEmpty()) + { + CRequestBase* request = iPending.First(); + request->CompleteAndDestruct(KErrDied, NULL); + } + + // Remove all PDP Contexts from NIF and GUQOS + while (!iContexts.IsEmpty()) + { + CPdpContext *context = iContexts.First(); + // Remove context from iContexts list before + // giving control out (to NIF) After this + // the context instance is no more "reachable" + // from anywhere else. + RemoveContext(*context); + context->Delete(); // Delete context from NIF + delete context; + } + iContexts.Reset(); + iTimeout.Cancel(); + LOG(Log::Printf(_L("\tCNif[%u] Destruction Completed"), (TInt)this)); + } + +// Initialize the iParameters and return reference +TContextParameters& CNif::ContextParameters() + { + //?? Hope this doesn't generate a memory leak? + //?? is iParameters.iContextConfig.Reset() required instead? + iParameters = TContextParameters(); + return iParameters; + } + + +CPdpContext* CNif::FindContext(TInt aContextId) + { + TContextIter iter(iContexts); + CPdpContext *context; + + while ((context = iter++) != NULL) + if (context->ContextId() == aContextId) + return context; + return NULL; + } + +CPdpContext* CNif::FindChannel(TInt aChannelId) + { + TContextIter iter(iContexts); + CPdpContext *context; + + while ((context = iter++) != NULL) + if (context->ChannelId() == aChannelId) + return context; + return NULL; + } + + +// Choose new default Pdp context with the highest QoS profile (according to TS 23.107). +// +// *Note* +// Because in 3GPP there can be only ONE Context without any TFT's, this code +// assumes that the lower layer (NIF) has already deleted the current primary +// context. If this is not true, the TFT removal will fail at 3GPP level! +void CNif::SelectNewDefaultContext() + { + TContextIter iter(iContexts); + CPdpContext* context; + CPdpContext* highestQoS=NULL; + + while ((context = iter++) != NULL) + { + if (context == DefaultPdpContext()) + continue; // Exclude the current default from the search. + + //lint -e{961} would want terminating 'else' (we don't) + if (!highestQoS) + highestQoS = context; + else if (context->GetQoSRanking() < highestQoS->GetQoSRanking()) + highestQoS = context; + } + if (highestQoS == NULL) + { + // No other contexts available, cannot delete or change the primary! + LOG(Log::Printf(_L("\tOnly primary context available -- cannot be deleted"))); + return; + } + // + // Assign the new default context! + // (iPrimary pointer is only additional reference, it does not "own" the + // pointed object, thus just overwriting it is ok -- all contexts are in + // iContexts list). + iPrimary = highestQoS; + LOG(Log::Printf(_L("\tContext %d (channel=%d) is the new default context"), iPrimary->ContextId(), iPrimary->ChannelId())); + // Use the CClose request to clean out all TFT filters + // from the context (when context is primary, the RemoveFilters + // removes all, regardless of how many flows are connected). + CClose* request = CClose::New(*highestQoS); + if (request) + { + highestQoS->Nif().AddRequest(*request); + } + else + { + // If allocation of the request fails, there is not much that can + // be done, the filters will be left there and packets not matching + // them will be dropped. However, the cleanup will be attempted + // again any time the context is modified in such way that filters + // need to be removed. + LOG(Log::Printf(_L("\tCould not remove TFTs from new default context -- no room for request"))); + } + } + +// delete PDP context, and bind flows using this context to default PDP context +void CNif::DeletePdpContext(CPdpContext* aContext) + { + + if (DefaultPdpContext() == aContext) + { + // Attempt to select a new default context (may fail) + SelectNewDefaultContext(); + } + + CPdpContext *pdpDefault = DefaultPdpContext(); + if (pdpDefault != aContext) + { + while (!aContext->Flows().IsEmpty()) + { + CFlowData* flow = aContext->Flows().First(); + // The SetContext will remove the flow from old + // context, and eventually the aContext->Flows() + // *MUST* become empty + // (because aContext != pdpContext) + flow->SetContext(pdpDefault); + } + RemoveContext(*aContext); + delete aContext; + } + else + { + // Cannot delete the default context, just block flows (as the + // context is not actually existing any more). + aContext->Block(); + } + } + +TInt CNif::RegisterEventHandler() + { + TPckgBuf opt; + opt().iEvent = this; + LOG(Log::Printf(_L("\tcall NIF Control(KRegisterEventHandler)"))); + return iNif.Control(KSOLInterface, KRegisterEventHandler, opt); + } + +TInt CNif::SetEvents(TBool aValue) + { + TPckgBuf opt; + opt() = aValue; + LOG(Log::Printf(_L("\tcall NIF Control(KContextSetEvents. %d)"), aValue)); + return iNif.Control(KSOLInterface, KContextSetEvents, opt); + } + + +// event received from Nif when the Network status changes +TInt CNif::NetworkStatusEvent(const TNetworkParameters& aNetworkEvent) + { + switch(aNetworkEvent.iNetworkEventCode) + { + + case KNetworkConnectionLost: + break; + + case KNetworkInterfaceDown: + iModule.IfManager()->DeleteNif(this); + break; + + default: + return KErrNotSupported; + } + + return KErrNone; + } + + +// Receive events from umtsnif +TInt CNif::Event(CProtocolBase* aProtocol, TUint aName, TDes8& aOption, TAny* /*aSource*/) + { + LOG(Log::Printf(_L(""))); // just make empty line into log +#ifdef _LOG + TBuf<40> name; + + switch(aName) + { + case KContextDeleteEvent: name.Append(_L("KContextDeleteEvent")); break; + case KContextActivateEvent: name.Append(_L("KContextActivateEvent")); break; + case KContextParametersChangeEvent: name.Append(_L("KContextParametersChangeEvent")); break; + case KContextBlockedEvent: name.Append(_L("KContextBlockedEvent")); break; + case KContextUnblockedEvent: name.Append(_L("KContextUnblockedEvent")); break; + case KNetworkStatusEvent: name.Append(_L("KNetworkStatusEvent")); break; + case KContextQoSSetEvent: name.Append(_L("KContextQoSSetEvent")); break; + case KContextTFTModifiedEvent: name.Append(_L("KContextTFTModifiedEvent")); break; + case KPrimaryContextCreated: name.Append(_L("KPrimaryContextCreated")); break; + case KSecondaryContextCreated: name.Append(_L("KSecondaryContextCreated")); break; + case KContextModifyActiveEvent: name.Append(_L("KContextModifyActiveEvent")); break; + case KGetNegQoSEvent: name.Append(_L("KGetNegQoSEvent")); break; + default: name.Append(_L("error")); break; + } + + LOG(Log::Printf(_L("CNif::Event,aName: %d, name: {%S}"), aName, &name)); +#else + LOG(Log::Printf(_L("CNif::Event(name=%d)"), aName)); +#endif + +#ifndef SYMBIAN_NON_SEAMLESS_NETWORK_BEARER_MOBILITY +//PREQ399 had to disable this check because in the new architecture, in order to +//plug UmtsIf capable CFProtos (formerly Nifs) they need to be below IProto, +//and hence aProtocol will never match the iNif reference that we have here. + if ((CNifIfBase *)aProtocol != &iNif) + { + LOG(Log::Printf(_L("\tThe CNifIfBase[%u] parameter does not match my CNifIBase[%u]"), (TInt)aProtocol, (TInt)&iNif)); + return KErrNotFound; // Not called from what we expect! (invalid use of API) + } +#else + (void)aProtocol; +#endif + // Handle KNetworkStatusEvent first. + if (aName == KNetworkStatusEvent) + { + if (aOption.Length() >= (TInt)sizeof(TNetworkParameters)) + { + const TNetworkParameters& opt = *(TNetworkParameters*)aOption.Ptr(); + NetworkStatusEvent(opt); + return KErrNone; + } + LOG(Log::Printf(_L("\tThe aOption length=%d is too short for TNetworkParameters (size=%d)"), + aOption.Length(), (TInt)sizeof(TNetworkParameters))); + return KErrArgument; + } + + // All remaining valid events use TContextParameters, do the shared + // checking and preprocess before the switch... + if (aOption.Length() != (TInt)sizeof(TContextParameters)) + { + LOG(Log::Printf(_L("\tThe aOption length=%d does not match TContextParamaters (size=%d)"), + aOption.Length(), (TInt)sizeof(TContextParameters))); + return KErrArgument; + } + const TContextParameters& opt = *(TContextParameters*)aOption.Ptr(); + + // PrimaryContextCreated/SecondaryContextCreated do not have the context + // id in opt yet, handle those separately here (because searching PDP + // context by id would fail. + if (aName == KPrimaryContextCreated) + { + return PrimaryContextCreated(opt); + } + else if (aName == KSecondaryContextCreated) + { + SecondaryContextCreated(opt); + return KErrNone; + } + + // Even if the aOption is not TContextParameters, nothing bad happens. + // In that case teh context is searced with random id, which either is + // found or not. + CPdpContext *context = FindContext(opt.iContextInfo.iContextId); + if (context == NULL) + { + return KErrNotFound; // Context does not exist any more + } + + switch (aName) + { + case KContextUnblockedEvent: + // independent of any request pending + // NOTE: NIF *MUST* always provide the correct context status for Unblock + context->SetContextStatus(opt.iContextInfo.iStatus); + context->UnBlock(); + return KErrNone; + + case KContextBlockedEvent: + // independent of any request pending + // NOTE: NIF *MUST* always provide the correct context status for Block + context->SetContextStatus(opt.iContextInfo.iStatus); + context->Block(); + return KErrNone; + + case KContextDeleteEvent: + // independent of any request pending + context->SetContextStatus(opt.iContextInfo.iStatus); + + // qos framework has to be notified!!! + context->DeleteEvent(opt); + DeletePdpContext(context); + return KErrNone; + + case KContextParametersChangeEvent: + // independent of any request pending + context->ParametersChangedEvent(opt); + return KErrNone; + + case KContextQoSSetEvent: + context->SetQoSReply(iCurrentRequest, opt); + return KErrNone; + + case KContextTFTModifiedEvent: + context->ModifyTftReply(iCurrentRequest, opt); + return KErrNone; + + + case KContextActivateEvent: + context->ActivateReply(iCurrentRequest, opt); + return KErrNone; + + case KContextModifyActiveEvent: + context->ModifyActiveReply(iCurrentRequest, opt); + return KErrNone; + + default: + break; + } + return KErrNotSupported; + } + + +void CNif::SetDefaultQoS() + { + LOG(Log::Printf(_L("\tSetDefaultQoS -- begin"))); + TQoSRequested policy; + TInt ret = iModule.GetDefaultParameters(policy, iIapId); + if (ret == KErrNone) + { + TContextParameters& parameters(ContextParameters()); + parameters.iContextConfig.SetUMTSQoSReq(policy); + TPckg opt(parameters); + LOG(Log::Printf(_L("\tcall NIF Control(KNifSetDefaultQoS)"))); + iNif.Control(KSOLInterface, KNifSetDefaultQoS, opt); + } + LOG(Log::Printf(_L("\tSetDefaultQoS -- end"))); + } + +void CNif::AddContext(CPdpContext& aContext) + { + iContexts.AddLast(aContext); + } + +TInt CNif::PrimaryContextCreated(const TContextParameters& aParams) + { + TNifIfInfo info; + iNif.Info(info); + LOG(Log::Printf(_L("CNif::PrimaryContextCreated [Nif=%S]"),&info.iName)); + + iPrimary->SetContextStatus(aParams.iContextInfo.iStatus); + + SetStatus(EReady); + IssueRequest(); + return KErrNone; + } + +void CNif::SecondaryContextCreated(const TContextParameters& aParams) + { + if (iCurrentRequest) + { + SetStatus(EReady); + CPdpContext* context=NULL; + TRAPD(err, context = CPdpContext::NewL(*this, ESecondaryContext, aParams.iContextInfo.iContextId)); + if (err == KErrNone) + { + //coverity[leave_without_push] + context->SetContextStatus(aParams.iContextInfo.iStatus); + AddContext(*context); + iCurrentRequest->Run(EPendingCreate, context, aParams); + return; // Do not fall to context destruction. + } + else + { + iCurrentRequest->CompleteAndDestruct(err, NULL); + // Fall to context destruction... + } + } + // Either there was no request to receive this, or running low on memory + LOG(Log::Printf(_L("CNif::SecondaryContextCreated -- but, GUGOS cannot use it, deleting context"))); + TPckg options(aParams); + iNif.Control(KSOLInterface, KContextDelete, options); + } + +// Issue the creation of secondary PDP context +TInt CNif::NewPdpContext() + { + LOG(Log::Printf(_L("\t\tCNif::NewPdpContext -- request creation of secondary context"))); + TPckg opt(ContextParameters()); + opt().iContextType = ESecondaryContext; + const TInt ret(iNif.Control(KSOLInterface, KContextCreate, opt)); + return (ret == KErrNone) ? opt().iReasonCode : ret; + } + + +// Turn off events +TInt CNif::CloseInterface() + { + return SetEvents(EFalse); + } + +void CNif::AddRequest(CRequestBase& aRequest) + { + iPending.AddLast(aRequest); + ++iPendingSequence; // iPending Modified + LOG(Log::Printf(_L("\trequest %S[%u] -- Queued for activation on IAP=%u"), aRequest.iName, (TInt)&aRequest, iIapId)); + IssueRequest(); + } + +void CNif::CancelPendingRequest(CFlowData* aFlowData) + { + LOG(Log::Printf(_L("\tCancelPendingRequest for FLOW -- BEGIN"))); + TUint32 mark; + do + { + mark = iPendingSequence; + TSglQueIter iter(iPending); + CRequestBase* request; + while (iPendingSequence == mark && (request = iter++) != NULL) + request->Cancel(aFlowData); + } + while (mark != iPendingSequence); + LOG(Log::Printf(_L("\tCancelPendingRequest FLOW -- END"))); + } + + +void CNif::CancelPendingRequest(CPdpContext* aContext) + { + LOG(Log::Printf(_L("\tCancelPendingRequest for PDP -- BEGIN"))); + + TUint32 mark; + do + { + mark = iPendingSequence; + TSglQueIter iter(iPending); + CRequestBase* request; + while (iPendingSequence == mark && (request = iter++) != NULL) + request->Cancel(aContext); + } + while (mark != iPendingSequence); + + LOG(Log::Printf(_L("\tCancelPendingRequest for PDP -- END"))); + } + +void CNif::IssueRequest() + { + // Request a callback with delay 0, calls RunPendingRequests as soon as possible. + iTimeout.Set(Module().TimeoutManager(), 0); + }; + +void CNif::RunPendingRequests() + { + LOG(Log::Printf(_L(""))); + if (Status() != EReady) + return; + + while (!iPending.IsEmpty() && iCurrentRequest == NULL) + { + iCurrentRequest = iPending.First(); + iCurrentRequest->Start(); + } + } + +// Recompute evaluation precedences in use from the current state of PDP contexts. +void CNif::RecomputeEvaluationPrecedences() + { + // This is called every time NIF returns the current set of filters. + // + // When new packet filters are added for the PDP Context, the + // precedence indexes must be selected for each of them and + // marked as used in the current table. Removal of filter must + // release the index, however at the time of TFT removal request, + // it is not yet known whether it will succeed. + // + // Eventually, NIF reports back what filters + // and indexes are actually used. Just recomputing the used + // status by scanning all (few) PDP Contexts and their current + // filters is the simplest solution to keep the information in + // in synch with NIF/network view of things. + + Mem::FillZ(iEvaluationPrecedenceMap, sizeof(iEvaluationPrecedenceMap)); + TContextIter iter(iContexts); + CPdpContext* context; + while ((context = iter++) != NULL) + { + for (TInt i = 0; i < context->iNumFilters; ++i) + { + const TInt p = context->iFilters[i].iEvaluationPrecedenceIndex; + if (p >= 0 && p < KMaxEvaluationPrecedences) + iEvaluationPrecedenceMap[p] = 1; + } + } + } + +// Evaluation precedence must be unique amongst all packet filters related to +// same APN. +TInt CNif::FindEvaluationPrecedence() + { + TUint i; + for (i=0; i < KMaxEvaluationPrecedences; i++) + { + if (iEvaluationPrecedenceMap[i] == 0) + { + iEvaluationPrecedenceMap[i] = 1; + return i; + } + } + return KErrNotFound; + } + +void CNif::CloseRequest(CRequestBase* aRequest) + { + LOG(Log::Printf(_L("\t\tCNif::CloseRequest"))); + iPending.Remove(*aRequest); + ++iPendingSequence; + // Note: If current request is already NULL, then this does not + // activate a new request from queue, even if there would be some. + // This is intentional -- see ~CNif() destructor! + if (iCurrentRequest != aRequest) + return; + iCurrentRequest = NULL; + IssueRequest(); + } +// +CNifManager* CNifManager::NewL() + { + CNifManager* manager = new (ELeave) CNifManager(); + CleanupStack::PushL(manager); + manager->ConstructL(); + CleanupStack::Pop(); + return manager; + } + +CNifManager::~CNifManager() + { + while (!iNifs.IsEmpty()) + { + CNif* nif = iNifs.First(); + iNifs.Remove(*nif); + delete nif; + } + iNifs.Reset(); + } + + +CNifManager::CNifManager() + { + iNifs.SetOffset(_FOFF(CNif, iNext)); + } + +void CNifManager::ConstructL() + { + } + +CNif* CNifManager::CreateNifL(CNifIfBase& aInterface, CModuleGuqos& aModule) + { + CNif* nif = CNif::NewL(aInterface, aModule); + iNifs.AddLast(*nif); + nif->SetEvents(ETrue); + nif->SetDefaultQoS(); + return nif; + } + +void CNifManager::DeleteNif(CNifIfBase* aInterface) + { + CNif* nif = FindInterface(aInterface); + if (nif) + DeleteNif(nif); + } + +void CNifManager::DeleteNif(CNif* aInterface) + { + iNifs.Remove(*aInterface); + delete aInterface; + } + +CNif* CNifManager::FindInterface(const CNifIfBase *aIface) + { + TNifIter iter(iNifs); + CNif* nif; + while ((nif = iter++) != NULL) + if (&nif->Interface() == aIface) + return nif; + return NULL; + } + +CPdpContext* CNifManager::FindChannel(TInt aChannelId) + { + TNifIter iter(iNifs); + CNif* nif; + while ((nif = iter++) != NULL) + { + CPdpContext* context = nif->FindChannel(aChannelId); + if (context) + return context; + } + return NULL; + } + + +// delete all pending requests related to this flow +void CNifManager::CancelPendingRequests(CFlowData* aFlowData) + { + __ASSERT_ALWAYS(aFlowData!=NULL, User::Panic(_L("CNifManager::CancelPendingRequests"), 0)); + TNifIter iter(iNifs); + CNif *nif; + while ((nif = iter++) != NULL) + nif->CancelPendingRequest(aFlowData); + }