telephonyprotocols/gprsumtsqosprt/src/guqos.cpp
changeset 0 3553901f7fa8
child 24 6638e7f4bd8f
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/telephonyprotocols/gprsumtsqosprt/src/guqos.cpp	Tue Feb 02 01:41:59 2010 +0200
@@ -0,0 +1,529 @@
+// Copyright (c) 2007-2009 Nokia Corporation and/or its subsidiary(-ies).
+// All rights reserved.
+// This component and the accompanying materials are made available
+// under the terms of "Eclipse Public License v1.0"
+// which accompanies this distribution, and is available
+// at the URL "http://www.eclipse.org/legal/epl-v10.html".
+//
+// Initial Contributors:
+// Nokia Corporation - initial contribution.
+//
+// Contributors:
+//
+// Description:
+//
+
+#include <es_ini.h>
+#include <timeout.h>
+#include <es_prot_internal.h>
+
+#include "guqos.h"
+#include "iface.h"
+#include "tc.h"
+#include "guqos_ini.h"
+#include "context.h"
+#include "async_request.h"
+#include "guqos_log.h"
+
+CModuleGuqos* CModuleGuqos::NewL()
+	{
+	CModuleGuqos* module = new (ELeave) CModuleGuqos();
+	CleanupStack::PushL(module);
+	module->ConstructL();
+	CleanupStack::Pop();
+	return module;
+	}
+
+CModuleGuqos::CModuleGuqos()
+	{
+	LOG(Log::Printf(_L("")));
+	LOG(Log::Printf(_L("**** CREATING MODULE GUQOS (size=%u) ****"), sizeof(*this)));
+	iFlowCount = 0;
+	}
+
+void CModuleGuqos::ConstructL()
+	{
+	iIfManager = CNifManager::NewL();
+	iTimeoutManager = TimeoutFactory::NewL(100);	// Time Unit is [1/100 seconds]
+	ReadConfigOptions();
+	}
+
+void CModuleGuqos::ReadConfigOptions()
+	{
+	CESockIniData* config = NULL;
+	TRAP_IGNORE(config = CESockIniData::NewL(GUQOS_INI_DATA));
+	TInt value;
+	if (config == NULL || !config->FindVar(GUQOS_INI_VARIABLES, GUQOS_INI_TIMEOUT, value))
+		value = KPdpContextDeleteTimeout;
+	delete config;
+	iOptions.iTimeout = (value + 9999) / 10000;
+	LOG(Log::Printf(_L("%S = %d [microseconds] converted to %d [1/100 seconds]"), &GUQOS_INI_TIMEOUT, value, iOptions.iTimeout));
+	}
+
+CModuleGuqos::~CModuleGuqos()
+	{
+	LOG(Log::Printf(_L("~\tGUGOS DESTRUCTOR")));
+	ASSERT(iFlowCount == 0);
+	delete iIfManager;
+	delete iTimeoutManager;
+	}
+
+void CModuleGuqos::InterfaceAttached(const TDesC& /*aName*/, CNifIfBase* aIf)
+	{
+	LOG(Log::Printf(_L("")));
+	LOG(Log::Printf(_L("Guqos::InterfaceAttached -- start CNifIfBase[%u]"), (TInt)aIf));
+	if (!aIf)
+		return;		// Nothing to attach! (someone is making invalid calls!).
+
+	if (!iIfManager->FindInterface(aIf))
+		{
+		TRAPD(err, iIfManager->CreateNifL(*aIf, *this));
+		if (err != KErrNone)
+			{
+			LOG(Log::Printf(_L("\tError creating Nif [error code = %d]"), err));				
+			}
+		}
+	LOG(Log::Printf(_L("Guqos::InterfaceAttached -- end")));
+	}
+
+void CModuleGuqos::InterfaceDetached(const TDesC& /*aName*/, CNifIfBase* aIf)
+	{
+	LOG(Log::Printf(_L("")));
+	LOG(Log::Printf(_L("Guqos::InterfaceDetached -- start")));
+	iIfManager->DeleteNif(aIf);
+	LOG(Log::Printf(_L("Guqos::InterfaceDetached -- end")));
+	}
+
+// references to protocol must be removed, i.e. drop all packets because they contain CFlowContext references
+void CModuleGuqos::Unbind(CProtocolBase* /*aProtocol*/, TUint /*aId = 0*/)
+	{
+	LOG(Log::Printf(_L("")));
+	LOG(Log::Printf(_L("Guqos::Unbind -- Nothing to do")));
+	// NOTHING TO īDO FOR NOW
+	// CFlowData may include CFlowContext reference, but those
+	// are never dereferenced, unless the context is otherwise
+	// known to be valid (e.g. in OpenL or is referenced from
+	// RMBufSendInfo. Otherwise the reference is used only as
+	// opaque identifier.
+	}
+
+void CModuleGuqos::InitModuleL(MEventInterface& aEventInterface, CExtension* /*aData*/)
+	{
+	iNotify = &aEventInterface;
+	}
+
+void CModuleGuqos::OpenL(CFlowContext& aFlow, CNifIfBase* aIf)
+	{
+	LOG(Log::Printf(_L("")));
+	LOG(Log::Printf(_L("Guqos::OpenL -- start")));
+    CNif* nif = iIfManager->FindInterface(aIf);
+    if (!nif)
+        {
+        User::Leave(KErrNotFound);
+        }
+    CFlowData *flowdata = FindFlow(&aFlow);
+    if (flowdata)
+        {
+        User::Leave(KErrAlreadyExists);
+        }
+
+	// Add new CFlowData to iList (will be the first element in the list)
+	iList = CFlowData::NewL(aFlow, *nif, iList);
+	iFlowCount++;
+      aFlow.iIgnoreFlowControl = ETrue;
+	iList->SetContext(nif->DefaultPdpContext());
+	//aFlow.SetStatus(EFlow_READY);
+    LOG(Log::Printf(_L("Guqos::OpenL -- end")));
+	}
+
+void CModuleGuqos::Close(CFlowContext& aFlow)
+	{
+	LOG(Log::Printf(_L("")));
+	LOG(Log::Printf(_L("Guqos::Close -- start")));
+	CFlowData* flowdata = CFlowData::Remove(&aFlow, &iList);
+	if (flowdata)
+		{
+		CPdpContext* context = flowdata->PdpContext();
+		if (context && !context->IsPrimary())
+			{
+			// Schedule a detached CClose request for updating
+			// the TFT filters on secondary context. (Either
+			// the context is flow specific or leave has not
+			// been issued).
+			CClose* request = CClose::New(*context);
+			if (request)
+				{
+				context->Nif().AddRequest(*request);
+				}
+			else
+				{
+				// Note: If the CClose cannot be allocated due to
+				// memory problem, the TFT will remain in effect,
+				// it will automaticly correct itself when there
+				// is another Leave or Close, that can be executed.
+				LOG(Log::Printf(_L("\tCannot schedule TFT filter removal due to out of memory")));
+				}
+			}
+		iFlowCount--;
+		iIfManager->CancelPendingRequests(flowdata);
+		delete flowdata;
+		}
+	LOG(Log::Printf(_L("Guqos::Close -- end")));
+	}
+
+void CModuleGuqos::Release(CFlowContext& aFlow)
+	{
+	LOG(Log::Printf(_L("")));
+	LOG(Log::Printf(_L("Guqos::Release -- start")));
+	CFlowData* flowdata = FindFlow(&aFlow);
+	if (!flowdata)
+		return;
+	// Abort any pending actions on the flow
+	iIfManager->CancelPendingRequests(flowdata);
+
+	CPdpContext* context = flowdata->PdpContext();
+	CPdpContext* default_context = flowdata->Nif().DefaultPdpContext();
+	if (context && context != default_context)
+		{
+		// Flow was attached to some other context, shedule some
+		// cleanup for that context.
+		CClose* request = CClose::New(*context);
+		if (request)
+			{
+			context->Nif().AddRequest(*request);
+			}
+		}
+	// Note: this detaches the flow from the old context,
+	// and may trigger destruction of the "context". Do not
+	// reference "context" after this! (the sheduled
+	// request is also cancelled implicitly in such case).
+	flowdata->SetContext(default_context);
+	// Flow will be in blocked state, if default_context does not exist.
+	}
+
+void CModuleGuqos::OpenChannel(TInt aChannelId, const TQoSParameters& aParams, CExtensionPolicy& aPolicy, MQoSNegotiateEvent& aNotify, CFlowContext& aFlow)
+	{
+	LOG(Log::Printf(_L("")));
+	LOG(Log::Printf(_L("Guqos::OpenChannel -- start id=%d"), aChannelId));
+	TInt ret = KErrNone; 
+
+	for (;;) /* FOR EXISTS ONLY FOR BREAK EXITS */
+		{
+		if (aChannelId <= 0)
+			{
+			// Channel ID must be > 0
+			ret = KErrArgument;
+			break;
+			}
+		CFlowData *flowdata = FindFlow(&aFlow);
+		if (flowdata == NULL)
+			{
+			ret = KErrNotFound;
+			break;
+			}
+
+		if (IfManager()->FindChannel(aChannelId))
+			{
+			// Note: This could be due to FindChannel finding a channel
+			// which has the delayed destruction and QoS framework is
+			// for some reason reusing the channel id for new channel
+			// (not possible currently, because QoS does not reuse
+			// channel ids).
+			ret = KErrAlreadyExists;
+			break;		
+			}
+
+		// Instead of creating totally new secondary context, there
+		// could be a check for expiring, but not yet deleted contexts,
+		// and reuse one of them for this channel -- that action
+		// would require a different request class, not COpenChannel,
+		// which would renegotiate the channel and recompute it's
+		// filters.
+		// coverity[alloc_fn] coverity[assign] coverity [memory_leak]  	
+		COpenChannel* open_request = COpenChannel::New(aChannelId, *flowdata, &aNotify);
+		if (open_request == NULL)
+			{
+			ret = KErrNoMemory;
+			break;
+			}
+		open_request->SetParameters(aParams, aPolicy);
+		open_request->SetParametersFlowExtn(aPolicy);
+		flowdata->Nif().AddRequest(*open_request);
+		/* TERMINATE FOR LOOP -- OpenChannel success -- request completes asyncronously */
+		LOG(Log::Printf(_L("Guqos::OpenChannel -- end")));
+		return;
+		}
+
+	aNotify.RequestComplete(ret, &aParams);
+	LOG(Log::Printf(_L("Guqos::OpenChannel -- end err=%d"), ret));
+	}
+
+void CModuleGuqos::CloseChannel(TInt aChannelId)
+	{
+	LOG(Log::Printf(_L("")));
+	LOG(Log::Printf(_L("Guqos::CloseChannel -- start id=%d"), aChannelId));
+	if (aChannelId <= 0)
+		return;
+	CPdpContext* context = IfManager()->FindChannel(aChannelId);
+	if (!context)
+		return;
+	if (context->IsPrimary())
+		{
+		// This is a secondary context turned into a default context. The use
+		// context as a channel is terminated, but it must continue to live as
+		// a default context. Thus, only remove the channel association from
+		// the context.
+		LOG(Log::Printf(_L("\tTurning secondary context into default only")));
+		context->SetChannelId(0);
+		}
+	else
+		{
+		//?? Need to use CDeleteRequest request to serialize NIF control!!!
+		//?? What happens if this is called in the middle of some request processing?
+		context->Delete();		// Notify NIF, destroy context on NIF.
+		context->Nif().DeletePdpContext(context);
+		}
+	LOG(Log::Printf(_L("Guqos::CloseChannel -- end")));
+	}
+
+void CModuleGuqos::NegotiateChannel(TInt aChannelId, const TQoSParameters& aParams, CExtensionPolicy& aPolicy, MQoSNegotiateEvent& aNotify)
+	{
+	LOG(Log::Printf(_L("")));
+	LOG(Log::Printf(_L("Guqos::NegotiateChannel -- start id=%d"), aChannelId));
+	TInt ret = KErrNone;
+
+	if (aChannelId <= 0)
+		{
+		ret = KErrArgument;
+		}
+	else
+		{
+		CPdpContext* context = IfManager()->FindChannel(aChannelId); 
+		if (context == NULL)
+			{
+			ret = KErrNotFound;
+			}
+		else
+			{
+			CNegotiateChannel* request = CNegotiateChannel::New(context, &aNotify);
+			if (request)
+				{
+				//coverity[leave_without_push]
+				request->SetParameters(aParams, aPolicy);
+				//coverity[leave_without_push]
+				request->SetParametersFlowExtn(aPolicy);
+				context->Nif().AddRequest(*request);
+				}
+			else
+				ret = KErrNoMemory;
+			}
+		}
+
+	if (ret != KErrNone)
+		aNotify.RequestComplete(ret, &aParams);
+	LOG(Log::Printf(_L("Guqos::NegotiateChannel -- end res=%d"), ret));
+	}
+
+void CModuleGuqos::Join(TInt aChannelId, CFlowContext& aFlow, MQoSNegotiateEvent& aNotify)
+	{
+	LOG(Log::Printf(_L("")));
+	LOG(Log::Printf(_L("Guqos::JoinChannel -- start id=%d"), aChannelId));
+	TInt ret = KErrNone;
+	CPdpContext* channel = IfManager()->FindChannel(aChannelId);
+	CFlowData *flowdata = FindFlow(&aFlow);
+	if (!channel || !flowdata)
+		ret = KErrNotFound;
+	else
+		{
+		CJoinRequest* join_request = CJoinRequest::New(channel, flowdata, &aNotify);
+		if (join_request)
+			channel->Nif().AddRequest(*join_request);
+		else
+			ret = KErrNoMemory;
+		}
+
+	if (ret != KErrNone)
+		aNotify.RequestComplete(ret, NULL);
+	LOG(Log::Printf(_L("Guqos::JoinChannel -- end err=%d"), ret));
+	}
+
+void CModuleGuqos::Leave(TInt aChannelId, CFlowContext& aFlow, MQoSNegotiateEvent& aNotify)
+	{
+	LOG(Log::Printf(_L("")));
+	LOG(Log::Printf(_L("Guqos::LeaveChannel -- start id=%d"), aChannelId));
+	TInt ret = KErrNone;
+	CPdpContext* channel = IfManager()->FindChannel(aChannelId);
+	CFlowData *flowdata = FindFlow(&aFlow);
+	if (!channel || !flowdata)
+		ret = KErrNotFound;
+
+	if (ret == KErrNone)
+		{
+		CLeaveRequest* leave_request = CLeaveRequest::New(channel, flowdata, &aNotify);
+		if (leave_request)
+			channel->Nif().AddRequest(*leave_request);
+		else
+			ret = KErrNoMemory;
+		}
+
+	if (ret != KErrNone)
+		aNotify.RequestComplete(ret, NULL);
+	LOG(Log::Printf(_L("Guqos::LeaveChannel -- end ret=%d"), ret));
+	}
+
+void CModuleGuqos::Negotiate(CFlowContext &aFlow, const TQoSParameters& aParams, MQoSNegotiateEvent& aNotify)
+	{
+	LOG(Log::Printf(_L("")));
+	LOG(Log::Printf(_L("Guqos::Negotiate -- start")));
+	TInt ret = KErrNone;
+
+	for (;;) /* FOR EXISTS ONLY FOR BREAK EXITS */
+		{
+		CFlowData* flowdata = FindFlow(&aFlow);
+		if (!flowdata)
+			{
+			ret = KErrNotFound;
+			break;
+			}
+		//?? This needs rework and specification of how things should work!
+		//?? But, for now the assumption is that the Negotiate() is not used in the
+		//?? current framework.
+		
+		// The Negotiate operation does not have the extension policies as parameter,
+		// like OpenChannel or NegotiateChannel has. The code below attempts to fix
+		// this by searching the matching policy. However, only first found is used
+		// (in order Override, Application, Default). This gets murky, if more than
+		// one match would exist -- should extensions be merged in such case?
+		CExtensionPolicy *policy = NULL;
+		if ((policy = (CExtensionPolicy*)iNotify->Lookup(aFlow, EPfqosExtensionPolicy, EPfqosOverridePriority)) == NULL &&
+			(policy = (CExtensionPolicy*)iNotify->Lookup(aFlow, EPfqosExtensionPolicy, EPfqosApplicationPriority)) == NULL &&
+			(policy = (CExtensionPolicy*)iNotify->Lookup(aFlow, EPfqosExtensionPolicy, EPfqosDefaultPriority)) == NULL)
+			{
+			ret = KErrNotFound;
+			break;
+			}
+
+		// Instead of creating totally new context, one could search for
+		// expiring contexts, and reuse one of them for this flow. Could
+		// also prefer a context that has been used for this same flow
+		// previously -- less negotiating, as the filter might already be
+		// correct.
+		COpenChannel* negotiate_request = COpenChannel::New(0, *flowdata, &aNotify);
+		if (negotiate_request == NULL)
+			{
+			ret = KErrNoMemory;
+			break;
+			}
+		//coverity[leave_without_push]
+		negotiate_request->SetParameters(aParams, *policy);
+		//coverity[leave_without_push]
+		negotiate_request->SetParametersFlowExtn(*policy);
+		flowdata->Nif().AddRequest(*negotiate_request);
+		LOG(Log::Printf(_L("Guqos::Negotiate -- end OK")));
+		return;
+		}
+
+	aNotify.RequestComplete(ret, &aParams);
+	LOG(Log::Printf(_L("Guqos::Negotiate -- end ret=%d"), ret));
+	}
+
+TInt CModuleGuqos::Configure(TUint aLevel,TUint aName, TDes8& aOption, TAny* /*aSource*/)
+	{
+	LOG(Log::Printf(_L("guqos::Configure")));
+	if (aLevel == KSOLQoSModule)
+		{
+		switch (aName)
+			{
+		case KSoCapabilities:
+			if (aOption.Length() >= (TInt)sizeof(TInt))
+				{
+				//lint -e{826} complains, pointer conversion is ok
+				TInt& opt = *(TInt*)aOption.Ptr();
+				opt = KModuleCapabilites;
+				return KErrNone;
+				}
+			return KErrArgument;
+		default:
+			break;
+			}
+		}
+	return KErrNotSupported;
+	}
+
+
+TInt CModuleGuqos::Send(RMBufChain& aPacket, CProtocolBase* /*aSourceProtocol*/)
+	{
+	for (;;)	/* FOREVER, ONLY FOR BREAK EXITS */
+		{
+		RMBufSendInfo* const info = RMBufSendPacket::PeekInfoInChain(aPacket);
+		if (!info)
+			{
+			// Malformed packet, cannot do anything with it
+			LOG(Log::Printf(_L("Guqos::Send -- packet has no info block!")));	
+			break;
+			}
+		CFlowContext* const context = info->iFlow.FlowContext();
+		if (!context)
+			{
+			// Malformed packet, cannot do anything with it
+			LOG(Log::Printf(_L("Guqos::Send -- packet has no flow context!")));
+			break;
+			}
+
+		CFlowData* flowdata = FindFlow(context);
+		if (!flowdata)
+			{
+			// Flow is not registered with GUQOS and this Send should
+			// not have happened.
+			info->iFlow.Close();
+			LOG(Log::Printf(_L("Guqos::Send -- flow is not open in GUQOS!")));
+			break;
+			}
+		return flowdata->Send(aPacket, *info);
+		}
+	aPacket.Free();
+	return 1;
+	}
+
+void CModuleGuqos::Identify(TServerProtocolDesc* aProtocolDesc) const
+	{
+	Identify(*aProtocolDesc);
+	}
+
+void CModuleGuqos::Identify(TServerProtocolDesc& aDesc)
+	{
+	_LIT(Kguqos, "quqos");
+
+	aDesc.iName=Kguqos;
+	aDesc.iAddrFamily=KAfInet;
+	aDesc.iSockType=KSockDatagram;
+	aDesc.iProtocol=KModuleGUQoS;
+	aDesc.iVersion=TVersion(KMajorVersionNumber, KMinorVersionNumber, KBuildVersionNumber);
+	aDesc.iByteOrder=EBigEndian;
+	aDesc.iServiceInfo=0;
+	aDesc.iNamingServices=0;
+	aDesc.iSecurity=KSocketNoSecurity;
+	aDesc.iMessageSize=0xffff;
+	aDesc.iServiceTypeInfo=EPreferMBufChains | ENeedMBufs;
+	aDesc.iNumSockets=KUnlimitedSockets;
+	}
+
+
+
+CFlowData* CModuleGuqos::FindFlow(const CFlowContext* aFlow)
+	{
+	return CFlowData::Find(aFlow, iList);
+	}
+
+// Default parameters are fetched from the QoS policy db.
+TInt CModuleGuqos::GetDefaultParameters(TQoSRequested& aParameters, TUint32 aIapId)
+	{
+	TInetAddr addr;
+	addr.SetAddress(KInet6AddrNone);
+	TUidType uid(TUid::Uid(0), TUid::Uid(0), TUid::Uid(0));
+	CExtensionPolicy* sel = (CExtensionPolicy*)iNotify->Lookup(addr, addr, 0, 0, 0, EPfqosExtensionPolicy, uid, aIapId, 0);
+	if (!sel)
+		return KErrNotFound;
+	aParameters.ParsePolicyData(sel);
+	return KErrNone;
+	}