bluetooth/btstack/linkmgr/SyncSap.cpp
changeset 0 29b1cd4cb562
child 23 5b153be919d4
equal deleted inserted replaced
-1:000000000000 0:29b1cd4cb562
       
     1 // Copyright (c) 2003-2009 Nokia Corporation and/or its subsidiary(-ies).
       
     2 // All rights reserved.
       
     3 // This component and the accompanying materials are made available
       
     4 // under the terms of "Eclipse Public License v1.0"
       
     5 // which accompanies this distribution, and is available
       
     6 // at the URL "http://www.eclipse.org/legal/epl-v10.html".
       
     7 //
       
     8 // Initial Contributors:
       
     9 // Nokia Corporation - initial contribution.
       
    10 //
       
    11 // Contributors:
       
    12 //
       
    13 // Description:
       
    14 // Implements the Synchronous SAP class
       
    15 // 
       
    16 //
       
    17 
       
    18 #include <bluetooth/logger.h>
       
    19 #include <bt_sock.h>
       
    20 #include "SCOSAP.h"
       
    21 #include "physicallinksmanager.h"
       
    22 #include "physicallinks.h"
       
    23 #include "hcifacade.h"
       
    24 #include "linkutil.h"
       
    25 #include "linkmuxer.h"
       
    26 
       
    27 #ifdef __FLOG_ACTIVE
       
    28 _LIT8(KLogComponent, LOG_COMPONENT_LINKMGR);
       
    29 #endif
       
    30 
       
    31 
       
    32 CBTSynchronousLink::CBTSynchronousLink(CPhysicalLinksManager& aLinksMan, CPhysicalLink* aPhysicalLink, TLinkType aLinkType)
       
    33 : CBTBasebandSAP(aLinksMan, aPhysicalLink), iLinkType(aLinkType)
       
    34 	{
       
    35 	LOG2(_L("Creating sync SAP 0x%08x, type %d"), this, aLinkType);
       
    36 	iState = &aLinksMan.LinkManagerProtocol().
       
    37 			SyncStateFactory().GetState(CSyncLinkStateFactory::EClosed);
       
    38 	}
       
    39 
       
    40 void CBTSynchronousLink::ConstructL()
       
    41 	{
       
    42 	CBTBasebandSAP::ConstructL();
       
    43 	}
       
    44 
       
    45 CBTSynchronousLink::~CBTSynchronousLink()
       
    46 	{
       
    47 	LOG2(_L("Destroying sync SAP 0x%08x, iPhysicalLink 0x%08x"), this, iPhysicalLink);
       
    48 	
       
    49 	/*
       
    50 	The ASSERT below has been commented due to an issue with ESock.
       
    51 
       
    52 	During a normal shutdown, a CBTSynchronousLink will always unbind from the physical link before
       
    53 	calling CanClose(). However automated tests have shown that there is no guarantee that during a 
       
    54 	normal shutdown ESock will wait until the SAP has called CanClose() before deleting it. If
       
    55 	the socket is closed whilst ESock is waiting for the CanClose() it will immediately delete the
       
    56 	SAP (and complete the Shutdown request with KErrCancel). In this case the SAP will still be
       
    57 	bound to a physical link.
       
    58 	
       
    59 	If the ESock issue is rectified, the ASSERT should be reinstated.
       
    60 	
       
    61 	
       
    62 	ASSERT(!iPhysicalLink);
       
    63 	*/
       
    64 	
       
    65 	iState->Deletion(*this);
       
    66 	
       
    67 	ReleaseAndUnbind();
       
    68 	}
       
    69 
       
    70 void CBTSynchronousLink::ReleaseAndUnbind()
       
    71 /**
       
    72 	NB If the physical link is unbound, and the SCO packet type is
       
    73 	already ENoSCOLink, this function will do nothing!
       
    74 */
       
    75 	{
       
    76 	LOG1(_L("CBTSyncLink (0x%08x): Release and unbind"), this);
       
    77 
       
    78 	if(iPhysicalLink)
       
    79 		{
       
    80 		iPhysicalLink->UndoOverridePark();
       
    81 		}
       
    82 	UnbindLink(iLinkType); //sets iPhysicalLink to NULL
       
    83 	}
       
    84 
       
    85 void CBTSynchronousLink::PhysicalLinkChange(const TBTBasebandEventNotification& IF_FLOGGING(aEvent), CPhysicalLink& __DEBUG_ONLY(aPhysicalLink))
       
    86 	{
       
    87 	__ASSERT_DEBUG(&aPhysicalLink == iPhysicalLink, Panic(EBTSCOSAPWrongPhysicalLink));
       
    88 
       
    89 	// spec says should never have got parked with sco link
       
    90 	//__ASSERT_DEBUG(!(aEvent.EventType() & ENotifyParkMode), Panic(EBTSCOSAPExistsWhenParking));
       
    91 	//
       
    92 	// Above ASSERT too strong as races are occuring. Log, but 'ignore'.
       
    93 
       
    94 	#ifdef __FLOG_ACTIVE
       
    95 	if (!(aEvent.EventType() & ENotifyParkMode))
       
    96 		{
       
    97 		LOG(_L("Sync SAP: Park request received with an active eSCO link. Likely to be a race."));
       
    98 		}
       
    99 	#endif
       
   100 	}
       
   101 
       
   102 void CBTSynchronousLink::AutoBind()
       
   103 	/**
       
   104 	   Pick an appropriate local port.
       
   105 	**/
       
   106 	{
       
   107 	}
       
   108 
       
   109 TInt CBTSynchronousLink::SetLocalName(TSockAddr& /*aAddr*/)
       
   110 /**
       
   111 	Override KErrNotSupported in base class.
       
   112 */
       
   113 	{
       
   114 	return KErrNone;
       
   115 	}
       
   116 
       
   117 void CBTSynchronousLink::Start()
       
   118 	{
       
   119 	iState->Start(*this);
       
   120 	}
       
   121 
       
   122 void CBTSynchronousLink::Timeout(TBasebandTimeout aTimeout)
       
   123 	{
       
   124 	iState->Timeout(*this, aTimeout);
       
   125 	}
       
   126 
       
   127 TInt CBTSynchronousLink::SetRemName(TSockAddr& aAddr)
       
   128 	{
       
   129 	TBTSockAddr bbAddr(aAddr);	// convert
       
   130 	iRemoteDev=bbAddr.BTAddr();
       
   131 
       
   132 	return KErrNone;	// to avoid race conditions we check for ACL later
       
   133 	}
       
   134 
       
   135 void CBTSynchronousLink::ActiveOpen()
       
   136 /**
       
   137 	Attach SCO link - the packet type should have been set by this stage
       
   138 **/
       
   139 	{
       
   140 	iState->ActiveOpen(*this);
       
   141 	}
       
   142 
       
   143 void CBTSynchronousLink::DoActiveOpenL()
       
   144 	{
       
   145 	// proceed with creating the SCO link, but first check that phy exists
       
   146 	CPhysicalLink* foundPhysicalLink = iLinksMan.FindPhysicalLink(iRemoteDev);
       
   147 
       
   148 	if (!foundPhysicalLink)
       
   149 		{
       
   150 		// cannot do this until baseband is up...SCO Transports aren't allowed to instantiate
       
   151 		// PHYs
       
   152 		User::Leave(KErrNotReady);
       
   153 		}
       
   154 	else
       
   155 		{
       
   156 		if (foundPhysicalLink->HasSyncLink())
       
   157 			{
       
   158 			User::Leave(KErrInUse);
       
   159 			}
       
   160 		
       
   161 		// go and actually make the link
       
   162 		if(!IsLinkProbablyPossible())
       
   163 			{
       
   164 			User::Leave(KErrOverflow);
       
   165 			}
       
   166 		foundPhysicalLink->OverridePark();
       
   167 		BindLink(iLinkType, *foundPhysicalLink);
       
   168 		
       
   169 		//Initiate a SCO connection with user specified packet types. all types will be allowed
       
   170 		//if SetOption has not been called.
       
   171 		TInt err = MakeConnection(foundPhysicalLink);
       
   172 		if (err)
       
   173 			{
       
   174 			UnbindLink(iLinkType);
       
   175 			User::Leave(err);
       
   176 			}
       
   177 		}
       
   178 	}
       
   179 
       
   180 TInt CBTSynchronousLink::PassiveOpen(TUint aQueSize)
       
   181 /**
       
   182 	Record ourselves as willing to accept a SCO link (possibly of the CoD we want)
       
   183 **/
       
   184 	{
       
   185 	LOG1(_L("CBTSyncLink (0x%08x): PassiveOpen"), this);
       
   186 	return iState->PassiveOpen(*this, aQueSize);
       
   187 	}
       
   188 
       
   189 TUint CBTSynchronousLink::Write(const TDesC8& aData, TUint aOptions, TSockAddr* aAddr)
       
   190 	{
       
   191 	return iState->Write(*this, aData, aOptions, aAddr);
       
   192 	}
       
   193 
       
   194 void CBTSynchronousLink::PacketsSent(THCIConnHandle /*aHandle*/, TUint16 /*aNumPackets*/)
       
   195 	{
       
   196 	// ignore - no need to unblock socket
       
   197 	}
       
   198 
       
   199 TBool CBTSynchronousLink::ConnectRequest(const TBTConnect& aSCOLink, const CPhysicalLink& aPhysicalLink)
       
   200 	{
       
   201 	return iState->ConnectRequest(*this, aSCOLink, aPhysicalLink);
       
   202 	}
       
   203 
       
   204 void CBTSynchronousLink::GetData(TDes8& aDesc,TUint /*aOptions*/,TSockAddr* /*aAddr*/)
       
   205 	{
       
   206 	// return the packet - we only have one - if other stuff has got overwritten
       
   207 	// so be it - we're going for synchronicity...
       
   208 	aDesc = InboundFrame();
       
   209 	}
       
   210 
       
   211 void CBTSynchronousLink::ConnectComplete(const TBTConnect& aSCOLink)
       
   212 	{
       
   213 	TBTSyncConnectOpts SCODefaultOpts(KSCODefaultTransmissionInterval, KSCODefaultRetransmissionWindow,
       
   214 		KSCODefaultRxPacketLength, KSCODefaultTxPacketLength, KSCODefaultAirMode);
       
   215 
       
   216 	SyncConnectComplete(aSCOLink, SCODefaultOpts);
       
   217 	}
       
   218 
       
   219 void CBTSynchronousLink::SyncConnectComplete(const TBTConnect& aSCOLink, const TBTSyncConnectOpts& aSyncOpts)
       
   220 	{
       
   221 	iState->ConnectComplete(*this, aSCOLink, aSyncOpts);
       
   222 	}
       
   223 
       
   224 void CBTSynchronousLink::Disconnection()
       
   225 	{
       
   226 	LOG2(_L("CBTSyncLink (0x%08x): disconnection on handle %d"), this, iHandle);
       
   227 	iState->Disconnection(*this);
       
   228 	}
       
   229 
       
   230 void CBTSynchronousLink::ParentClosing()
       
   231 	{
       
   232 	iState->ParentClosing(*this);
       
   233 	}
       
   234 
       
   235 void CBTSynchronousLink::DataReceived(THCIConnHandle __DEBUG_ONLY(aConnH), TUint8 /*aIgnore*/, const TDesC8& aData)
       
   236 	{
       
   237 	__ASSERT_DEBUG(aConnH == Handle(), Panic(EBTSCOSAPWrongSCOSAP));
       
   238 	iState->DataReceived(*this, aData);
       
   239 	}
       
   240 
       
   241 void CBTSynchronousLink::Error(TInt aErr)
       
   242 	{
       
   243 	iState->Error(*this, aErr);
       
   244 	}
       
   245 
       
   246 void CBTSynchronousLink::Shutdown(TCloseType aClose)
       
   247 /**
       
   248 	Detach SCO
       
   249 **/
       
   250 	{
       
   251 	LOG2(_L("CBTSyncLink (0x%08x): shutdown no data on handle %d"), this, iHandle);
       
   252 	iState->Shutdown(*this, aClose);
       
   253 	}
       
   254 
       
   255 void CBTSynchronousLink::Shutdown(TCloseType aClose, const TDesC8& /*aDisconnectionData*/)
       
   256 /**
       
   257 	Detach SCO - ESock seems to send us through this way 
       
   258 	whatever the client request has been. The overload above probably does
       
   259 	not need implementing.
       
   260 **/
       
   261 	{
       
   262 	LOG2(_L("CBTSyncLink (0x%08x): shutdown on handle %d"), this, iHandle);
       
   263 	iState->Shutdown(*this, aClose);
       
   264 	}
       
   265 
       
   266 TBool CBTSynchronousLink::IsIdle() const
       
   267 	{
       
   268 	return iState->IsIdle();
       
   269 	}
       
   270 
       
   271 TBool CBTSynchronousLink::IsOpen() const
       
   272 	{
       
   273 	return iState->IsOpen();
       
   274 	}
       
   275 
       
   276 TInt CBTSynchronousLink::CommonShutdown()
       
   277 	{
       
   278 	// not common to all baseband SAPs - only for closing SCO links at moment
       
   279 	// since PhysicalLink object looks after the PHY, and ACL goes up/down with that
       
   280 	TRAPD(err, iLinksMan.HCIFacade().DisconnectL(Handle(), static_cast<THCIErrorCode>(KActiveDisconnectReason)));
       
   281 	return err;
       
   282 	}
       
   283 
       
   284 TBool CBTSynchronousLink::ConnectPending() const
       
   285 	{
       
   286 	return iState->IsConnectPending();
       
   287 	}
       
   288 		
       
   289 
       
   290 
       
   291 //----------------------------------------------------------------------------------
       
   292 // STATE FACTORY
       
   293 //----------------------------------------------------------------------------------
       
   294 
       
   295 CSyncLinkStateFactory* CSyncLinkStateFactory::NewL()
       
   296 	{
       
   297 	CSyncLinkStateFactory* ret=new (ELeave) CSyncLinkStateFactory();
       
   298 	CleanupStack::PushL(ret);
       
   299 	ret->ConstructL();
       
   300 	CleanupStack::Pop();
       
   301 	return ret;
       
   302 	}
       
   303 
       
   304 void CSyncLinkStateFactory::ConstructL()
       
   305 	{
       
   306 	iStates[EClosed]				=new (ELeave) TSCOLinkStateClosed(*this);
       
   307 	iStates[EWaitForSCO]			=new (ELeave) TSCOLinkStateWaitForSCO(*this);
       
   308 	iStates[EListening]				=new (ELeave) TSCOLinkStateListening(*this);
       
   309 	iStates[EAccepting]				=new (ELeave) TSCOLinkStateAccepting(*this);
       
   310 	iStates[EWaitForStart]			=new (ELeave) TSCOLinkStateWaitForStart(*this);
       
   311 	iStates[EWaitForStartError]		=new (ELeave) TSCOLinkStateWaitForStartError(*this);
       
   312 	iStates[EOpen]					=new (ELeave) TSCOLinkStateOpen(*this);
       
   313 	iStates[EClosing]				=new (ELeave) TSCOLinkStateClosing(*this);
       
   314 	}
       
   315 
       
   316 CSyncLinkStateFactory::~CSyncLinkStateFactory()
       
   317 	{
       
   318 	iStates.DeleteAll();
       
   319 	}
       
   320 
       
   321 TSyncLinkState& CSyncLinkStateFactory::GetState(CSyncLinkStateFactory::TSyncLinkStates aState)
       
   322 	{
       
   323 	__ASSERT_DEBUG(iStates[aState],  Panic(ELinkMgrBadSCOState));
       
   324 	return *iStates[aState];
       
   325 	}
       
   326 
       
   327 TInt CSyncLinkStateFactory::StateIndex(const TSyncLinkState* aState) const
       
   328 	{
       
   329 	TInt state;
       
   330 	for (state = 0; state < ESyncLinkMaxState; state++)
       
   331 		{
       
   332 		if (iStates[state] == aState)
       
   333 			{
       
   334 			return state;
       
   335 			}
       
   336 		}
       
   337 	
       
   338 	return KUnknownState;
       
   339 	}
       
   340 
       
   341 
       
   342 
       
   343 
       
   344 //----------------------------------------------------------------------------------
       
   345 // STATES
       
   346 //----------------------------------------------------------------------------------
       
   347 
       
   348 TSyncLinkState::TSyncLinkState(CSyncLinkStateFactory& aFactory)
       
   349 : iFactory(aFactory)
       
   350 	{
       
   351 	}
       
   352 
       
   353 void TSyncLinkState::PanicInState(TLinkPanic aPanic) const
       
   354 	{
       
   355 	Panic(aPanic, iFactory.StateIndex(this));
       
   356 	}
       
   357 
       
   358 void TSyncLinkState::ChangeState(CBTSynchronousLink& aContext, CSyncLinkStateFactory::TSyncLinkStates aState) const
       
   359 	{
       
   360 	aContext.iState->Exit(aContext);
       
   361 
       
   362 #ifdef __FLOG_ACTIVE
       
   363 	TSyncLinkState* state=&iFactory.GetState(aState);
       
   364 	LOG2(_L("SCOLink: State %S -> %S"), &aContext.iState->iName, &state->iName);
       
   365 #endif //__FLOG_ACTIVE
       
   366 	aContext.iState=&iFactory.GetState(aState);
       
   367 
       
   368 	aContext.iState->Enter(aContext);
       
   369 	}
       
   370 
       
   371 void TSyncLinkState::Enter(CBTSynchronousLink& /*aContext*/) const
       
   372 	{
       
   373 	// do nothing
       
   374 	}
       
   375 
       
   376 void TSyncLinkState::Deletion(CBTSynchronousLink& /*aContext*/) const
       
   377 	{
       
   378 	PanicInState(EBTSCOSAPUnexpectedEvent);
       
   379 	}
       
   380 
       
   381 void TSyncLinkState::Exit(CBTSynchronousLink& /*aContext*/) const
       
   382 	{
       
   383 	// do nothing
       
   384 	}
       
   385 
       
   386 void TSyncLinkState::Timeout(CBTSynchronousLink& /*aContext*/, TBasebandTimeout /*aTimeout*/) const
       
   387 	{
       
   388 	PanicInState(EBTSCOSAPUnexpectedEvent);
       
   389 	}
       
   390 
       
   391 void TSyncLinkState::Shutdown(CBTSynchronousLink& /*aContext*/, CServProviderBase::TCloseType /*aCloseType*/) const
       
   392 	{
       
   393 	PanicInState(EBTSCOSAPUnexpectedEvent);
       
   394 	}
       
   395 
       
   396 TInt TSyncLinkState::PassiveOpen(CBTSynchronousLink& /*aContext*/, TUint /*aQueSize*/) const
       
   397 	{
       
   398 	PanicInState(EBTSCOSAPUnexpectedEvent);
       
   399 	return KErrNotSupported;
       
   400 	}
       
   401 
       
   402 void TSyncLinkState::ActiveOpen(CBTSynchronousLink& /*aContext*/) const
       
   403 	{
       
   404 	PanicInState(EBTSCOSAPUnexpectedEvent);
       
   405 	}
       
   406 
       
   407 TBool TSyncLinkState::ConnectRequest(CBTSynchronousLink& /*aContext*/, const TBTConnect& /*aSCOLink*/, const CPhysicalLink& /*aPhysicalLink*/) const
       
   408 	{
       
   409 #ifdef _DEBUG
       
   410 	PanicInState(EBTSCOSAPUnexpectedEvent); //listener code could be suspect
       
   411 #endif
       
   412 	return EFalse;
       
   413 	}
       
   414 
       
   415 void TSyncLinkState::Error(CBTSynchronousLink& /*aContext*/, TInt __DEBUG_ONLY(aErr)) const
       
   416 	{
       
   417 	// Don't propogate the error as listeners don't care if the hardware has gone down
       
   418 	__ASSERT_DEBUG(aErr==KErrHardwareNotAvailable||aErr==EHostSecurityRejection, PanicInState(EBTSCOSAPUnexpectedEvent));
       
   419 	}
       
   420 
       
   421 void TSyncLinkState::ConnectComplete(CBTSynchronousLink& /*aContext*/, const TBTConnect& /*aConnect*/, const TBTSyncConnectOpts& /*aSyncOpts*/) const
       
   422 	{
       
   423 	// eat unexpected HW event
       
   424 	}
       
   425 
       
   426 void TSyncLinkState::Disconnection(CBTSynchronousLink& /*aContext*/) const
       
   427 	{
       
   428 	// eat unexpected HW event
       
   429 	}
       
   430 
       
   431 void TSyncLinkState::DataReceived(CBTSynchronousLink& /*aContext*/, const TDesC8& /*aData*/) const
       
   432 	{
       
   433 	// dump data
       
   434 	}
       
   435 
       
   436 void TSyncLinkState::PacketTypeChange(CBTSynchronousLink& /*aContext*/, THCIErrorCode /*aErr*/, THCIConnHandle /*aConnH*/, TUint16 /*aNewPacket*/) const
       
   437 	{
       
   438 	PanicInState(EBTSCOSAPUnexpectedEvent);
       
   439 	}
       
   440 
       
   441 void TSyncLinkState::Start(CBTSynchronousLink& /*aContext*/) const
       
   442 	{
       
   443 	PanicInState(EBTSCOSAPUnexpectedEvent);
       
   444 	}
       
   445 
       
   446 TUint TSyncLinkState::Write(CBTSynchronousLink& /*aContext*/, const TDesC8& /*aData*/, TUint /*aOptions*/, TSockAddr* /*aAddr*/) const
       
   447 	{
       
   448 	PanicInState(EBTSCOSAPUnexpectedEvent);
       
   449 	return 0;
       
   450 	}
       
   451 
       
   452 TBool TSyncLinkState::IsIdle() const
       
   453 	{
       
   454 	return EFalse;
       
   455 	}
       
   456 
       
   457 TBool TSyncLinkState::IsOpen() const
       
   458 	{
       
   459 	return EFalse;
       
   460 	}
       
   461 
       
   462 void TSyncLinkState::ParentClosing(CBTSynchronousLink& /*aContext*/) const
       
   463 	{
       
   464 	// Swallow the event.
       
   465 	}
       
   466 
       
   467 TBool TSyncLinkState::IsConnectPending() const
       
   468 	{
       
   469 	return EFalse;
       
   470 	}
       
   471 
       
   472 //----------------------------------------------------------------------------------
       
   473 
       
   474 TSCOLinkStateClosed::TSCOLinkStateClosed(CSyncLinkStateFactory& aFactory)
       
   475 : TSyncLinkState(aFactory)
       
   476 	{
       
   477 	STATENAME("TSCOLinkStateClosed");
       
   478 	}
       
   479 
       
   480 void TSCOLinkStateClosed::Enter(CBTSynchronousLink& aContext) const
       
   481 	{
       
   482 	aContext.ReleaseAndUnbind();
       
   483 	}
       
   484 
       
   485 void TSCOLinkStateClosed::Start(CBTSynchronousLink& /*aContext*/) const
       
   486 	{
       
   487 	//gulp
       
   488 	}
       
   489 
       
   490 void TSCOLinkStateClosed::ActiveOpen(CBTSynchronousLink& aContext) const
       
   491 	{
       
   492 	TRAPD(err, aContext.DoActiveOpenL());
       
   493 	if (err)
       
   494 		{
       
   495 		aContext.Socket()->Error(err, MSocketNotify::EErrorConnect);
       
   496 		}
       
   497 	else
       
   498 		{
       
   499 		ChangeState(aContext, CSyncLinkStateFactory::EWaitForSCO);
       
   500 		}
       
   501 	}
       
   502 
       
   503 TBool TSCOLinkStateClosed::IsIdle() const
       
   504 	{
       
   505 	return ETrue;
       
   506 	}
       
   507 
       
   508 
       
   509 TInt TSCOLinkStateClosed::PassiveOpen(CBTSynchronousLink& aContext, TUint /*aQueSize*/) const
       
   510 	{
       
   511 	// need to attach to the PHY
       
   512 
       
   513 	TInt err = aContext.iLinksMan.AddListener(aContext, aContext.iLinkType);
       
   514 	
       
   515 	if (err == KErrNone)
       
   516 		{
       
   517 		ChangeState(aContext, CSyncLinkStateFactory::EListening);
       
   518 		}
       
   519 	
       
   520 	return err;
       
   521 	}
       
   522 
       
   523 void TSCOLinkStateClosed::Shutdown(CBTSynchronousLink& aContext, CServProviderBase::TCloseType aCloseType) const
       
   524 	{
       
   525 	LOG(_L("CBTSyncLink: closed state shutdown, immediate"));
       
   526 	if (aCloseType == CServProviderBase::ENormal)
       
   527 		{
       
   528 		aContext.Socket()->CanClose();	// tell socket it's fine to go
       
   529 		}
       
   530 	}
       
   531 
       
   532 
       
   533 void TSCOLinkStateClosed::Deletion(CBTSynchronousLink& /*aContext*/) const
       
   534 	{
       
   535 	// allowed
       
   536 	}
       
   537 
       
   538 void TSCOLinkStateClosed::Disconnection(CBTSynchronousLink& /*aContext*/) const
       
   539 	{
       
   540 	//discard 
       
   541 	}
       
   542 	
       
   543 //----------------------------------------------------------------------------------
       
   544 
       
   545 TSCOLinkStateWaitForSCO::TSCOLinkStateWaitForSCO(CSyncLinkStateFactory& aFactory)
       
   546 : TSyncLinkState(aFactory)
       
   547 	{
       
   548 	STATENAME("TSCOLinkStateWaitForSCO");
       
   549 	}
       
   550 
       
   551 
       
   552 void TSCOLinkStateWaitForSCO::ConnectComplete(CBTSynchronousLink& aContext, const TBTConnect& aSCOInfo, const TBTSyncConnectOpts& aSyncOpts) const
       
   553 	{
       
   554 	aContext.iHandle = aSCOInfo.iConnH;
       
   555 	aContext.iRemoteDev = aSCOInfo.iBdaddr;
       
   556 
       
   557 	aContext.SetExtOptions(aSyncOpts);
       
   558 
       
   559 	aContext.Socket()->ConnectComplete();
       
   560 
       
   561 	ChangeState(aContext, CSyncLinkStateFactory::EOpen);
       
   562 	}
       
   563 
       
   564 void TSCOLinkStateWaitForSCO::Disconnection(CBTSynchronousLink& aContext) const
       
   565 	{
       
   566 	// The SCO is not currently connected, therefore this 
       
   567 	// must be a PHY disconnection event.  A ConnectionComplete event
       
   568 	// should not be received now so enter the closed state. 
       
   569 	aContext.Socket()->Error(ENoConnection, MSocketNotify::EErrorConnect);
       
   570 	ChangeState(aContext, CSyncLinkStateFactory::EClosed);
       
   571 	}
       
   572 
       
   573 
       
   574 void TSCOLinkStateWaitForSCO::Error(CBTSynchronousLink& aContext, TInt aErr) const
       
   575 	{
       
   576 	// SCO failed
       
   577 	aContext.Socket()->Error(aErr, MSocketNotify::EErrorConnect);
       
   578 	ChangeState(aContext, CSyncLinkStateFactory::EClosed);
       
   579 	}
       
   580 
       
   581 
       
   582 void TSCOLinkStateWaitForSCO::Shutdown(CBTSynchronousLink& aContext, CServProviderBase::TCloseType aCloseType) const
       
   583 	{
       
   584 	// just change to closing - we'll get notified of PhyUp, but that'll get swallowed
       
   585 	// and the connection will timeout and die.
       
   586 	if (aCloseType == CServProviderBase::ENormal)
       
   587 		{
       
   588 		LOG(_L("CBTSyncLink: wait for SCO state shutdown, move to closing"));
       
   589 		ChangeState(aContext, CSyncLinkStateFactory::EClosing);
       
   590 		}
       
   591 	else
       
   592 		{
       
   593 		LOG(_L("CBTSyncLink: wait for SCO state shutdown, die immediately"));
       
   594 		aContext.ReleaseAndUnbind();
       
   595 		ChangeState(aContext, CSyncLinkStateFactory::EClosed);
       
   596 		}
       
   597 	}
       
   598 
       
   599 TBool TSCOLinkStateWaitForSCO::IsConnectPending() const
       
   600 	{
       
   601 	return ETrue;
       
   602 	}
       
   603 
       
   604 
       
   605 
       
   606 
       
   607 //----------------------------------------------------------------------------------
       
   608 
       
   609 TSCOLinkStateWaitForStart::TSCOLinkStateWaitForStart(CSyncLinkStateFactory& aFactory)
       
   610 : TSyncLinkState(aFactory)
       
   611 	{
       
   612 	STATENAME("TSCOLinkStateWaitForStart");
       
   613 	}
       
   614 
       
   615 
       
   616 void TSCOLinkStateWaitForStart::Start(CBTSynchronousLink& aContext) const
       
   617 /**
       
   618 	Called as a result of a socket "ConnectComplete"
       
   619 */
       
   620 	{
       
   621 	aContext.ListeningSAP()->RemoveChild(&aContext);
       
   622 	aContext.ListeningSAP() = NULL;
       
   623 	ChangeState(aContext, CSyncLinkStateFactory::EOpen);
       
   624 	}
       
   625 
       
   626 void TSCOLinkStateWaitForStart::Error(CBTSynchronousLink& aContext, TInt /*aError*/) const
       
   627 	{
       
   628 	aContext.ReleaseAndUnbind();
       
   629 	// no socket to notify of error until Start - transition state
       
   630 	ChangeState(aContext, CSyncLinkStateFactory::EWaitForStartError);
       
   631 	}
       
   632 
       
   633 void TSCOLinkStateWaitForStart::Disconnection(CBTSynchronousLink& aContext) const
       
   634 	{
       
   635 	// the open - but unaccepted - link has disappeared
       
   636 	Error(aContext, KErrDisconnected);
       
   637 	}
       
   638 
       
   639 void TSCOLinkStateWaitForStart::Deletion(CBTSynchronousLink& aContext) const
       
   640 	{
       
   641 	// allowed deletion - should be listening SAP that is deleting us from
       
   642 	// an unwanted connection
       
   643 	aContext.ReleaseAndUnbind();
       
   644 	aContext.ListeningSAP()->RemoveChild(&aContext);
       
   645 	}
       
   646 
       
   647 void TSCOLinkStateWaitForStart::Shutdown(CBTSynchronousLink& aContext, CServProviderBase::TCloseType /*aCloseType*/) const
       
   648 	{
       
   649 	LOG(_L("CBTSyncLink: wait for start state shutdown, tell peer and die"));
       
   650 	TInt err = aContext.CommonShutdown(); // tell peer
       
   651 	if (err)
       
   652 		{
       
   653 		Error(aContext, KErrCouldNotDisconnect);
       
   654 		}
       
   655 
       
   656 	aContext.ReleaseAndUnbind();
       
   657 
       
   658 	// just go
       
   659 	aContext.ListeningSAP()->DeleteChild(&aContext);
       
   660 	}
       
   661 
       
   662 
       
   663 //----------------------------------------------------------------------------------
       
   664 
       
   665 TSCOLinkStateWaitForStartError::TSCOLinkStateWaitForStartError(CSyncLinkStateFactory& aFactory)
       
   666 : TSyncLinkState(aFactory)
       
   667 	{
       
   668 	STATENAME("TSCOLinkStateWaitForStartError");
       
   669 	}
       
   670 
       
   671 void TSCOLinkStateWaitForStartError::Shutdown(CBTSynchronousLink& aContext, CServProviderBase::TCloseType aCloseType) const
       
   672 	{
       
   673 	LOG(_L("CBTSyncLink: wait for start error state shutdown, immediate"));
       
   674 	ChangeState(aContext, CSyncLinkStateFactory::EClosed);
       
   675 	if (aCloseType == CServProviderBase::ENormal)
       
   676 		{
       
   677 		aContext.Socket()->CanClose();
       
   678 		}
       
   679 	}
       
   680 
       
   681 void TSCOLinkStateWaitForStartError::Start(CBTSynchronousLink& aContext) const
       
   682 	{
       
   683 	// summat went wrong - close and remove this acceptor
       
   684 	ChangeState(aContext, CSyncLinkStateFactory::EClosed);
       
   685 	aContext.Socket()->Disconnect();
       
   686 
       
   687 	aContext.ListeningSAP()->RemoveChild(&aContext);
       
   688 	}
       
   689 
       
   690 void TSCOLinkStateWaitForStartError::Deletion(CBTSynchronousLink& aContext) const
       
   691  	{
       
   692  	// allowed deletion - should be listening SAP that is deleting us from
       
   693  	// an unwanted connection
       
   694  	aContext.ReleaseAndUnbind();
       
   695  	aContext.ListeningSAP()->RemoveChild(&aContext);
       
   696  	}
       
   697 
       
   698 //----------------------------------------------------------------------------------
       
   699 
       
   700 TSCOLinkStateListening::TSCOLinkStateListening(CSyncLinkStateFactory& aFactory)
       
   701 : TSyncLinkState(aFactory)
       
   702 	{
       
   703 	STATENAME("TSCOLinkStateListening");
       
   704 	}
       
   705 
       
   706 void TSCOLinkStateListening::Deletion(CBTSynchronousLink& aContext) const
       
   707 	{
       
   708 	delete aContext.iChild;
       
   709 	}
       
   710 
       
   711 void TSCOLinkStateListening::Shutdown(CBTSynchronousLink& aContext, CServProviderBase::TCloseType aCloseType) const
       
   712 	{
       
   713 	LOG(_L("CBTSyncLink: listening state shutdown, immediate"));
       
   714 	if (aContext.iChild)
       
   715 		{
       
   716 		aContext.iChild->ParentClosing();
       
   717 		}
       
   718 	
       
   719 	ChangeState(aContext, CSyncLinkStateFactory::EClosed);
       
   720 	if (aCloseType == CServProviderBase::ENormal)
       
   721 		{
       
   722 		aContext.Socket()->CanClose();
       
   723 		}
       
   724 	}
       
   725 
       
   726 TBool TSCOLinkStateListening::ConnectRequest(CBTSynchronousLink& aContext, const TBTConnect& /*aSCOLink*/, const CPhysicalLink& aPhysicalLink) const
       
   727 	{
       
   728 	// only support one 'link' (HCI-wise!) at moment
       
   729 	TRAPD(err, SpawnL(aContext, const_cast<CPhysicalLink&>(aPhysicalLink)));
       
   730 	return (err==KErrNone);
       
   731 	// don't transition state yet - connection may fail.
       
   732 	};
       
   733 
       
   734 
       
   735 void TSCOLinkStateListening::SpawnL(CBTSynchronousLink& aContext, CPhysicalLink& aPhysicalLink) const
       
   736 	{
       
   737 	CBTSynchronousLink* child;
       
   738 	switch (aContext.iLinkType)
       
   739 		{
       
   740 		case ESCOLink:
       
   741 			child = CSCOLink::NewLC(aContext.iLinksMan, NULL);
       
   742 			break;
       
   743 		default:
       
   744 			child = CeSCOLink::NewLC(aContext.iLinksMan, NULL);
       
   745 			break;
       
   746 		}
       
   747 
       
   748 	// configure the SAP and prepare PHY for SCO link
       
   749 	User::LeaveIfError(child->BindLink(child->iLinkType, aPhysicalLink));
       
   750 
       
   751 	// tell child about parent
       
   752 	child->ListeningSAP() = &aContext;
       
   753 
       
   754 	// and tell parent about child
       
   755 	aContext.iChild = child;
       
   756 	CleanupStack::Pop(child);
       
   757 
       
   758 	aPhysicalLink.OverridePark();
       
   759 
       
   760 	child->iRemoteDev = aPhysicalLink.BDAddr();
       
   761 
       
   762 	// transition for child is Closed->Accepting
       
   763 	ChangeState(*child, CSyncLinkStateFactory::EAccepting);
       
   764 	}
       
   765 
       
   766 void TSCOLinkStateListening::Exit(CBTSynchronousLink& aContext) const
       
   767 	{
       
   768 	aContext.iLinksMan.RemoveListener(aContext);
       
   769 	}
       
   770 
       
   771 
       
   772 //----------------------------------------------------------------------------------
       
   773 
       
   774 TSCOLinkStateAccepting::TSCOLinkStateAccepting(CSyncLinkStateFactory& aFactory)
       
   775 : TSyncLinkState(aFactory)
       
   776 	{
       
   777 	STATENAME("TSCOLinkStateAccepting");
       
   778 	}
       
   779 
       
   780 void TSCOLinkStateAccepting::Enter(CBTSynchronousLink& aContext) const
       
   781 	{
       
   782 	// start watchdog
       
   783 	aContext.iAcceptWatchdog.Start();
       
   784 	}
       
   785 
       
   786 void TSCOLinkStateAccepting::Exit(CBTSynchronousLink& aContext) const
       
   787 	{
       
   788 	// Ensure watchdog is cancelled (calling Cancel twice is OK)
       
   789 	aContext.iAcceptWatchdog.Cancel();
       
   790 	}
       
   791 	
       
   792 void TSCOLinkStateAccepting::ConnectComplete(CBTSynchronousLink& aContext, const TBTConnect& aSCOInfo, const TBTSyncConnectOpts& aSyncOpts) const
       
   793 	{
       
   794 	// cancel watchdog
       
   795 	aContext.iAcceptWatchdog.Cancel();
       
   796 
       
   797 	aContext.iHandle = aSCOInfo.iConnH;
       
   798 	aContext.iRemoteDev = aSCOInfo.iBdaddr;
       
   799 	aContext.SetExtOptions(aSyncOpts);
       
   800 
       
   801 	if (aContext.iClosePending)
       
   802 		{
       
   803 		//aContext.Shutdown(CServProviderBase::EImmediate);//this just resets iClosePending to ETrue
       
   804 		TInt err = aContext.CommonShutdown();
       
   805 
       
   806 		if (err)
       
   807 			{
       
   808 			aContext.Socket()->Error(KErrCouldNotDisconnect, MSocketNotify::EErrorClose);
       
   809 			ChangeState(aContext, CSyncLinkStateFactory::EOpen);
       
   810 			}
       
   811 		else
       
   812 			{
       
   813 			// wait for disconnection complete before anything else
       
   814 			ChangeState(aContext, CSyncLinkStateFactory::EClosing);
       
   815 			}
       
   816 		}
       
   817 	else
       
   818 		{
       
   819 		// let the listening Socket know...
       
   820 		__ASSERT_DEBUG(aContext.iParent, PanicInState(EBTSCOSAPParentlessChild));
       
   821 		__ASSERT_DEBUG(aContext.iParent->Socket(), PanicInState(EBTSCOSAPNullSocket));
       
   822 		ChangeState(aContext, CSyncLinkStateFactory::EWaitForStart);
       
   823 		aContext.iParent->Socket()->ConnectComplete(aContext);	// tell listening socket about its new SAP
       
   824 		}
       
   825 	}
       
   826 
       
   827 void TSCOLinkStateAccepting::Error(CBTSynchronousLink& aContext, TInt /*aError*/) const
       
   828 	{
       
   829 	// cancel watchdog
       
   830 	aContext.iAcceptWatchdog.Cancel();
       
   831 
       
   832 	aContext.ReleaseAndUnbind();
       
   833 
       
   834 	// aContext will be deleted
       
   835 	aContext.ListeningSAP()->DeleteChild(&aContext);
       
   836 	}
       
   837 
       
   838 void TSCOLinkStateAccepting::Deletion(CBTSynchronousLink& aContext) const
       
   839 	{
       
   840 	// cancel watchdog
       
   841 	aContext.iAcceptWatchdog.Cancel();
       
   842 
       
   843 	// allowed deletion
       
   844 	aContext.ReleaseAndUnbind();
       
   845 	aContext.ListeningSAP()->RemoveChild(&aContext);
       
   846 	}
       
   847 
       
   848 void TSCOLinkStateAccepting::Timeout(CBTSynchronousLink& aContext, TBasebandTimeout aTimeout) const
       
   849 	{
       
   850 	if (aTimeout == EAccept)
       
   851 		{
       
   852 		// the accept never worked - give up
       
   853 		Error(aContext, KErrTimedOut);
       
   854 		}
       
   855 	}
       
   856 
       
   857 void TSCOLinkStateAccepting::Shutdown(CBTSynchronousLink& aContext, CServProviderBase::TCloseType IF_FLOGGING(aCloseType)) const
       
   858 	{
       
   859 	LOG1(_L("CBTSyncLink: accepting state shutdown, just take a note. Type: %d"), aCloseType);
       
   860 	aContext.iClosePending = ETrue;
       
   861 	}
       
   862 
       
   863 void TSCOLinkStateAccepting::Disconnection(CBTSynchronousLink& aContext) const
       
   864 	{
       
   865 	LOG(_L("CBTSyncLink: accepting state and PHY has disconnected."));
       
   866 	Error(aContext, KErrDisconnected);
       
   867 	}
       
   868 
       
   869 void TSCOLinkStateAccepting::ParentClosing(CBTSynchronousLink& aContext) const
       
   870 	{
       
   871 	aContext.iClosePending = ETrue;
       
   872 	}
       
   873 
       
   874 
       
   875 //----------------------------------------------------------------------------------
       
   876 
       
   877 TSCOLinkStateOpen::TSCOLinkStateOpen(CSyncLinkStateFactory& aFactory)
       
   878 : TSyncLinkState(aFactory)
       
   879 	{
       
   880 	STATENAME("TSCOLinkStateOpen");
       
   881 	}
       
   882 
       
   883 void TSCOLinkStateOpen::Shutdown(CBTSynchronousLink& aContext, CServProviderBase::TCloseType aCloseType) const
       
   884 	{
       
   885 	TInt err = aContext.CommonShutdown();
       
   886 
       
   887 	if (err)
       
   888 		{
       
   889 		aContext.Socket()->Error(KErrCouldNotDisconnect, MSocketNotify::EErrorClose);
       
   890 		}
       
   891 	else
       
   892 		{
       
   893 		if (aCloseType == CServProviderBase::ENormal)
       
   894 			{
       
   895 			LOG(_L("CBTSyncLink: open state shutdown, wait for link down"));
       
   896 
       
   897 			// wait for disconnection complete before anything else
       
   898 			ChangeState(aContext, CSyncLinkStateFactory::EClosing);
       
   899 			}
       
   900 		else
       
   901 			{
       
   902 			LOG(_L("CBTSyncLink: open state shutdown, immediate"));
       
   903 
       
   904 			aContext.ReleaseAndUnbind();
       
   905 			aContext.iLinksMan.Baseband().UpdateModelForDisconnection(aContext.iHandle, aContext.LinkType());
       
   906 			ChangeState(aContext, CSyncLinkStateFactory::EClosed);
       
   907 			}
       
   908 		}
       
   909 	}
       
   910 
       
   911 TUint TSCOLinkStateOpen::Write(CBTSynchronousLink& aContext, const TDesC8& aData, TUint aOptions, TSockAddr* aAddr) const
       
   912 	{
       
   913 	TUint retVal = aContext.DoWrite(aData, aOptions, aAddr);
       
   914 	return retVal;
       
   915 	}
       
   916 
       
   917 void TSCOLinkStateOpen::Disconnection(CBTSynchronousLink& aContext) const
       
   918 	{
       
   919 	// we were open, go to closed	
       
   920 	ChangeState(aContext, CSyncLinkStateFactory::EClosed);
       
   921 	aContext.Socket()->Disconnect();
       
   922 	}
       
   923 
       
   924 void TSCOLinkStateOpen::Error(CBTSynchronousLink& aContext, TInt /*aErr*/) const
       
   925 	{
       
   926 	// the link has gone - go through disconnect path
       
   927 	aContext.Disconnection();
       
   928 	}
       
   929 
       
   930 void TSCOLinkStateOpen::PacketTypeChange(CBTSynchronousLink& __DEBUG_ONLY(aContext), THCIErrorCode aErr, THCIConnHandle __DEBUG_ONLY(aConnH), TUint16 /*aNewPacketMask*/) const
       
   931 	{
       
   932 	__ASSERT_DEBUG(aConnH == aContext.Handle(), PanicInState(EBTSCOSAPWrongSCOSAP));
       
   933 
       
   934 	if (aErr == KErrNone)
       
   935 		{
       
   936 		// in 1.1 this won't happen - for 1.2 eSCO do something here
       
   937 		}
       
   938 	else
       
   939 		{
       
   940 		// dump
       
   941 		}
       
   942 	}
       
   943 
       
   944 void TSCOLinkStateOpen::DataReceived(CBTSynchronousLink& aContext, const TDesC8& aData) const
       
   945 	{
       
   946 	__ASSERT_DEBUG(aData.Size() <= KInboundSCODataSize, Panic(ESCOInboundPacketTooLarge));
       
   947 	aContext.InboundFrame() = aData;
       
   948 
       
   949 	// signal (stream) socket of number of bytes we have
       
   950 	aContext.Socket()->NewData(aData.Length());
       
   951 	}
       
   952 
       
   953 TBool TSCOLinkStateOpen::IsOpen() const
       
   954 	{
       
   955 	return ETrue;
       
   956 	}
       
   957 
       
   958 void TSCOLinkStateOpen::Exit(CBTSynchronousLink& /*aContext*/) const
       
   959 	{
       
   960 	}
       
   961 
       
   962 
       
   963 //----------------------------------------------------------------------------------
       
   964 
       
   965 TSCOLinkStateClosing::TSCOLinkStateClosing(CSyncLinkStateFactory& aFactory)
       
   966 : TSyncLinkState(aFactory)
       
   967 	{
       
   968 	STATENAME("TSCOLinkStateClosing");
       
   969 	}
       
   970 
       
   971 void TSCOLinkStateClosing::Shutdown(CBTSynchronousLink& aContext, CServProviderBase::TCloseType aCloseType) const
       
   972 	{
       
   973 	LOG(_L("CBTSyncLink: closing state shutdown, immediate"));
       
   974 
       
   975 	ChangeState(aContext, CSyncLinkStateFactory::EClosed);
       
   976 	if ((aCloseType == CServProviderBase::ENormal) && (aContext.iSocket))
       
   977 		{
       
   978 		aContext.Socket()->CanClose();
       
   979 		}
       
   980 	}
       
   981 
       
   982 void TSCOLinkStateClosing::ActiveOpen(CBTSynchronousLink& aContext) const
       
   983 	{
       
   984 	// erk - have been asked to Open as we're closing down
       
   985 	// the link hasn't gone yet, so just say it's there!
       
   986 	ChangeState(aContext, CSyncLinkStateFactory::EOpen);
       
   987 	aContext.Socket()->ConnectComplete();
       
   988 	}
       
   989 
       
   990 
       
   991 void TSCOLinkStateClosing::Error(CBTSynchronousLink& aContext, TInt /*aError*/) const
       
   992 	{
       
   993 	ChangeState(aContext, CSyncLinkStateFactory::EClosed);
       
   994 	}
       
   995 
       
   996 void TSCOLinkStateClosing::Disconnection(CBTSynchronousLink& aContext) const
       
   997 	{
       
   998 	LOG(_L("CBTSyncLink: disconnection notification in closing state"));
       
   999 
       
  1000 	// as expected
       
  1001 	ChangeState(aContext, CSyncLinkStateFactory::EClosed);
       
  1002 	if (aContext.iSocket)
       
  1003 		{
       
  1004 		aContext.Socket()->CanClose();
       
  1005 		}
       
  1006 	// Otherwise just swallow the event...
       
  1007 	}
       
  1008 
       
  1009 TBool TSCOLinkStateClosing::IsIdle() const
       
  1010 	{
       
  1011 	return ETrue;
       
  1012 	}
       
  1013 
       
  1014 void TSCOLinkStateClosing::ConnectComplete(CBTSynchronousLink& aContext, const TBTConnect& aSCOInfo, const TBTSyncConnectOpts& /*aSyncOpts*/) const
       
  1015 	{
       
  1016 	LOG(_L("CBTSyncLink: connection notification in closing state"));
       
  1017 
       
  1018 	// This connection is no longer required, so close the socket
       
  1019 	ChangeState(aContext, CSyncLinkStateFactory::EClosed);
       
  1020 
       
  1021 		
       
  1022 	//The call path to this ConnectComplete will have included CPhysicalLink::ConnectionComplete.
       
  1023 	//CPhysicalLink::ConnectionComplete updates the baseband model to show a new SCO link is in place
       
  1024 	//But if the SCO link is in state TSCOLinkStateClosing, setup of the new link won't occur. So the model 
       
  1025 	//should be updated again. If it isn't updated, it won't be possible to add a new SCO link, because the 
       
  1026 	//model will indicate that one already exists.
       
  1027 	aContext.iLinksMan.Baseband().UpdateModelForDisconnection(aSCOInfo.iConnH, aSCOInfo.iLinkType);	
       
  1028 	
       
  1029 	//Even though the SAP is now EClosed, the baseband might have a SCO link to the remote device.
       
  1030 	//The only way to be sure is to send a Disconnect. The transition to EClosed at the will have 
       
  1031 	//unbound this SAP from the PHY, so if the remote device responds to the Disconnect, the response
       
  1032 	//will be thrown away.
       
  1033 	TRAP_IGNORE(aContext.iLinksMan.HCIFacade().DisconnectL(aSCOInfo.iConnH, ERemoteUserEndedConnection));
       
  1034 
       
  1035 	if (aContext.iSocket)
       
  1036 		{
       
  1037 		aContext.Socket()->CanClose();
       
  1038 		}
       
  1039 	// socket could be deleted now so be careful
       
  1040 	}
       
  1041 
       
  1042 void TSCOLinkStateClosing::Deletion(CBTSynchronousLink& /*aContext*/) const
       
  1043  	{
       
  1044  	// allowed
       
  1045  	}
       
  1046 
       
  1047 //-----------------------------------------------------------------------------------
       
  1048