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 "lib/spinel/spinel_driver.hpp"
41
42 #include "ncp/async_task.hpp"
43
44 namespace otbr {
45 namespace Ncp {
46
47 // =============================== NcpNetworkProperties ===============================
48
NcpNetworkProperties(void)49 NcpNetworkProperties::NcpNetworkProperties(void)
50 : mDeviceRole(OT_DEVICE_ROLE_DISABLED)
51 {
52 memset(&mDatasetActiveTlvs, 0, sizeof(mDatasetActiveTlvs));
53 }
54
GetDeviceRole(void) const55 otDeviceRole NcpNetworkProperties::GetDeviceRole(void) const
56 {
57 return mDeviceRole;
58 }
59
SetDeviceRole(otDeviceRole aRole)60 void NcpNetworkProperties::SetDeviceRole(otDeviceRole aRole)
61 {
62 mDeviceRole = aRole;
63 }
64
Ip6IsEnabled(void) const65 bool NcpNetworkProperties::Ip6IsEnabled(void) const
66 {
67 // TODO: Implement the method under NCP mode.
68 return false;
69 }
70
GetPartitionId(void) const71 uint32_t NcpNetworkProperties::GetPartitionId(void) const
72 {
73 // TODO: Implement the method under NCP mode.
74 return 0;
75 }
76
SetDatasetActiveTlvs(const otOperationalDatasetTlvs & aActiveOpDatasetTlvs)77 void NcpNetworkProperties::SetDatasetActiveTlvs(const otOperationalDatasetTlvs &aActiveOpDatasetTlvs)
78 {
79 mDatasetActiveTlvs.mLength = aActiveOpDatasetTlvs.mLength;
80 memcpy(mDatasetActiveTlvs.mTlvs, aActiveOpDatasetTlvs.mTlvs, aActiveOpDatasetTlvs.mLength);
81 }
82
GetDatasetActiveTlvs(otOperationalDatasetTlvs & aDatasetTlvs) const83 void NcpNetworkProperties::GetDatasetActiveTlvs(otOperationalDatasetTlvs &aDatasetTlvs) const
84 {
85 aDatasetTlvs.mLength = mDatasetActiveTlvs.mLength;
86 memcpy(aDatasetTlvs.mTlvs, mDatasetActiveTlvs.mTlvs, mDatasetActiveTlvs.mLength);
87 }
88
GetDatasetPendingTlvs(otOperationalDatasetTlvs & aDatasetTlvs) const89 void NcpNetworkProperties::GetDatasetPendingTlvs(otOperationalDatasetTlvs &aDatasetTlvs) const
90 {
91 // TODO: Implement the method under NCP mode.
92 OTBR_UNUSED_VARIABLE(aDatasetTlvs);
93 }
94
95 // ===================================== NcpHost ======================================
96
NcpHost(const char * aInterfaceName,bool aDryRun)97 NcpHost::NcpHost(const char *aInterfaceName, bool aDryRun)
98 : mSpinelDriver(*static_cast<ot::Spinel::SpinelDriver *>(otSysGetSpinelDriver()))
99 , mNetif()
100 {
101 memset(&mConfig, 0, sizeof(mConfig));
102 mConfig.mInterfaceName = aInterfaceName;
103 mConfig.mDryRun = aDryRun;
104 mConfig.mSpeedUpFactor = 1;
105 }
106
GetCoprocessorVersion(void)107 const char *NcpHost::GetCoprocessorVersion(void)
108 {
109 return mSpinelDriver.GetVersion();
110 }
111
Init(void)112 void NcpHost::Init(void)
113 {
114 otSysInit(&mConfig);
115 mNcpSpinel.Init(mSpinelDriver, *this);
116 mNetif.Init(mConfig.mInterfaceName,
117 [this](const uint8_t *aData, uint16_t aLength) { return mNcpSpinel.Ip6Send(aData, aLength); });
118
119 mNcpSpinel.Ip6SetAddressCallback(
120 [this](const std::vector<Ip6AddressInfo> &aAddrInfos) { mNetif.UpdateIp6UnicastAddresses(aAddrInfos); });
121 mNcpSpinel.Ip6SetAddressMulticastCallback(
122 [this](const std::vector<Ip6Address> &aAddrs) { mNetif.UpdateIp6MulticastAddresses(aAddrs); });
123 mNcpSpinel.NetifSetStateChangedCallback([this](bool aState) { mNetif.SetNetifState(aState); });
124 }
125
Deinit(void)126 void NcpHost::Deinit(void)
127 {
128 mNcpSpinel.Deinit();
129 mNetif.Deinit();
130 otSysDeinit();
131 }
132
Join(const otOperationalDatasetTlvs & aActiveOpDatasetTlvs,const AsyncResultReceiver & aReceiver)133 void NcpHost::Join(const otOperationalDatasetTlvs &aActiveOpDatasetTlvs, const AsyncResultReceiver &aReceiver)
134 {
135 AsyncTaskPtr task;
136 auto errorHandler = [aReceiver](otError aError, const std::string &aErrorInfo) { aReceiver(aError, aErrorInfo); };
137
138 task = std::make_shared<AsyncTask>(errorHandler);
139 task->First([this, aActiveOpDatasetTlvs](AsyncTaskPtr aNext) {
140 mNcpSpinel.DatasetSetActiveTlvs(aActiveOpDatasetTlvs, std::move(aNext));
141 })
142 ->Then([this](AsyncTaskPtr aNext) { mNcpSpinel.Ip6SetEnabled(true, std::move(aNext)); })
143 ->Then([this](AsyncTaskPtr aNext) { mNcpSpinel.ThreadSetEnabled(true, std::move(aNext)); });
144 task->Run();
145 }
146
Leave(const AsyncResultReceiver & aReceiver)147 void NcpHost::Leave(const AsyncResultReceiver &aReceiver)
148 {
149 AsyncTaskPtr task;
150 auto errorHandler = [aReceiver](otError aError, const std::string &aErrorInfo) { aReceiver(aError, aErrorInfo); };
151
152 task = std::make_shared<AsyncTask>(errorHandler);
153 task->First([this](AsyncTaskPtr aNext) { mNcpSpinel.ThreadDetachGracefully(std::move(aNext)); })
154 ->Then([this](AsyncTaskPtr aNext) { mNcpSpinel.ThreadErasePersistentInfo(std::move(aNext)); });
155 task->Run();
156 }
157
ScheduleMigration(const otOperationalDatasetTlvs & aPendingOpDatasetTlvs,const AsyncResultReceiver aReceiver)158 void NcpHost::ScheduleMigration(const otOperationalDatasetTlvs &aPendingOpDatasetTlvs,
159 const AsyncResultReceiver aReceiver)
160 {
161 otDeviceRole role = GetDeviceRole();
162 otError error = OT_ERROR_NONE;
163 auto errorHandler = [aReceiver](otError aError, const std::string &aErrorInfo) { aReceiver(aError, aErrorInfo); };
164
165 VerifyOrExit(role != OT_DEVICE_ROLE_DISABLED && role != OT_DEVICE_ROLE_DETACHED, error = OT_ERROR_INVALID_STATE);
166
167 mNcpSpinel.DatasetMgmtSetPending(std::make_shared<otOperationalDatasetTlvs>(aPendingOpDatasetTlvs),
168 std::make_shared<AsyncTask>(errorHandler));
169
170 exit:
171 if (error != OT_ERROR_NONE)
172 {
173 mTaskRunner.Post(
174 [aReceiver, error](void) { aReceiver(error, "Cannot schedule migration when this device is detached"); });
175 }
176 }
177
SetThreadEnabled(bool aEnabled,const AsyncResultReceiver aReceiver)178 void NcpHost::SetThreadEnabled(bool aEnabled, const AsyncResultReceiver aReceiver)
179 {
180 OT_UNUSED_VARIABLE(aEnabled);
181
182 // TODO: Implement SetThreadEnabled under NCP mode.
183 mTaskRunner.Post([aReceiver](void) { aReceiver(OT_ERROR_NOT_IMPLEMENTED, "Not implemented!"); });
184 }
185
SetCountryCode(const std::string & aCountryCode,const AsyncResultReceiver & aReceiver)186 void NcpHost::SetCountryCode(const std::string &aCountryCode, const AsyncResultReceiver &aReceiver)
187 {
188 OT_UNUSED_VARIABLE(aCountryCode);
189
190 // TODO: Implement SetCountryCode under NCP mode.
191 mTaskRunner.Post([aReceiver](void) { aReceiver(OT_ERROR_NOT_IMPLEMENTED, "Not implemented!"); });
192 }
193
GetChannelMasks(const ChannelMasksReceiver & aReceiver,const AsyncResultReceiver & aErrReceiver)194 void NcpHost::GetChannelMasks(const ChannelMasksReceiver &aReceiver, const AsyncResultReceiver &aErrReceiver)
195 {
196 OT_UNUSED_VARIABLE(aReceiver);
197
198 // TODO: Implement GetChannelMasks under NCP mode.
199 mTaskRunner.Post([aErrReceiver](void) { aErrReceiver(OT_ERROR_NOT_IMPLEMENTED, "Not implemented!"); });
200 }
201
SetChannelMaxPowers(const std::vector<ChannelMaxPower> & aChannelMaxPowers,const AsyncResultReceiver & aReceiver)202 void NcpHost::SetChannelMaxPowers(const std::vector<ChannelMaxPower> &aChannelMaxPowers,
203 const AsyncResultReceiver &aReceiver)
204 {
205 OT_UNUSED_VARIABLE(aChannelMaxPowers);
206
207 // TODO: Implement SetChannelMaxPowers under NCP mode.
208 mTaskRunner.Post([aReceiver](void) { aReceiver(OT_ERROR_NOT_IMPLEMENTED, "Not implemented!"); });
209 }
210
AddThreadStateChangedCallback(ThreadStateChangedCallback aCallback)211 void NcpHost::AddThreadStateChangedCallback(ThreadStateChangedCallback aCallback)
212 {
213 // TODO: Implement AddThreadStateChangedCallback under NCP mode.
214 OT_UNUSED_VARIABLE(aCallback);
215 }
216
Process(const MainloopContext & aMainloop)217 void NcpHost::Process(const MainloopContext &aMainloop)
218 {
219 mSpinelDriver.Process(&aMainloop);
220 }
221
Update(MainloopContext & aMainloop)222 void NcpHost::Update(MainloopContext &aMainloop)
223 {
224 mSpinelDriver.GetSpinelInterface()->UpdateFdSet(&aMainloop);
225
226 if (mSpinelDriver.HasPendingFrame())
227 {
228 aMainloop.mTimeout.tv_sec = 0;
229 aMainloop.mTimeout.tv_usec = 0;
230 }
231 }
232
233 } // namespace Ncp
234 } // namespace otbr
235