diff -r 000000000000 -r 3553901f7fa8 telephonyprotocols/secondarypdpcontextumtsdriver/spudman/src/spudman.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/telephonyprotocols/secondarypdpcontextumtsdriver/spudman/src/spudman.cpp Tue Feb 02 01:41:59 2010 +0200 @@ -0,0 +1,3247 @@ +// 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: +// SPUD event manager +// WINSCW compiler has problems disambiguating TMetaDes8 and Meta::TMetaDes8 so +// I prevent metabuffer.h from being recursively included from other headers +// by defining its include guard. +// +// + +/** + @file + @internalComponent +*/ +#define METABUFFER_H + +#include "spudman.h" +#include "bindman.h" +#include "mux.h" + +#include +#include "rpdpfsminterface.h" +using namespace SpudMan; // Access the SpudFsm event names + +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +const TUint KQosPlugInProtocolId = 360; //< Plug-in protocol ID + +static const TInt KMaxInetAddrPrintSize = 50; //This should be big enough for an IPv6 printout + + +#ifdef __FLOG_ACTIVE + +_LIT(KSpudUnknownLit, "Unknown event"); + +_LIT(KSpudSoIfControllerPlugInLit, "KSoIfControllerPlugIn"); +_LIT(KSpudLitKRegisterEventHandler, "KRegisterEventHandler"); +_LIT(KSpudContextSetEventsLit, "KContextSetEvents"); +_LIT(KSpudNifSetDefaultQoSLit, "KNifSetDefaultQoS"); +_LIT(KSpudContextCreateLit, "KContextCreate"); +_LIT(KSpudContextDeleteLit, "KContextDelete"); +_LIT(KSpudContextActivateLit, "KContextActivate"); +_LIT(KSpudContextQoSSetLit, "KContextQoSSet"); +_LIT(KSpudContextTFTModifyLit, "KContextTFTModify"); +_LIT(KSpudGetNegQoSLit, "KGetNegQoS"); +_LIT(KSpudContextModifyActiveLit, "KContextModifyActive"); +_LIT(KInitialisePdpFsmLit, "KInitialisePdpFsm"); + + +static const TDesC *SpudGuQoSEventToText(TInt aEvent) + { + switch(aEvent) + { + case KSoIfControllerPlugIn: + return &KSpudSoIfControllerPlugInLit; + case KRegisterEventHandler: + return &KSpudLitKRegisterEventHandler; + case KContextSetEvents: + return &KSpudContextSetEventsLit; + case KNifSetDefaultQoS: + return &KSpudNifSetDefaultQoSLit; + case KContextCreate: + return &KSpudContextCreateLit; + case KContextDelete: + return &KSpudContextDeleteLit; + case KContextActivate: + return &KSpudContextActivateLit; + case KContextQoSSet: + return &KSpudContextQoSSetLit; + case KContextTFTModify: + return &KSpudContextTFTModifyLit; + case KGetNegQoS: + return &KSpudGetNegQoSLit; + case KContextModifyActive: + return &KSpudContextModifyActiveLit; + case KInitialisePdpFsm: + return &KInitialisePdpFsmLit; + default: + return &KSpudUnknownLit; + } + } + +_LIT(KSpudContextDeleteEventLit, "KContextDeleteEvent"); +_LIT(KSpudContextActivateEventLit, "KContextActivateEvent"); +_LIT(KSpudContextParametersChangeEventLit, "KContextParametersChangeEvent"); +_LIT(KSpudContextBlockedEventLit, "KContextBlockedEvent"); +_LIT(KSpudContextUnblockedEventLit, "KContextUnblockedEvent"); +_LIT(KSpudNetworkStatusEventLit, "KNetworkStatusEvent"); +_LIT(KSpudContextQoSSetEventLit, "KContextQoSSetEvent"); +_LIT(KSpudContextTFTModifiedEventLit, "KContextTFTModifiedEvent"); +_LIT(KSpudPrimaryContextCreatedLit, "KPrimaryContextCreated"); +_LIT(KSpudSecondaryContextCreatedLit, "KSecondaryContextCreated"); +_LIT(KSpudGetNegQoSEventLit,"KGetNegQoSEvent"); +_LIT(KSpudContextModifyActiveEventLit, "KContextModifyActiveEvent"); +_LIT(KPdpFsmShuttingDownLit, "KPdpFsmShuttingDown"); + +static const TDesC *SpudFsmEventToText(TInt aEvent) + { + switch(aEvent) + { + case KContextDeleteEvent: + return &KSpudContextDeleteEventLit; + case KContextActivateEvent: + return &KSpudContextActivateEventLit; + case KContextParametersChangeEvent: + return &KSpudContextParametersChangeEventLit; + case KContextBlockedEvent: + return &KSpudContextBlockedEventLit; + case KContextUnblockedEvent: + return &KSpudContextUnblockedEventLit; + case KNetworkStatusEvent: + return &KSpudNetworkStatusEventLit; + case KContextQoSSetEvent: + return &KSpudContextQoSSetEventLit; + case KContextTFTModifiedEvent: + return &KSpudContextTFTModifiedEventLit; + case KPrimaryContextCreated: + return &KSpudPrimaryContextCreatedLit; + case KSecondaryContextCreated: + return &KSpudSecondaryContextCreatedLit; + case KGetNegQoSEvent: + return &KSpudGetNegQoSEventLit; + case KContextModifyActiveEvent: + return &KSpudContextModifyActiveEventLit; + case KPdpFsmShuttingDown: + return &KPdpFsmShuttingDownLit; + default: + return &KSpudUnknownLit; + } + } + +_LIT(KSpudStateSpudInactiveLit, "ESpudInactive"); +_LIT(KSpudStateSpudHaveQosLit, "ESpudHaveQos"); +_LIT(KSpudStateSpudCreatingPrimary, "ESpudCreatingPrimary"); +_LIT(KSpudStateSpudStartingPrimaryLit, "ESpudStartingPrimary"); +_LIT(KSpudStateSpudStartingPrimaryLowerNifLit, "ESpudStartingPrimaryLowerNif"); +_LIT(KSpudStateSpudStartingSecondaryLit, "ESpudStartingSecondary"); +_LIT(KSpudStateSpudStartingSecondaryLowerNifLit, "ESpudStartingSecondaryLowerNif"); +_LIT(KSpudStateSpudGettingNegQoSLit, "ESpudGettingNegQoS"); +_LIT(KSpudStateSpudUpLit, "ESpudUp"); +_LIT(KSpudStateSpudFlowOffLit, "ESpudFlowOff"); +_LIT(KSpudStateSpudSuspendedLit, "ESpudSuspended"); +_LIT(KSpudStateSpudFlowOffAndSuspendedLit, "ESpudFlowOffAndSuspended"); +_LIT(KSpudStateSpudLinkDownLit, "ESpudLinkDown"); +_LIT(KSpudStateSpudContextDeleteLit, "ESpudContextDelete"); +_LIT(KSpudStateSpudWaitLinkDownLit, "ESpudWaitLinkDown"); +_LIT(KSpudStateSpudWaitBinderDeleteLit, "ESpudWaitBinderDelete"); +_LIT(KSpudStateUnknownLit, "Unknown spud state"); + +static const TDesC *SpudStateToText(TSpudContextStates aState) + { + switch(aState) + { + case ESpudInactive: + return &KSpudStateSpudInactiveLit; + case ESpudHaveQos: + return &KSpudStateSpudHaveQosLit; + case ESpudCreatingPrimary: + return &KSpudStateSpudCreatingPrimary; + case ESpudStartingPrimary: + return &KSpudStateSpudStartingPrimaryLit; + case ESpudStartingPrimaryLowerNif: + return &KSpudStateSpudStartingPrimaryLowerNifLit; + case ESpudStartingSecondary: + return &KSpudStateSpudStartingSecondaryLit; + case ESpudStartingSecondaryLowerNif: + return &KSpudStateSpudStartingSecondaryLowerNifLit; + case ESpudGettingNegQoS: + return &KSpudStateSpudGettingNegQoSLit; + case ESpudUp: + return &KSpudStateSpudUpLit; + case ESpudFlowOff: + return &KSpudStateSpudFlowOffLit; + case ESpudSuspended: + return &KSpudStateSpudSuspendedLit; + case ESpudFlowOffAndSuspended: + return &KSpudStateSpudFlowOffAndSuspendedLit; + case ESpudLinkDown: + return &KSpudStateSpudLinkDownLit; + case ESpudContextDelete: + return &KSpudStateSpudContextDeleteLit; + case ESpudWaitLinkDown: + return &KSpudStateSpudWaitLinkDownLit; + case ESpudWaitBinderDelete: + return &KSpudStateSpudWaitBinderDeleteLit; + default: + return &KSpudStateUnknownLit; + } + } + +#endif + +CSpudMan::CSpudMan(CNifIfFactory& aFactory, MNifIfNotify* aNotify) + : CNifIfLink(aFactory), + iContextStatusOverride(RPacketContext::EStatusUnknown) + { + iNotify = aNotify; + ASSERT(iNotify); + __FLOG_OPEN(KSpudFirstTag,KSpudLog); + __FLOG_0(_L("CSpudMan::CSpudMan")); + } + +CSpudMan::~CSpudMan() + { + if (iBindMan) + { + // Only log if ConstructL() was called, where logger was initialized + __FLOG_0(_L("CSpudMan::~CSpudMan")); + } + + + if (AreQoSEventsEnabled()) + { + // Spud is being destroyed by Nifman. Tell GUQoS to stop bothering SPUD. + // GUQoS returns the favour by turning off the NIF events within this very call. + // ********************************************************************************************* + // N.B.: "DEF055691 GUQoS crashes at shutdown": This defect results in GUQoS crash if the stack + // closes flows after the NIF proxy was deleted by GUQoS, as a result of KNetworkInterfaceDown event. + // Because of this defect, we cannot signal KNetworkInterfaceDown from anywhere but here. Once it is fixed, + // calls from the appropriate places will be enabled, and the line below will not be strictly necessary. + SendNetworkStatusEvent(KNetworkInterfaceDown, RPacketService::EStatusUnattached); + // For safety, we should call this from here in any case. + //********************************************************************************************** + } + + delete iParkedDefaultQoS; + delete iBinderSweeperNotifierCb; + + delete iBindMan; + iPdpFsmInterface.Close(); + iSipServerAddr.Close(); + __FLOG_CLOSE; + } + +/** +Panics the current thread. + +@param aReason Panic reason code +*/ +void CSpudMan::Panic(TInt aReason) const + { + __FLOG_1(_L("CSpudMan::Panic with reason %d"), aReason); + User::Panic(KSpudName, aReason); + } + +/** +Construct the Link Protocol Object + +@param aBindMan Pointer to BindMan object (ownership is transferred) +@leave leaves if could not allocate memory +*/ +void CSpudMan::ConstructL(CBindMan* aBindMan) + { + __FLOG_1(_L("CSpudMan starting %x"), iNotify); + + + iBindMan = aBindMan; + ASSERT(iBindMan); + + // The lower NIF binder deletion & Nifman notification callback + iBinderSweeperNotifierCb = new (ELeave) CBinderSweeperNotifierCb(*this); + } + + +/** +Implements the Control method of CNifBase - used for retrieval of the P-CSCF (Sip Server) address +*/ +TInt CSpudMan::Control(TUint aLevel,TUint aName,TDes8& aOption, TAny*) + { + if (aLevel == KCOLConfiguration && aName == KConnGetSipServerAddr) + { + if (aOption.Length() != sizeof (SSipServerAddr)) + { + __FLOG(_L("CSpudMan::Control - Invalid Descriptior - Descriptor Must contain an SSipServerAddr")); + Panic(); + } + SSipServerAddr* sipServerAddr = reinterpret_cast(const_cast(aOption.Ptr())); + if (sipServerAddr->index < 0 || sipServerAddr->index >= iSipServerAddr.Count()) + { + __FLOG_1(_L("CSpudMan::Control - Index out of range : value = %d"), sipServerAddr->index); + return KErrNotFound; + } + sipServerAddr->address = iSipServerAddr[sipServerAddr->index]; +#ifdef __FLOG_ACTIVE + TBuf buf; + sipServerAddr->address.Output(buf); + __FLOG_2(_L("CSpudMan::Control Address %S returned for index %d"), &buf, sipServerAddr->index); +#endif + return KErrNone; + } + return KErrNotSupported; + } + + +/** +Retrieve any GPRS settings required from CommDB + +@param aConfigGprs Context configuration parameters to be filled in +*/ +void CSpudMan::RetrieveGprsConfig(RPacketContext::TContextConfigGPRS& aConfigGprs) const + { + TUint32 pdpType(0); + Notify()->ReadInt(TPtrC(GPRS_PDP_TYPE), pdpType); + __FLOG_1(_L8("ReadInt GPRS_PDP_TYPE [%d]"), pdpType); + aConfigGprs.iPdpType = STATIC_CAST(RPacketContext::TProtocolType, pdpType); + + Notify()->ReadDes(TPtrC(GPRS_APN), aConfigGprs.iAccessPointName); + __FLOG_1(_L8("ReadDes GPRS_APN [%S]"), &aConfigGprs.iAccessPointName); + + + TBool fromServer; + aConfigGprs.iPdpAddress.SetLength(0); + Notify()->ReadBool(TPtrC(GPRS_IP_ADDR_FROM_SERVER), fromServer); + if (!fromServer) + { + Notify()->ReadDes(TPtrC(GPRS_IP_ADDR), aConfigGprs.iPdpAddress); + } + + + // We can only use IPv4 or IPv6 - we use the first one listed in the IfNetworks field + TBuf networks; + Notify()->ReadDes(TPtrC(LAN_IF_NETWORKS), networks); + + ASSERT(networks.Length() > 0); // If the IfNetworks field is empty there is a serious misconfiguration + + TInt pos = networks.Find(_L(",")); + if (pos == KErrNotFound) + { + pos = networks.Length(); + } + TPtrC protocol(networks.Ptr(), pos); + _LIT(KIp4, "ip"); + _LIT(KIp6, "ip6"); + if (protocol.CompareF(KIp4) == 0) + { + // IPv4 settings + Notify()->ReadBool(TPtrC(GPRS_IP_DNS_ADDR_FROM_SERVER), fromServer); + if (!fromServer) + { + Notify()->ReadDes(TPtrC(GPRS_IP_NAME_SERVER1), aConfigGprs.iProtocolConfigOption.iDnsAddresses.iPrimaryDns); + Notify()->ReadDes(TPtrC(GPRS_IP_NAME_SERVER2), aConfigGprs.iProtocolConfigOption.iDnsAddresses.iSecondaryDns); + } + } + else if (protocol.CompareF(KIp6) == 0) + { + // IPv6 settings + Notify()->ReadBool(TPtrC(GPRS_IP6_DNS_ADDR_FROM_SERVER), fromServer); + if (!fromServer) + { + Notify()->ReadDes(TPtrC(GPRS_IP6_NAME_SERVER1), aConfigGprs.iProtocolConfigOption.iDnsAddresses.iPrimaryDns); + Notify()->ReadDes(TPtrC(GPRS_IP6_NAME_SERVER2), aConfigGprs.iProtocolConfigOption.iDnsAddresses.iSecondaryDns); + } + } + else + { + // Anything else is a serious misconfiguration + ASSERT(0); + } + + RetrieveGprsCompression(aConfigGprs.iPdpCompression); + __FLOG_1(_L8("Read GprsCompression [%d]"), aConfigGprs.iPdpCompression); + + RetrieveGprsAnonymousAccess(aConfigGprs.iAnonymousAccessReqd); + __FLOG_1(_L8("Read AnonymousAccess [%d]"), aConfigGprs.iAnonymousAccessReqd); + + TBuf readBuf; + Notify()->ReadDes(TPtrC(SERVICE_IF_AUTH_NAME), readBuf); + aConfigGprs.iProtocolConfigOption.iAuthInfo.iUsername.Copy(readBuf); + readBuf.Zero(); + Notify()->ReadDes(TPtrC(SERVICE_IF_AUTH_PASS), readBuf); + aConfigGprs.iProtocolConfigOption.iAuthInfo.iPassword.Copy(readBuf); + __FLOG_2(_L8("ReadDes SERVICE_IF_AUTH_NAME [%S] and SERVICE_IF_AUTH_PASS [%S]"), &aConfigGprs.iProtocolConfigOption.iAuthInfo.iUsername, + &aConfigGprs.iProtocolConfigOption.iAuthInfo.iPassword); + + aConfigGprs.iUseEdge = EFalse; + } + +void CSpudMan::RetrieveGprsCompression(TUint& aCompression) const + { + aCompression = 0; + TBool isCompression = EFalse; + Notify()->ReadBool(TPtrC(GPRS_DATA_COMPRESSION), isCompression); + if (isCompression) + { + aCompression |= RPacketContext::KPdpDataCompression; + } + + isCompression = EFalse; + Notify()->ReadBool(TPtrC(GPRS_HEADER_COMPRESSION), isCompression); + if (isCompression) + { + aCompression |= RPacketContext::KPdpHeaderCompression; + } + } + +void CSpudMan::RetrieveGprsAnonymousAccess(RPacketContext::TAnonymousAccess& aAnonymous) const + { + TBool isAnonymous = EFalse; + Notify()->ReadBool(TPtrC(GPRS_ANONYMOUS_ACCESS),isAnonymous); + if (isAnonymous) + aAnonymous = RPacketContext::ERequired; + else + aAnonymous = RPacketContext::ENotRequired; + } + + + +/** +Indicates whether QoS has been enabled or not. + +@return ETrue if QoS has been enabled for this NIF. +*/ +TBool CSpudMan::AreQoSEventsEnabled() const + { + return iQosEventHandler != NULL; + } + + +/** +Returns pointer to BindMan. + +@return Pointer to BindMan object +*/ +CBindMan* CSpudMan::BindMan() const + { + return iBindMan; + } + +/** +Returns pointer to the MNifIfNotify object in NIFMAN. + +@return Pointer to MNifIfNotify object +*/ +MNifIfNotify* CSpudMan::Notify() const + { + ASSERT(iNotify); + return iNotify; + } + +/** +Set default QoS parameters from the values in CommDb. + +@param aQos QoS parameters filled in on exit +*/ +void CSpudMan::ReadDefaultQoS(RPacketQoS::TQoSR99_R4Requested& aQos) const + { + TBuf<2*KCommsDbSvrMaxColumnNameLength+1> columnName; + _LIT(KFormatText,"%s\\%s"); + + columnName.Format(KFormatText,QOS_UMTS_R99_AND_ON_TABLE,GPRS_QOS_REQ_TRAFFIC_CLASS); + Notify()->ReadInt (columnName, reinterpret_cast(aQos.iReqTrafficClass)); + + columnName.Format(KFormatText,QOS_UMTS_R99_AND_ON_TABLE,GPRS_QOS_MIN_TRAFFIC_CLASS); + Notify()->ReadInt (columnName, reinterpret_cast(aQos.iMinTrafficClass)); + + columnName.Format(KFormatText,QOS_UMTS_R99_AND_ON_TABLE,GPRS_QOS_REQ_DELIVERY_ORDER); + Notify()->ReadInt (columnName, reinterpret_cast(aQos.iReqDeliveryOrderReqd)); + + columnName.Format(KFormatText,QOS_UMTS_R99_AND_ON_TABLE,GPRS_QOS_MIN_DELIVERY_ORDER); + Notify()->ReadInt (columnName, reinterpret_cast(aQos.iMinDeliveryOrderReqd)); + + columnName.Format(KFormatText,QOS_UMTS_R99_AND_ON_TABLE,GPRS_QOS_REQ_DELIVER_ERRONEOUS_SDU); + Notify()->ReadInt (columnName, reinterpret_cast(aQos.iReqDeliverErroneousSDU)); + + columnName.Format(KFormatText,QOS_UMTS_R99_AND_ON_TABLE,GPRS_QOS_MIN_DELIVER_ERRONEOUS_SDU); + Notify()->ReadInt (columnName, reinterpret_cast(aQos.iMinDeliverErroneousSDU)); + + columnName.Format(KFormatText,QOS_UMTS_R99_AND_ON_TABLE,GPRS_QOS_REQ_MAX_SDUSIZE); + Notify()->ReadInt (columnName, reinterpret_cast(aQos.iReqMaxSDUSize)); + + columnName.Format(KFormatText,QOS_UMTS_R99_AND_ON_TABLE,GPRS_QOS_MIN_ACCEPTABLE_MAX_SDU_SIZE); + Notify()->ReadInt (columnName, reinterpret_cast(aQos.iMinAcceptableMaxSDUSize)); + + columnName.Format(KFormatText,QOS_UMTS_R99_AND_ON_TABLE,GPRS_QOS_REQ_MAX_UPLINK_RATE); + Notify()->ReadInt (columnName, reinterpret_cast(aQos.iReqMaxRate.iUplinkRate)); + + columnName.Format(KFormatText,QOS_UMTS_R99_AND_ON_TABLE,GPRS_QOS_REQ_MIN_UPLINK_RATE); + Notify()->ReadInt (columnName, reinterpret_cast(aQos.iMinAcceptableMaxRate.iUplinkRate)); + + columnName.Format(KFormatText,QOS_UMTS_R99_AND_ON_TABLE,GPRS_QOS_REQ_MAX_DOWNLINK_RATE); + Notify()->ReadInt (columnName, reinterpret_cast(aQos.iReqMaxRate.iDownlinkRate)); + + columnName.Format(KFormatText,QOS_UMTS_R99_AND_ON_TABLE,GPRS_QOS_REQ_MIN_DOWNLINK_RATE); + Notify()->ReadInt (columnName, reinterpret_cast(aQos.iMinAcceptableMaxRate.iDownlinkRate)); + + columnName.Format(KFormatText,QOS_UMTS_R99_AND_ON_TABLE,GPRS_QOS_REQ_BER); + Notify()->ReadInt (columnName, reinterpret_cast(aQos.iReqBER)); + + columnName.Format(KFormatText,QOS_UMTS_R99_AND_ON_TABLE,GPRS_QOS_MAX_BER); + Notify()->ReadInt (columnName, reinterpret_cast(aQos.iMaxBER)); + + columnName.Format(KFormatText,QOS_UMTS_R99_AND_ON_TABLE,GPRS_QOS_REQ_SDU_ERROR_RATIO); + Notify()->ReadInt (columnName, reinterpret_cast(aQos.iReqSDUErrorRatio)); + + columnName.Format(KFormatText,QOS_UMTS_R99_AND_ON_TABLE,GPRS_QOS_MAX_SDU_ERROR_RATIO); + Notify()->ReadInt (columnName, reinterpret_cast(aQos.iMaxSDUErrorRatio)); + + columnName.Format(KFormatText,QOS_UMTS_R99_AND_ON_TABLE,GPRS_QOS_REQ_TRAFFIC_HANDLING_PRIORITY); + Notify()->ReadInt (columnName, reinterpret_cast(aQos.iReqTrafficHandlingPriority)); + + columnName.Format(KFormatText,QOS_UMTS_R99_AND_ON_TABLE,GPRS_QOS_MIN_TRAFFIC_HANDLING_PRIORITY); + Notify()->ReadInt (columnName, reinterpret_cast(aQos.iMinTrafficHandlingPriority)); + + columnName.Format(KFormatText,QOS_UMTS_R99_AND_ON_TABLE,GPRS_QOS_REQ_TRANSFER_DELAY); + Notify()->ReadInt (columnName, reinterpret_cast(aQos.iReqTransferDelay)); + + columnName.Format(KFormatText,QOS_UMTS_R99_AND_ON_TABLE,GPRS_QOS_MAX_TRANSFER_DELAY); + Notify()->ReadInt (columnName, reinterpret_cast(aQos.iMaxTransferDelay)); + + columnName.Format(KFormatText,QOS_UMTS_R99_AND_ON_TABLE,GPRS_QOS_REQ_GUARANTEED_UPLINK_RATE); + Notify()->ReadInt (columnName, reinterpret_cast(aQos.iReqGuaranteedRate.iUplinkRate)); + + columnName.Format(KFormatText,QOS_UMTS_R99_AND_ON_TABLE,GPRS_QOS_MIN_GUARANTEED_UPLINK_RATE); + Notify()->ReadInt (columnName, reinterpret_cast(aQos.iMinGuaranteedRate.iUplinkRate)); + + columnName.Format(KFormatText,QOS_UMTS_R99_AND_ON_TABLE,GPRS_QOS_REQ_GUARANTEED_DOWNLINK_RATE); + Notify()->ReadInt (columnName, reinterpret_cast(aQos.iReqGuaranteedRate.iDownlinkRate)); + + columnName.Format(KFormatText,QOS_UMTS_R99_AND_ON_TABLE,GPRS_QOS_MIN_GUARANTEED_DOWNLINK_RATE); + Notify()->ReadInt (columnName, reinterpret_cast(aQos.iMinGuaranteedRate.iDownlinkRate)); + } + +#ifdef SYMBIAN_NETWORKING_UMTSR5 +/** +Set default R5 QoS parameters from the values in CommDb. + +@param aQos R5 QoS parameters filled in on exit +*/ +void CSpudMan::ReadDefaultR5QoS(RPacketQoS::TQoSR5Requested& aQos) const + { + ReadDefaultQoS(aQos); + + TBuf<2*KCommsDbSvrMaxColumnNameLength+2> columnName; + _LIT(KFormatText,"%s\\%s"); + columnName.Format(KFormatText,QOS_UMTS_R99_AND_ON_TABLE,GPRS_QOS_SIGNALLING_INDICATION); + Notify()->ReadBool (columnName, aQos.iSignallingIndication); + columnName.Format(KFormatText,QOS_UMTS_R99_AND_ON_TABLE,GPRS_QOS_SOURCE_STATISTICS_DESCRIPTOR); + Notify()->ReadInt(columnName, reinterpret_cast(aQos.iSourceStatisticsDescriptor)); + } +#endif +// SYMBIAN_NETWORKING_UMTSR5 + + +/** +Reads TSY name from CommDb. + +@param aTsyName TSY name filled in on exit +*/ +void CSpudMan::ReadTsyName(TName& aTsyName) const + { + TBuf<2*KCommsDbSvrMaxColumnNameLength+1> columnName; + _LIT(KFormatText,"%s\\%s"); + columnName.Format(KFormatText,MODEM_BEARER,MODEM_TSY_NAME); + Notify()->ReadDes(columnName, aTsyName); // ignore error + } + +/** +Sets the error code to use for termination, unless it is already set. + +@param aError Error code to use when notifying NIFMAN +*/ +void CSpudMan::SetTerminateError(TInt aError) + { + if (iTerminateError == KErrNone) + { + iTerminateError = aError; + __FLOG_1(_L("Set SPUD termination error to [%d]"), aError); + + // There is no specific context associated with this termination + // So prevent a later overwrite of the error by the ETel error + if (iETelTerminateError == KErrNone) + { + iETelTerminateError = aError; + } + } + } + +/** +Sets the error code to use for termination, unless it is already set. + +@param aContextId context ID +@param aError Error code to use when notifying NIFMAN +*/ +void CSpudMan::SetTerminateError(TContextId aContextId, TInt aError) + { + if (iTerminateError == KErrNone) + { + iTerminateError = aError; + + // Now check to see if ETel was the originator of this error + if (iETelTerminateError == KErrNone) + { + // ask for the last ETel error received - ignore return code + iPdpFsmInterface.GetLastErrorCause(aContextId, iETelTerminateError); + } + + if (iETelTerminateError != KErrNone) + { + // Overwrite with the most likely original error code! + iTerminateError = iETelTerminateError; + } + + __FLOG_1(_L("Set SPUD termination error to [%d]"), iTerminateError); + } + + } + +//***************************************************************************** +// Events receivers +//***************************************************************************** + +/** +Receives events from SpudFsm. + +@param aContextId context ID +@param aEvent event ID +@param aParam optional parameter to event +*/ +void CSpudMan::Input(TContextId aContextId, TInt aEvent, TInt aParam) + { + __FLOG_4(_L("SpudMan::Input: SpudFsm event %S(%d) param[%d] context[%d]"), SpudFsmEventToText(aEvent), aEvent, aParam, aContextId); + ASSERT(aContextId == KAllContexts || (aContextId >=0 && aContextId < KMaxPdpContexts)); + + switch (aEvent) + { + case KPrimaryContextCreated: + { + HandlePrimaryContextCreatedEvent(aContextId, aParam); + break; + } + + case KContextDeleteEvent: + { + HandleContextDeleteEvent(aContextId, aParam); + break; + } + + case KSecondaryContextCreated: + { + HandleSecondaryContextCreatedEvent(aContextId, aParam); + break; + } + + case KContextActivateEvent: + { + HandleContextActivateEvent(aContextId, aParam); + break; + } + + case KContextQoSSetEvent: + { + HandleContextQoSSetEvent(aContextId, aParam); + break; + } + + case KContextTFTModifiedEvent: + { + HandleContextTFTModifiedEvent(aContextId, aParam); + break; + } + + case KGetNegQoSEvent: + { + HandleGetNegQoSEvent(aContextId, aParam); + break; + } + + case KContextModifyActiveEvent: + { + HandleContextModifyActiveEvent(aContextId, aParam); + break; + } + + case KNetworkStatusEvent: + { + HandleNetworkStatusEvent(); + break; + } + + case KContextParametersChangeEvent: + { + HandleContextParametersChangeEvent(aContextId, aParam); + break; + } + + case KContextBlockedEvent: + { + HandleContextBlockedEvent(aContextId); + break; + } + + case KContextUnblockedEvent: + { + HandleContextUnblockedEvent(aContextId); + break; + } + + + case KPdpFsmShuttingDown: + { + __FLOG_0(_L("UmtsGprsSCPR has shutdown the PDP Fsm Interface")); + iPdpFsmInterface.Close(); + break; + } + + default: + __FLOG_1(_L("Unhandled event %d"), aEvent); + ASSERT(EFalse); + break; + } + } + +void CSpudMan::InitPdpFsmInterfaceL() + { + class XAssociatedNifConnectionProviderQuery : public MFactoryQuery + /** Finds the connection provider associated with the specified Nif. + @internalTechnology + */ + { + public: + XAssociatedNifConnectionProviderQuery( const TDesC& aName, ::TMetaDes8& aNameBuffer ) : iNifName( aName ), iNifNameBuffer( aNameBuffer ) + { + } + + virtual TMatchResult Match( TFactoryObjectInfo& aObjectInfo ) + { + TConnInterfaceName& name = *reinterpret_cast( const_cast( iNifNameBuffer.iDes->Ptr() ) ); + name.iIndex = 1; + + TInt err = KErrNone; + TMatchResult result = EContinue; + + // Check each of the connection provider interfaces' names to see if it + // is associated with this NIF. + while( ETrue ) + { + TRAP( err, static_cast( aObjectInfo.iInfo.iFactoryObject )->ControlL(KCOLProvider, KConnGetInterfaceName, iNifNameBuffer, NULL) ); + + if( err == KErrNone ) + { + if( name.iName == iNifName ) + { + result = EMatch; + + break; + } + } + else + { + break; + } + + name.iIndex ++; + } + + return result; + } + + private: + const TDesC& iNifName; + ::TMetaDes8& iNifNameBuffer; + }; + + const TUint KShimConnectionProviderFactoryId = 0x10207104; //the same as CSubConnectionProviderFactoryShim + + TSockManData* sockManData = SockManGlobals::Get(); + ASSERT(sockManData); + + CConnectionFactoryContainer* connectionFactories = sockManData->iConnectionFactories; + ASSERT(connectionFactories); + + CConnectionProviderFactoryBase *factory = connectionFactories->FindFactory(KShimConnectionProviderFactoryId); + if(!factory) + { + User::Leave( KErrNotFound ); + } + + CSpudMux* mux = iBindMan->SpudMux(); + + // Get the name of our SPUDMUX interface - it uniquely identifies this SPUD object assembly. + TNifIfInfo info; + mux->Info( info ); + + // Create a buffer to hold the name of each interface we check to see if the interface is our SPUDMUX. + TPckgBuf name; + TPtrC8 desC( name ); + ::TMetaDes8* des = ::TMetaDes8::NewL( &desC ); + + XAssociatedNifConnectionProviderQuery connProviderQuery( info.iName, *des ); + + // Find the connection provider associated with this NIF. + CConnectionProviderBase* connectionProvider = factory->FindProvider(connProviderQuery); + delete des; + if(!connectionProvider) + { + User::Leave( KErrNotFound ); + } + + CSubConnectionFactoryContainer* subConnectionFactories = sockManData->iSubConnectionFactories; + ASSERT(subConnectionFactories); + + TDataClientQuery subConnProviderQuery(connectionProvider, ESubConnPlane, RSubConnection::EAttachToDefault); + + // Find the default subconnection provider. + CSubConnectionProviderBase* subConnProvider = subConnectionFactories->FindOrCreateProviderL(connectionProvider->CanDoSubConnection(RSubConnection::EAttachToDefault), subConnProviderQuery); + if(!subConnProvider || subConnProvider->Factory().Id() != KUmtsGprsSubConnectionProviderFactoryId) + { + User::Leave( KErrNotFound ); + } + + // Initialise the PDP FSM interface. + TPckg nifPckg(mux); + User::LeaveIfError(subConnProvider->Control(KSOLInterface, KInitialisePdpFsm, nifPckg)); + } + +void CSpudMan::HandlePrimaryContextCreatedEvent(TContextId aContextId, TInt aError) + { + + // Save the contextId for later + iPrimaryContextId = aContextId; + + // Primary context has been created; Start the lower NIF + CSpudBinderRef* ref = NULL; + TRAPD(rc, ref = iBindMan->GetRefL(aContextId)); + __FLOG_2(_L("Got lower NIF binder for context[%d] with error[%d]"), aContextId, rc); + if (rc == KErrNone) + { + // Make sure context was created successfully + rc = aError; + ASSERT(ref->IsBound()); + __FLOG_3(_L("context %d is in state %S(%d)"), aContextId, SpudStateToText(ref->State()), ref->State()); + } + + if (KErrNone == rc) // Context created successfully. + { + // Get the details of the P-CSCF + RPacketContext::TContextConfigGPRS config; + + if (iPdpFsmInterface.Get(aContextId, config) == KErrNone) + { + TRAP(rc, SetSipServerAddrL(config.iProtocolConfigOption);); + { + __FLOG_1(_L("CSpudMan::HandlePrimaryContextCreatedEvent -Error occurred extracting the sip server address: %d"),rc); + //rc is now non zero, therefore should fail gracefully + } +#ifdef SYMBIAN_NETWORKING_UMTSR5 + // Since the value or IMCN Signalling flag also comes in PCO IE buffer along with the SipAddress, we + // need to extract the value here and set it to Parameter Bundle of SCPR + TBool imcn = EFalse; + TRAPD(err,imcn=GetIMCNSignallingFlagPcoL(config.iProtocolConfigOption);); + __FLOG_1(_L("CSpudMan::GetIMCNSignallingFlagPcoL - returns eror=%d"),err); + if (err == KErrNone) + { + iPdpFsmInterface.SetIMCNSignalling(imcn); //imcn contains ETrue or Efalse +#ifdef __FLOG_ACTIVE + if (imcn) + { + __FLOG_0(_L("CSpudMan::HandlePrimaryContextCreatedEvent - Network ACCEPTS the request for dedicated IMCN Signalling Flag ")); + } + else + { + __FLOG_0(_L("CSpudMan::HandlePrimaryContextCreatedEvent - Network REJECTS the request for dedicated IMCN Signalling Flag ")); + } +#endif + } + else + { + +#ifdef __FLOG_ACTIVE + __FLOG_1(_L("CSpudMan::HandlePrimaryContextCreatedEvent -Leave Error occurred %d"),err); +#endif + } +#endif // SYMBIAN_NETWORKING_UMTSR5 + } + } + + if (KErrNone == rc) // Sip server address retrieved + { + ASSERT(ref->State() == ESpudCreatingPrimary); + ref->SetState(ESpudStartingPrimary); + // Start the lower NIF and wait for the LinkLayerUp call + rc = ref->NifLink()->Start(); + if(KErrNone == rc) + { + ref->SetState(ESpudStartingPrimaryLowerNif); + } + // Lower NIF may still report failure to Start by signalling LinkLayerDown with error. + // E.G. PPP negotiation may fail. + __FLOG_2(_L("Lower NIF for Primary Context[%d] started with error[%d]."), aContextId, rc); + } + + + // Catch-all error handling: Could not create context, or could not start lower NIF. + if(KErrNone != rc) + { + SetTerminateError(rc); + + SendPrimaryContextCreated(aContextId, rc); // Notify GUQoS of error + + if(ESpudStartingPrimary == ref->State()) // Context created + { + ref->SetState(ESpudWaitLinkDown); // We've just notified GUQoS. + // Delete context via SpudFsm + __FLOG_2(_L("Failed to start lower NIF for Primary PDP context[%d] due to error[%d]. Deleting Primary via SpudFsm."), aContextId, rc); + rc = iPdpFsmInterface.Input(aContextId, EContextDelete); + ASSERT(rc == KErrNone); + } + else // Context was not created, else we would not be here. There is nothing to stop and delete. + { + __FLOG_1(_L("Failed to create Primary context[%d]. Spud will shut down."), aContextId); + + if (AreQoSEventsEnabled()) + { + // At this point we know that SPUD is about to shut down, because we are + // deleting the last context. We want to indicate to the upper layers early that + // data can no longer be sent to SPUD. + + // Tell GUQoS to stop bothering SPUD. + // GUQoS returns the favour by turning off the NIF events within this very call. + // This means we won't send KNetworkInterfaceDown, even if we try. + // ********************************************************************************************* + // N.B.: "DEF055691 GUQoS crashes at shutdown": This defect results in GUQoS crash if the stack + // closes flows after the NIF proxy was deleted by GUQoS, as a result of KNetworkInterfaceDown event. + // As a temporary workaround, this call is disabled, and KNetworkInterfaceDown event is signalled only + // from the destructor. Once this defect is fixed, the following line must be uncommented: + // SendNetworkStatusEvent(KNetworkInterfaceDown, RPacketService::EStatusUnattached); + //********************************************************************************************** + } + // We are about to delete the primary context NIF: this will notify Nifman that SPUD is finished. + DisposeOfBinder(ref); + } + } + } + +void CSpudMan::HandleContextDeleteEvent(TContextId aContextId, TInt aError) + { + aError = aError; // suppress compiler warning + // Ignore error on delete--nothing we can do, anyway + // For the upper layers, we treat this event as KErrDisconnected, because the network has torn down an + // existing context. + CSpudBinderRef* ref = NULL; + TRAPD(rc, ref = iBindMan->GetRefL(aContextId)); + __ASSERT_ALWAYS(rc == KErrNone, Panic()); + ASSERT(ref->IsBound()); + __FLOG_4(_L("Network deleted context %d, in state %S(%d). Deletion error=%d"), + aContextId, SpudStateToText(ref->State()), ref->State(), aError); + + // Are we about to shut down after this context is deleted? + TBool shutdownStarted(BindMan()->IsLastContext(aContextId)); + // We must determine this before we make any state transitions. But we can act on this + // only after we've done all GUQoS notifications to make sure shutdown is graceful. + + switch (ref->State()) + { + case ESpudUp: + case ESpudFlowOff: + case ESpudSuspended: + case ESpudFlowOffAndSuspended: + ref->SetState(ESpudWaitLinkDown); + SendContextDeleteEvent(aContextId); + ref->NifLink()->Stop(KErrConnectionTerminated, MNifIfNotify::EDisconnect); + break; + + case ESpudStartingPrimaryLowerNif: + ref->SetState(ESpudWaitLinkDown); + SendPrimaryContextCreated(aContextId, KErrDisconnected); + SendContextDeleteEvent(aContextId); + ref->NifLink()->Stop(KErrConnectionTerminated, MNifIfNotify::EDisconnect); + break; + + case ESpudGettingNegQoS: // Context was activated. It is assumed that QoS retrieval would be cancelled by deletion. + case ESpudStartingSecondaryLowerNif: + ref->SetState(ESpudStartingSecondary); + FillInContextConfig(iTempContextConfig, aContextId); + SendContextActivateEvent(aContextId, iTempContextConfig, KErrDisconnected); + SendContextDeleteEvent(aContextId); + ref->NifLink()->Stop(KErrConnectionTerminated, MNifIfNotify::EDisconnect); + break; + + case ESpudStartingPrimary: + // this should never happen + SendPrimaryContextCreated(aContextId, KErrDisconnected); + SendContextDeleteEvent(aContextId); + DisposeOfBinder(ref); + break; + + case ESpudStartingSecondary: + // This should never happen, and may cause problems if it does because there could be + // another outstanding request from GUQoS that never gets completed. + __FLOG_0(_L("Network should not have deleted context in ESpudStartingSecondary state!")); + // Fall through and treat the same as ESpudLinkDown + + case ESpudLinkDown: + SendContextDeleteEvent(aContextId); + DisposeOfBinder(ref); + break; + + case ESpudWaitLinkDown: + DisposeOfBinder(ref); + break; + + + default: + __FLOG_2(_L("Unexpected state %S(%d)"), SpudStateToText(ref->State()), ref->State()); + ASSERT(EFalse); + break; + } + + if(shutdownStarted) // Additional steps if we are shutting down as as result of this deletion. + { + SetTerminateError(KErrDisconnected); + // We don't reuse errorForGuqos, because we may want to keep Nifman errors and GUQoS errors separate. + if (AreQoSEventsEnabled()) + { + // At this point we know that SPUD is about to shut down, because the last context + // was just deleted. We want to indicate to the upper layers early that + // data can no longer be sent to SPUD. + + // Tell GUQoS to stop bothering SPUD. + // GUQoS returns the favour by turning off the NIF events within this very call. + // This means we send KNetworkInterfaceDown only once. + // ******************************************************************************************* + // N.B.: "DEF055691 GUQoS crashes at shutdown": This defect results in GUQoS crash if the stack + // closes flows after the NIF proxy was deleted by GUQoS, as a result of KNetworkInterfaceDown event. + // As a temporary workaround, this call is disabled, and KNetworkInterfaceDown event is signalled only + // from the destructor. Once this defect is fixed, the following line must be uncommented: + // SendNetworkStatusEvent(KNetworkInterfaceDown, RPacketService::EStatusUnattached); + //********************************************************************************************** + } + } + } + +void CSpudMan::HandleSecondaryContextCreatedEvent(TContextId aContextId, TInt aError) + { + CSpudBinderRef* ref = NULL; + TRAPD(rc, ref = iBindMan->GetRefL(aContextId)); + if (rc == KErrNone) + { + ASSERT(ref->IsBound()); + __FLOG_4(_L("Network created secondary context[%d] in state %S(%d). Creation error=%d "),aContextId, SpudStateToText(ref->State()), ref->State(), aError); + if (ref->State() != ESpudStartingSecondary) + { + __FLOG_3(_L("KSecondaryContextCreated context %d is in unexpected state %S(%d)"), + aContextId, SpudStateToText(ref->State()), ref->State()); + } + ref = ref; // Eliminate compiler warning in release builds + + // Make sure context was created successfully + rc = aError; + if(KErrNone != rc) // Creation failed: we don't have the context, only the binder. Delete it. + { + DisposeOfBinder(ref); + } + } + SendSecondaryContextCreated(aContextId, rc); + } + +void CSpudMan::HandleContextActivateEvent(TContextId aContextId, TInt aError) + { + CSpudBinderRef* ref = NULL; + TRAPD(rc, ref = iBindMan->GetRefL(aContextId)); + if (rc == KErrNone) + { + // Make sure context was activated successfully + rc = aError; + + ASSERT(ref->IsBound()); + __FLOG_3(_L("context %d is in state %S(%d)"), aContextId, SpudStateToText(ref->State()), ref->State()); + } + + if (rc == KErrNone) + { + ASSERT(ref->State() == ESpudStartingSecondary); + // Start the lower NIF + rc = ref->NifLink()->Start(); + // Even if we get KErrNone here, we may still get LinkLayerDown with error later. + __FLOG_2(_L("KContextActivateEvent: Start on lower NIF returned for Context[%d] returned error[%d]"),aContextId, rc); + } + + if(KErrNone == rc) // Start OK. + { + + ref->SetState(ESpudStartingSecondaryLowerNif); + } + else + { + // Error activating PDP context + FillInContextConfig(iTempContextConfig, aContextId); + SendContextActivateEvent(aContextId, iTempContextConfig, rc); + } + } + +void CSpudMan::HandleContextQoSSetEvent(TContextId aContextId, TInt aError) + { + // Notify GUQoS of success or failure + SendContextQoSSetEvent(aContextId, aError); + } + +void CSpudMan::HandleContextTFTModifiedEvent(TContextId aContextId, TInt aError) + { + CSpudBinderRef* ref = NULL; + TRAPD(rc, ref = iBindMan->GetRefL(aContextId)); + __ASSERT_ALWAYS(rc == KErrNone, Panic()); + ASSERT(ref->IsBound()); + __FLOG_3(_L("context %d is in state %S(%d)"), aContextId, SpudStateToText(ref->State()), ref->State()); + ref = ref; // Eliminate compiler warning in release builds + + // Notify GUQoS of success or failure + TTFTOperationCode opCode; + iPdpFsmInterface.Get(aContextId, opCode); + SendContextTFTModifiedEvent(aContextId, opCode, aError); + } + +void CSpudMan::HandleGetNegQoSEvent(TContextId aContextId, TInt aError) + { + CSpudBinderRef* ref = NULL; + TRAPD(rc, ref = iBindMan->GetRefL(aContextId)); + __ASSERT_ALWAYS(rc == KErrNone, Panic()); + + ASSERT(ref->IsBound()); + __FLOG_4(_L("CSpudMan: Retrieved Negotiated QoS, error=%d: context %d is in state %S(%d)"), aError, aContextId, SpudStateToText(ref->State()), ref->State()); + if (ref->State() == ESpudGettingNegQoS) + { + // Negotiated QoS retrieved successfully. At this point the ctx is up both on control and data paths. + if(KErrNone == aError) + { + // Context is now fully up. Notify GUQoS. + ref->SetState(ESpudUp); + // Notify GUQoS of success or failure + FillInContextConfig(iTempContextConfig, aContextId); + SendContextActivateEvent(aContextId, iTempContextConfig, aError); + + if(ref->State() == ESpudUp) // GUQoS can delete the context from the activation notification. + { + BindMan()->SpudProtocol()->DoStartSending(); + SendContextUnblockedEvent(aContextId); + } + } + // If the QoS could not be retrieved, we remain in ESpudGettingNegQoS and wait for + // the network to delete the context. The rest would be handled from the deletion event. + + // N.B. CRITICAL ASSUMPTION: + // The network / TSY will delete the ctx if the negotiated QoS could not be retrieved. + // (The idea is that if QoS negotiation is errored out, then everything is errored out. + // So we either do not get to this point at all, or will be errored out soon after this handler completes.) + // This leaves the issue of internal ETel/TSY errors such as OOM conditions.) + // At this point Spud is idle (no requests outstanding), so nothing will drive us + // forward to clean up this failure. GUQoS does NOT time out on PDP context creation failure. + } + else + { + // If we are not getting negotiated QoS, but we receive this notification, then: + // - ETel has sent us a stray notification totally uncalled for, i.e. it's a bug in ETel or more likely a TSY. + if(KErrNone == aError) + { + __FLOG(_L("WARNING! Negotiated QoS retrieval completed successfully, but totally out of order! See the comments in the code.)")); + } + // We don't assert, because it can be a race condition outside of ETel's control: + // - ETel, lower NIF, GUQoS, Nifman has errored the context out (e.g. ctx deleted by network), so there was a + // state transition as a result of that, and now the QoS retrieval has completed after that with KErrNone. + } + } + + +void CSpudMan::HandleContextModifyActiveEvent(TContextId aContextId, TInt aError) + { + // Notify GUQoS of success or failure + FillInContextConfig(iTempContextConfig, aContextId); + SendContextModifyActiveEvent(aContextId, iTempContextConfig, aError); + } + +void CSpudMan::HandleNetworkStatusEvent() + { + // This is a network status change + RPacketService::TStatus status; + iPdpFsmInterface.Get(status); + + // Assume that anything other than EStatusUnattached means the network + // is still up, so just ignore this event. + if (status == RPacketService::EStatusUnattached) + { + // Notify GUQoS + SendNetworkStatusEvent(KNetworkConnectionLost, status); + } + else + { + __FLOG_1(_L("Ignoring KNetworkStatusEvent with status %d (NOT EStatusUnattached)"), + status); + } + } + +void CSpudMan::HandleContextParametersChangeEvent(TContextId aContextId, TInt aError) + { + // This is a status change on an individual context + CSpudBinderRef* ref = NULL; + TRAPD(rc, ref = iBindMan->GetRefL(aContextId)); + __ASSERT_ALWAYS(rc == KErrNone, Panic()); + ASSERT(ref->IsBound()); + __FLOG_3(_L("context %d is in state %S(%d)"), aContextId, SpudStateToText(ref->State()), ref->State()); + if (ref->State() == ESpudGettingNegQoS || ref->State() == ESpudUp || ref->State() == ESpudFlowOff || ref->State() == ESpudSuspended || ref->State() == ESpudFlowOffAndSuspended) + { + // Only pass on context changes while the context is up + FillInContextConfig(iTempContextConfig, aContextId); + SendContextParametersChangeEvent(aContextId, iTempContextConfig, aError); + } + else + { + __FLOG_3(_L("KContextParametersChangeEvent ignored on context %d because of nonoperational state %S(%d)"), + aContextId, SpudStateToText(ref->State()), ref->State()); + SetTerminateError(aContextId, aError); + } + } + +void CSpudMan::HandleContextBlockedEvent(TContextId aContextId) + { + // Context is suspended (see StopSending) + CSpudBinderRef* ref = NULL; + TRAPD(rc, ref = iBindMan->GetRefL(aContextId)); + __ASSERT_ALWAYS(rc == KErrNone, Panic()); + ASSERT(ref->IsBound()); + __FLOG_3(_L("context %d is in state %S(%d)"), aContextId, SpudStateToText(ref->State()), ref->State()); + switch (ref->State()) + { + case ESpudUp: + { + ref->SetState(ESpudSuspended); + if (AreQoSEventsEnabled()) + { + SendContextBlockedEvent(aContextId); + } + + // Send an IfProgress to Nifman so that RConnection::ProgressNotification reports the blockage etc. + Notify()->IfProgress(KDataTransferTemporarilyBlocked, KErrNone); + break; + } + + case ESpudFlowOff: + ref->SetState(ESpudFlowOffAndSuspended); + + // Send an IfProgress to Nifman so that RConnection::ProgressNotification reports the blockage etc. + Notify()->IfProgress(KDataTransferTemporarilyBlocked, KErrNone); + break; + + case ESpudSuspended: + case ESpudFlowOffAndSuspended: + // Ignore this since we're already suspended + break; + + default: + // Ignore this since we're still starting up or shutting down + __FLOG_1(_L("Can't send blocked event now on context %d"), aContextId); + break; + } + + // TODO: Probably need to send IfProgress here where aStage==KDataTransferTemporarilyBlocked + // or Notification with aEvent=EAgentToNifEventTypeDisableTimers + // + // NOTE: The IfProgress has now been implemented so that callers of RConnection::ProgressNotification + // expecting to receive KDataTransferTemporarilyBlocked actually get it if the PDP context is + // suspended (details of problem in INC107930). + } + +void CSpudMan::HandleContextUnblockedEvent(TContextId aContextId) + { + // Context is suspended (see StartSending) + CSpudBinderRef* ref = NULL; + TRAPD(rc, ref = iBindMan->GetRefL(aContextId)); + __ASSERT_ALWAYS(rc == KErrNone, Panic()); + ASSERT(ref->IsBound()); + __FLOG_3(_L("context %d is in state %S(%d)"), aContextId, SpudStateToText(ref->State()), ref->State()); + switch (ref->State()) + { + case ESpudUp: + case ESpudFlowOff: + // Ignore this since we're already in an unsuspended state + break; + + case ESpudSuspended: + { + ref->SetState(ESpudUp); + + BindMan()->SpudProtocol()->DoStartSending(); + + if (AreQoSEventsEnabled()) + { + SendContextUnblockedEvent(aContextId); + } + Notify()->IfProgress(KLinkLayerOpen, KErrNone); + break; + } + + case ESpudFlowOffAndSuspended: + { + ref->SetState(ESpudFlowOff); + Notify()->IfProgress(KLinkLayerOpen, KErrNone); + break; + } + + default: + // Ignore this since we're still starting up or shutting down + __FLOG_1(_L("Can't send unblocked event now on context %d; this may cause problems"), aContextId); + break; + } + } + +/** +Receives event from GUQoS. + +@param aName event identification +@param aOption optional data associated with event +@return error code +*/ +TInt CSpudMan::GuqosInput(TUint aName, TDes8& aOption) + { + __FLOG_2(_L("SpudMan::GuqosInput: GUQoS event %S(%d)"), SpudGuQoSEventToText(aName), aName); + switch (aName) + { + case KSoIfControllerPlugIn: + { + // Indicate that we want GUQoS + TSoIfControllerInfo& opt = *reinterpret_cast(const_cast(aOption.Ptr())); + _LIT(KQosPlugInName, "guqos"); + opt.iPlugIn = KQosPlugInName; + opt.iProtocolId = KQosPlugInProtocolId; + return KErrNone; + } + + case KRegisterEventHandler: + { + // GUQoS has passed a pointer to its event handler + const TEvent *handler = reinterpret_cast(aOption.Ptr()); + iQosEventHandler = static_cast(handler->iEvent); + ASSERT(iQosEventHandler); + return KErrNone; + } + + case KContextSetEvents: + { + if (!AreQoSEventsEnabled()) + { + // Event handler must be registered first + return KErrGeneral; + } + const TBool* eventsEnabledPtr = reinterpret_cast(aOption.Ptr()); + ASSERT(eventsEnabledPtr); + iQosEventsEnabled = *eventsEnabledPtr; + + // Has a primary PDP context has already been created? + CSpudBinderRef* ref = NULL; + TRAPD(rc, ref = BindMan()->GetAnyRefL()); + const TBool havePrimary(rc == KErrNone && (ref->State() == ESpudUp || ref->State() == ESpudFlowOff || ref->State() == ESpudSuspended || ref->State() == ESpudFlowOffAndSuspended)); + + if (iQosEventsEnabled && havePrimary) + { + // iPrimaryContextId == KPrimaryContextId == 0 at startup + SendPrimaryContextCreated(iPrimaryContextId, KErrNone); + } + return KErrNone; + } + + case KNifSetDefaultQoS: + { + ASSERT(aOption.Ptr()); + const TContextParameters& opt = *reinterpret_cast(aOption.Ptr()); + + if (!iPdpFsmInterface.IsInitialised()) + { + __FLOG_0(_L("CSpudMan::GuqosInput: iPdpFsmInterface not initialised, parking KNifSetDefaultQoS.")); + + if (iParkedDefaultQoS == NULL) + { + TRAPD(err, iParkedDefaultQoS = HBufC8::NewL (sizeof (TContextParameters))); + if (err != KErrNone) + { + __FLOG_0(_L("CSpudMan::GuqosInput: Failed to park default QoS.")); + return err; + } + } + + iParkedDefaultQoS->Des().Copy (aOption); + + return KErrNone; + } + + CSpudBinderRef* ref = NULL; + // Lower NIF for primary context has already been loaded by factory + TRAPD(rc, ref = BindMan()->GetRefL(iPrimaryContextId)); + __ASSERT_ALWAYS(rc == KErrNone, Panic()); + ASSERT(ref->IsBound()); + + ASSERT(ref->State() == ESpudInactive); + ref->SetState(ESpudHaveQos); + +#ifdef SYMBIAN_NETWORKING_UMTSR5 + // Store R5 QoS parameters + RPacketQoS::TQoSR5Requested qos; +#else + // Store QoS parameters + RPacketQoS::TQoSR99_R4Requested qos; +#endif +// SYMBIAN_NETWORKING_UMTSR5 + + opt.iContextConfig.GetUMTSQoSReq(qos); + rc = iPdpFsmInterface.Set(iPrimaryContextId, qos); + + if (rc != KErrNone) + { + __FLOG_1(_L("Setting default QoS on primary context failed with error [%d]. SPUD will shut down."), rc); + SetTerminateError(iPrimaryContextId, rc); + + SendPrimaryContextCreated(iPrimaryContextId, rc); // Notify GUQoS of error + + // At this point we know that SPUD is about to shut down, because we could not bring the + // primary context UP. + + //********************************************************************************************** + // Tell GUQoS to stop bothering SPUD. + // GUQoS returns the favour by turning off the NIF events within this very call. + // N.B.: "DEF055691 GUQoS crashes at shutdown": This defect results in GUQoS crash if the stack + // closes flows after the NIF proxy was deleted by GUQoS, as a result of KNetworkInterfaceDown event. + // As a temporary workaround, this call is disabled, and KNetworkInterfaceDown event is signalled only + // from the destructor. Once this defect is fixed, the following line must be uncommented: + // SendNetworkStatusEvent(KNetworkInterfaceDown, RPacketService::EStatusUnattached); + //********************************************************************************************** + + __FLOG_1(_L("Setting default QoS on primary context failed: mark lower NIF binder for context[%d] for async deletion."), iPrimaryContextId); + // We are about to delete the primary context NIF: this will notify Nifman that SPUD is finished. + DisposeOfBinder(ref); + } + + return KErrNone; + } + + case KContextCreate: + { + ASSERT(aOption.Ptr()); + TContextParameters& opt=*reinterpret_cast(const_cast(aOption.Ptr())); + ASSERT(opt.iContextType == ESecondaryContext); + + TContextId id(KAllContexts); // placeholder context ID + CSpudBinderRef* ref = NULL; + TRAPD(rc, ref = BindMan()->GetNewRefForSecondaryL(id);); + if(KErrNone != rc) + { + __FLOG_1(_L("Error %d creating a binder for the lower NIF"), rc); + opt.iReasonCode = rc; // The error code is the only argument we can pass to GUQoS, because there is no context. + return KErrNone; + } + + + TRAP(rc, BindMan()->LoadNifL(iName, *ref);) + if (rc != KErrNone) + { + __FLOG_1(_L("Error %d loading the lower NIF"), rc); + FillInParameters(opt, id, rc); + return KErrNone; + } + + ASSERT(ref->IsBound()); + ref->SetState(ESpudStartingSecondary); + + + // Reset the default QoS and TFT here. KContextQoSSet should arrive soon + // with the proper values. +#ifdef SYMBIAN_NETWORKING_UMTSR5 + RPacketQoS::TQoSR5Requested qos; +#else + RPacketQoS::TQoSR99_R4Requested qos; +#endif +// SYMBIAN_NETWORKING_UMTSR5 + rc = iPdpFsmInterface.Set(id, qos); + + TTFTInfo tft; + rc = iPdpFsmInterface.Set(id, tft); + + // Pass in the new context ID to SpudFsm to use for the new context + + // Notify SpudFsm + __FLOG_1(_L("Sending SpudFsm event ECreateSecondaryPDPContext context %d"), id); + rc = iPdpFsmInterface.Input(id, ECreateSecondaryPDPContext); + + // Set up the synchronous response + FillInParameters(opt, id, rc); + return KErrNone; + } + + case KContextDelete: + { + ASSERT(aOption.Ptr()); + TContextParameters& opt = *reinterpret_cast(const_cast(aOption.Ptr())); + CSpudBinderRef* ref = NULL; + TRAPD(rc, ref = BindMan()->GetRefL(opt.iContextInfo.iContextId)); + if (rc != KErrNone) + { + __FLOG_1(_L("Error: KContextDelete specifies context %d which does not exist"), opt.iContextInfo.iContextId); + opt.iReasonCode = rc; // The error code is the only argument we can pass to GUQoS, because there is no context. + return KErrNone; + } + + ASSERT(ref->IsBound()); + __FLOG_3(_L("KContextDelete context[%d] in state %S(%d)"), opt.iContextInfo.iContextId, SpudStateToText(ref->State()), ref->State()); + + switch (ref->State()) + { + // NIF is up: need to stop the NIF first, then delete the binder. + case ESpudGettingNegQoS: + case ESpudStartingSecondaryLowerNif: + // Assumption: GUQoS will never delete primary context. + case ESpudUp: + case ESpudFlowOff: + case ESpudSuspended: + case ESpudFlowOffAndSuspended: + ref->SetState(ESpudContextDelete); + + if(BindMan()->IsLastContext(opt.iContextInfo.iContextId)) // Are we about to shutdown after this? + { + SetTerminateError(KErrCancel); // KErrCancel is normally used to indicate "graceful" + } // user-initiated shutdown. + + // GUQoS ordered the shutdown: Network is still OK: initiate graceful shutdown of the lower NIF. + ref->NifLink()->Stop(KErrCancel, MNifIfNotify::EDisconnect); + break; + + case ESpudStartingSecondary: // NIF not started: need to delete the context. + ref->SetState(ESpudWaitLinkDown); + + // Delete context via SpudFsm + __FLOG_1(_L("Sending SpudFsm event EContextDelete context %d"), opt.iContextInfo.iContextId); + rc = iPdpFsmInterface.Input(opt.iContextInfo.iContextId, EContextDelete); + ASSERT(rc == KErrNone); + break; + + case ESpudWaitLinkDown: + // We're in the middle of deleting. Ignore this request. + break; + + case ESpudLinkDown: + // We're in the middle of deleting, but we no longer need to notify + // GUQoS when done. + ref->SetState(ESpudWaitLinkDown); + break; + + case ESpudWaitBinderDelete: // context deleted, NIF about to be deleted: ignore. + break; + + default: + __FLOG_2(_L("Unexpected state %S(%d)"), SpudStateToText(ref->State()), ref->State()); + ASSERT(EFalse); + break; + } + + FillInParameters(opt, opt.iContextInfo.iContextId, rc); + return KErrNone; + } + + case KContextActivate: + { + ASSERT(aOption.Ptr()); + TContextParameters& opt=*reinterpret_cast(const_cast(aOption.Ptr())); + CSpudBinderRef* ref = NULL; + // Validate context ID + TRAPD(rc, ref = BindMan()->GetRefL(opt.iContextInfo.iContextId)); + if (KErrNone != rc) + { + opt.iReasonCode = rc; // Can supply error code only, as the context does not exist. + __FLOG_1(_L("Error: KContextActivate specifies context %d which does not exist."), opt.iContextInfo.iContextId); + return KErrNone; + } + + if(!ref->IsBound() || ref->State() == ESpudWaitBinderDelete) + { + __FLOG_1(_L("Error: KContextActivate specifies context %d which is not bound to lower NIF."), opt.iContextInfo.iContextId); + rc = KErrNotReady; // that's what the GuQoS docs say + FillInParameters(opt, opt.iContextInfo.iContextId, rc); + return KErrNone; + } + + __FLOG_3(_L("KContextActivate context %d in state %S(%d)"), opt.iContextInfo.iContextId, SpudStateToText(ref->State()), ref->State()); + + ASSERT(ref->State() == ESpudStartingSecondary || ref->State() == ESpudUp); + + + // Notify SpudFsm + __FLOG_1(_L("Sending SpudFsm event EContextActivate context %d"), opt.iContextInfo.iContextId); + rc = iPdpFsmInterface.Input(opt.iContextInfo.iContextId, EContextActivate); + // Set up the synchronous response + FillInParameters(opt, opt.iContextInfo.iContextId, rc); + return KErrNone; + } + + case KContextQoSSet: + { + TContextParameters& opt=*reinterpret_cast(const_cast(aOption.Ptr())); + CSpudBinderRef* ref = NULL; + // Validate context ID + TRAPD(rc, ref = BindMan()->GetRefL(opt.iContextInfo.iContextId)); + if (KErrNone != rc) + { + __FLOG_1(_L("Error: KContextQoSSet specifies context %d which does not exist"), opt.iContextInfo.iContextId); + opt.iReasonCode = rc; // Can only supply error code, as the context does not exist. + return KErrNone; + } + + if(!ref->IsBound() || ref->State() == ESpudWaitBinderDelete) + { + __FLOG_1(_L("Error: KContextQoSSet specifies context %d which is not bound to the lower NIF."), opt.iContextInfo.iContextId); + rc = KErrNotReady; // that's what the GuQoS docs say + FillInParameters(opt, opt.iContextInfo.iContextId, rc); + return KErrNone; + } + __FLOG_3(_L("KContextQoSSet context %d in state %S(%d)"), opt.iContextInfo.iContextId, SpudStateToText(ref->State()), ref->State()); + + ASSERT(ref->State() == ESpudStartingSecondary + || ref->State() == ESpudUp + || ref->State() == ESpudFlowOff + || ref->State() == ESpudSuspended + || ref->State() == ESpudFlowOffAndSuspended + || ref->State() == ESpudLinkDown ); + +#ifdef SYMBIAN_NETWORKING_UMTSR5 + RPacketQoS::TQoSR5Requested qos; +#else + RPacketQoS::TQoSR99_R4Requested qos; +#endif +// SYMBIAN_NETWORKING_UMTSR5 + + // Store QoS parameters + opt.iContextConfig.GetUMTSQoSReq(qos); + rc = iPdpFsmInterface.Set(opt.iContextInfo.iContextId, qos); + + // Notify SpudFsm + if (rc == KErrNone) + { + __FLOG_1(_L("Sending SpudFsm event EContextQoSSet context %d"), opt.iContextInfo.iContextId); + rc = iPdpFsmInterface.Input(opt.iContextInfo.iContextId, EContextQoSSet); + } + FillInParameters(opt, opt.iContextInfo.iContextId, rc); + return KErrNone; + } + + case KContextTFTModify: + { + TContextParameters& opt=*reinterpret_cast(const_cast(aOption.Ptr())); + CSpudBinderRef* ref = NULL; + // Validate context ID + TRAPD(rc, ref = BindMan()->GetRefL(opt.iContextInfo.iContextId)); + if (KErrNone != rc) + { + __FLOG_1(_L("Error: KContextTFTModify specifies context %d which does not exist"), opt.iContextInfo.iContextId); + opt.iReasonCode = rc; // Can only supply error code as the context does not exist. + return KErrNone; + } + + // keep local reference to Primary Context up to date + if (opt.iContextType == EPrimaryContext) + { + iPrimaryContextId = opt.iContextInfo.iContextId; + } + + if(!ref->IsBound() || ref->State() == ESpudWaitBinderDelete) + { + __FLOG_1(_L("Error: KContextTFTModify specifies context %d which is not bound to the lower NIF."), opt.iContextInfo.iContextId); + rc = KErrNotReady; // that's what the GuQoS docs say + FillInParameters(opt, opt.iContextInfo.iContextId, rc); + return KErrNone; + } + __FLOG_3(_L("KContextTFTModify context %d in state %S(%d)"), opt.iContextInfo.iContextId, SpudStateToText(ref->State()), ref->State()); + + ASSERT(ref->State() == ESpudStartingSecondary || + ref->State() == ESpudUp || + ref->State() == ESpudWaitLinkDown || + ref->State() == ESpudLinkDown || + ref->State() == ESpudGettingNegQoS); + // It is assumed that the TFT can be modified before the ctx reported activation + + // Store TFT parameters + TTFTInfo tft; + opt.iContextConfig.GetTFTInfo(tft); + + rc = iPdpFsmInterface.Set(opt.iContextInfo.iContextId, tft); + if (rc != KErrNone) + { + __FLOG_1(_L("Error %d setting TFT"), rc); + FillInParameters(opt, opt.iContextInfo.iContextId, rc); + return KErrNone; + } + rc = iPdpFsmInterface.Set(opt.iContextInfo.iContextId, opt.iTFTOperationCode); + if (rc != KErrNone) + { + __FLOG_1(_L("Error %d setting TFT operation code"), rc); + FillInParameters(opt, opt.iContextInfo.iContextId, rc); + return KErrNone; + } + + // Notify SpudFsm + __FLOG_1(_L("Sending SpudFsm event EContextTFTModify context %d"), opt.iContextInfo.iContextId); + rc = iPdpFsmInterface.Input(opt.iContextInfo.iContextId, EContextTFTModify); + + FillInParameters(opt, opt.iContextInfo.iContextId, rc); + return KErrNone; + } + + case KContextModifyActive: + { + TContextParameters& opt=*reinterpret_cast(const_cast(aOption.Ptr())); + CSpudBinderRef* ref = NULL; + // Validate context ID + TRAPD(rc, ref = BindMan()->GetRefL(opt.iContextInfo.iContextId)); + if (KErrNone != rc) + { + __FLOG_1(_L("Error: KContextModifyActive specifies context %d which does not exist"), opt.iContextInfo.iContextId); + opt.iReasonCode = rc; + return KErrNone; + } + + if(!ref->IsBound() || ref->State() == ESpudWaitBinderDelete) + { + __FLOG_1(_L("Error: KContextModifyActive specifies context %d which is not bound to the lower NIF."), opt.iContextInfo.iContextId); + rc = KErrNotReady; // that's what the GuQoS docs say + FillInParameters(opt, opt.iContextInfo.iContextId, rc); + return KErrNone; + } + + // Notify SpudFsm + __FLOG_1(_L("Sending SpudFsm event EContextModifyActive context %d"), opt.iContextInfo.iContextId); + rc = iPdpFsmInterface.Input(opt.iContextInfo.iContextId, EContextModifyActive); + + FillInParameters(opt, opt.iContextInfo.iContextId, rc); + return KErrNone; + } + + case KInitialisePdpFsm: + { + MPdpFsmInterface* pdpFsm = *reinterpret_cast(const_cast(aOption.Ptr())); + iPdpFsmInterface.Init(pdpFsm); + return KErrNone; + } + + default: + __FLOG_1(_L("Unhandled event %d"), aName); + break; + } + return KErrNotSupported; + } + +/** +Receives link up indication from a lower NIF. + +@param aContextId PDP context ID of the associated lower NIF +*/ +void CSpudMan::LinkLayerUp(TContextId aContextId) + { + CSpudBinderRef* ref = NULL; + TRAPD(rc, ref = iBindMan->GetRefL(aContextId)); + __ASSERT_ALWAYS(rc == KErrNone, Panic()); + ASSERT(ref->IsBound() && ref->State() != ESpudWaitBinderDelete); + + __FLOG_3(_L("Lower NIF LinkLayerUp on context[%d], in state %S(%d)"), aContextId, SpudStateToText(ref->State()), ref->State()); + switch (ref->State()) + { + case ESpudStartingPrimaryLowerNif: + { + ref->SetState(ESpudUp); + if (AreQoSEventsEnabled()) + { + SendPrimaryContextCreated(aContextId, KErrNone); + } + // If QoS is not yet enabled, SendPrimaryContextCreated() will be called when it is + + Notify()->LinkLayerUp(); + + // If mobile IP is enabled, this progress notification should not be sent here. + // This will need to be addressed if MIP is ever enabled for UMTS. + Notify()->IfProgress(KLinkLayerOpen, KErrNone); + } + break; + + case ESpudStartingSecondaryLowerNif: + { + // lower nif is up, now get the Negotiated QoS + ref->SetState(ESpudGettingNegQoS); + + TInt rc = iPdpFsmInterface.Input(aContextId, EGetNegQoS); + ASSERT(rc == KErrNone); + // Now wait for retrieval of negotiated QoS. + // The following 2 functions will be fired from CSpudMan::HandleGetNegQoSEvent(): + // FillInContextConfig(iTempContextConfig, aContextId); + // SendContextActivateEvent(aContextId, iTempContextConfig, KErrNone); + } + break; + + default: + __FLOG_3(_L("Lower NIF LinkLayerUp on context[%d], in unexpected state %S(%d)"), aContextId, SpudStateToText(ref->State()), ref->State()); + ASSERT(EFalse); + } + } + +/** +Receives link down indication from a lower NIF. + +@param aContextId Valid PDP context ID of the associated lower NIF +@param aReason A Symbian OS error code indicating the reason for the link closing down +@param aAction The action that should be taken as a result of link layer down being signalled +*/ +void CSpudMan::LinkLayerDown(TContextId aContextId, TInt aReason, MNifIfNotify::TAction aAction) + { + __FLOG_3(_L("CSpudMan::LinkLayerDown: context %d reason %d action %d"), aContextId, aReason, aAction); + + if (aAction == MNifIfNotify::ENoAction) + { + // This call indicates a renegotiation of the lower link, not that the link has + // actually gone down. This is of no interest to SPUD. + __FLOG_0(_L("Ignoring MNifIfNotify::ENoAction")); + return; + } + + CSpudBinderRef* ref = NULL; + TRAPD(rc, ref = iBindMan->GetRefL(aContextId)); + __ASSERT_ALWAYS(rc == KErrNone, Panic()); + __FLOG_3(_L("context %d is in state %S(%d)"), aContextId, SpudStateToText(ref->State()), ref->State()); + + + TBool isUpperFlowOn = EFalse; // Can GUQoS attempt to send on this PDP context? + + switch (ref->State()) + { + case ESpudUp: + isUpperFlowOn = ETrue; // ESpudUp is the only state where GUQoS is allowed to send on us. + // Fall through + case ESpudFlowOff:// NIF & context were activated succcessfully. + case ESpudSuspended: + case ESpudFlowOffAndSuspended: + ref->SetState(ESpudLinkDown); // Must Notify GUQoS that context is down. + + // Must inform upper layers they can no longer send on this context. + if (isUpperFlowOn && AreQoSEventsEnabled()) + { + // At this point the context status does not indicate to GUQoS that the context is about to go down. + // Most likely, the status is EStatusActive, indicating that the context can handle requests. + // Since the lower NIF is down, we are about to delete the context, so logically, this context + // is EStatusDeactivating.(From spudman's PoV, a context is a union of lower NIF and ETel's RPacketContext) + // GUQoS does not know this, it may try to issue requests, + // such as send packets on this context, modify TFT, modify QoS, etc. + // Note: this does not seem to happen, but it is feasible. + + // N.B. Ideally, we should signal context change status in not just in EStateUp, but in + // EStateFlowOff etc, i.e. any state in which the context is alive. Unfortunately, GUQoS + // does not allow us to do this - there is no pure "ContextStatusChange" upcall on GUQoS. + // Context Parameters Change upcall should not be used, because GUQoS interprets it as a + // QoS parameters change, and sends QoSEventAdapt to the QoS framework. This is wrong. + // + // We could send ContextBlocked in FlowOff / Suspended states, however, this is problematic, + // because GUQoS has already received one of these notifications. We could "trick" it and cycle + // the state (send Flow On / Resume, then Flow Off / Suspend again), but this hackery should be + // avoided unless absolutely necessary. So far, GUQoS does not seem to do anything beside sending + // a packet on a deactivaing context, so we only worry about it. + + // N.B. SpudFSM is asynchronous, it will not issue a an actual deletion request on ETel until this RunL + // returns. This means that the context status will not be updated to EStatusDeactiving in ETel + // until then, so when we fill in context paramters, the status is going to be EStatusActive. + // We "sneak in" the correct status by overriding it. Doing it in SpudFSM would be wrong, because + // SpudFSM reflects the ETel side of the context, so overriding the status there is misleading, + // because it can be used internally by SpudFSM / SpudTel. + + __FLOG_0(_L("Upper flow on the context is On: flow Off GUQoS to prevent it from sending on a context with dead lower NIF.")); + iContextStatusOverride = RPacketContext::EStatusDeactivating; + SendContextBlockedEvent(aContextId); // resets the override + isUpperFlowOn = EFalse; + } + + // The only way we can notify GUQoS once the context has been activated is + // to delete it via the SpudFsm + __FLOG_1(_L("Sending SpudFsm event EContextDelete context %d"), aContextId); + rc = iPdpFsmInterface.Input(aContextId, EContextDelete); + __FLOG_1(_L("SpudFsm::Input() returned status %d"), rc); + if (rc == KErrNotReady) //This is becuase the FSM has been shut down via UMTSGPRSSCPR + { + HandleContextDeleteEvent(aContextId, aReason); //Just pretend that the fsm returned something sensible + } + ASSERT(rc == KErrNone || rc == KErrNotReady); + break; + + case ESpudGettingNegQoS: // CNifIfLink::Stop will handle this just as if the NIF was still being CNifIfLink::Start'ed. + case ESpudStartingSecondaryLowerNif: // Attempt to bring up the lower NIF was made. + ref->SetState(ESpudStartingSecondary); // GUQoS should delete us later. + // Notify GUQoS that Activation failed. It will take the control from here, most likely deleting the context. + FillInContextConfig(iTempContextConfig, aContextId); + if(ref->Error() != KErrNone) + { + SendContextActivateEvent(aContextId, iTempContextConfig, ref->Error()); + } + else + { + SendContextActivateEvent(aContextId, iTempContextConfig, aReason); + } + break; + + case ESpudContextDelete: + ref->SetState(ESpudWaitLinkDown); // GUQoS triggered us: don't need to notify it. + + // Delete context via SpudFsm + __FLOG_1(_L("Sending SpudFsm event EContextDelete context %d"), aContextId); + rc = iPdpFsmInterface.Input(aContextId, EContextDelete); + __FLOG_1(_L("SpudFsm::Input() returned status %d"), rc); + ASSERT(rc == KErrNone); + break; + + case ESpudStartingSecondary: + case ESpudLinkDown: // Added for INC066156. + case ESpudWaitLinkDown: + // Link has finally gone down. Context is already deleted. + __FLOG_1(_L("Lower NIF reported LinkLayerDown: mark the binder for context[%d] for async deletion"), aContextId); + DisposeOfBinder(ref); + break; + + case ESpudStartingPrimaryLowerNif: + { + SetTerminateError(aContextId, aReason); // SPUD is going to terminate. + SetTerminateError(KErrCouldNotConnect); + + // The primary context is managed by SPUD. We delete it ourselves. + __FLOG_1(_L("Sending SpudFsm event EContextDelete context %d"), aContextId); + ref->SetState(ESpudWaitLinkDown); + rc = iPdpFsmInterface.Input(aContextId, EContextDelete); + __FLOG_1(_L("SpudFsm::Input() returned status %d"), rc); + ASSERT(rc == KErrNone); + + // Meanwhile, notify GUQoS about the failed primary creation + if (AreQoSEventsEnabled()) + { + SendPrimaryContextCreated(aContextId, aReason); + } + break; + } + + default: + __FLOG_2(_L("Unexpected state %S(%d)"), SpudStateToText(ref->State()), ref->State()); + ASSERT(EFalse); + break; + } + } + +/** +Receives flow off indication from a lower NIF. + +@param aContextId Valid PDP context ID of the associated lower NIF +*/ +void CSpudMan::StopSending(TContextId aContextId) + { + __FLOG_1(_L("CSpudMan::StopSending context %d"), aContextId); + CSpudBinderRef* ref = NULL; + TRAPD(rc, ref = iBindMan->GetRefL(aContextId)); + __ASSERT_ALWAYS(rc == KErrNone, Panic()); + __FLOG_3(_L("context %d is in state %S(%d)"), aContextId, SpudStateToText(ref->State()), ref->State()); + switch (ref->State()) + { + case ESpudUp: + ref->SetState(ESpudFlowOff); + if (AreQoSEventsEnabled()) + { + SendContextBlockedEvent(aContextId); + } + break; + + case ESpudSuspended: + ref->SetState(ESpudFlowOffAndSuspended); + break; + + case ESpudContextDelete: + case ESpudWaitLinkDown: + case ESpudLinkDown: //sometimes this leaks in + // ignore + break; + + default: + // error + __FLOG_2(_L("Unexpected state %S(%d)"), SpudStateToText(ref->State()), ref->State()); + ASSERT(EFalse); + break; + } + } + +/** +Receives flow on indication from a lower NIF. + +@param aContextId Valid PDP context ID of the associated lower NIF +*/ +void CSpudMan::StartSending(TContextId aContextId) + { + __FLOG_1(_L("CSpudMan::StartSending context %d"), aContextId); + CSpudBinderRef* ref = NULL; + TRAPD(rc, ref = iBindMan->GetRefL(aContextId)); + __ASSERT_ALWAYS(rc == KErrNone, Panic()); + __FLOG_3(_L("context %d is in state %S(%d)"), aContextId, SpudStateToText(ref->State()), ref->State()); + switch (ref->State()) + { + case ESpudGettingNegQoS: // Lower NIF for the 2ndary ctx is up, waiting for negotiated QoS. + break; // GUQoS, TCP/IP stack will be notified when negotiated QoS is retrieved. + // Notes: + // 1.there is a *potential* race condition where the negotiated QoS is retrived before lower NIF signals StartSending. + // This does not seem to happen with PPP. If this happens somehow, the ctx will be in the "UP" state and it will be handled correctly. + // 2. Theoretically, the lower NIF may report LinkLayerUp (triggering retrieval of negotiated QoS), then signal + // StartSending much later (e.g. a PPP implementation singalling LinkLayerUp from LCP, but StartSending from NCP). + // SPUD CANNOT HANDLE THIS. + // Existing Raw IP and PPP NIFs signal StartSending immediately after LinkLayerUp, so this has no consequences. + + case ESpudUp: + // This can happen for the initial StartSending after the lower NIF comes up + // Treat it the same as ESpudFlowOff and fall through. + case ESpudFlowOff: + ref->SetState(ESpudUp); + + // This must only be done AFTER the KPrimaryContextCreated event is sent (which in this state it is) + // It's not clear if StartSending is needed/allowed in addition to the GUQoS event. + BindMan()->SpudProtocol()->DoStartSending(); + + if (AreQoSEventsEnabled()) + { + SendContextUnblockedEvent(aContextId); + } + break; + + case ESpudFlowOffAndSuspended: + ref->SetState(ESpudSuspended); + break; + + case ESpudSuspended: + case ESpudContextDelete: + case ESpudWaitLinkDown: + // ignore + __FLOG_1(_L("Ignored StartSending on context %d (this should be OK)"), aContextId); + break; + + default: + // We have encountered a serious problem. If we get StartSending before reaching the ESpudUp + // state, we'll lose it and the upper networking protocol will never be notified. + // As long as the lower NIF calls LinkLayerUp before StartSending, we'll be fine. + __FLOG_1(_L("Can't send unblocked event now on context %d; this may cause problems"), aContextId); + ASSERT(EFalse); + break; + } + } + + +//***************************************************************************** +// Event senders to GUQoS +//***************************************************************************** + +/** +Sends event to GUQoS. + +@param aName event identifier +@param aOption TPckg<> event data +*/ +void CSpudMan::RaiseEvent(TUint aName, TDes8& aOption) const + { + __FLOG_2(_L("Sending event %S(%d) to GUQoS"), SpudFsmEventToText(aName), aName); + iQosEventHandler->Event(reinterpret_cast(iBindMan->SpudMux()), aName, aOption); + } + +/** +Fills in common event parameters for the given context. + +@param aParams parameter structure +@param aContextId Valid PDP context ID +*/ +void CSpudMan::FillInParameters(TContextParameters& aParams, TContextId aContextId, TInt aError) const + { + CSpudBinderRef* ref = NULL; + TRAPD(rc, ref = BindMan()->GetRefL(aContextId)); + ASSERT(rc == KErrNone); + + TContextType type(EContextTypeUnknown); + if (rc == KErrNone) + { + switch (ref->State()) + { + case ESpudHaveQos: + case ESpudCreatingPrimary: + case ESpudStartingPrimary: + type = EPrimaryContext; + break; + + case ESpudStartingSecondary: + type = ESecondaryContext; + break; + + default: + type = (aContextId == iPrimaryContextId) ? EPrimaryContext : ESecondaryContext;; + break; + } + } + aParams.iContextType = type; // Context type + aParams.iReasonCode = aError; // Error code + //aParams.iContextInfo.iStatus = StateToStatus(*ref); + iPdpFsmInterface.Get(aContextId, aParams.iContextInfo.iStatus); + aParams.iContextInfo.iContextId = aContextId; + } + +/** +Fill in context configuration parameter structure using SpudFsm's parameters. + +@param aConfig parameter structure +@param aContextId PDP context ID +*/ +void CSpudMan::FillInContextConfig(TContextConfig& aConfig, TContextId aContextId) const + { +#ifdef SYMBIAN_NETWORKING_UMTSR5 + RPacketQoS::TQoSR5Negotiated qos; +#else + RPacketQoS::TQoSR99_R4Negotiated qos; +#endif +// SYMBIAN_NETWORKING_UMTSR5 + + iPdpFsmInterface.Get(aContextId, qos); + aConfig.SetUMTSQoSNeg(qos); + + iPdpFsmInterface.Get(aContextId, iTempTftInfo); + aConfig.SetTFTInfo(iTempTftInfo); + + iPdpFsmInterface.Get(aContextId, iTempGprsContext); + aConfig.SetContextConfig(iTempGprsContext); + } + +/** +Sends KPrimaryContextCreated event to GUQoS. +*/ +void CSpudMan::SendPrimaryContextCreated(TContextId aContextId, TInt aError) + { + __FLOG_2(_L("SendPrimaryContextCreated context %d error %d"), aContextId, aError); + + if (!iQosEventsEnabled) + { + __FLOG_0(_L("Error: sending events is disabled - GuQoS not present ?")); + return; + } + // We make this assumption in various places + ASSERT(aContextId == 0); + + TContextParameters primaryContextCreatedEvent; + FillInParameters(primaryContextCreatedEvent, aContextId, aError); + TPckg event(primaryContextCreatedEvent); + RaiseEvent(KPrimaryContextCreated, event); + } + + +/** +Sends KSecondaryContextCreated event to GUQoS. + +@param aContextId Context ID +@param aError error code +*/ +void CSpudMan::SendSecondaryContextCreated(TContextId aContextId, TInt aError) + { + __FLOG_2(_L("SendSecondaryContextCreated context %d error %d"), aContextId, aError); + + if (!iQosEventsEnabled) + { + __FLOG_0(_L("Error: sending events is disabled - GuQoS not present ?")); + return; + } + + // We make this assumption in various places + ASSERT(aContextId != 0); + + TContextParameters event; + FillInParameters(event, aContextId, aError); + TPckg eventPckg(event); + RaiseEvent(KSecondaryContextCreated, eventPckg); + } + + +/** +Sends KContextBlockedEvent event to GUQoS. +*/ +void CSpudMan::SendContextBlockedEvent(TContextId aContextId) + { + __FLOG_2(_L("SendContextBlockedEvent context %d error %d"), aContextId, KErrNone); + + if (!iQosEventsEnabled) + { + __FLOG_0(_L("Error: sending events is disabled - GuQoS not present ?")); + return; + } + + TContextParameters event; + FillInParameters(event, aContextId); + + /** The status we want to signal to GUQoS may be different from the context status we get from SpudFSM + E.g., if we are about to deactivate the context, the logical status of the context is EStatusDeactivating, rather than + EStatusActive, even though that's what SpudFSM will tell us */ + if(RPacketContext::EStatusUnknown != iContextStatusOverride) + { + __FLOG_2(_L("SendContextBlockedEvent: context status overriden to %d, original: %d."), + iContextStatusOverride, event.iContextInfo.iStatus); + + event.iContextInfo.iStatus = iContextStatusOverride; + iContextStatusOverride = RPacketContext::EStatusUnknown; + } + + TPckg eventPckg(event); + RaiseEvent(KContextBlockedEvent, eventPckg); + } + +/** +Sends KContextUnblockedEvent event to GUQoS. +*/ +void CSpudMan::SendContextUnblockedEvent(TContextId aContextId) + { + __FLOG_2(_L("SendContextUnblockedEvent context %d error %d"), aContextId, KErrNone); + + if (!iQosEventsEnabled) + { + __FLOG_0(_L("Error: sending events is disabled - GuQoS not present ?")); + return; + } + + TContextParameters event; + FillInParameters(event, aContextId); + TPckg eventPckg(event); + RaiseEvent(KContextUnblockedEvent, eventPckg); + } + +/** +Sends KContextQoSSetEvent event to GUQoS. + +@param aContextId Context ID +@param aError error code +*/ +void CSpudMan::SendContextQoSSetEvent(TContextId aContextId, TInt aError) + { + __FLOG_2(_L("SendContextQoSSetEvent context %d error %d"), aContextId, aError); + + if (!iQosEventsEnabled) + { + __FLOG_0(_L("Error: sending events is disabled - GuQoS not present ?")); + return; + } + + TContextParameters event; + FillInParameters(event, aContextId, aError); + TPckg eventPckg(event); + RaiseEvent(KContextQoSSetEvent, eventPckg); + } + +/** +Sends KContextTFTModifiedEvent event to GUQoS. + +@param aContextId Context ID +#param aTFTOperationCode TFT operation code +@param aError error code +*/ +void CSpudMan::SendContextTFTModifiedEvent(TContextId aContextId, TTFTOperationCode aTFTOperationCode, TInt aError) + { + __FLOG_2(_L("SendContextTFTModifiedEvent context %d error %d"), aContextId, aError); + + if (!iQosEventsEnabled) + { + __FLOG_0(_L("Error: sending events is disabled - GuQoS not present ?")); + return; + } + + TContextParameters event; + FillInParameters(event, aContextId, aError); + // Also need to fill in TTFTOperationCode, an undocumented requirement + event.iTFTOperationCode = aTFTOperationCode; + TPckg eventPckg(event); + RaiseEvent(KContextTFTModifiedEvent, eventPckg); + } + +/** +Sends KContextModifyActiveEvent event to GUQoS. + +@param aContextId Context ID +@param aContextConfig Configuration parameters for this context +@param aError error code +*/ +void CSpudMan::SendContextModifyActiveEvent(TContextId aContextId, TContextConfig& aContextConfig, TInt aError) + { + __FLOG_2(_L("SendContextModifyActiveEvent context %d error %d"), aContextId, aError); + + if (!iQosEventsEnabled) + { + __FLOG_0(_L("Error: sending events is disabled - GuQoS not present ?")); + return; + } + + TContextParameters event; + FillInParameters(event, aContextId, aError); + event.iContextConfig = aContextConfig; + TPckg eventPckg(event); + RaiseEvent(KContextModifyActiveEvent, eventPckg); + } + +/** +Sends KContextActivateEvent event to GUQoS. + +@param aContextId Context ID +@param aContextConfig Configuration parameters for this context +@param aError error code +*/ +void CSpudMan::SendContextActivateEvent(TContextId aContextId, TContextConfig& aContextConfig, TInt aError) + { + __FLOG_2(_L("SendContextActivateEvent context %d error %d"), aContextId, aError); + + if (!iQosEventsEnabled) + { + __FLOG_0(_L("Error: sending events is disabled - GuQoS not present ?")); + return; + } + + TContextParameters event; + FillInParameters(event, aContextId, aError); + event.iContextConfig = aContextConfig; + TPckg eventPckg(event); + RaiseEvent(KContextActivateEvent, eventPckg); + } + +/** +Sends KContextParametersChangeEvent event to GUQoS. + +@param aContextId Context ID +@param aContextConfig Configuration parameters for this context +@param aError error code +*/ +void CSpudMan::SendContextParametersChangeEvent(TContextId aContextId, TContextConfig& aContextConfig, TInt aError) + { + __FLOG_2(_L("SendContextParametersChangeEvent context %d error %d"), aContextId, aError); + + if (!iQosEventsEnabled) + { + __FLOG_0(_L("Error: sending events is disabled - GuQoS not present ?")); + return; + } + + TContextParameters event; + FillInParameters(event, aContextId, aError); + event.iContextConfig = aContextConfig; + TPckg eventPckg(event); + RaiseEvent(KContextParametersChangeEvent, eventPckg); + } + +/** +Sends KContextDeleteEvent event to GUQoS. + +@param aContextId Context ID +*/ +void CSpudMan::SendContextDeleteEvent(TContextId aContextId) + { + __FLOG_2(_L("SendContextDeleteEvent context %d error %d"), aContextId, KErrNone); + + if (!iQosEventsEnabled) + { + __FLOG_0(_L("Error: sending events is disabled - GuQoS not present ?")); + return; + } + + TContextParameters event; + FillInParameters(event, aContextId); + TPckg eventPckg(event); + RaiseEvent(KContextDeleteEvent, eventPckg); + } + +/** +Sends KNetworkStatusEvent event to GUQoS. + +@param aEventCode Event code +@param aStatus Network status +*/ +void CSpudMan::SendNetworkStatusEvent(TNetworkEventCode aEventCode, RPacketService::TStatus aStatus) + { + __FLOG_2(_L("SendNetworkStatusEvent event code %d status %d"), aEventCode, aStatus); + + if (!iQosEventsEnabled) // QoS events not turned on yet, or have been turned off by GUQoS + { + __FLOG_0(_L("Error: sending events is disabled - GuQoS not present ?")); + return; + } + + TNetworkParameters event; + event.iNetworkEventCode = aEventCode; + event.iNetworkStatus = aStatus; + TPckg eventPckg(event); + RaiseEvent(KNetworkStatusEvent, eventPckg); + } + + +//***************************************************************************** +// CNifIfLink methods +//***************************************************************************** + + +/** +Return the link protocol handler object. + +@param aName Protocol name desired +@return Pointer to link protocol handler (ownership is transferred) +*/ +CNifIfBase* CSpudMan::GetBinderL(const TDesC& aName) + { + __FLOG_1(_L("CSpudMan::GetBinderL %S"), &aName); + iName = aName; + return static_cast(iBindMan->TransferSpudMux()); + } + +/** +Return information about the SPUD NIF. + +@param aInfo Receives the NIF interface info +*/ +void CSpudMan::Info(TNifIfInfo& aInfo) const + { + CSpudBinderRef* ref = NULL; + // Get the binder for the first (default) lower NIF. + TRAPD(err, ref = iBindMan->GetAnyRefL()); + if (err == KErrNone) + { + // Read the protocol supported value from the lower NIF + ref->NifLink()->Info(aInfo); + ASSERT(aInfo.iFlags == (KNifIfIsBase | KNifIfUsesNotify | KNifIfIsLink | KNifIfCreatedByFactory | KNifIfCreatesBinder)); + } + else + { + aInfo.iProtocolSupported=KProtocolUnknown; + } + + aInfo.iVersion = TVersion(KSpudMajorVersionNumber, KSpudMinorVersionNumber, KSpudBuildVersionNumber); + aInfo.iFlags = KNifIfIsBase | + KNifIfUsesNotify | + KNifIfIsLink | + KNifIfCreatedByFactory | + KNifIfCreatesBinder; + aInfo.iName = KSpudName; + } + +/** +Processes notifications from Agent + +@param aEvent Event type +@param aInfo Data relating to event + +@return Error code +*/ +TInt CSpudMan::Notification(TAgentToNifEventType aEvent, void * aInfo) + { + __FLOG_1(_L("CSpudMan::Notification event %d"), aEvent); + TInt rc = KErrNotSupported; + switch (aEvent) + { + case EAgentToNifEventTypeModifyInitialTimer: + case EAgentToNifEventTypeDisableTimers: + case EAgentToNifEventTypeEnableTimers: + case EAgentToNifEventTsyConfig: + case EAgentToNifEventTsyConnectionSpeed: + // Send notification to all lower NIFs + rc = KErrNotReady; + TContextId i; + for (i=0; i < KMaxPdpContexts; ++i) + { + CSpudBinderRef* ref = NULL; + TRAP(rc, ref = iBindMan->GetRefL(i)); + if (rc == KErrNone) + { + rc = ref->NifLink()->Notification(aEvent, aInfo); + } + } + break; + + case EAgentToNifEventTypeGetDataTransfer: + { + TPckg* totalDataPackage = (TPckg*) aInfo; + RPacketContext::TDataVolume& totalData = (*totalDataPackage)(); + totalData.iBytesSent = 0; + totalData.iOverflowCounterSent = 0; + totalData.iBytesReceived = 0; + totalData.iOverflowCounterReceived = 0; + + RPacketContext::TDataVolume data; + TPckg dataPackage(data); + + // Add up data reported by all NIFs + rc = KErrNotReady; + TContextId i; + for (i=0; i < KMaxPdpContexts; ++i) + { + CSpudBinderRef* ref = NULL; + TRAP(rc, ref = iBindMan->GetRefL(i)); + if (rc == KErrNone) + { + rc = ref->NifLink()->Notification(aEvent, &dataPackage); + if (rc == KErrNone) + { + totalData.iBytesSent += data.iBytesSent; + totalData.iOverflowCounterSent += data.iOverflowCounterSent; + totalData.iBytesReceived += data.iBytesReceived; + totalData.iOverflowCounterReceived += data.iOverflowCounterReceived; + } + } + } + break; + } + + case EAgentToNifEventTypeDisableConnection: + // TODO: what to do with this? + default: + __FLOG_1(_L("Notification event %d was ignored"), aEvent); + break; + } + + return rc; + } + + +/** +Start the link. +At this point only the primary PDP context is valid. + +@return Error code +*/ +TInt CSpudMan::Start() + { + __FLOG_1(_L("CSpudMan::Start(0x%x)"), this); + + // SpudTel needs TSY name from CommDb + TName tsyName; + ReadTsyName(tsyName); + + // Initialise SpudFsm + TRAPD(err, InitPdpFsmInterfaceL()); + if (err != KErrNone) + { + __FLOG_1(_L("CSpudMan::Start: Failed to initialise the PDP Fsm Interface,Error = %d"),err); + return err; + } + + // Open SpudFsm + TRAP(err, iPdpFsmInterface.OpenL(this, tsyName)); + if (err != KErrNone) + { + __FLOG_1(_L("CSpudMan::Start: Failed to open the PDP Fsm Interface,Error = %d"),err); + return err; + } + + + // re-initialise the temporary data structure before retrieving + // GPRS config parameters from CommDB + __FLOG_0(_L("CSpudMan::Start: Getting default GPRS settings from Commdb")); + RetrieveGprsConfig(iTempGprsContext); + + TRAP(err, SetupSipServerAddrRetrievalL(iTempGprsContext.iProtocolConfigOption);); + +#ifdef SYMBIAN_NETWORKING_UMTSR5 + // Add the IMCN Signalling Status flag. IM CN status flag is retrieved from the Database + // Request For the status of IM CN dedicated signalling context + TRAP(err,SetIMCNSignallingFlagPcoL(iTempGprsContext.iProtocolConfigOption)); + + // Not sure what can be done after trapping the error, because its not an error condition for starting of + // Primary PDP context. +#ifdef __FLOG_ACTIVE + if(err != KErrNone) + { + __FLOG_1(_L("CSpudMan::Start: Failed to set IM CN signalling Flag.Error = %d"),err); + } +#endif +#endif // SYMBIAN_NETWORKING_UMTSR5 + + iPdpFsmInterface.Set(iPrimaryContextId, iTempGprsContext); + if (err != KErrNone) + { + __FLOG_1(_L("CSpudMan::Start: Setup sip server address retrieval. Failed with %d"),err); + return err; + } + + if (iParkedDefaultQoS != NULL) + { + __FLOG_0(_L("CSpudMan::Start: Found parked QoS settings from GuQoS")); + + TPtr8 qos(iParkedDefaultQoS->Des()); + GuqosInput (KNifSetDefaultQoS, qos); + + delete iParkedDefaultQoS; + iParkedDefaultQoS = NULL; + } + + CSpudBinderRef* ref = NULL; + // Get the binder for the first (default) lower NIF. + TRAP(err, ref = iBindMan->GetRefL(iPrimaryContextId)); + if (err != KErrNone) + { + __FLOG_0(_L("CSpudMan::Start: Error - no context could be found")); + return err; + } + + ASSERT(ref->State() == ESpudInactive || ref->State() == ESpudHaveQos); + ASSERT(ref->State() != ESpudWaitBinderDelete); + + if (ref->State() == ESpudWaitBinderDelete) + { + return KErrNotReady; + } + + if (ref->State() != ESpudHaveQos) + { + __FLOG_0(_L("CSpudMan::Start: No QoS parameters have been set - is GuQoS present?")); + + // Sets default QoS parameters because either + // 1) they weren't supplied by GUQoS - this shouldn't happen + // 2) or GuQoS has been configured out +#ifdef SYMBIAN_NETWORKING_UMTSR5 + // Sets default R5 QoS parameters because they weren't supplied by GUQoS. + RPacketQoS::TQoSR5Requested qos; + ReadDefaultR5QoS(qos); +#else + RPacketQoS::TQoSR99_R4Requested qos; + ReadDefaultQoS(qos); +#endif +// SYMBIAN_NETWORKING_UMTSR5 + iPdpFsmInterface.Set(iPrimaryContextId, qos); // ignore any error + + } + + // Set default TFT + TTFTInfo tft; + iPdpFsmInterface.Set(iPrimaryContextId, tft); // ignore any error + + + // Have Etel create a context + ref->SetState(ESpudCreatingPrimary); + __FLOG_1(_L("CSpudMan::Start: Sending SpudFsm event ECreatePrimaryPDPContext context %d"), iPrimaryContextId); + TInt rc = iPdpFsmInterface.Input(iPrimaryContextId, ECreatePrimaryPDPContext); + // TODO: handle errors properly + ASSERT(rc == KErrNone); + rc = rc; // Eliminate compiler warning in release builds + + return KErrNone; + } + +/** +Cleanly stop the link. + +@param aReason The reason the link is going down +@param aAction The action to take once the link is down +*/ +void CSpudMan::Stop(TInt aReason, MNifIfNotify::TAction aAction) + { + __FLOG_3(_L("CSpudMan::Stop: reason %d action %d. %d contexts exist."), aReason, aAction, BindMan()->NumContexts()); + ASSERT(BindMan()->NumContexts()); // Primary PDP context is created in the factory. + + SetTerminateError(aReason); // Store this error code for use when the SPUD goes down + if (AreQoSEventsEnabled()) + { + // Spud was administratively stopped. It can be some time before SPUD signals LinkLayerDown. + // In the meanwhile, we can receive requests from GUQoS that can interfere with the shutdown. + // To prevent this, we tell GUQoS to stop bothering SPUD. + // GUQoS returns the favour by turning off the NIF events within this very call. + // This means we will not send KNetworkInterfaceDown again, even though we'll try. + // ********************************************************************************************* + // N.B.: "DEF055691 GUQoS crashes at shutdown": This defect results in GUQoS crash if the stack + // closes flows after the NIF proxy was deleted by GUQoS, as a result of KNetworkInterfaceDown event. + // As a temporary workaround, this call is disabled, and KNetworkInterfaceDown event is signalled only + // from the destructor. Once this defect is fixed, the following line must be uncommented: + // SendNetworkStatusEvent(KNetworkInterfaceDown, RPacketService::EStatusUnattached); + //********************************************************************************************** + } + + + // Send Stop to all lower NIFs that were started but not stopped yet + TContextId i; + for (i=0; i < KMaxPdpContexts; ++i) + { + StopContext(i, aReason, aAction); + } + // Eventually, the last lower NIF will call LinkLayerDown to trigger the final cleanup + } + +/** +Cleanly stop a context. + +@param aReason The reason the link is going down +@param aAction The action to take once the link is down +@param aContextId context +*/ +void CSpudMan::StopContext(TContextId aContextId, TInt aReason, MNifIfNotify::TAction aAction) + { + CSpudBinderRef* ref = NULL; + TRAPD(rc, ref = iBindMan->GetRefL(aContextId)); + if (rc == KErrNone && // Binder exists + ref->IsBound()) // Is bound to a lower NIF. + { + + // Save the Context failure reason + if (ref->Error() == KErrNone) + { + ref->SetError(aReason); + } + + switch(ref->State()) // NIFs in some states are not eligible for Stop. + { + // Context created and NIF started. + case ESpudStartingPrimaryLowerNif: // Waiting for LinkLayerUp/Down + case ESpudStartingSecondaryLowerNif:// Waiting for LinkLayerUp/Down + case ESpudGettingNegQoS: // Waiting for retrieval of negotiated QoS, context activated + case ESpudUp: // LinkLayerUp received, NIF is up. + case ESpudFlowOff: // LinkLayerUp received, NIF is up. + case ESpudSuspended: // LinkLayerUp received, NIF is up. + case ESpudFlowOffAndSuspended: // LinkLayerUp received, NIF is up. + __FLOG_2(_L("Lower NIF binder for context[%d] is in state[%S]: Stopping lower NIF."),aContextId,SpudStateToText(ref->State())); + // Stop the NIF and delete the context via SpudFsm: + ref->NifLink()->Stop(aReason, aAction); + // stay in the Up state so that that GUQoS is notified. + break; + + // Context is being created + case ESpudCreatingPrimary: + // SpudFsm will clean up the context and generate a context created event with an error + rc = iPdpFsmInterface.Input(aContextId, ECancelContextCreate); + break; + + // Context created, but NIF not started + case ESpudStartingPrimary: + ref->SetState(ESpudWaitLinkDown); + // Any outstanding SpudFsm request will be cancelled by this delete request. + rc = iPdpFsmInterface.Input(aContextId, EContextDelete); + aReason = (KErrNone != aReason) ? aReason : KErrCancel; // Must not be KErrNone. + SendPrimaryContextCreated(aContextId, aReason); + break; + + case ESpudStartingSecondary: + // Delete the context via SpudFsm + ref->SetState(ESpudLinkDown); // We'll notify GUQoS from deletion event handler. + __FLOG_1(_L("Context[%d] created: Sending SpudFsm event EContextDelete"), aContextId); + + // Any outstanding SpudFsm request will be cancelled by this delete request. + rc = iPdpFsmInterface.Input(aContextId, EContextDelete); + ASSERT(rc == KErrNone); + break; + + // Can't call Stop: the NIF either not started, or stopped already + case ESpudContextDelete: // Context deleted by GUQoS, Stop was called. + case ESpudWaitLinkDown: // Stop was called, waiting for LinkLayerDown + case ESpudWaitBinderDelete: // LinkLayerDown received, queued for deletion + case ESpudLinkDown: // LinkLayerDown received, not queued for deletion. + __FLOG_2(_L("Lower NIF binder for context[%d] is in state[%S], and is not eligible for Stop."),aContextId,SpudStateToText(ref->State())); + break; + + case ESpudHaveQos: + default: + __FLOG_2(_L("Lower NIF binder for context[%d] is in unexpected state[%S]."),aContextId,SpudStateToText(ref->State())); + ASSERT(EFalse); + break; + } + } + } + +/** +Send a packet across the link. +This function should not be called; the Mux is the one that should get the data. + +@param aPacket MBuf chain containing packet (ignored) +@param aSource (ignored) + +@return Error code, or 1 if packet was queued, + or KErrNone to flow off sender +*/ +TInt CSpudMan::Send(RMBufChain& /*aPacket*/, TAny* /*aSource*/) + { + _LIT(KPanicMsg, "CSpudMan"); + User::Panic(KPanicMsg, KErrNotSupported); + return KErrNotSupported; // never reached + } + + +/** +Receives notification from NIFMAN that the authenticate data is ready. +*/ +void CSpudMan::AuthenticateComplete(TInt aResult) + { + // Send AuthenticateComplete to all lower NIFs + TContextId i; + for (i=0; i < KMaxPdpContexts; ++i) + { + CSpudBinderRef* ref = NULL; + TRAPD(rc, ref = iBindMan->GetRefL(i)); + if (rc == KErrNone) + { + ref->NifLink()->AuthenticateComplete(aResult); + } + } + } + +void CSpudMan::Restart(CNifIfBase* /*aIf*/) + { + // TODO: Is it safe to simply ignore this? + __FLOG_0(_L("CSpudMan::Restart. Ignored.")); + ASSERT(EFalse); + } + + +//***************************************************************************** +// SPUD methods +//***************************************************************************** + +/** +Receives progress notifications from lower NIF. + +@param aContextId Context ID of lower NIF +@param aStage Progress stage +@param aError Error code +*/ +void CSpudMan::IfProgress(TContextId aContextId, TInt aStage, TInt aError) + { + __FLOG_3(_L("CSpudMan::IfProgress context ID %d received stage %d error %d"), + aContextId, aStage, aError); + // Eliminate compiler warnings in release builds + aContextId = aContextId; + aStage = aStage; + aError = aError; + + // Drop all progress indications from lower NIFs on the floor because they'll just confuse NIFMAN. + // SpudMan generates its own progress notifications. + } + +/** +Receives progress notifications from lower NIF. + +@param aContextId Context ID of lower NIF +@param aSubConnectionUniqueId Subconnection ID +@param aStage Progress stage +@param aError Error code +*/ +void CSpudMan::IfProgress(TContextId aContextId, TSubConnectionUniqueId aSubConnectionUniqueId, TInt aStage, TInt aError) + { + __FLOG_4(_L("CSpudMan::IfProgress context ID %d subconnection ID %d received stage %d error %d"), + aContextId, aSubConnectionUniqueId, aStage, aError); + // Eliminate compiler warnings in release builds + aContextId = aContextId; + aSubConnectionUniqueId = aSubConnectionUniqueId; + aStage = aStage; + aError = aError; + + // Drop all progress indications from lower NIFs on the floor because they'll just confuse NIFMAN. + // SpudMan generates its own progress notifications. + } + +/** +Receives notifications from lower NIF to agent. + +@param aContextId Valid context ID of lower NIF +@param aEvent Event type +@param aInfo Additional information for event (ignored) +@return KErrNone on success, or KErrNotSupported +*/ +TInt CSpudMan::Notification(TContextId aContextId, TNifToAgentEventType aEvent, void* /*aInfo*/) + { + __FLOG_2(_L("CSpudMan::Notification context ID %d event ID %d"), aContextId, aEvent); + switch (aEvent) + { + case ENifToAgentEventTsyConfig: + { + // Return GPRS context structure to lower NIF + CSpudBinderRef* ref = NULL; + TRAPD(rc, ref = iBindMan->GetRefL(aContextId)); + __ASSERT_ALWAYS(rc == KErrNone, Panic()); + ASSERT(ref->IsBound()); + + iPdpFsmInterface.Get(aContextId, iTempGprsContext); + ref->NifLink()->Notification(EAgentToNifEventTsyConfig, reinterpret_cast(&iTempGprsContext)); + return KErrNone; + } + + case ENifToAgentEventTsyConnectionSpeed: + { + // Return connection speed to lower NIF + CSpudBinderRef* ref = NULL; + TRAPD(rc, ref = iBindMan->GetRefL(aContextId)); + __ASSERT_ALWAYS(rc == KErrNone, Panic()); + ASSERT(ref->IsBound()); + +#ifdef SYMBIAN_NETWORKING_UMTSR5 + RPacketQoS::TQoSR5Negotiated params; +#else + RPacketQoS::TQoSR99_R4Negotiated params; +#endif +// SYMBIAN_NETWORKING_UMTSR5 + + iPdpFsmInterface.Get(aContextId, params); + ref->NifLink()->Notification(EAgentToNifEventTsyConnectionSpeed, + reinterpret_cast(static_cast(params.iMaxRate.iUplinkRate))); + + return KErrNone; + } + + default: + // Just ignore all the other notifications + __FLOG_0(_L("Ignoring notification")); + break; + } + + return KErrNotSupported; + } + +/** +Read a boolean field from the connection settings provider. +Intercepts reads of CommPort and returns the correct value. + +@param aContextId Valid context ID of lower NIF +@param aField The name of the field to read +@param aValue On return, contains the value of the field read +@return KErrNone, if successful; otherwise one of the standard Symbian OS error codes +*/ +TInt CSpudMan::ReadInt(TContextId aContextId, const TDesC& aField, TUint32& aValue) + { + + //This fix is needed to ensure that multiple PPP channels can be used for different PDP contexts. + //The returned value of ECommDbCdmaNaiMobileIp will cause PPP to skip external IP configuration (NCPIP). + if ( (TPtrC(CDMA_NAI_TYPE) == aField) && (iPrimaryContextId != aContextId) ) + { + __FLOG_2(_L("CSpudMan::ReadInt context ID %d field = CDMA_NAI_TYPE - Therefore explicitly setting Value to ECommDbCdmaNaiMobileIp"), + aContextId, &aField); + __FLOG(_L("No call to AgentRef Will be make")); + // Lower NIF is requesting the NAI type + aValue = ECommDbCdmaNaiMobileIp; + return KErrNone; + } + __FLOG_2(_L("CSpudMan::ReadInt context ID %d field %S"), aContextId, &aField); + // Read CommDB normally + return Notify()->ReadInt(aField, aValue); + } + +/** +Read a 8-bit descriptor field from the connection settings provider. +Intercepts reads of CommPort and returns the value returned from ETel. + +@param aContextId Valid context ID of lower NIF +@param aField The name of the field to read +@param aValue On return, contains the value of the field read +@return KErrNone, if successful; otherwise one of the standard Symbian OS error codes +*/ +TInt CSpudMan::ReadDes(TContextId aContextId, const TDesC& aField, TDes8& aValue) + { + __FLOG_2(_L("CSpudMan::ReadDes context ID %d field %S"), aContextId, &aField); + TBuf<2*KCommsDbSvrMaxColumnNameLength+1> columnName; + _LIT(KFormatText,"%s\\%s"); + + columnName.Format(KFormatText,MODEM_BEARER,MODEM_PORT_NAME); + if (columnName == aField) + { + // Lower NIF is requesting the CSY port name + // Use the TDes16 version of ReadDes to retrieve the data + TBuf16 data; + TInt rc(ReadDes(aContextId, aField, data)); + aValue.Copy(data); + return rc; + } + + columnName.Format(KFormatText,MODEM_BEARER,MODEM_CSY_NAME); + if (columnName == aField) + { + // Lower NIF is requesting the CSY file name + // Use the TDes16 version of ReadDes to retrieve the data + TBuf16 data; + TInt rc(ReadDes(aContextId, aField, data)); + aValue.Copy(data); + return rc; + } + + // Read CommDB normally + return Notify()->ReadDes(aField, aValue); + } + +/** +Read a 16-bit descriptor field from the connection settings provider. +Intercepts reads of CommPort and returns the value returned from ETel. + +@param aContextId Valid context ID of lower NIF +@param aField The name of the field to read +@param aValue On return, contains the value of the field read +@return KErrNone, if successful; otherwise one of the standard Symbian OS error codes +*/ +TInt CSpudMan::ReadDes(TContextId aContextId, const TDesC& aField, TDes16& aValue) + { + __FLOG_2(_L("CSpudMan::ReadDes context ID %d field %S"), aContextId, &aField); + TBuf<2*KCommsDbSvrMaxColumnNameLength+1> columnName; + _LIT(KFormatText,"%s\\%s"); + + columnName.Format(KFormatText,MODEM_BEARER,MODEM_PORT_NAME); + if (columnName == aField) + { + // Lower NIF is requesting the CSY port name + iPdpFsmInterface.Get(aContextId, iTempDataChannelV2); + __FLOG_1(_L("Returning ETel port name %S"), &iTempDataChannelV2.iPort); + aValue.Copy(iTempDataChannelV2.iPort); + return KErrNone; + } + + columnName.Format(KFormatText,MODEM_BEARER,MODEM_CSY_NAME); + if (columnName == aField) + { + // Lower NIF is requesting the CSY file name + iPdpFsmInterface.Get(aContextId, iTempDataChannelV2); + __FLOG_1(_L("Returning ETel CSY name %S"), &iTempDataChannelV2.iCsy); + aValue.Copy(iTempDataChannelV2.iCsy); + return KErrNone; + } + + return Notify()->ReadDes(aField, aValue); + } + +/** +Marks the binder to the lower NIF for asynchronous deletion + +@param aRef the binder +@pre the binder must be bound the lower NIF. +*/ +void CSpudMan::DisposeOfBinder(CSpudBinderRef* aRef) + { + ASSERT(aRef); + ASSERT(aRef->IsBound()); // We can only mark - sweep bound instances + ASSERT(aRef->State() != ESpudWaitBinderDelete); + aRef->SetState(ESpudWaitBinderDelete); + iBinderSweeperNotifierCb->Call(); // Queue deletion of marked binders & optional Nifman notification. + } + +/** +Sweeps the set of lower NIF binding, deleting the marked ones. If no contexts remain after, +notifies Nifman that SPUD has gone down. + +@param aContextId The ID of the context to delete +@param aReason error code that is passed to Nifman +*/ +void CSpudMan::SweepBindersAndNotify() + { + const TUint KNumContextsRemaining(BindMan()->SweepBinders()); + if (0 == KNumContextsRemaining) + { + SetTerminateError(KErrAbort); // This is a last ditched effort to provide termination + // error code. We cannot determine in all cases what has caused SPUD to terminate. + // E.g. if several secondary contexts were deleted by the network, which of them caused SPUD termination? + // In such case we say that SPUD is shutting down due to internal event (namely, last context deletion). + + __FLOG_3(_L("Last lower NIF has been deleted: Notifying Nifman with action EDisconnect[%d], progress KLinkLayerClosed[%d], reason[%d]"), + MNifIfNotify::EDisconnect, KLinkLayerClosed, iTerminateError); + + // Once we've notified LinkLayerDown & IfProgress, we are finished. Nifman will delete us any moment after + // the RunL we are working from returns. + __FLOG(_L("SPUD is finished, and expects to be deleted by Nifman. Reason: last PDP context has gone down, possibly due to Stop on SPUD.")); + + // Tell Nifman clients that SPUD is finished. + Notify()->LinkLayerDown(iTerminateError, MNifIfNotify::EDisconnect); + Notify()->IfProgress(KLinkLayerClosed, iTerminateError); + } + else + { + __FLOG_1(_L("There are [%d] contexts remaining."), KNumContextsRemaining); + } + } + +void CSpudMan::SetupSipServerAddrRetrievalL(RPacketContext::TProtocolConfigOptionV2& aPco) + { + __FLOG(_L("CSpudMan::SetupSipServerAddrRetrieval - Requesting the P-CSCF address from the PCO buffer")); + + TPtr8 pcoPtr(const_cast(aPco.iMiscBuffer.Ptr()),aPco.iMiscBuffer.Length(),aPco.iMiscBuffer.MaxLength()); + + // attach TTlv to the buffer + TTlvStruct tlv(pcoPtr,0); + tlv.AppendItemL(RPacketContext::TPcoId(RPacketContext::EEtelPcktPCSCFAddressRequest), + TPtr8(static_cast(NULL), 0, 0)); + aPco.iMiscBuffer.SetLength(pcoPtr.Length()); + } + + +#ifdef SYMBIAN_NETWORKING_UMTSR5 + +void CSpudMan::SetIMCNSignallingFlagPcoL(RPacketContext::TProtocolConfigOptionV2& aPco) +/** +Put the value for IMCN Signalling flag in the pco buffer if it is set in database + +@param PCO IE Buffer +*/ + { + TBool imcn=EFalse; + TBuf<2*KCommsDbSvrMaxColumnNameLength+2> columnName; + _LIT(KFormatText,"%s\\%s"); + columnName.Format(KFormatText,QOS_UMTS_R99_AND_ON_TABLE,GPRS_QOS_IM_CN_SIGNALLING_INDICATOR); + TRAPD(ret, Notify()->ReadBool(columnName,imcn);); + __FLOG_1(_L("CSpudMan::SetIMCNSignallingFlagPcoL - Requesting IMCN Signalling status from Database: error = %d"),ret); + + if (imcn && ret==KErrNone ) + + { + TPtr8 pcoPtr(const_cast(aPco.iMiscBuffer.Ptr()),aPco.iMiscBuffer.Length(),aPco.iMiscBuffer.MaxLength()); + TTlvStruct tlv(pcoPtr,0); + tlv.AppendItemL(RPacketContext::TPcoId(RPacketContext::EEtelPcktIMCNMSSubsystemSignallingFlag ), + TPtr8(static_cast(NULL), 0, 0)); + aPco.iMiscBuffer.SetLength(pcoPtr.Length()); + } + } +TBool CSpudMan::GetIMCNSignallingFlagPcoL(RPacketContext::TProtocolConfigOptionV2& aPco) +/** +Get the value for IMCN Signalling from the network pco buffer + +@param PCO IE Buffer +*/ + { + + __FLOG(_L("CSpudMan::GetIMCNSignallingFlagPcoL - Retrieving the IMCN signalling Flag from the PCO buffer")); + + TPtr8 pcoPtr(const_cast(aPco.iMiscBuffer.Ptr()),aPco.iMiscBuffer.Length(),aPco.iMiscBuffer.MaxLength()); + TTlvStruct tlv(pcoPtr,0); + tlv.ResetCursorPos(); + + TInt err = tlv.NextItemL(RPacketContext::EEtelPcktIMCNNetworkSubsystemSignallingFlag,pcoPtr); + return (err == KErrNone); + + } + +#endif // SYMBIAN_NETWORKING_UMTSR5 + + +void CSpudMan::SetSipServerAddrL(const RPacketContext::TProtocolConfigOptionV2& aPco) + { + __FLOG(_L("CSpudMan::SetSipServerAddr - Retrieving the P-CSCF address from the PCO buffer")); + iSipServerAddr.Reset(); //Free all existing entries + TPtr8 pcoPtr(const_cast(aPco.iMiscBuffer.Ptr()),aPco.iMiscBuffer.Length(),aPco.iMiscBuffer.MaxLength()); + TTlvStruct + tlv(pcoPtr,0); + + tlv.ResetCursorPos(); + TIp6Addr addr; + TPtr8 addrPtr(NULL, 0); + TPckg addrPckg(addr); + + while (tlv.NextItemL(RPacketContext::EEtelPcktPCSCFAddress,addrPtr) == KErrNone) + { + TInetAddr inetAddr; + addrPckg.Copy(addrPtr); + inetAddr.SetAddress(addr); + TBuf testbuf; + inetAddr.Output(testbuf); + __FLOG_1(_L("CSpudMan::SetSipServerAddr - P-CSCF address ---> %S"),&testbuf); + if (testbuf.Length()) //ie the address is invalid + { + iSipServerAddr.AppendL(inetAddr); + } + } + } + +void CSpudMan::SetContextTerminationErrorAndStop(TContextId aContextId, TInt aErrorCode) + { + __FLOG_2(_L("SetContextTerminateError on StatusEvent: aContextId[%d], aErrorCode[%d]"), + aContextId, aErrorCode); + + // If there is no error then simply return + if (KErrNone == aErrorCode) return; + + // If secondary context, store error code in individual contexts reference + // and stop the secondary context + if(aContextId != iPrimaryContextId) + { + StopContext(aContextId, aErrorCode, MNifIfNotify::EDisconnect); + } + else + { + // This is a problem with the Primary context so stop and disconnect + // Now save the termination error code if not already set + if (iTerminateError == KErrNone) + { + iTerminateError = aErrorCode; + } + + // This may be the first ETel error code so save it + if (iETelTerminateError == KErrNone) + { + iETelTerminateError = aErrorCode; + } + + // Primary context has a problem so disconnect + Stop(aErrorCode, MNifIfNotify::EDisconnect); + } + } + +//************************************************************************* +// CLowerNifBinderDeletionCb +// Asynchronous deletion of CSpudBinderRefs and notification to Nifman +//************************************************************************* +// Use Spudman's logging. +// Because we are owned by Spudman, we don't have to worry about the logger being deleted. +#ifdef __FLOG_ACTIVE +#define BINDER_SWEEPER_LOG(x) iSpudMan.x +#else +#define BINDER_SWEEPER_LOG(x) +#endif + +// Construct a High-Priority AO that calls into the SPUD +// This will work with any priority AO, but because we are releasing memory and +// potentially notifying Nifman, we want to run ASAP. +CBinderSweeperNotifierCb::CBinderSweeperNotifierCb(CSpudMan& aSpudMan) + : + CAsyncOneShot(CActive::EPriorityHigh), + iSpudMan(aSpudMan) + { + } + +// Queues the deletion callback +void CBinderSweeperNotifierCb::Call() + { + if(!IsActive()) // We can be called again before we had a chance to run. + { + BINDER_SWEEPER_LOG(__FLOG(_L("CBinderSweeperNotifierCb: Queueing async deletion of dead lower NIF bindings."));) + CAsyncOneShot::Call(); + return; + } + BINDER_SWEEPER_LOG(__FLOG(_L("CBinderSweeperNotifierCb: Async deletion of dead lower NIF bindings is already queued."));) + } + + +// Called by ActiveScheduler. +// +// If the lower NIF deletion is attempted after Nifman deletes the SPUD +// (from CNifAgentRef::DisconnectionComplete), the lower NIF deletion AO is corrupted in the +// ActiveScheduler, causing ESock thread to crash. To prevent this, lower NIFs are deleted +// before signalling LinkLayerDown to Nifman. When a lower NIF signals LinkLayerDown, a callback into Spudman is queued. +// This callback deletes the lower NIFs that are eligible for deletion, and notifies Nifman, if necessary. */ +void CBinderSweeperNotifierCb::RunL() + { + iSpudMan.SweepBindersAndNotify(); + } +