// 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 <in_iface.h>
#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<CNif, KNifTimeoutOffset>
{
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<TSoIfConnectionInfo> 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<TEvent> opt;
opt().iEvent = this;
LOG(Log::Printf(_L("\tcall NIF Control(KRegisterEventHandler)")));
return iNif.Control(KSOLInterface, KRegisterEventHandler, opt);
}
TInt CNif::SetEvents(TBool aValue)
{
TPckgBuf<TBool> 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<TContextParameters> 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<TContextParameters> 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<TContextParameters> 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<CRequestBase> 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<CRequestBase> 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);
}