blob: 7bb6edebb872b344dc721680640a51f9b42e50e7 [file]
/*
* Copyright (c) 2024, The OpenThread Authors.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the copyright holder nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
#define OTBR_LOG_TAG "NCP_HOST"
#include "ncp_host.hpp"
#include <memory>
#include <openthread/error.h>
#include <openthread/thread.h>
#include <openthread/openthread-system.h>
#include "host/async_task.hpp"
#include "lib/spinel/spinel_driver.hpp"
namespace otbr {
namespace Host {
// =============================== NcpNetworkProperties ===============================
NcpNetworkProperties::NcpNetworkProperties(void)
: mDeviceRole(OT_DEVICE_ROLE_DISABLED)
{
memset(&mDatasetActiveTlvs, 0, sizeof(mDatasetActiveTlvs));
}
otDeviceRole NcpNetworkProperties::GetDeviceRole(void) const
{
return mDeviceRole;
}
void NcpNetworkProperties::SetDeviceRole(otDeviceRole aRole)
{
mDeviceRole = aRole;
}
bool NcpNetworkProperties::Ip6IsEnabled(void) const
{
// TODO: Implement the method under NCP mode.
return false;
}
uint32_t NcpNetworkProperties::GetPartitionId(void) const
{
// TODO: Implement the method under NCP mode.
return 0;
}
void NcpNetworkProperties::SetDatasetActiveTlvs(const otOperationalDatasetTlvs &aActiveOpDatasetTlvs)
{
mDatasetActiveTlvs.mLength = aActiveOpDatasetTlvs.mLength;
memcpy(mDatasetActiveTlvs.mTlvs, aActiveOpDatasetTlvs.mTlvs, aActiveOpDatasetTlvs.mLength);
}
void NcpNetworkProperties::GetDatasetActiveTlvs(otOperationalDatasetTlvs &aDatasetTlvs) const
{
aDatasetTlvs.mLength = mDatasetActiveTlvs.mLength;
memcpy(aDatasetTlvs.mTlvs, mDatasetActiveTlvs.mTlvs, mDatasetActiveTlvs.mLength);
}
void NcpNetworkProperties::GetDatasetPendingTlvs(otOperationalDatasetTlvs &aDatasetTlvs) const
{
// TODO: Implement the method under NCP mode.
OTBR_UNUSED_VARIABLE(aDatasetTlvs);
}
// ===================================== NcpHost ======================================
NcpHost::NcpHost(const char *aInterfaceName, const char *aBackboneInterfaceName, bool aDryRun)
: mSpinelDriver(*static_cast<ot::Spinel::SpinelDriver *>(otSysGetSpinelDriver()))
, mNetif(mNcpSpinel)
, mInfraIf(mNcpSpinel)
{
memset(&mConfig, 0, sizeof(mConfig));
mConfig.mInterfaceName = aInterfaceName;
mConfig.mBackboneInterfaceName = aBackboneInterfaceName;
mConfig.mDryRun = aDryRun;
mConfig.mSpeedUpFactor = 1;
}
const char *NcpHost::GetCoprocessorVersion(void)
{
return mSpinelDriver.GetVersion();
}
void NcpHost::Init(void)
{
otSysInit(&mConfig);
mNcpSpinel.Init(mSpinelDriver, *this);
mNetif.Init(mConfig.mInterfaceName);
mInfraIf.Init();
mNcpSpinel.Ip6SetAddressCallback(
[this](const std::vector<Ip6AddressInfo> &aAddrInfos) { mNetif.UpdateIp6UnicastAddresses(aAddrInfos); });
mNcpSpinel.Ip6SetAddressMulticastCallback(
[this](const std::vector<Ip6Address> &aAddrs) { mNetif.UpdateIp6MulticastAddresses(aAddrs); });
mNcpSpinel.NetifSetStateChangedCallback([this](bool aState) { mNetif.SetNetifState(aState); });
mNcpSpinel.Ip6SetReceiveCallback(
[this](const uint8_t *aData, uint16_t aLength) { mNetif.Ip6Receive(aData, aLength); });
mNcpSpinel.InfraIfSetIcmp6NdSendCallback(
[this](uint32_t aInfraIfIndex, const otIp6Address &aAddr, const uint8_t *aData, uint16_t aDataLen) {
OTBR_UNUSED_VARIABLE(mInfraIf.SendIcmp6Nd(aInfraIfIndex, aAddr, aData, aDataLen));
});
if (mConfig.mBackboneInterfaceName != nullptr && strlen(mConfig.mBackboneInterfaceName) > 0)
{
mInfraIf.SetInfraIf(mConfig.mBackboneInterfaceName);
}
#if OTBR_ENABLE_SRP_ADVERTISING_PROXY
#if OTBR_ENABLE_SRP_SERVER_AUTO_ENABLE_MODE
// Let SRP server use auto-enable mode. The auto-enable mode delegates the control of SRP server to the Border
// Routing Manager. SRP server automatically starts when bi-directional connectivity is ready.
mNcpSpinel.SrpServerSetAutoEnableMode(/* aEnabled */ true);
#else
mNcpSpinel.SrpServerSetEnabled(/* aEnabled */ true);
#endif
#endif
}
void NcpHost::Deinit(void)
{
mNcpSpinel.Deinit();
mNetif.Deinit();
otSysDeinit();
}
void NcpHost::Join(const otOperationalDatasetTlvs &aActiveOpDatasetTlvs, const AsyncResultReceiver &aReceiver)
{
AsyncTaskPtr task;
auto errorHandler = [aReceiver](otError aError, const std::string &aErrorInfo) { aReceiver(aError, aErrorInfo); };
task = std::make_shared<AsyncTask>(errorHandler);
task->First([this, aActiveOpDatasetTlvs](AsyncTaskPtr aNext) {
mNcpSpinel.DatasetSetActiveTlvs(aActiveOpDatasetTlvs, std::move(aNext));
})
->Then([this](AsyncTaskPtr aNext) { mNcpSpinel.Ip6SetEnabled(true, std::move(aNext)); })
->Then([this](AsyncTaskPtr aNext) { mNcpSpinel.ThreadSetEnabled(true, std::move(aNext)); });
task->Run();
}
void NcpHost::Leave(bool aEraseDataset, const AsyncResultReceiver &aReceiver)
{
AsyncTaskPtr task;
auto errorHandler = [aReceiver](otError aError, const std::string &aErrorInfo) { aReceiver(aError, aErrorInfo); };
task = std::make_shared<AsyncTask>(errorHandler);
task->First([this](AsyncTaskPtr aNext) { mNcpSpinel.ThreadDetachGracefully(std::move(aNext)); })
->Then([this, aEraseDataset](AsyncTaskPtr aNext) {
if (aEraseDataset)
{
mNcpSpinel.ThreadErasePersistentInfo(std::move(aNext));
}
else
{
aNext->SetResult(OT_ERROR_NONE, "");
}
});
task->Run();
}
void NcpHost::ScheduleMigration(const otOperationalDatasetTlvs &aPendingOpDatasetTlvs,
const AsyncResultReceiver aReceiver)
{
otDeviceRole role = GetDeviceRole();
otError error = OT_ERROR_NONE;
auto errorHandler = [aReceiver](otError aError, const std::string &aErrorInfo) { aReceiver(aError, aErrorInfo); };
VerifyOrExit(role != OT_DEVICE_ROLE_DISABLED && role != OT_DEVICE_ROLE_DETACHED, error = OT_ERROR_INVALID_STATE);
mNcpSpinel.DatasetMgmtSetPending(std::make_shared<otOperationalDatasetTlvs>(aPendingOpDatasetTlvs),
std::make_shared<AsyncTask>(errorHandler));
exit:
if (error != OT_ERROR_NONE)
{
mTaskRunner.Post(
[aReceiver, error](void) { aReceiver(error, "Cannot schedule migration when this device is detached"); });
}
}
void NcpHost::SetThreadEnabled(bool aEnabled, const AsyncResultReceiver aReceiver)
{
OT_UNUSED_VARIABLE(aEnabled);
// TODO: Implement SetThreadEnabled under NCP mode.
mTaskRunner.Post([aReceiver](void) { aReceiver(OT_ERROR_NOT_IMPLEMENTED, "Not implemented!"); });
}
void NcpHost::SetCountryCode(const std::string &aCountryCode, const AsyncResultReceiver &aReceiver)
{
OT_UNUSED_VARIABLE(aCountryCode);
// TODO: Implement SetCountryCode under NCP mode.
mTaskRunner.Post([aReceiver](void) { aReceiver(OT_ERROR_NOT_IMPLEMENTED, "Not implemented!"); });
}
void NcpHost::GetChannelMasks(const ChannelMasksReceiver &aReceiver, const AsyncResultReceiver &aErrReceiver)
{
OT_UNUSED_VARIABLE(aReceiver);
// TODO: Implement GetChannelMasks under NCP mode.
mTaskRunner.Post([aErrReceiver](void) { aErrReceiver(OT_ERROR_NOT_IMPLEMENTED, "Not implemented!"); });
}
#if OTBR_ENABLE_POWER_CALIBRATION
void NcpHost::SetChannelMaxPowers(const std::vector<ChannelMaxPower> &aChannelMaxPowers,
const AsyncResultReceiver &aReceiver)
{
OT_UNUSED_VARIABLE(aChannelMaxPowers);
// TODO: Implement SetChannelMaxPowers under NCP mode.
mTaskRunner.Post([aReceiver](void) { aReceiver(OT_ERROR_NOT_IMPLEMENTED, "Not implemented!"); });
}
#endif
void NcpHost::AddThreadStateChangedCallback(ThreadStateChangedCallback aCallback)
{
// TODO: Implement AddThreadStateChangedCallback under NCP mode.
OT_UNUSED_VARIABLE(aCallback);
}
void NcpHost::AddThreadEnabledStateChangedCallback(ThreadEnabledStateCallback aCallback)
{
// TODO: Implement AddThreadEnabledStateChangedCallback under NCP mode.
OT_UNUSED_VARIABLE(aCallback);
}
void NcpHost::Process(const MainloopContext &aMainloop)
{
mSpinelDriver.Process(&aMainloop);
mNetif.Process(&aMainloop);
}
void NcpHost::Update(MainloopContext &aMainloop)
{
mSpinelDriver.GetSpinelInterface()->UpdateFdSet(&aMainloop);
if (mSpinelDriver.HasPendingFrame())
{
aMainloop.mTimeout.tv_sec = 0;
aMainloop.mTimeout.tv_usec = 0;
}
mNetif.UpdateFdSet(&aMainloop);
}
#if OTBR_ENABLE_SRP_ADVERTISING_PROXY
void NcpHost::SetMdnsPublisher(Mdns::Publisher *aPublisher)
{
mNcpSpinel.SetMdnsPublisher(aPublisher);
}
void NcpHost::HandleMdnsState(Mdns::Publisher::State aState)
{
mNcpSpinel.DnssdSetState(aState);
}
#endif
} // namespace Host
} // namespace otbr