--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/bluetooth/btcomm/src/states.cpp Fri Jan 15 08:13:17 2010 +0200
@@ -0,0 +1,1788 @@
+// Copyright (c) 1997-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:
+// BTComm state machine state implementation.
+//
+//
+
+#include <bluetooth/logger.h>
+#include <cs_port.h>
+#include "btcomm.h"
+#include "btstate.h"
+#include "btcommactive.h"
+#include <bt_sock.h>
+#include "sdpkey.h"
+#include "sdpclientcsy.h"
+#include <e32test.h>
+#include <btmanclient.h>
+#include "btcommutil.h"
+
+#ifdef __FLOG_ACTIVE
+_LIT8(KLogComponent, LOG_COMPONENT_BT_COMM);
+#endif
+
+#define BAD_BTCOMM_EVENT PanicInState(EBTCommBadEventForThisState);
+
+
+
+TBTPortState::TBTPortState(CBTPortStateFactory* aParent)
+ : iFactory(aParent)
+/**
+ TBTPortState c'tor.
+ TBTPortState is the abstract base class that all CSY state
+ objects inherit from. However, it is not a 'C' class but
+ a 'T' class which is slightly bogus but the best way to
+ implement the base state for the pattern.
+**/
+ {
+ LOG_FUNC
+ }
+
+TBTPortState::~TBTPortState()
+ {
+ LOG_FUNC
+ }
+
+/**
+Calls the appropriate panic function to encode the panic
+code with the current state identifier.
+@param aPanic The panic code that the state is panicking with.
+*/
+void TBTPortState::PanicInState(TBTCommPanic aPanic) const
+ {
+ LOG_FUNC
+ BTCommUtil::Panic(aPanic, iFactory->StateIndex(this));
+ }
+
+// ***** Default state *****
+
+TBTPortDefaultState::TBTPortDefaultState(CBTPortStateFactory* aParent)
+ : TBTPortState(aParent)
+ {
+ LOG_FUNC
+ }
+
+TBTPortDefaultState::~TBTPortDefaultState()
+ {
+ LOG_FUNC
+ }
+
+void TBTPortDefaultState::Open(CBTPortProxy* /*aContext*/)
+ {
+ LOG_FUNC
+ BAD_BTCOMM_EVENT
+ }
+
+void TBTPortDefaultState::Close(CBTPortProxy* /*aContext*/)
+ {
+ LOG_FUNC
+ BAD_BTCOMM_EVENT
+ }
+
+void TBTPortDefaultState::Read(CBTPortProxy* /*aContext*/,TAny* /*aPtr*/,TInt /*aLength*/)
+/**
+ TBTPortDefaultState Read.
+**/
+ {
+ LOG_FUNC
+ BAD_BTCOMM_EVENT
+ }
+
+void TBTPortDefaultState::Write(CBTPortProxy* /*aContext*/,TAny* /*aPtr*/,TInt /*aLength*/)
+/**
+ TBTPortDefaultState Write.
+**/
+ {
+ LOG_FUNC
+ BAD_BTCOMM_EVENT
+ }
+
+void TBTPortDefaultState::ReadCancel(CBTPortProxy* /*aContext*/)
+ {
+ LOG_FUNC
+ BAD_BTCOMM_EVENT
+ }
+
+void TBTPortDefaultState::WriteCancel(CBTPortProxy* /*aContext*/)
+ {
+ LOG_FUNC
+ BAD_BTCOMM_EVENT
+ }
+
+void TBTPortDefaultState::DoRunL(CBTPortProxy* /*aContext*/)
+ {
+ LOG_FUNC
+ BAD_BTCOMM_EVENT
+ }
+
+void TBTPortDefaultState::DoCancel(CBTPortProxy* /*aContext*/)
+ {
+ LOG_FUNC
+ BAD_BTCOMM_EVENT
+ }
+
+void TBTPortDefaultState::DoLockedAction(CBTPortProxy* /*aContext*/)
+ {
+ LOG_FUNC
+ BAD_BTCOMM_EVENT
+ }
+
+void TBTPortDefaultState::DoWriteCompleted(CBTPortProxy* /*aContext*/,TInt /*aError*/)
+ {
+ LOG_FUNC
+ BAD_BTCOMM_EVENT
+ }
+
+void TBTPortDefaultState::DoReadCompleted(CBTPortProxy* /*aContext*/,TInt aError)
+ {
+ // because we managed to get reopened before destruction
+ // and this event sources from the reader's request completing
+ // after a read cancel was issued from the closing.
+ LOG_FUNC
+ if(aError==KErrCancel) {return;}
+
+ BAD_BTCOMM_EVENT
+ }
+
+
+void TBTPortDefaultState::Error(CBTPortProxy* aContext,TInt aError)
+/**
+ This will call each state's error loging and move the CSY to the error state.
+*/
+ {
+ LOG_FUNC
+ LogStateError(aContext,aError);
+ aContext->iLastError=aError; // record this before we lose context
+ aContext->MoveToErrorState();
+ }
+
+
+// ****** Common Pre-Connection I/O Base State *****
+
+TBTPortCommonBaseState::TBTPortCommonBaseState(CBTPortStateFactory* aParent)
+ :TBTPortDefaultState(aParent)
+ {
+ LOG_FUNC
+ }
+
+void TBTPortCommonBaseState::Read(CBTPortProxy* aContext,TAny* aPtr, TInt aLength)
+ {
+ LOG_FUNC
+
+ if(aLength)
+ {
+ aContext->iClientReadPtr=aPtr;
+ aContext->iClientReadLength=aLength;
+ aContext->iClientRemainderToRead=aLength;
+#ifdef _DEBUG
+ aContext->iReadsPending++;
+#endif
+ }
+ else
+ {
+ //zero length read, complete it immediately
+ aContext->iPort->ReadCompleted(KErrNone);
+ }
+ }
+
+void TBTPortCommonBaseState::Write(CBTPortProxy* aContext,TAny* aPtr, TInt aLength)
+ {
+ LOG_FUNC
+
+ aContext->iClientWritePtr=aPtr;
+ aContext->iClientWriteLength=aLength;
+ aContext->iClientLengthWrittenSoFar=aLength;
+ }
+
+void TBTPortCommonBaseState::WriteCancel(CBTPortProxy* aContext)
+/**
+ Cancel initial Write if there was one or do nothing.
+*/
+ {
+ LOG_FUNC
+
+ if(aContext->iClientWritePtr)
+ {
+ aContext->iClientWritePtr=NULL;
+ aContext->iClientWriteLength=0;
+ aContext->iClientLengthWrittenSoFar=0;
+ aContext->iPort->WriteCompleted(KErrCancel);
+ }
+ }
+
+void TBTPortCommonBaseState::ReadCancel(CBTPortProxy* aContext)
+/**
+ Cancel initial Read if there was one or do nothing.
+*/
+ {
+ LOG_FUNC
+
+ if(aContext->iClientReadPtr)
+ {
+ aContext->iClientReadPtr=NULL;
+ aContext->iClientReadLength=0;
+ aContext->iClientRemainderToRead=0;
+#ifdef _DEBUG
+ aContext->iReadsPending--;
+#endif
+ aContext->iPort->ReadCompleted(KErrCancel);
+ }
+ }
+
+void TBTPortCommonBaseState::Close(CBTPortProxy* aContext)
+ {
+ LOG_FUNC
+
+ aContext->StopReader();
+
+ //check to see if we had any client requests cached and cancel them
+ if(aContext->iClientReadPtr)
+ {
+ ReadCancel(aContext);
+ }
+ if(aContext->iClientWritePtr)
+ {
+ WriteCancel(aContext);
+ }
+
+ aContext->SetState(iFactory->GetState(CBTPortStateFactory::EClosing));
+ }
+
+// ***** Idle state *****
+
+TBTPortStateIdle::TBTPortStateIdle(CBTPortStateFactory* aParent)
+ :TBTPortCommonBaseState(aParent)
+ {
+ LOG_FUNC
+ }
+
+TBTPortStateIdle::~TBTPortStateIdle()
+ {
+ LOG_FUNC
+ }
+
+void TBTPortStateIdle::Open(CBTPortProxy* aContext)
+/**
+ This function is the entry point for the BT CSY state machine.
+ The first thing we will do is to pretend we opened a BTCOMM connection.
+ The reason for this is to avoid any deadlock across the C32-ESock-ETel
+ trilogy.
+ **/
+ {
+ LOG_FUNC
+ aContext->CancelShutDownTimer(); // just in case we were going down before this call
+ }
+
+void TBTPortStateIdle::Close(CBTPortProxy* aContext)
+/**
+ A call to this method will cancel any pending requests.
+ It will also cancel any transition to any other state.
+ */
+ {
+ LOG_FUNC
+
+ aContext->Cancel();// any transition
+
+ //check to see if we had any client requests cached and cancel them
+ ReadCancel(aContext);
+ WriteCancel(aContext);
+ }
+
+
+void TBTPortStateIdle::Read(CBTPortProxy* aContext,TAny* aPtr,TInt aLength)
+/**
+ Caches the Read() for completion after the connection is setup, starts the protocol and state machine.
+*/
+ {
+ LOG_FUNC
+ LOG(_L("**TBTPortStateIdle::Read -- Queueing one**"));
+ TBTPortCommonBaseState::Read(aContext,aPtr,aLength);
+ // Connect to ESock
+ SockServConnect(aContext);
+ }
+
+void TBTPortStateIdle::Write(CBTPortProxy* aContext,TAny* aPtr,TInt aLength)
+/**
+ Caches the Write() for completion after the connection is setup, starts the protocol and state machine.
+*/
+ {
+ LOG_FUNC
+ LOG(_L("**TBTPortStateIdle::Write -- Queueing one**"));
+ TBTPortCommonBaseState::Write(aContext,aPtr,aLength);
+ // Connect to ESock
+ SockServConnect(aContext);
+ }
+
+
+void TBTPortStateIdle::DoRunL(CBTPortProxy* /*aContext*/)
+/**
+ Will be called only after we decided to close in the Discovering state.
+ This is a NoOp method.
+ */
+ {
+ LOG_FUNC
+ }
+
+void TBTPortStateIdle::SockServConnect(CBTPortProxy* aContext)
+/**
+ Makes an asynchronous attempt to connect to ESock and moves to the next state.
+**/
+ {
+ LOG_FUNC
+ aContext->iSockServConnector->SockServConnect(aContext->iStatus);
+
+ // now move to the next state where we pre-load the protocol
+ aContext->SetState(iFactory->GetState(CBTPortStateFactory::ELoadingProtocol));
+ aContext->SetActive();
+ }
+
+void TBTPortStateIdle::LogStateError(CBTPortProxy* /*aContext*/,TInt /*aError*/)
+ {
+ LOG_FUNC
+ }
+
+// ***** Loading Protocol state *****
+
+TBTPortStateLoadingProtocol::TBTPortStateLoadingProtocol(CBTPortStateFactory* aParent)
+ :TBTPortCommonBaseState(aParent), iClosePending(EFalse)
+ {
+ LOG_FUNC
+ }
+
+TBTPortStateLoadingProtocol::~TBTPortStateLoadingProtocol()
+ {
+ LOG_FUNC
+ }
+
+void TBTPortStateLoadingProtocol::Read(CBTPortProxy* aContext,TAny* aPtr, TInt aLength)
+ {
+ LOG_FUNC
+
+ // Cache the read ready for when our connection is up.
+ if(aLength)
+ {
+ aContext->iClientReadPtr=aPtr;
+ aContext->iClientReadLength=aLength;
+ aContext->iClientRemainderToRead=aLength;
+#ifdef _DEBUG
+ aContext->iReadsPending++;
+#endif
+ }
+ else
+ {
+ //zero length read, complete it immediately
+ aContext->iPort->ReadCompleted(KErrNone);
+ }
+
+ // If we were planning on closing this reopens us - we've cached the read as
+ // normal, just unset the close pending flag and we can carry on setting up
+ // the connection as normal.
+ if(!iClosePending)
+ {
+ iClosePending = EFalse;
+ }
+ }
+
+void TBTPortStateLoadingProtocol::Write(CBTPortProxy* aContext,TAny* aPtr, TInt aLength)
+ {
+ LOG_FUNC
+
+ // Cache the write ready for when our connection is up.
+ aContext->iClientWritePtr=aPtr;
+ aContext->iClientWriteLength=aLength;
+ aContext->iClientLengthWrittenSoFar=aLength;
+
+ // If we were planning on closing this reopens us - we've cached the write as
+ // normal, just unset the close pending flag and we can carry on setting up
+ // the connection as normal.
+ if(iClosePending)
+ {
+ iClosePending = EFalse;
+ }
+ }
+
+void TBTPortStateLoadingProtocol::Close(CBTPortProxy* aContext)
+/**
+ A call to this method will cancel any pending requests.
+ It will also cancel any transition to any other state.
+ */
+ {
+ LOG_FUNC
+
+ // If we're in this state our connection attempt on ESock hasn't
+ // completed yet. We can't transition to the closing state yet
+ // because we don't have the handle for our ESock session which
+ // we need to close. When the connection request completes we'll
+ // do the transition, unless there's a Read or Write before then,
+ // when we'll allow the connection set up to continue.
+
+ //check to see if we had any client requests cached and cancel them
+ ReadCancel(aContext);
+ WriteCancel(aContext);
+
+ // Set the flag so we know where to go when the Esock connect has
+ // completed
+ iClosePending = ETrue;
+ }
+
+void TBTPortStateLoadingProtocol::DoRunL(CBTPortProxy* aContext)
+/**
+ Will be called only after we decided to close in the Discovering state.
+ This is a NoOp method.
+ */
+ {
+ LOG_FUNC
+
+ if (aContext->iStatus != KErrNone)
+ {
+ Error(aContext, aContext->iStatus.Int()); // couldn't connect to esock
+ return;
+ }
+
+ // If we're waiting for the ESock handle to allow us to close, transition
+ // to the closing state now, otherwise continue connection set up.
+ if(iClosePending)
+ {
+ aContext->SetState(iFactory->GetState(CBTPortStateFactory::EClosing));
+ aContext->SetActive();
+ TRequestStatus* pStatus = &(aContext->iStatus);
+ User::RequestComplete(pStatus, KErrNone);
+
+ // Reset this incase we come back through the state machine later
+ iClosePending = EFalse;
+ }
+ else
+ {
+ StartProtocol(aContext);
+ }
+ }
+
+void TBTPortStateLoadingProtocol::StartProtocol(CBTPortProxy* aContext)
+/**
+ Makes an asynchronous attempt to invoke RSocketServ::StartProtocol() and moves to the next state.
+**/
+ {
+ LOG_FUNC
+ // now async load Bluetooth RFComm protocol to stop comm port open thread lock.
+ RSocketServ &sockServ = aContext->iSockServ;
+ LOG1(_L("TBTPortStateLoadingProtocol::Start L2CAP Protocol OK, iSockServ=0x%x"),sockServ.Handle());
+ sockServ.StartProtocol(KBTAddrFamily,KSockSeqPacket,KL2CAP,aContext->iStatus);
+ // now move to the next state were the results will be handled
+ aContext->SetState(iFactory->GetState(CBTPortStateFactory::EDiscovering));
+ aContext->SetActive();
+ }
+
+void TBTPortStateLoadingProtocol::LogStateError(CBTPortProxy* /*aContext*/,TInt /*aError*/)
+ {
+ LOG_FUNC
+ }
+
+// ***** Discovering state *****
+
+TBTPortStateDiscovering::TBTPortStateDiscovering(CBTPortStateFactory* aParent)
+ : TBTPortCommonBaseState(aParent)
+ {
+ LOG_FUNC
+ }
+
+TBTPortStateDiscovering::~TBTPortStateDiscovering()
+ {
+ LOG_FUNC
+ }
+
+
+void TBTPortStateDiscovering::DoRunL(CBTPortProxy* aContext)
+/**
+ Find-out the port's corresponding device and settings.
+ This is done by looking at the registry default settings for this
+ BTComm port.
+**/
+ {
+ LOG_FUNC
+ TInt ret=aContext->iStatus.Int();
+ if (ret!=KErrNone)
+ {// could not start Bluetooth protocol
+ // must error here so that the user notices this
+ Error(aContext,ret);
+ LOG(_L(" Could not load Bluetooth protocol !"));
+ // do tidy up here
+ return;
+ }
+ else
+ {// we have successfully started Bluetooth, lets discover the settings for this port
+ aContext->iDefaultService.SetPort(aContext->iPortNo);
+ ret=aContext->iPortSettings.Get(aContext->iDefaultService);
+
+ if(ret!=KErrNone)
+ {
+ LOG(_L(" Could not find default device !"));
+ Error(aContext,ret);
+ return;
+ }
+ aContext->iBdaddr=aContext->iDefaultService.BDAddr();
+
+ // In the context of RFCOMM, at this stage, we would be looking
+ // to do an SDP service search query on the remote RFCOMM service
+ // which means opening an SDP sap first which is a synchronous
+ // action so has to be done as a locked action.
+ aContext->StartLocker();
+ }
+ }
+
+void TBTPortStateDiscovering::DoLockedAction(CBTPortProxy* aContext)
+/**
+ Do an SDP query for the remote service port number as a synchronous (locked) action.
+ At this point we have locked the session. We now need to open
+ our NetDB and then follow that with our async state change action
+ which is an SDP query.
+**/
+ {
+ LOG_FUNC
+ // need to open the netdb, fire off an async SDP Connect Query
+ // and then do the state transition.
+ RNetDatabase& netdb=aContext->iNetDatabase;
+ RSocketServ ss=aContext->iSockServ;
+ // note this needs to be a reference.
+ TInt ret=netdb.Open(ss,KBTAddrFamily,KSDP);
+ // sync action completed ok - now stop locker.
+ aContext->StopLocker();
+ if (ret!=KErrNone)
+ {
+ Error(aContext,ret);
+ return;
+ }
+
+ // if NetDB was opened ok make a note of it for cleanup on closing state
+ aContext->SetNetDbInUse();
+
+ // now queue async action and move into next state
+ // which is an SDP connect query on remote RFCOMM service.
+ TSDPConnectQuery connQ;
+ connQ.iQueryType = KSDPConnectQuery;
+ connQ.iAddr = aContext->iBdaddr;
+ aContext->iSDPRequest.Copy(TSDPConnectBuf(connQ));
+ aContext->iSDPResult.SetMax();
+ netdb.Query(aContext->iSDPRequest,aContext->iSDPResult,aContext->iStatus);
+ aContext->SetActive();
+ aContext->SetState(iFactory->GetState(CBTPortStateFactory::ESDPConnected));
+ }
+
+void TBTPortStateDiscovering::LogStateError(CBTPortProxy* /*aContext*/,TInt /*aError*/)
+/**
+ TBTPortStateDiscovering Error.
+ Reaching this error means that we haven't found a device.
+**/
+ {
+ LOG_FUNC
+ }
+
+// ***** SDP Connected state *****
+
+TBTPortStateSDPConnected::TBTPortStateSDPConnected(CBTPortStateFactory* aParent)
+ : TBTPortCommonBaseState(aParent)
+ {
+ LOG_FUNC
+ }
+
+TBTPortStateSDPConnected::~TBTPortStateSDPConnected()
+ {
+ LOG_FUNC
+ }
+
+void TBTPortStateSDPConnected::DoRunL(CBTPortProxy* aContext)
+/**
+ Entry at this call signals that the SDP connection query has completed.
+ We now need to check the remote port number to connect to before
+ invoking the locker to handle the opening of the port proxy's socket.
+**/
+ {
+ LOG_FUNC
+ TInt ret=aContext->iStatus.Int();
+ if (ret!=KErrNone)
+ {// didn't manage to succeed with SDP Connect query.
+ Error(aContext,ret);
+ LOG(_L("CSY: Could not connect to remote SDP server !"));
+ return;
+ }
+ // we have successfully found the remote SDP server.
+ RNetDatabase& netdb=aContext->iNetDatabase;
+
+ // here we create the SDP service search query.
+ TSDPServiceSearchKey key;
+ key.iQueryType = KSDPServiceQuery;
+ key.iMaxCount = 20;
+ key.iUUID = aContext->iDefaultService.UUID();
+
+ key.iStateLength = 0;
+
+ aContext->iSDPRequest.Copy(TSDPServiceSearchKeyBuf(key));
+ aContext->iSDPServRecordHandle.SetMax();
+ netdb.Query(aContext->iSDPRequest,aContext->iSDPServRecordHandle,aContext->iStatus);
+ aContext->SetState(iFactory->GetState(CBTPortStateFactory::ESDPServiceQuery));
+ aContext->SetActive();
+ }
+
+void TBTPortStateSDPConnected::LogStateError(CBTPortProxy* /*aContext*/,TInt /*aError*/)
+/**
+ Reaching this error means that we haven't found a remote device
+ with the right service level on it. ie. the SDP/IAS query failed.
+**/
+ {
+ LOG_FUNC
+ }
+
+
+// ***** SDP Service Retrieved State *****
+
+TBTPortStateSDPServiceQuery::TBTPortStateSDPServiceQuery(CBTPortStateFactory* aParent)
+ : TBTPortCommonBaseState(aParent)
+ {
+ LOG_FUNC
+ }
+
+TBTPortStateSDPServiceQuery::~TBTPortStateSDPServiceQuery()
+ {
+ LOG_FUNC
+ }
+
+void TBTPortStateSDPServiceQuery::DoRunL(CBTPortProxy* aContext)
+ {
+ LOG_FUNC
+ // we have completed the SDP service search query
+ // get number of record handles in this response
+ TInt ret=aContext->iStatus.Int();
+
+ if (ret!=KErrNone)
+ {// could not start Bluetooth protocol
+ // must error here so that the user notices this
+ Error(aContext,ret);
+ LOG(_L("SDPService query - error."));
+ // do tidy up here
+ return;
+ }
+ else
+ {
+
+ iFactory->iSDPServRecordHandleCount=BigEndian::Get16(&aContext->iSDPServRecordHandle[2]);
+ if (!iFactory->iSDPServRecordHandleCount)
+ {// this is an error - found no matching service records.
+ Error(aContext,KErrNotFound);
+ return;
+ }
+ if (iFactory->iSDPServRecordHandleCount*4 > aContext->iSDPServRecordHandle.Length() - 5)
+ {// this is a check on the length of the response.
+ Error(aContext,KErrUnknown);
+ LOG2(_L("Bad length field %d on results length %d"),
+ iFactory->iSDPServRecordHandleCount*4,aContext->iSDPServRecordHandle.Length());
+ return;
+ }
+
+ LOG(_L("\nFound Service records for UUID 0x03\n")); //add the number of records found
+
+ //now retrieve the ServiceClassIDList from the first ServiceRecordHandle
+ RNetDatabase& netdb=aContext->iNetDatabase;
+
+ TSDPAttributeKey key;
+ key.iQueryType = KSDPAttributeQuery;
+ // suck out the first service record handle.
+ iFactory->iExtractedHandleCount = 1;
+ key.iServiceRecordHandle = BigEndian::Get32(&aContext->iSDPServRecordHandle[4]);
+ key.iMaxLength = 200;
+ key.iRange = EFalse;
+ key.iAttribute = KSdpAttrIdServiceClassIDList;
+ key.iStateLength = 0;
+ aContext->iSDPRequest.Copy(TSDPAttributeKeyBuf(key));
+ aContext->iSDPResult.SetMax();
+ // Now go away and do the SDP attribute request query
+ netdb.Query(aContext->iSDPRequest,aContext->iSDPResult,aContext->iStatus);
+ aContext->SetState(iFactory->GetState(CBTPortStateFactory::ESDPServiceIDListRetrieved));
+ aContext->SetActive();
+ }
+ }
+
+void TBTPortStateSDPServiceQuery::LogStateError(CBTPortProxy* /*aContext*/,TInt /*aError*/)
+/**
+ Reaching this error means that we haven't found a remote device
+ with the right service level on it. ie. the SDP query failed.
+**/
+ {
+ LOG_FUNC
+ }
+
+
+
+// ***** SDP Service ID List Retrieved State *****
+
+TBTPortStateServiceIDListRetrieved::TBTPortStateServiceIDListRetrieved(CBTPortStateFactory* aParent)
+ : TBTPortCommonBaseState(aParent)
+ {
+ LOG_FUNC
+ }
+
+
+TBTPortStateServiceIDListRetrieved::~TBTPortStateServiceIDListRetrieved()
+ {
+ LOG_FUNC
+ }
+
+void TBTPortStateServiceIDListRetrieved::DoRunL(CBTPortProxy* aContext)
+ {
+ LOG_FUNC
+
+ TInt ret=aContext->iStatus.Int();
+ if (ret!=KErrNone)
+ {// could not start Bluetooth protocol
+ // must error here so that the user notices this
+ Error(aContext,ret);
+ LOG(_L("Service ID List error."));
+ // do tidy up here
+ return;
+ }
+ else
+ {
+ RNetDatabase& netdb=aContext->iNetDatabase;
+
+ const TInt KRspAttributeCountSize = 2;
+ const TInt KContStateHeader = 1;
+
+ LOG(_L("Successful Attribute query!!\r\nHere's the result....\n"));
+ FTRACE(FHex(aContext->iSDPResult));
+
+ if (aContext->iSDPResult.Length() < KRspAttributeCountSize+KContStateHeader)
+ {
+ Error(aContext,KErrNotSupported);
+ LOG1(_L("Result is far too short at %d bytes\r\n"),aContext->iSDPResult.Length());
+ return;
+ }
+ // suck out the byte count
+ TUint16 bytecount=BigEndian::Get16(&aContext->iSDPResult[0]);
+ if (aContext->iSDPResult.Length() < KRspAttributeCountSize+bytecount+KContStateHeader)
+ {
+ Error(aContext,KErrNotSupported);
+ return;
+ }
+ if (aContext->iSDPResult[KRspAttributeCountSize+bytecount]!=0)
+ {// continuation state to deal with
+ // fixme - do something here!
+ Error(aContext,KErrNotSupported);
+ LOG(_L("Continuation state to deal with in SDP attr query response!!\r\n"));
+ return;
+ }
+
+ //check for the expected UUID in the ServiceIDList
+
+ CRFCommClass* builder=CRFCommClass::NewL(aContext->iDefaultService.UUID());
+ CleanupStack::PushL(builder);
+ CElementParser* parser = CElementParser::NewL(builder);
+ CleanupStack::PushL(parser);
+ //Parsing simple attribute list
+ TRAPD(err, parser->ParseElementsL(aContext->iSDPResult.Mid(KRspAttributeCountSize,bytecount)));
+ if (err)
+ {
+ LOG1(_L("Parser left with error %d\n"),err);
+ Error(aContext,err);
+ return;
+ }
+
+ TBool getProtocolDesc = builder->InService();
+ CleanupStack::PopAndDestroy(2);
+
+ TSDPAttributeKey key;
+ key.iMaxLength = 200;
+ key.iRange = EFalse;
+ key.iStateLength = 0;
+ key.iQueryType = KSDPAttributeQuery;
+
+ aContext->iSDPResult.Zero();
+ aContext->iSDPResult.SetMax();
+
+ if (getProtocolDesc)
+ {
+ LOG(_L("**UUID found in ServiceClassIDList, moving on to getting Protocol Desc **"));
+
+
+ // suck out the next service record handle.
+ //if there are still some Handles left
+ key.iServiceRecordHandle = BigEndian::Get32(&aContext->
+ iSDPServRecordHandle[4*iFactory->iExtractedHandleCount]); //this handle
+ key.iAttribute = KSdpAttrIdProtocolDescriptorList;
+ aContext->iSDPRequest.Copy(TSDPAttributeKeyBuf(key));
+ // Now go away and do the SDP attribute request query
+ // i.e search for the first service record, to find what protocols are supported
+ netdb.Query(aContext->iSDPRequest,aContext->iSDPResult,aContext->iStatus);
+ aContext->SetState(iFactory->GetState(CBTPortStateFactory::ESDPAttribListRetrieved));
+ aContext->SetActive();
+ }
+ else if ( iFactory->iExtractedHandleCount < (iFactory->iSDPServRecordHandleCount) )
+ {
+ LOG(_L("**UUID not found in ServiceClassIDList, inquire of next ServiceRecordHandle **"));
+
+ // suck out the next service record handle.
+ //if there are still some Handles left
+ iFactory->iExtractedHandleCount++;
+ key.iServiceRecordHandle = BigEndian::Get32(&aContext->
+ iSDPServRecordHandle[4*iFactory->iExtractedHandleCount]); //next handle
+ key.iAttribute = KSdpAttrIdServiceClassIDList;
+ aContext->iSDPRequest.Copy(TSDPAttributeKeyBuf(key));
+ // Now go away and do the SDP attribute request query
+ netdb.Query(aContext->iSDPRequest,aContext->iSDPResult,aContext->iStatus);
+ //state is remaining unchanged so no need to call aContext->SetState()
+ aContext->SetActive();
+
+ }
+ else //UUID has not been found & there are no more ServiceRecordHandles
+ {
+ LOG(_L("**UUID not found and no ServiceRecordHandles left **"));
+ Error(aContext,KErrNotFound);
+ }
+ }
+ }
+
+
+void TBTPortStateServiceIDListRetrieved::LogStateError(CBTPortProxy* /*aContext*/, TInt /*aError*/)
+ {
+ LOG_FUNC
+ }
+
+
+// ***** SDP Attribute List Retrieved State *****
+
+TBTPortStateSDPAttributeListRetrieved::TBTPortStateSDPAttributeListRetrieved(CBTPortStateFactory* aParent)
+ : TBTPortCommonBaseState(aParent)
+ {
+ LOG_FUNC
+ }
+
+TBTPortStateSDPAttributeListRetrieved::~TBTPortStateSDPAttributeListRetrieved()
+ {
+ LOG_FUNC
+ }
+
+
+void TBTPortStateSDPAttributeListRetrieved::DoRunL(CBTPortProxy* aContext)
+/**
+ The SDP attribute request query completed and here we parse the results.
+*/
+ {
+ LOG_FUNC
+ TInt ret=aContext->iStatus.Int();
+ if (ret!=KErrNone)
+ {// could not start Bluetooth protocol
+ // must error here so that the user notices this
+ Error(aContext,ret);
+ LOG(_L("AttributeListRetrieved - error."));
+ // do tidy up here
+ return;
+ }
+ else
+ {
+ const TInt KContStateHeader = 1;
+ const TInt KRspAttributeCountSize = 2;
+
+ if (aContext->iSDPResult.Length() < KRspAttributeCountSize+KContStateHeader)
+ {
+ Error(aContext,KErrNotSupported);
+ LOG1(_L("Result is far too short at %d bytes\r\n"),aContext->iSDPResult.Length());
+ return;
+ }
+ // suck out the byte count
+ TUint16 bytecount=BigEndian::Get16(&aContext->iSDPResult[0]);
+ if (aContext->iSDPResult.Length() < KRspAttributeCountSize+bytecount+KContStateHeader)
+ {
+ Error(aContext,KErrNotSupported);
+ return;
+ }
+ if (aContext->iSDPResult[KRspAttributeCountSize+bytecount]!=0)
+ {// continuation state to deal with
+ // fixme - do something here!
+ Error(aContext,KErrNotSupported);
+ LOG(_L("Continuation state to deal with in SDP attr query response!!\r\n"));
+ return;
+ }
+ LOG(_L("Successful Attribute query!!\r\nHere's the result....\n"));
+ FTRACE(FHex(aContext->iSDPResult));
+
+ // Parse the attribute list
+
+ CRFCommAttribs* builder=CRFCommAttribs::NewL(NULL);
+ CleanupStack::PushL(builder);
+ CElementParser* parser = CElementParser::NewL(builder);
+ CleanupStack::PushL(parser);
+ //Parsing simple attribute list
+ TUint rem=0;
+
+ TRAPD(err,rem=parser->ParseElementsL(aContext->iSDPResult.Mid(KRspAttributeCountSize,bytecount)));
+ if (err)
+ {
+ LOG1(_L("Parser left with error %d\n"),err);
+ Error(aContext,err);
+ // cleanup since the leave/error does not get propagated and we return.
+ CleanupStack::PopAndDestroy(2);
+ return;
+ }
+ else
+ {
+ LOG1(_L("Parser returned %d\n"),rem);
+ }
+
+ (void)(rem != KMaxTUint); // keep the compiler happy by taking rem as an r-value in urel
+
+ // get the remote rfcomm port number to connect to.
+
+ err = builder->GetRFCommPort(aContext->iRemoteRfcommPortNumber); // channel number
+ if (err != KErrNone)
+ {
+ LOG1(_L("Error %d determining RFCOMM server channel\n"), err);
+ Error(aContext,err);
+ }
+
+ CleanupStack::PopAndDestroy(2); // getting rid of parser and builder.
+
+ // do an RFCOMM connect on the remote RFCOMM port
+ // which means opening the socket first which is a synchronous
+ // action so has to be done as a locked action.
+ aContext->StartLocker();
+ }
+ }
+
+void TBTPortStateSDPAttributeListRetrieved::DoLockedAction(CBTPortProxy* aContext)
+/**
+ Get a locked connection to the Socket server/RFComm and attempt a connection to the remote RFComm port.
+**/
+ {
+ LOG_FUNC
+
+ TPckgBuf<TUint8> aSig;
+ RSocket& sock=aContext->iSocket;
+ RSocketServ ss=aContext->iSockServ;
+ // note this needs to be a reference.
+ TInt ret=sock.Open(ss,KBTAddrFamily,KSockStream,KRFCOMM);
+ if (ret!=KErrNone)
+ {
+ Error(aContext,ret);
+ LOG(_L("**TBTPortStateSDPAttributeListRetrieved could NOT open RFComm socket connection **"));
+ return;
+ }
+ aSig = KModemSignalDV; // KModemSignalRTC and KModemSignalRTR have been turned off
+ sock.SetOpt(KRFCOMMErrOnMSC, KSolBtRFCOMM, aSig);
+ // sync action completed ok - now stop locker.
+ aContext->StopLocker();
+ // now queue async action and move into next state
+ // which is an RFComm connect on remote port.
+ aContext->iAddr.SetBTAddr(aContext->iBdaddr);
+ aContext->iAddr.SetPort(aContext->iRemoteRfcommPortNumber);
+
+ TBTServiceSecurity security;
+ security.SetAuthentication(aContext->iDefaultService.IsSecuritySet()); // note: does this need to be updated to be SSP aware?
+
+ TBool doEncrypt = aContext->iDefaultService.IsEncryptionSet();
+ security.SetEncryption(doEncrypt);
+
+ aContext->iAddr.SetSecurity(security);
+
+ sock.Connect(aContext->iAddr, aContext->iStatus);
+ aContext->SetActive();
+ aContext->SetState(iFactory->GetState(CBTPortStateFactory::EConnecting));
+ }
+
+
+void TBTPortStateSDPAttributeListRetrieved::LogStateError(CBTPortProxy* /*aContext*/,TInt /*aError*/)
+/**
+ Signals a problem with the retrieval of the remote SDP attribute List.
+**/
+ {
+ LOG_FUNC
+ }
+
+
+// ***** Connecting state *****
+
+TBTPortStateConnecting::TBTPortStateConnecting(CBTPortStateFactory* aParent)
+ : TBTPortCommonBaseState(aParent)
+ {
+ LOG_FUNC
+ }
+
+TBTPortStateConnecting::~TBTPortStateConnecting()
+ {
+ LOG_FUNC
+ }
+
+void TBTPortStateConnecting::DoRunL(CBTPortProxy* aContext)
+/**
+ At this point we have finally got our connection response.
+ If successful then we are in the open state and can begin
+ to freely handle reads and writes. The only state transition
+ that can take us away from the open state is a close.
+**/
+ {
+ LOG_FUNC
+ TInt ret=aContext->iStatus.Int();
+ if (ret!=KErrNone)
+ {// didn't manage to succeed with connect.
+ Error(aContext,ret);
+ LOG(_L("Didn't manage to succeed with connect"));
+ return;
+ }
+ else
+ {// we have successfully connected!!!!
+ // so we can transition to the open state.
+ // first we check if we have any queued writes
+ // to do and we also queue a read.
+ if (aContext->iClientWritePtr)
+ {
+ if (aContext->iClientWriteLength>KBTCOMMSendBufferLength)
+ {
+ aContext->iClientLengthWrittenSoFar=KBTCOMMSendBufferLength;
+ aContext->iMoreSendsToCome=ETrue;
+ }
+ //else see TBTPortStateConnecting::Write(..)
+
+ TPtr8& sendptr=aContext->iSendBufPtr;
+ sendptr.SetLength(0);
+ TPtr8 ptr((TUint8*)sendptr.Ptr(),0,aContext->iClientLengthWrittenSoFar);
+ if(aContext->iClientLengthWrittenSoFar>0)
+ { // to avoid panicing on zero length write to from client
+ aContext->iPort->IPCRead(aContext->iClientWritePtr,ptr,0);
+ }
+ sendptr.Append(ptr);
+ aContext->iSendBufPtr.SetLength(aContext->iClientLengthWrittenSoFar);
+ aContext->StartWriter();
+ }
+
+ if((aContext->iClientReadLength==0)&&(aContext->iClientReadPtr))
+ {// someone asked for a zero length read
+ aContext->iPort->ReadCompleted(KErrNone);
+ }
+ aContext->StartReader();
+ aContext->SetState(iFactory->GetState(CBTPortStateFactory::EOpen));
+ }
+ }
+
+void TBTPortStateConnecting::LogStateError(CBTPortProxy* /*aContext*/,TInt /*aError*/)
+/**
+ TBTPortStateConnecting Error.
+ At this point our RFComm connect has failed for some reason.
+**/
+ {
+ LOG_FUNC
+ }
+
+// ***** Open state *****
+
+TBTPortStateOpen::TBTPortStateOpen(CBTPortStateFactory* aParent)
+ : TBTPortCommonBaseState(aParent)
+ {
+ LOG_FUNC
+ }
+
+TBTPortStateOpen::~TBTPortStateOpen()
+ {
+ LOG_FUNC
+ }
+
+void TBTPortStateOpen::Close(CBTPortProxy* aContext)
+/**
+ TBTPortStateOpen Close.
+ This function is called when the user has invoked a Close on
+ the CSY session.
+ It will stop the reader and begin the shutdown and close of the socket
+ session.
+**/
+ {
+ LOG_FUNC
+
+ aContext->StopReader();
+
+ // the close() call is a direct call to C32 from the client (in ESock probably)
+ // thus can pre-empt both DoLockedAction() and DoRunL(). Consequently
+ // if someone issued a WriteCancel(), just before the call
+ // this needs to be serviced within a DoLockedAction. But since Shutdown
+ // starts here, this will not be needed. Therefore we will NOT stop the locker
+ // here and will check wether a cancel is pending in the Closing state's
+ // DoLockedAction().
+
+ aContext->SetState(iFactory->GetState(CBTPortStateFactory::EClosing));
+ aContext->SetActive();
+ TRequestStatus* pStatus = &(aContext->iStatus);
+ User::RequestComplete(pStatus, KErrNone);
+ }
+
+void TBTPortStateOpen::Read(CBTPortProxy* aContext,TAny* aPtr,TInt aLength)
+/**
+ The client side has requested that a read call be completed.
+ Moreover if the reader (for the socket reads) was stoped it
+ will be restarted from here if the low watermark for the CBTPortProxy
+ read-in circular buffer was reached.
+**/
+ {
+ LOG_FUNC
+#ifdef _DEBUG
+ aContext->iReadsPending++;
+#endif
+
+ aContext->iClientReadPtr=aPtr;
+ aContext->iClientReadLength=aLength;
+ aContext->iClientRemainderToRead=aLength;
+ aContext->iClientWriteOffset=0; // to indicate the offset in the buf
+ // for the first write back to client
+ if(aLength)
+ {
+ // need to check if there's anything in the circular buffer.
+ HandleIPCWriteToClient(aContext);
+ }
+ else
+ {
+ aContext->iPort->ReadCompleted(KErrNone);
+ }
+ // unfortunately the state has to decide whether the reader should be
+ // kicked into action again or not, because there are states like the
+ // Idle one where this is irrelevant.
+ if(aContext->ReadInBufferLowWatermarkReached())
+ {
+ // and the reader was previously stoped
+ if(!aContext->iPortReader->IsReading())
+ {
+ aContext->StartReader();
+ }
+ }
+ }
+
+void TBTPortStateOpen::DoReadCompleted(CBTPortProxy* aContext,TInt aError)
+/**
+ New data arrived from the socket to C32
+ This method is called by the CBTportProxy when new data arrive
+ from the socket.
+
+ This function first needs to see if there's anything that needs to
+ be written client side. HandleIPCWriteToClient() does the actual
+ work of invoking the c32 IPCWrite.
+
+ Then it checks to see if more reads to the socket should be queued or
+ whether the CBTPortProxy read-in buffer is filling up.
+**/
+ {
+ LOG_FUNC
+#ifdef _DEBUG
+ aContext->iReadsPending--;
+#endif
+
+ if ((aError!=KErrNone)&&(aError!=KErrCancel))
+ {// we need to break out of here without requeueing the reader.
+ aContext->iPort->ReadCompleted(aError);
+ return;
+ }
+ if (aContext->iClientReadPtr)
+ {// we have a read queued -
+ // need to see if we can do IPCWrite client side
+ HandleIPCWriteToClient(aContext);
+ }
+
+ // check to see if we've reached or surpased our watermark
+ if(aContext->ReadInBufferHighWatermarkReached())
+ {
+ aContext->StopReader();
+ return;
+ }
+ // else
+ aContext->StartReader();
+
+ }
+
+void TBTPortStateOpen::HandleIPCWriteToClient(CBTPortProxy* aContext)
+/**
+ This function works out how much of the circular buffer to write back to the client.
+ The c32 ReadCompleted() function is only invoked when the complete read has been done.
+**/
+ {
+ LOG_FUNC
+ TInt dataLenInBuf=aContext->iCircularReadBuf->Count();
+ if (dataLenInBuf==0)
+ {
+ return; //nothing to do
+ }
+
+ TInt bytesToCopy=0;
+ TPtr8& readptr=aContext->iReadBufPtr; // incoming data buffer
+ readptr.SetLength(0);
+ TUint8* ptr=CONST_CAST(TUint8*,(readptr.Ptr()));
+ TInt clientMaxLen=aContext->iClientReadLength;
+
+ // This should cover terminated reads, plain reads and some read one
+ // or more cases
+ if(dataLenInBuf>=clientMaxLen)
+ {
+ // complete if possible any terminated reads for both larger or smaller
+ // client buffers than ours
+ if(aContext->iTerminatedReads)
+ { // try to find the terminated string
+ // bytesToCopy here can be greater than the clientMaxLen
+ // or can be -1 so we have to check
+ bytesToCopy=aContext->iCircularReadBuf->ScanForTerminator(aContext->iTerm);
+ if((bytesToCopy>clientMaxLen) || (bytesToCopy == KErrNotFound))
+ {
+ bytesToCopy=clientMaxLen;
+ }
+ // else the terminated reads complete with no terminator because
+ // the client buffer can be filled completely.
+ }
+ else
+ {
+ // The plain reads where the client has a smaller buffer should
+ // complete here for the same reason.
+ // Read one or more would complete anyway for this case.
+ bytesToCopy=clientMaxLen;
+ }
+ }
+ // This should cover Receive one or more for the rest of the cases
+
+ //will allow the full coverage of the high watermark case even if we do not do bounded reads
+//#define BTCOMM_TEST_HIGH_WATERMARK
+#ifndef BTCOMM_TEST_HIGH_WATERMARK
+ else if(aContext->iClientReadOneOrMore)
+ {
+ // if we reached this point it means that datLenInBuf<clientMaxLen
+ bytesToCopy=dataLenInBuf;
+ }
+#endif
+ // Our client can handle lots of data, more than we can
+ else if(KBTCOMMCircularBufferLength<clientMaxLen)
+ {
+ if(aContext->iClientRemainderToRead<=dataLenInBuf)
+ { // then this is the last chunk of a multi write to client
+ if(aContext->iTerminatedReads)
+ { // try to find the terminated string
+ // bytesToCopy here can be -1
+ // hence we have to check
+ bytesToCopy=aContext->iCircularReadBuf->ScanForTerminator(aContext->iTerm);
+ if(bytesToCopy==KErrNotFound)
+ {// ternminator was requested but not found... what the heck
+ bytesToCopy=aContext->iClientRemainderToRead;
+ }
+ }
+ else
+ {
+ // this is the last chunk to be read for this client read
+ // write as much as needed to the client
+ bytesToCopy=aContext->iClientRemainderToRead;
+ }
+ }
+ else if(aContext->ReadInBufferHighWatermarkReached()) // to minimise IPC
+ {
+ if(aContext->iTerminatedReads)
+ { // try to find the terminated string
+ // bytesToCopy here can be -1
+ // hence we have to check
+ bytesToCopy=aContext->iCircularReadBuf->ScanForTerminator(aContext->iTerm);
+ if(bytesToCopy==KErrNotFound)
+ {// terminator was not found... but
+ // we have almost filled our buffer; write what we've got to the client
+ bytesToCopy=aContext->iCircularReadBuf->Remove(ptr,dataLenInBuf);
+ __ASSERT_DEBUG(bytesToCopy==dataLenInBuf,PanicInState(EBTCommOpenStateWriteToClientPossibleLossOfData));
+
+ // fill their buf as much as we can and continue
+ readptr.SetLength(bytesToCopy);
+ TInt offset=aContext->iClientWriteOffset;
+ aContext->iPort->IPCWrite(aContext->iClientReadPtr,readptr,offset);
+ aContext->iClientRemainderToRead-=bytesToCopy;
+ aContext->iClientWriteOffset+=bytesToCopy; // offset for next write to client
+ return; //our work is done wait for the next
+ }
+ // else the terminator was found hence copy data up to the
+ // terminator and complete the read
+ }
+ else // if no terminated reads were requested but the high watermark was reached
+ {
+ // copy what we have across and keep track of the offset
+ bytesToCopy=aContext->iCircularReadBuf->Remove(ptr,dataLenInBuf);
+ __ASSERT_DEBUG(bytesToCopy==dataLenInBuf,PanicInState(EBTCommOpenStateWriteToClientPossibleLossOfData));
+
+ // fill their buf as much as we can and continue
+ readptr.SetLength(bytesToCopy);
+ TInt offset=aContext->iClientWriteOffset;
+ aContext->iPort->IPCWrite(aContext->iClientReadPtr,readptr,offset);
+ aContext->iClientRemainderToRead-=bytesToCopy;
+ aContext->iClientWriteOffset+=bytesToCopy; // offset for next write to client
+ return; //our work is done wait for the next
+ }
+ } //matches else if(aContext->ReadInBufferHighWatermarkReached())
+ // if the high watermark was not reached but we do terminated reads
+#ifndef BTCOMM_TEST_HIGH_WATERMARK
+ else if (aContext->iTerminatedReads)
+ {
+ bytesToCopy=aContext->iCircularReadBuf->ScanForTerminator(aContext->iTerm);
+ }
+#endif
+ }
+ // complete if possible any terminated reads for smaller
+ // client buffers than ours
+ else if(aContext->iTerminatedReads)
+ { // try to find the terminated string
+ // bytesToCopy here cannot be greater than the clientMaxLen
+ bytesToCopy=aContext->iCircularReadBuf->ScanForTerminator(aContext->iTerm);
+ }
+
+ if(bytesToCopy>KErrNone)
+ {
+ // do the copy to the client buffer
+ TInt offset=aContext->iClientWriteOffset;
+ readptr.SetLength(bytesToCopy);
+ bytesToCopy=aContext->iCircularReadBuf->Remove(ptr,bytesToCopy);
+ aContext->iPort->IPCWrite(aContext->iClientReadPtr,readptr,offset);
+
+ // reset the offsets and counters and complete the read
+ aContext->iClientReadLength=0;
+ aContext->iClientReadPtr=0;
+ aContext->iClientRemainderToRead=0;
+ aContext->iClientWriteOffset=0;
+
+ aContext->iPort->ReadCompleted(KErrNone);
+ }
+ }
+
+void TBTPortStateOpen::Write(CBTPortProxy* aContext,TAny* aPtr,TInt aLength)
+/**
+ TBTPortState Open.
+ Store the client side ptr and length and then signal
+ start write immediately.
+**/
+ {
+ LOG_FUNC
+ // this is again due to the fact that we have to be asynchronous to avoid
+ // deadlocks with (mostly ESock) other comms components.
+ if(aContext->IsWriteCancelPending())
+ {
+ aContext->iPort->WriteCompleted(KErrNotReady);
+ //aContext->DoWriteCancel(); a deadlock example !
+ return;
+ }
+
+ LOG(_L("**TBTPortStateOpen::Write**"));
+ aContext->iClientWritePtr=aPtr;
+ aContext->iClientWriteLength=aLength;
+ aContext->iClientLengthWrittenSoFar=aLength;
+
+ if (aLength>KBTCOMMSendBufferLength)
+ {
+ aContext->iClientLengthWrittenSoFar=KBTCOMMSendBufferLength;
+ aContext->iMoreSendsToCome=ETrue;
+ }
+ TPtr8& sendptr=aContext->iSendBufPtr;
+ sendptr.SetLength(0);
+ // now read the client side data
+ TPtr8 ptr((TUint8*)sendptr.Ptr(),0,aContext->iClientLengthWrittenSoFar);
+ aContext->iPort->IPCRead(aPtr,ptr,0);
+
+ sendptr.Append(ptr);
+ aContext->iSendBufPtr.SetLength(aContext->iClientLengthWrittenSoFar);
+ aContext->StartWriter();
+ }
+
+void TBTPortStateOpen::DoWriteCompleted(CBTPortProxy* aContext,TInt aError)
+/**
+ TBTPortStateOpen DoWriteCompleted.
+ If there is no error here, at this point the write
+ has successfully completed and
+ can be signalled complete back to the client code.
+**/
+ {
+ LOG_FUNC
+ if (aContext->iMoreSendsToCome)
+ {// we need to keep going
+ LOG(_L("**TBTPortStateOpen::DoWriteCompleted - more to come**"));
+
+ TInt lentowrite=aContext->iClientWriteLength-aContext->iClientLengthWrittenSoFar;
+ TInt lensofar=aContext->iClientLengthWrittenSoFar;
+ if (lentowrite>KBTCOMMSendBufferLength)
+ {// still more to come
+ lentowrite=KBTCOMMSendBufferLength;
+ aContext->iClientLengthWrittenSoFar+=KBTCOMMSendBufferLength;
+ aContext->iMoreSendsToCome=ETrue;
+ }
+ else
+ {
+ aContext->iClientLengthWrittenSoFar+=lentowrite;
+ aContext->iMoreSendsToCome=EFalse;
+ }
+ TPtr8 sendptr=aContext->iSendBufPtr;
+ sendptr.SetLength(0);
+ // read the client side data
+ TPtr8 ptr((TUint8*)sendptr.Ptr(),0,lentowrite);
+ aContext->iPort->IPCRead(aContext->iClientWritePtr,ptr,lensofar);
+
+ sendptr.Append(ptr);
+ aContext->iSendBufPtr.SetLength(lentowrite);
+ aContext->StartWriter();
+ }
+ else
+ {// we have finished - can signal to client and 0 the client stuff.
+ LOG(_L("**TBTPortStateOpen::DoWriteCompleted - finished**"));
+ aContext->iMoreSendsToCome=EFalse;
+ aContext->iClientLengthWrittenSoFar=0;
+ aContext->iClientWritePtr=0;
+ aContext->iClientWriteLength=0;
+ aContext->iPort->WriteCompleted(aError);
+ }
+ }
+
+void TBTPortStateOpen::WriteCancel(CBTPortProxy* aContext)
+/**
+ It will complete the cancelation to the client and then request it from the server.
+*/
+ {
+ LOG_FUNC
+ // do say yes you cancelled and then cancel if necessary
+ aContext->iPort->WriteCompleted(KErrCancel);
+ aContext->SetWriteCancelPending();
+ aContext->StartLocker();
+ }
+
+void TBTPortStateOpen::ReadCancel(CBTPortProxy* aContext)
+/**
+ It will complete the cancelation to the client.
+*/
+ {
+ LOG_FUNC
+#ifdef _DEBUG
+ aContext->iReadsPending--;
+#endif
+ // do say yes you cancelled and then cancel if necessary
+ aContext->iPort->ReadCompleted(KErrCancel);
+ // Read Cancel does not need to be propagated to the underlying socket
+ // since CSY has a ring buffer which is filled with inbound data regardless
+ // of what the upper app is up to with its RComm
+ }
+
+void TBTPortStateOpen::DoLockedAction(CBTPortProxy* aContext)
+ {
+ LOG_FUNC
+ TInt ret=aContext->iStatus.Int();
+ if (ret!=KErrNone)
+ {
+ aContext->StopLocker();
+ Error(aContext,ret);
+ return;
+ }
+ if(aContext->IsWriteCancelPending())
+ {
+ aContext->DoWriteCancel(); // the locked action for which we came in here
+ }
+ else //default handle - stray event must be caught
+ {
+ ((TBTPortDefaultState*)this)->DoLockedAction(aContext);
+ }
+ aContext->StopLocker();
+ //NOTE: we remain in this state
+ }
+
+void TBTPortStateOpen::LogStateError(CBTPortProxy* /*aContext*/,TInt /*aError*/)
+ {
+ LOG_FUNC
+ }
+
+// ***** Closing state *****
+
+TBTPortStateClosing::TBTPortStateClosing(CBTPortStateFactory* aParent)
+ : TBTPortCommonBaseState(aParent)
+ {
+ LOG_FUNC
+ }
+
+TBTPortStateClosing::~TBTPortStateClosing()
+ {
+ LOG_FUNC
+ }
+
+void TBTPortStateClosing::Close(CBTPortProxy* /*aContext*/)
+ {
+ LOG_FUNC
+ }
+
+void TBTPortStateClosing::Read(CBTPortProxy* aContext, TAny* aPtr,TInt aLength)
+/**
+ Read in this state indirectly calls the DoLockedAction to get out and in the state machine again.
+ This is done because of the huge window of opportunity for race conditions
+ while the async timer is ticking before shutdown. Since we need the
+ shutdown timer for the closing to be async (to avoid deadlocks with Etel etc),
+ hence we should close the socket etc and transfer the Read() to the
+ Idle state.
+*/
+ {
+ LOG_FUNC
+
+ LOG(_L("**TBTPortStateClosing Canceling the shutdown timer **"));
+ aContext->CancelShutDownTimer();
+
+ // cache the read
+ aContext->iClientReadPtr=aPtr;
+ aContext->iClientReadLength=aLength;
+ aContext->iClientRemainderToRead=aLength;
+ // close sockets resolver etc
+ aContext->StartLocker(); // next call is to our DoLockedAction(..);
+ // we now know that someone re-opened us while going down so...
+ // transfer the call
+ }
+
+void TBTPortStateClosing::Write(CBTPortProxy* aContext,TAny* aPtr,TInt aLength)
+/**
+ Write in this state indirectly calls the DoLockedAction to get out and in the state machine again.
+ This is done because of the huge window of opportunity for race conditions
+ while the async timer is ticking before shutdown. Since we need the
+ shutdown timer for the closing to be async (to avoid deadlocks with Etel etc),
+ hence we should close the socket etc and transfer the write() to the
+ Idle state.
+*/
+ {
+ LOG_FUNC
+
+ LOG(_L("**TBTPortStateClosing Canceling the shutdown timer **"));
+ aContext->CancelShutDownTimer();
+
+ // cache the write
+ aContext->iClientWritePtr=aPtr;
+ aContext->iClientWriteLength=aLength;
+ aContext->iClientLengthWrittenSoFar=aLength;
+
+ // close sockets resolver etc
+ //next call is in our DoLockedAction(..);
+ aContext->StartLocker();
+ // we now know that someone re-opened us while going down so...
+ // transfer the call
+ }
+
+void TBTPortStateClosing::DoRunL(CBTPortProxy* aContext)
+/**
+ At this point our socket shutdown has returned.
+ We can now close down the host resolver, netdb and finally
+ the session to the socket server itself. These are all
+ synchronous actions so must be handled by the locker.
+
+ But we need to defer the actual completion of the final closing for later
+ in order to give some time for other components like Etel to close first.
+ Note the values used here are arbitrary and should be considered a
+ hacked fix :-(.
+
+ NOTE: the next transition is in TBTPortStateClosing::DoLockedAction
+ unless the shutdown timer is cancelled by a new read or write
+**/
+ {
+ LOG_FUNC
+ TInt ret=aContext->iStatus.Int();
+ if (ret!=KErrNone)
+ {// didn't manage to succeed with socket shutdown somehow.
+ LOG(_L("**TBTPortStateClosing Closing with an error !**"));
+ aContext->StartShutdownTimerL();// this will eventually call aContext->StartLocker()
+ }
+ else
+ {// we have successfully shutdown the socket.
+ aContext->StartShutdownTimerL();// this will eventually call aContext->StartLocker()
+ }
+ //NOTE: the next transition is in TBTPortStateClosing::DoLockedAction
+ // unless the shutdown timer is cancelled by a new read or write
+ }
+
+void TBTPortStateClosing::DoLockedAction(CBTPortProxy* aContext)
+/**
+ TBTPortStateClosing DoLockedAction.
+ At this point we have obtained the lock hence we are able to undertake
+ the locked actions.
+ This method should be called indirectly by the shutdown timer or due to
+ a previous WriteCancel call at the open state, followed by a
+ Close() call ( which brought as here in this state).
+**/
+ {
+ LOG_FUNC
+ if(aContext->IsWriteCancelPending())
+ {
+ LOG(_L("**TBTPortStateClosing::DoLockedAction -- Servicing Write cancelations first**"));
+ aContext->DoWriteCancel(); // the locked action for which we came in here
+ aContext->StopLocker();
+ return;
+ }
+
+ if(aContext->IsNetDbInUse())
+ {
+ // we need this in the case of cancelation before the netdb.Open()
+ // because the kernel will panic us if it doesn't find the session
+ RNetDatabase& netdb=aContext->iNetDatabase;
+ netdb.Cancel();
+ netdb.Close();
+ aContext->SetNetDbNotInUse();
+ LOG(_L("**TBTPortStateClosing, NetDB session clean-up done **"));
+ }
+
+ // the following immediate shutdown will not stiff C32 and is needed for
+ // the next close to be possible in ESock without it being blocked waiting
+ // for the protocol SAP to be cleaned
+ //
+ // This shutdown will cause any outstanding socket ops to be cancelled so
+ // we can safely call aContext->Cancel() afterwards.
+ if(aContext->iSocket.SubSessionHandle())
+ {// Make sure we had managed to open the socket connection before we arriveed here
+ aContext->iSocket.Shutdown(RSocket::EImmediate,aContext->iStatus);
+ User::WaitForRequest(aContext->iStatus);
+ }
+
+ // Do BT CSY specific locked action.
+ aContext->iSocket.Close();
+
+ // We can only make synch calls to ESock from within the locker, otherwise
+ // we risk deadlock. This means we can't put any Cancel()s that may end
+ // up in ESock in the port proxy's DoCancel() unless we guarantee that Cancel()
+ // can only be called from within the locker. Instead we ensure we've done
+ // all the necessary cancelling before calling Cancel().
+ aContext->Cancel();
+
+
+ // We should never transition to the closing state whilst waiting for
+ // an ESock connection, unless I've bugged the state machine.
+ __ASSERT_DEBUG(!aContext->iSockServConnector->IsActive(), PanicInState(EBTCommCloseWhileWaitingForSockServHandle));
+
+ if(aContext->iSockServ.Handle())
+ {
+ aContext->iSockServ.Close();
+ }
+ aContext->StopLocker(); // sync actions completed ok - now stop locker.
+
+ // we do this just before destruction, just in case someone has reopened us
+ aContext->SetState(iFactory->GetState(CBTPortStateFactory::EIdle));
+ // UGLY
+ aContext->DestructContext(); // will not do anything if we got reopened
+ }
+
+void TBTPortStateClosing::DoWriteCompleted(CBTPortProxy * /*aContext*/,TInt /*aError*/)
+ {
+ LOG_FUNC
+ }
+
+void TBTPortStateClosing::DoReadCompleted(CBTPortProxy * /*aContext*/, TInt /*aError*/)
+ {
+ LOG_FUNC
+ }
+
+void TBTPortStateClosing::LogStateError(CBTPortProxy * /*aContext*/,TInt /*aError*/)
+ {
+ LOG_FUNC
+ }
+
+
+// Error State
+
+TBTPortErrorState::TBTPortErrorState(CBTPortStateFactory* aParent)
+ : TBTPortState(aParent)
+ {
+ LOG_FUNC
+ }
+
+TBTPortErrorState::~TBTPortErrorState()
+ {
+ LOG_FUNC
+ }
+
+void TBTPortErrorState::Open(CBTPortProxy* /*aContext*/)
+ {
+ LOG_FUNC
+ }
+
+void TBTPortErrorState::Read(CBTPortProxy* aContext,TAny* /*aPtr*/,TInt /*aLength*/)
+/**
+ Notify the client about this recent error.
+*/
+ {
+ LOG_FUNC
+ aContext->iPort->ReadCompleted(aContext->iLastError);
+ }
+
+void TBTPortErrorState::Write(CBTPortProxy* aContext,TAny* /*aPtr*/,TInt /*aLength*/)
+/**
+ Notify the client about this recent error.
+*/
+ {
+ LOG_FUNC
+ aContext->iPort->WriteCompleted(aContext->iLastError);
+ }
+
+void TBTPortErrorState::Close(CBTPortProxy* aContext)
+ {
+ LOG_FUNC
+ aContext->SetState(iFactory->GetState(CBTPortStateFactory::EClosing));
+
+ // check to see if the error came before we managed to do the locked action
+ // ..and cancel the pending locked action then
+ if(aContext->IsLockerOn())
+ {
+ aContext->StopLocker();
+ }
+ aContext->SetActive();
+ TRequestStatus* pStatus = &(aContext->iStatus);
+ User::RequestComplete(pStatus, KErrNone);
+ }
+
+void TBTPortErrorState::DoRunL(CBTPortProxy* /*aContext*/)
+ {
+ LOG_FUNC
+ }
+
+void TBTPortErrorState::DoCancel(CBTPortProxy* /*aContext*/)
+ {
+ LOG_FUNC
+ }
+
+void TBTPortErrorState::WriteCancel(CBTPortProxy* /*aContext*/)
+ {
+ LOG_FUNC
+ }
+
+void TBTPortErrorState::ReadCancel(CBTPortProxy* /*aContext*/)
+ {
+ LOG_FUNC
+ }
+
+void TBTPortErrorState::DoLockedAction(CBTPortProxy* /*aContext*/)
+ {
+ LOG_FUNC
+ BAD_BTCOMM_EVENT
+ }
+
+void TBTPortErrorState::DoWriteCompleted(CBTPortProxy* /*aContext*/,TInt /*aError*/)
+ {
+ LOG_FUNC
+ }
+
+void TBTPortErrorState::DoReadCompleted(CBTPortProxy* /*aContext*/,TInt /*aError*/)
+ {
+ LOG_FUNC
+ }
+
+void TBTPortErrorState::Error(CBTPortProxy* aContext,TInt aError)
+/**
+ Error will be the first method to be called in entering this state.
+ So here Error will check to see if there are any Queued reads, which
+ it will immediately complete.
+
+ This should only be called on moving from any other state to the error
+ state and not otherwise.
+ */
+ {
+ LOG_FUNC
+ if (aContext->iClientReadPtr)
+ { // this means that a read was queued already so we need to signal
+ // the error immediately
+ aContext->iPort->ReadCompleted(aError);
+ }
+ if (aContext->iClientWritePtr)
+ { // same for write
+ aContext->iPort->WriteCompleted(aError);
+ }
+ }
+