Skip to content

Commit

Permalink
[thread-host] implement schedule migration for rcp host (#2559)
Browse files Browse the repository at this point in the history
  • Loading branch information
Irving-cl authored Nov 1, 2024
1 parent 62395a5 commit 5c66507
Show file tree
Hide file tree
Showing 3 changed files with 102 additions and 4 deletions.
49 changes: 45 additions & 4 deletions src/ncp/rcp_host.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -308,7 +308,8 @@ void RcpHost::Deinit(void)
mThreadStateChangedCallbacks.clear();
mResetHandlers.clear();

mSetThreadEnabledReceiver = nullptr;
mSetThreadEnabledReceiver = nullptr;
mScheduleMigrationReceiver = nullptr;
}

void RcpHost::HandleStateChanged(otChangedFlags aFlags)
Expand Down Expand Up @@ -425,10 +426,43 @@ void RcpHost::Leave(const AsyncResultReceiver &aReceiver)
void RcpHost::ScheduleMigration(const otOperationalDatasetTlvs &aPendingOpDatasetTlvs,
const AsyncResultReceiver aReceiver)
{
OT_UNUSED_VARIABLE(aPendingOpDatasetTlvs);
otError error = OT_ERROR_NONE;
std::string errorMsg;
otOperationalDataset emptyDataset;

// TODO: Implement ScheduleMigration under RCP mode.
mTaskRunner.Post([aReceiver](void) { aReceiver(OT_ERROR_NOT_IMPLEMENTED, "Not implemented!"); });
VerifyOrExit(mInstance != nullptr, error = OT_ERROR_INVALID_STATE, errorMsg = "OT is not initialized");
VerifyOrExit(IsAttached(), error = OT_ERROR_FAILED,
errorMsg = "Cannot schedule migration when this device is detached");

// TODO: check supported channel mask

SuccessOrExit(error = otDatasetSendMgmtPendingSet(mInstance, &emptyDataset, aPendingOpDatasetTlvs.mTlvs,
static_cast<uint8_t>(aPendingOpDatasetTlvs.mLength),
SendMgmtPendingSetCallback, this),
errorMsg = "Failed to send MGMT_PENDING_SET.req");

exit:
if (error != OT_ERROR_NONE)
{
mTaskRunner.Post([aReceiver, error, errorMsg](void) { aReceiver(error, errorMsg); });
}
else
{
// otDatasetSendMgmtPendingSet() returns OT_ERROR_BUSY if it has already been called before but the
// callback hasn't been invoked. So we can guarantee that mMigrationReceiver is always nullptr here
assert(mScheduleMigrationReceiver == nullptr);
mScheduleMigrationReceiver = aReceiver;
}
}

void RcpHost::SendMgmtPendingSetCallback(otError aError, void *aContext)
{
static_cast<RcpHost *>(aContext)->SendMgmtPendingSetCallback(aError);
}

void RcpHost::SendMgmtPendingSetCallback(otError aError)
{
SafeInvokeAndClear(mScheduleMigrationReceiver, aError, "");
}

void RcpHost::SetThreadEnabled(bool aEnabled, const AsyncResultReceiver aReceiver)
Expand Down Expand Up @@ -553,6 +587,13 @@ void RcpHost::SetCountryCode(const std::string &aCountryCode, const AsyncResultR
mTaskRunner.Post([aReceiver, error, errorMsg](void) { aReceiver(error, errorMsg); });
}

bool RcpHost::IsAttached(void)
{
otDeviceRole role = GetDeviceRole();

return role == OT_DEVICE_ROLE_CHILD || role == OT_DEVICE_ROLE_ROUTER || role == OT_DEVICE_ROLE_LEADER;
}

/*
* Provide, if required an "otPlatLog()" function
*/
Expand Down
5 changes: 5 additions & 0 deletions src/ncp/rcp_host.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -255,10 +255,14 @@ class RcpHost : public MainloopProcessor, public ThreadHost, public OtNetworkPro

