--- /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 <bluetooth/logger.h>
+#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<TUint8>(aTSID << KALHeaderTSIDOffset);
+ // make sure frag = 0
+ ALhdr &= ~(1<<KALHeaderFOffset);
+ // set LCODE - no support of jumbograms, so only choice of 2 LCODEs
+ // i.e. we always set the msb of LCODE then figure out lsb
+ ALhdr &= ((1 << KALHeaderLCODE_MSBOffset) | (aPacketLen >= 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<CMuxChannel*>(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<TUserPlaneTransportSessionState>* 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<iMediaSessions.Count();i++)
+ {
+ if (iMediaSessions[i].iSession.SEID()==aAddr.SEID())
+ {
+ // got media session for same stream - can't attach
+ return EFalse;
+ }
+ }
+ for (i=0;i<iReportingSessions.Count();i++)
+ {
+ if (iReportingSessions[i].iSession.SEID()==aAddr.SEID())
+ {
+ // got reporting session for same stream - can't attach
+ return EFalse;
+ }
+ }
+ }
+ return ETrue;
+ }
+
+
+void CMuxChannel::DetachTransportSession(CUserPlaneTransportSession& aSession, TAvdtpTransportSessionType aType)
+ {
+ LOG_FUNC
+ // find it - for runtime efficiency the session tells us its type
+ // we also don't make it virtual to save ram/rom and runtime (can't inline)
+
+ // remove it - having asserted that it is found
+ RArray<TUserPlaneTransportSessionState>* 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; i<array->Count(); 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<<KALHeaderFOffset)))
+ {
+ // F-bit not set, so proceed
+ TTSID tsid(ALHeader >> 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<TUint16*>(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 <class T>
+TSessionIterator<T>::TSessionIterator(const TArray< const TArray<T> >& aArrayOfArrays)
+: iArrays(aArrayOfArrays)
+ {
+ Reset();
+ }
+
+template <class T>
+const T& TSessionIterator<T>::operator++(TInt /*aPostIncrementDummy*/)
+ {
+ const T& t = iArrays[iArray].operator[](iArrayIndex);
+ DoIncrement();
+ return t;
+ }
+
+template <class T>
+void TSessionIterator<T>::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<iArrays.Count());
+ if (iArray == iArrays.Count())
+ {
+ // got the last array, finish
+ iFinished = ETrue;
+ }
+ }
+
+
+/*
+Conversion operator - 'returns' a Bool. ETrue if not completed
+Required to have an iterator of the usual form.
+*/
+template <class T>
+TSessionIterator<T>::operator TBool()
+ {
+ return !iFinished;
+ }
+
+template <class T>
+void TSessionIterator<T>::Reset()
+ {
+ iFinished = EFalse;
+ iArrayIndex =0;
+ iArray =0;
+ }
+