1 /*
2 * Copyright (c) 2024, The OpenThread Authors.
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are met:
7 * 1. Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
12 * 3. Neither the name of the copyright holder nor the
13 * names of its contributors may be used to endorse or promote products
14 * derived from this software without specific prior written permission.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
17 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
20 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
21 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
22 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
23 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
24 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
25 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
26 * POSSIBILITY OF SUCH DAMAGE.
27 */
28
29 #define OTBR_LOG_TAG "NCP_HOST"
30
31 #include "ncp_host.hpp"
32
33 #include <memory>
34
35 #include <openthread/error.h>
36 #include <openthread/thread.h>
37
38 #include <openthread/openthread-system.h>
39
40 #include "host/async_task.hpp"
41 #include "lib/spinel/spinel_driver.hpp"
42
43 namespace otbr {
44 namespace Host {
45
46 // =============================== NcpNetworkProperties ===============================
47
NcpNetworkProperties(void)48 NcpNetworkProperties::NcpNetworkProperties(void)
49 : mDeviceRole(OT_DEVICE_ROLE_DISABLED)
50 {
51 memset(&mDatasetActiveTlvs, 0, sizeof(mDatasetActiveTlvs));
52 }
53
GetDeviceRole(void) const54 otDeviceRole NcpNetworkProperties::GetDeviceRole(void) const
55 {
56 return mDeviceRole;
57 }
58
SetDeviceRole(otDeviceRole aRole)59 void NcpNetworkProperties::SetDeviceRole(otDeviceRole aRole)
60 {
61 mDeviceRole = aRole;
62 }
63
Ip6IsEnabled(void) const64 bool NcpNetworkProperties::Ip6IsEnabled(void) const
65 {
66 // TODO: Implement the method under NCP mode.
67 return false;
68 }
69
GetPartitionId(void) const70 uint32_t NcpNetworkProperties::GetPartitionId(void) const
71 {
72 // TODO: Implement the method under NCP mode.
73 return 0;
74 }
75
SetDatasetActiveTlvs(const otOperationalDatasetTlvs & aActiveOpDatasetTlvs)76 void NcpNetworkProperties::SetDatasetActiveTlvs(const otOperationalDatasetTlvs &aActiveOpDatasetTlvs)
77 {
78 mDatasetActiveTlvs.mLength = aActiveOpDatasetTlvs.mLength;
79 memcpy(mDatasetActiveTlvs.mTlvs, aActiveOpDatasetTlvs.mTlvs, aActiveOpDatasetTlvs.mLength);
80 }
81
GetDatasetActiveTlvs(otOperationalDatasetTlvs & aDatasetTlvs) const82 void NcpNetworkProperties::GetDatasetActiveTlvs(otOperationalDatasetTlvs &aDatasetTlvs) const
83 {
84 aDatasetTlvs.mLength = mDatasetActiveTlvs.mLength;
85 memcpy(aDatasetTlvs.mTlvs, mDatasetActiveTlvs.mTlvs, mDatasetActiveTlvs.mLength);
86 }
87
GetDatasetPendingTlvs(otOperationalDatasetTlvs & aDatasetTlvs) const88 void NcpNetworkProperties::GetDatasetPendingTlvs(otOperationalDatasetTlvs &aDatasetTlvs) const
89 {
90 // TODO: Implement the method under NCP mode.
91 OTBR_UNUSED_VARIABLE(aDatasetTlvs);
92 }
93
94 // ===================================== NcpHost ======================================
95
NcpHost(const char * aInterfaceName,const char * aBackboneInterfaceName,bool aDryRun)96 NcpHost::NcpHost(const char *aInterfaceName, const char *aBackboneInterfaceName, bool aDryRun)
97 : mSpinelDriver(*static_cast<ot::Spinel::SpinelDriver *>(otSysGetSpinelDriver()))
98 , mNetif(mNcpSpinel)
99 , mInfraIf(mNcpSpinel)
100 {
101 memset(&mConfig, 0, sizeof(mConfig));
102 mConfig.mInterfaceName = aInterfaceName;
103 mConfig.mBackboneInterfaceName = aBackboneInterfaceName;
104 mConfig.mDryRun = aDryRun;
105 mConfig.mSpeedUpFactor = 1;
106 }
107
GetCoprocessorVersion(void)108 const char *NcpHost::GetCoprocessorVersion(void)
109 {
110 return mSpinelDriver.GetVersion();
111 }
112
Init(void)113 void NcpHost::Init(void)
114 {
115 otSysInit(&mConfig);
116 mNcpSpinel.Init(mSpinelDriver, *this);
117 mNetif.Init(mConfig.mInterfaceName);
118 mInfraIf.Init();
119
120 mNcpSpinel.Ip6SetAddressCallback(
121 [this](const std::vector<Ip6AddressInfo> &aAddrInfos) { mNetif.UpdateIp6UnicastAddresses(aAddrInfos); });
122 mNcpSpinel.Ip6SetAddressMulticastCallback(
123 [this](const std::vector<Ip6Address> &aAddrs) { mNetif.UpdateIp6MulticastAddresses(aAddrs); });
124 mNcpSpinel.NetifSetStateChangedCallback([this](bool aState) { mNetif.SetNetifState(aState); });
125 mNcpSpinel.Ip6SetReceiveCallback(
126 [this](const uint8_t *aData, uint16_t aLength) { mNetif.Ip6Receive(aData, aLength); });
127 mNcpSpinel.InfraIfSetIcmp6NdSendCallback(
128 [this](uint32_t aInfraIfIndex, const otIp6Address &aAddr, const uint8_t *aData, uint16_t aDataLen) {
129 OTBR_UNUSED_VARIABLE(mInfraIf.SendIcmp6Nd(aInfraIfIndex, aAddr, aData, aDataLen));
130 });
131
132 if (mConfig.mBackboneInterfaceName != nullptr && strlen(mConfig.mBackboneInterfaceName) > 0)
133 {
134 mInfraIf.SetInfraIf(mConfig.mBackboneInterfaceName);
135 }
136
137 #if OTBR_ENABLE_SRP_ADVERTISING_PROXY
138 #if OTBR_ENABLE_SRP_SERVER_AUTO_ENABLE_MODE
139 // Let SRP server use auto-enable mode. The auto-enable mode delegates the control of SRP server to the Border
140 // Routing Manager. SRP server automatically starts when bi-directional connectivity is ready.
141 mNcpSpinel.SrpServerSetAutoEnableMode(/* aEnabled */ true);
142 #else
143 mNcpSpinel.SrpServerSetEnabled(/* aEnabled */ true);
144 #endif
145 #endif
146 }
147
Deinit(void)148 void NcpHost::Deinit(void)
149 {
150 mNcpSpinel.Deinit();
151 mNetif.Deinit();
152 otSysDeinit();
153 }
154
Join(const otOperationalDatasetTlvs & aActiveOpDatasetTlvs,const AsyncResultReceiver & aReceiver)155 void NcpHost::Join(const otOperationalDatasetTlvs &aActiveOpDatasetTlvs, const AsyncResultReceiver &aReceiver)
156 {
157 AsyncTaskPtr task;
158 auto errorHandler = [aReceiver](otError aError, const std::string &aErrorInfo) { aReceiver(aError, aErrorInfo); };
159
160 task = std::make_shared<AsyncTask>(errorHandler);
161 task->First([this, aActiveOpDatasetTlvs](AsyncTaskPtr aNext) {
162 mNcpSpinel.DatasetSetActiveTlvs(aActiveOpDatasetTlvs, std::move(aNext));
163 })
164 ->Then([this](AsyncTaskPtr aNext) { mNcpSpinel.Ip6SetEnabled(true, std::move(aNext)); })
165 ->Then([this](AsyncTaskPtr aNext) { mNcpSpinel.ThreadSetEnabled(true, std::move(aNext)); });
166 task->Run();
167 }
168
Leave(bool aEraseDataset,const AsyncResultReceiver & aReceiver)169 void NcpHost::Leave(bool aEraseDataset, const AsyncResultReceiver &aReceiver)
170 {
171 AsyncTaskPtr task;
172 auto errorHandler = [aReceiver](otError aError, const std::string &aErrorInfo) { aReceiver(aError, aErrorInfo); };
173
174 task = std::make_shared<AsyncTask>(errorHandler);
175 task->First([this](AsyncTaskPtr aNext) { mNcpSpinel.ThreadDetachGracefully(std::move(aNext)); })
176 ->Then([this, aEraseDataset](AsyncTaskPtr aNext) {
177 if (aEraseDataset)
178 {
179 mNcpSpinel.ThreadErasePersistentInfo(std::move(aNext));
180 }
181 else
182 {
183 aNext->SetResult(OT_ERROR_NONE, "");
184 }
185 });
186 task->Run();
187 }
188
ScheduleMigration(const otOperationalDatasetTlvs & aPendingOpDatasetTlvs,const AsyncResultReceiver aReceiver)189 void NcpHost::ScheduleMigration(const otOperationalDatasetTlvs &aPendingOpDatasetTlvs,
190 const AsyncResultReceiver aReceiver)
191 {
192 otDeviceRole role = GetDeviceRole();
193 otError error = OT_ERROR_NONE;
194 auto errorHandler = [aReceiver](otError aError, const std::string &aErrorInfo) { aReceiver(aError, aErrorInfo); };
195
196 VerifyOrExit(role != OT_DEVICE_ROLE_DISABLED && role != OT_DEVICE_ROLE_DETACHED, error = OT_ERROR_INVALID_STATE);
197
198 mNcpSpinel.DatasetMgmtSetPending(std::make_shared<otOperationalDatasetTlvs>(aPendingOpDatasetTlvs),
199 std::make_shared<AsyncTask>(errorHandler));
200
201 exit:
202 if (error != OT_ERROR_NONE)
203 {
204 mTaskRunner.Post(
205 [aReceiver, error](void) { aReceiver(error, "Cannot schedule migration when this device is detached"); });
206 }
207 }
208
SetThreadEnabled(bool aEnabled,const AsyncResultReceiver aReceiver)209 void NcpHost::SetThreadEnabled(bool aEnabled, const AsyncResultReceiver aReceiver)
210 {
211 OT_UNUSED_VARIABLE(aEnabled);
212
213 // TODO: Implement SetThreadEnabled under NCP mode.
214 mTaskRunner.Post([aReceiver](void) { aReceiver(OT_ERROR_NOT_IMPLEMENTED, "Not implemented!"); });
215 }
216
SetCountryCode(const std::string & aCountryCode,const AsyncResultReceiver & aReceiver)217 void NcpHost::SetCountryCode(const std::string &aCountryCode, const AsyncResultReceiver &aReceiver)
218 {
219 OT_UNUSED_VARIABLE(aCountryCode);
220
221 // TODO: Implement SetCountryCode under NCP mode.
222 mTaskRunner.Post([aReceiver](void) { aReceiver(OT_ERROR_NOT_IMPLEMENTED, "Not implemented!"); });
223 }
224
GetChannelMasks(const ChannelMasksReceiver & aReceiver,const AsyncResultReceiver & aErrReceiver)225 void NcpHost::GetChannelMasks(const ChannelMasksReceiver &aReceiver, const AsyncResultReceiver &aErrReceiver)
226 {
227 OT_UNUSED_VARIABLE(aReceiver);
228
229 // TODO: Implement GetChannelMasks under NCP mode.
230 mTaskRunner.Post([aErrReceiver](void) { aErrReceiver(OT_ERROR_NOT_IMPLEMENTED, "Not implemented!"); });
231 }
232
233 #if OTBR_ENABLE_POWER_CALIBRATION
SetChannelMaxPowers(const std::vector<ChannelMaxPower> & aChannelMaxPowers,const AsyncResultReceiver & aReceiver)234 void NcpHost::SetChannelMaxPowers(const std::vector<ChannelMaxPower> &aChannelMaxPowers,
235 const AsyncResultReceiver &aReceiver)
236 {
237 OT_UNUSED_VARIABLE(aChannelMaxPowers);
238
239 // TODO: Implement SetChannelMaxPowers under NCP mode.
240 mTaskRunner.Post([aReceiver](void) { aReceiver(OT_ERROR_NOT_IMPLEMENTED, "Not implemented!"); });
241 }
242 #endif
243
AddThreadStateChangedCallback(ThreadStateChangedCallback aCallback)244 void NcpHost::AddThreadStateChangedCallback(ThreadStateChangedCallback aCallback)
245 {
246 // TODO: Implement AddThreadStateChangedCallback under NCP mode.
247 OT_UNUSED_VARIABLE(aCallback);
248 }
249
AddThreadEnabledStateChangedCallback(ThreadEnabledStateCallback aCallback)250 void NcpHost::AddThreadEnabledStateChangedCallback(ThreadEnabledStateCallback aCallback)
251 {
252 // TODO: Implement AddThreadEnabledStateChangedCallback under NCP mode.
253 OT_UNUSED_VARIABLE(aCallback);
254 }
255
Process(const MainloopContext & aMainloop)256 void NcpHost::Process(const MainloopContext &aMainloop)
257 {
258 mSpinelDriver.Process(&aMainloop);
259
260 mNetif.Process(&aMainloop);
261 }
262
Update(MainloopContext & aMainloop)263 void NcpHost::Update(MainloopContext &aMainloop)
264 {
265 mSpinelDriver.GetSpinelInterface()->UpdateFdSet(&aMainloop);
266
267 if (mSpinelDriver.HasPendingFrame())
268 {
269 aMainloop.mTimeout.tv_sec = 0;
270 aMainloop.mTimeout.tv_usec = 0;
271 }
272
273 mNetif.UpdateFdSet(&aMainloop);
274 }
275
276 #if OTBR_ENABLE_SRP_ADVERTISING_PROXY
SetMdnsPublisher(Mdns::Publisher * aPublisher)277 void NcpHost::SetMdnsPublisher(Mdns::Publisher *aPublisher)
278 {
279 mNcpSpinel.SetMdnsPublisher(aPublisher);
280 }
281
HandleMdnsState(Mdns::Publisher::State aState)282 void NcpHost::HandleMdnsState(Mdns::Publisher::State aState)
283 {
284 mNcpSpinel.DnssdSetState(aState);
285 }
286 #endif
287
288 } // namespace Host
289 } // namespace otbr
290