diff -r 000000000000 -r 29b1cd4cb562 bluetoothmgmt/bluetoothclientlib/btlib/btsynclink.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/bluetoothmgmt/bluetoothclientlib/btlib/btsynclink.cpp Fri Jan 15 08:13:17 2010 +0200 @@ -0,0 +1,1488 @@ +// 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: +// Generic functions associated with all Bluetooth socket addresses +// +// + +#include "bt_sock.h" +#include "internaltypes.h" +#include "btsocketpanic.h" +#include "btsynclinkhelpers.h" +#include + + +EXPORT_C TBTSyncPackets::TBTSyncPackets(TBTSyncPacketTypes aPacketTypes) + : iPacketTypes(aPacketTypes) + {} + + +EXPORT_C TBTSyncPacketTypes TBTSyncPackets::operator()() const + { + return iPacketTypes; + } + +EXPORT_C TBTSyncBandwidth::TBTSyncBandwidth() + {} + +EXPORT_C TBTSyncBandwidth::TBTSyncBandwidth(TUint aBandwidth) + : iTransmit(aBandwidth), iReceive(aBandwidth) + {} + +TBTeSCOLinkParams::TBTeSCOLinkParams(TUint aBandwidth, TUint16 aCoding, TUint16 aLatency, TUint8 aRetransmission) + : iBandwidth(aBandwidth), iCoding(aCoding), iLatency(aLatency), iRetransmissionEffort(aRetransmission) + {} + +/** Allocate and open a socket sub-session for Bluetooth SCO + +@param aNotifier Notifier object which will receive callbacks after asynchronous requests +@param aServer Handle to a currently connected session on the socket server +@return Newly allocated CBluetoothSynchronousLink object +@capability LocalServices +*/ +EXPORT_C CBluetoothSynchronousLink* CBluetoothSynchronousLink::NewL(MBluetoothSynchronousLinkNotifier& aNotifier, + RSocketServ& aServer) + { + CBluetoothSynchronousLink* self = NewLC(aNotifier, aServer); + CleanupStack::Pop(); + return self; + } + + +/** Allocate and open a socket sub-session for Bluetooth SCO + +@param aNotifier Notifier object which will receive callbacks after asynchronous requests +@param aServer Handle to a currently connected session on the socket server +@return Newly allocated CBluetoothSynchronousLink object +@capability LocalServices +*/ +EXPORT_C CBluetoothSynchronousLink* CBluetoothSynchronousLink::NewLC(MBluetoothSynchronousLinkNotifier& aNotifier, + RSocketServ& aServer) + { + CBluetoothSynchronousLink* self=new (ELeave) CBluetoothSynchronousLink(aNotifier, aServer); + CleanupStack::PushL(self); + self->ConstructL(); + return self; + } + + +/** +Destructor +*/ +EXPORT_C CBluetoothSynchronousLink::~CBluetoothSynchronousLink() + { + FTRACE(FPrint(_L("Destroying CBTSyncLink 0x%08x"), this)); + delete iBTSynchronousLinkSenderSCO; + delete iBTSynchronousLinkSenderESCO; + delete iBTSynchronousLinkReceiverSCO; + delete iBTSynchronousLinkReceiverESCO; + delete iBTSynchronousLinkAccepterSCO; + delete iBTSynchronousLinkAccepterESCO; + delete iBTSynchronousLinkAttacherSCO; + delete iBTSynchronousLinkAttacherESCO; + delete iBTSynchronousLinkDetacherSCO; + delete iBTSynchronousLinkDetacherESCO; + delete iBTSynchronousLinkBaseband; + + FLOG(_L("Closing sockets")); + ListeningSCOSocket().Close(); + ListeningESCOSocket().Close(); + SCOSocket().Close(); + ESCOSocket().Close(); + } + + +void CBluetoothSynchronousLink::ConstructL() + { + iBTSynchronousLinkSenderSCO = CBTSynchronousLinkSender::NewL(*this,ESCO); + iBTSynchronousLinkSenderESCO = CBTSynchronousLinkSender::NewL(*this,EeSCO); + iBTSynchronousLinkReceiverSCO = CBTSynchronousLinkReceiver::NewL(*this,ESCO); + iBTSynchronousLinkReceiverESCO = CBTSynchronousLinkReceiver::NewL(*this,EeSCO); + iBTSynchronousLinkAccepterSCO = CBTSynchronousLinkAccepter::NewL(*this,ESCO); + iBTSynchronousLinkAccepterESCO = CBTSynchronousLinkAccepter::NewL(*this,EeSCO); + iBTSynchronousLinkAttacherSCO = CBTSynchronousLinkAttacher::NewL(*this,ESCO); + iBTSynchronousLinkAttacherESCO = CBTSynchronousLinkAttacher::NewL(*this,EeSCO); + iBTSynchronousLinkDetacherSCO = CBTSynchronousLinkDetacher::NewL(*this,ESCO); + iBTSynchronousLinkDetacherESCO = CBTSynchronousLinkDetacher::NewL(*this,EeSCO); + iBTSynchronousLinkBaseband = CBTSynchronousLinkBaseband::NewL(*this); + } + + +CBluetoothSynchronousLink::CBluetoothSynchronousLink(MBluetoothSynchronousLinkNotifier& aNotifier, + RSocketServ& aServer) + : iNotifier(&aNotifier), iSockServer(aServer), + iRequestedLink(KBTSync64KBit, 0, 25, EeSCORetransmitDontCare), + iNegotiatedLink(0, 0, 0, 0) + { + FTRACE(FPrint(_L("Constructing CBTSyncLink 0x%08x"), this)); + } + + +/** +Create a synchronous link (e.g. voice) on an EXISTING physical link. + +@param aBDAddr Bluetooth address of remote device (specifying existing physical link) +@return Error code +*/ +EXPORT_C TInt CBluetoothSynchronousLink::SetupConnection(const TBTDevAddr& aBDAddr) + { + return SetupConnection(aBDAddr, TBTSyncPackets(TBTSyncPackets::ESyncAnySCOPacket)); + } + + +/** +Create a synchronous link (e.g. voice) on an EXISTING physical link. +@see TBTPacketType +@param aBDAddr Bluetooth address of remote device (specifying existing physical link) +@param aPacketTypes Bitmask of the SCO packet types required on this connection +Use only the three SCO specific packet type enums +E.g. EPacketsHV1|EPacketsHV2|EPacketsHV3 (EAnySCOPacket) specifies all the three packet +types are allowed. +OR if ESCO is supported +three ESCO specific packet type enums +@return Error code +*/ +EXPORT_C TInt CBluetoothSynchronousLink::SetupConnection(const TBTDevAddr& aBDAddr, const TUint16 aPacketTypes) + { + return SetupConnection(aBDAddr, TBTSyncPackets((aPacketTypes & EAnySCOPacket) >> KSCOvsSyncHVOffset)); + } + + +// This needs to be a macro or the 'return' won't return properly +#define CLOSE_RETURN_IF_ERROR(error) if (error) { LinkDown(); SCOSocket().Close(); ESCOSocket().Close(); return error; } +#define CLOSE_LISTENER_RETURN_IF_ERROR(error) if (error) { ListeningSCOSocket().Close(); ListeningESCOSocket().Close(); return error; } + +EXPORT_C TInt CBluetoothSynchronousLink::SetupConnection(const TBTDevAddr& aBDAddr, const TBTSyncPackets& aPacketTypes) + { + TBool openSCO = EFalse; + TBool openESCO = EFalse; + + FTRACE(FPrint(_L("SetupConnection on CBTSyncLink 0x%08x"), this)); + + TBTSyncPacketTypes packets = aPacketTypes(); + + __ASSERT_ALWAYS(packets, Panic(EBadSyncPacketTypes)); + + packets &= (TBTSyncPackets::ESyncAnySCOPacket | TBTSyncPackets::ESyncAnyESCOPacket); + if (!packets) + { + FLOG(_L("Bad packets specified at initial mask")); + return KErrNotSupported; + } + + TBTSyncPacketTypes packetsSCO = packets & TBTSyncPackets::ESyncAnySCOPacket; + if (packetsSCO) + { + iSCOTypes |= ESCO; + openSCO = ETrue; + } + + TBTSyncPacketTypes packetsESCO = packets & TBTSyncPackets::ESyncAnyESCOPacket; + if (packetsESCO) + { + iSCOTypes |= EeSCO; + openESCO = ETrue; + } + + // but must be one + __ASSERT_ALWAYS(packetsSCO || packetsESCO, Panic(EBadSyncPacketTypes)); + + if (iBTSynchronousLinkAttacherSCO->IsActive()) + { + FLOG(_L("Link attacher already active")); + return KErrInUse; + } + + if (iBTSynchronousLinkAttacherESCO->IsActive()) + { + FLOG(_L("Link attacher already active")); + return KErrInUse; + } + + TInt linkState = LinkUp(aBDAddr); + if (linkState != KErrNone) + { + FLOG(_L("Baseband link not available")); + return KErrDisconnected; + } + + TInt err = KErrNone; + + if (openSCO && openESCO) + { + /* open both socket types */ + /* first try ESCO */ + err = ESCOSocket().Open(iSockServer, + KBTAddrFamily, + KSockBluetoothTypeESCO, + KBTLinkManager); + if(!err) + { + err = ESCOSocket().SetOpt(ESyncUserPacketTypes, KSolBtSCO, packets); + + if (!err) + { + TPckgBuf options; + options() = iRequestedLink; + err = ESCOSocket().SetOpt(EeSCOExtOptions, KSolBtESCO, options); + } + } + if (err) + { // ESCO failed so try only SCO + ESCOSocket().Close(); + openESCO = EFalse; + } + + err = SCOSocket().Open(iSockServer, + KBTAddrFamily, + KSockBluetoothTypeSCO, + KBTLinkManager); + if(!err) + { + err = SCOSocket().SetOpt(ESyncUserPacketTypes, KSolBtSCO, packetsSCO); + } + else + {// SCO failed + openSCO = EFalse; + } + + CLOSE_RETURN_IF_ERROR(err); // this will sort out if both have failed + } + else if (openSCO) + { + err = SCOSocket().Open(iSockServer, + KBTAddrFamily, + KSockBluetoothTypeSCO, + KBTLinkManager); + if(err) + { + return err; + } + err = SCOSocket().SetOpt(ESyncUserPacketTypes, KSolBtSCO, packetsSCO); + CLOSE_RETURN_IF_ERROR(err); + } + else if (openESCO) + { + err = ESCOSocket().Open(iSockServer, + KBTAddrFamily, + KSockBluetoothTypeESCO, + KBTLinkManager); + if(err) + { + SCOSocket().Close(); + return err; + } + err = ESCOSocket().SetOpt(ESyncUserPacketTypes, KSolBtSCO, packetsESCO); + + TPckgBuf options; + options() = iRequestedLink; + err = ESCOSocket().SetOpt(EeSCOExtOptions, KSolBtESCO, options); + CLOSE_RETURN_IF_ERROR(err); + } + + iSockAddr.SetBTAddr(aBDAddr); + + iBTSynchronousLinkBaseband->PreventPark(); + + if(openESCO) + { + iOpeningESCO = ETrue; + iBTSynchronousLinkAttacherESCO->AttachSCOLink(iSockAddr); + iBTSynchronousLinkBaseband->CatchEvents(); + } + + if(openSCO) + { + iOpeningSCO = ETrue; + iBTSynchronousLinkAttacherSCO->AttachSCOLink(iSockAddr); + iBTSynchronousLinkBaseband->CatchEvents(); + } + + return KErrNone; + } + + +/** +Cancel creating a synchronous link. +This cancels the asynchronous request to prevent the callback reaching the client. +It makes no claims about the resultant state of the CBluetoothSynchronousLink. If +a client wishes to continue using the CBluetoothSynchronousLink it is strongly +recommended to follow a call to CancelSetup with a call to Disconnect, which will +ensure that any established connection is tidied up. +@see CBluetoothSynchronousLink::Disconnect +*/ +EXPORT_C void CBluetoothSynchronousLink::CancelSetup() + { + iBTSynchronousLinkAttacherSCO->Cancel(); + iBTSynchronousLinkAttacherESCO->Cancel(); + iBTSynchronousLinkBaseband->StopAll(); + } + + +/** +Disconnect a synchronous link. + +The physical link will remain unless no other services are running on it. +@return Error code +*/ +EXPORT_C TInt CBluetoothSynchronousLink::Disconnect() + { + if (!SCOSocket().SubSessionHandle()) + { + if(!ESCOSocket().SubSessionHandle()) + { + return KErrDisconnected; + } + } + + if (iBTSynchronousLinkDetacherSCO->IsActive() || + iBTSynchronousLinkDetacherESCO->IsActive()) + { + return KErrInUse; + } + + __ASSERT_ALWAYS(iSCOTypes, Panic(EBadSyncPacketTypes)); + if (iSCOTypes & ESCO) + { + iBTSynchronousLinkDetacherSCO->DetachSCOLink(iDummySCOShutdownDescriptor, iDummySCOShutdownDescriptor); + } + + if (iSCOTypes & EeSCO) + { + iBTSynchronousLinkDetacherESCO->DetachSCOLink(iDummySCOShutdownDescriptor, iDummySCOShutdownDescriptor); + } + + return KErrNone; + } + + +/** +Send data over synchronous link. It is worth noting that Bluetooth hardware +may have dedicated pins to supply synchronous data, and the RSocket interface +cannot provide the synchronous requirements of the SCO link. As a result of +this, the CBluetoothSynchronousLink class may only provide the control plane of +a SCO connection. In this situation, Receive returns KErrNotSupported via the +MBluetoothSynchronousLinkNotifier::HandleSendCompleteL callback. + +@param const aData Data to be sent. +@return Error code +*/ +EXPORT_C TInt CBluetoothSynchronousLink::Send(const TDesC8& aData) + { + __ASSERT_ALWAYS(SCOSocket().SubSessionHandle() || ESCOSocket().SubSessionHandle(), Panic(EInvalidSubSession)); + if (!SCOSocket().SubSessionHandle()) + { + if(!ESCOSocket().SubSessionHandle()) + { + return KErrDisconnected; + } + } + + if (iBTSynchronousLinkSenderSCO->IsActive() || + iBTSynchronousLinkSenderESCO->IsActive()) + { + return KErrInUse; + } + + __ASSERT_ALWAYS(iSCOTypes, Panic(EBadSyncPacketTypes)); + if (iSCOTypes & ESCO) + { + iBTSynchronousLinkSenderSCO->SendSCOData(aData); + } + + if (iSCOTypes & EeSCO) + { + iBTSynchronousLinkSenderESCO->SendSCOData(aData); + } + + return KErrNone; + } + + +/** +Cancel sending data. +*/ +EXPORT_C void CBluetoothSynchronousLink::CancelSend() + { + iBTSynchronousLinkSenderSCO->Cancel(); + iBTSynchronousLinkSenderESCO->Cancel(); + } + + +/** +Receive data over synchronous link. It is worth noting that Bluetooth hardware +may have dedicated pins to supply synchronous data, and the RSocket interface +cannot provide the synchronous requirements of the SCO link. As a result of +this, the CBluetoothSynchronousLink class may only provide the control plane of +a SCO connection. In this situation, Receive returns KErrNotSupported via the +MBluetoothSynchronousLinkNotifier::HandleReceiveCompleteL callback. + +@param const aData Buffer for data to be received. +@return Error code +*/ +EXPORT_C TInt CBluetoothSynchronousLink::Receive(TDes8& aData) + { + __ASSERT_ALWAYS(SCOSocket().SubSessionHandle() || ESCOSocket().SubSessionHandle(), Panic(EInvalidSubSession)); + if (!SCOSocket().SubSessionHandle()) + { + if(!ESCOSocket().SubSessionHandle()) + { + return KErrDisconnected; + } + } + + if (iBTSynchronousLinkSenderSCO->IsActive() || + iBTSynchronousLinkSenderESCO->IsActive()) + { + return KErrInUse; + } + + __ASSERT_ALWAYS(iSCOTypes, Panic(EBadSyncPacketTypes)); + if (iSCOTypes & ESCO) + { + iBTSynchronousLinkReceiverSCO->ReadSCOData(aData); + } + + if (iSCOTypes & EeSCO) + { + iBTSynchronousLinkReceiverESCO->ReadSCOData(aData); + } + + return KErrNone; + } + + +/** +Cancel receiving data. +*/ +EXPORT_C void CBluetoothSynchronousLink::CancelReceive() + { + iBTSynchronousLinkReceiverSCO->Cancel(); + iBTSynchronousLinkReceiverESCO->Cancel(); + } + + +/** +Prepare for a remote device to set up a synchronous link on the local device. + +Whilst this facility is set, a response can be sent to a remote device trying to +set up a synchronous link, allowing that synchronous link to be brought up. +This object will represent that synchronous link locally when/if it does come up. +@return Error code +*/ +EXPORT_C TInt CBluetoothSynchronousLink::AcceptConnection() + { + return AcceptConnection(TBTSyncPackets(TBTSyncPackets::ESyncAnySCOPacket)); + } + + +/** +Prepare for a remote device to set up a synchronous link on the local device. + +Whilst this facility is set, a response can be sent to a remote device trying to +set up a synchronous link, allowing that synchronous link to be brought up. +This object will represent that synchronous link locally when/if it does come up. + +@see TBTPacketType +@param aPacketTypes Bitmask of supported packets. +@return Error code +*/ +EXPORT_C TInt CBluetoothSynchronousLink::AcceptConnection(const TBTSyncPackets& aPacketTypes) + { + TBool listenForSCO = EFalse; + TBool listenForESCO = EFalse; + + FTRACE(FPrint(_L("AcceptConnection on CBTSyncLink 0x%08x"), this)); + + TBTSyncPacketTypes packets = aPacketTypes(); + + + __ASSERT_ALWAYS(packets, Panic(EBadSyncPacketTypes)); + packets &= (TBTSyncPackets::ESyncAnySCOPacket | TBTSyncPackets::ESyncAnyESCOPacket); + if (!packets) + { + return KErrNotSupported; + } + + if (iBTSynchronousLinkAccepterSCO->IsActive()) + { + return KErrInUse; + } + + if (iBTSynchronousLinkAccepterESCO->IsActive()) + { + return KErrInUse; + } + + TInt err = ListeningSCOSocket().Open(iSockServer, KBTAddrFamily, KSockBluetoothTypeSCO, KBTLinkManager); + if(err) + { + return err; + } + + err = ListeningESCOSocket().Open(iSockServer, KBTAddrFamily, KSockBluetoothTypeESCO, KBTLinkManager); + if(err) + { + ListeningSCOSocket().Close(); + return err; + } + + TBTSyncPacketTypes packetsSCO = packets & TBTSyncPackets::ESyncAnySCOPacket; + if (packetsSCO) + { + err = ListeningSCOSocket().SetOpt(ESyncUserPacketTypes, KSolBtSCO, packetsSCO); + if(!err) + { + iSCOTypes |= ESCO; + listenForSCO = ETrue; + } + } + + TBTSyncPacketTypes packetsESCO = packets & TBTSyncPackets::ESyncAnyESCOPacket; + if (packetsESCO) + { + err = ListeningESCOSocket().SetOpt(ESyncUserPacketTypes, KSolBtSCO, packetsESCO); + if (!err) + { + iSCOTypes |= EeSCO; + listenForESCO = ETrue; + } + + TPckgBuf options; + options() = iRequestedLink; + err = ListeningESCOSocket().SetOpt(EeSCOExtOptions, KSolBtESCO, options); + CLOSE_LISTENER_RETURN_IF_ERROR(err); + } + + __ASSERT_ALWAYS(listenForSCO || listenForESCO, Panic(EBadSyncPacketTypes)); + + TBTSockAddr sa; + TBTServiceSecurity sec; + + //security settings + sec.SetAuthentication(EMitmNotRequired); + sec.SetEncryption(EFalse); + sec.SetAuthorisation(EFalse); + sec.SetDenied(EFalse); + + sa.SetBTAddr(TBTDevAddr()); // I don't think this matters which address is being used + sa.SetSecurity(sec); + + if (listenForSCO) + { + err = ListeningSCOSocket().Bind(sa); + CLOSE_LISTENER_RETURN_IF_ERROR(err); + + err = ListeningSCOSocket().Listen(KSCOListenQueSize); + CLOSE_LISTENER_RETURN_IF_ERROR(err); + + err = SCOSocket().Open(SocketServer()); + if(err) + { + return err; + } + } + + if(listenForESCO) + { + err = ListeningESCOSocket().Bind(sa); + CLOSE_LISTENER_RETURN_IF_ERROR(err); + + err = ListeningESCOSocket().Listen(KSCOListenQueSize); + CLOSE_LISTENER_RETURN_IF_ERROR(err); + + err = ESCOSocket().Open(SocketServer()); + if(err) + { + return err; + } + } + + if (listenForSCO) + { + iBTSynchronousLinkAccepterSCO->Accept(ListeningSCOSocket()); + } + + if(listenForESCO) + { + iBTSynchronousLinkAccepterESCO->Accept(ListeningESCOSocket()); + } + + return err; + } + + +/** +Cancel ability to respond to a remote request to set up a synchronous link. +It is possible for a race condition to mean that a connection has been established, +but as this call consumes the callback, for this fact not to reach the caller. +For this reason, it may be desirable to follow a call to CancelAccept with a call +to Disconnect. +@see CBluetoothSynchronousLink::Disconnect +*/ +EXPORT_C void CBluetoothSynchronousLink::CancelAccept() + { + iBTSynchronousLinkAccepterSCO->Cancel(); + iBTSynchronousLinkAccepterESCO->Cancel(); + iBTSynchronousLinkBaseband->StopAll(); + + ListeningSCOSocket().Close(); + ListeningESCOSocket().Close(); + SCOSocket().Close(); + ESCOSocket().Close(); + } + + +/** + * Specify Voice Setting. + * See section 6.12 in Bluetooth Core Specification v1.2, Vol. 2, Part E. + */ +EXPORT_C void CBluetoothSynchronousLink::SetCoding(TUint16 aVoiceCoding) + { + iRequestedLink.iCoding = aVoiceCoding; + } + + +/** + * Specify maximum bandwidths in octets/second. + */ +EXPORT_C void CBluetoothSynchronousLink::SetMaxBandwidth(TBTSyncBandwidth aBandwidth) + { + iRequestedLink.iBandwidth = aBandwidth; + } + + +/** + * Specify maximum acceptable latency in milliseconds. + */ +EXPORT_C void CBluetoothSynchronousLink::SetMaxLatency(TUint16 aLatency) + { + iRequestedLink.iLatency = Max(aLatency, KMinESCOLatency); + } + + +/** + * Specify link retransmission policy. + */ +EXPORT_C void CBluetoothSynchronousLink::SetRetransmissionEffort(TBTeSCORetransmissionTypes aRetransmissionEffort) + { + iRequestedLink.iRetransmissionEffort = static_cast(aRetransmissionEffort); + } + + +/** + * Return the air coding portion only of the coding specified on the link. + * See section 6.12 in Bluetooth Core Specification v1.2, Vol. 2, Part E. + */ +EXPORT_C TUint16 CBluetoothSynchronousLink::Coding() + { + return iNegotiatedLink.iCoding & KAirCodingFormatBits; + } + + +/** + * Return the negotiated bandwidth. + */ +EXPORT_C TBTSyncBandwidth CBluetoothSynchronousLink::Bandwidth() + { + return iNegotiatedLink.iBandwidth; + } + + + +/** + * Return the negotiated latency in miliseconds (rounded up if non-integer on the link). + */ +EXPORT_C TUint16 CBluetoothSynchronousLink::Latency() + { + return iNegotiatedLink.iLatency; + } + + +/** + * Return an estimate of the retransmission policy on the link. + * Estimate is calculated by taking the size of the retransmission window, + * estimating the packet type based on packet length and calculating the + * number of packet retransmissions which are possible. This number is + * then returned by this function. + */ +EXPORT_C TUint8 CBluetoothSynchronousLink::RetransmissionEffort() + { + return iNegotiatedLink.iRetransmissionEffort; + } + + +/** Gets the socket address of the remote Bluetooth device. +@panic ESock_client 17 Raised when the link is not connected so there is no valid remote name to return. +@param aAddr The socket address. +*/ +EXPORT_C void CBluetoothSynchronousLink::RemoteName(TSockAddr& aAddr) + { + __ASSERT_ALWAYS(iSCOTypes, Panic(EBadSyncPacketTypes)); + if (iSCOTypes & ESCO) + { + SCOSocket().RemoteName(aAddr); + } + + if (iSCOTypes & EeSCO) + { + ESCOSocket().RemoteName(aAddr); + } + } + +/** +Set the object to be notified of synchronous link events. This does +not take ownership. This notifier will replace the current one. + +@param aNotifer The new object to inform of synchronous link events. +*/ +EXPORT_C void CBluetoothSynchronousLink::SetNotifier(MBluetoothSynchronousLinkNotifier& aNotifier) + { + iNotifier = &aNotifier; + } + +void CBluetoothSynchronousLink::HandleSetupConnectionCompleteL(TInt aErr, TSCOType aSCOType) + { + TBool notify = EFalse; + FTRACE(FPrint(_L("CBluetoothSynchronousLink::HandleSetupConnectionCompleteL(aErr %d)"), aErr)); + + /* we can be in a situation where we are trying: + SCO connection only + ESCO connection only + either SCO or ESCO + + If the connection is successful and we are trying one type then we accept it + If the connection isn't successful and we are trying one type then clean up + + If we are trying both and + a) only one is successful then clean up the other one + b) neither are successful then clean up + c) ESCO and SCO are both successful take the ESCO one and close SCO. + + + What ever case we are dealing with only notify when we are sure of a finished + result + */ + if (aErr==KErrNone) + { + iSCOTypes = (TUint8)aSCOType; + if (EeSCO == aSCOType) + { + if (iOpeningSCO) + { + // if we are trying both then close the SCO Socket + // we only want one open + SCOSocket().Close(); + iOpeningSCO = EFalse; + iOpenedSCO = EFalse; + } + else //we can update and notify as we are only trying ESCO + { + UpdateLinkParams(aSCOType); + } + notify = ETrue; + } + if (ESCO == aSCOType) + { + if (!iOpeningESCO) + {// we are only trying SCO so we can update and notify + UpdateLinkParams(aSCOType); + notify = ETrue; + } + else + { + iOpenedSCO = ETrue; // if the ESCO is rejected then we need to notify SCO was opened + } + } + } + else + { + if (EeSCO == aSCOType) + { + if (!iOpeningSCO) + { + // if we are trying both and the other socket might be accepted (or has + // been) we don't want to close this all down, but if we're not trying + // to open SCO then close it all down and tell the user + iNegotiatedLink = TBTeSCOLinkParams(0, 0, 0, 0); + iBTSynchronousLinkBaseband->StopAll(); + LinkDown(); + notify = ETrue; + } + else if (iOpenedSCO) + { + // successfully opened SCO connection, ESCO failed so tell the user + // about the SCO + notify = ETrue; + } + // no longer trying to open ESCO + iOpeningESCO = EFalse; + ESCOSocket().Close(); + } + else // ESCO = aSCOType - plain SCO + { + if (!iOpeningESCO) + { + // if we are trying both and the other socket might be accepted (or has + // been) we don't want to close this all down but if we're not trying + // to open ESCO then close it all down and tell the called + iNegotiatedLink = TBTeSCOLinkParams(0, 0, 0, 0); + iBTSynchronousLinkBaseband->StopAll(); + LinkDown(); + notify = ETrue; + } + else + {// no longer trying to open SCO + iOpeningSCO = EFalse; + } + SCOSocket().Close(); + } + } + + if (notify) + { +#ifdef __FLOGGING__ + TRAPD(err, Notifier().HandleSetupConnectionCompleteL(aErr)); + FTRACE(FPrint(_L("Setup upcall to link owner returned %d"), err)); + User::LeaveIfError(err); +#else + Notifier().HandleSetupConnectionCompleteL(aErr); +#endif + } + } + + +void CBluetoothSynchronousLink::HandleDisconnectionCompleteL(TInt aErr) + { + FTRACE(FPrint(_L("CBluetoothSynchronousLink::HandleDisconnectionCompleteL(aErr %d)"), aErr)); + + iNegotiatedLink = TBTeSCOLinkParams(0, 0, 0, 0); + + iBTSynchronousLinkBaseband->StopAll(); + LinkDown(); + SCOSocket().Close(); + ESCOSocket().Close(); + +#ifdef __FLOGGING__ + TRAPD(err, Notifier().HandleDisconnectionCompleteL(aErr)); + FTRACE(FPrint(_L("Disconnect upcall to link owner returned %d"), err)); + User::LeaveIfError(err); +#else + Notifier().HandleDisconnectionCompleteL(aErr); +#endif + + } + + +void CBluetoothSynchronousLink::HandleAcceptConnectionCompleteL(TInt aErr, TSCOType aSCOType) + { + FTRACE(FPrint(_L("CBluetoothSynchronousLink::HandleAcceptConnectionCompleteL(aErr %d)"), aErr)); + + if (aErr==KErrNone) + { + iSCOTypes = (TUint8)aSCOType; + + TSockAddr sockAddr; + if (aSCOType & ESCO) + { + iBTSynchronousLinkAccepterESCO->Cancel(); + SCOSocket().RemoteName(sockAddr); + } + else + { + iBTSynchronousLinkAccepterSCO->Cancel(); + ESCOSocket().RemoteName(sockAddr); + } + + if(sockAddr.Family() == KBTAddrFamily) + { + TBTSockAddr& btSockAddr = static_cast(sockAddr); // subclasses of TSockAddr are forbidden to add members + TBTDevAddr da = btSockAddr.BTAddr(); + TInt linkState = LinkUp(da); + + __ASSERT_ALWAYS((linkState == KErrNone), Panic(EBasebandFailedConnect)); + + iBTSynchronousLinkBaseband->PreventPark(); + iBTSynchronousLinkBaseband->CatchEvents(); + UpdateLinkParams(aSCOType); + } + else + { + // reading RemoteName has failed, probably socket state is already closed + // for example after quick disconnection initiated from remote side + aErr = KErrDisconnected; + } + } + else + { + iNegotiatedLink = TBTeSCOLinkParams(0, 0, 0, 0); + } + +#ifdef __FLOGGING__ + TRAPD(err, Notifier().HandleAcceptConnectionCompleteL(aErr)); + FTRACE(FPrint(_L("Accept upcall to link owner returned %d"), err)); + User::LeaveIfError(err); +#else + Notifier().HandleAcceptConnectionCompleteL(aErr); +#endif + + ListeningSCOSocket().Close(); + ListeningESCOSocket().Close(); + } + + +void CBluetoothSynchronousLink::HandleSendCompleteL(TInt aErr) + { + Notifier().HandleSendCompleteL(aErr); + } + + +void CBluetoothSynchronousLink::HandleReceiveCompleteL(TInt aErr) + { + Notifier().HandleReceiveCompleteL(aErr); + } + + +MBluetoothSynchronousLinkNotifier& CBluetoothSynchronousLink::Notifier() + { + return *iNotifier; + } + + +RSocket& CBluetoothSynchronousLink::ListeningSCOSocket() + { + return iListeningSCOSocket; + } + + +RSocket& CBluetoothSynchronousLink::ListeningESCOSocket() + { + return iListeningESCOSocket; + } + +RSocket& CBluetoothSynchronousLink::SCOSocket() + { + return iSCOSocket; + } + +RSocket& CBluetoothSynchronousLink::ESCOSocket() + { + return iESCOSocket; + } + +RSocketServ& CBluetoothSynchronousLink::SocketServer() + { + return iSockServer; + } + + +RBTBaseband& CBluetoothSynchronousLink::Baseband() + { + return iBaseband; + } + + +void CBluetoothSynchronousLink::UpdateLinkParams(TSCOType aSCOType) + { + TPckgBuf options; + + TInt err = KErrNotSupported; + if(aSCOType == EeSCO) + { + err = ESCOSocket().GetOpt(EeSCOExtOptions, KSolBtESCO, options); + } + if (err) return; + + iNegotiatedLink.iLatency = options().iLatency; + iNegotiatedLink.iBandwidth = options().iBandwidth; + iNegotiatedLink.iRetransmissionEffort = options().iRetransmissionEffort; + iNegotiatedLink.iCoding = options().iCoding; + } + + +TInt CBluetoothSynchronousLink::LinkUp(TBTDevAddr aAddr) + { + __ASSERT_ALWAYS((iBaseband.SubSessionHandle() == 0), Panic(EBasebandAlreadyConnected)); + return iBaseband.Open(SocketServer(), aAddr); + } + + +void CBluetoothSynchronousLink::LinkDown() + { + iBaseband.Close(); + __ASSERT_ALWAYS((iBaseband.SubSessionHandle() == 0), Panic(EBasebandFailedDisconnect)); + } + + +//========================================================== +//Active Object Helpers +// + +// +//for CBluetoothSynchronousLink +// +CBTSynchronousLinkBaseband* CBTSynchronousLinkBaseband::NewL(CBluetoothSynchronousLink& aParent) + { + CBTSynchronousLinkBaseband* self = new(ELeave) CBTSynchronousLinkBaseband(aParent); + CleanupStack::PushL(self); + self->ConstructL(); + CleanupStack::Pop(self); + return self; + } + + +CBTSynchronousLinkBaseband::CBTSynchronousLinkBaseband(CBluetoothSynchronousLink& aParent) + : CActive(CActive::EPriorityStandard), iParent(aParent) + {} + + +void CBTSynchronousLinkBaseband::ConstructL() + { + CActiveScheduler::Add(this); + } + + +CBTSynchronousLinkBaseband::~CBTSynchronousLinkBaseband() + { + StopAll(); + } + +void CBTSynchronousLinkBaseband::PreventPark() + { + iParent.Baseband().PreventLowPowerModes(EParkMode); + } + + +void CBTSynchronousLinkBaseband::CatchEvents() + { + if (!IsActive()) + { + iParent.Baseband().ActivateNotifierForOneShot(iEvent, iStatus, ENotifySCOLinkDown|ENotifyPhysicalLinkDown); + SetActive(); + } + } + + +void CBTSynchronousLinkBaseband::RunL() + { + FLOG(_L("CBTSynchronousLinkBaseband caught disconnection event")); + iParent.HandleDisconnectionCompleteL(iStatus.Int()); + } + + +TInt CBTSynchronousLinkBaseband::RunError(TInt /*aError*/) + { + // Swallow the error + return KErrNone; + } + + +void CBTSynchronousLinkBaseband::StopAll() + { + iParent.Baseband().AllowLowPowerModes(EParkMode); + Cancel(); + } + + +void CBTSynchronousLinkBaseband::DoCancel() + { + iParent.Baseband().CancelNextBasebandChangeEventNotifier(); + } + + +//-- +CBTSynchronousLinkAttacher* CBTSynchronousLinkAttacher::NewL(CBluetoothSynchronousLink& aParent, TSCOType aSCOType) + { + CBTSynchronousLinkAttacher* self = new (ELeave) CBTSynchronousLinkAttacher(aParent, aSCOType); + CleanupStack::PushL(self); + self->ConstructL(); + CleanupStack::Pop(self); + return self; + } + + +CBTSynchronousLinkAttacher::CBTSynchronousLinkAttacher(CBluetoothSynchronousLink& aParent, TSCOType aSCOType) + : CActive(CActive::EPriorityStandard), iParent(aParent), iSCOType(aSCOType) + { + } + +void CBTSynchronousLinkAttacher::ConstructL() + { + CActiveScheduler::Add(this); + } + +CBTSynchronousLinkAttacher::~CBTSynchronousLinkAttacher() + { + Cancel(); + } + +void CBTSynchronousLinkAttacher::AttachSCOLink(TBTSockAddr& aSockAddr) + { + __ASSERT_ALWAYS(!IsActive(), Panic(EUnfinishedBusiness)); + + FLOG(_L("CBTSynchronousLinkAttacher attaching sync link")); + + if(iSCOType == ESCO) + { + iParent.SCOSocket().Connect(aSockAddr, iStatus); + } + else + { + iParent.ESCOSocket().Connect(aSockAddr, iStatus); + } + + SetActive(); + } + +void CBTSynchronousLinkAttacher::RunL() + { + iParent.HandleSetupConnectionCompleteL(iStatus.Int(), iSCOType); + } + +TInt CBTSynchronousLinkAttacher::RunError(TInt /*aError*/) + { + // Swallow the error + return KErrNone; + } + + +void CBTSynchronousLinkAttacher::DoCancel() + { + FLOG(_L("CBTSynchronousLinkAttacher cancel attach sync link")); + if (iSCOType == ESCO) + { + iParent.SCOSocket().CancelConnect(); + } + else + { + iParent.ESCOSocket().CancelConnect(); + } + } + + +//-- +CBTSynchronousLinkDetacher* CBTSynchronousLinkDetacher::NewL(CBluetoothSynchronousLink& aParent, TSCOType aSCOType) + { + CBTSynchronousLinkDetacher* self = new (ELeave) CBTSynchronousLinkDetacher(aParent, aSCOType); + CleanupStack::PushL(self); + self->ConstructL(); + CleanupStack::Pop(self); + return self; + } + + +CBTSynchronousLinkDetacher::CBTSynchronousLinkDetacher(CBluetoothSynchronousLink& aParent, TSCOType aSCOType) + : CActive(CActive::EPriorityStandard), iParent(aParent), iSCOType(aSCOType) + { + } + +void CBTSynchronousLinkDetacher::ConstructL() + { + CActiveScheduler::Add(this); + } + +CBTSynchronousLinkDetacher::~CBTSynchronousLinkDetacher() + { + Cancel(); + } + +void CBTSynchronousLinkDetacher::DetachSCOLink(const TDesC8& aDesOut, TDes8& aDesIn) + { + __ASSERT_ALWAYS(!IsActive(), Panic(EUnfinishedBusiness)); + + FTRACE(FPrint(_L("CBTSynchronousLinkDetacher detach sync link (0x%08x)"), &iParent)); + + if(iSCOType == ESCO) + { + iParent.SCOSocket().Shutdown(RSocket::ENormal, aDesOut, aDesIn, iStatus); + } + else + { + iParent.ESCOSocket().Shutdown(RSocket::ENormal, aDesOut, aDesIn, iStatus); + } + + SetActive(); + } + +void CBTSynchronousLinkDetacher::RunL() + { + FTRACE(FPrint(_L("CBTSynchronousLinkDetacher detached sync link (0x%08x)"), &iParent)); + iParent.HandleDisconnectionCompleteL(iStatus.Int()); + } + +TInt CBTSynchronousLinkDetacher::RunError(TInt /*aError*/) + { + // Swallow the error + return KErrNone; + } + + +void CBTSynchronousLinkDetacher::DoCancel() +// +//not possible. +// + { + FTRACE(FPrint(_L("CBTSynchronousLinkDetacher cancel detach sync link (0x%08x) -- no-op"), &iParent)); + } + + +//-- +CBTSynchronousLinkAccepter* CBTSynchronousLinkAccepter::NewL(CBluetoothSynchronousLink& aParent, TSCOType aSCOType) + { + CBTSynchronousLinkAccepter* self = new (ELeave) CBTSynchronousLinkAccepter(aParent, aSCOType); + CleanupStack::PushL(self); + self->ConstructL(); + CleanupStack::Pop(self); + return self; + } + + +CBTSynchronousLinkAccepter::CBTSynchronousLinkAccepter(CBluetoothSynchronousLink& aParent, TSCOType aSCOType) + : CActive(CActive::EPriorityStandard), iParent(aParent), iSCOType(aSCOType) + { + } + +void CBTSynchronousLinkAccepter::ConstructL() + { + CActiveScheduler::Add(this); + } + +CBTSynchronousLinkAccepter::~CBTSynchronousLinkAccepter() + { + Cancel(); + } + +void CBTSynchronousLinkAccepter::Accept(RSocket& aSocket) + + { + __ASSERT_ALWAYS(!IsActive(), Panic(EUnfinishedBusiness)); + + FLOG(_L("CBTSynchronousLinkAccepter accept sync link")); + if (iSCOType == ESCO) + { + aSocket.Accept(iParent.SCOSocket(), iStatus); + } + else + { + aSocket.Accept(iParent.ESCOSocket(), iStatus); + } + SetActive(); + } + + +void CBTSynchronousLinkAccepter::RunL() +// +//When logical socket has connected (only async bit), +//opens baseband socket. +// + { + FLOG(_L("CBTSynchronousLinkAccepter accepted sync link")); + iParent.HandleAcceptConnectionCompleteL(iStatus.Int(), iSCOType); + } + +TInt CBTSynchronousLinkAccepter::RunError(TInt /*aError*/) + { + // Swallow the error + return KErrNone; + } + + +void CBTSynchronousLinkAccepter::DoCancel() + { + FLOG(_L("CBTSynchronousLinkAccepter cancel accept sync link")); + if (iSCOType == ESCO) + { + iParent.ListeningSCOSocket().CancelAccept(); + } + else + { + iParent.ListeningESCOSocket().CancelAccept(); + } + } + + +//-- +CBTSynchronousLinkSender* CBTSynchronousLinkSender::NewL(CBluetoothSynchronousLink& aParent, TSCOType aSCOType) + { + CBTSynchronousLinkSender* self = new (ELeave) CBTSynchronousLinkSender(aParent, aSCOType); + CleanupStack::PushL(self); + self->ConstructL(); + CleanupStack::Pop(self); + return self; + } + + +CBTSynchronousLinkSender::CBTSynchronousLinkSender(CBluetoothSynchronousLink& aParent, TSCOType aSCOType) + : CActive(CActive::EPriorityStandard), iParent(aParent), iSCOType(aSCOType) + { + } + +void CBTSynchronousLinkSender::ConstructL() + { + CActiveScheduler::Add(this); + } + +CBTSynchronousLinkSender::~CBTSynchronousLinkSender() + { + Cancel(); + } + + +void CBTSynchronousLinkSender::SendSCOData(const TDesC8& aSCOData) + { + __ASSERT_ALWAYS(!IsActive(), Panic(EUnfinishedBusiness)); + +#ifdef STACK_SCO_DATA + + if (iSCOType == ESCO) + { + iParent.SCOSocket().Write(aSCOData, iStatus); + } + else + { + iParent.ESCOSocket().Write(aSCOData, iStatus); + } + + SetActive(); +#else + (void)aSCOData; + + // As transferring data through the HCI is not supported in this configuration, + // complete our own request with Not Supported. + // Uses event completion to keep sync/async behaviour consistent between + // configurations. + TRequestStatus* status = &iStatus; + iStatus = KRequestPending; + SetActive(); + User::RequestComplete(status, KErrNotSupported); +#endif + } + +void CBTSynchronousLinkSender::RunL() + { + iParent.HandleSendCompleteL(iStatus.Int()); + } + +TInt CBTSynchronousLinkSender::RunError(TInt /*aError*/) + { + // Swallow the error + return KErrNone; + } + + +void CBTSynchronousLinkSender::DoCancel() + { +#ifdef STACK_SCO_DATA + + if(iSCOType == ESCO) + { + iParent.SCOSocket().CancelWrite(); + } + else + { + iParent.ESCOSocket().CancelWrite(); + } + +#endif + // !STACK_SCO_DATA will have already completed the request, but Cancel() will + // swallow callback. + } + + +//-- +CBTSynchronousLinkReceiver* CBTSynchronousLinkReceiver::NewL(CBluetoothSynchronousLink& aParent, TSCOType aSCOType) + { + CBTSynchronousLinkReceiver* self = new (ELeave) CBTSynchronousLinkReceiver(aParent, aSCOType); + CleanupStack::PushL(self); + self->ConstructL(); + CleanupStack::Pop(self); + return self; + } + + +CBTSynchronousLinkReceiver::CBTSynchronousLinkReceiver(CBluetoothSynchronousLink& aParent, TSCOType aSCOType) + : CActive(CActive::EPriorityStandard), iParent(aParent), iSCOType(aSCOType) + { + } + +void CBTSynchronousLinkReceiver::ConstructL() + { + CActiveScheduler::Add(this); + } + +CBTSynchronousLinkReceiver::~CBTSynchronousLinkReceiver() + { + Cancel(); + } + +void CBTSynchronousLinkReceiver::ReadSCOData(TDes8& aDesc) + + { + __ASSERT_ALWAYS(!IsActive(), Panic(EUnfinishedBusiness)); + +#ifdef STACK_SCO_DATA + + if(iSCOType == ESCO) + { + iParent.SCOSocket().Read(aDesc, iStatus); + } + else + { + iParent.ESCOSocket().Read(aDesc, iStatus); + } + + SetActive(); +#else + (void)aDesc; + + // As transferring data through the HCI is not supported in this configuration, + // complete our own request with Not Supported. + // Uses event completion to keep sync/async behaviour consistent between + // configurations. + TRequestStatus* status = &iStatus; + iStatus = KRequestPending; + SetActive(); + User::RequestComplete(status, KErrNotSupported); +#endif + } + + +void CBTSynchronousLinkReceiver::RunL() + { + iParent.HandleReceiveCompleteL(iStatus.Int()); + } + +TInt CBTSynchronousLinkReceiver::RunError(TInt /*aError*/) + { + // Swallow the error + return KErrNone; + } + + +void CBTSynchronousLinkReceiver::DoCancel() + { +#ifdef STACK_SCO_DATA + + if (iSCOType == ESCO) + { + iParent.SCOSocket().CancelRead(); + } + else + { + iParent.ESCOSocket().CancelRead(); + } + +#endif + // !STACK_SCO_DATA will have already completed the request, but Cancel() will + // swallow callback. + }