|
1 // Copyright (c) 1999-2010 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 "hostmbufpool.h" |
|
17 |
|
18 #include <bluetooth/hcicommandqueue.h> |
|
19 #include <bluetooth/hci/hostnumberofcompletedpacketscommand.h> |
|
20 #include <bluetooth/hci/hcievents.h> |
|
21 #include <bluetooth/hci/commandcompleteevent.h> |
|
22 #include <bluetooth/hci/hciutil.h> |
|
23 |
|
24 #include "linkconsts.h" |
|
25 #include "linkutil.h" |
|
26 |
|
27 #include <bluetooth/logger.h> |
|
28 |
|
29 #ifdef HOSTCONTROLLER_TO_HOST_FLOW_CONTROL |
|
30 |
|
31 #ifdef __FLOG_ACTIVE |
|
32 _LIT8(KLogComponent, LOG_COMPONENT_LINKMGR); |
|
33 #endif |
|
34 |
|
35 __DEBUG_ONLY(PANICCATEGORY("mbufpool");) |
|
36 |
|
37 |
|
38 CHostMBufPool* CHostMBufPool::NewL(MHCICommandQueue& aCommandQueue) |
|
39 { |
|
40 LOG_STATIC_FUNC |
|
41 CHostMBufPool* self = new (ELeave) CHostMBufPool(aCommandQueue); |
|
42 CleanupStack::PushL(self); |
|
43 self->ConstructL(); |
|
44 CleanupStack::Pop(self); |
|
45 return self; |
|
46 } |
|
47 |
|
48 void CHostMBufPool::DeletePool(TSglQue<TPoolBuffer>& aQueue) |
|
49 { |
|
50 LOG_FUNC |
|
51 TPoolBuffer* buffer = NULL; |
|
52 TSglQueIter<TPoolBuffer> iter(aQueue); |
|
53 while(buffer=iter++, buffer) |
|
54 { |
|
55 aQueue.Remove(*buffer); |
|
56 DeleteBuffer(buffer); |
|
57 } |
|
58 } |
|
59 |
|
60 void CHostMBufPool::DeleteBuffer(TPoolBuffer*& aBuffer) |
|
61 { |
|
62 LOG_FUNC |
|
63 if(aBuffer) |
|
64 { |
|
65 aBuffer->iMBufChain.Free(); |
|
66 delete aBuffer; |
|
67 aBuffer = NULL; |
|
68 } |
|
69 } |
|
70 |
|
71 CHostMBufPool::~CHostMBufPool() |
|
72 { |
|
73 LOG_FUNC |
|
74 Cancel(); |
|
75 // iMBufRequester is cleaned in it's destructor (not much of an R-class...but it is what it is) |
|
76 DeletePool(iBufferPool); |
|
77 DeletePool(iWaitingAllocPool); |
|
78 DeleteBuffer(iBufferBeingAllocd); |
|
79 } |
|
80 |
|
81 CHostMBufPool::CHostMBufPool(MHCICommandQueue& aCommandQueue) |
|
82 : CActive(EPriorityHigh) |
|
83 // High priority so that buffers are allocated occur before more data is read, this prevents |
|
84 // the cases the data floods the device and exhausts the buffers before any more can be allocated. |
|
85 // This maximises throughput since we will ensure we send packet completion notifications in a |
|
86 // timely manner. |
|
87 , iCmdQ(aCommandQueue) |
|
88 , iBufferPool(_FOFF(TPoolBuffer,iLink)) |
|
89 , iWaitingAllocPool(_FOFF(TPoolBuffer,iLink)) |
|
90 , iCurrAckHandle(KErrNotFound) |
|
91 { |
|
92 LOG_FUNC |
|
93 } |
|
94 |
|
95 void CHostMBufPool::ConstructL() |
|
96 /** |
|
97 2nd phase constructor for the Host MBuf Pool. |
|
98 |
|
99 This method will attempt to reserve enough MBufs from the global pool |
|
100 for bluetooth use. |
|
101 @leave KErrNoMemory If the required number of MBufs couldn't be reserved |
|
102 */ |
|
103 { |
|
104 LOG_FUNC |
|
105 LOG2(_L("CHostMBufPool: now reserving %d size %d MBufChains"), KStackACLBuffersNum, KLinkMgrIncomingBufferSize); |
|
106 |
|
107 for (TInt i=0; i<=KStackACLBuffersNum-1; i++) |
|
108 { |
|
109 TPoolBuffer* thisBuffer = CreatePoolBufferL(); |
|
110 AddToBufferPool(*thisBuffer); |
|
111 } |
|
112 |
|
113 CActiveScheduler::Add(this); |
|
114 } |
|
115 |
|
116 CHostMBufPool::TPoolBuffer* CHostMBufPool::CreatePoolBufferL() |
|
117 { |
|
118 LOG_FUNC |
|
119 TPoolBuffer* newBuffer = new(ELeave) TPoolBuffer(); |
|
120 CleanupStack::PushL(newBuffer); |
|
121 newBuffer->iCurrentHandle = KInvalidConnectionHandle; // we assert on this later |
|
122 newBuffer->iMBufChain.AllocL(KLinkMgrIncomingBufferSize); |
|
123 CleanupStack::Pop(newBuffer); |
|
124 return newBuffer; |
|
125 } |
|
126 |
|
127 void CHostMBufPool::DoCancel() |
|
128 { |
|
129 LOG_FUNC |
|
130 iMBufRequester.Cancel(); |
|
131 } |
|
132 |
|
133 RMBufChain CHostMBufPool::TakeBufferL(THCIConnHandle aConnHandle) |
|
134 /** |
|
135 Takes a buffer from the pool and schedules an asynchronous allocation |
|
136 of the next buffer. Only when that allocation has succeeded will the host |
|
137 controller be signalled with a host_number_of_completed_packets. Hence, |
|
138 if we cannot allocate a buffer from the global MBuf pool, the host controller |
|
139 will be flowed off and no data will be lost. |
|
140 */ |
|
141 { |
|
142 LOG_FUNC |
|
143 ASSERT_DEBUG(aConnHandle != KInvalidConnectionHandle); |
|
144 |
|
145 // Speculatively attempt to allocate any queued allocations that may have previously failed. |
|
146 TryToAllocQueuedBuffer(); |
|
147 |
|
148 TPoolBuffer* ready = iBufferPool.First(); |
|
149 |
|
150 if(!ready) |
|
151 { |
|
152 // Whoops run out of buffers - even though we were trying to prevent this with |
|
153 // flow control, in the case of disconnection the controller will assume all the |
|
154 // data for a connection handle will be flushed and therefore the buffers associated |
|
155 // with that connection handle will be free. Unfortunately for us we don't have |
|
156 // that much control with the MBuf pool (since flow control is for asynchronous |
|
157 // buffer allocation rather than waiting for the given MBufs to be relinquished |
|
158 // by a higher layer). |
|
159 // So the controller could think we have more buffers than we actually have... |
|
160 LOG(_L8("CHostMBufPool: Ran out of buffers!!!!")); |
|
161 LEAVEL(KErrOverflow); |
|
162 } |
|
163 |
|
164 // If here then we should have a valid pool buffer to use |
|
165 __ASSERT_DEBUG(!ready->iMBufChain.IsEmpty(), Panic(ELinkMgrHostControllerHasOverflowedHost)); |
|
166 __ASSERT_DEBUG(ready->iCurrentHandle == KInvalidConnectionHandle, Panic(ELinkMgrHostControllerHasOverflowedHost)); |
|
167 |
|
168 RemoveFromBufferPool(*ready); |
|
169 ready->iCurrentHandle = aConnHandle; |
|
170 |
|
171 RMBufChain retChain; |
|
172 retChain.Assign(ready->iMBufChain); |
|
173 |
|
174 if (IsActive()) |
|
175 { |
|
176 //This buffer will be reclaimed from the global pool |
|
177 //after the one(s) we're currently trying to reclaim |
|
178 LOG(_L8("CHostMBufPool: TakeBuffer, buffer taken while alloc outstanding: queued alloc")); |
|
179 iWaitingAllocPool.AddLast(*ready); |
|
180 } |
|
181 else |
|
182 { |
|
183 LOG(_L8("CHostMBufPool: TakeBuffer, buffer taken")); |
|
184 AllocNewBuffer(*ready); |
|
185 } |
|
186 |
|
187 return retChain; |
|
188 } |
|
189 |
|
190 void CHostMBufPool::InvalidateByConnH(THCIConnHandle aConnHandle) |
|
191 { |
|
192 LOG_FUNC |
|
193 ASSERT_DEBUG(aConnHandle != KInvalidConnectionHandle); |
|
194 |
|
195 // We need to scan through the two pending "lists" to remove the |
|
196 // connection handle from record so we don't try to provide a |
|
197 // packet completion notification (the controller already assumes |
|
198 // the buffers are free as they are entitled to by the spec). |
|
199 |
|
200 // The current buffer being allocated |
|
201 if(iBufferBeingAllocd && iBufferBeingAllocd->iCurrentHandle == aConnHandle) |
|
202 { |
|
203 iBufferBeingAllocd->iCurrentHandle = KInvalidConnectionHandle; |
|
204 } |
|
205 |
|
206 // The list of buffers waiting to be allocted |
|
207 TPoolBuffer* buffer = NULL; |
|
208 TSglQueIter<TPoolBuffer> iter(iWaitingAllocPool); |
|
209 while(buffer=iter++, buffer) |
|
210 { |
|
211 if(buffer->iCurrentHandle == aConnHandle) |
|
212 { |
|
213 buffer->iCurrentHandle = KInvalidConnectionHandle; |
|
214 } |
|
215 } |
|
216 |
|
217 // Finally we need to purge any batched up completions if they |
|
218 // are for this connection handle |
|
219 if(iCurrAckHandle == aConnHandle) |
|
220 { |
|
221 iCurrAckHandle = KErrNotFound; |
|
222 iCurrCompletedPackets = 0; |
|
223 } |
|
224 } |
|
225 |
|
226 void CHostMBufPool::RunL() |
|
227 { |
|
228 LOG_FUNC |
|
229 LEAVEIFERRORL(iStatus.Int()); |
|
230 |
|
231 // We've successfully allocated a new MBufChain |
|
232 TPoolBuffer* justAllocd = iBufferBeingAllocd; |
|
233 iBufferBeingAllocd = NULL; |
|
234 THCIConnHandle justAllocdHandle = justAllocd->iCurrentHandle; |
|
235 |
|
236 // Return buffer to pool for re-use |
|
237 AddToBufferPool(*justAllocd); |
|
238 justAllocd->iCurrentHandle = KInvalidConnectionHandle; |
|
239 |
|
240 // If connection handle is still valid then we need to provide a completion |
|
241 // notification for the packet to the connection handle it was from. |
|
242 if(justAllocdHandle != KInvalidConnectionHandle) |
|
243 { |
|
244 if (iCurrAckHandle == KErrNotFound) |
|
245 { |
|
246 // This is the first completion we have seen |
|
247 iCurrAckHandle = justAllocdHandle; |
|
248 } |
|
249 ASSERT_DEBUG(iCurrAckHandle != KInvalidConnectionHandle); // just to be sure |
|
250 |
|
251 TBool ackNow = (justAllocdHandle != iCurrAckHandle); |
|
252 |
|
253 if (!ackNow) |
|
254 { |
|
255 iCurrCompletedPackets++; |
|
256 LOG2(_L8("CHostMBufPool: CompletedPackets++ for conn: %d [->%d]"), justAllocdHandle, iCurrCompletedPackets); |
|
257 ackNow = (iCurrCompletedPackets >= KStackACLBuffersTideMarkNum); |
|
258 } |
|
259 |
|
260 if (ackNow) |
|
261 { |
|
262 TInt err = KErrNone; |
|
263 |
|
264 if (iCurrCompletedPackets > 0) |
|
265 { |
|
266 LOG2(_L8("CHostMBufPool: Sending HostNumberOfCompletedPackets for conn: %d [%d completed]"), iCurrAckHandle, iCurrCompletedPackets); |
|
267 //Acknowledge the completed packets |
|
268 TRAP(err, HostNumberOfCompletedPacketsL(iCurrAckHandle, iCurrCompletedPackets)); |
|
269 //if this failed we probably couldn't alloc the memory for the command frame, |
|
270 //the HC is still flowed off. |
|
271 __ASSERT_DEBUG(err == KErrNone, Panic(ELinkMgrCouldNotSendHostNumberOfCompletedPackets)); |
|
272 LEAVEIFERRORL(err); |
|
273 } |
|
274 |
|
275 iCurrCompletedPackets = (justAllocdHandle != iCurrAckHandle) ? 1 : 0; |
|
276 iCurrAckHandle = justAllocdHandle; |
|
277 } |
|
278 } |
|
279 |
|
280 TryToAllocQueuedBuffer(); |
|
281 } |
|
282 |
|
283 void CHostMBufPool::TryToAllocQueuedBuffer() |
|
284 { |
|
285 LOG_FUNC |
|
286 if (!iWaitingAllocPool.IsEmpty() && !IsActive()) |
|
287 { |
|
288 TPoolBuffer* needsAlloc = iWaitingAllocPool.First(); |
|
289 iWaitingAllocPool.Remove(*needsAlloc); |
|
290 AllocNewBuffer(*needsAlloc); |
|
291 } |
|
292 } |
|
293 |
|
294 void CHostMBufPool::AllocNewBuffer(TPoolBuffer& aBuffer) |
|
295 { |
|
296 LOG_FUNC |
|
297 ASSERT_DEBUG(!iBufferBeingAllocd); |
|
298 iBufferBeingAllocd = &aBuffer; |
|
299 iMBufRequester.Alloc(aBuffer.iMBufChain, KLinkMgrIncomingBufferSize, iStatus); |
|
300 SetActive(); |
|
301 } |
|
302 |
|
303 void CHostMBufPool::HostNumberOfCompletedPacketsL(THCIConnHandle aConnH, TUint16 aNumPackets) |
|
304 { |
|
305 RArray<THCIConnectionHandle> connHandles; |
|
306 connHandles.AppendL(aConnH); |
|
307 CleanupClosePushL(connHandles); |
|
308 |
|
309 RArray<THCINumOfCompletedPackets> numPackets; |
|
310 numPackets.AppendL(aNumPackets); |
|
311 CleanupClosePushL(numPackets); |
|
312 |
|
313 CHostNumberOfCompletedPacketsCommand* cmd = CHostNumberOfCompletedPacketsCommand::NewL(1, connHandles, numPackets); |
|
314 // Ownership of the arrays is taken by the command object. |
|
315 CleanupStack::Pop(2, &connHandles); // &numPackets, &connHandles |
|
316 |
|
317 // This is a priority command as we want to try to get this out as soon as possible (and not wait |
|
318 // for all normal control aspects to be processed). This command shouldn't normally consume any credits |
|
319 // so as a priority command it has little impact. |
|
320 // Ownership of cmd transfered even if MhcqAddPriorityCommandL leaves |
|
321 iCmdQ.MhcqAddPriorityCommandL(cmd, *this); |
|
322 } |
|
323 |
|
324 void CHostMBufPool::MhcqcCommandEventReceived(const THCIEventBase& aEvent, const CHCICommandBase* /*aRelatedCommand*/) |
|
325 { |
|
326 LOG_FUNC |
|
327 // We don't expect a non-error event back because we're only sending Host_Number_of_Completed_Packet commands |
|
328 if(aEvent.EventCode() == ECommandCompleteEvent) |
|
329 { |
|
330 const THCICommandCompleteEvent& completeEvent = THCICommandCompleteEvent::Cast(aEvent); |
|
331 if(completeEvent.CommandOpcode() == KHostNumberOfCompletedPacketsOpcode) |
|
332 { |
|
333 // a regular error for a Host_Number_Of_Completed_Packets command |
|
334 TInt err = CHciUtil::SymbianErrorCode(completeEvent.ErrorCode()); |
|
335 if(err != KErrNone) // we shouldn't get a non-erroring event back, but just in case |
|
336 { |
|
337 Error(err); |
|
338 } |
|
339 } |
|
340 else // an unexpected command complete event |
|
341 { |
|
342 LOG1(_L8("CHostMBufPool: Unexpected HCI command complete event; opcode = 0x%04x"), completeEvent.CommandOpcode()); |
|
343 DEBUG_PANIC_LINENUM; |
|
344 } |
|
345 } |
|
346 else // some unexpected event |
|
347 { |
|
348 LOG1(_L8("CHostMBufPool: Unexpected HCI event received; code = 0x%02x"), aEvent.EventCode()); |
|
349 DEBUG_PANIC_LINENUM; |
|
350 } |
|
351 } |
|
352 |
|
353 void CHostMBufPool::MhcqcCommandErrored(TInt aErrorCode, const CHCICommandBase* /*aCommand*/) |
|
354 { |
|
355 LOG_FUNC |
|
356 Error(aErrorCode); |
|
357 } |
|
358 |
|
359 TInt CHostMBufPool::RunError(TInt aError) |
|
360 { |
|
361 LOG_FUNC |
|
362 if(iBufferBeingAllocd) |
|
363 { |
|
364 TPoolBuffer* justFailedToAlloc = iBufferBeingAllocd; |
|
365 iBufferBeingAllocd = NULL; |
|
366 // Add to wait for alloc queue - we may get another chance |
|
367 iWaitingAllocPool.AddFirst(*justFailedToAlloc); |
|
368 } |
|
369 Error(aError); |
|
370 return KErrNone; |
|
371 } |
|
372 |
|
373 void CHostMBufPool::Error(TInt IF_FLOGGING(aError)) |
|
374 { |
|
375 LOG_FUNC |
|
376 // So there has been some internal error when handling controller to host |
|
377 // flow control. Tough, we've done our best for now - the only real thing |
|
378 // that might be worth doing is a hard reset to start-up clean. |
|
379 LOG1(_L8("CHostMBufPool: ERROR (%d) - inbound data to the stack may stall soon"), aError); |
|
380 } |
|
381 |
|
382 #endif |