--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/networkcontrol/qosfwconfig/qos/src/negotiation.cpp Tue Jan 26 15:23:49 2010 +0200
@@ -0,0 +1,450 @@
+// Copyright (c) 2006-2009 Nokia Corporation and/or its subsidiary(-ies).
+// All rights reserved.
+// This component and the accompanying materials are made available
+// under the terms of "Eclipse Public License v1.0"
+// which accompanies this distribution, and is available
+// at the URL "http://www.eclipse.org/legal/epl-v10.html".
+//
+// Initial Contributors:
+// Nokia Corporation - initial contribution.
+//
+// Contributors:
+//
+// Description:
+//
+
+#include "qos_prot.h"
+#include "qoserr.h"
+#include "policy_sap.h"
+#include "interface.h"
+#include "qos_channel.h"
+#include "modules.h"
+#include "flowhook.h"
+#include "negotiation.h"
+
+// CQoSSessionBase
+CQoSSessionBase::CQoSSessionBase(CFlowHook& aHook, TInt aChannelId)
+ : iHook(aHook), iModuleList(aHook.ModuleList()), iChannelId(aChannelId)
+ {
+ iPending.SetOffset(_FOFF(CNegotiateItem, iLink));
+ iFatalError = EFalse;
+ iError = KErrNone;
+ iNegotiated = aHook.QoSParameters();
+ }
+
+CQoSSessionBase::~CQoSSessionBase()
+ {
+ while (!iPending.IsEmpty())
+ {
+ CNegotiateItem* request = iPending.First();
+ iPending.Remove(*request);
+ // Because there is no way to cancel a pending CNegotiateItem,
+ // it *CANNOT* be deleted here. Make it a ZOMBIE by removing
+ // the session pointer and hope that RequestComplete arrives
+ // someday!
+ request->Kill();
+ }
+ iPending.Reset();
+ }
+
+void CQoSSessionBase::Run()
+ /**
+ * Start the session.
+ */
+ {
+ iCurrent = 0;
+ if (iModuleList.Count() == 0)
+ {
+ // There are no modules present, the negotiation would
+ // not do or achieve anything. It is somewhat unclear
+ // whether this is an error or not. Treat it as an error
+ // and pick the associated error code from the interface
+ // (if there is no TrafficControlStatus error on interface
+ // then this just completes the request without error)
+ FatalError(iHook.Interface() ? iHook.Interface()->TrafficControlStatus() : EQoSNoModules);
+ }
+ Proceed();
+ }
+
+// Proceed negotiation
+void CQoSSessionBase::Proceed()
+ {
+ if (iProceed)
+ {
+ // Getting here from DoCall, do nothing here
+ // Proceeding after DoCall returns! DoCall
+ // completed the subrequest immediate!
+ return;
+ }
+
+ while (!iFatalError && iCurrent < iModuleList.Count())
+ {
+ RModule* module = iModuleList[iCurrent];
+ ASSERT(module);
+
+ const TUint flags = module->Flags();
+ if ((flags & KQoSModuleSerialize) && !iPending.IsEmpty())
+ {
+ // The module requires previous requests to be completed.
+ // There is at least one pending request. When it completes,
+ // the Proceed() gets called to restart the loop.
+ return;
+ }
+ ++iCurrent;
+
+ CNegotiateItem*const item = new CNegotiateItem(this, flags);
+ if (item == NULL)
+ {
+ FatalError(KErrNoMemory);
+ break;
+ }
+ iPending.AddLast(*item);
+
+ LOG(Log::Printf(_L("")));
+ LOG(Log::Printf(_L("calling\tmodule[%S] for session[%u]"), &module->Name(), (TInt)this));
+ ++iProceed; // ..in case DoCall calls (Sub)RequestComplete!
+ const TInt ret = DoCall(*module, *item);
+ --iProceed;
+ LOG(Log::Printf(_L("<<<\treturns from module[%S] for session[%u]"), &module->Name(), (TInt)this));
+ if (ret != KErrNone)
+ {
+ // DoCall didn't call the module, there will be no (Sub)RequestCompete call!
+ // Destroy the item here!
+ iPending.Remove(*item);
+ delete item;
+ FatalError(ret);
+ break;
+ }
+ }
+
+ if (iPending.IsEmpty() || iFatalError)
+ {
+ RequestComplete();
+ //?? In case of iFatalError, should we start a "shutdown" process
+ //?? session for those modules that actually succeeded (at least
+ //?? in case of some session, like if some Joins succeed, shouldn't
+ //?? there be a Leave for those, if the whole session cannot be
+ //?? completed? [apparenty there is never more than one module, so
+ //?? the issue has not come up yet...]
+ delete this;
+ }
+ }
+
+
+CInternalQoSChannel* CQoSSessionBase::Channel() const
+ /**
+ * Return internal channel object or NULL.
+ */
+
+ {
+ // The sessions only store the channel id, and when needed, the
+ // channel object is located by this. This takes care of the
+ // possibility that the channel disappears while sessions is
+ // running (if a pointer was stored, the channel object destructor
+ // would have to find all sessions referencing it, and currently
+ // this would be somewhat complicated (as flows holding the session
+ // might not be members of that channel any more).
+ if (iChannelId > 0)
+ return iHook.Protocol().ChannelMgr()->FindChannel(iChannelId);
+ else
+ return NULL;
+ }
+
+void CQoSSessionBase::FatalError(TInt aErrorCode)
+ {
+ iFatalError = ETrue;
+ iError = aErrorCode;
+ }
+
+void CQoSSessionBase::SubRequestComplete(TInt aErrorCode,
+ const TQoSParameters* aParams, const TExtensionData& aExtension,
+ CNegotiateItem* aItem)
+ {
+ LOG(Log::Printf(_L("<>\tqos session[%u] NegotiateItem[%u] SubRequestComplete"), (TInt)this, (TInt)aItem));
+
+ iPending.Remove(*aItem);
+
+ if (aParams)
+ {
+ iNegotiated = *aParams;
+ }
+
+ // add possible extensions to PF_QOS event
+ if (aExtension.iData.Length() > 0)
+ {
+ TRAPD(err, iMsg.AddExtensionL(aExtension.iData, aExtension.iType));
+ if (err != KErrNone)
+ {
+ LOG(Log::Printf(_L("iMsg.AddExtensionL error: %d"), err));
+ }
+ }
+
+ if (aErrorCode == KErrNone)
+ {
+ //lint -e{961} Missing 'else' is OK
+ if (KQoSModuleSignaling & aItem->Flags())
+ {
+ iValue |= KQoSModuleSignaling;
+ }
+ else if (KQoSModulePartialSignaling & aItem->Flags())
+ {
+ iValue |= KQoSModulePartialSignaling;
+ }
+ else if (KQoSModuleProvisioning & aItem->Flags())
+ {
+ iValue |= KQoSModuleProvisioning;
+ }
+ }
+ else
+ {
+ if (aItem->Flags() & KQoSFatalFailure
+ || aErrorCode == EQoSLeaveFailure)
+ {
+ FatalError(aErrorCode);
+ }
+ }
+ Proceed();
+ }
+
+void CQoSSessionBase::DeliverEvent(TUint16 aEvent)
+ {
+ // If the caller does not set event, choose one by iFatalError
+ if (aEvent == 0)
+ {
+ //?? Event seems to be a bitmask. Should this actually be done
+ //?? always (and with OR to the aEvent)?
+ aEvent = iFatalError ? KPfqosEventFailure : KPfqosEventConfirm;
+ }
+
+ T_pfqos_msg base(EPfqosEvent);
+ if (iError != KErrNone)
+ {
+ base.pfqos_msg_errno = iError;
+ }
+ iMsg.iBase.iMsg = &base;
+
+ T_pfqos_event event(EPfqosExtEvent, aEvent, iValue);
+ iMsg.iEvent.iExt = &event;
+
+ T_pfqos_flowspec spec(iNegotiated);
+ iMsg.iFlowSpec.iExt = &spec;
+
+ // Add flow identifying PF_QOS blocks
+ // note: the variables must be outside the if-block, because they must exist
+ // when Deliver is called!
+ TInetAddr msk;
+ pfqos_address src, dst;
+ pfqos_selector sel;
+ iHook.FillFlowInfo(iMsg, src, dst, sel, msk);
+
+
+ // Add channel identifying PF_QOS block (only present if requested)
+ T_pfqos_channel channel(iChannelId);
+ if (iChannelId > 0)
+ iMsg.iChannel.iExt = &channel;
+
+ iMsg.iNumModules = 0;
+ iHook.Protocol().Deliver(iMsg, KProviderKey_RegisteredQoSConf);
+ }
+
+
+// Negotiate
+CNegotiateSession::CNegotiateSession(CFlowHook& aHook) : CQoSSessionBase(aHook, 0)
+ {
+ LOG(Log::Printf(_L("new\tqos session[%u] Negotiate for HOOK[%u] size=%d"), (TInt)this, (TInt)&aHook, sizeof(*this)));
+ }
+
+CNegotiateSession::~CNegotiateSession()
+ {
+ LOG(Log::Printf(_L("~\tqos session[%u] Negotiate deleted"), (TInt)this));
+ }
+
+TInt CNegotiateSession::DoCall(RModule& aModule, MQoSNegotiateEvent& aCallback)
+ {
+ LOG(Log::Printf(_L("\t\tmodule[%S]::Negotiate(...)"), &aModule.Name()));
+ aModule.Module()->Negotiate(iHook.Context(), iHook.QoSParameters(), aCallback);
+ return KErrNone;
+ }
+
+void CNegotiateSession::RequestComplete()
+ {
+ DeliverEvent(0);
+ iHook.ClearPendingRequest(iError);
+ }
+
+
+// Create channel session
+CCreateChannelSession::CCreateChannelSession(CFlowHook& aHook, TInt aChannelId) : CQoSSessionBase(aHook, aChannelId)
+ {
+ LOG(Log::Printf(_L("new\tqos session[%u] for HOOK[%u] CreateChannel size=%d"), (TInt)this, (TInt)&aHook, sizeof(*this)));
+ }
+
+CCreateChannelSession::~CCreateChannelSession()
+ {
+ LOG(Log::Printf(_L("~\tqos session[%u] CreateChannel deleted"), (TInt)this));
+ }
+
+TInt CCreateChannelSession::DoCall(RModule& aModule, MQoSNegotiateEvent& aCallback)
+ {
+ LOG(Log::Printf(_L("\t\tmodule[%S]::OpenChannel(channel[%u],..., FLOW[%u])"),
+ &aModule.Name(), iChannelId, (TInt)&iHook.Context()));
+ CInternalQoSChannel*const channel = iHook.Protocol().ChannelMgr()->FindChannel(iChannelId);
+ if (channel == NULL)
+ {
+ return EQoSChannelDeleted;
+ }
+ aModule.Module()->OpenChannel(iChannelId, channel->QoSParameters(), channel->Extension(), aCallback, iHook.Context());
+ return KErrNone;
+ }
+
+void CCreateChannelSession::RequestComplete()
+ {
+ LOG(Log::Printf(_L("\tqos session[%u] CreateChannel complete"), (TInt)this));
+ DeliverEvent(0);
+ iHook.SetChannelJoined(TRUE);
+ iHook.ClearPendingRequest(iError);
+ CInternalQoSChannel*const channel = iHook.Protocol().ChannelMgr()->FindChannel(iChannelId);
+ if (channel)
+ channel->RequestComplete(iError);
+ }
+
+
+
+// Negotiate channel session
+CNegotiateChannelSession::CNegotiateChannelSession(CFlowHook& aHook, TInt aChannelId) : CQoSSessionBase(aHook, aChannelId)
+ {
+ LOG(Log::Printf(_L("new\tqos session[%u] for HOOK[%u] NegotiateChannel size=%d"), (TInt)this, (TInt)&aHook, sizeof(*this)));
+ }
+
+CNegotiateChannelSession::~CNegotiateChannelSession()
+ {
+ LOG(Log::Printf(_L("~\tqos session[%u] NegotiateChannel deleted"), (TInt)this));
+ }
+
+TInt CNegotiateChannelSession::DoCall(RModule& aModule, MQoSNegotiateEvent& aCallback)
+ {
+ LOG(Log::Printf(_L("\t\tmodule[%S]::NegotiateChannel(channel[%u], ...)"), &aModule.Name(), iChannelId));
+ CInternalQoSChannel*const channel = Channel();
+ if (channel == NULL)
+ {
+ return EQoSChannelDeleted;
+ }
+ aModule.Module()->NegotiateChannel(iChannelId, channel->QoSParameters(), channel->Extension(), aCallback);
+ return KErrNone;
+ }
+
+void CNegotiateChannelSession::RequestComplete()
+ {
+ LOG(Log::Printf(_L("~\tqos session[%u] NegotiateChannel complete"), (TInt)this));
+ DeliverEvent(0);
+ iHook.ClearPendingRequest(iError);
+
+ CInternalQoSChannel*const channel = Channel();
+ if (channel)
+ channel->RequestComplete(iError);
+ }
+
+// Join
+CJoinSession::CJoinSession(CFlowHook& aHook, TInt aChannelId) : CQoSSessionBase(aHook, aChannelId)
+ {
+ LOG(Log::Printf(_L("new\tqos session[%u] for HOOK[%u] JoinChannel size=%d"), (TInt)this, (TInt)&aHook, sizeof(*this)));
+ }
+
+CJoinSession::~CJoinSession()
+ {
+ LOG(Log::Printf(_L("~\tqos session[%u] JoinChannel deleted"), (TInt)this));
+ }
+
+TInt CJoinSession::DoCall(RModule& aModule, MQoSNegotiateEvent& aCallback)
+ {
+ LOG(Log::Printf(_L("\t\tmodule[%S]::Join(channel[%u], FLOW[%u])"), &aModule.Name(), iChannelId, (TInt)&iHook.Context()));
+ const CInternalQoSChannel*const channel = Channel();
+ if (channel == NULL)
+ {
+ return EQoSChannelDeleted;
+ }
+ aModule.Module()->Join(iChannelId, iHook.Context(), aCallback);
+ return KErrNone;
+ }
+
+
+void CJoinSession::RequestComplete()
+ {
+ LOG(Log::Printf(_L("\tqos session[%u] JoinChannel complete"), (TInt)this));
+ DeliverEvent(KPfqosEventJoin);
+ iHook.SetChannelJoined(TRUE);
+ iHook.ClearPendingRequest(iError);
+
+ CInternalQoSChannel*const channel = Channel();
+ if (channel)
+ channel->RequestComplete(iError);
+ }
+
+
+
+// Leave
+CLeaveSession::CLeaveSession(CFlowHook& aHook, TInt aChannelId) : CQoSSessionBase(aHook, aChannelId)
+ {
+ LOG(Log::Printf(_L("new\tqos session[%u] for HOOK[%u] LeaveChannel size=%d"), (TInt)this, (TInt)&aHook, sizeof(*this)));
+ }
+
+CLeaveSession::~CLeaveSession()
+ {
+ LOG(Log::Printf(_L("~\tqos session[%u] LeaveChannel deleted"), (TInt)this));
+ }
+
+TInt CLeaveSession::DoCall(RModule& aModule, MQoSNegotiateEvent& aCallback)
+ {
+ LOG(Log::Printf(_L("\t\tmodule[%S]::Leave(channel[%u], FLOW[%u])"), &aModule.Name(), iChannelId, (TInt)&iHook.Context()));
+ aModule.Module()->Leave(iChannelId, iHook.Context(), aCallback);
+ return KErrNone;
+ }
+
+void CLeaveSession::RequestComplete()
+ {
+ LOG(Log::Printf(_L("\tqos session[%u] LeaveChannel complete"), (TInt)this));
+ DeliverEvent(KPfqosEventLeave);
+ // Somewhat icky here. RestartQoS would destroy the session, and
+ // to prevent that, ClearPendingRequest must be called first. However
+ // it also set the parameter as flow status. Use PENDING so that we
+ // don't get accidental and useless ReadyL sequence triggered here
+ // (because it will be triggered anyway via RestartQoS).
+ iHook.ClearPendingRequest(EFlow_PENDING);
+ if (!iFatalError)
+ {
+ iHook.RestartQoS(); // Need find out new QoS without channel.
+ }
+ }
+
+//
+CNegotiateItem::CNegotiateItem(CQoSSessionBase* aSession, TUint aFlags) : iSession(aSession)
+ {
+ LOG(Log::Printf(_L("new\tqos session[%u] NegotiateItem[%u] size=%d"), (TInt)iSession, (TInt)this, sizeof(*this)));
+ iFlags = aFlags;
+ }
+
+void CNegotiateItem::Kill()
+ {
+ // Going negotiation cannot be stopped (there is no call for it), just
+ // make this a zombie without a session and hope that RequestComplete
+ // happens (if not, then there is a memory leak!)
+ iSession = NULL;
+ LOG(Log::Printf(_L("\tqos session[%u] NegotiateItem[%u] is now a ZOMBIE!"), (TInt)iSession, (TInt)this));
+ }
+
+CNegotiateItem::~CNegotiateItem()
+ {
+ LOG(Log::Printf(_L("~\tqos session[%u] NegotiateItem[%u] deleted"), (TInt)iSession, (TInt)this));
+ }
+
+void CNegotiateItem::RequestComplete(TInt aErrorCode,
+ const TQoSParameters* aParams, const TExtensionData& aExtension)
+ {
+
+ if (iSession)
+ {
+ // I'm ALIVE! Not a ZOMBIE!
+ iSession->SubRequestComplete(aErrorCode, aParams, aExtension, this);
+ }
+ delete this;
+ }