networkcontrol/qosfwconfig/qos/src/negotiation.cpp
changeset 0 af10295192d8
equal deleted inserted replaced
-1:000000000000 0:af10295192d8
       
     1 // Copyright (c) 2006-2009 Nokia Corporation and/or its subsidiary(-ies).
       
     2 // All rights reserved.
       
     3 // This component and the accompanying materials are made available
       
     4 // under the terms of "Eclipse Public License v1.0"
       
     5 // which accompanies this distribution, and is available
       
     6 // at the URL "http://www.eclipse.org/legal/epl-v10.html".
       
     7 //
       
     8 // Initial Contributors:
       
     9 // Nokia Corporation - initial contribution.
       
    10 //
       
    11 // Contributors:
       
    12 //
       
    13 // Description:
       
    14 //
       
    15 
       
    16 #include "qos_prot.h"
       
    17 #include "qoserr.h"
       
    18 #include "policy_sap.h"
       
    19 #include "interface.h"
       
    20 #include "qos_channel.h"
       
    21 #include "modules.h"
       
    22 #include "flowhook.h"
       
    23 #include "negotiation.h"
       
    24 
       
    25 // CQoSSessionBase
       
    26 CQoSSessionBase::CQoSSessionBase(CFlowHook& aHook, TInt aChannelId) 
       
    27 	: iHook(aHook), iModuleList(aHook.ModuleList()), iChannelId(aChannelId)
       
    28 	{
       
    29 	iPending.SetOffset(_FOFF(CNegotiateItem, iLink));
       
    30 	iFatalError = EFalse;
       
    31 	iError = KErrNone;
       
    32 	iNegotiated = aHook.QoSParameters();
       
    33 	}
       
    34 
       
    35 CQoSSessionBase::~CQoSSessionBase()
       
    36 	{
       
    37 	while (!iPending.IsEmpty())
       
    38 		{
       
    39 		CNegotiateItem* request = iPending.First();
       
    40 		iPending.Remove(*request);
       
    41 		// Because there is no way to cancel a pending CNegotiateItem,
       
    42 		// it *CANNOT* be deleted here. Make it a ZOMBIE by removing
       
    43 		// the session pointer and hope that RequestComplete arrives
       
    44 		// someday!
       
    45 		request->Kill();
       
    46 		}
       
    47 	iPending.Reset();
       
    48 	}
       
    49 
       
    50 void CQoSSessionBase::Run()
       
    51 	/**
       
    52 	* Start the session.
       
    53 	*/
       
    54 	{
       
    55 	iCurrent = 0;
       
    56 	if (iModuleList.Count() == 0)
       
    57 		{
       
    58 		// There are no modules present, the negotiation would
       
    59 		// not do or achieve anything. It is somewhat unclear
       
    60 		// whether this is an error or not. Treat it as an error
       
    61 		// and pick the associated error code from the interface
       
    62 		// (if there is no TrafficControlStatus error on interface
       
    63 		// then this just completes the request without error)
       
    64 		FatalError(iHook.Interface() ? iHook.Interface()->TrafficControlStatus() : EQoSNoModules);
       
    65 		}
       
    66 	Proceed();
       
    67 	}
       
    68 
       
    69 // Proceed negotiation
       
    70 void CQoSSessionBase::Proceed()
       
    71 	{
       
    72 	if (iProceed)
       
    73 		{
       
    74 		// Getting here from DoCall, do nothing here
       
    75 		// Proceeding after DoCall returns! DoCall
       
    76 		// completed the subrequest immediate!
       
    77 		return;
       
    78 		}
       
    79 
       
    80 	while (!iFatalError && iCurrent < iModuleList.Count())
       
    81 		{
       
    82 		RModule* module = iModuleList[iCurrent];
       
    83 		ASSERT(module);
       
    84 
       
    85 		const TUint flags = module->Flags();
       
    86 		if ((flags & KQoSModuleSerialize) && !iPending.IsEmpty())
       
    87 			{
       
    88 			// The module requires previous requests to be completed.
       
    89 			// There is at least one pending request. When it completes,
       
    90 			// the Proceed() gets called to restart the loop.
       
    91 			return;
       
    92 			}
       
    93 		++iCurrent;
       
    94 
       
    95 		CNegotiateItem*const item = new CNegotiateItem(this, flags);
       
    96 		if (item == NULL)
       
    97 			{
       
    98 			FatalError(KErrNoMemory);
       
    99 			break;
       
   100 			}
       
   101 		iPending.AddLast(*item);
       
   102 
       
   103 		LOG(Log::Printf(_L("")));
       
   104 		LOG(Log::Printf(_L("calling\tmodule[%S] for session[%u]"), &module->Name(), (TInt)this));
       
   105 		++iProceed;	// ..in case DoCall calls (Sub)RequestComplete!
       
   106 		const TInt ret = DoCall(*module, *item);
       
   107 		--iProceed;
       
   108 		LOG(Log::Printf(_L("<<<\treturns from module[%S] for session[%u]"), &module->Name(), (TInt)this));
       
   109 		if (ret != KErrNone)
       
   110 			{
       
   111 			// DoCall didn't call the module, there will be no (Sub)RequestCompete call!
       
   112 			// Destroy the item here!
       
   113 			iPending.Remove(*item);
       
   114 			delete item;
       
   115 			FatalError(ret);
       
   116 			break;
       
   117 			}
       
   118 		}
       
   119 
       
   120 	if (iPending.IsEmpty() || iFatalError)
       
   121 		{
       
   122 		RequestComplete();
       
   123 		//?? In case of iFatalError, should we start a "shutdown" process
       
   124 		//?? session for those modules that actually succeeded (at least
       
   125 		//?? in case of some session, like if some Joins succeed, shouldn't
       
   126 		//?? there be a Leave for those, if the whole session cannot be
       
   127 		//?? completed? [apparenty there is never more than one module, so
       
   128 		//?? the issue has not come up yet...]
       
   129 		delete this;
       
   130 		}
       
   131 	}
       
   132 
       
   133 
       
   134 CInternalQoSChannel* CQoSSessionBase::Channel() const
       
   135 	/**
       
   136 	* Return internal channel object or NULL.
       
   137 	*/
       
   138 
       
   139 	{
       
   140 	// The sessions only store the channel id, and when needed, the
       
   141 	// channel object is located by this. This takes care of the
       
   142 	// possibility that the channel disappears while sessions is
       
   143 	// running (if a pointer was stored, the channel object destructor
       
   144 	// would have to find all sessions referencing it, and currently
       
   145 	// this would be somewhat complicated (as flows holding the session
       
   146 	// might not be members of that channel any more).
       
   147 	if (iChannelId > 0)
       
   148 		return iHook.Protocol().ChannelMgr()->FindChannel(iChannelId);
       
   149 	else
       
   150 		return NULL;
       
   151 	}
       
   152 
       
   153 void CQoSSessionBase::FatalError(TInt aErrorCode)
       
   154 	{
       
   155 	iFatalError = ETrue;
       
   156 	iError = aErrorCode;
       
   157 	}
       
   158 
       
   159 void CQoSSessionBase::SubRequestComplete(TInt aErrorCode, 
       
   160 	const TQoSParameters* aParams, const TExtensionData& aExtension, 
       
   161 	CNegotiateItem* aItem)
       
   162 	{
       
   163 	LOG(Log::Printf(_L("<>\tqos session[%u] NegotiateItem[%u] SubRequestComplete"), (TInt)this, (TInt)aItem));
       
   164 
       
   165 	iPending.Remove(*aItem);
       
   166 
       
   167 	if (aParams)
       
   168 		{
       
   169 		iNegotiated = *aParams;
       
   170 		}
       
   171 
       
   172 	// add possible extensions to PF_QOS event
       
   173 	if (aExtension.iData.Length() > 0)
       
   174 		{
       
   175 		TRAPD(err, iMsg.AddExtensionL(aExtension.iData, aExtension.iType));
       
   176 		if (err != KErrNone)
       
   177 			{
       
   178 			LOG(Log::Printf(_L("iMsg.AddExtensionL error: %d"), err));
       
   179 			}
       
   180 		}
       
   181 
       
   182 	if (aErrorCode == KErrNone)
       
   183 		{
       
   184 		//lint -e{961} Missing 'else' is OK
       
   185 		if (KQoSModuleSignaling & aItem->Flags())
       
   186 			{
       
   187 			iValue |= KQoSModuleSignaling;
       
   188 			}
       
   189 		else if (KQoSModulePartialSignaling & aItem->Flags())
       
   190 			{
       
   191 			iValue |= KQoSModulePartialSignaling;
       
   192 			}
       
   193 		else if (KQoSModuleProvisioning & aItem->Flags())
       
   194 			{
       
   195 			iValue |= KQoSModuleProvisioning;
       
   196 			}
       
   197 		}
       
   198 	else
       
   199 		{
       
   200 		if (aItem->Flags() & KQoSFatalFailure
       
   201 		||  aErrorCode == EQoSLeaveFailure)
       
   202 		    {
       
   203 			FatalError(aErrorCode);
       
   204 			}
       
   205 		}	
       
   206 	Proceed();
       
   207 	}
       
   208 
       
   209 void CQoSSessionBase::DeliverEvent(TUint16 aEvent)
       
   210 	{
       
   211 	// If the caller does not set event, choose one by iFatalError
       
   212 	if (aEvent == 0)
       
   213 		{
       
   214 		//?? Event seems to be a bitmask. Should this actually be done
       
   215 		//?? always (and with OR to the aEvent)?
       
   216 		aEvent = iFatalError ? KPfqosEventFailure : KPfqosEventConfirm;
       
   217 		}
       
   218 
       
   219 	T_pfqos_msg base(EPfqosEvent);
       
   220 	if (iError != KErrNone)
       
   221 		{
       
   222 		base.pfqos_msg_errno = iError;
       
   223 		}
       
   224 	iMsg.iBase.iMsg = &base;
       
   225 
       
   226 	T_pfqos_event event(EPfqosExtEvent, aEvent, iValue);
       
   227 	iMsg.iEvent.iExt = &event;
       
   228 
       
   229 	T_pfqos_flowspec spec(iNegotiated);
       
   230 	iMsg.iFlowSpec.iExt = &spec;
       
   231 
       
   232 	// Add flow identifying PF_QOS blocks
       
   233 	// note: the variables must be outside the if-block, because they must exist
       
   234 	// when Deliver is called!
       
   235 	TInetAddr msk;
       
   236 	pfqos_address src, dst;
       
   237 	pfqos_selector sel;
       
   238 	iHook.FillFlowInfo(iMsg, src, dst, sel, msk);
       
   239 
       
   240 
       
   241 	// Add channel identifying PF_QOS block (only present if requested)
       
   242 	T_pfqos_channel channel(iChannelId);
       
   243 	if (iChannelId > 0)
       
   244 		iMsg.iChannel.iExt = &channel;
       
   245 
       
   246 	iMsg.iNumModules = 0;
       
   247 	iHook.Protocol().Deliver(iMsg, KProviderKey_RegisteredQoSConf);
       
   248 	}
       
   249 
       
   250 
       
   251 // Negotiate
       
   252 CNegotiateSession::CNegotiateSession(CFlowHook& aHook) 	: CQoSSessionBase(aHook, 0)
       
   253 	{
       
   254 	LOG(Log::Printf(_L("new\tqos session[%u] Negotiate for HOOK[%u] size=%d"), (TInt)this, (TInt)&aHook, sizeof(*this)));
       
   255 	}
       
   256 
       
   257 CNegotiateSession::~CNegotiateSession()
       
   258 	{
       
   259 	LOG(Log::Printf(_L("~\tqos session[%u] Negotiate deleted"), (TInt)this));
       
   260 	}
       
   261 
       
   262 TInt CNegotiateSession::DoCall(RModule& aModule, MQoSNegotiateEvent& aCallback)
       
   263 	{
       
   264 	LOG(Log::Printf(_L("\t\tmodule[%S]::Negotiate(...)"), &aModule.Name()));
       
   265 	aModule.Module()->Negotiate(iHook.Context(), iHook.QoSParameters(), aCallback);
       
   266 	return KErrNone;
       
   267 	}
       
   268 
       
   269 void CNegotiateSession::RequestComplete()
       
   270 	{
       
   271 	DeliverEvent(0);
       
   272 	iHook.ClearPendingRequest(iError);
       
   273 	}
       
   274 
       
   275 
       
   276 // Create channel session
       
   277 CCreateChannelSession::CCreateChannelSession(CFlowHook& aHook, TInt aChannelId) : CQoSSessionBase(aHook, aChannelId)
       
   278 	{
       
   279 	LOG(Log::Printf(_L("new\tqos session[%u] for HOOK[%u] CreateChannel size=%d"), (TInt)this, (TInt)&aHook, sizeof(*this)));
       
   280 	}
       
   281 
       
   282 CCreateChannelSession::~CCreateChannelSession()
       
   283 	{
       
   284 	LOG(Log::Printf(_L("~\tqos session[%u] CreateChannel deleted"), (TInt)this));
       
   285 	}
       
   286 
       
   287 TInt CCreateChannelSession::DoCall(RModule& aModule, MQoSNegotiateEvent& aCallback)
       
   288 	{
       
   289 	LOG(Log::Printf(_L("\t\tmodule[%S]::OpenChannel(channel[%u],..., FLOW[%u])"),
       
   290 		&aModule.Name(), iChannelId, (TInt)&iHook.Context()));
       
   291 	CInternalQoSChannel*const channel = iHook.Protocol().ChannelMgr()->FindChannel(iChannelId);
       
   292 	if (channel == NULL)
       
   293 		{
       
   294 		return EQoSChannelDeleted;
       
   295 		}
       
   296 	aModule.Module()->OpenChannel(iChannelId, channel->QoSParameters(), channel->Extension(), aCallback, iHook.Context());
       
   297 	return KErrNone;
       
   298 	}
       
   299 
       
   300 void CCreateChannelSession::RequestComplete()
       
   301 	{
       
   302 	LOG(Log::Printf(_L("\tqos session[%u] CreateChannel complete"), (TInt)this));
       
   303 	DeliverEvent(0);
       
   304 	iHook.SetChannelJoined(TRUE);
       
   305 	iHook.ClearPendingRequest(iError);
       
   306 	CInternalQoSChannel*const channel = iHook.Protocol().ChannelMgr()->FindChannel(iChannelId);
       
   307 	if (channel)
       
   308 		channel->RequestComplete(iError);
       
   309 	}
       
   310 
       
   311 
       
   312 
       
   313 // Negotiate channel session
       
   314 CNegotiateChannelSession::CNegotiateChannelSession(CFlowHook& aHook, TInt aChannelId) : CQoSSessionBase(aHook, aChannelId)
       
   315 	{
       
   316 	LOG(Log::Printf(_L("new\tqos session[%u] for HOOK[%u] NegotiateChannel size=%d"), (TInt)this, (TInt)&aHook, sizeof(*this)));
       
   317 	}
       
   318 
       
   319 CNegotiateChannelSession::~CNegotiateChannelSession()
       
   320 	{
       
   321 	LOG(Log::Printf(_L("~\tqos session[%u] NegotiateChannel deleted"), (TInt)this));
       
   322 	}
       
   323 
       
   324 TInt CNegotiateChannelSession::DoCall(RModule& aModule, MQoSNegotiateEvent& aCallback)
       
   325 	{
       
   326 	LOG(Log::Printf(_L("\t\tmodule[%S]::NegotiateChannel(channel[%u], ...)"), &aModule.Name(), iChannelId));
       
   327 	CInternalQoSChannel*const channel = Channel();
       
   328 	if (channel == NULL)
       
   329 		{
       
   330 		return EQoSChannelDeleted;
       
   331 		}
       
   332 	aModule.Module()->NegotiateChannel(iChannelId, channel->QoSParameters(), channel->Extension(), aCallback);
       
   333 	return KErrNone;
       
   334 	}
       
   335 
       
   336 void CNegotiateChannelSession::RequestComplete()
       
   337 	{
       
   338 	LOG(Log::Printf(_L("~\tqos session[%u] NegotiateChannel complete"), (TInt)this));
       
   339 	DeliverEvent(0);
       
   340 	iHook.ClearPendingRequest(iError);
       
   341 
       
   342 	CInternalQoSChannel*const channel = Channel();
       
   343 	if (channel)
       
   344 		channel->RequestComplete(iError);
       
   345 	}
       
   346 
       
   347 // Join
       
   348 CJoinSession::CJoinSession(CFlowHook& aHook, TInt aChannelId) : CQoSSessionBase(aHook, aChannelId)
       
   349 	{
       
   350 	LOG(Log::Printf(_L("new\tqos session[%u] for HOOK[%u] JoinChannel size=%d"), (TInt)this, (TInt)&aHook, sizeof(*this)));
       
   351 	}
       
   352 
       
   353 CJoinSession::~CJoinSession()
       
   354 	{
       
   355 	LOG(Log::Printf(_L("~\tqos session[%u] JoinChannel deleted"), (TInt)this));
       
   356 	}
       
   357 
       
   358 TInt CJoinSession::DoCall(RModule& aModule, MQoSNegotiateEvent& aCallback)
       
   359 	{
       
   360 	LOG(Log::Printf(_L("\t\tmodule[%S]::Join(channel[%u], FLOW[%u])"), &aModule.Name(), iChannelId, (TInt)&iHook.Context()));
       
   361 	const CInternalQoSChannel*const channel = Channel();
       
   362 	if (channel == NULL)
       
   363 		{
       
   364 		return EQoSChannelDeleted;
       
   365 		}
       
   366 	aModule.Module()->Join(iChannelId, iHook.Context(), aCallback);
       
   367 	return KErrNone;
       
   368 	}
       
   369 
       
   370 
       
   371 void CJoinSession::RequestComplete()
       
   372 	{
       
   373 	LOG(Log::Printf(_L("\tqos session[%u] JoinChannel complete"), (TInt)this));
       
   374 	DeliverEvent(KPfqosEventJoin);
       
   375 	iHook.SetChannelJoined(TRUE);
       
   376 	iHook.ClearPendingRequest(iError);
       
   377 
       
   378 	CInternalQoSChannel*const channel = Channel();
       
   379 	if (channel)
       
   380 		channel->RequestComplete(iError);
       
   381 	}
       
   382 
       
   383 
       
   384 
       
   385 // Leave
       
   386 CLeaveSession::CLeaveSession(CFlowHook& aHook, TInt aChannelId) : CQoSSessionBase(aHook, aChannelId)
       
   387 	{
       
   388 	LOG(Log::Printf(_L("new\tqos session[%u] for HOOK[%u] LeaveChannel size=%d"), (TInt)this, (TInt)&aHook, sizeof(*this)));
       
   389 	}
       
   390 
       
   391 CLeaveSession::~CLeaveSession()
       
   392 	{
       
   393 	LOG(Log::Printf(_L("~\tqos session[%u] LeaveChannel deleted"), (TInt)this));
       
   394 	}
       
   395 
       
   396 TInt CLeaveSession::DoCall(RModule& aModule, MQoSNegotiateEvent& aCallback)
       
   397 	{
       
   398 	LOG(Log::Printf(_L("\t\tmodule[%S]::Leave(channel[%u], FLOW[%u])"), &aModule.Name(), iChannelId, (TInt)&iHook.Context()));
       
   399 	aModule.Module()->Leave(iChannelId, iHook.Context(), aCallback);
       
   400 	return KErrNone;
       
   401 	}
       
   402 
       
   403 void CLeaveSession::RequestComplete()
       
   404 	{
       
   405 	LOG(Log::Printf(_L("\tqos session[%u] LeaveChannel complete"), (TInt)this));
       
   406 	DeliverEvent(KPfqosEventLeave);
       
   407 	// Somewhat icky here. RestartQoS would destroy the session, and
       
   408 	// to prevent that, ClearPendingRequest must be called first. However
       
   409 	// it also set the parameter as flow status. Use PENDING so that we
       
   410 	// don't get accidental and useless ReadyL sequence triggered here
       
   411 	// (because it will be triggered anyway via RestartQoS).
       
   412 	iHook.ClearPendingRequest(EFlow_PENDING);
       
   413 	if (!iFatalError)
       
   414 	    {
       
   415 	    iHook.RestartQoS();	// Need find out new QoS without channel.
       
   416 	    }
       
   417 	}
       
   418 
       
   419 //
       
   420 CNegotiateItem::CNegotiateItem(CQoSSessionBase* aSession, TUint aFlags) : iSession(aSession)
       
   421 	{
       
   422 	LOG(Log::Printf(_L("new\tqos session[%u] NegotiateItem[%u] size=%d"), (TInt)iSession, (TInt)this, sizeof(*this)));
       
   423 	iFlags = aFlags;
       
   424 	}
       
   425 
       
   426 void CNegotiateItem::Kill()
       
   427 	{
       
   428 	// Going negotiation cannot be stopped (there is no call for it), just
       
   429 	// make this a zombie without a session and hope that RequestComplete
       
   430 	// happens (if not, then there is a memory leak!)
       
   431 	iSession = NULL;
       
   432 	LOG(Log::Printf(_L("\tqos session[%u] NegotiateItem[%u] is now a ZOMBIE!"), (TInt)iSession, (TInt)this));
       
   433 	}
       
   434 
       
   435 CNegotiateItem::~CNegotiateItem()
       
   436 	{
       
   437 	LOG(Log::Printf(_L("~\tqos session[%u] NegotiateItem[%u] deleted"), (TInt)iSession, (TInt)this));
       
   438 	}
       
   439 
       
   440 void CNegotiateItem::RequestComplete(TInt aErrorCode, 
       
   441 	const TQoSParameters* aParams, const TExtensionData& aExtension)
       
   442 	{
       
   443 	
       
   444 	if (iSession)
       
   445 		{
       
   446 		// I'm ALIVE! Not a ZOMBIE!	
       
   447 		iSession->SubRequestComplete(aErrorCode, aParams, aExtension, this);
       
   448 		}
       
   449 	delete this;
       
   450 	}