1 /*
2 * Copyright (c) 2019, 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"
30
31 #include "ncp/ncp_openthread.hpp"
32
33 #include <assert.h>
34 #include <stdio.h>
35 #include <string.h>
36
37 #include <openthread/backbone_router_ftd.h>
38 #include <openthread/dataset.h>
39 #include <openthread/logging.h>
40 #include <openthread/srp_server.h>
41 #include <openthread/tasklet.h>
42 #include <openthread/thread.h>
43 #include <openthread/thread_ftd.h>
44 #include <openthread/platform/logging.h>
45 #include <openthread/platform/misc.h>
46 #include <openthread/platform/radio.h>
47 #include <openthread/platform/settings.h>
48
49 #include "common/code_utils.hpp"
50 #include "common/logging.hpp"
51 #include "common/types.hpp"
52
53 #if OTBR_ENABLE_LEGACY
54 #include <ot-legacy-pairing-ext.h>
55 #endif
56
57 namespace otbr {
58 namespace Ncp {
59
60 static const uint16_t kThreadVersion11 = 2; ///< Thread Version 1.1
61 static const uint16_t kThreadVersion12 = 3; ///< Thread Version 1.2
62 static const uint16_t kThreadVersion13 = 4; ///< Thread Version 1.3
63
ControllerOpenThread(const char * aInterfaceName,const std::vector<const char * > & aRadioUrls,const char * aBackboneInterfaceName,bool aDryRun,bool aEnableAutoAttach)64 ControllerOpenThread::ControllerOpenThread(const char * aInterfaceName,
65 const std::vector<const char *> &aRadioUrls,
66 const char * aBackboneInterfaceName,
67 bool aDryRun,
68 bool aEnableAutoAttach)
69 : mInstance(nullptr)
70 , mEnableAutoAttach(aEnableAutoAttach)
71 {
72 VerifyOrDie(aRadioUrls.size() <= OT_PLATFORM_CONFIG_MAX_RADIO_URLS, "Too many Radio URLs!");
73
74 memset(&mConfig, 0, sizeof(mConfig));
75
76 mConfig.mInterfaceName = aInterfaceName;
77 mConfig.mBackboneInterfaceName = aBackboneInterfaceName;
78 mConfig.mDryRun = aDryRun;
79
80 for (const char *url : aRadioUrls)
81 {
82 mConfig.mRadioUrls[mConfig.mRadioUrlNum++] = url;
83 }
84 mConfig.mSpeedUpFactor = 1;
85 }
86
~ControllerOpenThread(void)87 ControllerOpenThread::~ControllerOpenThread(void)
88 {
89 // Make sure OpenThread Instance was gracefully de-initialized.
90 assert(mInstance == nullptr);
91 }
92
ConvertToOtbrLogLevel(otLogLevel aLogLevel)93 otbrLogLevel ControllerOpenThread::ConvertToOtbrLogLevel(otLogLevel aLogLevel)
94 {
95 otbrLogLevel otbrLogLevel;
96
97 switch (aLogLevel)
98 {
99 case OT_LOG_LEVEL_NONE:
100 otbrLogLevel = OTBR_LOG_EMERG;
101 break;
102 case OT_LOG_LEVEL_CRIT:
103 otbrLogLevel = OTBR_LOG_CRIT;
104 break;
105 case OT_LOG_LEVEL_WARN:
106 otbrLogLevel = OTBR_LOG_WARNING;
107 break;
108 case OT_LOG_LEVEL_NOTE:
109 otbrLogLevel = OTBR_LOG_NOTICE;
110 break;
111 case OT_LOG_LEVEL_INFO:
112 otbrLogLevel = OTBR_LOG_INFO;
113 break;
114 case OT_LOG_LEVEL_DEBG:
115 default:
116 otbrLogLevel = OTBR_LOG_DEBUG;
117 break;
118 }
119
120 return otbrLogLevel;
121 }
122
ConvertToOtLogLevel(otbrLogLevel aLevel)123 otLogLevel ControllerOpenThread::ConvertToOtLogLevel(otbrLogLevel aLevel)
124 {
125 otLogLevel level;
126
127 switch (aLevel)
128 {
129 case OTBR_LOG_EMERG:
130 case OTBR_LOG_ALERT:
131 case OTBR_LOG_CRIT:
132 level = OT_LOG_LEVEL_CRIT;
133 break;
134 case OTBR_LOG_ERR:
135 case OTBR_LOG_WARNING:
136 level = OT_LOG_LEVEL_WARN;
137 break;
138 case OTBR_LOG_NOTICE:
139 level = OT_LOG_LEVEL_NOTE;
140 break;
141 case OTBR_LOG_INFO:
142 level = OT_LOG_LEVEL_INFO;
143 break;
144 case OTBR_LOG_DEBUG:
145 default:
146 level = OT_LOG_LEVEL_DEBG;
147 break;
148 }
149
150 return level;
151 }
152
Init(void)153 void ControllerOpenThread::Init(void)
154 {
155 otbrError error = OTBR_ERROR_NONE;
156 otLogLevel level = ConvertToOtLogLevel(otbrLogGetLevel());
157
158 VerifyOrExit(otLoggingSetLevel(level) == OT_ERROR_NONE, error = OTBR_ERROR_OPENTHREAD);
159
160 mInstance = otSysInit(&mConfig);
161 assert(mInstance != nullptr);
162
163 #if OTBR_ENABLE_LEGACY
164 otLegacyInit();
165 #endif
166
167 {
168 otError result = otSetStateChangedCallback(mInstance, &ControllerOpenThread::HandleStateChanged, this);
169
170 agent::ThreadHelper::LogOpenThreadResult("Set state callback", result);
171 VerifyOrExit(result == OT_ERROR_NONE, error = OTBR_ERROR_OPENTHREAD);
172 }
173
174 #if OTBR_ENABLE_SRP_ADVERTISING_PROXY
175 otSrpServerSetEnabled(mInstance, /* aEnabled */ true);
176 #endif
177
178 mThreadHelper = std::unique_ptr<otbr::agent::ThreadHelper>(new otbr::agent::ThreadHelper(mInstance, this));
179
180 exit:
181 SuccessOrDie(error, "Failed to initialize NCP!");
182 }
183
Deinit(void)184 void ControllerOpenThread::Deinit(void)
185 {
186 assert(mInstance != nullptr);
187
188 otSysDeinit();
189 mInstance = nullptr;
190 }
191
HandleStateChanged(otChangedFlags aFlags)192 void ControllerOpenThread::HandleStateChanged(otChangedFlags aFlags)
193 {
194 if (aFlags & OT_CHANGED_THREAD_ROLE)
195 {
196 switch (otThreadGetDeviceRole(mInstance))
197 {
198 case OT_DEVICE_ROLE_DISABLED:
199 #if OTBR_ENABLE_LEGACY
200 otLegacyStop();
201 #endif
202 break;
203 case OT_DEVICE_ROLE_CHILD:
204 case OT_DEVICE_ROLE_ROUTER:
205 case OT_DEVICE_ROLE_LEADER:
206 #if OTBR_ENABLE_LEGACY
207 otLegacyStart();
208 #endif
209 break;
210 default:
211 break;
212 }
213 }
214
215 for (auto &stateCallback : mThreadStateChangedCallbacks)
216 {
217 stateCallback(aFlags);
218 }
219
220 mThreadHelper->StateChangedCallback(aFlags);
221 }
222
Update(MainloopContext & aMainloop)223 void ControllerOpenThread::Update(MainloopContext &aMainloop)
224 {
225 if (otTaskletsArePending(mInstance))
226 {
227 aMainloop.mTimeout = ToTimeval(Microseconds::zero());
228 }
229
230 otSysMainloopUpdate(mInstance, &aMainloop);
231 }
232
Process(const MainloopContext & aMainloop)233 void ControllerOpenThread::Process(const MainloopContext &aMainloop)
234 {
235 otTaskletsProcess(mInstance);
236
237 otSysMainloopProcess(mInstance, &aMainloop);
238
239 if (IsAutoAttachEnabled() && mThreadHelper->TryResumeNetwork() == OT_ERROR_NONE)
240 {
241 DisableAutoAttach();
242 }
243 }
244
IsAutoAttachEnabled(void)245 bool ControllerOpenThread::IsAutoAttachEnabled(void)
246 {
247 return mEnableAutoAttach;
248 }
249
DisableAutoAttach(void)250 void ControllerOpenThread::DisableAutoAttach(void)
251 {
252 mEnableAutoAttach = false;
253 }
254
PostTimerTask(Milliseconds aDelay,TaskRunner::Task<void> aTask)255 void ControllerOpenThread::PostTimerTask(Milliseconds aDelay, TaskRunner::Task<void> aTask)
256 {
257 mTaskRunner.Post(std::move(aDelay), std::move(aTask));
258 }
259
RegisterResetHandler(std::function<void (void)> aHandler)260 void ControllerOpenThread::RegisterResetHandler(std::function<void(void)> aHandler)
261 {
262 mResetHandlers.emplace_back(std::move(aHandler));
263 }
264
AddThreadStateChangedCallback(ThreadStateChangedCallback aCallback)265 void ControllerOpenThread::AddThreadStateChangedCallback(ThreadStateChangedCallback aCallback)
266 {
267 mThreadStateChangedCallbacks.emplace_back(std::move(aCallback));
268 }
269
Reset(void)270 void ControllerOpenThread::Reset(void)
271 {
272 gPlatResetReason = OT_PLAT_RESET_REASON_SOFTWARE;
273
274 otSysDeinit();
275 mInstance = nullptr;
276
277 Init();
278 for (auto &handler : mResetHandlers)
279 {
280 handler();
281 }
282 mEnableAutoAttach = true;
283 }
284
GetThreadVersion(void)285 const char *ControllerOpenThread::GetThreadVersion(void)
286 {
287 const char *version;
288
289 switch (otThreadGetVersion())
290 {
291 case kThreadVersion11:
292 version = "1.1.1";
293 break;
294 case kThreadVersion12:
295 version = "1.2.0";
296 break;
297 case kThreadVersion13:
298 version = "1.3.0";
299 break;
300 default:
301 otbrLogEmerg("Unexpected thread version %hu", otThreadGetVersion());
302 exit(-1);
303 }
304 return version;
305 }
306
307 /*
308 * Provide, if required an "otPlatLog()" function
309 */
otPlatLog(otLogLevel aLogLevel,otLogRegion aLogRegion,const char * aFormat,...)310 extern "C" void otPlatLog(otLogLevel aLogLevel, otLogRegion aLogRegion, const char *aFormat, ...)
311 {
312 OT_UNUSED_VARIABLE(aLogRegion);
313
314 otbrLogLevel otbrLogLevel = ControllerOpenThread::ConvertToOtbrLogLevel(aLogLevel);
315
316 va_list ap;
317 va_start(ap, aFormat);
318 otbrLogvNoFilter(otbrLogLevel, aFormat, ap);
319 va_end(ap);
320 }
321
otPlatLogHandleLevelChanged(otLogLevel aLogLevel)322 extern "C" void otPlatLogHandleLevelChanged(otLogLevel aLogLevel)
323 {
324 otbrLogSetLevel(ControllerOpenThread::ConvertToOtbrLogLevel(aLogLevel));
325 otbrLogInfo("OpenThread log level changed to %d", aLogLevel);
326 }
327
328 } // namespace Ncp
329 } // namespace otbr
330