diff -r 000000000000 -r 29b1cd4cb562 bluetooth/btstack/l2cap/l2sap.cpp --- /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 + +#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(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(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(aOption.Ptr()); + if (aOption.Length() != sizeof(TInt) || val < HL2CapCommand::KMinRTXTimerDuration || val > HL2CapCommand::KMaxRTXTimerDuration) + return KErrArgument; + return iL2CapSAPSignalHandler->SetRTXTimerDuration(static_cast(val)); + } + case KL2CAPERTXTimer: + { + TInt val = *reinterpret_cast(aOption.Ptr()); + if (aOption.Length() != sizeof(TInt) || val < HL2CapCommand::KMinERTXTimerDuration || val > HL2CapCommand::KMaxERTXTimerDuration) + return KErrArgument; + return iL2CapSAPSignalHandler->SetERTXTimerDuration(static_cast(val)); + } + case KL2CAPInboundMTU: + { + TUint16 val = *reinterpret_cast(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(aOption.Ptr()); + return KErrNone; + } +#endif + case KL2CAPNegotiatedOutboundMTU: + case KL2CAPOutboundMTUForBestPerformance: //equals KL2CAPGetOutboundMTU here for legacy purposes + { + TUint16 val = *reinterpret_cast(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(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(&val), sizeof(TInt)); + } + else + { + rValue = KErrArgument; + } + break; + + case KL2CAPGetMaxInboundMTU: + if(aOption.Length() == sizeof(TInt)) + { + val = KL2CapMaxMTUSize; + aOption = TPtrC8(reinterpret_cast(&val), sizeof(TInt)); + } + else + { + rValue = KErrArgument; + } + break; + + case KL2CAPNegotiatedOutboundMTU: + if(aOption.Length() == sizeof(TInt)) + { + if(iL2CapDataQueue) + { + val = iL2CapDataQueue->MaxOutgoingMTU(); + aOption = TPtrC8(reinterpret_cast(&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(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 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(&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(&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(&val), sizeof(TInt)); + } + else + { + rValue = KErrNotReady; + } + } + else + { + rValue = KErrArgument; + } + break; + + case KL2CAPRTXTimer: + if(aOption.Length() == sizeof(TInt)) + { + val = iL2CapSAPSignalHandler->RTXTimerDuration(); + aOption = TPtrC8(reinterpret_cast(&val), sizeof(TInt)); + } + else + { + rValue = KErrArgument; + } + break; + + case KL2CAPERTXTimer: + if(aOption.Length() == sizeof(TInt)) + { + val = iL2CapSAPSignalHandler->ERTXTimerDuration(); + aOption = TPtrC8(reinterpret_cast(&val), sizeof(TInt)); + } + else + { + rValue = KErrArgument; + } + break; + + case KBTSecurityDeviceOverride: + // aspect oriented programming would be nice. + rValue = GetDeviceOverride(aOption); + break; + + case KL2CAPGetDebug1: + val = static_cast((static_cast(Dll::Tls()))->iPort); + aOption = TPtrC8(reinterpret_cast(&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 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 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(&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(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(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(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 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(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; + } + }