--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/bluetooth/btstack/linkmgr/AclDataQController.cpp Fri Jan 15 08:13:17 2010 +0200
@@ -0,0 +1,624 @@
+// 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:
+// Link Command Q Controlling internal methods
+// The command Q is used to store commands used by the stack
+// The HCI is at liberty to use its own command Q for any other HC signalling
+// it requires.
+// The HCI in such cases would then signal the stack with not all credits etc.
+//
+//
+
+#include <bluetooth/logger.h>
+#include "AclDataQController.h"
+#include "linkmgr.h"
+#include "AclDataQ.h"
+#include "hcifacade.h"
+
+#ifdef __FLOG_ACTIVE
+_LIT8(KLogComponent, LOG_COMPONENT_LINKMGR);
+#endif
+
+const TInt KACLDataQConnectionRecordGranularity = 2;
+
+/**
+ Factory function.
+
+ @return Ownership of a new CACLDataQController.
+ */
+CACLDataQController* CACLDataQController::NewL(CLinkMgrProtocol& aProtocol,
+ CLinkMuxer& aMuxer,
+ TUint16 aBufSize,
+ TUint16 aFrameOverhead,
+ TUint aNumBufs)
+ {
+ LOG_STATIC_FUNC
+ LOG3(_L("CACLDataQController::NewL aBufSize = %d, aFramingOverhead = %d, aNumBufs = %d"),
+ aBufSize, aFrameOverhead, aNumBufs);
+
+ CACLDataQController* self = new(ELeave) CACLDataQController(aProtocol.HCIFacade(), aMuxer);
+ CleanupStack::PushL(self);
+ self->ConstructL(aProtocol, aBufSize, aFrameOverhead, aNumBufs);
+ CleanupStack::Pop(self);
+
+ LOG1(_L("CACLDataQController::NewL self = 0x%08x"), self);
+ return self;
+ }
+
+CACLDataQController::CACLDataQController(CHCIFacade& aHCIFacade,
+ CLinkMuxer& aMuxer)
+ : iAclConns(KACLDataQConnectionRecordGranularity),
+ iLinkMuxer(aMuxer),
+ iHCIFacade(aHCIFacade)
+ {
+ LOG_FUNC
+ }
+
+CACLDataQController::~CACLDataQController()
+ {
+ LOG_FUNC
+
+ delete iDataQ;
+ iAclConns.Reset();
+ iAclConns.Close();
+ }
+
+void CACLDataQController::ConstructL(CLinkMgrProtocol& aProtocol,
+ TUint16 aBufSize,
+ TUint16 aFrameOverhead,
+ TUint aNumBufs)
+ {
+ LOG_FUNC
+
+ iDataQ = CAclDataQ::NewL(aProtocol, aNumBufs, aBufSize, aFrameOverhead);
+
+#ifdef PROXY_COMMUNICATES
+ LOG(_L("\tPROXY_COMMUNICATES defined- reserving slots for broadcast channel"));
+
+ // Reserve BC one now
+ User::LeaveIfError(ACLLogicalLinkUp(KHCIBroadcastHandle, EFalse));
+#endif
+ }
+
+void CACLDataQController::InitialDataCredits(TUint16 aCredits)
+/**
+ Set the initial data credits to whatever number of buffers the HC has.
+*/
+ {
+ LOG_FUNC
+ LOG1(_L("CACLDataQController::InitialDataCredits aCredits = %d"), aCredits);
+
+ iDataCredits = aCredits;
+ iNumControllerBufs = aCredits;
+ }
+
+CACLDataItem* CACLDataQController::GetFreeItem()
+/**
+ Returns ownership of a data item, or NULL if there are none free.
+ */
+ {
+ LOG_FUNC
+
+ __ASSERT_DEBUG(iDataQ, Panic(EHCIBufferMgrBadState));
+ CACLDataItem* ret = iDataQ->RemoveFirstSpareItem();
+
+ LOG1(_L("CACLDataQController::GetFreeItem ret = 0x%08x"), ret);
+ return ret;
+ }
+
+void CACLDataQController::AddItem(CACLDataItem& aAclItem)
+ {
+ LOG_FUNC
+ LOG1(_L("CACLDataQController::AddItem &aAclItem = 0x%08x"), &aAclItem);
+
+ // what Connection Handle is this frame for?
+ __ASSERT_DEBUG(aAclItem.Frame(), Panic(EHCIBufferMgrBadState));
+ THCIConnHandle connh = (*(aAclItem.Frame())).ConnectionHandle();
+
+ // Get the connection record
+ const TInt index = FindConnection(connh);
+ __ASSERT_ALWAYS(index>=0, Panic(EHCIACLDataControllerConnectionListConnectionNotFound));
+
+ ++(iAclConns[index].iPacketsQueued);
+ LOG3(_L("\tadded an item for connection handle %d: iPacketsQueued = %d, iPacketsPending = %d"),
+ connh, iAclConns[index].iPacketsQueued, iAclConns[index].iPacketsPending);
+
+ __ASSERT_DEBUG(iDataQ, Panic(EHCIBufferMgrBadState));
+ iDataQ->AddItem(aAclItem);
+ iLinkMuxer.TryToSend();
+ }
+
+THCIConnHandle CACLDataQController::HighestPriority()
+/**
+ Get the highest priority connection, starting to look from the connection
+ which sent last, and ignoring connections with no queued data to send.
+ If there's no clear highest priority connection, the connection handle of the
+ 0th connection in our array will be returned, whether it has anything to send
+ or not (i.e. you always get a valid connection handle).
+ Assumes that there is at least 1 connection handle in our array.
+ */
+ {
+ LOG_FUNC
+ LOG1(_L("CACLDataQController::HighestPriority iIndexOfLastSendingConn = %d"),
+ iIndexOfLastSendingConn);
+
+ const TUint count = iAclConns.Count();
+ LOG1(_L("\tcount = %d"), count);
+ // Should only be called if we have connections to judge the highest
+ // priority of.
+ __ASSERT_DEBUG(count != 0, Panic(EHCIBufferMgrBadState));
+
+ // Deal with the most likely case first, for speed...
+ if ( count == 1 )
+ {
+ LOG1(_L("CACLDataQController::HighestPriority returning %d"),
+ iAclConns[0].iConnH);
+ return iAclConns[0].iConnH;
+ }
+
+ // Now look through iAclConns from the one which sent last...
+ __ASSERT_DEBUG(iIndexOfLastSendingConn < count, Panic(EHCIBufferMgrBadState));
+ TInt highestpriority = -KMaxTInt;
+ // NB If no connections have anything to send, connection index 0 will be
+ // indicated by the return value.
+ TInt highestindex = 0;
+ TUint index;
+ if ( iIndexOfLastSendingConn + 1 == count )
+ {
+ index = 0;
+ }
+ else
+ {
+ index = iIndexOfLastSendingConn + 1;
+ }
+
+ FOREVER
+ {
+ __ASSERT_DEBUG(index < static_cast<TUint>(iAclConns.Count()), Panic(EHCIBufferMgrBadState));
+
+ const TInt priority = -(iAclConns[index].iPacketsPending);
+ const TInt queued = iAclConns[index].iPacketsQueued;
+
+#ifdef __FLOG_ACTIVE
+ const THCIConnHandle connh = iAclConns[index].iConnH;
+ LOG3(_L("\tconnection handle %d: iPacketsQueued = %d, iPacketsPending = %d"),
+ connh, queued, -priority);
+#endif
+
+ // Only consider connections with some data queued.
+ if ( queued > 0 && priority > highestpriority )
+ {
+ highestpriority = priority;
+ highestindex = index;
+ }
+
+ LOG3(_L("\tindex = %d, highestpriority = %d, highestindex = %d"),
+ index, highestpriority, highestindex);
+
+ ++index;
+ // Have we gone all the way round?
+ if ( index == iIndexOfLastSendingConn + 1 )
+ {
+ break;
+ }
+ // If we're at the end of the array, continue looking from the
+ // beginning.
+ if ( index == count )
+ {
+ index = 0;
+ }
+ }
+
+ LOG1(_L("CACLDataQController::HighestPriority connection handle = %d"),
+ iAclConns[highestindex].iConnH);
+ return iAclConns[highestindex].iConnH;
+ }
+
+TBool CACLDataQController::AnotherPacketAllowed(TDataQConnectionInfo& aRecord)
+/**
+ A small strategy to see if another packet is allowed on a given connection
+ handle.
+ Always try to keep some spare host controller buffers for another
+ connection handle that might arise. This maxes out at 8.
+*/
+ {
+ LOG_FUNC
+
+ TBool allowAnotherPacket;
+
+ if ( aRecord.iParked && aRecord.iConnH != KHCIBroadcastHandle )
+ {
+ // not allowed to send on non-broadcast handle when parked
+ allowAnotherPacket = EFalse;
+ }
+ else
+ {
+ // the rest of the handles are in principle sendable, but need to
+ // check for room in HC. NB The '- 1' here is to make sure that one
+ // connection can't result in the whole device being locked up if the
+ // remote device stops responding- there'll still be one slot left
+ // over.
+ allowAnotherPacket = aRecord.iPacketsPending < iNumControllerBufs - 1 ? ETrue : EFalse;
+ }
+
+ LOG1(_L("CACLDataQController::AnotherPacketAllowed allowAnotherPacket = %d"),
+ allowAnotherPacket);
+ return allowAnotherPacket;
+ }
+
+/**
+ Called by the link muxer to do an actual send.
+ Get the link which is entitled to send (on the basis of priority (which is
+ simply (0-number of packets waiting to be sent)) and recent history (if it
+ was the last to send, we check all the other links first for a higher or
+ equal priority one).
+ Finally, get a packet belonging to the winning link and attempt to send it.
+
+ @return EFalse if no send occurred, ETrue otherwise.
+ */
+TBool CACLDataQController::IssueNextACLDataFragment()
+ {
+ LOG_FUNC
+ __ASSERT_DEBUG(iDataCredits>0,Panic(EHCIACLDataControllerDataCreditsZeroBeforeIssue));
+
+ if ( !iAclConns.Count() )
+ {
+ LOG(_L("CACLDataQController::IssueNextACLDataFragment returning EFalse"));
+ return EFalse;
+ }
+
+ THCIConnHandle connh = HighestPriority(); // find suggested connection handle to send on
+ //ask the DataQ for the first item on this connection handle
+ CACLDataItem* item = NULL;
+ __ASSERT_DEBUG(iDataQ, Panic(EHCIBufferMgrBadState));
+ item = iDataQ->FirstItemByConnectionHandle(iHCIFacade, connh);
+ //if still NULL then there is no longer stuff on that connH on the Q so get the head item
+ if ( !item )
+ {
+ item = iDataQ->FirstItem(iHCIFacade, connh); // connh will be set
+ }
+
+ TBool sent = EFalse;
+
+ const TInt index = FindConnection(connh);
+ LOG1(_L("\tFindConnection returned %d"), index);
+ if ( index == KErrNotFound )
+ {
+ // Connection not found, because it went down between the link muxer
+ // realising it could try to send and it (asynchronously) actually
+ // asking us to send.
+ LOG1(_L("CACLDataQController::IssueNextACLDataFragment sent = %d"),
+ sent);
+ return sent;
+ }
+
+ if ( !AnotherPacketAllowed(iAclConns[index]) )
+ {
+ LOG1(_L("CACLDataQController::IssueNextACLDataFragment sent = %d"),
+ sent);
+ return sent; // no more allowed, otherwise HC liable to clogging
+ }
+
+ __ASSERT_DEBUG(item, Panic(EHCIBufferMgrBadState));
+ sent = SendItem(*item);
+
+ // Regardless of whether the send succeeded, make a note that this
+ // connection handle was the last to 'have a go'...
+ iIndexOfLastSendingConn = index;
+
+ if ( sent )
+ {
+ --iDataCredits;
+ --(iAclConns[index].iPacketsQueued);
+ ++(iAclConns[index].iPacketsPending);
+ LOG3(_L("\tsent on connection handle %d: iPacketsQueued = %d, iPacketsPending = %d"),
+ connh, iAclConns[index].iPacketsQueued, iAclConns[index].iPacketsPending);
+ }
+
+ LOG1(_L("CACLDataQController::IssueNextACLDataFragment sent = %d"), sent);
+ return sent;
+ }
+
+TBool CACLDataQController::SendItem(CACLDataItem& aItem)
+/**
+ Actually do the send - return true if successful
+**/
+ {
+ LOG_FUNC
+
+ TBool ret = EFalse;
+ __ASSERT_DEBUG(aItem.Frame(), Panic(EHCIBufferMgrBadState));
+ if ( iHCIFacade.WriteACLData(*aItem.Frame()) == KErrNone )
+ {
+ __ASSERT_DEBUG(iDataQ, Panic(EHCIBufferMgrBadState));
+ iDataQ->PendingItem(aItem);
+ ret = ETrue;
+ }
+
+ LOG1(_L("CACLDataQController::SendItem ret = %d"), ret);
+ return ret;
+ }
+
+void CACLDataQController::GetBufferInfo(TUint16& aBufSize, TUint& aNumBufs)
+/**
+ Retrieves information about what the capabilities of the Data Q are.
+ This information will be used in order to inform the HCI client about
+ our MTU and maximum buffers so flow control can be possible.
+*/
+ {
+ LOG_FUNC
+ __ASSERT_DEBUG(iDataQ, Panic(EHCIBufferMgrBadState));
+ aBufSize = iDataQ->ItemSize();
+ aNumBufs = iDataQ->Ceiling();
+
+ LOG2(_L("CACLDataQController::GetBufferInfo aBufSize = %d, aNumBufs = %d"),
+ aBufSize, aNumBufs);
+ }
+
+void CACLDataQController::GetDataQRecords(TUint& aQFillLevel, TUint16& aCredits)
+/**
+ Returns the number of things ready to send, and the number of credits we
+ have to send
+**/
+ {
+ LOG_FUNC
+ __ASSERT_DEBUG(iDataQ, Panic(EHCIBufferMgrBadState));
+ aQFillLevel = iDataQ->FillLevel(); // items in the Q
+ aCredits = iDataCredits;
+ LOG2(_L("LL: CACLDataQController: Model: FillLevel %d, FillCeiling %d"),
+ aQFillLevel, iDataQ->Ceiling());
+
+ LOG2(_L("CACLDataQController::GetDataQRecords aQFillLevel = %d, aCredits = %d"),
+ aQFillLevel, aCredits);
+ }
+
+void CACLDataQController::FlushOccurred(THCIConnHandle /*aConnH*/)
+ {
+ LOG_FUNC
+ // do nothing
+ // we will receive a num_completed_packets event
+ }
+
+TInt CACLDataQController::ACLLogicalLinkUp(THCIConnHandle aConnH, TBool aIsParked)
+/**
+ This object cares more about *ACL* links
+ Whether or not ACL comes up for free with physical channel, it
+ is worth drawing the distinction
+
+ If an error occurs we tell the caller - it's likely the ACL will need to
+ be torn down
+**/
+ {
+ LOG_FUNC
+ LOG2(_L("CACLDataQController::ACLLogicalLinkUp aConnH = %d, aIsParked = %d"),
+ aConnH, aIsParked);
+
+ TDataQConnectionInfo tmpConnRecord;
+ tmpConnRecord.iConnH = aConnH;
+ tmpConnRecord.iPacketsQueued = 0;
+ tmpConnRecord.iPacketsPending = 0;
+ tmpConnRecord.iParked = aIsParked;
+
+ TInt err = iAclConns.Append(tmpConnRecord);
+
+ // We don't need to update iIndexOfLastSendingConn because the new
+ // connection will have been added 'after' its current position.
+
+ LOG1(_L("CACLDataQController::ACLLogicalLinkUp err = %d"), err);
+ return err;
+ }
+
+void CACLDataQController::ACLLogicalLinkDown(THCIConnHandle aConnH)
+/**
+ Updates the Q.
+ */
+ {
+ LOG_FUNC
+ LOG1(_L("CACLDataQController::ACLLogicalLinkDown aConnH = %d"),
+ aConnH);
+
+ TInt connection = FindConnection(aConnH);
+
+ if ( connection == KErrNotFound )
+ {
+ LOG(_L("CACLDataQController::ACLLogicalLinkDown (Connection not found)."));
+ return;
+ }
+
+ const TUint index = static_cast<TUint>(connection);
+
+ const TUint16 dropped = iAclConns[index].iPacketsPending;
+ LOG1(_L("\treusing %d credits"), dropped);
+
+ // Increase iDataCredits by how many packets this link had on the air (we
+ // won't receive a num_completed_packets event to do it the normal way)
+ iDataCredits = static_cast<TUint16>(iDataCredits + dropped);
+
+ __ASSERT_DEBUG(iDataQ, Panic(EHCIBufferMgrBadState));
+ iDataQ->InvalidateByConnH(aConnH, iHCIFacade);
+ iAclConns.Remove(index);
+
+ // The array of connections has changed- if the removed connection is
+ // 'after' iIndexOfLastSendingConn, we don't need to bother changing it.
+ // If the removed connection IS the connection that sent last, reset it to
+ // zero. If the removed connection is before the one that sent last, just
+ // decrement iIndexOfLastSendingConn.
+ if ( index == iIndexOfLastSendingConn )
+ {
+ iIndexOfLastSendingConn = 0;
+ }
+ else if ( index < iIndexOfLastSendingConn )
+ {
+ --iIndexOfLastSendingConn;
+ }
+
+ __ASSERT_DEBUG(iDataCredits<=iNumControllerBufs, Panic(EHCIACLDataControllerMaxAllowedDataCreditsExceeded));
+ }
+
+void CACLDataQController::SetParked(THCIConnHandle aConnH, TBool aParked)
+ {
+ LOG_FUNC
+ LOG2(_L("CACLDataQController::SetParked aConnH = %d, aParked = %d"),
+ aConnH, aParked);
+
+ // Check if there are any data connections.
+ if ( iAclConns.Count() > 1 )
+ {
+ const TInt index = FindConnection(aConnH);
+ if(index != KErrNotFound)
+ {
+ iAclConns[index].iParked = aParked;
+ }
+ else
+ {
+ LOG(_L("\tConnection handle not found."));
+ }
+ }
+ else
+ {
+ LOG(_L("\tcurrently no ACL connections"));
+ }
+
+ if ( aParked == EFalse )
+ {
+ // kick off draining of queue
+ iLinkMuxer.TryToSend();
+ }
+ }
+
+void CACLDataQController::CompletedPackets(THCIConnHandle aConnH, TUint16 aNo)
+/**
+ Called when some buffer slots become free in our HC (a
+ NumberOfCompletedPackets event)- in other words, we can now send aNo more
+ packets down if we wish.
+ */
+ {
+ LOG_FUNC
+ LOG2(_L("CACLDataQController::CompletedPackets aConnH = %d, aNo = %d"),
+ aConnH, aNo);
+
+ // Adjust the connection that's just had packets put on the air
+ const TInt index = FindConnection(aConnH);
+
+ //We have to check pending packets because it is possible this completed packets event comes
+ //from a previous link and current link reuses the same connection handle
+ if (index!=KErrNotFound && iAclConns[index].iPacketsPending > 0)
+ {
+ iAclConns[index].iPacketsPending = static_cast<TUint16>(iAclConns[index].iPacketsPending - aNo);
+
+ LOG4(_L("\t%d completed packets for connection handle %d: iPacketsQueued = %d, iPacketsPending = %d"),
+ aNo, aConnH, iAclConns[index].iPacketsQueued, iAclConns[index].iPacketsPending);
+
+ iDataCredits = static_cast<TUint16>(iDataCredits + aNo);
+ __ASSERT_DEBUG(iDataCredits<=iNumControllerBufs, Panic(EHCIACLDataControllerMaxAllowedDataCreditsExceeded));
+
+ LOG3(_L("LinkMgr: CACLDataQController: Got %d DataCredits on ConnH 0x%04x: total %d DataCredts"),
+ aNo, aConnH, iDataCredits);
+ LOG2(_L("LL: CACLDataQController: Got %d DataCredits; total %d DataCredts"),
+ aNo,iDataCredits);
+
+ // Notify sent items.
+ iDataQ->ItemsSent(aNo);
+
+ // now we have credits - have a go at using them
+ iLinkMuxer.TryToSend();
+ }
+ else
+ {
+ LOG1(_L("LinkMgr: CACLDataQController: Got Completed Packets on lost ConnH(%d) [dropped?]"), aConnH);
+ //The Hardware has sent num_completed_packets for a ConnH which we don't know about,
+ //if this was a race with the connection being dropped we will already have reused the
+ //credits.
+ }
+ }
+
+#pragma warning (default:4244)// "conversion from 'int' to 'unsigned short', possible loss of data"
+
+TInt CACLDataQController::FindConnection(THCIConnHandle aConnH)
+/**
+ Returns the index (in iAclConns) of the connection record with THCIConnHandle
+ aConnH, or error.
+ */
+ {
+ LOG_FUNC
+ LOG2(_L("CACLDataQController::FindConnection aConnH = %d, iAclConns.Count() = %d"),
+ aConnH, iAclConns.Count());
+
+ TDataQConnectionInfo tmpInfo;
+ tmpInfo.iConnH = aConnH;
+
+ TIdentityRelation<TDataQConnectionInfo> relation(LinkMatch);
+
+ const TInt position = iAclConns.Find(tmpInfo, relation);
+
+ LOG1(_L("CACLDataQController::FindConnection position = %d"), position);
+ return position;
+ }
+
+/*static*/ TBool CACLDataQController::LinkMatch(const TDataQConnectionInfo& aA,
+ const TDataQConnectionInfo& aB)
+ {
+ LOG_STATIC_FUNC
+ return (aA.iConnH == aB.iConnH);
+ }
+
+void CACLDataQController::FlushComplete(TInt __DEBUG_ONLY(aErr), THCIConnHandle aConnH)
+ {
+ LOG_FUNC
+ __ASSERT_DEBUG(aErr == KErrNone || aErr == ECommandDisallowed, Panic(EHCIACLDataControllerFlushCompleteError));
+
+ TInt ix = FindConnection(aConnH);
+ __ASSERT_DEBUG(ix != KErrNotFound, Panic(EHCIACLDataControllerFlushCompleteError));
+ if(ix != KErrNotFound)
+ {
+ __ASSERT_DEBUG(iAclConns[ix].iFlushInProgress, Panic(EHCIACLDataControllerFlushCompleteError));
+ iAclConns[ix].iFlushInProgress = EFalse;
+
+ iLinkMuxer.TryToSend();
+ }
+ }
+
+
+TInt CACLDataQController::SetFlushInProgress(THCIConnHandle aConnH)
+ {
+ LOG_FUNC
+ // Check a flush is not currently outstanding against this
+ // Connection handle.
+ TInt rerr = KErrNone;
+ TInt ix = FindConnection(aConnH);
+ if(ix != KErrNotFound)
+ {
+ if(!iAclConns[ix].iFlushInProgress)
+ {
+ TRAP(rerr, iHCIFacade.FlushL(aConnH));
+ if(rerr == KErrNone)
+ {
+ iAclConns[ix].iFlushInProgress = ETrue;
+ }
+ }
+
+ if(rerr == KErrNone)
+ {
+ iDataQ->ProcessFlush(iHCIFacade, aConnH);
+ }
+ }
+ else
+ {
+ rerr = ix;
+ }
+
+ return rerr;
+ }
+
+//
+// End of file