networkcontrol/qosfwconfig/qos/src/negotiation.cpp
changeset 0 af10295192d8
--- /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;
+	}