diff -r 000000000000 -r 29b1cd4cb562 bluetooth/btstack/avdtp/avdtpMuxChannel.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/bluetooth/btstack/avdtp/avdtpMuxChannel.cpp Fri Jan 15 08:13:17 2010 +0200 @@ -0,0 +1,674 @@ +// Copyright (c) 2003-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 avdtp mux channel +// +// + +/** + @file + @internalComponent +*/ + +#include +#include "avdtpMuxChannel.h" +#include "avdtpTransportSession.h" +#include "avdtp.h" +#include "avdtpSignallingChannel.h" +#include "avdtputil.h" + +#ifdef __FLOG_ACTIVE +_LIT8(KLogComponent, LOG_COMPONENT_AVDTP); +#endif + +/*static*/ CAvdtpOutboundMuxedMessage* CAvdtpOutboundMuxedMessage::NewL() + { + LOG_STATIC_FUNC + return new (ELeave) CAvdtpOutboundMuxedMessage; + } + + +TInt CAvdtpOutboundMuxedMessage::NewData(TUint /*aCount*/) + { + LOG_FUNC + __DEBUGGER(); + return 0; + } + +CAvdtpOutboundMuxedMessage::TMuxedMessageOperation CAvdtpOutboundMuxedMessage::TryToAddPacket(TTSID aTSID, RMBufChain& aChain) + { + LOG_FUNC + if (Data().Length() + KALHeaderLength + aChain.Length() > KMaxMuxedPacketLength) + { + // this would cause a jumbogram - we don't support them, so send + return EReadyToSendPacketNotConsumed; + } + + TInt r = AddHeader(aTSID, aChain.Length()); + + if (!r) + { + // add in the payload + Data().Append(aChain); + // see whether to send or not - strategy is that if just under 3 slot we wait + // for callback, otherwise send now + if (Data().Length() > KMuxedPacketLengthReadyToSend) + { + return EReadyToSendPacketAdded; + } + else + { + return EPacketAdded; + } + + } + else + { + return ENoMemory; + } + } + +TInt CAvdtpOutboundMuxedMessage::AddHeader(TTSID aTSID, TInt aPacketLen) + { + LOG_FUNC + // grow chain for adding tsid - put in same chain for ease of sending + TRAPD(r, Data().PrependL(KALHeaderLength)); + if (!r) + { + TUint8& ALhdr = Data().First()->Ptr()[0]; + ALhdr = static_cast(aTSID << KALHeaderTSIDOffset); + // make sure frag = 0 + ALhdr &= ~(1<= 255 ? 1 : 0)); + } + return r; + } + +TInt CAvdtpOutboundMuxedMessage::Reset() + { + LOG_FUNC + // get a packet of 1byte for header, then we jion on payload later + TRAPD(res, Data().AllocL(KALHeaderLength)); + return res; + } + +#ifdef HAVE_INBOUND_MUX_PACKET +/*static*/ CAvdtpInboundMuxedMessage* CAvdtpInboundMuxedMessage::NewL(CMuxChannel& aMuxChannel) + { + LOG_STATIC_FUNC + return new (ELeave) CAvdtpInboundMuxedMessage(aMuxChannel); + } + +CAvdtpInboundMuxedMessage::CAvdtpInboundMuxedMessage(CMuxChannel& aMuxChannel) +: iMuxChannel(aMuxChannel) + { + LOG_FUNC + } + +void CAvdtpInboundMuxedMessage::Reset() + { + LOG_FUNC + // get a packet of 1byte for header, then we jion on payload later + Data().Free(); + } + +TInt CAvdtpInboundMuxedMessage::NewData(TUint aCount) + { + LOG_FUNC + LOG1(_L("Count = %d (check for >1 !)"), aCount); + + TInt ret = KErrNone; + // get the data now? + // need to parse of the AL header - and give to relevant session + // as we don't support fragmentation we can "assume" the this l2cap packet + // is a whole mux packet - of course we need to test the frag bit and moan if set + while (aCount--) + { + RMBufChain frag; + ret = iMuxChannel.GetData(frag); + Data().Append(frag); + } + + LOG(_L("Inbound packet built - now parse")) + + return ret; + } + +#endif //HAVE_INBOUND_MUX_PACKET + +CMuxChannel* CMuxChannel::NewL(CAvdtpProtocol& aProtocol, + const TBTDevAddr& aRemoteDevice, + TTCID aRemotelyAssignedTCID/*=KInvalidTCID*/) + { + LOG_STATIC_FUNC + CMuxChannel* s = new (ELeave) CMuxChannel(aProtocol, aRemoteDevice); + CleanupStack::PushL(s); + s->ConstructL(aRemotelyAssignedTCID); + CleanupStack::Pop(s); + return s; + } + +void CMuxChannel::ConstructL(TTCID aRemotelyAssignedTCID) + { + LOG_FUNC +// iHeartbeat = CHeartbeat::NewL() + iMuxSendTimer = CPeriodic::NewL(EPriorityHigh); +#ifdef HAVE_INBOUND_MUX_PACKET + iInboundMessage = CAvdtpInboundMuxedMessage::NewL(*this); +#endif + iOutboundMessage = CAvdtpOutboundMuxedMessage::NewL(); + + CSignallingChannel* sc = Protocol().FindSignallingChannel(RemoteAddress()); + if (!sc) + { + User::Leave(KErrDisconnected); + } + + if (aRemotelyAssignedTCID==KInvalidTCID) + { + User::LeaveIfError(sc->TCIDManager().GetTCID(iTCID)); + } + else + { + User::LeaveIfError(sc->TCIDManager().GrabTCID(iTCID, aRemotelyAssignedTCID)); + } + + User::LeaveIfError(iSessions.Append(iMediaSessions.Array())); + User::LeaveIfError(iSessions.Append(iReportingSessions.Array())); + User::LeaveIfError(iSessions.Append(iRecoverySessions.Array())); + } + +CMuxChannel::CMuxChannel(CAvdtpProtocol& aProtocol, + const TBTDevAddr& aRemoteDevice) +: CTransportChannel(aProtocol, aRemoteDevice), +#ifdef SESSION_ITERATOR_CONCRETE + iIter(iMediaSessions, iReportingSessions, iRecoverySessions) +#else + iIter(iSessions.Array()) +#endif + { + LOG_FUNC + } + +CMuxChannel::~CMuxChannel() + { + LOG_FUNC + iMuxSendTimer->Cancel(); + iTCID.Close(); + } + +TTCID CMuxChannel::TCID() const + { + LOG_FUNC + return iTCID.TCID(); + } + +/*static*/ TInt CMuxChannel::MuxSendIntervalCb(TAny* aCMuxChannel) + { + LOG_STATIC_FUNC + // mux send interval has expired + // this will send the packet regardless of whether it's full + // thus guarding against noone filling the packet, and keeping some finite latency + CMuxChannel& me = *static_cast(aCMuxChannel); + (void)me.DoSend(); + return KErrNone; + } + +TUint CMuxChannel::SendPacket(TTSID aTSID, RMBufChain& aPacket) + { + LOG_FUNC + // also allows the odd case of "muxing" from one session on one stream! + // this is useful as it allows the same "fill a baseband packet for 10p" game + TUint wrote = 0; + + // we always know all our bound sessions can send on this channel + CAvdtpOutboundMuxedMessage::TMuxedMessageOperation result = iOutboundMessage->TryToAddPacket(aTSID, aPacket); + + switch (result) + { + case CAvdtpOutboundMuxedMessage::ENoMemory: + Error(KErrNoMemory); + break; + case CAvdtpOutboundMuxedMessage::EReadyToSendPacketAdded: + case CAvdtpOutboundMuxedMessage::EReadyToSendPacketNotConsumed: + wrote = DoSend(); + break; + case CAvdtpOutboundMuxedMessage::EPacketAdded: + { + // guard against no other write by starting timer + TCallBack cb(MuxSendIntervalCb, this); + + iMuxSendTimer->Start(KMuxSendInitial, + KMuxSendPeriod, + cb); + + break; + } + } + + if (result==CAvdtpOutboundMuxedMessage::EReadyToSendPacketNotConsumed && wrote !=0) + { + // need to start new muxed packet with aChain as payload + // old one was sent though, and we notice that l2cap hasn't blocked us + TInt r = iOutboundMessage->Reset(); + if (!r) + { + // recurse + wrote = SendPacket(aTSID, aPacket); + } + else + { + // for *this* write we need to tell them we're blocked + wrote = 0; + } + } + + return wrote; + } + +TUint CMuxChannel::DoSend() + { + LOG_FUNC + TUint wrote =0; + if (iLogicalChannel) + { + wrote = iLogicalChannel->Write(iOutboundMessage->Data(), 0); + } + return wrote; + } +/* +TInt CMuxChannel::SessionDataReceived(TTSID aTSID, RMBufChain& aChain) + { + // put on pool and notify + #pragma message("is there n pools for eg media, or just one?") + } +*/ +TInt CMuxChannel::AttachTransportSession(CUserPlaneTransportSession& aSession, TAvdtpTransportSessionType aType) +/** +Protocol must ensure it has found appropriate muxchannel for +Recovery packets +*/ + { + LOG_FUNC + RArray* sessionArray = NULL; + + switch (aType) + { + case EMedia: + sessionArray = &iMediaSessions; + break; + + case EReporting: + sessionArray = &iReportingSessions; + break; + + case ERecovery: + sessionArray = &iRecoverySessions; + break; + } + + __ASSERT_DEBUG(sessionArray, Panic(EAVDTPBadSessionAttachToTransportChannel)); + + return sessionArray->Append(TUserPlaneTransportSessionState(aSession)); + } + +/** +Actually activate multiplexing mode in packet domain +- we are either multiplexing for one session, or many and should expect and generate +AL headers in the packets. + +This channel may have previously been declared as a multiplexor inbound, as we support +muxing, but remote SEP may not have configured it: another remote SEP (same device) +may then choose to use muxing, which is why we need this "upgradeability" +*/ +/* +void CMuxChannel::ActiveMultiplexer() + { + iActivatedMultiplexer = ETrue; + } +*/ +TBool CMuxChannel::CouldAttachSession(const TAvdtpSockAddr& aAddr) + { + LOG_FUNC + // need to see if this is for the same device, and not an incompatible session + if (aAddr.BTAddr()!=RemoteAddress()) + { + // can't go to different device! + return EFalse; + } + + // we know of the different session types by class design + // so we just want to check that: + // if this prospective session is Recovery that we dont' have other sessions with same SEID + // or vice versa + TInt i=0; + if (aAddr.Session() == ERecovery) + { + for (i=0;i* array = NULL; + + switch (aType) + { + case EMedia: + array = &iMediaSessions; + break; + case EReporting: + array = &iReportingSessions; + break; + case ERecovery: + array = &iRecoverySessions; + break; + } + +#ifdef _DEBUG + TInt found =0; +#endif + + // find in the array + for (TInt i=0; iCount(); i++) + { + if (&(*array)[i].iSession == &aSession) + { + // found it + array->Remove(i); // don't delete as don't own +#ifdef _DEBUG + found++; +#else + break; +#endif + } + } + __ASSERT_DEBUG(found==1, Panic(EAVDTPBadSessionDetachFromTransportChannel)); + + CheckForClose(); + } + +CServProviderBase* CMuxChannel::ObtainSAP() + { + __ASSERT_DEBUG(EFalse, Panic(EAvdtpTransferSapCalledForMuxChannel)); + return NULL; + } + +// from logical channel +void CMuxChannel::CanSend() + { + LOG_FUNC + iLogicalChannelBlocked = EFalse; + + // send muxed packet, and see if we're still unblocked + TUint r = DoSend(); + + if (!r) + { + //iterate over all sessions to induce them to send + iIter.Reset(); + + while(iIter) + { + iIter++.iSession.CanSend(); + } + } + } + + +void CMuxChannel::NewData(TUint aCount) +/** +Must get out of L2CAP so that we can read the TSID (and check internally the TCID) +*/ + { + LOG_FUNC +#ifdef HAVE_INBOUND_MUX_PACKET + iInboundPacket->NewData(aCount); +#else + LOG1(_L("Count = %d (check for >1 !)"), aCount); + + // get the data now? + // need to parse of the AL header - and give to relevant session + // as we don't support fragmentation we can "assume" the this l2cap packet + // is a whole mux packet - of course we need to test the frag bit and moan if set + while (aCount--) + { + RMBufChain packet; + TInt err = iLogicalChannel->GetData(packet,0,0); //returns negative error code or the number of datagrams ( = 1 ) + if (err > 0) + { + // without frag one L2CAP datagram is one muxed packet + LOG(_L("Inbound packet built - now parse")) + + while (packet.Length()) + { + LOG1(_L("Muxed packet length remaining =%d byte"), packet.Length()); + // iterate over all ALHeaders in muxed packet + // check for frag - we don't support, so if set drop - remote is bad + TUint8 ALHeader = *packet.First()->Ptr(); + packet.TrimStart(1);// ALHeader + + LOG1(_L("AL Header = 0x%02x"), ALHeader); + + if (!(ALHeader & (1<> 3); + TInt lenCode(ALHeader & 0x3); + TInt len=0; + + switch (lenCode) + { + case 0x00: + LOG(_L("no len field")); + // no length (for last packet in the muxed packet + len = packet.Length(); + break; + case 0x01: + LOG(_L("16 bit len field")); + // 16 bit length field + if (packet.Length()>=2) + { + len = *reinterpret_cast(packet.First()->Ptr()); + packet.TrimStart(2); + } + else + { + // bad mux packet, try to parse rest lurking in l2cap + packet.Free(); + continue; + } + break; + case 0x02: + LOG(_L("9 bit len field MSB=0")); + // 9 bit length field = basically 8 bit but don't add 256 + len = *packet.First()->Ptr(); + // drop through + case 0x03: + LOG(_L("9 bit len field MSB=1")); + // 9 bit length field = basically 8 bit but add 256 + len += 256; + packet.TrimStart(1); + break; + } // switch + LOG1(_L("Session data len = %d bytes"), len); + // split chain so that we can pass relevant bit to session + RMBufChain sessionData; + + iIter.Reset(); + while (iIter) + { + CUserPlaneTransportSession* session = &(iIter++).iSession; + if (session && session->TSID() == tsid) + { + session->NewData(sessionData); + break; + } + } + } // end if F bit test + } // end while data left in muxed packet + } // end if no error from l2cap + } // end aCount while +#endif + } + +/** +Logical Channel has Disconnected - no point our staying around +Error all sessions and die. +*/ +void CMuxChannel::Disconnect() + { + LOG_FUNC + iLogicalChannel = NULL; + Error(KErrDisconnected); + } + +/** +Iterate over all sessions and error them. +*/ +void CMuxChannel::Error(TInt aError, TUint /*aOperationMask*/) + { + LOG_FUNC + iIter.Reset(); + while (iIter) + { + iIter++.iSession.ChannelError(aError); + } + } + + +void CMuxChannel::CheckForClose() + { + LOG_FUNC + if (!iMediaSessions.Count() &!iReportingSessions.Count() &!iRecoverySessions.Count()) + { + Protocol().TransportChannelClosing(*this); + CloseLogicalChannel(); + } + } + +/** +Called by mux message to get data from logical channel +*/ +TInt CMuxChannel::GetData(RMBufChain& aRxBuffer) + { + return iLogicalChannel->GetData(aRxBuffer,0,0); + } + +void CMuxChannel::TransportSessionBlocked(TAvdtpTransportSessionType /*aSession*/, TBool /*aBlocked*/) + { + LOG_FUNC + + } + + +// helper class + +template +TSessionIterator::TSessionIterator(const TArray< const TArray >& aArrayOfArrays) +: iArrays(aArrayOfArrays) + { + Reset(); + } + +template +const T& TSessionIterator::operator++(TInt /*aPostIncrementDummy*/) + { + const T& t = iArrays[iArray].operator[](iArrayIndex); + DoIncrement(); + return t; + } + +template +void TSessionIterator::DoIncrement() + { + // increment index of current array + iArrayIndex++; + // if we max out then move onto next array + if (iArrayIndex == iArrays[iArray].Count()) + { + // got to the end of the iArray-th array, go onto the next + iArrayIndex =0; + iArray++; + } + // the next array may be blank, as may the next next etc, so skip them + do + { + // it may be that we get to the very end of all the arrays + if (iArray == iArrays.Count()) + { + // got the last array, finish + iFinished = ETrue; + } + if (!iFinished && !iArrays[iArray].Count()) + { + // skip empty array + iArray++; + } + else + { + // this array has stuff in it + break; + } + } + while (iArray +TSessionIterator::operator TBool() + { + return !iFinished; + } + +template +void TSessionIterator::Reset() + { + iFinished = EFalse; + iArrayIndex =0; + iArray =0; + } +