|
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 } |