|
1 // Copyright (c) 1999-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 // Rfcomm muxer |
|
15 // |
|
16 // |
|
17 |
|
18 #include <bluetooth/logger.h> |
|
19 |
|
20 #include <bt_sock.h> |
|
21 |
|
22 #include "rfcommmuxer.h" |
|
23 #include "rfcomm.h" |
|
24 #include "rfcommutil.h" |
|
25 #include "rfcommframe.h" |
|
26 #include "rfcommtypes.h" |
|
27 #include "rfcommfcs.h" |
|
28 #include "rfcommsap.h" |
|
29 |
|
30 #include "rfcommflow.h" |
|
31 #include "rfcommmuxchannel.h" |
|
32 #include "AsyncErrorKicker.h" |
|
33 |
|
34 #ifdef __FLOG_ACTIVE |
|
35 _LIT8(KLogComponent, LOG_COMPONENT_RFCOMM); |
|
36 #endif |
|
37 |
|
38 // Disable int to char warnings |
|
39 #ifdef __WINS__ |
|
40 #pragma warning( disable : 4244 ) |
|
41 #endif |
|
42 |
|
43 #ifdef __FLOG_ACTIVE |
|
44 _LIT(KCommandText, "command"); |
|
45 _LIT(KResponseText, "response"); |
|
46 #endif //__FLOG_ACTIVE |
|
47 |
|
48 const TUint8 KBitsForNumberInTInt = 32 - 1; // -1 is because we don't count the sign bit |
|
49 |
|
50 CRfcommMuxer* CRfcommMuxer::NewL(CRfcommProtocol& aProt, CProtocolBase& aL2CAP, |
|
51 CMuxChannelStateFactory& aFactory) |
|
52 /** |
|
53 Factory function for CRfcommMuxer - called when local device initiates the connection. |
|
54 |
|
55 Note the CProtocolBase passed in so that the muxer can create its own L2CAP SAP |
|
56 **/ |
|
57 { |
|
58 CRfcommMuxer* mux= new (ELeave) CRfcommMuxer(aProt,KInitiationDirectionOutgoing); |
|
59 CleanupStack::PushL(mux); |
|
60 mux->ConstructL(aFactory, aL2CAP); |
|
61 CleanupStack::Pop(); |
|
62 return mux; |
|
63 } |
|
64 |
|
65 CRfcommMuxer* CRfcommMuxer::NewL(CRfcommProtocol& aProt, CServProviderBase* aSAP, |
|
66 CMuxChannelStateFactory& aFactory) |
|
67 /** |
|
68 Factory function for CRfcommMuxer - called when remote device initiates the connection. |
|
69 |
|
70 Note the CServProviderBase passed in, which becomes the L2CAP SAP used by this muxer. |
|
71 |
|
72 @param aSAP This is a connected L2CAP sap |
|
73 @param aFactory The Mux channel state factory |
|
74 @return A new Muxer in the right state |
|
75 **/ |
|
76 { |
|
77 CRfcommMuxer* mux= new (ELeave) CRfcommMuxer(aProt,KInitiationDirectionIncoming); |
|
78 CleanupStack::PushL(mux); |
|
79 mux->ConstructL(aFactory, aSAP); |
|
80 CleanupStack::Pop(); |
|
81 return mux; |
|
82 } |
|
83 |
|
84 |
|
85 CRfcommMuxer::CRfcommMuxer(CRfcommProtocol& aProt, TInitiationDirection aDirection) |
|
86 : iSAPs(_FOFF(CRfcommSAP, iLink)), |
|
87 iOutboundQ(_FOFF(CRfcommFrame, iLink)), |
|
88 iResponseQ(_FOFF(CRfcommFrame, iLink)), |
|
89 iOutboundQLength(0), |
|
90 iProtocol(aProt), |
|
91 iDirection(aDirection), |
|
92 iMuxIdleTimeout(KRfcommMuxIdleTimeoutOpening), |
|
93 iCanSend(ETrue), |
|
94 iBlockedSAPs(_FOFF(CRfcommSAP, iLink)), |
|
95 iDataFramesSent(0), |
|
96 iL2CAPSendBlocked(EFalse), |
|
97 iTriedCBFC(EFalse), |
|
98 iCurrentCredit(0), |
|
99 iInitialTxCredit(0), |
|
100 iFlowStrategy(0) |
|
101 { |
|
102 TCallBack cb(IdleTimerExpired, this); |
|
103 iIdleTimerEntry.Set(cb); |
|
104 } |
|
105 |
|
106 void CRfcommMuxer::ConstructL(CMuxChannelStateFactory& aFactory, CProtocolBase& aL2CAP) |
|
107 { |
|
108 // Create a SAP for this to use |
|
109 iBoundSAP=aL2CAP.NewSAPL(KSockSeqPacket); |
|
110 iMuxChannel = new (ELeave) CRfcommMuxChannel(aFactory, *this, *iBoundSAP, CMuxChannelStateFactory::EClosed); |
|
111 |
|
112 // We now have a bound L2Cap SAP. |
|
113 // Use it to setup L2Cap config for RFComm. |
|
114 TL2CapConfigPkg configPkg; |
|
115 iProtocol.SetL2CapConfig(configPkg); |
|
116 User::LeaveIfError(iBoundSAP->SetOption(KSolBtL2CAP, KL2CAPUpdateChannelConfig, configPkg)); |
|
117 |
|
118 CommonConstructL(); |
|
119 } |
|
120 |
|
121 void CRfcommMuxer::ConstructL(CMuxChannelStateFactory& aFactory, CServProviderBase* aSAP) |
|
122 { |
|
123 // SAP has been provided for this to use, so start it |
|
124 __ASSERT_DEBUG(aSAP!=NULL, Panic(ERfcommNullSAPForMuxer)); |
|
125 iBoundSAP=aSAP; |
|
126 iBoundSAP->Start(); |
|
127 TBTSockAddr btAddr; |
|
128 aSAP->RemName(btAddr); |
|
129 iRemoteAddr=btAddr.BTAddr(); |
|
130 iMuxChannel = new (ELeave) CRfcommMuxChannel(aFactory, *this, *iBoundSAP, CMuxChannelStateFactory::ELinkUp); |
|
131 CommonConstructL(); |
|
132 } |
|
133 |
|
134 void CRfcommMuxer::CommonConstructL() |
|
135 { |
|
136 iBoundSAP->SetNotify(this); |
|
137 TCallBack cb(TryToSendCallbackStatic,this); |
|
138 iSendCallback=new (ELeave) CAsyncCallBack(cb, ECAsyncImmediatePriority); |
|
139 iNextPacket=HBufC8::NewL(KRfcommDefaultMTU); // This will do for now |
|
140 #ifdef NO_CBFC |
|
141 iFlowStrategy = TRfcommFlowStrategyNonCreditBased::NewL(*this); |
|
142 #else |
|
143 if(iProtocol.CBFCDisallowed()) |
|
144 { |
|
145 SetFlowType(CRfcommFlowStrategyFactory::EFlowNonCreditBased); |
|
146 iTriedCBFC = ETrue; |
|
147 } |
|
148 else |
|
149 { |
|
150 SetFlowType(CRfcommFlowStrategyFactory::EFlowInitial); |
|
151 } |
|
152 #endif |
|
153 } |
|
154 |
|
155 CRfcommMuxer::~CRfcommMuxer() |
|
156 { |
|
157 LOG(_L("CRfcommMuxer::~CRfcommMuxer")); |
|
158 |
|
159 DequeIdleTimer(); |
|
160 DeleteQueuedFrames(); |
|
161 |
|
162 // remove the references to this muxer in any SAPs it is/was linked to |
|
163 TDblQueIter<CRfcommSAP> iter(iSAPs); |
|
164 CRfcommSAP* sap; |
|
165 while(iter) |
|
166 { |
|
167 sap=iter++; |
|
168 if (sap) |
|
169 { |
|
170 sap->iMux = NULL; |
|
171 sap->iLink.Deque(); |
|
172 } |
|
173 } |
|
174 |
|
175 |
|
176 |
|
177 delete iSendCallback; |
|
178 delete iBoundSAP; |
|
179 delete iNextPacket; |
|
180 delete iMuxChannel; |
|
181 } |
|
182 |
|
183 /***************************************************************************/ |
|
184 /* |
|
185 Commands from the protocol. |
|
186 |
|
187 */ |
|
188 |
|
189 |
|
190 void CRfcommMuxer::Bind(TBTDevAddr& aAddr) |
|
191 /** |
|
192 Bind this mux to the given remote address |
|
193 |
|
194 Calling this function implies that this end is to be the |
|
195 initiator (direction bit = 1). The bound sap will be used to |
|
196 create an L2CAP link to the remote end over which the RFCOMM |
|
197 frames will flow. The muxchannel will then run this channel. |
|
198 **/ |
|
199 { |
|
200 iRemoteAddr=aAddr; |
|
201 iMuxChannel->SetAddress(aAddr); |
|
202 } |
|
203 |
|
204 void CRfcommMuxer::AddSAP(CRfcommSAP& aSAP) |
|
205 /** |
|
206 Adds a sap to the Q for this mux. |
|
207 **/ |
|
208 { |
|
209 DequeIdleTimer(); |
|
210 iSAPs.AddFirst(aSAP); |
|
211 if(iMuxChannel->IsOpen()) |
|
212 aSAP.MuxUp(); |
|
213 else |
|
214 iMuxChannel->Open(); // Eventually calls back |
|
215 } |
|
216 |
|
217 void CRfcommMuxer::DetachSAP(CRfcommSAP& aSAP) |
|
218 /** |
|
219 Detach this sap. |
|
220 |
|
221 If it's the last one, shut down. We clean out the outbound and |
|
222 response queues as we no longer want these frames since the sap |
|
223 has gone. |
|
224 **/ |
|
225 { |
|
226 aSAP.iLink.Deque(); |
|
227 aSAP.iMux=0; |
|
228 |
|
229 ClearOutboundQueue(aSAP); |
|
230 ClearResponseQueue(aSAP); |
|
231 CheckForIdle(ETrue); |
|
232 } |
|
233 |
|
234 void CRfcommMuxer::GetInboundServerChannelsInUse(TFixedArray<TBool, KMaxRfcommServerChannel>& aChannelInUse) |
|
235 /** |
|
236 Identify inbound server channels in use for connected SAPs. |
|
237 |
|
238 aChannelInUse is an array indexed by (server channel - 1) and on return |
|
239 aChannelInUse will be updated with any inbound server channels that |
|
240 are currently in use by connected SAPs. |
|
241 **/ |
|
242 { |
|
243 TDblQueIter<CRfcommSAP> iter(iSAPs); |
|
244 CRfcommSAP* sap; |
|
245 |
|
246 // Only interested in cloned SAPs as this identifies if the SAP is for |
|
247 // an inbound connection |
|
248 while(iter) |
|
249 { |
|
250 sap=iter++; |
|
251 if (sap->IsCloned()) |
|
252 { |
|
253 aChannelInUse[sap->ServerChannel() - 1] = ETrue; |
|
254 } |
|
255 } |
|
256 |
|
257 iter=iBlockedSAPs; |
|
258 while(iter) |
|
259 { |
|
260 sap=iter++; |
|
261 if (sap->IsCloned()) |
|
262 { |
|
263 aChannelInUse[sap->ServerChannel() - 1] = ETrue; |
|
264 } |
|
265 } |
|
266 } |
|
267 |
|
268 /****************************************************************************/ |
|
269 /* |
|
270 Notifications from the MSocketNotify interface |
|
271 |
|
272 */ |
|
273 |
|
274 void CRfcommMuxer::NewData(TUint aCount) |
|
275 /** |
|
276 Called when new data is available. |
|
277 This is called each time a new packet of data arrives from L2CAP. |
|
278 We get to the data by calling GetData on the SAP. |
|
279 |
|
280 Frames never span a L2CAP packet, and are one per packet. |
|
281 |
|
282 This assumes a packet interface from L2CAP |
|
283 |
|
284 @param aCount Number of new packets waiting |
|
285 **/ |
|
286 { |
|
287 if (aCount == KNewDataEndofData) |
|
288 { |
|
289 LOG(_L("RFCOMM: L2CAP signalled EndOfData")); |
|
290 Disconnect(); |
|
291 } |
|
292 else |
|
293 { |
|
294 LOG1(_L("RFCOMM: New data, count %d"), aCount); |
|
295 //BLOG: KBlogNewData |
|
296 iPacketsWaiting+=aCount; |
|
297 while(CanProcessNewData() && iPacketsWaiting) |
|
298 { |
|
299 // Get a packet into the buffer |
|
300 TPtr8 data=iNextPacket->Des(); |
|
301 data.SetMax(); |
|
302 iBoundSAP->GetData(data, 0); |
|
303 // process the new data |
|
304 ProcessFrame(); |
|
305 iPacketsWaiting--; |
|
306 } |
|
307 } |
|
308 } |
|
309 |
|
310 |
|
311 void CRfcommMuxer::CanSend() |
|
312 /** |
|
313 Notification that we can now send data to the lower layer. |
|
314 **/ |
|
315 { |
|
316 L2CAPBlocked(EFalse); |
|
317 TryToSend(); |
|
318 } |
|
319 |
|
320 //CBFC |
|
321 CRfcommFlowStrategyFactory::TFlowStrategies CRfcommMuxer::FlowType() |
|
322 { |
|
323 return iFlowStrategy->FlowType(); |
|
324 } |
|
325 |
|
326 #ifdef NO_CBFC |
|
327 TBool CRfcommMuxer::FlowType(CRfcommFlowStrategyFactory::TFlowStrategies /*aFlow*/) |
|
328 { |
|
329 return EFalse; |
|
330 } |
|
331 #else |
|
332 TBool CRfcommMuxer::SetFlowType(CRfcommFlowStrategyFactory::TFlowStrategies aFlow) |
|
333 { |
|
334 if(iTriedCBFC) |
|
335 { |
|
336 LOG(_L("RFCOMM: Requesting new flow strategy again: DISALLOWED")); |
|
337 return EFalse; |
|
338 } |
|
339 |
|
340 switch(aFlow) |
|
341 { |
|
342 case CRfcommFlowStrategyFactory::EFlowCreditBased: |
|
343 LOG(_L("RFCOMM: Requesting CBFC at SAP level")); |
|
344 iTriedCBFC = ETrue; // stop the test being done again |
|
345 iFlowStrategy = &(iProtocol.FlowStrategyFactory()->GetFlowStrategy(aFlow)); |
|
346 break; |
|
347 case CRfcommFlowStrategyFactory::EFlowNonCreditBased: |
|
348 LOG(_L("RFCOMM: Not requesting CBFC at SAP level")); |
|
349 iTriedCBFC = ETrue; // stop the test being done again |
|
350 iFlowStrategy = &(iProtocol.FlowStrategyFactory()->GetFlowStrategy(aFlow)); |
|
351 break; |
|
352 default: |
|
353 iFlowStrategy = &(iProtocol.FlowStrategyFactory()->GetFlowStrategy(CRfcommFlowStrategyFactory::EFlowInitial)); |
|
354 break; |
|
355 } |
|
356 return ETrue; |
|
357 } |
|
358 #endif |
|
359 |
|
360 void CRfcommMuxer::DisallowCBFC() |
|
361 { |
|
362 iCBFCDisallowed = ETrue; |
|
363 } |
|
364 |
|
365 void CRfcommMuxer::AllowCBFC() |
|
366 { |
|
367 iCBFCDisallowed = EFalse; |
|
368 } |
|
369 |
|
370 TRfcommFlowStrategy* CRfcommMuxer::FlowStrategy() |
|
371 { |
|
372 return iFlowStrategy; |
|
373 } |
|
374 |
|
375 |
|
376 |
|
377 void CRfcommMuxer::ConnectComplete(const TDesC8& /*aConnectData*/) |
|
378 /** |
|
379 Version with connection data. |
|
380 |
|
381 Ignore the data (since L2CAP should never provide this!) |
|
382 **/ |
|
383 { |
|
384 ConnectComplete(); |
|
385 } |
|
386 |
|
387 void CRfcommMuxer::ConnectComplete(CServProviderBase& /*aSSP*/) |
|
388 /** |
|
389 Incoming connection completed on listen socket. |
|
390 **/ |
|
391 { |
|
392 Panic(ERfcommIncomingNotSupported); |
|
393 } |
|
394 |
|
395 void CRfcommMuxer::ConnectComplete(CServProviderBase& /*aSSP*/,const TDesC8& /*aConnectData*/) |
|
396 { |
|
397 Panic(ERfcommIncomingNotSupported); |
|
398 } |
|
399 |
|
400 void CRfcommMuxer::CanClose(TDelete /*aDelete*/) |
|
401 /** |
|
402 We must have asked the socket to shutdown, which means we're dying. |
|
403 **/ |
|
404 { |
|
405 LOG1(_L("RFCOMM: CanClose from L2CAP for mux %08x"), this); |
|
406 iMuxChannel->CanClose(); |
|
407 } |
|
408 |
|
409 void CRfcommMuxer::CanClose(const TDesC8& /*aDisconnectData*/,TDelete /*aDelete*/) |
|
410 { |
|
411 Panic(ERfcommDisconnectDataNotSupported); |
|
412 } |
|
413 |
|
414 void CRfcommMuxer::Error(TInt aError,TUint aOperationMask) |
|
415 /** |
|
416 Something's gone wrong. |
|
417 |
|
418 We get told whether it's just this operation or all that are |
|
419 affected, but we'll just assume that all of them are bust for |
|
420 now. |
|
421 **/ |
|
422 { |
|
423 LOG1(_L("RFCOMM: Error on L2CAP sap %d"), aError); |
|
424 if(aOperationMask & MSocketNotify::EErrorIoctl) |
|
425 { |
|
426 // This was an ioctl problem, so pass up to the saps |
|
427 PropagateIoctlCompletion(aError, iIoctlLevel, iIoctlName, NULL); |
|
428 iIoctlName=0; |
|
429 iIoctlLevel=0; |
|
430 } |
|
431 |
|
432 // Pass this on to the Mux channel as well |
|
433 iMuxChannel->Error(aError, aOperationMask); |
|
434 } |
|
435 |
|
436 void CRfcommMuxer::MuxChannelError(TBool aFatal, TInt aError) |
|
437 /** |
|
438 Something's gone wrong on the mux channel |
|
439 |
|
440 If this is a fatal error it means we need a new muxchannel, |
|
441 else we can live with it. Either way we need to error all the |
|
442 existing saps. |
|
443 **/ |
|
444 { |
|
445 LOG1(_L("RFCOMM: Error on Mux %d"), aError) |
|
446 |
|
447 TDblQueIter<CRfcommSAP> iter(iSAPs); |
|
448 CRfcommSAP* sap; |
|
449 |
|
450 // Erroring the saps will remove them from us when we signal a |
|
451 // general error |
|
452 while(iter) |
|
453 { |
|
454 sap=iter++; |
|
455 sap->Error(aError, CRfcommSAP::EErrorGeneral); |
|
456 } |
|
457 iter=iBlockedSAPs; |
|
458 while(iter) |
|
459 { |
|
460 sap=iter++; |
|
461 sap->Error(aError, CRfcommSAP::EErrorGeneral); |
|
462 } |
|
463 |
|
464 if(aFatal) |
|
465 { |
|
466 // The channel is now useless, and so is the mux |
|
467 // Set the address to 0 to stop any more saps attaching |
|
468 DeleteQueuedFrames(); |
|
469 iRemoteAddr=TInt64(0); |
|
470 return; |
|
471 } |
|
472 CheckForIdle(ETrue); |
|
473 } |
|
474 |
|
475 const TBTDevAddr& CRfcommMuxer::RemoteBTAddr() const |
|
476 { |
|
477 return iRemoteAddr; |
|
478 } |
|
479 |
|
480 void CRfcommMuxer::MuxChannelDown() |
|
481 /** |
|
482 The mux channel has gone down. |
|
483 Pass this on to the saps |
|
484 **/ |
|
485 { |
|
486 CloseSAPs(); |
|
487 DeleteQueuedFrames(); // since we can't send or receive any more |
|
488 CheckForIdle(ETrue); |
|
489 } |
|
490 |
|
491 void CRfcommMuxer::CloseSAPs() |
|
492 { |
|
493 TDblQueIter<CRfcommSAP> iter(iSAPs); |
|
494 CRfcommSAP* sap; |
|
495 |
|
496 while(iter) |
|
497 { |
|
498 sap=iter++; |
|
499 sap->LinkDown(); |
|
500 } |
|
501 iter=iBlockedSAPs; |
|
502 while(iter) |
|
503 { |
|
504 sap=iter++; |
|
505 sap->LinkDown(); |
|
506 } |
|
507 } |
|
508 |
|
509 void CRfcommMuxer::MuxChannelUp() |
|
510 /** |
|
511 The mux channel has come up. |
|
512 |
|
513 We need to allocate the buffer for incoming packets. Note this |
|
514 may not be the same as the RFCOMM MTU since we can't guarantee |
|
515 that the other end won't send us omore in an L2CAP packet. |
|
516 **/ |
|
517 { |
|
518 |
|
519 TPckgBuf<TInt> buf; |
|
520 // Find out what the max data size is. |
|
521 iBoundSAP->GetOption(KSolBtL2CAP, KL2CAPInboundMTU, buf); |
|
522 TInt incoming = buf(); |
|
523 |
|
524 TRAPD(err, iNextPacket=iNextPacket->ReAllocL(incoming)); |
|
525 if(err != KErrNone) |
|
526 { |
|
527 // We can't do anything here, so close & error |
|
528 iMuxChannel->Close(); |
|
529 MuxChannelError(ETrue, err); |
|
530 } |
|
531 else |
|
532 { |
|
533 TDblQueIter<CRfcommSAP> iter(iSAPs); |
|
534 CRfcommSAP* sap; |
|
535 |
|
536 while(iter) |
|
537 { |
|
538 sap=iter++; |
|
539 sap->MuxUp(); |
|
540 } |
|
541 iter=iBlockedSAPs; |
|
542 while(iter) |
|
543 { |
|
544 sap=iter++; |
|
545 sap->MuxUp(); |
|
546 } |
|
547 } |
|
548 } |
|
549 |
|
550 void CRfcommMuxer::Disconnect() |
|
551 /** |
|
552 The L2CAP link has disconnected. |
|
553 |
|
554 |
|
555 **/ |
|
556 { |
|
557 iMuxChannel->Disconnect(); |
|
558 } |
|
559 |
|
560 void CRfcommMuxer::Disconnect(TDesC8& /*aDisconnectData*/) |
|
561 { |
|
562 Disconnect(); |
|
563 } |
|
564 |
|
565 void CRfcommMuxer::IoctlComplete(TDesC8* aBuf) |
|
566 /** |
|
567 An ioctl has completed. |
|
568 |
|
569 Broadcast it to all the saps that might be interested |
|
570 **/ |
|
571 { |
|
572 PropagateIoctlCompletion(KErrNone, iIoctlLevel, iIoctlName, aBuf); |
|
573 iIoctlName=0; |
|
574 iIoctlLevel=0; |
|
575 } |
|
576 |
|
577 void CRfcommMuxer::PropagateIoctlCompletion(TInt aError, TUint aIoctlLevel, TUint aIoctlName, TDesC8* aBuf) |
|
578 { |
|
579 TDblQueIter<CRfcommSAP> iter(iSAPs); |
|
580 CRfcommSAP* sap; |
|
581 while(iter) |
|
582 { |
|
583 sap=iter++; |
|
584 sap->IoctlComplete(aError, aIoctlLevel, aIoctlName, aBuf); |
|
585 } |
|
586 iter=iBlockedSAPs; |
|
587 while(iter) |
|
588 { |
|
589 sap=iter++; |
|
590 sap->IoctlComplete(aError, aIoctlLevel, aIoctlName, aBuf); |
|
591 } |
|
592 } |
|
593 |
|
594 void CRfcommMuxer::ConnectComplete() |
|
595 /** |
|
596 Signal from bound sap that connection has occurred. |
|
597 |
|
598 Part of the MSocketNotify interface. This is called when L2CAP |
|
599 has brought up the lower layer link to the remote device. |
|
600 Pass this through to the muxchannel. |
|
601 **/ |
|
602 { |
|
603 FTRACE(TSockAddr addr; |
|
604 iBoundSAP->RemName(addr); |
|
605 LOG1(_L("RFCOMM: Connect complete rcid %d"), |
|
606 addr.Port()); |
|
607 iBoundSAP->LocalName(addr); |
|
608 LOG1(_L("RFCOMM: Connect complete lcid %d"), |
|
609 addr.Port()); |
|
610 ); |
|
611 iMuxChannel->ConnectComplete(); |
|
612 } |
|
613 |
|
614 /**********************************************************************/ |
|
615 /* |
|
616 Commands from the SAP. |
|
617 */ |
|
618 |
|
619 TUint8 CRfcommMuxer::MakeDLCI(TUint8 aServerChannel, TUint8 aDirectionBit) |
|
620 { |
|
621 TUint8 addr = aServerChannel; |
|
622 addr <<= 1; |
|
623 addr |= aDirectionBit; |
|
624 return addr; |
|
625 } |
|
626 |
|
627 TUint8 CRfcommMuxer::MakeOutboundDLCI(TUint8 aServerChannel) |
|
628 /** |
|
629 Given a server channel, add the direction bit for an outbound connection |
|
630 **/ |
|
631 { |
|
632 // Use the inverse of our direction bit |
|
633 return MakeDLCI(aServerChannel, ~iDirection & KDirectionMask); |
|
634 } |
|
635 |
|
636 TUint8 CRfcommMuxer::MakeInboundDLCI(TUint8 aServerChannel) |
|
637 /** |
|
638 Given a server channel, add the direction bit for an inbound connection |
|
639 **/ |
|
640 { |
|
641 return MakeDLCI(aServerChannel, iDirection); |
|
642 } |
|
643 |
|
644 TUint8 CRfcommMuxer::MakeServerChannel(TUint8 aDLCI) |
|
645 /** |
|
646 Given a DLCI, turn that back into a server channel |
|
647 **/ |
|
648 { |
|
649 return aDLCI>>1; // Lose the bottom (direction) bit. |
|
650 } |
|
651 |
|
652 TInt CRfcommMuxer::GetMaxDataSize() const |
|
653 /** |
|
654 Returns the current max data size allowed |
|
655 **/ |
|
656 { |
|
657 return iMuxChannel->MaxDataSize(); |
|
658 } |
|
659 |
|
660 |
|
661 void CRfcommMuxer::Donate(CRfcommSAP& aSAP, TUint8 aCredit) |
|
662 /** |
|
663 Send an empty UIH data frame, cos the other end has been caught short |
|
664 **/ |
|
665 { |
|
666 //Try to find another "Donate" frame in the outbound Q and attach to that... |
|
667 TDblQueIter<CRfcommFrame> iter(iOutboundQ); |
|
668 CRfcommFrame* frmInQ; |
|
669 |
|
670 while(iter) |
|
671 { |
|
672 frmInQ=iter; |
|
673 if(frmInQ->Ctrl()==KUIHCBFCCtrlField && !(frmInQ->DataLength())) |
|
674 { |
|
675 if (frmInQ->SAP() && |
|
676 (frmInQ->SAP()->RemoteAddress() == aSAP.RemoteAddress() && |
|
677 (frmInQ->SAP()->DLCI()) == aSAP.DLCI())) |
|
678 { |
|
679 LOG1(_L("RFCOMM: Old credit in donate frame: %d"), frmInQ->Credit()); |
|
680 TUint8 revisedDonation = aCredit+frmInQ->Credit(); |
|
681 frmInQ->SetCredit(revisedDonation); |
|
682 LOG1(_L("RFCOMM: New credit in donate frame: %d"), frmInQ->Credit()); |
|
683 break; |
|
684 } |
|
685 } |
|
686 iter++; |
|
687 } |
|
688 |
|
689 if(!iter) |
|
690 //If no other "Donate" frame in the outbound Q..... |
|
691 { |
|
692 CRfcommDataFrame* frm=0; |
|
693 |
|
694 frm=NewDataFrame(aSAP.DLCI(), 0, aCredit, &aSAP); |
|
695 if(!frm) |
|
696 { |
|
697 LOG1(_L("RFCOMM: OOM when writing for SAP %08x, queueing SAP Error callback"), &aSAP); |
|
698 aSAP.iErrorKicker->SetError(KErrNoMemory, CRfcommSAP::EErrorOperation); |
|
699 aSAP.iErrorKicker->Call(); |
|
700 } |
|
701 if(!frm) |
|
702 { |
|
703 SetSendBlocked(aSAP, ETrue); |
|
704 return; |
|
705 } |
|
706 |
|
707 EnqueFrame(frm); |
|
708 } |
|
709 |
|
710 FlowStrategy()->ReviseDonatedCredits(aSAP, aCredit); //Rx credit |
|
711 } |
|
712 |
|
713 TInt CRfcommMuxer::Write(CRfcommSAP& aSAP, TUint8 aCredit, const TDesC8& aData) |
|
714 /** |
|
715 Write some data. |
|
716 |
|
717 We attempt to create a datapacket on the Q for this packet. If |
|
718 this fails or the data Q is too long then we return less than |
|
719 the length of the data. In which case we mark the SAP as |
|
720 needing a CanSend, and provide one at a suitable later date. |
|
721 **/ |
|
722 { |
|
723 __ASSERT_DEBUG(aData.Length() <= iMuxChannel->MaxDataSize(), Panic(ERfcommDataTooLong)); |
|
724 CRfcommDataFrame* frm=0; |
|
725 |
|
726 if(iOutboundQLength < KMaxOutboundQLength) |
|
727 { |
|
728 frm=NewDataFrame(aSAP.DLCI(), aData.Length(), aCredit, &aSAP); |
|
729 |
|
730 if(!frm) |
|
731 { |
|
732 LOG1(_L("RFCOMM: OOM when writing for SAP %08x, queueing SAP Error callback"), &aSAP); |
|
733 aSAP.iErrorKicker->SetError(KErrNoMemory, CRfcommSAP::EErrorOperation); |
|
734 aSAP.iErrorKicker->Call(); |
|
735 } |
|
736 } |
|
737 |
|
738 |
|
739 if(!frm) |
|
740 { |
|
741 SetSendBlocked(aSAP, ETrue); |
|
742 return 0; |
|
743 } |
|
744 if(!aData.Length()) |
|
745 { |
|
746 LOG(_L("RFCOMM: Write has sent ZERO length data.")); |
|
747 } |
|
748 |
|
749 frm->PutData(aData); |
|
750 LOG1(_L("RFCOMM: Writing 0x%x"),/*frm->Data()*/aData[0]); |
|
751 //BLOG: (KBlogWriteData, aData[0]) |
|
752 |
|
753 FlowStrategy()->ReviseTransmittedCredits(aSAP); //TxCredit |
|
754 FlowStrategy()->ReviseDonatedCredits(aSAP, aCredit); //Rx credit |
|
755 EnqueFrame(frm); |
|
756 return aData.Length(); |
|
757 } |
|
758 |
|
759 TInt CRfcommMuxer::Ioctl(TUint aLevel, TUint aName, TDes8* aOption) |
|
760 /** |
|
761 A request to do an ioctl onto our lower level sap. |
|
762 |
|
763 If one's already in progress, simply return KErrInUse |
|
764 **/ |
|
765 { |
|
766 if(iIoctlLevel) |
|
767 { |
|
768 return KErrInUse; |
|
769 } |
|
770 else |
|
771 { |
|
772 iIoctlLevel=aLevel; |
|
773 iIoctlName=aName; |
|
774 iBoundSAP->Ioctl(aLevel, aName, aOption); |
|
775 } |
|
776 return KErrNone; |
|
777 } |
|
778 |
|
779 void CRfcommMuxer::CancelIoctl(TUint aLevel, TUint aName) |
|
780 /** |
|
781 One of the RFCOMM SAPs has been asked to cancel an Ioctl which |
|
782 was being run at the L2CAP level. |
|
783 **/ |
|
784 { |
|
785 if(aLevel==iIoctlLevel && iIoctlName==aName) |
|
786 { |
|
787 iBoundSAP->CancelIoctl(aLevel, aName); |
|
788 iIoctlLevel=0; |
|
789 iIoctlName=0; |
|
790 } |
|
791 } |
|
792 |
|
793 TInt CRfcommMuxer::SetOption(TUint aLevel, TUint aName, const TDesC8 &aOption) |
|
794 // Handle SetOption passed down from SAP |
|
795 { |
|
796 return iBoundSAP->SetOption(aLevel, aName, aOption); |
|
797 } |
|
798 |
|
799 TInt CRfcommMuxer::GetOption(TUint aLevel, TUint aName, TDes8 &aOption) |
|
800 // Handle SetOption passed down from SAP |
|
801 { |
|
802 return iBoundSAP->GetOption(aLevel, aName, aOption); |
|
803 } |
|
804 |
|
805 void CRfcommMuxer::SetCanHandleData(CRfcommSAP& aSAP, TBool aCanReceive) |
|
806 /** |
|
807 Inform the muxer whether this sap can handle more data arriving. |
|
808 |
|
809 The mux uses this information to see if it is safe to read more |
|
810 data up from L2CAP. |
|
811 |
|
812 @param aCanReceive False if this sap can't handle any more |
|
813 **/ |
|
814 { |
|
815 LOG2(_L("RFCOMM: CanHandleData sap %08x, State %d"), |
|
816 &aSAP, aCanReceive); |
|
817 |
|
818 //FC |
|
819 // Update the bitmask that controls whether we will take new data from L2CAP |
|
820 SetNoFreeSpace(aSAP.DLCI(), !aCanReceive); |
|
821 |
|
822 if(CanProcessNewData() && iPacketsWaiting) |
|
823 { |
|
824 NewData(0); // Tickle ourselves |
|
825 } |
|
826 } |
|
827 |
|
828 TInt CRfcommMuxer::SendSABM(CRfcommSAP& aSAP) |
|
829 /** |
|
830 Send a SABM for this SAP |
|
831 **/ |
|
832 { |
|
833 LOG2(_L("RFCOMM: SendSABM for SAP %08x, Addr %d"), &aSAP, aSAP.DLCI()); |
|
834 return(TransmitSABM(aSAP.DLCI(), &aSAP)); |
|
835 } |
|
836 |
|
837 TInt CRfcommMuxer::SendMSC(CRfcommSAP& aSAP, TUint8 aFlags) |
|
838 /** |
|
839 Send a MSC command for this SAP. |
|
840 **/ |
|
841 { |
|
842 //FC |
|
843 LOG1(_L("RFCOMM: Sending MSC command for sap %08x"), &aSAP); |
|
844 return TransmitMSC(aSAP.DLCI(), ETrue, aFlags, &aSAP); |
|
845 } |
|
846 |
|
847 TInt CRfcommMuxer::SendMSCRsp(CRfcommSAP& aSAP, TUint8 aFlags) |
|
848 /** |
|
849 Send a MSC response for this SAP. |
|
850 **/ |
|
851 { |
|
852 LOG1(_L("RFCOMM: Sending MSC response for sap %08x"), &aSAP); |
|
853 return TransmitMSC(aSAP.DLCI(), EFalse, aFlags, &aSAP); |
|
854 } |
|
855 |
|
856 TInt CRfcommMuxer::SendRLS(CRfcommSAP& aSAP, TUint8 aStatus) |
|
857 /** |
|
858 Send a Remote Line Status command for this SAP. |
|
859 **/ |
|
860 { |
|
861 LOG1(_L("RFCOMM: Sending RLS cmd for sap %08x"), &aSAP); |
|
862 return TransmitRLS(ETrue, aSAP.DLCI(), aStatus, &aSAP); |
|
863 } |
|
864 |
|
865 TInt CRfcommMuxer::SendRLSRsp(CRfcommSAP& aSAP, TUint8 aStatus) |
|
866 /** |
|
867 Send a Remote Line Status response for this SAP. |
|
868 **/ |
|
869 { |
|
870 LOG1(_L("RFCOMM: Sending RLS resp for sap %08x"), &aSAP); |
|
871 return TransmitRLS(EFalse, aSAP.DLCI(), aStatus, &aSAP); |
|
872 } |
|
873 |
|
874 TInt CRfcommMuxer::SendRPN(CRfcommSAP& aSAP, TBool aCommand, TUint8 aLength, |
|
875 const TRfcommRPNTransaction& aRPNTransaction) |
|
876 /** |
|
877 Send a RPN frame to L2CAP for this SAP. |
|
878 **/ |
|
879 { |
|
880 return TransmitRPN(aSAP.DLCI(), aCommand, aLength, aRPNTransaction, &aSAP); |
|
881 } |
|
882 |
|
883 TInt CRfcommMuxer::SendUA(CRfcommSAP& aSAP) |
|
884 /** |
|
885 Send a UA frame on behalf of the SAP |
|
886 **/ |
|
887 { |
|
888 return TransmitUA(aSAP.DLCI(), &aSAP); |
|
889 } |
|
890 |
|
891 TInt CRfcommMuxer::SendUA(TUint8 aDLCI) |
|
892 /** |
|
893 Send a UA frame on for the DLC |
|
894 **/ |
|
895 { |
|
896 return TransmitUA(aDLCI); |
|
897 } |
|
898 |
|
899 TInt CRfcommMuxer::SendPN(CRfcommSAP& aSAP, const TRfcommPortParams& aParams) |
|
900 /** |
|
901 Send a PN frame on behalf of the SAP |
|
902 This function takes the proposed maximum frame size parameter from the SAP itself. |
|
903 **/ |
|
904 { |
|
905 return TransmitPN(aSAP.DLCI(), ETrue, aParams, &aSAP); |
|
906 } |
|
907 |
|
908 TInt CRfcommMuxer::SendPNResponse(CRfcommSAP& aSAP, const TRfcommPortParams& aParams) |
|
909 /** |
|
910 Send a PN Response frame on behalf of the SAP |
|
911 **/ |
|
912 { |
|
913 return TransmitPN(aSAP.DLCI(), EFalse, aParams, &aSAP); |
|
914 } |
|
915 |
|
916 void CRfcommMuxer::SendDISC(CRfcommSAP& aSAP) |
|
917 /** |
|
918 Disconnect this sap and associated DLC. |
|
919 **/ |
|
920 { |
|
921 TransmitDISC(aSAP.DLCI(), &aSAP); |
|
922 } |
|
923 |
|
924 void CRfcommMuxer::SendDISC(TUint8 aDLCI) |
|
925 /** |
|
926 Disconnect the associated DLC. |
|
927 **/ |
|
928 { |
|
929 TransmitDISC(aDLCI); |
|
930 } |
|
931 |
|
932 void CRfcommMuxer::SendDM(TUint8 aDLCI) |
|
933 { |
|
934 TransmitDM(aDLCI,ETrue); |
|
935 } |
|
936 |
|
937 TInt CRfcommMuxer::SendFCon() |
|
938 /** |
|
939 Sends a FCon command to the peer RFCOMM entity. |
|
940 |
|
941 Used only for internal testing purposes. |
|
942 **/ |
|
943 { |
|
944 //FC |
|
945 return TransmitFCon(ETrue,NULL); |
|
946 } |
|
947 |
|
948 TInt CRfcommMuxer::SendFCoff() |
|
949 /** |
|
950 Sends a FCoff command to the peer RFCOMM entity. |
|
951 |
|
952 Used only for internal testing purposes. |
|
953 **/ |
|
954 { |
|
955 //FC |
|
956 return TransmitFCoff(ETrue,NULL); |
|
957 } |
|
958 |
|
959 /* |
|
960 Timeout from the frames |
|
961 */ |
|
962 |
|
963 void CRfcommMuxer::FrameResponseTimeout(CRfcommFrame* aFrm) |
|
964 /* |
|
965 Called when a frame that needs a response times out |
|
966 |
|
967 */ |
|
968 { |
|
969 // I could code the next bit more efficiently, but don't like obfuscation! |
|
970 TBool done=EFalse; |
|
971 if(aFrm->SAP()!=NULL) |
|
972 done=aFrm->SAP()->HandleFrameResponseTimeout(); |
|
973 |
|
974 if(!done) |
|
975 iMuxChannel->FrameTimeout(aFrm); |
|
976 // The frame will be cleaned up either by the mux error handling or by the SAP |
|
977 // going into the closed or error state. |
|
978 } |
|
979 |
|
980 void CRfcommMuxer::ClearOutboundQueue(CRfcommSAP& aSAP) |
|
981 /** |
|
982 Clear any frames on the outbound queue that point to this SAP. |
|
983 |
|
984 This prevents any frame timeout getting back to a deleted SAP. |
|
985 If a SAP wants to send a frame to the remote end after its |
|
986 death, it must make sure that the iSAP member of the frame does |
|
987 not point to it (ie is null). |
|
988 **/ |
|
989 { |
|
990 TDblQueIter<CRfcommFrame> iter(iOutboundQ); |
|
991 CRfcommFrame* frm; |
|
992 |
|
993 while(iter) |
|
994 { |
|
995 frm=iter++; |
|
996 if(frm->SAP() == &aSAP) |
|
997 { |
|
998 --iOutboundQLength; |
|
999 delete frm; |
|
1000 } |
|
1001 } |
|
1002 } |
|
1003 |
|
1004 void CRfcommMuxer::ClearResponseQueue(CRfcommSAP& aSAP) |
|
1005 /** |
|
1006 Clear the response queue of all frames associated with the SAP. |
|
1007 |
|
1008 The association is based on the frame pointing to the sap, or |
|
1009 being for a DLCI which matches that of the SAP. Frames on DLCI 0 |
|
1010 are not matched by the DLCI matching. |
|
1011 |
|
1012 This is generally called in response to a SAP being closed or |
|
1013 entering an error state. Clearly since the sap is no longer |
|
1014 around, it makes no sense to keep the frames that are awaiting a |
|
1015 response for timeout purposes. |
|
1016 **/ |
|
1017 { |
|
1018 TDblQueIter<CRfcommFrame> iter(iResponseQ); |
|
1019 CRfcommFrame* frm=NULL; |
|
1020 TUint8 frmDLCI=KMuxDLCI; |
|
1021 |
|
1022 TUint8 dlci=aSAP.iDLCI; |
|
1023 if(dlci == KMuxDLCI) |
|
1024 dlci=KMaxRfcommDLCI+1; // This is an impossible DLCI that will never match |
|
1025 while(iter) |
|
1026 { |
|
1027 frm=iter++; |
|
1028 |
|
1029 // determine the DLCI the frame is associated with |
|
1030 if(frm->Type() == KMuxCtrlFrameType) |
|
1031 frmDLCI=static_cast<CRfcommMuxCtrlFrame*>(frm)->DLCI(); |
|
1032 else |
|
1033 frmDLCI=frm->Address(); |
|
1034 |
|
1035 if(frmDLCI==dlci || frm->SAP() == &aSAP) |
|
1036 { |
|
1037 LOG2(_L("RFCOMM: Clearing response for DLCI %d, sap 0x%08x"), frmDLCI, &aSAP); |
|
1038 delete frm; |
|
1039 break; |
|
1040 } |
|
1041 } |
|
1042 } |
|
1043 |
|
1044 /**********************************************************************/ |
|
1045 /* |
|
1046 Internal functions. |
|
1047 */ |
|
1048 |
|
1049 void CRfcommMuxer::SetNoFreeSpace(TInt aDLCI, TBool aVal) |
|
1050 /** |
|
1051 Sets or resets this DLCI as having no space |
|
1052 |
|
1053 @param aVal True if no space |
|
1054 **/ |
|
1055 { |
|
1056 __ASSERT_DEBUG(aDLCI <= KMaxRfcommDLCI, Panic(ERfcommInvalidDLCI)); |
|
1057 if(aVal) |
|
1058 iSAPNoFreeSpaceMask[aDLCI >> 5] |= 1 << (aDLCI % 32); |
|
1059 else |
|
1060 iSAPNoFreeSpaceMask[aDLCI >> 5] &= ~(1 << (aDLCI % 32)); |
|
1061 } |
|
1062 |
|
1063 TBool CRfcommMuxer::CanProcessNewData() const |
|
1064 { |
|
1065 return iFlowStrategy->CanProcessNewData(!iSAPNoFreeSpaceMask[0] && !iSAPNoFreeSpaceMask[1]); |
|
1066 } |
|
1067 |
|
1068 void CRfcommMuxer::SetSendBlocked(CRfcommSAP& aSAP, TBool aIsBlocked) |
|
1069 /** |
|
1070 Mark the state of this SAP as able/unable to send |
|
1071 |
|
1072 This is done by moving the SAP from the active to the blocked |
|
1073 Q. To ensure all saps get a chance, we wake saps from the |
|
1074 front of the blocked Q, but add them to the end, thus |
|
1075 round-robining the chance to send. |
|
1076 **/ |
|
1077 { |
|
1078 |
|
1079 aSAP.iLink.Deque(); |
|
1080 if(aIsBlocked) |
|
1081 /*AddFirst - this helps avoid an infinite loop. |
|
1082 For example SignalSAPsCanSend() unblocks a SAP |
|
1083 then can cause it to be reblocked when it calls |
|
1084 sap->CanSend(). |
|
1085 */ |
|
1086 iBlockedSAPs.AddFirst(aSAP); |
|
1087 else |
|
1088 iSAPs.AddLast(aSAP); |
|
1089 } |
|
1090 |
|
1091 void CRfcommMuxer::ProcessFrame() |
|
1092 /** |
|
1093 Process the frame that is at the start data buffer. |
|
1094 |
|
1095 When this is called, a frame is in the data buffer. The |
|
1096 contents should be verified and processed appropriately. |
|
1097 **/ |
|
1098 { |
|
1099 if(iNextPacket->Length() < KMinFrameLength) |
|
1100 { |
|
1101 LOG(_L("RFCOMM: ** ERROR ** received frame too short")); |
|
1102 return; |
|
1103 } |
|
1104 LOG(_L("1st five bytes of incoming frame....")); |
|
1105 LOGHEXRAW(&(*iNextPacket)[0], 5); |
|
1106 |
|
1107 TUint8 addr=DecodeDLCI((*iNextPacket)[KFrameAddrOffset]); |
|
1108 TUint8 ctrl=(*iNextPacket)[KFrameCtrlOffset]; |
|
1109 // Is this really necessary ? |
|
1110 TBool poll = ((ctrl & KPollFinalBitmask) != 0)?ETrue:EFalse; |
|
1111 //TBool poll = ctrl & KPollFinalBitmask; |
|
1112 ctrl = static_cast<TUint8>(ctrl & ~KPollFinalBitmask); // zero the poll/final bit |
|
1113 |
|
1114 //LOG1(_L("RFCOMM: Frame for dlci %d"), addr); |
|
1115 if(addr!=0 && !iMuxChannel->IsOpen()) |
|
1116 { |
|
1117 LOG1(_L("RFCOMM: Error: Frame for dlci %d before mux channel up - junking"), addr); |
|
1118 return; |
|
1119 } |
|
1120 DecodeLengthAndCredit((poll && ctrl == KUIHCtrlField && addr != KMuxDLCI)); // get the length and the credit if correct packet |
|
1121 |
|
1122 // Frame length is header+data+fcs (fcs is one byte) |
|
1123 if(iCurrentDataLength+iCurrentHeaderLength+1 != iNextPacket->Length()) |
|
1124 { |
|
1125 if(iCurrentDataLength+iCurrentHeaderLength+1 < iNextPacket->Length()) |
|
1126 { |
|
1127 LOG(_L("RFCOMM: Frame shorter than L2CAP packet, junking")); |
|
1128 } |
|
1129 else |
|
1130 { |
|
1131 LOG(_L("RFCOMM: Frame longer than L2CAP packet, junking")); |
|
1132 } |
|
1133 return; |
|
1134 } |
|
1135 |
|
1136 TUint8 fcs=(*iNextPacket)[iCurrentDataLength+iCurrentHeaderLength]; |
|
1137 |
|
1138 if(CheckFCS(fcs,ctrl)) |
|
1139 { |
|
1140 switch (ctrl) |
|
1141 { |
|
1142 case KSABMCtrlField: |
|
1143 if(poll) |
|
1144 { |
|
1145 LOG(_L("Rx: SABM")); |
|
1146 HandleSABM(addr); |
|
1147 } |
|
1148 else |
|
1149 { |
|
1150 // Must ignore SABM with poll bit 0, TS07.10 5.4.4.1 |
|
1151 LOG(_L("RFCOMM: SABM with P/F = 0, ignoring")); |
|
1152 } |
|
1153 break; |
|
1154 |
|
1155 case KUACtrlField: |
|
1156 LOG(_L("Rx: UA")); |
|
1157 HandleUA(addr); |
|
1158 break; |
|
1159 |
|
1160 case KDMCtrlField: |
|
1161 LOG(_L("Rx: DM")); |
|
1162 HandleDM(addr); |
|
1163 break; |
|
1164 |
|
1165 case KDISCCtrlField: |
|
1166 if(poll) |
|
1167 { |
|
1168 LOG(_L("Rx: DISC")); |
|
1169 HandleDISC(addr); |
|
1170 } |
|
1171 else |
|
1172 { |
|
1173 // Must ignore DISC with poll bit 0, TS07.10 5.4.4.1 |
|
1174 LOG(_L("RFCOMM: DISC with P/F = 0, ignoring")); |
|
1175 } |
|
1176 break; |
|
1177 |
|
1178 case KUIHCtrlField: |
|
1179 if(addr == KMuxDLCI) |
|
1180 { |
|
1181 LOG(_L("Rx: UIH Ctrl Frame")); |
|
1182 TRAPD(err, ParseCtrlMessageL()); |
|
1183 if(err !=KErrNone) |
|
1184 { |
|
1185 LOG(_L("RFCOMM: Error in parsing the ctrl frames")); |
|
1186 } |
|
1187 } |
|
1188 else |
|
1189 { |
|
1190 LOG(_L("Rx: Simple UIH")); |
|
1191 ProcessDataFrame(addr, poll); |
|
1192 } |
|
1193 break; |
|
1194 |
|
1195 default: |
|
1196 { |
|
1197 LOG(_L("Error: RFCOMM: Unexpected frame ctrl field")); |
|
1198 } |
|
1199 } |
|
1200 } |
|
1201 else |
|
1202 { |
|
1203 LOG(_L("RFCOMM: Frame failed checksum")); |
|
1204 } |
|
1205 } |
|
1206 |
|
1207 TUint8 CRfcommMuxer::DecodeDLCI(TUint8 aAddr) |
|
1208 /** |
|
1209 Strip off the EA and C/R bits and return an TS07.10 DLCI. |
|
1210 |
|
1211 Note that this is not the same as the RFCOMM server channel, |
|
1212 which is 5 bits + 1 direction bit. |
|
1213 **/ |
|
1214 { |
|
1215 return TUint8(aAddr >> 2); |
|
1216 } |
|
1217 |
|
1218 void CRfcommMuxer::DecodeLengthAndCredit(TBool aCBFC) |
|
1219 /** |
|
1220 Decode the length of a frame. |
|
1221 |
|
1222 Return the overall length of the header. This can be one or |
|
1223 two bytes in length. |
|
1224 **/ |
|
1225 { |
|
1226 if((*iNextPacket)[2] & 1) |
|
1227 { |
|
1228 // Single byte length |
|
1229 iCurrentDataLength=(*iNextPacket)[2] >> 1; |
|
1230 iCurrentHeaderLength= KShortFrameHeaderLength; |
|
1231 } |
|
1232 else |
|
1233 { |
|
1234 iCurrentDataLength=((*iNextPacket)[2] >> 1) + |
|
1235 ((*iNextPacket)[3] << 7); |
|
1236 iCurrentHeaderLength= KLongFrameHeaderLength; |
|
1237 } |
|
1238 |
|
1239 //if doing CBFC updates credit buffer and revises header length buffer |
|
1240 //(iCurrentHeaderLength and iCurrentCredit) |
|
1241 iFlowStrategy->DecodeLength(aCBFC, *iNextPacket, |
|
1242 iCurrentCredit, iCurrentHeaderLength); |
|
1243 } |
|
1244 |
|
1245 void CRfcommMuxer::HandleSABM(TUint8 aDLCI) |
|
1246 /** |
|
1247 Handles an incoming SABM command. |
|
1248 This function basically dispatches the SABM to either the |
|
1249 mux channel or an appropriate SAP. |
|
1250 **/ |
|
1251 { |
|
1252 if(iCurrentDataLength != 0) |
|
1253 { |
|
1254 LOG1(_L("RFCOMM: SABM frame with length %d"), iCurrentDataLength); |
|
1255 } |
|
1256 // want this for UPF test verification - need the whole of a SABM in log file |
|
1257 LOG1(_L("RFCOMM: SABM command received, DLCI %d"), aDLCI); |
|
1258 LOGHEXRAW(&((iNextPacket->Des())[0]), (iCurrentHeaderLength+iCurrentDataLength)); |
|
1259 |
|
1260 if(aDLCI == KMuxDLCI) |
|
1261 // A SABM intended to initialise the Mux channel |
|
1262 iMuxChannel->SABM(); |
|
1263 else |
|
1264 { |
|
1265 // A SABM intended for a SAP |
|
1266 CRfcommSAP* mySAP=FindConnectedOrListeningSAP(aDLCI); |
|
1267 if(mySAP) |
|
1268 mySAP->SABM(*this, aDLCI); |
|
1269 else |
|
1270 { |
|
1271 // There is nothing out there to handle this SABM. |
|
1272 // Send a DM back as response. |
|
1273 SendDM(aDLCI); |
|
1274 } |
|
1275 } |
|
1276 } |
|
1277 |
|
1278 void CRfcommMuxer::HandleDISC(TUint8 aAddr) |
|
1279 /** |
|
1280 Handles an incoming DISConnect request. |
|
1281 |
|
1282 If this is for the mux channel then we should shutdown totally. |
|
1283 Otherwise we signal the appropriate SAP and then send a UA |
|
1284 frame to acknowledge. |
|
1285 **/ |
|
1286 { |
|
1287 LOG1(_L("RFCOMM: DISC frame for DLCI %d"), aAddr); |
|
1288 if(iCurrentDataLength !=0) |
|
1289 { |
|
1290 LOG(_L("RFCOMM: DISC frame length !=0")); |
|
1291 } |
|
1292 |
|
1293 CRfcommSAP* sap; |
|
1294 |
|
1295 if(aAddr != KMuxDLCI) |
|
1296 { |
|
1297 sap=FindSAP(aAddr); |
|
1298 if(!sap) |
|
1299 { |
|
1300 LOG(_L("RFCOMM: DISC for non-existant sap")); |
|
1301 TransmitDM(aAddr, ETrue); |
|
1302 } |
|
1303 else |
|
1304 { |
|
1305 sap->DISC(); |
|
1306 } |
|
1307 } |
|
1308 else |
|
1309 { |
|
1310 iMuxChannel->DISC(); |
|
1311 } |
|
1312 } |
|
1313 |
|
1314 void CRfcommMuxer::HandleDM(TUint8 aAddr) |
|
1315 /** |
|
1316 Handles an incoming DM frame. |
|
1317 |
|
1318 We must remove the equivalent SABM or DISC from the response Q |
|
1319 **/ |
|
1320 { |
|
1321 LOG1(_L("RFCOMM: DM Frame for DLCI %d"), aAddr); |
|
1322 if(aAddr != KMuxDLCI) |
|
1323 { |
|
1324 CRfcommSAP* sap=FindSAP(aAddr); |
|
1325 if(!sap) |
|
1326 { |
|
1327 LOG(_L("RFCOMM: DM for unanticipated DLCI!")); |
|
1328 } |
|
1329 else |
|
1330 { |
|
1331 sap->DM(); |
|
1332 } |
|
1333 } |
|
1334 else |
|
1335 { |
|
1336 iMuxChannel->DM(); |
|
1337 } |
|
1338 if(!CtrlFrameResponse(aAddr)) |
|
1339 MuxCtrlResponseDM(aAddr); |
|
1340 } |
|
1341 |
|
1342 void CRfcommMuxer::HandleUA(TUint8 aAddr) |
|
1343 /** |
|
1344 Handles an incoming UA frame. |
|
1345 |
|
1346 This represents an acknowledgement of a command on this channel. |
|
1347 **/ |
|
1348 { |
|
1349 LOG1(_L("RFCOMM: UA Frame for addr %d"), aAddr); |
|
1350 if(aAddr != KMuxDLCI) |
|
1351 { |
|
1352 CRfcommSAP* sap=FindSAP(aAddr); |
|
1353 if(!sap) |
|
1354 { |
|
1355 LOG(_L("RFCOMM: UA for invalid DLCI, ignoring")); |
|
1356 } |
|
1357 else |
|
1358 { |
|
1359 sap->UA(); |
|
1360 } |
|
1361 } |
|
1362 else |
|
1363 { |
|
1364 iMuxChannel->UA(); |
|
1365 } |
|
1366 // Attempt to find the matching control frame - ignore errors. |
|
1367 (void)CtrlFrameResponse(aAddr); |
|
1368 } |
|
1369 |
|
1370 // TRY_CBFC is this used for PNresponses ? got to check if our CBFC overture has been |
|
1371 // accepted. Appears only to be used for DM and UA responses where no action is needed |
|
1372 TInt CRfcommMuxer::CtrlFrameResponse(TUint8 aAddr) |
|
1373 /** |
|
1374 Remove the ctrl frame that caused this response from the response pending Q |
|
1375 |
|
1376 Responses are UA and DM, which may both be caused by SABM & |
|
1377 DISC frames. However, there should only ever be one SABM/DISC |
|
1378 outstanding for a given address, so we simply look for a |
|
1379 matching address. |
|
1380 **/ |
|
1381 { |
|
1382 TDblQueIter<CRfcommFrame> iter(iResponseQ); |
|
1383 CRfcommFrame* frm; |
|
1384 |
|
1385 while(iter) |
|
1386 { |
|
1387 frm=iter++; |
|
1388 if(frm->Type() == KCtrlFrameType && DecodeDLCI(frm->Address()) == aAddr) |
|
1389 { |
|
1390 LOG(_L("RFCOMM: Found matching Ctrl command for response")); |
|
1391 delete frm; |
|
1392 break; |
|
1393 } |
|
1394 } |
|
1395 return(iter?KErrNone:KErrNotFound); |
|
1396 } |
|
1397 |
|
1398 void CRfcommMuxer::ProcessDataFrame(TUint8 aAddr, TBool aPoll) |
|
1399 /** |
|
1400 Send this data up to the sap. |
|
1401 **/ |
|
1402 { |
|
1403 __ASSERT_DEBUG(iNextPacket, Panic(ERfcommNoPacket)); |
|
1404 LOG(_L("CRfcommMuxer::ProcessDataFrame")); |
|
1405 CRfcommSAP* sap=FindSAP(aAddr); |
|
1406 if(sap) |
|
1407 { |
|
1408 if(iFlowStrategy->ProcessDataFrameReviseCredits(*sap, aPoll, iCurrentCredit)) |
|
1409 //returning'ETrue' => sap needs unblocking |
|
1410 { |
|
1411 LOG(_L("RFCOMM: CBFC unblocking")); |
|
1412 SetSendBlocked(*sap, EFalse); |
|
1413 sap->CanSend(); |
|
1414 } |
|
1415 if (iCurrentDataLength) |
|
1416 { |
|
1417 sap->Data(iNextPacket->Mid(iCurrentHeaderLength, iCurrentDataLength)); |
|
1418 } |
|
1419 else |
|
1420 { |
|
1421 LOG(_L("RFCOMM: Data frame with no data - probably a CBFC credit")); |
|
1422 } |
|
1423 } |
|
1424 else |
|
1425 { |
|
1426 LOG1(_L("RFCOMM: Data for unknown sap %d"), aAddr); |
|
1427 } |
|
1428 } |
|
1429 |
|
1430 void CRfcommMuxer::ParseCtrlMessageL() |
|
1431 /** |
|
1432 Parse the control messages for the mux. |
|
1433 |
|
1434 The data on the channel has the following format. |
|
1435 |
|
1436 | Type | Length | Value 1 | Value 2 | ... | Value n | |
|
1437 unless this is PN frame then CL bits may indicate CBFC |
|
1438 and the K bits indicate the initial credit |
|
1439 |
|
1440 **/ |
|
1441 { |
|
1442 TInt offset=0; // Number of bytes we've parsed. |
|
1443 TInt x=0; |
|
1444 TUint8 type=0; |
|
1445 TInt length; |
|
1446 TBool command; // command or response |
|
1447 |
|
1448 if(!iMuxChannel->IsOpen()) |
|
1449 { |
|
1450 LOG(_L("RFCOMM: Error - mux ctrl message before muxchannel open! Junking.")); |
|
1451 // return; // MB - fixme |
|
1452 } |
|
1453 |
|
1454 while(offset+2 <= iCurrentDataLength) |
|
1455 { |
|
1456 command=(*iNextPacket)[iCurrentHeaderLength+offset] & KCRBitmask; |
|
1457 offset+=GetTypeFieldL(x, offset); |
|
1458 // Type must be 8 bits or less for us to deal with |
|
1459 if(x > 255 || x < 0) |
|
1460 { |
|
1461 User::Leave(KErrArgument); |
|
1462 } |
|
1463 type=static_cast<TUint8>(x); |
|
1464 offset+=GetLengthFieldL(length, offset); |
|
1465 if(offset+length > iCurrentDataLength) |
|
1466 { |
|
1467 User::Leave(KErrArgument); |
|
1468 } |
|
1469 |
|
1470 LOG2(_L("RFCOMM: Incoming Packet, %d, %d"),type, length); |
|
1471 |
|
1472 // Now parse the individual types. Offset points to the data |
|
1473 switch(type) |
|
1474 { |
|
1475 case KTestType: |
|
1476 HandleTest(command, iCurrentHeaderLength+offset, length); |
|
1477 break; |
|
1478 |
|
1479 case KFConType: |
|
1480 //FC |
|
1481 if(length!=0) |
|
1482 { |
|
1483 LOG(_L("RFCOMM: FCon with non-zero length!")); |
|
1484 } |
|
1485 HandleFCon(command); |
|
1486 // |
|
1487 break; |
|
1488 |
|
1489 case KFCoffType: |
|
1490 //FC |
|
1491 if(length!=0) |
|
1492 { |
|
1493 LOG(_L("RFCOMM: FCoff with non-zero length!")); |
|
1494 } |
|
1495 HandleFCoff(command); |
|
1496 // |
|
1497 break; |
|
1498 |
|
1499 case KMSCType: |
|
1500 { |
|
1501 LOG(_L("RFCOMM: MSC:")); |
|
1502 LOGHEXRAW(&((iNextPacket->Des())[0]), (iCurrentHeaderLength+iCurrentDataLength)); |
|
1503 |
|
1504 LOG2(_L("RFCOMM: MSC Hex: Incoming Packet, %d, %d"), (*iNextPacket)[0], 80); |
|
1505 |
|
1506 // See TS 7.10, pg 29 for details |
|
1507 // Length must be exactly 2, or 3 if 'Break Signals' are present |
|
1508 if(length < 2 || length > 3) |
|
1509 { |
|
1510 LOG1(_L("RFCOMM: MSC with erroneous length %d"), length); |
|
1511 break; |
|
1512 } |
|
1513 // Next byte is DLCI... |
|
1514 TUint8 dlci = static_cast<TUint8>((*iNextPacket)[iCurrentHeaderLength+offset] >> 2); |
|
1515 LOG1(_L("RFCOMM: DLCI %d"), dlci); |
|
1516 // ...and the next is the V.24 signals |
|
1517 TUint8 signals = static_cast<TUint8>((*iNextPacket)[iCurrentHeaderLength+offset+1] >> 1); |
|
1518 LOG3(_L("RFCOMM: MSC %S received dlci %d signals 0x%02x"), |
|
1519 (command?&KCommandText:&KResponseText), dlci, signals); |
|
1520 |
|
1521 if(!command) |
|
1522 { |
|
1523 LOG(_L("RFCOMM: Response")); |
|
1524 } |
|
1525 else |
|
1526 { |
|
1527 LOG(_L("RFCOMM: Command")); |
|
1528 } |
|
1529 LOG1(_L("RFCOMM: Signals 0x%02x"), signals); |
|
1530 |
|
1531 HandleMSC(command, dlci, signals); |
|
1532 break; |
|
1533 } |
|
1534 |
|
1535 case KRPNType: |
|
1536 { |
|
1537 // RPN frame received from L2CAP |
|
1538 LOG(_L("RFCOMM: RPN received")); |
|
1539 if(length != KRPNRequestLength && |
|
1540 length != KRPNCommandLength && |
|
1541 length != KRPNResponseLength) |
|
1542 { |
|
1543 LOG(_L("RFCOMM: Ignoring RPN with bad length")); |
|
1544 break; |
|
1545 } |
|
1546 TUint8 dlci=0; |
|
1547 if(length == KRPNRequestLength) |
|
1548 { |
|
1549 // There's no parameter data |
|
1550 ParseRPN(iCurrentHeaderLength+offset, length, dlci); |
|
1551 HandleRPN(command, dlci); |
|
1552 } |
|
1553 else // There was data so need to parse this too |
|
1554 { |
|
1555 TRfcommRPNTransaction rpnTransaction; |
|
1556 ParseRPN(iCurrentHeaderLength+offset, length, dlci, |
|
1557 &rpnTransaction); |
|
1558 HandleRPN(command, dlci, &rpnTransaction); |
|
1559 } |
|
1560 break; |
|
1561 } |
|
1562 |
|
1563 case KPNType: |
|
1564 { |
|
1565 LOG(_L("RFCOMM: PN received")); |
|
1566 LOGHEXRAW(&((iNextPacket->Des())[0]), (iCurrentHeaderLength+iCurrentDataLength)); |
|
1567 |
|
1568 TRfcommPortParams portparams; |
|
1569 TUint8 dlci; |
|
1570 ParsePNL(iCurrentHeaderLength+offset, length, dlci, portparams); |
|
1571 HandlePN(command, dlci, portparams); |
|
1572 break; |
|
1573 } |
|
1574 |
|
1575 case KNSCType: |
|
1576 LOG(_L("RFCOMM: NSC received")); |
|
1577 break; |
|
1578 |
|
1579 case KRLSType: |
|
1580 { |
|
1581 LOG(_L("RFCOMM: RLS received")); |
|
1582 // See TS 7.10, pgs 34/35 for details |
|
1583 const TUint8 KRLSLength = 2; // Length *must* be exactly 2 |
|
1584 if(length != KRLSLength) |
|
1585 { |
|
1586 LOG1(_L("RFCOMM: RLS with erroneous length %d"), |
|
1587 length); |
|
1588 break; |
|
1589 } |
|
1590 // First byte is DLCI... |
|
1591 TUint8 dlci = TUint8((*iNextPacket)[iCurrentHeaderLength+offset]) >> 2; |
|
1592 // ...and the next is the status octet |
|
1593 TUint8 status = TUint8((*iNextPacket)[iCurrentHeaderLength+offset+1]); |
|
1594 LOG3(_L("RFCOMM: RLS %S received dlci %d status 0x%02x"), |
|
1595 (command?&KCommandText:&KResponseText), dlci, status); |
|
1596 HandleRLS(command, dlci, status); |
|
1597 break; |
|
1598 } |
|
1599 default: |
|
1600 LOG1(_L("RFCOMM: Unknown type 0x%02x"), type); |
|
1601 TransmitNSC(command, type); |
|
1602 break; |
|
1603 } |
|
1604 offset+=length; |
|
1605 } |
|
1606 } |
|
1607 |
|
1608 void CRfcommMuxer::ParsePNL(TInt aOffset, TInt aLen, TUint8& aDLCI, |
|
1609 TRfcommPortParams& aParams) |
|
1610 /** |
|
1611 Parse the PN field at the specified offset in the frame. |
|
1612 Format is: |
|
1613 @verbatim |
|
1614 Value Octet Bit 1 Bit 2 Bit 3 Bit 4 Bit 5 Bit 6 Bit 7 Bit 8 |
|
1615 1 D1 D2 D3 D4 D5 D6 0 0 |
|
1616 2 I1 I2 I3 I4 CL1 CL2 CL3 CL4 |
|
1617 3 P1 P2 P3 P4 P5 P6 0 0 |
|
1618 4 T1 T2 T3 T4 T5 T6 T7 T8 |
|
1619 5 N1 N2 N3 N4 N5 N6 N7 N8 |
|
1620 6 N9 N10 N11 N12 N13 N14 N15 N16 |
|
1621 7 NA1 NA2 NA3 NA4 NA5 NA6 NA7 NA8 |
|
1622 8 K1 K2 K3 0 0 0 0 0 |
|
1623 |
|
1624 Where: |
|
1625 D1-6 : DLCI |
|
1626 I1-4 : frame type (must be zero) |
|
1627 CL1-4 : Convergence layer (if CBFC 0xf for command, 0xe for response... |
|
1628 ...otherwise must be zero) |
|
1629 P1-6 : Priority |
|
1630 T1-8 : Acknowledgement timer T1 (must be zero) |
|
1631 N1-16 : Max frame size (default 127) |
|
1632 NA1-8 : Max retransmissions (must be zero) |
|
1633 K1-3 : either Initial CBFC credit or Error recovery window (must be zero) |
|
1634 @endverbatim |
|
1635 **/ |
|
1636 { |
|
1637 // Length must be exactly 8 |
|
1638 const TUint8 KPNLength = 8; |
|
1639 if(aLen < KPNLength) |
|
1640 { |
|
1641 LOG(_L("RFCOMM: Invalid PN packet length")); |
|
1642 User::Leave(KErrGeneral); |
|
1643 } |
|
1644 |
|
1645 HBufC8& packet = *iNextPacket; |
|
1646 |
|
1647 // Constants for offsets into packet |
|
1648 // DLCI is at start of byte, (offset = 0) |
|
1649 // Ignore frame type (offset = 1) |
|
1650 const TUint8 KConvergenceLayerOffset = 1; //used by CBFC |
|
1651 const TUint8 KPriorityOffset = 2; |
|
1652 // Ignore Ack timer (offset = 3) |
|
1653 const TUint8 KMaxFrameLowerOffset = 4; |
|
1654 const TUint8 KMaxFrameUpperOffset = 5; |
|
1655 const TUint8 KErrorRecoveryWindow = 7; //used by CBFC |
|
1656 // Ignore the rest |
|
1657 |
|
1658 // DLCI - only want lowest 6 bits of byte |
|
1659 aDLCI = packet[aOffset] & KMaxRfcommDLCI; |
|
1660 // Priority - only want lowest 6 bits of byte |
|
1661 aParams.iPriority = packet[aOffset+KPriorityOffset] & 0x3f; |
|
1662 // Frame size is 2 bytes. Lower byte first... |
|
1663 aParams.iMaxFrameSize = TUint16(packet[aOffset+KMaxFrameLowerOffset]); |
|
1664 // ...then upper byte |
|
1665 aParams.iMaxFrameSize += TUint16(packet[aOffset+KMaxFrameUpperOffset] << 8); |
|
1666 aParams.iCreditIndicator = TUint8(packet[aOffset + KConvergenceLayerOffset] >> 4); |
|
1667 aParams.iInitialCredit = TUint8(packet[aOffset + KErrorRecoveryWindow] & 7); |
|
1668 LOG2(_L("RFCOMM: PN Rx(CBFC) CL 0x%02x, K 0x%02x"), aParams.iCreditIndicator, aParams.iInitialCredit); |
|
1669 } |
|
1670 |
|
1671 void CRfcommMuxer::ParseRPN(TInt aOffset, TInt aLen, TUint8& aDLCI, |
|
1672 TRfcommRPNTransaction* aRPNTransactionPtr) |
|
1673 /** |
|
1674 |
|
1675 Parse an incoming RPN frame. |
|
1676 aOffset points to the start of the data octets on entry |
|
1677 |
|
1678 @verbatim |
|
1679 Format is: |
|
1680 Value Octet Bit 1 Bit 2 Bit 3 Bit 4 Bit 5 Bit 6 Bit 7 Bit 8 |
|
1681 0 EA 1 DLCI |
|
1682 1 B1 B2 B3 B4 B5 B6 B7 B8 |
|
1683 2 D1 D2 S P PT1 PT2 res res |
|
1684 3 FLC1 FLC2 FLC3 FLC4 FLC5 FLC6 res res |
|
1685 4 XON1 XON2 XON3 XON4 XON5 XON6 XON7 XON8 |
|
1686 5 XOF1 XOF2 XOF3 XOF4 XOF5 XOF6 XOF7 XOF8 |
|
1687 6 PM1 PM2 PM3 PM4 PM5 PM6 PM7 PM8 |
|
1688 7 PM9 PM10 PM11 PM12 PM13 PM14 PM15 PM16 |
|
1689 |
|
1690 Where: |
|
1691 B1-8 : Baud rate |
|
1692 D1-2 : data bits |
|
1693 S : Stop bits |
|
1694 P : Parity |
|
1695 PT1-2 : Parity Type |
|
1696 FLC1-6 : Flow ctrl type |
|
1697 XON1-8 : Xon character |
|
1698 XOFF1-8: Xoff character |
|
1699 PM1-16 : Which values are to be set/accepted |
|
1700 @endverbatim |
|
1701 See TS07.10 5.4.6.3.9 for more details |
|
1702 **/ |
|
1703 { |
|
1704 // Wrap a descriptor over the data we want |
|
1705 TPtrC8 valueOctets=iNextPacket->Des().Mid(aOffset, aLen); |
|
1706 |
|
1707 // Get the DLCI |
|
1708 aDLCI = valueOctets[0] >> 2; |
|
1709 LOG1(_L("RFCOMM: RPN frame, DLCI %d"), aDLCI); |
|
1710 |
|
1711 // Now parse out the valid fields if necessary |
|
1712 if(aRPNTransactionPtr) |
|
1713 { |
|
1714 LOG(_L("Parsing full RPN data\n")); |
|
1715 // Local references into the transaction structure |
|
1716 TRfcommRemotePortParams& portParams = aRPNTransactionPtr->iPortParams; |
|
1717 TUint16& paramMask = aRPNTransactionPtr->iParamMask; |
|
1718 // construct the parameter mask out of the last two value octets |
|
1719 paramMask = TUint16(valueOctets[7]); |
|
1720 paramMask = TUint16((paramMask << 8) + valueOctets[6]); |
|
1721 |
|
1722 // Bit rate |
|
1723 if(paramMask & EPMBitRate) |
|
1724 { |
|
1725 TUint8 bitRate=valueOctets[1]; |
|
1726 switch(bitRate) |
|
1727 { |
|
1728 // Magic numbers because enums are not the same |
|
1729 // values as the ones specified in the spec and |
|
1730 // adding even more enums would confuse matters |
|
1731 case 0: |
|
1732 portParams.SetBitRate(EBps2400); |
|
1733 break; |
|
1734 case 1: |
|
1735 portParams.SetBitRate(EBps4800); |
|
1736 break; |
|
1737 case 2: |
|
1738 case 3: |
|
1739 case 4: |
|
1740 case 5: |
|
1741 case 6: |
|
1742 case 7: |
|
1743 case 8: |
|
1744 portParams.SetBitRate(static_cast<TBps>(EBps7200+(bitRate-2))); |
|
1745 break; |
|
1746 default: |
|
1747 { |
|
1748 LOG(_L("RFCOMM: RPN with invalid bitrate field, ignoring")); |
|
1749 } |
|
1750 } |
|
1751 } |
|
1752 |
|
1753 // Value octet 2 is compound |
|
1754 // Masks for bit position within octet |
|
1755 // Bit number: 76543210 |
|
1756 const TUint8 KDataBitsPos = 0x03; // 000000XX |
|
1757 const TUint8 KStopBitPos = 0x04; // 00000X00 |
|
1758 const TUint8 KParityBitPos = 0x08; // 0000X000 |
|
1759 const TUint8 KParityTypeBitsPos = 0x30; // 00XX0000 |
|
1760 // 2 most significant bits are reserved (always 0) |
|
1761 |
|
1762 const TUint8 KStopBitOffset = 2; // offset for 00000X00 |
|
1763 const TUint8 KParityBitOffset = 3; // offset for 0000X000 |
|
1764 const TUint8 KParityTypeOffset = 4; // offset for 00XX0000 |
|
1765 |
|
1766 if(paramMask & EPMDataBits) |
|
1767 { |
|
1768 TUint8 dataBits = TUint8(valueOctets[2] & KDataBitsPos); |
|
1769 // Data bits |
|
1770 switch(dataBits) |
|
1771 { |
|
1772 // Magic numbers because enums are not the same |
|
1773 // values as the ones specified in the spec and |
|
1774 // adding even more enums would confuse matters |
|
1775 case 0: |
|
1776 portParams.SetDataBits(EData5); |
|
1777 break; |
|
1778 case 1: |
|
1779 portParams.SetDataBits(EData7); |
|
1780 break; |
|
1781 case 2: |
|
1782 portParams.SetDataBits(EData6); |
|
1783 break; |
|
1784 case 3: |
|
1785 portParams.SetDataBits(EData8); |
|
1786 break; |
|
1787 default: |
|
1788 __DEBUGGER() |
|
1789 LOG(_L("How did we get more than 3 with 2 bits?!\n")); |
|
1790 } |
|
1791 } |
|
1792 |
|
1793 if(paramMask & EPMStopBit) |
|
1794 { |
|
1795 TUint8 stopBit = TUint8((valueOctets[2] & KStopBitPos)>>KStopBitOffset); |
|
1796 portParams.SetStopBit(static_cast<TStopBits>(stopBit)); |
|
1797 } |
|
1798 |
|
1799 // The Parity and ParityType bits have a combined effect.. |
|
1800 |
|
1801 if(paramMask & EPMParity) |
|
1802 { |
|
1803 TUint8 parityBit = TUint8((valueOctets[2] & KParityBitPos)>>KParityBitOffset); |
|
1804 |
|
1805 if(!parityBit) |
|
1806 { |
|
1807 portParams.SetParity(EParityNone); |
|
1808 } |
|
1809 } |
|
1810 |
|
1811 if(paramMask & EPMParityType) |
|
1812 { |
|
1813 TParity parityType = EParitySpace; //Just to stop the compiler warnings |
|
1814 |
|
1815 //It is intended that this 'GetParity' will only update 'parityType' |
|
1816 //if 'SetParity' was called in the previous 'if' clause. |
|
1817 portParams.GetParity(parityType); |
|
1818 |
|
1819 if(parityType == EParityNone) |
|
1820 { |
|
1821 // Can't have a parity type if there's no parity set |
|
1822 LOG(_L("RFCOMM: WARNING!: Tried to set parity type when parity is none or invalid")); |
|
1823 } |
|
1824 else |
|
1825 { |
|
1826 // Parity Type is in first two bits of upper nibble |
|
1827 // Need to move it down to the lower nibble |
|
1828 switch((valueOctets[2] & KParityTypeBitsPos)>>KParityTypeOffset) |
|
1829 { |
|
1830 case 0: |
|
1831 portParams.SetParity(EParityOdd); |
|
1832 break; |
|
1833 case 1: |
|
1834 portParams.SetParity(EParityMark); |
|
1835 break; |
|
1836 case 2: |
|
1837 portParams.SetParity(EParityEven); |
|
1838 break; |
|
1839 case 3: |
|
1840 portParams.SetParity(EParitySpace); |
|
1841 break; |
|
1842 default: |
|
1843 break; |
|
1844 } |
|
1845 } |
|
1846 } |
|
1847 |
|
1848 if(paramMask & EPMXOnChar) |
|
1849 { |
|
1850 portParams.SetXOnChar(valueOctets[4]); |
|
1851 } |
|
1852 |
|
1853 if(paramMask & EPMXOffChar) |
|
1854 { |
|
1855 portParams.SetXOffChar(valueOctets[5]); |
|
1856 } |
|
1857 |
|
1858 // Flow control is only in the first 6 bits of this value octet |
|
1859 TUint8 flowCtrl = TUint8(valueOctets[3] & 0x3f); |
|
1860 portParams.UpdateWholeFlowCtrl(paramMask, flowCtrl); |
|
1861 } |
|
1862 else |
|
1863 { |
|
1864 LOG(_L("Query request received - package has not been filled out\n")); |
|
1865 } |
|
1866 } |
|
1867 |
|
1868 |
|
1869 void CRfcommMuxer::HandleRLS(TBool aCommand, TUint8 aDLCI, TUint8 aStatus) |
|
1870 /** |
|
1871 Handle a RLS frame from remote dev |
|
1872 |
|
1873 If it's a command, respond then pass the data up to the user |
|
1874 If it's a response, just ignore it - it'll just be what we sent |
|
1875 **/ |
|
1876 { |
|
1877 CRfcommSAP* sap=FindSAP(aDLCI); |
|
1878 if(!sap) |
|
1879 { |
|
1880 LOG1(_L("RFCOMM: RLS for unknown dlci %d"), aDLCI); |
|
1881 if(aCommand) |
|
1882 SendDM(aDLCI); |
|
1883 return; |
|
1884 } |
|
1885 |
|
1886 if(aCommand) |
|
1887 { |
|
1888 LOG(_L("RFCOMM: RLS command received")); |
|
1889 // Send a response to the remote device |
|
1890 SendRLSRsp(*sap, aStatus); |
|
1891 // Pass status up to the user |
|
1892 sap->RLS(aStatus); |
|
1893 } |
|
1894 else |
|
1895 { |
|
1896 // Response to our command |
|
1897 LOG(_L("RFCOMM: RLS response received")); |
|
1898 MuxCtrlResponse(KRLSType); |
|
1899 } |
|
1900 } |
|
1901 |
|
1902 |
|
1903 void CRfcommMuxer::HandleRPN(const TBool& aCommand, const TUint8& aDLCI, |
|
1904 const TRfcommRPNTransaction* aRPNTransactionPtr) |
|
1905 /** |
|
1906 Handle an RPN frame from remote dev |
|
1907 |
|
1908 If it's valid, just pass it on to the SAP |
|
1909 **/ |
|
1910 { |
|
1911 LOG1(_L("RFCOMM: Handling RPN for dlci %d"), aDLCI); |
|
1912 |
|
1913 if(aDLCI == KMuxDLCI) |
|
1914 { |
|
1915 LOG(_L("RFCOMM: RPN for Mux channel. Ignoring")); |
|
1916 return; |
|
1917 } |
|
1918 |
|
1919 if(!aCommand && aRPNTransactionPtr == 0) |
|
1920 { |
|
1921 LOG(_L("RFCOMM: Corrupt RPN response. Ignoring")); |
|
1922 return; |
|
1923 } |
|
1924 |
|
1925 // Check to see if there's a sap to pass this onto |
|
1926 CRfcommSAP* sap=FindConnectedOrListeningSAP(aDLCI); |
|
1927 |
|
1928 if(sap == 0)// No listening or connected SAP available |
|
1929 { |
|
1930 if(aCommand) |
|
1931 { |
|
1932 LOG(_L("RFCOMM: RPN command for unknown sap - Sending DM")); |
|
1933 SendDM(aDLCI); |
|
1934 } |
|
1935 else |
|
1936 { |
|
1937 LOG(_L("RFCOMM: RPN response for unknown sap. Ignoring")); |
|
1938 } |
|
1939 return; |
|
1940 } |
|
1941 |
|
1942 // Still here? Everything's OK to pass the RPN on to the SAP |
|
1943 if(aCommand) |
|
1944 sap->RPN(aRPNTransactionPtr, *this, aDLCI); |
|
1945 else // It's a valid response |
|
1946 { |
|
1947 // So we're no longer waiting for a response |
|
1948 MuxCtrlResponse(KRPNType); |
|
1949 sap->RPNRsp(*aRPNTransactionPtr); |
|
1950 } |
|
1951 } |
|
1952 |
|
1953 void CRfcommMuxer::HandlePN(TBool aCommand, TUint8 aDLCI, TRfcommPortParams& aParams) |
|
1954 /** |
|
1955 Handles PN commands & responses. |
|
1956 |
|
1957 Hands over to the SAP, but checks the max length first and |
|
1958 truncates if necessary before passing on. We may also get a PN |
|
1959 for the mux channel, which is setting the frame size. |
|
1960 **/ |
|
1961 { |
|
1962 LOG2(_L("RFCOMM: Handling PN for dlci %d (MTU=%d)"), aDLCI, aParams.iMaxFrameSize); |
|
1963 if(aDLCI == KMuxDLCI) |
|
1964 { |
|
1965 iMuxChannel->PN(aCommand, aParams); |
|
1966 } |
|
1967 else |
|
1968 { |
|
1969 CRfcommSAP* sap=FindConnectedOrListeningSAP(aDLCI); |
|
1970 TBool useCBFC = aCommand?(aParams.iCreditIndicator == (KCBFCCommandFlag >> 4)): |
|
1971 (aParams.iCreditIndicator == (KCBFCResponseFlag >> 4)); |
|
1972 if(!sap) |
|
1973 { |
|
1974 if(aCommand) |
|
1975 { |
|
1976 // Means no suitable listening SAP is around. |
|
1977 LOG(_L("RFCOMM: PN for unknown sap - incoming connection request with no listening SAP")); |
|
1978 TransmitDM(aDLCI, ETrue); // DM for specified DLCI with P/F bit set |
|
1979 } |
|
1980 else |
|
1981 { |
|
1982 // unexpected response |
|
1983 LOG(_L("RFCOMM: PN response for unknown sap, ignoring")); |
|
1984 } |
|
1985 } |
|
1986 else |
|
1987 {// Found a SAP for this PN |
|
1988 if (!iTriedCBFC) |
|
1989 { |
|
1990 if(useCBFC) |
|
1991 { |
|
1992 SetFlowType(CRfcommFlowStrategyFactory::EFlowCreditBased); |
|
1993 } |
|
1994 else |
|
1995 { |
|
1996 SetFlowType(CRfcommFlowStrategyFactory::EFlowNonCreditBased); |
|
1997 } |
|
1998 } |
|
1999 |
|
2000 if(aParams.iCreditIndicator == (KCBFCCommandFlag >> 4)) |
|
2001 //done for each PN |
|
2002 { |
|
2003 // need to set a one time initial credit in case |
|
2004 // subsequent DLCs don't have a PN frame |
|
2005 iInitialTxCredit = aParams.iInitialCredit; |
|
2006 } |
|
2007 |
|
2008 if(aCommand) |
|
2009 { |
|
2010 sap->PN(aParams,*this,aDLCI); |
|
2011 } |
|
2012 else |
|
2013 { |
|
2014 sap->PNResp(aParams); |
|
2015 } |
|
2016 } |
|
2017 } |
|
2018 // FIXME not sure about this, |
|
2019 // if this is a SAP PN frame will it be in the muxctrl queue ? |
|
2020 if(!aCommand) |
|
2021 MuxCtrlResponse(KPNType); |
|
2022 } |
|
2023 |
|
2024 |
|
2025 void CRfcommMuxer::HandleMSC(TBool aCommand, TUint8 aDLCI, TUint8 aSignals) |
|
2026 /** |
|
2027 Handles a MSC command & response from the remote device |
|
2028 |
|
2029 If it's a command, send response and filter up to SAP |
|
2030 If it's a response, we're no longer expecting a response (!) |
|
2031 **/ |
|
2032 { |
|
2033 CRfcommSAP* sap=FindSAP(aDLCI); |
|
2034 if(aCommand) |
|
2035 { |
|
2036 LOG(_L("RFCOMM: MSC command received")); |
|
2037 if(sap) |
|
2038 { |
|
2039 // Send a response to remote device |
|
2040 SendMSCRsp(*sap, aSignals); |
|
2041 // Pass signals up to user |
|
2042 sap->MSC(aSignals); |
|
2043 } |
|
2044 else |
|
2045 { |
|
2046 LOG1(_L("RFCOMM: MSC for unknown dlci %d"), aDLCI); |
|
2047 SendDM(aDLCI); |
|
2048 } |
|
2049 } |
|
2050 else |
|
2051 { |
|
2052 LOG(_L("RFCOMM: MSC response received")); |
|
2053 MuxCtrlResponse(KMSCType); |
|
2054 } |
|
2055 } |
|
2056 |
|
2057 void CRfcommMuxer::HandleTest(TBool aCommand, TInt aOffset, TInt aLen) |
|
2058 /** |
|
2059 Handles a test command. |
|
2060 |
|
2061 If this is a command, then respond with the same data bytes as |
|
2062 were sent to us. Else ignore it. |
|
2063 **/ |
|
2064 { |
|
2065 if(aCommand) |
|
2066 { |
|
2067 LOG(_L("RFCOMM: Test command received")); |
|
2068 __ASSERT_DEBUG(aLen < iMuxChannel->MaxDataSize(), Panic(ERfcommTestDataTooLong)); |
|
2069 TransmitTest(!aCommand, TPtr8(&iNextPacket->Des()[aOffset], aLen, aLen)); |
|
2070 } |
|
2071 else |
|
2072 { |
|
2073 MuxCtrlResponse(KTestType); |
|
2074 LOG(_L("RFCOMM: Test response received")); |
|
2075 } |
|
2076 } |
|
2077 |
|
2078 void CRfcommMuxer::HandleFCon(TBool aCommand) |
|
2079 /** |
|
2080 Handle a FCon instruction. |
|
2081 |
|
2082 If this is a command, then we need to respond and set flow ctrl |
|
2083 to OK. Otherwise it's a response to our FCon command, and we |
|
2084 can expect the remote end to start sending. |
|
2085 **/ |
|
2086 { |
|
2087 if(aCommand) |
|
2088 { |
|
2089 LOG(_L("RFCOMM: FCon command received")); |
|
2090 if(ClearToSend()) |
|
2091 { |
|
2092 LOG(_L("RFCOMM: Received FCon when state was ON")); |
|
2093 } |
|
2094 SetCanSend(ETrue); |
|
2095 if(TransmitFCon(EFalse) != KErrNone) |
|
2096 { |
|
2097 LOG(_L("RFCOMM: Failed to send FCon response!")); |
|
2098 } |
|
2099 TryToSend(); // Try to send even if we couldn't respond |
|
2100 } |
|
2101 else |
|
2102 { |
|
2103 LOG(_L("RFCOMM: FCon response received")); |
|
2104 MuxCtrlResponse(KFConType); |
|
2105 } |
|
2106 } |
|
2107 |
|
2108 void CRfcommMuxer::HandleFCoff(TBool aCommand) |
|
2109 /** |
|
2110 Handles a FCoff command. |
|
2111 |
|
2112 If this is a command, then set our state to flow ctrl off, and |
|
2113 send a response. If it was a reply, then we should now not |
|
2114 expect any more data packets till we send a FCon. |
|
2115 **/ |
|
2116 { |
|
2117 //FC |
|
2118 if(aCommand) |
|
2119 { |
|
2120 LOG(_L("RFCOMM: FCoff command received")); |
|
2121 if(!ClearToSend()) |
|
2122 { |
|
2123 LOG(_L("RFCOMM: Received FCoff when state was off")); |
|
2124 } |
|
2125 SetCanSend(EFalse); |
|
2126 TransmitFCoff(EFalse); |
|
2127 } |
|
2128 else |
|
2129 { |
|
2130 LOG(_L("RFCOMM: FCoff response received")); |
|
2131 MuxCtrlResponse(KFCoffType); |
|
2132 } |
|
2133 // |
|
2134 } |
|
2135 |
|
2136 TInt CRfcommMuxer::GetTypeFieldL(TInt& aType, TInt aPos) |
|
2137 /** |
|
2138 Parse the type field. Due to EA bits this is of unknown length. |
|
2139 |
|
2140 Type field looks like this : |
|
2141 @verbatim |
|
2142 | EA | C/R | T1 | T2 | T3 | T4 | T5 | T6 | |
|
2143 |
|
2144 If EA is 0, then the following bytes are extensions: |
|
2145 |
|
2146 | EA | T7 | T8 | T9 | T10 | T11 | T12 | T13 | |
|
2147 @endverbatim |
|
2148 @param aType The type is returned in this |
|
2149 @param aPos The start position in frame's data |
|
2150 @return The length of the type field in bytes. |
|
2151 **/ |
|
2152 { |
|
2153 const TUint8 KBitsInFirstTypeFieldByte = 6; |
|
2154 const TUint8 KBitsInSubsequentTypeFieldBytes = 7; |
|
2155 |
|
2156 TInt readptr = iCurrentHeaderLength + aPos; |
|
2157 aType = (*iNextPacket)[readptr] >> 2; // drop the EA and C/R bit (any good reason the latter?) |
|
2158 TInt offset = 1; // in bytes |
|
2159 TUint8 bitshift = KBitsInFirstTypeFieldByte; |
|
2160 |
|
2161 if(!((*iNextPacket)[readptr] & KEABitmask)) |
|
2162 { |
|
2163 // Multiple length type field |
|
2164 LOG(_L("RFCOMM: Multi-length type field in mux channel")); |
|
2165 do |
|
2166 { |
|
2167 if(((readptr + offset - iCurrentHeaderLength) > iCurrentDataLength) |
|
2168 || ((bitshift + KBitsInSubsequentTypeFieldBytes) > KBitsForNumberInTInt)) |
|
2169 { |
|
2170 // We enter here if the EA's are such that we cannot handle the field, by either |
|
2171 // 1) They indicated there was another type field byte which isn't present. |
|
2172 // 2) They indicate a type field value that cannot be represented in a TInt. |
|
2173 User::Leave(KErrArgument); |
|
2174 } |
|
2175 // we should also check that we don't have too extensions |
|
2176 // (i.e. we have a number larger than a TInt) |
|
2177 aType += ((*iNextPacket)[readptr+offset] >> 1) << bitshift; |
|
2178 offset++; |
|
2179 bitshift += KBitsInSubsequentTypeFieldBytes; |
|
2180 } |
|
2181 while (!((*iNextPacket)[readptr+offset-1] & KEABitmask)); |
|
2182 } |
|
2183 |
|
2184 return offset; |
|
2185 } |
|
2186 |
|
2187 TInt CRfcommMuxer::GetLengthFieldL(TInt& aLen, TInt aPos) |
|
2188 /** |
|
2189 Parse the length field. Due to EA bits this is of unknown length. |
|
2190 |
|
2191 Type field looks like this : |
|
2192 |
|
2193 | EA | L1 | L2 | L3 | L4 | L5 | L6 | L7 | |
|
2194 |
|
2195 @param aLen The length is returned in this |
|
2196 @param aPos The start position in the frame's data |
|
2197 @return The length of the length field. |
|
2198 **/ |
|
2199 { |
|
2200 const TUint8 KBitsInLengthFieldByte = 7; |
|
2201 |
|
2202 TInt readptr = iCurrentHeaderLength + aPos; |
|
2203 aLen = (*iNextPacket)[readptr] >> 1; // discard the EA bit |
|
2204 TInt offset = 1; |
|
2205 TUint8 bitshift = KBitsInLengthFieldByte; |
|
2206 |
|
2207 if(!((*iNextPacket)[readptr] & KEABitmask)) |
|
2208 { |
|
2209 // Multiple length Len field |
|
2210 LOG(_L("RFCOMM: Multi-length length field in mux channel")); |
|
2211 do |
|
2212 { |
|
2213 // Initially we do some checking to ensure field isn't malformed. |
|
2214 // Part 1 of 2. |
|
2215 if((readptr + offset - iCurrentHeaderLength) > iCurrentDataLength) |
|
2216 { |
|
2217 // We enter here if the EAs indicate that there is another length field byte |
|
2218 // when there isn't. |
|
2219 User::Leave(KErrArgument); |
|
2220 } |
|
2221 |
|
2222 // Now we know that there is sufficient data in the packet, we can get the next length value |
|
2223 TUint8 value((*iNextPacket)[readptr+offset] >> 1); |
|
2224 |
|
2225 // Do some more checking of the field |
|
2226 // Part 2 of 2. |
|
2227 if((bitshift + KBitsInLengthFieldByte) > KBitsForNumberInTInt) |
|
2228 { |
|
2229 // We enter here if the EA's indicate that the length value the field represents is |
|
2230 // potentially larger than a TInt. |
|
2231 // However it may be that the overflowed bits are just trailing zeros, meaning that |
|
2232 // we can represent it. |
|
2233 TUint8 trailingZeroMask(0xff); |
|
2234 trailingZeroMask <<= (KBitsForNumberInTInt - bitshift); |
|
2235 trailingZeroMask &= value; |
|
2236 if(trailingZeroMask != 0x00) |
|
2237 { |
|
2238 // We enter here if there are bits which would overflow a TInt |
|
2239 User::Leave(KErrArgument); |
|
2240 } |
|
2241 } |
|
2242 |
|
2243 // If here the byte appears valid, so update the length as appropriate. |
|
2244 aLen += value << bitshift; |
|
2245 offset++; |
|
2246 bitshift += KBitsInLengthFieldByte; |
|
2247 } |
|
2248 while (!((*iNextPacket)[readptr+offset-1] & KEABitmask)); |
|
2249 } |
|
2250 |
|
2251 return offset; |
|
2252 } |
|
2253 |
|
2254 void CRfcommMuxer::MuxCtrlResponse(TUint8 aType) |
|
2255 /** |
|
2256 Remove the mux ctrl frame that caused this response from the |
|
2257 response pending Q |
|
2258 |
|
2259 To fully match a response, one would need to look at all the |
|
2260 parameters to the command. However, we cheat by just matching |
|
2261 the command type. This may lead us to remove the wrong one, |
|
2262 but since any lack of a match brings down the link, we go for |
|
2263 the easy option. |
|
2264 **/ |
|
2265 { |
|
2266 TDblQueIter<CRfcommFrame> iter(iResponseQ); |
|
2267 CRfcommFrame* frm; |
|
2268 |
|
2269 while(iter) |
|
2270 { |
|
2271 frm=iter++; |
|
2272 if(frm->Type() == KMuxCtrlFrameType) |
|
2273 { |
|
2274 CRfcommMuxCtrlFrame* muxframe=static_cast<CRfcommMuxCtrlFrame*>(frm); |
|
2275 if(muxframe->CommandType() == aType) |
|
2276 { |
|
2277 LOG(_L("RFCOMM: Found matching muxctrl command for response")); |
|
2278 delete frm; |
|
2279 break; |
|
2280 } |
|
2281 } |
|
2282 } |
|
2283 } |
|
2284 |
|
2285 void CRfcommMuxer::MuxCtrlResponseDM(TUint8 aDLCI) |
|
2286 /** |
|
2287 Remove the mux ctrl frame that caused this DM response from the |
|
2288 response pending Q |
|
2289 |
|
2290 All we have to work on is the DLCI of the channel which issued the |
|
2291 original mux control frame which elicited the DM response. We |
|
2292 can search through the response queue until we find a frame sent |
|
2293 out for this DLCI. |
|
2294 **/ |
|
2295 { |
|
2296 TDblQueIter<CRfcommFrame> iter(iResponseQ); |
|
2297 CRfcommFrame* frm; |
|
2298 |
|
2299 while(iter) |
|
2300 { |
|
2301 frm=iter++; |
|
2302 if(frm->Type() == KMuxCtrlFrameType) |
|
2303 { |
|
2304 CRfcommMuxCtrlFrame* muxframe=static_cast<CRfcommMuxCtrlFrame*>(frm); |
|
2305 if(muxframe->DLCI() == aDLCI) |
|
2306 { |
|
2307 LOG(_L("RFCOMM: Found matching muxctrl command for DM response")); |
|
2308 delete frm; |
|
2309 break; |
|
2310 } |
|
2311 } |
|
2312 } |
|
2313 } |
|
2314 |
|
2315 TInt CRfcommMuxer::TransmitSABM(TUint8 aDLCI, CRfcommSAP* aSAP) |
|
2316 /** |
|
2317 Send a SABM command for specified DLCI. |
|
2318 This command is used to start the connection of a channel, |
|
2319 including the control channel. |
|
2320 @verbatim |
|
2321 Frame format is: |
|
2322 |
|
2323 Address | Control | Length | FCS |
|
2324 |
|
2325 Address format is: |
|
2326 |
|
2327 | 1 | 2 | 3 | 4 5 6 7 8 | |
|
2328 | EA | C/R | D | Server channel | |
|
2329 |
|
2330 Where aDLCI already includes the D & Server channel bits. |
|
2331 @endverbatim |
|
2332 |
|
2333 We always set the P/F bit to one in a SABM frame. |
|
2334 |
|
2335 @param aDLCI This value is the server channel plus direction |
|
2336 bit part of the DLCI. This will have the E/A & C/R bits added. |
|
2337 |
|
2338 @return Error code |
|
2339 **/ |
|
2340 { |
|
2341 LOG1(_L("RFCOMM: Sending SABM for DLCI %d"), aDLCI); |
|
2342 CRfcommCtrlFrame* frm=NewFrame(aSAP); |
|
2343 |
|
2344 if(!frm) |
|
2345 return KErrNoMemory; |
|
2346 |
|
2347 frm->SetResponseNeeded(ETrue); |
|
2348 frm->SetAddress(BuildAddr(aDLCI, ETrue)); |
|
2349 frm->SetControl(KSABMCtrlField | KPollFinalBitmask); |
|
2350 /* |
|
2351 BLOG: The frame has now been prepared therefore can show all bits. |
|
2352 //BLOG: Can show all the information about this frame in TrytoSend() just as it is physically about |
|
2353 //to go over the air - Bluetooth...wooohooooo! |
|
2354 //Mind you probably should blog DLCI here? |
|
2355 //BLOG KBlogDLCI |
|
2356 BlogTransmitCtrlFrame(frm, aDLCI, aSAP) |
|
2357 */ |
|
2358 EnqueFrame(frm); |
|
2359 |
|
2360 return KErrNone; |
|
2361 } |
|
2362 |
|
2363 TInt CRfcommMuxer::TransmitUA(TUint8 aDLCI, CRfcommSAP* aSAP) |
|
2364 { |
|
2365 LOG1(_L("RFCOMM: Sending UA for DLCI %d"), aDLCI); |
|
2366 CRfcommCtrlFrame* frm=NewFrame(aSAP); |
|
2367 if(!frm) |
|
2368 return KErrNoMemory; |
|
2369 |
|
2370 // UA always has Final set |
|
2371 frm->SetControl(KUACtrlField | KPollFinalBitmask); |
|
2372 frm->SetAddress(BuildAddr(aDLCI, EFalse)); |
|
2373 EnqueFrame(frm); |
|
2374 return KErrNone; |
|
2375 } |
|
2376 |
|
2377 TInt CRfcommMuxer::TransmitDM(TUint8 aDLCI, TBool aPFBit, CRfcommSAP* aSAP) |
|
2378 { |
|
2379 LOG1(_L("RFCOMM: Sending DM for DLCI %d"), aDLCI); |
|
2380 CRfcommCtrlFrame* frm=NewFrame(aSAP); |
|
2381 if(!frm) |
|
2382 return KErrNoMemory; |
|
2383 |
|
2384 if(aPFBit) |
|
2385 frm->SetControl(KDMCtrlField | KPollFinalBitmask); |
|
2386 else |
|
2387 frm->SetControl(KDMCtrlField); |
|
2388 frm->SetAddress(BuildAddr(aDLCI, EFalse)); |
|
2389 EnqueFrame(frm); |
|
2390 return KErrNone; |
|
2391 } |
|
2392 |
|
2393 TInt CRfcommMuxer::TransmitDISC(TUint8 aDLCI, CRfcommSAP* aSAP) |
|
2394 { |
|
2395 LOG1(_L("RFCOMM: Sending DISC for DLCI %d"), aDLCI); |
|
2396 CRfcommCtrlFrame* frm=NewFrame(aSAP); |
|
2397 if(!frm) |
|
2398 return KErrNoMemory; |
|
2399 |
|
2400 // DISC always has Final set |
|
2401 frm->SetResponseNeeded(ETrue); |
|
2402 frm->SetControl(KDISCCtrlField | KPollFinalBitmask); |
|
2403 frm->SetAddress(BuildAddr(aDLCI, ETrue)); // C/R set |
|
2404 EnqueFrame(frm); |
|
2405 return KErrNone; |
|
2406 } |
|
2407 |
|
2408 TInt CRfcommMuxer::TransmitRPN(TUint8 aDLCI, TBool aCommand, TUint8 aLen, |
|
2409 const TRfcommRPNTransaction& aRPNTransaction, CRfcommSAP* aSAP) |
|
2410 /** |
|
2411 |
|
2412 Transmit an RPN frame. |
|
2413 |
|
2414 @verbatim |
|
2415 Format is: |
|
2416 Value Octet Bit 1 Bit 2 Bit 3 Bit 4 Bit 5 Bit 6 Bit 7 Bit 8 |
|
2417 1 E/A C/R ---------------DLCI---------------- |
|
2418 2 B1 B2 B3 B4 B5 B6 B7 B8 |
|
2419 3 D1 D2 S P PT1 PT2 res res |
|
2420 4 FLC1 FLC2 FLC3 FLC4 FLC5 FLC6 res res |
|
2421 5 XON1 XON2 XON3 XON4 XON5 XON6 XON7 XON8 |
|
2422 6 XOF1 XOF2 XOF3 XOF4 XOF5 XOF6 XOF7 XOF8 |
|
2423 7 PM1 PM2 PM3 PM4 PM5 PM6 PM7 PM8 |
|
2424 8 PM9 PM10 PM11 PM12 PM13 PM14 PM15 PM16 |
|
2425 |
|
2426 Where: |
|
2427 B1-8 : Baud rate |
|
2428 D1-2 : data bits |
|
2429 S : Stop bits |
|
2430 P : Parity |
|
2431 PT1-2 : Parity Type |
|
2432 FLC1-6 : Flow ctrl type |
|
2433 XON1-8 : Xon character |
|
2434 XOFF1-8: Xoff character |
|
2435 PM1-16 : Which values are to be set/accepted |
|
2436 |
|
2437 See TS07.10 5.4.6.3.9 for more details |
|
2438 @endverbatim |
|
2439 **/ |
|
2440 { |
|
2441 CRfcommMuxCtrlFrame* frm = 0; |
|
2442 |
|
2443 __ASSERT_DEBUG(aLen == KRPNCommandLength || aLen == KRPNRequestLength || |
|
2444 aLen == KRPNResponseLength, Panic(ERfcommInvalidRPNLength)); |
|
2445 LOG(_L("RFCOMM: Creating RPN frame")); |
|
2446 frm=NewSignalFrame(aLen, aCommand, aSAP); |
|
2447 if(!frm) |
|
2448 return KErrNoMemory; |
|
2449 |
|
2450 frm->SetDLCI(aDLCI); // For use when matching DM responses to mux control command frames |
|
2451 frm->SetCommandType(KRPNType, aCommand); |
|
2452 frm->SetResponseNeeded(aCommand); |
|
2453 |
|
2454 // Now the only parameter that's always present, the DLCI |
|
2455 // Make sure the DLCI is only 6 bits long |
|
2456 __ASSERT_DEBUG(aDLCI <= KMaxRfcommDLCI, Panic(ERfcommInvalidDLCI)); |
|
2457 aDLCI &= KMaxRfcommDLCI; |
|
2458 frm->PutByte((aDLCI << 2) | 0x03); // Add E/A and C/R (both 1) |
|
2459 |
|
2460 TUint8 valueOctet; |
|
2461 |
|
2462 // If we're creating a negotiation request or a response of any kind, |
|
2463 // fill in the rest of the value octets |
|
2464 if(aLen == KRPNCommandLength || aLen == KRPNResponseLength) |
|
2465 { |
|
2466 const TRfcommRemotePortParams& portParams = aRPNTransaction.iPortParams; |
|
2467 |
|
2468 // Second octet carries Baud Rate (AKA 'Bit Rate') |
|
2469 TBps bitRate = EBps9600; //Just to stop the compiler warnings |
|
2470 if(portParams.GetBitRate(bitRate)) // Sets bitRate if valid in portParams |
|
2471 { |
|
2472 valueOctet = 0; |
|
2473 switch(bitRate) |
|
2474 { |
|
2475 // Magic numbers because enums are not the same |
|
2476 // values as the ones specified in the spec and |
|
2477 // adding even more enums would confuse matters |
|
2478 case EBps2400: |
|
2479 valueOctet = 0; |
|
2480 break; |
|
2481 case EBps4800: |
|
2482 valueOctet = 1; |
|
2483 break; |
|
2484 case EBps7200: |
|
2485 case EBps9600: |
|
2486 case EBps19200: |
|
2487 case EBps38400: |
|
2488 case EBps57600: |
|
2489 case EBps115200: |
|
2490 case EBps230400: |
|
2491 valueOctet = (bitRate - EBps7200) + 2; |
|
2492 break; |
|
2493 default: |
|
2494 LOG(_L("RFCOMM:[rfcommmuxer.cpp] Invalid bitrate, ignoring")); |
|
2495 __DEBUGGER( ); |
|
2496 } |
|
2497 } |
|
2498 else |
|
2499 { |
|
2500 valueOctet = 0; |
|
2501 } |
|
2502 frm->PutByte(valueOctet); |
|
2503 |
|
2504 // Third octet is compound. Each part is offset from |
|
2505 // the beginning of the octet. |
|
2506 // Bit No. 76543210 |
|
2507 // Data Bits are stored at start: 000000XX |
|
2508 const TUint8 KStopBitOffset = 2; // 00000X00 |
|
2509 const TUint8 KParityBitOffset = 3; // 0000X000 |
|
2510 const TUint8 KParityTypeOffset = 4; // 00XX0000 |
|
2511 |
|
2512 // Now it gets complicated - here we go... |
|
2513 // Start with the 2 data bits |
|
2514 TDataBits dataBits = EData5; //Just to stop the compiler warnings |
|
2515 if(portParams.GetDataBits(dataBits)) |
|
2516 { |
|
2517 switch (dataBits) |
|
2518 { |
|
2519 // Magic numbers because enums are not the same |
|
2520 // values as the ones specified in the spec and |
|
2521 // adding even more enums would confuse matters |
|
2522 case EData5: |
|
2523 valueOctet = 0; |
|
2524 break; |
|
2525 case EData6: |
|
2526 valueOctet = 2; |
|
2527 break; |
|
2528 case EData7: |
|
2529 valueOctet = 1; |
|
2530 break; |
|
2531 case EData8: |
|
2532 valueOctet = 3; |
|
2533 break; |
|
2534 default: |
|
2535 __DEBUGGER(); |
|
2536 LOG(_L("TransmitRPN - Dodgy Data Bits. Assuming 0\n")); |
|
2537 valueOctet = 0; |
|
2538 } |
|
2539 } |
|
2540 else |
|
2541 valueOctet = 0; |
|
2542 |
|
2543 // Set the stop bit if necessary |
|
2544 // EStop1 corresponds to leaving the stop bit clear |
|
2545 // EStop2 corresponds to setting the stop bit |
|
2546 TStopBits stopBit = EStop1; //Just to stop the compiler warnings |
|
2547 if(portParams.GetStopBit(stopBit)) |
|
2548 { |
|
2549 if(stopBit == EStop2) |
|
2550 valueOctet |= (1 << KStopBitOffset); |
|
2551 } |
|
2552 |
|
2553 TParity parity = EParityNone; //Just to stop the compiler warnings |
|
2554 if(portParams.GetParity(parity)) |
|
2555 { |
|
2556 // Set the parity bit by default |
|
2557 valueOctet |= (1 << KParityBitOffset); |
|
2558 // Set Parity Type (and clear parity bit if necessary) |
|
2559 switch(parity) |
|
2560 { |
|
2561 // Magic numbers because enums are not the same |
|
2562 // values as the ones specified in the spec and |
|
2563 // adding even more enums would confuse matters |
|
2564 case EParityNone: |
|
2565 // Clear the parity bit |
|
2566 valueOctet &= ~(1 << KParityBitOffset); |
|
2567 break; |
|
2568 case EParityOdd: |
|
2569 // Do nothing, this parity type is stored as 0 |
|
2570 break; |
|
2571 case EParityEven: |
|
2572 valueOctet |= (2 << KParityTypeOffset); |
|
2573 break; |
|
2574 case EParityMark: |
|
2575 valueOctet |= (1 << KParityTypeOffset); |
|
2576 break; |
|
2577 case EParitySpace: |
|
2578 valueOctet |= (3 << KParityTypeOffset); |
|
2579 break; |
|
2580 } |
|
2581 } |
|
2582 // The reserved bits are already 0 so ignore them |
|
2583 // Finally finished building Octet 3, now store it |
|
2584 frm->PutByte(valueOctet); |
|
2585 |
|
2586 // Octet 4 contains 6 flow control bits and |
|
2587 // 2 reserved bits (both zero) |
|
2588 if(portParams.GetFlowCtrl(valueOctet)) |
|
2589 { |
|
2590 // Make sure the Flow Control is only 6 bits |
|
2591 if (valueOctet >> 6 != 0) |
|
2592 { |
|
2593 LOG(_L("Invalid Flow Control\n")); |
|
2594 __DEBUGGER(); |
|
2595 delete frm; |
|
2596 |
|
2597 return KErrArgument; |
|
2598 } |
|
2599 frm->PutByte(valueOctet); |
|
2600 } |
|
2601 else |
|
2602 { |
|
2603 frm->PutByte(0); |
|
2604 } |
|
2605 |
|
2606 |
|
2607 // Octet 5 contains 8 XON bits |
|
2608 if(portParams.GetXOnChar(valueOctet)) |
|
2609 { |
|
2610 frm->PutByte(valueOctet); |
|
2611 } |
|
2612 else |
|
2613 { |
|
2614 frm->PutByte(0); |
|
2615 } |
|
2616 |
|
2617 // Octet 6 contains 8 XOFF bits |
|
2618 if(portParams.GetXOffChar(valueOctet)) |
|
2619 { |
|
2620 frm->PutByte(valueOctet); |
|
2621 } |
|
2622 else |
|
2623 { |
|
2624 frm->PutByte(0); |
|
2625 } |
|
2626 |
|
2627 // Octets 7 and 8 are the Parameter Mask |
|
2628 // Lowest byte first... |
|
2629 valueOctet = TUint8(aRPNTransaction.iParamMask & 0xff); |
|
2630 frm->PutByte(valueOctet); |
|
2631 // ...then the upper byte |
|
2632 valueOctet = TUint8(aRPNTransaction.iParamMask >> 8); |
|
2633 frm->PutByte(valueOctet); |
|
2634 } |
|
2635 |
|
2636 // Finished building frame, so send it |
|
2637 EnqueFrame(frm); |
|
2638 |
|
2639 return KErrNone; |
|
2640 } |
|
2641 |
|
2642 |
|
2643 TInt CRfcommMuxer::TransmitPN(TUint8 aDLCI, TBool aCommand, |
|
2644 const TRfcommPortParams& aParams, CRfcommSAP* aSAP) |
|
2645 /** |
|
2646 Format is: |
|
2647 @verbatim |
|
2648 Value Octet Bit 1 Bit 2 Bit 3 Bit 4 Bit 5 Bit 6 Bit 7 Bit 8 |
|
2649 1 D1 D2 D3 D4 D5 D6 0 0 |
|
2650 2 I1 I2 I3 I4 CL1 CL2 CL3 CL4 |
|
2651 3 P1 P2 P3 P4 P5 P6 0 0 |
|
2652 4 T1 T2 T3 T4 T5 T6 T7 T8 |
|
2653 5 N1 N2 N3 N4 N5 N6 N7 N8 |
|
2654 6 N9 N10 N11 N12 N13 N14 N15 N16 |
|
2655 7 NA1 NA2 NA3 NA4 NA5 NA6 NA7 NA8 |
|
2656 8 K1 K2 K3 0 0 0 0 0 |
|
2657 Where: |
|
2658 D1-6 : DLCI |
|
2659 I1-4 : frame type (must be zero) |
|
2660 CL1-4 : Convergence layer |
|
2661 (if CBFC, set to 0x0f in request, 0x0e in response.... |
|
2662 .....otherewise must be zero) |
|
2663 P1-6 : Priority |
|
2664 T1-8 : Acknowledgement timer T1 (must be zero) |
|
2665 N1-16 : Max frame size (default 127) |
|
2666 NA1-8 : Max retransmissions (must be zero) |
|
2667 K1-3 : Error recovery window |
|
2668 (initial credit (0-7) for CBFC or set to 0 if fallback to FCon/off) |
|
2669 @endverbatim |
|
2670 **/ |
|
2671 { |
|
2672 LOG3(_L("RFCOMM: Sending PN %S for dlci %d (MTU=%d)"), |
|
2673 (aCommand?&KCommandText:&KResponseText) , aDLCI, aParams.iMaxFrameSize); |
|
2674 CRfcommMuxCtrlFrame* frm=NewSignalFrame(KRPNCommandLength, aCommand, aSAP); |
|
2675 |
|
2676 if(!frm) |
|
2677 return KErrNoMemory; |
|
2678 |
|
2679 frm->SetDLCI(aDLCI); // For use when matching DM responses to mux control command frames |
|
2680 frm->SetCommandType(KPNType, aCommand); |
|
2681 frm->SetResponseNeeded(aCommand); |
|
2682 // Now the 8 parameters |
|
2683 __ASSERT_DEBUG(aDLCI <= KMaxRfcommDLCI, Panic(ERfcommInvalidDLCI)); |
|
2684 aDLCI &= KMaxRfcommDLCI; |
|
2685 frm->PutByte(aDLCI); |
|
2686 //TRY_CBFC ... NB used to do some negotiating here - but in current version all |
|
2687 // such negotiation is performed in HandlePN |
|
2688 #ifdef NO_CBFC |
|
2689 frm->PutByte(0x00); |
|
2690 #else |
|
2691 frm->PutByte(iFlowStrategy->PNConvergenceLayer(aCommand)); |
|
2692 #endif |
|
2693 LOG2(_L("RFCOMM: CBFC Tried 0x%x, Type 0x%x"), iTriedCBFC, FlowType()); |
|
2694 |
|
2695 __ASSERT_DEBUG(aParams.iPriority <= KMaxRfcommPriority, Panic(ERfcommInvalidDLCI)); |
|
2696 TUint8 params = aParams.iPriority & KMaxRfcommPriority; |
|
2697 frm->PutByte(params); |
|
2698 frm->PutByte(0x00); // T bits |
|
2699 frm->PutLittleEndian16(aParams.iMaxFrameSize); |
|
2700 frm->PutByte(0x00); // NA bits |
|
2701 #ifdef NO_CBFC |
|
2702 frm->PutByte(0x00); |
|
2703 #else |
|
2704 frm->PutByte(aParams.iInitialCredit); |
|
2705 #endif |
|
2706 EnqueFrame(frm); |
|
2707 |
|
2708 return KErrNone; |
|
2709 } |
|
2710 |
|
2711 |
|
2712 TInt CRfcommMuxer::TransmitFCon(TBool aCommand, CRfcommSAP* aSAP) |
|
2713 /** |
|
2714 Send a FCon command frame. |
|
2715 |
|
2716 If aCommand is True then this should be a command, else it is a |
|
2717 response. |
|
2718 **/ |
|
2719 { |
|
2720 LOG(_L("RFCOMM: Trying to send FCon")); |
|
2721 if(!(iFlowStrategy->AllowFCOnOff(aCommand))) |
|
2722 return KErrNotSupported; //Occurs if CBFC is on. |
|
2723 |
|
2724 LOG1(_L("RFCOMM: Sending FCon %S"),aCommand?&KCommandText:&KResponseText); |
|
2725 //FC |
|
2726 CRfcommMuxCtrlFrame* frm=NewSignalFrame(KFConCommandLength, aCommand, aSAP); |
|
2727 if(!frm) |
|
2728 return KErrNoMemory; |
|
2729 |
|
2730 frm->SetCommandType(KFConType, aCommand); |
|
2731 frm->SetResponseNeeded(aCommand); |
|
2732 EnqueFrame(frm); |
|
2733 // |
|
2734 return KErrNone; |
|
2735 } |
|
2736 |
|
2737 TInt CRfcommMuxer::TransmitNSC(TBool aCommand, TUint8 aType) |
|
2738 /** |
|
2739 Send a NSC command frame. |
|
2740 |
|
2741 **/ |
|
2742 { |
|
2743 LOG(_L("RFCOMM: Sending NSC")); |
|
2744 |
|
2745 CRfcommMuxCtrlFrame* frm=NewSignalFrame(KNSCCommandLength, EFalse); |
|
2746 if(!frm) |
|
2747 return KErrNoMemory; |
|
2748 |
|
2749 frm->SetCommandType(KNSCType, EFalse); |
|
2750 aCommand = (aCommand) ? 1 : 0; |
|
2751 frm->PutByte(aType | (aCommand << 1)); |
|
2752 EnqueFrame(frm); |
|
2753 |
|
2754 return KErrNone; |
|
2755 } |
|
2756 |
|
2757 |
|
2758 TInt CRfcommMuxer::TransmitFCoff(TBool aCommand, CRfcommSAP* aSAP) |
|
2759 /** |
|
2760 Send a FCoff command frame. |
|
2761 |
|
2762 If aCommand is True then this should be a command, else it is a |
|
2763 response. |
|
2764 **/ |
|
2765 { |
|
2766 LOG(_L("RFCOMM: Trying to send FCoff")); |
|
2767 if(!(iFlowStrategy->AllowFCOnOff(aCommand))) |
|
2768 return KErrNotSupported; //Occurs if CBFC is on. |
|
2769 |
|
2770 LOG1(_L("RFCOMM: Sending FCoff %S"),aCommand?&KCommandText:&KResponseText); |
|
2771 //FC |
|
2772 CRfcommMuxCtrlFrame* frm=NewSignalFrame(KFConCommandLength, aCommand, aSAP); |
|
2773 if(!frm) |
|
2774 return KErrNoMemory; |
|
2775 |
|
2776 frm->SetCommandType(KFCoffType, aCommand); |
|
2777 frm->SetResponseNeeded(aCommand); |
|
2778 EnqueFrame(frm); |
|
2779 // |
|
2780 return KErrNone; |
|
2781 } |
|
2782 |
|
2783 |
|
2784 TInt CRfcommMuxer::TransmitMSC(TUint8 aDLCI, TBool aCommand, TUint8 aSignals, CRfcommSAP* aSAP) |
|
2785 /** |
|
2786 Send out a MSC signalling command (or response) which travels in a UIH frame |
|
2787 |
|
2788 Format is (TS07.10 5.4.6.3.7): |
|
2789 |
|
2790 | Command | Length | DLCI | Signals | |
|
2791 **/ |
|
2792 { |
|
2793 LOG3(_L("RFCOMM: SendMSC %S DLCI %d, Signals %x"), |
|
2794 (aCommand?&KCommandText:&KResponseText), aDLCI, aSignals); |
|
2795 |
|
2796 CRfcommMuxCtrlFrame* frm=NewSignalFrame(KMSCCommandLength, aCommand, aSAP); |
|
2797 if(!frm) |
|
2798 return KErrNoMemory; |
|
2799 |
|
2800 frm->SetDLCI(aDLCI); // For use when matching DM responses to mux control command frames |
|
2801 frm->SetCommandType(KMSCType, aCommand); |
|
2802 frm->SetResponseNeeded(aCommand); |
|
2803 // DLCI |
|
2804 frm->PutByte((aDLCI << 2) | 0x03); // dlci, 1, EA |
|
2805 // Signals |
|
2806 iFlowStrategy->OutgoingMSCReviseSignals(aCommand, aSignals); //FC-bit to zero if CBFC. |
|
2807 frm->PutByte((aSignals << 1) | 0x01); // signals, EA |
|
2808 EnqueFrame(frm); |
|
2809 |
|
2810 return KErrNone; |
|
2811 } |
|
2812 |
|
2813 TInt CRfcommMuxer::TransmitTest(TBool aCommand, const TDesC8& aData, CRfcommSAP* aSAP) |
|
2814 /** |
|
2815 Send out a Test signalling command |
|
2816 **/ |
|
2817 { |
|
2818 LOG1(_L("RFCOMM: Sending Test %S"),(aCommand?&KCommandText:&KResponseText)); |
|
2819 |
|
2820 CRfcommMuxCtrlFrame* frm=NewSignalFrame(aData.Length(), aCommand, aSAP); |
|
2821 if(!frm) |
|
2822 return KErrNoMemory; |
|
2823 |
|
2824 frm->SetCommandType(KTestType, aCommand); |
|
2825 frm->SetResponseNeeded(aCommand); |
|
2826 // Put the data in |
|
2827 frm->PutData(aData); |
|
2828 EnqueFrame(frm); |
|
2829 |
|
2830 return KErrNone; |
|
2831 } |
|
2832 |
|
2833 TInt CRfcommMuxer::TransmitRLS(TBool aCommand, TUint8 aDLCI, TUint8 aStatus, |
|
2834 CRfcommSAP* aSAP) |
|
2835 /** |
|
2836 Send out a RLS signalling command |
|
2837 **/ |
|
2838 { |
|
2839 CRfcommMuxCtrlFrame* frm=NewSignalFrame(KRLSCommandLength, aCommand, aSAP); |
|
2840 if(!frm) |
|
2841 return KErrNoMemory; |
|
2842 |
|
2843 frm->SetDLCI(aDLCI); // For use when matching DM responses to mux control command frames |
|
2844 frm->SetCommandType(KRLSType, aCommand); |
|
2845 frm->SetResponseNeeded(aCommand); |
|
2846 // Now the DLCI |
|
2847 frm->PutByte(aDLCI << 2 | 0x03); // C/R & EA both 1 |
|
2848 // Now the status |
|
2849 frm->PutByte(aStatus); |
|
2850 EnqueFrame(frm); |
|
2851 |
|
2852 return KErrNone; |
|
2853 } |
|
2854 |
|
2855 |
|
2856 TUint8 CRfcommMuxer::BuildAddr(TUint8 aDLCI, TBool aCommand) |
|
2857 /** |
|
2858 Add C/R and EA bits to an existing DLCI to create an address field. |
|
2859 |
|
2860 Format of address is: |
|
2861 |
|
2862 EA | C/R | DLCI | |
|
2863 |
|
2864 where C/R depends on whether this is a command frame and what |
|
2865 the direction bit is set to. |
|
2866 **/ |
|
2867 |
|
2868 { |
|
2869 aDLCI<<=2; // Shift up for C/R and EA bits |
|
2870 aDLCI |= 0x01; // EA is 1 |
|
2871 if(aCommand) |
|
2872 { |
|
2873 aDLCI |= iDirection << 1; // C/R is same as direction for commands |
|
2874 } |
|
2875 else |
|
2876 { |
|
2877 aDLCI |= (~iDirection & 1) << 1; // Response has C/R =!Direction |
|
2878 } |
|
2879 return aDLCI; |
|
2880 } |
|
2881 |
|
2882 CRfcommMuxCtrlFrame* CRfcommMuxer::NewSignalFrame(TInt aCommandLength, TBool aCommand, CRfcommSAP* aSAP) |
|
2883 /** |
|
2884 Create a new UIH frame for a mux command |
|
2885 |
|
2886 NB The command length excludes the Type octet and the Length octet(s) |
|
2887 **/ |
|
2888 { |
|
2889 CRfcommMuxCtrlFrame* frm=NULL; |
|
2890 TRAPD(err, frm=CRfcommMuxCtrlFrame::NewL(aCommandLength, *this, aSAP)); |
|
2891 if(!err) |
|
2892 { |
|
2893 // The C/R bit for the UIH frame is always 1 for the initiator |
|
2894 // and 0 for the responder, which is the same as for commands. |
|
2895 frm->SetAddress(BuildAddr(KMuxDLCI, ETrue)); |
|
2896 frm->SetControl(KUIHCtrlField); |
|
2897 frm->SetResponseNeeded(aCommand); // Commands need responses |
|
2898 } |
|
2899 return frm; // NB frm will be NULL if NewL failed. |
|
2900 } |
|
2901 |
|
2902 CRfcommCtrlFrame* CRfcommMuxer::NewFrame(CRfcommSAP* aSAP) |
|
2903 /** |
|
2904 Returns a ctrl frame (SABM, DISC, DM, UA) |
|
2905 **/ |
|
2906 { |
|
2907 return new CRfcommCtrlFrame(*this, aSAP); |
|
2908 } |
|
2909 |
|
2910 CRfcommDataFrame* CRfcommMuxer::NewDataFrame(TUint8 aDLCI, TInt aLen, |
|
2911 TUint8 aCredit, CRfcommSAP* aSAP) |
|
2912 /** |
|
2913 Returns a new data (UIH) frame for the specified DLCI |
|
2914 |
|
2915 The C/R bit for the UIH frame is always 1 for the initiator and |
|
2916 0 for the responder, which is the same as for commands. |
|
2917 **/ |
|
2918 { |
|
2919 // NB should be NULL if CRfcommDataFrame was not created |
|
2920 return iFlowStrategy->NewDataFrame(*this, BuildAddr(aDLCI, ETrue), aLen, aCredit, aSAP); |
|
2921 } |
|
2922 |
|
2923 TBool CRfcommMuxer::CheckFCS(TUint8 aFCS, TUint8 aCtrl) |
|
2924 /** |
|
2925 Check the FCS on the incoming frame. |
|
2926 |
|
2927 FCS calculated over addr & ctrl for UIH, and addr, ctrl & len |
|
2928 for the others. |
|
2929 **/ |
|
2930 { |
|
2931 TUint8* data=&iNextPacket->Des()[0]; |
|
2932 TInt len; |
|
2933 |
|
2934 if(aCtrl == KUIHCtrlField) |
|
2935 len=2; // addr & ctrl fields |
|
2936 else |
|
2937 len=iCurrentHeaderLength; |
|
2938 |
|
2939 return ::CheckFCS(data, len, aFCS); |
|
2940 } |
|
2941 |
|
2942 CRfcommSAP* CRfcommMuxer::FindSAP(const TUint8 aDLCI) |
|
2943 /** |
|
2944 Find the SAP that is on this channel. |
|
2945 |
|
2946 If no such sap exists, return 0 |
|
2947 **/ |
|
2948 { |
|
2949 TDblQueIter<CRfcommSAP> iter(iSAPs); |
|
2950 CRfcommSAP* sap; |
|
2951 |
|
2952 while(iter) |
|
2953 { |
|
2954 sap=iter++; |
|
2955 if(sap->DLCI() == aDLCI) |
|
2956 return sap; |
|
2957 } |
|
2958 |
|
2959 iter=iBlockedSAPs; |
|
2960 while(iter) |
|
2961 { |
|
2962 sap=iter++; |
|
2963 if(sap->DLCI() == aDLCI) |
|
2964 return sap; |
|
2965 } |
|
2966 |
|
2967 return 0; |
|
2968 } |
|
2969 |
|
2970 CRfcommSAP* CRfcommMuxer::FindConnectedOrListeningSAP(const TUint8 aDLCI) |
|
2971 /** |
|
2972 Find a SAP that is on this channel, or get a listening SAP from the protocol. |
|
2973 |
|
2974 If there is neither a SAP on this channel or a listening SAP then return NULL. |
|
2975 **/ |
|
2976 { |
|
2977 CRfcommSAP* mySAP=FindSAP(aDLCI); |
|
2978 if(mySAP==NULL) |
|
2979 { |
|
2980 // No SAP is currently in existance for this DLCI. |
|
2981 // Find a listening SAP if possible. |
|
2982 TBTSockAddr addr; |
|
2983 addr.SetBTAddr(iRemoteAddr); |
|
2984 addr.SetPort(aDLCI>>1); // i.e. convert from DLCI to server channel |
|
2985 mySAP=iProtocol.FindIdleSAP(addr); |
|
2986 } |
|
2987 |
|
2988 return mySAP; |
|
2989 } |
|
2990 |
|
2991 void CRfcommMuxer::EnqueFrame(CRfcommFrame* aFrm) |
|
2992 /** |
|
2993 Add this frame to the outbound Q. |
|
2994 |
|
2995 Exactly where the frame is added depends on the type of frame, |
|
2996 frames where Priority==ETrue are added just behind any other |
|
2997 priority frames in the queue, others are added right at the end. |
|
2998 **/ |
|
2999 { |
|
3000 //FC |
|
3001 if(!aFrm->Priority()) |
|
3002 { |
|
3003 // Not a high priority frame. To the back of the entire queue! |
|
3004 iOutboundQ.AddLast(*aFrm); |
|
3005 } |
|
3006 else |
|
3007 { |
|
3008 // A priority frame. Jump to the back of the priority queue! |
|
3009 |
|
3010 // First, we need to find the back of the priority queue... |
|
3011 TDblQueIter<CRfcommFrame> iter(iOutboundQ); |
|
3012 while(iter && (*iter).Priority()) |
|
3013 { |
|
3014 iter++; |
|
3015 } |
|
3016 |
|
3017 if(iter) |
|
3018 { |
|
3019 // We found a non-priority frame - jump into queue before this one |
|
3020 aFrm->iLink.AddBefore(&(*iter).iLink); |
|
3021 } |
|
3022 else |
|
3023 { |
|
3024 // We ran out of frames, so either entire Q is made up of priority |
|
3025 // frames, or the Q is empty. Either way, we can add to the end |
|
3026 // of the queue. |
|
3027 iOutboundQ.AddLast(*aFrm); |
|
3028 } |
|
3029 } |
|
3030 iOutboundQLength++; |
|
3031 TryToSend(); |
|
3032 } |
|
3033 |
|
3034 void CRfcommMuxer::TryToSend() |
|
3035 /** |
|
3036 Just Q an async callback |
|
3037 **/ |
|
3038 { |
|
3039 iSendCallback->CallBack(); |
|
3040 } |
|
3041 |
|
3042 TInt CRfcommMuxer::TryToSendCallbackStatic(TAny* aMux) |
|
3043 { |
|
3044 (static_cast<CRfcommMuxer*>(aMux))->TryToSendCallback(); |
|
3045 return EFalse; |
|
3046 } |
|
3047 |
|
3048 #ifdef __FLOG_ACTIVE |
|
3049 void CRfcommMuxer::LogMuxCommand(CRfcommSAP* aSAP, CRfcommMuxer* /*aMux*/, TUint8 aCommand) |
|
3050 { |
|
3051 TUint8 signals; |
|
3052 |
|
3053 switch(aCommand) |
|
3054 { |
|
3055 case KTestType: |
|
3056 LOG(_L("TestCommand")); |
|
3057 break; |
|
3058 case KPNType: |
|
3059 LOG(_L("PN"));// |
|
3060 break; |
|
3061 case KRPNType: |
|
3062 LOG(_L("RPN"));// |
|
3063 break; |
|
3064 case KFConType: |
|
3065 //may need length byte value |
|
3066 LOG(_L("FcOn"));// |
|
3067 break; |
|
3068 case KFCoffType: |
|
3069 LOG(_L("FcOff"));// |
|
3070 break; |
|
3071 case KMSCType: |
|
3072 if (aSAP) |
|
3073 { |
|
3074 signals = aSAP->Signals();//V.24 signals in MSC |
|
3075 LOG1(_L("MSC %d"),signals);// |
|
3076 } |
|
3077 else |
|
3078 { |
|
3079 LOG(_L("RFCOMM: Unable to get signals for KMSCType command, unknown SAP")); |
|
3080 } |
|
3081 break; |
|
3082 case KNSCType: |
|
3083 LOG(_L("NSC"));// |
|
3084 break; |
|
3085 case KRLSType: |
|
3086 LOG(_L("RLS"));// |
|
3087 }; |
|
3088 |
|
3089 } |
|
3090 |
|
3091 void CRfcommMuxer::ExplainOutgoingFrame(CRfcommFrame* aFrm, CRfcommMuxer* aMux ) |
|
3092 { |
|
3093 #ifndef TCI |
|
3094 //Show what type of frame we have sent and the various parts of the frame which are important. |
|
3095 |
|
3096 TInt frametype = aFrm->Type(); //Is it a Ctrl Frame, Data Frame, CreditDataFrame or Mux Ctrl Frame ? |
|
3097 TUint8 ctrlfield = aFrm->Ctrl(); |
|
3098 TUint8 ctrl = ctrlfield&~KPollFinalBitmask; //tells whether SABM, DISC, UA, DM |
|
3099 TUint8 addressfield = aFrm->Address(); //Contains EA, CR, DLCI |
|
3100 // TUint8 dlci = aMux->DecodeDLCI(addressfield); //extracts dlci |
|
3101 TBool EA = addressfield & KEABitmask; //Is the EA bit set? |
|
3102 TBool CR = addressfield & KCRBitmask; //Is the CR bit set? |
|
3103 TBool poll = ctrlfield & KPollFinalBitmask; //Is the p/f bit set? |
|
3104 |
|
3105 switch(frametype) |
|
3106 { |
|
3107 case KCtrlFrameType: |
|
3108 { |
|
3109 //CRfcommCtrlFrame* ctrlfrm=static_cast<CRfcommCtrlFrame*>(aFrm); |
|
3110 //TUint16 ctrlframelength = ctrlfrm->DataLength(); |
|
3111 LOG(_L("Tx:")); |
|
3112 |
|
3113 if(ctrl==KSABMCtrlField) |
|
3114 { |
|
3115 LOG(_L("Tx: SABM")); |
|
3116 } |
|
3117 if(ctrl==KUACtrlField) |
|
3118 { |
|
3119 LOG(_L("Tx: UA")); |
|
3120 } |
|
3121 if(ctrl==KDMCtrlField) |
|
3122 { |
|
3123 LOG(_L("Tx: DM")); |
|
3124 } |
|
3125 if(ctrl==KDISCCtrlField) |
|
3126 { |
|
3127 LOG(_L("Tx: DISC")); |
|
3128 } |
|
3129 } |
|
3130 break; |
|
3131 case KDataFrameType: //CRfCommUIHFrame |
|
3132 { |
|
3133 CRfcommUIHFrame* uihfrm=static_cast<CRfcommUIHFrame*>(aFrm); |
|
3134 TUint16 uihframelength = uihfrm->DataLength(); |
|
3135 |
|
3136 if(poll) |
|
3137 LOG(_L("Tx: UIH credit data frame")); |
|
3138 |
|
3139 if (uihframelength<=127) |
|
3140 { |
|
3141 LOG(_L("Tx: UIH simple data frame")); |
|
3142 } |
|
3143 else |
|
3144 { |
|
3145 LOG(_L("Tx: UIH simple data frame")); |
|
3146 } |
|
3147 } |
|
3148 break; |
|
3149 case KCreditDataFrameType: //CRfcommCreditDataFrame |
|
3150 { |
|
3151 CRfcommCreditDataFrame* creditfrm=static_cast<CRfcommCreditDataFrame*>(aFrm); |
|
3152 TUint8 credits = creditfrm->Credit(); |
|
3153 //BLOG Credits |
|
3154 TUint16 length = creditfrm->DataLength(); |
|
3155 |
|
3156 if (length<=127) |
|
3157 { |
|
3158 LOG1(_L("Tx: Short UIH credit data frame with %d credits"), credits); |
|
3159 } |
|
3160 else |
|
3161 { |
|
3162 LOG1(_L("Tx :Long UIH credit data frame with %d credits"), credits); |
|
3163 } |
|
3164 } |
|
3165 break; |
|
3166 case KMuxCtrlFrameType: //CRfcommMuxCtrlFrame containing muxer messages/commands |
|
3167 { |
|
3168 //#ifndef TCI |
|
3169 CRfcommMuxCtrlFrame* muxfrm=static_cast<CRfcommMuxCtrlFrame*>(aFrm); |
|
3170 TUint8 muxdlci = muxfrm->iDLCI; |
|
3171 CRfcommSAP* sap = aMux->FindSAP(muxdlci); //Find the SAP that is on this dlci for aMux |
|
3172 TUint8 command = muxfrm->CommandType(); |
|
3173 if (sap) |
|
3174 { |
|
3175 LogMuxCommand(sap, aMux, command); |
|
3176 } |
|
3177 else |
|
3178 { |
|
3179 LOG1(_L("RFCOMM: Outgoing frame for unknown SAP: command = %d"),command); |
|
3180 LogMuxCommand(NULL, aMux, command); |
|
3181 } |
|
3182 //#endif |
|
3183 } |
|
3184 break; |
|
3185 default: //CRfcommDataFrame |
|
3186 break; |
|
3187 }; |
|
3188 LOG1(_L(" P/F bit = %d"), poll); |
|
3189 LOG1(_L(" EA bit = %d"), EA); |
|
3190 LOG1(_L(" C/R bit = %d"), CR); |
|
3191 |
|
3192 LOG1(_L("Frame of type %d"), frametype); |
|
3193 #endif |
|
3194 } |
|
3195 #endif //FLOG_ACTIVE |
|
3196 |
|
3197 void CRfcommMuxer::TryToSendCallback() |
|
3198 /** |
|
3199 Attempt to send a frame off the bottom of the Q. |
|
3200 |
|
3201 This is called back via an Async callback to break |
|
3202 "up to the top and down to the bottom from the RunL" |
|
3203 |
|
3204 Currently assumes that a frame will always fit in an L2CAP |
|
3205 packet. |
|
3206 **/ |
|
3207 { |
|
3208 LOG1(_L("RFCOMM: Send Callback with Q len %d"), iOutboundQLength); |
|
3209 CRfcommFrame* frm; |
|
3210 |
|
3211 __ASSERT_DEBUG(!(iOutboundQLength !=0 && iOutboundQ.IsEmpty()),User::Panic(_L("Debug"),1)); |
|
3212 |
|
3213 while ( !iOutboundQ.IsEmpty() && !L2CAPBlocked() && |
|
3214 ((iOutboundQ.First()->Priority()) || ClearToSend()) ) |
|
3215 // i.e. We have frames to send, L2CAP isn't blocked and we are EITHER clear to send |
|
3216 // OR we have priority frames to send. |
|
3217 { |
|
3218 LOG1(_L("RFCOMM: Sending @ Q len %d"), iOutboundQLength); |
|
3219 frm=iOutboundQ.First(); |
|
3220 |
|
3221 FTRACE(ExplainOutgoingFrame(frm, this)); |
|
3222 #ifdef _DEBUG |
|
3223 //FOR DEBUG PURPOSES ONLY |
|
3224 if(frm->Address() != KMuxDLCI) |
|
3225 { |
|
3226 CRfcommSAP* sap = FindSAP(DecodeDLCI(frm->Address())); |
|
3227 if(sap) |
|
3228 { |
|
3229 switch(frm->Ctrl()) |
|
3230 { |
|
3231 case KUIHCtrlField: |
|
3232 case KUIHCBFCCtrlField: |
|
3233 if(frm->DataLength()) |
|
3234 { |
|
3235 iDataFramesSent++; |
|
3236 LOG1(_L("RFCOMM: Total data frames transmitted by mux (=> caused Tx decrement): %d"), iDataFramesSent); |
|
3237 } |
|
3238 break; |
|
3239 default: |
|
3240 break; |
|
3241 } |
|
3242 } |
|
3243 } |
|
3244 #endif |
|
3245 |
|
3246 // Datagram send, so 0 or length of packet returned |
|
3247 |
|
3248 if(!iBoundSAP->Write(frm->Data(), 0)) |
|
3249 { |
|
3250 LOG(_L("RFCOMM: L2CAP send failed, restoring frame")); |
|
3251 L2CAPBlocked(ETrue); |
|
3252 } |
|
3253 else |
|
3254 { |
|
3255 // Managed to send this packet, so either move to responseQ or delete |
|
3256 frm->iLink.Deque(); |
|
3257 if(frm->ResponseNeeded()) |
|
3258 { |
|
3259 iResponseQ.AddLast(*frm); |
|
3260 frm->QueResponseTimer(); |
|
3261 } |
|
3262 else |
|
3263 { |
|
3264 delete frm; |
|
3265 } |
|
3266 iOutboundQLength--; |
|
3267 } |
|
3268 } |
|
3269 |
|
3270 // We may have made room for some new sap to send, so let them know |
|
3271 SignalSAPsCanSend(); |
|
3272 |
|
3273 // Equally, we may have sent all the remaining queued frames, and have no SAPs |
|
3274 // left to service. |
|
3275 CheckForIdle(); |
|
3276 } |
|
3277 |
|
3278 |
|
3279 void CRfcommMuxer::SignalSAPsCanSend() |
|
3280 /** |
|
3281 We may be able to let some saps send, so start signalling them |
|
3282 **/ |
|
3283 { |
|
3284 CRfcommSAP* sap; |
|
3285 TDblQueIter<CRfcommSAP> iter(iBlockedSAPs); |
|
3286 |
|
3287 while(iter && !L2CAPBlocked() && ClearToSend()) |
|
3288 { |
|
3289 sap = iter++; |
|
3290 SetSendBlocked(*sap, EFalse); |
|
3291 sap->CanSend(); |
|
3292 } |
|
3293 } |
|
3294 |
|
3295 void CRfcommMuxer::CheckForIdle(TBool aClosing) |
|
3296 /** |
|
3297 Check to see if we're still needed. If not, Q a delayed delete. |
|
3298 **/ |
|
3299 { |
|
3300 if (aClosing) |
|
3301 { |
|
3302 // If we could be in the position where we have no SAPs then the next |
|
3303 // time we set the idle timer we should use the closing value. |
|
3304 iMuxIdleTimeout = KRfcommMuxIdleTimeoutClosing; |
|
3305 } |
|
3306 |
|
3307 if(iOutboundQLength==0 && iSAPs.IsEmpty() && iBlockedSAPs.IsEmpty()) |
|
3308 { |
|
3309 QueIdleTimer(); |
|
3310 } |
|
3311 } |
|
3312 |
|
3313 void CRfcommMuxer::QueIdleTimer() |
|
3314 /** |
|
3315 Queues the idle timer if necessary |
|
3316 **/ |
|
3317 { |
|
3318 if(!iIdleTimerQueued) |
|
3319 { |
|
3320 LOG2(_L("RFCOMM: Mux 0x%08x Q idle timer for %d microsecs"), this, iMuxIdleTimeout); |
|
3321 iIdleTimerQueued=ETrue; |
|
3322 BTSocketTimer::Queue(iMuxIdleTimeout, iIdleTimerEntry); |
|
3323 } |
|
3324 } |
|
3325 |
|
3326 void CRfcommMuxer::DequeIdleTimer() |
|
3327 /** |
|
3328 Deques idle timer if necessary |
|
3329 **/ |
|
3330 { |
|
3331 if(iIdleTimerQueued) |
|
3332 { |
|
3333 LOG1(_L("RFCOMM: Mux 0x%08x deQ idle timer"), this); |
|
3334 BTSocketTimer::Remove(iIdleTimerEntry); |
|
3335 iIdleTimerQueued=EFalse; |
|
3336 } |
|
3337 } |
|
3338 |
|
3339 TInt CRfcommMuxer::IdleTimerExpired(TAny* aMux) |
|
3340 /** |
|
3341 Static idle callback. |
|
3342 |
|
3343 This is entered after all our saps have gone away. |
|
3344 |
|
3345 **/ |
|
3346 { |
|
3347 LOG1(_L("RFCOMM: Mux %08x idle timer expired"), aMux); |
|
3348 // Start the delete process |
|
3349 CRfcommMuxer* mux = static_cast<CRfcommMuxer*>(aMux); |
|
3350 mux->iIdleTimerQueued=EFalse; // Obviously... |
|
3351 |
|
3352 __ASSERT_DEBUG(mux->iSAPs.IsEmpty() && mux->iBlockedSAPs.IsEmpty(), |
|
3353 Panic(ERfcommIdleTimeoutWhenNotIdle)); |
|
3354 |
|
3355 mux->iMuxChannel->Close(); // May call back syncronously or asynchronously |
|
3356 // into MuxChannelClosed |
|
3357 return FALSE; |
|
3358 } |
|
3359 |
|
3360 void CRfcommMuxer::MuxChannelClosed() |
|
3361 { |
|
3362 // remove this muxer |
|
3363 iProtocol.MuxDown(*this); |
|
3364 } |
|
3365 |
|
3366 void CRfcommMuxer::DeleteQueuedFrames() |
|
3367 /** |
|
3368 Delete all frames that are on either Q. |
|
3369 **/ |
|
3370 { |
|
3371 TDblQueIter<CRfcommFrame> iter(iOutboundQ); |
|
3372 CRfcommFrame* frm; |
|
3373 |
|
3374 while(iter) |
|
3375 { |
|
3376 frm=iter++; |
|
3377 delete frm; |
|
3378 } |
|
3379 iOutboundQLength=0; |
|
3380 |
|
3381 iter=iResponseQ; |
|
3382 while(iter) |
|
3383 { |
|
3384 frm=iter++; |
|
3385 delete frm; |
|
3386 } |
|
3387 } |
|
3388 |