|
1 // Copyright (c) 2004-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 <bluetooth/logger.h> |
|
17 |
|
18 #include "btsockettimer.h" |
|
19 |
|
20 #include "L2CapSDU.h" |
|
21 #include "L2CapPDU.h" |
|
22 #include "l2util.h" |
|
23 #include "L2CapDebugControlInterface.h" |
|
24 |
|
25 #ifdef _DEBUG |
|
26 #include "L2CapDebugControlInterface.h" |
|
27 #endif |
|
28 |
|
29 #ifdef __FLOG_ACTIVE |
|
30 _LIT8(KLogComponent, LOG_COMPONENT_L2CAP_SDU); |
|
31 #endif |
|
32 |
|
33 CL2CapSDU* CL2CapSDU::NewLC(RMBufChain& aSDUData, |
|
34 ML2CapSDUHandler& aParent, |
|
35 TUint16 aPDUSize, |
|
36 TBool aIsBasicDataVersion, |
|
37 TUint16 aTimerDuration) |
|
38 { |
|
39 LOG_STATIC_FUNC |
|
40 CL2CapSDU* self = new(ELeave) CL2CapSDU(aParent); |
|
41 CleanupStack::PushL(self); |
|
42 self->ConstructL(aSDUData, aIsBasicDataVersion, aTimerDuration, aPDUSize); |
|
43 return self; |
|
44 } |
|
45 |
|
46 |
|
47 CL2CapSDU* CL2CapSDU::NewL(RMBufChain& aSDUData, |
|
48 ML2CapSDUHandler& aParent, |
|
49 TUint16 aPDUSize, |
|
50 TBool aIsBasicDataVersion, |
|
51 TUint16 aTimerDuration) |
|
52 { |
|
53 LOG_STATIC_FUNC |
|
54 CL2CapSDU* self = NewLC(aSDUData, aParent, aPDUSize, aIsBasicDataVersion, aTimerDuration); |
|
55 CleanupStack::Pop(); |
|
56 return self; |
|
57 } |
|
58 |
|
59 CL2CapSDU::CL2CapSDU(ML2CapSDUHandler& aParent) |
|
60 : iParent(aParent), |
|
61 iPDUs(_FOFF(HL2CapPDU, iLink)), |
|
62 iCurrentPDU(iPDUs) |
|
63 { |
|
64 LOG_FUNC |
|
65 L2CAP_DEBUG(ObjectAllocation(L2capDebugInfo::ESDU, |
|
66 L2capDebugInfo::EAllocated)); |
|
67 } |
|
68 |
|
69 void CL2CapSDU::ConstructL(RMBufChain& aSDUData, TBool aIsBasicDataVersion, TUint16 aTimerDuration, TUint16 aPDUSize) |
|
70 { |
|
71 LOG_FUNC |
|
72 LEAVEIFERRORL(SegmentSDUIntoPDUs(aSDUData, aIsBasicDataVersion, aPDUSize)); |
|
73 |
|
74 // Start the flush timer if required. |
|
75 StartFlushTimer(aTimerDuration); |
|
76 } |
|
77 |
|
78 CL2CapSDU::~CL2CapSDU() |
|
79 { |
|
80 LOG_FUNC |
|
81 iLink.Deque(); |
|
82 iCurrentPDU.SetToFirst(); |
|
83 HL2CapPDU* pduPtr; |
|
84 while(iCurrentPDU) |
|
85 { |
|
86 pduPtr = iCurrentPDU++; |
|
87 pduPtr->iLink.Deque(); |
|
88 delete pduPtr; |
|
89 } |
|
90 |
|
91 if(iFlushTimerRunning) |
|
92 { |
|
93 BTSocketTimer::Remove(iFlushTimerEntry); |
|
94 } |
|
95 |
|
96 L2CAP_DEBUG(ObjectAllocation(L2capDebugInfo::ESDU, |
|
97 L2capDebugInfo::EDeleted)); |
|
98 } |
|
99 |
|
100 /* |
|
101 * Function to return any additional SDU L2CAP overhead. This is in addition to any PDU |
|
102 * overhead as defined by HL2CapPDU::GetPDUOverhead. |
|
103 */ |
|
104 /*static*/ TInt CL2CapSDU::GetSDUOverhead(TBool aBasicMode) |
|
105 { |
|
106 LOG_STATIC_FUNC |
|
107 if (aBasicMode) |
|
108 { |
|
109 return HL2CapPDU::KPDUHeaderLength; |
|
110 } |
|
111 |
|
112 // Non-basic mode overhead |
|
113 return HL2CapPDU::KSDULengthFieldLength; |
|
114 } |
|
115 |
|
116 TInt CL2CapSDU::SegmentSDUIntoPDUs(RMBufChain& aSDUData, TBool aIsBasicDataVersion, TUint16 aPDUPayloadSize) |
|
117 { |
|
118 LOG_FUNC |
|
119 TInt sduLength = aSDUData.Length(); |
|
120 TInt rerr = KErrNone; |
|
121 |
|
122 if(aIsBasicDataVersion) |
|
123 { |
|
124 // This is basic mode. The entire SDU should be placed into |
|
125 // a B-Frame PDU. |
|
126 HBFramePDU* pdu = HBFramePDU::New(aSDUData, aPDUPayloadSize); |
|
127 if(pdu) |
|
128 { |
|
129 iPDUs.AddLast(*pdu); |
|
130 L2CAP_DEBUG_PDU(PDUTimer(L2capDebugInfo::EBFrameCreated, pdu)); |
|
131 } |
|
132 else |
|
133 { |
|
134 rerr = KErrNoMemory; |
|
135 } |
|
136 } |
|
137 else |
|
138 { |
|
139 // Check if the SDU needs to be segmented. |
|
140 if(aPDUPayloadSize >= sduLength) |
|
141 { |
|
142 HIFramePDU* pdu = HIFramePDU::New(aSDUData, EUnsegmentedL2CapSDU); |
|
143 if(pdu) |
|
144 { |
|
145 iPDUs.AddLast(*pdu); |
|
146 L2CAP_DEBUG_PDU(PDUTimer(L2capDebugInfo::EUnsegmentedFrameCreated, pdu)); |
|
147 } |
|
148 else |
|
149 { |
|
150 rerr = KErrNoMemory; |
|
151 } |
|
152 } |
|
153 else |
|
154 { |
|
155 // Work backwards through the SDU segmenting it into PDU's. The first segment will always |
|
156 // contain an extra SDU length field so this is taken into account when working out the |
|
157 // size of the last segment below. |
|
158 TInt sduPosition = sduLength - ((sduLength + HL2CapPDU::KSDULengthFieldLength) % aPDUPayloadSize); |
|
159 |
|
160 // If the PDU size is a factor of the SDU length then start from the |
|
161 // SDU length - PDU size. |
|
162 if(sduPosition == sduLength) |
|
163 { |
|
164 sduPosition -= aPDUPayloadSize; |
|
165 } |
|
166 |
|
167 HIFramePDU* pdu = NULL; |
|
168 RMBufChain pduData; |
|
169 |
|
170 TRAP(rerr, aSDUData.SplitL(sduPosition, pduData)); |
|
171 if(rerr == KErrNone) |
|
172 { |
|
173 pdu = HIFramePDU::New(pduData, EEndOfL2CapSDU); |
|
174 if(pdu) |
|
175 { |
|
176 sduPosition -= aPDUPayloadSize; |
|
177 iPDUs.AddFirst(*pdu); |
|
178 L2CAP_DEBUG_PDU(PDUTimer(L2capDebugInfo::EIFrameCreated, pdu)); |
|
179 |
|
180 while(sduPosition > 0) |
|
181 { |
|
182 __ASSERT_DEBUG(sduPosition >= 0, Panic(EL2CAPSDUPositionNegativeDuringSegmentation)); |
|
183 |
|
184 TRAP(rerr, aSDUData.SplitL(sduPosition, pduData)); |
|
185 if(rerr == KErrNone) |
|
186 { |
|
187 pdu = HIFramePDU::New(pduData, EContinuationOfL2CapSDU); |
|
188 if(pdu) |
|
189 { |
|
190 sduPosition -= aPDUPayloadSize; |
|
191 iPDUs.AddFirst(*pdu); |
|
192 } |
|
193 else |
|
194 { |
|
195 rerr = KErrNoMemory; |
|
196 break; |
|
197 } |
|
198 } |
|
199 else |
|
200 { |
|
201 break; |
|
202 } |
|
203 } |
|
204 |
|
205 if(rerr == KErrNone) |
|
206 { |
|
207 // Create and add the first PDU. |
|
208 pdu = HIFramePDU::New(aSDUData, EStartOfL2CapSDU); |
|
209 if(pdu) |
|
210 { |
|
211 pdu->SetSDUSize(static_cast<TUint16>(sduLength)); |
|
212 iPDUs.AddFirst(*pdu); |
|
213 } |
|
214 else |
|
215 { |
|
216 rerr = KErrNoMemory; |
|
217 } |
|
218 } |
|
219 } |
|
220 else |
|
221 { |
|
222 rerr = KErrNoMemory; |
|
223 } |
|
224 } |
|
225 } |
|
226 } |
|
227 |
|
228 if(rerr == KErrNone) |
|
229 { |
|
230 // Set the current PDU pointer to the first PDU. |
|
231 iCurrentPDU.SetToFirst(); |
|
232 } |
|
233 return rerr; |
|
234 } |
|
235 |
|
236 TBool CL2CapSDU::GetPDU(HL2CapPDU*& aReturnedPDU) |
|
237 { |
|
238 LOG_FUNC |
|
239 TBool isLastPDU = EFalse; |
|
240 if(!iPDUs.IsEmpty()) |
|
241 { |
|
242 aReturnedPDU = iCurrentPDU++; |
|
243 |
|
244 // TODO: this was put in as a fix and is in needed in general, but it also |
|
245 // actually hoses up some logic, i.e. CurrentPDUIsFirstPDU will always return |
|
246 // true. We either need a separate link for the list of PDUs inside the SDU, |
|
247 // or if possible just get rid of iCurrentPDU and have a iFirstPDU flag |
|
248 // set to one during initial segmentation. |
|
249 // Note - we the iCurrentPDU/iFirstPDU business is actually only used in two cases: |
|
250 // 1. to repack current SDU when PDU size has changed on a live connection - currently |
|
251 // unused and unnecessary. |
|
252 // 2. on flush timer expiry, to mark already sent packets as flushed and purge the |
|
253 // unsent ones - currently unused, but may get implemented in the future. However |
|
254 // the marking of the packets may be done their current owner, which is a data |
|
255 // controller or the muxer, so again this is unnecessary. |
|
256 // Conclusion: refactor to simplify. |
|
257 aReturnedPDU->iLink.Deque(); |
|
258 |
|
259 if(iCurrentPDU == NULL) |
|
260 { |
|
261 // At the end of the PDU list. |
|
262 L2CAP_DEBUG_PDU(PDUTimer(L2capDebugInfo::EGetPDUCalled, aReturnedPDU)); |
|
263 isLastPDU = ETrue; |
|
264 } |
|
265 } |
|
266 |
|
267 return isLastPDU; |
|
268 } |
|
269 |
|
270 |
|
271 /*static*/ TInt CL2CapSDU::FlushTimerExpired(TAny* aL2CapSDU) |
|
272 { |
|
273 LOG_STATIC_FUNC |
|
274 CL2CapSDU* sdu = reinterpret_cast<CL2CapSDU*>(aL2CapSDU); |
|
275 sdu->HandleFlushTimerExpired(); |
|
276 return EFalse; |
|
277 } |
|
278 |
|
279 void CL2CapSDU::HandleFlushTimerExpired() |
|
280 { |
|
281 LOG_FUNC |
|
282 // Note that the timer is no longer running. |
|
283 iFlushTimerRunning = EFalse; |
|
284 |
|
285 // Any PDU's that have already been sent need to be |
|
286 // informed of the flush. |
|
287 TDblQueIter<HL2CapPDU> pduIter(iPDUs); |
|
288 TBool done = EFalse; |
|
289 |
|
290 HL2CapPDU* pduPtr; |
|
291 while((pduPtr = pduIter++) != NULL && !done) |
|
292 { |
|
293 if(pduPtr == iCurrentPDU) |
|
294 { |
|
295 done = ETrue; |
|
296 } |
|
297 else |
|
298 { |
|
299 pduPtr->SetPDUFlushed(); |
|
300 } |
|
301 } |
|
302 |
|
303 // The remaining PDU's (if any) that have not yet |
|
304 // been sent can be deleted. |
|
305 while((pduPtr = iCurrentPDU++) != NULL) |
|
306 { |
|
307 pduPtr->iLink.Deque(); |
|
308 delete pduPtr; |
|
309 } |
|
310 |
|
311 // Inform the SDU queue. |
|
312 iParent.ProcessFlushTimerExpiry(*this); |
|
313 } |
|
314 |
|
315 TBool CL2CapSDU::IsSDUEmpty() const |
|
316 { |
|
317 LOG_FUNC |
|
318 return iPDUs.IsEmpty(); |
|
319 } |
|
320 |
|
321 |
|
322 TBool CL2CapSDU::CurrentPDUIsFirstPDU() |
|
323 { |
|
324 LOG_FUNC |
|
325 return iPDUs.IsFirst(iCurrentPDU); |
|
326 } |
|
327 |
|
328 void CL2CapSDU::StartFlushTimer(TUint16 aTimerDuration) |
|
329 { |
|
330 LOG_FUNC |
|
331 __ASSERT_DEBUG(!iFlushTimerRunning, Panic(EL2CAPAttemptToRestartFlushTimer)); |
|
332 |
|
333 // Only start the timer if the duration is valid. |
|
334 if(aTimerDuration >= TL2CapConfig::EMinDataObsolescenceTimeout && |
|
335 aTimerDuration != KInfiniteFlush) |
|
336 { |
|
337 TCallBack cb(FlushTimerExpired, this); |
|
338 iFlushTimerEntry.Set(cb); |
|
339 // Timer period is a factor of the duration parameter and 0.625ms |
|
340 BTSocketTimer::Queue(aTimerDuration*625, iFlushTimerEntry); |
|
341 iFlushTimerRunning = ETrue; |
|
342 } |
|
343 } |
|
344 |
|
345 TInt CL2CapSDU::ChangeSDUSegmentation(TBool aIsBasicDataVersion, TUint16 aPDUSize) |
|
346 { |
|
347 LOG_FUNC |
|
348 __ASSERT_DEBUG(CurrentPDUIsFirstPDU(), Panic(EL2CAPAttemptToChangeSegmentationForPartiallySentSDU)); |
|
349 |
|
350 TInt rerr = KErrNone; |
|
351 RMBufChain sduData; |
|
352 |
|
353 // Get the current SDU data, and delete all current |
|
354 // PDU's |
|
355 TDblQueIter<HL2CapPDU> iter(iPDUs); |
|
356 HL2CapPDU* pduPtr; |
|
357 while((pduPtr = iter++) != NULL) |
|
358 { |
|
359 pduPtr->AppendPayloadToBuffer(sduData); |
|
360 pduPtr->iLink.Deque(); |
|
361 delete pduPtr; |
|
362 } |
|
363 |
|
364 rerr = SegmentSDUIntoPDUs(sduData, aIsBasicDataVersion, aPDUSize); |
|
365 |
|
366 return rerr; |
|
367 } |
|
368 |
|
369 |
|
370 #ifdef _DEBUG |
|
371 TInt CL2CapSDU::DebugManualFlush() |
|
372 { |
|
373 LOG_FUNC |
|
374 TInt rcode = -1; |
|
375 |
|
376 if(!IsSDUEmpty()) |
|
377 { |
|
378 RMBuf* buf = iPDUs.First()->PDUBuffer().First(); |
|
379 if(buf->Length() > 8) |
|
380 { |
|
381 rcode = buf->Get(8); |
|
382 } |
|
383 else |
|
384 { |
|
385 buf = buf->Next(); |
|
386 if(buf) |
|
387 { |
|
388 rcode = buf->Get(0); |
|
389 } |
|
390 } |
|
391 } |
|
392 |
|
393 if(iFlushTimerRunning) |
|
394 { |
|
395 BTSocketTimer::Remove(iFlushTimerEntry); |
|
396 } |
|
397 HandleFlushTimerExpired(); |
|
398 return rcode; |
|
399 } |
|
400 #endif |