--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/bluetooth/btstack/l2cap/l2sap.cpp Fri Jan 15 08:13:17 2010 +0200
@@ -0,0 +1,1187 @@
+// Copyright (c) 1999-2009 Nokia Corporation and/or its subsidiary(-ies).
+// All rights reserved.
+// This component and the accompanying materials are made available
+// under the terms of "Eclipse Public License v1.0"
+// which accompanies this distribution, and is available
+// at the URL "http://www.eclipse.org/legal/epl-v10.html".
+//
+// Initial Contributors:
+// Nokia Corporation - initial contribution.
+//
+// Contributors:
+//
+// Description:
+// Implements the code for the connected and group L2CAP saps
+//
+//
+
+#include <bluetooth/logger.h>
+
+#include "l2sap.h"
+
+#include "l2sapstates.h"
+#include "l2cap.h"
+
+#include "l2util.h"
+
+#include "L2CapSDUQueue.h"
+#include "L2CapDataController.h"
+#include "l2capSAPSignalHandler.h"
+#include "L2types.h"
+
+#include "L2CapDebugControlInterface.h"
+#include "l2capCommand.h"
+
+#ifdef __FLOG_ACTIVE
+_LIT8(KLogComponent, LOG_COMPONENT_L2CAP);
+#endif
+
+CL2CAPConnectionSAP* CL2CAPConnectionSAP::NewL(CL2CAPProtocol& aProt)
+ {
+ LOG_STATIC_FUNC
+ // Create and return a new SAP
+ CL2CAPConnectionSAP* sap= new (ELeave) CL2CAPConnectionSAP(aProt);
+ CleanupStack::PushL(sap);
+ sap->ConstructL();
+ CleanupStack::Pop();
+ return sap;
+ }
+
+void CL2CAPConnectionSAP::ConstructL()
+ {
+ LOG_FUNC
+ CBluetoothSAP::ConstructL();
+ TCallBack cb(NewDataAsyncCallBack, this);
+ iNewDataAsyncCallBack = new (ELeave)CAsyncCallBack(cb, EActiveHighPriority);
+ iL2CapSAPSignalHandler = CL2CapSAPSignalHandler::NewL(*this);
+ LOG2(_L("CL2CAPConnectionSAP.iL2CapSAPSignalHandler = %X.%X"), (TAny*)this, (TAny*)iL2CapSAPSignalHandler)
+ }
+
+CL2CAPConnectionSAP::CL2CAPConnectionSAP(CL2CAPProtocol& aProt)
+ : CBluetoothSAP(aProt.SecMan(), aProt.CodMan()),
+ iProtocol(aProt),
+ iShutdownReceived(ESAPShutdownNone),
+ iSocketErrorCode(KErrNone),
+ iSocketErrorAction(MSocketNotify::TOperationBitmasks(0))
+ {
+ LOG_FUNC
+ iState=&aProt.StateFactory().GetState(CL2CAPSAPStateFactory::EClosed);
+
+ L2CAP_DEBUG(ObjectAllocation(L2capDebugInfo::ESAP,
+ L2capDebugInfo::EAllocated));
+ }
+
+
+
+CL2CAPConnectionSAP::~CL2CAPConnectionSAP()
+/**
+ SAP destructor.
+ Called when the socket deletes the SAP.
+*/
+ {
+ LOG_FUNC
+
+ __ASSERT_DEBUG(iClones.Count() == 0, Panic(EL2CAPClonesExistDuringSAPDestructor));
+
+ // Ensure that everything is completely shutdown.
+ iState->FastShutdown(*this);
+
+ // Inform the signal handler that the SAP is being destructed.
+ // The SH will normally destroy itself unless it has a message
+ // that still needs to be sent.
+ if(iL2CapSAPSignalHandler)
+ {
+ iL2CapSAPSignalHandler->SAPClosed();
+ }
+
+ __ASSERT_DEBUG(iL2CapDataQueue == NULL, Panic(EL2CAPSDUQueueStillExistsDuringSAPDestructor));
+
+ delete iNewDataAsyncCallBack;
+ iClones.Close();
+
+ //We may be still attached to the listening SAP if the connection was not fully completed before shutdown
+ //No checking needed because iListeningSAP will be NULL if this is the listening SAP or we are already
+ //detached from the listeningSAP
+ DetachFromListeningSAP();
+
+ L2CAP_DEBUG(ObjectAllocation(L2capDebugInfo::ESAP,
+ L2capDebugInfo::EDeleted));
+ }
+
+
+TBool CL2CAPConnectionSAP::CanAcceptConnection() const
+ {
+ LOG_FUNC
+ return (TotalOutstandingCloneCount() < iMaxAcceptingQCount);
+ }
+
+TUint8 CL2CAPConnectionSAP::TotalOutstandingCloneCount() const
+ {
+ LOG_FUNC
+ return static_cast<TUint8>(iClones.Count());
+ }
+
+CL2CAPConnectionSAP* CL2CAPConnectionSAP::CloneListeningSAP(const TBTDevAddr& aAddr)
+ {
+ LOG_FUNC
+
+ TInt rerr = KErrNone;
+ CL2CAPConnectionSAP* clone = NULL;
+
+ TRAP(rerr, clone = NewL(iProtocol));
+ if(rerr == KErrNone)
+ {
+ if(!EnqueClone(clone))
+ {
+ delete clone;
+ clone = NULL;
+ }
+ else
+ {
+ clone->SetState(iState->iFactory.GetState(CL2CAPSAPStateFactory::EPassiveLinkPending));
+ clone->SetListeningSAP(*this);
+
+ // Copy the details required for security.
+ clone->iSecurity = iSecurity;
+ clone->iRemoteDev = aAddr;
+ }
+ }
+ return clone;
+ }
+
+
+TBool CL2CAPConnectionSAP::EnqueClone(CL2CAPConnectionSAP* aSAP)
+ {
+ LOG_FUNC
+ TInt rerr = iClones.Append(aSAP);
+ return (rerr == KErrNone);
+ }
+
+TBool CL2CAPConnectionSAP::DequeClone(CL2CAPConnectionSAP* aSAP)
+ {
+ LOG_FUNC
+ TBool rcode = EFalse;
+ TInt ix = iClones.Find(aSAP);
+
+ if(ix != KErrNotFound)
+ {
+ iClones.Remove(ix);
+ rcode = ETrue;
+ }
+ return rcode;
+ }
+
+void CL2CAPConnectionSAP::DeleteAllClones()
+/**
+ This function is called by TL2CAPSAPStateListening::Exit() to clear
+ the iClones queue and delete all the clones. However, any clones
+ with their iAcceptingPending flag raised are owned by a socket,
+ so they shouldn't be deleted. Instead their pointer to this SAP
+ should be nulled, then they should be removed from the queue.
+**/
+ {
+ LOG_FUNC
+
+ for(TInt i=iClones.Count()-1;i>=0;i--)
+ {
+ if(iClones[i]->IsAcceptPending())
+ {
+ iClones[i]->DetachFromListeningSAP();
+ }
+ }
+ iClones.ResetAndDestroy();
+ }
+
+void CL2CAPConnectionSAP::DeleteSAP()
+ {
+ LOG_FUNC
+ delete this;
+ }
+
+void CL2CAPConnectionSAP::SocketConnectComplete()
+/**
+ A Clone has completed a connection, so we tell our socket.
+
+ Send the socket a ConnectComplete, the clone remains in the listening SAPs clone
+ queue, but its iAcceptPending flag is raised. Logically ownership of the clone has
+ been transferred from the listening SAP to the socket, however the listening SAP
+ keeps a pointer until the socket calls Start().
+**/
+ {
+ LOG_FUNC
+
+ // There was a mysterious problem with AVDTP signalling channel connections
+ // which suggested that iL2CapDataQueue was 0 when BearerConnectionComplete was
+ // being called. The problem has disappeared but added this just in case it comes
+ // back again.
+ __ASSERT_DEBUG(iL2CapDataQueue != NULL, Panic(EL2CAPSduQNotExisitantInConnectComplete));
+
+ if(iListeningSAP)
+ {
+ SetState(iState->iFactory.GetState(CL2CAPSAPStateFactory::EAccepting));
+ iAcceptPending = ETrue;
+ iListeningSAP->Socket()->ConnectComplete(*this);
+ }
+ else
+ {
+ SetState(iState->iFactory.GetState(CL2CAPSAPStateFactory::EAwaitingInitialData));
+ Socket()->ConnectComplete();
+ }
+ }
+
+/*static*/ TInt CL2CAPConnectionSAP::NewDataAsyncCallBack(TAny* aSAP)
+ {
+ LOG_STATIC_FUNC
+ CL2CAPConnectionSAP* sap = static_cast<CL2CAPConnectionSAP*>(aSAP);
+ sap->iState->NewDataAsyncCallBack(*sap);
+ return EFalse;
+ }
+
+void CL2CAPConnectionSAP::LocalName(TSockAddr& anAddr) const
+/**
+ Read the Local Name into aAddr.
+ Note: On protocol start-up, the protocol might not have received the
+ BTAddr back from the HW when this is called... :-(
+ So the user should use the read local address ioctl to be sure of
+ getting the right result.
+
+**/
+ {
+ LOG_FUNC
+
+ // Copy iLocalPort into TSockAddr and return
+ TL2CAPSockAddr& addr = TL2CAPSockAddr::Cast(anAddr);
+ iL2CapSAPSignalHandler->GetLocalParameters(addr);
+ }
+
+
+TInt CL2CAPConnectionSAP::SetLocalName(TSockAddr& anAddr)
+ {
+ LOG_FUNC
+
+ TL2CAPSockAddr l2capAddr = TL2CAPSockAddr::Cast(anAddr);
+ TL2CAPPort psm = l2capAddr.Port();
+ TInt err = KErrNone;
+
+ if(psm == KL2CAPPassiveAutoBind)
+ {
+ // User wishes for us to choose free ServerChannel
+ err = Protocol().MuxController().FindFreeUserPSM(psm);
+ if(err == KErrNone)
+ {
+ l2capAddr.SetPort(psm);
+ }
+ }
+ else
+ {
+ // User supplied explicit PSM. Check that the value is odd.
+ if(psm & 0x0001)
+ {
+ // Valid, so see if available
+ if(Protocol().MuxController().FindIdleSignalHandler(psm))
+ {//psm in use already
+ err = KErrInUse;
+ }
+ }
+ else
+ {
+ // PSM not valid (as per 1.0B spec)
+ err = KErrArgument;
+ }
+ }
+
+
+ if (err == KErrNone)
+ {
+ iL2CapSAPSignalHandler->SetLocalParameters(l2capAddr);
+ iState->Bound(*this);
+ iSecurity = l2capAddr.BTSecurity();
+ }
+
+ return err;
+ }
+
+void CL2CAPConnectionSAP::RemName(TSockAddr& anAddr) const
+ {
+ LOG_FUNC
+ //Return the remote name
+
+ // Copy iRemoteDev and iRemotePort into TSockAddr and return
+ TL2CAPSockAddr& addr = TL2CAPSockAddr::Cast(anAddr);
+ iL2CapSAPSignalHandler->GetRemName(addr);
+ }
+
+TInt CL2CAPConnectionSAP::SetRemName(TSockAddr& anAddr)
+ {
+ LOG_FUNC
+
+ // Copy this over
+ TL2CAPSockAddr& addr = TL2CAPSockAddr::Cast(anAddr);
+ iL2CapSAPSignalHandler->SetRemName(addr);
+
+ // Set the security and remote address values in the base class.
+ // This is required for security handling.
+ iSecurity = addr.BTSecurity(); //outgoing security
+ iRemoteDev = addr.BTAddr();
+
+ LOG1(_L("L2CAP [l2sap.cpp]: Set remname to port %d, Device...."), addr.Port());
+ LOGHEXDESC(addr.BTAddr().Des());
+ return KErrNone;
+ }
+
+const TBTDevAddr& CL2CAPConnectionSAP::RemoteDev() const
+ {
+ LOG_FUNC
+ return iRemoteDev;
+ }
+
+void CL2CAPConnectionSAP::SetRemoteDev(const TBTDevAddr& aAddr)
+ {
+ LOG_FUNC
+ iRemoteDev = aAddr;
+ LOGBTDEVADDR(iRemoteDev);
+ }
+
+TUint CL2CAPConnectionSAP::GetOptimalMTUSize(TUint aMTU, TUint aPduSize, TBool aBasicMode) const
+ {
+ LOG_FUNC
+
+ // If the negotiated MTU minus any overhead will fit into the optimal PDU then that
+ // is the optimal MTU. The overhead will differ for basic and non-basic mode, for basic mode
+ // we have to consider the SDU overhead as there is no fragment overhead and for non-basic we
+ // consider the PDU overhead only (the additional SDU overhead is taken account of later if
+ // more than one PDU is required for the optimal MTU).
+ TUint optimalMTU = aMTU;
+
+ // Calculate the size of the MTU + any overhead assuming that the MTU is not segmented
+ TUint singlePduSize = aBasicMode ? (aMTU + CL2CapSDU::GetSDUOverhead(aBasicMode)) : aMTU;
+
+ // If the unsegmented MTU + overhead can fit into the optimal PDU size then no
+ // further calculation is required
+ if(singlePduSize > aPduSize)
+ {
+ // The MTU will need to be segmented / fragmented (depending on L2CAP mode).
+ // Calculate an MTU size that will be a factor of the PDU size.
+ optimalMTU = aMTU - ((aMTU + CL2CapSDU::GetSDUOverhead(aBasicMode)) % aPduSize);
+ }
+
+ return optimalMTU;
+ }
+
+TInt CL2CAPConnectionSAP::SAPSetOption(TUint aLevel, TUint aName, const TDesC8& aOption)
+ {
+ LOG_FUNC
+ // Perform a setopt
+
+ if (aLevel!=KSolBtL2CAP)
+ {
+ // pass down
+
+ return iL2CapSAPSignalHandler->SetOption(aLevel, aName, aOption);
+ }
+
+ switch (aName)
+ {
+ case KL2CAPRTXTimer:
+ {
+ TInt val = *reinterpret_cast<const TInt*>(aOption.Ptr());
+ if (aOption.Length() != sizeof(TInt) || val < HL2CapCommand::KMinRTXTimerDuration || val > HL2CapCommand::KMaxRTXTimerDuration)
+ return KErrArgument;
+ return iL2CapSAPSignalHandler->SetRTXTimerDuration(static_cast<TUint8>(val));
+ }
+ case KL2CAPERTXTimer:
+ {
+ TInt val = *reinterpret_cast<const TInt*>(aOption.Ptr());
+ if (aOption.Length() != sizeof(TInt) || val < HL2CapCommand::KMinERTXTimerDuration || val > HL2CapCommand::KMaxERTXTimerDuration)
+ return KErrArgument;
+ return iL2CapSAPSignalHandler->SetERTXTimerDuration(static_cast<TUint8>(val));
+ }
+ case KL2CAPInboundMTU:
+ {
+ TUint16 val = *reinterpret_cast<const TUint16*>(aOption.Ptr());
+ if (aOption.Length() != sizeof(TUint16) || val < KL2MinMTU)
+ return KErrArgument;
+ TL2CapConfig conf;
+ conf.SetMaxReceiveUnitSize(val);
+ return iL2CapSAPSignalHandler->UpdateChannelConfig(conf);
+ }
+#ifdef _DEBUG
+ case KL2CAPDebugOptionMask:
+ {// Set the debug options mask
+ if (aOption.Length() != sizeof(TUint))
+ return KErrArgument;
+ Protocol().iDebugOptionMask = *reinterpret_cast<const TUint*>(aOption.Ptr());
+ return KErrNone;
+ }
+#endif
+ case KL2CAPNegotiatedOutboundMTU:
+ case KL2CAPOutboundMTUForBestPerformance: //equals KL2CAPGetOutboundMTU here for legacy purposes
+ {
+ TUint16 val = *reinterpret_cast<const TUint16*>(aOption.Ptr());
+ if (aOption.Length() != sizeof(TUint16) || val < KL2MinMTU)
+ return KErrArgument;
+ TL2CapConfig conf;
+ conf.SetMaxTransmitUnitSize(val);
+ return iL2CapSAPSignalHandler->UpdateChannelConfig(conf);
+ }
+
+ case KBTSecurityDeviceOverride:
+ {
+ return SetDeviceOverride(aOption);
+ }
+
+ case KL2CAPUpdateChannelConfig:
+ {
+ const TL2CapConfig apiConf = *reinterpret_cast<const TL2CapConfig*>(aOption.Ptr());
+ return iL2CapSAPSignalHandler->UpdateChannelConfig(apiConf);
+ }
+
+ // Can't set these
+ default:
+ return KErrNotSupported;
+ };
+ }
+
+TInt CL2CAPConnectionSAP::GetOption(TUint aLevel,TUint aName,TDes8& aOption) const
+ {
+ LOG_FUNC
+ if (aLevel!=KSolBtL2CAP)
+ {// it must be for the lower layers then
+
+ return iL2CapSAPSignalHandler->GetOption(aLevel, aName, aOption);
+ }
+
+ TInt val = 0;
+ TInt rValue = KErrNone;
+
+ switch (aName)
+ {
+ case KL2CAPGetMaxOutboundMTU:
+ if(aOption.Length() == sizeof(TInt))
+ {
+ val = KL2CapMaxMTUSize;
+ aOption = TPtrC8(reinterpret_cast<TUint8*>(&val), sizeof(TInt));
+ }
+ else
+ {
+ rValue = KErrArgument;
+ }
+ break;
+
+ case KL2CAPGetMaxInboundMTU:
+ if(aOption.Length() == sizeof(TInt))
+ {
+ val = KL2CapMaxMTUSize;
+ aOption = TPtrC8(reinterpret_cast<TUint8*>(&val), sizeof(TInt));
+ }
+ else
+ {
+ rValue = KErrArgument;
+ }
+ break;
+
+ case KL2CAPNegotiatedOutboundMTU:
+ if(aOption.Length() == sizeof(TInt))
+ {
+ if(iL2CapDataQueue)
+ {
+ val = iL2CapDataQueue->MaxOutgoingMTU();
+ aOption = TPtrC8(reinterpret_cast<TUint8*>(&val), sizeof(TInt));
+ }
+ else
+ {
+ rValue = KErrNotReady;
+ }
+ }
+ else
+ {
+ rValue = KErrArgument;
+ }
+ break;
+
+ case KL2CAPOutboundMTUForBestPerformanceWithRestriction:
+ // get the restriction value
+ if((aOption.Length() == sizeof(TInt)) && iL2CapDataQueue && iL2CapSAPSignalHandler)
+ {
+ val = *reinterpret_cast<const TInt*>(aOption.Ptr());
+
+ // Ensure that the restriction is less then the current MTU.
+ if (val < iL2CapDataQueue->MaxOutgoingMTU())
+ {
+ // We now need to recalculate the optimal PDU size for the restricted MTU as
+ // this is used in the calculation of the optimal MTU
+ TPckgBuf<TInt> buf;
+ TInt err = iL2CapSAPSignalHandler->GetOption(KSolBtACL, ELMOutboundACLSize, buf);
+
+ TInt optimalPduSize = HL2CapPDU::GetPDUOrFragmentSize(val, iL2CapDataQueue->MaximumPDUSize(), (err == KErrNone) ? buf() : 0, iL2CapDataQueue->IsBasicDataVersion());
+
+ // update the data queue to use the new optimal PDU size from now on
+ iL2CapDataQueue->SetOptimalPDUSize(optimalPduSize);
+ }
+ else
+ {
+ // can't increase the MTU at this stage so just use the existing MTU
+ val = iL2CapDataQueue->MaxOutgoingMTU();
+ }
+
+ // work out the optimal MTU
+ val = GetOptimalMTUSize(val, iL2CapDataQueue->OptimalPDUSize(), iL2CapDataQueue->IsBasicDataVersion());
+ aOption = TPtrC8(reinterpret_cast<TUint8*>(&val), sizeof(TInt));
+ }
+ break;
+
+ case KL2CAPOutboundMTUForBestPerformance: //equals KL2CAPGetOutboundMTU
+ if(aOption.Length() == sizeof(TInt))
+ {
+ if(iL2CapDataQueue && iL2CapSAPSignalHandler)
+ {
+ // work out the optimal MTU
+ val = GetOptimalMTUSize(iL2CapDataQueue->MaxOutgoingMTU(), iL2CapDataQueue->OptimalPDUSize(), iL2CapDataQueue->IsBasicDataVersion());
+ aOption = TPtrC8(reinterpret_cast<TUint8*>(&val), sizeof(TInt));
+ }
+ else
+ {
+ rValue = KErrNotReady;
+ }
+ }
+ else
+ {
+ rValue = KErrArgument;
+ }
+ break;
+
+ case KL2CAPInboundMTU:
+ if(aOption.Length() == sizeof(TInt))
+ {
+ if(iL2CapDataQueue)
+ {
+ val = iL2CapDataQueue->MaxIncomingMTU();
+ aOption = TPtrC8(reinterpret_cast<TUint8*>(&val), sizeof(TInt));
+ }
+ else
+ {
+ rValue = KErrNotReady;
+ }
+ }
+ else
+ {
+ rValue = KErrArgument;
+ }
+ break;
+
+ case KL2CAPRTXTimer:
+ if(aOption.Length() == sizeof(TInt))
+ {
+ val = iL2CapSAPSignalHandler->RTXTimerDuration();
+ aOption = TPtrC8(reinterpret_cast<TUint8*>(&val), sizeof(TInt));
+ }
+ else
+ {
+ rValue = KErrArgument;
+ }
+ break;
+
+ case KL2CAPERTXTimer:
+ if(aOption.Length() == sizeof(TInt))
+ {
+ val = iL2CapSAPSignalHandler->ERTXTimerDuration();
+ aOption = TPtrC8(reinterpret_cast<TUint8*>(&val), sizeof(TInt));
+ }
+ else
+ {
+ rValue = KErrArgument;
+ }
+ break;
+
+ case KBTSecurityDeviceOverride:
+ // aspect oriented programming would be nice.
+ rValue = GetDeviceOverride(aOption);
+ break;
+
+ case KL2CAPGetDebug1:
+ val = static_cast<TInt>((static_cast<SBtTls*>(Dll::Tls()))->iPort);
+ aOption = TPtrC8(reinterpret_cast<TUint8*>(&val), sizeof(TInt));
+ break;
+
+ // returns the PSM for the accepted SAP (or listening SAP if clients forgot)
+ case KL2CAPLocalPSM:
+ {
+ if (aOption.Length() == sizeof(TL2CAPPort))
+ {
+ const CL2CAPConnectionSAP* listeningSAP = ListeningSAP();
+ if (!listeningSAP)
+ {
+ listeningSAP = this;
+ }
+ TL2CAPSockAddr locAddr;
+ listeningSAP->LocalName(locAddr);
+ TPckg<TL2CAPPort> pckg(locAddr.Port());
+ aOption = pckg;
+ }
+ else
+ {
+ rValue = KErrArgument;
+ }
+ }
+ break;
+
+ // Returns the negotiated channel mode.
+ // KErrNotReady if the channel hasn't undergone configuration yet.
+ case KL2CAPNegotiatedChannelMode:
+ if(aOption.Length() == sizeof(TL2CapChannelMode))
+ {
+ TL2CapChannelMode mode;
+ rValue = iL2CapSAPSignalHandler->GetNegotiatedChannelMode(mode);
+ TPckgBuf<TL2CapChannelMode> pckg(mode);
+ aOption = pckg;
+ }
+ else
+ {
+ rValue = KErrArgument;
+ }
+ break;
+
+#ifdef _DEBUG
+
+ case KL2CAPVersion1_2:
+ // This debug option is used to check if the stack is a 1.2 version.
+ // For pre 1.2 versions the stack will respond with KErrNotSupported.
+ break;
+
+ case KL2CAPHeapAlloc:
+ if(aOption.Length() == sizeof(TInt))
+ {
+ val = User::Heap().Count();
+ aOption = TPtrC8(reinterpret_cast<TUint8*>(&val), sizeof(TInt));
+ }
+ else
+ {
+ rValue = KErrArgument;
+ }
+ break;
+
+ case KL2CAPDataPlaneConfig:
+ {
+ if(iL2CapDataQueue)
+ {
+ TL2DataPlaneConfig conf;
+ iL2CapDataQueue->GetDataPlaneConfig(conf);
+ TL2DataPlaneConfigPkg pckg(conf);
+ aOption = pckg;
+ }
+ else
+ {
+ rValue = KErrNotReady;
+ }
+ }
+ break;
+
+#endif
+
+ default:
+ rValue = KErrNotSupported;
+ break;
+ };
+ return rValue;
+ }
+
+
+void CL2CAPConnectionSAP::Ioctl(TUint aLevel,TUint aName,TDes8* aOption)
+/**
+ Handle an Ioctl from above.
+
+ Some Ioctls are not for L2CAP but must be handled here anyway.
+ Other non-L2CAP Ioctls should just be passed downward.
+
+ If the Ioctl is for L2CAP and should be handled the same in all states,
+ then handle it here, otherwise, pass it on to the current state.
+
+ @param aLevel Level of Ioctl (should be KSolBtL2CAP here)
+ @param aName Name of Ioctl
+ @param aOption Ioctl data
+**/
+ {
+ LOG_FUNC
+ __ASSERT_DEBUG(iOutstandingIoctlName == 0, Panic(EL2SAPTwoIoctls));
+
+ // Must set this first, in case any synchronous errors come up.
+ iOutstandingIoctlName = aName;
+ TInt rerr = KErrNotSupported;
+
+ if(aLevel == KSolBtL2CAP)
+ {
+ switch(aName)
+ {
+ case KL2CAPEchoRequestIoctl:
+ rerr = iL2CapSAPSignalHandler->SendEchoRequest(aOption);
+ break;
+
+ case KL2CAPIncomingMTUIoctl:
+ {
+ const TUint8* optionPtr = aOption->Ptr();
+ TUint16 newSize = *((TUint16*)optionPtr);
+ TL2CapConfig conf;
+ conf.SetMaxReceiveUnitSize(newSize);
+ rerr = iL2CapSAPSignalHandler->UpdateChannelConfig(conf);
+ if(rerr == KErrNone)
+ {
+ // The operation has completed.
+ IoctlComplete(KErrNone, KSolBtL2CAP, KL2CAPIncomingMTUIoctl, aOption);
+ rerr = KErrNone;
+ }
+ else
+ {
+ if(rerr == KErrL2CAPConfigPending)
+ {
+ // This indicates that configuration is pending.
+ rerr = KErrNone;
+ }
+ }
+ }
+ break;
+
+ case KL2CAPOutgoingMTUIoctl:
+ {
+ const TUint8* optionPtr = aOption->Ptr();
+ TUint16 newSize = *((TUint16*)optionPtr);
+ TL2CapConfig conf;
+ conf.SetMaxTransmitUnitSize(newSize);
+ rerr = iL2CapSAPSignalHandler->UpdateChannelConfig(conf);
+ if(rerr == KErrNone)
+ {
+ // The operation has completed.
+ IoctlComplete(KErrNone, KSolBtL2CAP, KL2CAPOutgoingMTUIoctl, aOption);
+ rerr = KErrNone;
+ }
+ else
+ {
+ if(rerr == KErrL2CAPConfigPending)
+ {
+ // This indicates that configuration is pending.
+ rerr = KErrNone;
+ }
+ }
+ }
+ break;
+
+ case KL2CAPUpdateChannelConfigIoctl:
+ {
+ const TL2CapConfig* conf = reinterpret_cast<const TL2CapConfig*>(aOption->Ptr());
+ rerr = UpdateChannelConfig(*conf);
+ if(rerr == KErrNone)
+ {
+ // The operation has completed.
+ IoctlComplete(KErrNone, KSolBtL2CAP, KL2CAPUpdateChannelConfigIoctl, aOption);
+ rerr = KErrNone;
+ }
+ else
+ {
+ if(rerr == KErrL2CAPConfigPending)
+ {
+ // This indicates that configuration is pending.
+ rerr = KErrNone;
+ }
+ }
+ }
+ break;
+
+ case KL2CAPPretendIncomingSduQFull:
+ {
+#ifdef _DEBUG
+ if (aOption && aOption->Length() == sizeof(TBool))
+ {
+ iL2CapDataQueue->PretendIncomingSduQFull(*reinterpret_cast<const TBool*>(aOption->Ptr()));
+ IoctlComplete(KErrNone, KSolBtL2CAP, KL2CAPPretendIncomingSduQFull, aOption);
+ rerr = KErrNone;
+ }
+ else
+ {
+ rerr = KErrArgument;
+ }
+#endif
+ }
+ // Return not supported in UREL.
+ break;
+
+ default:
+ // Return not supported.
+ break;
+ }
+ }
+
+ if (rerr != KErrNone)
+ {
+ iOutstandingIoctlName = 0;
+ iSocket->Error(rerr, MSocketNotify::EErrorIoctl);
+ }
+ }
+
+#ifdef _DEBUG
+void CL2CAPConnectionSAP::CancelIoctl(TUint aLevel, TUint aName)
+#else
+void CL2CAPConnectionSAP::CancelIoctl(TUint /*aLevel*/, TUint /*aName*/)
+#endif
+ {
+ LOG_FUNC
+ __ASSERT_DEBUG((aLevel == KSolBtL2CAP && aName == iOutstandingIoctlName), Panic(EL2SAPStrayIoctlCancel));
+ iOutstandingIoctlName = 0;
+ }
+
+void CL2CAPConnectionSAP::IoctlComplete(TInt aErr, TUint aLevel, TUint aName, TDesC8* aBuf)
+ {
+ LOG_FUNC
+ if(aLevel == KSolBtL2CAP && aName == iOutstandingIoctlName)
+ {
+ if(aErr == KErrNone)
+ {
+ if(iSocket)
+ {
+ iSocket->IoctlComplete(aBuf);
+ }
+ }
+ else
+ {
+ iSocket->Error(aErr, MSocketNotify::EErrorIoctl);
+ }
+ iOutstandingIoctlName = 0;
+ }
+ }
+
+void CL2CAPConnectionSAP::EchoResponseReceived(const TDesC8* aData)
+ {
+ LOG_FUNC
+ TDesC8* data = const_cast<TDesC8*>(aData);
+ IoctlComplete(KErrNone, KSolBtL2CAP, KL2CAPEchoRequestIoctl, data);
+ }
+
+
+void CL2CAPConnectionSAP::Start()
+ {
+ LOG_FUNC
+ // Called when data's about to flow
+
+ iAcceptPending = EFalse;
+ iState->Start(*this);
+ }
+
+
+void CL2CAPConnectionSAP::ActiveOpen()
+ {
+ LOG_FUNC
+ // Setup the link
+
+ iState->ActiveOpen(*this);
+ }
+
+void CL2CAPConnectionSAP::ActiveOpen(const TDesC8& /*aConnectionData*/)
+ {
+ LOG_FUNC
+ // This is an error
+ Panic(EL2CAPSAPOpenWithConnectionDataNotSupported);
+ }
+
+TInt CL2CAPConnectionSAP::PassiveOpen(TUint aQueSize)
+ {
+ LOG_FUNC
+ // A Listen request
+ // Now a listening SAP has been opened, start the underlying protocol listening.
+ return iState->PassiveOpen(*this, aQueSize);
+ }
+
+TInt CL2CAPConnectionSAP::PassiveOpen(TUint /*aQueSize*/, const TDesC8& /*aConnectionData*/)
+ {
+ LOG_FUNC
+ // This operation is not currently supported.
+ Panic(EL2CAPSAPOpenWithConnectionDataNotSupported);
+ return KErrNotSupported;
+ }
+
+void CL2CAPConnectionSAP::Shutdown(TCloseType aOption)
+ {
+ LOG_FUNC
+ LOG2(_L("L2CAP [l2sap.cpp]: CL2CAPConnectionSAP::Shutdown, option = %d, SAP %08x"), aOption, this);
+ // Shutdown
+ if(aOption != EImmediate)
+ {
+ // Normal shutdown, ESOCK will wait for the SAP to signal Can Close.
+ iShutdownReceived = ESAPShutdownNormal;
+ iState->Shutdown(*this);
+ }
+ else
+ {
+ iShutdownReceived = ESAPShutdownImmediate;
+ iState->FastShutdown(*this);
+ }
+ }
+
+void CL2CAPConnectionSAP::Shutdown(TCloseType /*option*/,
+ const TDesC8& /*aDisconnectionData*/)
+ {
+ LOG_FUNC
+ // this one's an error
+ Panic(EL2CAPShutdownWithDisconnectionDataNotSupported);
+ }
+
+void CL2CAPConnectionSAP::AutoBind()
+/**
+ Auto bind from esock.
+ Do nothing -- as we always allocate a local CID
+ in ActiveConnect anyway.
+**/
+ {
+ LOG_FUNC
+ }
+
+
+TInt CL2CAPConnectionSAP::Write(RMBufChain& aData, TUint aOptions, TSockAddr* /*anAddr*/)
+ {
+ LOG_FUNC
+ // If the write completes without error return 1.
+ // If not the definition [of the return parameter
+ // of this method] in CServProviderBase indicates that zero
+ // should be returned.
+ TInt err = iState->Send(*this, aData, aOptions);
+ // If there is an error then simply returning 0 will just "flow off"
+ // esock (i.e. the write won't be completed). We are reliant on
+ // some other event to either error the send or disconnect in order
+ // to complete the request.
+ return (err == KErrNone ? 1 : 0);
+ }
+
+TInt CL2CAPConnectionSAP::GetData(RMBufChain& aData, TUint /*aLength*/, TUint /*aOptions*/, TSockAddr* /*anAddr*/)
+ {
+ LOG_FUNC
+ return iState->Read(*this, aData);
+ }
+
+TUint CL2CAPConnectionSAP::Write(const TDesC8& aDesc, TUint aOptions, TSockAddr* /*anAddr*/)
+ {
+ LOG_FUNC
+ // Temporary Shim. Should be remove when no longer used.
+ RMBufChain buf;
+ TInt err = KErrNone;
+
+ // A zero return value indicates that no data was sent.
+ TUint rValue = 0;
+
+ TRAP(err, buf.CreateL(aDesc));
+ if(err == KErrNone)
+ {
+ rValue = Write(buf, aOptions);
+ }
+
+ buf.Free();
+ return rValue;
+ }
+
+void CL2CAPConnectionSAP::GetData(TDes8& aDesc, TUint aOptions, TSockAddr* anAddr)
+ {
+ LOG_FUNC
+ // Temporary Shim. Should be remove when no longer used.
+ RMBufChain buf;
+ GetData(buf, 0, aOptions, anAddr);
+ buf.CopyOut(aDesc);
+ buf.Free();
+ }
+
+
+// Methods from SAP Signal Handler
+void CL2CAPConnectionSAP::LinkUp()
+ {
+ LOG_FUNC
+ iState->LinkUp(*this);
+ }
+
+void CL2CAPConnectionSAP::ChannelOpened()
+ {
+ LOG_FUNC
+ iState->ChannelOpened(*this);
+ }
+
+void CL2CAPConnectionSAP::ChannelConfigured(CL2CapChannelConfig& aConfig,
+ CL2CAPMux& aMuxer,
+ TL2CAPPort aLocalPort,
+ TL2CAPPort aRemotePort)
+ {
+ LOG_FUNC
+ iState->ChannelConfigured(*this, aConfig, aMuxer, aLocalPort, aRemotePort);
+ }
+
+void CL2CAPConnectionSAP::ReconfiguringChannel()
+ {
+ LOG_FUNC
+ iState->ReconfiguringChannel(*this);
+ }
+
+void CL2CAPConnectionSAP::ChannelClosed()
+ {
+ LOG_FUNC
+ iState->ChannelClosed(*this);
+ }
+
+void CL2CAPConnectionSAP::DataPlaneError(TInt aErrorCode, MSocketNotify::TOperationBitmasks aErrorAction)
+ {
+ LOG_FUNC
+ iState->DataPlaneError(*this, aErrorCode, aErrorAction);
+ }
+
+void CL2CAPConnectionSAP::SignalHandlerError(TInt aErrorCode, MSocketNotify::TOperationBitmasks aErrorAction)
+ {
+ LOG_FUNC
+ iState->SignalHandlerError(*this, aErrorCode, aErrorAction);
+ }
+
+
+// Methods from Data Controller
+void CL2CAPConnectionSAP::NewData()
+ {
+ LOG_FUNC
+ iState->NewData(*this);
+ }
+
+void CL2CAPConnectionSAP::CanSend()
+ {
+ LOG_FUNC
+ iState->CanSend(*this);
+ }
+
+void CL2CAPConnectionSAP::CloseOutgoingSDUQueue()
+ {
+ LOG_FUNC
+ iState->CloseOutgoingSDUQueue(*this);
+ }
+
+void CL2CAPConnectionSAP::SDUQueueClosed()
+ {
+ LOG_FUNC
+ // Delete the SDU queue.
+ delete iL2CapDataQueue;
+ iL2CapDataQueue = NULL;
+
+ // Inform the state that the data plane is closed.
+ iState->SDUQueueClosed(*this);
+ }
+
+void CL2CAPConnectionSAP::SetState(TL2CAPSAPState& aState)
+ {
+ LOG_FUNC
+ iState->Exit(*this);
+ iState = &aState;
+ iState->Enter(*this);
+ }
+
+TInt CL2CAPConnectionSAP::CreateDataPlane(CL2CapChannelConfig& aConfig,
+ CL2CAPMux& aMuxer,
+ TL2CAPPort aLocalPort,
+ TL2CAPPort aRemotePort)
+ {
+ LOG_FUNC
+ TInt rerr = KErrNone;
+ const TL2CapFecNegotiator& fec = aConfig.FecNegotiator();
+ TL2CapDataControllerConfig* dataConfig = new TL2CapDataControllerConfig(fec.DataControllerConfig());
+
+ if(dataConfig)
+ {
+ TRAP(rerr, iL2CapDataQueue = CL2CapSDUQueue::NewL(*this,
+ aLocalPort,
+ aRemotePort,
+ aMuxer,
+ fec.OutgoingMaximumPDUSize(),
+ CL2CapSDUQueue::KDefaultOutboundQueueSize,
+ dataConfig,
+ aConfig.OutgoingFlushTimeout().Negotiated().FlushTimeoutDuration(),
+ aConfig.OutgoingMTU().Negotiated().MTU(),
+ aConfig.IncomingMTU().Negotiated().MTU(),
+ // Allow ourselves to drop SDUs we can't assemble if channel mode is not Reliable.
+ !TRetransmissionAndFlowControlOption::IsModeReliable(fec.IncomingLinkMode())));
+
+ // Set the optimal PDU size that the data controller should use.
+ TPckgBuf<TInt> buf;
+ TInt err = iL2CapSAPSignalHandler->GetOption(KSolBtACL, ELMOutboundACLSize, buf);
+
+ TInt optimalPduSize = HL2CapPDU::GetPDUOrFragmentSize(iL2CapDataQueue->MaxOutgoingMTU(), iL2CapDataQueue->MaximumPDUSize(), (err == KErrNone) ? buf() : 0, iL2CapDataQueue->IsBasicDataVersion());
+ iL2CapDataQueue->SetOptimalPDUSize(optimalPduSize);
+ }
+ else
+ {
+ rerr = KErrNoMemory;
+ }
+
+ return rerr;
+ }
+
+
+void CL2CAPConnectionSAP::PauseDataPlane()
+ {
+ LOG_FUNC
+ __ASSERT_DEBUG(iL2CapDataQueue, Panic(EL2CAPPauseDataPlaneCalledWhenDataPlaneDoesNotExist));
+ iL2CapDataQueue->SuspendSDUQueue();
+ }
+
+void CL2CAPConnectionSAP::TryToCompleteConfigurationIoctl(TInt aError)
+ {
+ LOG_FUNC
+ switch(iOutstandingIoctlName)
+ {
+ case KL2CAPIncomingMTUIoctl:
+ case KL2CAPOutgoingMTUIoctl:
+ case KL2CAPUpdateChannelConfigIoctl:
+ if(aError == KErrNone)
+ {
+ IoctlComplete(KErrNone, KSolBtL2CAP, iOutstandingIoctlName, NULL);
+ }
+ else
+ {
+ iSocket->Error(aError, MSocketNotify::EErrorIoctl);
+ }
+ iOutstandingIoctlName = 0;
+ break;
+
+ default:
+ break;
+ };
+ }
+
+void CL2CAPConnectionSAP::AccessRequestComplete(TInt aResult)
+ {
+ LOG_FUNC
+ iState->AccessRequestComplete(*this, aResult);
+ }
+
+TInt CL2CAPConnectionSAP::UpdateChannelConfig(const TL2CapConfig& aAPIConfig)
+ {
+ LOG_FUNC
+ TInt rerr = KErrNone;
+ if(iL2CapSAPSignalHandler)
+ {
+ // Update the channel config.
+ rerr = iL2CapSAPSignalHandler->UpdateChannelConfig(aAPIConfig);
+
+ // Check if the a new priority has been specified.
+ TBool specified;
+ TUint8 newPriority = static_cast<TUint8>(aAPIConfig.ChannelPriority(specified));
+ if(specified && iChannelPriority != newPriority)
+ {
+ iChannelPriority = newPriority;
+ if(iL2CapDataQueue)
+ {
+ iL2CapDataQueue->UpdateChannelPriority(iChannelPriority);
+ }
+ }
+ }
+ else
+ {
+ rerr = KErrNotReady;
+ }
+ return rerr;
+ }
+
+void CL2CAPConnectionSAP::DetachFromListeningSAP()
+ {
+ LOG_FUNC
+ if(iListeningSAP)
+ {
+ iListeningSAP->DequeClone(this);
+ iListeningSAP = NULL;
+ }
+ }