bluetooth/btstack/linkmgr/eSCOSAP.cpp
changeset 0 29b1cd4cb562
child 51 20ac952a623c
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/bluetooth/btstack/linkmgr/eSCOSAP.cpp	Fri Jan 15 08:13:17 2010 +0200
@@ -0,0 +1,346 @@
+// 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 eSCO SAP class
+// 
+//
+
+#include <bluetooth/logger.h>
+#include <bt_sock.h>
+#include <bluetooth/hci/hciframe.h>
+
+#include "SCOSAP.h"
+#include "physicallinksmanager.h"
+#include "physicallinks.h"
+#include "hcifacade.h"
+#include "linkutil.h"
+#include "linkmuxer.h"
+
+#ifdef __FLOG_ACTIVE
+_LIT8(KLogComponent, LOG_COMPONENT_LINKMGR);
+#endif
+
+// ----------------------------------------------------------------------------
+// eSCO link
+// ----------------------------------------------------------------------------
+CeSCOLink* CeSCOLink::NewLC(CPhysicalLinksManager& aConnectionMan, CPhysicalLink* aConnection)
+	{
+	CeSCOLink* s = new(ELeave) CeSCOLink(aConnectionMan, aConnection);
+	CleanupStack::PushL(s);
+	s->ConstructL();
+	return s;
+	}
+
+
+CeSCOLink* CeSCOLink::NewL(CPhysicalLinksManager& aLinksMan, CPhysicalLink* aPhysicalLink)
+	{
+	CeSCOLink* s = NewLC(aLinksMan, aPhysicalLink);
+	CleanupStack::Pop();
+	return s;
+	}
+
+CeSCOLink::CeSCOLink(CPhysicalLinksManager& aLinksMan, CPhysicalLink* aPhysicalLink)
+: CBTSynchronousLink(aLinksMan, aPhysicalLink, EeSCOLink)
+	{
+	iUserPacketTypes = TBTSyncPackets::ESyncAnyESCOPacket;
+	}
+
+void CeSCOLink::ConstructL()
+	{
+	CBTSynchronousLink::ConstructL();
+	}
+
+CeSCOLink::~CeSCOLink()
+	{
+	}
+
+TInt CeSCOLink::GetOption(TUint aLevel, TUint aName, TDes8& aOption) const
+	{
+	if (aLevel != KSolBtESCO)
+		{
+		return KErrNotSupported;
+		}
+	
+	switch (aName)
+		{
+		case EeSCOExtOptions:
+			{
+			TBTeSCOLinkParams* options = reinterpret_cast<TBTeSCOLinkParams*>(const_cast<TUint8*>(aOption.Ptr()));
+			options->iBandwidth.iTransmit = iTransmitBandwidth;
+			options->iBandwidth.iReceive = iReceiveBandwidth;
+			options->iLatency = iMaxLatency;
+			options->iRetransmissionEffort = iRetransmissionEffort;
+			options->iCoding = iVoiceSettings;
+			return KErrNone;
+			}
+		
+		default:
+			return KErrNotSupported;
+		}
+	}
+
+TInt CeSCOLink::SAPSetOption(TUint aLevel, TUint aName, const TDesC8& aOption)
+/**
+	Case ESyncSetUserPacketTypes:
+	Store the user specified pakcet types if at least one of the eSCO packet types is set
+	The packet types bitmask will be passed as a parameter of CPhysicalLink::Connect(const TBTSyncPackets aUserPacketTypes)
+	in DoActiveOpenL()
+*/
+	{
+	switch (aLevel)
+		{
+		case KSolBtSCO:
+			switch (aName)
+				{
+				case ESyncUserPacketTypes:
+					{
+					TBTSyncPacketTypes optPacketTypes = *reinterpret_cast<const TBTSyncPacketTypes*>(aOption.Ptr());
+					if (TBTSyncPackets::ESyncAnyESCOPacket & optPacketTypes) //make sure at least one packet type is supported
+						{
+						iUserPacketTypes = static_cast<TBTSyncPacketTypes>(optPacketTypes & (TBTSyncPackets::ESyncAnyESCOPacket));//Just eSCO packet types are stored
+						}
+					else
+						{
+						return KErrNotSupported;
+						}
+					
+					break;
+					}
+				
+				default:
+					return KErrNotSupported;
+				}
+			break;
+			
+		case KSolBtESCO:
+			switch (aName)
+				{
+				case EeSCOExtOptions:
+					{
+					TBTeSCOLinkParams options = *reinterpret_cast<const TBTeSCOLinkParams*>(aOption.Ptr());
+					
+					iTransmitBandwidth	= Max(options.iBandwidth.iTransmit, KMinESCOBandwidth);
+					iReceiveBandwidth	= Max(options.iBandwidth.iReceive, KMinESCOBandwidth);
+					iMaxLatency 		= Max(options.iLatency, KMinESCOLatency);
+					iRetransmissionEffort = options.iRetransmissionEffort;
+					iVoiceSettings		= options.iCoding;
+					
+					break;
+					}
+
+				default:
+					return KErrNotSupported;
+				}
+			break;
+			
+		default:
+			return KErrNotSupported;
+		}
+	
+	return KErrNone;
+	}
+
+
+TUint CeSCOLink::DoWrite(const TDesC8& aData, TUint /*aOptions*/, TSockAddr* /*aAddr*/)
+/**
+	Actually do the write - has been checked for length
+	In future could write data on a specific SCO "subchannel" if we do that sort of thing
+	between devices - possibly then use aAddr or aOptions
+**/
+	{
+	TUint retVal = 0;
+	TInt err = KErrNone;
+	
+#ifdef STACK_SCO_DATA
+	// Trim length of data if necessary
+	TPtrC8 data = aData.Left(CHctlSynchronousDataFrame::KHCTLMaxSynchronousDataSize);
+	if (iLinksMan.LinkManagerProtocol().LinkMuxer().CanWriteSCOData())
+		{
+		// for now just try to write - if it fails then it doesnt matter - its
+		// a lot more *s*ynchronous than sticking it in a queue ;-)
+		LOG(_L("CSCOLink: Can write SCO data..."));
+		delete iOutboundSCOFrame;
+		iOutboundSCOFrame = NULL;
+		const CHCIFacade& facade = iLinksMan.HCIFacade(); 
+		TRAP(err, iOutboundSCOFrame = facade.NewSynchronousDataFrameL(static_cast<TUint8>(data.Length())));
+		if(iOutboundSCOFrame && err == KErrNone)
+			{
+			// got the frame - so go and use it
+			facade.FormatSCOData(*iOutboundSCOFrame, iHandle, data);
+			if (facade.WriteSCOData(*iOutboundSCOFrame) == KErrNone)
+				{
+				retVal = data.Length(); // we sent this many bytes
+				}
+			}
+		}
+	else
+		{
+		LOG(_L("CSCOLink: ERROR Cannot write SCO data."));
+		Socket()->Error(((err == KErrNone) ? KErrInUse : err), MSocketNotify::EErrorSend);
+		}
+#else
+	LOG(_L("CSCOLink: ERROR SCO data via stack not supported.")))
+	Socket()->Error(KErrNotSupported, MSocketNotify::EErrorSend);
+#endif
+	return retVal;
+	}
+
+void CeSCOLink::PacketTypeChange(THCIErrorCode aErr, THCIConnHandle aConnH, TUint16 aNewPacket)
+	{
+	iState->PacketTypeChange(*this, aErr, aConnH, aNewPacket);
+	}
+
+TBool CeSCOLink::IsLinkProbablyPossible()
+	{
+	TUint minBandwidth = MinBandwidth();
+	if ((iTransmitBandwidth < minBandwidth) || (iReceiveBandwidth < minBandwidth))
+		{
+		return EFalse;
+		}
+	
+	TUint maxBandwidth = MaxBandwidth();
+	if ((iTransmitBandwidth != KESCOBandwidthDontCare) && (iTransmitBandwidth > maxBandwidth))
+		{
+		return EFalse;
+		}
+	
+	if ((iReceiveBandwidth != KESCOBandwidthDontCare) && (iReceiveBandwidth > maxBandwidth))
+		{
+		return EFalse;
+		}
+	
+	if (iMaxLatency < KMinESCOLatency)
+		{
+		return EFalse;
+		}
+	
+	TBool airCodingOK = EFalse;
+	TBTFeatures airCodings = iLinksMan.LinkManagerProtocol().PacketsSupportedLocally();
+	switch (iVoiceSettings & KAirCodingFormatBits)
+		{
+		case ECVSD:
+			airCodingOK = airCodings[ESupportedCVSDBit];
+			break;
+			
+		case EuLawLog:
+			airCodingOK = airCodings[ESupportedu_lawLogBit];
+			break;
+		
+		case EALawLog:
+			airCodingOK = airCodings[ESupportedA_lawLogBit];
+			break;
+		
+		case ETransparentData:
+			airCodingOK = airCodings[ESupportedTransparentSCODataBit];
+			break;
+		
+		default:
+			break;
+		}
+
+	return airCodingOK;
+	}
+
+
+TUint CeSCOLink::MinBandwidth()
+	{
+	TReal bandwidth = ((KMinESCOPacketSize *  KMilliSecondsFactor) / iMaxLatency);
+	return static_cast<TUint>(bandwidth);
+	}
+
+
+TUint CeSCOLink::MaxBandwidth()
+	{
+	TInt maxPacketSize = 0;
+	TInt interval = 1;
+	
+	if (iUserPacketTypes & TBTSyncPackets::ESyncPacketsEV5)
+		{
+		maxPacketSize = KMaxEV5Size;
+		interval = KEV5Slots + KEV3Slots;
+		}
+	else if (iUserPacketTypes & TBTSyncPackets::ESyncPacketsEV4)
+		{
+		maxPacketSize = KMaxEV4Size;
+		interval = KEV4Slots + KEV3Slots;
+		}
+	else if (iUserPacketTypes & TBTSyncPackets::ESyncPacketsEV3)
+		{
+		maxPacketSize = KMaxEV3Size;
+		interval = KEV3Slots + KEV3Slots;
+		}
+	
+	// Simple, easy to read version...
+	//TReal bandwidth = (maxPacketSize / (KBasebandSlotTime * tesco));
+	
+	// Faster, still correct but harder to understand...  Add 1 to force rounding up, just in case.
+	TUint bandwidth = ((KBasebandSlotsPerSecond * maxPacketSize) / interval) + 1;
+	return bandwidth;
+	}
+
+
+TInt CeSCOLink::MakeConnection(CPhysicalLink* aPhysLink)
+	{
+	return aPhysLink->SynchronousConnect(iTransmitBandwidth, iReceiveBandwidth,
+		iMaxLatency, iVoiceSettings,
+		iRetransmissionEffort, iUserPacketTypes);
+	}
+
+
+TDes8& CeSCOLink::InboundFrame()
+	{
+	return iInboundSCOFrame;
+	}
+
+void CeSCOLink::SetExtOptions(TBTSyncConnectOpts aSyncOpts)
+	{
+	iVoiceSettings = aSyncOpts.iAirMode & KAirCodingFormatBits;
+	
+	// Using nasty magic numbers as we have a naming clash...  Responses to a command
+	// use a different numbering scheme to setup commands.
+	const TInt KCVSDResponse = 2;
+	if (iVoiceSettings <= KCVSDResponse)
+		{
+		if (iVoiceSettings == KCVSDResponse)
+			{
+			iVoiceSettings = ECVSD;
+			}
+		else
+			{
+			iVoiceSettings++;
+			}
+		}
+	
+	TInt interval = aSyncOpts.iTransmissionInterval;
+	
+	TReal latency = interval * KBasebandSlotTime * KMilliSecondsFactor;
+	iMaxLatency = static_cast<TInt16>(latency + 1);
+	
+	iTransmitBandwidth = static_cast<TUint>((KBasebandSlotsPerSecond * aSyncOpts.iTxPacketLength / interval) + 1);
+	iReceiveBandwidth = static_cast<TUint>((KBasebandSlotsPerSecond * aSyncOpts.iRxPacketLength / interval) + 1);
+	iRetransmissionEffort = aSyncOpts.iRetransmissionWindow;
+	}
+
+TUint16 CeSCOLink::GetPacketMask() const
+	{
+	return iUserPacketTypes;
+	}
+
+void CeSCOLink::GetExtOptions(TBTeSCOLinkParams& aOptions) const
+	{
+	aOptions.iBandwidth.iTransmit = iTransmitBandwidth;
+	aOptions.iBandwidth.iReceive = iReceiveBandwidth;
+	aOptions.iLatency = iMaxLatency;
+	aOptions.iRetransmissionEffort = iRetransmissionEffort;
+	aOptions.iCoding = iVoiceSettings;
+	}