|
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 /** |
|
17 @file |
|
18 @internalComponent |
|
19 */ |
|
20 |
|
21 #include <bluetooth/hcicmdqcontroller.h> |
|
22 #include <bluetooth/hci/hciconsts.h> |
|
23 |
|
24 #include "HciCmdQTimer.h" |
|
25 #include "HciCmdQUtil.h" |
|
26 |
|
27 #include <bluetooth/hardresetinitiator.h> |
|
28 #include <bluetooth/hcicommandqitem.h> |
|
29 #include <bluetooth/hcicommandqueueclient.h> |
|
30 #include <bluetooth/linkmuxnotifier.h> |
|
31 #include <bluetooth/hci/commandstatusevent.h> |
|
32 #include <bluetooth/hci/event.h> |
|
33 #include <bluetooth/hci/commandcompleteevent.h> |
|
34 #include <bluetooth/hci/hcicmdqueuedecisionplugin.h> |
|
35 #include <bluetooth/hci/hcicmdqueuedecisioninterface.h> |
|
36 #include <bluetooth/hci/hciframe.h> |
|
37 #include <bluetooth/hci/hctlinterface.h> |
|
38 #include <bluetooth/hci/hciutil.h> |
|
39 #include <bluetooth/hci/command.h> |
|
40 #include <bluetooth/hci/hcievents.h> |
|
41 #include <bluetooth/hci/completingeventquery.h> |
|
42 |
|
43 #include <bluetooth/logger.h> |
|
44 |
|
45 #ifdef __FLOG_ACTIVE |
|
46 _LIT8(KLogComponent, LOG_COMPONENT_HCICMDQ); |
|
47 #endif |
|
48 |
|
49 #ifdef _DEBUG |
|
50 PANICCATEGORY("hcicmdqcon"); |
|
51 #endif |
|
52 |
|
53 _LIT(KHciCommandQueueComponentName, "cmdq"); |
|
54 |
|
55 EXPORT_C void MHCICmdQueueUtilities::InjectEvent(const THCIEventBase& aEvent) |
|
56 { |
|
57 MhcquiInjectEvent(aEvent); |
|
58 } |
|
59 |
|
60 EXPORT_C CHCICommandQItem* MHCICmdQueueUtilities::FindOutstandingCommand(THCIOpcode aOpcode) |
|
61 { |
|
62 return MhcquiFindOutstandingCommand(aOpcode); |
|
63 } |
|
64 |
|
65 EXPORT_C /*static*/ CHCICmdQController* CHCICmdQController::NewL() |
|
66 { |
|
67 LOG_STATIC_FUNC |
|
68 |
|
69 CHCICmdQController* self = new (ELeave) CHCICmdQController(); |
|
70 CleanupStack::PushL(self); |
|
71 self->ConstructL(); |
|
72 CleanupStack::Pop(self); |
|
73 return self; |
|
74 } |
|
75 |
|
76 /** |
|
77 Destructor. |
|
78 */ |
|
79 EXPORT_C CHCICmdQController::~CHCICmdQController() |
|
80 { |
|
81 LOG_FUNC |
|
82 |
|
83 // All (entry point) command queue clients should have removed any commands they added. |
|
84 #if defined(_DEBUG) && defined(__FLOG_ACTIVE) |
|
85 // In debug to help debugging by logging the commands left on the queues that should be empty. |
|
86 if(!iInitCommandQ.IsEmpty()) |
|
87 { |
|
88 LOG(_L("Initialisation Queue is not empty...")) |
|
89 LogQueue(iInitCommandQ); |
|
90 } |
|
91 if(!iNormalCommandQ.IsEmpty()) |
|
92 { |
|
93 LOG(_L("Normal Queue is not empty...")) |
|
94 LogQueue(iNormalCommandQ); |
|
95 } |
|
96 if(!iPriorityCommandQ.IsEmpty()) |
|
97 { |
|
98 LOG(_L("Priority Queue is not empty...")) |
|
99 LogQueue(iPriorityCommandQ); |
|
100 } |
|
101 if(!iWorkaroundCommandQ.IsEmpty()) |
|
102 { |
|
103 LOG(_L("Workaround Queue is not empty...")) |
|
104 LogQueue(iWorkaroundCommandQ); |
|
105 } |
|
106 #endif // _DEBUG && __FLOG_ACTIVE |
|
107 __ASSERT_ALWAYS(iInitCommandQ.IsEmpty(), PANIC(KHCICmdQPanic, EInitCommandQNotEmptyInDestructor)); |
|
108 __ASSERT_ALWAYS(iNormalCommandQ.IsEmpty(), PANIC(KHCICmdQPanic, ENormalCommandQNotEmptyInDestructor)); |
|
109 __ASSERT_ALWAYS(iPriorityCommandQ.IsEmpty(), PANIC(KHCICmdQPanic, EPriorityCommandQNotEmptyInDestructor)); |
|
110 __ASSERT_ALWAYS(iWorkaroundCommandQ.IsEmpty(), PANIC(KHCICmdQPanic, EWorkaroundCommandQNotEmptyInDestructor)); |
|
111 |
|
112 // Clean up internally managed queues |
|
113 TDblQueIter<CHCICommandQItem> sentIter(iSentCommandQ); |
|
114 CleanUpQueue(sentIter); |
|
115 TDblQueIter<CHCICommandQItem> resendIter(iResendCommandQ); |
|
116 CleanUpQueue(resendIter); |
|
117 |
|
118 __ASSERT_ALWAYS(iSentCommandQ.IsEmpty(), PANIC(KHCICmdQPanic, ESentCommandQNotEmptyInDestructor)); |
|
119 __ASSERT_ALWAYS(iResendCommandQ.IsEmpty(), PANIC(KHCICmdQPanic, EResendCommandQNotEmptyInDestructor)); |
|
120 |
|
121 delete iQStarvationTimer; |
|
122 delete iSendingCommand; |
|
123 delete iQdpPlugin; |
|
124 |
|
125 // Delete async CallBacks. If running, these should be cancelled by the |
|
126 // d'tor of CAsyncOneShot. |
|
127 delete iAsyncCallBackForReset; |
|
128 delete iAsyncCallBackForSend; |
|
129 |
|
130 delete iHciUtil; |
|
131 CLOSE_LOGGER |
|
132 } |
|
133 |
|
134 /** |
|
135 Sets the HCTL interface instance to be used by the Command Queue Controller to |
|
136 send command frames over the HCTL. |
|
137 |
|
138 @param aHctlInterface An instance implementing the interface to the HCTL. |
|
139 */ |
|
140 EXPORT_C void CHCICmdQController::SetHCTLInterface(MHCTLInterface& aHctlInterface) |
|
141 { |
|
142 LOG_FUNC |
|
143 __ASSERT_DEBUG(!iHctl, PANIC(KHCICmdQPanic, EHctlInterfaceInitialisedTwice)); |
|
144 iHctl = &aHctlInterface; |
|
145 } |
|
146 |
|
147 /** |
|
148 Sets the HCI Command Allocator interface instance to be used by the Command Queue |
|
149 Controller to create HCI command frames. |
|
150 |
|
151 @param aCommandAllocator Implementation of command allocator interface. |
|
152 */ |
|
153 EXPORT_C void CHCICmdQController::SetHCICommandAllocator(MHCICommandAllocator& aCommandAllocator) |
|
154 { |
|
155 LOG_FUNC |
|
156 __ASSERT_DEBUG(!iCommandAllocator, PANIC(KHCICmdQPanic, EHciCommandAllocatorInterfaceInitialisedTwice)); |
|
157 iCommandAllocator = &aCommandAllocator; |
|
158 } |
|
159 |
|
160 /** |
|
161 Sets the Link Mux Notifier interface instance to be used by the Command Queue |
|
162 Controller to provide co-ordinated sending to the HCTL. |
|
163 |
|
164 @param aLinkMuxer An instance implementing the interface to the Link Muxer. |
|
165 */ |
|
166 EXPORT_C void CHCICmdQController::SetLinkMuxNotifier(MLinkMuxNotifier& aLinkMuxer) |
|
167 { |
|
168 LOG_FUNC |
|
169 __ASSERT_DEBUG(!iLinkMuxer, PANIC(KHCICmdQPanic, ELinkMuxNotifierInitialisedTwice)); |
|
170 iLinkMuxer = &aLinkMuxer; |
|
171 } |
|
172 |
|
173 /** |
|
174 Sets the command queue client that will subsequently receive unmatched events. |
|
175 |
|
176 @param aUnmatchedEventObserver An instance implementing the command queue client interface. |
|
177 */ |
|
178 EXPORT_C void CHCICmdQController::SetHCIUnmatchedEventObserver(MHCICommandQueueClient& aUnmatchedEventObserver) |
|
179 { |
|
180 LOG_FUNC |
|
181 __ASSERT_DEBUG(!iUnmatchedEventObserver, PANIC(KHCICmdQPanic, EUnmatchedEventObserverInitialisedTwice)); |
|
182 iUnmatchedEventObserver = &aUnmatchedEventObserver; |
|
183 } |
|
184 |
|
185 /** |
|
186 Sets the Hard Reset Initiator interface instance to be used by the QDP. |
|
187 This will allow the QDP to request a Hard Reset. |
|
188 |
|
189 @param aHardResetInitiator An instance implementing the interface to the Hard Reset Initiator. |
|
190 */ |
|
191 EXPORT_C void CHCICmdQController::SetHardResetInitiator(MHardResetInitiator& aHardResetInitiator) |
|
192 { |
|
193 LOG_FUNC |
|
194 __ASSERT_DEBUG(!iHardResetInitiator, PANIC(KHCICmdQPanic, EHardResetInitiatorInitialisedTwice)); |
|
195 iHardResetInitiator = &aHardResetInitiator; |
|
196 iQdp->MhcqdiSetHardResetInitiator(aHardResetInitiator); |
|
197 } |
|
198 |
|
199 /** |
|
200 Sets the physical links state interface instance to be used by the QDP to query the state of |
|
201 physical links in the stack.. |
|
202 |
|
203 @param aStackInfo An instance implementing the interface to the physical links state information. |
|
204 */ |
|
205 EXPORT_C void CHCICmdQController::SetPhysicalLinksState(const MPhysicalLinksState& aStackInfo) |
|
206 { |
|
207 LOG_FUNC |
|
208 iQdp->MhcqdiSetPhysicalLinksState(aStackInfo); |
|
209 } |
|
210 |
|
211 /** |
|
212 This allows the Command Queue to start accepting commands to be queued. Whilst |
|
213 in the Initialise state only Initialisation commands will be sent but other |
|
214 commands will be added to the Normal and Priority queues so they can be sent |
|
215 when we transition to the Started state. |
|
216 @see CHCICmdQController::Start |
|
217 */ |
|
218 EXPORT_C void CHCICmdQController::Initialise() |
|
219 { |
|
220 LOG_FUNC |
|
221 |
|
222 // Can only enter the Initialising state from either the Uninitialised |
|
223 // state or the Resetting state |
|
224 switch(iCmdQControllerState) |
|
225 { |
|
226 case EUninitialised: |
|
227 iCmdQControllerState = EInitialising; |
|
228 break; |
|
229 |
|
230 case EResetting: |
|
231 iCmdQControllerState = EResetInit; |
|
232 break; |
|
233 |
|
234 default: |
|
235 PANIC(KHCICmdQPanic, EInvalidStateTransition); |
|
236 break; |
|
237 }; |
|
238 } |
|
239 |
|
240 /** |
|
241 Initiates an asynchronous request for reset processing on the command queue. |
|
242 Asynchronous reset processing is performed by CHCICmdQController::DoReset(), to |
|
243 empty the command queue and perform any other tasks on the Command Queue when |
|
244 the stack requests a sub-system reset. This will put the Command Queue into a |
|
245 state where no commands can be sent until CHCICmdQController::Initialise() is |
|
246 called. |
|
247 @see CHCICmdQController::Initialise |
|
248 */ |
|
249 EXPORT_C void CHCICmdQController::Reset() |
|
250 { |
|
251 LOG_FUNC |
|
252 |
|
253 // Mark the tails of the queues. |
|
254 StorePurgeMarks(); |
|
255 |
|
256 // Stop the queue starvation timer and reset the priority insert point. |
|
257 iQStarvationTimer->Cancel(); |
|
258 |
|
259 iCmdQControllerState = EResetting; |
|
260 |
|
261 // Cancel the sending callback if it is currently active. |
|
262 iAsyncCallBackForSend->Cancel(); |
|
263 |
|
264 // Call async CallBack to initiate Reset processing. |
|
265 iAsyncCallBackForReset->CallBack(); |
|
266 } |
|
267 |
|
268 /** |
|
269 Signals that the Stack has finished intialising the HCI, and that it is |
|
270 safe to send normal (non-initialisation) commands. |
|
271 Trying to add intialisation commands after this call will result in a panic. |
|
272 @see CHCICmdQController::Reset |
|
273 @panic EInvalidStateTransition if not in the initialising state. |
|
274 @panic EStartCalledWhenInitQNotEmpty if the method is called when the initialisation queue is not empty. |
|
275 @panic EObjectNotInitialised if not all inteface references have been populated. |
|
276 */ |
|
277 EXPORT_C void CHCICmdQController::Start() |
|
278 { |
|
279 LOG_FUNC |
|
280 |
|
281 // Can only enter the Started state from the Initialising state |
|
282 __ASSERT_ALWAYS((iCmdQControllerState == EInitialising), PANIC(KHCICmdQPanic, EInvalidStateTransition)); |
|
283 __ASSERT_ALWAYS((iInitCommandQ.IsEmpty()), PANIC(KHCICmdQPanic, EStartCalledWhenInitQNotEmpty)); |
|
284 __ASSERT_ALWAYS(iHctl && |
|
285 iCommandAllocator && |
|
286 iLinkMuxer && |
|
287 iUnmatchedEventObserver && |
|
288 iHardResetInitiator, PANIC(KHCICmdQPanic, EObjectNotInitialised)); |
|
289 |
|
290 iCmdQControllerState = EStarted; |
|
291 if(AnyCmdsToSend()) |
|
292 { |
|
293 iAsyncCallBackForSend->CallBack(); |
|
294 } |
|
295 } |
|
296 |
|
297 /** |
|
298 Called by CLinkMuxer only when the command sub-channel is open. |
|
299 This shall be as a result of this class calling MLinkMuxNotifier::TryToSend. |
|
300 Only one send should be sent during this function. If another command is suitable |
|
301 for sending then MLinkMuxNotifier::TryToSend should be called again to get |
|
302 permission to do the sending. |
|
303 */ |
|
304 EXPORT_C void CHCICmdQController::DoSend() |
|
305 { |
|
306 LOG_FUNC; |
|
307 |
|
308 LOG1(_L("CHCICmdQController::DoSend() - iSendingCommand = 0x%08x"), iSendingCommand); |
|
309 |
|
310 if(iSendingCommand) |
|
311 { |
|
312 // Format and send the command frame. Operation cannot fail. |
|
313 iSendingCommand->FormatFrame(); |
|
314 TInt err = iHctl->MhiWriteCommand(iSendingCommand->Frame().HCTLPayload()); |
|
315 LOG1(_L("CHCICmdQController::DoSend() - MhiWriteCommand, Err = %d"), err); |
|
316 |
|
317 if(err == KErrNone) |
|
318 { |
|
319 // Increment the commands sent count. |
|
320 iSendingCommand->CommandSent(); |
|
321 |
|
322 TUint consumed = iSendingCommand->Command().CreditsConsumed(); |
|
323 // Check if the vendor command has a completion event |
|
324 MHCICompletingEventQuery* completingEventInterface = NULL; |
|
325 err = iSendingCommand->Command().Extension_(KCompletingEventExpectUid, reinterpret_cast<TAny*&>(completingEventInterface), NULL); |
|
326 if( (err == KErrNone && !completingEventInterface->MhceqCompletingEventExpected()) || |
|
327 (err == KErrNotSupported && consumed == 0)) |
|
328 { |
|
329 // Certain commands (e.g. the Number of Host Complete Packets |
|
330 // command) use no credits and do not normally return any |
|
331 // event. In this case the command is not moved to the sent |
|
332 // queue to prevent it from continually growing. |
|
333 // This call will set iSendingCommand to NULL. |
|
334 DeleteCommand(iSendingCommand); |
|
335 } |
|
336 else |
|
337 { |
|
338 // Decrement command credits, move the command to the sent queue and clear the send buffer. |
|
339 __ASSERT_ALWAYS((consumed <= iCommandCredits), |
|
340 PANIC(KHCICmdQPanic, ECommandCreditsCountLessThanZero)); |
|
341 LOG2(_L("CHCICmdQController::DoSend() - iCommandCredits = %d, consumed = %d"), iCommandCredits, consumed); |
|
342 |
|
343 // If necessary start completion timer. |
|
344 TUint timeout(iQdp->MhcqdiTimeoutRequired(*iSendingCommand)); |
|
345 __ASSERT_ALWAYS((timeout <= MhcqMaxHciCommandTimeout()), |
|
346 PANIC(KHCICmdQPanic, ECommandTimeoutTooBig)); |
|
347 |
|
348 // If a timeout has been specified from the QDP start it. |
|
349 if(timeout != MHCICmdQueueDecisionInterface::KNoTimeoutRequired) |
|
350 { |
|
351 iSendingCommand->StartCompletionTimer(timeout, *this); |
|
352 } |
|
353 |
|
354 iCommandCredits -= consumed; |
|
355 iSentCommandQ.AddLast(*iSendingCommand); |
|
356 iSendingCommand = NULL; |
|
357 } |
|
358 } |
|
359 else |
|
360 { |
|
361 // Failed to write the command. Error the client. |
|
362 if(iSendingCommand->Client()) |
|
363 { |
|
364 iSendingCommand->Client()->MhcqcCommandErrored(err, &iSendingCommand->Command()); |
|
365 } |
|
366 DeleteCommand(iSendingCommand); // This will also NULL iSendingCommand |
|
367 } |
|
368 } |
|
369 // Clear the TryToSendBlock and reschedule if required. |
|
370 ClearBlock(ETryToSendBlock); |
|
371 if(AnyCmdsToSend()) |
|
372 { |
|
373 iAsyncCallBackForSend->CallBack(); |
|
374 } |
|
375 } |
|
376 |
|
377 /** |
|
378 This function is called when the timer for aUncompletedCmdId fires. |
|
379 @panic ETimedOutCommandError if aUncompletedCmdId does not exist on |
|
380 the sent queue. |
|
381 @param aUncompletedCmdId the ID of the uncompleted command. |
|
382 */ |
|
383 void CHCICmdQController::CompletionTimeoutFired(TUint aUncompletedCmdId) |
|
384 { |
|
385 LOG_FUNC |
|
386 |
|
387 CHCICommandQItem* cmd = ScanQueueByQId(iSentCommandQ, aUncompletedCmdId); |
|
388 __ASSERT_ALWAYS(cmd != NULL, PANIC(KHCICmdQPanic, ETimedOutCommandError)); |
|
389 |
|
390 ProcessCommandCompletionTimeout(cmd); |
|
391 |
|
392 if (AnyCmdsToSend()) |
|
393 { |
|
394 iAsyncCallBackForSend->CallBack(); |
|
395 } |
|
396 } |
|
397 |
|
398 /** |
|
399 @panic ECanSendDeadlock as a guard against a CanSend block deadlock in |
|
400 UDEB builds. In UREL builds, |
|
401 MHardResetInitiator::MhriStartHardReset will be called. |
|
402 @panic EResendDeadlock as a guard against a Resend block deadlock in |
|
403 UDEB builds. In UREL builds, |
|
404 MHardResetInitiator::MhriStartHardReset will be called. |
|
405 @panic EUnknownDeadlock as a guard against priority queue deadlock in |
|
406 UDEB builds, i.e. the priority queue hasn't changed at all. |
|
407 In UREL builds, MHardResetInitiator::MhriStartHardReset will be called. |
|
408 @param aNextPendingCmdId this was the ID of the next pending |
|
409 command when the timer was started. If this hasn't changed then |
|
410 panic / ResetCycle as above. |
|
411 */ |
|
412 void CHCICmdQController::StarvationTimeoutFired(TUint __DEBUG_ONLY(aNextPendingCmdId)) |
|
413 { |
|
414 LOG_FUNC |
|
415 |
|
416 #ifdef _DEBUG |
|
417 // Check for possible dead-lock situation that can occur. |
|
418 TDblQue<CHCICommandQItem>* queue = QueueSendingNext(); |
|
419 |
|
420 if (queue) |
|
421 { |
|
422 if (queue != &iPriorityCommandQ) |
|
423 { |
|
424 CHCICommandQItem* cmd = queue->First(); |
|
425 |
|
426 if (cmd->CommandQId() == aNextPendingCmdId) |
|
427 { |
|
428 if (Blocked(ECanSendBlock, ENoBlocks)) |
|
429 { |
|
430 PANIC(KHCICmdQPanic, ECanSendDeadlock); |
|
431 } |
|
432 else if (Blocked(EResendBlock, ENoBlocks)) |
|
433 { |
|
434 PANIC(KHCICmdQPanic, EResendDeadlock); |
|
435 } |
|
436 } |
|
437 } |
|
438 else if (aNextPendingCmdId == KInvalidCommandQueueItemId) |
|
439 { |
|
440 // This is priority queue, so we can't point to a single culprit. |
|
441 // Apparently none of the commands on the queue could be sent. |
|
442 PANIC(KHCICmdQPanic, EUnknownDeadlock); |
|
443 } |
|
444 } |
|
445 |
|
446 // Always assert in debug builds for starvation timer conditions. |
|
447 // This may be removed when more is known about these scenarios. |
|
448 __ASSERT_DEBUG(EFalse, PANIC(KHCICmdQPanic, EStarvationTimerFired)); |
|
449 #endif |
|
450 |
|
451 // Force possibly stale caching blocks to be recomputed. |
|
452 ClearBlock(ECachingCommandBlocks); |
|
453 |
|
454 iHardResetInitiator->MhriStartHardReset(); |
|
455 } |
|
456 |
|
457 /** |
|
458 Virtuals from MHCICommandQueue |
|
459 */ |
|
460 |
|
461 /*virtual*/ TUint CHCICmdQController::MhcqAddCommandL(CHCICommandQItem* aQueItem) |
|
462 { |
|
463 LOG_FUNC |
|
464 __ASSERT_ALWAYS(aQueItem != NULL, PANIC(KHCICmdQPanic, EAttemptToAddNullCommand)); |
|
465 |
|
466 return DoAddCommandL(*aQueItem, iNormalCommandQ, EStarted); |
|
467 } |
|
468 |
|
469 /*virtual*/ TUint CHCICmdQController::MhcqAddCommandL(CHCICommandBase* aCommandData, MHCICommandQueueClient& aCmdProgressRecipient) |
|
470 { |
|
471 LOG_FUNC |
|
472 __ASSERT_ALWAYS(aCommandData != NULL, PANIC(KHCICmdQPanic, EAttemptToAddNullCommand)); |
|
473 __ASSERT_ALWAYS(iCommandAllocator != NULL, PANIC(KHCICmdQPanic, EObjectNotInitialised)); |
|
474 |
|
475 CHCICommandQItem* aQueItem = CHCICommandQItem::NewL(*iCommandAllocator, |
|
476 aCmdProgressRecipient, |
|
477 aCommandData); |
|
478 return MhcqAddCommandL(aQueItem); |
|
479 } |
|
480 |
|
481 /*virtual*/ TUint CHCICmdQController::MhcqAddPriorityCommandL(CHCICommandQItem* aQueItem) |
|
482 { |
|
483 LOG_FUNC |
|
484 __ASSERT_ALWAYS(aQueItem != NULL, PANIC(KHCICmdQPanic, EAttemptToAddNullCommand)); |
|
485 |
|
486 return DoAddCommandL(*aQueItem, iPriorityCommandQ, EStarted); |
|
487 } |
|
488 |
|
489 /*virtual*/ TUint CHCICmdQController::MhcqAddPriorityCommandL(CHCICommandBase* aCommandData, MHCICommandQueueClient& aCmdProgressRecipient) |
|
490 { |
|
491 LOG_FUNC |
|
492 __ASSERT_ALWAYS(aCommandData != NULL, PANIC(KHCICmdQPanic, EAttemptToAddNullCommand)); |
|
493 __ASSERT_ALWAYS(iCommandAllocator != NULL, PANIC(KHCICmdQPanic, EObjectNotInitialised)); |
|
494 |
|
495 CHCICommandQItem* aQueItem = CHCICommandQItem::NewL(*iCommandAllocator, |
|
496 aCmdProgressRecipient, |
|
497 aCommandData); |
|
498 return MhcqAddPriorityCommandL(aQueItem); |
|
499 } |
|
500 |
|
501 /*virtual*/ TUint CHCICmdQController::MhcqAddInitCommandL(CHCICommandQItem* aQueItem) |
|
502 { |
|
503 LOG_FUNC |
|
504 __ASSERT_ALWAYS(aQueItem != NULL, PANIC(KHCICmdQPanic, EAttemptToAddNullCommand)); |
|
505 __ASSERT_ALWAYS((iCmdQControllerState != EStarted), PANIC(KHCICmdQPanic, EInitCmdAfterStart)); |
|
506 |
|
507 TUint ret = DoAddCommandL(*aQueItem, iInitCommandQ, EInitialising); |
|
508 // Mark as an Initialisation command. |
|
509 aQueItem->SetInitialisationCmd(); |
|
510 return ret; |
|
511 } |
|
512 |
|
513 /*virtual*/ TUint CHCICmdQController::MhcqAddInitCommandL(CHCICommandBase* aCommandData, MHCICommandQueueClient& aCmdProgressRecipient) |
|
514 { |
|
515 LOG_FUNC |
|
516 __ASSERT_ALWAYS(aCommandData != NULL, PANIC(KHCICmdQPanic, EAttemptToAddNullCommand)); |
|
517 __ASSERT_ALWAYS(iCommandAllocator != NULL, PANIC(KHCICmdQPanic, EObjectNotInitialised)); |
|
518 |
|
519 CHCICommandQItem* aQueItem = CHCICommandQItem::NewL(*iCommandAllocator, |
|
520 aCmdProgressRecipient, |
|
521 aCommandData); |
|
522 return MhcqAddInitCommandL(aQueItem); |
|
523 } |
|
524 |
|
525 /*virtual*/ TInt CHCICmdQController::MhcqRemoveCommand(TUint aCommandId, |
|
526 const MHCICommandQueueClient& aCmdOriginator) |
|
527 { |
|
528 LOG_FUNC |
|
529 |
|
530 CHCICommandQItem* pendingCmd = NULL; |
|
531 CHCICommandQItem* lockedCmd = NULL; |
|
532 TInt retVal = KErrNotFound; |
|
533 |
|
534 pendingCmd = ScanQueueByQId(iNormalCommandQ, aCommandId); |
|
535 if(!pendingCmd) |
|
536 { |
|
537 pendingCmd = ScanQueueByQId(iInitCommandQ, aCommandId); |
|
538 if (!pendingCmd) |
|
539 { |
|
540 pendingCmd = ScanQueueByQId(iPriorityCommandQ, aCommandId); |
|
541 if (!pendingCmd) |
|
542 { |
|
543 pendingCmd = ScanQueueByQId(iWorkaroundCommandQ, aCommandId); |
|
544 if(!pendingCmd) |
|
545 { |
|
546 lockedCmd = ScanQueueByQId(iSentCommandQ, aCommandId); |
|
547 if(!lockedCmd) |
|
548 { |
|
549 lockedCmd = ScanQueueByQId(iResendCommandQ, aCommandId); |
|
550 } |
|
551 } |
|
552 } |
|
553 } |
|
554 } |
|
555 |
|
556 if(lockedCmd) |
|
557 { |
|
558 __ASSERT_ALWAYS(lockedCmd->Client() == &aCmdOriginator, PANIC(KHCICmdQPanic, ETryingToDeleteCommandNotOwned)); |
|
559 retVal = KErrLocked; |
|
560 } |
|
561 else if(pendingCmd && pendingCmd->IsParent()) |
|
562 { |
|
563 __ASSERT_ALWAYS(pendingCmd->Client() == &aCmdOriginator, PANIC(KHCICmdQPanic, ETryingToDeleteCommandNotOwned)); |
|
564 retVal = KErrLocked; |
|
565 } |
|
566 else if(pendingCmd) |
|
567 { |
|
568 __ASSERT_ALWAYS(pendingCmd->Client() == &aCmdOriginator, PANIC(KHCICmdQPanic, ETryingToDeleteCommandNotOwned)); |
|
569 __ASSERT_ALWAYS(!pendingCmd->IsPreChild(), PANIC(KHCICmdQPanic, ETryingToDeletePreChildCommandById)); |
|
570 __ASSERT_ALWAYS(!pendingCmd->IsPostChild(), PANIC(KHCICmdQPanic, ETryingToDeletePostChildCommandById)); |
|
571 DeleteCommand(pendingCmd); |
|
572 retVal = KErrNone; |
|
573 } |
|
574 |
|
575 return retVal; |
|
576 } |
|
577 |
|
578 void CHCICmdQController::HandleCommandRemoval(CHCICommandQItem*& aCmd, TBool aCanDelete) |
|
579 { |
|
580 LOG_FUNC |
|
581 ASSERT_DEBUG(aCmd); |
|
582 if(aCanDelete && !aCmd->IsParent()) |
|
583 { |
|
584 // The client should never be the originator of child commands |
|
585 __ASSERT_ALWAYS(!aCmd->IsPreChild(), PANIC(KHCICmdQPanic, ETryingToDeletePreChildCommand)); |
|
586 __ASSERT_ALWAYS(!aCmd->IsPostChild(), PANIC(KHCICmdQPanic, ETryingToDeletePostChildCommand)); |
|
587 DeleteCommand(aCmd); |
|
588 } |
|
589 else |
|
590 { |
|
591 aCmd->DetachClient(); |
|
592 } |
|
593 } |
|
594 |
|
595 void CHCICmdQController::HandleCommandRemoval(CHCICommandQItem& aCmd, TBool aCanDelete) |
|
596 { |
|
597 LOG_FUNC |
|
598 CHCICommandQItem* cmd = &aCmd; |
|
599 HandleCommandRemoval(cmd, aCanDelete); |
|
600 } |
|
601 |
|
602 void CHCICmdQController::RemoveAllCommands(TDblQueIter<CHCICommandQItem> aIter, |
|
603 const MHCICommandQueueClient& aCmdOriginator, |
|
604 TBool aCanDelete) |
|
605 { |
|
606 LOG_FUNC |
|
607 |
|
608 for (CHCICommandQItem* item(aIter++);item != NULL;item = aIter++) |
|
609 { |
|
610 if(item->Client() == &aCmdOriginator) |
|
611 { |
|
612 HandleCommandRemoval(*item, aCanDelete); |
|
613 } |
|
614 } |
|
615 } |
|
616 |
|
617 /*virtual*/ void CHCICmdQController::MhcqRemoveAllCommands(const MHCICommandQueueClient& aCmdOriginator) |
|
618 { |
|
619 LOG_FUNC |
|
620 |
|
621 TDblQueIter<CHCICommandQItem> initIter(iInitCommandQ); |
|
622 RemoveAllCommands(initIter, aCmdOriginator, ETrue); |
|
623 |
|
624 TDblQueIter<CHCICommandQItem> normalIter(iNormalCommandQ); |
|
625 RemoveAllCommands(normalIter, aCmdOriginator, ETrue); |
|
626 |
|
627 TDblQueIter<CHCICommandQItem> priorityIter(iPriorityCommandQ); |
|
628 RemoveAllCommands(priorityIter, aCmdOriginator, ETrue); |
|
629 |
|
630 TDblQueIter<CHCICommandQItem> workaroundIter(iWorkaroundCommandQ); |
|
631 RemoveAllCommands(workaroundIter, aCmdOriginator, ETrue); |
|
632 |
|
633 TDblQueIter<CHCICommandQItem> sentIter(iSentCommandQ); |
|
634 RemoveAllCommands(sentIter, aCmdOriginator, EFalse); |
|
635 |
|
636 TDblQueIter<CHCICommandQItem> resendIter(iResendCommandQ); |
|
637 RemoveAllCommands(resendIter, aCmdOriginator, EFalse); |
|
638 |
|
639 if(iSendingCommand) |
|
640 { |
|
641 if(iSendingCommand->Client() == &aCmdOriginator) |
|
642 { |
|
643 HandleCommandRemoval(iSendingCommand, ETrue); |
|
644 } |
|
645 } |
|
646 } |
|
647 |
|
648 void CHCICmdQController::CleanUpQueue(TDblQueIter<CHCICommandQItem> aIter) |
|
649 { |
|
650 LOG_FUNC |
|
651 |
|
652 for (CHCICommandQItem* item(aIter++);item != NULL;item = aIter++) |
|
653 { |
|
654 DeleteCommand(item); |
|
655 } |
|
656 } |
|
657 |
|
658 /*virtual*/ TUint CHCICmdQController::MhcqMaxHciCommandTimeout() const |
|
659 { |
|
660 LOG_FUNC |
|
661 |
|
662 return iMaxHciCommandTimeout; |
|
663 } |
|
664 |
|
665 /*virtual*/ TAny* CHCICmdQController::MhcqQdpPluginInterface(TUid aUid) const |
|
666 { |
|
667 LOG_FUNC |
|
668 |
|
669 __ASSERT_ALWAYS((aUid != TUid::Uid(KHCICmdQueueDecisionInterfaceUid)) |
|
670 && (aUid != TUid::Uid(KHCICmdQueueDecisionEventModifierInterfaceUid)), |
|
671 PANIC(KHCICmdQPanic, EIllegalRequestForQdpInterface)); |
|
672 return (iQdpPlugin) ? iQdpPlugin->Interface(aUid) : NULL; |
|
673 } |
|
674 |
|
675 /** |
|
676 Virtuals from MHCICommandEventObserver |
|
677 */ |
|
678 |
|
679 /*virtual*/ void CHCICmdQController::MhceoEventNotification(const THCIEventBase& aEvent) |
|
680 { |
|
681 LOG_FUNC |
|
682 |
|
683 ProcessEvent(aEvent, ETrue); |
|
684 } |
|
685 |
|
686 void CHCICmdQController::ProcessEvent(const THCIEventBase& aEvent, TBool aSendToQdp) |
|
687 { |
|
688 LOG_FUNC |
|
689 |
|
690 // Process command credits. |
|
691 THCIEventCode eventCode(aEvent.EventCode()); |
|
692 if (eventCode == ECommandCompleteEvent) |
|
693 { |
|
694 |
|
695 const THCICommandCompleteEvent& event(THCICommandCompleteEvent::Cast(aEvent)); |
|
696 UpdateCommandCredits(event.NumHCICommandPackets()); |
|
697 |
|
698 if (event.CommandOpcode() == KNopOpcode) |
|
699 { |
|
700 // This is a special command complete event that does not |
|
701 // complete an event. Command_Opcode, 0x0000 is a NOP, and |
|
702 // can be used to change the number of outstanding HCI |
|
703 // command packets that the Host can send before waiting. |
|
704 |
|
705 // Nothing else to do for this event, so throw it away |
|
706 return; |
|
707 } |
|
708 } |
|
709 else if (eventCode == ECommandStatusEvent) |
|
710 { |
|
711 TCommandStatusEvent& event = TCommandStatusEvent::Cast(aEvent); |
|
712 UpdateCommandCredits(event.NumHCICommandPackets()); |
|
713 |
|
714 if (event.CommandOpcode() == KNopOpcode) |
|
715 { |
|
716 // Same as above for NOP command complete event. |
|
717 |
|
718 // Nothing else to do for this event, so throw it away |
|
719 return; |
|
720 } |
|
721 } |
|
722 |
|
723 // Iterate through sent queue to try and match the event to a sent command. |
|
724 CHCICommandQItem* cmd(0); |
|
725 TDblQueIter<CHCICommandQItem> qIter(iSentCommandQ); |
|
726 TBool matchesCmd(EFalse); |
|
727 TBool concludesCmd(EFalse); |
|
728 TBool continueMatching(EFalse); |
|
729 TBool eventMatched(EFalse); |
|
730 |
|
731 while ((qIter != NULL) && (!eventMatched || continueMatching)) |
|
732 { |
|
733 cmd = qIter++; |
|
734 cmd->Command().Match(aEvent, matchesCmd, concludesCmd, continueMatching); |
|
735 if (matchesCmd) |
|
736 { |
|
737 eventMatched = ETrue; |
|
738 |
|
739 // If the event concludes the command then remove it from the sent queue |
|
740 if (concludesCmd) |
|
741 { |
|
742 cmd->iLink.Deque(); |
|
743 } |
|
744 |
|
745 if (aSendToQdp) |
|
746 { |
|
747 // Notify QDP. |
|
748 if (iQdpEventModifier) |
|
749 { |
|
750 THCIEventCode origEventCode = aEvent.EventCode(); |
|
751 iQdpEventModifier->MhcqemiMatchedEventReceived(const_cast<THCIEventBase&>(aEvent), *cmd); |
|
752 __ASSERT_ALWAYS(origEventCode == aEvent.EventCode(), PANIC(KHCICmdQPanic, EQdpTryingToChangeEventCode)); |
|
753 } |
|
754 else |
|
755 { |
|
756 iQdp->MhcqdiMatchedEventReceived(aEvent, *cmd); |
|
757 } |
|
758 } |
|
759 |
|
760 // Process the matched event. |
|
761 if (aEvent.ErrorCode() == EOK) |
|
762 { |
|
763 ProcessMatchedEvent(aEvent, cmd, concludesCmd); |
|
764 } |
|
765 else |
|
766 { |
|
767 ProcessMatchedErrorEvent(aEvent, cmd, concludesCmd, aSendToQdp); |
|
768 } |
|
769 } |
|
770 } |
|
771 |
|
772 if (!eventMatched) |
|
773 { |
|
774 // Process the unmatched event. |
|
775 ProcessUnmatchedEvent(aEvent, aSendToQdp); |
|
776 } |
|
777 |
|
778 // Reschedule if there are commands queued. |
|
779 if (AnyCmdsToSend()) |
|
780 { |
|
781 iAsyncCallBackForSend->CallBack(); |
|
782 } |
|
783 |
|
784 } |
|
785 |
|
786 void CHCICmdQController::MhcquiInjectEvent(const THCIEventBase& aEvent) |
|
787 { |
|
788 LOG_FUNC |
|
789 |
|
790 ProcessEvent(aEvent, EFalse); |
|
791 } |
|
792 |
|
793 CHCICommandQItem* CHCICmdQController::MhcquiFindOutstandingCommand(THCIOpcode aOpcode) |
|
794 { |
|
795 LOG_FUNC |
|
796 |
|
797 return ScanQueueByOpcode(iSentCommandQ, aOpcode); |
|
798 } |
|
799 |
|
800 |
|
801 /** |
|
802 Constructor. |
|
803 */ |
|
804 CHCICmdQController::CHCICmdQController() |
|
805 : iMaxHciCommandTimeout(KDefaultMaxHciCommandTimeout), |
|
806 iQueueStarvationTimeout(2 * iMaxHciCommandTimeout), |
|
807 iInitCommandQ(_FOFF(CHCICommandQItem,iLink)), |
|
808 iNormalCommandQ(_FOFF(CHCICommandQItem,iLink)), |
|
809 iPriorityCommandQ(_FOFF(CHCICommandQItem,iLink)), |
|
810 iWorkaroundCommandQ(_FOFF(CHCICommandQItem,iLink)), |
|
811 iSentCommandQ(_FOFF(CHCICommandQItem,iLink)), |
|
812 iResendCommandQ(_FOFF(CHCICommandQItem,iLink)), |
|
813 iCmdQControllerState(EUninitialised), |
|
814 iNextCommandQItemId(1) |
|
815 |
|
816 { |
|
817 CONNECT_LOGGER |
|
818 LOG_FUNC |
|
819 } |
|
820 |
|
821 void CHCICmdQController::ConstructL() |
|
822 { |
|
823 LOG_FUNC |
|
824 |
|
825 // Add Async Callbacks |
|
826 TCallBack resetCallBack(AsyncCallBackForReset, this); |
|
827 |
|
828 iAsyncCallBackForReset = |
|
829 new (ELeave)CAsyncCallBack(resetCallBack, CActive::EPriorityStandard); |
|
830 |
|
831 TCallBack startCallBack(AsyncCallBackForSend, this); |
|
832 |
|
833 iAsyncCallBackForSend = |
|
834 new (ELeave)CAsyncCallBack(startCallBack, CActive::EPriorityStandard); |
|
835 |
|
836 iQStarvationTimer = CHCICmdQStarvationTimer::NewL(); |
|
837 |
|
838 // Create HCI Utility library |
|
839 iHciUtil = CHciUtil::NewL(KHciCommandQueueComponentName); |
|
840 |
|
841 // If we can't open the ini file then this will be treated in the same way |
|
842 // as not reading a valid UID from the ini file. |
|
843 TRAP_IGNORE(iHciUtil->OpenIniFileL()); |
|
844 |
|
845 _LIT(KSection, "QDP"); |
|
846 _LIT(KQdpUidTag, "EcomUid"); |
|
847 |
|
848 TUid qdpImplUid = TUid::Null(); |
|
849 TRAPD(err, qdpImplUid = iHciUtil->GetUidFromFileL(KSection, KQdpUidTag)); |
|
850 |
|
851 if (err == KErrNone) |
|
852 { |
|
853 // Valid UID found, load it |
|
854 iQdpPlugin = CHCICmdQueueDecisionPlugin::NewL(qdpImplUid); |
|
855 } |
|
856 else |
|
857 { |
|
858 // No UID found in ini file, attempt to load single instance of |
|
859 // implementation |
|
860 iQdpPlugin = CHCICmdQueueDecisionPlugin::NewL(); |
|
861 } |
|
862 |
|
863 iQdp = reinterpret_cast<MHCICmdQueueDecisionInterface*>(iQdpPlugin->Interface(TUid::Uid(KHCICmdQueueDecisionInterfaceUid))); |
|
864 iQdpEventModifier = reinterpret_cast<MHCICmdQueueEventModifierInterface*>(iQdpPlugin->Interface(TUid::Uid(KHCICmdQueueDecisionEventModifierInterfaceUid))); |
|
865 |
|
866 MHCICmdQueueUtilityUser* qdpUtilityUser = reinterpret_cast<MHCICmdQueueUtilityUser*>(iQdpPlugin->Interface(TUid::Uid(KHCICmdQueueUtilityUserUid))); |
|
867 |
|
868 if (qdpUtilityUser) |
|
869 { |
|
870 qdpUtilityUser->MhcquuSetUtilitiesProvider(*this); |
|
871 } |
|
872 |
|
873 // Read timeout values |
|
874 _LIT(KMaxHciCommandTimeoutTag, "MaxHciCommandTimeout"); |
|
875 _LIT(KQueueStarvationTimeoutTag, "QueueStarvationTimeout"); |
|
876 |
|
877 // Don't worry if we cannot read timeouts from file, already initialised |
|
878 // to default values |
|
879 TRAP_IGNORE(iMaxHciCommandTimeout = iHciUtil->GetValueFromFileL(KSection, KMaxHciCommandTimeoutTag)); |
|
880 TRAP_IGNORE(iQueueStarvationTimeout = iHciUtil->GetValueFromFileL(KSection, KQueueStarvationTimeoutTag)); |
|
881 |
|
882 __ASSERT_DEBUG(iMaxHciCommandTimeout < iQueueStarvationTimeout, PANIC(KHCICmdQPanic, EQStarvationTimerNotGreaterThanMaxCmdTimer)); |
|
883 iQdp->MhcqdiSetTimeouts(iQueueStarvationTimeout, iMaxHciCommandTimeout); |
|
884 iQdp->MhcqdiSetHCICommandQueue(*this); |
|
885 |
|
886 // Reset the QDP. |
|
887 iCommandCredits = iQdp->MhcqdiReset(); |
|
888 } |
|
889 |
|
890 /** |
|
891 Tests for the specified TQueueStateBits block(s) being set. |
|
892 */ |
|
893 inline TBool CHCICmdQController::Blocked(TUint aBlocksToCheck, TUint aBlocksToBypass) |
|
894 { |
|
895 return Blocked(iCommandQState, aBlocksToCheck, aBlocksToBypass); |
|
896 } |
|
897 |
|
898 /** |
|
899 Tests for the specified TQueueStateBits block(s) being set. |
|
900 */ |
|
901 inline TBool CHCICmdQController::Blocked(TUint aBlockStatus, TUint aBlocksToCheck, TUint aBlocksToBypass) |
|
902 { |
|
903 return (aBlockStatus & (ValidBlocks(aBlocksToCheck) & ValidBlocks(~aBlocksToBypass))); |
|
904 } |
|
905 |
|
906 /** |
|
907 Clears the specified TQueueStateBits block(s). |
|
908 */ |
|
909 inline void CHCICmdQController::ClearBlock(TUint aBlocks) |
|
910 { |
|
911 __ASSERT_DEBUG(!InvalidBlocks(aBlocks), PANIC(KHCICmdQPanic, EIllegalBlock)); |
|
912 iCommandQState &= ValidBlocks(~aBlocks); |
|
913 } |
|
914 |
|
915 /** |
|
916 Sets the specified TQueueStateBits block(s). |
|
917 */ |
|
918 inline void CHCICmdQController::SetBlock(TUint aBlocks) |
|
919 { |
|
920 __ASSERT_DEBUG(!InvalidBlocks(aBlocks), PANIC(KHCICmdQPanic, EIllegalBlock)); |
|
921 iCommandQState |= ValidBlocks(aBlocks); |
|
922 } |
|
923 |
|
924 /** |
|
925 Masks out invalid TQueueStateBits block bit(s). |
|
926 */ |
|
927 inline TUint CHCICmdQController::ValidBlocks(TUint aBlocks) |
|
928 { |
|
929 return (aBlocks & EAllBlocks); |
|
930 } |
|
931 |
|
932 /** |
|
933 Masks out valid TQueueStateBits block bit(s). |
|
934 */ |
|
935 inline TUint CHCICmdQController::InvalidBlocks(TUint aBlocks) |
|
936 { |
|
937 return (aBlocks & ~EAllBlocks); |
|
938 } |
|
939 |
|
940 /** |
|
941 Check for queue block bypass conditions. Currently the only such condition |
|
942 is to allow a non-credit consuming command (e.g. the Number of Host Complete |
|
943 Packets command) to bypass the insufficient credits block. |
|
944 */ |
|
945 inline TUint CHCICmdQController::CmdBypassBlocks(const CHCICommandQItem& aCmd) |
|
946 { |
|
947 TUint bypassBlocks = ENoBlocks; |
|
948 if (aCmd.Command().CreditsConsumed() == 0) |
|
949 { |
|
950 bypassBlocks |= EInsufficientCreditBlock; |
|
951 } |
|
952 return bypassBlocks; |
|
953 } |
|
954 |
|
955 /** |
|
956 Updates the command credits counter and maintains the EInsufficientCreditBlock |
|
957 */ |
|
958 inline void CHCICmdQController::UpdateCommandCredits(TUint8 aCommandCredits) |
|
959 { |
|
960 iCommandCredits = aCommandCredits; |
|
961 |
|
962 if (iCommandCredits > 0) |
|
963 { |
|
964 // We clear block flags when sending priority commands, so there may be some |
|
965 // commands blocked on insufficient credits, but the block flag not set |
|
966 // anymore. Hence we want to schedule the callback irrespectably of the status |
|
967 // of EInsufficientCreditBlock. |
|
968 if (AnyCmdsToSend()) |
|
969 { |
|
970 iAsyncCallBackForSend->CallBack(); |
|
971 } |
|
972 ClearBlock(EInsufficientCreditBlock); |
|
973 } |
|
974 else |
|
975 { |
|
976 SetBlock(EInsufficientCreditBlock); |
|
977 } |
|
978 } |
|
979 |
|
980 /** |
|
981 Selects the active command queue (iNormalCommandQ or iInitCommandQ) based on |
|
982 current state. |
|
983 */ |
|
984 inline TDblQue<CHCICommandQItem>* CHCICmdQController::ActiveRegularQueue() |
|
985 { |
|
986 if (iCmdQControllerState == EStarted) |
|
987 { |
|
988 return &iNormalCommandQ; |
|
989 } |
|
990 else if ((iCmdQControllerState == EResetInit) || (iCmdQControllerState == EInitialising)) |
|
991 { |
|
992 return &iInitCommandQ; |
|
993 } |
|
994 |
|
995 return NULL; |
|
996 } |
|
997 |
|
998 /** |
|
999 Returns the pointer to the queue which contains the command that would be scheduled next. |
|
1000 Does not take the Resend queue into account. |
|
1001 */ |
|
1002 TDblQue<CHCICommandQItem>* CHCICmdQController::QueueSendingNext() |
|
1003 { |
|
1004 if (!iWorkaroundCommandQ.IsEmpty()) |
|
1005 { |
|
1006 // Workaround has highest priority (well, second to resend which we don't care about). |
|
1007 return &iWorkaroundCommandQ; |
|
1008 } |
|
1009 else if ((iCmdQControllerState == EStarted) && !iPriorityCommandQ.IsEmpty()) |
|
1010 { |
|
1011 // Priority comes after Workaround, but we look at it only in EStarted. |
|
1012 return &iPriorityCommandQ; |
|
1013 } |
|
1014 else |
|
1015 { |
|
1016 // Normal or Initialisation or NULL, depending on the state we're in. |
|
1017 return ActiveRegularQueue(); |
|
1018 } |
|
1019 } |
|
1020 |
|
1021 /** |
|
1022 Returns the item at the head of the specified queue, or NULL if the queue is empty. |
|
1023 */ |
|
1024 inline CHCICommandQItem* CHCICmdQController::FirstQueueItem(TDblQue<CHCICommandQItem>& aQueue) |
|
1025 { |
|
1026 return (aQueue.IsEmpty() ? NULL : aQueue.First()); |
|
1027 } |
|
1028 |
|
1029 /** |
|
1030 Returns the item at the tail of the specified queue, or NULL if the queue is empty. |
|
1031 */ |
|
1032 inline CHCICommandQItem* CHCICmdQController::LastQueueItem(TDblQue<CHCICommandQItem>& aQueue) |
|
1033 { |
|
1034 return (aQueue.IsEmpty() ? NULL : aQueue.Last()); |
|
1035 } |
|
1036 |
|
1037 /** |
|
1038 Saves the purge marks for the queues that need them. When a reset is performed, |
|
1039 the queues will be purged up to those marks. |
|
1040 */ |
|
1041 void CHCICmdQController::StorePurgeMarks() |
|
1042 { |
|
1043 iInitQPurgeMark = LastQueueItem(iInitCommandQ); |
|
1044 iNormalQPurgeMark = LastQueueItem(iNormalCommandQ); |
|
1045 iPriorityQPurgeMark = LastQueueItem(iPriorityCommandQ); |
|
1046 iWorkaroundQPurgeMark = LastQueueItem(iWorkaroundCommandQ); |
|
1047 } |
|
1048 |
|
1049 /** |
|
1050 Purge the queues up to previously saved purge marks. |
|
1051 */ |
|
1052 void CHCICmdQController::PurgeAllQueues() |
|
1053 { |
|
1054 // Purge the queues to the recorded tail markers. |
|
1055 PurgeQueue(iInitCommandQ, iInitQPurgeMark); |
|
1056 PurgeQueue(iNormalCommandQ, iNormalQPurgeMark); |
|
1057 PurgeQueue(iPriorityCommandQ, iPriorityQPurgeMark); |
|
1058 PurgeQueue(iWorkaroundCommandQ, iWorkaroundQPurgeMark); |
|
1059 |
|
1060 iInitQPurgeMark = iNormalQPurgeMark = iPriorityQPurgeMark = iWorkaroundQPurgeMark = NULL; |
|
1061 |
|
1062 // Purge the sent and resend queues entirely. |
|
1063 PurgeQueue(iSentCommandQ, LastQueueItem(iSentCommandQ)); |
|
1064 PurgeQueue(iResendCommandQ, LastQueueItem(iResendCommandQ)); |
|
1065 } |
|
1066 |
|
1067 |
|
1068 /** |
|
1069 Removes and deletes items from the head of the queue up to and including the marked |
|
1070 item. |
|
1071 */ |
|
1072 void CHCICmdQController::PurgeQueue(TDblQue<CHCICommandQItem>& aQueue, |
|
1073 CHCICommandQItem* aMark) |
|
1074 { |
|
1075 LOG_FUNC |
|
1076 |
|
1077 // A null aMark implies that there are no commands to purge. |
|
1078 if(aMark) |
|
1079 { |
|
1080 TDblQueIter<CHCICommandQItem> qIter(aQueue); |
|
1081 TBool found = EFalse; |
|
1082 |
|
1083 // Ensure that the mark is in the queue. |
|
1084 while(qIter && !found) |
|
1085 { |
|
1086 if(aMark == qIter++) |
|
1087 { |
|
1088 found = ETrue; |
|
1089 } |
|
1090 } |
|
1091 |
|
1092 // If the mark was found remove comands upto and including |
|
1093 // the mark. |
|
1094 if(found) |
|
1095 { |
|
1096 // Reset the queue iterator. |
|
1097 qIter.SetToFirst(); |
|
1098 found = EFalse; |
|
1099 CHCICommandQItem* item = NULL; |
|
1100 |
|
1101 while(qIter && !found) |
|
1102 { |
|
1103 item = qIter++; |
|
1104 if(item == aMark) |
|
1105 { |
|
1106 // Mark found. Exit the loop after this element has been removed. |
|
1107 found = ETrue; |
|
1108 } |
|
1109 DeleteCommand(item); |
|
1110 } |
|
1111 } |
|
1112 } |
|
1113 } |
|
1114 |
|
1115 /** |
|
1116 Returns the first item with the specified opcode which is closest to the head of the |
|
1117 specified queue, or NULL if there is no such opcode on the queue. |
|
1118 */ |
|
1119 inline CHCICommandQItem* CHCICmdQController::ScanQueueByOpcode(TDblQue<CHCICommandQItem>& aQueue, THCIOpcode aOpcode) |
|
1120 { |
|
1121 LOG_FUNC |
|
1122 |
|
1123 CHCICommandQItem* item = NULL; |
|
1124 TDblQueIter<CHCICommandQItem> qIter(aQueue); |
|
1125 |
|
1126 while (((item = qIter) != NULL) && (item->Command().Opcode() != aOpcode)) |
|
1127 { |
|
1128 qIter++; |
|
1129 } |
|
1130 |
|
1131 return item; |
|
1132 } |
|
1133 |
|
1134 /** |
|
1135 Returns the item with the specified command queue ID from the specified queue, or NULL |
|
1136 if there is no such command on the queue. |
|
1137 */ |
|
1138 inline CHCICommandQItem* CHCICmdQController::ScanQueueByQId(TDblQue<CHCICommandQItem>& aQueue, TUint aCmdId) |
|
1139 { |
|
1140 LOG_FUNC |
|
1141 |
|
1142 CHCICommandQItem* item = NULL; |
|
1143 TDblQueIter<CHCICommandQItem> qIter(aQueue); |
|
1144 |
|
1145 while (((item = qIter) != NULL) && (item->CommandQId() != aCmdId)) |
|
1146 { |
|
1147 qIter++; |
|
1148 } |
|
1149 |
|
1150 return item; |
|
1151 } |
|
1152 |
|
1153 /** |
|
1154 Performs command queue EResetting state processing. EResetting state is |
|
1155 reached by a call to CHCICmdQController::Reset(). |
|
1156 */ |
|
1157 void CHCICmdQController::DoReset() |
|
1158 { |
|
1159 LOG_FUNC |
|
1160 |
|
1161 // Should only be reached if Reset has been called |
|
1162 __ASSERT_ALWAYS((iCmdQControllerState == EResetInit) || |
|
1163 (iCmdQControllerState == EResetting), |
|
1164 PANIC(KHCICmdQPanic, EInvalidStateTransition)); |
|
1165 |
|
1166 // Delete the parent command of possibly ongoing workaround. |
|
1167 // Wee need to do it here separately, because the command |
|
1168 // is not reachable any other way if it's in post-workaround phase |
|
1169 // - it's already been removed from its queue. |
|
1170 DeleteCommand(iParentCommand); |
|
1171 |
|
1172 PurgeAllQueues(); |
|
1173 |
|
1174 // Reset the QDP. |
|
1175 iCommandCredits = iQdp->MhcqdiReset(); |
|
1176 |
|
1177 // Delete any command waiting to be sent. |
|
1178 DeleteCommand(iSendingCommand); |
|
1179 |
|
1180 // Clear the block flags and execute state transition. |
|
1181 iCommandQState = 0; |
|
1182 |
|
1183 // Initialise could have been called during async break so check which |
|
1184 // state we should move into |
|
1185 if (iCmdQControllerState == EResetInit) |
|
1186 { |
|
1187 iCmdQControllerState = EInitialising; |
|
1188 } |
|
1189 else |
|
1190 { |
|
1191 // Acording to the ASSERT this must be EResetting |
|
1192 iCmdQControllerState = EUninitialised; |
|
1193 } |
|
1194 |
|
1195 // Reschedule if there are commands queued and we're ready to send. |
|
1196 if ((iCmdQControllerState == EInitialising) && CmdsQueued(&iInitCommandQ)) |
|
1197 { |
|
1198 iAsyncCallBackForSend->CallBack(); |
|
1199 } |
|
1200 } |
|
1201 |
|
1202 /** |
|
1203 The main queue processing routine. Schedules the next command and asks |
|
1204 HCTL to send. |
|
1205 */ |
|
1206 void CHCICmdQController::TryToSend() |
|
1207 { |
|
1208 LOG_FUNC |
|
1209 |
|
1210 __ASSERT_DEBUG((iCmdQControllerState == EInitialising) || (iCmdQControllerState == EStarted), |
|
1211 PANIC(KHCICmdQPanic, ETryToSendWhileUnoperational)); |
|
1212 |
|
1213 if ((iCmdQControllerState != EInitialising) && (iCmdQControllerState != EStarted)) |
|
1214 { |
|
1215 // This is just a safety net. We shouldn't have been scheduled to run |
|
1216 // if we're not ready. But even if we've been mistakenly scheduled or |
|
1217 // the state changed in the meantime, this return makes it harmless. |
|
1218 return; |
|
1219 } |
|
1220 |
|
1221 TBool stopProcessing(ProcessResendQueue()); |
|
1222 if (!stopProcessing) |
|
1223 { |
|
1224 stopProcessing = ProcessWorkaroundQueue(); |
|
1225 if (!stopProcessing) |
|
1226 { |
|
1227 if (iCmdQControllerState == EStarted) |
|
1228 { |
|
1229 stopProcessing = ProcessPriorityQueue(); |
|
1230 } |
|
1231 if (!stopProcessing) |
|
1232 { |
|
1233 TDblQue<CHCICommandQItem>* queue = ActiveRegularQueue(); |
|
1234 if (queue != NULL) |
|
1235 { |
|
1236 ProcessRegularQueue(*queue); |
|
1237 } |
|
1238 } |
|
1239 } |
|
1240 } |
|
1241 } |
|
1242 |
|
1243 /** |
|
1244 Returns ETrue if our current state allows commands to be queued. |
|
1245 */ |
|
1246 inline TBool CHCICmdQController::CanAddCommands() |
|
1247 { |
|
1248 switch (iCmdQControllerState) |
|
1249 { |
|
1250 case EStarted: |
|
1251 case EResetInit: |
|
1252 case EInitialising: |
|
1253 return ETrue; |
|
1254 default: |
|
1255 return EFalse; |
|
1256 } |
|
1257 } |
|
1258 |
|
1259 /** |
|
1260 Tests if there are items queued on the specified queue. |
|
1261 */ |
|
1262 inline TBool CHCICmdQController::CmdsQueued(TDblQue<CHCICommandQItem>* aQueue) |
|
1263 { |
|
1264 if (aQueue != NULL) |
|
1265 { |
|
1266 return (!aQueue->IsEmpty()); |
|
1267 } |
|
1268 return EFalse; |
|
1269 } |
|
1270 |
|
1271 /** |
|
1272 Tests if there are items queued on any of the queues active in current state. |
|
1273 */ |
|
1274 inline TBool CHCICmdQController::AnyCmdsToSend() |
|
1275 { |
|
1276 TDblQue<CHCICommandQItem>* activeQ = ActiveRegularQueue(); |
|
1277 |
|
1278 // Need to check those two no matter which state we're in. |
|
1279 TBool commonQueuesEmpty = iWorkaroundCommandQ.IsEmpty() && iResendCommandQ.IsEmpty(); |
|
1280 |
|
1281 if (activeQ == &iInitCommandQ) |
|
1282 { |
|
1283 return !iInitCommandQ.IsEmpty() || !commonQueuesEmpty; |
|
1284 } |
|
1285 else |
|
1286 { |
|
1287 return !iNormalCommandQ.IsEmpty() || !iPriorityCommandQ.IsEmpty() || !commonQueuesEmpty; |
|
1288 } |
|
1289 } |
|
1290 |
|
1291 /** |
|
1292 Returns the next Command Queue Item Id |
|
1293 */ |
|
1294 inline TUint CHCICmdQController::NextCommandQueueItemId() |
|
1295 { |
|
1296 __ASSERT_DEBUG(iNextCommandQItemId != KInvalidCommandQueueItemId, |
|
1297 PANIC(KHCICmdQPanic, EInvalidCommandQueueItemId)); |
|
1298 iNextCommandQItemId++; |
|
1299 if(iNextCommandQItemId == KInvalidCommandQueueItemId) |
|
1300 { |
|
1301 iNextCommandQItemId++; |
|
1302 } |
|
1303 return iNextCommandQItemId; |
|
1304 } |
|
1305 |
|
1306 /** |
|
1307 Processes the resend queue. |
|
1308 Returns ETrue if the scheduling loop shouldn't process lower priority queues |
|
1309 (which is when it schedules a command for resend) and EFalse if the loop should |
|
1310 go on. |
|
1311 */ |
|
1312 TBool CHCICmdQController::ProcessResendQueue() |
|
1313 { |
|
1314 LOG_FUNC |
|
1315 |
|
1316 if (!CmdsQueued(&iResendCommandQ)) |
|
1317 { |
|
1318 return EFalse; |
|
1319 } |
|
1320 |
|
1321 CHCICommandQItem* cmd = iResendCommandQ.First(); |
|
1322 TUint bypassBlocks = CmdBypassBlocks(*cmd); |
|
1323 if (Blocked(EResendQueueBlocks, bypassBlocks)) |
|
1324 { |
|
1325 // We're the highest priority queue, but allow the other queues to try |
|
1326 // and send if we have commands queued but are blocked - "Only one |
|
1327 // active resend at a time" is still fulfilled. |
|
1328 return EFalse; |
|
1329 } |
|
1330 |
|
1331 if (OkToSendCommand(cmd, bypassBlocks)) |
|
1332 { |
|
1333 // Process command. |
|
1334 SendCommand(*cmd); |
|
1335 SetBlock(EResendBlock); |
|
1336 return ETrue; |
|
1337 } |
|
1338 |
|
1339 return EFalse; |
|
1340 } |
|
1341 |
|
1342 /** |
|
1343 Processes the workaround queue. |
|
1344 Returns ETrue if the scheduling loop shouldn't process lower priority queues |
|
1345 (which is when the queue is non-empty) and EFalse if the loop should go on. |
|
1346 */ |
|
1347 TBool CHCICmdQController::ProcessWorkaroundQueue() |
|
1348 { |
|
1349 LOG_FUNC |
|
1350 |
|
1351 if (!CmdsQueued(&iWorkaroundCommandQ)) |
|
1352 { |
|
1353 return EFalse; |
|
1354 } |
|
1355 |
|
1356 CHCICommandQItem* cmd = iWorkaroundCommandQ.First(); |
|
1357 switch (cmd->Type()) |
|
1358 { |
|
1359 case CHCICommandQItem::EIndeterminate: |
|
1360 // Start of workaround sequence, set up the first command. |
|
1361 cmd->SetType(CHCICommandQItem::EParent); |
|
1362 SetUpNextWorkaroundCmd(NULL, NULL); |
|
1363 cmd = iWorkaroundCommandQ.First(); |
|
1364 UpdateStarvationTimer(); |
|
1365 break; |
|
1366 |
|
1367 case CHCICommandQItem::EPreChild: |
|
1368 case CHCICommandQItem::EPostChild: |
|
1369 case CHCICommandQItem::EParent: |
|
1370 // In the middle of a workaround. |
|
1371 // Nothing to do here really, command setup has been done by calling |
|
1372 // SetUpNextWorkaroundCmd() in Process*Event on completion of the previous |
|
1373 // command in the workaround sequence. |
|
1374 break; |
|
1375 |
|
1376 default: |
|
1377 __ASSERT_DEBUG(EFalse, PANIC(KHCICmdQPanic, EInvalidCmdQueueItemType)); |
|
1378 break; |
|
1379 } |
|
1380 |
|
1381 #ifdef _DEBUG |
|
1382 // We should only ever have at maximum one parent and one child on the |
|
1383 // workaround queue at any time. |
|
1384 TDblQueIter<CHCICommandQItem> iter(iWorkaroundCommandQ); |
|
1385 TUint count = 0; |
|
1386 while(iter++) |
|
1387 { |
|
1388 count++; |
|
1389 } |
|
1390 __ASSERT_DEBUG(count <= 2, PANIC(KHCICmdQPanic, ETooManyItemsOnWorkaroundQueue)); |
|
1391 #endif // _DEBUG |
|
1392 |
|
1393 // Now that we're sure we have pre-workaraound set up we can check blocks. |
|
1394 TUint bypassBlocks = CmdBypassBlocks(*cmd); |
|
1395 if (Blocked(ESendQueueBlocks, bypassBlocks)) |
|
1396 { |
|
1397 // Don't allow lower priority queues to send until we're empty. |
|
1398 return ETrue; |
|
1399 } |
|
1400 |
|
1401 if (OkToSendCommand(cmd, bypassBlocks)) |
|
1402 { |
|
1403 SendCommand(*cmd); |
|
1404 UpdateStarvationTimer(); |
|
1405 SetBlock(EWorkaroundBlock); |
|
1406 } |
|
1407 |
|
1408 // Don't allow lower priority queues to send until we're empty. |
|
1409 return ETrue; |
|
1410 } |
|
1411 |
|
1412 /** |
|
1413 Processes the priority queue. |
|
1414 Returns ETrue if the scheduling loop shouldn't process lower priority queues |
|
1415 (which is when the queue is non-empty) and EFalse if the loop should go on. |
|
1416 */ |
|
1417 TBool CHCICmdQController::ProcessPriorityQueue() |
|
1418 { |
|
1419 LOG_FUNC |
|
1420 |
|
1421 if (!CmdsQueued(&iPriorityCommandQ)) |
|
1422 { |
|
1423 return EFalse; |
|
1424 } |
|
1425 else if (Blocked(ESendQueueBlocks, ECachingCommandBlocks)) |
|
1426 { |
|
1427 // Check blocks excluding ECachingCommandBlocks |
|
1428 // The blocks we're checking don't depend on particular commands, so we can |
|
1429 // safely return here. |
|
1430 // Don't allow lower priority queues to send until we're empty. |
|
1431 return ETrue; |
|
1432 } |
|
1433 |
|
1434 // Search the queue for the first non-blocking command. |
|
1435 // Note that this is still just heurisitics, because even if we find a non-blocking |
|
1436 // command, it may still yield a blocking workaround. |
|
1437 CHCICommandQItem* cmd = NULL; |
|
1438 TDblQueIter<CHCICommandQItem> i(iPriorityCommandQ); |
|
1439 while ((cmd = i++) != NULL) |
|
1440 { |
|
1441 // For every command we clear the cached blocks as they apply to the last command |
|
1442 // we attempted to send, using OkToSendCommand. As the priority queue can send out |
|
1443 // of order, i.e. not just the head command, the cached blocks need to be |
|
1444 // re-evaluated for each command until we find the one we intend to send. |
|
1445 ClearBlock(ECachingCommandBlocks); |
|
1446 if (OkToSendCommand(cmd, CmdBypassBlocks(*cmd))) |
|
1447 { |
|
1448 // Found available command. |
|
1449 if (iQdp->MhcqdiDoesCommandRequireWorkaround(*cmd)) |
|
1450 { |
|
1451 // Start of workaround sequence. |
|
1452 // Move onto the workaround queue and kick the scheduling loop. |
|
1453 cmd->iLink.Deque(); |
|
1454 iWorkaroundCommandQ.AddLast(*cmd); |
|
1455 iAsyncCallBackForSend->CallBack(); |
|
1456 } |
|
1457 else |
|
1458 { |
|
1459 // Non-workaround. |
|
1460 cmd->SetType(CHCICommandQItem::ENormal); |
|
1461 SendCommand(*cmd); |
|
1462 UpdateStarvationTimer(); |
|
1463 } |
|
1464 break; |
|
1465 } |
|
1466 } |
|
1467 |
|
1468 // At this point we are in one of the following states: |
|
1469 // - All priority commands are either blocked or have errored and been deleted in |
|
1470 // OkToSendCommand. If any commands are errored then the scheduling loop will |
|
1471 // also have been kick. |
|
1472 // - A priority command has been added to the workaround queue and the scheduling |
|
1473 // loop has been kick. |
|
1474 // - A priority command has been sent. |
|
1475 // |
|
1476 // In any one of these conditions we want to stop processing further queues so can |
|
1477 // always return true. |
|
1478 return ETrue; |
|
1479 } |
|
1480 |
|
1481 /** |
|
1482 Processes a regular queue - normal or initialization command queue. |
|
1483 */ |
|
1484 void CHCICmdQController::ProcessRegularQueue(TDblQue<CHCICommandQItem>& aQueue) |
|
1485 { |
|
1486 LOG_FUNC |
|
1487 |
|
1488 if (!CmdsQueued(&aQueue)) |
|
1489 { |
|
1490 return; |
|
1491 } |
|
1492 |
|
1493 CHCICommandQItem* cmd = aQueue.First(); |
|
1494 TUint bypassBlocks = CmdBypassBlocks(*cmd); |
|
1495 if (Blocked(ESendQueueBlocks, bypassBlocks)) |
|
1496 { |
|
1497 return; |
|
1498 } |
|
1499 |
|
1500 if (cmd->Type() == CHCICommandQItem::EIndeterminate) |
|
1501 { |
|
1502 // It's the first time we're looking into cmd. See if it needs a workaround. |
|
1503 if (iQdp->MhcqdiDoesCommandRequireWorkaround(*cmd)) |
|
1504 { |
|
1505 // Start of workaround sequence. |
|
1506 // Move it onto the workaround queue and kick the scheduling loop. |
|
1507 cmd->iLink.Deque(); |
|
1508 iWorkaroundCommandQ.AddLast(*cmd); |
|
1509 iAsyncCallBackForSend->CallBack(); |
|
1510 |
|
1511 return; |
|
1512 } |
|
1513 else |
|
1514 { |
|
1515 // Non-workaround. |
|
1516 cmd->SetType(CHCICommandQItem::ENormal); |
|
1517 } |
|
1518 } |
|
1519 if (OkToSendCommand(cmd, bypassBlocks)) |
|
1520 { |
|
1521 SendCommand(*cmd); |
|
1522 UpdateStarvationTimer(); |
|
1523 } |
|
1524 } |
|
1525 |
|
1526 /** |
|
1527 Process workaround related commands, and is invoked every time a |
|
1528 workaround completes. As the QDP interface needs to be passed the |
|
1529 concluding event of each workaround command, and events are used |
|
1530 synchronously and have a limited lifetime, this means workaround |
|
1531 handling has to be done at the same time as the event processing. |
|
1532 |
|
1533 This processing ensures that the head of the workaround queue contains |
|
1534 the next command in the workaround. It also tidies up and deletes |
|
1535 commands as necessary. Child commands are deleted once their events |
|
1536 have been reported to the QDP. The parent command lives until the end |
|
1537 of the workaround. |
|
1538 */ |
|
1539 void CHCICmdQController::SetUpNextWorkaroundCmd(CHCICommandQItem* aPreviousCmd, |
|
1540 const THCIEventBase* aPreviousCmdResult) |
|
1541 { |
|
1542 LOG_FUNC |
|
1543 |
|
1544 // Update blocks. |
|
1545 ClearBlock(EWorkaroundBlock); |
|
1546 |
|
1547 // Get parent |
|
1548 if (iParentCommand == NULL) |
|
1549 { |
|
1550 iParentCommand = iWorkaroundCommandQ.First(); |
|
1551 } |
|
1552 |
|
1553 __ASSERT_ALWAYS(iParentCommand, PANIC(KHCICmdQPanic, EObjectNotInitialised)); |
|
1554 |
|
1555 // Determine next command in the workaround sequence. |
|
1556 CHCICommandQItem* child = NULL; |
|
1557 CHCICommandQItem::TType type; |
|
1558 |
|
1559 if (iParentCommand->SentCount() == 0) |
|
1560 { |
|
1561 child = iQdp->MhcqdiGetPreChildCommand(*iParentCommand, aPreviousCmd, aPreviousCmdResult); |
|
1562 if (child != NULL) |
|
1563 { |
|
1564 // Pre-workaround. |
|
1565 type = CHCICommandQItem::EPreChild; |
|
1566 } |
|
1567 else |
|
1568 { |
|
1569 // Transitioning to post-workaround. |
|
1570 type = CHCICommandQItem::EParent; |
|
1571 } |
|
1572 } |
|
1573 else |
|
1574 { |
|
1575 __ASSERT_ALWAYS(aPreviousCmd, PANIC(KHCICmdQPanic, EObjectNotInitialised)); |
|
1576 if (aPreviousCmd->Type() == CHCICommandQItem::EParent) |
|
1577 { |
|
1578 // Transitioning to post-workaround. |
|
1579 child = iQdp->MhcqdiGetPostChildCommand(*iParentCommand, NULL, aPreviousCmdResult); |
|
1580 } |
|
1581 else |
|
1582 { |
|
1583 // Post-workaround. |
|
1584 child = iQdp->MhcqdiGetPostChildCommand(*iParentCommand, aPreviousCmd, aPreviousCmdResult); |
|
1585 } |
|
1586 |
|
1587 type = CHCICommandQItem::EPostChild; |
|
1588 } |
|
1589 |
|
1590 // Delete previous command (if not parent). |
|
1591 if ((aPreviousCmd != NULL) && |
|
1592 (aPreviousCmd->Type() != CHCICommandQItem::EParent)) |
|
1593 { |
|
1594 DeleteCommand(aPreviousCmd); |
|
1595 } |
|
1596 |
|
1597 if (child != NULL) |
|
1598 { |
|
1599 // Enqueue child to head of queue. |
|
1600 child->SetType(type); |
|
1601 iWorkaroundCommandQ.AddFirst(*child); |
|
1602 } |
|
1603 else if (type != CHCICommandQItem::EParent) |
|
1604 { |
|
1605 // Give the QDP a chance to update state within the stack if required |
|
1606 THCIEventBase* fakedEvent = NULL; |
|
1607 while ((fakedEvent = iQdp->MhcqdiGetFakedUnsolicitedEvent(*iParentCommand, fakedEvent)) != NULL) |
|
1608 { |
|
1609 iUnmatchedEventObserver->MhcqcCommandEventReceived(*fakedEvent, NULL); |
|
1610 } |
|
1611 |
|
1612 // End of workaround sequence, delete parent. |
|
1613 DeleteCommand(iParentCommand); |
|
1614 } |
|
1615 } |
|
1616 |
|
1617 /** |
|
1618 A helper for restarting the Starvation Timer during command addition/sendout. |
|
1619 Determines the next command that will be sent and restarts the timer with |
|
1620 the ID of that command. |
|
1621 Caveat: this always restarts (or cancels, hence Update rather than just Restart in the name) |
|
1622 the timer, so use it only when you know that the command that will be sent next has really changed. |
|
1623 */ |
|
1624 void CHCICmdQController::UpdateStarvationTimer() |
|
1625 { |
|
1626 // Determine the id of the next command to be executed. |
|
1627 // Don't care about resend queue, the starvation timer shouldn't be restarted on resends. |
|
1628 |
|
1629 TUint cmdId = KInvalidCommandQueueItemId; |
|
1630 |
|
1631 if (!iWorkaroundCommandQ.IsEmpty()) |
|
1632 { |
|
1633 cmdId = iWorkaroundCommandQ.First()->CommandQId(); |
|
1634 } |
|
1635 else if ((iCmdQControllerState == EStarted) && !iPriorityCommandQ.IsEmpty()) |
|
1636 { |
|
1637 // Only send off priority queue in EStarted (i.e. not in EInitialising) |
|
1638 // Never know which priority command will be sent next. |
|
1639 cmdId = KInvalidCommandQueueItemId; |
|
1640 } |
|
1641 else |
|
1642 { |
|
1643 TDblQue<CHCICommandQItem>* queue = ActiveRegularQueue(); |
|
1644 if (queue && !queue->IsEmpty()) |
|
1645 { |
|
1646 cmdId = queue->First()->CommandQId(); |
|
1647 } |
|
1648 else |
|
1649 { |
|
1650 // No command to send. |
|
1651 iQStarvationTimer->Cancel(); |
|
1652 return; |
|
1653 } |
|
1654 } |
|
1655 iQStarvationTimer->Restart(iQueueStarvationTimeout, cmdId, *this); |
|
1656 } |
|
1657 |
|
1658 /** |
|
1659 Dequeues and deletes aItem from its queue. |
|
1660 */ |
|
1661 void CHCICmdQController::DeleteCommand(CHCICommandQItem* &aItem) |
|
1662 { |
|
1663 LOG_FUNC |
|
1664 |
|
1665 if (!aItem) |
|
1666 { |
|
1667 return; |
|
1668 } |
|
1669 aItem->iLink.Deque(); |
|
1670 iQdp->MhcqdiCommandAboutToBeDeleted(*aItem); |
|
1671 delete aItem; |
|
1672 aItem = NULL; |
|
1673 UpdateStarvationTimer(); |
|
1674 } |
|
1675 |
|
1676 /** |
|
1677 Helper to be called on command addition. |
|
1678 Returns ETrue if the command that will be sent next changed after we added a command to aQueue. |
|
1679 Ignores the Resend queue. |
|
1680 */ |
|
1681 TBool CHCICmdQController::NextCommandChanged(const TDblQue<CHCICommandQItem> &aQueue) |
|
1682 { |
|
1683 LOG_FUNC |
|
1684 |
|
1685 if (&aQueue != QueueSendingNext()) |
|
1686 { |
|
1687 // We added a command to aQueue, but it's still another queue |
|
1688 // that sends next, so we didn't change the next command to be sent. |
|
1689 return EFalse; |
|
1690 } |
|
1691 else // aQueue is sending next. |
|
1692 { |
|
1693 if (&aQueue == &iPriorityCommandQ) |
|
1694 { |
|
1695 // PriorityQ is not FIFO, so any added command can potentially |
|
1696 // be sent next irrespectably of whether the queue was empty or not. |
|
1697 return ETrue; |
|
1698 } |
|
1699 else |
|
1700 { |
|
1701 // Rest of queues is FIFO, so the next command to be sent changed |
|
1702 // if the queue had been empty when we added the command. |
|
1703 return aQueue.First() == aQueue.Last(); |
|
1704 } |
|
1705 } |
|
1706 } |
|
1707 |
|
1708 /** |
|
1709 The general command addition routine. Adds aQueItem to aQueue and schedules |
|
1710 the command processing loop if current state is equal to aActiveState. |
|
1711 */ |
|
1712 TUint CHCICmdQController::DoAddCommandL(CHCICommandQItem& aQueItem, |
|
1713 TDblQue<CHCICommandQItem> &aQueue, TCmdQControllerStates aActiveState) |
|
1714 { |
|
1715 LOG_FUNC |
|
1716 |
|
1717 // Ensure we are in a state that allows commands to be added. |
|
1718 if(!CanAddCommands()) |
|
1719 { |
|
1720 delete &aQueItem; |
|
1721 User::Leave(KErrHardwareNotAvailable); |
|
1722 } |
|
1723 |
|
1724 // Assign a unique CommandQId. |
|
1725 aQueItem.SetCommandQId(NextCommandQueueItemId()); |
|
1726 |
|
1727 aQueue.AddLast(aQueItem); |
|
1728 // Restart queue starvation timer if this is the next command to be sent. |
|
1729 if (NextCommandChanged(aQueue)) |
|
1730 { |
|
1731 // If this is priority queue, we don't really know which command should be executed |
|
1732 // next, so we feed the timer with KInvalidCommandQueueItemId in that case. |
|
1733 TUint cmdId = KInvalidCommandQueueItemId; |
|
1734 if (&aQueue != &iPriorityCommandQ) |
|
1735 { |
|
1736 cmdId = aQueItem.CommandQId(); |
|
1737 } |
|
1738 iQStarvationTimer->Restart(iQueueStarvationTimeout, cmdId, *this); |
|
1739 } |
|
1740 |
|
1741 // Only schedule if we're in the state in which given queue should be considered for scheduling. |
|
1742 if (iCmdQControllerState == aActiveState) |
|
1743 { |
|
1744 iAsyncCallBackForSend->CallBack(); |
|
1745 } |
|
1746 |
|
1747 return aQueItem.CommandQId(); |
|
1748 } |
|
1749 |
|
1750 /** |
|
1751 Evaluates various blocking conditions to determine if the command can be sent. |
|
1752 aCmdQItem is passed by reference to pointer as it can be deleted and NULLified in case of error. |
|
1753 @return whether the command can be sent. |
|
1754 */ |
|
1755 TBool CHCICmdQController::OkToSendCommand(CHCICommandQItem*& aCmdQItem, TUint aBypassBlocks) |
|
1756 { |
|
1757 TInt result = KErrNone; |
|
1758 LOG_FUNC |
|
1759 |
|
1760 __ASSERT_ALWAYS(aCmdQItem, PANIC(KHCICmdQPanic, ENullCmdQItemPtr)); |
|
1761 |
|
1762 if((iCommandCredits < aCmdQItem->Command().CreditsConsumed()) && |
|
1763 (!(aBypassBlocks & EInsufficientCreditBlock))) |
|
1764 { |
|
1765 // Insufficient HCI credits. Block and suspend processing queue. |
|
1766 SetBlock(EInsufficientCreditBlock); |
|
1767 } |
|
1768 else |
|
1769 { |
|
1770 CHCICommandQItem* const duplicate = ScanQueueByOpcode(iSentCommandQ, |
|
1771 aCmdQItem->Command().Opcode()); |
|
1772 if (duplicate && !(aBypassBlocks & EDuplicatedOpcodeBlock)) |
|
1773 { |
|
1774 // Duplicate command pending. Block and suspend processing queue. |
|
1775 SetBlock(EDuplicatedOpcodeBlock); |
|
1776 |
|
1777 // Need to mark the duplicate so that EDuplicatedOpcodeBlock is cleared |
|
1778 // on its completion. |
|
1779 duplicate->SetDuplicatedOpcode(ETrue); |
|
1780 } |
|
1781 else |
|
1782 { |
|
1783 const TDblQue<const CHCICommandQItem>* sentQ = (reinterpret_cast<const TDblQue<const CHCICommandQItem>*>(&iSentCommandQ)); |
|
1784 |
|
1785 result = iQdp->MhcqdiCanSend(*aCmdQItem, *sentQ); |
|
1786 // KErrNone if aCommand is to be sent, EBlock if Command |
|
1787 // is to be delayed. |
|
1788 if(result < 0) |
|
1789 { |
|
1790 if(aCmdQItem->Client()) |
|
1791 { |
|
1792 aCmdQItem->Client()->MhcqcCommandErrored(result, &aCmdQItem->Command()); |
|
1793 } |
|
1794 |
|
1795 // Dequeue, delete & set to NULL. |
|
1796 DeleteCommand(aCmdQItem); |
|
1797 |
|
1798 if(AnyCmdsToSend()) |
|
1799 { |
|
1800 iAsyncCallBackForSend->CallBack(); |
|
1801 } |
|
1802 } |
|
1803 else if(result == MHCICmdQueueDecisionInterface::EBlock && !(aBypassBlocks & ECanSendBlock)) |
|
1804 { |
|
1805 // QDP should never block when the sent queue is empty (would permanently block the queue. |
|
1806 __ASSERT_ALWAYS((!iSentCommandQ.IsEmpty()), PANIC(KHCICmdQPanic, ECanSendBlockWhenEmpty)); |
|
1807 SetBlock(ECanSendBlock); |
|
1808 } |
|
1809 } |
|
1810 } |
|
1811 |
|
1812 return ((!Blocked(ECachingCommandBlocks, aBypassBlocks))&&(result==KErrNone)); |
|
1813 } |
|
1814 |
|
1815 /** |
|
1816 Submits a request to the link muxer for permission to send a command. |
|
1817 */ |
|
1818 void CHCICmdQController::SendCommand(CHCICommandQItem& aCmdQItem) |
|
1819 { |
|
1820 LOG_FUNC |
|
1821 |
|
1822 __ASSERT_DEBUG(!iSendingCommand, PANIC(KHCICmdQPanic, ETryToSendWhilstSending)); |
|
1823 |
|
1824 iSendingCommand = &aCmdQItem; |
|
1825 // Dequeue command. |
|
1826 iSendingCommand->iLink.Deque(); |
|
1827 |
|
1828 // Initiate send request to HCTL, and block the queue. |
|
1829 iLinkMuxer->TryToSend(); |
|
1830 SetBlock(ETryToSendBlock); |
|
1831 } |
|
1832 |
|
1833 /** |
|
1834 Process matched non-error events. |
|
1835 */ |
|
1836 void CHCICmdQController::ProcessMatchedEvent(const THCIEventBase& aEvent, CHCICommandQItem* aCmd, TBool aConcludesCmd) |
|
1837 { |
|
1838 LOG_FUNC |
|
1839 |
|
1840 // If required by the command, a Command Status Event should always be received before any other event related to a command. |
|
1841 THCIEventCode eventCode(aEvent.EventCode()); |
|
1842 if (eventCode == ECommandStatusEvent) |
|
1843 { |
|
1844 if (aCmd->Command().ExpectsCommandStatusEvent()) |
|
1845 { |
|
1846 aCmd->SetReceivedCmdStatusEvent(ETrue); |
|
1847 } |
|
1848 else |
|
1849 { |
|
1850 // assert in debug that we don't get here. |
|
1851 } |
|
1852 } |
|
1853 else |
|
1854 { |
|
1855 // Non Command Status Event, clear possible CanSend block. |
|
1856 ClearBlock(ECanSendBlock); |
|
1857 } |
|
1858 |
|
1859 CHCICommandQItem::TType type = aCmd->Type(); |
|
1860 if ((type != CHCICommandQItem::EPreChild) && |
|
1861 (type != CHCICommandQItem::EPostChild)) |
|
1862 { |
|
1863 // Command client is notified of non-workaround command events. |
|
1864 if(aCmd->Client()) |
|
1865 { |
|
1866 aCmd->Client()->MhcqcCommandEventReceived(aEvent, &aCmd->Command()); |
|
1867 } |
|
1868 } |
|
1869 |
|
1870 if (aConcludesCmd) |
|
1871 { |
|
1872 // Check that if a command status is expected then it has been got before being concluded. |
|
1873 |
|
1874 // Cancel completion timer. |
|
1875 aCmd->CancelCompletionTimer(); |
|
1876 |
|
1877 // Update blocks, cleared blocks will be re-evaluated on next Run Queue |
|
1878 if (aCmd->SentCount() > 1) |
|
1879 { |
|
1880 ClearBlock(EResendBlock); |
|
1881 } |
|
1882 if (aCmd->DuplicatedOpcode()) |
|
1883 { |
|
1884 ClearBlock(EDuplicatedOpcodeBlock); |
|
1885 } |
|
1886 |
|
1887 // Process completed command. |
|
1888 if (type == CHCICommandQItem::ENormal) |
|
1889 { |
|
1890 // Non-workaround command. Delete processed command. |
|
1891 DeleteCommand(aCmd); |
|
1892 } |
|
1893 else |
|
1894 { |
|
1895 // Workaround in progress. Set up next workaround command. |
|
1896 SetUpNextWorkaroundCmd(aCmd, &aEvent); |
|
1897 } |
|
1898 } |
|
1899 } |
|
1900 |
|
1901 /** |
|
1902 Process matched error events. |
|
1903 */ |
|
1904 void CHCICmdQController::ProcessMatchedErrorEvent(const THCIEventBase& aEvent, CHCICommandQItem* aCmd, TBool /* aConcludesCmd */, TBool aSendToQdp) |
|
1905 { |
|
1906 LOG_FUNC |
|
1907 |
|
1908 // Cancel completion timer. |
|
1909 aCmd->CancelCompletionTimer(); |
|
1910 |
|
1911 /* |
|
1912 |
|
1913 Update blocks. The following blocks are not cleared: |
|
1914 |
|
1915 1. The Resend block untouched to avoid interfering with an in-progress resend and |
|
1916 to ensure that only a single resend is active at a time. |
|
1917 2. The TryToSend block which is strictly managed by the HCTL flow control methods |
|
1918 (SendCommand and DoSend). |
|
1919 3. The Workaround block to prevent possibly bypassing a valid workaround block |
|
1920 on completion of a resend. |
|
1921 |
|
1922 All other cleared blocks (the caching blocks) will be re-evaluated before sending |
|
1923 the next command. |
|
1924 */ |
|
1925 ClearBlock(ECachingCommandBlocks); |
|
1926 |
|
1927 // Remove item from sent queue. |
|
1928 aCmd->iLink.Deque(); |
|
1929 |
|
1930 if (aSendToQdp && iQdp->MhcqdiMatchedErrorEventReceived(aEvent, *aCmd) == MHCICmdQueueDecisionInterface::EResendErroredCommand) |
|
1931 { |
|
1932 // Clear the command status event flag and enqueue to the resend queue |
|
1933 aCmd->SetReceivedCmdStatusEvent(EFalse); |
|
1934 if (aCmd->SentCount() > 1) |
|
1935 { |
|
1936 // Command was previously resent, enqueue to head of resend queue and clear Resend block |
|
1937 iResendCommandQ.AddFirst(*aCmd); |
|
1938 ClearBlock(EResendBlock); |
|
1939 } |
|
1940 else |
|
1941 { |
|
1942 // Command has not previously been resent, enqueue to tail of resend queue. |
|
1943 iResendCommandQ.AddLast(*aCmd); |
|
1944 } |
|
1945 } |
|
1946 else |
|
1947 { |
|
1948 CHCICommandQItem::TType type = aCmd->Type(); |
|
1949 |
|
1950 if ((type != CHCICommandQItem::EPreChild) && |
|
1951 (type != CHCICommandQItem::EPostChild)) |
|
1952 { |
|
1953 // Command error event received, notify client if non-child command |
|
1954 if(aCmd->Client()) |
|
1955 { |
|
1956 aCmd->Client()->MhcqcCommandEventReceived(aEvent, &aCmd->Command()); |
|
1957 } |
|
1958 } |
|
1959 |
|
1960 // Process completed command. |
|
1961 if (type == CHCICommandQItem::ENormal) |
|
1962 { |
|
1963 // Non-workaround command. Delete processed command. |
|
1964 DeleteCommand(aCmd); |
|
1965 } |
|
1966 else |
|
1967 { |
|
1968 // Workaround in progress. Set up next workaround command. |
|
1969 SetUpNextWorkaroundCmd(aCmd, &aEvent); |
|
1970 } |
|
1971 } |
|
1972 } |
|
1973 |
|
1974 /** |
|
1975 Processes unmatched events. |
|
1976 */ |
|
1977 void CHCICmdQController::ProcessUnmatchedEvent(const THCIEventBase& aEvent, TBool aSendToQdp) |
|
1978 { |
|
1979 LOG_FUNC |
|
1980 |
|
1981 if (aSendToQdp) |
|
1982 { |
|
1983 // Notify QDP. |
|
1984 if (iQdpEventModifier) |
|
1985 { |
|
1986 THCIEventCode origEventCode = aEvent.EventCode(); |
|
1987 iQdpEventModifier->MhcqemiUnmatchedEventReceived(const_cast<THCIEventBase&>(aEvent)); |
|
1988 __ASSERT_ALWAYS(origEventCode == aEvent.EventCode(), PANIC(KHCICmdQPanic, EQdpTryingToChangeEventCode)); |
|
1989 } |
|
1990 else |
|
1991 { |
|
1992 iQdp->MhcqdiUnmatchedEventReceived(aEvent); |
|
1993 } |
|
1994 } |
|
1995 |
|
1996 iUnmatchedEventObserver->MhcqcCommandEventReceived(aEvent, NULL); |
|
1997 } |
|
1998 |
|
1999 /** |
|
2000 Processes timed out commands. |
|
2001 */ |
|
2002 void CHCICmdQController::ProcessCommandCompletionTimeout(CHCICommandQItem* aCmd) |
|
2003 { |
|
2004 LOG_FUNC |
|
2005 |
|
2006 /* |
|
2007 |
|
2008 Update blocks. The following blocks are not cleared: |
|
2009 |
|
2010 1. The Resend block untouched to avoid interfering with an in-progress resend and |
|
2011 to ensure that only a single resend is active at a time. |
|
2012 2. The TryToSend block which is strictly managed by the HCTL flow control methods |
|
2013 (SendCommand and DoSend). |
|
2014 3. The Workaround block to prevent possibly bypassing a valid workaround block |
|
2015 on completion of a resend. |
|
2016 |
|
2017 All other cleared blocks (the caching blocks) will be re-evaluated before sending |
|
2018 the next command. |
|
2019 */ |
|
2020 ClearBlock(ECachingCommandBlocks); |
|
2021 |
|
2022 // Remove item from sent queue. |
|
2023 aCmd->iLink.Deque(); |
|
2024 |
|
2025 TUint refundedCredits = 0; |
|
2026 MHCICmdQueueDecisionInterface::TCommandTimedOutAction action; |
|
2027 const TDblQue<const CHCICommandQItem>* sentQ = (reinterpret_cast<const TDblQue<const CHCICommandQItem>*>(&iSentCommandQ)); |
|
2028 |
|
2029 action = iQdp->MhcqdiCommandTimedOut(*aCmd, *sentQ, iCommandCredits, refundedCredits); |
|
2030 |
|
2031 // Take back any refunded credits |
|
2032 iCommandCredits += refundedCredits; |
|
2033 |
|
2034 if (action == MHCICmdQueueDecisionInterface::EContinueWithTimeoutEvent) |
|
2035 { |
|
2036 CHCICommandQItem::TType type = aCmd->Type(); |
|
2037 |
|
2038 if ((type != CHCICommandQItem::EPreChild) && |
|
2039 (type != CHCICommandQItem::EPostChild)) |
|
2040 { |
|
2041 // Command error event received, notify client if non-child command |
|
2042 if(aCmd->Client()) |
|
2043 { |
|
2044 aCmd->Client()->MhcqcCommandErrored(CHciUtil::SymbianErrorCode(EHardwareFail), &aCmd->Command()); |
|
2045 } |
|
2046 } |
|
2047 |
|
2048 // Process completed command. |
|
2049 if (type == CHCICommandQItem::ENormal) |
|
2050 { |
|
2051 // Non-workaround command. Delete processed command. |
|
2052 DeleteCommand(aCmd); |
|
2053 } |
|
2054 else |
|
2055 { |
|
2056 SetUpNextWorkaroundCmd(aCmd, NULL); |
|
2057 } |
|
2058 } |
|
2059 else if (action == MHCICmdQueueDecisionInterface::EResendTimedOutCommand) |
|
2060 { |
|
2061 // Clear the command status event flag and enqueue to the resend queue |
|
2062 aCmd->SetReceivedCmdStatusEvent(EFalse); |
|
2063 if (aCmd->SentCount() > 1) |
|
2064 { |
|
2065 // Command was previously resent, enqueue to head of resend queue and clear Resend block |
|
2066 iResendCommandQ.AddFirst(*aCmd); |
|
2067 ClearBlock(EResendBlock); |
|
2068 } |
|
2069 else |
|
2070 { |
|
2071 // Command has not previously been resent, enqueue to tail of resend queue. |
|
2072 iResendCommandQ.AddLast(*aCmd); |
|
2073 } |
|
2074 } |
|
2075 } |
|
2076 |
|
2077 // Static async CallBack methods. |
|
2078 /*static*/ TInt CHCICmdQController::AsyncCallBackForReset(TAny* aCmdQController) |
|
2079 { |
|
2080 CHCICmdQController* cmdQController = static_cast<CHCICmdQController*>(aCmdQController); |
|
2081 cmdQController->DoReset(); |
|
2082 return EFalse; // Don't call back any more. |
|
2083 } |
|
2084 |
|
2085 /*static*/ TInt CHCICmdQController::AsyncCallBackForSend(TAny* aCmdQController) |
|
2086 { |
|
2087 CHCICmdQController* cmdQController = static_cast<CHCICmdQController*>(aCmdQController); |
|
2088 cmdQController->TryToSend(); |
|
2089 return EFalse; // Don't call back any more. |
|
2090 } |
|
2091 |
|
2092 #ifdef _DEBUG |
|
2093 void CHCICmdQController::LogQueue(TDblQue<CHCICommandQItem>& aQueue) |
|
2094 { |
|
2095 TDblQueIter<CHCICommandQItem> iter(aQueue); |
|
2096 while(CHCICommandQItem* item = iter++) |
|
2097 { |
|
2098 THCIOpcode opcode = item->Command().Opcode(); |
|
2099 const TUint16 KOgfMask = 0xfc00; |
|
2100 TUint8 ogf = (opcode&KOgfMask) >> 10; // OGF starts at bit 10. |
|
2101 TUint16 ocf = (opcode&~KOgfMask); |
|
2102 LOG4(_L("Command OGF(0x%02x) OCF(0x%04x) item 0x%08x owned by 0x%08x"), ogf, ocf, item, item->Client()); |
|
2103 } |
|
2104 } |
|
2105 #endif // _DEBUG |
|
2106 |
|
2107 |