mirror of
https://github.com/espressif/openthread.git
synced 2026-08-08 11:47:46 +00:00
[mle] add Mle::Detacher class to manage graceful detach process (#11723)
This commit introduces a new `Mle::Detacher` class to encapsulate all state and logic for the graceful detach process. By managing its own internal state, timer, and completion callback, the `Detacher` class centralizes the detach logic, improving code clarity and maintainability.
This commit is contained in:
+67
-29
@@ -52,7 +52,6 @@ Mle::Mle(Instance &aInstance)
|
||||
, mRequestRouteTlv(false)
|
||||
, mHasRestored(false)
|
||||
, mReceivedResponseFromParent(false)
|
||||
, mDetachingGracefully(false)
|
||||
, mInitiallyAttachedAsSleepy(false)
|
||||
, mRole(kRoleDisabled)
|
||||
, mLastSavedRole(kRoleDisabled)
|
||||
@@ -75,6 +74,7 @@ Mle::Mle(Instance &aInstance)
|
||||
, mNeighborTable(aInstance)
|
||||
, mDelayedSender(aInstance)
|
||||
, mSocket(aInstance, *this)
|
||||
, mDetacher(aInstance)
|
||||
, mRetxTracker(aInstance)
|
||||
, mAnnounceHandler(aInstance)
|
||||
#if OPENTHREAD_CONFIG_PARENT_SEARCH_ENABLE
|
||||
@@ -273,11 +273,7 @@ void Mle::Stop(StopMode aMode)
|
||||
SetRole(kRoleDisabled);
|
||||
|
||||
exit:
|
||||
if (mDetachingGracefully)
|
||||
{
|
||||
mDetachingGracefully = false;
|
||||
mDetachGracefullyCallback.InvokeAndClearIfSet();
|
||||
}
|
||||
mDetacher.HandleStop();
|
||||
}
|
||||
|
||||
Error Mle::RestorePrevRole(void)
|
||||
@@ -1465,12 +1461,6 @@ void Mle::HandleAttachTimer(void)
|
||||
bool shouldAnnounce = true;
|
||||
ParentRequestType type;
|
||||
|
||||
if (mDetachingGracefully)
|
||||
{
|
||||
Stop();
|
||||
ExitNow();
|
||||
}
|
||||
|
||||
#if OPENTHREAD_FTD
|
||||
if (IsDetached() && mRouterRoleRestorer.IsActive())
|
||||
{
|
||||
@@ -3519,14 +3509,8 @@ void Mle::HandleChildUpdateResponseOnChild(RxInfo &aRxInfo)
|
||||
switch (Tlv::Find<TimeoutTlv>(aRxInfo.mMessage, timeout))
|
||||
{
|
||||
case kErrorNone:
|
||||
if (timeout == 0 && mDetachingGracefully)
|
||||
{
|
||||
Stop();
|
||||
}
|
||||
else
|
||||
{
|
||||
SetTimeout(timeout, kDoNotSendChildUpdateToParent);
|
||||
}
|
||||
SuccessOrExit(mDetacher.HandleChildUpdateResponse(timeout));
|
||||
SetTimeout(timeout, kDoNotSendChildUpdateToParent);
|
||||
break;
|
||||
case kErrorNotFound:
|
||||
break;
|
||||
@@ -4264,32 +4248,32 @@ exit:
|
||||
}
|
||||
#endif // OPENTHREAD_CONFIG_WAKEUP_COORDINATOR_ENABLE
|
||||
|
||||
Error Mle::DetachGracefully(DetachCallback aCallback, void *aContext)
|
||||
Error Mle::Detacher::Detach(DetachCallback aCallback, void *aContext)
|
||||
{
|
||||
Error error = kErrorNone;
|
||||
uint32_t timeout = kDetachGracefullyTimeout;
|
||||
uint32_t timeout = kTimeout;
|
||||
|
||||
VerifyOrExit(!mDetachingGracefully, error = kErrorBusy);
|
||||
VerifyOrExit(mState == kIdle, error = kErrorBusy);
|
||||
|
||||
mDetachGracefullyCallback.Set(aCallback, aContext);
|
||||
mCallback.Set(aCallback, aContext);
|
||||
|
||||
#if OPENTHREAD_CONFIG_BORDER_ROUTING_ENABLE
|
||||
Get<BorderRouter::RoutingManager>().RequestStop();
|
||||
#endif
|
||||
|
||||
switch (mRole)
|
||||
switch (Get<Mle>().GetRole())
|
||||
{
|
||||
case kRoleLeader:
|
||||
break;
|
||||
|
||||
case kRoleRouter:
|
||||
#if OPENTHREAD_FTD
|
||||
SendAddressRelease();
|
||||
Get<Mle>().SendAddressRelease();
|
||||
#endif
|
||||
break;
|
||||
|
||||
case kRoleChild:
|
||||
IgnoreError(SendChildUpdateRequestToParent(kAppendZeroTimeout));
|
||||
IgnoreError(Get<Mle>().SendChildUpdateRequestToParent(kAppendZeroTimeout));
|
||||
break;
|
||||
|
||||
case kRoleDisabled:
|
||||
@@ -4302,8 +4286,8 @@ Error Mle::DetachGracefully(DetachCallback aCallback, void *aContext)
|
||||
break;
|
||||
}
|
||||
|
||||
mDetachingGracefully = true;
|
||||
mAttachTimer.Start(timeout);
|
||||
mState = kDetaching;
|
||||
mTimer.Start(timeout);
|
||||
|
||||
exit:
|
||||
return error;
|
||||
@@ -5382,6 +5366,60 @@ void Mle::ParentCandidate::CopyTo(Parent &aParent) const
|
||||
aParent = *candidateAsParent;
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------------------------------------------------
|
||||
// Detacher
|
||||
|
||||
Mle::Detacher::Detacher(Instance &aInstance)
|
||||
: InstanceLocator(aInstance)
|
||||
, mState(kIdle)
|
||||
, mTimer(aInstance)
|
||||
{
|
||||
}
|
||||
|
||||
void Mle::Detacher::HandleTimer(void)
|
||||
{
|
||||
if (mState == kDetaching)
|
||||
{
|
||||
Get<Mle>().Stop();
|
||||
}
|
||||
}
|
||||
|
||||
Error Mle::Detacher::HandleChildUpdateResponse(uint32_t aTimeout)
|
||||
{
|
||||
// Called while processing a "Child Update Response" from parent
|
||||
//
|
||||
// To gracefully detach as a child, we send a "Child Update
|
||||
// Request" with zero timeout value to the parent. Receiving
|
||||
// a "Child Update Response" confirms the parent has received the
|
||||
// request, allowing the device to then stop its MLE operations.
|
||||
|
||||
// Returns `kErrorDetached` to signal that MLE is stopped and the
|
||||
// incoming response should be ignored. Returns `kErrorNone` if
|
||||
// the response should be processed (i.e., not detaching or if
|
||||
// the conditions for MLE stop are not yet met).
|
||||
|
||||
Error error = kErrorNone;
|
||||
|
||||
VerifyOrExit(mState == kDetaching);
|
||||
VerifyOrExit(aTimeout == 0);
|
||||
Get<Mle>().Stop();
|
||||
error = kErrorDetached;
|
||||
|
||||
exit:
|
||||
return error;
|
||||
}
|
||||
|
||||
void Mle::Detacher::HandleStop(void)
|
||||
{
|
||||
// Called upon `Mle::Stop()` after role change.
|
||||
|
||||
if (mState == kDetaching)
|
||||
{
|
||||
mState = kIdle;
|
||||
mCallback.InvokeAndClearIfSet();
|
||||
}
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------------------------------------------------
|
||||
// RetxTracker::RetryInfo
|
||||
|
||||
|
||||
+34
-4
@@ -216,7 +216,7 @@ public:
|
||||
* @retval kErrorNone Successfully started detaching.
|
||||
* @retval kErrorBusy Detaching is already in progress.
|
||||
*/
|
||||
Error DetachGracefully(DetachCallback aCallback, void *aContext);
|
||||
Error DetachGracefully(DetachCallback aCallback, void *aContext) { return mDetacher.Detach(aCallback, aContext); }
|
||||
|
||||
/**
|
||||
* Indicates whether or not the Thread device is attached to a Thread network.
|
||||
@@ -1182,7 +1182,6 @@ private:
|
||||
static constexpr uint32_t kMaxLinkAcceptDelay = 1000; // Max delay to tx Link Accept for multicast Req
|
||||
static constexpr uint32_t kChildIdRequestTimeout = 5000; // Max delay to rx a Child ID Req after Parent Res
|
||||
static constexpr uint32_t kLinkRequestTimeout = 2000; // Max delay to rx a Link Accept
|
||||
static constexpr uint32_t kDetachGracefullyTimeout = 1000; // Timeout for graceful detach
|
||||
static constexpr uint32_t kUnicastRetxDelay = 1000; // Base delay for MLE unicast retx
|
||||
static constexpr uint32_t kMulticastRetxDelay = 5000; // Base delay for MLE multicast retx
|
||||
static constexpr uint32_t kMulticastRetxDelayMin = kMulticastRetxDelay * 9 / 10; // 0.9 * base delay
|
||||
@@ -1721,6 +1720,38 @@ private:
|
||||
|
||||
//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
|
||||
void HandleDetacherTimer(void) { mDetacher.HandleTimer(); }
|
||||
|
||||
class Detacher : public InstanceLocator
|
||||
{
|
||||
// Manages graceful detach process.
|
||||
|
||||
public:
|
||||
explicit Detacher(Instance &aInstance);
|
||||
|
||||
Error Detach(DetachCallback aCallback, void *aContext);
|
||||
void HandleTimer(void);
|
||||
Error HandleChildUpdateResponse(uint32_t aTimeout);
|
||||
void HandleStop(void);
|
||||
|
||||
private:
|
||||
static constexpr uint32_t kTimeout = 1000;
|
||||
|
||||
enum State : uint8_t
|
||||
{
|
||||
kIdle,
|
||||
kDetaching,
|
||||
};
|
||||
|
||||
using DetachTimer = TimerMilliIn<Mle, &Mle::HandleDetacherTimer>;
|
||||
|
||||
State mState;
|
||||
Callback<DetachCallback> mCallback;
|
||||
DetachTimer mTimer;
|
||||
};
|
||||
|
||||
//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
|
||||
void HandleRetxTrackerTimer(void) { mRetxTracker.HandleTimer(); }
|
||||
|
||||
class RetxTracker : public InstanceLocator
|
||||
@@ -2171,7 +2202,6 @@ private:
|
||||
bool mRequestRouteTlv : 1;
|
||||
bool mHasRestored : 1;
|
||||
bool mReceivedResponseFromParent : 1;
|
||||
bool mDetachingGracefully : 1;
|
||||
bool mInitiallyAttachedAsSleepy : 1;
|
||||
|
||||
DeviceRole mRole;
|
||||
@@ -2204,6 +2234,7 @@ private:
|
||||
ParentCandidate mParentCandidate;
|
||||
MleSocket mSocket;
|
||||
Counters mCounters;
|
||||
Detacher mDetacher;
|
||||
RetxTracker mRetxTracker;
|
||||
AnnounceHandler mAnnounceHandler;
|
||||
#if OPENTHREAD_CONFIG_PARENT_SEARCH_ENABLE
|
||||
@@ -2212,7 +2243,6 @@ private:
|
||||
#if OPENTHREAD_CONFIG_TMF_NETDATA_SERVICE_ENABLE
|
||||
ServiceAloc mServiceAlocs[kMaxServiceAlocs];
|
||||
#endif
|
||||
Callback<DetachCallback> mDetachGracefullyCallback;
|
||||
#if OPENTHREAD_CONFIG_MLE_PARENT_RESPONSE_CALLBACK_API_ENABLE
|
||||
Callback<otThreadParentResponseCallback> mParentResponseCallback;
|
||||
#endif
|
||||
|
||||
Reference in New Issue
Block a user