static void DisableThreadAfterDetach(void *aContext);
void DisableThreadAfterDetach(void);
static void SendMgmtPendingSetCallback(otError aError, void *aContext);
void SendMgmtPendingSetCallback(otError aError);

bool IsAutoAttachEnabled(void);
void DisableAutoAttach(void);

bool IsAttached(void);

otError SetOtbrAndOtLogLevel(otbrLogLevel aLevel);

otInstance *mInstance;
Expand All @@ -271,6 +275,7 @@ class RcpHost : public MainloopProcessor, public ThreadHost, public OtNetworkPro
bool mEnableAutoAttach = false;

AsyncResultReceiver mSetThreadEnabledReceiver;
AsyncResultReceiver mScheduleMigrationReceiver;

#if OTBR_ENABLE_FEATURE_FLAGS
// The applied FeatureFlagList in ApplyFeatureFlagList call, used for debugging purpose.
Expand Down
52 changes: 52 additions & 0 deletions tests/gtest/test_rcp_host_api.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -181,3 +181,55 @@ TEST(RcpHostApi, SetCountryCodeWorkCorrectly)

host.Deinit();
}

TEST(RcpHostApi, StateChangesCorrectlyAfterScheduleMigration)
{
otError error = OT_ERROR_NONE;
bool resultReceived = false;
otbr::MainloopContext mainloop;
otbr::Ncp::ThreadHost::AsyncResultReceiver receiver = [&resultReceived, &error](otError aError,
const std::string &aErrorMsg) {
OT_UNUSED_VARIABLE(aErrorMsg);
resultReceived = true;
error = aError;
};
otbr::Ncp::RcpHost host("wpan0", std::vector<const char *>(), /* aBackboneInterfaceName */ "", /* aDryRun */ false,
/* aEnableAutoAttach */ false);

otOperationalDataset dataset;
otOperationalDatasetTlvs datasetTlvs;

// 1. Call ScheduleMigration when host hasn't been initialized.
otbr::MainloopManager::GetInstance().RemoveMainloopProcessor(
&host); // Temporarily remove RcpHost because it's not initialized yet.
host.ScheduleMigration(datasetTlvs, receiver);
MainloopProcessUntil(mainloop, /* aTimeoutSec */ 0, [&resultReceived]() { return resultReceived; });
EXPECT_EQ(error, OT_ERROR_INVALID_STATE);
otbr::MainloopManager::GetInstance().AddMainloopProcessor(&host);

host.Init();

// 2. Call ScheduleMigration when the device is not attached.
error = OT_ERROR_NONE;
resultReceived = false;
host.ScheduleMigration(datasetTlvs, receiver);
MainloopProcessUntil(mainloop, /* aTimeoutSec */ 0, [&resultReceived]() { return resultReceived; });
EXPECT_EQ(error, OT_ERROR_FAILED);

// 3. Schedule migration to another network.
OT_UNUSED_VARIABLE(otDatasetCreateNewNetwork(ot::FakePlatform::CurrentInstance(), &dataset));
otDatasetConvertToTlvs(&dataset, &datasetTlvs);
OT_UNUSED_VARIABLE(otDatasetSetActiveTlvs(ot::FakePlatform::CurrentInstance(), &datasetTlvs));
error = OT_ERROR_NONE;
resultReceived = false;
host.SetThreadEnabled(true, receiver);
MainloopProcessUntil(mainloop, /* aTimeoutSec */ 1,
[&host]() { return host.GetDeviceRole() != OT_DEVICE_ROLE_DETACHED; });
EXPECT_EQ(host.GetDeviceRole(), OT_DEVICE_ROLE_LEADER);

host.ScheduleMigration(datasetTlvs, receiver);
MainloopProcessUntil(mainloop, /* aTimeoutSec */ 0, [&resultReceived]() { return resultReceived; });
EXPECT_EQ(error, OT_ERROR_NONE);

host.Deinit();
}

0 comments on commit 5c66507

Please sign in to comment.